From fff9281b8454dadf5e31c297af904959fe35800b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 22 Nov 2015 18:35:50 +0000 Subject: [PATCH 001/591] [intel] Forcibly skip PHY reset on some models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some models (notably ICH), the PHY reset mechanism appears to be broken. In particular, the PHY_CTRL register will be correctly loaded from NVM but the values will not be propagated to the "OEM bits" PHY register. This typically has the effect of dropping the link speed to 10Mbps. Since the original version of this driver in commit 945e428 ("[intel] Replace driver for Intel Gigabit NICs"), we have always worked around this problem by skipping the PHY reset if the link is already up. Enhance this workaround by explicitly checking for known-broken PCI IDs. Reported-by: Robin Smidsrød Tested-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 25 +++++++++++++++++-------- src/drivers/net/intel.h | 2 ++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 2326bc528..e155b644d 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -295,14 +295,23 @@ static int intel_reset ( struct intel_nic *intel ) { writel ( ctrl, intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); - /* If link is already up, do not attempt to reset the PHY. On - * some models (notably ICH), performing a PHY reset seems to - * drop the link speed to 10Mbps. + /* On some models (notably ICH), the PHY reset mechanism + * appears to be broken. In particular, the PHY_CTRL register + * will be correctly loaded from NVM but the values will not + * be propagated to the "OEM bits" PHY register. This + * typically has the effect of dropping the link speed to + * 10Mbps. + * + * Work around this problem by skipping the PHY reset if + * either (a) the link is already up, or (b) this particular + * NIC is known to be broken. */ status = readl ( intel->regs + INTEL_STATUS ); - if ( status & INTEL_STATUS_LU ) { - DBGC ( intel, "INTEL %p MAC reset (ctrl %08x)\n", - intel, ctrl ); + if ( ( intel->flags & INTEL_NO_PHY_RST ) || + ( status & INTEL_STATUS_LU ) ) { + DBGC ( intel, "INTEL %p %sMAC reset (ctrl %08x)\n", intel, + ( ( intel->flags & INTEL_NO_PHY_RST ) ? "forced " : "" ), + ctrl ); return 0; } @@ -1029,7 +1038,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x10f5, "82567lm", "82567LM", 0 ), PCI_ROM ( 0x8086, 0x10f6, "82574l", "82574L", 0 ), PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", INTEL_PBS_ERRATA ), - PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", 0 ), + PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x1503, "82579v", "82579V", 0 ), PCI_ROM ( 0x8086, 0x150a, "82576ns", "82576NS", 0 ), PCI_ROM ( 0x8086, 0x150c, "82583v", "82583V", 0 ), @@ -1057,7 +1066,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", 0 ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ), - PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", 0 ), + PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index ce9e3f467..436229ef6 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -301,6 +301,8 @@ enum intel_flags { INTEL_PBS_ERRATA = 0x0001, /** VMware missing interrupt workaround required */ INTEL_VMWARE = 0x0002, + /** PHY reset is broken */ + INTEL_NO_PHY_RST = 0x0004, }; /** From f3c2da7d4a0e7cf3ab3e9cc3c49517aedb9cf4cc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 22 Nov 2015 19:17:24 +0000 Subject: [PATCH 002/591] [intel] Correct definition of receive overrun bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Robin Smidsrød Tested-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/drivers/net/intel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index 436229ef6..16a72a11b 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -99,8 +99,8 @@ struct intel_descriptor { #define INTEL_IRQ_TXQE 0x00000002UL /**< Transmit queue empty */ #define INTEL_IRQ_LSC 0x00000004UL /**< Link status change */ #define INTEL_IRQ_RXDMT0 0x00000010UL /**< Receive queue low */ +#define INTEL_IRQ_RXO 0x00000040UL /**< Receive overrun */ #define INTEL_IRQ_RXT0 0x00000080UL /**< Receive timer */ -#define INTEL_IRQ_RXO 0x00000400UL /**< Receive overrun */ /** Interrupt Mask Set/Read Register */ #define INTEL_IMS 0x000d0UL From 8aa2026a9ff09538effe687feee9f48746bff6e8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Nov 2015 08:44:20 +0000 Subject: [PATCH 003/591] [infiniband] Add definitions for FDR and EDR link speeds Signed-off-by: Michael Brown --- src/include/ipxe/ib_mad.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/include/ipxe/ib_mad.h b/src/include/ipxe/ib_mad.h index ae1eea7e4..454eda1f5 100644 --- a/src/include/ipxe/ib_mad.h +++ b/src/include/ipxe/ib_mad.h @@ -144,6 +144,9 @@ struct ib_port_info { #define IB_LINK_SPEED_SDR 0x01 #define IB_LINK_SPEED_DDR 0x02 #define IB_LINK_SPEED_QDR 0x04 +#define IB_LINK_SPEED_FDR10 0x08 +#define IB_LINK_SPEED_FDR 0x10 +#define IB_LINK_SPEED_EDR 0x20 #define IB_PORT_STATE_DOWN 0x01 #define IB_PORT_STATE_INIT 0x02 From 475cc92b0bfc3158514c5ec816a4d80f86ef51fc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Nov 2015 16:24:23 +0000 Subject: [PATCH 004/591] [infiniband] Add qword accessors for ib_guid and ib_gid Signed-off-by: Michael Brown --- src/include/ipxe/ib_packet.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/include/ipxe/ib_packet.h b/src/include/ipxe/ib_packet.h index f275fcb09..2cea016bd 100644 --- a/src/include/ipxe/ib_packet.h +++ b/src/include/ipxe/ib_packet.h @@ -19,6 +19,7 @@ union ib_guid { uint8_t bytes[8]; uint16_t words[4]; uint32_t dwords[2]; + uint64_t qword; }; /** Infiniband Globally Unique Identifier debug message format */ @@ -33,6 +34,7 @@ union ib_gid { uint8_t bytes[16]; uint16_t words[8]; uint32_t dwords[4]; + uint64_t qwords[2]; struct { union ib_guid prefix; union ib_guid guid; From ed18cd56789ff4484028995c17aef232e9cc8e83 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Nov 2015 09:32:45 +0000 Subject: [PATCH 005/591] [pci] Add definitions for PCI Express function level reset (FLR) Signed-off-by: Michael Brown --- src/drivers/net/tg3/tg3.h | 1 - src/include/ipxe/pci.h | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index 2b85b065b..bfabad071 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -52,7 +52,6 @@ #define PCI_X_CMD 2 /* Modes & Features */ #define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ -#define PCI_EXP_DEVCTL 8 /* Device Control */ #define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ #define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ #define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index a841e00ff..89d9d8040 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -104,6 +104,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_PM_CTRL_PME_ENABLE 0x0100 /**< PME pin enable */ #define PCI_PM_CTRL_PME_STATUS 0x8000 /**< PME pin status */ +/** PCI Express */ +#define PCI_EXP_DEVCTL 0x08 +#define PCI_EXP_DEVCTL_FLR 0x8000 /**< Function level reset */ + /** Uncorrectable error status */ #define PCI_ERR_UNCOR_STATUS 0x04 @@ -128,6 +132,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ( ( ( (base) & 0xff ) << 16 ) | ( ( (sub) & 0xff ) << 8 ) | \ ( ( (progif) & 0xff) << 0 ) ) +/** PCI Express function level reset delay (in ms) */ +#define PCI_EXP_FLR_DELAY_MS 100 + /** A PCI device ID list entry */ struct pci_device_id { /** Name */ From 4957285b22b8c8749a7330f0cf11ccda910cfda6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 30 Nov 2015 22:22:13 +0000 Subject: [PATCH 006/591] [bitops] Fix definitions for big-endian devices Signed-off-by: Michael Brown --- src/include/ipxe/bitops.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/include/ipxe/bitops.h b/src/include/ipxe/bitops.h index 220ab0fe7..2e22c1dbc 100644 --- a/src/include/ipxe/bitops.h +++ b/src/include/ipxe/bitops.h @@ -38,19 +38,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Endianness selection. * - * This is a property of the NIC, not a property of the host CPU. + * This is a property of the device, not a property of the host CPU. */ #ifdef BITOPS_LITTLE_ENDIAN #define cpu_to_BIT64 cpu_to_le64 #define cpu_to_BIT32 cpu_to_le32 #define BIT64_to_cpu le64_to_cpu #define BIT32_to_cpu le32_to_cpu +#define QWORD_SHIFT( offset, width ) (offset) #endif #ifdef BITOPS_BIG_ENDIAN #define cpu_to_BIT64 cpu_to_be64 #define cpu_to_BIT32 cpu_to_be32 #define BIT64_to_cpu be64_to_cpu #define BIT32_to_cpu be32_to_cpu +#define QWORD_SHIFT( offset, width ) ( 64 - (offset) - (width) ) #endif /** Datatype used to represent a bit in the pseudo-structures */ @@ -93,6 +95,11 @@ typedef unsigned char pseudo_bit_t; #define QWORD_BIT_OFFSET( _ptr, _index, _field ) \ ( BIT_OFFSET ( _ptr, _field ) - ( 64 * (_index) ) ) +/** Qword bit shift for a field within a pseudo_bit_t structure */ +#define QWORD_BIT_SHIFT( _ptr, _index, _field ) \ + QWORD_SHIFT ( QWORD_BIT_OFFSET ( _ptr, _index, _field ), \ + BIT_WIDTH ( _ptr, _field ) ) + /** Bit mask for a field within a pseudo_bit_t structure */ #define BIT_MASK( _ptr, _field ) \ ( ( ~( ( uint64_t ) 0 ) ) >> \ @@ -105,7 +112,7 @@ typedef unsigned char pseudo_bit_t; #define BIT_ASSEMBLE_1( _ptr, _index, _field, _value ) \ ( ( ( uint64_t) (_value) ) << \ - QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + QWORD_BIT_SHIFT ( _ptr, _index, _field ) ) #define BIT_ASSEMBLE_2( _ptr, _index, _field, _value, ... ) \ ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ @@ -138,7 +145,7 @@ typedef unsigned char pseudo_bit_t; #define BIT_MASK_1( _ptr, _index, _field ) \ ( BIT_MASK ( _ptr, _field ) << \ - QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + QWORD_BIT_SHIFT ( _ptr, _index, _field ) ) #define BIT_MASK_2( _ptr, _index, _field, ... ) \ ( BIT_MASK_1 ( _ptr, _index, _field ) | \ @@ -165,7 +172,7 @@ typedef unsigned char pseudo_bit_t; BIT_MASK_6 ( _ptr, _index, __VA_ARGS__ ) ) /* - * Populate little-endian qwords from named fields and values + * Populate device-endian qwords from named fields and values * */ @@ -212,7 +219,7 @@ typedef unsigned char pseudo_bit_t; uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ uint64_t __value = BIT64_to_cpu ( *__ptr ); \ __value >>= \ - QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ + QWORD_BIT_SHIFT ( _ptr, __index, _field ); \ __value &= BIT_MASK ( _ptr, _field ); \ __value; \ } ) @@ -225,7 +232,7 @@ typedef unsigned char pseudo_bit_t; unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ unsigned int __shift = \ - QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ + QWORD_BIT_SHIFT ( _ptr, __index, _field ); \ uint64_t __value = (_value); \ *__ptr &= cpu_to_BIT64 ( ~( BIT_MASK ( _ptr, _field ) << \ __shift ) ); \ From 89c767bfd605a8ff966ccff0e593f0ba8e827f12 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 13 Nov 2015 13:51:17 +0000 Subject: [PATCH 007/591] [smsc95xx] Add driver for SMSC/Microchip LAN95xx USB Ethernet NICs Tested using QEMU and usbredir to expose the LAN9512 chip present on a Raspberry Pi. There is a known issue with the LAN9512: an extra two bytes are appended to every transmitted packet. These two bytes comprise: { 0x00, 0x08 } if packet length == 0 (mod 8) { CRC[0], 0x00 } if packet length == 7 (mod 8) { CRC[0], CRC[1] } otherwise The extra bytes are appended whether the Ethernet CRC is generated manually or added automatically by the hardware. The issue occurs with the Linux kernel driver as well as the iPXE driver. It appears to be an undocumented hardware errata. TCP/IP traffic is not affected, since the IP header length field causes the extraneous bytes to be discarded by the receiver. However, protocols that rely on the length of the Ethernet frame (such as FCoE or iPXE's "lotest" protocol) will be unusable on this hardware. Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 1127 ++++++++++++++++++++++++++++++++++++ src/drivers/net/smsc95xx.h | 254 ++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 1382 insertions(+) create mode 100644 src/drivers/net/smsc95xx.c create mode 100644 src/drivers/net/smsc95xx.h diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c new file mode 100644 index 000000000..9cd428cf9 --- /dev/null +++ b/src/drivers/net/smsc95xx.c @@ -0,0 +1,1127 @@ +/* + * Copyright (C) 2015 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 "smsc95xx.h" + +/** @file + * + * SMSC LAN95xx USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler smsc95xx_intr_profiler __profiler = + { .name = "smsc95xx.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler smsc95xx_in_profiler __profiler = + { .name = "smsc95xx.in" }; + +/** Bulk OUT profiler */ +static struct profiler smsc95xx_out_profiler __profiler = + { .name = "smsc95xx.out" }; + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Write register (without byte-swapping) + * + * @v smsc95xx SMSC95xx device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static int smsc95xx_raw_writel ( struct smsc95xx_device *smsc95xx, + unsigned int address, uint32_t value ) { + int rc; + + /* Write register */ + DBGCIO ( smsc95xx, "SMSC95XX %p [%03x] <= %08x\n", + smsc95xx, address, le32_to_cpu ( value ) ); + if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_REGISTER_WRITE, 0, + address, &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not write %03x: %s\n", + smsc95xx, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write register + * + * @v smsc95xx SMSC95xx device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smsc95xx_writel ( struct smsc95xx_device *smsc95xx, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = smsc95xx_raw_writel ( smsc95xx, address, + cpu_to_le32 ( value ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read register (without byte-swapping) + * + * @v smsc95xx SMSC95xx device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static int smsc95xx_raw_readl ( struct smsc95xx_device *smsc95xx, + unsigned int address, uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_REGISTER_READ, 0, + address, value, sizeof ( *value ) ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not read %03x: %s\n", + smsc95xx, address, strerror ( rc ) ); + return rc; + } + DBGCIO ( smsc95xx, "SMSC95XX %p [%03x] => %08x\n", + smsc95xx, address, le32_to_cpu ( *value ) ); + + return 0; +} + +/** + * Read register + * + * @v smsc95xx SMSC95xx device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smsc95xx_readl ( struct smsc95xx_device *smsc95xx, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = smsc95xx_raw_readl ( smsc95xx, address, value ) ) != 0 ) + return rc; + le32_to_cpus ( value ); + + return 0; +} + +/****************************************************************************** + * + * EEPROM access + * + ****************************************************************************** + */ + +/** + * Wait for EEPROM to become idle + * + * @v smsc95xx SMSC95xx device + * @ret rc Return status code + */ +static int smsc95xx_eeprom_wait ( struct smsc95xx_device *smsc95xx ) { + uint32_t e2p_cmd; + unsigned int i; + int rc; + + /* Wait for EPC_BSY to become clear */ + for ( i = 0 ; i < SMSC95XX_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* Read E2P_CMD and check EPC_BSY */ + if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_E2P_CMD, + &e2p_cmd ) ) != 0 ) + return rc; + if ( ! ( e2p_cmd & SMSC95XX_E2P_CMD_EPC_BSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smsc95xx, "SMSC95XX %p timed out waiting for EEPROM\n", + smsc95xx ); + return -ETIMEDOUT; +} + +/** + * Read byte from EEPROM + * + * @v smsc95xx SMSC95xx device + * @v address EEPROM address + * @ret byte Byte read, or negative error + */ +static int smsc95xx_eeprom_read_byte ( struct smsc95xx_device *smsc95xx, + unsigned int address ) { + uint32_t e2p_cmd; + uint32_t e2p_data; + int rc; + + /* Wait for EEPROM to become idle */ + if ( ( rc = smsc95xx_eeprom_wait ( smsc95xx ) ) != 0 ) + return rc; + + /* Initiate read command */ + e2p_cmd = ( SMSC95XX_E2P_CMD_EPC_BSY | SMSC95XX_E2P_CMD_EPC_CMD_READ | + SMSC95XX_E2P_CMD_EPC_ADDR ( address ) ); + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_E2P_CMD, + e2p_cmd ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc95xx_eeprom_wait ( smsc95xx ) ) != 0 ) + return rc; + + /* Read EEPROM data */ + if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_E2P_DATA, + &e2p_data ) ) != 0 ) + return rc; + + return SMSC95XX_E2P_DATA_GET ( e2p_data ); +} + +/** + * Read data from EEPROM + * + * @v smsc95xx SMSC95xx device + * @v address EEPROM address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smsc95xx_eeprom_read ( struct smsc95xx_device *smsc95xx, + unsigned int address, void *data, + size_t len ) { + uint8_t *bytes; + int byte; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smsc95xx_eeprom_read_byte ( smsc95xx, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/****************************************************************************** + * + * MII access + * + ****************************************************************************** + */ + +/** + * Wait for MII to become idle + * + * @v smsc95xx SMSC95xx device + * @ret rc Return status code + */ +static int smsc95xx_mii_wait ( struct smsc95xx_device *smsc95xx ) { + uint32_t mii_access; + unsigned int i; + int rc; + + /* Wait for MIIBZY to become clear */ + for ( i = 0 ; i < SMSC95XX_MII_MAX_WAIT_MS ; i++ ) { + + /* Read MII_ACCESS and check MIIBZY */ + if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_MII_ACCESS, + &mii_access ) ) != 0 ) + return rc; + if ( ! ( mii_access & SMSC95XX_MII_ACCESS_MIIBZY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smsc95xx, "SMSC95XX %p timed out waiting for MII\n", + smsc95xx ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int smsc95xx_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct smsc95xx_device *smsc95xx = + container_of ( mii, struct smsc95xx_device, mii ); + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) + return rc; + + /* Initiate read command */ + mii_access = ( SMSC95XX_MII_ACCESS_PHY_ADDRESS | + SMSC95XX_MII_ACCESS_MIIRINDA ( reg ) | + SMSC95XX_MII_ACCESS_MIIBZY ); + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MII_ACCESS, + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) + return rc; + + /* Read MII data */ + if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_MII_DATA, + &mii_data ) ) != 0 ) + return rc; + + return SMSC95XX_MII_DATA_GET ( mii_data ); +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int smsc95xx_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data ) { + struct smsc95xx_device *smsc95xx = + container_of ( mii, struct smsc95xx_device, mii ); + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) + return rc; + + /* Write MII data */ + mii_data = SMSC95XX_MII_DATA_SET ( data ); + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MII_DATA, + mii_data ) ) != 0 ) + return rc; + + /* Initiate write command */ + mii_access = ( SMSC95XX_MII_ACCESS_PHY_ADDRESS | + SMSC95XX_MII_ACCESS_MIIRINDA ( reg ) | + SMSC95XX_MII_ACCESS_MIIWNR | + SMSC95XX_MII_ACCESS_MIIBZY ); + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MII_ACCESS, + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) + return rc; + + return 0; +} + +/** MII operations */ +static struct mii_operations smsc95xx_mii_operations = { + .read = smsc95xx_mii_read, + .write = smsc95xx_mii_write, +}; + +/** + * Check link status + * + * @v smsc95xx SMSC95xx device + * @ret rc Return status code + */ +static int smsc95xx_check_link ( struct smsc95xx_device *smsc95xx ) { + struct net_device *netdev = smsc95xx->netdev; + int intr; + int rc; + + /* Read PHY interrupt source */ + intr = mii_read ( &smsc95xx->mii, SMSC95XX_MII_PHY_INTR_SOURCE ); + if ( intr < 0 ) { + rc = intr; + DBGC ( smsc95xx, "SMSC95XX %p could not get PHY interrupt " + "source: %s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Acknowledge PHY interrupt */ + if ( ( rc = mii_write ( &smsc95xx->mii, SMSC95XX_MII_PHY_INTR_SOURCE, + intr ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not acknowledge PHY " + "interrupt: %s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Check link status */ + if ( ( rc = mii_check_link ( &smsc95xx->mii, netdev ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not check link: %s\n", + smsc95xx, strerror ( rc ) ); + return rc; + } + + DBGC ( smsc95xx, "SMSC95XX %p link %s (intr %#04x)\n", + smsc95xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); + return 0; +} + +/****************************************************************************** + * + * Statistics (for debugging) + * + ****************************************************************************** + */ + +/** + * Get RX statistics + * + * @v smsc95xx SMSC95xx device + * @v stats Statistics to fill in + * @ret rc Return status code + */ +static int smsc95xx_get_rx_statistics ( struct smsc95xx_device *smsc95xx, + struct smsc95xx_rx_statistics *stats ) { + int rc; + + /* Get statistics */ + if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_GET_STATISTICS, 0, + SMSC95XX_RX_STATISTICS, stats, + sizeof ( *stats ) ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not get RX statistics: " + "%s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Get TX statistics + * + * @v smsc95xx SMSC95xx device + * @v stats Statistics to fill in + * @ret rc Return status code + */ +static int smsc95xx_get_tx_statistics ( struct smsc95xx_device *smsc95xx, + struct smsc95xx_tx_statistics *stats ) { + int rc; + + /* Get statistics */ + if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_GET_STATISTICS, 0, + SMSC95XX_TX_STATISTICS, stats, + sizeof ( *stats ) ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not get TX statistics: " + "%s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Dump statistics (for debugging) + * + * @v smsc95xx SMSC95xx device + * @ret rc Return status code + */ +static int smsc95xx_dump_statistics ( struct smsc95xx_device *smsc95xx ) { + struct smsc95xx_rx_statistics rx; + struct smsc95xx_tx_statistics tx; + int rc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return 0; + + /* Get RX statistics */ + if ( ( rc = smsc95xx_get_rx_statistics ( smsc95xx, &rx ) ) != 0 ) + return rc; + + /* Get TX statistics */ + if ( ( rc = smsc95xx_get_tx_statistics ( smsc95xx, &tx ) ) != 0 ) + return rc; + + /* Dump statistics */ + DBGC ( smsc95xx, "SMSC95XX %p RX good %d bad %d crc %d und %d aln %d " + "ovr %d lat %d drp %d\n", smsc95xx, le32_to_cpu ( rx.good ), + le32_to_cpu ( rx.bad ), le32_to_cpu ( rx.crc ), + le32_to_cpu ( rx.undersize ), le32_to_cpu ( rx.alignment ), + le32_to_cpu ( rx.oversize ), le32_to_cpu ( rx.late ), + le32_to_cpu ( rx.dropped ) ); + DBGC ( smsc95xx, "SMSC95XX %p TX good %d bad %d pau %d sgl %d mul %d " + "exc %d lat %d und %d def %d car %d\n", smsc95xx, + le32_to_cpu ( tx.good ), le32_to_cpu ( tx.bad ), + le32_to_cpu ( tx.pause ), le32_to_cpu ( tx.single ), + le32_to_cpu ( tx.multiple ), le32_to_cpu ( tx.excessive ), + le32_to_cpu ( tx.late ), le32_to_cpu ( tx.underrun ), + le32_to_cpu ( tx.deferred ), le32_to_cpu ( tx.carrier ) ); + + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v smsc95xx SMSC95xx device + * @ret rc Return status code + */ +static int smsc95xx_reset ( struct smsc95xx_device *smsc95xx ) { + uint32_t hw_cfg; + int rc; + + /* Reset device */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_HW_CFG, + SMSC95XX_HW_CFG_LRST ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + udelay ( SMSC95XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( hw_cfg & SMSC95XX_HW_CFG_LRST ) { + DBGC ( smsc95xx, "SMSC95XX %p failed to reset\n", smsc95xx ); + return -ETIMEDOUT; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc95xx_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc95xx_device *smsc95xx = + container_of ( ep, struct smsc95xx_device, usbnet.intr ); + struct net_device *netdev = smsc95xx->netdev; + struct smsc95xx_interrupt *intr; + + /* Profile completions */ + profile_start ( &smsc95xx_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p interrupt failed: %s\n", + smsc95xx, strerror ( rc ) ); + DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract interrupt data */ + if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { + DBGC ( smsc95xx, "SMSC95XX %p malformed interrupt\n", + smsc95xx ); + DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + intr = iobuf->data; + + /* Record interrupt status */ + smsc95xx->int_sts = le32_to_cpu ( intr->int_sts ); + profile_stop ( &smsc95xx_intr_profiler ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations smsc95xx_intr_operations = { + .complete = smsc95xx_intr_complete, +}; + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc95xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc95xx_device *smsc95xx = + container_of ( ep, struct smsc95xx_device, usbnet.in ); + struct net_device *netdev = smsc95xx->netdev; + struct smsc95xx_rx_header *header; + + /* Profile completions */ + profile_start ( &smsc95xx_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p bulk IN failed: %s\n", + smsc95xx, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { + DBGC ( smsc95xx, "SMSC95XX %p underlength bulk IN\n", + smsc95xx ); + DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header and CRC */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + iob_unput ( iobuf, 4 /* CRC */ ); + + /* Check for errors */ + if ( header->command & cpu_to_le32 ( SMSC95XX_RX_RUNT | + SMSC95XX_RX_LATE | + SMSC95XX_RX_CRC ) ) { + DBGC ( smsc95xx, "SMSC95XX %p receive error (%08x):\n", + smsc95xx, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + profile_stop ( &smsc95xx_in_profiler ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations smsc95xx_in_operations = { + .complete = smsc95xx_in_complete, +}; + +/** + * Transmit packet + * + * @v smsc95xx SMSC95xx device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc95xx_out_transmit ( struct smsc95xx_device *smsc95xx, + struct io_buffer *iobuf ) { + struct smsc95xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile transmissions */ + profile_start ( &smsc95xx_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->command = cpu_to_le32 ( SMSC95XX_TX_FIRST | SMSC95XX_TX_LAST | + SMSC95XX_TX_LEN ( len ) ); + header->len = cpu_to_le32 ( len ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &smsc95xx->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &smsc95xx_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc95xx_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc95xx_device *smsc95xx = + container_of ( ep, struct smsc95xx_device, usbnet.out ); + struct net_device *netdev = smsc95xx->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations smsc95xx_out_operations = { + .complete = smsc95xx_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int smsc95xx_open ( struct net_device *netdev ) { + struct smsc95xx_device *smsc95xx = netdev->priv; + union smsc95xx_mac mac; + int rc; + + /* Clear stored interrupt status */ + smsc95xx->int_sts = 0; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + + /* Configure bulk IN empty response */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_HW_CFG, + SMSC95XX_HW_CFG_BIR ) ) != 0 ) + goto err_hw_cfg; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &smsc95xx->usbnet ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not open: %s\n", + smsc95xx, strerror ( rc ) ); + goto err_open; + } + + /* Configure interrupt endpoint */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_INT_EP_CTL, + ( SMSC95XX_INT_EP_CTL_RXDF_EN | + SMSC95XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + goto err_int_ep_ctl; + + /* Configure bulk IN delay */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_BULK_IN_DLY, + SMSC95XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + goto err_bulk_in_dly; + + /* Configure MAC */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MAC_CR, + ( SMSC95XX_MAC_CR_RXALL | + SMSC95XX_MAC_CR_FDPX | + SMSC95XX_MAC_CR_MCPAS | + SMSC95XX_MAC_CR_PRMS | + SMSC95XX_MAC_CR_PASSBAD | + SMSC95XX_MAC_CR_TXEN | + SMSC95XX_MAC_CR_RXEN ) ) ) != 0 ) + goto err_mac_cr; + + /* Configure transmit datapath */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_TX_CFG, + SMSC95XX_TX_CFG_ON ) ) != 0 ) + goto err_tx_cfg; + + /* Write MAC address high register */ + if ( ( rc = smsc95xx_raw_writel ( smsc95xx, SMSC95XX_ADDRH, + mac.addr.h ) ) != 0 ) + goto err_addrh; + + /* Write MAC address low register */ + if ( ( rc = smsc95xx_raw_writel ( smsc95xx, SMSC95XX_ADDRL, + mac.addr.l ) ) != 0 ) + goto err_addrl; + + /* Enable PHY interrupts */ + if ( ( rc = mii_write ( &smsc95xx->mii, SMSC95XX_MII_PHY_INTR_MASK, + ( SMSC95XX_PHY_INTR_ANEG_DONE | + SMSC95XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not set PHY interrupt " + "mask: %s\n", smsc95xx, strerror ( rc ) ); + goto err_phy_intr_mask; + } + + /* Update link status */ + smsc95xx_check_link ( smsc95xx ); + + return 0; + + err_phy_intr_mask: + err_addrl: + err_addrh: + err_tx_cfg: + err_mac_cr: + err_bulk_in_dly: + err_int_ep_ctl: + usbnet_close ( &smsc95xx->usbnet ); + err_open: + err_hw_cfg: + smsc95xx_reset ( smsc95xx ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void smsc95xx_close ( struct net_device *netdev ) { + struct smsc95xx_device *smsc95xx = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &smsc95xx->usbnet ); + + /* Dump statistics (for debugging) */ + smsc95xx_dump_statistics ( smsc95xx ); + + /* Reset device */ + smsc95xx_reset ( smsc95xx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc95xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct smsc95xx_device *smsc95xx = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = smsc95xx_out_transmit ( smsc95xx, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void smsc95xx_poll ( struct net_device *netdev ) { + struct smsc95xx_device *smsc95xx = netdev->priv; + uint32_t int_sts; + int rc; + + /* Poll USB bus */ + usb_poll ( smsc95xx->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &smsc95xx->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + + /* Do nothing more unless there are interrupts to handle */ + int_sts = smsc95xx->int_sts; + if ( ! int_sts ) + return; + + /* Check link status if applicable */ + if ( int_sts & SMSC95XX_INT_STS_PHY_INT ) { + smsc95xx_check_link ( smsc95xx ); + int_sts &= ~SMSC95XX_INT_STS_PHY_INT; + } + + /* Record RX FIFO overflow if applicable */ + if ( int_sts & SMSC95XX_INT_STS_RXDF_INT ) { + DBGC2 ( smsc95xx, "SMSC95XX %p RX FIFO overflowed\n", + smsc95xx ); + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + int_sts &= ~SMSC95XX_INT_STS_RXDF_INT; + } + + /* Check for unexpected interrupts */ + if ( int_sts ) { + DBGC ( smsc95xx, "SMSC95XX %p unexpected interrupt %#08x\n", + smsc95xx, int_sts ); + netdev_rx_err ( netdev, NULL, -ENOTTY ); + } + + /* Clear interrupts */ + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_INT_STS, + smsc95xx->int_sts ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + smsc95xx->int_sts = 0; +} + +/** SMSC95xx network device operations */ +static struct net_device_operations smsc95xx_operations = { + .open = smsc95xx_open, + .close = smsc95xx_close, + .transmit = smsc95xx_transmit, + .poll = smsc95xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int smsc95xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct smsc95xx_device *smsc95xx; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *smsc95xx ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &smsc95xx_operations ); + netdev->dev = &func->dev; + smsc95xx = netdev->priv; + memset ( smsc95xx, 0, sizeof ( *smsc95xx ) ); + smsc95xx->usb = usb; + smsc95xx->bus = usb->port->hub->bus; + smsc95xx->netdev = netdev; + usbnet_init ( &smsc95xx->usbnet, func, &smsc95xx_intr_operations, + &smsc95xx_in_operations, &smsc95xx_out_operations ); + usb_refill_init ( &smsc95xx->usbnet.intr, 0, SMSC95XX_INTR_MAX_FILL ); + usb_refill_init ( &smsc95xx->usbnet.in, SMSC95XX_IN_MTU, + SMSC95XX_IN_MAX_FILL ); + mii_init ( &smsc95xx->mii, &smsc95xx_mii_operations ); + DBGC ( smsc95xx, "SMSC95XX %p on %s\n", smsc95xx, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &smsc95xx->usbnet, config ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not describe: %s\n", + smsc95xx, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = smsc95xx_reset ( smsc95xx ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = smsc95xx_eeprom_read ( smsc95xx, SMSC95XX_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + goto err_eeprom_read; + + /* Generate MAC address if EEPROM is not present */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + DBGC ( smsc95xx, "SMSC95XX %p has no EEPROM (%s)\n", + smsc95xx, eth_ntoa ( netdev->hw_addr ) ); + eth_random_addr ( netdev->hw_addr ); + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_eeprom_read: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void smsc95xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** SMSC95xx device IDs */ +static struct usb_device_id smsc95xx_ids[] = { + { + .name = "smsc9500", + .vendor = 0x0424, + .product = 0x9500, + }, + { + .name = "smsc9505", + .vendor = 0x0424, + .product = 0x9505, + }, + { + .name = "smsc9500a", + .vendor = 0x0424, + .product = 0x9e00, + }, + { + .name = "smsc9505a", + .vendor = 0x0424, + .product = 0x9e01, + }, + { + .name = "smsc9514", + .vendor = 0x0424, + .product = 0xec00, + }, + { + .name = "smsc9500-s", + .vendor = 0x0424, + .product = 0x9900, + }, + { + .name = "smsc9505-s", + .vendor = 0x0424, + .product = 0x9901, + }, + { + .name = "smsc9500a-s", + .vendor = 0x0424, + .product = 0x9902, + }, + { + .name = "smsc9505a-s", + .vendor = 0x0424, + .product = 0x9903, + }, + { + .name = "smsc9514-s", + .vendor = 0x0424, + .product = 0x9904, + }, + { + .name = "smsc9500a-h", + .vendor = 0x0424, + .product = 0x9905, + }, + { + .name = "smsc9505a-h", + .vendor = 0x0424, + .product = 0x9906, + }, + { + .name = "smsc9500-2", + .vendor = 0x0424, + .product = 0x9907, + }, + { + .name = "smsc9500a-2", + .vendor = 0x0424, + .product = 0x9908, + }, + { + .name = "smsc9514-2", + .vendor = 0x0424, + .product = 0x9909, + }, + { + .name = "smsc9530", + .vendor = 0x0424, + .product = 0x9530, + }, + { + .name = "smsc9730", + .vendor = 0x0424, + .product = 0x9730, + }, + { + .name = "smsc89530", + .vendor = 0x0424, + .product = 0x9e08, + }, +}; + +/** SMSC LAN95xx driver */ +struct usb_driver smsc95xx_driver __usb_driver = { + .ids = smsc95xx_ids, + .id_count = ( sizeof ( smsc95xx_ids ) / sizeof ( smsc95xx_ids[0] ) ), + .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ), + .score = USB_SCORE_NORMAL, + .probe = smsc95xx_probe, + .remove = smsc95xx_remove, +}; diff --git a/src/drivers/net/smsc95xx.h b/src/drivers/net/smsc95xx.h new file mode 100644 index 000000000..3b83327bf --- /dev/null +++ b/src/drivers/net/smsc95xx.h @@ -0,0 +1,254 @@ +#ifndef _SMSC95XX_H +#define _SMSC95XX_H + +/** @file + * + * SMSC LAN95xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** Register write command */ +#define SMSC95XX_REGISTER_WRITE \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa0 ) ) + +/** Register read command */ +#define SMSC95XX_REGISTER_READ \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa1 ) ) + +/** Get statistics command */ +#define SMSC95XX_GET_STATISTICS \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa2 ) ) + +/** Interrupt status register */ +#define SMSC95XX_INT_STS 0x008 +#define SMSC95XX_INT_STS_RXDF_INT 0x00000800UL /**< RX FIFO overflow */ +#define SMSC95XX_INT_STS_PHY_INT 0x00008000UL /**< PHY interrupt */ + +/** Transmit configuration register */ +#define SMSC95XX_TX_CFG 0x010 +#define SMSC95XX_TX_CFG_ON 0x00000004UL /**< TX enable */ + +/** Hardware configuration register */ +#define SMSC95XX_HW_CFG 0x014 +#define SMSC95XX_HW_CFG_BIR 0x00001000UL /**< Bulk IN use NAK */ +#define SMSC95XX_HW_CFG_LRST 0x00000008UL /**< Soft lite reset */ + +/** EEPROM command register */ +#define SMSC95XX_E2P_CMD 0x030 +#define SMSC95XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ +#define SMSC95XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ +#define SMSC95XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ + +/** EEPROM data register */ +#define SMSC95XX_E2P_DATA 0x034 +#define SMSC95XX_E2P_DATA_GET(e2p_data) \ + ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ + +/** MAC address EEPROM address */ +#define SMSC95XX_EEPROM_MAC 0x01 + +/** Interrupt endpoint control register */ +#define SMSC95XX_INT_EP_CTL 0x068 +#define SMSC95XX_INT_EP_CTL_RXDF_EN 0x00000800UL /**< RX FIFO overflow */ +#define SMSC95XX_INT_EP_CTL_PHY_EN 0x00008000UL /**< PHY interrupt */ + +/** Bulk IN delay register */ +#define SMSC95XX_BULK_IN_DLY 0x06c +#define SMSC95XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ + +/** MAC control register */ +#define SMSC95XX_MAC_CR 0x100 +#define SMSC95XX_MAC_CR_RXALL 0x80000000UL /**< Receive all */ +#define SMSC95XX_MAC_CR_FDPX 0x00100000UL /**< Full duplex */ +#define SMSC95XX_MAC_CR_MCPAS 0x00080000UL /**< All multicast */ +#define SMSC95XX_MAC_CR_PRMS 0x00040000UL /**< Promiscuous */ +#define SMSC95XX_MAC_CR_PASSBAD 0x00010000UL /**< Pass bad frames */ +#define SMSC95XX_MAC_CR_TXEN 0x00000008UL /**< TX enabled */ +#define SMSC95XX_MAC_CR_RXEN 0x00000004UL /**< RX enabled */ + +/** MAC address high register */ +#define SMSC95XX_ADDRH 0x104 + +/** MAC address low register */ +#define SMSC95XX_ADDRL 0x108 + +/** MII access register */ +#define SMSC95XX_MII_ACCESS 0x114 +#define SMSC95XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ +#define SMSC95XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ +#define SMSC95XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ +#define SMSC95XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ + +/** MII data register */ +#define SMSC95XX_MII_DATA 0x118 +#define SMSC95XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ +#define SMSC95XX_MII_DATA_GET(mii_data) \ + ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ + +/** PHY interrupt source MII register */ +#define SMSC95XX_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSC95XX_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSC95XX_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSC95XX_PHY_INTR_LINK_DOWN 0x0010 + +/** MAC address */ +union smsc95xx_mac { + /** MAC receive address registers */ + struct { + /** MAC receive address low register */ + uint32_t l; + /** MAC receive address high register */ + uint32_t h; + } __attribute__ (( packed )) addr; + /** Raw MAC address */ + uint8_t raw[ETH_ALEN]; +}; + +/** Receive packet header */ +struct smsc95xx_rx_header { + /** Command word */ + uint32_t command; +} __attribute__ (( packed )); + +/** Runt frame */ +#define SMSC95XX_RX_RUNT 0x00004000UL + +/** Late collision */ +#define SMSC95XX_RX_LATE 0x00000040UL + +/** CRC error */ +#define SMSC95XX_RX_CRC 0x00000002UL + +/** Transmit packet header */ +struct smsc95xx_tx_header { + /** Command word */ + uint32_t command; + /** Frame length */ + uint32_t len; +} __attribute__ (( packed )); + +/** First segment */ +#define SMSC95XX_TX_FIRST 0x00002000UL + +/** Last segment */ +#define SMSC95XX_TX_LAST 0x00001000UL + +/** Buffer size */ +#define SMSC95XX_TX_LEN(len) ( (len) << 0 ) + +/** Interrupt packet format */ +struct smsc95xx_interrupt { + /** Current value of INT_STS register */ + uint32_t int_sts; +} __attribute__ (( packed )); + +/** Receive statistics */ +struct smsc95xx_rx_statistics { + /** Good frames */ + uint32_t good; + /** CRC errors */ + uint32_t crc; + /** Runt frame errors */ + uint32_t undersize; + /** Alignment errors */ + uint32_t alignment; + /** Frame too long errors */ + uint32_t oversize; + /** Later collision errors */ + uint32_t late; + /** Bad frames */ + uint32_t bad; + /** Dropped frames */ + uint32_t dropped; +} __attribute__ (( packed )); + +/** Receive statistics */ +#define SMSC95XX_RX_STATISTICS 0 + +/** Transmit statistics */ +struct smsc95xx_tx_statistics { + /** Good frames */ + uint32_t good; + /** Pause frames */ + uint32_t pause; + /** Single collisions */ + uint32_t single; + /** Multiple collisions */ + uint32_t multiple; + /** Excessive collisions */ + uint32_t excessive; + /** Late collisions */ + uint32_t late; + /** Buffer underruns */ + uint32_t underrun; + /** Excessive deferrals */ + uint32_t deferred; + /** Carrier errors */ + uint32_t carrier; + /** Bad frames */ + uint32_t bad; +} __attribute__ (( packed )); + +/** Transmit statistics */ +#define SMSC95XX_TX_STATISTICS 1 + +/** A SMSC95xx network device */ +struct smsc95xx_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + /** MII interface */ + struct mii_interface mii; + /** Interrupt status */ + uint32_t int_sts; +}; + +/** Reset delay (in microseconds) */ +#define SMSC95XX_RESET_DELAY_US 2 + +/** Maximum time to wait for EEPROM (in milliseconds) */ +#define SMSC95XX_EEPROM_MAX_WAIT_MS 100 + +/** Maximum time to wait for MII (in milliseconds) */ +#define SMSC95XX_MII_MAX_WAIT_MS 100 + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define SMSC95XX_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define SMSC95XX_IN_MAX_FILL 8 + +/** Bulk IN buffer size */ +#define SMSC95XX_IN_MTU \ + ( sizeof ( struct smsc95xx_rx_header ) + \ + ETH_FRAME_LEN + 4 /* possible VLAN header */ \ + + 4 /* CRC */ ) + +#endif /* _SMSC95XX_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 651adbaee..413fa46f9 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -183,6 +183,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_smsc75xx ( ERRFILE_DRIVER | 0x00770000 ) #define ERRFILE_intelvf ( ERRFILE_DRIVER | 0x00780000 ) #define ERRFILE_intelxvf ( ERRFILE_DRIVER | 0x00790000 ) +#define ERRFILE_smsc95xx ( ERRFILE_DRIVER | 0x007a0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 1fcd4223cc5bce800bf253ab34cbf2e996c9958e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 24 Nov 2015 17:28:23 +0000 Subject: [PATCH 008/591] [bitops] Provide BIT_QWORD_PTR() Provide BIT_QWORD_PTR() to allow for easy extraction of non-endian fields (e.g. Infiniband GUIDs) without unnecessary byte swapping. Signed-off-by: Michael Brown --- src/include/ipxe/bitops.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/include/ipxe/bitops.h b/src/include/ipxe/bitops.h index 2e22c1dbc..93eb663e2 100644 --- a/src/include/ipxe/bitops.h +++ b/src/include/ipxe/bitops.h @@ -212,6 +212,13 @@ typedef unsigned char pseudo_bit_t; BIT_ASSEMBLE_6 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ _field1, __VA_ARGS__ ) ) +#define BIT_QWORD_PTR( _ptr, _field ) \ + ( { \ + unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + __ptr; \ + } ) + /** Extract value of named field */ #define BIT_GET64( _ptr, _field ) \ ( { \ From 7f65a08f3e2606a5102fb68acf6ccf5fc283ecb6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 5 Dec 2015 23:54:10 +0000 Subject: [PATCH 009/591] [efi] Add %.usb target for building EFI-bootable USB (or other) disk images Signed-off-by: Michael Brown --- src/arch/i386/Makefile.efi | 4 +++ src/arch/x86/Makefile.efi | 4 +++ src/arch/x86_64/Makefile.efi | 4 +++ src/util/genefidsk | 60 ++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100755 src/util/genefidsk diff --git a/src/arch/i386/Makefile.efi b/src/arch/i386/Makefile.efi index aa809eb5d..37ede65ac 100644 --- a/src/arch/i386/Makefile.efi +++ b/src/arch/i386/Makefile.efi @@ -8,6 +8,10 @@ ELF2EFI = $(ELF2EFI32) # CFLAGS += -malign-double +# Specify EFI boot file +# +EFI_BOOT_FILE = bootia32.efi + # Include generic EFI Makefile # MAKEDEPS += arch/x86/Makefile.efi diff --git a/src/arch/x86/Makefile.efi b/src/arch/x86/Makefile.efi index f73bc7d5d..c4bc2308c 100644 --- a/src/arch/x86/Makefile.efi +++ b/src/arch/x86/Makefile.efi @@ -40,3 +40,7 @@ $(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM) $(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined $(QM)$(ECHO) " [CAB] $@" $(Q)$(LCAB) -n -q $(ALL_drv.efi) $@ + +$(BIN)/%.usb : $(BIN)/%.efi + $(QM)$(ECHO) " [GENEFIDSK] $@" + $(Q)bash util/genefidsk -o $@ -b $(EFI_BOOT_FILE) $< diff --git a/src/arch/x86_64/Makefile.efi b/src/arch/x86_64/Makefile.efi index 26b712780..12408f862 100644 --- a/src/arch/x86_64/Makefile.efi +++ b/src/arch/x86_64/Makefile.efi @@ -8,6 +8,10 @@ CFLAGS += -mno-red-zone # ELF2EFI = $(ELF2EFI64) +# Specify EFI boot file +# +EFI_BOOT_FILE = bootx64.efi + # Include generic EFI Makefile # MAKEDEPS += arch/x86/Makefile.efi diff --git a/src/util/genefidsk b/src/util/genefidsk new file mode 100755 index 000000000..7064f99b6 --- /dev/null +++ b/src/util/genefidsk @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Generate an EFI bootable disk image + +set -e + +function help() { + echo "Usage: ${0} [OPTIONS] " + echo + echo "where OPTIONS are:" + echo " -h Show this help" + echo " -b Specify boot file name (e.g. bootx64.efi)" + echo " -o FILE Save disk image to file" +} + +BOOT=bootx64.efi + +while getopts "hb:o:" opt; do + case ${opt} in + h) + help + exit 0 + ;; + b) + BOOT="${OPTARG}" + ;; + o) + OUT="${OPTARG}" + ;; + esac +done + +shift $((OPTIND - 1)) +IN=$1 + +if [ -z "${IN}" ]; then + echo "${0}: no input file given" >&2 + help + exit 1 +fi + +if [ -z "${OUT}" ]; then + echo "${0}: no output file given" >&2 + help + exit 1 +fi + +# Create sparse output file +rm -f ${OUT} +truncate -s 1440K ${OUT} + +# Format disk +mformat -i ${OUT} -f 1440 :: + +# Create directory structure +mmd -i ${OUT} ::efi +mmd -i ${OUT} ::efi/boot + +# Copy bootable image +mcopy -i ${OUT} ${IN} ::efi/boot/${BOOT} From 15ce7ce355b3cfb5ec8bc2d51528b08f02f4648b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 6 Dec 2015 23:22:37 +0000 Subject: [PATCH 010/591] [usb] Use port->disconnected to check for disconnected devices The usb_message() and usb_stream() functions currently check for port->speed==USB_SPEED_NONE to determine whether or not a device has been unplugged. This test will give a false negative result if a new device has been plugged in before the hotplug mechanism has finished handling the removal of the old device. Fix by checking instead the port->disconnected flag, which is now cleared only after completing the removal of the old device. Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 19 ++++++++++--------- src/drivers/usb/usbhub.c | 3 ++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 63a7e46a6..53501b46c 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -485,7 +485,7 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request, assert ( iob_headroom ( iobuf ) >= sizeof ( *packet ) ); /* Fail immediately if device has been unplugged */ - if ( port->speed == USB_SPEED_NONE ) + if ( port->disconnected ) return -ENODEV; /* Reset endpoint if required */ @@ -534,7 +534,7 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc; /* Fail immediately if device has been unplugged */ - if ( port->speed == USB_SPEED_NONE ) + if ( port->disconnected ) return -ENODEV; /* Reset endpoint if required */ @@ -1717,23 +1717,24 @@ static int usb_hotplugged ( struct usb_port *port ) { if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", hub->name, port->address, strerror ( rc ) ); - goto err_speed; + /* Treat as a disconnection */ + port->disconnected = 1; + port->speed = USB_SPEED_NONE; } /* Detach device, if applicable */ if ( port->attached && ( port->disconnected || ! port->speed ) ) usb_detached ( port ); + /* Clear any recorded disconnections */ + port->disconnected = 0; + /* Attach device, if applicable */ if ( port->speed && ( ! port->attached ) && ( ( rc = usb_attached ( port ) ) != 0 ) ) - goto err_attached; + return rc; - err_attached: - err_speed: - /* Clear any recorded disconnections */ - port->disconnected = 0; - return rc; + return 0; } /****************************************************************************** diff --git a/src/drivers/usb/usbhub.c b/src/drivers/usb/usbhub.c index 5cfc40520..a3e7bc00c 100644 --- a/src/drivers/usb/usbhub.c +++ b/src/drivers/usb/usbhub.c @@ -496,9 +496,10 @@ static void hub_remove ( struct usb_function *func ) { unsigned int i; /* If hub has been unplugged, mark all ports as unplugged */ - if ( usb->port->speed == USB_SPEED_NONE ) { + if ( usb->port->disconnected ) { for ( i = 1 ; i <= hub->ports ; i++ ) { port = usb_port ( hub, i ); + port->disconnected = 1; port->speed = USB_SPEED_NONE; } } From eb1fc1e9579a4f70bb28283bf3eadcf493fa0dcb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 6 Dec 2015 23:41:26 +0000 Subject: [PATCH 011/591] [usb] Record USB device speed separately from current port speed Record the speed of a USB device based on the port's speed at the time that the device was enabled. This allows us to remember the device's speed even after the device has been disconnected (and so the port's current speed has changed). In particular, this allows us to correctly identify the transaction translator for a low-speed or full-speed device after the device has been disconnected. Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 18 +++++++++--------- src/drivers/usb/ehci.c | 4 ++-- src/drivers/usb/uhci.c | 2 +- src/drivers/usb/xhci.c | 3 +-- src/include/ipxe/usb.h | 2 ++ 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 53501b46c..a3718d831 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -243,7 +243,6 @@ int usb_endpoint_described ( struct usb_endpoint *ep, struct usb_interface_descriptor *interface, unsigned int type, unsigned int index ) { struct usb_device *usb = ep->usb; - struct usb_port *port = usb->port; struct usb_endpoint_descriptor *desc; struct usb_endpoint_companion_descriptor *descx; unsigned int sizes; @@ -267,7 +266,7 @@ int usb_endpoint_described ( struct usb_endpoint *ep, /* Calculate interval */ if ( ( type & USB_ENDPOINT_ATTR_TYPE_MASK ) == USB_ENDPOINT_ATTR_INTERRUPT ) { - if ( port->speed >= USB_SPEED_HIGH ) { + if ( usb->speed >= USB_SPEED_HIGH ) { /* 2^(desc->interval-1) is a microframe count */ interval = ( 1 << ( desc->interval - 1 ) ); } else { @@ -1492,8 +1491,9 @@ static int register_usb ( struct usb_device *usb ) { hub->name, port->address, strerror ( rc ) ); goto err_speed; } + usb->speed = port->speed; DBGC2 ( usb, "USB %s attached as %s-speed device\n", - usb->name, usb_speed_name ( port->speed ) ); + usb->name, usb_speed_name ( usb->speed ) ); /* Open device */ if ( ( rc = usb->host->open ( usb ) ) != 0 ) { @@ -1503,7 +1503,7 @@ static int register_usb ( struct usb_device *usb ) { } /* Describe control endpoint */ - mtu = USB_EP0_DEFAULT_MTU ( port->speed ); + mtu = USB_EP0_DEFAULT_MTU ( usb->speed ); usb_endpoint_describe ( &usb->control, USB_EP0_ADDRESS, USB_EP0_ATTRIBUTES, mtu, USB_EP0_BURST, USB_EP0_INTERVAL ); @@ -1554,7 +1554,7 @@ static int register_usb ( struct usb_device *usb ) { le16_to_cpu ( usb->device.product ), usb->device.class.class, usb->device.class.subclass, usb->device.class.protocol, usb_bcd ( le16_to_cpu ( usb->device.protocol ) ), - usb_speed_name ( port->speed ), usb->control.mtu ); + usb_speed_name ( usb->speed ), usb->control.mtu ); /* Configure device */ if ( ( rc = usb_autoconfigure ( usb ) ) != 0 ) @@ -2233,12 +2233,12 @@ struct usb_port * usb_transaction_translator ( struct usb_device *usb ) { struct usb_device *parent; /* Navigate up to root hub. If we find a low-speed or - * full-speed port with a higher-speed parent device, then - * that port is the transaction translator. + * full-speed device with a higher-speed parent hub, then that + * device's port is the transaction translator. */ for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) { - if ( ( usb->port->speed <= USB_SPEED_FULL ) && - ( parent->port->speed > USB_SPEED_FULL ) ) + if ( ( usb->speed <= USB_SPEED_FULL ) && + ( parent->speed > USB_SPEED_FULL ) ) return usb->port; } diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c index f90d6a91a..617a43b03 100644 --- a/src/drivers/usb/ehci.c +++ b/src/drivers/usb/ehci.c @@ -970,10 +970,10 @@ static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { chr |= EHCI_CHR_TOGGLE; /* Determine endpoint speed */ - if ( usb->port->speed == USB_SPEED_HIGH ) { + if ( usb->speed == USB_SPEED_HIGH ) { chr |= EHCI_CHR_EPS_HIGH; } else { - if ( usb->port->speed == USB_SPEED_FULL ) { + if ( usb->speed == USB_SPEED_FULL ) { chr |= EHCI_CHR_EPS_FULL; } else { chr |= EHCI_CHR_EPS_LOW; diff --git a/src/drivers/usb/uhci.c b/src/drivers/usb/uhci.c index 4220bef72..528c1be1d 100644 --- a/src/drivers/usb/uhci.c +++ b/src/drivers/usb/uhci.c @@ -697,7 +697,7 @@ static int uhci_endpoint_open ( struct usb_endpoint *ep ) { goto err_ring_alloc; endpoint->ring.mtu = ep->mtu; endpoint->ring.flags = UHCI_FL_CERR_MAX; - if ( usb->port->speed < USB_SPEED_FULL ) + if ( usb->speed < USB_SPEED_FULL ) endpoint->ring.flags |= UHCI_FL_LS; endpoint->ring.control = ( UHCI_CONTROL_DEVICE ( usb->address ) | UHCI_CONTROL_ENDPOINT ( ep->address ) ); diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 110b43287..67de0a832 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -2753,7 +2753,6 @@ static void xhci_device_close ( struct usb_device *usb ) { static int xhci_device_address ( struct usb_device *usb ) { struct xhci_slot *slot = usb_get_hostdata ( usb ); struct xhci_device *xhci = slot->xhci; - struct usb_port *port = usb->port; struct usb_port *root_port; int psiv; int rc; @@ -2766,7 +2765,7 @@ static int xhci_device_address ( struct usb_device *usb ) { slot->port = root_port->address; /* Calculate protocol speed ID */ - psiv = xhci_port_psiv ( xhci, slot->port, port->speed ); + psiv = xhci_port_psiv ( xhci, slot->port, usb->speed ); if ( psiv < 0 ) { rc = psiv; return rc; diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index b3ce7b741..c268988f4 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -698,6 +698,8 @@ struct usb_device { char name[32]; /** USB port */ struct usb_port *port; + /** Device speed */ + unsigned int speed; /** List of devices on this bus */ struct list_head list; /** Device address, if assigned */ From fb8c52de9b50d3562ce08469f23bbd221946519d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Dec 2015 00:32:08 +0000 Subject: [PATCH 012/591] [usb] Allow USB device IDs to include arbitrary driver-specific data Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 8 +++++--- src/include/ipxe/usb.h | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index a3718d831..b1fa4efb5 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -1186,6 +1186,11 @@ static int usb_probe ( struct usb_function *func, return -ENOENT; } + /* Record driver */ + func->driver = driver; + func->id = id; + func->dev.driver_name = id->name; + /* Probe driver */ if ( ( rc = driver->probe ( func, config ) ) != 0 ) { DBGC ( usb, "USB %s failed to probe driver %s: %s\n", @@ -1193,9 +1198,6 @@ static int usb_probe ( struct usb_function *func, return rc; } - /* Record driver */ - func->driver = driver; - func->dev.driver_name = id->name; return 0; } diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index c268988f4..37b6d94ed 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -662,6 +662,8 @@ struct usb_function { struct usb_driver *driver; /** Driver private data */ void *priv; + /** Driver device ID */ + struct usb_device_id *id; /** List of interface numbers * @@ -1308,6 +1310,8 @@ struct usb_device_id { uint16_t vendor; /** Product ID */ uint16_t product; + /** Arbitrary driver data */ + unsigned long driver_data; }; /** Match-anything ID */ From 53ba5936b5c70b030d81e8f2349d75a7264581ae Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Dec 2015 00:54:01 +0000 Subject: [PATCH 013/591] [usb] Allow additional settling time for out-of-spec hubs Some hubs (e.g. the Avocent Corp. Virtual Hub on a Lenovo x3550 Integrated Management Module) have been observed to require more than the standard 200ms for ports to stabilise, with the result that devices appear to disconnect and immediately reconnect during the initial bus enumeration. Work around this problem by allowing specific hubs an extra 500ms of settling time. Signed-off-by: Michael Brown --- src/drivers/usb/usbhub.c | 11 +++++++++++ src/drivers/usb/usbhub.h | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/src/drivers/usb/usbhub.c b/src/drivers/usb/usbhub.c index a3e7bc00c..7095fc31b 100644 --- a/src/drivers/usb/usbhub.c +++ b/src/drivers/usb/usbhub.c @@ -155,6 +155,10 @@ static int hub_open ( struct usb_hub *hub ) { /* Refill interrupt ring */ hub_refill ( hubdev ); + /* Delay to allow ports to stabilise on out-of-spec hubs */ + if ( hubdev->flags & USB_HUB_SLOW_START ) + mdelay ( USB_HUB_SLOW_START_DELAY_MS ); + return 0; usb_endpoint_close ( &hubdev->intr ); @@ -410,6 +414,7 @@ static int hub_probe ( struct usb_function *func, hubdev->usb = usb; hubdev->features = ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES ); + hubdev->flags = func->id->driver_data; usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations ); usb_refill_init ( &hubdev->intr, 0, USB_HUB_INTR_FILL ); process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL ); @@ -517,6 +522,12 @@ static void hub_remove ( struct usb_function *func ) { /** USB hub device IDs */ static struct usb_device_id hub_ids[] = { + { + .name = "avocent-hub", + .vendor = 0x0624, + .product = 0x0248, + .driver_data = USB_HUB_SLOW_START, + }, { .name = "hub", .vendor = USB_ANY_ID, diff --git a/src/drivers/usb/usbhub.h b/src/drivers/usb/usbhub.h index d7d8f9610..a5f123acc 100644 --- a/src/drivers/usb/usbhub.h +++ b/src/drivers/usb/usbhub.h @@ -257,6 +257,8 @@ struct usb_hub_device { struct usb_hub *hub; /** Features */ unsigned int features; + /** Flags */ + unsigned int flags; /** Interrupt endpoint */ struct usb_endpoint intr; @@ -264,6 +266,12 @@ struct usb_hub_device { struct process refill; }; +/** Hub requires additional settling delay */ +#define USB_HUB_SLOW_START 0x0001 + +/** Additional setting delay for out-of-spec hubs */ +#define USB_HUB_SLOW_START_DELAY_MS 500 + /** Interrupt ring fill level * * This is a policy decision. From 296dee6d38ffb9db4da79e868b741a689953246f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 1 Dec 2015 14:46:48 +0000 Subject: [PATCH 014/591] [acm] Add support for CDC-ACM (aka USB RNDIS) devices Signed-off-by: Michael Brown --- src/drivers/net/acm.c | 529 +++++++++++++++++++++++++++++++++++++ src/drivers/net/acm.h | 69 +++++ src/include/ipxe/cdc.h | 49 ++++ src/include/ipxe/errfile.h | 1 + 4 files changed, 648 insertions(+) create mode 100644 src/drivers/net/acm.c create mode 100644 src/drivers/net/acm.h diff --git a/src/drivers/net/acm.c b/src/drivers/net/acm.c new file mode 100644 index 000000000..955ad4ab4 --- /dev/null +++ b/src/drivers/net/acm.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2015 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 "acm.h" + +/** @file + * + * USB RNDIS driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler acm_intr_profiler __profiler = + { .name = "acm.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler acm_in_profiler __profiler = + { .name = "acm.in" }; + +/** Bulk OUT profiler */ +static struct profiler acm_out_profiler __profiler = + { .name = "acm.out" }; + +/****************************************************************************** + * + * USB RNDIS communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void acm_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct acm_device *acm = container_of ( ep, struct acm_device, + usbnet.intr ); + struct rndis_device *rndis = acm->rndis; + struct usb_setup_packet *message; + + /* Profile completions */ + profile_start ( &acm_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Drop packets with errors */ + if ( rc != 0 ) { + DBGC ( acm, "ACM %p interrupt failed: %s\n", + acm, strerror ( rc ) ); + DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( iob_len ( iobuf ) < sizeof ( *message ) ) { + DBGC ( acm, "ACM %p underlength interrupt:\n", acm ); + DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + message = iobuf->data; + + /* Parse message header */ + switch ( message->request ) { + + case cpu_to_le16 ( CDC_RESPONSE_AVAILABLE ) : + case cpu_to_le16 ( 0x0001 ) : /* qemu seems to use this value */ + acm->responded = 1; + break; + + default: + DBGC ( acm, "ACM %p unrecognised interrupt:\n", acm ); + DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -ENOTSUP; + goto error; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &acm_intr_profiler ); + + return; + + error: + rndis_rx_err ( rndis, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations acm_intr_operations = { + .complete = acm_intr_complete, +}; + +/****************************************************************************** + * + * USB RNDIS data interface + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void acm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct acm_device *acm = container_of ( ep, struct acm_device, + usbnet.in ); + struct rndis_device *rndis = acm->rndis; + + /* Profile receive completions */ + profile_start ( &acm_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the RNDIS device */ + if ( rc != 0 ) { + DBGC ( acm, "ACM %p bulk IN failed: %s\n", + acm, strerror ( rc ) ); + goto error; + } + + /* Hand off to RNDIS */ + rndis_rx ( rndis, iob_disown ( iobuf ) ); + + profile_stop ( &acm_in_profiler ); + return; + + error: + rndis_rx_err ( rndis, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations acm_in_operations = { + .complete = acm_in_complete, +}; + +/** + * Transmit packet + * + * @v acm USB RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int acm_out_transmit ( struct acm_device *acm, + struct io_buffer *iobuf ) { + int rc; + + /* Profile transmissions */ + profile_start ( &acm_out_profiler ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &acm->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &acm_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void acm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct acm_device *acm = container_of ( ep, struct acm_device, + usbnet.out ); + struct rndis_device *rndis = acm->rndis; + + /* Report TX completion */ + rndis_tx_complete_err ( rndis, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations acm_out_operations = { + .complete = acm_out_complete, +}; + +/****************************************************************************** + * + * USB RNDIS control interface + * + ****************************************************************************** + */ + +/** + * Send control packet + * + * @v acm USB RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int acm_control_transmit ( struct acm_device *acm, + struct io_buffer *iobuf ) { + struct rndis_device *rndis = acm->rndis; + struct usb_device *usb = acm->usb; + int rc; + + /* Send packet as an encapsulated command */ + if ( ( rc = cdc_send_encapsulated_command ( usb, acm->usbnet.comms, + iobuf->data, + iob_len ( iobuf ) ) ) != 0){ + DBGC ( acm, "ACM %p could not send encapsulated command: %s\n", + acm, strerror ( rc ) ); + return rc; + } + + /* Complete packet immediately */ + rndis_tx_complete ( rndis, iobuf ); + + return 0; +} + +/** + * Receive control packet + * + * @v acm USB RNDIS device + * @ret rc Return status code + */ +static int acm_control_receive ( struct acm_device *acm ) { + struct rndis_device *rndis = acm->rndis; + struct usb_device *usb = acm->usb; + struct io_buffer *iobuf; + struct rndis_header *header; + size_t mtu = ACM_RESPONSE_MTU; + size_t len; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( mtu ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get encapsulated response */ + if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms, + iobuf->data, mtu ) ) != 0 ){ + DBGC ( acm, "ACM %p could not get encapsulated response: %s\n", + acm, strerror ( rc ) ); + goto err_get_response; + } + + /* Fix up buffer length */ + header = iobuf->data; + len = le32_to_cpu ( header->len ); + if ( len > mtu ) { + DBGC ( acm, "ACM %p overlength encapsulated response\n", acm ); + DBGC_HDA ( acm, 0, iobuf->data, mtu ); + rc = -EPROTO; + goto err_len; + } + iob_put ( iobuf, len ); + + /* Hand off to RNDIS */ + rndis_rx ( rndis, iob_disown ( iobuf ) ); + + return 0; + + err_len: + err_get_response: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/****************************************************************************** + * + * RNDIS interface + * + ****************************************************************************** + */ + +/** + * Open RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int acm_open ( struct rndis_device *rndis ) { + struct acm_device *acm = rndis->priv; + int rc; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &acm->usbnet ) ) != 0 ) + goto err_open; + + return 0; + + usbnet_close ( &acm->usbnet ); + err_open: + return rc; +} + +/** + * Close RNDIS device + * + * @v rndis RNDIS device + */ +static void acm_close ( struct rndis_device *rndis ) { + struct acm_device *acm = rndis->priv; + + /* Close USB network device */ + usbnet_close ( &acm->usbnet ); +} + +/** + * Transmit packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int acm_transmit ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct acm_device *acm = rndis->priv; + struct rndis_header *header = iobuf->data; + + /* Sanity check */ + assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); + assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); + + /* Transmit packet via appropriate mechanism */ + if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) { + return acm_out_transmit ( acm, iobuf ); + } else { + return acm_control_transmit ( acm, iobuf ); + } +} + +/** + * Poll for completed and received packets + * + * @v rndis RNDIS device + */ +static void acm_poll ( struct rndis_device *rndis ) { + struct acm_device *acm = rndis->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( acm->bus ); + + /* Refill rings */ + if ( ( rc = usbnet_refill ( &acm->usbnet ) ) != 0 ) + rndis_rx_err ( rndis, NULL, rc ); + + /* Retrieve encapsulated response, if applicable */ + if ( acm->responded ) { + + /* Clear flag */ + acm->responded = 0; + + /* Get encapsulated response */ + if ( ( rc = acm_control_receive ( acm ) ) != 0 ) + rndis_rx_err ( rndis, NULL, rc ); + } +} + +/** USB RNDIS operations */ +static struct rndis_operations acm_operations = { + .open = acm_open, + .close = acm_close, + .transmit = acm_transmit, + .poll = acm_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int acm_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct rndis_device *rndis; + struct acm_device *acm; + int rc; + + /* Allocate and initialise structure */ + rndis = alloc_rndis ( sizeof ( *acm ) ); + if ( ! rndis ) { + rc = -ENOMEM; + goto err_alloc; + } + rndis_init ( rndis, &acm_operations ); + rndis->netdev->dev = &func->dev; + acm = rndis->priv; + acm->usb = usb; + acm->bus = usb->port->hub->bus; + acm->rndis = rndis; + usbnet_init ( &acm->usbnet, func, &acm_intr_operations, + &acm_in_operations, &acm_out_operations ); + usb_refill_init ( &acm->usbnet.intr, 0, ACM_INTR_MAX_FILL ); + usb_refill_init ( &acm->usbnet.in, ACM_IN_MTU, ACM_IN_MAX_FILL ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &acm->usbnet, config ) ) != 0 ) { + DBGC ( acm, "ACM %p could not describe: %s\n", + acm, strerror ( rc ) ); + goto err_describe; + } + + /* Register RNDIS device */ + if ( ( rc = register_rndis ( rndis ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, acm ); + return 0; + + unregister_rndis ( rndis ); + err_register: + err_describe: + free_rndis ( rndis ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void acm_remove ( struct usb_function *func ) { + struct acm_device *acm = usb_func_get_drvdata ( func ); + struct rndis_device *rndis = acm->rndis; + + /* Unregister RNDIS device */ + unregister_rndis ( rndis ); + + /* Free RNDIS device */ + free_rndis ( rndis ); +} + +/** USB CDC-ACM device IDs */ +static struct usb_device_id cdc_acm_ids[] = { + { + .name = "cdc-acm", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + }, +}; + +/** USB CDC-ACM driver */ +struct usb_driver cdc_acm_driver __usb_driver = { + .ids = cdc_acm_ids, + .id_count = ( sizeof ( cdc_acm_ids ) / sizeof ( cdc_acm_ids[0] ) ), + .class = USB_CLASS_ID ( USB_CLASS_CDC, USB_SUBCLASS_CDC_ACM, + USB_PROTOCOL_ACM_RNDIS ), + .score = USB_SCORE_DEPRECATED, + .probe = acm_probe, + .remove = acm_remove, +}; + +/** USB RF-RNDIS device IDs */ +static struct usb_device_id rf_rndis_ids[] = { + { + .name = "rf-rndis", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + }, +}; + +/** USB RF-RNDIS driver */ +struct usb_driver rf_rndis_driver __usb_driver = { + .ids = rf_rndis_ids, + .id_count = ( sizeof ( rf_rndis_ids ) / sizeof ( rf_rndis_ids[0] ) ), + .class = USB_CLASS_ID ( USB_CLASS_WIRELESS, USB_SUBCLASS_WIRELESS_RADIO, + USB_PROTOCOL_RADIO_RNDIS ), + .score = USB_SCORE_DEPRECATED, + .probe = acm_probe, + .remove = acm_remove, +}; diff --git a/src/drivers/net/acm.h b/src/drivers/net/acm.h new file mode 100644 index 000000000..d4944967b --- /dev/null +++ b/src/drivers/net/acm.h @@ -0,0 +1,69 @@ +#ifndef _ACM_H +#define _ACM_H + +/** @file + * + * USB RNDIS Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** CDC-ACM subclass */ +#define USB_SUBCLASS_CDC_ACM 0x02 + +/** CDC-ACM RNDIS device protocol */ +#define USB_PROTOCOL_ACM_RNDIS 0xff + +/** Class code for wireless devices */ +#define USB_CLASS_WIRELESS 0xe0 + +/** Radio frequency device subclass */ +#define USB_SUBCLASS_WIRELESS_RADIO 0x01 + +/** Radio frequency RNDIS device protocol */ +#define USB_PROTOCOL_RADIO_RNDIS 0x03 + +/** A USB RNDIS network device */ +struct acm_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** RNDIS device */ + struct rndis_device *rndis; + /** USB network device */ + struct usbnet_device usbnet; + + /** An encapsulated response is available */ + int responded; +}; + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define ACM_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define ACM_IN_MAX_FILL 8 + +/** Bulk IN buffer size + * + * This is a policy decision. + */ +#define ACM_IN_MTU 2048 + +/** Encapsulated response buffer size + * + * This is a policy decision. + */ +#define ACM_RESPONSE_MTU 128 + +#endif /* _ACM_H */ diff --git a/src/include/ipxe/cdc.h b/src/include/ipxe/cdc.h index f1799cd9a..b8b4a59d9 100644 --- a/src/include/ipxe/cdc.h +++ b/src/include/ipxe/cdc.h @@ -14,6 +14,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Class code for communications devices */ #define USB_CLASS_CDC 2 +/** Send encapsulated command */ +#define CDC_SEND_ENCAPSULATED_COMMAND \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x00 ) ) + +/** Get encapsulated response */ +#define CDC_GET_ENCAPSULATED_RESPONSE \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + /** Union functional descriptor */ struct cdc_union_descriptor { /** Descriptor header */ @@ -30,6 +40,11 @@ struct cdc_union_descriptor { /** Ethernet descriptor subtype */ #define CDC_SUBTYPE_ETHERNET 15 +/** Response available */ +#define CDC_RESPONSE_AVAILABLE \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + /** Network connection notification */ #define CDC_NETWORK_CONNECTION \ ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ @@ -52,4 +67,38 @@ extern struct cdc_union_descriptor * cdc_union_descriptor ( struct usb_configuration_descriptor *config, struct usb_interface_descriptor *interface ); +/** + * Send encapsulated command + * + * @v usb USB device + * @v interface Interface number + * @v data Command + * @v len Length of command + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +cdc_send_encapsulated_command ( struct usb_device *usb, unsigned int interface, + void *data, size_t len ) { + + return usb_control ( usb, CDC_SEND_ENCAPSULATED_COMMAND, 0, interface, + data, len ); +} + +/** +* Get encapsulated response +* +* @v usb USB device +* @v interface Interface number +* @v data Response buffer +* @v len Length of response buffer +* @ret rc Return status code +*/ +static inline __attribute__ (( always_inline )) int +cdc_get_encapsulated_response ( struct usb_device *usb, unsigned int interface, + void *data, size_t len ) { + + return usb_control ( usb, CDC_GET_ENCAPSULATED_RESPONSE, 0, interface, + data, len ); +} + #endif /* _IPXE_CDC_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 413fa46f9..65b4d9c91 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -184,6 +184,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_intelvf ( ERRFILE_DRIVER | 0x00780000 ) #define ERRFILE_intelxvf ( ERRFILE_DRIVER | 0x00790000 ) #define ERRFILE_smsc95xx ( ERRFILE_DRIVER | 0x007a0000 ) +#define ERRFILE_acm ( ERRFILE_DRIVER | 0x007b0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From d6945925d8e68bf8e317131a4505e205ccc161a9 Mon Sep 17 00:00:00 2001 From: Torgeir Wulfsberg Date: Mon, 7 Dec 2015 19:52:16 +0000 Subject: [PATCH 015/591] [intel] Add INTEL_NO_PHY_RST for I217-LM Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index e155b644d..840bad80c 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1058,7 +1058,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ), PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ), - PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", 0 ), + PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), From ee8388ec6950ea596ddb374dcc2abb940db70aea Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Dec 2015 22:30:27 +0000 Subject: [PATCH 016/591] [xhci] Ensure that zero-length packets are not part of a TRB chain Some xHCI controllers (such as qemu's emulated xHCI controller) do not correctly handle zero-length packets that are part of a TRB chain. The zero-length TRB ends up being squashed and does not result in a zero-length packet as seen by the device. Work around this problem by marking the zero-length packet as belonging to a separate transfer descriptor. Signed-off-by: Michael Brown --- src/drivers/usb/xhci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 67de0a832..48ac6a307 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -2606,6 +2606,12 @@ static int xhci_endpoint_stream ( struct usb_endpoint *ep, len -= trb_len; trb++; } + + /* Mark zero-length packet (if present) as a separate transfer */ + if ( zlp && ( count > 1 ) ) + trb[-2].normal.flags = 0; + + /* Generate completion for final TRB */ trb[-1].normal.flags = XHCI_TRB_IOC; /* Enqueue TRBs */ From e3012f99492cc6be331f3f740e5a38e7920bc42c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Dec 2015 17:35:24 +0000 Subject: [PATCH 017/591] [efi] Centralise EFI file system info GUIDs Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi.h | 3 +++ src/interface/efi/efi_file.c | 6 ------ src/interface/efi/efi_guid.c | 8 ++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 40d3beb12..390d4e6e6 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -205,6 +205,9 @@ extern EFI_GUID efi_usb2_hc_protocol_guid; extern EFI_GUID efi_usb_io_protocol_guid; extern EFI_GUID efi_vlan_config_protocol_guid; +extern EFI_GUID efi_file_info_id; +extern EFI_GUID efi_file_system_info_id; + extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 05eadc971..52de0987c 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -47,12 +47,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** EFI file information GUID */ -static EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; - -/** EFI file system information GUID */ -static EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; - /** EFI media ID */ #define EFI_MEDIA_ID_MAGIC 0x69505845 diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 8ec5e541d..39da5efe0 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -66,6 +66,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** @file * @@ -281,3 +283,9 @@ EFI_GUID efi_usb_io_protocol_guid /** VLAN configuration protocol GUID */ EFI_GUID efi_vlan_config_protocol_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID; + +/** File information GUID */ +EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; + +/** File system information GUID */ +EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; From 91dd5524b762e2438cc6a949dd1440ee0998f5ef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Dec 2015 13:45:35 +0000 Subject: [PATCH 018/591] [build] Allow extra objects to be included in an all-drivers build The build system allows for additional drivers (or other objects) to be specified using build targets such as make bin/intel--realtek.usb make bin/8086100e--8086100f.mrom This currently fails if the base target is the "bin/ipxe.*" all-drivers target, e.g. make bin/ipxe--acm.usb Fix the build target parsing logic to allow additional drivers (or other objects) to be included on top of the base all-drivers target. This can be used to include USB network card drivers, which are not yet included by default in the all-drivers build. Reported-by: Andrew Sloma Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 1b6f092bb..70ec50c5e 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -960,13 +960,13 @@ DRIVERS_ipxe = $(DRIVERS_net) $(DRIVERS_infiniband) \ # TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci") # TGT_ROM_NAME : the ROM name (e.g. "dfe538") # -CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1)) TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@)))) TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS)) -TGT_DRIVERS = $(strip $(if $(DRIVERS_$(TGT_ROM_NAME)), \ - $(DRIVERS_$(TGT_ROM_NAME)), \ - $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \ - $(call CARD_DRIVER,$(TGT_ELEMENT))) )) +TGT_DRIVERS = $(strip $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \ + $(if $(DRIVERS_$(TGT_ELEMENT)), \ + $(DRIVERS_$(TGT_ELEMENT)), \ + $(firstword $(DRIVER_$(TGT_ELEMENT)) \ + $(TGT_ELEMENT))))) TGT_PREFIX_NAME = $(word 2,$(subst ., ,$(notdir $@))) TGT_PREFIX = $(strip $(if $(filter rom,$(TGT_PREFIX_NAME)), \ $(ROM_TYPE_$(TGT_ROM_NAME))rom, \ From 0f67f2edb7688d8f874239928b3c4e0446d252bd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 18 Dec 2015 10:05:58 +0000 Subject: [PATCH 019/591] [bios] Add support for injecting keypresses When USB network card drivers are used, the BIOS' legacy USB capability is necessarily disabled since there is no way to share the host controller between the BIOS and iPXE. Commit 3726722 ("[usb] Add basic support for USB keyboards") added support allowing a USB keyboard to be used within iPXE. However, external code such as a PXE NBP has no way to utilise this support, and so a USB keyboard cannot be used to control a PXE NBP loaded from a USB network card. Add support for injecting keypresses from any iPXE console into the BIOS keyboard buffer. This allows external code such as a PXE NBP to function with a USB keyboard even after the BIOS' legacy USB capability has been disabled. Signed-off-by: Michael Brown --- src/arch/i386/firmware/pcbios/bios_console.c | 241 ++++++++++++++++--- 1 file changed, 206 insertions(+), 35 deletions(-) diff --git a/src/arch/i386/firmware/pcbios/bios_console.c b/src/arch/i386/firmware/pcbios/bios_console.c index 63413cdc1..2e252ecbd 100644 --- a/src/arch/i386/firmware/pcbios/bios_console.c +++ b/src/arch/i386/firmware/pcbios/bios_console.c @@ -26,9 +26,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include +#include #include +#include #include #define ATTR_BOLD 0x08 @@ -66,6 +69,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Current character attribute */ static unsigned int bios_attr = ATTR_DEFAULT; +/** Keypress injection lock */ +static uint8_t __text16 ( bios_inject_lock ); +#define bios_inject_lock __use_text16 ( bios_inject_lock ) + +/** Vector for chaining to other INT 16 handlers */ +static struct segoff __text16 ( int16_vector ); +#define int16_vector __use_text16 ( int16_vector ) + +/** Assembly wrapper */ +extern void int16_wrapper ( void ); + /** * Handle ANSI CUP (cursor position) * @@ -265,30 +279,35 @@ static void bios_putchar ( int character ) { * not in the middle of such a sequence, this will point to a NUL * (note: not "will be NULL"). */ -static const char *ansi_input = ""; +static const char *bios_ansi_input = ""; -/** A mapping from a BIOS scan code to an ANSI escape sequence */ -#define BIOS_KEY( key, ansi ) key ansi "\0" +/** A BIOS key */ +struct bios_key { + /** Scancode */ + uint8_t scancode; + /** Key code */ + uint16_t key; +} __attribute__ (( packed )); -/** Mapping from BIOS scan codes to ANSI escape sequences */ -static const char ansi_sequences[] = { - BIOS_KEY ( "\x53", "[3~" ) /* Delete */ - BIOS_KEY ( "\x48", "[A" ) /* Up arrow */ - BIOS_KEY ( "\x50", "[B" ) /* Down arrow */ - BIOS_KEY ( "\x4b", "[D" ) /* Left arrow */ - BIOS_KEY ( "\x4d", "[C" ) /* Right arrow */ - BIOS_KEY ( "\x47", "[H" ) /* Home */ - BIOS_KEY ( "\x4f", "[F" ) /* End */ - BIOS_KEY ( "\x49", "[5~" ) /* Page up */ - BIOS_KEY ( "\x51", "[6~" ) /* Page down */ - BIOS_KEY ( "\x3f", "[15~" ) /* F5 */ - BIOS_KEY ( "\x40", "[17~" ) /* F6 */ - BIOS_KEY ( "\x41", "[18~" ) /* F7 */ - BIOS_KEY ( "\x42", "[19~" ) /* F8 (required for PXE) */ - BIOS_KEY ( "\x43", "[20~" ) /* F9 */ - BIOS_KEY ( "\x44", "[21~" ) /* F10 */ - BIOS_KEY ( "\x85", "[23~" ) /* F11 */ - BIOS_KEY ( "\x86", "[24~" ) /* F12 */ +/** Mapping from BIOS scan codes to iPXE key codes */ +static const struct bios_key bios_keys[] = { + { 0x53, KEY_DC }, + { 0x48, KEY_UP }, + { 0x50, KEY_DOWN }, + { 0x4b, KEY_LEFT }, + { 0x4d, KEY_RIGHT }, + { 0x47, KEY_HOME }, + { 0x4f, KEY_END }, + { 0x49, KEY_PPAGE }, + { 0x51, KEY_NPAGE }, + { 0x3f, KEY_F5 }, + { 0x40, KEY_F6 }, + { 0x41, KEY_F7 }, + { 0x42, KEY_F8 }, + { 0x43, KEY_F9 }, + { 0x44, KEY_F10 }, + { 0x85, KEY_F11 }, + { 0x86, KEY_F12 }, }; /** @@ -297,14 +316,35 @@ static const char ansi_sequences[] = { * @v scancode BIOS scancode * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL */ -static const char * scancode_to_ansi_seq ( unsigned int scancode ) { - const char *seq = ansi_sequences; +static const char * bios_ansi_seq ( unsigned int scancode ) { + static char buf[ 5 /* "[" + two digits + terminator + NUL */ ]; + unsigned int key; + unsigned int terminator; + unsigned int n; + unsigned int i; + char *tmp = buf; - while ( *seq ) { - if ( *(seq++) == ( ( char ) scancode ) ) - return seq; - seq += ( strlen ( seq ) + 1 ); + /* Construct ANSI escape sequence for scancode, if known */ + for ( i = 0 ; i < ( sizeof ( bios_keys ) / + sizeof ( bios_keys[0] ) ) ; i++ ) { + + /* Look for matching scancode */ + if ( bios_keys[i].scancode != scancode ) + continue; + + /* Construct escape sequence */ + key = bios_keys[i].key; + n = KEY_ANSI_N ( key ); + terminator = KEY_ANSI_TERMINATOR ( key ); + *(tmp++) = '['; + if ( n ) + tmp += sprintf ( tmp, "%d", n ); + *(tmp++) = terminator; + *(tmp++) = '\0'; + assert ( tmp <= &buf[ sizeof ( buf ) ] ); + return buf; } + DBG ( "Unrecognised BIOS scancode %02x\n", scancode ); return NULL; } @@ -336,16 +376,23 @@ static int bios_getchar ( void ) { const char *ansi_seq; /* If we are mid-sequence, pass out the next byte */ - if ( ( character = *ansi_input ) ) { - ansi_input++; + if ( ( character = *bios_ansi_input ) ) { + bios_ansi_input++; return character; } + /* Do nothing if injection is in progress */ + if ( bios_inject_lock ) + return 0; + /* Read character from real BIOS console */ + bios_inject_lock++; __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" "int $0x16\n\t" "cli\n\t" ) - : "=a" ( keypress ) : "a" ( 0x1000 ) ); + : "=a" ( keypress ) + : "a" ( 0x1000 ), "m" ( bios_inject_lock ) ); + bios_inject_lock--; character = ( keypress & 0xff ); /* If it's a normal character, just map and return it */ @@ -353,9 +400,9 @@ static int bios_getchar ( void ) { return bios_keymap ( character ); /* Otherwise, check for a special key that we know about */ - if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) { + if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) { /* Start of escape sequence: return ESC (0x1b) */ - ansi_input = ansi_seq; + bios_ansi_input = ansi_seq; return 0x1b; } @@ -373,23 +420,147 @@ static int bios_iskey ( void ) { unsigned int flags; /* If we are mid-sequence, we are always ready */ - if ( *ansi_input ) + if ( *bios_ansi_input ) return 1; + /* Do nothing if injection is in progress */ + if ( bios_inject_lock ) + return 0; + /* Otherwise check the real BIOS console */ + bios_inject_lock++; __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" "int $0x16\n\t" "pushfw\n\t" "popw %w0\n\t" "cli\n\t" ) : "=r" ( flags ), "=a" ( discard_a ) - : "a" ( 0x1100 ) ); + : "a" ( 0x1100 ), "m" ( bios_inject_lock ) ); + bios_inject_lock--; return ( ! ( flags & ZF ) ); } +/** BIOS console */ struct console_driver bios_console __console_driver = { .putchar = bios_putchar, .getchar = bios_getchar, .iskey = bios_iskey, .usage = CONSOLE_PCBIOS, }; + +/** + * Inject keypresses + * + * @v ix86 Registers as passed to INT 16 + */ +static __asmcall void bios_inject ( struct i386_all_regs *ix86 ) { + unsigned int discard_a; + unsigned int scancode; + unsigned int i; + uint16_t keypress; + int key; + + /* If this is a blocking call, then loop until the + * non-blocking variant of the call indicates that a keypress + * is available. Do this without acquiring the injection + * lock, so that injection may take place. + */ + if ( ( ix86->regs.ah & ~0x10 ) == 0x00 ) { + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "\n1:\n\t" + "pushw %%ax\n\t" + "int $0x16\n\t" + "popw %%ax\n\t" + "jc 2f\n\t" + "jz 1b\n\t" + "\n2:\n\t" + "cli\n\t" ) + : "=a" ( discard_a ) + : "a" ( ix86->regs.eax | 0x0100 ), + "m" ( bios_inject_lock ) ); + } + + /* Acquire injection lock */ + bios_inject_lock++; + + /* Check for keypresses */ + if ( iskey() ) { + + /* Get key */ + key = getkey ( 0 ); + + /* Reverse internal CR->LF mapping */ + if ( key == '\n' ) + key = '\r'; + + /* Convert to keypress */ + keypress = ( ( key << 8 ) | key ); + + /* Handle special keys */ + if ( key >= KEY_MIN ) { + for ( i = 0 ; i < ( sizeof ( bios_keys ) / + sizeof ( bios_keys[0] ) ) ; i++ ) { + if ( bios_keys[i].key == key ) { + scancode = bios_keys[i].scancode; + keypress = ( scancode << 8 ); + break; + } + } + } + + /* Inject keypress */ + DBGC ( &bios_console, "BIOS injecting keypress %04x\n", + keypress ); + __asm__ __volatile__ ( REAL_CODE ( "int $0x16\n\t" ) + : "=a" ( discard_a ) + : "a" ( 0x0500 ), "c" ( keypress ), + "m" ( bios_inject_lock ) ); + } + + /* Release injection lock */ + bios_inject_lock--; +} + +/** + * Start up keypress injection + * + */ +static void bios_inject_startup ( void ) { + + /* Assembly wrapper to call bios_inject() */ + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint16_wrapper:\n\t" + "pushfw\n\t" + "cmpb $0, %%cs:bios_inject_lock\n\t" + "jnz 1f\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "\n1:\n\t" + "popfw\n\t" + "ljmp *%%cs:int16_vector\n\t" ) + : : "i" ( bios_inject ) ); + + /* Hook INT 16 */ + hook_bios_interrupt ( 0x16, ( ( unsigned int ) int16_wrapper ), + &int16_vector ); +} + +/** + * Shut down keypress injection + * + * @v booting System is shutting down for OS boot + */ +static void bios_inject_shutdown ( int booting __unused ) { + + /* Unhook INT 16 */ + unhook_bios_interrupt ( 0x16, ( ( unsigned int ) int16_wrapper ), + &int16_vector ); +} + +/** Keypress injection startup function */ +struct startup_fn bios_inject_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .startup = bios_inject_startup, + .shutdown = bios_inject_shutdown, +}; From 721302fa545a06c736ffe59278ad5404ad19014c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Dec 2015 15:28:08 +0000 Subject: [PATCH 020/591] [settings] Expose SMBIOS settings as global variables Signed-off-by: Michael Brown --- src/include/ipxe/settings.h | 10 ++++++++++ src/interface/smbios/smbios_settings.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 95a553cc8..4f56f3e9e 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -452,6 +452,16 @@ extern const struct setting busid_setting __setting ( SETTING_NETDEV, busid ); extern const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA, user-class ); +extern const struct setting +manufacturer_setting __setting ( SETTING_HOST_EXTRA, manufacturer ); +extern const struct setting +product_setting __setting ( SETTING_HOST_EXTRA, product ); +extern const struct setting +serial_setting __setting ( SETTING_HOST_EXTRA, serial ); +extern const struct setting +asset_setting __setting ( SETTING_HOST_EXTRA, asset ); +extern const struct setting +board_serial_setting __setting ( SETTING_HOST_EXTRA, board-serial ); /** * Initialise a settings block diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index 5eadfa081..2d571f2e4 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -248,7 +248,7 @@ const struct setting asset_setting __setting ( SETTING_HOST_EXTRA, asset ) = { /** Board serial number setting (may differ from chassis serial number) */ const struct setting board_serial_setting __setting ( SETTING_HOST_EXTRA, - board_serial ) = { + board-serial ) = { .name = "board-serial", .description = "Base board serial", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_BASE_BOARD_INFORMATION, From 82e03764d868034ec3bbf9eafc311363dfb9b494 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Dec 2015 15:02:48 +0000 Subject: [PATCH 021/591] [smsc95xx] Allow for multiple methods for obtaining the MAC address The SMSC95xx devices tend to be used in embedded systems with a variety of ad-hoc mechanisms for storing the MAC address. Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 67 +++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index 9cd428cf9..e852e06f2 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -249,6 +249,59 @@ static int smsc95xx_eeprom_read ( struct smsc95xx_device *smsc95xx, return 0; } +/****************************************************************************** + * + * MAC address + * + ****************************************************************************** + */ + +/** + * Fetch MAC address from EEPROM + * + * @v smsc95xx SMSC95xx device + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +static int smsc95xx_fetch_mac_eeprom ( struct smsc95xx_device *smsc95xx, + uint8_t *hw_addr ) { + int rc; + + /* Read MAC address from EEPROM */ + if ( ( rc = smsc95xx_eeprom_read ( smsc95xx, SMSC95XX_EEPROM_MAC, + hw_addr, ETH_ALEN ) ) != 0 ) + return rc; + + /* Check that EEPROM is physically present */ + if ( ! is_valid_ether_addr ( hw_addr ) ) { + DBGC ( smsc95xx, "SMSC95XX %p has no EEPROM (%s)\n", + smsc95xx, eth_ntoa ( hw_addr ) ); + return -ENODEV; + } + + return 0; +} + +/** + * Fetch MAC address + * + * @v smsc95xx SMSC95xx device + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +static int smsc95xx_fetch_mac ( struct smsc95xx_device *smsc95xx, + uint8_t *hw_addr ) { + int rc; + + /* Read MAC address from EEPROM, if present */ + if ( ( rc = smsc95xx_fetch_mac_eeprom ( smsc95xx, hw_addr ) ) == 0 ) + return 0; + + /* Otherwise, generate a random MAC address */ + eth_random_addr ( hw_addr ); + return 0; +} + /****************************************************************************** * * MII access @@ -980,16 +1033,8 @@ static int smsc95xx_probe ( struct usb_function *func, goto err_reset; /* Read MAC address */ - if ( ( rc = smsc95xx_eeprom_read ( smsc95xx, SMSC95XX_EEPROM_MAC, - netdev->hw_addr, ETH_ALEN ) ) != 0 ) - goto err_eeprom_read; - - /* Generate MAC address if EEPROM is not present */ - if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { - DBGC ( smsc95xx, "SMSC95XX %p has no EEPROM (%s)\n", - smsc95xx, eth_ntoa ( netdev->hw_addr ) ); - eth_random_addr ( netdev->hw_addr ); - } + if ( ( rc = smsc95xx_fetch_mac ( smsc95xx, netdev->hw_addr ) ) != 0 ) + goto err_fetch_mac; /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) @@ -1000,7 +1045,7 @@ static int smsc95xx_probe ( struct usb_function *func, unregister_netdev ( netdev ); err_register: - err_eeprom_read: + err_fetch_mac: err_reset: err_describe: netdev_nullify ( netdev ); From 0c396dd4052732da751249e5057425adb0692db2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 31 Dec 2015 14:44:45 +0000 Subject: [PATCH 022/591] [crypto] Dual-license selected DRBG files Allow the use of the iPXE DRBG implementation in BSD-licensed projects. Requested-by: Sean Davis Signed-off-by: Michael Brown --- src/crypto/drbg.c | 12 ++++++++++++ src/crypto/hash_df.c | 12 ++++++++++++ src/crypto/hmac_drbg.c | 12 ++++++++++++ src/crypto/rbg.c | 12 ++++++++++++ 4 files changed, 48 insertions(+) diff --git a/src/crypto/drbg.c b/src/crypto/drbg.c index 5c8b5e612..a3366e806 100644 --- a/src/crypto/drbg.c +++ b/src/crypto/drbg.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/hash_df.c b/src/crypto/hash_df.c index c1417e683..dc0dc0ce8 100644 --- a/src/crypto/hash_df.c +++ b/src/crypto/hash_df.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/hmac_drbg.c b/src/crypto/hmac_drbg.c index 6c1d5deb2..098297716 100644 --- a/src/crypto/hmac_drbg.c +++ b/src/crypto/hmac_drbg.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/rbg.c b/src/crypto/rbg.c index 943b288c3..a5a77e3cd 100644 --- a/src/crypto/rbg.c +++ b/src/crypto/rbg.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); From ae8dfd74c014a722060737cf01a30725ab7cc852 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Dec 2015 15:25:31 +0000 Subject: [PATCH 023/591] [smsc95xx] Fetch MAC from SMBIOS OEM string for Honeywell VM3 The Honeywell VM3 has no attached EEPROM, and records the MAC address within an SMBIOS OEM string. Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 113 +++++++++++++++++++++++++++++++++++++ src/drivers/net/smsc95xx.h | 3 + src/include/ipxe/smbios.h | 3 + 3 files changed, 119 insertions(+) diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index e852e06f2..c1dd08051 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -31,6 +31,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include "smsc95xx.h" /** @file @@ -279,6 +281,111 @@ static int smsc95xx_fetch_mac_eeprom ( struct smsc95xx_device *smsc95xx, return -ENODEV; } + DBGC ( smsc95xx, "SMSC95XX %p using EEPROM MAC %s\n", + smsc95xx, eth_ntoa ( hw_addr ) ); + return 0; +} + +/** + * Construct MAC address for Honeywell VM3 + * + * @v smsc95xx SMSC95xx device + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, + uint8_t *hw_addr ) { + struct smbios_structure structure; + struct smbios_system_information system; + struct { + char manufacturer[ 10 /* "Honeywell" + NUL */ ]; + char product[ 4 /* "VM3" + NUL */ ]; + char mac[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + } strings; + int len; + int rc; + + /* Find system information */ + if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_SYSTEM_INFORMATION, 0, + &structure ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not find system " + "information: %s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Read system information */ + if ( ( rc = read_smbios_structure ( &structure, &system, + sizeof ( system ) ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not read system " + "information: %s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + /* NUL-terminate all strings to be fetched */ + memset ( &strings, 0, sizeof ( strings ) ); + + /* Fetch system manufacturer name */ + len = read_smbios_string ( &structure, system.manufacturer, + strings.manufacturer, + ( sizeof ( strings.manufacturer ) - 1 ) ); + if ( len < 0 ) { + rc = len; + DBGC ( smsc95xx, "SMSC95XX %p could not read manufacturer " + "name: %s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Fetch system product name */ + len = read_smbios_string ( &structure, system.product, strings.product, + ( sizeof ( strings.product ) - 1 ) ); + if ( len < 0 ) { + rc = len; + DBGC ( smsc95xx, "SMSC95XX %p could not read product name: " + "%s\n", smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Ignore non-VM3 devices */ + if ( ( strcmp ( strings.manufacturer, "Honeywell" ) != 0 ) || + ( strcmp ( strings.product, "VM3" ) != 0 ) ) + return -ENOTTY; + + /* Find OEM strings */ + if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_OEM_STRINGS, 0, + &structure ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not find OEM strings: %s\n", + smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Fetch MAC address */ + len = read_smbios_string ( &structure, SMSC95XX_VM3_OEM_STRING_MAC, + strings.mac, ( sizeof ( strings.mac ) - 1 )); + if ( len < 0 ) { + rc = len; + DBGC ( smsc95xx, "SMSC95XX %p could not read OEM string: %s\n", + smsc95xx, strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( len != ( ( int ) ( sizeof ( strings.mac ) - 1 ) ) ) { + DBGC ( smsc95xx, "SMSC95XX %p invalid MAC address \"%s\"\n", + smsc95xx, strings.mac ); + return -EINVAL; + } + + /* Decode MAC address */ + len = base16_decode ( strings.mac, hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + DBGC ( smsc95xx, "SMSC95XX %p invalid MAC address \"%s\"\n", + smsc95xx, strings.mac ); + return rc; + } + + DBGC ( smsc95xx, "SMSC95XX %p using VM3 MAC %s\n", + smsc95xx, eth_ntoa ( hw_addr ) ); return 0; } @@ -297,8 +404,14 @@ static int smsc95xx_fetch_mac ( struct smsc95xx_device *smsc95xx, if ( ( rc = smsc95xx_fetch_mac_eeprom ( smsc95xx, hw_addr ) ) == 0 ) return 0; + /* Construct MAC address for Honeywell VM3, if applicable */ + if ( ( rc = smsc95xx_fetch_mac_vm3 ( smsc95xx, hw_addr ) ) == 0 ) + return 0; + /* Otherwise, generate a random MAC address */ eth_random_addr ( hw_addr ); + DBGC ( smsc95xx, "SMSC95XX %p using random MAC %s\n", + smsc95xx, eth_ntoa ( hw_addr ) ); return 0; } diff --git a/src/drivers/net/smsc95xx.h b/src/drivers/net/smsc95xx.h index 3b83327bf..d66d86803 100644 --- a/src/drivers/net/smsc95xx.h +++ b/src/drivers/net/smsc95xx.h @@ -251,4 +251,7 @@ struct smsc95xx_device { ETH_FRAME_LEN + 4 /* possible VLAN header */ \ + 4 /* CRC */ ) +/** Honeywell VM3 MAC address OEM string index */ +#define SMSC95XX_VM3_OEM_STRING_MAC 2 + #endif /* _SMSC95XX_H */ diff --git a/src/include/ipxe/smbios.h b/src/include/ipxe/smbios.h index 24b05ed62..c1d8fea3e 100644 --- a/src/include/ipxe/smbios.h +++ b/src/include/ipxe/smbios.h @@ -152,6 +152,9 @@ struct smbios_enclosure_information { /** SMBIOS enclosure information structure type */ #define SMBIOS_TYPE_ENCLOSURE_INFORMATION 3 +/** SMBIOS OEM strings structure type */ +#define SMBIOS_TYPE_OEM_STRINGS 11 + /** * SMBIOS entry point descriptor * From 29cb090f98132417eb7e1d0639823f7f32c63ffb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jan 2016 15:39:07 +0000 Subject: [PATCH 024/591] [crypto] Dual-license more selected DRBG files Allow the use of the iPXE DRBG implementation in BSD-licensed projects. Requested-by: Sean Davis Signed-off-by: Michael Brown --- src/crypto/hmac.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c index 95a46195c..f898619c8 100644 --- a/src/crypto/hmac.c +++ b/src/crypto/hmac.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); From dd485992dc213dc19876a331f3a044dcb2e7560b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jan 2016 17:20:36 +0000 Subject: [PATCH 025/591] [vmware] Expose GuestRPC mechanism in 64-bit builds The GuestRPC mechanism (used for VMWARE_SETTINGS and CONSOLE_VMWARE) does not use any real-mode code and so can be exposed in both 64-bit and 32-bit builds. Reported-by: Matthew Helton Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 1 - src/arch/x86/Makefile | 1 + src/arch/{i386 => x86}/include/ipxe/guestrpc.h | 0 src/arch/{i386 => x86}/include/ipxe/vmware.h | 0 src/arch/{i386 => x86}/interface/vmware/guestinfo.c | 0 src/arch/{i386 => x86}/interface/vmware/guestrpc.c | 0 src/arch/{i386 => x86}/interface/vmware/vmconsole.c | 0 src/arch/{i386 => x86}/interface/vmware/vmware.c | 0 8 files changed, 1 insertion(+), 1 deletion(-) rename src/arch/{i386 => x86}/include/ipxe/guestrpc.h (100%) rename src/arch/{i386 => x86}/include/ipxe/vmware.h (100%) rename src/arch/{i386 => x86}/interface/vmware/guestinfo.c (100%) rename src/arch/{i386 => x86}/interface/vmware/guestrpc.c (100%) rename src/arch/{i386 => x86}/interface/vmware/vmconsole.c (100%) rename src/arch/{i386 => x86}/interface/vmware/vmware.c (100%) diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 7f706a5a4..478e0634c 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -115,7 +115,6 @@ SRCDIRS += arch/i386/interface/pcbios SRCDIRS += arch/i386/interface/pxe SRCDIRS += arch/i386/interface/pxeparent SRCDIRS += arch/i386/interface/syslinux -SRCDIRS += arch/i386/interface/vmware SRCDIRS += arch/i386/hci/commands # Include common x86 Makefile diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 98c49b98d..4ab741db7 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -6,6 +6,7 @@ INCDIRS += arch/x86/include # SRCDIRS += arch/x86/core SRCDIRS += arch/x86/interface/efi +SRCDIRS += arch/x86/interface/vmware SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen diff --git a/src/arch/i386/include/ipxe/guestrpc.h b/src/arch/x86/include/ipxe/guestrpc.h similarity index 100% rename from src/arch/i386/include/ipxe/guestrpc.h rename to src/arch/x86/include/ipxe/guestrpc.h diff --git a/src/arch/i386/include/ipxe/vmware.h b/src/arch/x86/include/ipxe/vmware.h similarity index 100% rename from src/arch/i386/include/ipxe/vmware.h rename to src/arch/x86/include/ipxe/vmware.h diff --git a/src/arch/i386/interface/vmware/guestinfo.c b/src/arch/x86/interface/vmware/guestinfo.c similarity index 100% rename from src/arch/i386/interface/vmware/guestinfo.c rename to src/arch/x86/interface/vmware/guestinfo.c diff --git a/src/arch/i386/interface/vmware/guestrpc.c b/src/arch/x86/interface/vmware/guestrpc.c similarity index 100% rename from src/arch/i386/interface/vmware/guestrpc.c rename to src/arch/x86/interface/vmware/guestrpc.c diff --git a/src/arch/i386/interface/vmware/vmconsole.c b/src/arch/x86/interface/vmware/vmconsole.c similarity index 100% rename from src/arch/i386/interface/vmware/vmconsole.c rename to src/arch/x86/interface/vmware/vmconsole.c diff --git a/src/arch/i386/interface/vmware/vmware.c b/src/arch/x86/interface/vmware/vmware.c similarity index 100% rename from src/arch/i386/interface/vmware/vmware.c rename to src/arch/x86/interface/vmware/vmware.c From 173c48a57e43b747ab8ace1e5a59bfdb2132ddee Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jan 2016 23:40:18 +0000 Subject: [PATCH 026/591] [romprefix] Report an optimistic runtime size estimate Commit 5de45cd ("[romprefix] Report a pessimistic runtime size estimate") set the PCI3.0 "runtime size" field equal to the worst-case runtime size, on the basis that there is no guarantee that PMM allocation will succeed and hence no guarantee that we will be able to shrink the ROM image. On a PCI3.0 system where PMM allocation would succeed, this can cause the BIOS to unnecessarily refuse to initialise the iPXE ROM due to a perceived shortage of option ROM space. Fix by reporting the best-case runtime size via the PCI header, and checking that we have sufficient runtime space (if applicable). This allows iPXE ROMs to initialise on PCI3.0 systems that might otherwise fail due to a shortage of option ROM space. This may cause iPXE ROMs to fail to initialise on PCI3.0 systems where PMM is broken. (Pre-PCI3.0 systems are unaffected since there must already have been sufficient option ROM space available for the initialisation entry point to be called.) On balance, it seems preferable to avoid breaking "good" systems (PCI3.0 with working PMM) at the cost of potentially breaking "bad" systems (PCI3.0 with broken PMM). Signed-off-by: Michael Brown --- src/arch/i386/prefix/romprefix.S | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S index 18dda2b37..8974c5398 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/i386/prefix/romprefix.S @@ -123,7 +123,7 @@ pci_devlist_end: .long pciheader_image_length .long 512 .long 0 - .ascii ZINFO_TYPE_ADxW + .ascii "ADHW" .long pciheader_runtime_length .long 512 .long 0 @@ -435,13 +435,25 @@ no_pmm: * memory. Will be a no-op for lower PCI versions. */ .ifeqs BUSTYPE, "PCIR" + /* Get runtime segment address and length */ + movw %gs, %ax + movw %ax, %es + movzbw romheader_size, %cx + /* Print runtime segment address */ xorw %di, %di call print_space - movw %gs, %ax call print_hex_word - movzbw romheader_size, %cx + /* Fail if we have insufficient space in final location */ + movw %cs, %si + cmpw %si, %ax + je 1f + cmpw pciheader_runtime_length, %cx + jbe 1f + movb $( '!' ), %al + call print_character + xorw %cx, %cx +1: /* Copy to final location */ shlw $9, %cx - movw %ax, %es xorw %si, %si xorw %di, %di cs rep movsb From 2f861d736f8b156aa87de3f0e250380ca292f767 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Jan 2016 16:30:01 +0000 Subject: [PATCH 027/591] [usb] Add support for numeric keypad on USB keyboards Signed-off-by: Michael Brown --- src/drivers/usb/usbkbd.c | 88 ++++++++++++++++++++++++++++++++++++--- src/drivers/usb/usbkbd.h | 19 ++++++++- src/include/ipxe/usbhid.h | 34 +++++++++++++++ 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 512adfe2f..76db5771f 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -53,13 +53,14 @@ static LIST_HEAD ( usb_keyboards ); * * @v keycode Keycode * @v modifiers Modifiers + * @v leds LED state * @ret key iPXE key * * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad * page. */ -static unsigned int usbkbd_map ( unsigned int keycode, - unsigned int modifiers ) { +static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, + unsigned int leds ) { unsigned int key; if ( keycode < USBKBD_KEY_A ) { @@ -70,7 +71,8 @@ static unsigned int usbkbd_map ( unsigned int keycode, key = ( keycode - USBKBD_KEY_A + 'a' ); if ( modifiers & USBKBD_CTRL ) { key -= ( 'a' - CTRL_A ); - } else if ( modifiers & USBKBD_SHIFT ) { + } else if ( ( modifiers & USBKBD_SHIFT ) || + ( leds & USBKBD_LED_CAPS_LOCK ) ) { key -= ( 'a' - 'A' ); } } else if ( keycode <= USBKBD_KEY_0 ) { @@ -100,7 +102,22 @@ static unsigned int usbkbd_map ( unsigned int keycode, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP }; - key = special[ keycode - USBKBD_KEY_CAPSLOCK ]; + key = special[ keycode - USBKBD_KEY_CAPS_LOCK ]; + } else if ( keycode <= USBKBD_KEY_PAD_ENTER ) { + /* Keypad (unaffected by Num Lock) */ + key = "\0/*-+\n" [ keycode - USBKBD_KEY_NUM_LOCK ]; + } else if ( keycode <= USBKBD_KEY_PAD_DOT ) { + /* Keypad (affected by Num Lock) */ + if ( leds & USBKBD_LED_NUM_LOCK ) { + key = "1234567890." [ keycode - USBKBD_KEY_PAD_1 ]; + } else { + static const uint16_t keypad[] = { + KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, 0, + KEY_RIGHT, KEY_HOME, KEY_UP, KEY_PPAGE, + KEY_IC, KEY_DC + }; + key = keypad[ keycode - USBKBD_KEY_PAD_1 ]; + }; } else { key = 0; } @@ -124,10 +141,25 @@ static unsigned int usbkbd_map ( unsigned int keycode, */ static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode, unsigned int modifiers ) { + unsigned int leds = 0; unsigned int key; + /* Check for LED-modifying keys */ + if ( keycode == USBKBD_KEY_CAPS_LOCK ) { + leds = USBKBD_LED_CAPS_LOCK; + } else if ( keycode == USBKBD_KEY_NUM_LOCK ) { + leds = USBKBD_LED_NUM_LOCK; + } + + /* Handle LED-modifying keys */ + if ( leds ) { + kbd->leds ^= leds; + kbd->leds_changed = 1; + return; + } + /* Map to iPXE key */ - key = usbkbd_map ( keycode, modifiers ); + key = usbkbd_map ( keycode, modifiers, kbd->leds ); /* Do nothing if this keycode has no corresponding iPXE key */ if ( ! key ) { @@ -333,6 +365,37 @@ static struct usb_endpoint_driver_operations usbkbd_operations = { .complete = usbkbd_complete, }; +/****************************************************************************** + * + * Keyboard LEDs + * + ****************************************************************************** + */ + +/** + * Set keyboard LEDs + * + * @v kbd USB keyboard + * @ret rc Return status code + */ +static int usbkbd_set_leds ( struct usb_keyboard *kbd ) { + struct usb_function *func = kbd->hid.func; + int rc; + + DBGC2 ( kbd, "KBD %s setting LEDs to %#02x\n", kbd->name, kbd->leds ); + + /* Set keyboard LEDs */ + if ( ( rc = usbhid_set_report ( func->usb, func->interface[0], + USBHID_REPORT_OUTPUT, 0, &kbd->leds, + sizeof ( kbd->leds ) ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not set LEDs to %#02x: %s\n", + kbd->name, kbd->leds, strerror ( rc ) ); + return rc; + } + + return 0; +} + /****************************************************************************** * * USB interface @@ -400,6 +463,9 @@ static int usbkbd_probe ( struct usb_function *func, /* Add to list of USB keyboards */ list_add_tail ( &kbd->list, &usb_keyboards ); + /* Set initial LED state */ + usbkbd_set_leds ( kbd ); + usb_func_set_drvdata ( func, kbd ); return 0; @@ -484,10 +550,20 @@ static int usbkbd_iskey ( void ) { struct usb_keyboard *kbd; unsigned int fill; - /* Poll all USB keyboards and refill endpoints */ + /* Poll USB keyboards, refill endpoints, and set LEDs if applicable */ list_for_each_entry ( kbd, &usb_keyboards, list ) { + + /* Poll keyboard */ usb_poll ( kbd->bus ); + + /* Refill endpoints */ usb_refill ( &kbd->hid.in ); + + /* Update keyboard LEDs, if applicable */ + if ( kbd->leds_changed ) { + usbkbd_set_leds ( kbd ); + kbd->leds_changed = 0; + } } /* Check for a non-empty keyboard buffer */ diff --git a/src/drivers/usb/usbkbd.h b/src/drivers/usb/usbkbd.h index 7eab24e46..cedebfe71 100644 --- a/src/drivers/usb/usbkbd.h +++ b/src/drivers/usb/usbkbd.h @@ -68,8 +68,20 @@ enum usb_keycode { USBKBD_KEY_SPACE = 0x2c, USBKBD_KEY_MINUS = 0x2d, USBKBD_KEY_SLASH = 0x38, - USBKBD_KEY_CAPSLOCK = 0x39, + USBKBD_KEY_CAPS_LOCK = 0x39, + USBKBD_KEY_F1 = 0x3a, USBKBD_KEY_UP = 0x52, + USBKBD_KEY_NUM_LOCK = 0x53, + USBKBD_KEY_PAD_ENTER = 0x58, + USBKBD_KEY_PAD_1 = 0x59, + USBKBD_KEY_PAD_DOT = 0x63, +}; + +/** USB keyboard LEDs */ +enum usb_keyboard_led { + USBKBD_LED_NUM_LOCK = 0x01, + USBKBD_LED_CAPS_LOCK = 0x02, + USBKBD_LED_SCROLL_LOCK = 0x04, }; /** Keyboard idle duration (in 4ms units) @@ -120,6 +132,11 @@ struct usb_keyboard { /** Autorepeat hold-off time (in number of completions reported) */ unsigned int holdoff; + /** Keyboard LED state */ + uint8_t leds; + /** Keyboard LEDs changed */ + uint8_t leds_changed; + /** Keyboard buffer * * This stores iPXE key values. diff --git a/src/include/ipxe/usbhid.h b/src/include/ipxe/usbhid.h index fe9d84455..233534e0f 100644 --- a/src/include/ipxe/usbhid.h +++ b/src/include/ipxe/usbhid.h @@ -33,6 +33,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ USB_REQUEST_TYPE ( 0x0a ) ) +/** Set report */ +#define USBHID_SET_REPORT \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x09 ) ) + +/** Input report type */ +#define USBHID_REPORT_INPUT 0x01 + +/** Output report type */ +#define USBHID_REPORT_OUTPUT 0x02 + +/** Feature report type */ +#define USBHID_REPORT_FEATURE 0x03 + /** A USB human interface device */ struct usb_hid { /** USB function */ @@ -97,6 +111,26 @@ usbhid_set_idle ( struct usb_device *usb, unsigned int interface, interface, NULL, 0 ); } +/** + * Set report + * + * @v usb USB device + * @v interface Interface number + * @v type Report type + * @v report Report ID + * @v data Report data + * @v len Length of report data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usbhid_set_report ( struct usb_device *usb, unsigned int interface, + unsigned int type, unsigned int report, void *data, + size_t len ) { + + return usb_control ( usb, USBHID_SET_REPORT, ( ( type << 8 ) | report ), + interface, data, len ); +} + extern int usbhid_open ( struct usb_hid *hid ); extern void usbhid_close ( struct usb_hid *hid ); extern int usbhid_refill ( struct usb_hid *hid ); From 74c812a68cb272971708fff16f2ad1d8dadfcb5c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 9 Jan 2016 13:20:55 +0000 Subject: [PATCH 028/591] [http] Handle relative redirection URIs Resolve redirection URIs as being relative to the original HTTP request URI, rather than treating them as being implicitly relative to the current working URI. Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 53 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index f3685de04..d40633aaf 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -684,6 +684,51 @@ int http_open ( struct interface *xfer, struct http_method *method, return rc; } +/** + * Redirect HTTP transaction + * + * @v http HTTP transaction + * @v location New location + * @ret rc Return status code + */ +static int http_redirect ( struct http_transaction *http, + const char *location ) { + struct uri *location_uri; + struct uri *resolved_uri; + int rc; + + DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", http, location ); + + /* Parse location URI */ + location_uri = parse_uri ( location ); + if ( ! location_uri ) { + rc = -ENOMEM; + goto err_parse_uri; + } + + /* Resolve as relative to original URI */ + resolved_uri = resolve_uri ( http->uri, location_uri ); + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto err_resolve_uri; + } + + /* Redirect to new URI */ + if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI, + resolved_uri ) ) != 0 ) { + DBGC ( http, "HTTP %p could not redirect: %s\n", + http, strerror ( rc ) ); + goto err_redirect; + } + + err_redirect: + uri_put ( resolved_uri ); + err_resolve_uri: + uri_put ( location_uri ); + err_parse_uri: + return rc; +} + /** * Handle successful transfer completion * @@ -717,14 +762,8 @@ static int http_transfer_complete ( struct http_transaction *http ) { /* Perform redirection, if applicable */ if ( ( location = http->response.location ) ) { - DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", - http, location ); - if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, - location ) ) != 0 ) { - DBGC ( http, "HTTP %p could not redirect: %s\n", - http, strerror ( rc ) ); + if ( ( rc = http_redirect ( http, location ) ) != 0 ) return rc; - } http_close ( http, 0 ); return 0; } From 57fa0db03f9d0a4c73b7fea5b23f98941a1e0e22 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 9 Jan 2016 13:22:37 +0000 Subject: [PATCH 029/591] [image] Provide image_set_uri() to modify an image's URI Signed-off-by: Michael Brown --- src/core/image.c | 38 ++++++++++++++++++++++++++++---------- src/include/ipxe/image.h | 1 + 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/core/image.c b/src/core/image.c index 529e3d72c..4a4e9c2a0 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -88,7 +88,6 @@ static void free_image ( struct refcnt *refcnt ) { * @ret image Executable image */ struct image * alloc_image ( struct uri *uri ) { - const char *name; struct image *image; int rc; @@ -99,23 +98,42 @@ struct image * alloc_image ( struct uri *uri ) { /* Initialise image */ ref_init ( &image->refcnt, free_image ); - if ( uri ) { - image->uri = uri_get ( uri ); - if ( uri->path ) { - name = basename ( ( char * ) uri->path ); - if ( ( rc = image_set_name ( image, name ) ) != 0 ) - goto err_set_name; - } - } + if ( uri && ( ( rc = image_set_uri ( image, uri ) ) != 0 ) ) + goto err_set_uri; return image; - err_set_name: + err_set_uri: image_put ( image ); err_alloc: return NULL; } +/** + * Set image URI + * + * @v image Image + * @v uri New image URI + * @ret rc Return status code + */ +int image_set_uri ( struct image *image, struct uri *uri ) { + const char *name; + int rc; + + /* Set name, if image does not already have one */ + if ( uri->path && ( ! ( image->name && image->name[0] ) ) ) { + name = basename ( ( char * ) uri->path ); + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + return rc; + } + + /* Update image URI */ + uri_put ( image->uri ); + image->uri = uri_get ( uri ); + + return 0; +} + /** * Set image name * diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index 6abd7a2d2..f33feddad 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -158,6 +158,7 @@ static inline struct image * first_image ( void ) { } extern struct image * alloc_image ( struct uri *uri ); +extern int image_set_uri ( struct image *image, struct uri *uri ); extern int image_set_name ( struct image *image, const char *name ); extern int image_set_cmdline ( struct image *image, const char *cmdline ); extern int register_image ( struct image *image ); From a8bef53908db7682aff6957c88fa267c4476dbb1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 9 Jan 2016 13:23:13 +0000 Subject: [PATCH 030/591] [downloader] Update image URI in response to a redirection Update the image's recorded URI when a download redirection occurs. This ensures that URIs relative to a redirected download are resolved correctly. In particular, this allows for the use of relative URIs in scripts that are themselves downloaded via a redirection, such as the HTTP 301 redirection used to fix up URIs pointing to directories but omitting the trailing slash (e.g. "http://boot.ipxe.org/demo", which will be redirected to "http://boot.ipxe.org/demo/"). Signed-off-by: Michael Brown --- src/core/downloader.c | 47 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/core/downloader.c b/src/core/downloader.c index d745f3617..ba678f868 100644 --- a/src/core/downloader.c +++ b/src/core/downloader.c @@ -136,9 +136,9 @@ static int downloader_progress ( struct downloader *downloader, * @v meta Data transfer metadata * @ret rc Return status code */ -static int downloader_xfer_deliver ( struct downloader *downloader, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { +static int downloader_deliver ( struct downloader *downloader, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { int rc; /* Add data to buffer */ @@ -160,16 +160,51 @@ static int downloader_xfer_deliver ( struct downloader *downloader, * @ret xferbuf Data transfer buffer, or NULL on error */ static struct xfer_buffer * -downloader_xfer_buffer ( struct downloader *downloader ) { +downloader_buffer ( struct downloader *downloader ) { /* Provide direct access to underlying data transfer buffer */ return &downloader->buffer; } +/** + * Redirect data transfer interface + * + * @v downloader Downloader + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +static int downloader_vredirect ( struct downloader *downloader, int type, + va_list args ) { + va_list tmp; + struct uri *uri; + int rc; + + /* Intercept redirects to a LOCATION_URI and update the image URI */ + if ( type == LOCATION_URI ) { + + /* Extract URI argument */ + va_copy ( tmp, args ); + uri = va_arg ( tmp, struct uri * ); + va_end ( tmp ); + + /* Set image URI */ + if ( ( rc = image_set_uri ( downloader->image, uri ) ) != 0 ) + return rc; + } + + /* Redirect to new location */ + if ( ( rc = xfer_vreopen ( &downloader->xfer, type, args ) ) != 0 ) + return rc; + + return 0; +} + /** Downloader data transfer interface operations */ static struct interface_operation downloader_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct downloader *, downloader_xfer_deliver ), - INTF_OP ( xfer_buffer, struct downloader *, downloader_xfer_buffer ), + INTF_OP ( xfer_deliver, struct downloader *, downloader_deliver ), + INTF_OP ( xfer_buffer, struct downloader *, downloader_buffer ), + INTF_OP ( xfer_vredirect, struct downloader *, downloader_vredirect ), INTF_OP ( intf_close, struct downloader *, downloader_finished ), }; From 0af08888329e29541d8525732d48e75ab1eebecc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 9 Jan 2016 14:51:21 +0000 Subject: [PATCH 031/591] [tftp] Do not change current working URI when TFTP server is cleared For historical reasons, iPXE sets the current working URI to the root of the TFTP server whenever the TFTP server address is changed. This was originally implemented in the hope of allowing a DHCP-provided TFTP filename to be treated simply as a relative URI. This usage turns out to be impractical since DHCP-provided TFTP filenames may include characters which would have special significance to the URI parser, and so the DHCP next-server+filename combination is now handled by the dedicated pxe_uri() function instead. The practice of setting the current working URI to the root of the TFTP server is potentially helpful for interactive uses of iPXE, allowing a user to type e.g. iPXE> dhcp Configuring (net0 52:54:00:12:34:56)... ok iPXE> chain pxelinux.0 and have the URI "pxelinux.0" interpreted as being relative to the root of the TFTP server provided via DHCP. The current implementation of tftp_apply_settings() has an unintended flaw. When the "dhcp" command is used to renew a DHCP lease (or to pick up potentially modified DHCP options), the old settings block will be unregistered before the new settings block is registered. This causes tftp_apply_settings() to believe that the TFTP server has been changed twice (to 0.0.0.0 and back again), and so the current working URI will always be set to the root of the TFTP server, even if the DHCP response provides exactly the same TFTP server as previously. Fix by doing nothing in tftp_apply_settings() whenever there is no TFTP server address. Debugged-by: Andrew Widdersheim Signed-off-by: Michael Brown --- src/net/udp/tftp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index 953bcb46a..f3cb34342 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -1180,13 +1180,12 @@ struct uri_opener mtftp_uri_opener __uri_opener = { */ static int tftp_apply_settings ( void ) { static struct in_addr tftp_server = { 0 }; - struct in_addr last_tftp_server; + struct in_addr new_tftp_server; char uri_string[32]; struct uri *uri; /* Retrieve TFTP server setting */ - last_tftp_server = tftp_server; - fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server ); + fetch_ipv4_setting ( NULL, &next_server_setting, &new_tftp_server ); /* If TFTP server setting has changed, set the current working * URI to match. Do it only when the TFTP server has changed @@ -1195,18 +1194,19 @@ static int tftp_apply_settings ( void ) { * an unrelated setting and triggered all the settings * applicators. */ - if ( tftp_server.s_addr != last_tftp_server.s_addr ) { - if ( tftp_server.s_addr ) { - snprintf ( uri_string, sizeof ( uri_string ), - "tftp://%s/", inet_ntoa ( tftp_server ) ); - uri = parse_uri ( uri_string ); - if ( ! uri ) - return -ENOMEM; - } else { - uri = NULL; - } + if ( new_tftp_server.s_addr && + ( new_tftp_server.s_addr != tftp_server.s_addr ) ) { + DBGC ( &tftp_server, "TFTP server changed %s => ", + inet_ntoa ( tftp_server ) ); + DBGC ( &tftp_server, "%s\n", inet_ntoa ( new_tftp_server ) ); + snprintf ( uri_string, sizeof ( uri_string ), + "tftp://%s/", inet_ntoa ( new_tftp_server ) ); + uri = parse_uri ( uri_string ); + if ( ! uri ) + return -ENOMEM; churi ( uri ); uri_put ( uri ); + tftp_server = new_tftp_server; } return 0; From 7c6858e95df1e0b654286049c15a4a48982ef8af Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Jan 2016 15:44:00 +0000 Subject: [PATCH 032/591] [infiniband] Profile post work queue entry operations Signed-off-by: Michael Brown --- src/net/infiniband.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/net/infiniband.c b/src/net/infiniband.c index 2e3d76d54..c059fe8c4 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -53,6 +54,14 @@ struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices ); /** List of open Infiniband devices, in reverse order of opening */ static struct list_head open_ib_devices = LIST_HEAD_INIT ( open_ib_devices ); +/** Post send work queue entry profiler */ +static struct profiler ib_post_send_profiler __profiler = + { .name = "ib.post_send" }; + +/** Post receive work queue entry profiler */ +static struct profiler ib_post_recv_profiler __profiler = + { .name = "ib.post_recv" }; + /* Disambiguate the various possible EINPROGRESSes */ #define EINPROGRESS_INIT __einfo_error ( EINFO_EINPROGRESS_INIT ) #define EINFO_EINPROGRESS_INIT __einfo_uniqify \ @@ -397,6 +406,9 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_address_vector dest_copy; int rc; + /* Start profiling */ + profile_start ( &ib_post_send_profiler ); + /* Check queue fill level */ if ( qp->send.fill >= qp->send.num_wqes ) { DBGC ( ibdev, "IBDEV %p QPN %#lx send queue full\n", @@ -425,7 +437,12 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, return rc; } + /* Increase fill level */ qp->send.fill++; + + /* Stop profiling */ + profile_stop ( &ib_post_send_profiler ); + return 0; } @@ -441,6 +458,9 @@ int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct io_buffer *iobuf ) { int rc; + /* Start profiling */ + profile_start ( &ib_post_recv_profiler ); + /* Check packet length */ if ( iob_tailroom ( iobuf ) < IB_MAX_PAYLOAD_SIZE ) { DBGC ( ibdev, "IBDEV %p QPN %#lx wrong RX buffer size (%zd)\n", @@ -462,7 +482,12 @@ int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, return rc; } + /* Increase fill level */ qp->recv.fill++; + + /* Stop profiling */ + profile_stop ( &ib_post_recv_profiler ); + return 0; } From 07e14bfb8ae3172182b66d6a04f91a3c9d56af68 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 11 Jan 2016 16:20:05 +0000 Subject: [PATCH 033/591] [pxe] Colourise debug output Signed-off-by: Michael Brown --- src/arch/i386/interface/pxe/pxe_call.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 0b67880c7..7da4d7605 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -307,8 +307,8 @@ int pxe_deactivate ( void ) { if ( ( rc = unhook_bios_interrupt ( 0x1a, (unsigned int) pxe_int_1a, &pxe_int_1a_vector ))!= 0){ - DBG ( "Could not unhook INT 1A: %s\n", - strerror ( rc ) ); + DBGC ( &pxe_netdev, "PXE could not unhook INT 1A: %s\n", + strerror ( rc ) ); return rc; } devices_put(); @@ -334,7 +334,7 @@ int pxe_start_nbp ( void ) { /* Allow restarting NBP via PXENV_RESTART_TFTP */ jmp = rmsetjmp ( pxe_restart_nbp ); if ( jmp ) - DBG ( "Restarting NBP (%x)\n", jmp ); + DBGC ( &pxe_netdev, "PXE NBP restarting (%x)\n", jmp ); /* Far call to PXE NBP */ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ From 7d48affec200bc1d74f42813eca0c31d548d1167 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 11 Jan 2016 16:21:08 +0000 Subject: [PATCH 034/591] [pxe] Add debug message to display real-mode segment addresses Signed-off-by: Michael Brown --- src/arch/i386/interface/pxe/pxe_call.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 7da4d7605..ed17a96a1 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -54,6 +54,12 @@ extern void pxe_int_1a ( void ); /** INT 1A hooked flag */ static int int_1a_hooked = 0; +/** Real-mode code segment size */ +extern char _text16_memsz[]; + +/** Real-mode data segment size */ +extern char _data16_memsz[]; + /** PXENV_UNDI_TRANSMIT API call profiler */ static struct profiler pxe_api_tx_profiler __profiler = { .name = "pxeapi.tx" }; @@ -331,6 +337,11 @@ int pxe_start_nbp ( void ) { int discard_b, discard_c, discard_d, discard_D; uint16_t status; + DBGC ( &pxe_netdev, "PXE NBP starting with netdev %s, code %04x:%04x, " + "data %04x:%04x\n", ( pxe_netdev ? pxe_netdev->name : "" ), + rm_cs, ( ( unsigned int ) _text16_memsz ), + rm_ds, ( ( unsigned int ) _data16_memsz ) ); + /* Allow restarting NBP via PXENV_RESTART_TFTP */ jmp = rmsetjmp ( pxe_restart_nbp ); if ( jmp ) From 83c8f2e8e352e676d9951dda58b74e587101c0b4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 12 Jan 2016 08:27:59 +0000 Subject: [PATCH 035/591] [i386] Add check_bios_interrupts() debug function Provide a debug function check_bios_interrupts() to look for changes to the interrupt vector table. This can be useful when investigating the behaviour (including crashes) of external PXE NBPs. Signed-off-by: Michael Brown --- src/arch/i386/include/biosint.h | 1 + src/arch/i386/interface/pcbios/biosint.c | 27 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/arch/i386/include/biosint.h b/src/arch/i386/include/biosint.h index 67d6a3811..f47116f70 100644 --- a/src/arch/i386/include/biosint.h +++ b/src/arch/i386/include/biosint.h @@ -29,5 +29,6 @@ extern void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler, extern int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler, struct segoff *chain_vector ); +extern void check_bios_interrupts ( void ); #endif /* BIOSINT_H */ diff --git a/src/arch/i386/interface/pcbios/biosint.c b/src/arch/i386/interface/pcbios/biosint.c index 3b8e80438..667e9ed81 100644 --- a/src/arch/i386/interface/pcbios/biosint.c +++ b/src/arch/i386/interface/pcbios/biosint.c @@ -90,3 +90,30 @@ int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler, hooked_bios_interrupts--; return 0; } + +/** + * Dump changes to interrupt vector table (for debugging) + * + */ +void check_bios_interrupts ( void ) { + static struct segoff vectors[256]; + static uint8_t initialised; + struct segoff vector; + unsigned int i; + + /* Print any changed interrupt vectors */ + for ( i = 0; i < ( sizeof ( vectors ) / sizeof ( vectors[0] ) ); i++ ) { + copy_from_real ( &vector, 0, ( i * sizeof ( vector ) ), + sizeof ( vector ) ); + if ( memcmp ( &vector, &vectors[i], sizeof ( vector ) ) == 0 ) + continue; + if ( initialised ) { + dbg_printf ( "INT %02x changed %04x:%04x => " + "%04x:%04x\n", i, vectors[i].segment, + vectors[i].offset, vector.segment, + vector.offset ); + } + memcpy ( &vectors[i], &vector, sizeof ( vectors[i] ) ); + } + initialised = 1; +} From 47dcd392dcd79d02eec2714696012785dd7a47c9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 12 Jan 2016 15:35:06 +0000 Subject: [PATCH 036/591] [debug] Allow debug colourisation to be disabled Some BIOS console redirection capabilities do not work well with the colourised debug messages used by iPXE. We already allow the range of colours to be controlled via the DBGCOL=... build parameter. Extend this syntax to allow DBGCOL=0 to be used to mean "disable colours". Requested-by: Wissam Shoukair Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 4 ++-- src/core/debug.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 70ec50c5e..0a80d2ac9 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -734,8 +734,8 @@ $(DBGCOL_LIST) : $(MAKEDEPS) VERYCLEANUP += $(DBGCOL_LIST) DBGCOL_COLOURS := $(subst -, ,$(DBGCOL)) -DBGCOL_MIN := $(word 1,$(DBGCOL_COLOURS)) -DBGCOL_MAX := $(word 2,$(DBGCOL_COLOURS)) +DBGCOL_MIN := $(firstword $(DBGCOL_COLOURS)) +DBGCOL_MAX := $(lastword $(DBGCOL_COLOURS)) debug_DEPS += $(DBGCOL_LIST) diff --git a/src/core/debug.c b/src/core/debug.c index def5d8b09..9b2a823f5 100644 --- a/src/core/debug.c +++ b/src/core/debug.c @@ -194,8 +194,12 @@ static int dbg_autocolour ( unsigned long stream ) { * @v stream Message stream ID */ void dbg_autocolourise ( unsigned long stream ) { - dbg_printf ( "\033[%dm", - ( stream ? ( DBGCOL_MIN + dbg_autocolour ( stream ) ) :0)); + + if ( DBGCOL_MIN ) { + dbg_printf ( "\033[%dm", + ( stream ? + ( DBGCOL_MIN + dbg_autocolour ( stream ) ) : 0)); + } } /** @@ -203,5 +207,7 @@ void dbg_autocolourise ( unsigned long stream ) { * */ void dbg_decolourise ( void ) { - dbg_printf ( "\033[0m" ); + + if ( DBGCOL_MIN ) + dbg_printf ( "\033[0m" ); } From 8af8886d0aa50fd4e34eb338321b3051257062fe Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 14 Jan 2016 12:39:01 +0000 Subject: [PATCH 037/591] [stp] Fix incorrectly disambiguated errors The three nominally-disambiguated ENOTSUP errors accidentally all used the same error disambiguator, rendering them identical. Fix by changing all three values. We avoid reusing the 0x01 disambiguator value, since that remains ambiguous in older binaries. Signed-off-by: Michael Brown --- src/net/stp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/net/stp.c b/src/net/stp.c index d4e65a1a2..defdaed9e 100644 --- a/src/net/stp.c +++ b/src/net/stp.c @@ -40,15 +40,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Disambiguate the various error causes */ #define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL ) #define EINFO_ENOTSUP_PROTOCOL \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x02, \ "Non-STP packet received" ) #define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION ) #define EINFO_ENOTSUP_VERSION \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x03, \ "Legacy STP packet received" ) #define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE ) #define EINFO_ENOTSUP_TYPE \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x04, \ "Non-RSTP packet received" ) /** From cc252605ce9e0e224fbc34f946b80e90d012dc42 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 18 Jan 2016 08:37:27 +0000 Subject: [PATCH 038/591] [build] Add named configuration for public cloud environments Add a named CONFIG=cloud configuration, which enables console types useful for obtaining output from virtual machines in public clouds such as AWS EC2. An image suitable for use in AWS EC2 can be built using make bin/ipxe.usb CONFIG=cloud EMBED=config/cloud/aws.ipxe The embedded script will direct iPXE to download and execute the EC2 "user-data" file, which is always available to an EC2 VM via the URI http://169.254.169.254/latest/user-data (regardless of the VPC networking settings). The boot can therefore be controlled by modifying the per-instance user data, without having to modify the boot disk image. Console output can be obtained via syslog (with a syslog server configured in the user-data script), via the AWS "System Log" (after the instance has been stopped), or as a last resort from the log partition on the boot disk. Signed-off-by: Michael Brown --- src/config/cloud/aws.ipxe | 7 +++++++ src/config/cloud/colour.h | 0 src/config/cloud/console.h | 31 +++++++++++++++++++++++++++++++ src/config/cloud/crypto.h | 0 src/config/cloud/general.h | 0 src/config/cloud/serial.h | 0 src/config/cloud/settings.h | 0 src/config/cloud/sideband.h | 0 src/config/cloud/usb.h | 0 9 files changed, 38 insertions(+) create mode 100644 src/config/cloud/aws.ipxe create mode 100644 src/config/cloud/colour.h create mode 100644 src/config/cloud/console.h create mode 100644 src/config/cloud/crypto.h create mode 100644 src/config/cloud/general.h create mode 100644 src/config/cloud/serial.h create mode 100644 src/config/cloud/settings.h create mode 100644 src/config/cloud/sideband.h create mode 100644 src/config/cloud/usb.h diff --git a/src/config/cloud/aws.ipxe b/src/config/cloud/aws.ipxe new file mode 100644 index 000000000..d857d71df --- /dev/null +++ b/src/config/cloud/aws.ipxe @@ -0,0 +1,7 @@ +#!ipxe + +echo Amazon EC2 - iPXE boot via user-data +ifstat || +dhcp || +route || +chain -ar http://169.254.169.254/latest/user-data diff --git a/src/config/cloud/colour.h b/src/config/cloud/colour.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/console.h b/src/config/cloud/console.h new file mode 100644 index 000000000..dae18e556 --- /dev/null +++ b/src/config/cloud/console.h @@ -0,0 +1,31 @@ +/* + * Console configuration suitable for use in public cloud + * environments, or any environment where direct console access is not + * available. + * + */ + +/* Log to syslog(s) server + * + * The syslog server to be used must be specified via e.g. + * "set syslog 192.168.0.1". + */ +#define CONSOLE_SYSLOG +#define CONSOLE_SYSLOGS + +/* Log to serial port + * + * Note that the serial port output from an AWS EC2 virtual machine is + * generally available (as the "System Log") only after the instance + * has been stopped. + */ +#define CONSOLE_SERIAL + +/* Log to partition on local disk + * + * If all other log mechanisms fail then the VM boot disk containing + * the iPXE image can be detached and attached to another machine in + * the same cloud, allowing the log to be retrieved from the log + * partition. + */ +#define CONSOLE_INT13 diff --git a/src/config/cloud/crypto.h b/src/config/cloud/crypto.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/general.h b/src/config/cloud/general.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/serial.h b/src/config/cloud/serial.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/settings.h b/src/config/cloud/settings.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/sideband.h b/src/config/cloud/sideband.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/usb.h b/src/config/cloud/usb.h new file mode 100644 index 000000000..e69de29bb From 3fd81799ba3857fb45443bf085cd402656a90b2f Mon Sep 17 00:00:00 2001 From: Andrew Widdersheim Date: Sat, 16 Jan 2016 11:21:34 -0500 Subject: [PATCH 039/591] [netdevice] Add "ifname" setting Expose the network interface name (e.g. "net0") as a setting. This allows a script to obtain the name of the most recently opened network interface via ${netX/ifname}. Signed-off-by: Andrew Widdersheim Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/net/netdev_settings.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index edd4c4b9f..c4fd36941 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -65,6 +65,11 @@ const struct setting chip_setting __setting ( SETTING_NETDEV, chip ) = { .description = "Chip", .type = &setting_type_string, }; +const struct setting ifname_setting __setting ( SETTING_NETDEV, ifname ) = { + .name = "ifname", + .description = "Interface name", + .type = &setting_type_string, +}; /** * Store MAC address setting @@ -199,6 +204,22 @@ static int netdev_fetch_chip ( struct net_device *netdev, void *data, return strlen ( chip ); } +/** + * Fetch ifname setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_ifname ( struct net_device *netdev, void *data, + size_t len ) { + const char *ifname = netdev->name; + + strncpy ( data, ifname, len ); + return strlen ( ifname ); +} + /** A network device setting operation */ struct netdev_setting_operation { /** Setting */ @@ -229,6 +250,7 @@ static struct netdev_setting_operation netdev_setting_operations[] = { { &busloc_setting, NULL, netdev_fetch_busloc }, { &busid_setting, NULL, netdev_fetch_busid }, { &chip_setting, NULL, netdev_fetch_chip }, + { &ifname_setting, NULL, netdev_fetch_ifname }, }; /** From 8dc23d9b83027ea93e5e8689316f8448ff964647 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 13 Jan 2016 07:25:05 +0000 Subject: [PATCH 040/591] [smsc95xx] Enable LEDs The LED pins are configured by default as GPIO inputs. While it is conceivable that a board might actually use these pins as GPIOs, no such board is known to exist. The Linux smsc95xx driver configures these pins unconditionally as LED outputs. Assume that it is safe to do likewise. Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 12 ++++++++++++ src/drivers/net/smsc95xx.h | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index c1dd08051..21e89a44d 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -687,6 +687,7 @@ static int smsc95xx_dump_statistics ( struct smsc95xx_device *smsc95xx ) { */ static int smsc95xx_reset ( struct smsc95xx_device *smsc95xx ) { uint32_t hw_cfg; + uint32_t led_gpio_cfg; int rc; /* Reset device */ @@ -706,6 +707,17 @@ static int smsc95xx_reset ( struct smsc95xx_device *smsc95xx ) { return -ETIMEDOUT; } + /* Configure LEDs */ + led_gpio_cfg = ( SMSC95XX_LED_GPIO_CFG_GPCTL2_NSPD_LED | + SMSC95XX_LED_GPIO_CFG_GPCTL1_NLNKA_LED | + SMSC95XX_LED_GPIO_CFG_GPCTL0_NFDX_LED ); + if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_LED_GPIO_CFG, + led_gpio_cfg ) ) != 0 ) { + DBGC ( smsc95xx, "SMSC95XX %p could not configure LEDs: %s\n", + smsc95xx, strerror ( rc ) ); + /* Ignore error and continue */ + } + return 0; } diff --git a/src/drivers/net/smsc95xx.h b/src/drivers/net/smsc95xx.h index d66d86803..c2512e0ee 100644 --- a/src/drivers/net/smsc95xx.h +++ b/src/drivers/net/smsc95xx.h @@ -43,6 +43,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC95XX_HW_CFG_BIR 0x00001000UL /**< Bulk IN use NAK */ #define SMSC95XX_HW_CFG_LRST 0x00000008UL /**< Soft lite reset */ +/** LED GPIO configuration register */ +#define SMSC95XX_LED_GPIO_CFG 0x024 +#define SMSC95XX_LED_GPIO_CFG_GPCTL2(x) ( (x) << 24 ) /**< GPIO 2 control */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL2_NSPD_LED \ + SMSC95XX_LED_GPIO_CFG_GPCTL2 ( 1 ) /**< Link speed LED */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL1(x) ( (x) << 20 ) /**< GPIO 1 control */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL1_NLNKA_LED \ + SMSC95XX_LED_GPIO_CFG_GPCTL1 ( 1 ) /**< Activity LED */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL0(x) ( (x) << 16 ) /**< GPIO 0 control */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL0_NFDX_LED \ + SMSC95XX_LED_GPIO_CFG_GPCTL0 ( 1 ) /**< Full-duplex LED */ + /** EEPROM command register */ #define SMSC95XX_E2P_CMD 0x030 #define SMSC95XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ From 71b83a6d00caedb62cc62a5810f99a7a1478f2ae Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jan 2016 00:01:11 +0000 Subject: [PATCH 041/591] [usb] Allow USB endpoints to specify a reserved header length for refills Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 8 ++++++-- src/drivers/net/acm.c | 4 ++-- src/drivers/net/dm96xx.c | 4 ++-- src/drivers/net/ecm.c | 4 ++-- src/drivers/net/ncm.c | 4 ++-- src/drivers/net/smsc75xx.c | 5 +++-- src/drivers/net/smsc95xx.c | 5 +++-- src/drivers/usb/usbhub.c | 2 +- src/drivers/usb/usbkbd.c | 2 +- src/include/ipxe/usb.h | 11 ++++++++--- src/interface/efi/efi_usb.c | 2 +- 11 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index b1fa4efb5..880e3f08c 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -601,6 +601,7 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, */ int usb_prefill ( struct usb_endpoint *ep ) { struct io_buffer *iobuf; + size_t reserve = ep->reserve; size_t len = ( ep->len ? ep->len : ep->mtu ); unsigned int fill; int rc; @@ -614,11 +615,12 @@ int usb_prefill ( struct usb_endpoint *ep ) { for ( fill = 0 ; fill < ep->max ; fill++ ) { /* Allocate I/O buffer */ - iobuf = alloc_iob ( len ); + iobuf = alloc_iob ( reserve + len ); if ( ! iobuf ) { rc = -ENOMEM; goto err_alloc; } + iob_reserve ( iobuf, reserve ); /* Add to recycled buffer list */ list_add_tail ( &iobuf->list, &ep->recycled ); @@ -639,6 +641,7 @@ int usb_prefill ( struct usb_endpoint *ep ) { */ int usb_refill ( struct usb_endpoint *ep ) { struct io_buffer *iobuf; + size_t reserve = ep->reserve; size_t len = ( ep->len ? ep->len : ep->mtu ); int rc; @@ -652,9 +655,10 @@ int usb_refill ( struct usb_endpoint *ep ) { /* Get or allocate buffer */ if ( list_empty ( &ep->recycled ) ) { /* Recycled buffer list is empty; allocate new buffer */ - iobuf = alloc_iob ( len ); + iobuf = alloc_iob ( reserve + len ); if ( ! iobuf ) return -ENOMEM; + iob_reserve ( iobuf, reserve ); } else { /* Get buffer from recycled buffer list */ iobuf = list_first_entry ( &ep->recycled, diff --git a/src/drivers/net/acm.c b/src/drivers/net/acm.c index 955ad4ab4..16dab4be8 100644 --- a/src/drivers/net/acm.c +++ b/src/drivers/net/acm.c @@ -447,8 +447,8 @@ static int acm_probe ( struct usb_function *func, acm->rndis = rndis; usbnet_init ( &acm->usbnet, func, &acm_intr_operations, &acm_in_operations, &acm_out_operations ); - usb_refill_init ( &acm->usbnet.intr, 0, ACM_INTR_MAX_FILL ); - usb_refill_init ( &acm->usbnet.in, ACM_IN_MTU, ACM_IN_MAX_FILL ); + usb_refill_init ( &acm->usbnet.intr, 0, 0, ACM_INTR_MAX_FILL ); + usb_refill_init ( &acm->usbnet.in, 0, ACM_IN_MTU, ACM_IN_MAX_FILL ); /* Describe USB network device */ if ( ( rc = usbnet_describe ( &acm->usbnet, config ) ) != 0 ) { diff --git a/src/drivers/net/dm96xx.c b/src/drivers/net/dm96xx.c index 817a84a29..61b957be9 100644 --- a/src/drivers/net/dm96xx.c +++ b/src/drivers/net/dm96xx.c @@ -532,8 +532,8 @@ static int dm96xx_probe ( struct usb_function *func, dm96xx->netdev = netdev; usbnet_init ( &dm96xx->usbnet, func, &dm96xx_intr_operations, &dm96xx_in_operations, &dm96xx_out_operations ); - usb_refill_init ( &dm96xx->usbnet.intr, 0, DM96XX_INTR_MAX_FILL ); - usb_refill_init ( &dm96xx->usbnet.in, DM96XX_IN_MTU, + usb_refill_init ( &dm96xx->usbnet.intr, 0, 0, DM96XX_INTR_MAX_FILL ); + usb_refill_init ( &dm96xx->usbnet.in, 0, DM96XX_IN_MTU, DM96XX_IN_MAX_FILL ); DBGC ( dm96xx, "DM96XX %p on %s\n", dm96xx, func->name ); diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 371611d54..f2d9161c1 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -437,8 +437,8 @@ static int ecm_probe ( struct usb_function *func, ecm->netdev = netdev; usbnet_init ( &ecm->usbnet, func, &ecm_intr_operations, &ecm_in_operations, &ecm_out_operations ); - usb_refill_init ( &ecm->usbnet.intr, 0, ECM_INTR_MAX_FILL ); - usb_refill_init ( &ecm->usbnet.in, ECM_IN_MTU, ECM_IN_MAX_FILL ); + usb_refill_init ( &ecm->usbnet.intr, 0, 0, ECM_INTR_MAX_FILL ); + usb_refill_init ( &ecm->usbnet.in, 0, ECM_IN_MTU, ECM_IN_MAX_FILL ); DBGC ( ecm, "ECM %p on %s\n", ecm, func->name ); /* Describe USB network device */ diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c index fed77a00f..1837291f7 100644 --- a/src/drivers/net/ncm.c +++ b/src/drivers/net/ncm.c @@ -186,7 +186,7 @@ static int ncm_in_prefill ( struct ncm_device *ncm ) { count = NCM_IN_MIN_COUNT; if ( ( count * mtu ) > NCM_IN_MAX_SIZE ) continue; - usb_refill_init ( &ncm->usbnet.in, mtu, count ); + usb_refill_init ( &ncm->usbnet.in, 0, mtu, count ); if ( ( rc = usb_prefill ( &ncm->usbnet.in ) ) != 0 ) { DBGC ( ncm, "NCM %p could not prefill %dx %zd-byte " "buffers for bulk IN\n", ncm, count, mtu ); @@ -575,7 +575,7 @@ static int ncm_probe ( struct usb_function *func, ncm->netdev = netdev; usbnet_init ( &ncm->usbnet, func, &ncm_intr_operations, &ncm_in_operations, &ncm_out_operations ); - usb_refill_init ( &ncm->usbnet.intr, 0, NCM_INTR_COUNT ); + usb_refill_init ( &ncm->usbnet.intr, 0, 0, NCM_INTR_COUNT ); DBGC ( ncm, "NCM %p on %s\n", ncm, func->name ); /* Describe USB network device */ diff --git a/src/drivers/net/smsc75xx.c b/src/drivers/net/smsc75xx.c index 5e4e0e12b..9a9634600 100644 --- a/src/drivers/net/smsc75xx.c +++ b/src/drivers/net/smsc75xx.c @@ -979,8 +979,9 @@ static int smsc75xx_probe ( struct usb_function *func, smsc75xx->netdev = netdev; usbnet_init ( &smsc75xx->usbnet, func, &smsc75xx_intr_operations, &smsc75xx_in_operations, &smsc75xx_out_operations ); - usb_refill_init ( &smsc75xx->usbnet.intr, 0, SMSC75XX_INTR_MAX_FILL ); - usb_refill_init ( &smsc75xx->usbnet.in, SMSC75XX_IN_MTU, + usb_refill_init ( &smsc75xx->usbnet.intr, 0, 0, + SMSC75XX_INTR_MAX_FILL ); + usb_refill_init ( &smsc75xx->usbnet.in, 0, SMSC75XX_IN_MTU, SMSC75XX_IN_MAX_FILL ); mii_init ( &smsc75xx->mii, &smsc75xx_mii_operations ); DBGC ( smsc75xx, "SMSC75XX %p on %s\n", smsc75xx, func->name ); diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index 21e89a44d..1e237d01a 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -1140,8 +1140,9 @@ static int smsc95xx_probe ( struct usb_function *func, smsc95xx->netdev = netdev; usbnet_init ( &smsc95xx->usbnet, func, &smsc95xx_intr_operations, &smsc95xx_in_operations, &smsc95xx_out_operations ); - usb_refill_init ( &smsc95xx->usbnet.intr, 0, SMSC95XX_INTR_MAX_FILL ); - usb_refill_init ( &smsc95xx->usbnet.in, SMSC95XX_IN_MTU, + usb_refill_init ( &smsc95xx->usbnet.intr, 0, 0, + SMSC95XX_INTR_MAX_FILL ); + usb_refill_init ( &smsc95xx->usbnet.in, 0, SMSC95XX_IN_MTU, SMSC95XX_IN_MAX_FILL ); mii_init ( &smsc95xx->mii, &smsc95xx_mii_operations ); DBGC ( smsc95xx, "SMSC95XX %p on %s\n", smsc95xx, func->name ); diff --git a/src/drivers/usb/usbhub.c b/src/drivers/usb/usbhub.c index 7095fc31b..47914bcdb 100644 --- a/src/drivers/usb/usbhub.c +++ b/src/drivers/usb/usbhub.c @@ -416,7 +416,7 @@ static int hub_probe ( struct usb_function *func, ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES ); hubdev->flags = func->id->driver_data; usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations ); - usb_refill_init ( &hubdev->intr, 0, USB_HUB_INTR_FILL ); + usb_refill_init ( &hubdev->intr, 0, 0, USB_HUB_INTR_FILL ); process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL ); /* Locate hub interface descriptor */ diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 76db5771f..a8ab6ab76 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -425,7 +425,7 @@ static int usbkbd_probe ( struct usb_function *func, kbd->name = func->name; kbd->bus = usb->port->hub->bus; usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL ); - usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ), + usb_refill_init ( &kbd->hid.in, 0, sizeof ( kbd->report ), USBKBD_INTR_MAX_FILL ); /* Describe USB human interface device */ diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index 37b6d94ed..e7909d300 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -414,7 +414,9 @@ struct usb_endpoint { /** Recycled I/O buffer list */ struct list_head recycled; - /** Refill buffer length */ + /** Refill buffer reserved header length */ + size_t reserve; + /** Refill buffer payload length */ size_t len; /** Maximum fill level */ unsigned int max; @@ -588,13 +590,16 @@ extern void usb_complete_err ( struct usb_endpoint *ep, * Initialise USB endpoint refill * * @v ep USB endpoint - * @v len Refill buffer length (or zero to use endpoint's MTU) + * @v reserve Refill buffer reserved header length + * @v len Refill buffer payload length (zero for endpoint's MTU) * @v max Maximum fill level */ static inline __attribute__ (( always_inline )) void -usb_refill_init ( struct usb_endpoint *ep, size_t len, unsigned int max ) { +usb_refill_init ( struct usb_endpoint *ep, size_t reserve, size_t len, + unsigned int max ) { INIT_LIST_HEAD ( &ep->recycled ); + ep->reserve = reserve; ep->len = len; ep->max = max; } diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index 94f216104..db8c3d348 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -472,7 +472,7 @@ static int efi_usb_async_start ( struct efi_usb_interface *usbintf, usbep->context = context; /* Prefill endpoint */ - usb_refill_init ( &usbep->ep, len, EFI_USB_ASYNC_FILL ); + usb_refill_init ( &usbep->ep, 0, len, EFI_USB_ASYNC_FILL ); if ( ( rc = usb_prefill ( &usbep->ep ) ) != 0 ) { DBGC ( usbdev, "USBDEV %s %s could not prefill: %s\n", usbintf->name, usb_endpoint_name ( &usbep->ep ), From 207edc46158a91128c28de43c47cb4c1ac98e207 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jan 2016 00:04:33 +0000 Subject: [PATCH 042/591] [smsc95xx] Reserve headroom in received packets Some protocols (such as ARP) may modify the received packet and re-use the same I/O buffer for transmission of a reply. The SMSC95XX transmit header is larger than the receive header: the re-used I/O buffer therefore does not have sufficient headroom for the transmit header, and the ARP reply will therefore fail to be transmitted. This is essentially the same problem as in commit 2e72d10 ("[ncm] Reserve headroom in received packets"). Fix by reserving sufficient space at the start of each received packet to allow for the difference between the lengths of the transmit and receive headers. This problem is not caught by the current driver development test suite (documented at http://ipxe.org/dev/driver), since even the large file transfer tests tend to completely sufficiently quickly that there is no need for the server to ever send an ARP request. The failure shows up only when using a very slow protocol such as RFC7440-enhanced TFTP (as used by Windows Deployment Services). Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index 1e237d01a..3d9c0f1aa 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -1142,8 +1142,10 @@ static int smsc95xx_probe ( struct usb_function *func, &smsc95xx_in_operations, &smsc95xx_out_operations ); usb_refill_init ( &smsc95xx->usbnet.intr, 0, 0, SMSC95XX_INTR_MAX_FILL ); - usb_refill_init ( &smsc95xx->usbnet.in, 0, SMSC95XX_IN_MTU, - SMSC95XX_IN_MAX_FILL ); + usb_refill_init ( &smsc95xx->usbnet.in, + ( sizeof ( struct smsc95xx_tx_header ) - + sizeof ( struct smsc95xx_rx_header ) ), + SMSC95XX_IN_MTU, SMSC95XX_IN_MAX_FILL ); mii_init ( &smsc95xx->mii, &smsc95xx_mii_operations ); DBGC ( smsc95xx, "SMSC95XX %p on %s\n", smsc95xx, func->name ); From 3c26ffafceef176286c108fe2b01ccebd83062a0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jan 2016 08:16:17 +0000 Subject: [PATCH 043/591] [autoboot] Fix incorrect boolean logic Commit 53d2d9e ("[uri] Generalise tftp_uri() to pxe_uri()") introduced a regression in which an NFS root path would no longer be treated as an unsupported root path, causing a boot with an NFS root path to fail with a "Could not open SAN device" error. Reported-by: David Evans Signed-off-by: Michael Brown --- src/usr/autoboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 8c6b6904e..e93b0150d 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -379,8 +379,8 @@ int netboot ( struct net_device *netdev ) { * it may represent an NFS root. */ if ( filename && root_path && - ( ! ( uri_is_absolute ( root_path ) || - ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) ) ) { + ( ( ! uri_is_absolute ( root_path ) ) || + ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) ) { printf ( "Ignoring unsupported root path\n" ); uri_put ( root_path ); root_path = NULL; From 295ad113675dbc2cd595d783ec7b25b93b2ea465 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Jan 2016 15:53:44 +0000 Subject: [PATCH 044/591] [uri] Avoid potentially large stack allocation Avoid potentially large stack allocation in resolve_path(). Signed-off-by: Michael Brown --- src/core/uri.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index 4ae346856..5bb721ff1 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -606,7 +606,7 @@ struct uri * uri_dup ( const struct uri *uri ) { * * @v base_uri Base path * @v relative_uri Relative path - * @ret resolved_uri Resolved path + * @ret resolved_uri Resolved path, or NULL on failure * * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative * path (e.g. "initrd.gz") and produces a new path @@ -617,9 +617,8 @@ struct uri * uri_dup ( const struct uri *uri ) { */ char * resolve_path ( const char *base_path, const char *relative_path ) { - size_t base_len = ( strlen ( base_path ) + 1 ); - char base_path_copy[base_len]; - char *base_tmp = base_path_copy; + char *base_copy; + char *base_tmp; char *resolved; /* If relative path is absolute, just re-use it */ @@ -627,8 +626,12 @@ char * resolve_path ( const char *base_path, return strdup ( relative_path ); /* Create modifiable copy of path for dirname() */ - memcpy ( base_tmp, base_path, base_len ); - base_tmp = dirname ( base_tmp ); + base_copy = strdup ( base_path ); + if ( ! base_copy ) + return NULL; + + /* Strip filename portion of base path */ + base_tmp = dirname ( base_copy ); /* Process "./" and "../" elements */ while ( *relative_path == '.' ) { @@ -658,8 +661,8 @@ char * resolve_path ( const char *base_path, if ( asprintf ( &resolved, "%s%s%s", base_tmp, ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ? "" : "/" ), relative_path ) < 0 ) - return NULL; - + resolved = NULL; + free ( base_copy ); return resolved; } @@ -668,7 +671,7 @@ char * resolve_path ( const char *base_path, * * @v base_uri Base URI, or NULL * @v relative_uri Relative URI - * @ret resolved_uri Resolved URI + * @ret resolved_uri Resolved URI, or NULL on failure * * Takes a base URI (e.g. "http://ipxe.org/kernels/vmlinuz" and a * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI From 42c2a6aab7727e7359600515471f463c65315ff0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Jan 2016 17:50:34 +0000 Subject: [PATCH 045/591] [ocsp] Avoid including a double path separator in request URI The OCSP responder URI included within an X.509 certificate may or may not include a trailing slash. We currently rely on the fact that format_uri() incorrectly inserts an initial slash, which we include unconditionally within the OCSP request URI. Switch to using uri_encode() directly, and insert a slash only if the X.509 certificate's OCSP responder URI does not already include a trailing slash. Signed-off-by: Michael Brown --- src/crypto/ocsp.c | 54 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index 5df55bc96..e7adcdba9 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -209,10 +209,10 @@ static int ocsp_request ( struct ocsp_check *ocsp ) { static int ocsp_uri_string ( struct ocsp_check *ocsp ) { struct x509_ocsp_responder *responder = &ocsp->cert->extensions.auth_info.ocsp; - struct uri path_uri; - char *path_base64_string; - char *path_uri_string; - size_t path_len; + char *base64; + char *sep; + size_t base64_len; + size_t uri_len; size_t len; int rc; @@ -224,46 +224,44 @@ static int ocsp_uri_string ( struct ocsp_check *ocsp ) { goto err_no_uri; } - /* Base64-encode the request as the URI path */ - path_len = ( base64_encoded_len ( ocsp->request.builder.len ) - + 1 /* NUL */ ); - path_base64_string = malloc ( path_len ); - if ( ! path_base64_string ) { + /* Calculate base64-encoded request length */ + base64_len = ( base64_encoded_len ( ocsp->request.builder.len ) + + 1 /* NUL */ ); + + /* Allocate and construct the base64-encoded request */ + base64 = malloc ( base64_len ); + if ( ! base64 ) { rc = -ENOMEM; - goto err_path_base64; + goto err_alloc_base64; } base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len, - path_base64_string, path_len ); + base64, base64_len ); - /* URI-encode the Base64-encoded request */ - memset ( &path_uri, 0, sizeof ( path_uri ) ); - path_uri.path = path_base64_string; - path_uri_string = format_uri_alloc ( &path_uri ); - if ( ! path_uri_string ) { - rc = -ENOMEM; - goto err_path_uri; - } + /* Calculate URI-encoded base64-encoded request length */ + uri_len = ( uri_encode ( URI_PATH, base64, ( base64_len - 1 /* NUL */ ), + NULL, 0 ) + 1 /* NUL */ ); - /* Construct URI string */ - len = ( responder->uri.len + strlen ( path_uri_string ) + 1 /* NUL */ ); + /* Allocate and construct the URI string */ + len = ( responder->uri.len + 1 /* possible "/" */ + uri_len ); ocsp->uri_string = zalloc ( len ); if ( ! ocsp->uri_string ) { rc = -ENOMEM; - goto err_ocsp_uri; + goto err_alloc_uri; } memcpy ( ocsp->uri_string, responder->uri.data, responder->uri.len ); - strcpy ( &ocsp->uri_string[responder->uri.len], path_uri_string ); + sep = &ocsp->uri_string[ responder->uri.len - 1 ]; + if ( *sep != '/' ) + *(++sep) = '/'; + uri_encode ( URI_PATH, base64, base64_len, ( sep + 1 ), uri_len ); DBGC2 ( ocsp, "OCSP %p \"%s\" URI is %s\n", ocsp, x509_name ( ocsp->cert ), ocsp->uri_string ); /* Success */ rc = 0; - err_ocsp_uri: - free ( path_uri_string ); - err_path_uri: - free ( path_base64_string ); - err_path_base64: + err_alloc_uri: + free ( base64 ); + err_alloc_base64: err_no_uri: return rc; } From f0e9e55442023c2f18e62cf74fe9098e0a6f5347 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Jan 2016 16:24:16 +0000 Subject: [PATCH 046/591] [tftp] Mangle initial slash on TFTP URIs TFTP URIs are intrinsically problematic, since: - TFTP servers may use either normal slashes or backslashes as a directory separator, - TFTP servers allow filenames to be specified using relative paths (with no initial directory separator), - TFTP filenames present in a DHCP filename field may use special characters such as "?" or "#" that prevent parsing as a generic URI. As of commit 7667536 ("[uri] Refactor URI parsing and formatting"), we have directly constructed TFTP URIs from DHCP next-server and filename pairs, avoiding the generic URI parser. This eliminated the problems related to special characters, but indirectly made it impossible to parse a "tftp://..." URI string into a TFTP URI with a non-absolute path. Re-introduce the convention of requiring an extra slash in a "tftp://..." URI string in order to specify a TFTP URI with an initial slash in the filename. For example: tftp://192.168.0.1/boot/pxelinux.0 => RRQ "boot/pxelinux.0" tftp://192.168.0.1//boot/pxelinux.0 => RRQ "/boot/pxelinux.0" This is ugly, but there seems to be no other sensible way to provide the ability to specify all possible TFTP filenames. A side-effect of this change is that format_uri() will no longer add a spurious initial "/" when formatting a relative URI string. This improves the console output when fetching an image specified via a relative URI. Signed-off-by: Michael Brown --- src/core/uri.c | 72 +++++++++++++++++++++++++++++++------------- src/net/udp/tftp.c | 4 ++- src/tests/uri_test.c | 12 ++++---- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index 5bb721ff1..a8fdb70a7 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -368,7 +368,7 @@ struct uri * parse_uri ( const char *uri_string ) { goto done; /* Identify net/absolute/relative path */ - if ( strncmp ( path, "//", 2 ) == 0 ) { + if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) { /* Net path. If this is terminated by the first '/' * of an absolute path, then we have no space for a * terminator after the authority field, so shuffle @@ -459,7 +459,6 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { [URI_OPAQUE] = ':', [URI_PASSWORD] = ':', [URI_PORT] = ':', - [URI_PATH] = '/', [URI_QUERY] = '?', [URI_FRAGMENT] = '#', }; @@ -486,8 +485,6 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { prefix = prefixes[field]; if ( ( field == URI_HOST ) && ( uri->user != NULL ) ) prefix = '@'; - if ( ( field == URI_PATH ) && ( uri->path[0] == '/' ) ) - prefix = '\0'; if ( prefix ) { used += ssnprintf ( ( buf + used ), ( len - used ), "%c", prefix ); @@ -714,6 +711,55 @@ struct uri * resolve_uri ( const struct uri *base_uri, return new_uri; } +/** + * Construct TFTP URI from server address and filename + * + * @v sa_server Server address + * @v filename Filename + * @ret uri URI, or NULL on failure + */ +static struct uri * tftp_uri ( struct sockaddr *sa_server, + const char *filename ) { + struct sockaddr_tcpip *st_server = + ( ( struct sockaddr_tcpip * ) sa_server ); + char buf[ 6 /* "65535" + NUL */ ]; + char *path; + struct uri tmp; + struct uri *uri = NULL; + + /* Initialise TFTP URI */ + memset ( &tmp, 0, sizeof ( tmp ) ); + tmp.scheme = "tftp"; + + /* Construct TFTP server address */ + tmp.host = sock_ntoa ( sa_server ); + if ( ! tmp.host ) + goto err_host; + + /* Construct TFTP server port, if applicable */ + if ( st_server->st_port ) { + snprintf ( buf, sizeof ( buf ), "%d", + ntohs ( st_server->st_port ) ); + tmp.port = buf; + } + + /* Construct TFTP path */ + if ( asprintf ( &path, "/%s", filename ) < 0 ) + goto err_path; + tmp.path = path; + + /* Demangle URI */ + uri = uri_dup ( &tmp ); + if ( ! uri ) + goto err_uri; + + err_uri: + free ( path ); + err_path: + err_host: + return uri; +} + /** * Construct URI from server address and filename * @@ -727,10 +773,6 @@ struct uri * resolve_uri ( const struct uri *base_uri, * constructing a TFTP URI from the next-server and filename. */ struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) { - char buf[ 6 /* "65535" + NUL */ ]; - struct sockaddr_tcpip *st_server = - ( ( struct sockaddr_tcpip * ) sa_server ); - struct uri tmp; struct uri *uri; /* Fail if filename is empty */ @@ -748,17 +790,5 @@ struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) { uri_put ( uri ); /* Otherwise, construct a TFTP URI directly */ - memset ( &tmp, 0, sizeof ( tmp ) ); - tmp.scheme = "tftp"; - tmp.host = sock_ntoa ( sa_server ); - if ( ! tmp.host ) - return NULL; - if ( st_server->st_port ) { - snprintf ( buf, sizeof ( buf ), "%d", - ntohs ( st_server->st_port ) ); - tmp.port = buf; - } - tmp.path = filename; - uri = uri_dup ( &tmp ); - return uri; + return tftp_uri ( sa_server, filename ); } diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index f3cb34342..4255472ee 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -325,7 +325,7 @@ void tftp_set_mtftp_port ( unsigned int port ) { * @ret rc Return status code */ static int tftp_send_rrq ( struct tftp_request *tftp ) { - const char *path = tftp->uri->path; + const char *path = ( tftp->uri->path + 1 /* skip '/' */ ); struct tftp_rrq *rrq; size_t len; struct io_buffer *iobuf; @@ -1067,6 +1067,8 @@ static int tftp_core_open ( struct interface *xfer, struct uri *uri, return -EINVAL; if ( ! uri->path ) return -EINVAL; + if ( uri->path[0] != '/' ) + return -EINVAL; /* Allocate and populate TFTP structure */ tftp = zalloc ( sizeof ( *tftp ) ); diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 42c1c43d4..a068ab33b 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -713,9 +713,9 @@ static struct uri_pxe_test uri_pxe_absolute_path = { { .scheme = "tftp", .host = "192.168.0.2", - .path = "/absolute/path", + .path = "//absolute/path", }, - "tftp://192.168.0.2/absolute/path", + "tftp://192.168.0.2//absolute/path", }; /** PXE URI with relative path */ @@ -731,7 +731,7 @@ static struct uri_pxe_test uri_pxe_relative_path = { { .scheme = "tftp", .host = "192.168.0.3", - .path = "relative/path", + .path = "/relative/path", }, "tftp://192.168.0.3/relative/path", }; @@ -749,7 +749,7 @@ static struct uri_pxe_test uri_pxe_icky = { { .scheme = "tftp", .host = "10.0.0.6", - .path = "C:\\tftpboot\\icky#path", + .path = "/C:\\tftpboot\\icky#path", }, "tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path", }; @@ -769,9 +769,9 @@ static struct uri_pxe_test uri_pxe_port = { .scheme = "tftp", .host = "192.168.0.1", .port = "4069", - .path = "/another/path", + .path = "//another/path", }, - "tftp://192.168.0.1:4069/another/path", + "tftp://192.168.0.1:4069//another/path", }; /** Current working URI test */ From e55ec845e6ed889a43c14c72ddb9183e0b87c60b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 26 Jan 2016 16:16:13 +0000 Subject: [PATCH 047/591] [uri] Apply URI decoding for all parsed URIs The various early-exit paths in parse_uri() accidentally bypass the URI field decoding. The result is that opaque or relative URIs do not undergo URI field decoding, resulting in double-encoding when the URIs are subsequently used. For example: #!ipxe set mac ${macstring} imgfetch /boot/by-mac/${mac:uristring} would result in an HTTP GET such as GET /boot/by-mac/00%253A0c%253A29%253Ac5%253A39%253Aa1 HTTP/1.1 rather than the expected GET /boot/by-mac/00%3A0c%3A29%3Ac5%3A39%3Aa1 HTTP/1.1 Fix by ensuring that URI decoding is always applied regardless of the URI format. Reported-by: Andrew Widdersheim Signed-off-by: Michael Brown --- src/core/uri.c | 2 +- src/tests/uri_test.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/uri.c b/src/core/uri.c index a8fdb70a7..aa6eedb91 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -419,11 +419,11 @@ struct uri * parse_uri ( const char *uri_string ) { uri->port = tmp; } + done: /* Decode fields in-place */ for ( field = 0 ; field < URI_FIELDS ; field++ ) uri_decode_inplace ( uri, field ); - done: DBGC ( uri, "URI parsed \"%s\" to", uri_string ); uri_dump ( uri ); DBGC ( uri, "\n" ); diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index a068ab33b..57f211aaf 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -499,6 +499,18 @@ static struct uri_test uri_mailto = { { .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" } }; +/** Basic path-only URI */ +static struct uri_test uri_path = { + "/var/lib/tftpboot/pxelinux.0", + { .path = "/var/lib/tftpboot/pxelinux.0" }, +}; + +/** Path-only URI with escaped characters */ +static struct uri_test uri_path_escaped = { + "/hello%20world%3F", + { .path = "/hello world?" }, +}; + /** HTTP URI with all the trimmings */ static struct uri_test uri_http_all = { "http://anon:password@example.com:3001/~foo/cgi-bin/foo.pl?a=b&c=d#bit", @@ -877,6 +889,8 @@ static void uri_test_exec ( void ) { uri_parse_format_dup_ok ( &uri_empty ); uri_parse_format_dup_ok ( &uri_boot_ipxe_org ); uri_parse_format_dup_ok ( &uri_mailto ); + uri_parse_format_dup_ok ( &uri_path ); + uri_parse_format_dup_ok ( &uri_path_escaped ); uri_parse_format_dup_ok ( &uri_http_all ); uri_parse_format_dup_ok ( &uri_http_escaped ); uri_parse_ok ( &uri_http_escaped_improper ); /* Parse only */ From 6366fa7af6018b84243b21997b6aca68ad48f9b4 Mon Sep 17 00:00:00 2001 From: Hummel Frank Date: Wed, 27 Jan 2016 13:07:42 +0000 Subject: [PATCH 048/591] [intel] Add INTEL_NO_PHY_RST for I218-LM Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 840bad80c..cdfac1579 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1063,7 +1063,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), - PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ), PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), From fef8e34b6f990de651002e00b60b2a6212369fb5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 27 Jan 2016 23:06:50 +0000 Subject: [PATCH 049/591] [tcp] Guard against malformed TCP options Signed-off-by: Michael Brown --- src/include/ipxe/tcp.h | 2 -- src/net/tcp.c | 64 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h index 063ebaa4b..9d9aecd07 100644 --- a/src/include/ipxe/tcp.h +++ b/src/include/ipxe/tcp.h @@ -140,8 +140,6 @@ struct tcp_timestamp_padded_option { /** Parsed TCP options */ struct tcp_options { - /** MSS option, if present */ - const struct tcp_mss_option *mssopt; /** Window scale option, if present */ const struct tcp_window_scale_option *wsopt; /** SACK permitted option, if present */ diff --git a/src/net/tcp.c b/src/net/tcp.c index c69c83b85..68128e84a 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -904,50 +904,86 @@ static struct tcp_connection * tcp_demux ( unsigned int local_port ) { /** * Parse TCP received options * - * @v tcp TCP connection - * @v data Raw options data - * @v len Raw options length + * @v tcp TCP connection (may be NULL) + * @v tcphdr TCP header + * @v hlen TCP header length * @v options Options structure to fill in + * @ret rc Return status code */ -static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, - size_t len, struct tcp_options *options ) { - const void *end = ( data + len ); +static int tcp_rx_opts ( struct tcp_connection *tcp, + const struct tcp_header *tcphdr, size_t hlen, + struct tcp_options *options ) { + const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ); + const void *end = ( ( ( void * ) tcphdr ) + hlen ); const struct tcp_option *option; unsigned int kind; + size_t remaining; + size_t min; + /* Sanity check */ + assert ( hlen >= sizeof ( *tcphdr ) ); + + /* Parse options */ memset ( options, 0, sizeof ( *options ) ); - while ( data < end ) { + while ( ( remaining = ( end - data ) ) ) { + + /* Extract option code */ option = data; kind = option->kind; + + /* Handle single-byte options */ if ( kind == TCP_OPTION_END ) - return; + break; if ( kind == TCP_OPTION_NOP ) { data++; continue; } + + /* Handle multi-byte options */ + min = sizeof ( *option ); switch ( kind ) { case TCP_OPTION_MSS: - options->mssopt = data; + /* Ignore received MSS */ break; case TCP_OPTION_WS: options->wsopt = data; + min = sizeof ( *options->wsopt ); break; case TCP_OPTION_SACK_PERMITTED: options->spopt = data; + min = sizeof ( *options->spopt ); break; case TCP_OPTION_SACK: /* Ignore received SACKs */ break; case TCP_OPTION_TS: options->tsopt = data; + min = sizeof ( *options->tsopt ); break; default: DBGC ( tcp, "TCP %p received unknown option %d\n", tcp, kind ); break; } + if ( remaining < min ) { + DBGC ( tcp, "TCP %p received truncated option %d\n", + tcp, kind ); + return -EINVAL; + } + if ( option->length < min ) { + DBGC ( tcp, "TCP %p received underlength option %d\n", + tcp, kind ); + return -EINVAL; + } + if ( option->length > remaining ) { + DBGC ( tcp, "TCP %p received overlength option %d\n", + tcp, kind ); + return -EINVAL; + } data += option->length; } + + return 0; } /** @@ -1011,6 +1047,12 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, tcp->snd_win_scale = options->wsopt->scale; tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE; } + DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window " + "x%d, RX window x%d\n", tcp, + ( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ), + ( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ), + ( 1 << tcp->snd_win_scale ), + ( 1 << tcp->rcv_win_scale ) ); } /* Ignore duplicate SYN */ @@ -1369,8 +1411,8 @@ static int tcp_rx ( struct io_buffer *iobuf, ack = ntohl ( tcphdr->ack ); raw_win = ntohs ( tcphdr->win ); flags = tcphdr->flags; - tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ), - ( hlen - sizeof ( *tcphdr ) ), &options ); + if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 ) + goto discard; if ( tcp && options.tsopt ) tcp->ts_val = ntohl ( options.tsopt->tsval ); iob_pull ( iobuf, hlen ); From 4ddd3d99c36d1fd3e8a4b31c1814e7d82ca91081 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 27 Jan 2016 23:27:47 +0000 Subject: [PATCH 050/591] [slam] Avoid potential division by zero Signed-off-by: Michael Brown --- src/net/udp/slam.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/net/udp/slam.c b/src/net/udp/slam.c index 8b26bfb3c..8fcc97632 100644 --- a/src/net/udp/slam.c +++ b/src/net/udp/slam.c @@ -415,6 +415,8 @@ static int slam_pull_value ( struct slam_request *slam, static int slam_pull_header ( struct slam_request *slam, struct io_buffer *iobuf ) { void *header = iobuf->data; + unsigned long total_bytes; + unsigned long block_size; int rc; /* If header matches cached header, just pull it and return */ @@ -431,22 +433,26 @@ static int slam_pull_header ( struct slam_request *slam, */ if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 ) return rc; - if ( ( rc = slam_pull_value ( slam, iobuf, - &slam->total_bytes ) ) != 0 ) + if ( ( rc = slam_pull_value ( slam, iobuf, &total_bytes ) ) != 0 ) return rc; - if ( ( rc = slam_pull_value ( slam, iobuf, - &slam->block_size ) ) != 0 ) + if ( ( rc = slam_pull_value ( slam, iobuf, &block_size ) ) != 0 ) return rc; + /* Sanity check */ + if ( block_size == 0 ) { + DBGC ( slam, "SLAM %p ignoring zero block size\n", slam ); + return -EINVAL; + } + /* Update the cached header */ slam->header_len = ( iobuf->data - header ); assert ( slam->header_len <= sizeof ( slam->header ) ); memcpy ( slam->header, header, slam->header_len ); /* Calculate number of blocks */ - slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) / - slam->block_size ); - + slam->total_bytes = total_bytes; + slam->block_size = block_size; + slam->num_blocks = ( ( total_bytes + block_size - 1 ) / block_size ); DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num " "blocks %ld\n", slam, slam->total_bytes, slam->block_size, slam->num_blocks ); From d0bfd830e4e5ddd1015dda66833a99b068b6a519 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jan 2016 14:15:36 +0000 Subject: [PATCH 051/591] [ath9k] Remove broken ath_rxbuf_alloc() ath_rx_init() demonstrates some serious confusion over how to use pointers, resulting in (uint32_t*)NULL being used as a temporary variable. This does not end well. The broken code in question is performing manual alignment of I/O buffers, which can now be achieved more simply using alloc_iob_raw(). Fix by removing ath_rxbuf_alloc() entirely. Signed-off-by: Michael Brown --- src/drivers/net/ath/ath.h | 4 -- src/drivers/net/ath/ath9k/ath9k_recv.c | 12 +++--- src/drivers/net/ath/ath_main.c | 59 -------------------------- 3 files changed, 5 insertions(+), 70 deletions(-) delete mode 100644 src/drivers/net/ath/ath_main.c diff --git a/src/drivers/net/ath/ath.h b/src/drivers/net/ath/ath.h index 42ad59f78..65b97f6aa 100644 --- a/src/drivers/net/ath/ath.h +++ b/src/drivers/net/ath/ath.h @@ -229,10 +229,6 @@ struct ath_common { int btcoex_enabled; }; -struct io_buffer *ath_rxbuf_alloc(struct ath_common *common, - u32 len, - u32 *iob_addr); - void ath_hw_setbssidmask(struct ath_common *common); int ath_hw_keyreset(struct ath_common *common, u16 entry); void ath_hw_cycle_counters_update(struct ath_common *common); diff --git a/src/drivers/net/ath/ath9k/ath9k_recv.c b/src/drivers/net/ath/ath9k/ath9k_recv.c index ba363c676..0ffe9d45a 100644 --- a/src/drivers/net/ath/ath9k/ath9k_recv.c +++ b/src/drivers/net/ath/ath9k/ath9k_recv.c @@ -98,7 +98,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct io_buffer *iob; - u32 *iob_addr = NULL; struct ath_buf *bf; int error = 0; @@ -122,15 +121,14 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) } list_for_each_entry(bf, &sc->rx.rxbuf, list) { - iob = ath_rxbuf_alloc(common, common->rx_bufsize, - iob_addr); + iob = alloc_iob_raw ( common->rx_bufsize, common->cachelsz, 0 ); if (iob == NULL) { error = -ENOMEM; goto err; } bf->bf_mpdu = iob; - bf->bf_buf_addr = *iob_addr; + bf->bf_buf_addr = virt_to_bus ( iob->data ); } sc->rx.rxlink = NULL; @@ -433,7 +431,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, int hp __unused) { struct ath_buf *bf; struct io_buffer *iob = NULL, *requeue_iob; - u32 *requeue_iob_addr = NULL; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); /* @@ -476,7 +473,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, int hp __unused) /* Ensure we always have an iob to requeue once we are done * processing the current buffer's iob */ - requeue_iob = ath_rxbuf_alloc(common, common->rx_bufsize, requeue_iob_addr); + requeue_iob = alloc_iob_raw ( common->rx_bufsize, + common->cachelsz, 0 ); /* If there is no memory we ignore the current RX'd frame, * tell hardware it can give us a new frame using the old @@ -491,7 +489,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, int hp __unused) /* We will now give hardware our shiny new allocated iob */ bf->bf_mpdu = requeue_iob; - bf->bf_buf_addr = *requeue_iob_addr; + bf->bf_buf_addr = virt_to_bus ( requeue_iob->data ); /* * change the default rx antenna if rx diversity chooses the diff --git a/src/drivers/net/ath/ath_main.c b/src/drivers/net/ath/ath_main.c deleted file mode 100644 index 85d159a36..000000000 --- a/src/drivers/net/ath/ath_main.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2009 Atheros Communications Inc. - * - * Modified for iPXE by Scott K Logan July 2011 - * Original from Linux kernel 3.0.1 - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include "ath.h" - -struct io_buffer *ath_rxbuf_alloc(struct ath_common *common, - u32 len, - u32 *iob_addr) -{ - struct io_buffer *iob; - u32 off; - - /* - * Cache-line-align. This is important (for the - * 5210 at least) as not doing so causes bogus data - * in rx'd frames. - */ - - /* Note: the kernel can allocate a value greater than - * what we ask it to give us. We really only need 4 KB as that - * is this hardware supports and in fact we need at least 3849 - * as that is the MAX AMSDU size this hardware supports. - * Unfortunately this means we may get 8 KB here from the - * kernel... and that is actually what is observed on some - * systems :( */ - iob = alloc_iob(len + common->cachelsz - 1); - if (iob != NULL) { - *iob_addr = virt_to_bus(iob->data); - off = ((unsigned long) iob->data) % common->cachelsz; - if (off != 0) - { - iob_reserve(iob, common->cachelsz - off); - *iob_addr += common->cachelsz - off; - } - } else { - DBG("ath: iobuffer alloc of size %d failed\n", len); - return NULL; - } - - return iob; -} From 17a200257ac76f775565e33c22e18fc23d74c79b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 5 Feb 2016 21:03:17 +0000 Subject: [PATCH 052/591] [ehci] Add extra debugging information Signed-off-by: Michael Brown --- src/drivers/usb/ehci.c | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c index 617a43b03..35cbc8de9 100644 --- a/src/drivers/usb/ehci.c +++ b/src/drivers/usb/ehci.c @@ -173,6 +173,61 @@ static int ehci_ctrl_reachable ( struct ehci_device *ehci, void *ptr ) { return -ENOTSUP; } +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Dump host controller registers + * + * @v ehci EHCI device + */ +static __unused void ehci_dump ( struct ehci_device *ehci ) { + uint8_t caplength; + uint16_t hciversion; + uint32_t hcsparams; + uint32_t hccparams; + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t configflag; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump capability registers */ + caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH ); + hciversion = readw ( ehci->cap + EHCI_CAP_HCIVERSION ); + hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS ); + hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS ); + DBGC ( ehci, "EHCI %s caplen %02x hciversion %04x hcsparams %08x " + "hccparams %08x\n", ehci->name, caplength, hciversion, + hcsparams, hccparams ); + + /* Dump operational registers */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + usbintr = readl ( ehci->op + EHCI_OP_USBINTR ); + frindex = readl ( ehci->op + EHCI_OP_FRINDEX ); + ctrldssegment = readl ( ehci->op + EHCI_OP_CTRLDSSEGMENT ); + periodiclistbase = readl ( ehci->op + EHCI_OP_PERIODICLISTBASE ); + asynclistaddr = readl ( ehci->op + EHCI_OP_ASYNCLISTADDR ); + configflag = readl ( ehci->op + EHCI_OP_CONFIGFLAG ); + DBGC ( ehci, "EHCI %s usbcmd %08x usbsts %08x usbint %08x frindx " + "%08x\n", ehci->name, usbcmd, usbsts, usbintr, frindex ); + DBGC ( ehci, "EHCI %s ctrlds %08x period %08x asyncl %08x cfgflg " + "%08x\n", ehci->name, ctrldssegment, periodiclistbase, + asynclistaddr, configflag ); +} + /****************************************************************************** * * USB legacy support @@ -233,6 +288,14 @@ static void ehci_legacy_claim ( struct ehci_device *ehci, if ( ! legacy ) return; + /* Dump original SMI usage */ + pci_read_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), + &ctlsts ); + if ( ctlsts ) { + DBGC ( ehci, "EHCI %s BIOS using SMIs: %08x\n", + ehci->name, ctlsts ); + } + /* Claim ownership */ pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), EHCI_USBLEGSUP_OS_OWNED ); @@ -276,9 +339,11 @@ static void ehci_legacy_claim ( struct ehci_device *ehci, */ static void ehci_legacy_release ( struct ehci_device *ehci, struct pci_device *pci ) { + unsigned int legacy = ehci->legacy; + uint32_t ctlsts; /* Do nothing unless legacy support capability is present */ - if ( ! ehci->legacy ) + if ( ! legacy ) return; /* Do nothing if releasing ownership is prevented */ @@ -289,8 +354,14 @@ static void ehci_legacy_release ( struct ehci_device *ehci, } /* Release ownership */ - pci_write_config_byte ( pci, ( ehci->legacy + EHCI_USBLEGSUP_OS ), 0 ); + pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), 0 ); DBGC ( ehci, "EHCI %s released ownership to BIOS\n", ehci->name ); + + /* Dump restored SMI usage */ + pci_read_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), + &ctlsts ); + DBGC ( ehci, "EHCI %s BIOS reclaimed SMIs: %08x\n", + ehci->name, ctlsts ); } /****************************************************************************** From e2b1140486e6d5da756d64ae5fc051b79664c6d6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 6 Feb 2016 10:20:57 +0000 Subject: [PATCH 053/591] [malloc] Guard against unsigned integer overflow Commit f3fbb5f ("[malloc] Avoid integer overflow for excessively large memory allocations") fixed signed integer overflow issues caused by the use of ssize_t, but did not guard against unsigned integer overflow. Add explicit checks for unsigned integer overflow where needed. As a side bonus, erroneous calls to malloc_dma() with an (illegal) size of zero will now fail cleanly. Signed-off-by: Michael Brown --- src/core/malloc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/malloc.c b/src/core/malloc.c index d7c67823a..32c203532 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -291,9 +291,17 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { */ actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ) ); + if ( ! actual_size ) { + /* The requested size is not permitted to be zero. A + * zero result at this point indicates that either the + * original requested size was zero, or that unsigned + * integer overflow has occurred. + */ + ptr = NULL; + goto done; + } assert ( actual_size >= size ); align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); - assert ( ( actual_size + align_mask ) > actual_size ); DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); @@ -302,7 +310,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { list_for_each_entry ( block, &free_blocks, list ) { pre_size = ( ( offset - virt_to_phys ( block ) ) & align_mask ); - if ( block->size < ( pre_size + actual_size ) ) + if ( ( block->size < pre_size ) || + ( ( block->size - pre_size ) < actual_size ) ) continue; post_size = ( block->size - pre_size - actual_size ); /* Split block into pre-block, block, and @@ -506,6 +515,8 @@ void * realloc ( void *old_ptr, size_t new_size ) { if ( new_size ) { new_total_size = ( new_size + offsetof ( struct autosized_block, data ) ); + if ( new_total_size < new_size ) + return NULL; new_block = alloc_memblock ( new_total_size, 1, 0 ); if ( ! new_block ) return NULL; From 12b3b578869d5c25a32edd81950e104a286643d7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Feb 2016 18:44:24 +0000 Subject: [PATCH 054/591] [iobuf] Improve robustness of I/O buffer allocation Guard against various corner cases (such as zero-length buffers, zero alignments, and integer overflow when rounding up allocation lengths and alignments) and ensure that the struct io_buffer is correctly aligned even when the caller requests a non-zero alignment for the I/O buffer itself. Add self-tests to verify that the resulting alignments and lengths are correct for a range of allocations. Signed-off-by: Michael Brown --- src/core/iobuf.c | 47 ++++++++++---- src/tests/iobuf_test.c | 136 +++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 3 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 src/tests/iobuf_test.c diff --git a/src/core/iobuf.c b/src/core/iobuf.c index 3e52ada4f..0ee53e038 100644 --- a/src/core/iobuf.c +++ b/src/core/iobuf.c @@ -47,20 +47,45 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) { struct io_buffer *iobuf; + size_t padding; + size_t threshold; + unsigned int align_log2; void *data; - /* Align buffer length to ensure that struct io_buffer is aligned */ - len = ( len + __alignof__ ( *iobuf ) - 1 ) & - ~( __alignof__ ( *iobuf ) - 1 ); - - /* Round up alignment to the nearest power of two */ - align = ( 1 << fls ( align - 1 ) ); - - /* Allocate buffer plus descriptor as a single unit, unless - * doing so will push the total size over the alignment - * boundary. + /* Calculate padding required below alignment boundary to + * ensure that a correctly aligned inline struct io_buffer + * could fit (regardless of the requested offset). */ - if ( ( len + sizeof ( *iobuf ) ) <= align ) { + padding = ( sizeof ( *iobuf ) + __alignof__ ( *iobuf ) - 1 ); + + /* Round up requested alignment to at least the size of the + * padding, to simplify subsequent calculations. + */ + if ( align < padding ) + align = padding; + + /* Round up alignment to the nearest power of two, avoiding + * a potentially undefined shift operation. + */ + align_log2 = fls ( align - 1 ); + if ( align_log2 >= ( 8 * sizeof ( align ) ) ) + return NULL; + align = ( 1UL << align_log2 ); + + /* Calculate length threshold */ + assert ( align >= padding ); + threshold = ( align - padding ); + + /* Allocate buffer plus an inline descriptor as a single unit, + * unless doing so would push the total size over the + * alignment boundary. + */ + if ( len <= threshold ) { + + /* Round up buffer length to ensure that struct + * io_buffer is aligned. + */ + len += ( ( - len - offset ) & ( __alignof__ ( *iobuf ) - 1 ) ); /* Allocate memory for buffer plus descriptor */ data = malloc_dma_offset ( len + sizeof ( *iobuf ), align, diff --git a/src/tests/iobuf_test.c b/src/tests/iobuf_test.c new file mode 100644 index 000000000..a417c2e8c --- /dev/null +++ b/src/tests/iobuf_test.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 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 ); + +/** @file + * + * I/O buffer tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include + +/* Forward declaration */ +struct self_test iobuf_test __self_test; + +/** + * Report I/O buffer allocation test result + * + * @v len Required length of buffer + * @v align Physical alignment + * @v offset Offset from physical alignment + * @v file Test code file + * @v line Test code line + */ +static inline void alloc_iob_okx ( size_t len, size_t align, size_t offset, + const char *file, unsigned int line ) { + struct io_buffer *iobuf; + + /* Allocate I/O buffer */ + iobuf = alloc_iob_raw ( len, align, offset ); + okx ( iobuf != NULL, file, line ); + DBGC ( &iobuf_test, "IOBUF %p (%#08lx+%#zx) for %#zx align %#zx " + "offset %#zx\n", iobuf, virt_to_phys ( iobuf->data ), + iob_tailroom ( iobuf ), len, align, offset ); + + /* Validate requested length and alignment */ + okx ( ( ( ( intptr_t ) iobuf ) & ( __alignof__ ( *iobuf ) - 1 ) ) == 0, + file, line ); + okx ( iob_tailroom ( iobuf ) >= len, file, line ); + okx ( ( ( align == 0 ) || + ( ( virt_to_phys ( iobuf->data ) & ( align - 1 ) ) == + ( offset & ( align - 1 ) ) ) ), file, line ); + + /* Overwrite entire content of I/O buffer (for Valgrind) */ + memset ( iob_put ( iobuf, len ), 0x55, len ); + + /* Free I/O buffer */ + free_iob ( iobuf ); +} +#define alloc_iob_ok( len, align, offset ) \ + alloc_iob_okx ( len, align, offset, __FILE__, __LINE__ ) + +/** + * Report I/O buffer allocation failure test result + * + * @v len Required length of buffer + * @v align Physical alignment + * @v offset Offset from physical alignment + * @v file Test code file + * @v line Test code line + */ +static inline void alloc_iob_fail_okx ( size_t len, size_t align, size_t offset, + const char *file, unsigned int line ) { + struct io_buffer *iobuf; + + /* Allocate I/O buffer */ + iobuf = alloc_iob_raw ( len, align, offset ); + okx ( iobuf == NULL, file, line ); +} +#define alloc_iob_fail_ok( len, align, offset ) \ + alloc_iob_fail_okx ( len, align, offset, __FILE__, __LINE__ ) + +/** + * Perform I/O buffer self-tests + * + */ +static void iobuf_test_exec ( void ) { + + /* Check zero-length allocations */ + alloc_iob_ok ( 0, 0, 0 ); + alloc_iob_ok ( 0, 0, 1 ); + alloc_iob_ok ( 0, 1, 0 ); + alloc_iob_ok ( 0, 1024, 0 ); + alloc_iob_ok ( 0, 139, -17 ); + + /* Check various sensible allocations */ + alloc_iob_ok ( 1, 0, 0 ); + alloc_iob_ok ( 16, 16, 0 ); + alloc_iob_ok ( 64, 0, 0 ); + alloc_iob_ok ( 65, 0, 0 ); + alloc_iob_ok ( 65, 1024, 19 ); + alloc_iob_ok ( 1536, 1536, 0 ); + alloc_iob_ok ( 2048, 2048, 0 ); + alloc_iob_ok ( 2048, 2048, -10 ); + + /* Excessively large or excessively aligned allocations should fail */ + alloc_iob_fail_ok ( -1UL, 0, 0 ); + alloc_iob_fail_ok ( -1UL, 1024, 0 ); + alloc_iob_fail_ok ( 0, -1UL, 0 ); + alloc_iob_fail_ok ( 1024, -1UL, 0 ); +} + +/** I/O buffer self-test */ +struct self_test iobuf_test __self_test = { + .name = "iobuf", + .exec = iobuf_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 54ce86677..2e67043e6 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -67,3 +67,4 @@ REQUIRE_OBJECT ( profile_test ); REQUIRE_OBJECT ( setjmp_test ); REQUIRE_OBJECT ( pccrc_test ); REQUIRE_OBJECT ( linebuf_test ); +REQUIRE_OBJECT ( iobuf_test ); From 6de378aae80d614c6bd2ee69b09f1fa7facb3bd6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Feb 2016 19:14:00 +0000 Subject: [PATCH 055/591] [pxe] Clarify comments regarding shrinking of cached DHCP packet Signed-off-by: Michael Brown --- src/arch/i386/core/cachedhcp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/arch/i386/core/cachedhcp.c b/src/arch/i386/core/cachedhcp.c index a5c624035..ff35b9256 100644 --- a/src/arch/i386/core/cachedhcp.c +++ b/src/arch/i386/core/cachedhcp.c @@ -58,6 +58,7 @@ static void cachedhcp_init ( void ) { struct dhcp_packet *dhcppkt; struct dhcp_packet *tmp; struct dhcphdr *dhcphdr; + size_t max_len; size_t len; /* Do nothing if no cached DHCPACK is present */ @@ -69,23 +70,25 @@ static void cachedhcp_init ( void ) { /* No reliable way to determine length before parsing packet; * start by assuming maximum length permitted by PXE. */ - len = sizeof ( BOOTPLAYER_t ); + max_len = sizeof ( BOOTPLAYER_t ); /* Allocate and populate DHCP packet */ - dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len ); + dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len ); if ( ! dhcppkt ) { DBGC ( colour, "CACHEDHCP could not allocate copy\n" ); return; } dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) ); copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0, - len ); - dhcppkt_init ( dhcppkt, dhcphdr, len ); + max_len ); + dhcppkt_init ( dhcppkt, dhcphdr, max_len ); - /* Resize packet to required length. If reallocation fails, - * just continue to use the original packet. + /* Shrink packet to required length. If reallocation fails, + * just continue to use the original packet and waste the + * unused space. */ len = dhcppkt_len ( dhcppkt ); + assert ( len <= max_len ); tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) ); if ( tmp ) dhcppkt = tmp; From 1ae9adee425a94a1f7aed37ecac3528c51f2db42 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Feb 2016 13:08:52 +0000 Subject: [PATCH 056/591] [efi] Add missing definitions for function key scancodes Signed-off-by: Michael Brown --- src/interface/efi/efi_console.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 3b30f3097..047baed47 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -239,6 +239,14 @@ static const char *ansi_sequences[] = { [SCAN_DELETE] = "[3~", [SCAN_PAGE_UP] = "[5~", [SCAN_PAGE_DOWN] = "[6~", + [SCAN_F5] = "[15~", + [SCAN_F6] = "[17~", + [SCAN_F7] = "[18~", + [SCAN_F8] = "[19~", + [SCAN_F9] = "[20~", + [SCAN_F10] = "[21~", + [SCAN_F11] = "[23~", + [SCAN_F12] = "[24~", /* EFI translates some (but not all) incoming escape sequences * via the serial console into equivalent scancodes. When it * doesn't recognise a sequence, it helpfully(!) translates From 0588c03772d1fbbeca66362363ec866d745f95ed Mon Sep 17 00:00:00 2001 From: Mika Tiainen Date: Tue, 9 Feb 2016 22:38:29 +0200 Subject: [PATCH 057/591] [intel] Add INTEL_NO_PHY_RST for another I218-LM variant Fixed booting on HP EliteBook 820 G2 laptop. Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index cdfac1579..dc06466a7 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1065,7 +1065,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), - PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), From 7ecfe7159f94193a666200a17057d4cd5981b850 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Feb 2016 13:59:06 +0000 Subject: [PATCH 058/591] [prefix] Pad .text16 and .data16 segment sizes at build time Commit c64747d ("[librm] Speed up real-to-protected mode transition under KVM") rounded down the .text16 segment address calculated in alloc_basemem() to a multiple of 64 bytes in order to speed up mode transitions under KVM. This creates a potential discrepancy between alloc_basemem() and free_basemem(), meaning that free_basemem() may free less memory than was allocated by alloc_basemem(). Fix by padding the calculated sizes of both .text16 and .data16 to a multiple of 64 bytes at build time. Debugged-by: Yossef Efraim Signed-off-by: Michael Brown --- src/arch/i386/prefix/libprefix.S | 13 +++++-------- src/arch/i386/scripts/i386.lds | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index 7d5c1ed53..3cdb6ec9a 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -558,14 +558,11 @@ alloc_basemem: shlw $6, %ax /* Calculate .data16 segment address */ - subw $_data16_memsz_pgh, %ax + subw $_data16_memsz_ppgh, %ax pushw %ax - /* Calculate .text16 segment address. Round down to ensure - * low bits are zero, to speed up mode transitions under KVM. - */ - subw $_text16_memsz_pgh, %ax - andb $~0x03, %al + /* Calculate .text16 segment address */ + subw $_text16_memsz_ppgh, %ax pushw %ax /* Update FBMS */ @@ -616,8 +613,8 @@ free_basemem: /* OK to free memory */ movw %cs, %ax - addw $_text16_memsz_pgh, %ax - addw $_data16_memsz_pgh, %ax + addw $_text16_memsz_ppgh, %ax + addw $_data16_memsz_ppgh, %ax shrw $6, %ax movw %ax, %fs:0x13 xorw %ax, %ax diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index 38c89e14b..865591ae2 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -247,8 +247,8 @@ SECTIONS { * Values calculated to save code from doing it * */ - _text16_memsz_pgh = ( ( _text16_memsz + 15 ) / 16 ); - _data16_memsz_pgh = ( ( _data16_memsz + 15 ) / 16 ); + _text16_memsz_ppgh = ( ( ( _text16_memsz + 63 ) / 64 ) * 4 ); + _data16_memsz_ppgh = ( ( ( _data16_memsz + 63 ) / 64 ) * 4 ); _textdata_memsz_pgh = ( ( _textdata_memsz + 15 ) / 16 ); _textdata_memsz_kb = ( ( _textdata_memsz + 1023 ) / 1024 ); } From b9c4c2676bf8e3851eebaf22930512bbae9d5612 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 15:48:03 +0000 Subject: [PATCH 059/591] [libc] Split rmsetjmp() and rmlongjmp() into a separate rmsetjmp.h Signed-off-by: Michael Brown --- src/arch/i386/include/comboot.h | 2 +- src/arch/i386/include/pxe_call.h | 2 +- src/arch/i386/include/rmsetjmp.h | 28 +++++++++++++++++++ src/arch/i386/include/setjmp.h | 20 ------------- src/arch/i386/interface/pxe/pxe_call.c | 2 +- src/arch/i386/interface/pxe/pxe_preboot.c | 2 +- .../i386/interface/syslinux/comboot_call.c | 2 +- 7 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 src/arch/i386/include/rmsetjmp.h diff --git a/src/arch/i386/include/comboot.h b/src/arch/i386/include/comboot.h index 2d2f04fe1..5cb1ba54c 100644 --- a/src/arch/i386/include/comboot.h +++ b/src/arch/i386/include/comboot.h @@ -10,7 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include -#include +#include #include /** Segment used for COMBOOT PSP and image */ diff --git a/src/arch/i386/include/pxe_call.h b/src/arch/i386/include/pxe_call.h index cbd548318..2ad0a9505 100644 --- a/src/arch/i386/include/pxe_call.h +++ b/src/arch/i386/include/pxe_call.h @@ -10,7 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -#include +#include struct net_device; diff --git a/src/arch/i386/include/rmsetjmp.h b/src/arch/i386/include/rmsetjmp.h new file mode 100644 index 000000000..3470be477 --- /dev/null +++ b/src/arch/i386/include/rmsetjmp.h @@ -0,0 +1,28 @@ +#ifndef _RMSETJMP_H +#define _RMSETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** A real-mode-extended jump buffer */ +typedef struct { + /** Jump buffer */ + jmp_buf env; + /** Real-mode stack pointer */ + segoff_t rm_stack; +} rmjmp_buf[1]; + +#define rmsetjmp( _env ) ( { \ + (_env)->rm_stack.segment = rm_ss; \ + (_env)->rm_stack.offset = rm_sp; \ + setjmp ( (_env)->env ); } ) \ + +#define rmlongjmp( _env, _val ) do { \ + rm_ss = (_env)->rm_stack.segment; \ + rm_sp = (_env)->rm_stack.offset; \ + longjmp ( (_env)->env, (_val) ); \ + } while ( 0 ) + +#endif /* _RMSETJMP_H */ diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/setjmp.h index fe1a9ef4d..98566696a 100644 --- a/src/arch/i386/include/setjmp.h +++ b/src/arch/i386/include/setjmp.h @@ -4,7 +4,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#include /** A jump buffer */ typedef struct { @@ -22,29 +21,10 @@ typedef struct { uint32_t ebp; } jmp_buf[1]; -/** A real-mode-extended jump buffer */ -typedef struct { - /** Jump buffer */ - jmp_buf env; - /** Real-mode stack pointer */ - segoff_t rm_stack; -} rmjmp_buf[1]; - extern int __asmcall __attribute__ (( returns_twice )) setjmp ( jmp_buf env ); extern void __asmcall __attribute__ (( noreturn )) longjmp ( jmp_buf env, int val ); -#define rmsetjmp( _env ) ( { \ - (_env)->rm_stack.segment = rm_ss; \ - (_env)->rm_stack.offset = rm_sp; \ - setjmp ( (_env)->env ); } ) \ - -#define rmlongjmp( _env, _val ) do { \ - rm_ss = (_env)->rm_stack.segment; \ - rm_sp = (_env)->rm_stack.offset; \ - longjmp ( (_env)->env, (_val) ); \ - } while ( 0 ) - #endif /* _SETJMP_H */ diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index ed17a96a1..414b356d5 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -27,7 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include +#include #include #include #include diff --git a/src/arch/i386/interface/pxe/pxe_preboot.c b/src/arch/i386/interface/pxe/pxe_preboot.c index cc9c052ed..09e721b34 100644 --- a/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/src/arch/i386/interface/pxe/pxe_preboot.c @@ -33,7 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include #include @@ -44,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include "pxe.h" #include "pxe_call.h" diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index 69d94c407..d70340c65 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -32,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include +#include #include #include #include From 15fadab5331f0588eac2d756d680ec65173ca079 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 16:24:30 +0000 Subject: [PATCH 060/591] [bios] Use intptr_t when casting .text16 function pointers Signed-off-by: Michael Brown --- src/arch/i386/drivers/net/undinet.c | 6 ++---- src/arch/i386/firmware/pcbios/bios_console.c | 4 ++-- src/arch/i386/firmware/pcbios/fakee820.c | 4 ++-- src/arch/i386/firmware/pcbios/hidemem.c | 5 ++--- src/arch/i386/image/bootsector.c | 8 ++++---- src/arch/i386/interface/pcbios/int13.c | 5 ++--- src/arch/i386/interface/pcbios/rtc_entropy.c | 5 ++--- src/arch/i386/interface/pxe/pxe_call.c | 4 ++-- src/arch/i386/interface/syslinux/comboot_call.c | 15 ++++++--------- 9 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c index 6450665ff..e273ae0a2 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/i386/drivers/net/undinet.c @@ -143,8 +143,7 @@ static void undinet_hook_isr ( unsigned int irq ) { assert ( undiisr_irq == 0 ); undiisr_irq = irq; - hook_bios_interrupt ( IRQ_INT ( irq ), - ( ( unsigned int ) undiisr ), + hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ), &undiisr_next_handler ); } @@ -157,8 +156,7 @@ static void undinet_unhook_isr ( unsigned int irq ) { assert ( irq <= IRQ_MAX ); - unhook_bios_interrupt ( IRQ_INT ( irq ), - ( ( unsigned int ) undiisr ), + unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ), &undiisr_next_handler ); undiisr_irq = 0; } diff --git a/src/arch/i386/firmware/pcbios/bios_console.c b/src/arch/i386/firmware/pcbios/bios_console.c index 2e252ecbd..ee6cdce2f 100644 --- a/src/arch/i386/firmware/pcbios/bios_console.c +++ b/src/arch/i386/firmware/pcbios/bios_console.c @@ -543,7 +543,7 @@ static void bios_inject_startup ( void ) { : : "i" ( bios_inject ) ); /* Hook INT 16 */ - hook_bios_interrupt ( 0x16, ( ( unsigned int ) int16_wrapper ), + hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ), &int16_vector ); } @@ -555,7 +555,7 @@ static void bios_inject_startup ( void ) { static void bios_inject_shutdown ( int booting __unused ) { /* Unhook INT 16 */ - unhook_bios_interrupt ( 0x16, ( ( unsigned int ) int16_wrapper ), + unhook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ), &int16_vector ); } diff --git a/src/arch/i386/firmware/pcbios/fakee820.c b/src/arch/i386/firmware/pcbios/fakee820.c index 15f4d772f..8b083c4f0 100644 --- a/src/arch/i386/firmware/pcbios/fakee820.c +++ b/src/arch/i386/firmware/pcbios/fakee820.c @@ -88,11 +88,11 @@ void fake_e820 ( void ) { "ljmp *%%cs:real_int15_vector\n\t" ) : : "i" ( sizeof ( e820map ) ) ); - hook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + hook_bios_interrupt ( 0x15, ( intptr_t ) int15_fakee820, &real_int15_vector ); } void unfake_e820 ( void ) { - unhook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + unhook_bios_interrupt ( 0x15, ( intptr_t ) int15_fakee820, &real_int15_vector ); } diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c index 253c601ff..9f9e4f5f7 100644 --- a/src/arch/i386/firmware/pcbios/hidemem.c +++ b/src/arch/i386/firmware/pcbios/hidemem.c @@ -179,8 +179,7 @@ static void hide_etherboot ( void ) { } /* Hook INT 15 */ - hook_bios_interrupt ( 0x15, ( unsigned int ) int15, - &int15_vector ); + hook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector ); /* Dump memory map after mangling */ DBG ( "Hidden iPXE from system memory map\n" ); @@ -210,7 +209,7 @@ static void unhide_etherboot ( int flags __unused ) { } /* Try to unhook INT 15 */ - if ( ( rc = unhook_bios_interrupt ( 0x15, ( unsigned int ) int15, + if ( ( rc = unhook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector ) ) != 0 ) { DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) ); /* Leave it hooked; there's nothing else we can do, diff --git a/src/arch/i386/image/bootsector.c b/src/arch/i386/image/bootsector.c index dba87613c..67dad04f8 100644 --- a/src/arch/i386/image/bootsector.c +++ b/src/arch/i386/image/bootsector.c @@ -71,9 +71,9 @@ int call_bootsector ( unsigned int segment, unsigned int offset, DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset ); /* Hook INTs 18 and 19 to capture failure paths */ - hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + hook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail, &int18_vector ); - hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + hook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail, &int19_vector ); /* Boot the loaded sector @@ -132,9 +132,9 @@ int call_bootsector ( unsigned int segment, unsigned int offset, DBG ( "Booted disk returned via INT 18 or 19\n" ); /* Unhook INTs 18 and 19 */ - unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + unhook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail, &int18_vector ); - unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + unhook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail, &int19_vector ); return -ECANCELED; diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index f0450da90..6ba129218 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -1516,15 +1516,14 @@ static void int13_hook_vector ( void ) { "iret\n\t" ) : : "i" ( int13 ) ); - hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, - &int13_vector ); + hook_bios_interrupt ( 0x13, ( intptr_t ) int13_wrapper, &int13_vector ); } /** * Unhook INT 13 handler */ static void int13_unhook_vector ( void ) { - unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, + unhook_bios_interrupt ( 0x13, ( intptr_t ) int13_wrapper, &int13_vector ); } diff --git a/src/arch/i386/interface/pcbios/rtc_entropy.c b/src/arch/i386/interface/pcbios/rtc_entropy.c index 9aab03c03..89b797581 100644 --- a/src/arch/i386/interface/pcbios/rtc_entropy.c +++ b/src/arch/i386/interface/pcbios/rtc_entropy.c @@ -77,8 +77,7 @@ static void rtc_hook_isr ( void ) { "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ), "i" ( RTC_STATUS_C ) ); - hook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr, - &rtc_old_handler ); + hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler ); } /** @@ -88,7 +87,7 @@ static void rtc_hook_isr ( void ) { static void rtc_unhook_isr ( void ) { int rc; - rc = unhook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr, + rc = unhook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler ); assert ( rc == 0 ); /* Should always be able to unhook */ } diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 414b356d5..dd5f8849c 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -278,7 +278,7 @@ void pxe_activate ( struct net_device *netdev ) { /* Ensure INT 1A is hooked */ if ( ! int_1a_hooked ) { - hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, + hook_bios_interrupt ( 0x1a, ( intptr_t ) pxe_int_1a, &pxe_int_1a_vector ); devices_get(); int_1a_hooked = 1; @@ -311,7 +311,7 @@ int pxe_deactivate ( void ) { /* Ensure INT 1A is unhooked, if possible */ if ( int_1a_hooked ) { if ( ( rc = unhook_bios_interrupt ( 0x1a, - (unsigned int) pxe_int_1a, + ( intptr_t ) pxe_int_1a, &pxe_int_1a_vector ))!= 0){ DBGC ( &pxe_netdev, "PXE could not unhook INT 1A: %s\n", strerror ( rc ) ); diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index d70340c65..22848006c 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -668,8 +668,7 @@ void hook_comboot_interrupts ( ) { "iret\n\t" ) : : "i" ( int20 ) ); - hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, - &int20_vector ); + hook_bios_interrupt ( 0x20, ( intptr_t ) int20_wrapper, &int20_vector ); __asm__ __volatile__ ( TEXT16_CODE ( "\nint21_wrapper:\n\t" @@ -681,8 +680,7 @@ void hook_comboot_interrupts ( ) { "iret\n\t" ) : : "i" ( int21 ) ); - hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, - &int21_vector ); + hook_bios_interrupt ( 0x21, ( intptr_t ) int21_wrapper, &int21_vector ); __asm__ __volatile__ ( TEXT16_CODE ( "\nint22_wrapper:\n\t" @@ -694,8 +692,7 @@ void hook_comboot_interrupts ( ) { "iret\n\t" ) : : "i" ( int22) ); - hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, - &int22_vector ); + hook_bios_interrupt ( 0x22, ( intptr_t ) int22_wrapper, &int22_vector ); } /** @@ -703,13 +700,13 @@ void hook_comboot_interrupts ( ) { */ void unhook_comboot_interrupts ( ) { - unhook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, + unhook_bios_interrupt ( 0x20, ( intptr_t ) int20_wrapper, &int20_vector ); - unhook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, + unhook_bios_interrupt ( 0x21, ( intptr_t ) int21_wrapper, &int21_vector ); - unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, + unhook_bios_interrupt ( 0x22, ( intptr_t ) int22_wrapper, &int22_vector ); } From 9f79f5f1a5b56501a8835fa9e01e6f36d8ee614a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 16:28:12 +0000 Subject: [PATCH 061/591] [bios] Use size_t when casting _text16_memsz and _data16_memsz Signed-off-by: Michael Brown --- src/arch/i386/firmware/pcbios/hidemem.c | 4 ++-- src/arch/i386/interface/pxe/pxe_call.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c index 9f9e4f5f7..a3728123c 100644 --- a/src/arch/i386/firmware/pcbios/hidemem.c +++ b/src/arch/i386/firmware/pcbios/hidemem.c @@ -76,9 +76,9 @@ extern struct segoff __text16 ( int15_vector ); extern char _textdata[]; extern char _etextdata[]; extern char _text16_memsz[]; -#define _text16_memsz ( ( unsigned int ) _text16_memsz ) +#define _text16_memsz ( ( size_t ) _text16_memsz ) extern char _data16_memsz[]; -#define _data16_memsz ( ( unsigned int ) _data16_memsz ) +#define _data16_memsz ( ( size_t ) _data16_memsz ) /** * Hide region of memory from system memory map diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index dd5f8849c..671182991 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -56,9 +56,11 @@ static int int_1a_hooked = 0; /** Real-mode code segment size */ extern char _text16_memsz[]; +#define _text16_memsz ( ( size_t ) _text16_memsz ) /** Real-mode data segment size */ extern char _data16_memsz[]; +#define _data16_memsz ( ( size_t ) _data16_memsz ) /** PXENV_UNDI_TRANSMIT API call profiler */ static struct profiler pxe_api_tx_profiler __profiler = @@ -337,10 +339,9 @@ int pxe_start_nbp ( void ) { int discard_b, discard_c, discard_d, discard_D; uint16_t status; - DBGC ( &pxe_netdev, "PXE NBP starting with netdev %s, code %04x:%04x, " - "data %04x:%04x\n", ( pxe_netdev ? pxe_netdev->name : "" ), - rm_cs, ( ( unsigned int ) _text16_memsz ), - rm_ds, ( ( unsigned int ) _data16_memsz ) ); + DBGC ( &pxe_netdev, "PXE NBP starting with netdev %s, code %04x:%04zx, " + "data %04x:%04zx\n", ( pxe_netdev ? pxe_netdev->name : ""), + rm_cs, _text16_memsz, rm_ds, _data16_memsz ); /* Allow restarting NBP via PXENV_RESTART_TFTP */ jmp = rmsetjmp ( pxe_restart_nbp ); From 44e05b0ffc2099ad51bcb9ff8c444d6a032c30da Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 16:13:30 +0000 Subject: [PATCH 062/591] [bios] Allow relocate.c to be compiled for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/core/relocate.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c index 54ad387e4..65a36e008 100644 --- a/src/arch/i386/core/relocate.c +++ b/src/arch/i386/core/relocate.c @@ -16,7 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ extern char _max_align[]; -#define max_align ( ( unsigned int ) _max_align ) +#define max_align ( ( size_t ) _max_align ) /* Linker symbols */ extern char _textdata[]; @@ -44,8 +44,8 @@ extern char _etextdata[]; */ __asmcall void relocate ( struct i386_all_regs *ix86 ) { struct memory_map memmap; - unsigned long start, end, size, padded_size, max; - unsigned long new_start, new_end; + uint32_t start, end, size, padded_size, max; + uint32_t new_start, new_end; unsigned i; /* Get memory map and current location */ @@ -55,15 +55,15 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { size = ( end - start ); padded_size = ( size + max_align - 1 ); - DBG ( "Relocate: currently at [%lx,%lx)\n" - "...need %lx bytes for %d-byte alignment\n", + DBG ( "Relocate: currently at [%x,%x)\n" + "...need %x bytes for %zd-byte alignment\n", start, end, padded_size, max_align ); /* Determine maximum usable address */ max = MAX_ADDR; if ( ix86->regs.ebp < max ) { max = ix86->regs.ebp; - DBG ( "Limiting relocation to [0,%lx)\n", max ); + DBG ( "Limiting relocation to [0,%x)\n", max ); } /* Walk through the memory map and find the highest address @@ -72,7 +72,7 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { new_end = end; for ( i = 0 ; i < memmap.count ; i++ ) { struct memory_region *region = &memmap.regions[i]; - unsigned long r_start, r_end; + uint32_t r_start, r_end; DBG ( "Considering [%llx,%llx)\n", region->start, region->end); @@ -81,17 +81,17 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { * with using just 32-bit arithmetic after this stage. */ if ( region->start > max ) { - DBG ( "...starts after max=%lx\n", max ); + DBG ( "...starts after max=%x\n", max ); continue; } r_start = region->start; if ( region->end > max ) { - DBG ( "...end truncated to max=%lx\n", max ); + DBG ( "...end truncated to max=%x\n", max ); r_end = max; } else { r_end = region->end; } - DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end ); + DBG ( "...usable portion is [%x,%x)\n", r_start, r_end ); /* If we have rounded down r_end below r_ start, skip * this block. @@ -103,7 +103,7 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { /* Check that there is enough space to fit in iPXE */ if ( ( r_end - r_start ) < size ) { - DBG ( "...too small (need %lx bytes)\n", size ); + DBG ( "...too small (need %x bytes)\n", size ); continue; } @@ -128,7 +128,7 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { new_start += ( start - new_start ) & ( max_align - 1 ); new_end = new_start + size; - DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n", + DBG ( "Relocating from [%x,%x) to [%x,%x)\n", start, end, new_start, new_end ); /* Let prefix know what to copy */ From 9c58ba2cd7314e6d83ecd923895abc6b232eeddd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 16:57:46 +0000 Subject: [PATCH 063/591] [bios] Allow rtc_entropy.c to be compiled for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/interface/pcbios/rtc_entropy.c | 29 +++++++++----------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/arch/i386/interface/pcbios/rtc_entropy.c b/src/arch/i386/interface/pcbios/rtc_entropy.c index 89b797581..6e7ac833e 100644 --- a/src/arch/i386/interface/pcbios/rtc_entropy.c +++ b/src/arch/i386/interface/pcbios/rtc_entropy.c @@ -36,10 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** RTC "interrupt triggered" flag */ -static uint8_t __text16 ( rtc_flag ); -#define rtc_flag __use_text16 ( rtc_flag ) - /** RTC interrupt handler */ extern void rtc_isr ( void ); @@ -58,23 +54,24 @@ static void rtc_hook_isr ( void ) { /* Preserve registers */ "pushw %%ax\n\t" /* Set "interrupt triggered" flag */ - "cs movb $0x01, %c0\n\t" + "movb $0x01, %%cs:rtc_flag\n\t" /* Read RTC status register C to * acknowledge interrupt */ - "movb %3, %%al\n\t" - "outb %%al, %1\n\t" - "inb %2\n\t" + "movb %2, %%al\n\t" + "outb %%al, %0\n\t" + "inb %1\n\t" /* Send EOI */ "movb $0x20, %%al\n\t" "outb %%al, $0xa0\n\t" "outb %%al, $0x20\n\t" /* Restore registers and return */ "popw %%ax\n\t" - "iret\n\t" ) + "iret\n\t" + "\nrtc_flag:\n\t" + ".byte 0\n\t" ) : - : "p" ( __from_text16 ( &rtc_flag ) ), - "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ), + : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ), "i" ( RTC_STATUS_C ) ); hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler ); @@ -167,9 +164,9 @@ uint8_t rtc_sample ( void ) { REAL_CODE ( /* Enable interrupts */ "sti\n\t" /* Wait for RTC interrupt */ - "cs movb %b2, %c4\n\t" + "movb %b2, %%cs:rtc_flag\n\t" "\n1:\n\t" - "cs xchgb %b2, %c4\n\t" /* Serialize */ + "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */ "testb %b2, %b2\n\t" "jz 1b\n\t" /* Read "before" TSC */ @@ -178,9 +175,9 @@ uint8_t rtc_sample ( void ) { "pushl %0\n\t" /* Wait for another RTC interrupt */ "xorb %b2, %b2\n\t" - "cs movb %b2, %c4\n\t" + "movb %b2, %%cs:rtc_flag\n\t" "\n1:\n\t" - "cs xchgb %b2, %c4\n\t" /* Serialize */ + "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */ "testb %b2, %b2\n\t" "jz 1b\n\t" /* Read "after" TSC */ @@ -191,7 +188,7 @@ uint8_t rtc_sample ( void ) { "cli\n\t" ) : "=a" ( after ), "=d" ( before ), "=q" ( temp ) - : "2" ( 0 ), "p" ( __from_text16 ( &rtc_flag ) ) ); + : "2" ( 0 ) ); return ( after - before ); } From 4d224c95d4980acebfb1d3b455c119424130cd15 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 18:41:58 +0000 Subject: [PATCH 064/591] [bios] Allow bzimage.c to be compiled for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/image/bzimage.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index a64206cd3..d9b5ddc07 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/i386/image/bzimage.c @@ -631,9 +631,9 @@ static int bzimage_exec ( struct image *image ) { "pushw %w2\n\t" "pushw $0\n\t" "lret\n\t" ) - : : "r" ( bzimg.rm_kernel_seg ), - "r" ( bzimg.rm_heap ), - "r" ( bzimg.rm_kernel_seg + 0x20 ) ); + : : "R" ( bzimg.rm_kernel_seg ), + "R" ( bzimg.rm_heap ), + "R" ( bzimg.rm_kernel_seg + 0x20 ) ); /* There is no way for the image to return, since we provide * no return address. From 7691e5eedbb3c18588c06493a614e020d3ef5c54 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 18:48:37 +0000 Subject: [PATCH 065/591] [bios] Allow bios_console.c to be compiled for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/firmware/pcbios/bios_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/firmware/pcbios/bios_console.c b/src/arch/i386/firmware/pcbios/bios_console.c index ee6cdce2f..cfffd9542 100644 --- a/src/arch/i386/firmware/pcbios/bios_console.c +++ b/src/arch/i386/firmware/pcbios/bios_console.c @@ -434,7 +434,7 @@ static int bios_iskey ( void ) { "pushfw\n\t" "popw %w0\n\t" "cli\n\t" ) - : "=r" ( flags ), "=a" ( discard_a ) + : "=R" ( flags ), "=a" ( discard_a ) : "a" ( 0x1100 ), "m" ( bios_inject_lock ) ); bios_inject_lock--; return ( ! ( flags & ZF ) ); From 8819a34c0e588e7007bd8a7a162193ace8186b72 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 18:49:38 +0000 Subject: [PATCH 066/591] [bios] Allow memmap.c to be compiled for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/firmware/pcbios/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/i386/firmware/pcbios/memmap.c b/src/arch/i386/firmware/pcbios/memmap.c index bcacecd6a..88bfec9f4 100644 --- a/src/arch/i386/firmware/pcbios/memmap.c +++ b/src/arch/i386/firmware/pcbios/memmap.c @@ -92,7 +92,7 @@ static unsigned int extmemsize_e801 ( void ) { "int $0x15\n\t" "pushfw\n\t" "popw %w0\n\t" ) - : "=r" ( flags ), + : "=R" ( flags ), "=a" ( extmem_1m_to_16m_k ), "=b" ( extmem_16m_plus_64k ), "=c" ( confmem_1m_to_16m_k ), From 1a457e933a5e6c61fe08e1a6813f4514978b4c1d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 16:45:12 +0000 Subject: [PATCH 067/591] [bios] Allow librm to be compiled for x86_64 This commit does not make librm functional for x86_64; it merely allows it to compile without errors. Signed-off-by: Michael Brown --- src/arch/i386/include/librm.h | 15 ++++++++++++--- src/arch/i386/transitions/librm_mgmt.c | 10 +++++----- src/arch/i386/transitions/librm_test.c | 20 +++++++++++++------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/arch/i386/include/librm.h b/src/arch/i386/include/librm.h index a8a578a39..44d931ea0 100644 --- a/src/arch/i386/include/librm.h +++ b/src/arch/i386/include/librm.h @@ -173,17 +173,24 @@ extern uint16_t __text16 ( rm_ds ); extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); +/* CODE_DEFAULT: restore default .code32/.code64 directive */ +#ifdef __x86_64__ +#define CODE_DEFAULT ".code64" +#else +#define CODE_DEFAULT ".code32" +#endif + /* TEXT16_CODE: declare a fragment of code that resides in .text16 */ #define TEXT16_CODE( asm_code_str ) \ ".section \".text16\", \"ax\", @progbits\n\t" \ ".code16\n\t" \ asm_code_str "\n\t" \ - ".code32\n\t" \ + CODE_DEFAULT "\n\t" \ ".previous\n\t" /* REAL_CODE: declare a fragment of code that executes in real mode */ #define REAL_CODE( asm_code_str ) \ - "pushl $1f\n\t" \ + "push $1f\n\t" \ "call real_call\n\t" \ "addl $4, %%esp\n\t" \ TEXT16_CODE ( "\n1:\n\t" \ @@ -194,8 +201,10 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); /* PHYS_CODE: declare a fragment of code that executes in flat physical mode */ #define PHYS_CODE( asm_code_str ) \ "call _virt_to_phys\n\t" \ + ".code32\n\t" \ asm_code_str \ - "call _phys_to_virt\n\t" + "call _phys_to_virt\n\t" \ + CODE_DEFAULT "\n\t" /** Number of interrupts */ #define NUM_INT 256 diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c index becb02677..32695ae0c 100644 --- a/src/arch/i386/transitions/librm_mgmt.c +++ b/src/arch/i386/transitions/librm_mgmt.c @@ -80,8 +80,8 @@ void set_interrupt_vector ( unsigned int intr, void *vector ) { idte = &idt[intr]; idte->segment = VIRTUAL_CS; idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); - idte->low = ( ( ( uint32_t ) vector ) & 0xffff ); - idte->high = ( ( ( uint32_t ) vector ) >> 16 ); + idte->low = ( ( ( intptr_t ) vector ) & 0xffff ); + idte->high = ( ( ( intptr_t ) vector ) >> 16 ); } /** @@ -99,8 +99,8 @@ void init_idt ( void ) { vec->movb = MOVB_INSN; vec->intr = intr; vec->jmp = JMP_INSN; - vec->offset = ( ( uint32_t ) interrupt_wrapper - - ( uint32_t ) vec->next ); + vec->offset = ( ( intptr_t ) interrupt_wrapper - + ( intptr_t ) vec->next ); set_interrupt_vector ( intr, vec ); } DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n", @@ -132,7 +132,7 @@ static struct profiler * interrupt_profiler ( int intr ) { * * @v intr Interrupt number */ -void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int intr ) { +void __attribute__ (( regparm ( 1 ) )) interrupt ( int intr ) { struct profiler *profiler = interrupt_profiler ( intr ); uint32_t discard_eax; diff --git a/src/arch/i386/transitions/librm_test.c b/src/arch/i386/transitions/librm_test.c index f1a517eda..496d56124 100644 --- a/src/arch/i386/transitions/librm_test.c +++ b/src/arch/i386/transitions/librm_test.c @@ -69,9 +69,11 @@ static void librm_test_prot_call ( void ) { static void librm_test_exec ( void ) { unsigned int i; unsigned long timestamp; - unsigned long started; - unsigned long stopped; - unsigned int discard_d; + uint32_t timestamp_lo; + uint32_t timestamp_hi; + uint32_t started; + uint32_t stopped; + uint32_t discard_d; /* Profile mode transitions. We want to profile each * direction of the transition separately, so perform an RDTSC @@ -81,8 +83,12 @@ static void librm_test_exec ( void ) { for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { profile_start ( &p2r_profiler ); __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" ) - : "=a" ( timestamp ), "=d" ( discard_d ) + : "=a" ( timestamp_lo ), + "=d" ( timestamp_hi ) : ); + timestamp = timestamp_lo; + if ( sizeof ( timestamp ) > sizeof ( timestamp_lo ) ) + timestamp |= ( ( ( uint64_t ) timestamp_hi ) << 32 ); profile_start_at ( &r2p_profiler, timestamp ); profile_stop ( &r2p_profiler ); profile_stop_at ( &p2r_profiler, timestamp ); @@ -98,14 +104,14 @@ static void librm_test_exec ( void ) { /* Profile complete protected-mode call cycle */ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" - "movl %0, %2\n\t" - "pushl %3\n\t" + "movl %k0, %k2\n\t" + "pushl %k3\n\t" "pushw %%cs\n\t" "call prot_call\n\t" "addw $4, %%sp\n\t" "rdtsc\n\t" ) : "=a" ( stopped ), "=d" ( discard_d ), - "=r" ( started ) + "=R" ( started ) : "i" ( librm_test_prot_call ) ); profile_start_at ( &prot_call_profiler, started ); profile_stop_at ( &prot_call_profiler, stopped ); From 43515f9f1a3e3bfaa9171f4711ed0aafef1caf06 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 17:42:36 +0000 Subject: [PATCH 068/591] [bios] Move isolinux definitions to Makefile.pcbios Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 22 ---------------------- src/arch/i386/Makefile.pcbios | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 478e0634c..58915b8f9 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -84,28 +84,6 @@ endif # CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" -# Locations of isolinux files -# -SYSLINUX_DIR_LIST := \ - /usr/lib/syslinux \ - /usr/lib/syslinux/bios \ - /usr/lib/syslinux/modules/bios \ - /usr/share/syslinux \ - /usr/share/syslinux/bios \ - /usr/share/syslinux/modules/bios \ - /usr/local/share/syslinux \ - /usr/local/share/syslinux/bios \ - /usr/local/share/syslinux/modules/bios \ - /usr/lib/ISOLINUX -ISOLINUX_BIN_LIST := \ - $(ISOLINUX_BIN) \ - $(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST)) -LDLINUX_C32_LIST := \ - $(LDLINUX_C32) \ - $(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST)) -ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) -LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST))) - # i386-specific directories containing source files # SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios index 02952f81b..8ae37399f 100644 --- a/src/arch/i386/Makefile.pcbios +++ b/src/arch/i386/Makefile.pcbios @@ -53,6 +53,28 @@ LIST_NAME_mrom := ROMS LIST_NAME_pcirom := ROMS LIST_NAME_isarom := ROMS +# Locations of isolinux files +# +SYSLINUX_DIR_LIST := \ + /usr/lib/syslinux \ + /usr/lib/syslinux/bios \ + /usr/lib/syslinux/modules/bios \ + /usr/share/syslinux \ + /usr/share/syslinux/bios \ + /usr/share/syslinux/modules/bios \ + /usr/local/share/syslinux \ + /usr/local/share/syslinux/bios \ + /usr/local/share/syslinux/modules/bios \ + /usr/lib/ISOLINUX +ISOLINUX_BIN_LIST := \ + $(ISOLINUX_BIN) \ + $(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST)) +LDLINUX_C32_LIST := \ + $(LDLINUX_C32) \ + $(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST)) +ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) +LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST))) + # rule to make a non-emulation ISO boot image NON_AUTO_MEDIA += iso %iso: %lkrn util/geniso From f468f12b1eca15e703aa2a79f1c82969c04c2322 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 15:19:01 +0000 Subject: [PATCH 069/591] [bios] Add bin-x86_64-pcbios build platform Move most arch/i386 files to arch/x86, and adjust the contents of the Makefiles and the include/bits/*.h headers to reflect the new locations. This patch makes no substantive code changes, as can be seen using a rename-aware diff (e.g. "git show -M5"). This patch does not make the pcbios platform functional for x86_64; it merely allows it to compile without errors. Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 13 +- src/arch/i386/Makefile.pcbios | 123 +----------------- src/arch/x86/Makefile | 9 ++ src/arch/x86/Makefile.pcbios | 123 ++++++++++++++++++ src/arch/{i386 => x86}/core/basemem_packet.c | 0 src/arch/{i386 => x86}/core/cachedhcp.c | 0 src/arch/{i386 => x86}/core/dumpregs.c | 0 src/arch/{i386 => x86}/core/patch_cf.S | 0 src/arch/{i386 => x86}/core/pci_autoboot.c | 0 src/arch/{i386 => x86}/core/rdtsc_timer.c | 0 src/arch/{i386 => x86}/core/relocate.c | 0 src/arch/{i386 => x86}/core/runtime.c | 0 src/arch/{i386 => x86}/core/stack.S | 0 src/arch/{i386 => x86}/core/stack16.S | 0 src/arch/{i386 => x86}/core/video_subr.c | 0 src/arch/{i386 => x86}/core/virtaddr.S | 0 src/arch/{i386 => x86}/drivers/net/undi.c | 0 src/arch/{i386 => x86}/drivers/net/undiisr.S | 0 src/arch/{i386 => x86}/drivers/net/undiload.c | 0 src/arch/{i386 => x86}/drivers/net/undinet.c | 0 src/arch/{i386 => x86}/drivers/net/undionly.c | 0 .../{i386 => x86}/drivers/net/undipreload.c | 0 src/arch/{i386 => x86}/drivers/net/undirom.c | 0 src/arch/{i386 => x86}/hci/commands/pxe_cmd.c | 0 src/arch/{i386 => x86}/image/bootsector.c | 0 src/arch/{i386 => x86}/image/bzimage.c | 0 src/arch/{i386 => x86}/image/elfboot.c | 0 src/arch/{i386 => x86}/image/initrd.c | 0 src/arch/{i386 => x86}/image/multiboot.c | 0 src/arch/{i386 => x86}/image/nbi.c | 0 src/arch/{i386 => x86}/image/pxe_image.c | 0 src/arch/{i386 => x86}/image/sdi.c | 0 src/arch/{i386 => x86}/include/basemem.h | 0 .../{i386 => x86}/include/basemem_packet.h | 0 src/arch/{i386 => x86}/include/bios.h | 0 src/arch/{i386 => x86}/include/bios_disks.h | 0 src/arch/{i386 => x86}/include/biosint.h | 0 src/arch/{i386 => x86}/include/bits/entropy.h | 2 +- src/arch/{i386 => x86}/include/bits/nap.h | 2 +- src/arch/{i386 => x86}/include/bits/reboot.h | 2 +- src/arch/{i386 => x86}/include/bits/sanboot.h | 2 +- src/arch/{i386 => x86}/include/bits/smbios.h | 2 +- src/arch/{i386 => x86}/include/bits/time.h | 2 +- src/arch/{i386 => x86}/include/bits/timer.h | 2 +- src/arch/{i386 => x86}/include/bits/uaccess.h | 2 +- src/arch/{i386 => x86}/include/bits/umalloc.h | 2 +- src/arch/{i386 => x86}/include/bochs.h | 0 src/arch/{i386 => x86}/include/bootsector.h | 0 src/arch/{i386 => x86}/include/bzimage.h | 0 src/arch/{i386 => x86}/include/fakee820.h | 0 src/arch/{i386 => x86}/include/initrd.h | 0 src/arch/{i386 => x86}/include/int13.h | 0 .../{i386 => x86}/include/ipxe/bios_nap.h | 0 .../{i386 => x86}/include/ipxe/bios_reboot.h | 0 .../{i386 => x86}/include/ipxe/bios_sanboot.h | 0 .../{i386 => x86}/include/ipxe/bios_smbios.h | 0 .../{i386 => x86}/include/ipxe/bios_timer.h | 0 .../{i386 => x86}/include/ipxe/errno/pcbios.h | 0 .../include/ipxe/memtop_umalloc.h | 0 .../{i386 => x86}/include/ipxe/rdtsc_timer.h | 0 .../{i386 => x86}/include/ipxe/rtc_entropy.h | 0 .../{i386 => x86}/include/ipxe/rtc_time.h | 0 src/arch/{i386 => x86}/include/ipxe/vesafb.h | 0 src/arch/{i386 => x86}/include/kir.h | 0 src/arch/{i386 => x86}/include/libkir.h | 0 src/arch/{i386 => x86}/include/librm.h | 0 src/arch/{i386 => x86}/include/memsizes.h | 0 src/arch/{i386 => x86}/include/multiboot.h | 0 src/arch/{i386 => x86}/include/pnpbios.h | 0 src/arch/{i386 => x86}/include/pxe.h | 0 src/arch/{i386 => x86}/include/pxe_api.h | 0 src/arch/{i386 => x86}/include/pxe_call.h | 0 src/arch/{i386 => x86}/include/pxe_error.h | 0 src/arch/{i386 => x86}/include/pxe_types.h | 0 src/arch/{i386 => x86}/include/pxeparent.h | 0 src/arch/{i386 => x86}/include/realmode.h | 0 src/arch/{i386 => x86}/include/registers.h | 0 src/arch/{i386 => x86}/include/rmsetjmp.h | 0 src/arch/{i386 => x86}/include/rtc.h | 0 src/arch/{i386 => x86}/include/sdi.h | 0 src/arch/{i386 => x86}/include/undi.h | 0 src/arch/{i386 => x86}/include/undiload.h | 0 src/arch/{i386 => x86}/include/undinet.h | 0 src/arch/{i386 => x86}/include/undipreload.h | 0 src/arch/{i386 => x86}/include/undirom.h | 0 src/arch/{i386 => x86}/include/vga.h | 0 src/arch/{i386 => x86}/interface/pcbios/apm.c | 0 .../interface}/pcbios/basemem.c | 0 .../interface}/pcbios/bios_console.c | 0 .../{i386 => x86}/interface/pcbios/bios_nap.c | 0 .../interface/pcbios/bios_reboot.c | 0 .../interface/pcbios/bios_smbios.c | 0 .../interface/pcbios/bios_timer.c | 0 .../{i386 => x86}/interface/pcbios/biosint.c | 0 .../interface}/pcbios/e820mangler.S | 0 .../interface}/pcbios/fakee820.c | 0 .../interface}/pcbios/hidemem.c | 0 .../{i386 => x86}/interface/pcbios/int13.c | 0 .../{i386 => x86}/interface/pcbios/int13con.c | 0 .../interface}/pcbios/memmap.c | 0 .../interface/pcbios/memtop_umalloc.c | 0 .../{i386 => x86}/interface/pcbios/pcibios.c | 0 .../interface}/pcbios/pnpbios.c | 0 .../interface/pcbios/rtc_entropy.c | 0 .../{i386 => x86}/interface/pcbios/rtc_time.c | 0 .../{i386 => x86}/interface/pcbios/vesafb.c | 0 .../{i386 => x86}/interface/pxe/pxe_call.c | 0 .../{i386 => x86}/interface/pxe/pxe_entry.S | 0 .../interface/pxe/pxe_exit_hook.c | 0 .../{i386 => x86}/interface/pxe/pxe_file.c | 0 .../{i386 => x86}/interface/pxe/pxe_loader.c | 0 .../{i386 => x86}/interface/pxe/pxe_preboot.c | 0 .../{i386 => x86}/interface/pxe/pxe_tftp.c | 0 .../{i386 => x86}/interface/pxe/pxe_udp.c | 0 .../{i386 => x86}/interface/pxe/pxe_undi.c | 0 .../interface/pxeparent/pxeparent.c | 0 src/arch/{i386 => x86}/prefix/bootpart.S | 0 src/arch/{i386 => x86}/prefix/dskprefix.S | 0 src/arch/{i386 => x86}/prefix/exeprefix.S | 0 src/arch/{i386 => x86}/prefix/hdprefix.S | 0 src/arch/{i386 => x86}/prefix/isaromprefix.S | 0 src/arch/{i386 => x86}/prefix/kkkpxeprefix.S | 0 src/arch/{i386 => x86}/prefix/kkpxeprefix.S | 0 src/arch/{i386 => x86}/prefix/kpxeprefix.S | 0 src/arch/{i386 => x86}/prefix/libprefix.S | 0 src/arch/{i386 => x86}/prefix/lkrnprefix.S | 0 src/arch/{i386 => x86}/prefix/mbr.S | 0 src/arch/{i386 => x86}/prefix/mromprefix.S | 0 src/arch/{i386 => x86}/prefix/nbiprefix.S | 0 src/arch/{i386 => x86}/prefix/nullprefix.S | 0 src/arch/{i386 => x86}/prefix/pciromprefix.S | 0 src/arch/{i386 => x86}/prefix/pxeprefix.S | 0 src/arch/{i386 => x86}/prefix/romprefix.S | 0 src/arch/{i386 => x86}/prefix/undiloader.S | 0 src/arch/{i386 => x86}/prefix/unlzma.S | 0 src/arch/{i386 => x86}/prefix/unlzma16.S | 0 src/arch/{i386 => x86}/prefix/usbdisk.S | 0 .../i386.lds => x86/scripts/pcbios.lds} | 0 src/arch/{i386 => x86}/transitions/liba20.S | 0 src/arch/{i386 => x86}/transitions/libkir.S | 0 src/arch/{i386 => x86}/transitions/libpm.S | 0 src/arch/{i386 => x86}/transitions/librm.S | 0 .../{i386 => x86}/transitions/librm_mgmt.c | 0 .../{i386 => x86}/transitions/librm_test.c | 0 src/arch/x86_64/Makefile.pcbios | 6 + src/arch/x86_64/include/bits/entropy.h | 12 -- src/arch/x86_64/include/bits/nap.h | 12 -- src/arch/x86_64/include/bits/reboot.h | 12 -- src/arch/x86_64/include/bits/sanboot.h | 12 -- src/arch/x86_64/include/bits/smbios.h | 10 -- src/arch/x86_64/include/bits/time.h | 12 -- src/arch/x86_64/include/bits/timer.h | 10 -- src/arch/x86_64/include/bits/uaccess.h | 10 -- src/arch/x86_64/include/bits/umalloc.h | 10 -- .../x86_64/include/pcbios/ipxe/dhcp_arch.h | 46 +++++++ 155 files changed, 198 insertions(+), 240 deletions(-) create mode 100644 src/arch/x86/Makefile.pcbios rename src/arch/{i386 => x86}/core/basemem_packet.c (100%) rename src/arch/{i386 => x86}/core/cachedhcp.c (100%) rename src/arch/{i386 => x86}/core/dumpregs.c (100%) rename src/arch/{i386 => x86}/core/patch_cf.S (100%) rename src/arch/{i386 => x86}/core/pci_autoboot.c (100%) rename src/arch/{i386 => x86}/core/rdtsc_timer.c (100%) rename src/arch/{i386 => x86}/core/relocate.c (100%) rename src/arch/{i386 => x86}/core/runtime.c (100%) rename src/arch/{i386 => x86}/core/stack.S (100%) rename src/arch/{i386 => x86}/core/stack16.S (100%) rename src/arch/{i386 => x86}/core/video_subr.c (100%) rename src/arch/{i386 => x86}/core/virtaddr.S (100%) rename src/arch/{i386 => x86}/drivers/net/undi.c (100%) rename src/arch/{i386 => x86}/drivers/net/undiisr.S (100%) rename src/arch/{i386 => x86}/drivers/net/undiload.c (100%) rename src/arch/{i386 => x86}/drivers/net/undinet.c (100%) rename src/arch/{i386 => x86}/drivers/net/undionly.c (100%) rename src/arch/{i386 => x86}/drivers/net/undipreload.c (100%) rename src/arch/{i386 => x86}/drivers/net/undirom.c (100%) rename src/arch/{i386 => x86}/hci/commands/pxe_cmd.c (100%) rename src/arch/{i386 => x86}/image/bootsector.c (100%) rename src/arch/{i386 => x86}/image/bzimage.c (100%) rename src/arch/{i386 => x86}/image/elfboot.c (100%) rename src/arch/{i386 => x86}/image/initrd.c (100%) rename src/arch/{i386 => x86}/image/multiboot.c (100%) rename src/arch/{i386 => x86}/image/nbi.c (100%) rename src/arch/{i386 => x86}/image/pxe_image.c (100%) rename src/arch/{i386 => x86}/image/sdi.c (100%) rename src/arch/{i386 => x86}/include/basemem.h (100%) rename src/arch/{i386 => x86}/include/basemem_packet.h (100%) rename src/arch/{i386 => x86}/include/bios.h (100%) rename src/arch/{i386 => x86}/include/bios_disks.h (100%) rename src/arch/{i386 => x86}/include/biosint.h (100%) rename src/arch/{i386 => x86}/include/bits/entropy.h (79%) rename src/arch/{i386 => x86}/include/bits/nap.h (79%) rename src/arch/{i386 => x86}/include/bits/reboot.h (79%) rename src/arch/{i386 => x86}/include/bits/sanboot.h (79%) rename src/arch/{i386 => x86}/include/bits/smbios.h (79%) rename src/arch/{i386 => x86}/include/bits/time.h (79%) rename src/arch/{i386 => x86}/include/bits/timer.h (81%) rename src/arch/{i386 => x86}/include/bits/uaccess.h (76%) rename src/arch/{i386 => x86}/include/bits/umalloc.h (74%) rename src/arch/{i386 => x86}/include/bochs.h (100%) rename src/arch/{i386 => x86}/include/bootsector.h (100%) rename src/arch/{i386 => x86}/include/bzimage.h (100%) rename src/arch/{i386 => x86}/include/fakee820.h (100%) rename src/arch/{i386 => x86}/include/initrd.h (100%) rename src/arch/{i386 => x86}/include/int13.h (100%) rename src/arch/{i386 => x86}/include/ipxe/bios_nap.h (100%) rename src/arch/{i386 => x86}/include/ipxe/bios_reboot.h (100%) rename src/arch/{i386 => x86}/include/ipxe/bios_sanboot.h (100%) rename src/arch/{i386 => x86}/include/ipxe/bios_smbios.h (100%) rename src/arch/{i386 => x86}/include/ipxe/bios_timer.h (100%) rename src/arch/{i386 => x86}/include/ipxe/errno/pcbios.h (100%) rename src/arch/{i386 => x86}/include/ipxe/memtop_umalloc.h (100%) rename src/arch/{i386 => x86}/include/ipxe/rdtsc_timer.h (100%) rename src/arch/{i386 => x86}/include/ipxe/rtc_entropy.h (100%) rename src/arch/{i386 => x86}/include/ipxe/rtc_time.h (100%) rename src/arch/{i386 => x86}/include/ipxe/vesafb.h (100%) rename src/arch/{i386 => x86}/include/kir.h (100%) rename src/arch/{i386 => x86}/include/libkir.h (100%) rename src/arch/{i386 => x86}/include/librm.h (100%) rename src/arch/{i386 => x86}/include/memsizes.h (100%) rename src/arch/{i386 => x86}/include/multiboot.h (100%) rename src/arch/{i386 => x86}/include/pnpbios.h (100%) rename src/arch/{i386 => x86}/include/pxe.h (100%) rename src/arch/{i386 => x86}/include/pxe_api.h (100%) rename src/arch/{i386 => x86}/include/pxe_call.h (100%) rename src/arch/{i386 => x86}/include/pxe_error.h (100%) rename src/arch/{i386 => x86}/include/pxe_types.h (100%) rename src/arch/{i386 => x86}/include/pxeparent.h (100%) rename src/arch/{i386 => x86}/include/realmode.h (100%) rename src/arch/{i386 => x86}/include/registers.h (100%) rename src/arch/{i386 => x86}/include/rmsetjmp.h (100%) rename src/arch/{i386 => x86}/include/rtc.h (100%) rename src/arch/{i386 => x86}/include/sdi.h (100%) rename src/arch/{i386 => x86}/include/undi.h (100%) rename src/arch/{i386 => x86}/include/undiload.h (100%) rename src/arch/{i386 => x86}/include/undinet.h (100%) rename src/arch/{i386 => x86}/include/undipreload.h (100%) rename src/arch/{i386 => x86}/include/undirom.h (100%) rename src/arch/{i386 => x86}/include/vga.h (100%) rename src/arch/{i386 => x86}/interface/pcbios/apm.c (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/basemem.c (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/bios_console.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/bios_nap.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/bios_reboot.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/bios_smbios.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/bios_timer.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/biosint.c (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/e820mangler.S (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/fakee820.c (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/hidemem.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/int13.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/int13con.c (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/memmap.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/memtop_umalloc.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/pcibios.c (100%) rename src/arch/{i386/firmware => x86/interface}/pcbios/pnpbios.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/rtc_entropy.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/rtc_time.c (100%) rename src/arch/{i386 => x86}/interface/pcbios/vesafb.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_call.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_entry.S (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_exit_hook.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_file.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_loader.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_preboot.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_tftp.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_udp.c (100%) rename src/arch/{i386 => x86}/interface/pxe/pxe_undi.c (100%) rename src/arch/{i386 => x86}/interface/pxeparent/pxeparent.c (100%) rename src/arch/{i386 => x86}/prefix/bootpart.S (100%) rename src/arch/{i386 => x86}/prefix/dskprefix.S (100%) rename src/arch/{i386 => x86}/prefix/exeprefix.S (100%) rename src/arch/{i386 => x86}/prefix/hdprefix.S (100%) rename src/arch/{i386 => x86}/prefix/isaromprefix.S (100%) rename src/arch/{i386 => x86}/prefix/kkkpxeprefix.S (100%) rename src/arch/{i386 => x86}/prefix/kkpxeprefix.S (100%) rename src/arch/{i386 => x86}/prefix/kpxeprefix.S (100%) rename src/arch/{i386 => x86}/prefix/libprefix.S (100%) rename src/arch/{i386 => x86}/prefix/lkrnprefix.S (100%) rename src/arch/{i386 => x86}/prefix/mbr.S (100%) rename src/arch/{i386 => x86}/prefix/mromprefix.S (100%) rename src/arch/{i386 => x86}/prefix/nbiprefix.S (100%) rename src/arch/{i386 => x86}/prefix/nullprefix.S (100%) rename src/arch/{i386 => x86}/prefix/pciromprefix.S (100%) rename src/arch/{i386 => x86}/prefix/pxeprefix.S (100%) rename src/arch/{i386 => x86}/prefix/romprefix.S (100%) rename src/arch/{i386 => x86}/prefix/undiloader.S (100%) rename src/arch/{i386 => x86}/prefix/unlzma.S (100%) rename src/arch/{i386 => x86}/prefix/unlzma16.S (100%) rename src/arch/{i386 => x86}/prefix/usbdisk.S (100%) rename src/arch/{i386/scripts/i386.lds => x86/scripts/pcbios.lds} (100%) rename src/arch/{i386 => x86}/transitions/liba20.S (100%) rename src/arch/{i386 => x86}/transitions/libkir.S (100%) rename src/arch/{i386 => x86}/transitions/libpm.S (100%) rename src/arch/{i386 => x86}/transitions/librm.S (100%) rename src/arch/{i386 => x86}/transitions/librm_mgmt.c (100%) rename src/arch/{i386 => x86}/transitions/librm_test.c (100%) create mode 100644 src/arch/x86_64/Makefile.pcbios delete mode 100644 src/arch/x86_64/include/bits/entropy.h delete mode 100644 src/arch/x86_64/include/bits/nap.h delete mode 100644 src/arch/x86_64/include/bits/reboot.h delete mode 100644 src/arch/x86_64/include/bits/sanboot.h delete mode 100644 src/arch/x86_64/include/bits/smbios.h delete mode 100644 src/arch/x86_64/include/bits/time.h delete mode 100644 src/arch/x86_64/include/bits/timer.h delete mode 100644 src/arch/x86_64/include/bits/uaccess.h delete mode 100644 src/arch/x86_64/include/bits/umalloc.h create mode 100644 src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 58915b8f9..89393d06c 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -80,20 +80,11 @@ PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie') WORKAROUND_CFLAGS += $(PIE_FLAGS) endif -# Define version string for lkrnprefix.S -# -CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" - # i386-specific directories containing source files # -SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix -SRCDIRS += arch/i386/firmware/pcbios +SRCDIRS += arch/i386/core SRCDIRS += arch/i386/image -SRCDIRS += arch/i386/interface/pcbios -SRCDIRS += arch/i386/interface/pxe -SRCDIRS += arch/i386/interface/pxeparent -SRCDIRS += arch/i386/interface/syslinux -SRCDIRS += arch/i386/hci/commands +SRCDIRS += arch/i386/interface/syslinux # Include common x86 Makefile # diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios index 8ae37399f..dfb8db0a0 100644 --- a/src/arch/i386/Makefile.pcbios +++ b/src/arch/i386/Makefile.pcbios @@ -1,123 +1,6 @@ # -*- makefile -*- : Force emacs to use Makefile mode -# The i386 linker script +# Include generic BIOS Makefile # -LDSCRIPT = arch/i386/scripts/i386.lds - -# Stop ld from complaining about our customised linker script -# -LDFLAGS += -N --no-check-sections - -# pcbios specific drivers -SRCDIRS += arch/i386/drivers -SRCDIRS += arch/i386/drivers/net - -# Media types. -# -MEDIA += rom -MEDIA += mrom -MEDIA += pcirom -MEDIA += isarom -MEDIA += pxe -MEDIA += kpxe -MEDIA += kkpxe -MEDIA += kkkpxe -MEDIA += lkrn -MEDIA += dsk -MEDIA += nbi -MEDIA += hd -MEDIA += raw -MEDIA += exe - -# Padding rules -# -PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff -PAD_mrom = $(PAD_rom) -PAD_pcirom = $(PAD_rom) -PAD_isarom = $(PAD_rom) -PAD_dsk = $(PERL) $(PADIMG) --blksize=512 -PAD_hd = $(PERL) $(PADIMG) --blksize=32768 -PAD_exe = $(PERL) $(PADIMG) --blksize=512 - -# Finalisation rules -# -FINALISE_rom = $(PERL) $(FIXROM) -FINALISE_mrom = $(FINALISE_rom) -FINALISE_pcirom = $(FINALISE_rom) -FINALISE_isarom = $(FINALISE_rom) - -# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc. -# -LIST_NAME_rom := ROMS -LIST_NAME_mrom := ROMS -LIST_NAME_pcirom := ROMS -LIST_NAME_isarom := ROMS - -# Locations of isolinux files -# -SYSLINUX_DIR_LIST := \ - /usr/lib/syslinux \ - /usr/lib/syslinux/bios \ - /usr/lib/syslinux/modules/bios \ - /usr/share/syslinux \ - /usr/share/syslinux/bios \ - /usr/share/syslinux/modules/bios \ - /usr/local/share/syslinux \ - /usr/local/share/syslinux/bios \ - /usr/local/share/syslinux/modules/bios \ - /usr/lib/ISOLINUX -ISOLINUX_BIN_LIST := \ - $(ISOLINUX_BIN) \ - $(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST)) -LDLINUX_C32_LIST := \ - $(LDLINUX_C32) \ - $(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST)) -ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) -LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST))) - -# rule to make a non-emulation ISO boot image -NON_AUTO_MEDIA += iso -%iso: %lkrn util/geniso - $(QM)$(ECHO) " [GENISO] $@" - $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) LDLINUX_C32=$(LDLINUX_C32) \ - VERSION="$(VERSION)" bash util/geniso -o $@ $< - -# rule to make a floppy emulation ISO boot image -NON_AUTO_MEDIA += liso -%liso: %lkrn util/geniso - $(QM)$(ECHO) " [GENISO] $@" - $(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $< - -# rule to make a syslinux floppy image (mountable, bootable) -NON_AUTO_MEDIA += sdsk -%sdsk: %lkrn util/gensdsk - $(QM)$(ECHO) " [GENSDSK] $@" - $(Q)bash util/gensdsk $@ $< - -# rule to write disk images to /dev/fd0 -NON_AUTO_MEDIA += fd0 -%fd0 : %dsk - $(QM)$(ECHO) " [DD] $@" - $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0 - $(Q)sync - -# Special target for building Master Boot Record binary -$(BIN)/mbr.bin : $(BIN)/mbr.o - $(QM)$(ECHO) " [OBJCOPY] $@" - $(Q)$(OBJCOPY) -O binary $< $@ - -# rule to make a USB disk image -$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o - $(QM)$(ECHO) " [OBJCOPY] $@" - $(Q)$(OBJCOPY) -O binary $< $@ - -NON_AUTO_MEDIA += usb -%usb: $(BIN)/usbdisk.bin %hd - $(QM)$(ECHO) " [FINISH] $@" - $(Q)cat $^ > $@ - -# Padded floppy image (e.g. for iLO) -NON_AUTO_MEDIA += pdsk -%pdsk : %dsk - $(Q)cp $< $@ - $(Q)$(PADIMG) --blksize=1474560 $@ +MAKEDEPS += arch/x86/Makefile.pcbios +include arch/x86/Makefile.pcbios diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 4ab741db7..6ad8031fd 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -5,12 +5,17 @@ INCDIRS += arch/x86/include # x86-specific directories containing source files # SRCDIRS += arch/x86/core +SRCDIRS += arch/x86/image +SRCDIRS += arch/x86/interface/pcbios +SRCDIRS += arch/x86/interface/pxe +SRCDIRS += arch/x86/interface/pxeparent SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/interface/vmware SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen SRCDIRS += arch/x86/drivers/hyperv +SRCDIRS += arch/x86/transitions # breaks building some of the linux-related objects CFLAGS += -Ulinux @@ -18,6 +23,10 @@ CFLAGS += -Ulinux # disable valgrind CFLAGS += -DNVALGRIND +# Define version string for lkrnprefix.S +# +CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" + # Include Hyper-V driver in the all-drivers build # DRIVERS_hyperv += hyperv diff --git a/src/arch/x86/Makefile.pcbios b/src/arch/x86/Makefile.pcbios new file mode 100644 index 000000000..18a6f7597 --- /dev/null +++ b/src/arch/x86/Makefile.pcbios @@ -0,0 +1,123 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# BIOS-specific directories containing source files +# +SRCDIRS += arch/x86/drivers/net + +# The i386 linker script +# +LDSCRIPT = arch/x86/scripts/pcbios.lds + +# Stop ld from complaining about our customised linker script +# +LDFLAGS += -N --no-check-sections + +# Media types. +# +MEDIA += rom +MEDIA += mrom +MEDIA += pcirom +MEDIA += isarom +MEDIA += pxe +MEDIA += kpxe +MEDIA += kkpxe +MEDIA += kkkpxe +MEDIA += lkrn +MEDIA += dsk +MEDIA += nbi +MEDIA += hd +MEDIA += raw +MEDIA += exe + +# Padding rules +# +PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff +PAD_mrom = $(PAD_rom) +PAD_pcirom = $(PAD_rom) +PAD_isarom = $(PAD_rom) +PAD_dsk = $(PERL) $(PADIMG) --blksize=512 +PAD_hd = $(PERL) $(PADIMG) --blksize=32768 +PAD_exe = $(PERL) $(PADIMG) --blksize=512 + +# Finalisation rules +# +FINALISE_rom = $(PERL) $(FIXROM) +FINALISE_mrom = $(FINALISE_rom) +FINALISE_pcirom = $(FINALISE_rom) +FINALISE_isarom = $(FINALISE_rom) + +# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc. +# +LIST_NAME_rom := ROMS +LIST_NAME_mrom := ROMS +LIST_NAME_pcirom := ROMS +LIST_NAME_isarom := ROMS + +# Locations of isolinux files +# +SYSLINUX_DIR_LIST := \ + /usr/lib/syslinux \ + /usr/lib/syslinux/bios \ + /usr/lib/syslinux/modules/bios \ + /usr/share/syslinux \ + /usr/share/syslinux/bios \ + /usr/share/syslinux/modules/bios \ + /usr/local/share/syslinux \ + /usr/local/share/syslinux/bios \ + /usr/local/share/syslinux/modules/bios \ + /usr/lib/ISOLINUX +ISOLINUX_BIN_LIST := \ + $(ISOLINUX_BIN) \ + $(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST)) +LDLINUX_C32_LIST := \ + $(LDLINUX_C32) \ + $(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST)) +ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) +LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST))) + +# rule to make a non-emulation ISO boot image +NON_AUTO_MEDIA += iso +%iso: %lkrn util/geniso + $(QM)$(ECHO) " [GENISO] $@" + $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) LDLINUX_C32=$(LDLINUX_C32) \ + VERSION="$(VERSION)" bash util/geniso -o $@ $< + +# rule to make a floppy emulation ISO boot image +NON_AUTO_MEDIA += liso +%liso: %lkrn util/geniso + $(QM)$(ECHO) " [GENISO] $@" + $(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $< + +# rule to make a syslinux floppy image (mountable, bootable) +NON_AUTO_MEDIA += sdsk +%sdsk: %lkrn util/gensdsk + $(QM)$(ECHO) " [GENSDSK] $@" + $(Q)bash util/gensdsk $@ $< + +# rule to write disk images to /dev/fd0 +NON_AUTO_MEDIA += fd0 +%fd0 : %dsk + $(QM)$(ECHO) " [DD] $@" + $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0 + $(Q)sync + +# Special target for building Master Boot Record binary +$(BIN)/mbr.bin : $(BIN)/mbr.o + $(QM)$(ECHO) " [OBJCOPY] $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +# rule to make a USB disk image +$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o + $(QM)$(ECHO) " [OBJCOPY] $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +NON_AUTO_MEDIA += usb +%usb: $(BIN)/usbdisk.bin %hd + $(QM)$(ECHO) " [FINISH] $@" + $(Q)cat $^ > $@ + +# Padded floppy image (e.g. for iLO) +NON_AUTO_MEDIA += pdsk +%pdsk : %dsk + $(Q)cp $< $@ + $(Q)$(PADIMG) --blksize=1474560 $@ diff --git a/src/arch/i386/core/basemem_packet.c b/src/arch/x86/core/basemem_packet.c similarity index 100% rename from src/arch/i386/core/basemem_packet.c rename to src/arch/x86/core/basemem_packet.c diff --git a/src/arch/i386/core/cachedhcp.c b/src/arch/x86/core/cachedhcp.c similarity index 100% rename from src/arch/i386/core/cachedhcp.c rename to src/arch/x86/core/cachedhcp.c diff --git a/src/arch/i386/core/dumpregs.c b/src/arch/x86/core/dumpregs.c similarity index 100% rename from src/arch/i386/core/dumpregs.c rename to src/arch/x86/core/dumpregs.c diff --git a/src/arch/i386/core/patch_cf.S b/src/arch/x86/core/patch_cf.S similarity index 100% rename from src/arch/i386/core/patch_cf.S rename to src/arch/x86/core/patch_cf.S diff --git a/src/arch/i386/core/pci_autoboot.c b/src/arch/x86/core/pci_autoboot.c similarity index 100% rename from src/arch/i386/core/pci_autoboot.c rename to src/arch/x86/core/pci_autoboot.c diff --git a/src/arch/i386/core/rdtsc_timer.c b/src/arch/x86/core/rdtsc_timer.c similarity index 100% rename from src/arch/i386/core/rdtsc_timer.c rename to src/arch/x86/core/rdtsc_timer.c diff --git a/src/arch/i386/core/relocate.c b/src/arch/x86/core/relocate.c similarity index 100% rename from src/arch/i386/core/relocate.c rename to src/arch/x86/core/relocate.c diff --git a/src/arch/i386/core/runtime.c b/src/arch/x86/core/runtime.c similarity index 100% rename from src/arch/i386/core/runtime.c rename to src/arch/x86/core/runtime.c diff --git a/src/arch/i386/core/stack.S b/src/arch/x86/core/stack.S similarity index 100% rename from src/arch/i386/core/stack.S rename to src/arch/x86/core/stack.S diff --git a/src/arch/i386/core/stack16.S b/src/arch/x86/core/stack16.S similarity index 100% rename from src/arch/i386/core/stack16.S rename to src/arch/x86/core/stack16.S diff --git a/src/arch/i386/core/video_subr.c b/src/arch/x86/core/video_subr.c similarity index 100% rename from src/arch/i386/core/video_subr.c rename to src/arch/x86/core/video_subr.c diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/x86/core/virtaddr.S similarity index 100% rename from src/arch/i386/core/virtaddr.S rename to src/arch/x86/core/virtaddr.S diff --git a/src/arch/i386/drivers/net/undi.c b/src/arch/x86/drivers/net/undi.c similarity index 100% rename from src/arch/i386/drivers/net/undi.c rename to src/arch/x86/drivers/net/undi.c diff --git a/src/arch/i386/drivers/net/undiisr.S b/src/arch/x86/drivers/net/undiisr.S similarity index 100% rename from src/arch/i386/drivers/net/undiisr.S rename to src/arch/x86/drivers/net/undiisr.S diff --git a/src/arch/i386/drivers/net/undiload.c b/src/arch/x86/drivers/net/undiload.c similarity index 100% rename from src/arch/i386/drivers/net/undiload.c rename to src/arch/x86/drivers/net/undiload.c diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c similarity index 100% rename from src/arch/i386/drivers/net/undinet.c rename to src/arch/x86/drivers/net/undinet.c diff --git a/src/arch/i386/drivers/net/undionly.c b/src/arch/x86/drivers/net/undionly.c similarity index 100% rename from src/arch/i386/drivers/net/undionly.c rename to src/arch/x86/drivers/net/undionly.c diff --git a/src/arch/i386/drivers/net/undipreload.c b/src/arch/x86/drivers/net/undipreload.c similarity index 100% rename from src/arch/i386/drivers/net/undipreload.c rename to src/arch/x86/drivers/net/undipreload.c diff --git a/src/arch/i386/drivers/net/undirom.c b/src/arch/x86/drivers/net/undirom.c similarity index 100% rename from src/arch/i386/drivers/net/undirom.c rename to src/arch/x86/drivers/net/undirom.c diff --git a/src/arch/i386/hci/commands/pxe_cmd.c b/src/arch/x86/hci/commands/pxe_cmd.c similarity index 100% rename from src/arch/i386/hci/commands/pxe_cmd.c rename to src/arch/x86/hci/commands/pxe_cmd.c diff --git a/src/arch/i386/image/bootsector.c b/src/arch/x86/image/bootsector.c similarity index 100% rename from src/arch/i386/image/bootsector.c rename to src/arch/x86/image/bootsector.c diff --git a/src/arch/i386/image/bzimage.c b/src/arch/x86/image/bzimage.c similarity index 100% rename from src/arch/i386/image/bzimage.c rename to src/arch/x86/image/bzimage.c diff --git a/src/arch/i386/image/elfboot.c b/src/arch/x86/image/elfboot.c similarity index 100% rename from src/arch/i386/image/elfboot.c rename to src/arch/x86/image/elfboot.c diff --git a/src/arch/i386/image/initrd.c b/src/arch/x86/image/initrd.c similarity index 100% rename from src/arch/i386/image/initrd.c rename to src/arch/x86/image/initrd.c diff --git a/src/arch/i386/image/multiboot.c b/src/arch/x86/image/multiboot.c similarity index 100% rename from src/arch/i386/image/multiboot.c rename to src/arch/x86/image/multiboot.c diff --git a/src/arch/i386/image/nbi.c b/src/arch/x86/image/nbi.c similarity index 100% rename from src/arch/i386/image/nbi.c rename to src/arch/x86/image/nbi.c diff --git a/src/arch/i386/image/pxe_image.c b/src/arch/x86/image/pxe_image.c similarity index 100% rename from src/arch/i386/image/pxe_image.c rename to src/arch/x86/image/pxe_image.c diff --git a/src/arch/i386/image/sdi.c b/src/arch/x86/image/sdi.c similarity index 100% rename from src/arch/i386/image/sdi.c rename to src/arch/x86/image/sdi.c diff --git a/src/arch/i386/include/basemem.h b/src/arch/x86/include/basemem.h similarity index 100% rename from src/arch/i386/include/basemem.h rename to src/arch/x86/include/basemem.h diff --git a/src/arch/i386/include/basemem_packet.h b/src/arch/x86/include/basemem_packet.h similarity index 100% rename from src/arch/i386/include/basemem_packet.h rename to src/arch/x86/include/basemem_packet.h diff --git a/src/arch/i386/include/bios.h b/src/arch/x86/include/bios.h similarity index 100% rename from src/arch/i386/include/bios.h rename to src/arch/x86/include/bios.h diff --git a/src/arch/i386/include/bios_disks.h b/src/arch/x86/include/bios_disks.h similarity index 100% rename from src/arch/i386/include/bios_disks.h rename to src/arch/x86/include/bios_disks.h diff --git a/src/arch/i386/include/biosint.h b/src/arch/x86/include/biosint.h similarity index 100% rename from src/arch/i386/include/biosint.h rename to src/arch/x86/include/biosint.h diff --git a/src/arch/i386/include/bits/entropy.h b/src/arch/x86/include/bits/entropy.h similarity index 79% rename from src/arch/i386/include/bits/entropy.h rename to src/arch/x86/include/bits/entropy.h index bfeb5e3b5..5ac7fcd2e 100644 --- a/src/arch/i386/include/bits/entropy.h +++ b/src/arch/x86/include/bits/entropy.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific entropy API implementations + * x86-specific entropy API implementations * */ diff --git a/src/arch/i386/include/bits/nap.h b/src/arch/x86/include/bits/nap.h similarity index 79% rename from src/arch/i386/include/bits/nap.h rename to src/arch/x86/include/bits/nap.h index e8bcfd13b..7103b94c0 100644 --- a/src/arch/i386/include/bits/nap.h +++ b/src/arch/x86/include/bits/nap.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific CPU sleeping API implementations + * x86-specific CPU sleeping API implementations * */ diff --git a/src/arch/i386/include/bits/reboot.h b/src/arch/x86/include/bits/reboot.h similarity index 79% rename from src/arch/i386/include/bits/reboot.h rename to src/arch/x86/include/bits/reboot.h index 803dacfe4..e702dd3d0 100644 --- a/src/arch/i386/include/bits/reboot.h +++ b/src/arch/x86/include/bits/reboot.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific reboot API implementations + * x86-specific reboot API implementations * */ diff --git a/src/arch/i386/include/bits/sanboot.h b/src/arch/x86/include/bits/sanboot.h similarity index 79% rename from src/arch/i386/include/bits/sanboot.h rename to src/arch/x86/include/bits/sanboot.h index f02d2e649..1b9924e64 100644 --- a/src/arch/i386/include/bits/sanboot.h +++ b/src/arch/x86/include/bits/sanboot.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific sanboot API implementations + * x86-specific sanboot API implementations * */ diff --git a/src/arch/i386/include/bits/smbios.h b/src/arch/x86/include/bits/smbios.h similarity index 79% rename from src/arch/i386/include/bits/smbios.h rename to src/arch/x86/include/bits/smbios.h index 2ab31e74b..9977c87ac 100644 --- a/src/arch/i386/include/bits/smbios.h +++ b/src/arch/x86/include/bits/smbios.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific SMBIOS API implementations + * x86-specific SMBIOS API implementations * */ diff --git a/src/arch/i386/include/bits/time.h b/src/arch/x86/include/bits/time.h similarity index 79% rename from src/arch/i386/include/bits/time.h rename to src/arch/x86/include/bits/time.h index 6a5d63d32..556d96f64 100644 --- a/src/arch/i386/include/bits/time.h +++ b/src/arch/x86/include/bits/time.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific time API implementations + * x86-specific time API implementations * */ diff --git a/src/arch/i386/include/bits/timer.h b/src/arch/x86/include/bits/timer.h similarity index 81% rename from src/arch/i386/include/bits/timer.h rename to src/arch/x86/include/bits/timer.h index f7d86d78c..b0ff5ee11 100644 --- a/src/arch/i386/include/bits/timer.h +++ b/src/arch/x86/include/bits/timer.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific timer API implementations + * x86-specific timer API implementations * */ diff --git a/src/arch/i386/include/bits/uaccess.h b/src/arch/x86/include/bits/uaccess.h similarity index 76% rename from src/arch/i386/include/bits/uaccess.h rename to src/arch/x86/include/bits/uaccess.h index aac09ba95..e9e7e5af5 100644 --- a/src/arch/i386/include/bits/uaccess.h +++ b/src/arch/x86/include/bits/uaccess.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific user access API implementations + * x86-specific user access API implementations * */ diff --git a/src/arch/i386/include/bits/umalloc.h b/src/arch/x86/include/bits/umalloc.h similarity index 74% rename from src/arch/i386/include/bits/umalloc.h rename to src/arch/x86/include/bits/umalloc.h index 113f16fd1..5d1f554d8 100644 --- a/src/arch/i386/include/bits/umalloc.h +++ b/src/arch/x86/include/bits/umalloc.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific user memory allocation API implementations + * x86-specific user memory allocation API implementations * */ diff --git a/src/arch/i386/include/bochs.h b/src/arch/x86/include/bochs.h similarity index 100% rename from src/arch/i386/include/bochs.h rename to src/arch/x86/include/bochs.h diff --git a/src/arch/i386/include/bootsector.h b/src/arch/x86/include/bootsector.h similarity index 100% rename from src/arch/i386/include/bootsector.h rename to src/arch/x86/include/bootsector.h diff --git a/src/arch/i386/include/bzimage.h b/src/arch/x86/include/bzimage.h similarity index 100% rename from src/arch/i386/include/bzimage.h rename to src/arch/x86/include/bzimage.h diff --git a/src/arch/i386/include/fakee820.h b/src/arch/x86/include/fakee820.h similarity index 100% rename from src/arch/i386/include/fakee820.h rename to src/arch/x86/include/fakee820.h diff --git a/src/arch/i386/include/initrd.h b/src/arch/x86/include/initrd.h similarity index 100% rename from src/arch/i386/include/initrd.h rename to src/arch/x86/include/initrd.h diff --git a/src/arch/i386/include/int13.h b/src/arch/x86/include/int13.h similarity index 100% rename from src/arch/i386/include/int13.h rename to src/arch/x86/include/int13.h diff --git a/src/arch/i386/include/ipxe/bios_nap.h b/src/arch/x86/include/ipxe/bios_nap.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_nap.h rename to src/arch/x86/include/ipxe/bios_nap.h diff --git a/src/arch/i386/include/ipxe/bios_reboot.h b/src/arch/x86/include/ipxe/bios_reboot.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_reboot.h rename to src/arch/x86/include/ipxe/bios_reboot.h diff --git a/src/arch/i386/include/ipxe/bios_sanboot.h b/src/arch/x86/include/ipxe/bios_sanboot.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_sanboot.h rename to src/arch/x86/include/ipxe/bios_sanboot.h diff --git a/src/arch/i386/include/ipxe/bios_smbios.h b/src/arch/x86/include/ipxe/bios_smbios.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_smbios.h rename to src/arch/x86/include/ipxe/bios_smbios.h diff --git a/src/arch/i386/include/ipxe/bios_timer.h b/src/arch/x86/include/ipxe/bios_timer.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_timer.h rename to src/arch/x86/include/ipxe/bios_timer.h diff --git a/src/arch/i386/include/ipxe/errno/pcbios.h b/src/arch/x86/include/ipxe/errno/pcbios.h similarity index 100% rename from src/arch/i386/include/ipxe/errno/pcbios.h rename to src/arch/x86/include/ipxe/errno/pcbios.h diff --git a/src/arch/i386/include/ipxe/memtop_umalloc.h b/src/arch/x86/include/ipxe/memtop_umalloc.h similarity index 100% rename from src/arch/i386/include/ipxe/memtop_umalloc.h rename to src/arch/x86/include/ipxe/memtop_umalloc.h diff --git a/src/arch/i386/include/ipxe/rdtsc_timer.h b/src/arch/x86/include/ipxe/rdtsc_timer.h similarity index 100% rename from src/arch/i386/include/ipxe/rdtsc_timer.h rename to src/arch/x86/include/ipxe/rdtsc_timer.h diff --git a/src/arch/i386/include/ipxe/rtc_entropy.h b/src/arch/x86/include/ipxe/rtc_entropy.h similarity index 100% rename from src/arch/i386/include/ipxe/rtc_entropy.h rename to src/arch/x86/include/ipxe/rtc_entropy.h diff --git a/src/arch/i386/include/ipxe/rtc_time.h b/src/arch/x86/include/ipxe/rtc_time.h similarity index 100% rename from src/arch/i386/include/ipxe/rtc_time.h rename to src/arch/x86/include/ipxe/rtc_time.h diff --git a/src/arch/i386/include/ipxe/vesafb.h b/src/arch/x86/include/ipxe/vesafb.h similarity index 100% rename from src/arch/i386/include/ipxe/vesafb.h rename to src/arch/x86/include/ipxe/vesafb.h diff --git a/src/arch/i386/include/kir.h b/src/arch/x86/include/kir.h similarity index 100% rename from src/arch/i386/include/kir.h rename to src/arch/x86/include/kir.h diff --git a/src/arch/i386/include/libkir.h b/src/arch/x86/include/libkir.h similarity index 100% rename from src/arch/i386/include/libkir.h rename to src/arch/x86/include/libkir.h diff --git a/src/arch/i386/include/librm.h b/src/arch/x86/include/librm.h similarity index 100% rename from src/arch/i386/include/librm.h rename to src/arch/x86/include/librm.h diff --git a/src/arch/i386/include/memsizes.h b/src/arch/x86/include/memsizes.h similarity index 100% rename from src/arch/i386/include/memsizes.h rename to src/arch/x86/include/memsizes.h diff --git a/src/arch/i386/include/multiboot.h b/src/arch/x86/include/multiboot.h similarity index 100% rename from src/arch/i386/include/multiboot.h rename to src/arch/x86/include/multiboot.h diff --git a/src/arch/i386/include/pnpbios.h b/src/arch/x86/include/pnpbios.h similarity index 100% rename from src/arch/i386/include/pnpbios.h rename to src/arch/x86/include/pnpbios.h diff --git a/src/arch/i386/include/pxe.h b/src/arch/x86/include/pxe.h similarity index 100% rename from src/arch/i386/include/pxe.h rename to src/arch/x86/include/pxe.h diff --git a/src/arch/i386/include/pxe_api.h b/src/arch/x86/include/pxe_api.h similarity index 100% rename from src/arch/i386/include/pxe_api.h rename to src/arch/x86/include/pxe_api.h diff --git a/src/arch/i386/include/pxe_call.h b/src/arch/x86/include/pxe_call.h similarity index 100% rename from src/arch/i386/include/pxe_call.h rename to src/arch/x86/include/pxe_call.h diff --git a/src/arch/i386/include/pxe_error.h b/src/arch/x86/include/pxe_error.h similarity index 100% rename from src/arch/i386/include/pxe_error.h rename to src/arch/x86/include/pxe_error.h diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/x86/include/pxe_types.h similarity index 100% rename from src/arch/i386/include/pxe_types.h rename to src/arch/x86/include/pxe_types.h diff --git a/src/arch/i386/include/pxeparent.h b/src/arch/x86/include/pxeparent.h similarity index 100% rename from src/arch/i386/include/pxeparent.h rename to src/arch/x86/include/pxeparent.h diff --git a/src/arch/i386/include/realmode.h b/src/arch/x86/include/realmode.h similarity index 100% rename from src/arch/i386/include/realmode.h rename to src/arch/x86/include/realmode.h diff --git a/src/arch/i386/include/registers.h b/src/arch/x86/include/registers.h similarity index 100% rename from src/arch/i386/include/registers.h rename to src/arch/x86/include/registers.h diff --git a/src/arch/i386/include/rmsetjmp.h b/src/arch/x86/include/rmsetjmp.h similarity index 100% rename from src/arch/i386/include/rmsetjmp.h rename to src/arch/x86/include/rmsetjmp.h diff --git a/src/arch/i386/include/rtc.h b/src/arch/x86/include/rtc.h similarity index 100% rename from src/arch/i386/include/rtc.h rename to src/arch/x86/include/rtc.h diff --git a/src/arch/i386/include/sdi.h b/src/arch/x86/include/sdi.h similarity index 100% rename from src/arch/i386/include/sdi.h rename to src/arch/x86/include/sdi.h diff --git a/src/arch/i386/include/undi.h b/src/arch/x86/include/undi.h similarity index 100% rename from src/arch/i386/include/undi.h rename to src/arch/x86/include/undi.h diff --git a/src/arch/i386/include/undiload.h b/src/arch/x86/include/undiload.h similarity index 100% rename from src/arch/i386/include/undiload.h rename to src/arch/x86/include/undiload.h diff --git a/src/arch/i386/include/undinet.h b/src/arch/x86/include/undinet.h similarity index 100% rename from src/arch/i386/include/undinet.h rename to src/arch/x86/include/undinet.h diff --git a/src/arch/i386/include/undipreload.h b/src/arch/x86/include/undipreload.h similarity index 100% rename from src/arch/i386/include/undipreload.h rename to src/arch/x86/include/undipreload.h diff --git a/src/arch/i386/include/undirom.h b/src/arch/x86/include/undirom.h similarity index 100% rename from src/arch/i386/include/undirom.h rename to src/arch/x86/include/undirom.h diff --git a/src/arch/i386/include/vga.h b/src/arch/x86/include/vga.h similarity index 100% rename from src/arch/i386/include/vga.h rename to src/arch/x86/include/vga.h diff --git a/src/arch/i386/interface/pcbios/apm.c b/src/arch/x86/interface/pcbios/apm.c similarity index 100% rename from src/arch/i386/interface/pcbios/apm.c rename to src/arch/x86/interface/pcbios/apm.c diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/x86/interface/pcbios/basemem.c similarity index 100% rename from src/arch/i386/firmware/pcbios/basemem.c rename to src/arch/x86/interface/pcbios/basemem.c diff --git a/src/arch/i386/firmware/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c similarity index 100% rename from src/arch/i386/firmware/pcbios/bios_console.c rename to src/arch/x86/interface/pcbios/bios_console.c diff --git a/src/arch/i386/interface/pcbios/bios_nap.c b/src/arch/x86/interface/pcbios/bios_nap.c similarity index 100% rename from src/arch/i386/interface/pcbios/bios_nap.c rename to src/arch/x86/interface/pcbios/bios_nap.c diff --git a/src/arch/i386/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c similarity index 100% rename from src/arch/i386/interface/pcbios/bios_reboot.c rename to src/arch/x86/interface/pcbios/bios_reboot.c diff --git a/src/arch/i386/interface/pcbios/bios_smbios.c b/src/arch/x86/interface/pcbios/bios_smbios.c similarity index 100% rename from src/arch/i386/interface/pcbios/bios_smbios.c rename to src/arch/x86/interface/pcbios/bios_smbios.c diff --git a/src/arch/i386/interface/pcbios/bios_timer.c b/src/arch/x86/interface/pcbios/bios_timer.c similarity index 100% rename from src/arch/i386/interface/pcbios/bios_timer.c rename to src/arch/x86/interface/pcbios/bios_timer.c diff --git a/src/arch/i386/interface/pcbios/biosint.c b/src/arch/x86/interface/pcbios/biosint.c similarity index 100% rename from src/arch/i386/interface/pcbios/biosint.c rename to src/arch/x86/interface/pcbios/biosint.c diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/x86/interface/pcbios/e820mangler.S similarity index 100% rename from src/arch/i386/firmware/pcbios/e820mangler.S rename to src/arch/x86/interface/pcbios/e820mangler.S diff --git a/src/arch/i386/firmware/pcbios/fakee820.c b/src/arch/x86/interface/pcbios/fakee820.c similarity index 100% rename from src/arch/i386/firmware/pcbios/fakee820.c rename to src/arch/x86/interface/pcbios/fakee820.c diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/x86/interface/pcbios/hidemem.c similarity index 100% rename from src/arch/i386/firmware/pcbios/hidemem.c rename to src/arch/x86/interface/pcbios/hidemem.c diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c similarity index 100% rename from src/arch/i386/interface/pcbios/int13.c rename to src/arch/x86/interface/pcbios/int13.c diff --git a/src/arch/i386/interface/pcbios/int13con.c b/src/arch/x86/interface/pcbios/int13con.c similarity index 100% rename from src/arch/i386/interface/pcbios/int13con.c rename to src/arch/x86/interface/pcbios/int13con.c diff --git a/src/arch/i386/firmware/pcbios/memmap.c b/src/arch/x86/interface/pcbios/memmap.c similarity index 100% rename from src/arch/i386/firmware/pcbios/memmap.c rename to src/arch/x86/interface/pcbios/memmap.c diff --git a/src/arch/i386/interface/pcbios/memtop_umalloc.c b/src/arch/x86/interface/pcbios/memtop_umalloc.c similarity index 100% rename from src/arch/i386/interface/pcbios/memtop_umalloc.c rename to src/arch/x86/interface/pcbios/memtop_umalloc.c diff --git a/src/arch/i386/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c similarity index 100% rename from src/arch/i386/interface/pcbios/pcibios.c rename to src/arch/x86/interface/pcbios/pcibios.c diff --git a/src/arch/i386/firmware/pcbios/pnpbios.c b/src/arch/x86/interface/pcbios/pnpbios.c similarity index 100% rename from src/arch/i386/firmware/pcbios/pnpbios.c rename to src/arch/x86/interface/pcbios/pnpbios.c diff --git a/src/arch/i386/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c similarity index 100% rename from src/arch/i386/interface/pcbios/rtc_entropy.c rename to src/arch/x86/interface/pcbios/rtc_entropy.c diff --git a/src/arch/i386/interface/pcbios/rtc_time.c b/src/arch/x86/interface/pcbios/rtc_time.c similarity index 100% rename from src/arch/i386/interface/pcbios/rtc_time.c rename to src/arch/x86/interface/pcbios/rtc_time.c diff --git a/src/arch/i386/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c similarity index 100% rename from src/arch/i386/interface/pcbios/vesafb.c rename to src/arch/x86/interface/pcbios/vesafb.c diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/x86/interface/pxe/pxe_call.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_call.c rename to src/arch/x86/interface/pxe/pxe_call.c diff --git a/src/arch/i386/interface/pxe/pxe_entry.S b/src/arch/x86/interface/pxe/pxe_entry.S similarity index 100% rename from src/arch/i386/interface/pxe/pxe_entry.S rename to src/arch/x86/interface/pxe/pxe_entry.S diff --git a/src/arch/i386/interface/pxe/pxe_exit_hook.c b/src/arch/x86/interface/pxe/pxe_exit_hook.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_exit_hook.c rename to src/arch/x86/interface/pxe/pxe_exit_hook.c diff --git a/src/arch/i386/interface/pxe/pxe_file.c b/src/arch/x86/interface/pxe/pxe_file.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_file.c rename to src/arch/x86/interface/pxe/pxe_file.c diff --git a/src/arch/i386/interface/pxe/pxe_loader.c b/src/arch/x86/interface/pxe/pxe_loader.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_loader.c rename to src/arch/x86/interface/pxe/pxe_loader.c diff --git a/src/arch/i386/interface/pxe/pxe_preboot.c b/src/arch/x86/interface/pxe/pxe_preboot.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_preboot.c rename to src/arch/x86/interface/pxe/pxe_preboot.c diff --git a/src/arch/i386/interface/pxe/pxe_tftp.c b/src/arch/x86/interface/pxe/pxe_tftp.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_tftp.c rename to src/arch/x86/interface/pxe/pxe_tftp.c diff --git a/src/arch/i386/interface/pxe/pxe_udp.c b/src/arch/x86/interface/pxe/pxe_udp.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_udp.c rename to src/arch/x86/interface/pxe/pxe_udp.c diff --git a/src/arch/i386/interface/pxe/pxe_undi.c b/src/arch/x86/interface/pxe/pxe_undi.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_undi.c rename to src/arch/x86/interface/pxe/pxe_undi.c diff --git a/src/arch/i386/interface/pxeparent/pxeparent.c b/src/arch/x86/interface/pxeparent/pxeparent.c similarity index 100% rename from src/arch/i386/interface/pxeparent/pxeparent.c rename to src/arch/x86/interface/pxeparent/pxeparent.c diff --git a/src/arch/i386/prefix/bootpart.S b/src/arch/x86/prefix/bootpart.S similarity index 100% rename from src/arch/i386/prefix/bootpart.S rename to src/arch/x86/prefix/bootpart.S diff --git a/src/arch/i386/prefix/dskprefix.S b/src/arch/x86/prefix/dskprefix.S similarity index 100% rename from src/arch/i386/prefix/dskprefix.S rename to src/arch/x86/prefix/dskprefix.S diff --git a/src/arch/i386/prefix/exeprefix.S b/src/arch/x86/prefix/exeprefix.S similarity index 100% rename from src/arch/i386/prefix/exeprefix.S rename to src/arch/x86/prefix/exeprefix.S diff --git a/src/arch/i386/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S similarity index 100% rename from src/arch/i386/prefix/hdprefix.S rename to src/arch/x86/prefix/hdprefix.S diff --git a/src/arch/i386/prefix/isaromprefix.S b/src/arch/x86/prefix/isaromprefix.S similarity index 100% rename from src/arch/i386/prefix/isaromprefix.S rename to src/arch/x86/prefix/isaromprefix.S diff --git a/src/arch/i386/prefix/kkkpxeprefix.S b/src/arch/x86/prefix/kkkpxeprefix.S similarity index 100% rename from src/arch/i386/prefix/kkkpxeprefix.S rename to src/arch/x86/prefix/kkkpxeprefix.S diff --git a/src/arch/i386/prefix/kkpxeprefix.S b/src/arch/x86/prefix/kkpxeprefix.S similarity index 100% rename from src/arch/i386/prefix/kkpxeprefix.S rename to src/arch/x86/prefix/kkpxeprefix.S diff --git a/src/arch/i386/prefix/kpxeprefix.S b/src/arch/x86/prefix/kpxeprefix.S similarity index 100% rename from src/arch/i386/prefix/kpxeprefix.S rename to src/arch/x86/prefix/kpxeprefix.S diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S similarity index 100% rename from src/arch/i386/prefix/libprefix.S rename to src/arch/x86/prefix/libprefix.S diff --git a/src/arch/i386/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S similarity index 100% rename from src/arch/i386/prefix/lkrnprefix.S rename to src/arch/x86/prefix/lkrnprefix.S diff --git a/src/arch/i386/prefix/mbr.S b/src/arch/x86/prefix/mbr.S similarity index 100% rename from src/arch/i386/prefix/mbr.S rename to src/arch/x86/prefix/mbr.S diff --git a/src/arch/i386/prefix/mromprefix.S b/src/arch/x86/prefix/mromprefix.S similarity index 100% rename from src/arch/i386/prefix/mromprefix.S rename to src/arch/x86/prefix/mromprefix.S diff --git a/src/arch/i386/prefix/nbiprefix.S b/src/arch/x86/prefix/nbiprefix.S similarity index 100% rename from src/arch/i386/prefix/nbiprefix.S rename to src/arch/x86/prefix/nbiprefix.S diff --git a/src/arch/i386/prefix/nullprefix.S b/src/arch/x86/prefix/nullprefix.S similarity index 100% rename from src/arch/i386/prefix/nullprefix.S rename to src/arch/x86/prefix/nullprefix.S diff --git a/src/arch/i386/prefix/pciromprefix.S b/src/arch/x86/prefix/pciromprefix.S similarity index 100% rename from src/arch/i386/prefix/pciromprefix.S rename to src/arch/x86/prefix/pciromprefix.S diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S similarity index 100% rename from src/arch/i386/prefix/pxeprefix.S rename to src/arch/x86/prefix/pxeprefix.S diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S similarity index 100% rename from src/arch/i386/prefix/romprefix.S rename to src/arch/x86/prefix/romprefix.S diff --git a/src/arch/i386/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S similarity index 100% rename from src/arch/i386/prefix/undiloader.S rename to src/arch/x86/prefix/undiloader.S diff --git a/src/arch/i386/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S similarity index 100% rename from src/arch/i386/prefix/unlzma.S rename to src/arch/x86/prefix/unlzma.S diff --git a/src/arch/i386/prefix/unlzma16.S b/src/arch/x86/prefix/unlzma16.S similarity index 100% rename from src/arch/i386/prefix/unlzma16.S rename to src/arch/x86/prefix/unlzma16.S diff --git a/src/arch/i386/prefix/usbdisk.S b/src/arch/x86/prefix/usbdisk.S similarity index 100% rename from src/arch/i386/prefix/usbdisk.S rename to src/arch/x86/prefix/usbdisk.S diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/x86/scripts/pcbios.lds similarity index 100% rename from src/arch/i386/scripts/i386.lds rename to src/arch/x86/scripts/pcbios.lds diff --git a/src/arch/i386/transitions/liba20.S b/src/arch/x86/transitions/liba20.S similarity index 100% rename from src/arch/i386/transitions/liba20.S rename to src/arch/x86/transitions/liba20.S diff --git a/src/arch/i386/transitions/libkir.S b/src/arch/x86/transitions/libkir.S similarity index 100% rename from src/arch/i386/transitions/libkir.S rename to src/arch/x86/transitions/libkir.S diff --git a/src/arch/i386/transitions/libpm.S b/src/arch/x86/transitions/libpm.S similarity index 100% rename from src/arch/i386/transitions/libpm.S rename to src/arch/x86/transitions/libpm.S diff --git a/src/arch/i386/transitions/librm.S b/src/arch/x86/transitions/librm.S similarity index 100% rename from src/arch/i386/transitions/librm.S rename to src/arch/x86/transitions/librm.S diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c similarity index 100% rename from src/arch/i386/transitions/librm_mgmt.c rename to src/arch/x86/transitions/librm_mgmt.c diff --git a/src/arch/i386/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c similarity index 100% rename from src/arch/i386/transitions/librm_test.c rename to src/arch/x86/transitions/librm_test.c diff --git a/src/arch/x86_64/Makefile.pcbios b/src/arch/x86_64/Makefile.pcbios new file mode 100644 index 000000000..dfb8db0a0 --- /dev/null +++ b/src/arch/x86_64/Makefile.pcbios @@ -0,0 +1,6 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Include generic BIOS Makefile +# +MAKEDEPS += arch/x86/Makefile.pcbios +include arch/x86/Makefile.pcbios diff --git a/src/arch/x86_64/include/bits/entropy.h b/src/arch/x86_64/include/bits/entropy.h deleted file mode 100644 index a9b3bc10e..000000000 --- a/src/arch/x86_64/include/bits/entropy.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_ENTROPY_H -#define _BITS_ENTROPY_H - -/** @file - * - * x86_64-specific entropy API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_ENTROPY_H */ diff --git a/src/arch/x86_64/include/bits/nap.h b/src/arch/x86_64/include/bits/nap.h deleted file mode 100644 index 8b42c0a4a..000000000 --- a/src/arch/x86_64/include/bits/nap.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_NAP_H -#define _BITS_NAP_H - -/** @file - * - * x86_64-specific CPU sleeping API implementations - * - */ - -#include - -#endif /* _BITS_MAP_H */ diff --git a/src/arch/x86_64/include/bits/reboot.h b/src/arch/x86_64/include/bits/reboot.h deleted file mode 100644 index f9bcd6a7b..000000000 --- a/src/arch/x86_64/include/bits/reboot.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_REBOOT_H -#define _BITS_REBOOT_H - -/** @file - * - * x86_64-specific reboot API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_REBOOT_H */ diff --git a/src/arch/x86_64/include/bits/sanboot.h b/src/arch/x86_64/include/bits/sanboot.h deleted file mode 100644 index dcab830f6..000000000 --- a/src/arch/x86_64/include/bits/sanboot.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_SANBOOT_H -#define _BITS_SANBOOT_H - -/** @file - * - * x86_64-specific sanboot API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_SANBOOT_H */ diff --git a/src/arch/x86_64/include/bits/smbios.h b/src/arch/x86_64/include/bits/smbios.h deleted file mode 100644 index 2f0118d02..000000000 --- a/src/arch/x86_64/include/bits/smbios.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _BITS_SMBIOS_H -#define _BITS_SMBIOS_H - -/** @file - * - * i386-specific SMBIOS API implementations - * - */ - -#endif /* _BITS_SMBIOS_H */ diff --git a/src/arch/x86_64/include/bits/time.h b/src/arch/x86_64/include/bits/time.h deleted file mode 100644 index aa74fac8c..000000000 --- a/src/arch/x86_64/include/bits/time.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_TIME_H -#define _BITS_TIME_H - -/** @file - * - * x86_64-specific time API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_TIME_H */ diff --git a/src/arch/x86_64/include/bits/timer.h b/src/arch/x86_64/include/bits/timer.h deleted file mode 100644 index dfa6c270c..000000000 --- a/src/arch/x86_64/include/bits/timer.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _BITS_TIMER_H -#define _BITS_TIMER_H - -/** @file - * - * x86_64-specific timer API implementations - * - */ - -#endif /* _BITS_TIMER_H */ diff --git a/src/arch/x86_64/include/bits/uaccess.h b/src/arch/x86_64/include/bits/uaccess.h deleted file mode 100644 index 455829242..000000000 --- a/src/arch/x86_64/include/bits/uaccess.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _BITS_UACCESS_H -#define _BITS_UACCESS_H - -/** @file - * - * x86_64-specific user access API implementations - * - */ - -#endif /* _BITS_UACCESS_H */ diff --git a/src/arch/x86_64/include/bits/umalloc.h b/src/arch/x86_64/include/bits/umalloc.h deleted file mode 100644 index 12bf949d1..000000000 --- a/src/arch/x86_64/include/bits/umalloc.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _BITS_UMALLOC_H -#define _BITS_UMALLOC_H - -/** @file - * - * x86_64-specific user memory allocation API implementations - * - */ - -#endif /* _BITS_UMALLOC_H */ diff --git a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h new file mode 100644 index 000000000..e07e4c192 --- /dev/null +++ b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * 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. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#define DHCP_ARCH_VENDOR_CLASS_ID \ + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ) + +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86 ) + +#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ) + +#endif From 9dc340d73538ecac72851407b4865e2a778589f8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 23:16:49 +0000 Subject: [PATCH 070/591] [librm] Discard argument as part of return from prot_call() Signed-off-by: Michael Brown --- src/arch/i386/interface/syslinux/comboot_call.c | 6 +++--- src/arch/x86/core/dumpregs.c | 1 - src/arch/x86/interface/pcbios/bios_console.c | 1 - src/arch/x86/interface/pxe/pxe_entry.S | 1 - src/arch/x86/prefix/dskprefix.S | 1 - src/arch/x86/prefix/exeprefix.S | 1 - src/arch/x86/prefix/hdprefix.S | 1 - src/arch/x86/prefix/libprefix.S | 1 - src/arch/x86/prefix/lkrnprefix.S | 1 - src/arch/x86/prefix/nbiprefix.S | 1 - src/arch/x86/prefix/pxeprefix.S | 1 - src/arch/x86/prefix/romprefix.S | 1 - src/arch/x86/prefix/undiloader.S | 4 +--- src/arch/x86/transitions/librm.S | 6 ++---- src/arch/x86/transitions/librm_test.c | 1 - 15 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index 22848006c..37cba1b77 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -663,7 +663,7 @@ void hook_comboot_interrupts ( ) { "pushl %0\n\t" "pushw %%cs\n\t" "call prot_call\n\t" - "addw $4, %%sp\n\t" + "clc\n\t" "call patch_cf\n\t" "iret\n\t" ) : : "i" ( int20 ) ); @@ -675,7 +675,7 @@ void hook_comboot_interrupts ( ) { "pushl %0\n\t" "pushw %%cs\n\t" "call prot_call\n\t" - "addw $4, %%sp\n\t" + "clc\n\t" "call patch_cf\n\t" "iret\n\t" ) : : "i" ( int21 ) ); @@ -687,7 +687,7 @@ void hook_comboot_interrupts ( ) { "pushl %0\n\t" "pushw %%cs\n\t" "call prot_call\n\t" - "addw $4, %%sp\n\t" + "clc\n\t" "call patch_cf\n\t" "iret\n\t" ) : : "i" ( int22) ); diff --git a/src/arch/x86/core/dumpregs.c b/src/arch/x86/core/dumpregs.c index 82dc21847..d23988d30 100644 --- a/src/arch/x86/core/dumpregs.c +++ b/src/arch/x86/core/dumpregs.c @@ -9,7 +9,6 @@ void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { "pushl $_dump_regs\n\t" "pushw %%cs\n\t" "call prot_call\n\t" - "addr32 leal 4(%%esp), %%esp\n\t" "ret\n\t" ) : : ); printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index cfffd9542..364c294da 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -536,7 +536,6 @@ static void bios_inject_startup ( void ) { "pushl %0\n\t" "pushw %%cs\n\t" "call prot_call\n\t" - "addw $4, %%sp\n\t" "\n1:\n\t" "popfw\n\t" "ljmp *%%cs:int16_vector\n\t" ) diff --git a/src/arch/x86/interface/pxe/pxe_entry.S b/src/arch/x86/interface/pxe/pxe_entry.S index 07852cd50..84eba1e06 100644 --- a/src/arch/x86/interface/pxe/pxe_entry.S +++ b/src/arch/x86/interface/pxe/pxe_entry.S @@ -123,7 +123,6 @@ pxenv_entry: pushl $pxe_api_call pushw %cs call prot_call - addl $4, %esp lret .size pxenv_entry, . - pxenv_entry diff --git a/src/arch/x86/prefix/dskprefix.S b/src/arch/x86/prefix/dskprefix.S index 7aa017ccd..d716a30f0 100644 --- a/src/arch/x86/prefix/dskprefix.S +++ b/src/arch/x86/prefix/dskprefix.S @@ -373,7 +373,6 @@ start_runtime: pushl $main pushw %cs call prot_call - popl %ecx /* discard */ /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/exeprefix.S b/src/arch/x86/prefix/exeprefix.S index 5c648d51d..35061b152 100644 --- a/src/arch/x86/prefix/exeprefix.S +++ b/src/arch/x86/prefix/exeprefix.S @@ -151,7 +151,6 @@ _exe_start: pushl $main pushw %cs call prot_call - popl %ecx /* discard */ /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S index 1d012d80b..9f5752aa8 100644 --- a/src/arch/x86/prefix/hdprefix.S +++ b/src/arch/x86/prefix/hdprefix.S @@ -102,7 +102,6 @@ start_image: pushl $main pushw %cs call prot_call - popl %ecx /* discard */ /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 3cdb6ec9a..897a6656f 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -880,7 +880,6 @@ payload_death_message: movw %ax, (prot_call_vector+2) pushl $relocate lcall *prot_call_vector - popl %edx /* discard */ /* Copy code to new location */ progress " copy\n" diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S index 64135e14b..34e2bdc8f 100644 --- a/src/arch/x86/prefix/lkrnprefix.S +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -200,7 +200,6 @@ no_cmd_line: pushl $main pushw %cs call prot_call - popl %ecx /* discard */ /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/nbiprefix.S b/src/arch/x86/prefix/nbiprefix.S index 16c79566c..539b5ebae 100644 --- a/src/arch/x86/prefix/nbiprefix.S +++ b/src/arch/x86/prefix/nbiprefix.S @@ -69,7 +69,6 @@ _nbi_start: pushl $main pushw %cs call prot_call - popl %ecx /* discard */ /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S index 465ce4345..c742add63 100644 --- a/src/arch/x86/prefix/pxeprefix.S +++ b/src/arch/x86/prefix/pxeprefix.S @@ -823,7 +823,6 @@ run_ipxe: pushl $main pushw %cs call prot_call - popl %ecx /* discard */ /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S index 8974c5398..57eb31af4 100644 --- a/src/arch/x86/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -807,7 +807,6 @@ exec: /* Set %ds = %cs */ pushl $main pushw %cs call prot_call - popl %eax /* discard */ /* Set up flat real mode for return to BIOS */ call flatten_real_mode diff --git a/src/arch/x86/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S index 5cace44b7..952661330 100644 --- a/src/arch/x86/prefix/undiloader.S +++ b/src/arch/x86/prefix/undiloader.S @@ -41,9 +41,7 @@ undiloader: pushw %ax pushw $prot_call lret -1: popw %bx /* discard */ - popw %bx /* discard */ - /* Restore registers and return */ +1: /* Restore registers and return */ popw %bx popw %es popw %ds diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 863e22415..bf79637a8 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -134,7 +134,6 @@ init_librm: pushl $init_idt pushw %cs call prot_call - popl %eax /* discard */ /* Restore registers */ negl %edi @@ -385,8 +384,8 @@ rm_gdtr: * * Example usage: * pushl $pxe_api_call + * pushw %cs * call prot_call - * addw $4, %sp * to call in to the C function * void pxe_api_call ( struct i386_all_regs *ix86 ); **************************************************************************** @@ -455,7 +454,7 @@ pc_rmode: */ addr32 movl -20(%esp), %esp popfl - lret + lret $4 /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) @@ -554,7 +553,6 @@ flatten_real_mode: pushl $flatten_dummy pushw %cs call prot_call - addw $4, %sp /* Restore GDT */ movb $0x00, real_cs + 6 movb $0x00, real_ds + 6 diff --git a/src/arch/x86/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c index 496d56124..f86584e3f 100644 --- a/src/arch/x86/transitions/librm_test.c +++ b/src/arch/x86/transitions/librm_test.c @@ -108,7 +108,6 @@ static void librm_test_exec ( void ) { "pushl %k3\n\t" "pushw %%cs\n\t" "call prot_call\n\t" - "addw $4, %%sp\n\t" "rdtsc\n\t" ) : "=a" ( stopped ), "=d" ( discard_d ), "=R" ( started ) From 079b98b63a43b48c15677ff92cd784b59ea4b6f1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Feb 2016 23:37:46 +0000 Subject: [PATCH 071/591] [librm] Discard argument as part of return from real_call() Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 1 - src/arch/x86/transitions/librm.S | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 44d931ea0..379e085a2 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -192,7 +192,6 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); #define REAL_CODE( asm_code_str ) \ "push $1f\n\t" \ "call real_call\n\t" \ - "addl $4, %%esp\n\t" \ TEXT16_CODE ( "\n1:\n\t" \ asm_code_str \ "\n\t" \ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index bf79637a8..a3b78c784 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -518,7 +518,7 @@ rc_rmode: rc_pmode: /* Restore registers and return */ popal - ret + ret $4 /* Function vector, used because "call xx(%sp)" is not a valid From a4f5c4647e5c3381dcb942d7e84c4f159e963580 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 17 Feb 2016 15:26:31 +0000 Subject: [PATCH 072/591] [prefix] Align INT 15,88 temporary decompression area to a page boundary Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 1 + 1 file changed, 1 insertion(+) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 897a6656f..0c8332825 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -827,6 +827,7 @@ payload_death_message: movw %ax, %di addl $0x400, %edi subl $_textdata_memsz_kb, %edi + andw $~0x03, %di shll $10, %edi /* Sanity check: if we have ended up below 1MB, use 1MB */ cmpl $0x100000, %edi From 4e4727079b718a8c0c62629f7d0df2f492074853 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 02:58:03 +0000 Subject: [PATCH 073/591] [romprefix] Align PMM temporary decompression area to a page boundary Signed-off-by: Michael Brown --- src/arch/x86/prefix/romprefix.S | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S index 57eb31af4..287a986c4 100644 --- a/src/arch/x86/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -402,19 +402,22 @@ pmm_scan: /* Shrink ROM */ movb shrunk_rom_size, %al movb %al, romheader_size -1: /* Allocate decompression PMM block. Round up the size to the - * nearest 128kB and use the size within the PMM handle; this - * allows the same decompression area to be shared between - * multiple iPXE ROMs even with differing build IDs +1: /* Allocate decompression PMM block. Allow 4kB for page + * alignment and round up the size to the nearest 128kB, then + * use the size within the PMM handle; this allows the same + * decompression area to be shared between multiple iPXE ROMs + * even with differing build IDs */ movl $_textdata_memsz_pgh, %ecx - addl $0x00001fff, %ecx - andl $0xffffe000, %ecx + addl $( 0x00000100 /* 4kB */ + 0x00001fff /* 128kB - 1 */ ), %ecx + andl $( 0xffffe000 /* ~( 128kB - 1 ) */ ), %ecx movl %ecx, %ebx shrw $12, %bx orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx movw $get_pmm_decompress_to, %bp call get_pmm + addl $( 0x00000fff /* 4kB - 1 */ ), %esi + andl $( 0xfffff000 /* ~( 4kB - 1 ) */ ), %esi movl %esi, decompress_to /* Restore registers */ popal From a3b4d6328c99c1c9bd731521e00bb8b495057931 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 14:38:41 +0000 Subject: [PATCH 074/591] [bios] Make uses of REAL_CODE() and PHYS_CODE() 64-bit clean On a 64-bit CPU, any modification of a register by 32-bit or 16-bit code will destroy the invisible upper 32 bits of the corresponding 64-bit register. For example: a 32-bit "pushl %eax" followed by a "popl %eax" will zero the upper half of %rax. This differs from the treatment of upper halves of 32-bit registers by 16-bit code: a "pushw %ax" followed by a "popw %ax" will leave the upper 16 bits of %eax unmodified. Inline assembly generated using REAL_CODE() or PHYS_CODE() will therefore have to preserve the upper halves of all registers, to avoid clobbering registers that gcc expects to be preserved. Output operands from REAL_CODE() and PHYS_CODE() assembly may therefore contain undefined values in the upper 32 bits. Fix by using explicit variable widths (e.g. uint32_t) for non-discarded output operands, to ensure that undefined values in the upper 32 bits of 64-bit registers are ignored. Signed-off-by: Michael Brown --- src/arch/x86/image/nbi.c | 4 ++-- src/arch/x86/interface/pcbios/memmap.c | 4 ++-- src/arch/x86/interface/pcbios/pcibios.c | 8 ++++---- src/arch/x86/interface/pcbios/rtc_entropy.c | 2 +- src/arch/x86/interface/pxeparent/pxeparent.c | 12 ++++++++---- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/arch/x86/image/nbi.c b/src/arch/x86/image/nbi.c index 99046144d..b691bee20 100644 --- a/src/arch/x86/image/nbi.c +++ b/src/arch/x86/image/nbi.c @@ -241,7 +241,7 @@ static int nbi_process_segments ( struct image *image, */ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) { int discard_D, discard_S, discard_b; - int rc; + int32_t rc; DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image, imgheader->execaddr.segoff.segment, @@ -283,7 +283,7 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) { 0 }; int discard_D, discard_S, discard_b; - int rc; + int32_t rc; DBGC ( image, "NBI %p executing 32-bit image at %lx\n", image, imgheader->execaddr.linear ); diff --git a/src/arch/x86/interface/pcbios/memmap.c b/src/arch/x86/interface/pcbios/memmap.c index 88bfec9f4..daae382b8 100644 --- a/src/arch/x86/interface/pcbios/memmap.c +++ b/src/arch/x86/interface/pcbios/memmap.c @@ -174,7 +174,7 @@ static int meme820 ( struct memory_map *memmap ) { struct memory_region *prev_region = NULL; uint32_t next = 0; uint32_t smap; - size_t size; + uint32_t size; unsigned int flags; unsigned int discard_D; @@ -216,7 +216,7 @@ static int meme820 ( struct memory_map *memmap ) { } if ( size < E820_MIN_SIZE ) { - DBG ( "INT 15,e820 returned only %zd bytes\n", size ); + DBG ( "INT 15,e820 returned only %d bytes\n", size ); return -EINVAL; } diff --git a/src/arch/x86/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c index 34efa0b39..07ac0c18d 100644 --- a/src/arch/x86/interface/pcbios/pcibios.c +++ b/src/arch/x86/interface/pcbios/pcibios.c @@ -70,7 +70,7 @@ static int pcibios_num_bus ( void ) { */ int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ int discard_b, discard_D; - int status; + uint16_t status; __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" "int $0x1a\n\t" @@ -85,7 +85,7 @@ int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ "b" ( pci->busdevfn ) : "edx" ); - return ( ( status >> 8 ) & 0xff ); + return ( status >> 8 ); } /** @@ -98,7 +98,7 @@ int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ */ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ int discard_b, discard_c, discard_D; - int status; + uint16_t status; __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" "int $0x1a\n\t" @@ -111,7 +111,7 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ "b" ( pci->busdevfn ), "c" ( value ) : "edx" ); - return ( ( status >> 8 ) & 0xff ); + return ( status >> 8 ); } PROVIDE_PCIAPI ( pcbios, pci_num_bus, pcibios_num_bus ); diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c index 6e7ac833e..83c2445f8 100644 --- a/src/arch/x86/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -187,7 +187,7 @@ uint8_t rtc_sample ( void ) { /* Disable interrupts */ "cli\n\t" ) - : "=a" ( after ), "=d" ( before ), "=q" ( temp ) + : "=a" ( after ), "=d" ( before ), "=Q" ( temp ) : "2" ( 0 ) ); return ( after - before ); diff --git a/src/arch/x86/interface/pxeparent/pxeparent.c b/src/arch/x86/interface/pxeparent/pxeparent.c index 0b6be9a03..cc6101c1f 100644 --- a/src/arch/x86/interface/pxeparent/pxeparent.c +++ b/src/arch/x86/interface/pxeparent/pxeparent.c @@ -208,8 +208,10 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function, void *params, size_t params_len ) { struct pxeparent_profiler *profiler = pxeparent_profiler ( function ); PXENV_EXIT_t exit; - unsigned long started; - unsigned long stopped; + uint32_t before; + uint32_t started; + uint32_t stopped; + uint32_t after; int discard_D; int rc; @@ -240,12 +242,14 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function, "D" ( __from_data16 ( &pxeparent_params ) ) : "ecx", "esi" ); profile_stop ( &profiler->total ); - profile_start_at ( &profiler->p2r, profile_started ( &profiler->total)); + before = profile_started ( &profiler->total ); + after = profile_stopped ( &profiler->total ); + profile_start_at ( &profiler->p2r, before ); profile_stop_at ( &profiler->p2r, started ); profile_start_at ( &profiler->ext, started ); profile_stop_at ( &profiler->ext, stopped ); profile_start_at ( &profiler->r2p, stopped ); - profile_stop_at ( &profiler->r2p, profile_stopped ( &profiler->total )); + profile_stop_at ( &profiler->r2p, after ); /* Determine return status code based on PXENV_EXIT and * PXENV_STATUS From b1436e0b836a6858aee097c1048c3ce669cea81b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 15:40:33 +0000 Subject: [PATCH 075/591] [librm] Use garbage-collectable section names Allow unused sections of librm.o to be removed via --gc-sections. Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 48 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index a3b78c784..6cbc37954 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) * "non absolute segment" error. This is most probably a bug in gas. **************************************************************************** */ - .section ".data16", "aw", @progbits + .section ".data16.gdt", "aw", @progbits .align 16 gdt: gdtr: /* The first GDT entry is unused, the GDTR can fit here. */ @@ -89,7 +89,7 @@ gdt_end: * %edi : Physical base of protected-mode code (virt_offset) **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.init_librm", "ax", @progbits .code16 .globl init_librm init_librm: @@ -141,7 +141,7 @@ init_librm: popl %eax lret - .section ".text16", "ax", @progbits + .section ".text16.set_seg_base", "ax", @progbits .code16 set_seg_base: 1: movw %ax, 2(%bx) @@ -167,7 +167,7 @@ set_seg_base: * **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.real_to_prot", "ax", @progbits .code16 real_to_prot: /* Enable A20 line */ @@ -219,7 +219,7 @@ real_to_prot: orb $CR0_PE, %al movl %eax, %cr0 data32 ljmp $VIRTUAL_CS, $r2p_pmode - .section ".text", "ax", @progbits + .section ".text.real_to_prot", "ax", @progbits .code32 r2p_pmode: /* Set up protected-mode data segments and stack pointer */ @@ -272,7 +272,7 @@ r2p_pmode: * **************************************************************************** */ - .section ".text", "ax", @progbits + .section ".text.prot_to_real", "ax", @progbits .code32 prot_to_real: /* Copy real-mode global descriptor table register to RM code segment */ @@ -311,7 +311,7 @@ prot_to_real: movw %ax, %gs movw %ax, %ss ljmp $REAL_CS, $p2r_rmode - .section ".text16", "ax", @progbits + .section ".text16.prot_to_real", "ax", @progbits .code16 p2r_rmode: /* Load real-mode GDT */ @@ -347,12 +347,12 @@ p2r_ljmp_rm_cs: .globl rm_cs .equ rm_cs, ( p2r_ljmp_rm_cs + 3 ) - .section ".text16.data", "aw", @progbits + .section ".text16.data.rm_ds", "aw", @progbits .globl rm_ds rm_ds: .word 0 /* Real-mode global and interrupt descriptor table registers */ - .section ".text16.data", "aw", @progbits + .section ".text16.data.rm_gdtr", "aw", @progbits rm_gdtr: .word 0 /* Limit */ .long 0 /* Base */ @@ -398,7 +398,7 @@ rm_gdtr: #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 ) #define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 ) - .section ".text16", "ax", @progbits + .section ".text16.prot_call", "ax", @progbits .code16 .globl prot_call prot_call: @@ -423,7 +423,7 @@ prot_call: movl $PC_OFFSET_END, %ecx pushl $pc_pmode jmp real_to_prot - .section ".text", "ax", @progbits + .section ".text.prot_call", "ax", @progbits .code32 pc_pmode: /* Call function */ @@ -437,7 +437,7 @@ pc_pmode: movl %esp, %esi pushl $pc_rmode jmp prot_to_real - .section ".text16", "ax", @progbits + .section ".text16.prot_call", "ax", @progbits .code16 pc_rmode: /* Restore registers and flags and return */ @@ -484,7 +484,7 @@ pc_rmode: #define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 ) #define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 ) - .section ".text", "ax", @progbits + .section ".text.real_call", "ax", @progbits .code32 .globl real_call real_call: @@ -497,7 +497,7 @@ real_call: pushl $rc_rmode movl $rm_default_gdtr_idtr, %esi jmp prot_to_real - .section ".text16", "ax", @progbits + .section ".text16.real_call", "ax", @progbits .code16 rc_rmode: /* Call real-mode function */ @@ -513,7 +513,7 @@ rc_rmode: movl $RC_OFFSET_RETADDR, %ecx pushl $rc_pmode jmp real_to_prot - .section ".text", "ax", @progbits + .section ".text.real_call", "ax", @progbits .code32 rc_pmode: /* Restore registers and return */ @@ -524,11 +524,11 @@ rc_pmode: /* Function vector, used because "call xx(%sp)" is not a valid * 16-bit expression. */ - .section ".data16", "aw", @progbits + .section ".bss16.rc_function", "aw", @nobits rc_function: .word 0, 0 /* Default real-mode global and interrupt descriptor table registers */ - .section ".data", "aw", @progbits + .section ".data.rm_default_gdtr_idtr", "aw", @progbits rm_default_gdtr_idtr: .word 0 /* Global descriptor table limit */ .long 0 /* Global descriptor table base */ @@ -542,7 +542,7 @@ rm_default_gdtr_idtr: * **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.flatten_real_mode", "ax", @progbits .code16 .globl flatten_real_mode flatten_real_mode: @@ -559,7 +559,7 @@ flatten_real_mode: /* Return */ ret - .section ".text", "ax", @progbits + .section ".text.flatten_dummy", "ax", @progbits .code32 flatten_dummy: ret @@ -638,11 +638,15 @@ interrupt_wrapper: * to us. **************************************************************************** */ - .section ".data", "aw", @progbits + .section ".bss.rm_sp", "aw", @nobits .globl rm_sp rm_sp: .word 0 + + .section ".bss.rm_ss", "aw", @nobits .globl rm_ss rm_ss: .word 0 + + .section ".data.pm_esp", "aw", @progbits pm_esp: .long _estack /**************************************************************************** @@ -654,13 +658,13 @@ pm_esp: .long _estack **************************************************************************** */ /* Internal copies, created by init_librm (which runs in real mode) */ - .section ".data16", "aw", @progbits + .section ".bss16.rm_virt_offset", "aw", @nobits rm_virt_offset: .long 0 rm_text16: .long 0 rm_data16: .long 0 /* Externally-visible copies, created by real_to_prot */ - .section ".data", "aw", @progbits + .section ".bss.virt_offset", "aw", @nobits .globl virt_offset virt_offset: .long 0 .globl text16 From f0ea1f4d77e77a0d163ee34491006edded111b03 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 15:56:41 +0000 Subject: [PATCH 076/591] [bios] Use an 8kB stack for x86_64 Signed-off-by: Michael Brown --- src/arch/x86/core/stack.S | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arch/x86/core/stack.S b/src/arch/x86/core/stack.S index 98f1cd9b9..995c397ca 100644 --- a/src/arch/x86/core/stack.S +++ b/src/arch/x86/core/stack.S @@ -2,6 +2,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 +#ifdef __x86_64__ +#define STACK_SIZE 8192 +#else +#define STACK_SIZE 4096 +#endif + /**************************************************************************** * Internal stack **************************************************************************** @@ -10,6 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .align 8 .globl _stack _stack: - .space 4096 + .space STACK_SIZE .globl _estack _estack: From 02a88b748921a7a97d2a5aed08f450c67f2422a1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 16:02:55 +0000 Subject: [PATCH 077/591] [prefix] Use garbage-collectable section names Allow unused sections of libprefix.o to be removed via --gc-sections. Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 0c8332825..186f3d5b1 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -69,7 +69,7 @@ progress_\@: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_character", "awx", @progbits .code16 .globl print_character print_character: @@ -107,7 +107,7 @@ print_character: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_space", "awx", @progbits .code16 .globl print_space print_space: @@ -132,7 +132,7 @@ print_space: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_message", "awx", @progbits .code16 .globl print_message print_message: @@ -162,7 +162,7 @@ print_message: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_hex", "awx", @progbits .code16 .globl print_hex_dword print_hex_dword: @@ -210,7 +210,7 @@ print_hex_nibble: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_pci_busdevfn", "awx", @progbits .code16 .globl print_pci_busdevfn print_pci_busdevfn: @@ -247,7 +247,7 @@ print_pci_busdevfn: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_kill_line", "awx", @progbits .code16 .globl print_kill_line print_kill_line: @@ -285,7 +285,7 @@ print_kill_line: * None **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.copy_bytes", "awx", @progbits .code16 copy_bytes: pushl %ecx @@ -308,7 +308,7 @@ copy_bytes: * None **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.zero_bytes", "awx", @progbits .code16 zero_bytes: pushl %ecx @@ -343,7 +343,7 @@ zero_bytes: * None **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.process_bytes", "awx", @progbits .code16 process_bytes: @@ -495,7 +495,7 @@ process_bytes: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.install_block", "awx", @progbits .code16 install_block: /* Preserve registers */ @@ -544,7 +544,7 @@ install_block: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.alloc_basemem", "awx", @progbits .code16 .globl alloc_basemem alloc_basemem: @@ -591,7 +591,7 @@ alloc_basemem: * none **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.free_basemem", "ax", @progbits .code16 .globl free_basemem free_basemem: @@ -625,7 +625,7 @@ free_basemem: ret .size free_basemem, . - free_basemem - .section ".text16.data", "aw", @progbits + .section ".text16.data.hooked_bios_interrupts", "aw", @progbits .globl hooked_bios_interrupts hooked_bios_interrupts: .word 0 @@ -645,7 +645,7 @@ hooked_bios_interrupts: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.install", "awx", @progbits .code16 .globl install install: @@ -688,7 +688,7 @@ install: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.install_prealloc", "awx", @progbits .code16 .globl install_prealloc install_prealloc: @@ -751,7 +751,7 @@ install_prealloc: xorw %di, %di call print_message 2: jmp 2b - .section ".prefix.data", "aw", @progbits + .section ".prefix.data.a20_death_message", "aw", @progbits a20_death_message: .asciz "\nHigh memory inaccessible - cannot continue\n" .size a20_death_message, . - a20_death_message @@ -780,7 +780,7 @@ a20_death_message: cli hlt jmp 2b - .section ".prefix.data", "aw", @progbits + .section ".prefix.data.payload_death_message", "aw", @progbits payload_death_message: .asciz "\nPayload inaccessible - cannot continue\n" .size payload_death_message, . - payload_death_message @@ -918,7 +918,7 @@ payload_death_message: /* Vectors for far calls to .text16 functions. Must be in * .data16, since .prefix may not be writable. */ - .section ".data16", "aw", @progbits + .section ".data16.install_prealloc", "aw", @progbits #ifdef KEEP_IT_REAL init_libkir_vector: .word init_libkir @@ -940,7 +940,7 @@ close_payload_vector: .size close_payload_vector, . - close_payload_vector /* Dummy routines to open and close payload */ - .section ".text16.early.data", "aw", @progbits + .section ".text16.early.data.open_payload", "aw", @progbits .weak open_payload .weak close_payload open_payload: @@ -963,7 +963,7 @@ close_payload: * none **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.uninstall", "ax", @progbits .code16 .globl uninstall uninstall: From adac4b1984721aad4cf4f78482dd9208f56d2f18 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 16:41:48 +0000 Subject: [PATCH 078/591] [librm] Simplify definitions for prot_call() and real_call() stack frames Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 6cbc37954..c702c0ccb 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -390,13 +390,14 @@ rm_gdtr: * void pxe_api_call ( struct i386_all_regs *ix86 ); **************************************************************************** */ - -#define PC_OFFSET_GDT ( 0 ) -#define PC_OFFSET_IDT ( PC_OFFSET_GDT + 6 ) -#define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 6 ) -#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS ) -#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 ) -#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 ) + .struct 0 +PC_OFFSET_GDT: .space 6 +PC_OFFSET_IDT: .space 6 +PC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS +PC_OFFSET_RETADDR: .space 4 +PC_OFFSET_FUNCTION: .space 4 +PC_OFFSET_END: + .previous .section ".text16.prot_call", "ax", @progbits .code16 @@ -478,11 +479,13 @@ pc_rmode: * Returns: none **************************************************************************** */ - -#define RC_OFFSET_PRESERVE_REGS ( 0 ) -#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS ) -#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 ) -#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 ) + .struct 0 +RC_OFFSET_REGS: .space SIZEOF_I386_REGS +RC_OFFSET_REGS_END: +RC_OFFSET_RETADDR: .space 4 +RC_OFFSET_FUNCTION: .space 4 +RC_OFFSET_END: + .previous .section ".text.real_call", "ax", @progbits .code32 @@ -493,7 +496,7 @@ real_call: pushl RC_OFFSET_FUNCTION(%esp) /* Switch to real mode and move register dump to RM stack */ - movl $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx + movl $( RC_OFFSET_REGS_END + 4 /* function pointer copy */ ), %ecx pushl $rc_rmode movl $rm_default_gdtr_idtr, %esi jmp prot_to_real @@ -510,7 +513,7 @@ rc_rmode: cld /* Switch to protected mode and move register dump back to PM stack */ - movl $RC_OFFSET_RETADDR, %ecx + movl $RC_OFFSET_REGS_END, %ecx pushl $rc_pmode jmp real_to_prot .section ".text.real_call", "ax", @progbits From df2509db9587ee0d93cdab84496cbc468db6da08 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 16:32:37 +0000 Subject: [PATCH 079/591] [prefix] Standardise calls to prot_call() Use the standard "pushl $function ; pushw %cs ; call prot_call" sequence everywhere that prot_call() is used. Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 22 ++++++++++++++++------ src/arch/x86/prefix/undiloader.S | 17 ++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 186f3d5b1..94c32455b 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -867,6 +867,15 @@ payload_death_message: movw %ax, (init_librm_vector+2) lcall *init_librm_vector + /* Prepare for return to .prefix segment */ + pushw %cs + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16.install_prealloc", "ax", @progbits +1: /* Inhibit INT 15,e820 and INT 15,e801 if applicable */ testl %ebp, %ebp jnz 1f @@ -878,10 +887,15 @@ payload_death_message: * ready for the copy to the new location. */ progress " relocate\n" - movw %ax, (prot_call_vector+2) pushl $relocate - lcall *prot_call_vector + pushw %cs + call prot_call + /* Jump back to .prefix segment */ + pushw $1f + lret + .section ".prefix.install_prealloc", "awx", @progbits +1: /* Copy code to new location */ progress " copy\n" pushl %edi @@ -929,10 +943,6 @@ init_librm_vector: .word init_librm .word 0 .size init_librm_vector, . - init_librm_vector -prot_call_vector: - .word prot_call - .word 0 - .size prot_call_vector, . - prot_call_vector #endif close_payload_vector: .word close_payload diff --git a/src/arch/x86/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S index 952661330..0376afa88 100644 --- a/src/arch/x86/prefix/undiloader.S +++ b/src/arch/x86/prefix/undiloader.S @@ -18,13 +18,16 @@ undiloader: pushw %ds pushw %es pushw %bx + /* ROM segment address to %ds */ pushw %cs popw %ds + /* UNDI loader parameter structure address into %es:%di */ movw %sp, %bx movw %ss:22(%bx), %di movw %ss:24(%bx), %es + /* Install to specified real-mode addresses */ pushw %di movw %es:12(%di), %bx @@ -34,13 +37,18 @@ undiloader: orl $0xffffffff, %ebp /* Allow arbitrary relocation */ call install_prealloc popw %di + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "ax", @progbits +1: /* Call UNDI loader C code */ pushl $pxe_loader_call pushw %cs - pushw $1f - pushw %ax - pushw $prot_call - lret + call prot_call + 1: /* Restore registers and return */ popw %bx popw %es @@ -49,4 +57,3 @@ undiloader: popl %edi popl %esi lret - .size undiloader, . - undiloader From 196f0f2551a4f82d2968c6e3a50aaa54a45ec779 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 16:36:04 +0000 Subject: [PATCH 080/591] [librm] Convert prot_call() to a real-mode near call Signed-off-by: Michael Brown --- src/arch/i386/interface/syslinux/comboot_call.c | 3 --- src/arch/x86/core/dumpregs.c | 1 - src/arch/x86/interface/pcbios/bios_console.c | 1 - src/arch/x86/interface/pcbios/int13.c | 1 - src/arch/x86/interface/pxe/pxe_entry.S | 1 - src/arch/x86/prefix/dskprefix.S | 1 - src/arch/x86/prefix/exeprefix.S | 1 - src/arch/x86/prefix/hdprefix.S | 1 - src/arch/x86/prefix/libprefix.S | 1 - src/arch/x86/prefix/lkrnprefix.S | 1 - src/arch/x86/prefix/nbiprefix.S | 1 - src/arch/x86/prefix/pxeprefix.S | 1 - src/arch/x86/prefix/romprefix.S | 1 - src/arch/x86/prefix/undiloader.S | 1 - src/arch/x86/transitions/librm.S | 12 ++++++------ src/arch/x86/transitions/librm_test.c | 1 - 16 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index 37cba1b77..277ec4475 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -661,7 +661,6 @@ void hook_comboot_interrupts ( ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint20_wrapper:\n\t" "pushl %0\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" "clc\n\t" "call patch_cf\n\t" @@ -673,7 +672,6 @@ void hook_comboot_interrupts ( ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint21_wrapper:\n\t" "pushl %0\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" "clc\n\t" "call patch_cf\n\t" @@ -685,7 +683,6 @@ void hook_comboot_interrupts ( ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint22_wrapper:\n\t" "pushl %0\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" "clc\n\t" "call patch_cf\n\t" diff --git a/src/arch/x86/core/dumpregs.c b/src/arch/x86/core/dumpregs.c index d23988d30..9f0b08189 100644 --- a/src/arch/x86/core/dumpregs.c +++ b/src/arch/x86/core/dumpregs.c @@ -7,7 +7,6 @@ void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { TEXT16_CODE ( ".globl dump_regs\n\t" "\ndump_regs:\n\t" "pushl $_dump_regs\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" "ret\n\t" ) : : ); diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 364c294da..72d5b2093 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -534,7 +534,6 @@ static void bios_inject_startup ( void ) { "cmpb $0, %%cs:bios_inject_lock\n\t" "jnz 1f\n\t" "pushl %0\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" "\n1:\n\t" "popfw\n\t" diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 6ba129218..7fe247b5f 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -1481,7 +1481,6 @@ static void int13_hook_vector ( void ) { "orb $0, %%al\n\t" "stc\n\t" "pushl %0\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" /* Chain if OF not set */ "jo 1f\n\t" diff --git a/src/arch/x86/interface/pxe/pxe_entry.S b/src/arch/x86/interface/pxe/pxe_entry.S index 84eba1e06..2ce1ced2b 100644 --- a/src/arch/x86/interface/pxe/pxe_entry.S +++ b/src/arch/x86/interface/pxe/pxe_entry.S @@ -121,7 +121,6 @@ pxenv_null_entry: .code16 pxenv_entry: pushl $pxe_api_call - pushw %cs call prot_call lret .size pxenv_entry, . - pxenv_entry diff --git a/src/arch/x86/prefix/dskprefix.S b/src/arch/x86/prefix/dskprefix.S index d716a30f0..041ec4c4c 100644 --- a/src/arch/x86/prefix/dskprefix.S +++ b/src/arch/x86/prefix/dskprefix.S @@ -371,7 +371,6 @@ start_runtime: .section ".text16", "awx", @progbits 1: pushl $main - pushw %cs call prot_call /* Uninstall iPXE */ diff --git a/src/arch/x86/prefix/exeprefix.S b/src/arch/x86/prefix/exeprefix.S index 35061b152..9598122bc 100644 --- a/src/arch/x86/prefix/exeprefix.S +++ b/src/arch/x86/prefix/exeprefix.S @@ -149,7 +149,6 @@ _exe_start: /* Run iPXE */ pushl $main - pushw %cs call prot_call /* Uninstall iPXE */ diff --git a/src/arch/x86/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S index 9f5752aa8..6caf12fc0 100644 --- a/src/arch/x86/prefix/hdprefix.S +++ b/src/arch/x86/prefix/hdprefix.S @@ -100,7 +100,6 @@ start_image: .section ".text16", "awx", @progbits 1: pushl $main - pushw %cs call prot_call /* Uninstall iPXE */ diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 94c32455b..7eb1e501f 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -888,7 +888,6 @@ payload_death_message: */ progress " relocate\n" pushl $relocate - pushw %cs call prot_call /* Jump back to .prefix segment */ diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S index 34e2bdc8f..41a5b4678 100644 --- a/src/arch/x86/prefix/lkrnprefix.S +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -198,7 +198,6 @@ no_cmd_line: /* Run iPXE */ pushl $main - pushw %cs call prot_call /* Uninstall iPXE */ diff --git a/src/arch/x86/prefix/nbiprefix.S b/src/arch/x86/prefix/nbiprefix.S index 539b5ebae..c25c254e5 100644 --- a/src/arch/x86/prefix/nbiprefix.S +++ b/src/arch/x86/prefix/nbiprefix.S @@ -67,7 +67,6 @@ _nbi_start: .section ".text16", "awx", @progbits 1: pushl $main - pushw %cs call prot_call /* Uninstall iPXE */ diff --git a/src/arch/x86/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S index c742add63..2c6d7abb7 100644 --- a/src/arch/x86/prefix/pxeprefix.S +++ b/src/arch/x86/prefix/pxeprefix.S @@ -821,7 +821,6 @@ run_ipxe: /* Run main program */ pushl $main - pushw %cs call prot_call /* Uninstall iPXE */ diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S index 287a986c4..941e2ce6b 100644 --- a/src/arch/x86/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -808,7 +808,6 @@ exec: /* Set %ds = %cs */ /* Call main() */ pushl $main - pushw %cs call prot_call /* Set up flat real mode for return to BIOS */ diff --git a/src/arch/x86/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S index 0376afa88..fb42637c8 100644 --- a/src/arch/x86/prefix/undiloader.S +++ b/src/arch/x86/prefix/undiloader.S @@ -46,7 +46,6 @@ undiloader: 1: /* Call UNDI loader C code */ pushl $pxe_loader_call - pushw %cs call prot_call 1: /* Restore registers and return */ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index c702c0ccb..46c1ab979 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -132,7 +132,6 @@ init_librm: /* Initialise IDT */ pushl $init_idt - pushw %cs call prot_call /* Restore registers */ @@ -358,7 +357,7 @@ rm_gdtr: .long 0 /* Base */ /**************************************************************************** - * prot_call (real-mode far call, 16-bit real-mode far return address) + * prot_call (real-mode near call, 16-bit real-mode near return address) * * Call a specific C function in the protected-mode code. The * prototype of the C function must be @@ -384,7 +383,6 @@ rm_gdtr: * * Example usage: * pushl $pxe_api_call - * pushw %cs * call prot_call * to call in to the C function * void pxe_api_call ( struct i386_all_regs *ix86 ); @@ -394,7 +392,8 @@ rm_gdtr: PC_OFFSET_GDT: .space 6 PC_OFFSET_IDT: .space 6 PC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS -PC_OFFSET_RETADDR: .space 4 +PC_OFFSET_PADDING: .space 2 /* for alignment */ +PC_OFFSET_RETADDR: .space 2 PC_OFFSET_FUNCTION: .space 4 PC_OFFSET_END: .previous @@ -404,6 +403,7 @@ PC_OFFSET_END: .globl prot_call prot_call: /* Preserve registers, flags and GDT on external RM stack */ + pushfw /* padding */ pushfl pushal pushw %gs @@ -455,7 +455,8 @@ pc_rmode: */ addr32 movl -20(%esp), %esp popfl - lret $4 + popfw /* padding */ + ret $4 /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) @@ -554,7 +555,6 @@ flatten_real_mode: movb $0x8f, real_ds + 6 /* Call dummy protected-mode function */ pushl $flatten_dummy - pushw %cs call prot_call /* Restore GDT */ movb $0x00, real_cs + 6 diff --git a/src/arch/x86/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c index f86584e3f..fc318ec7e 100644 --- a/src/arch/x86/transitions/librm_test.c +++ b/src/arch/x86/transitions/librm_test.c @@ -106,7 +106,6 @@ static void librm_test_exec ( void ) { __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" "movl %k0, %k2\n\t" "pushl %k3\n\t" - "pushw %%cs\n\t" "call prot_call\n\t" "rdtsc\n\t" ) : "=a" ( stopped ), "=d" ( discard_d ), From 31b5c2e753dbcb3d5023bccc8e644d0bcb56b2ad Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 23:23:38 +0000 Subject: [PATCH 081/591] [librm] Provide an abstraction wrapper for prot_call Signed-off-by: Michael Brown --- src/arch/i386/include/bits/compiler.h | 2 +- .../i386/interface/syslinux/comboot_call.c | 18 +++++---------- src/arch/x86/core/dumpregs.c | 5 ++-- src/arch/x86/include/librm.h | 23 ++++++++++++++++++- src/arch/x86/interface/pcbios/bios_console.c | 8 +++---- src/arch/x86/interface/pcbios/bios_reboot.c | 2 +- src/arch/x86/interface/pcbios/int13.c | 6 ++--- src/arch/x86/interface/pxe/pxe_entry.S | 5 ++-- src/arch/x86/prefix/dskprefix.S | 6 +++-- src/arch/x86/prefix/exeprefix.S | 5 ++-- src/arch/x86/prefix/hdprefix.S | 6 +++-- src/arch/x86/prefix/libprefix.S | 5 ++-- src/arch/x86/prefix/lkrnprefix.S | 5 ++-- src/arch/x86/prefix/nbiprefix.S | 6 +++-- src/arch/x86/prefix/pxeprefix.S | 4 ++-- src/arch/x86/prefix/romprefix.S | 6 ++--- src/arch/x86/prefix/undiloader.S | 5 ++-- src/arch/x86/transitions/librm.S | 6 ++--- src/arch/x86/transitions/librm_test.c | 12 ++++------ src/arch/x86_64/include/bits/compiler.h | 2 +- 20 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/arch/i386/include/bits/compiler.h b/src/arch/i386/include/bits/compiler.h index 87201135f..7c4a09396 100644 --- a/src/arch/i386/include/bits/compiler.h +++ b/src/arch/i386/include/bits/compiler.h @@ -9,7 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ -#define __asmcall __attribute__ (( cdecl, regparm(0) )) +#define __asmcall __attribute__ (( used, cdecl, regparm(0) )) /** * Declare a function with libgcc implicit linkage diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index 277ec4475..565977811 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -660,34 +660,28 @@ void hook_comboot_interrupts ( ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint20_wrapper:\n\t" - "pushl %0\n\t" - "call prot_call\n\t" + VIRT_CALL ( int20 ) "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) - : : "i" ( int20 ) ); + "iret\n\t" ) ); hook_bios_interrupt ( 0x20, ( intptr_t ) int20_wrapper, &int20_vector ); __asm__ __volatile__ ( TEXT16_CODE ( "\nint21_wrapper:\n\t" - "pushl %0\n\t" - "call prot_call\n\t" + VIRT_CALL ( int21 ) "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) - : : "i" ( int21 ) ); + "iret\n\t" ) ); hook_bios_interrupt ( 0x21, ( intptr_t ) int21_wrapper, &int21_vector ); __asm__ __volatile__ ( TEXT16_CODE ( "\nint22_wrapper:\n\t" - "pushl %0\n\t" - "call prot_call\n\t" + VIRT_CALL ( int22 ) "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) - : : "i" ( int22) ); + "iret\n\t" ) ); hook_bios_interrupt ( 0x22, ( intptr_t ) int22_wrapper, &int22_vector ); } diff --git a/src/arch/x86/core/dumpregs.c b/src/arch/x86/core/dumpregs.c index 9f0b08189..37d62a7b6 100644 --- a/src/arch/x86/core/dumpregs.c +++ b/src/arch/x86/core/dumpregs.c @@ -6,9 +6,8 @@ void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { __asm__ __volatile__ ( TEXT16_CODE ( ".globl dump_regs\n\t" "\ndump_regs:\n\t" - "pushl $_dump_regs\n\t" - "call prot_call\n\t" - "ret\n\t" ) : : ); + VIRT_CALL ( _dump_regs ) + "ret\n\t" ) ); printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 379e085a2..2786027a0 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -19,7 +19,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define LONG_DS 0x40 #endif -#ifndef ASSEMBLY +#ifdef ASSEMBLY + +/** + * Call C function from real-mode code + * + * @v function C function + */ +.macro virtcall function + pushl $\function + call prot_call +.endm + +#else /* ASSEMBLY */ #ifdef UACCESS_LIBRM #define UACCESS_PREFIX_librm @@ -27,6 +39,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_PREFIX_librm __librm_ #endif +/** + * Call C function from real-mode code + * + * @v function C function + */ +#define VIRT_CALL( function ) \ + "pushl $( " #function " )\n\t" \ + "call prot_call\n\t" + /* Variables in librm.S */ extern unsigned long virt_offset; diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 72d5b2093..c081a41e6 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -531,14 +531,12 @@ static void bios_inject_startup ( void ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint16_wrapper:\n\t" "pushfw\n\t" - "cmpb $0, %%cs:bios_inject_lock\n\t" + "cmpb $0, %cs:bios_inject_lock\n\t" "jnz 1f\n\t" - "pushl %0\n\t" - "call prot_call\n\t" + VIRT_CALL ( bios_inject ) "\n1:\n\t" "popfw\n\t" - "ljmp *%%cs:int16_vector\n\t" ) - : : "i" ( bios_inject ) ); + "ljmp *%cs:int16_vector\n\t" ) ); /* Hook INT 16 */ hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ), diff --git a/src/arch/x86/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c index 10a1ecb89..ed18dde0b 100644 --- a/src/arch/x86/interface/pcbios/bios_reboot.c +++ b/src/arch/x86/interface/pcbios/bios_reboot.c @@ -46,7 +46,7 @@ static void bios_reboot ( int warm ) { put_real ( flag, BDA_SEG, BDA_REBOOT ); /* Jump to system reset vector */ - __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : : ); + __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) ); } PROVIDE_REBOOT ( pcbios, reboot, bios_reboot ); diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 7fe247b5f..38880e4f0 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -1480,8 +1480,7 @@ static void int13_hook_vector ( void ) { /* Clear OF, set CF, call int13() */ "orb $0, %%al\n\t" "stc\n\t" - "pushl %0\n\t" - "call prot_call\n\t" + VIRT_CALL ( int13 ) /* Chain if OF not set */ "jo 1f\n\t" "pushfw\n\t" @@ -1512,8 +1511,7 @@ static void int13_hook_vector ( void ) { "\n3:\n\t" "movw %%bp, %%sp\n\t" "popw %%bp\n\t" - "iret\n\t" ) - : : "i" ( int13 ) ); + "iret\n\t" ) : : ); hook_bios_interrupt ( 0x13, ( intptr_t ) int13_wrapper, &int13_vector ); } diff --git a/src/arch/x86/interface/pxe/pxe_entry.S b/src/arch/x86/interface/pxe/pxe_entry.S index 2ce1ced2b..663aa842e 100644 --- a/src/arch/x86/interface/pxe/pxe_entry.S +++ b/src/arch/x86/interface/pxe/pxe_entry.S @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .arch i386 /**************************************************************************** @@ -120,8 +122,7 @@ pxenv_null_entry: .section ".text16", "ax", @progbits .code16 pxenv_entry: - pushl $pxe_api_call - call prot_call + virtcall pxe_api_call lret .size pxenv_entry, . - pxenv_entry diff --git a/src/arch/x86/prefix/dskprefix.S b/src/arch/x86/prefix/dskprefix.S index 041ec4c4c..0503f113d 100644 --- a/src/arch/x86/prefix/dskprefix.S +++ b/src/arch/x86/prefix/dskprefix.S @@ -18,6 +18,8 @@ FILE_LICENCE ( GPL2_ONLY ) +#include + .equ BOOTSEG, 0x07C0 /* original address of boot-sector */ .equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ @@ -370,8 +372,8 @@ start_runtime: lret .section ".text16", "awx", @progbits 1: - pushl $main - call prot_call + /* Run iPXE */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/exeprefix.S b/src/arch/x86/prefix/exeprefix.S index 9598122bc..c351456e2 100644 --- a/src/arch/x86/prefix/exeprefix.S +++ b/src/arch/x86/prefix/exeprefix.S @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + /* Initial temporary stack size */ #define EXE_STACK_SIZE 0x400 @@ -148,8 +150,7 @@ _exe_start: movl %esi, cmdline_phys /* Run iPXE */ - pushl $main - call prot_call + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S index 6caf12fc0..24f5d3850 100644 --- a/src/arch/x86/prefix/hdprefix.S +++ b/src/arch/x86/prefix/hdprefix.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .text .arch i386 .section ".prefix", "awx", @progbits @@ -99,8 +101,8 @@ start_image: lret .section ".text16", "awx", @progbits 1: - pushl $main - call prot_call + /* Run iPXE */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 7eb1e501f..425f51484 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .arch i386 /* Image compression enabled */ @@ -887,8 +889,7 @@ payload_death_message: * ready for the copy to the new location. */ progress " relocate\n" - pushl $relocate - call prot_call + virtcall relocate /* Jump back to .prefix segment */ pushw $1f diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S index 41a5b4678..922181f0e 100644 --- a/src/arch/x86/prefix/lkrnprefix.S +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + #define BZI_LOAD_HIGH_ADDR 0x100000 .text @@ -197,8 +199,7 @@ no_cmd_line: movl %ecx, initrd_len /* Run iPXE */ - pushl $main - call prot_call + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/nbiprefix.S b/src/arch/x86/prefix/nbiprefix.S index c25c254e5..de38e4af6 100644 --- a/src/arch/x86/prefix/nbiprefix.S +++ b/src/arch/x86/prefix/nbiprefix.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .text .arch i386 .code16 @@ -66,8 +68,8 @@ _nbi_start: lret .section ".text16", "awx", @progbits 1: - pushl $main - call prot_call + /* Run iPXE */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S index 2c6d7abb7..52ea18039 100644 --- a/src/arch/x86/prefix/pxeprefix.S +++ b/src/arch/x86/prefix/pxeprefix.S @@ -16,6 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .org 0 .code16 +#include #include #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) @@ -820,8 +821,7 @@ run_ipxe: movl %ecx, cached_dhcpack_phys /* Run main program */ - pushl $main - call prot_call + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S index 941e2ce6b..f4ca20677 100644 --- a/src/arch/x86/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -8,6 +8,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include #include #include @@ -806,9 +807,8 @@ exec: /* Set %ds = %cs */ #endif /* AUTOBOOT_ROM_FILTER */ .endif - /* Call main() */ - pushl $main - call prot_call + /* Run iPXE */ + virtcall main /* Set up flat real mode for return to BIOS */ call flatten_real_mode diff --git a/src/arch/x86/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S index fb42637c8..530b48e8a 100644 --- a/src/arch/x86/prefix/undiloader.S +++ b/src/arch/x86/prefix/undiloader.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .text .code16 .arch i386 @@ -45,8 +47,7 @@ undiloader: .section ".text16", "ax", @progbits 1: /* Call UNDI loader C code */ - pushl $pxe_loader_call - call prot_call + virtcall pxe_loader_call 1: /* Restore registers and return */ popw %bx diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 46c1ab979..49a30851a 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -131,8 +131,7 @@ init_librm: addl $gdt, gdt_base /* Initialise IDT */ - pushl $init_idt - call prot_call + virtcall init_idt /* Restore registers */ negl %edi @@ -554,8 +553,7 @@ flatten_real_mode: movb $0x8f, real_cs + 6 movb $0x8f, real_ds + 6 /* Call dummy protected-mode function */ - pushl $flatten_dummy - call prot_call + virtcall flatten_dummy /* Restore GDT */ movb $0x00, real_cs + 6 movb $0x00, real_ds + 6 diff --git a/src/arch/x86/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c index fc318ec7e..3f9ead218 100644 --- a/src/arch/x86/transitions/librm_test.c +++ b/src/arch/x86/transitions/librm_test.c @@ -56,9 +56,9 @@ static struct profiler real_call_profiler __profiler = { .name = "real_call" }; static struct profiler prot_call_profiler __profiler = { .name = "prot_call" }; /** - * Dummy protected-mode function + * Dummy function for profiling tests */ -static void librm_test_prot_call ( void ) { +static __asmcall void librm_test_call ( struct i386_all_regs *ix86 __unused ) { /* Do nothing */ } @@ -97,7 +97,7 @@ static void librm_test_exec ( void ) { /* Profile complete real-mode call cycle */ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { profile_start ( &real_call_profiler ); - __asm__ __volatile__ ( REAL_CODE ( "" ) : : ); + __asm__ __volatile__ ( REAL_CODE ( "" ) ); profile_stop ( &real_call_profiler ); } @@ -105,12 +105,10 @@ static void librm_test_exec ( void ) { for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" "movl %k0, %k2\n\t" - "pushl %k3\n\t" - "call prot_call\n\t" + VIRT_CALL ( librm_test_call ) "rdtsc\n\t" ) : "=a" ( stopped ), "=d" ( discard_d ), - "=R" ( started ) - : "i" ( librm_test_prot_call ) ); + "=R" ( started ) : ); profile_start_at ( &prot_call_profiler, started ); profile_stop_at ( &prot_call_profiler, stopped ); } diff --git a/src/arch/x86_64/include/bits/compiler.h b/src/arch/x86_64/include/bits/compiler.h index f70b2e517..98c560e7d 100644 --- a/src/arch/x86_64/include/bits/compiler.h +++ b/src/arch/x86_64/include/bits/compiler.h @@ -7,7 +7,7 @@ #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ -#define __asmcall __attribute__ (( regparm(0) )) +#define __asmcall __attribute__ (( used, regparm(0) )) /** Declare a function with libgcc implicit linkage */ #define __libgcc From 6eb1c927a3e245dd571507e86bbde8e8a29f582b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Feb 2016 00:56:20 +0000 Subject: [PATCH 082/591] [librm] Transition to protected mode within init_librm() Long-mode operation will require page tables, which are too large to sensibly fit in our .data16 segment in base memory. Add a portion of init_librm() running in 32-bit protected mode to provide access to high memory. Use this portion of init_librm() to initialise the .textdata variables "virt_offset", "text16", and "data16", eliminating the redundant (re)initialisation currently performed on every mode transition as part of real_to_prot(). Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 5 +- src/arch/x86/transitions/librm.S | 229 +++++++++++++++++-------------- 2 files changed, 127 insertions(+), 107 deletions(-) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 2786027a0..974616409 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -14,10 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PHYSICAL_DS 0x20 #define REAL_CS 0x28 #define REAL_DS 0x30 -#if 0 -#define LONG_CS 0x38 -#define LONG_DS 0x40 -#endif +#define P2R_DS 0x38 #ifdef ASSEMBLY diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 49a30851a..42cfb9914 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -70,14 +70,96 @@ real_cs: /* 16 bit real mode code segment */ .word 0xffff, 0 .byte 0, 0x9b, 0x00, 0 - .org gdt + REAL_DS + .org gdt + REAL_DS, 0 real_ds: /* 16 bit real mode data segment */ - .word 0xffff, ( REAL_DS << 4 ) + .word 0xffff, 0 + .byte 0, 0x93, 0x00, 0 + + .org gdt + P2R_DS, 0 +p2r_ds: /* 16 bit real mode data segment for prot_to_real transition */ + .word 0xffff, ( P2R_DS << 4 ) .byte 0, 0x93, 0x00, 0 gdt_end: .equ gdt_length, gdt_end - gdt +/**************************************************************************** + * Stored real-mode and protected-mode stack pointers + * + * The real-mode stack pointer is stored here whenever real_to_prot + * is called and restored whenever prot_to_real is called. The + * converse happens for the protected-mode stack pointer. + * + * Despite initial appearances this scheme is, in fact re-entrant, + * because program flow dictates that we always return via the point + * we left by. For example: + * PXE API call entry + * 1 real => prot + * ... + * Print a text string + * ... + * 2 prot => real + * INT 10 + * 3 real => prot + * ... + * ... + * 4 prot => real + * PXE API call exit + * + * At point 1, the RM mode stack value, say RPXE, is stored in + * rm_ss,sp. We want this value to still be present in rm_ss,sp when + * we reach point 4. + * + * At point 2, the RM stack value is restored from RPXE. At point 3, + * the RM stack value is again stored in rm_ss,sp. This *does* + * overwrite the RPXE that we have stored there, but it's the same + * value, since the code between points 2 and 3 has managed to return + * to us. + **************************************************************************** + */ + .section ".bss.rm_sp", "aw", @nobits + .globl rm_sp +rm_sp: .word 0 + + .section ".bss.rm_ss", "aw", @nobits + .globl rm_ss +rm_ss: .word 0 + + .section ".data.pm_esp", "aw", @progbits +pm_esp: .long _estack + +/**************************************************************************** + * Virtual address offsets + * + * These are used by the protected-mode code to map between virtual + * and physical addresses, and to access variables in the .text16 or + * .data16 segments. + **************************************************************************** + */ + .struct 0 +VA_VIRT_OFFSET: .space 4 +VA_TEXT16: .space 4 +VA_DATA16: .space 4 +VA_SIZE: + .previous + + /* Internal copies, used only by librm itself */ + .section ".bss16.rm_virt_addrs", "aw", @nobits +rm_virt_addrs: .space VA_SIZE + .equ rm_virt_offset, ( rm_virt_addrs + VA_VIRT_OFFSET ) + .equ rm_text16, ( rm_virt_addrs + VA_TEXT16 ) + .equ rm_data16, ( rm_virt_addrs + VA_DATA16 ) + + /* Externally visible variables, used by C code */ + .section ".bss.virt_addrs", "aw", @nobits +virt_addrs: .space VA_SIZE + .globl virt_offset + .equ virt_offset, ( virt_addrs + VA_VIRT_OFFSET ) + .globl text16 + .equ text16, ( virt_addrs + VA_TEXT16 ) + .globl data16 + .equ data16, ( virt_addrs + VA_DATA16 ) + /**************************************************************************** * init_librm (real-mode far call, 16-bit real-mode far return address) * @@ -96,45 +178,65 @@ init_librm: /* Preserve registers */ pushl %eax pushl %ebx + pushl %edi - /* Store virt_offset and set up virtual_cs and virtual_ds segments */ + /* Store rm_virt_offset and set up virtual_cs and virtual_ds segments */ + movl %edi, rm_virt_offset movl %edi, %eax movw $virtual_cs, %bx call set_seg_base movw $virtual_ds, %bx - call set_seg_base - movl %edi, rm_virt_offset + call set_seg_base - /* Negate virt_offset */ - negl %edi - - /* Store rm_cs and text16, set up real_cs segment */ + /* Store rm_cs and rm_text16, set up real_cs segment */ xorl %eax, %eax movw %cs, %ax movw %ax, %cs:rm_cs shll $4, %eax movw $real_cs, %bx call set_seg_base - addr32 leal (%eax, %edi), %ebx - movl %ebx, rm_text16 + subl %edi, %eax + movl %eax, rm_text16 - /* Store rm_ds and data16 */ + /* Store rm_ds and rm_data16, set up real_ds segment and GDT base */ xorl %eax, %eax movw %ds, %ax movw %ax, %cs:rm_ds shll $4, %eax - addr32 leal (%eax, %edi), %ebx - movl %ebx, rm_data16 - - /* Set GDT base */ + movw $real_ds, %bx + call set_seg_base movl %eax, gdt_base addl $gdt, gdt_base + subl %edi, %eax + movl %eax, rm_data16 + + /* Switch to protected mode */ + virtcall init_librm_pmode + .section ".text.init_librm", "ax", @progbits + .code32 +init_librm_pmode: + + /* Store virt_offset, text16, and data16 */ + pushw %ds + movw $REAL_DS, %ax + movw %ax, %ds + movl $rm_virt_addrs, %esi + movl $virt_addrs, %edi + movl $( VA_SIZE / 4 ), %ecx + rep movsl + popw %ds + + /* Return to real mode */ + ret + .section ".text16.init_librm", "ax", @progbits + .code16 +init_librm_rmode: /* Initialise IDT */ virtcall init_idt /* Restore registers */ - negl %edi + popl %edi popl %ebx popl %eax lret @@ -177,16 +279,10 @@ real_to_prot: 1: jc 1b /* Make sure we have our data segment available */ - movw %cs:rm_ds, %ax - movw %ax, %ds + movw %cs:rm_ds, %ds - /* Add virt_offset, text16 and data16 to stack to be - * copied, and also copy the return address. - */ - pushl rm_virt_offset - pushl rm_text16 - pushl rm_data16 - addw $16, %cx /* %ecx must be less than 64kB anyway */ + /* Add protected-mode return address to length of data to be copied */ + addw $4, %cx /* %ecx must be less than 64kB anyway */ /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */ xorl %ebp, %ebp @@ -242,11 +338,6 @@ r2p_pmode: movl %esp, %edi rep movsb - /* Publish virt_offset, text16 and data16 for PM code to use */ - popl data16 - popl text16 - popl virt_offset - /* Return to virtual address */ ret @@ -284,7 +375,7 @@ prot_to_real: /* Add return address to data to be moved to RM stack */ addl $4, %ecx - + /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */ movzwl rm_ss, %ebp movzwl rm_sp, %edx @@ -293,16 +384,16 @@ prot_to_real: shll $4, %eax leal (%eax,%edx), %edi subl virt_offset, %edi - + /* Move data from PM stack to RM stack */ movl %esp, %esi rep movsb - + /* Record protected-mode %esp (after removal of data) */ movl %esi, pm_esp /* Load real-mode segment limits */ - movw $REAL_DS, %ax + movw $P2R_DS, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs @@ -604,71 +695,3 @@ interrupt_wrapper: /* Restore registers and return */ popal iret - -/**************************************************************************** - * Stored real-mode and protected-mode stack pointers - * - * The real-mode stack pointer is stored here whenever real_to_prot - * is called and restored whenever prot_to_real is called. The - * converse happens for the protected-mode stack pointer. - * - * Despite initial appearances this scheme is, in fact re-entrant, - * because program flow dictates that we always return via the point - * we left by. For example: - * PXE API call entry - * 1 real => prot - * ... - * Print a text string - * ... - * 2 prot => real - * INT 10 - * 3 real => prot - * ... - * ... - * 4 prot => real - * PXE API call exit - * - * At point 1, the RM mode stack value, say RPXE, is stored in - * rm_ss,sp. We want this value to still be present in rm_ss,sp when - * we reach point 4. - * - * At point 2, the RM stack value is restored from RPXE. At point 3, - * the RM stack value is again stored in rm_ss,sp. This *does* - * overwrite the RPXE that we have stored there, but it's the same - * value, since the code between points 2 and 3 has managed to return - * to us. - **************************************************************************** - */ - .section ".bss.rm_sp", "aw", @nobits - .globl rm_sp -rm_sp: .word 0 - - .section ".bss.rm_ss", "aw", @nobits - .globl rm_ss -rm_ss: .word 0 - - .section ".data.pm_esp", "aw", @progbits -pm_esp: .long _estack - -/**************************************************************************** - * Virtual address offsets - * - * These are used by the protected-mode code to map between virtual - * and physical addresses, and to access variables in the .text16 or - * .data16 segments. - **************************************************************************** - */ - /* Internal copies, created by init_librm (which runs in real mode) */ - .section ".bss16.rm_virt_offset", "aw", @nobits -rm_virt_offset: .long 0 -rm_text16: .long 0 -rm_data16: .long 0 - - /* Externally-visible copies, created by real_to_prot */ - .section ".bss.virt_offset", "aw", @nobits - .globl virt_offset -virt_offset: .long 0 - .globl text16 -text16: .long 0 - .globl data16 -data16: .long 0 From bfe6e3e90e8544452d4d76266fd8a93bfab8f9aa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Feb 2016 02:38:48 +0000 Subject: [PATCH 083/591] [relocate] Preserve page alignment during relocation Signed-off-by: Michael Brown --- src/arch/x86/core/relocate.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/arch/x86/core/relocate.c b/src/arch/x86/core/relocate.c index 65a36e008..765d46560 100644 --- a/src/arch/x86/core/relocate.c +++ b/src/arch/x86/core/relocate.c @@ -10,14 +10,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* - * The linker passes in the symbol _max_align, which is the alignment - * that we must preserve, in bytes. - * - */ -extern char _max_align[]; -#define max_align ( ( size_t ) _max_align ) - /* Linker symbols */ extern char _textdata[]; extern char _etextdata[]; @@ -30,6 +22,12 @@ extern char _etextdata[]; */ #define MAX_ADDR (0xfff00000UL) +/* Preserve alignment to a 4kB page + * + * Required for x86_64, and doesn't hurt for i386. + */ +#define ALIGN 4096 + /** * Relocate iPXE * @@ -53,11 +51,11 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { start = virt_to_phys ( _textdata ); end = virt_to_phys ( _etextdata ); size = ( end - start ); - padded_size = ( size + max_align - 1 ); + padded_size = ( size + ALIGN - 1 ); DBG ( "Relocate: currently at [%x,%x)\n" - "...need %x bytes for %zd-byte alignment\n", - start, end, padded_size, max_align ); + "...need %x bytes for %d-byte alignment\n", + start, end, padded_size, ALIGN ); /* Determine maximum usable address */ max = MAX_ADDR; @@ -125,7 +123,7 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { * required alignemnt. */ new_start = new_end - padded_size; - new_start += ( start - new_start ) & ( max_align - 1 ); + new_start += ( ( start - new_start ) & ( ALIGN - 1 ) ); new_end = new_start + size; DBG ( "Relocating from [%x,%x) to [%x,%x)\n", From d1562c38a6882a129998935cd63d09ab18f77add Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Feb 2016 01:50:13 +0000 Subject: [PATCH 084/591] [librm] Prepare for long-mode memory map The bulk of the iPXE binary (the .textdata section) is physically relocated at runtime to the top of the 32-bit address space in order to allow space for an OS to be loaded. The relocation is achieved with the assistance of segmentation: we adjust the code and data segment bases so that the link-time addresses remain valid. Segmentation is not available (for normal code and data segments) in long mode. We choose to compile the C code with -mcmodel=kernel and use a link-time address of 0xffffffffeb000000. This choice allows us to identity-map the entirety of the 32-bit address space, and to alias our chosen link-time address to the physical location of our .textdata section. (This requires the .textdata section to always be aligned to a page boundary.) We simultaneously choose to set the 32-bit virtual address segment bases such that the link-time addresses may simply be truncated to 32 bits in order to generate a valid 32-bit virtual address. This allows symbols in .textdata to be trivially accessed by both 32-bit and 64-bit code. There is no (sensible) way in 32-bit assembly code to generate the required R_X86_64_32S relocation records for these truncated symbols. However, subtracting the fixed constant 0xffffffff00000000 has the same effect as truncation, and can be represented in a standard R_X86_64_32 relocation record. We define the VIRTUAL() macro to abstract away this truncation operation, and apply it to all references by 32-bit (or 16-bit) assembly code to any symbols within the .textdata section. We define "virt_offset" for a 64-bit build as "the value to be added to an address within .textdata in order to obtain its physical address". With this definition, the low 32 bits of "virt_offset" can be treated by 32-bit code as functionally equivalent to "virt_offset" in a 32-bit build. We define "text16" and "data16" for a 64-bit build as the physical addresses of the .text16 and .data16 sections. Since a physical address within the 32-bit address space may be used directly as a 64-bit virtual address (thanks to the identity map), this definition provides the most natural access to variables in .text16 and .data16. Note that this requires a minor adjustment in prot_to_real(), which accesses .text16 using 32-bit virtual addresses. Signed-off-by: Michael Brown --- src/arch/x86/core/virtaddr.S | 10 ++--- src/arch/x86/include/librm.h | 39 +++++++++++++++++-- src/arch/x86/transitions/librm.S | 65 +++++++++++++++++++++----------- src/arch/x86_64/Makefile | 4 -- src/arch/x86_64/Makefile.efi | 4 ++ src/arch/x86_64/Makefile.pcbios | 9 +++++ 6 files changed, 96 insertions(+), 35 deletions(-) diff --git a/src/arch/x86/core/virtaddr.S b/src/arch/x86/core/virtaddr.S index 425591570..45beb164d 100644 --- a/src/arch/x86/core/virtaddr.S +++ b/src/arch/x86/core/virtaddr.S @@ -32,13 +32,13 @@ _virt_to_phys: pushl %ebp /* Change return address to a physical address */ - movl virt_offset, %ebp + movl VIRTUAL(virt_offset), %ebp addl %ebp, 12(%esp) /* Switch to physical code segment */ cli pushl $PHYSICAL_CS - leal 1f(%ebp), %eax + leal VIRTUAL(1f)(%ebp), %eax pushl %eax lret 1: @@ -78,7 +78,7 @@ _phys_to_virt: /* Switch to virtual code segment */ cli - ljmp $VIRTUAL_CS, $1f + ljmp $VIRTUAL_CS, $VIRTUAL(1f) 1: /* Reload data segment registers */ movl $VIRTUAL_DS, %eax @@ -88,7 +88,7 @@ _phys_to_virt: movl %eax, %gs /* Reload stack segment and adjust %esp */ - movl virt_offset, %ebp + movl VIRTUAL(virt_offset), %ebp movl %eax, %ss subl %ebp, %esp @@ -134,7 +134,7 @@ _intr_to_virt: /* Reload stack segment and adjust %esp if necessary */ je 1f - movl virt_offset, %ebp + movl VIRTUAL(virt_offset), %ebp movl %eax, %ss subl %ebp, %esp 1: diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 974616409..fc31c5036 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -7,7 +7,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * Don't change these unless you really know what you're doing. */ - #define VIRTUAL_CS 0x08 #define VIRTUAL_DS 0x10 #define PHYSICAL_CS 0x18 @@ -16,6 +15,40 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define REAL_DS 0x30 #define P2R_DS 0x38 +/* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS + * + * In a 64-bit build, we set the bases of VIRTUAL_CS and VIRTUAL_DS + * such that truncating a .textdata symbol value to 32 bits gives a + * valid 32-bit virtual address. + * + * The C code is compiled with -mcmodel=kernel and so we must place + * all .textdata symbols within the negative 2GB of the 64-bit address + * space. Consequently, all .textdata symbols will have the MSB set + * after truncation to 32 bits. This means that a straightforward + * R_X86_64_32 relocation record for the symbol will fail, since the + * truncated symbol value will not correctly zero-extend to the + * original 64-bit value. + * + * Using an R_X86_64_32S relocation record would work, but there is no + * (sensible) way to generate these relocation records within 32-bit + * or 16-bit code. + * + * The simplest solution is to generate an R_X86_64_32 relocation + * record with an addend of (-0xffffffff00000000). Since all + * .textdata symbols are within the negative 2GB of the 64-bit address + * space, this addend acts to effectively truncate the symbol to 32 + * bits, thereby matching the semantics of the R_X86_64_32 relocation + * records generated for 32-bit and 16-bit code. + * + * In a 32-bit build, this problem does not exist, and we can just use + * the .textdata symbol values directly. + */ +#ifdef __x86_64__ +#define VIRTUAL(address) ( (address) - 0xffffffff00000000 ) +#else +#define VIRTUAL(address) (address) +#endif + #ifdef ASSEMBLY /** @@ -24,7 +57,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v function C function */ .macro virtcall function - pushl $\function + pushl $VIRTUAL(\function) call prot_call .endm @@ -42,7 +75,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v function C function */ #define VIRT_CALL( function ) \ - "pushl $( " #function " )\n\t" \ + "pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \ "call prot_call\n\t" /* Variables in librm.S */ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 42cfb9914..495f272de 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -19,8 +19,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS ) #define SIZEOF_I386_FLAGS 4 #define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS ) - - .arch i386 + +/* Size of an address */ +#ifdef __x86_64__ +#define SIZEOF_ADDR 8 +#else +#define SIZEOF_ADDR 4 +#endif + +/* Selectively assemble code for 32-bit/64-bit builds */ +#ifdef __x86_64__ +#define if32 if 0 +#define if64 if 1 +#else +#define if32 if 1 +#define if64 if 0 +#endif /**************************************************************************** * Global descriptor table @@ -126,7 +140,7 @@ rm_sp: .word 0 rm_ss: .word 0 .section ".data.pm_esp", "aw", @progbits -pm_esp: .long _estack +pm_esp: .long VIRTUAL(_estack) /**************************************************************************** * Virtual address offsets @@ -137,9 +151,9 @@ pm_esp: .long _estack **************************************************************************** */ .struct 0 -VA_VIRT_OFFSET: .space 4 -VA_TEXT16: .space 4 -VA_DATA16: .space 4 +VA_VIRT_OFFSET: .space SIZEOF_ADDR +VA_TEXT16: .space SIZEOF_ADDR +VA_DATA16: .space SIZEOF_ADDR VA_SIZE: .previous @@ -168,7 +182,7 @@ virt_addrs: .space VA_SIZE * Parameters: * %cs : .text16 segment * %ds : .data16 segment - * %edi : Physical base of protected-mode code (virt_offset) + * %edi : Physical base of protected-mode code **************************************************************************** */ .section ".text16.init_librm", "ax", @progbits @@ -181,7 +195,9 @@ init_librm: pushl %edi /* Store rm_virt_offset and set up virtual_cs and virtual_ds segments */ + subl $VIRTUAL(_textdata), %edi movl %edi, rm_virt_offset +.if64 ; setae (rm_virt_offset+4) ; .endif movl %edi, %eax movw $virtual_cs, %bx call set_seg_base @@ -195,7 +211,7 @@ init_librm: shll $4, %eax movw $real_cs, %bx call set_seg_base - subl %edi, %eax +.if32 ; subl %edi, %eax ; .endif movl %eax, rm_text16 /* Store rm_ds and rm_data16, set up real_ds segment and GDT base */ @@ -207,7 +223,7 @@ init_librm: call set_seg_base movl %eax, gdt_base addl $gdt, gdt_base - subl %edi, %eax +.if32 ; subl %edi, %eax ; .endif movl %eax, rm_data16 /* Switch to protected mode */ @@ -221,7 +237,7 @@ init_librm_pmode: movw $REAL_DS, %ax movw %ax, %ds movl $rm_virt_addrs, %esi - movl $virt_addrs, %edi + movl $VIRTUAL(virt_addrs), %edi movl $( VA_SIZE / 4 ), %ecx rep movsl popw %ds @@ -312,7 +328,7 @@ real_to_prot: movl %cr0, %eax orb $CR0_PE, %al movl %eax, %cr0 - data32 ljmp $VIRTUAL_CS, $r2p_pmode + data32 ljmp $VIRTUAL_CS, $VIRTUAL(r2p_pmode) .section ".text.real_to_prot", "ax", @progbits .code32 r2p_pmode: @@ -323,15 +339,15 @@ r2p_pmode: movw %ax, %fs movw %ax, %gs movw %ax, %ss - movl pm_esp, %esp + movl VIRTUAL(pm_esp), %esp /* Load protected-mode interrupt descriptor table */ - lidt idtr + lidt VIRTUAL(idtr) /* Record real-mode %ss:sp (after removal of data) */ - movw %bp, rm_ss + movw %bp, VIRTUAL(rm_ss) addl %ecx, %edx - movw %dx, rm_sp + movw %dx, VIRTUAL(rm_sp) /* Move data from RM stack to PM stack */ subl %ecx, %esp @@ -365,7 +381,8 @@ r2p_pmode: .code32 prot_to_real: /* Copy real-mode global descriptor table register to RM code segment */ - movl text16, %edi + movl VIRTUAL(text16), %edi +.if64 ; subl VIRTUAL(virt_offset), %edi ; .endif leal rm_gdtr(%edi), %edi movsw movsl @@ -377,20 +394,20 @@ prot_to_real: addl $4, %ecx /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */ - movzwl rm_ss, %ebp - movzwl rm_sp, %edx + movzwl VIRTUAL(rm_ss), %ebp + movzwl VIRTUAL(rm_sp), %edx subl %ecx, %edx movl %ebp, %eax shll $4, %eax leal (%eax,%edx), %edi - subl virt_offset, %edi + subl VIRTUAL(virt_offset), %edi /* Move data from PM stack to RM stack */ movl %esp, %esi rep movsb /* Record protected-mode %esp (after removal of data) */ - movl %esi, pm_esp + movl %esi, VIRTUAL(pm_esp) /* Load real-mode segment limits */ movw $P2R_DS, %ax @@ -512,7 +529,7 @@ prot_call: /* Switch to protected mode and move register dump to PM stack */ movl $PC_OFFSET_END, %ecx - pushl $pc_pmode + pushl $VIRTUAL(pc_pmode) jmp real_to_prot .section ".text.prot_call", "ax", @progbits .code32 @@ -589,7 +606,7 @@ real_call: /* Switch to real mode and move register dump to RM stack */ movl $( RC_OFFSET_REGS_END + 4 /* function pointer copy */ ), %ecx pushl $rc_rmode - movl $rm_default_gdtr_idtr, %esi + movl $VIRTUAL(rm_default_gdtr_idtr), %esi jmp prot_to_real .section ".text16.real_call", "ax", @progbits .code16 @@ -605,7 +622,7 @@ rc_rmode: /* Switch to protected mode and move register dump back to PM stack */ movl $RC_OFFSET_REGS_END, %ecx - pushl $rc_pmode + pushl $VIRTUAL(rc_pmode) jmp real_to_prot .section ".text.real_call", "ax", @progbits .code32 @@ -665,6 +682,8 @@ flatten_dummy: * May be entered with either physical or virtual stack segment. **************************************************************************** */ + .section ".text.interrupt_wrapper", "ax", @progbits + .code32 .globl interrupt_wrapper interrupt_wrapper: /* Preserve segment registers and original %esp */ diff --git a/src/arch/x86_64/Makefile b/src/arch/x86_64/Makefile index 48c0aa1af..246905cdb 100644 --- a/src/arch/x86_64/Makefile +++ b/src/arch/x86_64/Makefile @@ -7,10 +7,6 @@ CFLAGS += -fstrength-reduce -fomit-frame-pointer # CFLAGS += -falign-jumps=1 -falign-loops=1 -falign-functions=1 -# Use %rip-relative addressing wherever possible. -# -CFLAGS += -fpie - # Force 64-bit code # CFLAGS += -m64 diff --git a/src/arch/x86_64/Makefile.efi b/src/arch/x86_64/Makefile.efi index 12408f862..0041bb8f0 100644 --- a/src/arch/x86_64/Makefile.efi +++ b/src/arch/x86_64/Makefile.efi @@ -1,5 +1,9 @@ # -*- makefile -*- : Force emacs to use Makefile mode +# Use %rip-relative addressing wherever possible. +# +CFLAGS += -fpie + # EFI probably doesn't guarantee us a red zone, so let's not rely on it. # CFLAGS += -mno-red-zone diff --git a/src/arch/x86_64/Makefile.pcbios b/src/arch/x86_64/Makefile.pcbios index dfb8db0a0..ba4c8d8dc 100644 --- a/src/arch/x86_64/Makefile.pcbios +++ b/src/arch/x86_64/Makefile.pcbios @@ -1,5 +1,14 @@ # -*- makefile -*- : Force emacs to use Makefile mode +# Place .textdata in negative 2GB of address space +# +CFLAGS += -mcmodel=kernel +LDFLAGS += --section-start=.textdata=0xffffffffeb000000 + +# Assembly code does not respect a red zone. +# +CFLAGS += -mno-red-zone + # Include generic BIOS Makefile # MAKEDEPS += arch/x86/Makefile.pcbios From 163f8acba0fbb6e3c44aec5286d3d076e1f44f22 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Feb 2016 03:18:11 +0000 Subject: [PATCH 085/591] [librm] Generate page tables for 64-bit builds Signed-off-by: Michael Brown --- src/arch/x86/scripts/pcbios.lds | 12 ++ src/arch/x86/transitions/librm.S | 183 ++++++++++++++++++++++++++++++- src/arch/x86_64/Makefile.pcbios | 4 + 3 files changed, 197 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/scripts/pcbios.lds b/src/arch/x86/scripts/pcbios.lds index 865591ae2..dccdfbed9 100644 --- a/src/arch/x86/scripts/pcbios.lds +++ b/src/arch/x86/scripts/pcbios.lds @@ -26,6 +26,12 @@ SECTIONS { PROVIDE ( _max_align = 16 ); + /* + * Default to not generating space for page tables + * + */ + PROVIDE ( _use_page_tables = 0 ); + /* * Allow decompressor to require a minimum amount of temporary stack * space. @@ -127,6 +133,12 @@ SECTIONS { *(COMMON) *(.stack) *(.stack.*) + *(.pages) + *(.pages.*) + _textdata_paged_len = ABSOLUTE ( . - _textdata ); + _textdata_ptes = ABSOLUTE ( ( _textdata_paged_len + 4095 ) / 4096 ); + _textdata_pdes = ABSOLUTE ( ( _textdata_ptes + 511 ) / 512 ); + . += ( _use_page_tables ? ( _textdata_pdes * 4096 ) : 0 ); _etextdata = .; } _textdata_filesz = ABSOLUTE ( _mtextdata ) - ABSOLUTE ( _textdata ); diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 495f272de..f3854dfe8 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -10,8 +10,38 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Drag in local definitions */ #include "librm.h" -/* For switches to/from protected mode */ -#define CR0_PE 1 +/* CR0: protection enabled */ +#define CR0_PE ( 1 << 0 ) + +/* CR0: paging */ +#define CR0_PG ( 1 << 31 ) + +/* CR4: physical address extensions */ +#define CR4_PAE ( 1 << 5 ) + +/* Page: present */ +#define PG_P 0x01 + +/* Page: read/write */ +#define PG_RW 0x02 + +/* Page: user/supervisor */ +#define PG_US 0x04 + +/* Page: page size */ +#define PG_PS 0x80 + +/* Size of various paging-related data structures */ +#define SIZEOF_PTE_LOG2 3 +#define SIZEOF_PTE ( 1 << SIZEOF_PTE_LOG2 ) +#define SIZEOF_PT_LOG2 12 +#define SIZEOF_PT ( 1 << SIZEOF_PT_LOG2 ) +#define SIZEOF_4KB_PAGE_LOG2 12 +#define SIZEOF_4KB_PAGE ( 1 << SIZEOF_4KB_PAGE_LOG2 ) +#define SIZEOF_2MB_PAGE_LOG2 21 +#define SIZEOF_2MB_PAGE ( 1 << SIZEOF_2MB_PAGE_LOG2 ) +#define SIZEOF_LOW_4GB_LOG2 32 +#define SIZEOF_LOW_4GB ( 1 << SIZEOF_LOW_4GB_LOG2 ) /* Size of various C data structures */ #define SIZEOF_I386_SEG_REGS 12 @@ -226,6 +256,10 @@ init_librm: .if32 ; subl %edi, %eax ; .endif movl %eax, rm_data16 +.if64 ; /* Reset page tables, if applicable */ + xorl %eax, %eax + movl %eax, pml4 +.endif /* Switch to protected mode */ virtcall init_librm_pmode .section ".text.init_librm", "ax", @progbits @@ -242,6 +276,10 @@ init_librm_pmode: rep movsl popw %ds +.if64 ; /* Initialise page tables, if applicable */ + movl VIRTUAL(virt_offset), %edi + call init_pages +.endif /* Return to real mode */ ret .section ".text16.init_librm", "ax", @progbits @@ -714,3 +752,144 @@ interrupt_wrapper: /* Restore registers and return */ popal iret + +/**************************************************************************** + * Page tables + * + **************************************************************************** + */ + .section ".pages", "aw", @nobits + .align SIZEOF_PT + + /* Page map level 4 entries (PML4Es) + * + * This comprises + * + * - PML4E[0x000] covering [0x0000000000000000-0x0000007fffffffff] + * - PML4E[0x1ff] covering [0xffffff8000000000-0xffffffffffffffff] + * + * These point to the PDPT. This creates some aliased + * addresses within unused portions of the 64-bit address + * space, but allows us to use just a single PDPT. + */ +pml4e: + .space SIZEOF_PT + .size pml4e, . - pml4e + + /* Page directory pointer table entries (PDPTEs) + * + * This comprises: + * + * - PDPTE[0x000] covering [0x0000000000000000-0x000000003fffffff] + * - PDPTE[0x001] covering [0x0000000040000000-0x000000007fffffff] + * - PDPTE[0x002] covering [0x0000000080000000-0x00000000bfffffff] + * - PDPTE[0x003] covering [0x00000000c0000000-0x00000000ffffffff] + * + * These point to the appropriate page directories (in pde_low) + * used to identity-map the whole of the 32-bit address space. + * + * - PDPTE[0x1ff] covering [0xffffffffc0000000-0xffffffffffffffff] + * + * This points back to the PDPT itself, allowing the PDPT to be + * (ab)used to hold PDEs covering .textdata. + * + * - PDE[N-M] covering [_textdata,_end) + * + * These are used to point to the page tables (in pte_textdata) + * used to map our .textdata section. Note that each PDE + * covers 2MB, so we are likely to use only a single PDE in + * practice. + */ +pdpte: + .space SIZEOF_PT + .size pdpte, . - pdpte + .equ pde_textdata, pdpte /* (ab)use */ + + /* Page directory entries (PDEs) for the low 4GB + * + * This comprises 2048 2MB pages to identity-map the whole of + * the 32-bit address space. + */ +pde_low: + .equ PDE_LOW_PTES, ( SIZEOF_LOW_4GB / SIZEOF_2MB_PAGE ) + .equ PDE_LOW_PTS, ( ( PDE_LOW_PTES * SIZEOF_PTE ) / SIZEOF_PT ) + .space ( PDE_LOW_PTS * SIZEOF_PT ) + .size pde_low, . - pde_low + + /* Page table entries (PTEs) for .textdata + * + * This comprises enough 4kB pages to map the whole of + * .textdata. The required number of PTEs is calculated by + * the linker script. + * + * Note that these mappings do not cover the PTEs themselves. + * This does not matter, since code running with paging + * enabled never needs to access these PTEs. + */ +pte_textdata: + /* Allocated by linker script; must be at the end of .textdata */ + + .section ".bss16.pml4", "aw", @nobits +pml4: .long 0 + +/**************************************************************************** + * init_pages (protected-mode near call) + * + * Initialise the page tables ready for long mode. + * + * Parameters: + * %edi : virt_offset + **************************************************************************** + */ + .section ".text.init_pages", "ax", @progbits + .code32 +init_pages: + /* Initialise PML4Es for low 4GB and negative 2GB */ + leal ( VIRTUAL(pdpte) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + movl %eax, VIRTUAL(pml4e) + movl %eax, ( VIRTUAL(pml4e) + SIZEOF_PT - SIZEOF_PTE ) + + /* Initialise PDPTE for negative 1GB */ + movl %eax, ( VIRTUAL(pdpte) + SIZEOF_PT - SIZEOF_PTE ) + + /* Initialise PDPTEs for low 4GB */ + movl $PDE_LOW_PTS, %ecx + leal ( VIRTUAL(pde_low) + ( PDE_LOW_PTS * SIZEOF_PT ) + \ + ( PG_P | PG_RW | PG_US ) )(%edi), %eax +1: subl $SIZEOF_PT, %eax + movl %eax, ( VIRTUAL(pdpte) - SIZEOF_PTE )(,%ecx,SIZEOF_PTE) + loop 1b + + /* Initialise PDEs for low 4GB */ + movl $PDE_LOW_PTES, %ecx + leal ( 0 + ( PG_P | PG_RW | PG_US | PG_PS ) ), %eax +1: subl $SIZEOF_2MB_PAGE, %eax + movl %eax, ( VIRTUAL(pde_low) - SIZEOF_PTE )(,%ecx,SIZEOF_PTE) + loop 1b + + /* Initialise PDEs for .textdata */ + movl $_textdata_pdes, %ecx + leal ( VIRTUAL(_etextdata) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + movl $VIRTUAL(_textdata), %ebx + shrl $( SIZEOF_2MB_PAGE_LOG2 - SIZEOF_PTE_LOG2 ), %ebx + andl $( SIZEOF_PT - 1 ), %ebx +1: subl $SIZEOF_PT, %eax + movl %eax, (VIRTUAL(pde_textdata) - SIZEOF_PTE)(%ebx,%ecx,SIZEOF_PTE) + loop 1b + + /* Initialise PTEs for .textdata */ + movl $_textdata_ptes, %ecx + leal ( VIRTUAL(_textdata) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + addl $_textdata_paged_len, %eax +1: subl $SIZEOF_4KB_PAGE, %eax + movl %eax, ( VIRTUAL(pte_textdata) - SIZEOF_PTE )(,%ecx,SIZEOF_PTE) + loop 1b + + /* Record PML4 physical address */ + leal VIRTUAL(pml4e)(%edi), %eax + movl VIRTUAL(data16), %ebx + subl %edi, %ebx + movl %eax, pml4(%ebx) + + /* Return */ + ret diff --git a/src/arch/x86_64/Makefile.pcbios b/src/arch/x86_64/Makefile.pcbios index ba4c8d8dc..54bc0e488 100644 --- a/src/arch/x86_64/Makefile.pcbios +++ b/src/arch/x86_64/Makefile.pcbios @@ -9,6 +9,10 @@ LDFLAGS += --section-start=.textdata=0xffffffffeb000000 # CFLAGS += -mno-red-zone +# Generate extra space for page tables to cover .textdata +# +LDFLAGS += --defsym=_use_page_tables=1 + # Include generic BIOS Makefile # MAKEDEPS += arch/x86/Makefile.pcbios From a4923354e31f83c17b9c5befadb801b80c9f9cc1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Feb 2016 19:45:23 +0000 Subject: [PATCH 086/591] [build] Fix building on older versions of binutils Some older versions of binutils have issues with both the use of PROVIDE() and the interpretation of numeric literals within a section description. Work around these older versions by defining the required numeric literals outside of any section description, and by automatically determining whether or not to generate extra space for page tables rather than relying on LDFLAGS. Signed-off-by: Michael Brown --- src/arch/x86/scripts/pcbios.lds | 26 ++++++++++++++++++++------ src/arch/x86_64/Makefile.pcbios | 4 ---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/arch/x86/scripts/pcbios.lds b/src/arch/x86/scripts/pcbios.lds index dccdfbed9..c9a91c02b 100644 --- a/src/arch/x86/scripts/pcbios.lds +++ b/src/arch/x86/scripts/pcbios.lds @@ -27,10 +27,18 @@ SECTIONS { PROVIDE ( _max_align = 16 ); /* - * Default to not generating space for page tables + * Values used in page table calculations + * + * On older versions of ld (without the SANE_EXPR feature), + * numeric literals within a section description tend to be + * interpreted as section-relative symbols. * */ - PROVIDE ( _use_page_tables = 0 ); + _page_size = 4096; + _page_size_1 = ( _page_size - 1 ); + _pte_size = 8; + _pte_count = ( _page_size / _pte_size ); + _pte_count_1 = ( _pte_count - 1 ); /* * Allow decompressor to require a minimum amount of temporary stack @@ -133,12 +141,18 @@ SECTIONS { *(COMMON) *(.stack) *(.stack.*) + _pages = .; *(.pages) *(.pages.*) - _textdata_paged_len = ABSOLUTE ( . - _textdata ); - _textdata_ptes = ABSOLUTE ( ( _textdata_paged_len + 4095 ) / 4096 ); - _textdata_pdes = ABSOLUTE ( ( _textdata_ptes + 511 ) / 512 ); - . += ( _use_page_tables ? ( _textdata_pdes * 4096 ) : 0 ); + _use_page_tables = ABSOLUTE ( . ) - ABSOLUTE ( _pages ); + _textdata_paged_len = + ABSOLUTE ( ABSOLUTE ( . ) - ABSOLUTE ( _textdata ) ); + _textdata_ptes = + ABSOLUTE ( ( _textdata_paged_len + _page_size_1 ) / _page_size ); + _textdata_pdes = + ABSOLUTE ( ( _textdata_ptes + _pte_count_1 ) / _pte_count ); + . += ( _use_page_tables ? ( _textdata_pdes * _page_size ) : 0 ); + _epages = .; _etextdata = .; } _textdata_filesz = ABSOLUTE ( _mtextdata ) - ABSOLUTE ( _textdata ); diff --git a/src/arch/x86_64/Makefile.pcbios b/src/arch/x86_64/Makefile.pcbios index 54bc0e488..ba4c8d8dc 100644 --- a/src/arch/x86_64/Makefile.pcbios +++ b/src/arch/x86_64/Makefile.pcbios @@ -9,10 +9,6 @@ LDFLAGS += --section-start=.textdata=0xffffffffeb000000 # CFLAGS += -mno-red-zone -# Generate extra space for page tables to cover .textdata -# -LDFLAGS += --defsym=_use_page_tables=1 - # Include generic BIOS Makefile # MAKEDEPS += arch/x86/Makefile.pcbios From ea203e4fe1c60504023ea2604604d8092f24effc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Feb 2016 19:43:04 +0000 Subject: [PATCH 087/591] [librm] Add phys_call() wrapper for calling code with physical addressing Add a phys_call() wrapper function (analogous to the existing real_call() wrapper function) for calling code with flat physical addressing, and use this wrapper within the PHYS_CODE() macro. Move the relevant functionality inside librm.S, where it more naturally belongs. The COMBOOT code currently uses explicit calls to _virt_to_phys and _phys_to_virt. These will need to be rewritten if our COMBOOT support is ever generalised to be able to run in a 64-bit build. Specifically: - com32_exec_loop() should be restructured to use PHYS_CODE() - com32_wrapper.S should be restructured to use an equivalent of prot_call(), passing parameters via a struct i386_all_regs - there appears to be no need for com32_wrapper.S to switch between external and internal stacks; this could be omitted to simplify the design. For now, librm.S continues to expose _virt_to_phys and _phys_to_virt for use by com32.c and com32_wrapper.S. Similarly, librm.S continues to expose _intr_to_virt for use by gdbidt.S. Signed-off-by: Michael Brown --- src/arch/x86/core/virtaddr.S | 145 --------------------- src/arch/x86/include/librm.h | 11 +- src/arch/x86/transitions/librm.S | 209 ++++++++++++++++++++++++++++++- 3 files changed, 212 insertions(+), 153 deletions(-) delete mode 100644 src/arch/x86/core/virtaddr.S diff --git a/src/arch/x86/core/virtaddr.S b/src/arch/x86/core/virtaddr.S deleted file mode 100644 index 45beb164d..000000000 --- a/src/arch/x86/core/virtaddr.S +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Functions to support the virtual addressing method of relocation - * that Etherboot uses. - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) - -#include "librm.h" - - .arch i386 - .text - .code32 - -/**************************************************************************** - * _virt_to_phys (virtual addressing) - * - * Switch from virtual to flat physical addresses. %esp is adjusted - * to a physical value. Segment registers are set to flat physical - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _virt_to_phys -_virt_to_phys: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Change return address to a physical address */ - movl VIRTUAL(virt_offset), %ebp - addl %ebp, 12(%esp) - - /* Switch to physical code segment */ - cli - pushl $PHYSICAL_CS - leal VIRTUAL(1f)(%ebp), %eax - pushl %eax - lret -1: - /* Reload other segment registers and adjust %esp */ - movl $PHYSICAL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - movl %eax, %ss - addl %ebp, %esp - - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret - -/**************************************************************************** - * _phys_to_virt (flat physical addressing) - * - * Switch from flat physical to virtual addresses. %esp is adjusted - * to a virtual value. Segment registers are set to virtual - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _phys_to_virt -_phys_to_virt: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Switch to virtual code segment */ - cli - ljmp $VIRTUAL_CS, $VIRTUAL(1f) -1: - /* Reload data segment registers */ - movl $VIRTUAL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - - /* Reload stack segment and adjust %esp */ - movl VIRTUAL(virt_offset), %ebp - movl %eax, %ss - subl %ebp, %esp - - /* Change the return address to a virtual address */ - subl %ebp, 12(%esp) - - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret - -/**************************************************************************** - * _intr_to_virt (virtual code segment, virtual or physical stack segment) - * - * Switch from virtual code segment with either a virtual or physical - * stack segment to using virtual addressing. %esp is adjusted if - * necessary to a virtual value. Segment registers are set to virtual - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _intr_to_virt -_intr_to_virt: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Check whether stack segment is physical or virtual */ - movl %ss, %eax - cmpw $VIRTUAL_DS, %ax - movl $VIRTUAL_DS, %eax - - /* Reload data segment registers */ - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - - /* Reload stack segment and adjust %esp if necessary */ - je 1f - movl VIRTUAL(virt_offset), %ebp - movl %eax, %ss - subl %ebp, %esp -1: - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index fc31c5036..bc925a2db 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -250,11 +250,16 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); /* PHYS_CODE: declare a fragment of code that executes in flat physical mode */ #define PHYS_CODE( asm_code_str ) \ - "call _virt_to_phys\n\t" \ + "push $1f\n\t" \ + "call phys_call\n\t" \ + ".section \".text.phys\", \"ax\", @progbits\n\t"\ ".code32\n\t" \ + "\n1:\n\t" \ asm_code_str \ - "call _phys_to_virt\n\t" \ - CODE_DEFAULT "\n\t" + "\n\t" \ + "ret\n\t" \ + CODE_DEFAULT "\n\t" \ + ".previous\n\t" /** Number of interrupts */ #define NUM_INT 256 diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index f3854dfe8..ab4994fb6 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -501,6 +501,150 @@ rm_gdtr: .word 0 /* Limit */ .long 0 /* Base */ +/**************************************************************************** + * phys_to_prot (protected-mode near call, 32-bit physical return address) + * + * Switch from 32-bit protected mode with physical addresses to 32-bit + * protected mode with virtual addresses. %esp is adjusted to a + * virtual address. All other registers and flags are preserved. + * + * The return address for this function should be a 32-bit physical + * (sic) address. + * + **************************************************************************** + */ + .section ".text.phys_to_prot", "ax", @progbits + .code32 + .globl phys_to_prot +phys_to_prot: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Switch to virtual code segment */ + cli + ljmp $VIRTUAL_CS, $VIRTUAL(1f) +1: + /* Switch to virtual data segment and adjust %esp */ + movw $VIRTUAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl VIRTUAL(virt_offset), %ebp + subl %ebp, %esp + + /* Adjust return address to a virtual address */ + subl %ebp, 12(%esp) + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + + /* Expose as _phys_to_virt for use by COMBOOT */ + .globl _phys_to_virt + .equ _phys_to_virt, phys_to_prot + +/**************************************************************************** + * prot_to_phys (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with virtual addresses to 32-bit + * protected mode with physical addresses. %esp is adjusted to a + * physical address. All other registers and flags are preserved. + * + * The return address for this function should be a 32-bit virtual + * (sic) address. + * + **************************************************************************** + */ + .section ".text.prot_to_phys", "ax", @progbits + .code32 +prot_to_phys: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Adjust return address to a physical address */ + movl VIRTUAL(virt_offset), %ebp + addl %ebp, 12(%esp) + + /* Switch to physical code segment */ + cli + pushl $PHYSICAL_CS + leal VIRTUAL(1f)(%ebp), %eax + pushl %eax + lret +1: + /* Switch to physical data segment and adjust %esp */ + movw $PHYSICAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + addl %ebp, %esp + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + + /* Expose as _virt_to_phys for use by COMBOOT */ + .globl _virt_to_phys + .equ _virt_to_phys, prot_to_phys + +/**************************************************************************** + * intr_to_prot (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with a virtual code segment and + * either a physical or virtual stack segment to 32-bit protected mode + * with normal virtual addresses. %esp is adjusted if necessary to a + * virtual address. All other registers and flags are preserved. + * + * The return address for this function should be a 32-bit virtual + * address. + * + **************************************************************************** + */ + .section ".text.intr_to_prot", "ax", @progbits + .code32 + .globl intr_to_prot +intr_to_prot: + /* Preserve registers and flags */ + pushfl + pushl %eax + + /* Check whether stack segment is physical or virtual */ + movw %ss, %ax + cmpw $VIRTUAL_DS, %ax + movw $VIRTUAL_DS, %ax + + /* Reload data segment registers */ + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Reload stack segment and adjust %esp if necessary */ + je 1f + movw %ax, %ss + subl VIRTUAL(virt_offset), %esp +1: + /* Restore registers and flags, and return */ + popl %eax + popfl + ret + + /* Expose as _intr_to_virt for use by GDB */ + .globl _intr_to_virt + .equ _intr_to_virt, intr_to_prot + /**************************************************************************** * prot_call (real-mode near call, 16-bit real-mode near return address) * @@ -539,6 +683,7 @@ PC_OFFSET_IDT: .space 6 PC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS PC_OFFSET_PADDING: .space 2 /* for alignment */ PC_OFFSET_RETADDR: .space 2 +PC_OFFSET_PARAMS: PC_OFFSET_FUNCTION: .space 4 PC_OFFSET_END: .previous @@ -601,7 +746,9 @@ pc_rmode: addr32 movl -20(%esp), %esp popfl popfw /* padding */ - ret $4 + + /* Return and discard function parameters */ + ret $( PC_OFFSET_END - PC_OFFSET_PARAMS ) /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) @@ -620,7 +767,7 @@ pc_rmode: * See librm.h and realmode.h for details and examples. * * Parameters: - * (32-bit) near pointer to real-mode function to call + * function : offset within .text16 of real-mode function to call * * Returns: none **************************************************************************** @@ -629,6 +776,7 @@ pc_rmode: RC_OFFSET_REGS: .space SIZEOF_I386_REGS RC_OFFSET_REGS_END: RC_OFFSET_RETADDR: .space 4 +RC_OFFSET_PARAMS: RC_OFFSET_FUNCTION: .space 4 RC_OFFSET_END: .previous @@ -665,9 +813,11 @@ rc_rmode: .section ".text.real_call", "ax", @progbits .code32 rc_pmode: - /* Restore registers and return */ + /* Restore registers */ popal - ret $4 + + /* Return and discard function parameters */ + ret $( RC_OFFSET_END - RC_OFFSET_PARAMS ) /* Function vector, used because "call xx(%sp)" is not a valid @@ -684,6 +834,55 @@ rm_default_gdtr_idtr: .word 0x03ff /* Interrupt descriptor table limit */ .long 0 /* Interrupt descriptor table base */ +/**************************************************************************** + * phys_call (protected-mode near call, 32-bit virtual return address) + * + * Call a function with flat 32-bit physical addressing + * + * The non-segment register values will be passed directly to the + * function. The segment registers will be set for flat 32-bit + * physical addressing. The non-segment register values set by the + * function will be passed back to the caller. + * + * librm.h defines a convenient macro PHYS_CODE() for using phys_call. + * + * Parameters: + * function : virtual (sic) address of function to call + * + **************************************************************************** + */ + .struct 0 +PHC_OFFSET_RETADDR: .space 4 +PHC_OFFSET_PARAMS: +PHC_OFFSET_FUNCTION: .space 4 +PHC_OFFSET_END: + .previous + + .section ".text.phys_call", "ax", @progbits + .code32 + .globl phys_call +phys_call: + /* Adjust function pointer to a physical address */ + pushl %ebp + movl VIRTUAL(virt_offset), %ebp + addl %ebp, ( PHC_OFFSET_FUNCTION + 4 /* saved %ebp */ )(%esp) + popl %ebp + + /* Switch to physical addresses */ + call prot_to_phys + + /* Call function */ + call *PHC_OFFSET_FUNCTION(%esp) + + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to virtual addresses */ + call phys_to_prot + + /* Return and discard function parameters */ + ret $( PHC_OFFSET_END - PHC_OFFSET_PARAMS ) + /**************************************************************************** * flatten_real_mode (real-mode near call) * @@ -733,7 +932,7 @@ interrupt_wrapper: pushl %esp /* Switch to virtual addressing */ - call _intr_to_virt + call intr_to_prot /* Expand IRQ number to whole %eax register */ movzbl %al, %eax From 5fbfe50ccbbafe2f6bebd45e927bf7f9e23846dd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 21 Feb 2016 01:01:28 +0000 Subject: [PATCH 088/591] [librm] Do not preserve flags unnecessarily No callers of prot_to_phys, phys_to_prot, or intr_to_prot require the flags to be preserved. Remove the unnecessary pushfl/popfl pairs. Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index ab4994fb6..a3046987e 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -506,7 +506,7 @@ rm_gdtr: * * Switch from 32-bit protected mode with physical addresses to 32-bit * protected mode with virtual addresses. %esp is adjusted to a - * virtual address. All other registers and flags are preserved. + * virtual address. All other registers are preserved. * * The return address for this function should be a 32-bit physical * (sic) address. @@ -517,8 +517,7 @@ rm_gdtr: .code32 .globl phys_to_prot phys_to_prot: - /* Preserve registers and flags */ - pushfl + /* Preserve registers */ pushl %eax pushl %ebp @@ -537,12 +536,11 @@ phys_to_prot: subl %ebp, %esp /* Adjust return address to a virtual address */ - subl %ebp, 12(%esp) + subl %ebp, 8(%esp) - /* Restore registers and flags, and return */ + /* Restore registers and return */ popl %ebp popl %eax - popfl ret /* Expose as _phys_to_virt for use by COMBOOT */ @@ -554,7 +552,7 @@ phys_to_prot: * * Switch from 32-bit protected mode with virtual addresses to 32-bit * protected mode with physical addresses. %esp is adjusted to a - * physical address. All other registers and flags are preserved. + * physical address. All other registers are preserved. * * The return address for this function should be a 32-bit virtual * (sic) address. @@ -564,14 +562,13 @@ phys_to_prot: .section ".text.prot_to_phys", "ax", @progbits .code32 prot_to_phys: - /* Preserve registers and flags */ - pushfl + /* Preserve registers */ pushl %eax pushl %ebp /* Adjust return address to a physical address */ movl VIRTUAL(virt_offset), %ebp - addl %ebp, 12(%esp) + addl %ebp, 8(%esp) /* Switch to physical code segment */ cli @@ -589,10 +586,9 @@ prot_to_phys: movw %ax, %ss addl %ebp, %esp - /* Restore registers and flags, and return */ + /* Restore registers and return */ popl %ebp popl %eax - popfl ret /* Expose as _virt_to_phys for use by COMBOOT */ @@ -605,7 +601,7 @@ prot_to_phys: * Switch from 32-bit protected mode with a virtual code segment and * either a physical or virtual stack segment to 32-bit protected mode * with normal virtual addresses. %esp is adjusted if necessary to a - * virtual address. All other registers and flags are preserved. + * virtual address. All other registers are preserved. * * The return address for this function should be a 32-bit virtual * address. @@ -616,8 +612,7 @@ prot_to_phys: .code32 .globl intr_to_prot intr_to_prot: - /* Preserve registers and flags */ - pushfl + /* Preserve registers */ pushl %eax /* Check whether stack segment is physical or virtual */ @@ -636,9 +631,8 @@ intr_to_prot: movw %ax, %ss subl VIRTUAL(virt_offset), %esp 1: - /* Restore registers and flags, and return */ + /* Restore registers and return */ popl %eax - popfl ret /* Expose as _intr_to_virt for use by GDB */ From b6ebafe1bba5fa841c71a03f1a264e6a17ed8584 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 21 Feb 2016 11:13:04 +0000 Subject: [PATCH 089/591] [librm] Mark virt_offset, text16, data16, rm_cs, and rm_ds as constant The physical locations of .textdata, .text16 and .data16 are constant from the point of view of C code. Mark the relevant variables as constant to allow gcc to optimise out redundant reads. Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index bc925a2db..efc8b114a 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -79,7 +79,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); "call prot_call\n\t" /* Variables in librm.S */ -extern unsigned long virt_offset; +extern const unsigned long virt_offset; /** * Convert physical address to user pointer @@ -170,8 +170,8 @@ UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset, * */ -extern char *data16; -extern char *text16; +extern char * const data16; +extern char * const text16; #define __data16( variable ) \ __attribute__ (( section ( ".data16" ) )) \ @@ -216,9 +216,9 @@ extern char *text16; /* Variables in librm.S, present in the normal data segment */ extern uint16_t rm_sp; extern uint16_t rm_ss; -extern uint16_t __text16 ( rm_cs ); +extern const uint16_t __text16 ( rm_cs ); #define rm_cs __use_text16 ( rm_cs ) -extern uint16_t __text16 ( rm_ds ); +extern const uint16_t __text16 ( rm_ds ); #define rm_ds __use_text16 ( rm_ds ) extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); From 4c1f2486e664e9c88164cfff917a99bfe6beed52 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 21 Feb 2016 11:37:37 +0000 Subject: [PATCH 090/591] [librm] Support userptr_t in 64-bit builds In a 64-bit build, the entirety of the 32-bit address space is identity-mapped and so any valid physical address may immediately be used as a virtual address. Conversely, a virtual address that is already within the 32-bit address space may immediately be used as a physical address. A valid virtual address that lies outside the 32-bit address space must be an address within .textdata, and so can be converted to a physical address by adding virt_offset. Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index efc8b114a..d6214ac80 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -89,6 +89,15 @@ extern const unsigned long virt_offset; */ static inline __always_inline userptr_t UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) { + + /* In a 64-bit build, any valid physical address is directly + * usable as a virtual address, since the low 4GB is + * identity-mapped. + */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) + return phys_addr; + + /* In a 32-bit build, subtract virt_offset */ return ( phys_addr - virt_offset ); } @@ -101,7 +110,20 @@ UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) { */ static inline __always_inline unsigned long UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) { - return ( userptr + offset + virt_offset ); + unsigned long addr = ( userptr + offset ); + + /* In a 64-bit build, any virtual address in the low 4GB is + * directly usable as a physical address, since the low 4GB is + * identity-mapped. + */ + if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) && + ( addr <= 0xffffffffUL ) ) + return addr; + + /* In a 32-bit build or in a 64-bit build with a virtual + * address above 4GB: add virt_offset + */ + return ( addr + virt_offset ); } static inline __always_inline userptr_t From e2cf3138f073905d31b1e6cad3cc69c6a63c34ac Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Feb 2016 00:49:08 +0000 Subject: [PATCH 091/591] [librm] Rename prot_call() to virt_call() Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 4 +- src/arch/x86/include/registers.h | 2 +- src/arch/x86/transitions/librm.S | 62 +++++++++++++-------------- src/arch/x86/transitions/librm_test.c | 10 ++--- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index d6214ac80..8e39e91b9 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -58,7 +58,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ .macro virtcall function pushl $VIRTUAL(\function) - call prot_call + call virt_call .endm #else /* ASSEMBLY */ @@ -76,7 +76,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define VIRT_CALL( function ) \ "pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \ - "call prot_call\n\t" + "call virt_call\n\t" /* Variables in librm.S */ extern const unsigned long virt_offset; diff --git a/src/arch/x86/include/registers.h b/src/arch/x86/include/registers.h index d9aa3c376..dd3b59fd5 100644 --- a/src/arch/x86/include/registers.h +++ b/src/arch/x86/include/registers.h @@ -167,7 +167,7 @@ struct i386_seg_regs { * * @endcode * - * prot_call() and kir_call() create this data structure on the stack + * virt_call() and kir_call() create this data structure on the stack * and pass in a pointer to this structure. * */ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index a3046987e..af22dee97 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -640,19 +640,19 @@ intr_to_prot: .equ _intr_to_virt, intr_to_prot /**************************************************************************** - * prot_call (real-mode near call, 16-bit real-mode near return address) + * virt_call (real-mode near call, 16-bit real-mode near return address) * * Call a specific C function in the protected-mode code. The * prototype of the C function must be * void function ( struct i386_all_regs *ix86 ); * ix86 will point to a struct containing the real-mode registers - * at entry to prot_call. + * at entry to virt_call(). * - * All registers will be preserved across prot_call(), unless the C + * All registers will be preserved across virt_call(), unless the C * function explicitly overwrites values in ix86. Interrupt status * and GDT will also be preserved. Gate A20 will be enabled. * - * Note that prot_call() does not rely on the real-mode stack + * Note that virt_call() does not rely on the real-mode stack * remaining intact in order to return, since everything relevant is * copied to the protected-mode stack for the duration of the call. * In particular, this means that a real-mode prefix can make a call @@ -666,26 +666,26 @@ intr_to_prot: * * Example usage: * pushl $pxe_api_call - * call prot_call + * call virt_call * to call in to the C function * void pxe_api_call ( struct i386_all_regs *ix86 ); **************************************************************************** */ .struct 0 -PC_OFFSET_GDT: .space 6 -PC_OFFSET_IDT: .space 6 -PC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS -PC_OFFSET_PADDING: .space 2 /* for alignment */ -PC_OFFSET_RETADDR: .space 2 -PC_OFFSET_PARAMS: -PC_OFFSET_FUNCTION: .space 4 -PC_OFFSET_END: +VC_OFFSET_GDT: .space 6 +VC_OFFSET_IDT: .space 6 +VC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS +VC_OFFSET_PADDING: .space 2 /* for alignment */ +VC_OFFSET_RETADDR: .space 2 +VC_OFFSET_PARAMS: +VC_OFFSET_FUNCTION: .space 4 +VC_OFFSET_END: .previous - .section ".text16.prot_call", "ax", @progbits + .section ".text16.virt_call", "ax", @progbits .code16 - .globl prot_call -prot_call: + .globl virt_call +virt_call: /* Preserve registers, flags and GDT on external RM stack */ pushfw /* padding */ pushfl @@ -696,37 +696,37 @@ prot_call: pushw %ds pushw %ss pushw %cs - subw $PC_OFFSET_IX86, %sp + subw $VC_OFFSET_IX86, %sp movw %sp, %bp - sidt PC_OFFSET_IDT(%bp) - sgdt PC_OFFSET_GDT(%bp) + sidt VC_OFFSET_IDT(%bp) + sgdt VC_OFFSET_GDT(%bp) /* For sanity's sake, clear the direction flag as soon as possible */ cld /* Switch to protected mode and move register dump to PM stack */ - movl $PC_OFFSET_END, %ecx - pushl $VIRTUAL(pc_pmode) + movl $VC_OFFSET_END, %ecx + pushl $VIRTUAL(vc_pmode) jmp real_to_prot - .section ".text.prot_call", "ax", @progbits + .section ".text.virt_call", "ax", @progbits .code32 -pc_pmode: +vc_pmode: /* Call function */ - leal PC_OFFSET_IX86(%esp), %eax + leal VC_OFFSET_IX86(%esp), %eax pushl %eax - call *(PC_OFFSET_FUNCTION+4)(%esp) + call *(VC_OFFSET_FUNCTION+4)(%esp) popl %eax /* discard */ /* Switch to real mode and move register dump back to RM stack */ - movl $PC_OFFSET_END, %ecx + movl $VC_OFFSET_END, %ecx movl %esp, %esi - pushl $pc_rmode + pushl $vc_rmode jmp prot_to_real - .section ".text16.prot_call", "ax", @progbits + .section ".text16.virt_call", "ax", @progbits .code16 -pc_rmode: +vc_rmode: /* Restore registers and flags and return */ - addw $( PC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp + addw $( VC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp popw %ds popw %es popw %fs @@ -742,7 +742,7 @@ pc_rmode: popfw /* padding */ /* Return and discard function parameters */ - ret $( PC_OFFSET_END - PC_OFFSET_PARAMS ) + ret $( VC_OFFSET_END - VC_OFFSET_PARAMS ) /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) diff --git a/src/arch/x86/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c index 3f9ead218..ba4254fe4 100644 --- a/src/arch/x86/transitions/librm_test.c +++ b/src/arch/x86/transitions/librm_test.c @@ -52,8 +52,8 @@ static struct profiler r2p_profiler __profiler = { .name = "r2p" }; /** Real-mode call profiler */ static struct profiler real_call_profiler __profiler = { .name = "real_call" }; -/** Protected-mode call profiler */ -static struct profiler prot_call_profiler __profiler = { .name = "prot_call" }; +/** Virtual call profiler */ +static struct profiler virt_call_profiler __profiler = { .name = "virt_call" }; /** * Dummy function for profiling tests @@ -101,7 +101,7 @@ static void librm_test_exec ( void ) { profile_stop ( &real_call_profiler ); } - /* Profile complete protected-mode call cycle */ + /* Profile complete virtual call cycle */ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" "movl %k0, %k2\n\t" @@ -109,8 +109,8 @@ static void librm_test_exec ( void ) { "rdtsc\n\t" ) : "=a" ( stopped ), "=d" ( discard_d ), "=R" ( started ) : ); - profile_start_at ( &prot_call_profiler, started ); - profile_stop_at ( &prot_call_profiler, stopped ); + profile_start_at ( &virt_call_profiler, started ); + profile_stop_at ( &virt_call_profiler, stopped ); } } From 614305743031bdfc02cb4ce346e450cd1d476e17 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Feb 2016 02:44:19 +0000 Subject: [PATCH 092/591] [librm] Add support for running in 64-bit long mode Add support for running the BIOS version of iPXE in 64-bit long mode. A 64-bit BIOS version of iPXE can be built using e.g. make bin-x86_64-pcbios/ipxe.usb make bin-x86_64-pcbios/8086100e.mrom The 64-bit BIOS version should appear to function identically to the normal 32-bit BIOS version. The physical memory layout is unaltered: iPXE is still relocated to the top of the available 32-bit address space. The code is linked to a virtual address of 0xffffffffeb000000 (in the negative 2GB as required by -mcmodel=kernel), with 4kB pages created to cover the whole of .textdata. 2MB pages are created to cover the whole of the 32-bit address space. The 32-bit portions of the code run with VIRTUAL_CS and VIRTUAL_DS configured such that truncating a 64-bit virtual address gives a 32-bit virtual address pointing to the same physical location. The stack pointer remains as a physical address when running in long mode (although the .stack section is accessible via the negative 2GB virtual address); this is done in order to simplify the handling of interrupts occurring while executing a portion of 32-bit code with flat physical addressing via PHYS_CODE(). Interrupts may be enabled in either 64-bit long mode, 32-bit protected mode with virtual addresses, 32-bit protected mode with physical addresses, or 16-bit real mode. Interrupts occurring in any mode other than real mode will be reflected down to real mode and handled by whichever ISR is hooked into the BIOS interrupt vector table. Signed-off-by: Michael Brown --- src/Makefile | 3 + src/arch/x86/include/librm.h | 48 ++- src/arch/x86/transitions/librm.S | 446 +++++++++++++++++++++++--- src/arch/x86/transitions/librm_mgmt.c | 56 +++- 4 files changed, 495 insertions(+), 58 deletions(-) diff --git a/src/Makefile b/src/Makefile index 2a9cc9e8f..3ffa1eba4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -151,6 +151,9 @@ all : $(ALL) everything : $(Q)$(MAKE) --no-print-directory $(ALL) \ bin/3c509.rom bin/intel.rom bin/intel.mrom \ + bin-x86_64-pcbios/8086100e.mrom bin-x86_64-pcbios/intel.rom \ + bin-x86_64-pcbios/ipxe.usb bin-x86_64-pcbios/ipxe.pxe \ + bin-x86_64-pcbios/undionly.kpxe \ bin-i386-efi/ipxe.efi bin-i386-efi/ipxe.efidrv \ bin-i386-efi/ipxe.efirom \ bin-x86_64-efi/ipxe.efi bin-x86_64-efi/ipxe.efidrv \ diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 8e39e91b9..02d3081b4 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define REAL_CS 0x28 #define REAL_DS 0x30 #define P2R_DS 0x38 +#define LONG_CS 0x40 /* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS * @@ -286,16 +287,24 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); /** Number of interrupts */ #define NUM_INT 256 -/** An interrupt descriptor table register */ -struct idtr { +/** A 32-bit interrupt descriptor table register */ +struct idtr32 { /** Limit */ uint16_t limit; /** Base */ uint32_t base; } __attribute__ (( packed )); -/** An interrupt descriptor table entry */ -struct interrupt_descriptor { +/** A 64-bit interrupt descriptor table register */ +struct idtr64 { + /** Limit */ + uint16_t limit; + /** Base */ + uint64_t base; +} __attribute__ (( packed )); + +/** A 32-bit interrupt descriptor table entry */ +struct interrupt32_descriptor { /** Low 16 bits of address */ uint16_t low; /** Code segment */ @@ -308,23 +317,44 @@ struct interrupt_descriptor { uint16_t high; } __attribute__ (( packed )); +/** A 64-bit interrupt descriptor table entry */ +struct interrupt64_descriptor { + /** Low 16 bits of address */ + uint16_t low; + /** Code segment */ + uint16_t segment; + /** Unused */ + uint8_t unused; + /** Type and attributes */ + uint8_t attr; + /** Middle 16 bits of address */ + uint16_t mid; + /** High 32 bits of address */ + uint32_t high; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + /** Interrupt descriptor is present */ #define IDTE_PRESENT 0x80 /** Interrupt descriptor 32-bit interrupt gate type */ #define IDTE_TYPE_IRQ32 0x0e +/** Interrupt descriptor 64-bit interrupt gate type */ +#define IDTE_TYPE_IRQ64 0x0e + /** An interrupt vector * * Each interrupt vector comprises an eight-byte fragment of code: * - * 60 pushal + * 50 pushl %eax (or pushq %rax in long mode) * b0 xx movb $INT, %al * e9 xx xx xx xx jmp interrupt_wrapper */ struct interrupt_vector { - /** "pushal" instruction */ - uint8_t pushal; + /** "push" instruction */ + uint8_t push; /** "movb" instruction */ uint8_t movb; /** Interrupt number */ @@ -337,8 +367,8 @@ struct interrupt_vector { uint8_t next[0]; } __attribute__ (( packed )); -/** "pushal" instruction */ -#define PUSHAL_INSN 0x60 +/** "push %eax" instruction */ +#define PUSH_INSN 0x50 /** "movb" instruction */ #define MOVB_INSN 0xb0 diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index af22dee97..dbb0b0843 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -19,6 +19,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* CR4: physical address extensions */ #define CR4_PAE ( 1 << 5 ) +/* Extended feature enable MSR (EFER) */ +#define MSR_EFER 0xc0000080 + +/* EFER: long mode enable */ +#define EFER_LME ( 1 << 8 ) + /* Page: present */ #define PG_P 0x01 @@ -49,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS ) #define SIZEOF_I386_FLAGS 4 #define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS ) +#define SIZEOF_X86_64_REGS 128 /* Size of an address */ #ifdef __x86_64__ @@ -57,6 +64,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define SIZEOF_ADDR 4 #endif +/* Default code size */ +#ifdef __x86_64__ +#define CODE_DEFAULT code64 +#else +#define CODE_DEFAULT code32 +#endif + /* Selectively assemble code for 32-bit/64-bit builds */ #ifdef __x86_64__ #define if32 if 0 @@ -124,6 +138,11 @@ p2r_ds: /* 16 bit real mode data segment for prot_to_real transition */ .word 0xffff, ( P2R_DS << 4 ) .byte 0, 0x93, 0x00, 0 + .org gdt + LONG_CS, 0 +long_cs: /* 64 bit long mode code segment */ + .word 0, 0 + .byte 0, 0x9a, 0x20, 0 + gdt_end: .equ gdt_length, gdt_end - gdt @@ -256,10 +275,9 @@ init_librm: .if32 ; subl %edi, %eax ; .endif movl %eax, rm_data16 -.if64 ; /* Reset page tables, if applicable */ - xorl %eax, %eax - movl %eax, pml4 -.endif + /* Configure virt_call for protected mode, if applicable */ +.if64 ; movl $VIRTUAL(vc_pmode), %cs:vc_jmp_offset ; .endif + /* Switch to protected mode */ virtcall init_librm_pmode .section ".text.init_librm", "ax", @progbits @@ -276,8 +294,10 @@ init_librm_pmode: rep movsl popw %ds -.if64 ; /* Initialise page tables, if applicable */ +.if64 ; /* Initialise long mode, if applicable */ movl VIRTUAL(virt_offset), %edi + leal VIRTUAL(p2l_ljmp_target)(%edi), %eax + movl %eax, VIRTUAL(p2l_ljmp_offset) call init_pages .endif /* Return to real mode */ @@ -286,6 +306,9 @@ init_librm_pmode: .code16 init_librm_rmode: + /* Configure virt_call for long mode, if applicable */ +.if64 ; movl $VIRTUAL(vc_lmode), %cs:vc_jmp_offset ; .endif + /* Initialise IDT */ virtcall init_idt @@ -361,9 +384,10 @@ real_to_prot: movw %ax, %gs movw %ax, %ss - /* Switch to protected mode */ + /* Switch to protected mode (with paging disabled if applicable) */ cli movl %cr0, %eax +.if64 ; andl $~CR0_PG, %eax ; .endif orb $CR0_PE, %al movl %eax, %cr0 data32 ljmp $VIRTUAL_CS, $VIRTUAL(r2p_pmode) @@ -380,7 +404,7 @@ r2p_pmode: movl VIRTUAL(pm_esp), %esp /* Load protected-mode interrupt descriptor table */ - lidt VIRTUAL(idtr) + lidt VIRTUAL(idtr32) /* Record real-mode %ss:sp (after removal of data) */ movw %bp, VIRTUAL(rm_ss) @@ -639,11 +663,234 @@ intr_to_prot: .globl _intr_to_virt .equ _intr_to_virt, intr_to_prot +/**************************************************************************** + * prot_to_long (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with virtual addresses to 64-bit + * long mode. The protected-mode %esp is adjusted to a physical + * address. All other registers are preserved. + * + * The return address for this function should be a 32-bit (sic) + * virtual address. + * + **************************************************************************** + */ + .if64 + + .section ".text.prot_to_long", "ax", @progbits + .code32 +prot_to_long: + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edx + + /* Set up PML4 */ + movl VIRTUAL(pml4), %eax + movl %eax, %cr3 + + /* Enable PAE */ + movl %cr4, %eax + orb $CR4_PAE, %al + movl %eax, %cr4 + + /* Enable long mode */ + movl $MSR_EFER, %ecx + rdmsr + orw $EFER_LME, %ax + wrmsr + + /* Enable paging */ + movl %cr0, %eax + orl $CR0_PG, %eax + movl %eax, %cr0 + + /* Restore registers */ + popl %edx + popl %ecx + popl %eax + + /* Construct 64-bit return address */ + pushl (%esp) + movl $0xffffffff, 4(%esp) +p2l_ljmp: + /* Switch to long mode (using a physical %rip) */ + ljmp $LONG_CS, $0 + .code64 +p2l_lmode: + /* Adjust and zero-extend %esp to a physical address */ + addl virt_offset, %esp + + /* Use long-mode IDT */ + lidt idtr64 + + /* Return to virtual address */ + ret + + /* Long mode jump offset and target. Required since an ljmp + * in protected mode will zero-extend the offset, and so + * cannot reach an address within the negative 2GB as used by + * -mcmodel=kernel. Assigned by the call to init_librm. + */ + .equ p2l_ljmp_offset, ( p2l_ljmp + 1 ) + .equ p2l_ljmp_target, p2l_lmode + + .endif + +/**************************************************************************** + * long_to_prot (long-mode near call, 64-bit virtual return address) + * + * Switch from 64-bit long mode to 32-bit protected mode with virtual + * addresses. The long-mode %rsp is adjusted to a virtual address. + * All other registers are preserved. + * + * The return address for this function should be a 64-bit (sic) + * virtual address. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_to_prot", "ax", @progbits + .code64 +long_to_prot: + /* Switch to protected mode */ + ljmp *l2p_vector + .code32 +l2p_pmode: + /* Adjust %esp to a virtual address */ + subl VIRTUAL(virt_offset), %esp + + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edx + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable PAE (in case external non-PAE-aware code enables paging) */ + movl %cr4, %eax + andb $~CR4_PAE, %al + movl %eax, %cr4 + + /* Disable long mode */ + movl $MSR_EFER, %ecx + rdmsr + andw $~EFER_LME, %ax + wrmsr + + /* Restore registers */ + popl %edx + popl %ecx + popl %eax + + /* Use protected-mode IDT */ + lidt VIRTUAL(idtr32) + + /* Return */ + ret $4 + + /* Long mode jump vector. Required since there is no "ljmp + * immediate" instruction in long mode. + */ + .section ".data.l2p_vector", "aw", @progbits +l2p_vector: + .long VIRTUAL(l2p_pmode), VIRTUAL_CS + + .endif + +/**************************************************************************** + * long_save_regs (long-mode near call, 64-bit virtual return address) + * + * Preserve registers that are accessible only in long mode. This + * includes %r8-%r15 and the upper halves of %rax, %rbx, %rcx, %rdx, + * %rsi, %rdi, and %rbp. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_preserve_regs", "ax", @progbits + .code64 +long_preserve_regs: + /* Preserve registers */ + pushq %rax + pushq %rcx + pushq %rdx + pushq %rbx + pushq %rsp + pushq %rbp + pushq %rsi + pushq %rdi + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + /* Return */ + jmp *SIZEOF_X86_64_REGS(%rsp) + + .endif + +/**************************************************************************** + * long_restore_regs (long-mode near call, 64-bit virtual return address) + * + * Restore registers that are accessible only in long mode. This + * includes %r8-%r15 and the upper halves of %rax, %rbx, %rcx, %rdx, + * %rsi, %rdi, and %rbp. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_restore_regs", "ax", @progbits + .code64 +long_restore_regs: + /* Move return address above register dump */ + popq SIZEOF_X86_64_REGS(%rsp) + + /* Restore registers */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + movl %edi, (%rsp) + popq %rdi + movl %esi, (%rsp) + popq %rsi + movl %ebp, (%rsp) + popq %rbp + leaq 8(%rsp), %rsp /* discard */ + movl %ebx, (%rsp) + popq %rbx + movl %edx, (%rsp) + popq %rdx + movl %ecx, (%rsp) + popq %rcx + movl %eax, (%rsp) + popq %rax + + /* Return */ + ret + + .endif + /**************************************************************************** * virt_call (real-mode near call, 16-bit real-mode near return address) * - * Call a specific C function in the protected-mode code. The - * prototype of the C function must be + * Call a specific C function in 32-bit protected mode or 64-bit long + * mode (as applicable). The prototype of the C function must be * void function ( struct i386_all_regs *ix86 ); * ix86 will point to a struct containing the real-mode registers * at entry to virt_call(). @@ -662,7 +909,7 @@ intr_to_prot: * critical data to registers before calling main()). * * Parameters: - * function : virtual address of protected-mode function to call + * function : 32-bit virtual address of function to call * * Example usage: * pushl $pxe_api_call @@ -674,6 +921,12 @@ intr_to_prot: .struct 0 VC_OFFSET_GDT: .space 6 VC_OFFSET_IDT: .space 6 +.if64 +VC_OFFSET_PADDING64: .space 4 /* for alignment */ +VC_OFFSET_CR3: .space 4 +VC_OFFSET_CR4: .space 4 +VC_OFFSET_EMER: .space 8 +.endif VC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS VC_OFFSET_PADDING: .space 2 /* for alignment */ VC_OFFSET_RETADDR: .space 2 @@ -701,22 +954,49 @@ virt_call: sidt VC_OFFSET_IDT(%bp) sgdt VC_OFFSET_GDT(%bp) +.if64 ; /* Preserve control registers, if applicable */ + movl $MSR_EFER, %ecx + rdmsr + movl %eax, (VC_OFFSET_EMER+0)(%bp) + movl %edx, (VC_OFFSET_EMER+4)(%bp) + movl %cr4, %eax + movl %eax, VC_OFFSET_CR4(%bp) + movl %cr3, %eax + movl %eax, VC_OFFSET_CR3(%bp) +.endif /* For sanity's sake, clear the direction flag as soon as possible */ cld /* Switch to protected mode and move register dump to PM stack */ movl $VC_OFFSET_END, %ecx pushl $VIRTUAL(vc_pmode) - jmp real_to_prot +vc_jmp: jmp real_to_prot .section ".text.virt_call", "ax", @progbits .code32 vc_pmode: - /* Call function */ + /* Call function (in protected mode) */ leal VC_OFFSET_IX86(%esp), %eax pushl %eax call *(VC_OFFSET_FUNCTION+4)(%esp) popl %eax /* discard */ +.if64 ; /* Switch to long mode */ + jmp 1f +vc_lmode: + call prot_to_long + .code64 + + /* Call function (in long mode) */ + leaq VC_OFFSET_IX86(%rsp), %rdi + pushq %rdi + movslq (VC_OFFSET_FUNCTION+8)(%rsp), %rax + callq *%rax + popq %rdi /* discard */ + + /* Switch to protected mode */ + call long_to_prot +1: .code32 +.endif /* Switch to real mode and move register dump back to RM stack */ movl $VC_OFFSET_END, %ecx movl %esp, %esi @@ -725,6 +1005,17 @@ vc_pmode: .section ".text16.virt_call", "ax", @progbits .code16 vc_rmode: +.if64 ; /* Restore control registers, if applicable */ + movw %sp, %bp + movl VC_OFFSET_CR3(%bp), %eax + movl %eax, %cr3 + movl VC_OFFSET_CR4(%bp), %eax + movl %eax, %cr4 + movl (VC_OFFSET_EMER+0)(%bp), %eax + movl (VC_OFFSET_EMER+4)(%bp), %edx + movl $MSR_EFER, %ecx + wrmsr +.endif /* Restore registers and flags and return */ addw $( VC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp popw %ds @@ -744,18 +1035,23 @@ vc_rmode: /* Return and discard function parameters */ ret $( VC_OFFSET_END - VC_OFFSET_PARAMS ) + + /* Protected-mode jump target */ + .equ vc_jmp_offset, ( vc_jmp - 4 ) + /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) + * real_call (long-mode near call, 64-bit virtual return address) * - * Call a real-mode function from protected-mode code. + * Call a real-mode function from protected-mode or long-mode code. * * The non-segment register values will be passed directly to the * real-mode code. The segment registers will be set as per * prot_to_real. The non-segment register values set by the real-mode - * function will be passed back to the protected-mode caller. A - * result of this is that this routine cannot be called directly from - * C code, since it clobbers registers that the C ABI expects the - * callee to preserve. + * function will be passed back to the protected-mode or long-mode + * caller. A result of this is that this routine cannot be called + * directly from C code, since it clobbers registers that the C ABI + * expects the callee to preserve. * * librm.h defines a convenient macro REAL_CODE() for using real_call. * See librm.h and realmode.h for details and examples. @@ -769,16 +1065,25 @@ vc_rmode: .struct 0 RC_OFFSET_REGS: .space SIZEOF_I386_REGS RC_OFFSET_REGS_END: -RC_OFFSET_RETADDR: .space 4 +.if64 +RC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS +RC_OFFSET_LREG_RETADDR: .space SIZEOF_ADDR +.endif +RC_OFFSET_RETADDR: .space SIZEOF_ADDR RC_OFFSET_PARAMS: -RC_OFFSET_FUNCTION: .space 4 +RC_OFFSET_FUNCTION: .space SIZEOF_ADDR RC_OFFSET_END: .previous .section ".text.real_call", "ax", @progbits - .code32 + .CODE_DEFAULT .globl real_call real_call: +.if64 ; /* Preserve registers and switch to protected mode, if applicable */ + call long_preserve_regs + call long_to_prot + .code32 +.endif /* Create register dump and function pointer copy on PM stack */ pushal pushl RC_OFFSET_FUNCTION(%esp) @@ -810,6 +1115,11 @@ rc_pmode: /* Restore registers */ popal +.if64 ; /* Switch to long mode and restore registers, if applicable */ + call prot_to_long + .code64 + call long_restore_regs +.endif /* Return and discard function parameters */ ret $( RC_OFFSET_END - RC_OFFSET_PARAMS ) @@ -830,6 +1140,7 @@ rm_default_gdtr_idtr: /**************************************************************************** * phys_call (protected-mode near call, 32-bit virtual return address) + * phys_call (long-mode near call, 64-bit virtual return address) * * Call a function with flat 32-bit physical addressing * @@ -846,16 +1157,25 @@ rm_default_gdtr_idtr: **************************************************************************** */ .struct 0 -PHC_OFFSET_RETADDR: .space 4 +.if64 +PHC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS +PHC_OFFSET_LREG_RETADDR:.space SIZEOF_ADDR +.endif +PHC_OFFSET_RETADDR: .space SIZEOF_ADDR PHC_OFFSET_PARAMS: -PHC_OFFSET_FUNCTION: .space 4 +PHC_OFFSET_FUNCTION: .space SIZEOF_ADDR PHC_OFFSET_END: .previous .section ".text.phys_call", "ax", @progbits - .code32 + .CODE_DEFAULT .globl phys_call phys_call: +.if64 ; /* Preserve registers and switch to protected mode, if applicable */ + call long_preserve_regs + call long_to_prot + .code32 +.endif /* Adjust function pointer to a physical address */ pushl %ebp movl VIRTUAL(virt_offset), %ebp @@ -874,6 +1194,11 @@ phys_call: /* Switch to virtual addresses */ call phys_to_prot +.if64 ; /* Switch to long mode and restore registers, if applicable */ + call prot_to_long + .code64 + call long_restore_regs +.endif /* Return and discard function parameters */ ret $( PHC_OFFSET_END - PHC_OFFSET_PARAMS ) @@ -900,15 +1225,15 @@ flatten_real_mode: ret .section ".text.flatten_dummy", "ax", @progbits - .code32 + .CODE_DEFAULT flatten_dummy: ret /**************************************************************************** * Interrupt wrapper * - * Used by the protected-mode interrupt vectors to call the - * interrupt() function. + * Used by the protected-mode and long-mode interrupt vectors to call + * the interrupt() function. * * May be entered with either physical or virtual stack segment. **************************************************************************** @@ -917,6 +1242,24 @@ flatten_dummy: .code32 .globl interrupt_wrapper interrupt_wrapper: + /* Preserve registers (excluding already-saved %eax and + * otherwise unused registers which are callee-save for both + * 32-bit and 64-bit ABIs). + */ + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + + /* Expand IRQ number to whole %eax register */ + movzbl %al, %eax + +.if64 ; /* Skip transition to long mode, if applicable */ + movw %cs, %bx + cmpw $LONG_CS, %bx + je 1f +.endif /* Preserve segment registers and original %esp */ pushl %ds pushl %es @@ -927,14 +1270,39 @@ interrupt_wrapper: /* Switch to virtual addressing */ call intr_to_prot +.if64 + /* Switch to long mode */ + call prot_to_long + .code64 - /* Expand IRQ number to whole %eax register */ - movzbl %al, %eax +1: /* Preserve long-mode caller-save registers */ + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + /* Expand IRQ number to whole %rdi register */ + movl %eax, %edi +.endif /* Call interrupt handler */ call interrupt +.if64 + /* Restore long-mode caller-save registers */ + popq %r11 + popq %r10 + popq %r9 + popq %r8 - /* Restore original stack and segment registers */ + /* Skip transition back to protected mode, if applicable */ + cmpw $LONG_CS, %bx + je 1f + + /* Switch to protected mode */ + call long_to_prot + .code32 + cmpw $LONG_CS, %bx +.endif + /* Restore segment registers and original %esp */ lss (%esp), %esp popl %ss popl %gs @@ -942,9 +1310,17 @@ interrupt_wrapper: popl %es popl %ds - /* Restore registers and return */ - popal - iret +1: /* Restore registers */ + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + + /* Return from interrupt (with REX prefix if required) */ +.if64 ; jne 1f ; .byte 0x48 ; .endif +1: iret /**************************************************************************** * Page tables @@ -1022,7 +1398,7 @@ pde_low: pte_textdata: /* Allocated by linker script; must be at the end of .textdata */ - .section ".bss16.pml4", "aw", @nobits + .section ".bss.pml4", "aw", @nobits pml4: .long 0 /**************************************************************************** @@ -1080,9 +1456,7 @@ init_pages: /* Record PML4 physical address */ leal VIRTUAL(pml4e)(%edi), %eax - movl VIRTUAL(data16), %ebx - subl %edi, %ebx - movl %eax, pml4(%ebx) + movl %eax, VIRTUAL(pml4) /* Return */ ret diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c index 32695ae0c..a81270a1c 100644 --- a/src/arch/x86/transitions/librm_mgmt.c +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -23,12 +23,22 @@ extern char interrupt_wrapper[]; /** The interrupt vectors */ static struct interrupt_vector intr_vec[NUM_INT]; -/** The interrupt descriptor table */ -struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) )); +/** The 32-bit interrupt descriptor table */ +static struct interrupt32_descriptor +idt32[NUM_INT] __attribute__ (( aligned ( 16 ) )); + +/** The 32-bit interrupt descriptor table register */ +struct idtr32 idtr32 = { + .limit = ( sizeof ( idt32 ) - 1 ), +}; + +/** The 64-bit interrupt descriptor table */ +static struct interrupt64_descriptor +idt64[NUM_INT] __attribute__ (( aligned ( 16 ) )); /** The interrupt descriptor table register */ -struct idtr idtr = { - .limit = ( sizeof ( idt ) - 1 ), +struct idtr64 idtr64 = { + .limit = ( sizeof ( idt64 ) - 1 ), }; /** Timer interrupt profiler */ @@ -75,13 +85,27 @@ void remove_user_from_rm_stack ( userptr_t data, size_t size ) { * @v vector Interrupt vector, or NULL to disable */ void set_interrupt_vector ( unsigned int intr, void *vector ) { - struct interrupt_descriptor *idte; + struct interrupt32_descriptor *idte32; + struct interrupt64_descriptor *idte64; + intptr_t addr = ( ( intptr_t ) vector ); - idte = &idt[intr]; - idte->segment = VIRTUAL_CS; - idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); - idte->low = ( ( ( intptr_t ) vector ) & 0xffff ); - idte->high = ( ( ( intptr_t ) vector ) >> 16 ); + /* Populate 32-bit interrupt descriptor */ + idte32 = &idt32[intr]; + idte32->segment = VIRTUAL_CS; + idte32->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); + idte32->low = ( addr >> 0 ); + idte32->high = ( addr >> 16 ); + + /* Populate 64-bit interrupt descriptor, if applicable */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + idte64 = &idt64[intr]; + idte64->segment = LONG_CS; + idte64->attr = ( vector ? + ( IDTE_PRESENT | IDTE_TYPE_IRQ64 ) : 0 ); + idte64->low = ( addr >> 0 ); + idte64->mid = ( addr >> 16 ); + idte64->high = ( ( ( uint64_t ) addr ) >> 32 ); + } } /** @@ -95,7 +119,7 @@ void init_idt ( void ) { /* Initialise the interrupt descriptor table and interrupt vectors */ for ( intr = 0 ; intr < NUM_INT ; intr++ ) { vec = &intr_vec[intr]; - vec->pushal = PUSHAL_INSN; + vec->push = PUSH_INSN; vec->movb = MOVB_INSN; vec->intr = intr; vec->jmp = JMP_INSN; @@ -107,8 +131,14 @@ void init_idt ( void ) { intr_vec, sizeof ( intr_vec[0] ), virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); - /* Initialise the interrupt descriptor table register */ - idtr.base = virt_to_phys ( idt ); + /* Initialise the 32-bit interrupt descriptor table register */ + idtr32.base = virt_to_phys ( idt32 ); + + /* Initialise the 64-bit interrupt descriptor table register, + * if applicable. + */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) + idtr64.base = virt_to_phys ( idt64 ); } /** From 5bd8427d3dad38993c1f8f175454f7fde0af34ca Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Feb 2016 15:33:40 +0000 Subject: [PATCH 093/591] [ioapi] Split ioremap() out to a separate IOMAP API Signed-off-by: Michael Brown --- src/arch/x86/core/x86_io.c | 3 -- src/arch/x86/include/bits/iomap.h | 12 +++++ src/arch/x86/include/ipxe/x86_io.h | 17 +------ src/config/defaults/efi.h | 1 + src/config/defaults/pcbios.h | 1 + src/core/iomap_virt.c | 36 ++++++++++++++ src/include/ipxe/io.h | 26 +--------- src/include/ipxe/iomap.h | 78 ++++++++++++++++++++++++++++++ src/include/ipxe/iomap_virt.h | 33 +++++++++++++ 9 files changed, 163 insertions(+), 44 deletions(-) create mode 100644 src/arch/x86/include/bits/iomap.h create mode 100644 src/core/iomap_virt.c create mode 100644 src/include/ipxe/iomap.h create mode 100644 src/include/ipxe/iomap_virt.h diff --git a/src/arch/x86/core/x86_io.c b/src/arch/x86/core/x86_io.c index 3081fa8b9..6c6b6e1e7 100644 --- a/src/arch/x86/core/x86_io.c +++ b/src/arch/x86/core/x86_io.c @@ -74,9 +74,6 @@ static __unused void i386_writeq ( uint64_t data, volatile uint64_t *io_addr ) { PROVIDE_IOAPI_INLINE ( x86, phys_to_bus ); PROVIDE_IOAPI_INLINE ( x86, bus_to_phys ); -PROVIDE_IOAPI_INLINE ( x86, ioremap ); -PROVIDE_IOAPI_INLINE ( x86, iounmap ); -PROVIDE_IOAPI_INLINE ( x86, io_to_bus ); PROVIDE_IOAPI_INLINE ( x86, readb ); PROVIDE_IOAPI_INLINE ( x86, readw ); PROVIDE_IOAPI_INLINE ( x86, readl ); diff --git a/src/arch/x86/include/bits/iomap.h b/src/arch/x86/include/bits/iomap.h new file mode 100644 index 000000000..6110e3187 --- /dev/null +++ b/src/arch/x86/include/bits/iomap.h @@ -0,0 +1,12 @@ +#ifndef _BITS_IOMAP_H +#define _BITS_IOMAP_H + +/** @file + * + * x86-specific I/O mapping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_IOMAP_H */ diff --git a/src/arch/x86/include/ipxe/x86_io.h b/src/arch/x86/include/ipxe/x86_io.h index 5214e9fbb..a6ebe1f4c 100644 --- a/src/arch/x86/include/ipxe/x86_io.h +++ b/src/arch/x86/include/ipxe/x86_io.h @@ -32,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PAGE_SHIFT 12 /* - * Physical<->Bus and Bus<->I/O address mappings + * Physical<->Bus address mappings * */ @@ -46,21 +46,6 @@ IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) { return bus_addr; } -static inline __always_inline void * -IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { - return ( bus_addr ? phys_to_virt ( bus_addr ) : NULL ); -} - -static inline __always_inline void -IOAPI_INLINE ( x86, iounmap ) ( volatile const void *io_addr __unused ) { - /* Nothing to do */ -} - -static inline __always_inline unsigned long -IOAPI_INLINE ( x86, io_to_bus ) ( volatile const void *io_addr ) { - return virt_to_phys ( io_addr ); -} - /* * MMIO reads and writes up to native word size * diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 502bef1d9..24b93a02d 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_EFI #define IOAPI_X86 +#define IOMAP_VIRT #define PCIAPI_EFI #define CONSOLE_EFI #define TIMER_EFI diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 3ed8343ce..56ed00738 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_LIBRM #define IOAPI_X86 +#define IOMAP_VIRT #define PCIAPI_PCBIOS #define TIMER_PCBIOS #define CONSOLE_PCBIOS diff --git a/src/core/iomap_virt.c b/src/core/iomap_virt.c new file mode 100644 index 000000000..c7f487274 --- /dev/null +++ b/src/core/iomap_virt.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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 ); + +/** @file + * + * iPXE I/O mapping API using phys_to_virt() + * + */ + +#include + +PROVIDE_IOMAP_INLINE ( virt, ioremap ); +PROVIDE_IOMAP_INLINE ( virt, iounmap ); +PROVIDE_IOMAP_INLINE ( virt, io_to_bus ); diff --git a/src/include/ipxe/io.h b/src/include/ipxe/io.h index af767915d..fe1388191 100644 --- a/src/include/ipxe/io.h +++ b/src/include/ipxe/io.h @@ -20,8 +20,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include -#include /** Page size */ #define PAGE_SIZE ( 1 << PAGE_SHIFT ) @@ -196,30 +196,6 @@ static inline __always_inline void * bus_to_virt ( unsigned long bus_addr ) { return phys_to_virt ( bus_to_phys ( bus_addr ) ); } -/** - * Map bus address as an I/O address - * - * @v bus_addr Bus address - * @v len Length of region - * @ret io_addr I/O address - */ -void * ioremap ( unsigned long bus_addr, size_t len ); - -/** - * Unmap I/O address - * - * @v io_addr I/O address - */ -void iounmap ( volatile const void *io_addr ); - -/** - * Convert I/O address to bus address (for debug only) - * - * @v io_addr I/O address - * @ret bus_addr Bus address - */ -unsigned long io_to_bus ( volatile const void *io_addr ); - /** * Read byte from memory-mapped device * diff --git a/src/include/ipxe/iomap.h b/src/include/ipxe/iomap.h new file mode 100644 index 000000000..b8ded38ef --- /dev/null +++ b/src/include/ipxe/iomap.h @@ -0,0 +1,78 @@ +#ifndef _IPXE_IOMAP_H +#define _IPXE_IOMAP_H + +/** @file + * + * iPXE I/O mapping API + * + * The I/O mapping API provides methods for mapping and unmapping I/O + * devices. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** + * Calculate static inline I/O mapping API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define IOMAP_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( IOMAP_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an I/O mapping API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_IOMAP( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( IOMAP_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline I/O mapping API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_IOMAP_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( IOMAP_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Map bus address as an I/O address + * + * @v bus_addr Bus address + * @v len Length of region + * @ret io_addr I/O address + */ +void * ioremap ( unsigned long bus_addr, size_t len ); + +/** + * Unmap I/O address + * + * @v io_addr I/O address + */ +void iounmap ( volatile const void *io_addr ); + +/** + * Convert I/O address to bus address (for debug only) + * + * @v io_addr I/O address + * @ret bus_addr Bus address + */ +unsigned long io_to_bus ( volatile const void *io_addr ); + +#endif /* _IPXE_IOMAP_H */ diff --git a/src/include/ipxe/iomap_virt.h b/src/include/ipxe/iomap_virt.h new file mode 100644 index 000000000..4962b7c37 --- /dev/null +++ b/src/include/ipxe/iomap_virt.h @@ -0,0 +1,33 @@ +#ifndef _IPXE_IOMAP_VIRT_H +#define _IPXE_IOMAP_VIRT_H + +/** @file + * + * iPXE I/O mapping API using phys_to_virt() + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOMAP_VIRT +#define IOMAP_PREFIX_virt +#else +#define IOMAP_PREFIX_virt __virt_ +#endif + +static inline __always_inline void * +IOMAP_INLINE ( virt, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { + return ( bus_addr ? phys_to_virt ( bus_addr ) : NULL ); +} + +static inline __always_inline void +IOMAP_INLINE ( virt, iounmap ) ( volatile const void *io_addr __unused ) { + /* Nothing to do */ +} + +static inline __always_inline unsigned long +IOMAP_INLINE ( virt, io_to_bus ) ( volatile const void *io_addr ) { + return virt_to_phys ( io_addr ); +} + +#endif /* _IPXE_IOMAP_VIRT_H */ From 99b5216b1c71dba22dab734e0945887525493cde Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Feb 2016 15:34:28 +0000 Subject: [PATCH 094/591] [librm] Support ioremap() for addresses above 4GB in a 64-bit build Signed-off-by: Michael Brown --- src/arch/x86/include/bits/iomap.h | 2 + src/arch/x86/include/ipxe/iomap_pages.h | 24 +++++ src/arch/x86/include/librm.h | 45 +++++++++ src/arch/x86/transitions/librm.S | 17 ++++ src/arch/x86/transitions/librm_mgmt.c | 122 ++++++++++++++++++++++++ src/config/defaults/pcbios.h | 7 +- 6 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 src/arch/x86/include/ipxe/iomap_pages.h diff --git a/src/arch/x86/include/bits/iomap.h b/src/arch/x86/include/bits/iomap.h index 6110e3187..d6fff257e 100644 --- a/src/arch/x86/include/bits/iomap.h +++ b/src/arch/x86/include/bits/iomap.h @@ -9,4 +9,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include + #endif /* _BITS_IOMAP_H */ diff --git a/src/arch/x86/include/ipxe/iomap_pages.h b/src/arch/x86/include/ipxe/iomap_pages.h new file mode 100644 index 000000000..18e0a3002 --- /dev/null +++ b/src/arch/x86/include/ipxe/iomap_pages.h @@ -0,0 +1,24 @@ +#ifndef _IPXE_IOMAP_PAGES_H +#define _IPXE_IOMAP_PAGES_H + +/** @file + * + * I/O mapping API using page tables + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOMAP_PAGES +#define IOMAP_PREFIX_pages +#else +#define IOMAP_PREFIX_pages __pages_ +#endif + +static inline __always_inline unsigned long +IOMAP_INLINE ( pages, io_to_bus ) ( volatile const void *io_addr ) { + /* Not easy to do; just return the CPU address for debugging purposes */ + return ( ( intptr_t ) io_addr ); +} + +#endif /* _IPXE_IOMAP_PAGES_H */ diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 02d3081b4..311748bec 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -378,6 +378,51 @@ struct interrupt_vector { extern void set_interrupt_vector ( unsigned int intr, void *vector ); +/** A page table */ +struct page_table { + /** Page address and flags */ + uint64_t page[512]; +}; + +/** Page flags */ +enum page_flags { + /** Page is present */ + PAGE_P = 0x01, + /** Page is writable */ + PAGE_RW = 0x02, + /** Page is accessible by user code */ + PAGE_US = 0x04, + /** Page-level write-through */ + PAGE_PWT = 0x08, + /** Page-level cache disable */ + PAGE_PCD = 0x10, + /** Page is a large page */ + PAGE_PS = 0x80, + /** Page is the last page in an allocation + * + * This bit is ignored by the hardware. We use it to track + * the size of allocations made by ioremap(). + */ + PAGE_LAST = 0x800, +}; + +/** The I/O space page table */ +extern struct page_table io_pages; + +/** I/O page size + * + * We choose to use 2MB pages for I/O space, to minimise the number of + * page table entries required. + */ +#define IO_PAGE_SIZE 0x200000UL + +/** I/O page base address + * + * We choose to place I/O space immediately above the identity-mapped + * 32-bit address space. + */ +#define IO_BASE ( ( void * ) 0x100000000ULL ) + #endif /* ASSEMBLY */ #endif /* LIBRM_H */ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index dbb0b0843..bb04ad67d 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -1340,11 +1340,19 @@ interrupt_wrapper: * These point to the PDPT. This creates some aliased * addresses within unused portions of the 64-bit address * space, but allows us to use just a single PDPT. + * + * - PDE[...] covering arbitrary 2MB portions of I/O space + * + * These are 2MB pages created by ioremap() to cover I/O + * device addresses. */ pml4e: .space SIZEOF_PT .size pml4e, . - pml4e + .globl io_pages + .equ io_pages, pml4e + /* Page directory pointer table entries (PDPTEs) * * This comprises: @@ -1357,6 +1365,11 @@ pml4e: * These point to the appropriate page directories (in pde_low) * used to identity-map the whole of the 32-bit address space. * + * - PDPTE[0x004] covering [0x0000000100000000-0x000000013fffffff] + * + * This points back to the PML4, allowing the PML4 to be + * (ab)used to hold 2MB pages used for I/O device addresses. + * * - PDPTE[0x1ff] covering [0xffffffffc0000000-0xffffffffffffffff] * * This points back to the PDPT itself, allowing the PDPT to be @@ -1421,6 +1434,10 @@ init_pages: /* Initialise PDPTE for negative 1GB */ movl %eax, ( VIRTUAL(pdpte) + SIZEOF_PT - SIZEOF_PTE ) + /* Initialise PDPTE for I/O space */ + leal ( VIRTUAL(pml4e) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + movl %eax, ( VIRTUAL(pdpte) + ( PDE_LOW_PTS * SIZEOF_PTE ) ) + /* Initialise PDPTEs for low 4GB */ movl $PDE_LOW_PTS, %ecx leal ( VIRTUAL(pde_low) + ( PDE_LOW_PTS * SIZEOF_PT ) + \ diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c index a81270a1c..8776f2854 100644 --- a/src/arch/x86/transitions/librm_mgmt.c +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -8,6 +8,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include #include #include #include @@ -176,6 +178,123 @@ void __attribute__ (( regparm ( 1 ) )) interrupt ( int intr ) { profile_exclude ( profiler ); } +/** + * Map pages for I/O + * + * @v bus_addr Bus address + * @v len Length of region + * @ret io_addr I/O address + */ +static void * ioremap_pages ( unsigned long bus_addr, size_t len ) { + unsigned long start; + unsigned int count; + unsigned int stride; + unsigned int first; + unsigned int i; + size_t offset; + void *io_addr; + + DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len ); + + /* Sanity check */ + assert ( len != 0 ); + + /* Round down start address to a page boundary */ + start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) ); + offset = ( bus_addr - start ); + assert ( offset < IO_PAGE_SIZE ); + + /* Calculate number of pages required */ + count = ( ( offset + len + IO_PAGE_SIZE - 1 ) / IO_PAGE_SIZE ); + assert ( count != 0 ); + assert ( count < ( sizeof ( io_pages.page ) / + sizeof ( io_pages.page[0] ) ) ); + + /* Round up number of pages to a power of two */ + stride = ( 1 << ( fls ( count ) - 1 ) ); + assert ( count <= stride ); + + /* Allocate pages */ + for ( first = 0 ; first < ( sizeof ( io_pages.page ) / + sizeof ( io_pages.page[0] ) ) ; + first += stride ) { + + /* Calculate I/O address */ + io_addr = ( IO_BASE + ( first * IO_PAGE_SIZE ) + offset ); + + /* Check that page table entries are available */ + for ( i = first ; i < ( first + count ) ; i++ ) { + if ( io_pages.page[i] & PAGE_P ) { + io_addr = NULL; + break; + } + } + if ( ! io_addr ) + continue; + + /* Create page table entries */ + for ( i = first ; i < ( first + count ) ; i++ ) { + io_pages.page[i] = ( start | PAGE_P | PAGE_RW | + PAGE_US | PAGE_PWT | PAGE_PCD | + PAGE_PS ); + start += IO_PAGE_SIZE; + } + + /* Mark last page as being the last in this allocation */ + io_pages.page[ i - 1 ] |= PAGE_LAST; + + /* Return I/O address */ + DBGC ( &io_pages, "IO mapped %08lx+%zx to %p using PTEs " + "[%d-%d]\n", bus_addr, len, io_addr, first, + ( first + count - 1 ) ); + return io_addr; + } + + DBGC ( &io_pages, "IO could not map %08lx+%zx\n", bus_addr, len ); + return NULL; +} + +/** + * Unmap pages for I/O + * + * @v io_addr I/O address + */ +static void iounmap_pages ( volatile const void *io_addr ) { + volatile const void *invalidate = io_addr; + unsigned int first; + unsigned int i; + int is_last; + + DBGC ( &io_pages, "IO unmapping %p\n", io_addr ); + + /* Calculate first page table entry */ + first = ( ( io_addr - IO_BASE ) / IO_PAGE_SIZE ); + + /* Clear page table entries */ + for ( i = first ; ; i++ ) { + + /* Sanity check */ + assert ( io_pages.page[i] & PAGE_P ); + + /* Check if this is the last page in this allocation */ + is_last = ( io_pages.page[i] & PAGE_LAST ); + + /* Clear page table entry */ + io_pages.page[i] = 0; + + /* Invalidate TLB for this page */ + __asm__ __volatile__ ( "invlpg (%0)" : : "r" ( invalidate ) ); + invalidate += IO_PAGE_SIZE; + + /* Terminate if this was the last page */ + if ( is_last ) + break; + } + + DBGC ( &io_pages, "IO unmapped %p using PTEs [%d-%d]\n", + io_addr, first, i ); +} + PROVIDE_UACCESS_INLINE ( librm, phys_to_user ); PROVIDE_UACCESS_INLINE ( librm, user_to_phys ); PROVIDE_UACCESS_INLINE ( librm, virt_to_user ); @@ -186,3 +305,6 @@ PROVIDE_UACCESS_INLINE ( librm, memmove_user ); PROVIDE_UACCESS_INLINE ( librm, memset_user ); PROVIDE_UACCESS_INLINE ( librm, strlen_user ); PROVIDE_UACCESS_INLINE ( librm, memchr_user ); +PROVIDE_IOMAP_INLINE ( pages, io_to_bus ); +PROVIDE_IOMAP ( pages, ioremap, ioremap_pages ); +PROVIDE_IOMAP ( pages, iounmap, iounmap_pages ); diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 56ed00738..e1915054c 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -11,7 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_LIBRM #define IOAPI_X86 -#define IOMAP_VIRT #define PCIAPI_PCBIOS #define TIMER_PCBIOS #define CONSOLE_PCBIOS @@ -23,6 +22,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define TIME_RTC #define REBOOT_PCBIOS +#ifdef __x86_64__ +#define IOMAP_PAGES +#else +#define IOMAP_VIRT +#endif + #define IMAGE_ELF /* ELF image support */ #define IMAGE_MULTIBOOT /* MultiBoot image support */ #define IMAGE_PXE /* PXE image support */ From fcf3b0354428df2e8237b7d0fca2457ff7d87fac Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Mar 2016 21:03:25 +0000 Subject: [PATCH 095/591] [netdevice] Refuse to create duplicate network device names Signed-off-by: Michael Brown --- src/net/netdevice.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 7c40a2ac8..b834c3cd9 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -675,6 +675,14 @@ int register_netdev ( struct net_device *netdev ) { goto err_duplicate; } + /* Reject named network devices that already exist */ + if ( netdev->name[0] && ( duplicate = find_netdev ( netdev->name ) ) ) { + DBGC ( netdev, "NETDEV rejecting duplicate name %s\n", + duplicate->name ); + rc = -EEXIST; + goto err_duplicate; + } + /* Record device index and create device name */ if ( netdev->name[0] == '\0' ) { snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", From 60e205a551f07882ed18a33fa192ad7cefe548a0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 1 Mar 2016 15:26:32 +0000 Subject: [PATCH 096/591] [infiniband] Remove concept of whole-device owner data Remove the implicit assumption that the IPoIB protocol owns the whole Infiniband device. Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 56 ++++++++++++++++++++++++++++------- src/include/ipxe/infiniband.h | 24 --------------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 6552d764e..e54f8df40 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -79,6 +79,8 @@ struct ipoib_device { struct net_device *netdev; /** Underlying Infiniband device */ struct ib_device *ibdev; + /** List of IPoIB devices */ + struct list_head list; /** Completion queue */ struct ib_completion_queue *cq; /** Queue pair */ @@ -116,6 +118,9 @@ struct errortab ipoib_errors[] __errortab = { __einfo_errortab ( EINFO_EINPROGRESS_JOINING ), }; +/** List of all IPoIB devices */ +static LIST_HEAD ( ipoib_devices ); + static struct net_device_operations ipoib_operations; /**************************************************************************** @@ -783,11 +788,11 @@ static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) { /** * Handle link status change * - * @v ibdev Infiniband device + * @v ipoib IPoIB device */ -static void ipoib_link_state_changed ( struct ib_device *ibdev ) { - struct net_device *netdev = ib_get_ownerdata ( ibdev ); - struct ipoib_device *ipoib = netdev->priv; +static void ipoib_link_state_changed ( struct ipoib_device *ipoib ) { + struct ib_device *ibdev = ipoib->ibdev; + struct net_device *netdev = ipoib->netdev; int rc; /* Leave existing broadcast group */ @@ -862,7 +867,7 @@ static int ipoib_open ( struct net_device *netdev ) { ib_refill_recv ( ibdev, ipoib->qp ); /* Fake a link status change to join the broadcast group */ - ipoib_link_state_changed ( ibdev ); + ipoib_link_state_changed ( ipoib ); return 0; @@ -928,7 +933,6 @@ static int ipoib_probe ( struct ib_device *ibdev ) { return -ENOMEM; netdev_init ( netdev, &ipoib_operations ); ipoib = netdev->priv; - ib_set_ownerdata ( ibdev, netdev ); netdev->dev = ibdev->dev; memset ( ipoib, 0, sizeof ( *ipoib ) ); ipoib->netdev = netdev; @@ -947,35 +951,65 @@ static int ipoib_probe ( struct ib_device *ibdev ) { memcpy ( &ipoib->broadcast, &ipoib_broadcast, sizeof ( ipoib->broadcast ) ); + /* Add to list of IPoIB devices */ + list_add_tail ( &ipoib->list, &ipoib_devices ); + /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; return 0; + unregister_netdev ( netdev ); err_register_netdev: + list_del ( &ipoib->list ); netdev_nullify ( netdev ); netdev_put ( netdev ); return rc; } +/** + * Handle device or link status change + * + * @v ibdev Infiniband device + */ +static void ipoib_notify ( struct ib_device *ibdev ) { + struct ipoib_device *ipoib; + + /* Handle link status change for any attached IPoIB devices */ + list_for_each_entry ( ipoib, &ipoib_devices, list ) { + if ( ipoib->ibdev != ibdev ) + continue; + ipoib_link_state_changed ( ipoib ); + } +} + /** * Remove IPoIB device * * @v ibdev Infiniband device */ static void ipoib_remove ( struct ib_device *ibdev ) { - struct net_device *netdev = ib_get_ownerdata ( ibdev ); + struct ipoib_device *ipoib; + struct ipoib_device *tmp; + struct net_device *netdev; - unregister_netdev ( netdev ); - netdev_nullify ( netdev ); - netdev_put ( netdev ); + /* Remove any attached IPoIB devices */ + list_for_each_entry_safe ( ipoib, tmp, &ipoib_devices, list ) { + if ( ipoib->ibdev != ibdev ) + continue; + netdev = ipoib->netdev; + unregister_netdev ( netdev ); + list_del ( &ipoib->list ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + } } /** IPoIB driver */ struct ib_driver ipoib_driver __ib_driver = { .name = "IPoIB", .probe = ipoib_probe, - .notify = ipoib_link_state_changed, + .notify = ipoib_notify, .remove = ipoib_remove, }; diff --git a/src/include/ipxe/infiniband.h b/src/include/ipxe/infiniband.h index 87cfe5082..6a99865f2 100644 --- a/src/include/ipxe/infiniband.h +++ b/src/include/ipxe/infiniband.h @@ -450,8 +450,6 @@ struct ib_device { /** Driver private data */ void *drv_priv; - /** Owner private data */ - void *owner_priv; }; /** An Infiniband upper-layer driver */ @@ -695,26 +693,4 @@ ib_get_drvdata ( struct ib_device *ibdev ) { return ibdev->drv_priv; } -/** - * Set Infiniband device owner-private data - * - * @v ibdev Infiniband device - * @v priv Private data - */ -static inline __always_inline void -ib_set_ownerdata ( struct ib_device *ibdev, void *priv ) { - ibdev->owner_priv = priv; -} - -/** - * Get Infiniband device owner-private data - * - * @v ibdev Infiniband device - * @ret priv Private data - */ -static inline __always_inline void * -ib_get_ownerdata ( struct ib_device *ibdev ) { - return ibdev->owner_priv; -} - #endif /* _IPXE_INFINIBAND_H */ From 7544763626d67b352d4b1440336080d63c0be0b7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 2 Mar 2016 09:29:33 +0000 Subject: [PATCH 097/591] [infiniband] Avoid multiple calls to ib_cmrc_shutdown() When a CMRC connection is closed, the deferred shutdown process calls ib_destroy_qp(). This will cause the receive work queue entries to complete in error (since they are being cancelled), which will in turn reschedule the deferred shutdown process. This eventually leads to ib_destroy_conn() being called on a connection that has already been freed. Fix by explicitly cancelling any pending shutdown process after the shutdown process has completed. Ironically, this almost exactly reverts commit 019d4c1 ("[infiniband] Use a one-shot process for CMRC shutdown"); prior to the introduction of one-shot processes the only way to achieve a one-shot process was for the process to cancel itself. Signed-off-by: Michael Brown --- src/net/infiniband/ib_cmrc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 1cc0fcfef..513c6f433 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -117,6 +117,9 @@ static void ib_cmrc_shutdown ( struct ib_cmrc_connection *cmrc ) { ib_destroy_cq ( cmrc->ibdev, cmrc->cq ); ib_close ( cmrc->ibdev ); + /* Cancel any pending shutdown */ + process_del ( &cmrc->shutdown ); + /* Drop the remaining reference */ ref_put ( &cmrc->refcnt ); } From ff13eeb74711f1b775baf61d5836a322dffdf82c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 1 Mar 2016 09:41:11 +0000 Subject: [PATCH 098/591] [infiniband] Add support for performing service record lookups Signed-off-by: Michael Brown --- src/include/ipxe/ib_mad.h | 20 +++++++++- src/include/ipxe/ib_service.h | 20 ++++++++++ src/net/infiniband/ib_service.c | 67 +++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/include/ipxe/ib_service.h create mode 100644 src/net/infiniband/ib_service.c diff --git a/src/include/ipxe/ib_mad.h b/src/include/ipxe/ib_mad.h index 454eda1f5..96e460edd 100644 --- a/src/include/ipxe/ib_mad.h +++ b/src/include/ipxe/ib_mad.h @@ -219,8 +219,25 @@ struct ib_sa_hdr { uint32_t comp_mask[2]; } __attribute__ (( packed )); -#define IB_SA_ATTR_MC_MEMBER_REC 0x38 +#define IB_SA_ATTR_SERVICE_REC 0x31 #define IB_SA_ATTR_PATH_REC 0x35 +#define IB_SA_ATTR_MC_MEMBER_REC 0x38 + +struct ib_service_record { + uint64_t id; + union ib_gid gid; + uint16_t pkey; + uint16_t reserved; + uint32_t lease; + uint8_t key[16]; + char name[64]; + uint8_t data8[16]; + uint16_t data16[8]; + uint32_t data32[4]; + uint64_t data64[2]; +} __attribute__ (( packed )); + +#define IB_SA_SERVICE_REC_NAME (1<<6) struct ib_path_record { uint32_t reserved0[2]; @@ -278,6 +295,7 @@ struct ib_mc_member_record { #define IB_SA_MCMEMBER_REC_PROXY_JOIN (1<<17) union ib_sa_data { + struct ib_service_record service_record; struct ib_path_record path_record; struct ib_mc_member_record mc_member_record; } __attribute__ (( packed )); diff --git a/src/include/ipxe/ib_service.h b/src/include/ipxe/ib_service.h new file mode 100644 index 000000000..88afe4e65 --- /dev/null +++ b/src/include/ipxe/ib_service.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_IB_SERVICE_H +#define _IPXE_IB_SERVICE_H + +/** @file + * + * Infiniband service records + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +extern struct ib_mad_transaction * +ib_create_service_madx ( struct ib_device *ibdev, + struct ib_mad_interface *mi, const char *name, + struct ib_mad_transaction_operations *op ); + +#endif /* _IPXE_IB_SERVICE_H */ diff --git a/src/net/infiniband/ib_service.c b/src/net/infiniband/ib_service.c new file mode 100644 index 000000000..f035382ee --- /dev/null +++ b/src/net/infiniband/ib_service.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband service records + * + */ + +/** + * Create service record management transaction + * + * @v ibdev Infiniband device + * @v mi Management interface + * @v name Service name + * @v op Management transaction operations + * @ret madx Management transaction, or NULL on error + */ +struct ib_mad_transaction * +ib_create_service_madx ( struct ib_device *ibdev, + struct ib_mad_interface *mi, const char *name, + struct ib_mad_transaction_operations *op ) { + union ib_mad mad; + struct ib_mad_sa *sa = &mad.sa; + struct ib_service_record *svc = &sa->sa_data.service_record; + + /* Construct service record request */ + memset ( sa, 0, sizeof ( *sa ) ); + sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; + sa->mad_hdr.class_version = IB_SA_CLASS_VERSION; + sa->mad_hdr.method = IB_MGMT_METHOD_GET; + sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_SERVICE_REC ); + sa->sa_hdr.comp_mask[1] = htonl ( IB_SA_SERVICE_REC_NAME ); + snprintf ( svc->name, sizeof ( svc->name ), "%s", name ); + + /* Create management transaction */ + return ib_create_madx ( ibdev, mi, &mad, NULL, op ); +} From d7794dcac717ec90f5c2c55a90a82cdbedcc8fe6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 4 Mar 2016 09:17:08 +0000 Subject: [PATCH 099/591] [infiniband] Assign names to Infiniband devices for debug messages Signed-off-by: Michael Brown --- src/include/ipxe/infiniband.h | 7 ++ src/net/infiniband.c | 124 ++++++++++++++++++-------------- src/net/infiniband/ib_cm.c | 4 +- src/net/infiniband/ib_mcast.c | 32 ++++----- src/net/infiniband/ib_packet.c | 45 ++++++------ src/net/infiniband/ib_pathrec.c | 24 +++---- src/net/infiniband/ib_sma.c | 2 +- src/net/infiniband/ib_smc.c | 20 +++--- 8 files changed, 140 insertions(+), 118 deletions(-) diff --git a/src/include/ipxe/infiniband.h b/src/include/ipxe/infiniband.h index 6a99865f2..065e42180 100644 --- a/src/include/ipxe/infiniband.h +++ b/src/include/ipxe/infiniband.h @@ -388,6 +388,9 @@ struct ib_device_operations { union ib_mad *mad ); }; +/** Maximum length of an Infiniband device name */ +#define IBDEV_NAME_LEN 8 + /** An Infiniband device */ struct ib_device { /** Reference counter */ @@ -396,6 +399,10 @@ struct ib_device { struct list_head list; /** List of open Infiniband devices */ struct list_head open_list; + /** Index of this Infiniband device */ + unsigned int index; + /** Name of this Infiniband device */ + char name[IBDEV_NAME_LEN]; /** Underlying device */ struct device *dev; /** List of completion queues */ diff --git a/src/net/infiniband.c b/src/net/infiniband.c index c059fe8c4..106371c2f 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -54,6 +54,9 @@ struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices ); /** List of open Infiniband devices, in reverse order of opening */ static struct list_head open_ib_devices = LIST_HEAD_INIT ( open_ib_devices ); +/** Infiniband device index */ +static unsigned int ibdev_index = 0; + /** Post send work queue entry profiler */ static struct profiler ib_post_send_profiler __profiler = { .name = "ib.post_send" }; @@ -97,7 +100,7 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, struct ib_completion_queue *cq; int rc; - DBGC ( ibdev, "IBDEV %p creating completion queue\n", ibdev ); + DBGC ( ibdev, "IBDEV %s creating completion queue\n", ibdev->name ); /* Allocate and initialise data structure */ cq = zalloc ( sizeof ( *cq ) ); @@ -111,13 +114,13 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, /* Perform device-specific initialisation and get CQN */ if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not initialise completion " - "queue: %s\n", ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not initialise completion " + "queue: %s\n", ibdev->name, strerror ( rc ) ); goto err_dev_create_cq; } - DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) " - "with CQN %#lx\n", ibdev, num_cqes, cq, + DBGC ( ibdev, "IBDEV %s created %d-entry completion queue %p (%p) " + "with CQN %#lx\n", ibdev->name, num_cqes, cq, ib_cq_get_drvdata ( cq ), cq->cqn ); return cq; @@ -137,8 +140,8 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, */ void ib_destroy_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ) { - DBGC ( ibdev, "IBDEV %p destroying completion queue %#lx\n", - ibdev, cq->cqn ); + DBGC ( ibdev, "IBDEV %s destroying completion queue %#lx\n", + ibdev->name, cq->cqn ); assert ( list_empty ( &cq->work_queues ) ); ibdev->op->destroy_cq ( ibdev, cq ); list_del ( &cq->list ); @@ -198,7 +201,7 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, size_t total_size; int rc; - DBGC ( ibdev, "IBDEV %p creating queue pair\n", ibdev ); + DBGC ( ibdev, "IBDEV %s creating queue pair\n", ibdev->name ); /* Allocate and initialise data structure */ total_size = ( sizeof ( *qp ) + @@ -229,17 +232,17 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, /* Perform device-specific initialisation and get QPN */ if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not initialise queue pair: " - "%s\n", ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not initialise queue pair: " + "%s\n", ibdev->name, strerror ( rc ) ); goto err_dev_create_qp; } - DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n", - ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn ); - DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n", - ibdev, qp->qpn, num_send_wqes, qp->send.iobufs, + DBGC ( ibdev, "IBDEV %s created queue pair %p (%p) with QPN %#lx\n", + ibdev->name, qp, ib_qp_get_drvdata ( qp ), qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx has %d send entries at [%p,%p)\n", + ibdev->name, qp->qpn, num_send_wqes, qp->send.iobufs, qp->recv.iobufs ); - DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n", - ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs, + DBGC ( ibdev, "IBDEV %s QPN %#lx has %d receive entries at [%p,%p)\n", + ibdev->name, qp->qpn, num_recv_wqes, qp->recv.iobufs, ( ( ( void * ) qp ) + total_size ) ); /* Calculate externally-visible QPN */ @@ -255,8 +258,8 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, break; } if ( qp->ext_qpn != qp->qpn ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx has external QPN %#lx\n", - ibdev, qp->qpn, qp->ext_qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx has external QPN %#lx\n", + ibdev->name, qp->qpn, qp->ext_qpn ); } return qp; @@ -281,11 +284,11 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { int rc; - DBGC ( ibdev, "IBDEV %p modifying QPN %#lx\n", ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s modifying QPN %#lx\n", ibdev->name, qp->qpn ); if ( ( rc = ibdev->op->modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not modify QPN %#lx: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not modify QPN %#lx: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); return rc; } @@ -302,8 +305,8 @@ void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { struct io_buffer *iobuf; unsigned int i; - DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n", - ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s destroying QPN %#lx\n", + ibdev->name, qp->qpn ); assert ( list_empty ( &qp->mgids ) ); @@ -411,8 +414,8 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Check queue fill level */ if ( qp->send.fill >= qp->send.num_wqes ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx send queue full\n", - ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx send queue full\n", + ibdev->name, qp->qpn ); return -ENOBUFS; } @@ -432,8 +435,8 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Post to hardware */ if ( ( rc = ibdev->op->post_send ( ibdev, qp, dest, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx could not post send WQE: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not post send WQE: " + "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); return rc; } @@ -463,22 +466,22 @@ int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Check packet length */ if ( iob_tailroom ( iobuf ) < IB_MAX_PAYLOAD_SIZE ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx wrong RX buffer size (%zd)\n", - ibdev, qp->qpn, iob_tailroom ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx wrong RX buffer size (%zd)\n", + ibdev->name, qp->qpn, iob_tailroom ( iobuf ) ); return -EINVAL; } /* Check queue fill level */ if ( qp->recv.fill >= qp->recv.num_wqes ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx receive queue full\n", - ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx receive queue full\n", + ibdev->name, qp->qpn ); return -ENOBUFS; } /* Post to hardware */ if ( ( rc = ibdev->op->post_recv ( ibdev, qp, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx could not post receive WQE: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not post receive WQE: " + "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); return rc; } @@ -556,8 +559,8 @@ void ib_refill_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { /* Post I/O buffer */ if ( ( rc = ib_post_recv ( ibdev, qp, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not refill: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not refill: %s\n", + ibdev->name, strerror ( rc ) ); free_iob ( iobuf ); /* Give up */ return; @@ -623,8 +626,8 @@ static void ib_notify ( struct ib_device *ibdev ) { */ void ib_link_state_changed ( struct ib_device *ibdev ) { - DBGC ( ibdev, "IBDEV %p link state is %s\n", - ibdev, ib_link_state_text ( ibdev ) ); + DBGC ( ibdev, "IBDEV %s link state is %s\n", + ibdev->name, ib_link_state_text ( ibdev ) ); /* Notify drivers of link state change */ ib_notify ( ibdev ); @@ -647,30 +650,30 @@ int ib_open ( struct ib_device *ibdev ) { /* Open device */ if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not open: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not open: %s\n", + ibdev->name, strerror ( rc ) ); goto err_open; } /* Create subnet management interface */ ibdev->smi = ib_create_mi ( ibdev, IB_QPT_SMI ); if ( ! ibdev->smi ) { - DBGC ( ibdev, "IBDEV %p could not create SMI\n", ibdev ); + DBGC ( ibdev, "IBDEV %s could not create SMI\n", ibdev->name ); rc = -ENOMEM; goto err_create_smi; } /* Create subnet management agent */ if ( ( rc = ib_create_sma ( ibdev, ibdev->smi ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not create SMA: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not create SMA: %s\n", + ibdev->name, strerror ( rc ) ); goto err_create_sma; } /* Create general services interface */ ibdev->gsi = ib_create_mi ( ibdev, IB_QPT_GSI ); if ( ! ibdev->gsi ) { - DBGC ( ibdev, "IBDEV %p could not create GSI\n", ibdev ); + DBGC ( ibdev, "IBDEV %s could not create GSI\n", ibdev->name ); rc = -ENOMEM; goto err_create_gsi; } @@ -833,14 +836,14 @@ int ib_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) { /* Adapters with embedded SMAs do not need to support this method */ if ( ! ibdev->op->set_port_info ) { - DBGC ( ibdev, "IBDEV %p does not support setting port " - "information\n", ibdev ); + DBGC ( ibdev, "IBDEV %s does not support setting port " + "information\n", ibdev->name ); return -ENOTSUP; } if ( ( rc = ibdev->op->set_port_info ( ibdev, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not set port information: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not set port information: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } @@ -858,14 +861,14 @@ int ib_set_pkey_table ( struct ib_device *ibdev, union ib_mad *mad ) { /* Adapters with embedded SMAs do not need to support this method */ if ( ! ibdev->op->set_pkey_table ) { - DBGC ( ibdev, "IBDEV %p does not support setting partition " - "key table\n", ibdev ); + DBGC ( ibdev, "IBDEV %s does not support setting partition " + "key table\n", ibdev->name ); return -ENOTSUP; } if ( ( rc = ibdev->op->set_pkey_table ( ibdev, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not set partition key table: " - "%s\n", ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not set partition key table: " + "%s\n", ibdev->name, strerror ( rc ) ); return rc; } @@ -954,17 +957,24 @@ int register_ibdev ( struct ib_device *ibdev ) { struct ib_driver *driver; int rc; + /* Record device index and create device name */ + if ( ibdev->name[0] == '\0' ) { + snprintf ( ibdev->name, sizeof ( ibdev->name ), "inf%d", + ibdev_index ); + } + ibdev->index = ++ibdev_index; + /* Add to device list */ ibdev_get ( ibdev ); list_add_tail ( &ibdev->list, &ib_devices ); - DBGC ( ibdev, "IBDEV %p registered (phys %s)\n", ibdev, + DBGC ( ibdev, "IBDEV %s registered (phys %s)\n", ibdev->name, ibdev->dev->name ); /* Probe device */ for_each_table_entry ( driver, IB_DRIVERS ) { if ( ( rc = driver->probe ( ibdev ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not add %s device: %s\n", - ibdev, driver->name, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not add %s device: %s\n", + ibdev->name, driver->name, strerror ( rc ) ); goto err_probe; } } @@ -994,7 +1004,11 @@ void unregister_ibdev ( struct ib_device *ibdev ) { /* Remove from device list */ list_del ( &ibdev->list ); ibdev_put ( ibdev ); - DBGC ( ibdev, "IBDEV %p unregistered\n", ibdev ); + DBGC ( ibdev, "IBDEV %s unregistered\n", ibdev->name ); + + /* Reset device index if no devices remain */ + if ( list_empty ( &ib_devices ) ) + ibdev_index = 0; } /** diff --git a/src/net/infiniband/ib_cm.c b/src/net/infiniband/ib_cm.c index 85982f09d..265ec4e4c 100644 --- a/src/net/infiniband/ib_cm.c +++ b/src/net/infiniband/ib_cm.c @@ -466,8 +466,8 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Add to list of connections */ list_add ( &conn->list, &ib_cm_conns ); - DBGC ( conn, "CM %p created for IBDEV %p QPN %lx\n", - conn, ibdev, qp->qpn ); + DBGC ( conn, "CM %p created for IBDEV %s QPN %lx\n", + conn, ibdev->name, qp->qpn ); DBGC ( conn, "CM %p connecting to " IB_GID_FMT " " IB_GUID_FMT "\n", conn, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); diff --git a/src/net/infiniband/ib_mcast.c b/src/net/infiniband/ib_mcast.c index fc4ff7f0a..1fa2fea57 100644 --- a/src/net/infiniband/ib_mcast.c +++ b/src/net/infiniband/ib_mcast.c @@ -94,23 +94,23 @@ static void ib_mcast_complete ( struct ib_device *ibdev, if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -ENOTCONN; if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx join failed: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %lx join failed: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); goto out; } /* Extract values from MAD */ joined = ( mad->hdr.method == IB_MGMT_METHOD_GET_RESP ); qkey = ntohl ( mc_member_record->qkey ); - DBGC ( ibdev, "IBDEV %p QPN %lx %s " IB_GID_FMT " qkey %lx\n", - ibdev, qp->qpn, ( joined ? "joined" : "left" ), + DBGC ( ibdev, "IBDEV %s QPN %lx %s " IB_GID_FMT " qkey %lx\n", + ibdev->name, qp->qpn, ( joined ? "joined" : "left" ), IB_GID_ARGS ( gid ), qkey ); /* Set queue key */ qp->qkey = qkey; if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not modify qkey: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %lx could not modify qkey: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); goto out; } @@ -147,8 +147,8 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_mad mad; int rc; - DBGC ( ibdev, "IBDEV %p QPN %lx joining " IB_GID_FMT "\n", - ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + DBGC ( ibdev, "IBDEV %s QPN %lx joining " IB_GID_FMT "\n", + ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); /* Sanity check */ assert ( qp != NULL ); @@ -160,8 +160,8 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Attach queue pair to multicast GID */ if ( ( rc = ib_mcast_attach ( ibdev, qp, gid ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not attach: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %lx could not attach: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); goto err_mcast_attach; } @@ -170,8 +170,8 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, &ib_mcast_op ); if ( ! membership->madx ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not create join " - "transaction\n", ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %lx could not create join " + "transaction\n", ibdev->name, qp->qpn ); rc = -ENOMEM; goto err_create_madx; } @@ -199,8 +199,8 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_mad mad; int rc; - DBGC ( ibdev, "IBDEV %p QPN %lx leaving " IB_GID_FMT "\n", - ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + DBGC ( ibdev, "IBDEV %s QPN %lx leaving " IB_GID_FMT "\n", + ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); /* Sanity check */ assert ( qp != NULL ); @@ -217,7 +217,7 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Send a single group leave MAD */ ib_mcast_mad ( ibdev, &membership->gid, 0, &mad ); if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not send leave request: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %lx could not send leave request: " + "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); } } diff --git a/src/net/infiniband/ib_packet.c b/src/net/infiniband/ib_packet.c index d3a22d309..8d4df6d40 100644 --- a/src/net/infiniband/ib_packet.c +++ b/src/net/infiniband/ib_packet.c @@ -63,8 +63,8 @@ int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf, unsigned int vl; unsigned int lnh; - DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n", - ibdev, ibdev->lid, qp->ext_qpn, dest->lid, dest->qpn, + DBGC2 ( ibdev, "IBDEV %s TX %04x:%08lx => %04x:%08lx (key %08lx)\n", + ibdev->name, ibdev->lid, qp->ext_qpn, dest->lid, dest->qpn, dest->qkey ); /* Calculate packet length */ @@ -152,8 +152,8 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, /* Extract LRH */ if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n", - ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for LRH\n", + ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } lrh = iobuf->data; @@ -166,16 +166,16 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, /* Reject unsupported packets */ if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) { - DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n", - ibdev, lnh ); + DBGC ( ibdev, "IBDEV %s RX unsupported LNH %x\n", + ibdev->name, lnh ); return -ENOTSUP; } /* Extract GRH, if present */ if ( lnh == IB_LNH_GRH ) { if ( iob_len ( iobuf ) < sizeof ( *grh ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) " - "for GRH\n", ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) " + "for GRH\n", ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } grh = iobuf->data; @@ -190,23 +190,23 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, /* Extract BTH */ if ( iob_len ( iobuf ) < sizeof ( *bth ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n", - ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for BTH\n", + ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } bth = iobuf->data; iob_pull ( iobuf, sizeof ( *bth ) ); if ( bth->opcode != BTH_OPCODE_UD_SEND ) { - DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n", - ibdev, bth->opcode ); + DBGC ( ibdev, "IBDEV %s unsupported BTH opcode %x\n", + ibdev->name, bth->opcode ); return -ENOTSUP; } dest->qpn = ntohl ( bth->dest_qp ); /* Extract DETH */ if ( iob_len ( iobuf ) < sizeof ( *deth ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n", - ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for DETH\n", + ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } deth = iobuf->data; @@ -226,24 +226,25 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, if ( qp ) { if ( IB_LID_MULTICAST ( dest->lid ) && grh ) { if ( ! ( *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ))){ - DBGC ( ibdev, "IBDEV %p RX for unknown MGID " - IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( &grh->dgid ) ); + DBGC ( ibdev, "IBDEV %s RX for unknown MGID " + IB_GID_FMT "\n", ibdev->name, + IB_GID_ARGS ( &grh->dgid ) ); return -ENODEV; } } else { if ( ! ( *qp = ib_find_qp_qpn ( ibdev, dest->qpn ) ) ) { - DBGC ( ibdev, "IBDEV %p RX for nonexistent " - "QPN %lx\n", ibdev, dest->qpn ); + DBGC ( ibdev, "IBDEV %s RX for nonexistent " + "QPN %lx\n", ibdev->name, dest->qpn ); return -ENODEV; } } assert ( *qp ); } - DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n", - ibdev, dest->lid, ( IB_LID_MULTICAST ( dest->lid ) ? - ( qp ? (*qp)->ext_qpn : -1UL ) : dest->qpn ), + DBGC2 ( ibdev, "IBDEV %s RX %04x:%08lx <= %04x:%08lx (key %08x)\n", + ibdev->name, dest->lid, + ( IB_LID_MULTICAST ( dest->lid ) ? + ( qp ? (*qp)->ext_qpn : -1UL ) : dest->qpn ), source->lid, source->qpn, ntohl ( deth->qkey ) ); DBGCP_HDA ( ibdev, 0, ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ), diff --git a/src/net/infiniband/ib_pathrec.c b/src/net/infiniband/ib_pathrec.c index f9cbab87f..287c25c74 100644 --- a/src/net/infiniband/ib_pathrec.c +++ b/src/net/infiniband/ib_pathrec.c @@ -61,9 +61,9 @@ static void ib_path_complete ( struct ib_device *ibdev, if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -ENETUNREACH; if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %p path lookup for " IB_GID_FMT + DBGC ( ibdev, "IBDEV %s path lookup for " IB_GID_FMT " failed: %s\n", - ibdev, IB_GID_ARGS ( dgid ), strerror ( rc ) ); + ibdev->name, IB_GID_ARGS ( dgid ), strerror ( rc ) ); goto out; } @@ -71,9 +71,9 @@ static void ib_path_complete ( struct ib_device *ibdev, path->av.lid = ntohs ( pathrec->dlid ); path->av.sl = ( pathrec->reserved__sl & 0x0f ); path->av.rate = ( pathrec->rate_selector__rate & 0x3f ); - DBGC ( ibdev, "IBDEV %p path to " IB_GID_FMT " is %04x sl %d rate " - "%d\n", ibdev, IB_GID_ARGS ( dgid ), path->av.lid, path->av.sl, - path->av.rate ); + DBGC ( ibdev, "IBDEV %s path to " IB_GID_FMT " is %04x sl %d rate " + "%d\n", ibdev->name, IB_GID_ARGS ( dgid ), path->av.lid, + path->av.sl, path->av.rate ); out: /* Destroy the completed transaction */ @@ -247,8 +247,8 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { /* Sanity check */ if ( ! av->gid_present ) { - DBGC ( ibdev, "IBDEV %p attempt to look up path without GID\n", - ibdev ); + DBGC ( ibdev, "IBDEV %s attempt to look up path without GID\n", + ibdev->name ); return -EINVAL; } @@ -259,11 +259,11 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { av->lid = cached->path->av.lid; av->rate = cached->path->av.rate; av->sl = cached->path->av.sl; - DBGC2 ( ibdev, "IBDEV %p cache hit for " IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( gid ) ); + DBGC2 ( ibdev, "IBDEV %s cache hit for " IB_GID_FMT "\n", + ibdev->name, IB_GID_ARGS ( gid ) ); return 0; } - DBGC ( ibdev, "IBDEV %p cache miss for " IB_GID_FMT "%s\n", ibdev, + DBGC ( ibdev, "IBDEV %s cache miss for " IB_GID_FMT "%s\n", ibdev->name, IB_GID_ARGS ( gid ), ( cached ? " (in progress)" : "" ) ); /* If lookup is already in progress, do nothing */ @@ -282,8 +282,8 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { /* Create new path */ cached->path = ib_create_path ( ibdev, av, &ib_cached_path_op ); if ( ! cached->path ) { - DBGC ( ibdev, "IBDEV %p could not create path\n", - ibdev ); + DBGC ( ibdev, "IBDEV %s could not create path\n", + ibdev->name ); return -ENOMEM; } ib_path_set_ownerdata ( cached->path, cached ); diff --git a/src/net/infiniband/ib_sma.c b/src/net/infiniband/ib_sma.c index a05d7c924..c4d0b2c5e 100644 --- a/src/net/infiniband/ib_sma.c +++ b/src/net/infiniband/ib_sma.c @@ -358,7 +358,7 @@ struct ib_mad_agent ib_sma_agent[] __ib_mad_agent = { int ib_create_sma ( struct ib_device *ibdev, struct ib_mad_interface *mi ) { /* Nothing to do */ - DBGC ( ibdev, "IBDEV %p SMA using SMI %p\n", ibdev, mi ); + DBGC ( ibdev, "IBDEV %s SMA using SMI %p\n", ibdev->name, mi ); return 0; } diff --git a/src/net/infiniband/ib_smc.c b/src/net/infiniband/ib_smc.c index c1741b26c..5de7deba2 100644 --- a/src/net/infiniband/ib_smc.c +++ b/src/net/infiniband/ib_smc.c @@ -86,8 +86,8 @@ static int ib_smc_get_node_info ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_NODE_INFO ), 0, local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get node info: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get node info: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -109,8 +109,8 @@ static int ib_smc_get_port_info ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_PORT_INFO ), htonl ( ibdev->port ), local_mad, mad )) !=0){ - DBGC ( ibdev, "IBDEV %p could not get port info: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get port info: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -132,8 +132,8 @@ static int ib_smc_get_guid_info ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_GUID_INFO ), 0, local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get GUID info: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -155,8 +155,8 @@ static int ib_smc_get_pkey_table ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_PKEY_TABLE ), 0, local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get pkey table: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -216,8 +216,8 @@ static int ib_smc_get ( struct ib_device *ibdev, ib_local_mad_t local_mad ) { return rc; ibdev->pkey = ntohs ( pkey_table->pkey[0] ); - DBGC ( ibdev, "IBDEV %p port GID is " IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( &ibdev->gid ) ); + DBGC ( ibdev, "IBDEV %s port GID is " IB_GID_FMT "\n", + ibdev->name, IB_GID_ARGS ( &ibdev->gid ) ); return 0; } From 7aef4d4c9412ad30f850e7fb1a33554ce8a97b00 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 09:06:37 +0000 Subject: [PATCH 100/591] [infiniband] Use "%#lx" as format specifier for queue pair numbers Signed-off-by: Michael Brown --- src/net/infiniband/ib_cm.c | 4 ++-- src/net/infiniband/ib_cmrc.c | 2 +- src/net/infiniband/ib_mcast.c | 16 ++++++++-------- src/net/infiniband/ib_packet.c | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/net/infiniband/ib_cm.c b/src/net/infiniband/ib_cm.c index 265ec4e4c..51e7c1ef1 100644 --- a/src/net/infiniband/ib_cm.c +++ b/src/net/infiniband/ib_cm.c @@ -280,7 +280,7 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, qp->send.psn = ( ntohl ( rep->starting_psn ) >> 8 ); private_data = &rep->private_data; private_data_len = sizeof ( rep->private_data ); - DBGC ( conn, "CM %p connected to QPN %lx PSN %x\n", + DBGC ( conn, "CM %p connected to QPN %#lx PSN %#x\n", conn, qp->av.qpn, qp->send.psn ); /* Modify queue pair */ @@ -466,7 +466,7 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Add to list of connections */ list_add ( &conn->list, &ib_cm_conns ); - DBGC ( conn, "CM %p created for IBDEV %s QPN %lx\n", + DBGC ( conn, "CM %p created for IBDEV %s QPN %#lx\n", conn, ibdev->name, qp->qpn ); DBGC ( conn, "CM %p connecting to " IB_GID_FMT " " IB_GUID_FMT "\n", conn, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 513c6f433..1e6bda5b8 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -428,7 +428,7 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, goto err_create_qp; } ib_qp_set_ownerdata ( cmrc->qp, cmrc ); - DBGC ( cmrc, "CMRC %p using QPN %lx\n", cmrc, cmrc->qp->qpn ); + DBGC ( cmrc, "CMRC %p using QPN %#lx\n", cmrc, cmrc->qp->qpn ); /* Attach to parent interface, transfer reference (implicitly) * to our shutdown process, and return. diff --git a/src/net/infiniband/ib_mcast.c b/src/net/infiniband/ib_mcast.c index 1fa2fea57..b7a6e8ce8 100644 --- a/src/net/infiniband/ib_mcast.c +++ b/src/net/infiniband/ib_mcast.c @@ -94,7 +94,7 @@ static void ib_mcast_complete ( struct ib_device *ibdev, if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -ENOTCONN; if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %s QPN %lx join failed: %s\n", + DBGC ( ibdev, "IBDEV %s QPN %#lx join failed: %s\n", ibdev->name, qp->qpn, strerror ( rc ) ); goto out; } @@ -102,14 +102,14 @@ static void ib_mcast_complete ( struct ib_device *ibdev, /* Extract values from MAD */ joined = ( mad->hdr.method == IB_MGMT_METHOD_GET_RESP ); qkey = ntohl ( mc_member_record->qkey ); - DBGC ( ibdev, "IBDEV %s QPN %lx %s " IB_GID_FMT " qkey %lx\n", + DBGC ( ibdev, "IBDEV %s QPN %#lx %s " IB_GID_FMT " qkey %lx\n", ibdev->name, qp->qpn, ( joined ? "joined" : "left" ), IB_GID_ARGS ( gid ), qkey ); /* Set queue key */ qp->qkey = qkey; if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %s QPN %lx could not modify qkey: %s\n", + DBGC ( ibdev, "IBDEV %s QPN %#lx could not modify qkey: %s\n", ibdev->name, qp->qpn, strerror ( rc ) ); goto out; } @@ -147,7 +147,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_mad mad; int rc; - DBGC ( ibdev, "IBDEV %s QPN %lx joining " IB_GID_FMT "\n", + DBGC ( ibdev, "IBDEV %s QPN %#lx joining " IB_GID_FMT "\n", ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); /* Sanity check */ @@ -160,7 +160,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Attach queue pair to multicast GID */ if ( ( rc = ib_mcast_attach ( ibdev, qp, gid ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %s QPN %lx could not attach: %s\n", + DBGC ( ibdev, "IBDEV %s QPN %#lx could not attach: %s\n", ibdev->name, qp->qpn, strerror ( rc ) ); goto err_mcast_attach; } @@ -170,7 +170,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, &ib_mcast_op ); if ( ! membership->madx ) { - DBGC ( ibdev, "IBDEV %s QPN %lx could not create join " + DBGC ( ibdev, "IBDEV %s QPN %#lx could not create join " "transaction\n", ibdev->name, qp->qpn ); rc = -ENOMEM; goto err_create_madx; @@ -199,7 +199,7 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_mad mad; int rc; - DBGC ( ibdev, "IBDEV %s QPN %lx leaving " IB_GID_FMT "\n", + DBGC ( ibdev, "IBDEV %s QPN %#lx leaving " IB_GID_FMT "\n", ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); /* Sanity check */ @@ -217,7 +217,7 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Send a single group leave MAD */ ib_mcast_mad ( ibdev, &membership->gid, 0, &mad ); if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %s QPN %lx could not send leave request: " + DBGC ( ibdev, "IBDEV %s QPN %#lx could not send leave request: " "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); } } diff --git a/src/net/infiniband/ib_packet.c b/src/net/infiniband/ib_packet.c index 8d4df6d40..8169925f1 100644 --- a/src/net/infiniband/ib_packet.c +++ b/src/net/infiniband/ib_packet.c @@ -234,7 +234,7 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, } else { if ( ! ( *qp = ib_find_qp_qpn ( ibdev, dest->qpn ) ) ) { DBGC ( ibdev, "IBDEV %s RX for nonexistent " - "QPN %lx\n", ibdev->name, dest->qpn ); + "QPN %#lx\n", ibdev->name, dest->qpn ); return -ENODEV; } } From 36c477935642dfd40d816348c384a7cd35d7b78b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 09:11:15 +0000 Subject: [PATCH 101/591] [infiniband] Use "%d" as format specifier for LIDs Signed-off-by: Michael Brown --- src/net/infiniband/ib_pathrec.c | 2 +- src/net/infiniband/ib_sma.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/net/infiniband/ib_pathrec.c b/src/net/infiniband/ib_pathrec.c index 287c25c74..113b90bda 100644 --- a/src/net/infiniband/ib_pathrec.c +++ b/src/net/infiniband/ib_pathrec.c @@ -71,7 +71,7 @@ static void ib_path_complete ( struct ib_device *ibdev, path->av.lid = ntohs ( pathrec->dlid ); path->av.sl = ( pathrec->reserved__sl & 0x0f ); path->av.rate = ( pathrec->rate_selector__rate & 0x3f ); - DBGC ( ibdev, "IBDEV %s path to " IB_GID_FMT " is %04x sl %d rate " + DBGC ( ibdev, "IBDEV %s path to " IB_GID_FMT " lid %d sl %d rate " "%d\n", ibdev->name, IB_GID_ARGS ( dgid ), path->av.lid, path->av.sl, path->av.rate ); diff --git a/src/net/infiniband/ib_sma.c b/src/net/infiniband/ib_sma.c index c4d0b2c5e..24ec9f4e0 100644 --- a/src/net/infiniband/ib_sma.c +++ b/src/net/infiniband/ib_sma.c @@ -176,9 +176,9 @@ static int ib_sma_set_port_info ( struct ib_device *ibdev, ( port_info->link_speed_active__link_speed_enabled & 0xf ) ) ) ibdev->link_speed_enabled = link_speed_enabled; ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf ); - DBGC ( mi, "SMA %p set LID %04x SMLID %04x link width %02x speed " - "%02x\n", mi, ibdev->lid, ibdev->sm_lid, - ibdev->link_width_enabled, ibdev->link_speed_enabled ); + DBGC ( mi, "SMA %p set LID %d SMLID %d link width %d speed %d\n", + mi, ibdev->lid, ibdev->sm_lid, ibdev->link_width_enabled, + ibdev->link_speed_enabled ); /* Update parameters on device */ if ( ( rc = ib_set_port_info ( ibdev, mad ) ) != 0 ) { From 8336186564dc7489de55fe16e0c050844e6665d6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 3 Mar 2016 18:09:03 +0000 Subject: [PATCH 102/591] [infiniband] Use connection's local ID as debug message identifier Signed-off-by: Michael Brown --- src/net/infiniband/ib_cm.c | 56 ++++++++++++++++++++---------------- src/net/infiniband/ib_cmrc.c | 2 ++ 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/net/infiniband/ib_cm.c b/src/net/infiniband/ib_cm.c index 51e7c1ef1..942939574 100644 --- a/src/net/infiniband/ib_cm.c +++ b/src/net/infiniband/ib_cm.c @@ -86,8 +86,9 @@ static int ib_cm_send_rtu ( struct ib_device *ibdev, mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE ); rtu->local_id = htonl ( local_id ); rtu->remote_id = htonl ( remote_id ); - if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){ - DBG ( "CM could not send RTU: %s\n", strerror ( rc ) ); + if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ) { + DBGC ( local_id, "CM %08x could not send RTU: %s\n", + local_id, strerror ( rc ) ); return rc; } @@ -125,7 +126,7 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, /* Ignore errors; the remote end will retry */ } } else { - DBG ( "CM unidentified connection %08x\n", local_id ); + DBGC ( local_id, "CM %08x unexpected REP\n", local_id ); } } @@ -155,8 +156,9 @@ static int ib_cm_send_drep ( struct ib_device *ibdev, mad.hdr.attr_id = htons ( IB_CM_ATTR_DISCONNECT_REPLY ); drep->local_id = htonl ( local_id ); drep->remote_id = htonl ( remote_id ); - if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){ - DBG ( "CM could not send DREP: %s\n", strerror ( rc ) ); + if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ) { + DBGC ( local_id, "CM %08x could not send DREP: %s\n", + local_id, strerror ( rc ) ); return rc; } @@ -191,7 +193,7 @@ static void ib_cm_recv_dreq ( struct ib_device *ibdev, &dreq->private_data, sizeof ( dreq->private_data ) ); } else { - DBG ( "CM unidentified connection %08x\n", local_id ); + DBGC ( local_id, "CM %08x unexpected DREQ\n", local_id ); } /* Send reply */ @@ -256,6 +258,7 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, struct ib_cm_common *common = &mad->cm.cm_data.common; struct ib_cm_connect_reply *rep = &mad->cm.cm_data.connect_reply; struct ib_cm_connect_reject *rej = &mad->cm.cm_data.connect_reject; + uint32_t local_id = conn->local_id; void *private_data = NULL; size_t private_data_len = 0; @@ -263,8 +266,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -EIO; if ( rc != 0 ) { - DBGC ( conn, "CM %p connection request failed: %s\n", - conn, strerror ( rc ) ); + DBGC ( local_id, "CM %08x connection request failed: %s\n", + local_id, strerror ( rc ) ); goto out; } @@ -280,13 +283,13 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, qp->send.psn = ( ntohl ( rep->starting_psn ) >> 8 ); private_data = &rep->private_data; private_data_len = sizeof ( rep->private_data ); - DBGC ( conn, "CM %p connected to QPN %#lx PSN %#x\n", - conn, qp->av.qpn, qp->send.psn ); + DBGC ( local_id, "CM %08x connected to QPN %#lx PSN %#x\n", + local_id, qp->av.qpn, qp->send.psn ); /* Modify queue pair */ if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( conn, "CM %p could not modify queue pair: %s\n", - conn, strerror ( rc ) ); + DBGC ( local_id, "CM %08x could not modify queue " + "pair: %s\n", local_id, strerror ( rc ) ); goto out; } @@ -300,8 +303,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, case htons ( IB_CM_ATTR_CONNECT_REJECT ) : /* Extract fields */ - DBGC ( conn, "CM %p connection rejected (reason %d)\n", - conn, ntohs ( rej->reason ) ); + DBGC ( local_id, "CM %08x connection rejected (reason %d)\n", + local_id, ntohs ( rej->reason ) ); /* Private data is valid only for a Consumer Reject */ if ( rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) { private_data = &rej->private_data; @@ -311,8 +314,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, break; default: - DBGC ( conn, "CM %p unexpected response (attribute %04x)\n", - conn, ntohs ( mad->hdr.attr_id ) ); + DBGC ( local_id, "CM %08x unexpected response (attribute " + "%04x)\n", local_id, ntohs ( mad->hdr.attr_id ) ); rc = -ENOTSUP; break; } @@ -347,12 +350,13 @@ static void ib_cm_path_complete ( struct ib_device *ibdev, struct ib_queue_pair *qp = conn->qp; union ib_mad mad; struct ib_cm_connect_request *req = &mad.cm.cm_data.connect_request; + uint32_t local_id = conn->local_id; size_t private_data_len; /* Report failures */ if ( rc != 0 ) { - DBGC ( conn, "CM %p path lookup failed: %s\n", - conn, strerror ( rc ) ); + DBGC ( local_id, "CM %08x path lookup failed: %s\n", + local_id, strerror ( rc ) ); conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 ); goto out; } @@ -405,8 +409,8 @@ static void ib_cm_path_complete ( struct ib_device *ibdev, conn->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, av, &ib_cm_req_op ); if ( ! conn->madx ) { - DBGC ( conn, "CM %p could not create connection request\n", - conn ); + DBGC ( local_id, "CM %08x could not create connection " + "request\n", local_id ); conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 ); goto out; } @@ -441,6 +445,7 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, void *private_data, size_t private_data_len, struct ib_connection_operations *op ) { struct ib_connection *conn; + uint32_t local_id; /* Allocate and initialise request */ conn = zalloc ( sizeof ( *conn ) + private_data_len ); @@ -451,7 +456,7 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, memset ( &qp->av, 0, sizeof ( qp->av ) ); qp->av.gid_present = 1; memcpy ( &qp->av.gid, dgid, sizeof ( qp->av.gid ) ); - conn->local_id = random(); + conn->local_id = local_id = random(); memcpy ( &conn->service_id, service_id, sizeof ( conn->service_id ) ); conn->op = op; conn->private_data_len = private_data_len; @@ -466,10 +471,11 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Add to list of connections */ list_add ( &conn->list, &ib_cm_conns ); - DBGC ( conn, "CM %p created for IBDEV %s QPN %#lx\n", - conn, ibdev->name, qp->qpn ); - DBGC ( conn, "CM %p connecting to " IB_GID_FMT " " IB_GUID_FMT "\n", - conn, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); + DBGC ( local_id, "CM %08x created for IBDEV %s QPN %#lx\n", + local_id, ibdev->name, qp->qpn ); + DBGC ( local_id, "CM %08x connecting to " IB_GID_FMT " " + IB_GUID_FMT "\n", local_id, IB_GID_ARGS ( dgid ), + IB_GUID_ARGS ( service_id ) ); return conn; diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 1e6bda5b8..16f85a9cd 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -303,6 +303,8 @@ static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, rc = -ENOMEM; goto out; } + DBGC ( cmrc, "CMRC %p using CM %08x\n", + cmrc, cmrc->conn->local_id ); } else { From bd1687465c3c03b51179bd3732356a55b9766061 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 4 Mar 2016 12:24:22 +0000 Subject: [PATCH 103/591] [infiniband] Use correct transaction identifier in CM responses Signed-off-by: Michael Brown --- src/include/ipxe/ib_mad.h | 8 +++++++- src/net/infiniband/ib_cm.c | 14 +++++++++++--- src/net/infiniband/ib_mi.c | 26 +++++++++++++------------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/include/ipxe/ib_mad.h b/src/include/ipxe/ib_mad.h index 96e460edd..134274026 100644 --- a/src/include/ipxe/ib_mad.h +++ b/src/include/ipxe/ib_mad.h @@ -525,6 +525,12 @@ union ib_mad_class_specific { struct ib_smp_class_specific smp; } __attribute__ (( packed )); +/** A management datagram transaction identifier */ +struct ib_mad_tid { + uint32_t high; + uint32_t low; +} __attribute__ (( packed )); + /** A management datagram common header * * Defined in section 13.4.2 of the IBA. @@ -536,7 +542,7 @@ struct ib_mad_hdr { uint8_t method; uint16_t status; union ib_mad_class_specific class_specific; - uint32_t tid[2]; + struct ib_mad_tid tid; uint16_t attr_id; uint8_t reserved[2]; uint32_t attr_mod; diff --git a/src/net/infiniband/ib_cm.c b/src/net/infiniband/ib_cm.c index 942939574..247b8e7a0 100644 --- a/src/net/infiniband/ib_cm.c +++ b/src/net/infiniband/ib_cm.c @@ -65,6 +65,7 @@ static struct ib_connection * ib_cm_find ( uint32_t local_id ) { * * @v ibdev Infiniband device * @v mi Management interface + * @v tid Transaction identifier * @v av Address vector * @v local_id Local communication ID * @v remote_id Remote communication ID @@ -72,6 +73,7 @@ static struct ib_connection * ib_cm_find ( uint32_t local_id ) { */ static int ib_cm_send_rtu ( struct ib_device *ibdev, struct ib_mad_interface *mi, + struct ib_mad_tid *tid, struct ib_address_vector *av, uint32_t local_id, uint32_t remote_id ) { union ib_mad mad; @@ -83,6 +85,7 @@ static int ib_cm_send_rtu ( struct ib_device *ibdev, mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; mad.hdr.class_version = IB_CM_CLASS_VERSION; mad.hdr.method = IB_MGMT_METHOD_SEND; + memcpy ( &mad.hdr.tid, tid, sizeof ( mad.hdr.tid ) ); mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE ); rtu->local_id = htonl ( local_id ); rtu->remote_id = htonl ( remote_id ); @@ -121,7 +124,8 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, conn = ib_cm_find ( local_id ); if ( conn ) { /* Try to send "ready to use" reply */ - if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id, + if ( ( rc = ib_cm_send_rtu ( ibdev, mi, &mad->hdr.tid, av, + conn->local_id, conn->remote_id ) ) != 0 ) { /* Ignore errors; the remote end will retry */ } @@ -135,6 +139,7 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, * * @v ibdev Infiniband device * @v mi Management interface + * @v tid Transaction identifier * @v av Address vector * @v local_id Local communication ID * @v remote_id Remote communication ID @@ -142,6 +147,7 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, */ static int ib_cm_send_drep ( struct ib_device *ibdev, struct ib_mad_interface *mi, + struct ib_mad_tid *tid, struct ib_address_vector *av, uint32_t local_id, uint32_t remote_id ) { union ib_mad mad; @@ -153,6 +159,7 @@ static int ib_cm_send_drep ( struct ib_device *ibdev, mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; mad.hdr.class_version = IB_CM_CLASS_VERSION; mad.hdr.method = IB_MGMT_METHOD_SEND; + memcpy ( &mad.hdr.tid, tid, sizeof ( mad.hdr.tid ) ); mad.hdr.attr_id = htons ( IB_CM_ATTR_DISCONNECT_REPLY ); drep->local_id = htonl ( local_id ); drep->remote_id = htonl ( remote_id ); @@ -197,7 +204,7 @@ static void ib_cm_recv_dreq ( struct ib_device *ibdev, } /* Send reply */ - if ( ( rc = ib_cm_send_drep ( ibdev, mi, av, local_id, + if ( ( rc = ib_cm_send_drep ( ibdev, mi, &mad->hdr.tid, av, local_id, remote_id ) ) != 0 ) { /* Ignore errors; the remote end will retry */ } @@ -294,7 +301,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, } /* Send "ready to use" reply */ - if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id, + if ( ( rc = ib_cm_send_rtu ( ibdev, mi, &mad->hdr.tid, av, + conn->local_id, conn->remote_id ) ) != 0 ) { /* Treat as non-fatal */ rc = 0; diff --git a/src/net/infiniband/ib_mi.c b/src/net/infiniband/ib_mi.c index b43212974..f9c0862b9 100644 --- a/src/net/infiniband/ib_mi.c +++ b/src/net/infiniband/ib_mi.c @@ -106,7 +106,7 @@ static int ib_mi_handle ( struct ib_device *ibdev, /* Otherwise, ignore it */ DBGC ( mi, "MI %p RX TID %08x%08x ignored\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ) ); return -ENOTSUP; } @@ -152,7 +152,7 @@ static void ib_mi_complete_recv ( struct ib_device *ibdev, goto out; } DBGC ( mi, "MI %p RX TID %08x%08x (%02x,%02x,%02x,%04x) status " - "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), + "%04x\n", mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ), hdr->mgmt_class, hdr->class_version, hdr->method, ntohs ( hdr->attr_id ), ntohs ( hdr->status ) ); DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) ); @@ -192,12 +192,12 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, /* Set common fields */ hdr->base_version = IB_MGMT_BASE_VERSION; - if ( ( hdr->tid[0] == 0 ) && ( hdr->tid[1] == 0 ) ) { - hdr->tid[0] = htonl ( IB_MI_TID_MAGIC ); - hdr->tid[1] = htonl ( ++next_tid ); + if ( ( hdr->tid.high == 0 ) && ( hdr->tid.low == 0 ) ) { + hdr->tid.high = htonl ( IB_MI_TID_MAGIC ); + hdr->tid.low = htonl ( ++next_tid ); } DBGC ( mi, "MI %p TX TID %08x%08x (%02x,%02x,%02x,%04x) status " - "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), + "%04x\n", mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ), hdr->mgmt_class, hdr->class_version, hdr->method, ntohs ( hdr->attr_id ), ntohs ( hdr->status ) ); DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) ); @@ -217,8 +217,8 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, smp->return_path.hops[hop_pointer] = ibdev->port; } else { DBGC ( mi, "MI %p TX TID %08x%08x invalid hop pointer " - "%d\n", mi, ntohl ( hdr->tid[0] ), - ntohl ( hdr->tid[1] ), hop_pointer ); + "%d\n", mi, ntohl ( hdr->tid.high ), + ntohl ( hdr->tid.low ), hop_pointer ); return -EINVAL; } } @@ -228,7 +228,7 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, if ( ! iobuf ) { DBGC ( mi, "MI %p could not allocate buffer for TID " "%08x%08x\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ) ); return -ENOMEM; } memcpy ( iob_put ( iobuf, sizeof ( *mad ) ), mad, sizeof ( *mad ) ); @@ -236,7 +236,7 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, /* Send I/O buffer */ if ( ( rc = ib_post_send ( ibdev, mi->qp, av, iobuf ) ) != 0 ) { DBGC ( mi, "MI %p TX TID %08x%08x failed: %s\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ), strerror ( rc ) ); free_iob ( iobuf ); return rc; @@ -261,7 +261,7 @@ static void ib_mi_timer_expired ( struct retry_timer *timer, int expired ) { /* Abandon transaction if we have tried too many times */ if ( expired ) { DBGC ( mi, "MI %p abandoning TID %08x%08x\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ) ); madx->op->complete ( ibdev, mi, madx, -ETIMEDOUT, NULL, NULL ); return; } @@ -408,8 +408,8 @@ void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ) { /* Flush any outstanding requests */ list_for_each_entry_safe ( madx, tmp, &mi->madx, list ) { DBGC ( mi, "MI %p destroyed while TID %08x%08x in progress\n", - mi, ntohl ( madx->mad.hdr.tid[0] ), - ntohl ( madx->mad.hdr.tid[1] ) ); + mi, ntohl ( madx->mad.hdr.tid.high ), + ntohl ( madx->mad.hdr.tid.low ) ); madx->op->complete ( ibdev, mi, madx, -ECANCELED, NULL, NULL ); } From 114a2f19a638bb92fb39e901d802f68a7276dca1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 4 Mar 2016 13:55:50 +0000 Subject: [PATCH 104/591] [infiniband] Do not use GRH for local paths Avoid including an unnecessary GRH in packets sent to unicast destinations within the local subnet. Signed-off-by: Michael Brown --- src/net/infiniband/ib_pathrec.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/net/infiniband/ib_pathrec.c b/src/net/infiniband/ib_pathrec.c index 113b90bda..4b00f3b96 100644 --- a/src/net/infiniband/ib_pathrec.c +++ b/src/net/infiniband/ib_pathrec.c @@ -75,6 +75,12 @@ static void ib_path_complete ( struct ib_device *ibdev, "%d\n", ibdev->name, IB_GID_ARGS ( dgid ), path->av.lid, path->av.sl, path->av.rate ); + /* Use only the LID if no GRH is needed for this path */ + if ( memcmp ( &path->av.gid.s.prefix, &ibdev->gid.s.prefix, + sizeof ( path->av.gid.s.prefix ) ) == 0 ) { + path->av.gid_present = 0; + } + out: /* Destroy the completed transaction */ ib_destroy_madx ( ibdev, mi, madx ); @@ -245,13 +251,6 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { struct ib_cached_path *cached; unsigned int cache_idx; - /* Sanity check */ - if ( ! av->gid_present ) { - DBGC ( ibdev, "IBDEV %s attempt to look up path without GID\n", - ibdev->name ); - return -EINVAL; - } - /* Look in cache for a matching entry */ cached = ib_find_path_cache_entry ( ibdev, gid ); if ( cached && cached->path->av.lid ) { From c335f8eae4e23f1f69418dfd1b2ec442e3c1a70a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 5 Mar 2016 15:33:28 +0000 Subject: [PATCH 105/591] [infiniband] Record multicast GID attachment as part of group membership Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 22 +++++----------------- src/include/ipxe/ib_mcast.h | 2 ++ src/net/infiniband/ib_mcast.c | 10 +++++++++- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index e54f8df40..8165f96e0 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -89,14 +89,8 @@ struct ipoib_device { struct ipoib_mac mac; /** Broadcast MAC */ struct ipoib_mac broadcast; - /** Joined to IPv4 broadcast multicast group - * - * This flag indicates whether or not we have initiated the - * join to the IPv4 broadcast multicast group. - */ - int broadcast_joined; /** IPv4 broadcast multicast group membership */ - struct ib_mc_membership broadcast_membership; + struct ib_mc_membership membership; /** REMAC cache */ struct list_head peers; }; @@ -742,8 +736,8 @@ void ipoib_join_complete ( struct ib_device *ibdev __unused, struct ib_queue_pair *qp __unused, struct ib_mc_membership *membership, int rc, union ib_mad *mad __unused ) { - struct ipoib_device *ipoib = container_of ( membership, - struct ipoib_device, broadcast_membership ); + struct ipoib_device *ipoib = + container_of ( membership, struct ipoib_device, membership ); /* Record join status as link status */ netdev_link_err ( ipoib->netdev, rc ); @@ -759,14 +753,12 @@ static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { int rc; if ( ( rc = ib_mcast_join ( ipoib->ibdev, ipoib->qp, - &ipoib->broadcast_membership, - &ipoib->broadcast.gid, + &ipoib->membership, &ipoib->broadcast.gid, ipoib_join_complete ) ) != 0 ) { DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n", ipoib, strerror ( rc ) ); return rc; } - ipoib->broadcast_joined = 1; return 0; } @@ -778,11 +770,7 @@ static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { */ static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) { - if ( ipoib->broadcast_joined ) { - ib_mcast_leave ( ipoib->ibdev, ipoib->qp, - &ipoib->broadcast_membership ); - ipoib->broadcast_joined = 0; - } + ib_mcast_leave ( ipoib->ibdev, ipoib->qp, &ipoib->membership ); } /** diff --git a/src/include/ipxe/ib_mcast.h b/src/include/ipxe/ib_mcast.h index 564066975..202f8e297 100644 --- a/src/include/ipxe/ib_mcast.h +++ b/src/include/ipxe/ib_mcast.h @@ -19,6 +19,8 @@ struct ib_mc_membership { struct ib_queue_pair *qp; /** Multicast GID */ union ib_gid gid; + /** Attached to multicast GID */ + int attached; /** Multicast group join transaction */ struct ib_mad_transaction *madx; /** Handle join success/failure diff --git a/src/net/infiniband/ib_mcast.c b/src/net/infiniband/ib_mcast.c index b7a6e8ce8..3a0559081 100644 --- a/src/net/infiniband/ib_mcast.c +++ b/src/net/infiniband/ib_mcast.c @@ -150,8 +150,9 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, DBGC ( ibdev, "IBDEV %s QPN %#lx joining " IB_GID_FMT "\n", ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); - /* Sanity check */ + /* Sanity checks */ assert ( qp != NULL ); + assert ( ! membership->attached ); /* Initialise structure */ membership->qp = qp; @@ -164,6 +165,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, ibdev->name, qp->qpn, strerror ( rc ) ); goto err_mcast_attach; } + membership->attached = 1; /* Initiate multicast membership join */ ib_mcast_mad ( ibdev, gid, 1, &mad ); @@ -182,6 +184,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx ); err_create_madx: ib_mcast_detach ( ibdev, qp, gid ); + membership->attached = 0; err_mcast_attach: return rc; } @@ -199,6 +202,10 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_mad mad; int rc; + /* Do nothing if we are already detached from the multicast GID */ + if ( ! membership->attached ) + return; + DBGC ( ibdev, "IBDEV %s QPN %#lx leaving " IB_GID_FMT "\n", ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); @@ -207,6 +214,7 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Detach from multicast GID */ ib_mcast_detach ( ibdev, qp, &membership->gid ); + membership->attached = 0; /* Cancel multicast membership join, if applicable */ if ( membership->madx ) { From 14ad9cbd6713daa10e473f6dfffad4d5373beadf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 09:54:42 +0000 Subject: [PATCH 106/591] [infiniband] Parse MLID, rate, and SL from multicast membership record Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 60 ++++++++++++++++++++++------------- src/include/ipxe/ib_mcast.h | 19 ++++------- src/net/infiniband/ib_mcast.c | 57 +++++++++++++++++---------------- 3 files changed, 74 insertions(+), 62 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 8165f96e0..8bad2b2a2 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -73,6 +73,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Number of IPoIB completion entries */ #define IPOIB_NUM_CQES 8 +/** An IPoIB broadcast address */ +struct ipoib_broadcast { + /** MAC address */ + struct ipoib_mac mac; + /** Address vector */ + struct ib_address_vector av; + /** Multicast group membership */ + struct ib_mc_membership membership; +}; + /** An IPoIB device */ struct ipoib_device { /** Network device */ @@ -87,10 +97,8 @@ struct ipoib_device { struct ib_queue_pair *qp; /** Local MAC */ struct ipoib_mac mac; - /** Broadcast MAC */ - struct ipoib_mac broadcast; - /** IPv4 broadcast multicast group membership */ - struct ib_mc_membership membership; + /** Broadcast address */ + struct ipoib_broadcast broadcast; /** REMAC cache */ struct list_head peers; }; @@ -149,7 +157,7 @@ static struct ipoib_mac * ipoib_find_remac ( struct ipoib_device *ipoib, * multicasts as broadcasts for simplicity. */ if ( is_multicast_ether_addr ( remac ) ) - return &ipoib->broadcast; + return &ipoib->broadcast.mac; /* Try to find via REMAC cache */ list_for_each_entry ( peer, &ipoib->peers, list ) { @@ -559,6 +567,7 @@ static int ipoib_transmit ( struct net_device *netdev, /* Construct address vector */ memset ( &dest, 0, sizeof ( dest ) ); dest.qpn = ( ntohl ( mac->flags__qpn ) & IB_QPN_MASK ); + dest.qkey = ipoib->broadcast.av.qkey; dest.gid_present = 1; memcpy ( &dest.gid, &mac->gid, sizeof ( dest.gid ) ); if ( ( rc = ib_resolve_path ( ibdev, &dest ) ) != 0 ) { @@ -650,8 +659,9 @@ static void ipoib_complete_recv ( struct ib_device *ibdev __unused, ethhdr->h_protocol = net_proto; /* Construct destination address */ - if ( dest->gid_present && ( memcmp ( &dest->gid, &ipoib->broadcast.gid, - sizeof ( dest->gid ) ) == 0 ) ) { + if ( dest->gid_present && + ( memcmp ( &dest->gid, &ipoib->broadcast.mac.gid, + sizeof ( dest->gid ) ) == 0 ) ) { /* Broadcast GID; use the Ethernet broadcast address */ memcpy ( ðhdr->h_dest, eth_broadcast, sizeof ( ethhdr->h_dest ) ); @@ -726,18 +736,13 @@ static void ipoib_poll ( struct net_device *netdev ) { /** * Handle IPv4 broadcast multicast group join completion * - * @v ibdev Infiniband device - * @v qp Queue pair * @v membership Multicast group membership * @v rc Status code - * @v mad Response MAD (or NULL on error) */ -void ipoib_join_complete ( struct ib_device *ibdev __unused, - struct ib_queue_pair *qp __unused, - struct ib_mc_membership *membership, int rc, - union ib_mad *mad __unused ) { - struct ipoib_device *ipoib = - container_of ( membership, struct ipoib_device, membership ); +void ipoib_join_complete ( struct ib_mc_membership *membership, int rc ) { + struct ipoib_device *ipoib = container_of ( membership, + struct ipoib_device, + broadcast.membership ); /* Record join status as link status */ netdev_link_err ( ipoib->netdev, rc ); @@ -752,8 +757,10 @@ void ipoib_join_complete ( struct ib_device *ibdev __unused, static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { int rc; + /* Join multicast group */ if ( ( rc = ib_mcast_join ( ipoib->ibdev, ipoib->qp, - &ipoib->membership, &ipoib->broadcast.gid, + &ipoib->broadcast.membership, + &ipoib->broadcast.av, ipoib_join_complete ) ) != 0 ) { DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n", ipoib, strerror ( rc ) ); @@ -770,7 +777,9 @@ static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { */ static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) { - ib_mcast_leave ( ipoib->ibdev, ipoib->qp, &ipoib->membership ); + /* Leave multicast group */ + ib_mcast_leave ( ipoib->ibdev, ipoib->qp, + &ipoib->broadcast.membership ); } /** @@ -791,10 +800,17 @@ static void ipoib_link_state_changed ( struct ipoib_device *ipoib ) { memcpy ( &ipoib->mac.gid.s.prefix, &ibdev->gid.s.prefix, sizeof ( ipoib->mac.gid.s.prefix ) ); - /* Update broadcast GID based on potentially-new partition key */ - ipoib->broadcast.gid.words[2] = + /* Update broadcast MAC GID based on potentially-new partition key */ + ipoib->broadcast.mac.gid.words[2] = htons ( ibdev->pkey | IB_PKEY_FULL ); + /* Construct broadcast address vector from broadcast MAC address */ + memset ( &ipoib->broadcast.av, 0, sizeof ( ipoib->broadcast.av ) ); + ipoib->broadcast.av.qpn = IB_QPN_BROADCAST; + ipoib->broadcast.av.gid_present = 1; + memcpy ( &ipoib->broadcast.av.gid, &ipoib->broadcast.mac.gid, + sizeof ( ipoib->broadcast.av.gid ) ); + /* Set net device link state to reflect Infiniband link state */ rc = ib_link_rc ( ibdev ); netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) ); @@ -936,8 +952,8 @@ static int ipoib_probe ( struct ib_device *ibdev ) { sizeof ( ipoib->mac.gid.s.guid ) ); /* Set default broadcast MAC address */ - memcpy ( &ipoib->broadcast, &ipoib_broadcast, - sizeof ( ipoib->broadcast ) ); + memcpy ( &ipoib->broadcast.mac, &ipoib_broadcast, + sizeof ( ipoib->broadcast.mac ) ); /* Add to list of IPoIB devices */ list_add_tail ( &ipoib->list, &ipoib_devices ); diff --git a/src/include/ipxe/ib_mcast.h b/src/include/ipxe/ib_mcast.h index 202f8e297..789a2f3bf 100644 --- a/src/include/ipxe/ib_mcast.h +++ b/src/include/ipxe/ib_mcast.h @@ -17,32 +17,25 @@ struct ib_mad_transaction; struct ib_mc_membership { /** Queue pair */ struct ib_queue_pair *qp; - /** Multicast GID */ - union ib_gid gid; + /** Address vector */ + struct ib_address_vector *av; /** Attached to multicast GID */ int attached; /** Multicast group join transaction */ struct ib_mad_transaction *madx; /** Handle join success/failure * - * @v ibdev Infiniband device - * @v qp Queue pair * @v membership Multicast group membership * @v rc Status code - * @v mad Response MAD (or NULL on error) */ - void ( * complete ) ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_mc_membership *membership, int rc, - union ib_mad *mad ); + void ( * complete ) ( struct ib_mc_membership *membership, int rc ); }; extern int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership, - union ib_gid *gid, - void ( * joined ) ( struct ib_device *ibdev, - struct ib_queue_pair *qp, - struct ib_mc_membership *memb, - int rc, union ib_mad *mad ) ); + struct ib_address_vector *av, + void ( * joined ) ( struct ib_mc_membership *memb, + int rc ) ); extern void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership ); diff --git a/src/net/infiniband/ib_mcast.c b/src/net/infiniband/ib_mcast.c index 3a0559081..6f904331c 100644 --- a/src/net/infiniband/ib_mcast.c +++ b/src/net/infiniband/ib_mcast.c @@ -42,11 +42,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Generate multicast membership MAD * * @v ibdev Infiniband device - * @v gid Multicast GID + * @v av Address vector * @v join Join (rather than leave) group * @v mad MAD to fill in */ -static void ib_mcast_mad ( struct ib_device *ibdev, union ib_gid *gid, +static void ib_mcast_mad ( struct ib_device *ibdev, + struct ib_address_vector *av, int join, union ib_mad *mad ) { struct ib_mad_sa *sa = &mad->sa; @@ -61,7 +62,7 @@ static void ib_mcast_mad ( struct ib_device *ibdev, union ib_gid *gid, htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | IB_SA_MCMEMBER_REC_JOIN_STATE ); sa->sa_data.mc_member_record.scope__join_state = 1; - memcpy ( &sa->sa_data.mc_member_record.mgid, gid, + memcpy ( &sa->sa_data.mc_member_record.mgid, &av->gid, sizeof ( sa->sa_data.mc_member_record.mgid ) ); memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid, sizeof ( sa->sa_data.mc_member_record.port_gid ) ); @@ -75,20 +76,19 @@ static void ib_mcast_mad ( struct ib_device *ibdev, union ib_gid *gid, * @v madx Management transaction * @v rc Status code * @v mad Received MAD (or NULL on error) - * @v av Source address vector (or NULL on error) + * @v src Source address vector (or NULL on error) */ static void ib_mcast_complete ( struct ib_device *ibdev, struct ib_mad_interface *mi __unused, struct ib_mad_transaction *madx, int rc, union ib_mad *mad, - struct ib_address_vector *av __unused ) { + struct ib_address_vector *src __unused ) { struct ib_mc_membership *membership = ib_madx_get_ownerdata ( madx ); struct ib_queue_pair *qp = membership->qp; - union ib_gid *gid = &membership->gid; + struct ib_address_vector *av = membership->av; struct ib_mc_member_record *mc_member_record = &mad->sa.sa_data.mc_member_record; int joined; - unsigned long qkey; /* Report failures */ if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) @@ -101,13 +101,17 @@ static void ib_mcast_complete ( struct ib_device *ibdev, /* Extract values from MAD */ joined = ( mad->hdr.method == IB_MGMT_METHOD_GET_RESP ); - qkey = ntohl ( mc_member_record->qkey ); - DBGC ( ibdev, "IBDEV %s QPN %#lx %s " IB_GID_FMT " qkey %lx\n", + av->qkey = ntohl ( mc_member_record->qkey ); + av->lid = ntohs ( mc_member_record->mlid ); + av->rate = ( mc_member_record->rate_selector__rate & 0x3f ); + av->sl = ( ( ntohl ( mc_member_record->sl__flow_label__hop_limit ) + >> 28 ) & 0x0f ); + DBGC ( ibdev, "IBDEV %s QPN %#lx %s " IB_GID_FMT " qkey %#lx\n", ibdev->name, qp->qpn, ( joined ? "joined" : "left" ), - IB_GID_ARGS ( gid ), qkey ); + IB_GID_ARGS ( &av->gid ), av->qkey ); /* Set queue key */ - qp->qkey = qkey; + qp->qkey = av->qkey; if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { DBGC ( ibdev, "IBDEV %s QPN %#lx could not modify qkey: %s\n", ibdev->name, qp->qpn, strerror ( rc ) ); @@ -120,7 +124,7 @@ static void ib_mcast_complete ( struct ib_device *ibdev, membership->madx = NULL; /* Hand off to upper completion handler */ - membership->complete ( ibdev, qp, membership, rc, mad ); + membership->complete ( membership, rc ); } /** Multicast membership management transaction completion operations */ @@ -134,21 +138,20 @@ static struct ib_mad_transaction_operations ib_mcast_op = { * @v ibdev Infiniband device * @v qp Queue pair * @v membership Multicast group membership - * @v gid Multicast GID to join + * @v av Address vector to fill in * @v joined Join completion handler * @ret rc Return status code */ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_mc_membership *membership, union ib_gid *gid, - void ( * complete ) ( struct ib_device *ibdev, - struct ib_queue_pair *qp, - struct ib_mc_membership *membership, - int rc, union ib_mad *mad ) ) { + struct ib_mc_membership *membership, + struct ib_address_vector *av, + void ( * complete ) ( struct ib_mc_membership *membership, + int rc ) ) { union ib_mad mad; int rc; DBGC ( ibdev, "IBDEV %s QPN %#lx joining " IB_GID_FMT "\n", - ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); + ibdev->name, qp->qpn, IB_GID_ARGS ( &av->gid ) ); /* Sanity checks */ assert ( qp != NULL ); @@ -156,11 +159,11 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Initialise structure */ membership->qp = qp; - memcpy ( &membership->gid, gid, sizeof ( membership->gid ) ); + membership->av = av; membership->complete = complete; /* Attach queue pair to multicast GID */ - if ( ( rc = ib_mcast_attach ( ibdev, qp, gid ) ) != 0 ) { + if ( ( rc = ib_mcast_attach ( ibdev, qp, &av->gid ) ) != 0 ) { DBGC ( ibdev, "IBDEV %s QPN %#lx could not attach: %s\n", ibdev->name, qp->qpn, strerror ( rc ) ); goto err_mcast_attach; @@ -168,7 +171,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, membership->attached = 1; /* Initiate multicast membership join */ - ib_mcast_mad ( ibdev, gid, 1, &mad ); + ib_mcast_mad ( ibdev, av, 1, &mad ); membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, &ib_mcast_op ); if ( ! membership->madx ) { @@ -183,7 +186,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx ); err_create_madx: - ib_mcast_detach ( ibdev, qp, gid ); + ib_mcast_detach ( ibdev, qp, &av->gid ); membership->attached = 0; err_mcast_attach: return rc; @@ -198,7 +201,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, */ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership ) { - union ib_gid *gid = &membership->gid; + struct ib_address_vector *av = membership->av; union ib_mad mad; int rc; @@ -207,13 +210,13 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, return; DBGC ( ibdev, "IBDEV %s QPN %#lx leaving " IB_GID_FMT "\n", - ibdev->name, qp->qpn, IB_GID_ARGS ( gid ) ); + ibdev->name, qp->qpn, IB_GID_ARGS ( &av->gid ) ); /* Sanity check */ assert ( qp != NULL ); /* Detach from multicast GID */ - ib_mcast_detach ( ibdev, qp, &membership->gid ); + ib_mcast_detach ( ibdev, qp, &av->gid ); membership->attached = 0; /* Cancel multicast membership join, if applicable */ @@ -223,7 +226,7 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, } /* Send a single group leave MAD */ - ib_mcast_mad ( ibdev, &membership->gid, 0, &mad ); + ib_mcast_mad ( ibdev, av, 0, &mad ); if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) { DBGC ( ibdev, "IBDEV %s QPN %#lx could not send leave request: " "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); From ffdf8ea7573a8bb45bb24c9685a5cda2b21e0176 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 10:06:24 +0000 Subject: [PATCH 107/591] [ipoib] Avoid unnecessary path record lookup for broadcast address Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 8bad2b2a2..d8c4efad5 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -503,8 +503,10 @@ static int ipoib_transmit ( struct net_device *netdev, struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipoib_hdr *ipoib_hdr; + struct ipoib_remac *remac; struct ipoib_mac *mac; - struct ib_address_vector dest; + struct ib_address_vector *dest; + struct ib_address_vector av; uint16_t net_proto; int rc; @@ -522,12 +524,32 @@ static int ipoib_transmit ( struct net_device *netdev, /* Strip eIPoIB header */ ethhdr = iobuf->data; + remac = ( ( struct ipoib_remac * ) ethhdr->h_dest ); net_proto = ethhdr->h_protocol; iob_pull ( iobuf, sizeof ( *ethhdr ) ); /* Identify destination address */ - mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) ); - if ( ! mac ) { + if ( is_multicast_ether_addr ( remac ) ) { + + /* Transmit multicasts as broadcasts, for simplicity */ + dest = &ipoib->broadcast.av; + + } else if ( ( mac = ipoib_find_remac ( ipoib, remac ) ) ) { + + /* Construct address vector from IPoIB MAC */ + dest = &av; + memset ( dest, 0, sizeof ( *dest ) ); + dest->qpn = ( ntohl ( mac->flags__qpn ) & IB_QPN_MASK ); + dest->qkey = ipoib->broadcast.av.qkey; + dest->gid_present = 1; + memcpy ( &dest->gid, &mac->gid, sizeof ( dest->gid ) ); + if ( ( rc = ib_resolve_path ( ibdev, dest ) ) != 0 ) { + /* Path not resolved yet */ + return rc; + } + + } else { + /* Generate a new ARP request (if possible) to trigger * population of the REMAC cache entry. */ @@ -564,18 +586,8 @@ static int ipoib_transmit ( struct net_device *netdev, ipoib_hdr->proto = net_proto; ipoib_hdr->reserved = 0; - /* Construct address vector */ - memset ( &dest, 0, sizeof ( dest ) ); - dest.qpn = ( ntohl ( mac->flags__qpn ) & IB_QPN_MASK ); - dest.qkey = ipoib->broadcast.av.qkey; - dest.gid_present = 1; - memcpy ( &dest.gid, &mac->gid, sizeof ( dest.gid ) ); - if ( ( rc = ib_resolve_path ( ibdev, &dest ) ) != 0 ) { - /* Path not resolved yet */ - return rc; - } - - return ib_post_send ( ibdev, ipoib->qp, &dest, iobuf ); + /* Transmit packet */ + return ib_post_send ( ibdev, ipoib->qp, dest, iobuf ); } /** From e62e52b2b9d09bee8e30f54fa225eb381813fc9a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 10:11:19 +0000 Subject: [PATCH 108/591] [ipoib] Simplify test for received broadcast packets Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index d8c4efad5..66e72ac1a 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -671,10 +671,8 @@ static void ipoib_complete_recv ( struct ib_device *ibdev __unused, ethhdr->h_protocol = net_proto; /* Construct destination address */ - if ( dest->gid_present && - ( memcmp ( &dest->gid, &ipoib->broadcast.mac.gid, - sizeof ( dest->gid ) ) == 0 ) ) { - /* Broadcast GID; use the Ethernet broadcast address */ + if ( IB_LID_MULTICAST ( dest->lid ) ) { + /* Multicast LID; use the Ethernet broadcast address */ memcpy ( ðhdr->h_dest, eth_broadcast, sizeof ( ethhdr->h_dest ) ); } else { From 5a7fd2cc90ef2d44527b24599d76225607be6f96 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 10:34:27 +0000 Subject: [PATCH 109/591] [infiniband] Allow for the creation of multicast groups Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 2 +- src/include/ipxe/ib_mcast.h | 2 +- src/net/infiniband/ib_mcast.c | 25 ++++++++++++++++--------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 66e72ac1a..d545b6f40 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -770,7 +770,7 @@ static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { /* Join multicast group */ if ( ( rc = ib_mcast_join ( ipoib->ibdev, ipoib->qp, &ipoib->broadcast.membership, - &ipoib->broadcast.av, + &ipoib->broadcast.av, 0, ipoib_join_complete ) ) != 0 ) { DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n", ipoib, strerror ( rc ) ); diff --git a/src/include/ipxe/ib_mcast.h b/src/include/ipxe/ib_mcast.h index 789a2f3bf..df348bd9b 100644 --- a/src/include/ipxe/ib_mcast.h +++ b/src/include/ipxe/ib_mcast.h @@ -33,7 +33,7 @@ struct ib_mc_membership { extern int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership, - struct ib_address_vector *av, + struct ib_address_vector *av, unsigned int mask, void ( * joined ) ( struct ib_mc_membership *memb, int rc ) ); diff --git a/src/net/infiniband/ib_mcast.c b/src/net/infiniband/ib_mcast.c index 6f904331c..f7264287c 100644 --- a/src/net/infiniband/ib_mcast.c +++ b/src/net/infiniband/ib_mcast.c @@ -43,25 +43,32 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @v ibdev Infiniband device * @v av Address vector - * @v join Join (rather than leave) group + * @v method Method (IB_MGMT_METHOD_SET or IB_MGMT_METHOD_DELETE) + * @v mask Additional component mask * @v mad MAD to fill in */ static void ib_mcast_mad ( struct ib_device *ibdev, struct ib_address_vector *av, - int join, union ib_mad *mad ) { + unsigned int method, unsigned int mask, + union ib_mad *mad ) { struct ib_mad_sa *sa = &mad->sa; /* Construct multicast membership record request */ memset ( sa, 0, sizeof ( *sa ) ); sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; sa->mad_hdr.class_version = IB_SA_CLASS_VERSION; - sa->mad_hdr.method = - ( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE ); + sa->mad_hdr.method = method; sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC ); sa->sa_hdr.comp_mask[1] = htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | - IB_SA_MCMEMBER_REC_JOIN_STATE ); - sa->sa_data.mc_member_record.scope__join_state = 1; + IB_SA_MCMEMBER_REC_JOIN_STATE | mask ); + sa->sa_data.mc_member_record.qkey = htonl ( av->qkey ); + sa->sa_data.mc_member_record.pkey = + htons ( ibdev->pkey | IB_PKEY_FULL ); + sa->sa_data.mc_member_record.rate_selector__rate = av->rate; + sa->sa_data.mc_member_record.sl__flow_label__hop_limit = + htonl ( av->sl << 28 ); + sa->sa_data.mc_member_record.scope__join_state = 0x01; memcpy ( &sa->sa_data.mc_member_record.mgid, &av->gid, sizeof ( sa->sa_data.mc_member_record.mgid ) ); memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid, @@ -144,7 +151,7 @@ static struct ib_mad_transaction_operations ib_mcast_op = { */ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership, - struct ib_address_vector *av, + struct ib_address_vector *av, unsigned int mask, void ( * complete ) ( struct ib_mc_membership *membership, int rc ) ) { union ib_mad mad; @@ -171,7 +178,7 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, membership->attached = 1; /* Initiate multicast membership join */ - ib_mcast_mad ( ibdev, av, 1, &mad ); + ib_mcast_mad ( ibdev, av, IB_MGMT_METHOD_SET, mask, &mad ); membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, &ib_mcast_op ); if ( ! membership->madx ) { @@ -226,7 +233,7 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, } /* Send a single group leave MAD */ - ib_mcast_mad ( ibdev, av, 0, &mad ); + ib_mcast_mad ( ibdev, av, IB_MGMT_METHOD_DELETE, 0, &mad ); if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) { DBGC ( ibdev, "IBDEV %s QPN %#lx could not send leave request: " "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); From d3db00ecf9f9860d8e029b22b17a5cef9dbdbc33 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 13:06:06 +0000 Subject: [PATCH 110/591] [pcbios] Restrict external memory allocations to the low 4GB When running the 64-bit BIOS version of iPXE, restrict external memory allocations to the low 4GB to ensure that allocations (such as for initrds) fall within our identity-mapped memory region, and will be accessible to the potentially 32-bit operating system. Move largest_memblock() back to memtop_umalloc.c, since this change imposes a restriction that applies only to BIOS builds. Signed-off-by: Michael Brown --- .../x86/interface/pcbios/memtop_umalloc.c | 53 ++++++++++++ src/core/memblock.c | 85 ------------------- 2 files changed, 53 insertions(+), 85 deletions(-) delete mode 100644 src/core/memblock.c diff --git a/src/arch/x86/interface/pcbios/memtop_umalloc.c b/src/arch/x86/interface/pcbios/memtop_umalloc.c index 957f8e324..f1ab73e29 100644 --- a/src/arch/x86/interface/pcbios/memtop_umalloc.c +++ b/src/arch/x86/interface/pcbios/memtop_umalloc.c @@ -38,6 +38,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** Maximum usable address for external allocated memory */ +#define EM_MAX_ADDRESS 0xffffffffUL + /** Alignment of external allocated memory */ #define EM_ALIGN ( 4 * 1024 ) @@ -61,6 +64,56 @@ static userptr_t bottom = UNULL; /** Remaining space on heap */ static size_t heap_size; +/** + * Find largest usable memory region + * + * @ret start Start of region + * @ret len Length of region + */ +size_t largest_memblock ( userptr_t *start ) { + struct memory_map memmap; + struct memory_region *region; + physaddr_t max = EM_MAX_ADDRESS; + physaddr_t region_start; + physaddr_t region_end; + size_t region_len; + unsigned int i; + size_t len = 0; + + /* Avoid returning uninitialised data on error */ + *start = UNULL; + + /* Scan through all memory regions */ + get_memmap ( &memmap ); + for ( i = 0 ; i < memmap.count ; i++ ) { + region = &memmap.regions[i]; + DBG ( "Considering [%llx,%llx)\n", region->start, region->end ); + + /* Truncate block to maximum physical address */ + if ( region->start > max ) { + DBG ( "...starts after maximum address %lx\n", max ); + continue; + } + region_start = region->start; + if ( region->end > max ) { + DBG ( "...end truncated to maximum address %lx\n", max); + region_end = 0; /* =max, given the wraparound */ + } else { + region_end = region->end; + } + region_len = ( region_end - region_start ); + + /* Use largest block */ + if ( region_len > len ) { + DBG ( "...new best block found\n" ); + *start = phys_to_user ( region_start ); + len = region_len; + } + } + + return len; +} + /** * Initialise external heap * diff --git a/src/core/memblock.c b/src/core/memblock.c deleted file mode 100644 index aecddc22c..000000000 --- a/src/core/memblock.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * 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 - * - * Largest memory block - * - */ - -#include -#include -#include -#include - -/** - * Find largest usable memory region - * - * @ret start Start of region - * @ret len Length of region - */ -size_t largest_memblock ( userptr_t *start ) { - struct memory_map memmap; - struct memory_region *region; - physaddr_t max = ~( ( physaddr_t ) 0 ); - physaddr_t region_start; - physaddr_t region_end; - size_t region_len; - unsigned int i; - size_t len = 0; - - /* Avoid returning uninitialised data on error */ - *start = UNULL; - - /* Scan through all memory regions */ - get_memmap ( &memmap ); - for ( i = 0 ; i < memmap.count ; i++ ) { - region = &memmap.regions[i]; - DBG ( "Considering [%llx,%llx)\n", region->start, region->end ); - - /* Truncate block to maximum physical address */ - if ( region->start > max ) { - DBG ( "...starts after maximum address %lx\n", max ); - continue; - } - region_start = region->start; - if ( region->end > max ) { - DBG ( "...end truncated to maximum address %lx\n", max); - region_end = 0; /* =max, given the wraparound */ - } else { - region_end = region->end; - } - region_len = ( region_end - region_start ); - - /* Use largest block */ - if ( region_len > len ) { - DBG ( "...new best block found\n" ); - *start = phys_to_user ( region_start ); - len = region_len; - } - } - - return len; -} From 174bf6b5698299678f2d928cae126353558a766d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 15:34:25 +0000 Subject: [PATCH 111/591] [infiniband] Assign names to CMRC connections Signed-off-by: Michael Brown --- src/include/ipxe/ib_cmrc.h | 7 ++- src/net/infiniband/ib_cmrc.c | 84 +++++++++++++++++++++--------------- src/net/infiniband/ib_srp.c | 2 +- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/include/ipxe/ib_cmrc.h b/src/include/ipxe/ib_cmrc.h index 47ad27fa6..f3276e6ef 100644 --- a/src/include/ipxe/ib_cmrc.h +++ b/src/include/ipxe/ib_cmrc.h @@ -12,9 +12,8 @@ FILE_LICENCE ( BSD2 ); #include #include -extern int ib_cmrc_open ( struct interface *xfer, - struct ib_device *ibdev, - union ib_gid *dgid, - union ib_guid *service_id ); +extern int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, + union ib_gid *dgid, union ib_guid *service_id, + const char *name ); #endif /* _IPXE_IB_CMRC_H */ diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 16f85a9cd..f4cd583d1 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -69,6 +69,8 @@ FILE_LICENCE ( BSD2 ); struct ib_cmrc_connection { /** Reference count */ struct refcnt refcnt; + /** Name */ + const char *name; /** Data transfer interface */ struct interface xfer; /** Infiniband device */ @@ -108,14 +110,16 @@ struct ib_cmrc_connection { * shutdown process has run. */ static void ib_cmrc_shutdown ( struct ib_cmrc_connection *cmrc ) { + struct ib_device *ibdev = cmrc->ibdev; - DBGC ( cmrc, "CMRC %p shutting down\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s shutting down\n", + ibdev->name, cmrc->name ); /* Shut down Infiniband interface */ - ib_destroy_conn ( cmrc->ibdev, cmrc->qp, cmrc->conn ); - ib_destroy_qp ( cmrc->ibdev, cmrc->qp ); - ib_destroy_cq ( cmrc->ibdev, cmrc->cq ); - ib_close ( cmrc->ibdev ); + ib_destroy_conn ( ibdev, cmrc->qp, cmrc->conn ); + ib_destroy_qp ( ibdev, cmrc->qp ); + ib_destroy_cq ( ibdev, cmrc->cq ); + ib_close ( ibdev ); /* Cancel any pending shutdown */ process_del ( &cmrc->shutdown ); @@ -149,7 +153,7 @@ static void ib_cmrc_close ( struct ib_cmrc_connection *cmrc, int rc ) { * @v private_data Private data, if available * @v private_data_len Length of private data */ -static void ib_cmrc_changed ( struct ib_device *ibdev __unused, +static void ib_cmrc_changed ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_connection *conn __unused, int rc_cm, void *private_data, size_t private_data_len ) { @@ -158,22 +162,24 @@ static void ib_cmrc_changed ( struct ib_device *ibdev __unused, /* Record connection status */ if ( rc_cm == 0 ) { - DBGC ( cmrc, "CMRC %p connected\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s connected\n", + ibdev->name, cmrc->name ); cmrc->connected = 1; } else { - DBGC ( cmrc, "CMRC %p disconnected: %s\n", - cmrc, strerror ( rc_cm ) ); + DBGC ( cmrc, "CMRC %s %s disconnected: %s\n", + ibdev->name, cmrc->name, strerror ( rc_cm ) ); cmrc->connected = 0; } /* Pass up any private data */ - DBGC2 ( cmrc, "CMRC %p received private data:\n", cmrc ); + DBGC2 ( cmrc, "CMRC %s %s received private data:\n", + ibdev->name, cmrc->name ); DBGC2_HDA ( cmrc, 0, private_data, private_data_len ); if ( private_data && ( rc_xfer = xfer_deliver_raw ( &cmrc->xfer, private_data, private_data_len ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not deliver private data: %s\n", - cmrc, strerror ( rc_xfer ) ); + DBGC ( cmrc, "CMRC %s %s could not deliver private data: %s\n", + ibdev->name, cmrc->name, strerror ( rc_xfer ) ); ib_cmrc_close ( cmrc, rc_xfer ); return; } @@ -201,7 +207,7 @@ static struct ib_connection_operations ib_cmrc_conn_op = { * @v iobuf I/O buffer * @v rc Completion status code */ -static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, +static void ib_cmrc_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct io_buffer *iobuf, int rc ) { struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp ); @@ -211,8 +217,8 @@ static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, /* Close the connection on any send errors */ if ( rc != 0 ) { - DBGC ( cmrc, "CMRC %p send error: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s send error: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); ib_cmrc_close ( cmrc, rc ); return; } @@ -228,7 +234,7 @@ static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, * @v iobuf I/O buffer * @v rc Completion status code */ -static void ib_cmrc_complete_recv ( struct ib_device *ibdev __unused, +static void ib_cmrc_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_address_vector *dest __unused, struct ib_address_vector *source __unused, @@ -237,20 +243,20 @@ static void ib_cmrc_complete_recv ( struct ib_device *ibdev __unused, /* Close the connection on any receive errors */ if ( rc != 0 ) { - DBGC ( cmrc, "CMRC %p receive error: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s receive error: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); free_iob ( iobuf ); ib_cmrc_close ( cmrc, rc ); return; } - DBGC2 ( cmrc, "CMRC %p received:\n", cmrc ); + DBGC2 ( cmrc, "CMRC %s %s received:\n", ibdev->name, cmrc->name ); DBGC2_HDA ( cmrc, 0, iobuf->data, iob_len ( iobuf ) ); /* Pass up data */ if ( ( rc = xfer_deliver_iob ( &cmrc->xfer, iobuf ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not deliver data: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s could not deliver data: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); ib_cmrc_close ( cmrc, rc ); return; } @@ -278,6 +284,7 @@ static struct ib_queue_pair_operations ib_cmrc_queue_pair_ops = { static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, struct io_buffer *iobuf, struct xfer_metadata *meta __unused ) { + struct ib_device *ibdev = cmrc->ibdev; int rc; /* If no connection has yet been attempted, send this datagram @@ -287,8 +294,9 @@ static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, /* Abort if we have already sent a CM connection request */ if ( cmrc->conn ) { - DBGC ( cmrc, "CMRC %p attempt to send before " - "connection is complete\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s attempt to send before " + "connection is complete\n", + ibdev->name, cmrc->name ); rc = -EIO; goto out; } @@ -299,20 +307,21 @@ static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, iobuf->data, iob_len ( iobuf ), &ib_cmrc_conn_op ); if ( ! cmrc->conn ) { - DBGC ( cmrc, "CMRC %p could not connect\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s could not connect\n", + ibdev->name, cmrc->name ); rc = -ENOMEM; goto out; } - DBGC ( cmrc, "CMRC %p using CM %08x\n", - cmrc, cmrc->conn->local_id ); + DBGC ( cmrc, "CMRC %s %s using CM %08x\n", + ibdev->name, cmrc->name, cmrc->conn->local_id ); } else { /* Send via QP */ if ( ( rc = ib_post_send ( cmrc->ibdev, cmrc->qp, NULL, iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not send: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s could not send: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); goto out; } @@ -382,10 +391,12 @@ static struct process_descriptor ib_cmrc_shutdown_desc = * @v ibdev Infiniband device * @v dgid Destination GID * @v service_id Service ID + * @v name Connection name * @ret rc Returns status code */ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, - union ib_gid *dgid, union ib_guid *service_id ) { + union ib_gid *dgid, union ib_guid *service_id, + const char *name ) { struct ib_cmrc_connection *cmrc; int rc; @@ -396,6 +407,7 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, goto err_alloc; } ref_init ( &cmrc->refcnt, NULL ); + cmrc->name = name; intf_init ( &cmrc->xfer, &ib_cmrc_xfer_desc, &cmrc->refcnt ); cmrc->ibdev = ibdev; memcpy ( &cmrc->dgid, dgid, sizeof ( cmrc->dgid ) ); @@ -405,8 +417,8 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, /* Open Infiniband device */ if ( ( rc = ib_open ( ibdev ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not open device: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s could not open device: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); goto err_open; } @@ -414,8 +426,8 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, cmrc->cq = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES, &ib_cmrc_completion_ops ); if ( ! cmrc->cq ) { - DBGC ( cmrc, "CMRC %p could not create completion queue\n", - cmrc ); + DBGC ( cmrc, "CMRC %s %s could not create completion queue\n", + ibdev->name, cmrc->name ); rc = -ENOMEM; goto err_create_cq; } @@ -425,12 +437,14 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, &ib_cmrc_queue_pair_ops ); if ( ! cmrc->qp ) { - DBGC ( cmrc, "CMRC %p could not create queue pair\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s could not create queue pair\n", + ibdev->name, cmrc->name ); rc = -ENOMEM; goto err_create_qp; } ib_qp_set_ownerdata ( cmrc->qp, cmrc ); - DBGC ( cmrc, "CMRC %p using QPN %#lx\n", cmrc, cmrc->qp->qpn ); + DBGC ( cmrc, "CMRC %s %s using QPN %#lx\n", + ibdev->name, cmrc->name, cmrc->qp->qpn ); /* Attach to parent interface, transfer reference (implicitly) * to our shutdown process, and return. diff --git a/src/net/infiniband/ib_srp.c b/src/net/infiniband/ib_srp.c index 3700184c0..3b4914abf 100644 --- a/src/net/infiniband/ib_srp.c +++ b/src/net/infiniband/ib_srp.c @@ -210,7 +210,7 @@ static int ib_srp_open ( struct interface *block, struct ib_device *ibdev, /* Open CMRC socket */ if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid, - service_id ) ) != 0 ) { + service_id, "SRP" ) ) != 0 ) { DBGC ( ib_srp, "IBSRP %p could not open CMRC socket: %s\n", ib_srp, strerror ( rc ) ); goto err_cmrc_open; From 6a3ffa0114411ae4aa6c4485c6058bf03fec3623 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 15:48:53 +0000 Subject: [PATCH 112/591] [infiniband] Assign names to queue pairs Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 2 +- src/drivers/net/ipoib.c | 2 +- src/include/ipxe/infiniband.h | 4 +++- src/net/infiniband.c | 15 +++++++++------ src/net/infiniband/ib_cmrc.c | 2 +- src/net/infiniband/ib_mi.c | 6 ++++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index a9c728706..cac5fe991 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -3242,7 +3242,7 @@ static int hermon_eth_open ( struct net_device *netdev ) { port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH, HERMON_ETH_NUM_SEND_WQES, port->eth_cq, HERMON_ETH_NUM_RECV_WQES, port->eth_cq, - &hermon_eth_qp_op ); + &hermon_eth_qp_op, netdev->name ); if ( ! port->eth_qp ) { DBGC ( hermon, "Hermon %p port %d could not create queue " "pair\n", hermon, ibdev->port ); diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index d545b6f40..b52ccb194 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -865,7 +865,7 @@ static int ipoib_open ( struct net_device *netdev ) { /* Allocate queue pair */ ipoib->qp = ib_create_qp ( ibdev, IB_QPT_UD, IPOIB_NUM_SEND_WQES, ipoib->cq, IPOIB_NUM_RECV_WQES, ipoib->cq, - &ipoib_qp_op ); + &ipoib_qp_op, netdev->name ); if ( ! ipoib->qp ) { DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n", ipoib ); diff --git a/src/include/ipxe/infiniband.h b/src/include/ipxe/infiniband.h index 065e42180..5910390da 100644 --- a/src/include/ipxe/infiniband.h +++ b/src/include/ipxe/infiniband.h @@ -158,6 +158,8 @@ struct ib_queue_pair { struct ib_device *ibdev; /** List of queue pairs on this Infiniband device */ struct list_head list; + /** Queue pair name */ + const char *name; /** Queue pair number */ unsigned long qpn; /** Externally-visible queue pair number @@ -498,7 +500,7 @@ extern struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, unsigned int num_send_wqes, struct ib_completion_queue *send_cq, unsigned int num_recv_wqes, struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op ); + struct ib_queue_pair_operations *op, const char *name ); extern int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ); extern void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ); diff --git a/src/net/infiniband.c b/src/net/infiniband.c index 106371c2f..df95579d1 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -107,7 +107,7 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, if ( ! cq ) goto err_alloc_cq; cq->ibdev = ibdev; - list_add ( &cq->list, &ibdev->cqs ); + list_add_tail ( &cq->list, &ibdev->cqs ); cq->num_cqes = num_cqes; INIT_LIST_HEAD ( &cq->work_queues ); cq->op = op; @@ -185,6 +185,7 @@ void ib_poll_cq ( struct ib_device *ibdev, * @v num_recv_wqes Number of receive work queue entries * @v recv_cq Receive completion queue * @v op Queue pair operations + * @v name Queue pair name * @ret qp Queue pair * * The queue pair will be left in the INIT state; you must call @@ -196,7 +197,8 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, struct ib_completion_queue *send_cq, unsigned int num_recv_wqes, struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op ) { + struct ib_queue_pair_operations *op, + const char *name ) { struct ib_queue_pair *qp; size_t total_size; int rc; @@ -211,24 +213,25 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, if ( ! qp ) goto err_alloc_qp; qp->ibdev = ibdev; - list_add ( &qp->list, &ibdev->qps ); + list_add_tail ( &qp->list, &ibdev->qps ); qp->type = type; qp->send.qp = qp; qp->send.is_send = 1; qp->send.cq = send_cq; - list_add ( &qp->send.list, &send_cq->work_queues ); + list_add_tail ( &qp->send.list, &send_cq->work_queues ); qp->send.psn = ( random() & 0xffffffUL ); qp->send.num_wqes = num_send_wqes; qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) ); qp->recv.qp = qp; qp->recv.cq = recv_cq; - list_add ( &qp->recv.list, &recv_cq->work_queues ); + list_add_tail ( &qp->recv.list, &recv_cq->work_queues ); qp->recv.psn = ( random() & 0xffffffUL ); qp->recv.num_wqes = num_recv_wqes; qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) + ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) )); INIT_LIST_HEAD ( &qp->mgids ); qp->op = op; + qp->name = name; /* Perform device-specific initialisation and get QPN */ if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) { @@ -756,7 +759,7 @@ int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, goto err_alloc_mgid; } memcpy ( &mgid->gid, gid, sizeof ( mgid->gid ) ); - list_add ( &mgid->list, &qp->mgids ); + list_add_tail ( &mgid->list, &qp->mgids ); /* Add to hardware multicast GID list */ if ( ( rc = ibdev->op->mcast_attach ( ibdev, qp, gid ) ) != 0 ) diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index f4cd583d1..2cd49018f 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -435,7 +435,7 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, /* Create queue pair */ cmrc->qp = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES, cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, - &ib_cmrc_queue_pair_ops ); + &ib_cmrc_queue_pair_ops, name ); if ( ! cmrc->qp ) { DBGC ( cmrc, "CMRC %s %s could not create queue pair\n", ibdev->name, cmrc->name ); diff --git a/src/net/infiniband/ib_mi.c b/src/net/infiniband/ib_mi.c index f9c0862b9..548a1c829 100644 --- a/src/net/infiniband/ib_mi.c +++ b/src/net/infiniband/ib_mi.c @@ -346,6 +346,7 @@ void ib_destroy_madx ( struct ib_device *ibdev __unused, struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, enum ib_queue_pair_type type ) { struct ib_mad_interface *mi; + const char *name; int rc; /* Allocate and initialise fields */ @@ -363,16 +364,17 @@ struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, } /* Create queue pair */ + name = ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ); mi->qp = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq, IB_MI_NUM_RECV_WQES, mi->cq, - &ib_mi_queue_pair_ops ); + &ib_mi_queue_pair_ops, name ); if ( ! mi->qp ) { DBGC ( mi, "MI %p could not allocate queue pair\n", mi ); goto err_create_qp; } ib_qp_set_ownerdata ( mi->qp, mi ); DBGC ( mi, "MI %p (%s) running on QPN %#lx\n", - mi, ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ), mi->qp->qpn ); + mi, mi->qp->name, mi->qp->qpn ); /* Set queue key */ mi->qp->qkey = ( ( type == IB_QPT_SMI ) ? IB_QKEY_SMI : IB_QKEY_GSI ); From 299fdabe4848fae1bd4403836e21964c754ea9ef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 15:49:52 +0000 Subject: [PATCH 113/591] [infiniband] Add "ibstat" command Signed-off-by: Michael Brown --- src/config/config_infiniband.c | 7 +++ src/config/general.h | 1 + src/hci/commands/ibmgmt_cmd.c | 79 ++++++++++++++++++++++++++++++++++ src/include/usr/ibmgmt.h | 16 +++++++ src/usr/ibmgmt.c | 62 ++++++++++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 src/hci/commands/ibmgmt_cmd.c create mode 100644 src/include/usr/ibmgmt.h create mode 100644 src/usr/ibmgmt.c diff --git a/src/config/config_infiniband.c b/src/config/config_infiniband.c index a742e7559..8c63fb00e 100644 --- a/src/config/config_infiniband.c +++ b/src/config/config_infiniband.c @@ -37,3 +37,10 @@ PROVIDE_REQUIRING_SYMBOL(); #ifdef SANBOOT_PROTO_IB_SRP REQUIRE_OBJECT ( ib_srp ); #endif + +/* + * Drag in Infiniband-specific commands + */ +#ifdef IBMGMT_CMD +REQUIRE_OBJECT ( ibmgmt_cmd ); +#endif diff --git a/src/config/general.h b/src/config/general.h index ee15f6bf1..dba412128 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -120,6 +120,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONFIG_CMD /* Option configuration console */ #define IFMGMT_CMD /* Interface management commands */ #define IWMGMT_CMD /* Wireless interface management commands */ +#define IBMGMT_CMD /* Infiniband management commands */ #define FCMGMT_CMD /* Fibre Channel management commands */ #define ROUTE_CMD /* Routing table management commands */ #define IMAGE_CMD /* Image management commands */ diff --git a/src/hci/commands/ibmgmt_cmd.c b/src/hci/commands/ibmgmt_cmd.c new file mode 100644 index 000000000..1154d749e --- /dev/null +++ b/src/hci/commands/ibmgmt_cmd.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband device management commands + * + */ + +/** "ibstat" options */ +struct ibstat_options {}; + +/** "ibstat" option list */ +static struct option_descriptor ibstat_opts[] = {}; + +/** "ibstat" command descriptor */ +static struct command_descriptor ibstat_cmd = + COMMAND_DESC ( struct ibstat_options, ibstat_opts, 0, 0, "" ); + +/** + * The "ibstat" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int ibstat_exec ( int argc, char **argv ) { + struct ibstat_options opts; + struct ib_device *ibdev; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &ibstat_cmd, &opts ) ) != 0 ) + return rc; + + /* Show all Infiniband devices */ + for_each_ibdev ( ibdev ) + ibstat ( ibdev ); + + return 0; +} + +/** Infiniband commands */ +struct command ibmgmt_commands[] __command = { + { + .name = "ibstat", + .exec = ibstat_exec, + }, +}; diff --git a/src/include/usr/ibmgmt.h b/src/include/usr/ibmgmt.h new file mode 100644 index 000000000..16a099134 --- /dev/null +++ b/src/include/usr/ibmgmt.h @@ -0,0 +1,16 @@ +#ifndef _USR_IBMGMT_H +#define _USR_IBMGMT_H + +/** @file + * + * Infiniband device management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +struct ib_device; + +extern void ibstat ( struct ib_device *ibdev ); + +#endif /* _USR_IBMGMT_H */ diff --git a/src/usr/ibmgmt.c b/src/usr/ibmgmt.c new file mode 100644 index 000000000..7857664d6 --- /dev/null +++ b/src/usr/ibmgmt.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband device management + * + */ + +/** + * Print status of Infiniband device + * + * @v ibdev Infiniband device + */ +void ibstat ( struct ib_device *ibdev ) { + struct ib_queue_pair *qp; + + printf ( "%s: " IB_GUID_FMT " using %s on %s port %d (%s)\n", + ibdev->name, IB_GUID_ARGS ( &ibdev->gid.s.guid ), + ibdev->dev->driver_name, ibdev->dev->name, ibdev->port, + ( ib_is_open ( ibdev ) ? "open" : "closed" ) ); + if ( ib_link_ok ( ibdev ) ) { + printf ( " [Link:up LID %d prefix " IB_GUID_FMT "]\n", + ibdev->lid, IB_GUID_ARGS ( &ibdev->gid.s.prefix ) ); + } else { + printf ( " [Link:down, port state %d]\n", ibdev->port_state ); + } + list_for_each_entry ( qp, &ibdev->qps, list ) { + printf ( " QPN %#lx send %d/%d recv %d/%d %s\n", + qp->qpn, qp->send.fill, qp->send.num_wqes, + qp->recv.fill, qp->recv.num_wqes, qp->name ); + } +} From 076d77264842d4cdefebf8cc3b19b3292608d43d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 17:20:28 +0000 Subject: [PATCH 114/591] [infiniband] Retrieve GID flag from cached path entries Signed-off-by: Michael Brown --- src/net/infiniband/ib_pathrec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/infiniband/ib_pathrec.c b/src/net/infiniband/ib_pathrec.c index 4b00f3b96..f846710f2 100644 --- a/src/net/infiniband/ib_pathrec.c +++ b/src/net/infiniband/ib_pathrec.c @@ -258,6 +258,7 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { av->lid = cached->path->av.lid; av->rate = cached->path->av.rate; av->sl = cached->path->av.sl; + av->gid_present = cached->path->av.gid_present; DBGC2 ( ibdev, "IBDEV %s cache hit for " IB_GID_FMT "\n", ibdev->name, IB_GID_ARGS ( gid ) ); return 0; From b5aa51ac62bd36294e1f312d0ac1d742f28fe7b0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 17:02:24 +0000 Subject: [PATCH 115/591] [ipoib] Resimplify test for received broadcast packets Commit e62e52b ("[ipoib] Simplify test for received broadcast packets") relies upon the multicast LID being present in the destination address vector as passed to ipoib_complete_recv(). Unfortunately, this information is not present in many Infiniband devices' completion queue entries. Fix by testing instead for the presence of a multicast GID. Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 4 ++-- src/include/ipxe/ib_packet.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index b52ccb194..4b85eda31 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -671,8 +671,8 @@ static void ipoib_complete_recv ( struct ib_device *ibdev __unused, ethhdr->h_protocol = net_proto; /* Construct destination address */ - if ( IB_LID_MULTICAST ( dest->lid ) ) { - /* Multicast LID; use the Ethernet broadcast address */ + if ( dest->gid_present && IB_GID_MULTICAST ( &dest->gid ) ) { + /* Multicast GID: use the Ethernet broadcast address */ memcpy ( ðhdr->h_dest, eth_broadcast, sizeof ( ethhdr->h_dest ) ); } else { diff --git a/src/include/ipxe/ib_packet.h b/src/include/ipxe/ib_packet.h index 2cea016bd..747f96399 100644 --- a/src/include/ipxe/ib_packet.h +++ b/src/include/ipxe/ib_packet.h @@ -48,6 +48,9 @@ union ib_gid { #define IB_GID_ARGS( gid ) \ IB_GUID_ARGS ( &(gid)->s.prefix ), IB_GUID_ARGS ( &(gid)->s.guid ) +/** Test for multicast GID */ +#define IB_GID_MULTICAST( gid ) ( (gid)->bytes[0] == 0xff ) + /** An Infiniband Local Route Header */ struct ib_local_route_header { /** Virtual lane and link version */ From 9939b704f132f08bebf95e5713d2b5e6bf2b0b0a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 17:24:17 +0000 Subject: [PATCH 116/591] [ipoib] Increase number of transmit work queue entries Avoid running out of transmit work queue entries under heavy load. Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 4b85eda31..4106c208c 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -65,13 +65,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); "Missing REMAC for IPv4 packet (ARP sent)" ) /** Number of IPoIB send work queue entries */ -#define IPOIB_NUM_SEND_WQES 2 +#define IPOIB_NUM_SEND_WQES 8 /** Number of IPoIB receive work queue entries */ #define IPOIB_NUM_RECV_WQES 4 /** Number of IPoIB completion entries */ -#define IPOIB_NUM_CQES 8 +#define IPOIB_NUM_CQES 16 /** An IPoIB broadcast address */ struct ipoib_broadcast { From 8290a10abaacf1f0b2b3e183deb201b6ed86caf2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2016 14:36:31 +0000 Subject: [PATCH 117/591] [ifmgmt] Include human-readable error message for configuration failure Signed-off-by: Michael Brown --- src/usr/ifmgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/usr/ifmgmt.c b/src/usr/ifmgmt.c index aefdaa45d..f367149f7 100644 --- a/src/usr/ifmgmt.c +++ b/src/usr/ifmgmt.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -50,6 +51,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EADDRNOTAVAIL, 0x01, \ "No configuration methods succeeded" ) +/** Human-readable error message */ +struct errortab ifmgmt_errors[] __errortab = { + __einfo_errortab ( EINFO_EADDRNOTAVAIL_CONFIG ), +}; + /** * Open network device * From 5bcaa1e4d4217911ef7c557a1da49b6248161e80 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2016 08:41:53 +0000 Subject: [PATCH 118/591] [infiniband] Make IPoIB support configurable at build time Add a build configuration option VNIC_IPOIB to control whether or not IPoIB support is included for Infiniband devices. Signed-off-by: Michael Brown --- src/config/config_infiniband.c | 7 +++++++ src/config/general.h | 6 ++++++ src/net/infiniband.c | 3 --- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/config/config_infiniband.c b/src/config/config_infiniband.c index 8c63fb00e..9bac3b7c4 100644 --- a/src/config/config_infiniband.c +++ b/src/config/config_infiniband.c @@ -38,6 +38,13 @@ PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT ( ib_srp ); #endif +/* + * Drag in Infiniband-specific virtual network devices + */ +#ifdef VNIC_IPOIB +REQUIRE_OBJECT ( ipoib ); +#endif + /* * Drag in Infiniband-specific commands */ diff --git a/src/config/general.h b/src/config/general.h index dba412128..9c4330399 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -153,6 +153,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef NONPNP_HOOK_INT19 /* Hook INT19 on non-PnP BIOSes */ #define AUTOBOOT_ROM_FILTER /* Autoboot only devices matching our ROM */ +/* + * Virtual network devices + * + */ +#define VNIC_IPOIB /* Infiniband IPoIB virtual NICs */ + /* * Error message tables to include * diff --git a/src/net/infiniband.c b/src/net/infiniband.c index df95579d1..15ff0529b 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -1052,6 +1052,3 @@ REQUIRING_SYMBOL ( register_ibdev ); /* Drag in Infiniband configuration */ REQUIRE_OBJECT ( config_infiniband ); - -/* Drag in IPoIB */ -REQUIRE_OBJECT ( ipoib ); From 9154d7a65c4fea575ff684bd08dc54db814f513c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2016 00:26:56 +0000 Subject: [PATCH 119/591] [eoib] Add Ethernet over Infiniband (EoIB) driver EoIB is a fairly simple protocol in which raw Ethernet frames (excluding the CRC) are encapsulated within Infiniband Unreliable Datagrams, with a four-byte fixed EoIB header (which conveys no actual information). The Ethernet broadcast domain is provided by a multicast group, similar to the IPoIB IPv4 multicast group. The mapping from Ethernet MAC addresses to Infiniband address vectors is achieved by snooping incoming traffic and building a peer cache which can then be used to map a MAC address into a port GID. The address vector is completed using a path record lookup, as for IPoIB. Note that this requires every packet to include a GRH. Add basic support for EoIB devices. This driver is substantially derived from the IPoIB driver. There is currently no mechanism for automatically creating EoIB devices. Signed-off-by: Michael Brown --- src/drivers/net/eoib.c | 754 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/eoib.h | 60 +++ src/include/ipxe/errfile.h | 1 + 3 files changed, 815 insertions(+) create mode 100644 src/drivers/net/eoib.c create mode 100644 src/include/ipxe/eoib.h diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c new file mode 100644 index 000000000..c5b2f5c7f --- /dev/null +++ b/src/drivers/net/eoib.c @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Ethernet over Infiniband + * + */ + +/** Number of EoIB send work queue entries */ +#define EOIB_NUM_SEND_WQES 8 + +/** Number of EoIB receive work queue entries */ +#define EOIB_NUM_RECV_WQES 4 + +/** Number of EoIB completion queue entries */ +#define EOIB_NUM_CQES 16 + +/** Link status for "broadcast join in progress" */ +#define EINPROGRESS_JOINING __einfo_error ( EINFO_EINPROGRESS_JOINING ) +#define EINFO_EINPROGRESS_JOINING __einfo_uniqify \ + ( EINFO_EINPROGRESS, 0x01, "Joining" ) + +/** Human-readable message for the link status */ +struct errortab eoib_errors[] __errortab = { + __einfo_errortab ( EINFO_EINPROGRESS_JOINING ), +}; + +/** List of EoIB devices */ +static LIST_HEAD ( eoib_devices ); + +static struct net_device_operations eoib_operations; + +/**************************************************************************** + * + * EoIB peer cache + * + **************************************************************************** + */ + +/** An EoIB peer cache entry */ +struct eoib_peer { + /** List of EoIB peer cache entries */ + struct list_head list; + /** Ethernet MAC */ + uint8_t mac[ETH_ALEN]; + /** Infiniband address vector */ + struct ib_address_vector av; +}; + +/** + * Find EoIB peer cache entry + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @ret peer EoIB peer, or NULL if not found + */ +static struct eoib_peer * eoib_find_peer ( struct eoib_device *eoib, + const uint8_t *mac ) { + struct eoib_peer *peer; + + /* Find peer cache entry */ + list_for_each_entry ( peer, &eoib->peers, list ) { + if ( memcmp ( mac, peer->mac, sizeof ( peer->mac ) ) == 0 ) { + /* Move peer to start of list */ + list_del ( &peer->list ); + list_add ( &peer->list, &eoib->peers ); + return peer; + } + } + + return NULL; +} + +/** + * Create EoIB peer cache entry + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @ret peer EoIB peer, or NULL on error + */ +static struct eoib_peer * eoib_create_peer ( struct eoib_device *eoib, + const uint8_t *mac ) { + struct eoib_peer *peer; + + /* Allocate and initialise peer cache entry */ + peer = zalloc ( sizeof ( *peer ) ); + if ( peer ) { + memcpy ( peer->mac, mac, sizeof ( peer->mac ) ); + list_add ( &peer->list, &eoib->peers ); + } + return peer; +} + +/** + * Flush EoIB peer cache + * + * @v eoib EoIB device + */ +static void eoib_flush_peers ( struct eoib_device *eoib ) { + struct eoib_peer *peer; + struct eoib_peer *tmp; + + list_for_each_entry_safe ( peer, tmp, &eoib->peers, list ) { + list_del ( &peer->list ); + free ( peer ); + } +} + +/** + * Discard some entries from the peer cache + * + * @ret discarded Number of cached items discarded + */ +static unsigned int eoib_discard ( void ) { + struct net_device *netdev; + struct eoib_device *eoib; + struct eoib_peer *peer; + unsigned int discarded = 0; + + /* Try to discard one cache entry for each EoIB device */ + for_each_netdev ( netdev ) { + + /* Skip non-EoIB devices */ + if ( netdev->op != &eoib_operations ) + continue; + eoib = netdev->priv; + + /* Discard least recently used cache entry (if any) */ + list_for_each_entry_reverse ( peer, &eoib->peers, list ) { + list_del ( &peer->list ); + free ( peer ); + discarded++; + break; + } + } + + return discarded; +} + +/** EoIB cache discarder */ +struct cache_discarder eoib_discarder __cache_discarder ( CACHE_EXPENSIVE ) = { + .discard = eoib_discard, +}; + +/** + * Find destination address vector + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @ret av Address vector, or NULL to send as broadcast + */ +static struct ib_address_vector * eoib_tx_av ( struct eoib_device *eoib, + const uint8_t *mac ) { + struct ib_device *ibdev = eoib->ibdev; + struct eoib_peer *peer; + int rc; + + /* If this is a broadcast or multicast MAC address, then send + * this packet as a broadcast. + */ + if ( is_multicast_ether_addr ( mac ) ) { + DBGCP ( eoib, "EoIB %s %s TX multicast\n", + eoib->name, eth_ntoa ( mac ) ); + return NULL; + } + + /* If we have no peer cache entry, then create one and send + * this packet as a broadcast. + */ + peer = eoib_find_peer ( eoib, mac ); + if ( ! peer ) { + DBGC ( eoib, "EoIB %s %s TX unknown\n", + eoib->name, eth_ntoa ( mac ) ); + eoib_create_peer ( eoib, mac ); + return NULL; + } + + /* If we have not yet recorded a received GID and QPN for this + * peer cache entry, then send this packet as a broadcast. + */ + if ( ! peer->av.gid_present ) { + DBGCP ( eoib, "EoIB %s %s TX not yet recorded\n", + eoib->name, eth_ntoa ( mac ) ); + return NULL; + } + + /* If we have not yet resolved a path to this peer, then send + * this packet as a broadcast. + */ + if ( ( rc = ib_resolve_path ( ibdev, &peer->av ) ) != 0 ) { + DBGCP ( eoib, "EoIB %s %s TX not yet resolved\n", + eoib->name, eth_ntoa ( mac ) ); + return NULL; + } + + /* Force use of GRH even for local destinations */ + peer->av.gid_present = 1; + + /* We have a fully resolved peer: send this packet as a + * unicast. + */ + DBGCP ( eoib, "EoIB %s %s TX " IB_GID_FMT " QPN %#lx\n", eoib->name, + eth_ntoa ( mac ), IB_GID_ARGS ( &peer->av.gid ), peer->av.qpn ); + return &peer->av; +} + +/** + * Record source address vector + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @v lid Infiniband LID + */ +static void eoib_rx_av ( struct eoib_device *eoib, const uint8_t *mac, + const struct ib_address_vector *av ) { + const union ib_gid *gid = &av->gid; + unsigned long qpn = av->qpn; + struct eoib_peer *peer; + + /* Sanity checks */ + if ( ! av->gid_present ) { + DBGC ( eoib, "EoIB %s %s RX with no GID\n", + eoib->name, eth_ntoa ( mac ) ); + return; + } + + /* Find peer cache entry (if any) */ + peer = eoib_find_peer ( eoib, mac ); + if ( ! peer ) { + DBGCP ( eoib, "EoIB %s %s RX " IB_GID_FMT " (ignored)\n", + eoib->name, eth_ntoa ( mac ), IB_GID_ARGS ( gid ) ); + return; + } + + /* Do nothing if peer cache entry is complete and correct */ + if ( ( peer->av.lid == av->lid ) && ( peer->av.qpn == qpn ) ) { + DBGCP ( eoib, "EoIB %s %s RX unchanged\n", + eoib->name, eth_ntoa ( mac ) ); + return; + } + + /* Update peer cache entry */ + peer->av.qpn = qpn; + peer->av.qkey = eoib->broadcast.qkey; + peer->av.gid_present = 1; + memcpy ( &peer->av.gid, gid, sizeof ( peer->av.gid ) ); + DBGC ( eoib, "EoIB %s %s RX " IB_GID_FMT " QPN %#lx\n", eoib->name, + eth_ntoa ( mac ), IB_GID_ARGS ( &peer->av.gid ), peer->av.qpn ); +} + +/**************************************************************************** + * + * EoIB network device + * + **************************************************************************** + */ + +/** + * Transmit packet via EoIB network device + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int eoib_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct eoib_device *eoib = netdev->priv; + struct eoib_header *eoib_hdr; + struct ethhdr *ethhdr; + struct ib_address_vector *av; + size_t zlen; + + /* Sanity checks */ + assert ( iob_len ( iobuf ) >= sizeof ( *ethhdr ) ); + assert ( iob_headroom ( iobuf ) >= sizeof ( *eoib_hdr ) ); + + /* Look up destination address vector */ + ethhdr = iobuf->data; + av = eoib_tx_av ( eoib, ethhdr->h_dest ); + + /* Prepend EoIB header */ + eoib_hdr = iob_push ( iobuf, sizeof ( *eoib_hdr ) ); + eoib_hdr->magic = htons ( EOIB_MAGIC ); + eoib_hdr->reserved = 0; + + /* Pad buffer to minimum Ethernet frame size */ + zlen = ( sizeof ( *eoib_hdr ) + ETH_ZLEN ); + assert ( zlen <= IOB_ZLEN ); + if ( iob_len ( iobuf ) < zlen ) + iob_pad ( iobuf, zlen ); + + /* If we have no unicast address then send as a broadcast */ + if ( ! av ) + av = &eoib->broadcast; + + /* Post send work queue entry */ + return ib_post_send ( eoib->ibdev, eoib->qp, av, iobuf ); +} + +/** + * Handle EoIB send completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void eoib_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ) { + struct eoib_device *eoib = ib_qp_get_ownerdata ( qp ); + + netdev_tx_complete_err ( eoib->netdev, iobuf, rc ); +} + +/** + * Handle EoIB receive completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v dest Destination address vector, or NULL + * @v source Source address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void eoib_complete_recv ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct ib_address_vector *dest __unused, + struct ib_address_vector *source, + struct io_buffer *iobuf, int rc ) { + struct eoib_device *eoib = ib_qp_get_ownerdata ( qp ); + struct net_device *netdev = eoib->netdev; + struct eoib_header *eoib_hdr; + struct ethhdr *ethhdr; + + /* Record errors */ + if ( rc != 0 ) { + netdev_rx_err ( netdev, iobuf, rc ); + return; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *eoib_hdr ) + sizeof ( *ethhdr ) )){ + DBGC ( eoib, "EoIB %s received packet too short to " + "contain EoIB and Ethernet headers\n", eoib->name ); + DBGC_HD ( eoib, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + return; + } + if ( ! source ) { + DBGC ( eoib, "EoIB %s received packet without address " + "vector\n", eoib->name ); + netdev_rx_err ( netdev, iobuf, -ENOTTY ); + return; + } + + /* Strip EoIB header */ + iob_pull ( iobuf, sizeof ( *eoib_hdr ) ); + + /* Update neighbour cache entry, if any */ + ethhdr = iobuf->data; + eoib_rx_av ( eoib, ethhdr->h_source, source ); + + /* Hand off to network layer */ + netdev_rx ( netdev, iobuf ); +} + +/** EoIB completion operations */ +static struct ib_completion_queue_operations eoib_cq_op = { + .complete_send = eoib_complete_send, + .complete_recv = eoib_complete_recv, +}; + +/** EoIB queue pair operations */ +static struct ib_queue_pair_operations eoib_qp_op = { + .alloc_iob = alloc_iob, +}; + +/** + * Poll EoIB network device + * + * @v netdev Network device + */ +static void eoib_poll ( struct net_device *netdev ) { + struct eoib_device *eoib = netdev->priv; + struct ib_device *ibdev = eoib->ibdev; + + /* Poll Infiniband device */ + ib_poll_eq ( ibdev ); + + /* Poll the retry timers (required for EoIB multicast join) */ + retry_poll(); +} + +/** + * Handle EoIB broadcast multicast group join completion + * + * @v membership Multicast group membership + * @v rc Status code + */ +static void eoib_join_complete ( struct ib_mc_membership *membership, int rc ) { + struct eoib_device *eoib = + container_of ( membership, struct eoib_device, membership ); + + /* Record join status as link status */ + netdev_link_err ( eoib->netdev, rc ); +} + +/** + * Join EoIB broadcast multicast group + * + * @v eoib EoIB device + * @ret rc Return status code + */ +static int eoib_join_broadcast_group ( struct eoib_device *eoib ) { + int rc; + + /* Join multicast group */ + if ( ( rc = ib_mcast_join ( eoib->ibdev, eoib->qp, + &eoib->membership, &eoib->broadcast, 0, + eoib_join_complete ) ) != 0 ) { + DBGC ( eoib, "EoIB %s could not join broadcast group: %s\n", + eoib->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Leave EoIB broadcast multicast group + * + * @v eoib EoIB device + */ +static void eoib_leave_broadcast_group ( struct eoib_device *eoib ) { + + /* Leave multicast group */ + ib_mcast_leave ( eoib->ibdev, eoib->qp, &eoib->membership ); +} + +/** + * Handle link status change + * + * @v eoib EoIB device + */ +static void eoib_link_state_changed ( struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + struct ib_device *ibdev = eoib->ibdev; + int rc; + + /* Leave existing broadcast group */ + if ( eoib->qp ) + eoib_leave_broadcast_group ( eoib ); + + /* Update broadcast GID based on potentially-new partition key */ + eoib->broadcast.gid.words[2] = htons ( ibdev->pkey | IB_PKEY_FULL ); + + /* Set net device link state to reflect Infiniband link state */ + rc = ib_link_rc ( ibdev ); + netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) ); + + /* Join new broadcast group */ + if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && eoib->qp && + ( ( rc = eoib_join_broadcast_group ( eoib ) ) != 0 ) ) { + DBGC ( eoib, "EoIB %s could not rejoin broadcast group: " + "%s\n", eoib->name, strerror ( rc ) ); + netdev_link_err ( netdev, rc ); + return; + } +} + +/** + * Open EoIB network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int eoib_open ( struct net_device *netdev ) { + struct eoib_device *eoib = netdev->priv; + struct ib_device *ibdev = eoib->ibdev; + int rc; + + /* Open IB device */ + if ( ( rc = ib_open ( ibdev ) ) != 0 ) { + DBGC ( eoib, "EoIB %s could not open %s: %s\n", + eoib->name, ibdev->name, strerror ( rc ) ); + goto err_ib_open; + } + + /* Allocate completion queue */ + eoib->cq = ib_create_cq ( ibdev, EOIB_NUM_CQES, &eoib_cq_op ); + if ( ! eoib->cq ) { + DBGC ( eoib, "EoIB %s could not allocate completion queue\n", + eoib->name ); + rc = -ENOMEM; + goto err_create_cq; + } + + /* Allocate queue pair */ + eoib->qp = ib_create_qp ( ibdev, IB_QPT_UD, EOIB_NUM_SEND_WQES, + eoib->cq, EOIB_NUM_RECV_WQES, eoib->cq, + &eoib_qp_op, netdev->name ); + if ( ! eoib->qp ) { + DBGC ( eoib, "EoIB %s could not allocate queue pair\n", + eoib->name ); + rc = -ENOMEM; + goto err_create_qp; + } + ib_qp_set_ownerdata ( eoib->qp, eoib ); + + /* Fill receive rings */ + ib_refill_recv ( ibdev, eoib->qp ); + + /* Fake a link status change to join the broadcast group */ + eoib_link_state_changed ( eoib ); + + return 0; + + ib_destroy_qp ( ibdev, eoib->qp ); + eoib->qp = NULL; + err_create_qp: + ib_destroy_cq ( ibdev, eoib->cq ); + eoib->cq = NULL; + err_create_cq: + ib_close ( ibdev ); + err_ib_open: + return rc; +} + +/** + * Close EoIB network device + * + * @v netdev Network device + */ +static void eoib_close ( struct net_device *netdev ) { + struct eoib_device *eoib = netdev->priv; + struct ib_device *ibdev = eoib->ibdev; + + /* Flush peer cache */ + eoib_flush_peers ( eoib ); + + /* Leave broadcast group */ + eoib_leave_broadcast_group ( eoib ); + + /* Tear down the queues */ + ib_destroy_qp ( ibdev, eoib->qp ); + eoib->qp = NULL; + ib_destroy_cq ( ibdev, eoib->cq ); + eoib->cq = NULL; + + /* Close IB device */ + ib_close ( ibdev ); +} + +/** EoIB network device operations */ +static struct net_device_operations eoib_operations = { + .open = eoib_open, + .close = eoib_close, + .transmit = eoib_transmit, + .poll = eoib_poll, +}; + +/** + * Create EoIB device + * + * @v ibdev Infiniband device + * @v hw_addr Ethernet MAC + * @v broadcast Broadcast address vector + * @v name Interface name (or NULL to use default) + * @ret rc Return status code + */ +int eoib_create ( struct ib_device *ibdev, const uint8_t *hw_addr, + struct ib_address_vector *broadcast, const char *name ) { + struct net_device *netdev; + struct eoib_device *eoib; + int rc; + + /* Allocate network device */ + netdev = alloc_etherdev ( sizeof ( *eoib ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &eoib_operations ); + eoib = netdev->priv; + netdev->dev = ibdev->dev; + eoib->netdev = netdev; + eoib->ibdev = ibdev_get ( ibdev ); + memcpy ( &eoib->broadcast, broadcast, sizeof ( eoib->broadcast ) ); + INIT_LIST_HEAD ( &eoib->peers ); + + /* Set MAC address */ + memcpy ( netdev->hw_addr, hw_addr, ETH_ALEN ); + + /* Set interface name, if applicable */ + if ( name ) + snprintf ( netdev->name, sizeof ( netdev->name ), "%s", name ); + eoib->name = netdev->name; + + /* Add to list of EoIB devices */ + list_add_tail ( &eoib->list, &eoib_devices ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + DBGC ( eoib, "EoIB %s created for %s MAC %s\n", + eoib->name, ibdev->name, eth_ntoa ( hw_addr ) ); + DBGC ( eoib, "EoIB %s broadcast GID " IB_GID_FMT "\n", + eoib->name, IB_GID_ARGS ( &broadcast->gid ) ); + return 0; + + unregister_netdev ( netdev ); + err_register: + list_del ( &eoib->list ); + ibdev_put ( ibdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Find EoIB device + * + * @v ibdev Infiniband device + * @v hw_addr Original Ethernet MAC + * @ret eoib EoIB device + */ +struct eoib_device * eoib_find ( struct ib_device *ibdev, + const uint8_t *hw_addr ) { + struct eoib_device *eoib; + + list_for_each_entry ( eoib, &eoib_devices, list ) { + if ( ( eoib->ibdev == ibdev ) && + ( memcmp ( eoib->netdev->hw_addr, hw_addr, + ETH_ALEN ) == 0 ) ) + return eoib; + } + return NULL; +} + +/** + * Remove EoIB device + * + * @v eoib EoIB device + */ +void eoib_destroy ( struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Remove from list of network devices */ + list_del ( &eoib->list ); + + /* Drop reference to Infiniband device */ + ibdev_put ( eoib->ibdev ); + + /* Free network device */ + DBGC ( eoib, "EoIB %s destroyed\n", eoib->name ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** + * Probe EoIB device + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int eoib_probe ( struct ib_device *ibdev __unused ) { + + /* EoIB devices are not created automatically */ + return 0; +} + +/** + * Handle device or link status change + * + * @v ibdev Infiniband device + */ +static void eoib_notify ( struct ib_device *ibdev ) { + struct eoib_device *eoib; + + /* Handle link status change for any attached EoIB devices */ + list_for_each_entry ( eoib, &eoib_devices, list ) { + if ( eoib->ibdev != ibdev ) + continue; + eoib_link_state_changed ( eoib ); + } +} + +/** + * Remove EoIB device + * + * @v ibdev Infiniband device + */ +static void eoib_remove ( struct ib_device *ibdev ) { + struct eoib_device *eoib; + struct eoib_device *tmp; + + /* Remove any attached EoIB devices */ + list_for_each_entry_safe ( eoib, tmp, &eoib_devices, list ) { + if ( eoib->ibdev != ibdev ) + continue; + eoib_destroy ( eoib ); + } +} + +/** EoIB driver */ +struct ib_driver eoib_driver __ib_driver = { + .name = "EoIB", + .probe = eoib_probe, + .notify = eoib_notify, + .remove = eoib_remove, +}; diff --git a/src/include/ipxe/eoib.h b/src/include/ipxe/eoib.h new file mode 100644 index 000000000..c53880b5c --- /dev/null +++ b/src/include/ipxe/eoib.h @@ -0,0 +1,60 @@ +#ifndef _IPXE_EOIB_H +#define _IPXE_EOIB_H + +/** @file + * + * Ethernet over Infiniband + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** An EoIB header */ +struct eoib_header { + /** Signature */ + uint16_t magic; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** EoIB magic signature */ +#define EOIB_MAGIC 0x8919 + +/** An EoIB device */ +struct eoib_device { + /** Name */ + const char *name; + /** Network device */ + struct net_device *netdev; + /** Underlying Infiniband device */ + struct ib_device *ibdev; + /** List of EoIB devices */ + struct list_head list; + /** Broadcast address */ + struct ib_address_vector broadcast; + + /** Completion queue */ + struct ib_completion_queue *cq; + /** Queue pair */ + struct ib_queue_pair *qp; + /** Broadcast group membership */ + struct ib_mc_membership membership; + + /** Peer cache */ + struct list_head peers; +}; + +extern int eoib_create ( struct ib_device *ibdev, const uint8_t *hw_addr, + struct ib_address_vector *broadcast, + const char *name ); +extern struct eoib_device * eoib_find ( struct ib_device *ibdev, + const uint8_t *hw_addr ); +extern void eoib_destroy ( struct eoib_device *eoib ); + +#endif /* _IPXE_EOIB_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 65b4d9c91..e64ccc39c 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -185,6 +185,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_intelxvf ( ERRFILE_DRIVER | 0x00790000 ) #define ERRFILE_smsc95xx ( ERRFILE_DRIVER | 0x007a0000 ) #define ERRFILE_acm ( ERRFILE_DRIVER | 0x007b0000 ) +#define ERRFILE_eoib ( ERRFILE_DRIVER | 0x007c0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From ecd93cfc116dd95864634b837e2b8dff09ae5dce Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2016 00:40:38 +0000 Subject: [PATCH 120/591] [eoib] Silently ignore EoIB heartbeat packets Some EoIB implementations transmit a vendor-proprietary heartbeat packet on the same multicast group used to provide the EoIB broadcast domain. Silently ignore these heartbeat packets, to avoid cluttering up the network interface error statistics. Signed-off-by: Michael Brown --- src/drivers/net/eoib.c | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c index c5b2f5c7f..44eed4cc5 100644 --- a/src/drivers/net/eoib.c +++ b/src/drivers/net/eoib.c @@ -752,3 +752,48 @@ struct ib_driver eoib_driver __ib_driver = { .notify = eoib_notify, .remove = eoib_remove, }; + +/**************************************************************************** + * + * EoIB heartbeat packets + * + **************************************************************************** + */ + +/** + * Silently ignore incoming EoIB heartbeat packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @v flags Packet flags + * @ret rc Return status code + */ +static int eoib_heartbeat_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + const void *ll_dest __unused, + const void *ll_source __unused, + unsigned int flags __unused ) { + free_iob ( iobuf ); + return 0; +} + +/** + * Transcribe EoIB heartbeat address + * + * @v net_addr EoIB heartbeat address + * @ret string "" + * + * This operation is meaningless for the EoIB heartbeat protocol. + */ +static const char * eoib_heartbeat_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** EoIB heartbeat network protocol */ +struct net_protocol eoib_heartbeat_protocol __net_protocol = { + .name = "EoIB", + .net_proto = htons ( EOIB_MAGIC ), + .rx = eoib_heartbeat_rx, + .ntoa = eoib_heartbeat_ntoa, +}; From 1a9ed68cbbbe7280fe85fe7a8c06748134130db3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2016 00:45:09 +0000 Subject: [PATCH 121/591] [eoib] Allow the multicast group to be forcefully created Some EoIB implementations require each individual EoIB node to create the multicast group for the EoIB broadcast domain. It is left as an exercise for the interested reader to determine how such an implementation might ever allow the parameters of such a multicast group to be changed without requiring a simultaneous upgrade of every driver on every operating system on every machine currently attached to the fabric. Signed-off-by: Michael Brown --- src/drivers/net/eoib.c | 4 ++-- src/include/ipxe/eoib.h | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c index 44eed4cc5..a5f9e29ee 100644 --- a/src/drivers/net/eoib.c +++ b/src/drivers/net/eoib.c @@ -454,8 +454,8 @@ static int eoib_join_broadcast_group ( struct eoib_device *eoib ) { /* Join multicast group */ if ( ( rc = ib_mcast_join ( eoib->ibdev, eoib->qp, - &eoib->membership, &eoib->broadcast, 0, - eoib_join_complete ) ) != 0 ) { + &eoib->membership, &eoib->broadcast, + eoib->mask, eoib_join_complete ) ) != 0 ) { DBGC ( eoib, "EoIB %s could not join broadcast group: %s\n", eoib->name, strerror ( rc ) ); return rc; diff --git a/src/include/ipxe/eoib.h b/src/include/ipxe/eoib.h index c53880b5c..acae542b6 100644 --- a/src/include/ipxe/eoib.h +++ b/src/include/ipxe/eoib.h @@ -48,8 +48,29 @@ struct eoib_device { /** Peer cache */ struct list_head peers; + + /** Multicast group additional component mask */ + unsigned int mask; }; +/** + * Force creation of multicast group + * + * @v eoib EoIB device + */ +static inline void eoib_force_group_creation ( struct eoib_device *eoib ) { + + /* Some dubious EoIB implementations require each endpoint to + * force the creation of the multicast group. Yes, this makes + * it impossible for the group parameters (e.g. SL) to ever be + * modified without breaking backwards compatiblity with every + * existing driver. + */ + eoib->mask = ( IB_SA_MCMEMBER_REC_PKEY | IB_SA_MCMEMBER_REC_QKEY | + IB_SA_MCMEMBER_REC_SL | IB_SA_MCMEMBER_REC_FLOW_LABEL | + IB_SA_MCMEMBER_REC_TRAFFIC_CLASS ); +} + extern int eoib_create ( struct ib_device *ibdev, const uint8_t *hw_addr, struct ib_address_vector *broadcast, const char *name ); From 3144e4fb646e7c03236b5f03c98dedc8eff210d6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2016 00:51:08 +0000 Subject: [PATCH 122/591] [eoib] Support non-FullMember gateway devices Some EoIB implementations utilise an EoIB-to-Ethernet gateway device that does not perform a FullMember join to the multicast group for the EoIB broadcast domain. This has various exciting side-effects, such as requiring every EoIB node to send every broadcast packet twice. As an added bonus, the gateway may also break the EoIB MAC address to GID mapping protocol by sending Ethernet-sourced packets from the wrong QPN. Signed-off-by: Michael Brown --- src/drivers/net/eoib.c | 100 +++++++++++++++++++++++++++++++++++++++- src/include/ipxe/eoib.h | 22 +++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c index a5f9e29ee..f58e74b7d 100644 --- a/src/drivers/net/eoib.c +++ b/src/drivers/net/eoib.c @@ -269,6 +269,14 @@ static void eoib_rx_av ( struct eoib_device *eoib, const uint8_t *mac, return; } + /* Some dubious EoIB implementations utilise an Ethernet-to- + * EoIB gateway that will send packets from the wrong QPN. + */ + if ( eoib_has_gateway ( eoib ) && + ( memcmp ( gid, &eoib->gateway.gid, sizeof ( *gid ) ) == 0 ) ) { + qpn = eoib->gateway.qpn; + } + /* Do nothing if peer cache entry is complete and correct */ if ( ( peer->av.lid == av->lid ) && ( peer->av.qpn == qpn ) ) { DBGCP ( eoib, "EoIB %s %s RX unchanged\n", @@ -326,9 +334,14 @@ static int eoib_transmit ( struct net_device *netdev, if ( iob_len ( iobuf ) < zlen ) iob_pad ( iobuf, zlen ); - /* If we have no unicast address then send as a broadcast */ - if ( ! av ) + /* If we have no unicast address then send as a broadcast, + * with a duplicate sent to the gateway if applicable. + */ + if ( ! av ) { av = &eoib->broadcast; + if ( eoib_has_gateway ( eoib ) ) + eoib->duplicate ( eoib, iobuf ); + } /* Post send work queue entry */ return ib_post_send ( eoib->ibdev, eoib->qp, av, iobuf ); @@ -797,3 +810,86 @@ struct net_protocol eoib_heartbeat_protocol __net_protocol = { .rx = eoib_heartbeat_rx, .ntoa = eoib_heartbeat_ntoa, }; + +/**************************************************************************** + * + * EoIB gateway + * + **************************************************************************** + * + * Some dubious EoIB implementations require all broadcast traffic to + * be sent twice: once to the actual broadcast group, and once as a + * unicast to the EoIB-to-Ethernet gateway. This somewhat curious + * design arises since the EoIB-to-Ethernet gateway hardware lacks the + * ability to attach a queue pair to a multicast GID (or LID), and so + * cannot receive traffic sent to the broadcast group. + * + */ + +/** + * Transmit duplicate packet to the EoIB gateway + * + * @v eoib EoIB device + * @v original Original I/O buffer + */ +static void eoib_duplicate ( struct eoib_device *eoib, + struct io_buffer *original ) { + struct net_device *netdev = eoib->netdev; + struct ib_device *ibdev = eoib->ibdev; + struct ib_address_vector *av = &eoib->gateway; + size_t len = iob_len ( original ); + struct io_buffer *copy; + int rc; + + /* Create copy of I/O buffer */ + copy = alloc_iob ( len ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_alloc; + } + memcpy ( iob_put ( copy, len ), original->data, len ); + + /* Append to network device's transmit queue */ + list_add_tail ( ©->list, &original->list ); + + /* Resolve path to gateway */ + if ( ( rc = ib_resolve_path ( ibdev, av ) ) != 0 ) { + DBGC ( eoib, "EoIB %s no path to gateway: %s\n", + eoib->name, strerror ( rc ) ); + goto err_path; + } + + /* Force use of GRH even for local destinations */ + av->gid_present = 1; + + /* Post send work queue entry */ + if ( ( rc = ib_post_send ( eoib->ibdev, eoib->qp, av, copy ) ) != 0 ) + goto err_post_send; + + return; + + err_post_send: + err_path: + err_alloc: + netdev_tx_complete_err ( netdev, copy, rc ); +} + +/** + * Set EoIB gateway + * + * @v eoib EoIB device + * @v av Address vector, or NULL to clear gateway + */ +void eoib_set_gateway ( struct eoib_device *eoib, + struct ib_address_vector *av ) { + + if ( av ) { + DBGC ( eoib, "EoIB %s using gateway " IB_GID_FMT "\n", + eoib->name, IB_GID_ARGS ( &av->gid ) ); + memcpy ( &eoib->gateway, av, sizeof ( eoib->gateway ) ); + eoib->duplicate = eoib_duplicate; + } else { + DBGC ( eoib, "EoIB %s not using gateway\n", eoib->name ); + eoib->duplicate = NULL; + } +} diff --git a/src/include/ipxe/eoib.h b/src/include/ipxe/eoib.h index acae542b6..93f496c36 100644 --- a/src/include/ipxe/eoib.h +++ b/src/include/ipxe/eoib.h @@ -49,10 +49,30 @@ struct eoib_device { /** Peer cache */ struct list_head peers; + /** Send duplicate packet to gateway (or NULL) + * + * @v eoib EoIB device + * @v original Original I/O buffer + */ + void ( * duplicate ) ( struct eoib_device *eoib, + struct io_buffer *original ); + /** Gateway (if any) */ + struct ib_address_vector gateway; /** Multicast group additional component mask */ unsigned int mask; }; +/** + * Check if EoIB device uses a gateway + * + * @v eoib EoIB device + * @v has_gw EoIB device uses a gateway + */ +static inline int eoib_has_gateway ( struct eoib_device *eoib ) { + + return ( eoib->duplicate != NULL ); +} + /** * Force creation of multicast group * @@ -77,5 +97,7 @@ extern int eoib_create ( struct ib_device *ibdev, const uint8_t *hw_addr, extern struct eoib_device * eoib_find ( struct ib_device *ibdev, const uint8_t *hw_addr ); extern void eoib_destroy ( struct eoib_device *eoib ); +extern void eoib_set_gateway ( struct eoib_device *eoib, + struct ib_address_vector *av ); #endif /* _IPXE_EOIB_H */ From e44f6dcb8994a3c5c29b16aacd97ac095f4588c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Mar 2016 00:27:15 +0000 Subject: [PATCH 123/591] [xsigo] Add support for Xsigo virtual Ethernet (XVE) EoIB devices Add support for EoIB devices as implemented by Xsigo. Based on the public (but out-of-tree) Linux kernel drivers at https://oss.oracle.com/git/?p=linux-uek.git;a=log;h=v4.1.12-32.2.1 Signed-off-by: Michael Brown --- src/config/config_infiniband.c | 3 + src/config/general.h | 1 + src/include/ipxe/errfile.h | 1 + src/include/ipxe/xsigo.h | 406 +++++++ src/net/infiniband/xsigo.c | 1858 ++++++++++++++++++++++++++++++++ 5 files changed, 2269 insertions(+) create mode 100644 src/include/ipxe/xsigo.h create mode 100644 src/net/infiniband/xsigo.c diff --git a/src/config/config_infiniband.c b/src/config/config_infiniband.c index 9bac3b7c4..4da8fe219 100644 --- a/src/config/config_infiniband.c +++ b/src/config/config_infiniband.c @@ -44,6 +44,9 @@ REQUIRE_OBJECT ( ib_srp ); #ifdef VNIC_IPOIB REQUIRE_OBJECT ( ipoib ); #endif +#ifdef VNIC_XSIGO +REQUIRE_OBJECT ( xsigo ); +#endif /* * Drag in Infiniband-specific commands diff --git a/src/config/general.h b/src/config/general.h index 9c4330399..e9b781fbf 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -158,6 +158,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ #define VNIC_IPOIB /* Infiniband IPoIB virtual NICs */ +//#define VNIC_XSIGO /* Infiniband Xsigo virtual NICs */ /* * Error message tables to include diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e64ccc39c..4129861ad 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -259,6 +259,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_peerdisc ( ERRFILE_NET | 0x00450000 ) #define ERRFILE_peerblk ( ERRFILE_NET | 0x00460000 ) #define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) +#define ERRFILE_xsigo ( ERRFILE_NET | 0x00480000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/xsigo.h b/src/include/ipxe/xsigo.h new file mode 100644 index 000000000..f4f14c487 --- /dev/null +++ b/src/include/ipxe/xsigo.h @@ -0,0 +1,406 @@ +#ifndef _IPXE_XSIGO_H +#define _IPXE_XSIGO_H + +/** @file + * + * Xsigo virtual Ethernet devices + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Xsigo directory service record name */ +#define XDS_SERVICE_NAME "XSIGOXDS" + +/** Xsigo configuration manager service ID */ +#define XCM_SERVICE_ID { 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x97, 0x01 } + +/** Xsigo management class */ +#define XSIGO_MGMT_CLASS 0x0b + +/** Xsigo management class version */ +#define XSIGO_MGMT_CLASS_VERSION 2 + +/** Xsigo configuration manager request MAD */ +#define XSIGO_ATTR_XCM_REQUEST 0xb002 + +/** Generic operating system type */ +#define XSIGO_OS_TYPE_GENERIC 0x40 + +/** Xsigo virtual Ethernet broadcast GID prefix */ +#define XVE_PREFIX 0xff15101cUL + +/** Xsigo resource types */ +enum xsigo_resource_type { + /** Virtual Ethernet resource type */ + XSIGO_RESOURCE_XVE = ( 1 << 6 ), + /** Absence-of-high-availability "resource" type */ + XSIGO_RESOURCE_NO_HA = ( 1 << 4 ), +}; + +/** A Xsigo server identifier */ +struct xsigo_server_id { + /** Virtual machine ID */ + uint32_t vm; + /** Port GUID */ + union ib_guid guid; +} __attribute__ (( packed )); + +/** A Xsigo configuration manager identifier */ +struct xsigo_manager_id { + /** Port GUID */ + union ib_guid guid; + /** LID */ + uint16_t lid; + /** Reserved */ + uint8_t reserved[10]; +} __attribute__ (( packed )); + +/** A Xsigo configuration manager request MAD */ +struct xsigo_managers_request { + /** MAD header */ + struct ib_mad_hdr mad_hdr; + /** Reserved */ + uint8_t reserved0[32]; + /** Server ID */ + struct xsigo_server_id server; + /** Hostname */ + char hostname[ 65 /* Seriously, guys? */ ]; + /** OS version */ + char os_version[32]; + /** CPU architecture */ + char arch[16]; + /** OS type */ + uint8_t os_type; + /** Reserved */ + uint8_t reserved1[3]; + /** Firmware version */ + uint64_t firmware_version; + /** Hardware version */ + uint32_t hardware_version; + /** Driver version */ + uint32_t driver_version; + /** System ID */ + union ib_gid system_id; + /** Resource types */ + uint16_t resources; + /** Reserved */ + uint8_t reserved2[2]; + /** Build version */ + char build[16]; + /** Reserved */ + uint8_t reserved3[19]; +} __attribute__ (( packed )); + +/** Resource types are present */ +#define XSIGO_RESOURCES_PRESENT 0x8000 + +/** A Xsigo configuration manager reply MAD */ +struct xsigo_managers_reply { + /** MAD header */ + struct ib_mad_hdr mad_hdr; + /** Reserved */ + uint8_t reserved0[32]; + /** Server ID */ + struct xsigo_server_id server; + /** Number of XCM records */ + uint8_t count; + /** Version */ + uint8_t version; + /** Reserved */ + uint8_t reserved1[2]; + /** Managers */ + struct xsigo_manager_id manager[8]; + /** Reserved */ + uint8_t reserved2[24]; +} __attribute__ (( packed )); + +/** A Xsigo MAD */ +union xsigo_mad { + /** Generic MAD */ + union ib_mad mad; + /** Configuration manager request */ + struct xsigo_managers_request request; + /** Configuration manager reply */ + struct xsigo_managers_reply reply; +} __attribute__ (( packed )); + +/** An XSMP node identifier */ +struct xsmp_node_id { + /** Auxiliary ID (never used) */ + uint32_t aux; + /** Port GUID */ + union ib_guid guid; +} __attribute__ (( packed )); + +/** An XSMP message header */ +struct xsmp_message_header { + /** Message type */ + uint8_t type; + /** Reason code */ + uint8_t code; + /** Length */ + uint16_t len; + /** Sequence number */ + uint32_t seq; + /** Source node ID */ + struct xsmp_node_id src; + /** Destination node ID */ + struct xsmp_node_id dst; +} __attribute__ (( packed )); + +/** XSMP message types */ +enum xsmp_message_type { + /** Session message type */ + XSMP_TYPE_SESSION = 1, + /** Virtual Ethernet message type */ + XSMP_TYPE_XVE = 6, +}; + +/** An XSMP session message */ +struct xsmp_session_message { + /** Message header */ + struct xsmp_message_header hdr; + /** Message type */ + uint8_t type; + /** Reason code */ + uint8_t code; + /** Length (excluding message header) */ + uint16_t len; + /** Operating system type */ + uint8_t os_type; + /** Reserved */ + uint8_t reserved0; + /** Resource types */ + uint16_t resources; + /** Driver version */ + uint32_t driver_version; + /** Required chassis version */ + uint32_t chassis_version; + /** Boot flags */ + uint32_t boot; + /** Firmware version */ + uint64_t firmware_version; + /** Hardware version */ + uint32_t hardware_version; + /** Vendor part ID */ + uint32_t vendor; + /** Protocol version */ + uint32_t xsmp_version; + /** Chassis name */ + char chassis[32]; + /** Session name */ + char session[32]; + /** Reserved */ + uint8_t reserved1[120]; +} __attribute__ (( packed )); + +/** XSMP session message types */ +enum xsmp_session_type { + /** Keepalive message */ + XSMP_SESSION_TYPE_HELLO = 1, + /** Initial registration message */ + XSMP_SESSION_TYPE_REGISTER = 2, + /** Registration confirmation message */ + XSMP_SESSION_TYPE_CONFIRM = 3, + /** Registration rejection message */ + XSMP_SESSION_TYPE_REJECT = 4, + /** Shutdown message */ + XSMP_SESSION_TYPE_SHUTDOWN = 5, +}; + +/** XSMP boot flags */ +enum xsmp_session_boot { + /** PXE boot */ + XSMP_BOOT_PXE = ( 1 << 0 ), +}; + +/** XSMP virtual Ethernet channel adapter parameters */ +struct xsmp_xve_ca { + /** Subnet prefix (little-endian) */ + union ib_guid prefix_le; + /** Control queue pair number */ + uint32_t ctrl; + /** Data queue pair number */ + uint32_t data; + /** Partition key */ + uint16_t pkey; + /** Queue key */ + uint16_t qkey; +} __attribute__ (( packed )); + +/** XSMP virtual Ethernet MAC address */ +struct xsmp_xve_mac { + /** High 16 bits */ + uint16_t high; + /** Low 32 bits */ + uint32_t low; +} __attribute__ (( packed )); + +/** An XSMP virtual Ethernet message */ +struct xsmp_xve_message { + /** Message header */ + struct xsmp_message_header hdr; + /** Message type */ + uint8_t type; + /** Reason code */ + uint8_t code; + /** Length (excluding message header) */ + uint16_t len; + /** Update bitmask */ + uint32_t update; + /** Resource identifier */ + union ib_guid resource; + /** TCA GUID (little-endian) */ + union ib_guid guid_le; + /** TCA LID */ + uint16_t lid; + /** MAC address (little-endian) */ + struct xsmp_xve_mac mac_le; + /** Rate */ + uint16_t rate; + /** Administrative state (non-zero = "up") */ + uint16_t state; + /** Encapsulation (apparently obsolete and unused) */ + uint16_t encap; + /** MTU */ + uint16_t mtu; + /** Installation flags (apparently obsolete and unused) */ + uint32_t install; + /** Interface name */ + char name[16]; + /** Service level */ + uint16_t sl; + /** Flow control enabled (apparently obsolete and unused) */ + uint16_t flow; + /** Committed rate (in Mbps) */ + uint16_t committed_mbps; + /** Peak rate (in Mbps) */ + uint16_t peak_mbps; + /** Committed burst size (in bytes) */ + uint32_t committed_burst; + /** Peak burst size (in bytes) */ + uint32_t peak_burst; + /** VMware index */ + uint8_t vmware; + /** Reserved */ + uint8_t reserved0; + /** Multipath flags */ + uint16_t multipath; + /** Multipath group name */ + char group[48]; + /** Link aggregation flag */ + uint8_t agg; + /** Link aggregation policy */ + uint8_t policy; + /** Network ID */ + uint32_t network; + /** Mode */ + uint8_t mode; + /** Uplink type */ + uint8_t uplink; + /** Target channel adapter parameters */ + struct xsmp_xve_ca tca; + /** Host channel adapter parameters */ + struct xsmp_xve_ca hca; + /** Reserved */ + uint8_t reserved1[336]; +} __attribute__ (( packed )); + +/** XSMP virtual Ethernet message types */ +enum xsmp_xve_type { + /** Install virtual NIC */ + XSMP_XVE_TYPE_INSTALL = 1, + /** Delete virtual NIC */ + XSMP_XVE_TYPE_DELETE = 2, + /** Update virtual NIC */ + XSMP_XVE_TYPE_UPDATE = 3, + /** Set operational state up */ + XSMP_XVE_TYPE_OPER_UP = 6, + /** Set operational state down */ + XSMP_XVE_TYPE_OPER_DOWN = 7, + /** Get operational state */ + XSMP_XVE_TYPE_OPER_REQ = 15, + /** Virtual NIC is ready */ + XSMP_XVE_TYPE_READY = 20, +}; + +/** XSMP virtual Ethernet message codes */ +enum xsmp_xve_code { + /* Something went wrong */ + XSMP_XVE_CODE_ERROR = 0x84, +}; + +/** XSMP virtual Ethernet update bitmask */ +enum xsmp_xve_update { + /** Update MTU */ + XSMP_XVE_UPDATE_MTU = ( 1 << 2 ), + /** Update administrative state */ + XSMP_XVE_UPDATE_STATE = ( 1 << 6 ), + /** Update gateway to mark as down */ + XSMP_XVE_UPDATE_GW_DOWN = ( 1 << 30 ), + /** Update gateway information */ + XSMP_XVE_UPDATE_GW_CHANGE = ( 1 << 31 ), +}; + +/** XSMP virtual Ethernet modes */ +enum xsmp_xve_mode { + /** Reliable Connected */ + XSMP_XVE_MODE_RC = 1, + /** Unreliable Datagram */ + XSMP_XVE_MODE_UD = 2, +}; + +/** XSMP virtual Ethernet uplink types */ +enum xsmp_xve_uplink { + /** No uplink */ + XSMP_XVE_NO_UPLINK = 1, + /** Has uplink */ + XSMP_XVE_UPLINK = 2, +}; + +/** An XSMP message */ +union xsmp_message { + /** Message header */ + struct xsmp_message_header hdr; + /** Session message */ + struct xsmp_session_message sess; + /** Virtual Ethernet message */ + struct xsmp_xve_message xve; +}; + +/** Delay between attempts to open the Infiniband device + * + * This is a policy decision. + */ +#define XSIGO_OPEN_RETRY_DELAY ( 2 * TICKS_PER_SEC ) + +/** Delay between unsuccessful discovery attempts + * + * This is a policy decision. + */ +#define XSIGO_DISCOVERY_FAILURE_DELAY ( 10 * TICKS_PER_SEC ) + +/** Delay between successful discovery attempts + * + * This is a policy decision. + */ +#define XSIGO_DISCOVERY_SUCCESS_DELAY ( 20 * TICKS_PER_SEC ) + +/** Delay between keepalive requests + * + * This is a policy decision. + */ +#define XSIGO_KEEPALIVE_INTERVAL ( 10 * TICKS_PER_SEC ) + +/** Maximum time to wait for a keepalive response + * + * This is a policy decision. + */ +#define XSIGO_KEEPALIVE_MAX_WAIT ( 2 * TICKS_PER_SEC ) + +#endif /* _IPXE_XSIGO_H */ diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c new file mode 100644 index 000000000..91b7b71f1 --- /dev/null +++ b/src/net/infiniband/xsigo.c @@ -0,0 +1,1858 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Xsigo virtual Ethernet devices + * + */ + +/** A Xsigo device */ +struct xsigo_device { + /** Reference count */ + struct refcnt refcnt; + /** Underlying Infiniband device */ + struct ib_device *ibdev; + /** List of Xsigo devices */ + struct list_head list; + /** Device name */ + const char *name; + + /** Link opener timer */ + struct retry_timer opener; + + /** Discovery timer */ + struct retry_timer discovery; + /** Discovery management transaction (if any) */ + struct ib_mad_transaction *madx; + + /** List of configuration managers */ + struct list_head managers; +}; + +/** A Xsigo configuration manager */ +struct xsigo_manager { + /** Reference count */ + struct refcnt refcnt; + /** Xsigo device */ + struct xsigo_device *xdev; + /** List of managers */ + struct list_head list; + /** Device name */ + char name[16]; + /** Manager ID */ + struct xsigo_manager_id id; + + /** Data transfer interface */ + struct interface xfer; + /** Connection timer */ + struct retry_timer reopen; + /** Keepalive timer */ + struct retry_timer keepalive; + /** Transmission process */ + struct process process; + /** Pending transmissions */ + unsigned int pending; + /** Transmit sequence number */ + uint32_t seq; + + /** List of virtual Ethernet devices */ + struct list_head nics; +}; + +/** Configuration manager pending transmissions */ +enum xsigo_manager_pending { + /** Send connection request */ + XCM_TX_CONNECT = 0x0001, + /** Send registration message */ + XCM_TX_REGISTER = 0x0002, +}; + +/** A Xsigo virtual Ethernet device */ +struct xsigo_nic { + /** Configuration manager */ + struct xsigo_manager *xcm; + /** List of virtual Ethernet devices */ + struct list_head list; + /** Device name */ + char name[16]; + + /** Resource identifier */ + union ib_guid resource; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Network ID */ + unsigned long network; +}; + +/** Configuration manager service ID */ +static union ib_guid xcm_service_id = { + .bytes = XCM_SERVICE_ID, +}; + +/** List of all Xsigo devices */ +static LIST_HEAD ( xsigo_devices ); + +/** + * Free Xsigo device + * + * @v refcnt Reference count + */ +static void xsigo_free ( struct refcnt *refcnt ) { + struct xsigo_device *xdev = + container_of ( refcnt, struct xsigo_device, refcnt ); + + /* Sanity checks */ + assert ( ! timer_running ( &xdev->opener ) ); + assert ( ! timer_running ( &xdev->discovery ) ); + assert ( xdev->madx == NULL ); + assert ( list_empty ( &xdev->managers ) ); + + /* Drop reference to Infiniband device */ + ibdev_put ( xdev->ibdev ); + + /* Free device */ + free ( xdev ); +} + +/** + * Free configuration manager + * + * @v refcnt Reference count + */ +static void xcm_free ( struct refcnt *refcnt ) { + struct xsigo_manager *xcm = + container_of ( refcnt, struct xsigo_manager, refcnt ); + + /* Sanity checks */ + assert ( ! timer_running ( &xcm->reopen ) ); + assert ( ! timer_running ( &xcm->keepalive ) ); + assert ( ! process_running ( &xcm->process ) ); + assert ( list_empty ( &xcm->nics ) ); + + /* Drop reference to Xsigo device */ + ref_put ( &xcm->xdev->refcnt ); + + /* Free manager */ + free ( xcm ); +} + +/**************************************************************************** + * + * Virtual Ethernet (XVE) devices + * + **************************************************************************** + */ + +/** + * Create virtual Ethernet device + * + * @v xcm Configuration manager + * @v resource Resource identifier + * @v mac Ethernet MAC + * @v network Network identifier + * @v name Device name + * @ret rc Return status code + */ +static int xve_create ( struct xsigo_manager *xcm, union ib_guid *resource, + const uint8_t *mac, unsigned long network, + unsigned long qkey, const char *name ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct xsigo_nic *xve; + struct ib_address_vector broadcast; + int rc; + + /* Allocate and initialise structure */ + xve = zalloc ( sizeof ( *xve ) ); + if ( ! xve ) { + rc = -ENOMEM; + goto err_alloc; + } + xve->xcm = xcm; + snprintf ( xve->name, sizeof ( xve->name ), "%s", name ); + memcpy ( &xve->resource, resource, sizeof ( xve->resource ) ); + memcpy ( xve->mac, mac, ETH_ALEN ); + xve->network = network; + DBGC ( xve, "XVE %s created for %s " IB_GUID_FMT "\n", + xve->name, xcm->name, IB_GUID_ARGS ( resource ) ); + DBGC ( xve, "XVE %s is MAC %s on network %ld\n", + xve->name, eth_ntoa ( mac ), network ); + + /* Construct broadcast address vector */ + memset ( &broadcast, 0, sizeof ( broadcast ) ); + broadcast.qpn = IB_QPN_BROADCAST; + broadcast.qkey = qkey; + broadcast.gid_present = 1; + broadcast.gid.dwords[0] = htonl ( XVE_PREFIX ); + broadcast.gid.words[2] = htons ( ibdev->pkey ); + broadcast.gid.dwords[3] = htonl ( network ); + + /* Create EoIB device */ + if ( ( rc = eoib_create ( ibdev, xve->mac, &broadcast, + xve->name ) ) != 0 ) { + DBGC ( xve, "XVE %s could not create EoIB device: %s\n", + xve->name, strerror ( rc ) ); + goto err_create; + } + + /* Add to list of virtual Ethernet devices. Do this only + * after creating the EoIB device, so that our net device + * notifier won't attempt to send an operational state update + * before we have acknowledged the installation. + */ + list_add ( &xve->list, &xcm->nics ); + + return 0; + + list_del ( &xve->list ); + err_create: + free ( xve ); + err_alloc: + return rc; +} + +/** + * Find virtual Ethernet device + * + * @v xcm Configuration manager + * @v resource Resource identifier + * @ret xve Virtual Ethernet device, or NULL + */ +static struct xsigo_nic * xve_find ( struct xsigo_manager *xcm, + union ib_guid *resource ) { + struct xsigo_nic *xve; + + list_for_each_entry ( xve, &xcm->nics, list ) { + if ( memcmp ( resource, &xve->resource, + sizeof ( *resource ) ) == 0 ) + return xve; + } + return NULL; +} + +/** + * Destroy virtual Ethernet device + * + * @v xve Virtual Ethernet device + */ +static void xve_destroy ( struct xsigo_nic *xve ) { + struct xsigo_manager *xcm = xve->xcm; + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct eoib_device *eoib; + + /* Destroy corresponding EoIB device, if any */ + if ( ( eoib = eoib_find ( ibdev, xve->mac ) ) ) + eoib_destroy ( eoib ); + + /* Remove from list of virtual Ethernet devices */ + list_del ( &xve->list ); + + /* Free virtual Ethernet device */ + DBGC ( xve, "XVE %s destroyed\n", xve->name ); + free ( xve ); +} + +/** + * Update virtual Ethernet device MTU + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v mtu New MTU (excluding Ethernet and EoIB headers) + * @ret rc Return status code + */ +static int xve_update_mtu ( struct xsigo_nic *xve, struct eoib_device *eoib, + size_t mtu ) { + struct net_device *netdev = eoib->netdev; + size_t max; + + /* Check that we can support this MTU */ + max = ( IB_MAX_PAYLOAD_SIZE - ( sizeof ( struct ethhdr ) + + sizeof ( struct eoib_header ) ) ); + if ( mtu > max ) { + DBGC ( xve, "XVE %s cannot support MTU %zd (max %zd)\n", + xve->name, mtu, max ); + return -ERANGE; + } + + /* Update MTU. No need to close/reopen the network device, + * since our Infiniband stack uses a fixed MTU anyway. Note + * that the network device sees the Ethernet frame header but + * not the EoIB header. + */ + netdev->max_pkt_len = ( mtu + sizeof ( struct ethhdr ) ); + DBGC ( xve, "XVE %s has MTU %zd\n", xve->name, mtu ); + + return 0; +} + +/** + * Open virtual Ethernet device + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v open New administrative state + * @ret rc Return status code + */ +static int xve_open ( struct xsigo_nic *xve, struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + int rc; + + /* Do nothing if network device is already open */ + if ( netdev_is_open ( netdev ) ) + return 0; + DBGC ( xve, "XVE %s opening network device\n", xve->name ); + + /* Open network device */ + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + DBGC ( xve, "XVE %s could not open: %s\n", + xve->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Close virtual Ethernet device + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + */ +static void xve_close ( struct xsigo_nic *xve, struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + + /* Do nothing if network device is already closed */ + if ( ! netdev_is_open ( netdev ) ) + return; + + /* Close network device */ + netdev_close ( netdev ); + DBGC ( xve, "XVE %s closed network device\n", xve->name ); +} + +/** + * Update virtual Ethernet device administrative state + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v open New administrative state + * @ret rc Return status code + */ +static int xve_update_state ( struct xsigo_nic *xve, struct eoib_device *eoib, + int open ) { + + /* Open or close device, as applicable */ + if ( open ) { + return xve_open ( xve, eoib ); + } else { + xve_close ( xve, eoib ); + return 0; + } +} + +/** + * Update gateway (TCA) + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v av Address vector, or NULL if no gateway + * @ret rc Return status code + */ +static int xve_update_tca ( struct xsigo_nic *xve, struct eoib_device *eoib, + struct ib_address_vector *av ) { + + /* Update gateway address */ + eoib_set_gateway ( eoib, av ); + if ( av ) { + DBGC ( xve, "XVE %s has TCA " IB_GID_FMT " data %#lx qkey " + "%#lx\n", xve->name, IB_GID_ARGS ( &av->gid ), av->qpn, + av->qkey ); + } else { + DBGC ( xve, "XVE %s has no TCA\n", xve->name ); + } + + /* The Linux driver will modify the local device's link state + * to reflect the EoIB-to-Ethernet gateway's link state, but + * this seems philosophically incorrect since communication + * within the EoIB broadcast domain still works regardless of + * the state of the gateway. + */ + + return 0; +} + +/**************************************************************************** + * + * Server management protocol (XSMP) session messages + * + **************************************************************************** + */ + +/** + * Get session message name (for debugging) + * + * @v type Message type + * @ret name Message name + */ +static const char * xsmp_session_type ( unsigned int type ) { + static char buf[16]; + + switch ( type ) { + case XSMP_SESSION_TYPE_HELLO: return "HELLO"; + case XSMP_SESSION_TYPE_REGISTER: return "REGISTER"; + case XSMP_SESSION_TYPE_CONFIRM: return "CONFIRM"; + case XSMP_SESSION_TYPE_REJECT: return "REJECT"; + case XSMP_SESSION_TYPE_SHUTDOWN: return "SHUTDOWN"; + default: + snprintf ( buf, sizeof ( buf ), "UNKNOWN<%d>", type ); + return buf; + } +} + +/** + * Extract chassis name (for debugging) + * + * @v msg Session message + * @ret chassis Chassis name + */ +static const char * xsmp_chassis_name ( struct xsmp_session_message *msg ) { + static char chassis[ sizeof ( msg->chassis ) + 1 /* NUL */ ]; + + memcpy ( chassis, msg->chassis, sizeof ( msg->chassis ) ); + return chassis; +} + +/** + * Extract session name (for debugging) + * + * @v msg Session message + * @ret session Session name + */ +static const char * xsmp_session_name ( struct xsmp_session_message *msg ) { + static char session[ sizeof ( msg->session ) + 1 /* NUL */ ]; + + memcpy ( session, msg->session, sizeof ( msg->session ) ); + return session; +} + +/** + * Send session message + * + * @v xcm Configuration manager + * @v type Message type + * @ret rc Return status code + */ +static int xsmp_tx_session ( struct xsigo_manager *xcm, unsigned int type ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct xsmp_session_message msg; + int rc; + + /* Construct session message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr.type = XSMP_TYPE_SESSION; + msg.hdr.len = htons ( sizeof ( msg ) ); + msg.hdr.seq = htonl ( ++xcm->seq ); + memcpy ( &msg.hdr.src.guid, &ibdev->gid.s.guid, + sizeof ( msg.hdr.src.guid ) ); + memcpy ( &msg.hdr.dst.guid, &xcm->id.guid, + sizeof ( msg.hdr.dst.guid ) ); + msg.type = type; + msg.len = htons ( sizeof ( msg ) - sizeof ( msg.hdr ) ); + msg.os_type = XSIGO_OS_TYPE_GENERIC; + msg.resources = htons ( XSIGO_RESOURCE_XVE | + XSIGO_RESOURCE_NO_HA ); + msg.boot = htonl ( XSMP_BOOT_PXE ); + DBGCP ( xcm, "XCM %s TX[%d] session %s\n", xcm->name, + ntohl ( msg.hdr.seq ), xsmp_session_type ( msg.type ) ); + DBGCP_HDA ( xcm, 0, &msg, sizeof ( msg ) ); + + /* Send session message */ + if ( ( rc = xfer_deliver_raw ( &xcm->xfer, &msg, + sizeof ( msg ) ) ) != 0 ) { + DBGC ( xcm, "XCM %s TX session %s failed: %s\n", xcm->name, + xsmp_session_type ( msg.type ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Send registration message + * + * @v xcm Configuration manager + * @ret rc Return status code + */ +static inline int xsmp_tx_session_register ( struct xsigo_manager *xcm ) { + + DBGC ( xcm, "XCM %s registering with " IB_GUID_FMT "\n", + xcm->name, IB_GUID_ARGS ( &xcm->id.guid ) ); + + /* Send registration message */ + return xsmp_tx_session ( xcm, XSMP_SESSION_TYPE_REGISTER ); +} + +/** + * Send keepalive message + * + * @v xcm Configuration manager + * @ret rc Return status code + */ +static int xsmp_tx_session_hello ( struct xsigo_manager *xcm ) { + + /* Send keepalive message */ + return xsmp_tx_session ( xcm, XSMP_SESSION_TYPE_HELLO ); +} + +/** + * Handle received keepalive message + * + * @v xcm Configuration manager + * @v msg Keepalive message + * @ret rc Return status code + */ +static int xsmp_rx_session_hello ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg __unused ) { + + /* Respond to keepalive message. Note that the XCM doesn't + * seem to actually ever send these. + */ + return xsmp_tx_session_hello ( xcm ); +} + +/** + * Handle received registration confirmation message + * + * @v xcm Configuration manager + * @v msg Registration confirmation message + * @ret rc Return status code + */ +static int xsmp_rx_session_confirm ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGC ( xcm, "XCM %s registered with \"%s\" as \"%s\"\n", xcm->name, + xsmp_chassis_name ( msg ), xsmp_session_name ( msg ) ); + + return 0; +} + +/** + * Handle received registration rejection message + * + * @v xcm Configuration manager + * @v msg Registration confirmation message + * @ret rc Return status code + */ +static int xsmp_rx_session_reject ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGC ( xcm, "XCM %s rejected by \"%s\":\n", + xcm->name, xsmp_chassis_name ( msg ) ); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + return -EPERM; +} + +/** + * Handle received shutdown message + * + * @v xcm Configuration manager + * @v msg Registration confirmation message + * @ret rc Return status code + */ +static int xsmp_rx_session_shutdown ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGC ( xcm, "XCM %s shut down by \"%s\":\n", + xcm->name, xsmp_chassis_name ( msg ) ); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + return -ENOTCONN; +} + +/** + * Handle received session message + * + * @v xcm Configuration manager + * @v msg Session message + * @ret rc Return status code + */ +static int xsmp_rx_session ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGCP ( xcm, "XCM %s RX[%d] session %s\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_session_type ( msg->type ) ); + DBGCP_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + /* Handle message according to type */ + switch ( msg->type ) { + case XSMP_SESSION_TYPE_HELLO: + return xsmp_rx_session_hello ( xcm, msg ); + case XSMP_SESSION_TYPE_CONFIRM: + return xsmp_rx_session_confirm ( xcm, msg ); + case XSMP_SESSION_TYPE_REJECT: + return xsmp_rx_session_reject ( xcm, msg ); + case XSMP_SESSION_TYPE_SHUTDOWN: + return xsmp_rx_session_shutdown ( xcm, msg ); + default: + DBGC ( xcm, "XCM %s RX[%d] session unexpected %s:\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_session_type ( msg->type )); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + return -EPROTO; + } +} + +/**************************************************************************** + * + * Server management protocol (XSMP) virtual Ethernet (XVE) messages + * + **************************************************************************** + */ + +/** + * Get virtual Ethernet message name (for debugging) + * + * @v type Message type + * @ret name Message name + */ +static const char * xsmp_xve_type ( unsigned int type ) { + static char buf[16]; + + switch ( type ) { + case XSMP_XVE_TYPE_INSTALL: return "INSTALL"; + case XSMP_XVE_TYPE_DELETE: return "DELETE"; + case XSMP_XVE_TYPE_UPDATE: return "UPDATE"; + case XSMP_XVE_TYPE_OPER_UP: return "OPER_UP"; + case XSMP_XVE_TYPE_OPER_DOWN: return "OPER_DOWN"; + case XSMP_XVE_TYPE_OPER_REQ: return "OPER_REQ"; + case XSMP_XVE_TYPE_READY: return "READY"; + default: + snprintf ( buf, sizeof ( buf ), "UNKNOWN<%d>", type ); + return buf; + } +} + +/** + * Send virtual Ethernet message + * + * @v xcm Configuration manager + * @v msg Partial message + * @ret rc Return status code + */ +static int xsmp_tx_xve ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + int rc; + + /* Fill in common header fields */ + msg->hdr.type = XSMP_TYPE_XVE; + msg->hdr.len = htons ( sizeof ( *msg ) ); + msg->hdr.seq = htonl ( ++xcm->seq ); + memcpy ( &msg->hdr.src.guid, &ibdev->gid.s.guid, + sizeof ( msg->hdr.src.guid ) ); + memcpy ( &msg->hdr.dst.guid, &xcm->id.guid, + sizeof ( msg->hdr.dst.guid ) ); + msg->len = htons ( sizeof ( *msg ) - sizeof ( msg->hdr ) ); + DBGCP ( xcm, "XCM %s TX[%d] xve %s code %#02x\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_xve_type ( msg->type ), + msg->code ); + DBGCP_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + /* Send virtual Ethernet message */ + if ( ( rc = xfer_deliver_raw ( &xcm->xfer, msg, + sizeof ( *msg ) ) ) != 0 ) { + DBGC ( xcm, "XCM %s TX xve %s failed: %s\n", xcm->name, + xsmp_xve_type ( msg->type ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Send virtual Ethernet message including current device parameters + * + * @v xcm Configuration manager + * @v msg Partial virtual Ethernet message + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @ret rc Return status code + */ +static int xsmp_tx_xve_params ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg, + struct xsigo_nic *xve, + struct eoib_device *eoib ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct net_device *netdev = eoib->netdev; + + /* Set successful response code */ + msg->code = 0; + + /* Include network identifier, MTU, and current HCA parameters */ + msg->network = htonl ( xve->network ); + msg->mtu = htons ( netdev->max_pkt_len - sizeof ( struct ethhdr ) ); + msg->hca.prefix_le.qword = bswap_64 ( ibdev->gid.s.prefix.qword ); + msg->hca.pkey = htons ( ibdev->pkey ); + msg->hca.qkey = msg->tca.qkey; + if ( eoib->qp ) { + msg->hca.data = htonl ( eoib->qp->ext_qpn ); + msg->hca.qkey = htons ( eoib->qp->qkey ); + } + + /* The message type field is (ab)used to return the current + * operational status. + */ + if ( msg->type == XSMP_XVE_TYPE_OPER_REQ ) { + msg->type = ( netdev_is_open ( netdev ) ? + XSMP_XVE_TYPE_OPER_UP : XSMP_XVE_TYPE_OPER_DOWN ); + } + + /* Send message */ + DBGC ( xve, "XVE %s network %d MTU %d ctrl %#x data %#x qkey %#04x " + "%s\n", xve->name, ntohl ( msg->network ), ntohs ( msg->mtu ), + ntohl ( msg->hca.ctrl ), ntohl ( msg->hca.data ), + ntohs ( msg->hca.qkey ), xsmp_xve_type ( msg->type ) ); + + return xsmp_tx_xve ( xcm, msg ); +} + +/** + * Send virtual Ethernet error response + * + * @v xcm Configuration manager + * @v msg Partial virtual Ethernet message + * @ret rc Return status code + */ +static inline int xsmp_tx_xve_nack ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + + /* Set error response code. (There aren't any meaningful + * detailed response codes defined by the wire protocol.) + */ + msg->code = XSMP_XVE_CODE_ERROR; + + /* Send message */ + return xsmp_tx_xve ( xcm, msg ); +} + +/** + * Send virtual Ethernet notification + * + * @v xcm Configuration manager + * @v type Message type + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @ret rc Return status code + */ +static int xsmp_tx_xve_notify ( struct xsigo_manager *xcm, + unsigned int type, + struct xsigo_nic *xve, + struct eoib_device *eoib ) { + struct xsmp_xve_message msg; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.type = type; + memcpy ( &msg.resource, &xve->resource, sizeof ( msg.resource ) ); + + /* Send message */ + return xsmp_tx_xve_params ( xcm, &msg, xve, eoib ); +} + +/** + * Send virtual Ethernet current operational state + * + * @v xcm Configuration manager + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @ret rc Return status code + */ +static inline int xsmp_tx_xve_oper ( struct xsigo_manager *xcm, + struct xsigo_nic *xve, + struct eoib_device *eoib ) { + + /* Send notification */ + return xsmp_tx_xve_notify ( xcm, XSMP_XVE_TYPE_OPER_REQ, xve, eoib ); +} + +/** + * Handle received virtual Ethernet modification message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @v update Update bitmask + * @ret rc Return status code + */ +static int xsmp_rx_xve_modify ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg, + unsigned int update ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct xsigo_nic *xve; + struct eoib_device *eoib; + struct ib_address_vector tca; + size_t mtu; + int rc; + + /* Avoid returning uninitialised HCA parameters in response */ + memset ( &msg->hca, 0, sizeof ( msg->hca ) ); + + /* Find virtual Ethernet device */ + xve = xve_find ( xcm, &msg->resource ); + if ( ! xve ) { + DBGC ( xcm, "XCM %s unrecognised resource " IB_GUID_FMT "\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + rc = -ENOENT; + goto err_no_xve; + } + + /* Find corresponding EoIB device */ + eoib = eoib_find ( ibdev, xve->mac ); + if ( ! eoib ) { + DBGC ( xve, "XVE %s has no EoIB device\n", xve->name ); + rc = -EPIPE; + goto err_no_eoib; + } + + /* The Xsigo management software fails to create the EoIB + * multicast group. This is a fundamental design flaw. + */ + eoib_force_group_creation ( eoib ); + + /* Extract modifiable parameters. Note that the TCA GID is + * erroneously transmitted as little-endian. + */ + mtu = ntohs ( msg->mtu ); + tca.qpn = ntohl ( msg->tca.data ); + tca.qkey = ntohs ( msg->tca.qkey ); + tca.gid_present = 1; + tca.gid.s.prefix.qword = bswap_64 ( msg->tca.prefix_le.qword ); + tca.gid.s.guid.qword = bswap_64 ( msg->guid_le.qword ); + + /* Update MTU, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_MTU ) && + ( ( rc = xve_update_mtu ( xve, eoib, mtu ) ) != 0 ) ) + goto err_mtu; + update &= ~XSMP_XVE_UPDATE_MTU; + + /* Update admin state, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_STATE ) && + ( ( rc = xve_update_state ( xve, eoib, msg->state ) ) != 0 ) ) + goto err_state; + update &= ~XSMP_XVE_UPDATE_STATE; + + /* Remove gateway, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_GW_DOWN ) && + ( ( rc = xve_update_tca ( xve, eoib, NULL ) ) != 0 ) ) + goto err_gw_down; + update &= ~XSMP_XVE_UPDATE_GW_DOWN; + + /* Update gateway, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_GW_CHANGE ) && + ( ( rc = xve_update_tca ( xve, eoib, &tca ) ) != 0 ) ) + goto err_gw_change; + update &= ~XSMP_XVE_UPDATE_GW_CHANGE; + + /* Warn about unexpected updates */ + if ( update ) { + DBGC ( xve, "XVE %s unrecognised update(s) %#08x\n", + xve->name, update ); + } + + xsmp_tx_xve_params ( xcm, msg, xve, eoib ); + return 0; + + err_gw_change: + err_gw_down: + err_state: + err_mtu: + err_no_eoib: + err_no_xve: + /* Send NACK */ + xsmp_tx_xve_nack ( xcm, msg ); + return rc; +} + +/** + * Handle received virtual Ethernet installation message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_install ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + union { + struct xsmp_xve_mac msg; + uint8_t raw[ETH_ALEN]; + } mac; + char name[ sizeof ( msg->name ) + 1 /* NUL */ ]; + unsigned long network; + unsigned long qkey; + unsigned int update; + int rc; + + /* Demangle MAC address (which is erroneously transmitted as + * little-endian). + */ + mac.msg.high = bswap_16 ( msg->mac_le.high ); + mac.msg.low = bswap_32 ( msg->mac_le.low ); + + /* Extract interface name (which may not be NUL-terminated) */ + memcpy ( name, msg->name, ( sizeof ( name ) - 1 /* NUL */ ) ); + name[ sizeof ( name ) - 1 /* NUL */ ] = '\0'; + + /* Extract remaining message parameters */ + network = ntohl ( msg->network ); + qkey = ntohs ( msg->tca.qkey ); + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " install \"%s\" %s net %ld qkey " + "%#lx\n", xcm->name, IB_GUID_ARGS ( &msg->resource ), name, + eth_ntoa ( mac.raw ), network, qkey ); + + /* Create virtual Ethernet device, if applicable */ + if ( ( xve_find ( xcm, &msg->resource ) == NULL ) && + ( ( rc = xve_create ( xcm, &msg->resource, mac.raw, network, + qkey, name ) ) != 0 ) ) + goto err_create; + + /* Handle remaining parameters as for a modification message */ + update = XSMP_XVE_UPDATE_MTU; + if ( msg->uplink == XSMP_XVE_UPLINK ) + update |= XSMP_XVE_UPDATE_GW_CHANGE; + return xsmp_rx_xve_modify ( xcm, msg, update ); + + err_create: + /* Send NACK */ + xsmp_tx_xve_nack ( xcm, msg ); + return rc; +} + +/** + * Handle received virtual Ethernet deletion message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_delete ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + struct xsigo_nic *xve; + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " delete\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + + /* Destroy virtual Ethernet device (if any) */ + if ( ( xve = xve_find ( xcm, &msg->resource ) ) ) + xve_destroy ( xve ); + + /* Send ACK */ + msg->code = 0; + xsmp_tx_xve ( xcm, msg ); + + return 0; +} + +/** + * Handle received virtual Ethernet update message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_update ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + unsigned int update = ntohl ( msg->update ); + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " update (%08x)\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ), update ); + + /* Handle as a modification message */ + return xsmp_rx_xve_modify ( xcm, msg, update ); +} + +/** + * Handle received virtual Ethernet operational request message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_oper_req ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " operational request\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + + /* Handle as a nullipotent modification message */ + return xsmp_rx_xve_modify ( xcm, msg, 0 ); +} + +/** + * Handle received virtual Ethernet readiness message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_ready ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + int rc; + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " ready\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + + /* Handle as a nullipotent modification message */ + if ( ( rc = xsmp_rx_xve_modify ( xcm, msg, 0 ) ) != 0 ) + return rc; + + /* Send an unsolicited operational state update, since there + * is no other way to convey the current operational state. + */ + msg->type = XSMP_XVE_TYPE_OPER_REQ; + if ( ( rc = xsmp_rx_xve_modify ( xcm, msg, 0 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Handle received virtual Ethernet message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + + DBGCP ( xcm, "XCM %s RX[%d] xve %s\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_xve_type ( msg->type ) ); + DBGCP_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + /* Handle message according to type */ + switch ( msg->type ) { + case XSMP_XVE_TYPE_INSTALL: + return xsmp_rx_xve_install ( xcm, msg ); + case XSMP_XVE_TYPE_DELETE: + return xsmp_rx_xve_delete ( xcm, msg ); + case XSMP_XVE_TYPE_UPDATE: + return xsmp_rx_xve_update ( xcm, msg ); + case XSMP_XVE_TYPE_OPER_REQ: + return xsmp_rx_xve_oper_req ( xcm, msg ); + case XSMP_XVE_TYPE_READY: + return xsmp_rx_xve_ready ( xcm, msg ); + default: + DBGC ( xcm, "XCM %s RX[%d] xve unexpected %s:\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_xve_type ( msg->type ) ); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + return -EPROTO; + } +} + +/**************************************************************************** + * + * Configuration managers (XCM) + * + **************************************************************************** + */ + +/** + * Close configuration manager connection + * + * @v xcm Configuration manager + * @v rc Reason for close + */ +static void xcm_close ( struct xsigo_manager *xcm, int rc ) { + + DBGC ( xcm, "XCM %s closed: %s\n", xcm->name, strerror ( rc ) ); + + /* Stop transmission process */ + process_del ( &xcm->process ); + + /* Stop keepalive timer */ + stop_timer ( &xcm->keepalive ); + + /* Restart data transfer interface */ + intf_restart ( &xcm->xfer, rc ); + + /* Schedule reconnection attempt */ + start_timer ( &xcm->reopen ); +} + +/** + * Send data to configuration manager + * + * @v xcm Configuration manager + */ +static void xcm_step ( struct xsigo_manager *xcm ) { + int rc; + + /* Do nothing unless we have something to send */ + if ( ! xcm->pending ) + return; + + /* Send (empty) connection request, if applicable */ + if ( xcm->pending & XCM_TX_CONNECT ) { + if ( ( rc = xfer_deliver_raw ( &xcm->xfer, NULL, 0 ) ) != 0 ) { + DBGC ( xcm, "XCM %s could not send connection request: " + "%s\n", xcm->name, strerror ( rc ) ); + goto err; + } + xcm->pending &= ~XCM_TX_CONNECT; + return; + } + + /* Wait until data transfer interface is connected */ + if ( ! xfer_window ( &xcm->xfer ) ) + return; + + /* Send registration message, if applicable */ + if ( xcm->pending & XCM_TX_REGISTER ) { + if ( ( rc = xsmp_tx_session_register ( xcm ) ) != 0 ) + goto err; + xcm->pending &= ~XCM_TX_REGISTER; + return; + } + + return; + + err: + xcm_close ( xcm, rc ); +} + +/** + * Receive data from configuration manager + * + * @v xcm Configuration manager + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int xcm_deliver ( struct xsigo_manager *xcm, struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + union xsmp_message *msg; + size_t len = iob_len ( iobuf ); + int rc; + + /* Sanity check */ + if ( len < sizeof ( msg->hdr ) ) { + DBGC ( xcm, "XCM %s underlength message:\n", xcm->name ); + DBGC_HDA ( xcm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + goto out; + } + msg = iobuf->data; + + /* Handle message according to type */ + if ( ! msg->hdr.type ) { + + /* Ignore unused communication manager private data blocks */ + rc = 0; + + } else if ( ( msg->hdr.type == XSMP_TYPE_SESSION ) && + ( len >= sizeof ( msg->sess ) ) ) { + + /* Session message */ + rc = xsmp_rx_session ( xcm, &msg->sess ); + + } else if ( ( msg->hdr.type == XSMP_TYPE_XVE ) && + ( len >= sizeof ( msg->xve ) ) ) { + + /* Virtual Ethernet message */ + xsmp_rx_xve ( xcm, &msg->xve ); + + /* Virtual Ethernet message errors are non-fatal */ + rc = 0; + + } else { + + /* Unknown message */ + DBGC ( xcm, "XCM %s unexpected message type %d:\n", + xcm->name, msg->hdr.type ); + DBGC_HDA ( xcm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + } + + out: + free_iob ( iobuf ); + if ( rc != 0 ) + xcm_close ( xcm, rc ); + return rc; +} + +/** Configuration manager data transfer interface operations */ +static struct interface_operation xcm_xfer_op[] = { + INTF_OP ( xfer_deliver, struct xsigo_manager *, xcm_deliver ), + INTF_OP ( xfer_window_changed, struct xsigo_manager *, xcm_step ), + INTF_OP ( intf_close, struct xsigo_manager *, xcm_close ), +}; + +/** Configuration manager data transfer interface descriptor */ +static struct interface_descriptor xcm_xfer_desc = + INTF_DESC ( struct xsigo_manager, xfer, xcm_xfer_op ); + +/** Configuration manager process descriptor */ +static struct process_descriptor xcm_process_desc = + PROC_DESC_ONCE ( struct xsigo_manager, process, xcm_step ); + +/** + * Handle configuration manager connection timer expiry + * + * @v timer Connection timer + * @v fail Failure indicator + */ +static void xcm_reopen ( struct retry_timer *timer, int fail __unused ) { + struct xsigo_manager *xcm = + container_of ( timer, struct xsigo_manager, reopen ); + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + union ib_gid gid; + int rc; + + /* Stop transmission process */ + process_del ( &xcm->process ); + + /* Stop keepalive timer */ + stop_timer ( &xcm->keepalive ); + + /* Restart data transfer interface */ + intf_restart ( &xcm->xfer, -ECANCELED ); + + /* Reset sequence number */ + xcm->seq = 0; + + /* Construct GID */ + memcpy ( &gid.s.prefix, &ibdev->gid.s.prefix, sizeof ( gid.s.prefix ) ); + memcpy ( &gid.s.guid, &xcm->id.guid, sizeof ( gid.s.guid ) ); + DBGC ( xcm, "XCM %s connecting to " IB_GID_FMT "\n", + xcm->name, IB_GID_ARGS ( &gid ) ); + + /* Open CMRC connection */ + if ( ( rc = ib_cmrc_open ( &xcm->xfer, ibdev, &gid, + &xcm_service_id, xcm->name ) ) != 0 ) { + DBGC ( xcm, "XCM %s could not open CMRC connection: %s\n", + xcm->name, strerror ( rc ) ); + start_timer ( &xcm->reopen ); + return; + } + + /* Schedule transmissions */ + xcm->pending |= ( XCM_TX_CONNECT | XCM_TX_REGISTER ); + process_add ( &xcm->process ); + + /* Start keepalive timer */ + start_timer_fixed ( &xcm->keepalive, XSIGO_KEEPALIVE_INTERVAL ); + + return; +} + +/** + * Handle configuration manager keepalive timer expiry + * + * @v timer Connection timer + * @v fail Failure indicator + */ +static void xcm_keepalive ( struct retry_timer *timer, int fail __unused ) { + struct xsigo_manager *xcm = + container_of ( timer, struct xsigo_manager, keepalive ); + int rc; + + /* Send keepalive message. The server won't actually respond + * to these, but it gives the RC queue pair a chance to + * complain if it doesn't ever at least get an ACK. + */ + if ( ( rc = xsmp_tx_session_hello ( xcm ) ) != 0 ) { + xcm_close ( xcm, rc ); + return; + } + + /* Restart keepalive timer */ + start_timer_fixed ( &xcm->keepalive, XSIGO_KEEPALIVE_INTERVAL ); +} + +/** + * Create configuration manager + * + * @v xsigo Xsigo device + * @v id Configuration manager ID + * @ret rc Return status code + */ +static int xcm_create ( struct xsigo_device *xdev, + struct xsigo_manager_id *id ) { + struct xsigo_manager *xcm; + + /* Allocate and initialise structure */ + xcm = zalloc ( sizeof ( *xcm ) ); + if ( ! xcm ) + return -ENOMEM; + ref_init ( &xcm->refcnt, xcm_free ); + xcm->xdev = xdev; + ref_get ( &xcm->xdev->refcnt ); + snprintf ( xcm->name, sizeof ( xcm->name ), "%s:xcm-%d", + xdev->name, ntohs ( id->lid ) ); + memcpy ( &xcm->id, id, sizeof ( xcm->id ) ); + intf_init ( &xcm->xfer, &xcm_xfer_desc, &xcm->refcnt ); + timer_init ( &xcm->keepalive, xcm_keepalive, &xcm->refcnt ); + timer_init ( &xcm->reopen, xcm_reopen, &xcm->refcnt ); + process_init_stopped ( &xcm->process, &xcm_process_desc, &xcm->refcnt ); + INIT_LIST_HEAD ( &xcm->nics ); + + /* Start timer to open connection */ + start_timer_nodelay ( &xcm->reopen ); + + /* Add to list of managers and transfer reference to list */ + list_add ( &xcm->list, &xdev->managers ); + DBGC ( xcm, "XCM %s created for " IB_GUID_FMT " (LID %d)\n", xcm->name, + IB_GUID_ARGS ( &xcm->id.guid ), ntohs ( id->lid ) ); + return 0; +} + +/** + * Find configuration manager + * + * @v xsigo Xsigo device + * @v id Configuration manager ID + * @ret xcm Configuration manager, or NULL + */ +static struct xsigo_manager * xcm_find ( struct xsigo_device *xdev, + struct xsigo_manager_id *id ) { + struct xsigo_manager *xcm; + union ib_guid *guid = &id->guid; + + /* Find configuration manager */ + list_for_each_entry ( xcm, &xdev->managers, list ) { + if ( memcmp ( guid, &xcm->id.guid, sizeof ( *guid ) ) == 0 ) + return xcm; + } + return NULL; +} + +/** + * Destroy configuration manager + * + * @v xcm Configuration manager + */ +static void xcm_destroy ( struct xsigo_manager *xcm ) { + struct xsigo_nic *xve; + + /* Remove all EoIB NICs */ + while ( ( xve = list_first_entry ( &xcm->nics, struct xsigo_nic, + list ) ) ) { + xve_destroy ( xve ); + } + + /* Stop transmission process */ + process_del ( &xcm->process ); + + /* Stop timers */ + stop_timer ( &xcm->keepalive ); + stop_timer ( &xcm->reopen ); + + /* Shut down data transfer interface */ + intf_shutdown ( &xcm->xfer, 0 ); + + /* Remove from list of managers and drop list's reference */ + DBGC ( xcm, "XCM %s destroyed\n", xcm->name ); + list_del ( &xcm->list ); + ref_put ( &xcm->refcnt ); +} + +/** + * Synchronise list of configuration managers + * + * @v xdev Xsigo device + * @v ids List of manager IDs + * @v count Number of manager IDs + * @ret rc Return status code + */ +static int xcm_list ( struct xsigo_device *xdev, struct xsigo_manager_id *ids, + unsigned int count ) { + struct xsigo_manager_id *id; + struct xsigo_manager *xcm; + struct xsigo_manager *tmp; + struct list_head list; + unsigned int i; + int rc; + + /* Create list of managers to be retained */ + INIT_LIST_HEAD ( &list ); + for ( i = 0, id = ids ; i < count ; i++, id++ ) { + if ( ( xcm = xcm_find ( xdev, id ) ) ) { + list_del ( &xcm->list ); + list_add_tail ( &xcm->list, &list ); + } + } + + /* Destroy any managers not in the list */ + list_for_each_entry_safe ( xcm, tmp, &xdev->managers, list ) + xcm_destroy ( xcm ); + list_splice ( &list, &xdev->managers ); + + /* Create any new managers in the list, and force reconnection + * for any changed LIDs. + */ + for ( i = 0, id = ids ; i < count ; i++, id++ ) { + if ( ( xcm = xcm_find ( xdev, id ) ) ) { + if ( xcm->id.lid != id->lid ) + start_timer_nodelay ( &xcm->reopen ); + continue; + } + if ( ( rc = xcm_create ( xdev, id ) ) != 0 ) { + DBGC ( xdev, "XDEV %s could not create manager: %s\n", + xdev->name, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/**************************************************************************** + * + * Configuration manager discovery + * + **************************************************************************** + */ + +/** A stage of discovery */ +struct xsigo_discovery { + /** Name */ + const char *name; + /** Management transaction operations */ + struct ib_mad_transaction_operations op; +}; + +/** + * Handle configuration manager lookup completion + * + * @v ibdev Infiniband device + * @v mi Management interface + * @v madx Management transaction + * @v rc Status code + * @v mad Received MAD (or NULL on error) + * @v av Source address vector (or NULL on error) + */ +static void xsigo_xcm_complete ( struct ib_device *ibdev, + struct ib_mad_interface *mi __unused, + struct ib_mad_transaction *madx, + int rc, union ib_mad *mad, + struct ib_address_vector *av __unused ) { + struct xsigo_device *xdev = ib_madx_get_ownerdata ( madx ); + union xsigo_mad *xsmad = container_of ( mad, union xsigo_mad, mad ); + struct xsigo_managers_reply *reply = &xsmad->reply; + + /* Check for failures */ + if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ) ) + rc = -ENODEV; + if ( rc != 0 ) { + DBGC ( xdev, "XDEV %s manager lookup failed: %s\n", + xdev->name, strerror ( rc ) ); + goto out; + } + + /* Sanity checks */ + if ( reply->count > ( sizeof ( reply->manager ) / + sizeof ( reply->manager[0] ) ) ) { + DBGC ( xdev, "XDEV %s has too many managers (%d)\n", + xdev->name, reply->count ); + goto out; + } + + /* Synchronise list of managers */ + if ( ( rc = xcm_list ( xdev, reply->manager, reply->count ) ) != 0 ) + goto out; + + /* Report an empty list of managers */ + if ( reply->count == 0 ) + DBGC ( xdev, "XDEV %s has no managers\n", xdev->name ); + + /* Delay next discovery attempt */ + start_timer_fixed ( &xdev->discovery, XSIGO_DISCOVERY_SUCCESS_DELAY ); + +out: + /* Destroy the completed transaction */ + ib_destroy_madx ( ibdev, ibdev->gsi, madx ); + xdev->madx = NULL; +} + +/** Configuration manager lookup discovery stage */ +static struct xsigo_discovery xsigo_xcm_discovery = { + .name = "manager", + .op = { + .complete = xsigo_xcm_complete, + }, +}; + +/** + * Handle directory service lookup completion + * + * @v ibdev Infiniband device + * @v mi Management interface + * @v madx Management transaction + * @v rc Status code + * @v mad Received MAD (or NULL on error) + * @v av Source address vector (or NULL on error) + */ +static void xsigo_xds_complete ( struct ib_device *ibdev, + struct ib_mad_interface *mi __unused, + struct ib_mad_transaction *madx, + int rc, union ib_mad *mad, + struct ib_address_vector *av __unused ) { + struct xsigo_device *xdev = ib_madx_get_ownerdata ( madx ); + union xsigo_mad *xsmad = container_of ( mad, union xsigo_mad, mad ); + struct xsigo_managers_request *request = &xsmad->request; + struct ib_service_record *svc; + struct ib_address_vector dest; + union ib_guid *guid; + + /* Allow for reuse of transaction pointer */ + xdev->madx = NULL; + + /* Check for failures */ + if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ) ) + rc = -ENODEV; + if ( rc != 0 ) { + DBGC ( xdev, "XDEV %s directory lookup failed: %s\n", + xdev->name, strerror ( rc ) ); + goto out; + } + + /* Construct address vector */ + memset ( &dest, 0, sizeof ( dest ) ); + svc = &mad->sa.sa_data.service_record; + dest.lid = ntohs ( svc->data16[0] ); + dest.sl = ibdev->sm_sl; + dest.qpn = IB_QPN_GSI; + dest.qkey = IB_QKEY_GSI; + guid = ( ( union ib_guid * ) &svc->data64[0] ); + DBGC2 ( xdev, "XDEV %s found directory at LID %d GUID " IB_GUID_FMT + "\n", xdev->name, dest.lid, IB_GUID_ARGS ( guid ) ); + + /* Construct request (reusing MAD buffer) */ + memset ( request, 0, sizeof ( *request ) ); + request->mad_hdr.mgmt_class = XSIGO_MGMT_CLASS; + request->mad_hdr.class_version = XSIGO_MGMT_CLASS_VERSION; + request->mad_hdr.method = IB_MGMT_METHOD_GET; + request->mad_hdr.attr_id = htons ( XSIGO_ATTR_XCM_REQUEST ); + memcpy ( &request->server.guid, &ibdev->gid.s.guid, + sizeof ( request->server.guid ) ); + snprintf ( request->os_version, sizeof ( request->os_version ), + "%s %s", product_short_name, product_version ); + snprintf ( request->arch, sizeof ( request->arch ), _S2 ( ARCH ) ); + request->os_type = XSIGO_OS_TYPE_GENERIC; + request->resources = htons ( XSIGO_RESOURCES_PRESENT | + XSIGO_RESOURCE_XVE | + XSIGO_RESOURCE_NO_HA ); + + /* The handling of this request on the server side is a + * textbook example of how not to design a wire protocol. The + * server uses the _driver_ version number to determine which + * fields are present. + */ + request->driver_version = htonl ( 0x2a2a2a ); + + /* The build version field is ignored unless it happens to + * contain the substring "xg-". + */ + snprintf ( request->build, sizeof ( request->build ), + "not-xg-%08lx", build_id ); + + /* The server side user interface occasionally has no way to + * refer to an entry with an empty hostname. + */ + fetch_string_setting ( NULL, &hostname_setting, request->hostname, + sizeof ( request->hostname ) ); + if ( ! request->hostname[0] ) { + snprintf ( request->hostname, sizeof ( request->hostname ), + "%s-" IB_GUID_FMT, product_short_name, + IB_GUID_ARGS ( &ibdev->gid.s.guid ) ); + } + + /* Start configuration manager lookup */ + xdev->madx = ib_create_madx ( ibdev, ibdev->gsi, mad, &dest, + &xsigo_xcm_discovery.op ); + if ( ! xdev->madx ) { + DBGC ( xdev, "XDEV %s could not start manager lookup\n", + xdev->name ); + goto out; + } + ib_madx_set_ownerdata ( xdev->madx, xdev ); + +out: + /* Destroy the completed transaction */ + ib_destroy_madx ( ibdev, ibdev->gsi, madx ); +} + +/** Directory service lookup discovery stage */ +static struct xsigo_discovery xsigo_xds_discovery = { + .name = "directory", + .op = { + .complete = xsigo_xds_complete, + }, +}; + +/** + * Discover configuration managers + * + * @v timer Retry timer + * @v over Failure indicator + */ +static void xsigo_discover ( struct retry_timer *timer, int over __unused ) { + struct xsigo_device *xdev = + container_of ( timer, struct xsigo_device, discovery ); + struct ib_device *ibdev = xdev->ibdev; + struct xsigo_discovery *discovery; + + /* Restart timer */ + start_timer_fixed ( &xdev->discovery, XSIGO_DISCOVERY_FAILURE_DELAY ); + + /* Cancel any pending discovery transaction */ + if ( xdev->madx ) { + discovery = container_of ( xdev->madx->op, + struct xsigo_discovery, op ); + DBGC ( xdev, "XDEV %s timed out waiting for %s lookup\n", + xdev->name, discovery->name ); + ib_destroy_madx ( ibdev, ibdev->gsi, xdev->madx ); + xdev->madx = NULL; + } + + /* Start directory service lookup */ + xdev->madx = ib_create_service_madx ( ibdev, ibdev->gsi, + XDS_SERVICE_NAME, + &xsigo_xds_discovery.op ); + if ( ! xdev->madx ) { + DBGC ( xdev, "XDEV %s could not start directory lookup\n", + xdev->name ); + return; + } + ib_madx_set_ownerdata ( xdev->madx, xdev ); +} + +/**************************************************************************** + * + * Infiniband device driver + * + **************************************************************************** + */ + +/** + * Open link and start discovery + * + * @v opener Link opener + * @v over Failure indicator + */ +static void xsigo_ib_open ( struct retry_timer *opener, int over __unused ) { + struct xsigo_device *xdev = + container_of ( opener, struct xsigo_device, opener ); + struct ib_device *ibdev = xdev->ibdev; + int rc; + + /* Open Infiniband device */ + if ( ( rc = ib_open ( ibdev ) ) != 0 ) { + DBGC ( xdev, "XDEV %s could not open: %s\n", + xdev->name, strerror ( rc ) ); + /* Delay and try again */ + start_timer_fixed ( &xdev->opener, XSIGO_OPEN_RETRY_DELAY ); + return; + } + + /* If link is already up, then start discovery */ + if ( ib_link_ok ( ibdev ) ) + start_timer_nodelay ( &xdev->discovery ); +} + +/** + * Probe Xsigo device + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int xsigo_ib_probe ( struct ib_device *ibdev ) { + struct xsigo_device *xdev; + + /* Allocate and initialise structure */ + xdev = zalloc ( sizeof ( *xdev ) ); + if ( ! xdev ) + return -ENOMEM; + ref_init ( &xdev->refcnt, xsigo_free ); + xdev->ibdev = ibdev_get ( ibdev ); + xdev->name = ibdev->name; + timer_init ( &xdev->opener, xsigo_ib_open, &xdev->refcnt ); + timer_init ( &xdev->discovery, xsigo_discover, &xdev->refcnt ); + INIT_LIST_HEAD ( &xdev->managers ); + + /* Start timer to open Infiniband device. (We are currently + * within the Infiniband device probe callback list; opening + * the device here would have interesting side-effects.) + */ + start_timer_nodelay ( &xdev->opener ); + + /* Add to list of devices and transfer reference to list */ + list_add_tail ( &xdev->list, &xsigo_devices ); + DBGC ( xdev, "XDEV %s created for " IB_GUID_FMT "\n", + xdev->name, IB_GUID_ARGS ( &ibdev->gid.s.guid ) ); + return 0; +} + +/** + * Handle device or link status change + * + * @v ibdev Infiniband device + */ +static void xsigo_ib_notify ( struct ib_device *ibdev ) { + struct xsigo_device *xdev; + + /* Stop/restart discovery on any attached devices */ + list_for_each_entry ( xdev, &xsigo_devices, list ) { + + /* Skip non-attached devices */ + if ( xdev->ibdev != ibdev ) + continue; + + /* Stop any ongoing discovery */ + if ( xdev->madx ) { + ib_destroy_madx ( ibdev, ibdev->gsi, xdev->madx ); + xdev->madx = NULL; + } + stop_timer ( &xdev->discovery ); + + /* If link is up, then start discovery */ + if ( ib_link_ok ( ibdev ) ) + start_timer_nodelay ( &xdev->discovery ); + } +} + +/** + * Remove Xsigo device + * + * @v ibdev Infiniband device + */ +static void xsigo_ib_remove ( struct ib_device *ibdev ) { + struct xsigo_device *xdev; + struct xsigo_device *tmp; + + /* Remove any attached Xsigo devices */ + list_for_each_entry_safe ( xdev, tmp, &xsigo_devices, list ) { + + /* Skip non-attached devices */ + if ( xdev->ibdev != ibdev ) + continue; + + /* Stop any ongoing discovery */ + if ( xdev->madx ) { + ib_destroy_madx ( ibdev, ibdev->gsi, xdev->madx ); + xdev->madx = NULL; + } + stop_timer ( &xdev->discovery ); + + /* Destroy all configuration managers */ + xcm_list ( xdev, NULL, 0 ); + + /* Close Infiniband device, if applicable */ + if ( ! timer_running ( &xdev->opener ) ) + ib_close ( xdev->ibdev ); + + /* Stop link opener */ + stop_timer ( &xdev->opener ); + + /* Remove from list of devices and drop list's reference */ + DBGC ( xdev, "XDEV %s destroyed\n", xdev->name ); + list_del ( &xdev->list ); + ref_put ( &xdev->refcnt ); + } +} + +/** Xsigo Infiniband driver */ +struct ib_driver xsigo_ib_driver __ib_driver = { + .name = "Xsigo", + .probe = xsigo_ib_probe, + .notify = xsigo_ib_notify, + .remove = xsigo_ib_remove, +}; + +/**************************************************************************** + * + * Network device driver + * + **************************************************************************** + */ + +/** + * Handle device or link status change + * + * @v netdev Network device + */ +static void xsigo_net_notify ( struct net_device *netdev ) { + struct xsigo_device *xdev; + struct ib_device *ibdev; + struct xsigo_manager *xcm; + struct xsigo_nic *xve; + struct eoib_device *eoib; + + /* Send current operational state to XCM, if applicable */ + list_for_each_entry ( xdev, &xsigo_devices, list ) { + ibdev = xdev->ibdev; + list_for_each_entry ( xcm, &xdev->managers, list ) { + list_for_each_entry ( xve, &xcm->nics, list ) { + eoib = eoib_find ( ibdev, xve->mac ); + if ( ! eoib ) + continue; + if ( eoib->netdev != netdev ) + continue; + xsmp_tx_xve_oper ( xcm, xve, eoib ); + } + } + } +} + +/** Xsigo network driver */ +struct net_driver xsigo_net_driver __net_driver = { + .name = "Xsigo", + .notify = xsigo_net_notify, +}; From e303a6b387628d4c65d1085f66a5d97855755ace Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 10 Mar 2016 18:06:26 +0000 Subject: [PATCH 124/591] [efi] Work around broken GetFontInfo() implementations Several UEFI platforms are known to return EFI_NOT_FOUND when asked to retrieve the system default font information via GetFontInfo(). Work around these broken platforms by iterating over the glyphs to find the maximum height used by a printable character. Originally-fixed-by: Jonathan Dieter Signed-off-by: Michael Brown --- src/interface/efi/efi_fbcon.c | 54 ++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index 01b691b65..abc5a9390 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -110,7 +110,6 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { */ static int efifb_glyphs ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_FONT_DISPLAY_INFO *info; EFI_IMAGE_OUTPUT *blt; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel; size_t offset; @@ -122,16 +121,42 @@ static int efifb_glyphs ( void ) { EFI_STATUS efirc; int rc; - /* Get font height */ - if ( ( efirc = efifb.hiifont->GetFontInfo ( efifb.hiifont, NULL, NULL, - &info, NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not get font information: %s\n", - strerror ( rc ) ); - goto err_info; + /* Get font height. The GetFontInfo() call nominally returns + * this information in an EFI_FONT_DISPLAY_INFO structure, but + * is known to fail on many UEFI implementations. Instead, we + * iterate over all printable characters to find the maximum + * height. + */ + efifb.font.height = 0; + for ( character = 0 ; character < 256 ; character++ ) { + + /* Skip non-printable characters */ + if ( ! isprint ( character ) ) + continue; + + /* Get glyph */ + blt = NULL; + if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, + character, NULL, &blt, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n", + character, strerror ( rc ) ); + continue; + } + assert ( blt != NULL ); + + /* Calculate maximum height */ + if ( efifb.font.height < blt->Height ) + efifb.font.height = blt->Height; + + /* Free glyph */ + bs->FreePool ( blt ); + } + if ( ! efifb.font.height ) { + DBGC ( &efifb, "EFIFB could not get font height\n" ); + return -ENOENT; } - assert ( info != NULL ); - efifb.font.height = info->FontInfo.FontSize; /* Allocate glyph data */ len = ( 256 * efifb.font.height * sizeof ( bitmask ) ); @@ -152,7 +177,7 @@ static int efifb_glyphs ( void ) { /* Get glyph */ blt = NULL; if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, - character, info, &blt, + character, NULL, &blt, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n", @@ -187,19 +212,16 @@ static int efifb_glyphs ( void ) { copy_to_user ( efifb.glyphs, offset++, &bitmask, sizeof ( bitmask ) ); } + + /* Free glyph */ bs->FreePool ( blt ); } - /* Free font information */ - bs->FreePool ( info ); - efifb.font.glyph = efifb_glyph; return 0; ufree ( efifb.glyphs ); err_alloc: - bs->FreePool ( info ); - err_info: return rc; } From 05dcb07cb239d8b7abe33f7701dbb81f370cea4b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 11 Mar 2016 16:09:40 +0000 Subject: [PATCH 125/591] [tls] Avoid potential out-of-bound reads in length fields Many TLS records contain variable-length fields. We currently validate the overall record length, but do so only after reading the length of the variable-length field. If the record is too short to even contain the length field, then we may read uninitialised data from beyond the end of the record. This is harmless in practice (since the subsequent overall record length check would fail regardless of the value read from the uninitialised length field), but causes warnings from some analysis tools. Fix by validating that the overall record length is sufficient to contain the length field before reading from the length field. Signed-off-by: Michael Brown --- src/net/tls.c | 109 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index db01fb291..90f9f9767 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1271,10 +1271,9 @@ static int tls_new_alert ( struct tls_session *tls, const void *data, uint8_t description; char next[0]; } __attribute__ (( packed )) *alert = data; - const void *end = alert->next; /* Sanity check */ - if ( end != ( data + len ) ) { + if ( sizeof ( *alert ) != len ) { DBGC ( tls, "TLS %p received overlength Alert\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_ALERT; @@ -1310,24 +1309,28 @@ static int tls_new_server_hello ( struct tls_session *tls, uint16_t version; uint8_t random[32]; uint8_t session_id_len; - char next[0]; + uint8_t session_id[0]; } __attribute__ (( packed )) *hello_a = data; + const uint8_t *session_id; const struct { - uint8_t session_id[hello_a->session_id_len]; uint16_t cipher_suite; uint8_t compression_method; char next[0]; - } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next; - const void *end = hello_b->next; + } __attribute__ (( packed )) *hello_b; uint16_t version; int rc; - /* Sanity check */ - if ( end > ( data + len ) ) { + /* Parse header */ + if ( ( sizeof ( *hello_a ) > len ) || + ( hello_a->session_id_len > ( len - sizeof ( *hello_a ) ) ) || + ( sizeof ( *hello_b ) > ( len - sizeof ( *hello_a ) - + hello_a->session_id_len ) ) ) { DBGC ( tls, "TLS %p received underlength Server Hello\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_HELLO; } + session_id = hello_a->session_id; + hello_b = ( ( void * ) ( session_id + hello_a->session_id_len ) ); /* Check and store protocol version */ version = ntohs ( hello_a->version ); @@ -1380,14 +1383,7 @@ static int tls_new_server_hello ( struct tls_session *tls, */ static int tls_parse_chain ( struct tls_session *tls, const void *data, size_t len ) { - const void *end = ( data + len ); - const struct { - tls24_t length; - uint8_t data[0]; - } __attribute__ (( packed )) *certificate; - size_t certificate_len; - struct x509_certificate *cert; - const void *next; + size_t remaining = len; int rc; /* Free any existing certificate chain */ @@ -1402,25 +1398,37 @@ static int tls_parse_chain ( struct tls_session *tls, } /* Add certificates to chain */ - while ( data < end ) { + while ( remaining ) { + const struct { + tls24_t length; + uint8_t data[0]; + } __attribute__ (( packed )) *certificate = data; + size_t certificate_len; + size_t record_len; + struct x509_certificate *cert; - /* Extract raw certificate data */ - certificate = data; + /* Parse header */ + if ( sizeof ( *certificate ) > remaining ) { + DBGC ( tls, "TLS %p underlength certificate:\n", tls ); + DBGC_HDA ( tls, 0, data, remaining ); + rc = -EINVAL_CERTIFICATE; + goto err_underlength; + } certificate_len = tls_uint24 ( &certificate->length ); - next = ( certificate->data + certificate_len ); - if ( next > end ) { + if ( certificate_len > ( remaining - sizeof ( *certificate ) )){ DBGC ( tls, "TLS %p overlength certificate:\n", tls ); - DBGC_HDA ( tls, 0, data, ( end - data ) ); + DBGC_HDA ( tls, 0, data, remaining ); rc = -EINVAL_CERTIFICATE; goto err_overlength; } + record_len = ( sizeof ( *certificate ) + certificate_len ); /* Add certificate to chain */ if ( ( rc = x509_append_raw ( tls->chain, certificate->data, certificate_len ) ) != 0 ) { DBGC ( tls, "TLS %p could not append certificate: %s\n", tls, strerror ( rc ) ); - DBGC_HDA ( tls, 0, data, ( end - data ) ); + DBGC_HDA ( tls, 0, data, remaining ); goto err_parse; } cert = x509_last ( tls->chain ); @@ -1428,13 +1436,15 @@ static int tls_parse_chain ( struct tls_session *tls, tls, x509_name ( cert ) ); /* Move to next certificate in list */ - data = next; + data += record_len; + remaining -= record_len; } return 0; err_parse: err_overlength: + err_underlength: x509_chain_put ( tls->chain ); tls->chain = NULL; err_alloc_chain: @@ -1455,12 +1465,18 @@ static int tls_new_certificate ( struct tls_session *tls, tls24_t length; uint8_t certificates[0]; } __attribute__ (( packed )) *certificate = data; - size_t certificates_len = tls_uint24 ( &certificate->length ); - const void *end = ( certificate->certificates + certificates_len ); + size_t certificates_len; int rc; - /* Sanity check */ - if ( end != ( data + len ) ) { + /* Parse header */ + if ( sizeof ( *certificate ) > len ) { + DBGC ( tls, "TLS %p received underlength Server Certificate\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_CERTIFICATES; + } + certificates_len = tls_uint24 ( &certificate->length ); + if ( certificates_len > ( len - sizeof ( *certificate ) ) ) { DBGC ( tls, "TLS %p received overlength Server Certificate\n", tls ); DBGC_HD ( tls, data, len ); @@ -1521,11 +1537,10 @@ static int tls_new_server_hello_done ( struct tls_session *tls, const struct { char next[0]; } __attribute__ (( packed )) *hello_done = data; - const void *end = hello_done->next; int rc; /* Sanity check */ - if ( end != ( data + len ) ) { + if ( sizeof ( *hello_done ) != len ) { DBGC ( tls, "TLS %p received overlength Server Hello Done\n", tls ); DBGC_HD ( tls, data, len ); @@ -1557,12 +1572,11 @@ static int tls_new_finished ( struct tls_session *tls, uint8_t verify_data[12]; char next[0]; } __attribute__ (( packed )) *finished = data; - const void *end = finished->next; uint8_t digest_out[ digest->digestsize ]; uint8_t verify_data[ sizeof ( finished->verify_data ) ]; /* Sanity check */ - if ( end != ( data + len ) ) { + if ( sizeof ( *finished ) != len ) { DBGC ( tls, "TLS %p received overlength Finished\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_FINISHED; @@ -1598,27 +1612,37 @@ static int tls_new_finished ( struct tls_session *tls, */ static int tls_new_handshake ( struct tls_session *tls, const void *data, size_t len ) { - const void *end = ( data + len ); + size_t remaining = len; int rc; - while ( data != end ) { + while ( remaining ) { const struct { uint8_t type; tls24_t length; uint8_t payload[0]; } __attribute__ (( packed )) *handshake = data; - const void *payload = &handshake->payload; - size_t payload_len = tls_uint24 ( &handshake->length ); - const void *next = ( payload + payload_len ); + const void *payload; + size_t payload_len; + size_t record_len; - /* Sanity check */ - if ( next > end ) { + /* Parse header */ + if ( sizeof ( *handshake ) > remaining ) { + DBGC ( tls, "TLS %p received underlength Handshake\n", + tls ); + DBGC_HD ( tls, data, remaining ); + return -EINVAL_HANDSHAKE; + } + payload_len = tls_uint24 ( &handshake->length ); + if ( payload_len > ( remaining - sizeof ( *handshake ) ) ) { DBGC ( tls, "TLS %p received overlength Handshake\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_HANDSHAKE; } + payload = &handshake->payload; + record_len = ( sizeof ( *handshake ) + payload_len ); + /* Handle payload */ switch ( handshake->type ) { case TLS_SERVER_HELLO: rc = tls_new_server_hello ( tls, payload, payload_len ); @@ -1648,16 +1672,15 @@ static int tls_new_handshake ( struct tls_session *tls, * which are explicitly excluded). */ if ( handshake->type != TLS_HELLO_REQUEST ) - tls_add_handshake ( tls, data, - sizeof ( *handshake ) + - payload_len ); + tls_add_handshake ( tls, data, record_len ); /* Abort on failure */ if ( rc != 0 ) return rc; /* Move to next handshake record */ - data = next; + data += record_len; + remaining -= record_len; } return 0; From 5a6ed90a00ea8b1070c808e4f7d5da173b2e848f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 11 Mar 2016 16:51:13 +0000 Subject: [PATCH 126/591] [crypto] Allow for zero-length ASN.1 cursors The assumption in asn1_type() that an ASN.1 cursor will always contain a type byte is incorrect. A cursor that has been cleanly invalidated via asn1_invalidate_cursor() will contain a type byte, but there are other ways in which to arrive at a zero-length cursor. Fix by explicitly checking the cursor length in asn1_type(). This allows asn1_invalidate_cursor() to be reduced to simply zeroing the length field. Signed-off-by: Michael Brown --- src/crypto/asn1.c | 12 ------------ src/include/ipxe/asn1.h | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index aca12bf30..9c71ffe10 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -81,18 +81,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EINFO_ENOTTY_ALGORITHM \ __einfo_uniqify ( EINFO_ENOTTY, 0x01, "Inappropriate algorithm" ) -/** - * Invalidate ASN.1 object cursor - * - * @v cursor ASN.1 object cursor - */ -void asn1_invalidate_cursor ( struct asn1_cursor *cursor ) { - static uint8_t asn1_invalid_object[] = { ASN1_END, 0 }; - - cursor->data = asn1_invalid_object; - cursor->len = 0; -} - /** * Start parsing ASN.1 object * diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 5fbd58281..2e635b48a 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -314,15 +314,27 @@ struct asn1_bit_string { unsigned int unused; } __attribute__ (( packed )); +/** + * Invalidate ASN.1 object cursor + * + * @v cursor ASN.1 object cursor + */ +static inline __attribute__ (( always_inline )) void +asn1_invalidate_cursor ( struct asn1_cursor *cursor ) { + cursor->len = 0; +} + /** * Extract ASN.1 type * * @v cursor ASN.1 object cursor - * @ret type Type + * @ret type Type, or ASN1_END if cursor is invalid */ static inline __attribute__ (( always_inline )) unsigned int asn1_type ( const struct asn1_cursor *cursor ) { - return ( *( ( const uint8_t * ) cursor->data ) ); + const uint8_t *type = cursor->data; + + return ( ( cursor->len >= sizeof ( *type ) ) ? *type : ASN1_END ); } extern void asn1_invalidate_cursor ( struct asn1_cursor *cursor ); From 11396473f5878af70b0ec9714a03d81a0c05a771 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 00:09:23 +0000 Subject: [PATCH 127/591] [pixbuf] Check for unsigned integer overflow on multiplication Signed-off-by: Michael Brown --- src/core/pixbuf.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/pixbuf.c b/src/core/pixbuf.c index 41e18f8dc..c12bd3c06 100644 --- a/src/core/pixbuf.c +++ b/src/core/pixbuf.c @@ -65,6 +65,10 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { pixbuf->height = height; pixbuf->len = ( width * height * sizeof ( uint32_t ) ); + /* Check for multiplication overflow */ + if ( ( ( pixbuf->len / sizeof ( uint32_t ) ) / width ) != height ) + goto err_overflow; + /* Allocate pixel data buffer */ pixbuf->data = umalloc ( pixbuf->len ); if ( ! pixbuf->data ) @@ -73,6 +77,7 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { return pixbuf; err_alloc_data: + err_overflow: pixbuf_put ( pixbuf ); err_alloc_pixbuf: return NULL; From 64acfd9ddd73bf38802f8c57e054d13a57b14198 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 01:21:18 +0000 Subject: [PATCH 128/591] [arp] Validate length of ARP packet There is no practical way to generate an underlength ARP packet since an ARP packet is always padded up to the minimum Ethernet frame length (or dropped by the receiving Ethernet hardware if incorrectly padded), but the absence of an explicit check causes warnings from some analysis tools. Fix by adding an explicit check on the I/O buffer length. Signed-off-by: Michael Brown --- src/include/ipxe/if_arp.h | 10 ++++++++++ src/net/arp.c | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/include/ipxe/if_arp.h b/src/include/ipxe/if_arp.h index 4eb1f80b7..9d7b03fe8 100644 --- a/src/include/ipxe/if_arp.h +++ b/src/include/ipxe/if_arp.h @@ -99,4 +99,14 @@ static inline void * arp_target_pa ( struct arphdr *arphdr ) { return ( arp_target_ha ( arphdr ) + arphdr->ar_hln ); } +/** ARP packet length + * + * @v arphdr ARP header + * @ret len Length (including header) + */ +static inline size_t arp_len ( struct arphdr *arphdr ) { + return ( sizeof ( *arphdr ) + + ( 2 * ( arphdr->ar_hln + arphdr->ar_pln ) ) ); +} + #endif /* _IPXE_IF_ARP_H */ diff --git a/src/net/arp.c b/src/net/arp.c index 1e27c44e7..c9b4109a9 100644 --- a/src/net/arp.c +++ b/src/net/arp.c @@ -139,8 +139,15 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, struct arp_net_protocol *arp_net_protocol; struct net_protocol *net_protocol; struct ll_protocol *ll_protocol; + size_t len = iob_len ( iobuf ); int rc; + /* Sanity check */ + if ( ( len < sizeof ( *arphdr ) ) || ( len < arp_len ( arphdr ) ) ) { + rc = -EINVAL; + goto done; + } + /* Identify network-layer and link-layer protocols */ arp_net_protocol = arp_find_protocol ( arphdr->ar_pro ); if ( ! arp_net_protocol ) { From cc9f31ee0cfd4ef9cf05ef0621fc933814489b3d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 12:39:17 +0000 Subject: [PATCH 129/591] [librm] Do not unconditionally preserve flags across virt_call() Commit 196f0f2 ("[librm] Convert prot_call() to a real-mode near call") introduced a regression in which any deliberate modification to the low 16 bits of the CPU flags (in struct i386_all_regs) would be overwritten with the original flags value at the time of entry to prot_call(). The regression arose because the alignment requirements of the protected-mode stack necessitated the insertion of two bytes of padding immediately below the prot_call() return address. The solution chosen was to extend the existing "pushfl / popfl" pair to "pushfw;pushfl / popfl;popfw". The extra "pushfw / popfw" appears at first glance to be a no-op, but fails to take into account the fact that the flags restored by popfl may have been deliberately modified by the protected-mode function. Fix by replacing "pushfw / popfw" with "pushw %ss / popw %ss". While %ss does appear within struct i386_all_regs, any modification to the stored value has always been ignored by prot_call() anyway. The most visible symptom of this regression was that SAN booting would fail since every INT 13 call would be chained to the original INT 13 vector. Reported-by: Vishvananda Ishaya Reported-by: Jamie Thompson Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index bb04ad67d..3a585a921 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -940,7 +940,7 @@ VC_OFFSET_END: .globl virt_call virt_call: /* Preserve registers, flags and GDT on external RM stack */ - pushfw /* padding */ + pushw %ss /* padding */ pushfl pushal pushw %gs @@ -1030,7 +1030,7 @@ vc_rmode: */ addr32 movl -20(%esp), %esp popfl - popfw /* padding */ + popw %ss /* padding */ /* Return and discard function parameters */ ret $( VC_OFFSET_END - VC_OFFSET_PARAMS ) From 5229662b7fcf8e335e2faaa3afbaa2ea0e8c2e9a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 17:42:30 +0000 Subject: [PATCH 130/591] [linda] Use standard readq() and writeq() implementations This driver is the original source of the current readq() and writeq() implementations for 32-bit iPXE. Switch to using the now-centralised definitions, to avoid including architecture-specific code in an otherwise architecture-independent driver. Signed-off-by: Michael Brown --- src/drivers/infiniband/linda.c | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/drivers/infiniband/linda.c b/src/drivers/infiniband/linda.c index a6ae9f529..0c1682745 100644 --- a/src/drivers/infiniband/linda.c +++ b/src/drivers/infiniband/linda.c @@ -112,32 +112,21 @@ struct linda { * This card requires atomic 64-bit accesses. Strange things happen * if you try to use 32-bit accesses; sometimes they work, sometimes * they don't, sometimes you get random data. - * - * These accessors use the "movq" MMX instruction, and so won't work - * on really old Pentiums (which won't have PCIe anyway, so this is - * something of a moot point). */ /** * Read Linda qword register * * @v linda Linda device - * @v dwords Register buffer to read into + * @v qword Register buffer to read into * @v offset Register offset */ -static void linda_readq ( struct linda *linda, uint32_t *dwords, +static void linda_readq ( struct linda *linda, uint64_t *qword, unsigned long offset ) { - void *addr = ( linda->regs + offset ); - - __asm__ __volatile__ ( "movq (%1), %%mm0\n\t" - "movq %%mm0, (%0)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); - - DBGIO ( "[%08lx] => %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); + *qword = readq ( linda->regs + offset ); } #define linda_readq( _linda, _ptr, _offset ) \ - linda_readq ( (_linda), (_ptr)->u.dwords, (_offset) ) + linda_readq ( (_linda), (_ptr)->u.qwords, (_offset) ) #define linda_readq_array8b( _linda, _ptr, _offset, _idx ) \ linda_readq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define linda_readq_array64k( _linda, _ptr, _offset, _idx ) \ @@ -147,22 +136,15 @@ static void linda_readq ( struct linda *linda, uint32_t *dwords, * Write Linda qword register * * @v linda Linda device - * @v dwords Register buffer to write + * @v qword Register buffer to write * @v offset Register offset */ -static void linda_writeq ( struct linda *linda, const uint32_t *dwords, +static void linda_writeq ( struct linda *linda, const uint64_t *qword, unsigned long offset ) { - void *addr = ( linda->regs + offset ); - - DBGIO ( "[%08lx] <= %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); - - __asm__ __volatile__ ( "movq (%0), %%mm0\n\t" - "movq %%mm0, (%1)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); + writeq ( *qword, ( linda->regs + offset ) ); } #define linda_writeq( _linda, _ptr, _offset ) \ - linda_writeq ( (_linda), (_ptr)->u.dwords, (_offset) ) + linda_writeq ( (_linda), (_ptr)->u.qwords, (_offset) ) #define linda_writeq_array8b( _linda, _ptr, _offset, _idx ) \ linda_writeq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define linda_writeq_array64k( _linda, _ptr, _offset, _idx ) \ From 4350d26a04e38ecbac95164068cdea7d589c24a4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 17:51:59 +0000 Subject: [PATCH 131/591] [qib7322] Use standard readq() and writeq() implementations Signed-off-by: Michael Brown --- src/drivers/infiniband/qib7322.c | 34 ++++++++------------------------ 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c index e22f2349a..08cedcb7e 100644 --- a/src/drivers/infiniband/qib7322.c +++ b/src/drivers/infiniband/qib7322.c @@ -137,32 +137,21 @@ struct qib7322 { * This card requires atomic 64-bit accesses. Strange things happen * if you try to use 32-bit accesses; sometimes they work, sometimes * they don't, sometimes you get random data. - * - * These accessors use the "movq" MMX instruction, and so won't work - * on really old Pentiums (which won't have PCIe anyway, so this is - * something of a moot point). */ /** * Read QIB7322 qword register * * @v qib7322 QIB7322 device - * @v dwords Register buffer to read into + * @v qword Register buffer to read into * @v offset Register offset */ -static void qib7322_readq ( struct qib7322 *qib7322, uint32_t *dwords, +static void qib7322_readq ( struct qib7322 *qib7322, uint64_t *qword, unsigned long offset ) { - void *addr = ( qib7322->regs + offset ); - - __asm__ __volatile__ ( "movq (%1), %%mm0\n\t" - "movq %%mm0, (%0)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); - - DBGIO ( "[%08lx] => %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); + *qword = readq ( qib7322->regs + offset ); } #define qib7322_readq( _qib7322, _ptr, _offset ) \ - qib7322_readq ( (_qib7322), (_ptr)->u.dwords, (_offset) ) + qib7322_readq ( (_qib7322), (_ptr)->u.qwords, (_offset) ) #define qib7322_readq_array8b( _qib7322, _ptr, _offset, _idx ) \ qib7322_readq ( (_qib7322), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define qib7322_readq_array64k( _qib7322, _ptr, _offset, _idx ) \ @@ -174,22 +163,15 @@ static void qib7322_readq ( struct qib7322 *qib7322, uint32_t *dwords, * Write QIB7322 qword register * * @v qib7322 QIB7322 device - * @v dwords Register buffer to write + * @v qword Register buffer to write * @v offset Register offset */ -static void qib7322_writeq ( struct qib7322 *qib7322, const uint32_t *dwords, +static void qib7322_writeq ( struct qib7322 *qib7322, const uint64_t *qword, unsigned long offset ) { - void *addr = ( qib7322->regs + offset ); - - DBGIO ( "[%08lx] <= %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); - - __asm__ __volatile__ ( "movq (%0), %%mm0\n\t" - "movq %%mm0, (%1)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); + writeq ( *qword, ( qib7322->regs + offset ) ); } #define qib7322_writeq( _qib7322, _ptr, _offset ) \ - qib7322_writeq ( (_qib7322), (_ptr)->u.dwords, (_offset) ) + qib7322_writeq ( (_qib7322), (_ptr)->u.qwords, (_offset) ) #define qib7322_writeq_array8b( _qib7322, _ptr, _offset, _idx ) \ qib7322_writeq ( (_qib7322), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define qib7322_writeq_array64k( _qib7322, _ptr, _offset, _idx ) \ From 9eff4284bd0d0dd9ea53817c4a999bf9d9ce9454 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 17:55:38 +0000 Subject: [PATCH 132/591] [test] Add missing #include Signed-off-by: Michael Brown --- src/tests/rsa_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/rsa_test.c b/src/tests/rsa_test.c index c0d05d263..91066faab 100644 --- a/src/tests/rsa_test.c +++ b/src/tests/rsa_test.c @@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Forcibly enable assertions */ #undef NDEBUG +#include #include #include #include From 3c84178003495b258f7604b47325afd6a53a3d56 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 18:02:20 +0000 Subject: [PATCH 133/591] [serial] Add missing #include Signed-off-by: Michael Brown --- src/core/serial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/serial.c b/src/core/serial.c index 4ce025519..dd22f6731 100644 --- a/src/core/serial.c +++ b/src/core/serial.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include +#include #include #include #include From 7e78cdddc8a2d6446659ee2ceef45bfeea0a8285 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 18:06:47 +0000 Subject: [PATCH 134/591] [3c595] Fix compilation when "char" is unsigned by default Signed-off-by: Michael Brown --- src/drivers/net/3c595.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/3c595.c b/src/drivers/net/3c595.c index 2338c54b7..92d38cfc5 100644 --- a/src/drivers/net/3c595.c +++ b/src/drivers/net/3c595.c @@ -391,7 +391,7 @@ vxsetlink(void) { int i, j; char *reason, *warning; - static char prev_conn = -1; + static signed char prev_conn = -1; if (prev_conn == -1) { prev_conn = vx_connector; From 86f96a40f490b1f10662a2d94a86a01276d2a125 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 21:15:43 +0000 Subject: [PATCH 135/591] [tg3] Remove x86-specific inline assembly Signed-off-by: Michael Brown --- src/drivers/net/tg3/tg3.h | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index bfabad071..d29523a83 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -3320,43 +3320,25 @@ void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val); /* Functions & macros to verify TG3_FLAGS types */ -static inline int variable_test_bit(int nr, volatile const unsigned long *addr) -{ - int oldbit; - - asm volatile("bt %2,%1\n\t" - "sbb %0,%0" - : "=r" (oldbit) - : "m" (*(unsigned long *)addr), "Ir" (nr)); - - return oldbit; -} - static inline int _tg3_flag(enum TG3_FLAGS flag, unsigned long *bits) { - return variable_test_bit(flag, bits); -} - -#define BITOP_ADDR(x) "+m" (*(volatile long *) (x)) - -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - asm volatile("bts %1,%0" : BITOP_ADDR(addr) : "Ir" (nr) : "memory"); + unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); + unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); + return ( bits[index] & ( 1UL << bit ) ); } static inline void _tg3_flag_set(enum TG3_FLAGS flag, unsigned long *bits) { - __set_bit(flag, bits); -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - asm volatile("btr %1,%0" : BITOP_ADDR(addr) : "Ir" (nr)); + unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); + unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); + bits[index] |= ( 1UL << bit ); } static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) { - __clear_bit(flag, bits); + unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); + unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); + bits[index] &= ~( 1UL << bit ); } #define tg3_flag(tp, flag) \ From a8037ee131a9501ddfc89ce157e3718b1a3b86a7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 12 Mar 2016 21:47:13 +0000 Subject: [PATCH 136/591] [efi] Centralise architecture-independent EFI Makefile and linker script Signed-off-by: Michael Brown --- src/Makefile.efi | 46 ++++++++++++++++++++++++++++++ src/arch/x86/Makefile.efi | 46 ++---------------------------- src/{arch/x86 => }/scripts/efi.lds | 0 3 files changed, 49 insertions(+), 43 deletions(-) create mode 100644 src/Makefile.efi rename src/{arch/x86 => }/scripts/efi.lds (100%) diff --git a/src/Makefile.efi b/src/Makefile.efi new file mode 100644 index 000000000..151b33186 --- /dev/null +++ b/src/Makefile.efi @@ -0,0 +1,46 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# The EFI linker script +# +LDSCRIPT = scripts/efi.lds + +# Retain relocation information for elf2efi +# +LDFLAGS += -q -S + +# Media types. +# +NON_AUTO_MEDIA += efi +NON_AUTO_MEDIA += efidrv +NON_AUTO_MEDIA += drv.efi +NON_AUTO_MEDIA += efirom + +# Include SNP driver in the all-drivers build +# +DRIVERS_net += snp + +# Rules for building EFI files +# +$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(ELF2EFI) --subsystem=10 $< $@ + +$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(ELF2EFI) --subsystem=11 $< $@ + +$(BIN)/%.drv.efi : $(BIN)/%.efidrv + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(CP) $< $@ + +$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@ + +$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined + $(QM)$(ECHO) " [CAB] $@" + $(Q)$(LCAB) -n -q $(ALL_drv.efi) $@ + +$(BIN)/%.usb : $(BIN)/%.efi + $(QM)$(ECHO) " [GENEFIDSK] $@" + $(Q)bash util/genefidsk -o $@ -b $(EFI_BOOT_FILE) $< diff --git a/src/arch/x86/Makefile.efi b/src/arch/x86/Makefile.efi index c4bc2308c..f04be425b 100644 --- a/src/arch/x86/Makefile.efi +++ b/src/arch/x86/Makefile.efi @@ -1,46 +1,6 @@ # -*- makefile -*- : Force emacs to use Makefile mode -# The EFI linker script +# Include generic EFI Makefile # -LDSCRIPT = arch/x86/scripts/efi.lds - -# Retain relocation information for elf2efi -# -LDFLAGS += -q -S - -# Media types. -# -NON_AUTO_MEDIA += efi -NON_AUTO_MEDIA += efidrv -NON_AUTO_MEDIA += drv.efi -NON_AUTO_MEDIA += efirom - -# Include SNP driver in the all-drivers build -# -DRIVERS_net += snp - -# Rules for building EFI files -# -$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI) - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(ELF2EFI) --subsystem=10 $< $@ - -$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI) - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(ELF2EFI) --subsystem=11 $< $@ - -$(BIN)/%.drv.efi : $(BIN)/%.efidrv - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(CP) $< $@ - -$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM) - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@ - -$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined - $(QM)$(ECHO) " [CAB] $@" - $(Q)$(LCAB) -n -q $(ALL_drv.efi) $@ - -$(BIN)/%.usb : $(BIN)/%.efi - $(QM)$(ECHO) " [GENEFIDSK] $@" - $(Q)bash util/genefidsk -o $@ -b $(EFI_BOOT_FILE) $< +MAKEDEPS += Makefile.efi +include Makefile.efi diff --git a/src/arch/x86/scripts/efi.lds b/src/scripts/efi.lds similarity index 100% rename from src/arch/x86/scripts/efi.lds rename to src/scripts/efi.lds From 1f65ed53da16e383416ae034b585fd52682f5ea7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Mar 2016 11:09:01 +0000 Subject: [PATCH 137/591] [build] Allow assembler section type character to vary by architecture On some architectures (such as ARM) the "@" character is used as a comment delimiter. A section type argument such as "@progbits" therefore becomes "%progbits". This is further complicated by the fact that the "%" character has special meaning for inline assembly when input or output operands are used, in which cases "@progbits" becomes "%%progbits". Allow the section type character(s) to be defined via Makefile variables. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 7 ++++++- src/arch/x86/Makefile | 5 +++++ src/crypto/certstore.c | 2 +- src/crypto/privkey.c | 2 +- src/image/embedded.c | 2 +- src/include/compiler.h | 17 ++++++++++++++--- src/include/errno.h | 2 +- 7 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 0a80d2ac9..4280f398e 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -491,6 +491,11 @@ LDFLAGS += -static # CFLAGS += -include include/compiler.h +# The section type character (e.g. "@" in "@progbits") varies by +# architecture. +# +CFLAGS += -DASM_TCHAR='$(ASM_TCHAR)' -DASM_TCHAR_OPS='$(ASM_TCHAR_OPS)' + # CFLAGS for specific object types # CFLAGS_c += @@ -896,7 +901,7 @@ endif # Device ID tables (using IDs from ROM definition file) # define obj_pci_id_asm - .section ".pci_devlist.$(1)", "a", @progbits + .section ".pci_devlist.$(1)", "a", $(ASM_TCHAR)progbits .globl pci_devlist_$(1) pci_devlist_$(1): .short ( 0x$(1) & 0xffff ) diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 6ad8031fd..e933f4a4f 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -1,3 +1,8 @@ +# Assembler section type character +# +ASM_TCHAR := @ +ASM_TCHAR_OPS := @ + # Include common x86 headers # INCDIRS += arch/x86/include diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index 503ce499e..e62d8330b 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -45,7 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CERT( _index, _path ) \ extern char stored_cert_ ## _index ## _data[]; \ extern char stored_cert_ ## _index ## _len[]; \ - __asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" \ + __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \ "\nstored_cert_" #_index "_data:\n\t" \ ".incbin \"" _path "\"\n\t" \ "\nstored_cert_" #_index "_end:\n\t" \ diff --git a/src/crypto/privkey.c b/src/crypto/privkey.c index a6043bd1e..0b10e9cf8 100644 --- a/src/crypto/privkey.c +++ b/src/crypto/privkey.c @@ -54,7 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Raw private key data */ extern char private_key_data[]; extern char private_key_len[]; -__asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" +__asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" #ifdef PRIVATE_KEY ".incbin \"" PRIVATE_KEY "\"\n\t" diff --git a/src/image/embedded.c b/src/image/embedded.c index 48dd86851..376e5d299 100644 --- a/src/image/embedded.c +++ b/src/image/embedded.c @@ -18,7 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EMBED( _index, _path, _name ) \ extern char embedded_image_ ## _index ## _data[]; \ extern char embedded_image_ ## _index ## _len[]; \ - __asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" \ + __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \ "\nembedded_image_" #_index "_data:\n\t" \ ".incbin \"" _path "\"\n\t" \ "\nembedded_image_" #_index "_end:\n\t" \ diff --git a/src/include/compiler.h b/src/include/compiler.h index ca82f9523..32afb64cf 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -52,6 +52,17 @@ /** Stringify expanded argument */ #define _S2( x ) _S1 ( x ) +/* Assembler section types */ +#ifdef ASSEMBLY +#define PROGBITS _C2 ( ASM_TCHAR, progbits ) +#define NOBITS _C2 ( ASM_TCHAR, nobits ) +#else +#define PROGBITS_OPS _S2 ( ASM_TCHAR_OPS ) "progbits" +#define PROGBITS _S2 ( ASM_TCHAR ) "progbits" +#define NOBITS_OPS _S2 ( ASM_TCHAR_OPS ) "nobits" +#define NOBITS _S2 ( ASM_TCHAR ) "nobits" +#endif + /** * @defgroup symmacros Macros to provide or require explicit symbols * @{ @@ -64,7 +75,7 @@ */ #ifdef ASSEMBLY #define PROVIDE_SYMBOL( symbol ) \ - .section ".provided", "a", @nobits ; \ + .section ".provided", "a", NOBITS ; \ .hidden symbol ; \ .globl symbol ; \ symbol: ; \ @@ -139,14 +150,14 @@ */ #ifdef ASSEMBLY #define PROVIDE_REQUIRING_SYMBOL() \ - .section ".tbl.requiring_symbols", "a", @progbits ; \ + .section ".tbl.requiring_symbols", "a", PROGBITS ; \ __requiring_symbol__: .byte 0 ; \ .size __requiring_symbol__, . - __requiring_symbol__ ; \ .previous #else #define PROVIDE_REQUIRING_SYMBOL() \ __asm__ ( ".section \".tbl.requiring_symbols\", " \ - " \"a\", @progbits\n" \ + " \"a\", " PROGBITS "\n" \ "__requiring_symbol__:\t.byte 0\n" \ ".size __requiring_symbol__, " \ " . - __requiring_symbol__\n" \ diff --git a/src/include/errno.h b/src/include/errno.h index 036479aff..342384fa4 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -258,7 +258,7 @@ static inline void eplatform_discard ( int dummy __unused, ... ) {} * @ret error Error */ #define __einfo_error( einfo ) ( { \ - __asm__ ( ".section \".einfo\", \"\", @progbits\n\t" \ + __asm__ ( ".section \".einfo\", \"\", " PROGBITS_OPS "\n\t" \ ".align 8\n\t" \ "\n1:\n\t" \ ".long ( 4f - 1b )\n\t" \ From 0d29cf2a4da86f65f74f94695c98bd175f897db5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Mar 2016 11:32:54 +0000 Subject: [PATCH 138/591] [build] Accept CROSS= as a synonym for CROSS_COMPILE= Signed-off-by: Michael Brown --- src/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile b/src/Makefile index 3ffa1eba4..0524bc761 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,6 +9,7 @@ ASFLAGS := LDFLAGS := HOST_CFLAGS := MAKEDEPS := Makefile +CROSS_COMPILE ?= $(CROSS) ############################################################################### # From 24415a3eeefbdeb59c947363b1ab2b3796616092 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Mar 2016 11:47:30 +0000 Subject: [PATCH 139/591] [efi] Update to current EDK2 headers Signed-off-by: Michael Brown --- src/include/ipxe/efi/Base.h | 15 +++++++-- src/include/ipxe/efi/Ia32/ProcessorBind.h | 6 ++-- .../ipxe/efi/IndustryStandard/Acpi51.h | 12 ++++++- .../ipxe/efi/IndustryStandard/Acpi60.h | 33 +++++++++++++------ src/include/ipxe/efi/IndustryStandard/Tpm20.h | 5 ++- .../ipxe/efi/Protocol/HiiConfigAccess.h | 3 +- src/include/ipxe/efi/Protocol/LoadFile.h | 4 +-- src/include/ipxe/efi/Protocol/SerialIo.h | 4 +-- src/include/ipxe/efi/Protocol/SimpleNetwork.h | 21 +++++++++++- src/include/ipxe/efi/Uefi/UefiBaseType.h | 6 ++-- src/include/ipxe/efi/Uefi/UefiPxe.h | 22 ++++++++++++- src/include/ipxe/efi/Uefi/UefiSpec.h | 28 ++++++++-------- src/include/ipxe/efi/X64/ProcessorBind.h | 6 ++-- 13 files changed, 123 insertions(+), 42 deletions(-) diff --git a/src/include/ipxe/efi/Base.h b/src/include/ipxe/efi/Base.h index ed678a9ce..8a047aef0 100644 --- a/src/include/ipxe/efi/Base.h +++ b/src/include/ipxe/efi/Base.h @@ -6,7 +6,7 @@ environment. There are a set of base libraries in the Mde Package that can be used to implement base modules. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -942,6 +942,11 @@ typedef UINTN RETURN_STATUS; /// #define RETURN_COMPROMISED_DATA ENCODE_ERROR (33) +/// +/// A HTTP error occurred during the network operation. +/// +#define RETURN_HTTP_ERROR ENCODE_ERROR (35) + /// /// The string contained one or more characters that /// the device could not render and were skipped. @@ -971,6 +976,12 @@ typedef UINTN RETURN_STATUS; /// #define RETURN_WARN_STALE_DATA ENCODE_WARNING (5) +/// +/// The resulting buffer contains UEFI-compliant file system. +/// +#define RETURN_WARN_FILE_SYSTEM ENCODE_WARNING (6) + + /** Returns a 16-bit signature built from 2 ASCII characters. @@ -1024,7 +1035,7 @@ typedef UINTN RETURN_STATUS; #define SIGNATURE_64(A, B, C, D, E, F, G, H) \ (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) -#if defined(_MSC_EXTENSIONS) && !defined (MDE_CPU_EBC) +#if defined(_MSC_EXTENSIONS) && !defined (__INTEL_COMPILER) && !defined (MDE_CPU_EBC) #pragma intrinsic(_ReturnAddress) /** Get the return address of the calling funcation. diff --git a/src/include/ipxe/efi/Ia32/ProcessorBind.h b/src/include/ipxe/efi/Ia32/ProcessorBind.h index 8fb5fbcf2..375ff2d92 100644 --- a/src/include/ipxe/efi/Ia32/ProcessorBind.h +++ b/src/include/ipxe/efi/Ia32/ProcessorBind.h @@ -95,7 +95,7 @@ FILE_LICENCE ( BSD3 ); // #pragma warning ( disable : 4206 ) -#if _MSC_VER == 1800 +#if _MSC_VER == 1800 || _MSC_VER == 1900 // // Disable these warnings for VS2013. @@ -103,13 +103,13 @@ FILE_LICENCE ( BSD3 ); // // This warning is for potentially uninitialized local variable, and it may cause false -// positive issues in VS2013 build +// positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4701 ) // // This warning is for potentially uninitialized local pointer variable, and it may cause -// false positive issues in VS2013 build +// false positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4703 ) diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi51.h b/src/include/ipxe/efi/IndustryStandard/Acpi51.h index 3d0e46bc6..b00613942 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi51.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi51.h @@ -3,6 +3,7 @@ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -531,9 +532,18 @@ typedef struct { UINT32 GicId; UINT64 PhysicalBaseAddress; UINT32 SystemVectorBase; - UINT32 Reserved2; + UINT8 GicVersion; + UINT8 Reserved2[3]; } EFI_ACPI_5_1_GIC_DISTRIBUTOR_STRUCTURE; +/// +/// GIC Version +/// +#define EFI_ACPI_5_1_GIC_V2 0x01 +#define EFI_ACPI_5_1_GIC_V2m 0x02 +#define EFI_ACPI_5_1_GIC_V3 0x03 +#define EFI_ACPI_5_1_GIC_V4 0x04 + /// /// GIC MSI Frame Structure /// diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi60.h b/src/include/ipxe/efi/IndustryStandard/Acpi60.h index f235b3750..18eb5f7d9 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi60.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi60.h @@ -1,7 +1,8 @@ /** @file ACPI 6.0 definitions from the ACPI Specification Revision 6.0 April, 2015. - Copyright (c) 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -534,9 +535,18 @@ typedef struct { UINT32 GicId; UINT64 PhysicalBaseAddress; UINT32 SystemVectorBase; - UINT32 Reserved2; + UINT8 GicVersion; + UINT8 Reserved2[3]; } EFI_ACPI_6_0_GIC_DISTRIBUTOR_STRUCTURE; +/// +/// GIC Version +/// +#define EFI_ACPI_6_0_GIC_V1 0x01 +#define EFI_ACPI_6_0_GIC_V2 0x02 +#define EFI_ACPI_6_0_GIC_V3 0x03 +#define EFI_ACPI_6_0_GIC_V4 0x04 + /// /// GIC MSI Frame Structure /// @@ -1385,14 +1395,14 @@ typedef struct { // #define EFI_ACPI_6_0_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_FLAGS_CONTROL_REGION_FOR_MANAGEMENT BIT0 #define EFI_ACPI_6_0_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_FLAGS_PROXIMITY_DOMAIN_VALID BIT1 -#define EFI_ACPI_6_0_NFIT_GUID_VOLATILE_MEMORY_REGION { 0x7305944F, 0xFDDA, 0x44E3, 0xB1, 0x6C, 0x3F, 0x22, 0xD2, 0x52, 0xE5, 0xD0 } -#define EFI_ACPI_6_0_NFIT_GUID_BYTE_ADDRESSABLE_PERSISTENT_MEMORY_REGION { 0x66F0D379, 0xB4F3, 0x4074, 0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB } -#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_CONTROL_REGION { 0x92F701F6, 0x13B4, 0x405D, 0x91, 0x0B, 0x29, 0x93, 0x67, 0xE8, 0x23, 0x4C } -#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_BLOCK_DATA_WINDOW_REGION { 0x91AF0530, 0x5D86, 0x470E, 0xA6, 0xB0, 0x0A, 0x2D, 0xB9, 0x40, 0x82, 0x49 } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_VOLATILE { 0x77AB535A, 0x45FC, 0x624B, 0x55, 0x60, 0xF7, 0xB2, 0x81, 0xD1, 0xF9, 0x6E } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_VOLATILE { 0x3D5ABD30, 0x4175, 0x87CE, 0x6D, 0x64, 0xD2, 0xAD, 0xE5, 0x23, 0xC4, 0xBB } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_PERSISTENT { 0x5CEA02C9, 0x4D07, 0x69D3, 0x26, 0x9F ,0x44, 0x96, 0xFB, 0xE0, 0x96, 0xF9 } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_PERSISTENT { 0x08018188, 0x42CD, 0xBB48, 0x10, 0x0F, 0x53, 0x87, 0xD5, 0x3D, 0xED, 0x3D } +#define EFI_ACPI_6_0_NFIT_GUID_VOLATILE_MEMORY_REGION { 0x7305944F, 0xFDDA, 0x44E3, { 0xB1, 0x6C, 0x3F, 0x22, 0xD2, 0x52, 0xE5, 0xD0 }} +#define EFI_ACPI_6_0_NFIT_GUID_BYTE_ADDRESSABLE_PERSISTENT_MEMORY_REGION { 0x66F0D379, 0xB4F3, 0x4074, { 0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB }} +#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_CONTROL_REGION { 0x92F701F6, 0x13B4, 0x405D, { 0x91, 0x0B, 0x29, 0x93, 0x67, 0xE8, 0x23, 0x4C }} +#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_BLOCK_DATA_WINDOW_REGION { 0x91AF0530, 0x5D86, 0x470E, { 0xA6, 0xB0, 0x0A, 0x2D, 0xB9, 0x40, 0x82, 0x49 }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_VOLATILE { 0x77AB535A, 0x45FC, 0x624B, { 0x55, 0x60, 0xF7, 0xB2, 0x81, 0xD1, 0xF9, 0x6E }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_VOLATILE { 0x3D5ABD30, 0x4175, 0x87CE, { 0x6D, 0x64, 0xD2, 0xAD, 0xE5, 0x23, 0xC4, 0xBB }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_PERSISTENT { 0x5CEA02C9, 0x4D07, 0x69D3, { 0x26, 0x9F ,0x44, 0x96, 0xFB, 0xE0, 0x96, 0xF9 }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_PERSISTENT { 0x08018188, 0x42CD, 0xBB48, { 0x10, 0x0F, 0x53, 0x87, 0xD5, 0x3D, 0xED, 0x3D }} typedef struct { UINT16 Type; UINT16 Length; @@ -1658,6 +1668,9 @@ typedef struct { #define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_LOCAL_INTERRUPT 0x02 #define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_SCI 0x03 #define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_NMI 0x04 +#define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_CMCI 0x05 +#define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_MCE 0x06 +#define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_GPIO_SIGNAL 0x07 /// /// Hardware Error Notification Configuration Write Enable Structure Definition diff --git a/src/include/ipxe/efi/IndustryStandard/Tpm20.h b/src/include/ipxe/efi/IndustryStandard/Tpm20.h index 0a5f1c0e1..656bf21eb 100644 --- a/src/include/ipxe/efi/IndustryStandard/Tpm20.h +++ b/src/include/ipxe/efi/IndustryStandard/Tpm20.h @@ -677,7 +677,10 @@ typedef UINT32 TPM_RH; #define TPM_RH_LOCKOUT (TPM_RH)(0x4000000A) #define TPM_RH_ENDORSEMENT (TPM_RH)(0x4000000B) #define TPM_RH_PLATFORM (TPM_RH)(0x4000000C) -#define TPM_RH_LAST (TPM_RH)(0x4000000C) +#define TPM_RH_PLATFORM_NV (TPM_RH)(0x4000000D) +#define TPM_RH_AUTH_00 (TPM_RH)(0x40000010) +#define TPM_RH_AUTH_FF (TPM_RH)(0x4000010F) +#define TPM_RH_LAST (TPM_RH)(0x4000010F) // Table 28 - TPM_HC Constants typedef TPM_HANDLE TPM_HC; diff --git a/src/include/ipxe/efi/Protocol/HiiConfigAccess.h b/src/include/ipxe/efi/Protocol/HiiConfigAccess.h index 17ce3fdcc..df9080591 100644 --- a/src/include/ipxe/efi/Protocol/HiiConfigAccess.h +++ b/src/include/ipxe/efi/Protocol/HiiConfigAccess.h @@ -5,7 +5,7 @@ This protocol is published by drivers providing and requesting configuration data from HII. It may only be invoked by HII. -Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -36,6 +36,7 @@ typedef UINTN EFI_BROWSER_ACTION; #define EFI_BROWSER_ACTION_RETRIEVE 2 #define EFI_BROWSER_ACTION_FORM_OPEN 3 #define EFI_BROWSER_ACTION_FORM_CLOSE 4 +#define EFI_BROWSER_ACTION_SUBMITTED 5 #define EFI_BROWSER_ACTION_DEFAULT_STANDARD 0x1000 #define EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING 0x1001 #define EFI_BROWSER_ACTION_DEFAULT_SAFE 0x1002 diff --git a/src/include/ipxe/efi/Protocol/LoadFile.h b/src/include/ipxe/efi/Protocol/LoadFile.h index 99387e89f..ba80fdc16 100644 --- a/src/include/ipxe/efi/Protocol/LoadFile.h +++ b/src/include/ipxe/efi/Protocol/LoadFile.h @@ -7,7 +7,7 @@ UEFI 2.0 can boot from any device that produces a LoadFile protocol. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -66,7 +66,7 @@ typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE_INTERFACE; @retval EFI_NO_RESPONSE The remote system did not respond. @retval EFI_NOT_FOUND The file was not found. @retval EFI_ABORTED The file load process was manually cancelled. - + @retval EFI_WARN_FILE_SYSTEM The resulting Buffer contains UEFI-compliant file system. **/ typedef EFI_STATUS diff --git a/src/include/ipxe/efi/Protocol/SerialIo.h b/src/include/ipxe/efi/Protocol/SerialIo.h index a96e5e945..130a6ecdc 100644 --- a/src/include/ipxe/efi/Protocol/SerialIo.h +++ b/src/include/ipxe/efi/Protocol/SerialIo.h @@ -4,7 +4,7 @@ Abstraction of a basic serial device. Targeted at 16550 UART, but could be much more generic. - Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -107,7 +107,7 @@ EFI_STATUS /** Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, - data buts, and stop bits on a serial device. + data bits, and stop bits on a serial device. @param This Protocol instance pointer. @param BaudRate The requested baud rate. A BaudRate value of 0 will use the diff --git a/src/include/ipxe/efi/Protocol/SimpleNetwork.h b/src/include/ipxe/efi/Protocol/SimpleNetwork.h index 2b521a9de..2faa668f3 100644 --- a/src/include/ipxe/efi/Protocol/SimpleNetwork.h +++ b/src/include/ipxe/efi/Protocol/SimpleNetwork.h @@ -9,7 +9,7 @@ MCast - MultiCast ... -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -123,6 +123,25 @@ typedef struct { /// UINT64 UnsupportedProtocol; + /// + /// Number of valid frames received that were duplicated. + /// + UINT64 RxDuplicatedFrames; + + /// + /// Number of encrypted frames received that failed to decrypt. + /// + UINT64 RxDecryptErrorFrames; + + /// + /// Number of frames that failed to transmit after exceeding the retry limit. + /// + UINT64 TxErrorFrames; + + /// + /// Number of frames transmitted successfully after more than one attempt. + /// + UINT64 TxRetryFrames; } EFI_NETWORK_STATISTICS; /// diff --git a/src/include/ipxe/efi/Uefi/UefiBaseType.h b/src/include/ipxe/efi/Uefi/UefiBaseType.h index 371dae649..ef0ea671a 100644 --- a/src/include/ipxe/efi/Uefi/UefiBaseType.h +++ b/src/include/ipxe/efi/Uefi/UefiBaseType.h @@ -1,8 +1,8 @@ /** @file Defines data types and constants introduced in UEFI. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
-Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Portions copyright (c) 2011 - 2016, ARM Ltd. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. @@ -153,12 +153,14 @@ typedef union { #define EFI_END_OF_FILE RETURN_END_OF_FILE #define EFI_INVALID_LANGUAGE RETURN_INVALID_LANGUAGE #define EFI_COMPROMISED_DATA RETURN_COMPROMISED_DATA +#define EFI_HTTP_ERROR RETURN_HTTP_ERROR #define EFI_WARN_UNKNOWN_GLYPH RETURN_WARN_UNKNOWN_GLYPH #define EFI_WARN_DELETE_FAILURE RETURN_WARN_DELETE_FAILURE #define EFI_WARN_WRITE_FAILURE RETURN_WARN_WRITE_FAILURE #define EFI_WARN_BUFFER_TOO_SMALL RETURN_WARN_BUFFER_TOO_SMALL #define EFI_WARN_STALE_DATA RETURN_WARN_STALE_DATA +#define EFI_WARN_FILE_SYSTEM RETURN_WARN_FILE_SYSTEM ///@} /// diff --git a/src/include/ipxe/efi/Uefi/UefiPxe.h b/src/include/ipxe/efi/Uefi/UefiPxe.h index 5c0b2038f..13be21aae 100644 --- a/src/include/ipxe/efi/Uefi/UefiPxe.h +++ b/src/include/ipxe/efi/Uefi/UefiPxe.h @@ -3,7 +3,7 @@ structure prototypes, global variables and constants that are needed for porting PXE to EFI. -Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -1460,6 +1460,26 @@ typedef struct s_pxe_db_statistics { /// #define PXE_STATISTICS_UNSUPPORTED_PROTOCOL 0x15 +/// +/// Number of valid frames received that were duplicated. +/// +#define PXE_STATISTICS_RX_DUPLICATED_FRAMES 0x16 + +/// +/// Number of encrypted frames received that failed to decrypt. +/// +#define PXE_STATISTICS_RX_DECRYPT_ERROR_FRAMES 0x17 + +/// +/// Number of frames that failed to transmit after exceeding the retry limit. +/// +#define PXE_STATISTICS_TX_ERROR_FRAMES 0x18 + +/// +/// Number of frames transmitted successfully after more than one attempt. +/// +#define PXE_STATISTICS_TX_RETRY_FRAMES 0x19 + typedef struct s_pxe_cpb_mcast_ip_to_mac { /// /// Multicast IP address to be converted to multicast MAC address. diff --git a/src/include/ipxe/efi/Uefi/UefiSpec.h b/src/include/ipxe/efi/Uefi/UefiSpec.h index ee276ffed..98ac8765f 100644 --- a/src/include/ipxe/efi/Uefi/UefiSpec.h +++ b/src/include/ipxe/efi/Uefi/UefiSpec.h @@ -1,11 +1,11 @@ /** @file Include file that supports UEFI. - This include file must contain things defined in the UEFI 2.5 specification. - If a code construct is defined in the UEFI 2.5 specification it must be included + This include file must contain things defined in the UEFI 2.6 specification. + If a code construct is defined in the UEFI 2.6 specification it must be included by this include file. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -138,8 +138,7 @@ typedef struct { MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders - that are provided by operating system vendors. The only illegal - memory type values are those in the range EfiMaxMemoryType..0x6FFFFFFF. + that are provided by operating system vendors. @param[in] Pages The number of contiguous 4 KB pages to allocate. @param[in, out] Memory The pointer to a physical address. On input, the way in which the address is used depends on the value of Type. @@ -150,7 +149,7 @@ typedef struct { 2) MemoryType is in the range EfiMaxMemoryType..0x6FFFFFFF. 3) Memory is NULL. - 4) MemoryType was EfiPersistentMemory. + 4) MemoryType is EfiPersistentMemory. @retval EFI_OUT_OF_RESOURCES The pages could not be allocated. @retval EFI_NOT_FOUND The requested pages could not be found. @@ -225,16 +224,16 @@ EFI_STATUS MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders - that are provided by operating system vendors. The only illegal - memory type values are those in the range EfiMaxMemoryType..0x6FFFFFFF. + that are provided by operating system vendors. @param[in] Size The number of bytes to allocate from the pool. @param[out] Buffer A pointer to a pointer to the allocated buffer if the call succeeds; undefined otherwise. @retval EFI_SUCCESS The requested number of bytes was allocated. @retval EFI_OUT_OF_RESOURCES The pool requested could not be allocated. - @retval EFI_INVALID_PARAMETER PoolType was invalid or Buffer is NULL. - PoolType was EfiPersistentMemory. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. + PoolType is EfiPersistentMemory. **/ typedef @@ -630,7 +629,8 @@ VOID attributes bitmask for the variable. @param[in, out] DataSize On input, the size in bytes of the return Data buffer. On output the size of data returned in Data. - @param[out] Data The buffer to return the contents of the variable. + @param[out] Data The buffer to return the contents of the variable. May be NULL + with a zero DataSize in order to determine the size buffer needed. @retval EFI_SUCCESS The function completed successfully. @retval EFI_NOT_FOUND The variable was not found. @@ -650,7 +650,7 @@ EFI_STATUS IN EFI_GUID *VendorGuid, OUT UINT32 *Attributes, OPTIONAL IN OUT UINTN *DataSize, - OUT VOID *Data + OUT VOID *Data OPTIONAL ); /** @@ -1752,11 +1752,13 @@ EFI_STATUS #define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004 #define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED 0x0000000000000008 #define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED 0x0000000000000010 +#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY 0x0000000000000040 // // EFI Runtime Services Table // #define EFI_SYSTEM_TABLE_SIGNATURE SIGNATURE_64 ('I','B','I',' ','S','Y','S','T') +#define EFI_2_60_SYSTEM_TABLE_REVISION ((2 << 16) | (60)) #define EFI_2_50_SYSTEM_TABLE_REVISION ((2 << 16) | (50)) #define EFI_2_40_SYSTEM_TABLE_REVISION ((2 << 16) | (40)) #define EFI_2_31_SYSTEM_TABLE_REVISION ((2 << 16) | (31)) @@ -1766,7 +1768,7 @@ EFI_STATUS #define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) #define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) #define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) -#define EFI_SYSTEM_TABLE_REVISION EFI_2_50_SYSTEM_TABLE_REVISION +#define EFI_SYSTEM_TABLE_REVISION EFI_2_60_SYSTEM_TABLE_REVISION #define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION #define EFI_RUNTIME_SERVICES_SIGNATURE SIGNATURE_64 ('R','U','N','T','S','E','R','V') diff --git a/src/include/ipxe/efi/X64/ProcessorBind.h b/src/include/ipxe/efi/X64/ProcessorBind.h index 8ce9d0fb1..b64c25c0f 100644 --- a/src/include/ipxe/efi/X64/ProcessorBind.h +++ b/src/include/ipxe/efi/X64/ProcessorBind.h @@ -96,7 +96,7 @@ FILE_LICENCE ( BSD3 ); // #pragma warning ( disable : 4206 ) -#if _MSC_VER == 1800 +#if _MSC_VER == 1800 || _MSC_VER == 1900 // // Disable these warnings for VS2013. @@ -104,13 +104,13 @@ FILE_LICENCE ( BSD3 ); // // This warning is for potentially uninitialized local variable, and it may cause false -// positive issues in VS2013 build +// positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4701 ) // // This warning is for potentially uninitialized local pointer variable, and it may cause -// false positive issues in VS2013 build +// false positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4703 ) From 11ccfb67fab3f2eac178551f980ec035e74d0a46 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Mar 2016 11:54:33 +0000 Subject: [PATCH 140/591] [efi] Add processor binding headers for ARM and AArch64 Signed-off-by: Michael Brown --- src/include/ipxe/efi/AArch64/ProcessorBind.h | 150 ++++++++++++++++ src/include/ipxe/efi/Arm/ProcessorBind.h | 171 +++++++++++++++++++ src/include/ipxe/efi/ProcessorBind.h | 8 + 3 files changed, 329 insertions(+) create mode 100644 src/include/ipxe/efi/AArch64/ProcessorBind.h create mode 100644 src/include/ipxe/efi/Arm/ProcessorBind.h diff --git a/src/include/ipxe/efi/AArch64/ProcessorBind.h b/src/include/ipxe/efi/AArch64/ProcessorBind.h new file mode 100644 index 000000000..d4301726f --- /dev/null +++ b/src/include/ipxe/efi/AArch64/ProcessorBind.h @@ -0,0 +1,150 @@ +/** @file + Processor or Compiler specific defines and types for AArch64. + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PROCESSOR_BIND_H__ +#define __PROCESSOR_BIND_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// Define the processor type so other code can make processor based choices +/// +#define MDE_CPU_AARCH64 + +// +// Make sure we are using the correct packing rules per EFI specification +// +#ifndef __GNUC__ +#pragma pack() +#endif + +#if _MSC_EXTENSIONS + // + // use Microsoft* C complier dependent integer width types + // + typedef unsigned __int64 UINT64; + typedef __int64 INT64; + typedef unsigned __int32 UINT32; + typedef __int32 INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#else + // + // Assume standard AARCH64 alignment. + // + typedef unsigned long long UINT64; + typedef long long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#endif + +/// +/// Unsigned value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef UINT64 UINTN; + +/// +/// Signed value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef INT64 INTN; + +// +// Processor specific defines +// + +/// +/// A value of native width with the highest bit set. +/// +#define MAX_BIT 0x8000000000000000ULL + +/// +/// A value of native width with the two highest bits set. +/// +#define MAX_2_BITS 0xC000000000000000ULL + +/// +/// Maximum legal AARCH64 address +/// +#define MAX_ADDRESS 0xFFFFFFFFFFFFFFFFULL + +/// +/// Maximum legal AArch64 INTN and UINTN values. +/// +#define MAX_INTN ((INTN)0x7FFFFFFFFFFFFFFFULL) +#define MAX_UINTN ((UINTN)0xFFFFFFFFFFFFFFFFULL) + +/// +/// The stack alignment required for AARCH64 +/// +#define CPU_STACK_ALIGNMENT 16 + +// +// Modifier to ensure that all protocol member functions and EFI intrinsics +// use the correct C calling convention. All protocol member functions and +// EFI intrinsics are required to modify their member functions with EFIAPI. +// +#define EFIAPI + +// When compiling with Clang, we still use GNU as for the assembler, so we still +// need to define the GCC_ASM* macros. +#if defined(__GNUC__) || defined(__clang__) + /// + /// For GNU assembly code, .global or .globl can declare global symbols. + /// Define this macro to unify the usage. + /// + #define ASM_GLOBAL .globl + + #define GCC_ASM_EXPORT(func__) \ + .global _CONCATENATE (__USER_LABEL_PREFIX__, func__) ;\ + .type ASM_PFX(func__), %function + + #define GCC_ASM_IMPORT(func__) \ + .extern _CONCATENATE (__USER_LABEL_PREFIX__, func__) + +#endif + +/** + Return the pointer to the first instruction of a function given a function pointer. + On ARM CPU architectures, these two pointer values are the same, + so the implementation of this macro is very simple. + + @param FunctionPointer A pointer to a function. + + @return The pointer to the first instruction of a function given a function pointer. + +**/ +#define FUNCTION_ENTRY_POINT(FunctionPointer) (VOID *)(UINTN)(FunctionPointer) + +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ +#endif + +#endif diff --git a/src/include/ipxe/efi/Arm/ProcessorBind.h b/src/include/ipxe/efi/Arm/ProcessorBind.h new file mode 100644 index 000000000..51a727145 --- /dev/null +++ b/src/include/ipxe/efi/Arm/ProcessorBind.h @@ -0,0 +1,171 @@ +/** @file + Processor or Compiler specific defines and types for ARM. + + Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+ Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PROCESSOR_BIND_H__ +#define __PROCESSOR_BIND_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// Define the processor type so other code can make processor based choices +/// +#define MDE_CPU_ARM + +// +// Make sure we are using the correct packing rules per EFI specification +// +#ifndef __GNUC__ +#pragma pack() +#endif + +#if _MSC_EXTENSIONS + // + // use Microsoft* C complier dependent integer width types + // + typedef unsigned __int64 UINT64; + typedef __int64 INT64; + typedef unsigned __int32 UINT32; + typedef __int32 INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#else + // + // Assume standard ARM alignment. + // Need to check portability of long long + // + typedef unsigned long long UINT64; + typedef long long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#endif + +/// +/// Unsigned value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef UINT32 UINTN; + +/// +/// Signed value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef INT32 INTN; + +// +// Processor specific defines +// + +/// +/// A value of native width with the highest bit set. +/// +#define MAX_BIT 0x80000000 + +/// +/// A value of native width with the two highest bits set. +/// +#define MAX_2_BITS 0xC0000000 + +/// +/// Maximum legal ARM address +/// +#define MAX_ADDRESS 0xFFFFFFFF + +/// +/// Maximum legal ARM INTN and UINTN values. +/// +#define MAX_INTN ((INTN)0x7FFFFFFF) +#define MAX_UINTN ((UINTN)0xFFFFFFFF) + +/// +/// The stack alignment required for ARM +/// +#define CPU_STACK_ALIGNMENT sizeof(UINT64) + +// +// Modifier to ensure that all protocol member functions and EFI intrinsics +// use the correct C calling convention. All protocol member functions and +// EFI intrinsics are required to modify their member functions with EFIAPI. +// +#define EFIAPI + +// When compiling with Clang, we still use GNU as for the assembler, so we still +// need to define the GCC_ASM* macros. +#if defined(__GNUC__) || defined(__clang__) + /// + /// For GNU assembly code, .global or .globl can declare global symbols. + /// Define this macro to unify the usage. + /// + #define ASM_GLOBAL .globl + + #if !defined(__APPLE__) + /// + /// ARM EABI defines that the linker should not manipulate call relocations + /// (do bl/blx conversion) unless the target symbol has function type. + /// CodeSourcery 2010.09 started requiring the .type to function properly + /// + #define INTERWORK_FUNC(func__) .type ASM_PFX(func__), %function + + #define GCC_ASM_EXPORT(func__) \ + .global _CONCATENATE (__USER_LABEL_PREFIX__, func__) ;\ + .type ASM_PFX(func__), %function + + #define GCC_ASM_IMPORT(func__) \ + .extern _CONCATENATE (__USER_LABEL_PREFIX__, func__) + + #else + // + // .type not supported by Apple Xcode tools + // + #define INTERWORK_FUNC(func__) + + #define GCC_ASM_EXPORT(func__) \ + .globl _CONCATENATE (__USER_LABEL_PREFIX__, func__) \ + + #define GCC_ASM_IMPORT(name) + + #endif +#endif + +/** + Return the pointer to the first instruction of a function given a function pointer. + On ARM CPU architectures, these two pointer values are the same, + so the implementation of this macro is very simple. + + @param FunctionPointer A pointer to a function. + + @return The pointer to the first instruction of a function given a function pointer. + +**/ +#define FUNCTION_ENTRY_POINT(FunctionPointer) (VOID *)(UINTN)(FunctionPointer) + +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ +#endif + +#endif + + diff --git a/src/include/ipxe/efi/ProcessorBind.h b/src/include/ipxe/efi/ProcessorBind.h index 7466814fa..ff1517f33 100644 --- a/src/include/ipxe/efi/ProcessorBind.h +++ b/src/include/ipxe/efi/ProcessorBind.h @@ -18,4 +18,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #endif +#if __arm__ +#include +#endif + +#if __aarch64__ +#include +#endif + #endif /* _IPXE_EFI_PROCESSOR_BIND_H */ From 17c1488a441756974d77d52a2b84c9c439327b47 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Mar 2016 14:51:15 +0000 Subject: [PATCH 141/591] [uri] Support URIs containing only scheme and path components Signed-off-by: Michael Brown --- src/core/uri.c | 5 ++--- src/tests/uri_test.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index aa6eedb91..0abd8bdcd 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -456,7 +456,6 @@ unsigned int uri_port ( const struct uri *uri, unsigned int default_port ) { */ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { static const char prefixes[URI_FIELDS] = { - [URI_OPAQUE] = ':', [URI_PASSWORD] = ':', [URI_PORT] = ':', [URI_QUERY] = '?', @@ -495,9 +494,9 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { ( buf + used ), ( len - used ) ); /* Suffix this field, if applicable */ - if ( ( field == URI_SCHEME ) && ( ! uri->opaque ) ) { + if ( field == URI_SCHEME ) { used += ssnprintf ( ( buf + used ), ( len - used ), - "://" ); + ":%s", ( uri->host ? "//" : "" ) ); } } diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 57f211aaf..add6e468c 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -610,6 +610,34 @@ static struct uri_test uri_iscsi = { }, }; +/** File URI with relative (opaque) path */ +static struct uri_test uri_file_relative = { + "file:script.ipxe", + { + .scheme = "file", + .opaque = "script.ipxe", + }, +}; + +/** File URI with absolute path */ +static struct uri_test uri_file_absolute = { + "file:/boot/script.ipxe", + { + .scheme = "file", + .path = "/boot/script.ipxe", + }, +}; + +/** File URI with volume name */ +static struct uri_test uri_file_volume = { + "file://hpilo/boot/script.ipxe", + { + .scheme = "file", + .host = "hpilo", + .path = "/boot/script.ipxe", + }, +}; + /** URI with port number */ static struct uri_port_test uri_explicit_port = { "http://192.168.0.1:8080/boot.php", @@ -899,6 +927,9 @@ static void uri_test_exec ( void ) { uri_parse_format_dup_ok ( &uri_ipv6_local ); uri_parse_ok ( &uri_ipv6_local_non_conforming ); /* Parse only */ uri_parse_format_dup_ok ( &uri_iscsi ); + uri_parse_format_dup_ok ( &uri_file_relative ); + uri_parse_format_dup_ok ( &uri_file_absolute ); + uri_parse_format_dup_ok ( &uri_file_volume ); /** URI port number tests */ uri_port_ok ( &uri_explicit_port ); From 75496817c2d28aef8b91fce8185814f8d39f69ab Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Mar 2016 17:39:17 +0000 Subject: [PATCH 142/591] [uri] Support "file:" URIs describing relative paths Signed-off-by: Michael Brown --- src/core/uri.c | 2 +- src/tests/uri_test.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index 0abd8bdcd..73ad2b227 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -157,7 +157,7 @@ static int uri_character_escaped ( char c, unsigned int field ) { * the reparsing of the URI, allowing everything else * (e.g. ':', which will appear in iSCSI URIs). */ - [URI_OPAQUE] = "/#", + [URI_OPAQUE] = "#", /* User name: escape everything */ [URI_USER] = "/#:@?", /* Password: escape everything */ diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index add6e468c..92c2f9037 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -612,10 +612,10 @@ static struct uri_test uri_iscsi = { /** File URI with relative (opaque) path */ static struct uri_test uri_file_relative = { - "file:script.ipxe", + "file:boot/script.ipxe", { .scheme = "file", - .opaque = "script.ipxe", + .opaque = "boot/script.ipxe", }, }; From 9913a405ea151706770559d42e758bc2c1da8525 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Mar 2016 15:23:42 +0000 Subject: [PATCH 143/591] [efi] Provide access to files stored on EFI filesystems Provide access to local files via the "file://" URI scheme. There are three syntaxes: - An opaque URI with a relative path (e.g. "file:script.ipxe"). This will be interpreted as a path relative to the iPXE binary. - A hierarchical URI with a non-network absolute path (e.g. "file:/boot/script.ipxe"). This will be interpreted as a path relative to the root of the filesystem from which the iPXE binary was loaded. - A hierarchical URI with a network path in which the authority is a volume label (e.g. "file://bootdisk/script.ipxe"). This will be interpreted as a path relative to the root of the filesystem with the specified volume label. Note that the potentially desirable shell mappings (e.g. "fs0:" and "blk0:") are concepts internal to the UEFI shell binary, and do not seem to be exposed in any way to external executables. The old EFI_SHELL_PROTOCOL (which did provide access to these mappings) is no longer installed by current versions of the UEFI shell. Signed-off-by: Michael Brown --- src/config/config_efi.c | 4 + src/config/defaults/efi.h | 2 + src/config/general.h | 1 + src/include/ipxe/errfile.h | 1 + src/interface/efi/efi_local.c | 573 ++++++++++++++++++++++++++++++++++ 5 files changed, 581 insertions(+) create mode 100644 src/interface/efi/efi_local.c diff --git a/src/config/config_efi.c b/src/config/config_efi.c index 1f73dad4d..92678d12d 100644 --- a/src/config/config_efi.c +++ b/src/config/config_efi.c @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include /** @file @@ -45,3 +46,6 @@ REQUIRE_OBJECT ( efi_fbcon ); #ifdef CONSOLE_FRAMEBUFFER REQUIRE_OBJECT ( efi_fbcon ); #endif +#ifdef DOWNLOAD_PROTO_FILE +REQUIRE_OBJECT ( efi_local ); +#endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 24b93a02d..ea9c31e27 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define TIME_EFI #define REBOOT_EFI +#define DOWNLOAD_PROTO_FILE /* Local filesystem access */ + #define IMAGE_EFI /* EFI image support */ #define IMAGE_SCRIPT /* iPXE script image support */ diff --git a/src/config/general.h b/src/config/general.h index e9b781fbf..675c11e07 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -57,6 +57,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ #undef DOWNLOAD_PROTO_NFS /* Network File System Protocol */ +//#undef DOWNLOAD_PROTO_FILE /* Local filesystem access */ /* * SAN boot protocols diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 4129861ad..638931578 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -347,6 +347,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_pxe ( ERRFILE_OTHER | 0x004a0000 ) #define ERRFILE_efi_usb ( ERRFILE_OTHER | 0x004b0000 ) #define ERRFILE_efi_fbcon ( ERRFILE_OTHER | 0x004c0000 ) +#define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) /** @} */ diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c new file mode 100644 index 000000000..bd010ad2e --- /dev/null +++ b/src/interface/efi/efi_local.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2016 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 +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI local file access + * + */ + +/** Download blocksize */ +#define EFI_LOCAL_BLKSIZE 4096 + +/** An EFI local file */ +struct efi_local { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Download process */ + struct process process; + + /** EFI root directory */ + EFI_FILE_PROTOCOL *root; + /** EFI file */ + EFI_FILE_PROTOCOL *file; + /** Length of file */ + size_t len; +}; + +/** + * Close local file + * + * @v local Local file + * @v rc Reason for close + */ +static void efi_local_close ( struct efi_local *local, int rc ) { + + /* Stop process */ + process_del ( &local->process ); + + /* Shut down data transfer interface */ + intf_shutdown ( &local->xfer, rc ); + + /* Close EFI file */ + if ( local->file ) { + local->file->Close ( local->file ); + local->file = NULL; + } + + /* Close EFI root directory */ + if ( local->root ) { + local->root->Close ( local->root ); + local->root = NULL; + } +} + +/** + * Local file process + * + * @v local Local file + */ +static void efi_local_step ( struct efi_local *local ) { + EFI_FILE_PROTOCOL *file = local->file; + struct io_buffer *iobuf = NULL; + size_t remaining; + size_t frag_len; + UINTN size; + EFI_STATUS efirc; + int rc; + + /* Wait until data transfer interface is ready */ + if ( ! xfer_window ( &local->xfer ) ) + return; + + /* Presize receive buffer */ + remaining = local->len; + xfer_seek ( &local->xfer, remaining ); + xfer_seek ( &local->xfer, 0 ); + + /* Get file contents */ + while ( remaining ) { + + /* Calculate length for this fragment */ + frag_len = remaining; + if ( frag_len > EFI_LOCAL_BLKSIZE ) + frag_len = EFI_LOCAL_BLKSIZE; + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &local->xfer, frag_len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err; + } + + /* Read block */ + size = frag_len; + if ( ( efirc = file->Read ( file, &size, iobuf->data ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not read from file: %s\n", + local, strerror ( rc ) ); + goto err; + } + assert ( size <= frag_len ); + iob_put ( iobuf, size ); + + /* Deliver data */ + if ( ( rc = xfer_deliver_iob ( &local->xfer, + iob_disown ( iobuf ) ) ) != 0 ) { + DBGC ( local, "LOCAL %p could not deliver data: %s\n", + local, strerror ( rc ) ); + goto err; + } + + /* Move to next block */ + remaining -= frag_len; + } + + /* Close download */ + efi_local_close ( local, 0 ); + + return; + + err: + free_iob ( iobuf ); + efi_local_close ( local, rc ); +} + +/** Data transfer interface operations */ +static struct interface_operation efi_local_operations[] = { + INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ), + INTF_OP ( intf_close, struct efi_local *, efi_local_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor efi_local_xfer_desc = + INTF_DESC ( struct efi_local, xfer, efi_local_operations ); + +/** Process descriptor */ +static struct process_descriptor efi_local_process_desc = + PROC_DESC_ONCE ( struct efi_local, process, efi_local_step ); + +/** + * Check for matching volume name + * + * @v local Local file + * @v device Device handle + * @v root Root filesystem handle + * @v volume Volume name + * @ret rc Return status code + */ +static int efi_local_check_volume_name ( struct efi_local *local, + EFI_HANDLE device, + EFI_FILE_PROTOCOL *root, + const char *volume ) { + EFI_FILE_SYSTEM_INFO *info; + UINTN size; + char *label; + EFI_STATUS efirc; + int rc; + + /* Get length of file system information */ + size = 0; + root->GetInfo ( root, &efi_file_system_info_id, &size, NULL ); + + /* Allocate file system information */ + info = malloc ( size ); + if ( ! info ) { + rc = -ENOMEM; + goto err_alloc_info; + } + + /* Get file system information */ + if ( ( efirc = root->GetInfo ( root, &efi_file_system_info_id, &size, + info ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not get file system info on %s: " + "%s\n", local, efi_handle_name ( device ), + strerror ( rc ) ); + goto err_get_info; + } + DBGC2 ( local, "LOCAL %p found %s with label \"%ls\"\n", + local, efi_handle_name ( device ), info->VolumeLabel ); + + /* Construct volume label for comparison */ + if ( asprintf ( &label, "%ls", info->VolumeLabel ) < 0 ) { + rc = -ENOMEM; + goto err_alloc_label; + } + + /* Compare volume label */ + if ( strcasecmp ( volume, label ) != 0 ) { + rc = -ENOENT; + goto err_compare; + } + + /* Success */ + rc = 0; + + err_compare: + free ( label ); + err_alloc_label: + err_get_info: + free ( info ); + err_alloc_info: + return rc; +} + +/** + * Open root filesystem + * + * @v local Local file + * @v device Device handle + * @v root Root filesystem handle to fill in + * @ret rc Return status code + */ +static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device, + EFI_FILE_PROTOCOL **root ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + void *interface; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; + } u; + EFI_STATUS efirc; + int rc; + + /* Open file system protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_file_system_protocol_guid, + &u.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not open filesystem on %s: %s\n", + local, efi_handle_name ( device ), strerror ( rc ) ); + goto err_filesystem; + } + + /* Open root directory */ + if ( ( efirc = u.fs->OpenVolume ( u.fs, root ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not open volume on %s: %s\n", + local, efi_handle_name ( device ), strerror ( rc ) ); + goto err_volume; + } + + /* Success */ + rc = 0; + + err_volume: + bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid, + efi_image_handle, device ); + err_filesystem: + return rc; +} + +/** + * Open root filesystem of specified volume + * + * @v local Local file + * @v volume Volume name, or NULL to use loaded image's device + * @ret rc Return status code + */ +static int efi_local_open_volume ( struct efi_local *local, + const char *volume ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; + int ( * check ) ( struct efi_local *local, EFI_HANDLE device, + EFI_FILE_PROTOCOL *root, const char *volume ); + EFI_FILE_PROTOCOL *root; + EFI_HANDLE *handles; + EFI_HANDLE device; + UINTN num_handles; + UINTN i; + EFI_STATUS efirc; + int rc; + + /* Identify candidate handles */ + if ( volume ) { + /* Locate all filesystem handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol, + NULL, &num_handles, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not enumerate handles: " + "%s\n", local, strerror ( rc ) ); + return rc; + } + check = efi_local_check_volume_name; + } else { + /* Use our loaded image's device handle */ + handles = &efi_loaded_image->DeviceHandle; + num_handles = 1; + check = NULL; + } + + /* Find matching handle */ + for ( i = 0 ; i < num_handles ; i++ ) { + + /* Get this device handle */ + device = handles[i]; + + /* Open root directory */ + if ( ( rc = efi_local_open_root ( local, device, &root ) ) != 0) + continue; + + /* Check volume name, if applicable */ + if ( ( check == NULL ) || + ( ( rc = check ( local, device, root, volume ) ) == 0 ) ) { + DBGC ( local, "LOCAL %p using %s", + local, efi_handle_name ( device ) ); + if ( volume ) + DBGC ( local, " with label \"%s\"", volume ); + DBGC ( local, "\n" ); + local->root = root; + break; + } + + /* Close root directory */ + root->Close ( root ); + } + + /* Free handles, if applicable */ + if ( volume ) + bs->FreePool ( handles ); + + /* Fail if we found no matching handle */ + if ( ! local->root ) { + DBGC ( local, "LOCAL %p found no matching handle\n", local ); + return -ENOENT; + } + + return 0; +} + +/** + * Open fully-resolved path + * + * @v local Local file + * @v resolved Resolved path + * @ret rc Return status code + */ +static int efi_local_open_resolved ( struct efi_local *local, + const char *resolved ) { + size_t name_len = strlen ( resolved ); + CHAR16 name[ name_len + 1 /* wNUL */ ]; + EFI_FILE_PROTOCOL *file; + EFI_STATUS efirc; + int rc; + + /* Construct filename */ + efi_snprintf ( name, ( name_len + 1 /* wNUL */ ), "%s", resolved ); + + /* Open file */ + if ( ( efirc = local->root->Open ( local->root, &file, name, + EFI_FILE_MODE_READ, 0 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not open \"%s\": %s\n", + local, resolved, strerror ( rc ) ); + return rc; + } + local->file = file; + + return 0; +} + +/** + * Open specified path + * + * @v local Local file + * @v path Path to file + * @ret rc Return status code + */ +static int efi_local_open_path ( struct efi_local *local, const char *path ) { + FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath, + FILEPATH_DEVICE_PATH, Header); + size_t fp_len = ( fp ? efi_devpath_len ( &fp->Header ) : 0 ); + char base[ fp_len / 2 /* Cannot exceed this length */ ]; + size_t remaining = sizeof ( base ); + size_t len; + char *resolved; + char *tmp; + int rc; + + /* Construct base path to our own image, if possible */ + memset ( base, 0, sizeof ( base ) ); + tmp = base; + while ( fp && ( fp->Header.Type != END_DEVICE_PATH_TYPE ) ) { + len = snprintf ( tmp, remaining, "%ls", fp->PathName ); + assert ( len < remaining ); + tmp += len; + remaining -= len; + fp = ( ( ( void * ) fp ) + ( ( fp->Header.Length[1] << 8 ) | + fp->Header.Length[0] ) ); + } + DBGC2 ( local, "LOCAL %p base path \"%s\"\n", + local, base ); + + /* Convert to sane path separators */ + for ( tmp = base ; *tmp ; tmp++ ) { + if ( *tmp == '\\' ) + *tmp = '/'; + } + + /* Resolve path */ + resolved = resolve_path ( base, path ); + if ( ! resolved ) { + rc = -ENOMEM; + goto err_resolve; + } + + /* Convert to insane path separators */ + for ( tmp = resolved ; *tmp ; tmp++ ) { + if ( *tmp == '/' ) + *tmp = '\\'; + } + DBGC ( local, "LOCAL %p using \"%s\"\n", + local, resolved ); + + /* Open resolved path */ + if ( ( rc = efi_local_open_resolved ( local, resolved ) ) != 0 ) + goto err_open; + + err_open: + free ( resolved ); + err_resolve: + return rc; +} + +/** + * Get file length + * + * @v local Local file + * @ret rc Return status code + */ +static int efi_local_len ( struct efi_local *local ) { + EFI_FILE_PROTOCOL *file = local->file; + EFI_FILE_INFO *info; + EFI_STATUS efirc; + UINTN size; + int rc; + + /* Get size of file information */ + size = 0; + file->GetInfo ( file, &efi_file_info_id, &size, NULL ); + + /* Allocate file information */ + info = malloc ( size ); + if ( ! info ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get file information */ + if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size, + info ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not get file info: %s\n", + local, strerror ( rc ) ); + goto err_info; + } + + /* Record file length */ + local->len = info->FileSize; + + /* Success */ + rc = 0; + + err_info: + free ( info ); + err_alloc: + return rc; +} + +/** + * Open local file + * + * @v xfer Data transfer interface + * @v uri Request URI + * @ret rc Return status code + */ +static int efi_local_open ( struct interface *xfer, struct uri *uri ) { + struct efi_local *local; + const char *volume; + const char *path; + int rc; + + /* Parse URI */ + volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL ); + path = ( uri->opaque ? uri->opaque : uri->path ); + + /* Allocate and initialise structure */ + local = zalloc ( sizeof ( *local ) ); + if ( ! local ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &local->refcnt, NULL ); + intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); + process_init ( &local->process, &efi_local_process_desc, + &local->refcnt ); + + /* Open specified volume */ + if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 ) + goto err_open_root; + + /* Open specified path */ + if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) + goto err_open_file; + + /* Get length of file */ + if ( ( rc = efi_local_len ( local ) ) != 0 ) + goto err_len; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &local->xfer, xfer ); + ref_put ( &local->refcnt ); + return 0; + + err_len: + err_open_file: + err_open_root: + efi_local_close ( local, 0 ); + ref_put ( &local->refcnt ); + err_alloc: + return rc; +} + +/** EFI local file URI opener */ +struct uri_opener efi_local_uri_opener __uri_opener = { + .scheme = "file", + .open = efi_local_open, +}; From 36fbc3f4bda1df07898b5664978172e8a2e5f3c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Mar 2016 16:53:16 +0000 Subject: [PATCH 144/591] [build] Remove long-obsolete header file Signed-off-by: Michael Brown --- src/include/ipxe/linux_compat.h | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/include/ipxe/linux_compat.h diff --git a/src/include/ipxe/linux_compat.h b/src/include/ipxe/linux_compat.h deleted file mode 100644 index 4704c4817..000000000 --- a/src/include/ipxe/linux_compat.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _IPXE_LINUX_COMPAT_H -#define _IPXE_LINUX_COMPAT_H - -/** @file - * - * Linux code compatibility - * - * This file exists to ease the building of Linux source code within - * iPXE. This is intended to facilitate quick testing; it is not - * intended to be a substitute for proper porting. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include -#include -#include -#include - -#define __init -#define __exit -#define __initdata -#define __exitdata -#define printk printf - -#endif /* _IPXE_LINUX_COMPAT_H */ From 2246a6b2749410c641e1472d31ab2d887860c2e9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Mar 2016 17:03:33 +0000 Subject: [PATCH 145/591] [pseudobit] Rename bitops.h to pseudobit.h Signed-off-by: Michael Brown --- src/drivers/infiniband/linda.h | 4 ++-- src/drivers/infiniband/qib7322.h | 4 ++-- src/include/ipxe/{bitops.h => pseudobit.h} | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) rename src/include/ipxe/{bitops.h => pseudobit.h} (98%) diff --git a/src/drivers/infiniband/linda.h b/src/drivers/infiniband/linda.h index 46a920a17..44c7686f4 100644 --- a/src/drivers/infiniband/linda.h +++ b/src/drivers/infiniband/linda.h @@ -33,8 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#define BITOPS_LITTLE_ENDIAN -#include +#define PSEUDOBIT_LITTLE_ENDIAN +#include #include "qib_7220_regs.h" struct ib_device; diff --git a/src/drivers/infiniband/qib7322.h b/src/drivers/infiniband/qib7322.h index 72797b240..dab95cfc0 100644 --- a/src/drivers/infiniband/qib7322.h +++ b/src/drivers/infiniband/qib7322.h @@ -33,8 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#define BITOPS_LITTLE_ENDIAN -#include +#define PSEUDOBIT_LITTLE_ENDIAN +#include #include "qib_7322_regs.h" /** A QIB7322 GPIO register */ diff --git a/src/include/ipxe/bitops.h b/src/include/ipxe/pseudobit.h similarity index 98% rename from src/include/ipxe/bitops.h rename to src/include/ipxe/pseudobit.h index 93eb663e2..431b106fa 100644 --- a/src/include/ipxe/bitops.h +++ b/src/include/ipxe/pseudobit.h @@ -1,5 +1,5 @@ -#ifndef _IPXE_BITOPS_H -#define _IPXE_BITOPS_H +#ifndef _IPXE_PSEUDOBIT_H +#define _IPXE_PSEUDOBIT_H /* * Copyright (C) 2008 Michael Brown . @@ -29,7 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file * - * Bit operations + * Pseudo-bit structures * */ @@ -40,14 +40,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * This is a property of the device, not a property of the host CPU. */ -#ifdef BITOPS_LITTLE_ENDIAN +#ifdef PSEUDOBIT_LITTLE_ENDIAN #define cpu_to_BIT64 cpu_to_le64 #define cpu_to_BIT32 cpu_to_le32 #define BIT64_to_cpu le64_to_cpu #define BIT32_to_cpu le32_to_cpu #define QWORD_SHIFT( offset, width ) (offset) #endif -#ifdef BITOPS_BIG_ENDIAN +#ifdef PSEUDOBIT_BIG_ENDIAN #define cpu_to_BIT64 cpu_to_be64 #define cpu_to_BIT32 cpu_to_be32 #define BIT64_to_cpu be64_to_cpu @@ -246,4 +246,4 @@ typedef unsigned char pseudo_bit_t; *__ptr |= cpu_to_BIT64 ( __value << __shift ); \ } while ( 0 ) -#endif /* _IPXE_BITOPS_H */ +#endif /* _IPXE_PSEUDOBIT_H */ From c867b5ab1ff00fae7f5d89bc0c5f273c40f37f90 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Mar 2016 21:18:33 +0000 Subject: [PATCH 146/591] [bitops] Add generic atomic bit test, set, and clear functions Signed-off-by: Michael Brown --- src/arch/x86/include/bits/bitops.h | 94 ++++++++++++++++++++++++++ src/include/ipxe/bitops.h | 19 ++++++ src/tests/bitops_test.c | 102 +++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 4 files changed, 216 insertions(+) create mode 100644 src/arch/x86/include/bits/bitops.h create mode 100644 src/include/ipxe/bitops.h create mode 100644 src/tests/bitops_test.c diff --git a/src/arch/x86/include/bits/bitops.h b/src/arch/x86/include/bits/bitops.h new file mode 100644 index 000000000..17dcf1024 --- /dev/null +++ b/src/arch/x86/include/bits/bitops.h @@ -0,0 +1,94 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * x86 bit operations + * + * We perform atomic bit set and bit clear operations using "lock bts" + * and "lock btr". We use the output constraint to inform the + * compiler that any memory from the start of the bit field up to and + * including the byte containing the bit may be modified. (This is + * overkill but shouldn't matter in practice since we're unlikely to + * subsequently read other bits from the same bit field.) + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + + __asm__ __volatile__ ( "lock bts %1, %0" + : "+m" ( *bytes ) : "Ir" ( bit ) ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + + __asm__ __volatile__ ( "lock btr %1, %0" + : "+m" ( *bytes ) : "Ir" ( bit ) ); +} + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + int old; + + __asm__ __volatile__ ( "lock bts %2, %0\n\t" + "sbb %1, %1\n\t" + : "+m" ( *bytes ), "=r" ( old ) + : "Ir" ( bit ) ); + return old; +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + int old; + + __asm__ __volatile__ ( "lock btr %2, %0\n\t" + "sbb %1, %1\n\t" + : "+m" ( *bytes ), "=r" ( old ) + : "Ir" ( bit ) ); + return old; +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/include/ipxe/bitops.h b/src/include/ipxe/bitops.h new file mode 100644 index 000000000..7366cd9f1 --- /dev/null +++ b/src/include/ipxe/bitops.h @@ -0,0 +1,19 @@ +#ifndef _IPXE_BITOPS_H +#define _IPXE_BITOPS_H + +/** @file + * + * Bit operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +void set_bit ( unsigned int bit, volatile void *bits ); +void clear_bit ( unsigned int bit, volatile void *bits ); +int test_and_set_bit ( unsigned int bit, volatile void *bits ); +int test_and_clear_bit ( unsigned int bit, volatile void *bits ); + +#endif /* _IPXE_BITOPS_H */ diff --git a/src/tests/bitops_test.c b/src/tests/bitops_test.c new file mode 100644 index 000000000..a689b949f --- /dev/null +++ b/src/tests/bitops_test.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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 ); + +/** @file + * + * Bit operations self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include + +/** + * Perform bit operations self-tests + * + */ +static void bitops_test_exec ( void ) { + uint8_t bits[32]; + + /* Initialise bits */ + memset ( bits, 0, sizeof ( bits ) ); + + /* Test set_bit() */ + set_bit ( 0, bits ); + ok ( bits[0] == 0x01 ); + set_bit ( 17, bits ); + ok ( bits[2] == 0x02 ); + set_bit ( 22, bits ); + ok ( bits[2] == 0x42 ); + set_bit ( 22, bits ); + ok ( bits[2] == 0x42 ); + + /* Test clear_bit() */ + clear_bit ( 0, bits ); + ok ( bits[5] == 0x00 ); + bits[5] = 0xff; + clear_bit ( 42, bits ); + ok ( bits[5] == 0xfb ); + clear_bit ( 42, bits ); + ok ( bits[5] == 0xfb ); + clear_bit ( 44, bits ); + ok ( bits[5] == 0xeb ); + + /* Test test_and_set_bit() */ + ok ( test_and_set_bit ( 0, bits ) == 0 ); + ok ( bits[0] == 0x01 ); + ok ( test_and_set_bit ( 0, bits ) != 0 ); + ok ( bits[0] == 0x01 ); + ok ( test_and_set_bit ( 69, bits ) == 0 ); + ok ( bits[8] == 0x20 ); + ok ( test_and_set_bit ( 69, bits ) != 0 ); + ok ( bits[8] == 0x20 ); + ok ( test_and_set_bit ( 69, bits ) != 0 ); + ok ( bits[8] == 0x20 ); + + /* Test test_and_clear_bit() */ + ok ( test_and_clear_bit ( 0, bits ) != 0 ); + ok ( bits[0] == 0x00 ); + ok ( test_and_clear_bit ( 0, bits ) == 0 ); + ok ( bits[0] == 0x00 ); + bits[31] = 0xeb; + ok ( test_and_clear_bit ( 255, bits ) != 0 ); + ok ( bits[31] == 0x6b ); + ok ( test_and_clear_bit ( 255, bits ) == 0 ); + ok ( bits[31] == 0x6b ); + ok ( test_and_clear_bit ( 255, bits ) == 0 ); + ok ( bits[31] == 0x6b ); +} + +/** Bit operations self-test */ +struct self_test bitops_test __self_test = { + .name = "bitops", + .exec = bitops_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 2e67043e6..0ec885f4f 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -68,3 +68,4 @@ REQUIRE_OBJECT ( setjmp_test ); REQUIRE_OBJECT ( pccrc_test ); REQUIRE_OBJECT ( linebuf_test ); REQUIRE_OBJECT ( iobuf_test ); +REQUIRE_OBJECT ( bitops_test ); From 9bab13a7727a36c36b320409e0e785e016a3ff5c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Mar 2016 21:24:13 +0000 Subject: [PATCH 147/591] [hyperv] Use generic set_bit() function Signed-off-by: Michael Brown --- src/arch/i386/include/bits/hyperv.h | 23 ----------------------- src/arch/x86_64/include/bits/hyperv.h | 23 ----------------------- src/interface/hyperv/vmbus.c | 5 +++-- 3 files changed, 3 insertions(+), 48 deletions(-) diff --git a/src/arch/i386/include/bits/hyperv.h b/src/arch/i386/include/bits/hyperv.h index 3565c8a83..0ba58afb7 100644 --- a/src/arch/i386/include/bits/hyperv.h +++ b/src/arch/i386/include/bits/hyperv.h @@ -46,27 +46,4 @@ hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, return result; } -/** - * Set bit atomically - * - * @v bits Bit field - * @v bit Bit to set - */ -static inline __attribute__ (( always_inline )) void -hv_set_bit ( void *bits, unsigned int bit ) { - struct { - uint32_t dword[ ( bit / 32 ) + 1 ]; - } *dwords = bits; - - /* Set bit using "lock bts". Inform compiler that any memory - * from the start of the bit field up to and including the - * dword containing this bit may be modified. (This is - * overkill but shouldn't matter in practice since we're - * unlikely to subsequently read other bits from the same bit - * field.) - */ - __asm__ __volatile__ ( "lock bts %1, %0" - : "+m" ( *dwords ) : "Ir" ( bit ) ); -} - #endif /* _BITS_HYPERV_H */ diff --git a/src/arch/x86_64/include/bits/hyperv.h b/src/arch/x86_64/include/bits/hyperv.h index 845c182f7..975b1eee0 100644 --- a/src/arch/x86_64/include/bits/hyperv.h +++ b/src/arch/x86_64/include/bits/hyperv.h @@ -49,27 +49,4 @@ hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, return result; } -/** - * Set bit atomically - * - * @v bits Bit field - * @v bit Bit to set - */ -static inline __attribute__ (( always_inline )) void -hv_set_bit ( void *bits, unsigned int bit ) { - struct { - uint64_t qword[ ( bit / 64 ) + 1 ]; - } *qwords = bits; - - /* Set bit using "lock bts". Inform compiler that any memory - * from the start of the bit field up to and including the - * qword containing this bit may be modified. (This is - * overkill but shouldn't matter in practice since we're - * unlikely to subsequently read other bits from the same bit - * field.) - */ - __asm__ __volatile__ ( "lock bts %1, %0" - : "+m" ( *qwords ) : "Ir" ( bit ) ); -} - #endif /* _BITS_HYPERV_H */ diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index 795929eae..fd809dda4 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -559,7 +560,7 @@ static void vmbus_signal_monitor ( struct vmbus_device *vmdev ) { group = ( vmdev->monitor / ( 8 * sizeof ( trigger->pending ) )); bit = ( vmdev->monitor % ( 8 * sizeof ( trigger->pending ) ) ); trigger = &vmbus->monitor_out->trigger[group]; - hv_set_bit ( trigger, bit ); + set_bit ( bit, trigger ); } /** @@ -720,7 +721,7 @@ static int vmbus_send ( struct vmbus_device *vmdev, return 0; /* Set channel bit in interrupt page */ - hv_set_bit ( vmbus->intr->out, vmdev->channel ); + set_bit ( vmdev->channel, vmbus->intr->out ); /* Signal the host */ vmdev->signal ( vmdev ); From c14971bf887dc7aaa85788f71f8c4bdf93d2b7a5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Mar 2016 21:27:07 +0000 Subject: [PATCH 148/591] [xen] Use generic test_and_clear_bit() function Signed-off-by: Michael Brown --- src/arch/x86/include/bits/xen.h | 19 ------------------- src/include/ipxe/xen.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/arch/x86/include/bits/xen.h b/src/arch/x86/include/bits/xen.h index fc065ea38..3433cea1f 100644 --- a/src/arch/x86/include/bits/xen.h +++ b/src/arch/x86/include/bits/xen.h @@ -161,23 +161,4 @@ xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall, return retval; } -/** - * Test and clear pending event - * - * @v xen Xen hypervisor - * @v port Event channel port - * @ret pending Event was pending - */ -static inline __attribute__ (( always_inline )) uint8_t -xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) { - uint8_t pending; - - __asm__ __volatile__ ( "lock btr %2, %0\n\t" - "setc %1\n\t" - : "+m" ( xen->shared->evtchn_pending ), - "=a" ( pending ) - : "Ir" ( port ) ); - return pending; -} - #endif /* _BITS_XEN_H */ diff --git a/src/include/ipxe/xen.h b/src/include/ipxe/xen.h index eac1145ad..0fb8b7625 100644 --- a/src/include/ipxe/xen.h +++ b/src/include/ipxe/xen.h @@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define __XEN_INTERFACE_VERSION__ 0x00040400 #include +#include #include #include #include @@ -58,6 +59,19 @@ struct xen_hypervisor { struct xen_store store; }; +/** + * Test and clear pending event + * + * @v xen Xen hypervisor + * @v port Event channel port + * @ret pending Event was pending + */ +static inline __attribute__ (( always_inline )) int +xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) { + + return test_and_clear_bit ( port, xen->shared->evtchn_pending ); +} + #include /** From dbc9e591a5e958b0b849fc1669902f38043b0422 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Mar 2016 14:29:18 +0000 Subject: [PATCH 149/591] [test] Move i386-specific tests to arch/i386/tests Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 1 + src/{ => arch/i386}/tests/comboot/shuffle-simple.asm | 2 +- src/{ => arch/i386}/tests/comboot/version.asm | 0 src/{ => arch/i386}/tests/gdbstub_test.S | 0 src/{ => arch/i386}/tests/gdbstub_test.gdb | 0 5 files changed, 2 insertions(+), 1 deletion(-) rename src/{ => arch/i386}/tests/comboot/shuffle-simple.asm (99%) rename src/{ => arch/i386}/tests/comboot/version.asm (100%) rename src/{ => arch/i386}/tests/gdbstub_test.S (100%) rename src/{ => arch/i386}/tests/gdbstub_test.gdb (100%) diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 89393d06c..a52986048 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -84,6 +84,7 @@ endif # SRCDIRS += arch/i386/core SRCDIRS += arch/i386/image +SRCDIRS += arch/i386/tests SRCDIRS += arch/i386/interface/syslinux # Include common x86 Makefile diff --git a/src/tests/comboot/shuffle-simple.asm b/src/arch/i386/tests/comboot/shuffle-simple.asm similarity index 99% rename from src/tests/comboot/shuffle-simple.asm rename to src/arch/i386/tests/comboot/shuffle-simple.asm index efc7d9b46..8ede8d097 100644 --- a/src/tests/comboot/shuffle-simple.asm +++ b/src/arch/i386/tests/comboot/shuffle-simple.asm @@ -2,7 +2,7 @@ org 100h jmp start - + shuffle_start: push 0xB800 pop es diff --git a/src/tests/comboot/version.asm b/src/arch/i386/tests/comboot/version.asm similarity index 100% rename from src/tests/comboot/version.asm rename to src/arch/i386/tests/comboot/version.asm diff --git a/src/tests/gdbstub_test.S b/src/arch/i386/tests/gdbstub_test.S similarity index 100% rename from src/tests/gdbstub_test.S rename to src/arch/i386/tests/gdbstub_test.S diff --git a/src/tests/gdbstub_test.gdb b/src/arch/i386/tests/gdbstub_test.gdb similarity index 100% rename from src/tests/gdbstub_test.gdb rename to src/arch/i386/tests/gdbstub_test.gdb From 04ef198d2fcf864b82a722ee737eac88fc48b1a4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Mar 2016 14:50:29 +0000 Subject: [PATCH 150/591] [efi] Move architecture-independent EFI prefixes to interface/efi Signed-off-by: Michael Brown --- src/{arch/x86/prefix => interface/efi}/efidrvprefix.c | 0 src/{arch/x86/prefix => interface/efi}/efiprefix.c | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{arch/x86/prefix => interface/efi}/efidrvprefix.c (100%) rename src/{arch/x86/prefix => interface/efi}/efiprefix.c (100%) diff --git a/src/arch/x86/prefix/efidrvprefix.c b/src/interface/efi/efidrvprefix.c similarity index 100% rename from src/arch/x86/prefix/efidrvprefix.c rename to src/interface/efi/efidrvprefix.c diff --git a/src/arch/x86/prefix/efiprefix.c b/src/interface/efi/efiprefix.c similarity index 100% rename from src/arch/x86/prefix/efiprefix.c rename to src/interface/efi/efiprefix.c From ef0297b527130ce851c3cb2276fb2729a28b254c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 18 Mar 2016 08:18:31 +0000 Subject: [PATCH 151/591] [libc] Allow container_of() to be used on volatile pointers Signed-off-by: Michael Brown --- src/include/stddef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stddef.h b/src/include/stddef.h index 3c056294f..fb01c489d 100644 --- a/src/include/stddef.h +++ b/src/include/stddef.h @@ -34,7 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define container_of( ptr, type, field ) ( { \ type *__container; \ - const typeof ( __container->field ) *__field = (ptr); \ + const volatile typeof ( __container->field ) *__field = (ptr); \ __container = ( ( ( void * ) __field ) - \ offsetof ( type, field ) ); \ __container; } ) From 750a2efeb2814623e1355885874e93cb7578ef73 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 09:22:55 +0000 Subject: [PATCH 152/591] [ipoib] Allow external code to identify IPoIB network devices Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 18 ++++++++++++++++++ src/include/ipxe/ipoib.h | 1 + 2 files changed, 19 insertions(+) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 4106c208c..a245cce1b 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -1027,3 +1027,21 @@ struct ib_driver ipoib_driver __ib_driver = { .notify = ipoib_notify, .remove = ipoib_remove, }; + +/** + * Find IPoIB network device + * + * @v ibdev Infiniband device + * @ret netdev IPoIB network device, or NULL if not found + */ +struct net_device * ipoib_netdev ( struct ib_device *ibdev ) { + struct ipoib_device *ipoib; + + /* Find matching IPoIB device */ + list_for_each_entry ( ipoib, &ipoib_devices, list ) { + if ( ipoib->ibdev != ibdev ) + continue; + return ipoib->netdev; + } + return NULL; +} diff --git a/src/include/ipxe/ipoib.h b/src/include/ipxe/ipoib.h index b34dd32d0..065eeabb7 100644 --- a/src/include/ipxe/ipoib.h +++ b/src/include/ipxe/ipoib.h @@ -62,5 +62,6 @@ struct ipoib_remac { extern const char * ipoib_ntoa ( const void *ll_addr ); extern struct net_device * alloc_ipoibdev ( size_t priv_size ); +extern struct net_device * ipoib_netdev ( struct ib_device *ibdev ); #endif /* _IPXE_IPOIB_H */ From e2cdbd51a8cca5f2cc5b7b6b6e65e62d3df8841a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 14:46:40 +0000 Subject: [PATCH 153/591] [hermon] Add missing iounmap() Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 42 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index cac5fe991..606d35473 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -3747,24 +3747,6 @@ static void hermon_free ( struct hermon *hermon ) { free ( hermon ); } -/** - * Initialise Hermon PCI parameters - * - * @v hermon Hermon device - */ -static void hermon_pci_init ( struct hermon *hermon ) { - struct pci_device *pci = hermon->pci; - - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Get PCI BARs */ - hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR), - HERMON_PCI_CONFIG_BAR_SIZE ); - hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ), - HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE ); -} - /** * Probe PCI device * @@ -3789,8 +3771,14 @@ static int hermon_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, hermon ); hermon->pci = pci; - /* Initialise PCI parameters */ - hermon_pci_init ( hermon ); + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BARs */ + hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR ), + HERMON_PCI_CONFIG_BAR_SIZE ); + hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ), + HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE ); /* Reset device */ hermon_reset ( hermon ); @@ -3885,6 +3873,8 @@ static int hermon_probe ( struct pci_device *pci ) { err_get_cap: hermon_stop_firmware ( hermon ); err_start_firmware: + iounmap ( hermon->uar ); + iounmap ( hermon->config ); hermon_free ( hermon ); err_alloc: return rc; @@ -3910,6 +3900,8 @@ static void hermon_remove ( struct pci_device *pci ) { } for ( i = ( hermon->cap.num_ports - 1 ) ; i >= 0 ; i-- ) ibdev_put ( hermon->port[i].ibdev ); + iounmap ( hermon->uar ); + iounmap ( hermon->config ); hermon_free ( hermon ); } @@ -3933,8 +3925,12 @@ static int hermon_bofm_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, hermon ); hermon->pci = pci; - /* Initialise PCI parameters */ - hermon_pci_init ( hermon ); + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BAR */ + hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR ), + HERMON_PCI_CONFIG_BAR_SIZE ); /* Initialise BOFM device */ bofm_init ( &hermon->bofm, pci, &hermon_bofm_operations ); @@ -3949,6 +3945,7 @@ static int hermon_bofm_probe ( struct pci_device *pci ) { return 0; err_bofm_register: + iounmap ( hermon->config ); hermon_free ( hermon ); err_alloc: return rc; @@ -3963,6 +3960,7 @@ static void hermon_bofm_remove ( struct pci_device *pci ) { struct hermon *hermon = pci_get_drvdata ( pci ); bofm_unregister ( &hermon->bofm ); + iounmap ( hermon->config ); hermon_free ( hermon ); } From 692324905e135b2eab151671db4494b31c2087c9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 14:50:36 +0000 Subject: [PATCH 154/591] [arbel] Add missing iounmap() Signed-off-by: Michael Brown --- src/drivers/infiniband/arbel.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index 2a6c32dec..746b78989 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -3000,6 +3000,16 @@ static int arbel_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, arbel ); arbel->pci = pci; + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BARs */ + arbel->config = ioremap ( pci_bar_start ( pci, ARBEL_PCI_CONFIG_BAR ), + ARBEL_PCI_CONFIG_BAR_SIZE ); + arbel->uar = ioremap ( ( pci_bar_start ( pci, ARBEL_PCI_UAR_BAR ) + + ARBEL_PCI_UAR_IDX * ARBEL_PCI_UAR_SIZE ), + ARBEL_PCI_UAR_SIZE ); + /* Allocate Infiniband devices */ for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) { ibdev = alloc_ibdev ( 0 ); @@ -3014,16 +3024,6 @@ static int arbel_probe ( struct pci_device *pci ) { ib_set_drvdata ( ibdev, arbel ); } - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Get PCI BARs */ - arbel->config = ioremap ( pci_bar_start ( pci, ARBEL_PCI_CONFIG_BAR ), - ARBEL_PCI_CONFIG_BAR_SIZE ); - arbel->uar = ioremap ( ( pci_bar_start ( pci, ARBEL_PCI_UAR_BAR ) + - ARBEL_PCI_UAR_IDX * ARBEL_PCI_UAR_SIZE ), - ARBEL_PCI_UAR_SIZE ); - /* Reset device */ arbel_reset ( arbel ); @@ -3072,6 +3072,8 @@ static int arbel_probe ( struct pci_device *pci ) { err_alloc_ibdev: for ( i-- ; i >= 0 ; i-- ) ibdev_put ( arbel->ibdev[i] ); + iounmap ( arbel->uar ); + iounmap ( arbel->config ); arbel_free ( arbel ); err_alloc: return rc; @@ -3090,6 +3092,8 @@ static void arbel_remove ( struct pci_device *pci ) { unregister_ibdev ( arbel->ibdev[i] ); for ( i = ( ARBEL_NUM_PORTS - 1 ) ; i >= 0 ; i-- ) ibdev_put ( arbel->ibdev[i] ); + iounmap ( arbel->uar ); + iounmap ( arbel->config ); arbel_free ( arbel ); } From bea9ee2397f9755012db3b54a81c011555ca6497 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 14:53:32 +0000 Subject: [PATCH 155/591] [linda] Add missing iounmap() Signed-off-by: Michael Brown --- src/drivers/infiniband/linda.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/drivers/infiniband/linda.c b/src/drivers/infiniband/linda.c index 0c1682745..391fff429 100644 --- a/src/drivers/infiniband/linda.c +++ b/src/drivers/infiniband/linda.c @@ -2327,7 +2327,7 @@ static int linda_probe ( struct pci_device *pci ) { /* Fix up PCI device */ adjust_pci_device ( pci ); - /* Get PCI BARs */ + /* Map PCI BARs */ linda->regs = ioremap ( pci->membase, LINDA_BAR0_SIZE ); DBGC2 ( linda, "Linda %p has BAR at %08lx\n", linda, pci->membase ); @@ -2388,6 +2388,7 @@ static int linda_probe ( struct pci_device *pci ) { err_init_ib_serdes: err_read_eeprom: err_init_i2c: + iounmap ( linda->regs ); ibdev_put ( ibdev ); err_alloc_ibdev: return rc; @@ -2405,6 +2406,7 @@ static void linda_remove ( struct pci_device *pci ) { unregister_ibdev ( ibdev ); linda_fini_recv ( linda ); linda_fini_send ( linda ); + iounmap ( linda->regs ); ibdev_put ( ibdev ); } From 4a861cc61cc2dca07f41d5932939c2c9993cec78 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 14:55:18 +0000 Subject: [PATCH 156/591] [qib7322] Add missing iounmap() Signed-off-by: Michael Brown --- src/drivers/infiniband/qib7322.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c index 08cedcb7e..3bd587ec0 100644 --- a/src/drivers/infiniband/qib7322.c +++ b/src/drivers/infiniband/qib7322.c @@ -2289,7 +2289,7 @@ static int qib7322_probe ( struct pci_device *pci ) { /* Fix up PCI device */ adjust_pci_device ( pci ); - /* Get PCI BARs */ + /* Map PCI BARs */ qib7322->regs = ioremap ( pci->membase, QIB7322_BAR0_SIZE ); DBGC2 ( qib7322, "QIB7322 %p has BAR at %08lx\n", qib7322, pci->membase ); @@ -2384,6 +2384,7 @@ static int qib7322_probe ( struct pci_device *pci ) { err_init_recv: err_read_eeprom: err_init_i2c: + iounmap ( qib7322->regs ); free ( qib7322 ); err_alloc_qib7322: return rc; @@ -2406,6 +2407,7 @@ static void qib7322_remove ( struct pci_device *pci ) { ibdev_put ( qib7322->ibdev[i] ); qib7322_fini_send ( qib7322 ); qib7322_fini_recv ( qib7322 ); + iounmap ( qib7322->regs ); free ( qib7322 ); } From 0141ea3a773aea7a2f4e81b2b2143c85683cc21c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 17:26:09 +0000 Subject: [PATCH 157/591] [crypto] Allow trusted certificates to be stored in non-volatile options The intention of the existing code (as documented in its own comments) is that it should be possible to override the list of trusted root certificates using a "trust" setting held in non-volatile stored options. However, the rootcert_init() function currently executes before any devices have been probed, and so will not be able to retrieve any such non-volatile stored options. Fix by executing rootcert_init() only after devices have been probed. Since startup functions may be executed multiple times (unlike initialisation functions), add an explicit flag to preserve the property that rootcert_init() should run only once. As before, if an explicit root of trust is specified at build time, then any runtime "trust" setting will be ignored. Signed-off-by: Michael Brown --- src/crypto/rootcert.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c index 00ea1647e..f7b9dcfb7 100644 --- a/src/crypto/rootcert.c +++ b/src/crypto/rootcert.c @@ -93,13 +93,14 @@ struct x509_root root_certificates = { * a rebuild. */ static void rootcert_init ( void ) { + static int initialised; void *external = NULL; int len; /* Allow trusted root certificates to be overridden only if * not explicitly specified at build time. */ - if ( ALLOW_TRUST_OVERRIDE ) { + if ( ALLOW_TRUST_OVERRIDE && ( ! initialised ) ) { /* Fetch copy of "trust" setting, if it exists. This * memory will never be freed. @@ -109,6 +110,9 @@ static void rootcert_init ( void ) { root_certificates.fingerprints = external; root_certificates.count = ( len / FINGERPRINT_LEN ); } + + /* Prevent subsequent modifications */ + initialised = 1; } DBGC ( &root_certificates, "ROOTCERT using %d %s certificate(s):\n", @@ -118,6 +122,6 @@ static void rootcert_init ( void ) { } /** Root certificate initialiser */ -struct init_fn rootcert_init_fn __init_fn ( INIT_LATE ) = { - .initialise = rootcert_init, +struct startup_fn rootcert_startup_fn __startup_fn ( STARTUP_LATE ) = { + .startup = rootcert_init, }; From e84c917f39cc33894aeddbce727fe5430378fe7d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 21 Mar 2016 08:18:15 +0000 Subject: [PATCH 158/591] [hermon] Allocate space for GRH on UD queue pairs The Infiniband specification (volume 1, section 11.4.1.2 "Post Receive Request") notes that for UD QPs, the GRH will be placed in the first 40 bytes of the receive buffer if present. (If no GRH is present, which is normal, then the first 40 bytes of the receive buffer will be unused.) Mellanox hardware performs this placement automatically: other headers will be stripped (and their values returned via the CQE), but the first 40 bytes of the data buffer will be consumed by the (probably non-existent) GRH. This does not fit neatly into iPXE's internal abstraction, which expects the data buffer to represent just the data payload with the addresses from the GRH (if present) passed as additional parameters to ib_complete_recv(). The end result of this discrepancy is that attempts to receive full-sized 2048-byte IPoIB packets on Mellanox hardware will fail. Fix by allocating a separate ring buffer to hold the received GRHs. Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 53 ++++++++++++++++++++++++++------- src/drivers/infiniband/hermon.h | 6 +++- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 606d35473..99a54a146 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -1111,6 +1111,8 @@ static int hermon_create_qp ( struct ib_device *ibdev, struct hermon *hermon = ib_get_drvdata ( ibdev ); struct hermon_queue_pair *hermon_qp; struct hermonprm_qp_ee_state_transitions qpctx; + struct hermonprm_wqe_segment_data_ptr *data; + unsigned int i; int rc; /* Calculate queue pair number */ @@ -1147,8 +1149,14 @@ static int hermon_create_qp ( struct ib_device *ibdev, sizeof ( hermon_qp->send.wqe[0] ) ); hermon_qp->recv.wqe_size = ( qp->recv.num_wqes * sizeof ( hermon_qp->recv.wqe[0] ) ); + if ( ( qp->type == IB_QPT_SMI ) || ( qp->type == IB_QPT_GSI ) || + ( qp->type == IB_QPT_UD ) ) { + hermon_qp->recv.grh_size = ( qp->recv.num_wqes * + sizeof ( hermon_qp->recv.grh[0] )); + } hermon_qp->wqe_size = ( hermon_qp->send.wqe_size + - hermon_qp->recv.wqe_size ); + hermon_qp->recv.wqe_size + + hermon_qp->recv.grh_size ); hermon_qp->wqe = malloc_dma ( hermon_qp->wqe_size, sizeof ( hermon_qp->send.wqe[0] ) ); if ( ! hermon_qp->wqe ) { @@ -1156,9 +1164,21 @@ static int hermon_create_qp ( struct ib_device *ibdev, goto err_alloc_wqe; } hermon_qp->send.wqe = hermon_qp->wqe; - memset ( hermon_qp->send.wqe, 0xff, hermon_qp->send.wqe_size ); hermon_qp->recv.wqe = ( hermon_qp->wqe + hermon_qp->send.wqe_size ); + if ( hermon_qp->recv.grh_size ) { + hermon_qp->recv.grh = ( hermon_qp->wqe + + hermon_qp->send.wqe_size + + hermon_qp->recv.wqe_size ); + } + + /* Initialise work queue entries */ + memset ( hermon_qp->send.wqe, 0xff, hermon_qp->send.wqe_size ); memset ( hermon_qp->recv.wqe, 0, hermon_qp->recv.wqe_size ); + data = &hermon_qp->recv.wqe[0].recv.data[0]; + for ( i = 0 ; i < ( hermon_qp->recv.wqe_size / sizeof ( *data ) ); i++){ + MLX_FILL_1 ( data, 1, l_key, HERMON_INVALID_LKEY ); + data++; + } /* Allocate MTT entries */ if ( ( rc = hermon_alloc_mtt ( hermon, hermon_qp->wqe, @@ -1633,6 +1653,8 @@ static int hermon_post_recv ( struct ib_device *ibdev, struct ib_work_queue *wq = &qp->recv; struct hermon_recv_work_queue *hermon_recv_wq = &hermon_qp->recv; struct hermonprm_recv_wqe *wqe; + struct hermonprm_wqe_segment_data_ptr *data; + struct ib_global_route_header *grh; unsigned int wqe_idx_mask; /* Allocate work queue entry */ @@ -1646,12 +1668,19 @@ static int hermon_post_recv ( struct ib_device *ibdev, wqe = &hermon_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv; /* Construct work queue entry */ - MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); - MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->lkey ); - MLX_FILL_H ( &wqe->data[0], 2, - local_address_h, virt_to_bus ( iobuf->data ) ); - MLX_FILL_1 ( &wqe->data[0], 3, - local_address_l, virt_to_bus ( iobuf->data ) ); + data = &wqe->data[0]; + if ( hermon_qp->recv.grh ) { + grh = &hermon_qp->recv.grh[wq->next_idx & wqe_idx_mask]; + MLX_FILL_1 ( data, 0, byte_count, sizeof ( *grh ) ); + MLX_FILL_1 ( data, 1, l_key, hermon->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( grh ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( grh ) ); + data++; + } + MLX_FILL_1 ( data, 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( data, 1, l_key, hermon->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( iobuf->data ) ); /* Update work queue's index */ wq->next_idx++; @@ -1676,6 +1705,7 @@ static int hermon_complete ( struct ib_device *ibdev, struct ib_completion_queue *cq, union hermonprm_completion_entry *cqe ) { struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_queue_pair *hermon_qp; struct ib_work_queue *wq; struct ib_queue_pair *qp; struct io_buffer *iobuf; @@ -1713,6 +1743,7 @@ static int hermon_complete ( struct ib_device *ibdev, return -EIO; } qp = wq->qp; + hermon_qp = ib_qp_get_drvdata ( qp ); /* Identify work queue entry */ wqe_idx = MLX_GET ( &cqe->normal, wqe_counter ); @@ -1747,9 +1778,9 @@ static int hermon_complete ( struct ib_device *ibdev, case IB_QPT_SMI: case IB_QPT_GSI: case IB_QPT_UD: - assert ( iob_len ( iobuf ) >= sizeof ( *grh ) ); - grh = iobuf->data; - iob_pull ( iobuf, sizeof ( *grh ) ); + /* Locate corresponding GRH */ + assert ( hermon_qp->recv.grh != NULL ); + grh = &hermon_qp->recv.grh[ wqe_idx & wqe_idx_mask ]; /* Construct address vector */ source = &recv_source; source->qpn = MLX_GET ( &cqe->normal, srq_rqpn ); diff --git a/src/drivers/infiniband/hermon.h b/src/drivers/infiniband/hermon.h index e0b028f26..61e285781 100644 --- a/src/drivers/infiniband/hermon.h +++ b/src/drivers/infiniband/hermon.h @@ -515,7 +515,7 @@ struct hermonprm_eth_send_wqe { struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER]; } __attribute__ (( packed )); -#define HERMON_MAX_SCATTER 1 +#define HERMON_MAX_SCATTER 2 struct hermonprm_recv_wqe { struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_SCATTER]; @@ -686,6 +686,10 @@ struct hermon_recv_work_queue { union hermon_recv_wqe *wqe; /** Size of work queue */ size_t wqe_size; + /** GRH buffers (if applicable) */ + struct ib_global_route_header *grh; + /** Size of GRH buffers */ + size_t grh_size; /** Doorbell record */ struct hermonprm_qp_db_record *doorbell; }; From 57c63047e3128a72f53c7dfd5eec75985d4345c7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 21 Mar 2016 08:55:02 +0000 Subject: [PATCH 159/591] [arbel] Allocate space for GRH on UD queue pairs As with the previous commit (for Hermon), allocate a separate ring buffer to hold received GRHs. Signed-off-by: Michael Brown --- src/drivers/infiniband/arbel.c | 65 ++++++++++++++++++++++++++-------- src/drivers/infiniband/arbel.h | 6 +++- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index 746b78989..5ab701551 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -897,26 +897,44 @@ static int arbel_create_send_wq ( struct arbel_send_work_queue *arbel_send_wq, * * @v arbel_recv_wq Receive work queue * @v num_wqes Number of work queue entries + * @v type Queue pair type * @ret rc Return status code */ static int arbel_create_recv_wq ( struct arbel_recv_work_queue *arbel_recv_wq, - unsigned int num_wqes ) { + unsigned int num_wqes, + enum ib_queue_pair_type type ) { struct arbelprm_recv_wqe *wqe; struct arbelprm_recv_wqe *next_wqe; unsigned int wqe_idx_mask; size_t nds; unsigned int i; unsigned int j; + int rc; /* Allocate work queue */ arbel_recv_wq->wqe_size = ( num_wqes * sizeof ( arbel_recv_wq->wqe[0] ) ); arbel_recv_wq->wqe = malloc_dma ( arbel_recv_wq->wqe_size, sizeof ( arbel_recv_wq->wqe[0] ) ); - if ( ! arbel_recv_wq->wqe ) - return -ENOMEM; + if ( ! arbel_recv_wq->wqe ) { + rc = -ENOMEM; + goto err_alloc_wqe; + } memset ( arbel_recv_wq->wqe, 0, arbel_recv_wq->wqe_size ); + /* Allocate GRH entries, if needed */ + if ( ( type == IB_QPT_SMI ) || ( type == IB_QPT_GSI ) || + ( type == IB_QPT_UD ) ) { + arbel_recv_wq->grh_size = ( num_wqes * + sizeof ( arbel_recv_wq->grh[0] ) ); + arbel_recv_wq->grh = malloc_dma ( arbel_recv_wq->grh_size, + sizeof ( void * ) ); + if ( ! arbel_recv_wq->grh ) { + rc = -ENOMEM; + goto err_alloc_grh; + } + } + /* Link work queue entries */ wqe_idx_mask = ( num_wqes - 1 ); nds = ( ( offsetof ( typeof ( *wqe ), data ) + @@ -935,6 +953,12 @@ static int arbel_create_recv_wq ( struct arbel_recv_work_queue *arbel_recv_wq, } return 0; + + free_dma ( arbel_recv_wq->grh, arbel_recv_wq->grh_size ); + err_alloc_grh: + free_dma ( arbel_recv_wq->wqe, arbel_recv_wq->wqe_size ); + err_alloc_wqe: + return rc; } /** @@ -985,8 +1009,8 @@ static int arbel_create_qp ( struct ib_device *ibdev, if ( ( rc = arbel_create_send_wq ( &arbel_qp->send, qp->send.num_wqes ) ) != 0 ) goto err_create_send_wq; - if ( ( rc = arbel_create_recv_wq ( &arbel_qp->recv, - qp->recv.num_wqes ) ) != 0 ) + if ( ( rc = arbel_create_recv_wq ( &arbel_qp->recv, qp->recv.num_wqes, + qp->type ) ) != 0 ) goto err_create_recv_wq; /* Send and receive work queue entries must be within the same 4GB */ @@ -1078,6 +1102,7 @@ static int arbel_create_qp ( struct ib_device *ibdev, MLX_FILL_1 ( send_db_rec, 1, res, ARBEL_UAR_RES_NONE ); MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE ); err_unsupported_address_split: + free_dma ( arbel_qp->recv.grh, arbel_qp->recv.grh_size ); free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size ); err_create_recv_wq: free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); @@ -1206,8 +1231,9 @@ static void arbel_destroy_qp ( struct ib_device *ibdev, MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE ); /* Free memory */ - free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); + free_dma ( arbel_qp->recv.grh, arbel_qp->recv.grh_size ); free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size ); + free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); free ( arbel_qp ); /* Mark queue number as free */ @@ -1477,6 +1503,8 @@ static int arbel_post_recv ( struct ib_device *ibdev, struct ib_work_queue *wq = &qp->recv; struct arbel_recv_work_queue *arbel_recv_wq = &arbel_qp->recv; struct arbelprm_recv_wqe *wqe; + struct arbelprm_wqe_segment_data_ptr *data; + struct ib_global_route_header *grh; union arbelprm_doorbell_record *db_rec; unsigned int wqe_idx_mask; @@ -1491,12 +1519,19 @@ static int arbel_post_recv ( struct ib_device *ibdev, wqe = &arbel_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv; /* Construct work queue entry */ - MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); - MLX_FILL_1 ( &wqe->data[0], 1, l_key, arbel->lkey ); - MLX_FILL_H ( &wqe->data[0], 2, - local_address_h, virt_to_bus ( iobuf->data ) ); - MLX_FILL_1 ( &wqe->data[0], 3, - local_address_l, virt_to_bus ( iobuf->data ) ); + data = &wqe->data[0]; + if ( arbel_recv_wq->grh ) { + grh = &arbel_recv_wq->grh[wq->next_idx & wqe_idx_mask]; + MLX_FILL_1 ( data, 0, byte_count, sizeof ( *grh ) ); + MLX_FILL_1 ( data, 1, l_key, arbel->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( grh ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( grh ) ); + data++; + } + MLX_FILL_1 ( data, 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( data, 1, l_key, arbel->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( iobuf->data ) ); /* Update doorbell record */ barrier(); @@ -1619,9 +1654,9 @@ static int arbel_complete ( struct ib_device *ibdev, case IB_QPT_SMI: case IB_QPT_GSI: case IB_QPT_UD: - assert ( iob_len ( iobuf ) >= sizeof ( *grh ) ); - grh = iobuf->data; - iob_pull ( iobuf, sizeof ( *grh ) ); + /* Locate corresponding GRH */ + assert ( arbel_recv_wq->grh != NULL ); + grh = &arbel_recv_wq->grh[wqe_idx]; /* Construct address vector */ source = &recv_source; memset ( source, 0, sizeof ( *source ) ); diff --git a/src/drivers/infiniband/arbel.h b/src/drivers/infiniband/arbel.h index 73394cd9a..8a5a996a3 100644 --- a/src/drivers/infiniband/arbel.h +++ b/src/drivers/infiniband/arbel.h @@ -237,7 +237,7 @@ struct arbelprm_rc_send_wqe { struct arbelprm_wqe_segment_data_ptr data[ARBEL_MAX_GATHER]; } __attribute__ (( packed )); -#define ARBEL_MAX_SCATTER 1 +#define ARBEL_MAX_SCATTER 2 struct arbelprm_recv_wqe { /* The autogenerated header is inconsistent between send and @@ -369,6 +369,10 @@ struct arbel_recv_work_queue { union arbel_recv_wqe *wqe; /** Size of work queue */ size_t wqe_size; + /** GRH buffers (if applicable) */ + struct ib_global_route_header *grh; + /** Size of GRB buffers */ + size_t grh_size; }; /** Number of special queue pairs */ From 173c0c25363fc7a9aaaef606ef35e73b765e888b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 21 Mar 2016 09:29:54 +0000 Subject: [PATCH 160/591] [infiniband] Allow drivers to override the eIPoIB LEMAC Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 1 + src/include/ipxe/infiniband.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index a245cce1b..8a65c87ba 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -956,6 +956,7 @@ static int ipoib_probe ( struct ib_device *ibdev ) { /* Extract hardware address */ memcpy ( netdev->hw_addr, &ibdev->gid.s.guid, sizeof ( ibdev->gid.s.guid ) ); + memcpy ( netdev->ll_addr, ibdev->lemac, ETH_ALEN ); /* Set local MAC address */ memcpy ( &ipoib->mac.gid.s.guid, &ibdev->gid.s.guid, diff --git a/src/include/ipxe/infiniband.h b/src/include/ipxe/infiniband.h index 5910390da..d7ecd1623 100644 --- a/src/include/ipxe/infiniband.h +++ b/src/include/ipxe/infiniband.h @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** Subnet management interface QPN */ #define IB_QPN_SMI 0 @@ -457,6 +458,9 @@ struct ib_device { /** General services interface */ struct ib_mad_interface *gsi; + /** IPoIB LEMAC (if non-default) */ + uint8_t lemac[ETH_ALEN]; + /** Driver private data */ void *drv_priv; }; From 1afcccd5fdd0662cfc45b5dd3cc32b6e3128719c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 21 Mar 2016 17:33:45 +0000 Subject: [PATCH 161/591] [build] Do not use "objcopy -O binary" for objects with relocation records The mbr.bin and usbdisk.bin standalone blobs are currently generated using "objcopy -O binary", which does not process relocation records. For the i386 build, this does not matter since the section start address is zero and so the ".rel" relocation records are effectively no-ops anyway. For the x86_64 build, the ".rela" relocation records are not no-ops, since the addend is included as part of the relocation record (rather than inline). Using "objcopy -O binary" will silently discard the relocation records, with the result that all symbols are effectively given a value of zero. Fix by using "ld --oformat binary" instead of "objcopy -O binary" to generate mbr.bin and usbdisk.bin. Signed-off-by: Michael Brown --- src/arch/x86/Makefile.pcbios | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/arch/x86/Makefile.pcbios b/src/arch/x86/Makefile.pcbios index 18a6f7597..f8c225352 100644 --- a/src/arch/x86/Makefile.pcbios +++ b/src/arch/x86/Makefile.pcbios @@ -12,6 +12,10 @@ LDSCRIPT = arch/x86/scripts/pcbios.lds # LDFLAGS += -N --no-check-sections +# Prefix always starts at address zero +# +LDFLAGS += --section-start=.prefix=0 + # Media types. # MEDIA += rom @@ -103,13 +107,13 @@ NON_AUTO_MEDIA += fd0 # Special target for building Master Boot Record binary $(BIN)/mbr.bin : $(BIN)/mbr.o - $(QM)$(ECHO) " [OBJCOPY] $@" - $(Q)$(OBJCOPY) -O binary $< $@ + $(QM)$(ECHO) " [LD] $@" + $(Q)$(LD) $(LDFLAGS) -o $@ --oformat binary -e 0 $< # rule to make a USB disk image $(BIN)/usbdisk.bin : $(BIN)/usbdisk.o - $(QM)$(ECHO) " [OBJCOPY] $@" - $(Q)$(OBJCOPY) -O binary $< $@ + $(QM)$(ECHO) " [LD] $@" + $(Q)$(LD) $(LDFLAGS) -o $@ --oformat binary -e 0 $< NON_AUTO_MEDIA += usb %usb: $(BIN)/usbdisk.bin %hd From 311a5732c8baa7ceb4f23db51dcbb5015e2ef965 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 12:00:15 +0000 Subject: [PATCH 162/591] [gdb] Add support for x86_64 Signed-off-by: Michael Brown --- src/arch/i386/core/gdbidt.S | 28 +--- src/arch/i386/core/gdbmach.c | 184 -------------------- src/arch/i386/include/gdbmach.h | 10 +- src/arch/x86/core/gdbmach.c | 251 ++++++++++++++++++++++++++++ src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86_64/core/gdbidt.S | 168 +++++++++++++++++++ src/arch/x86_64/include/gdbmach.h | 49 ++++-- src/core/gdbstub.c | 11 +- 8 files changed, 477 insertions(+), 225 deletions(-) delete mode 100644 src/arch/i386/core/gdbmach.c create mode 100644 src/arch/x86/core/gdbmach.c create mode 100644 src/arch/x86_64/core/gdbidt.S diff --git a/src/arch/i386/core/gdbidt.S b/src/arch/i386/core/gdbidt.S index a1e309d7c..666ecce3c 100644 --- a/src/arch/i386/core/gdbidt.S +++ b/src/arch/i386/core/gdbidt.S @@ -15,41 +15,29 @@ /* POSIX signal numbers for reporting traps to GDB */ #define SIGILL 4 #define SIGTRAP 5 -#define SIGBUS 7 #define SIGFPE 8 -#define SIGSEGV 11 #define SIGSTKFLT 16 - .globl gdbmach_nocode_sigfpe -gdbmach_nocode_sigfpe: + .globl gdbmach_sigfpe +gdbmach_sigfpe: pushl $SIGFPE jmp gdbmach_interrupt - .globl gdbmach_nocode_sigtrap -gdbmach_nocode_sigtrap: + .globl gdbmach_sigtrap +gdbmach_sigtrap: pushl $SIGTRAP jmp gdbmach_interrupt - .globl gdbmach_nocode_sigstkflt -gdbmach_nocode_sigstkflt: + .globl gdbmach_sigstkflt +gdbmach_sigstkflt: pushl $SIGSTKFLT jmp gdbmach_interrupt - .globl gdbmach_nocode_sigill -gdbmach_nocode_sigill: + .globl gdbmach_sigill +gdbmach_sigill: pushl $SIGILL jmp gdbmach_interrupt - .globl gdbmach_withcode_sigbus -gdbmach_withcode_sigbus: - movl $SIGBUS, (%esp) - jmp gdbmach_interrupt - - .globl gdbmach_withcode_sigsegv -gdbmach_withcode_sigsegv: - movl $SIGSEGV, (%esp) - jmp gdbmach_interrupt - /* When invoked, the stack contains: eflags, cs, eip, signo. */ #define IH_OFFSET_GDB_REGS ( 0 ) #define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS ) diff --git a/src/arch/i386/core/gdbmach.c b/src/arch/i386/core/gdbmach.c deleted file mode 100644 index d92a4ac08..000000000 --- a/src/arch/i386/core/gdbmach.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2008 Stefan Hajnoczi . - * - * 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 ); - -#include -#include -#include -#include -#include -#include -#include - -/** @file - * - * GDB architecture-specific bits for i386 - * - */ - -enum { - DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */ - DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */ -}; - -/** Hardware breakpoint, fields stored in x86 bit pattern form */ -struct hwbp { - int type; /* type (1=write watchpoint, 3=access watchpoint) */ - unsigned long addr; /* linear address */ - size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */ - int enabled; -}; - -static struct hwbp hwbps [ 4 ]; -static gdbreg_t dr7 = DR7_CLEAR; - -static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { - struct hwbp *available = NULL; - unsigned int i; - for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { - if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { - return &hwbps [ i ]; - } - if ( !hwbps [ i ].enabled ) { - available = &hwbps [ i ]; - } - } - return available; -} - -static void gdbmach_commit_hwbp ( struct hwbp *bp ) { - unsigned int regnum = bp - hwbps; - - /* Set breakpoint address */ - assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) ); - switch ( regnum ) { - case 0: - __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); - break; - case 1: - __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); - break; - case 2: - __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); - break; - case 3: - __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); - break; - } - - /* Set type */ - dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); - dr7 |= bp->type << ( 16 + 4 * regnum ); - - /* Set length */ - dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); - dr7 |= bp->len << ( 18 + 4 * regnum ); - - /* Set/clear local enable bit */ - dr7 &= ~( 0x3 << 2 * regnum ); - dr7 |= bp->enabled << 2 * regnum; -} - -int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { - struct hwbp *bp; - - /* Check and convert breakpoint type to x86 type */ - switch ( type ) { - case GDBMACH_WATCH: - type = 0x1; - break; - case GDBMACH_AWATCH: - type = 0x3; - break; - default: - return 0; /* unsupported breakpoint type */ - } - - /* Only lengths 1, 2, and 4 are supported */ - if ( len != 2 && len != 4 ) { - len = 1; - } - len--; /* convert to x86 breakpoint length bit pattern */ - - /* Calculate linear address by adding segment base */ - addr += virt_offset; - - /* Set up the breakpoint */ - bp = gdbmach_find_hwbp ( type, addr, len ); - if ( !bp ) { - return 0; /* ran out of hardware breakpoints */ - } - bp->type = type; - bp->addr = addr; - bp->len = len; - bp->enabled = enable; - gdbmach_commit_hwbp ( bp ); - return 1; -} - -static void gdbmach_disable_hwbps ( void ) { - /* Store and clear hardware breakpoints */ - __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); -} - -static void gdbmach_enable_hwbps ( void ) { - /* Clear breakpoint status register */ - __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) ); - - /* Restore hardware breakpoints */ - __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); -} - -__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { - gdbmach_disable_hwbps(); - gdbstub_handler ( signo, regs ); - gdbmach_enable_hwbps(); -} - -static void * gdbmach_interrupt_vectors[] = { - gdbmach_nocode_sigfpe, /* Divide by zero */ - gdbmach_nocode_sigtrap, /* Debug trap */ - NULL, /* Non-maskable interrupt */ - gdbmach_nocode_sigtrap, /* Breakpoint */ - gdbmach_nocode_sigstkflt, /* Overflow */ - gdbmach_nocode_sigstkflt, /* Bound range exceeded */ - gdbmach_nocode_sigill, /* Invalid opcode */ - NULL, /* Device not available */ - gdbmach_withcode_sigbus, /* Double fault */ - NULL, /* Coprocessor segment overrun */ - gdbmach_withcode_sigsegv, /* Invalid TSS */ - gdbmach_withcode_sigsegv, /* Segment not present */ - gdbmach_withcode_sigsegv, /* Stack segment fault */ - gdbmach_withcode_sigsegv, /* General protection fault */ - gdbmach_withcode_sigsegv, /* Page fault */ -}; - -void gdbmach_init ( void ) { - unsigned int i; - - for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) / - sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) { - set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] ); - } -} diff --git a/src/arch/i386/include/gdbmach.h b/src/arch/i386/include/gdbmach.h index 416ae341a..52cce7833 100644 --- a/src/arch/i386/include/gdbmach.h +++ b/src/arch/i386/include/gdbmach.h @@ -47,12 +47,10 @@ enum { }; /* Interrupt vectors */ -extern void gdbmach_nocode_sigfpe ( void ); -extern void gdbmach_nocode_sigtrap ( void ); -extern void gdbmach_nocode_sigstkflt ( void ); -extern void gdbmach_nocode_sigill ( void ); -extern void gdbmach_withcode_sigbus ( void ); -extern void gdbmach_withcode_sigsegv ( void ); +extern void gdbmach_sigfpe ( void ); +extern void gdbmach_sigtrap ( void ); +extern void gdbmach_sigstkflt ( void ); +extern void gdbmach_sigill ( void ); static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { regs [ GDBMACH_EIP ] = pc; diff --git a/src/arch/x86/core/gdbmach.c b/src/arch/x86/core/gdbmach.c new file mode 100644 index 000000000..af6abfedd --- /dev/null +++ b/src/arch/x86/core/gdbmach.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * GDB architecture-specific bits for x86 + * + */ + +/** Number of hardware breakpoints */ +#define NUM_HWBP 4 + +/** Debug register 7: Global breakpoint enable */ +#define DR7_G( bp ) ( 2 << ( 2 * (bp) ) ) + +/** Debug register 7: Global exact breakpoint enable */ +#define DR7_GE ( 1 << 9 ) + +/** Debug register 7: Break on data writes */ +#define DR7_RWLEN_WRITE 0x11110000 + +/** Debug register 7: Break on data access */ +#define DR7_RWLEN_ACCESS 0x33330000 + +/** Debug register 7: One-byte length */ +#define DR7_RWLEN_1 0x00000000 + +/** Debug register 7: Two-byte length */ +#define DR7_RWLEN_2 0x44440000 + +/** Debug register 7: Four-byte length */ +#define DR7_RWLEN_4 0xcccc0000 + +/** Debug register 7: Eight-byte length */ +#define DR7_RWLEN_8 0x88880000 + +/** Debug register 7: Breakpoint R/W and length mask */ +#define DR7_RWLEN_MASK( bp ) ( 0xf0000 << ( 4 * (bp) ) ) + +/** Hardware breakpoint addresses (debug registers 0-3) */ +static unsigned long dr[NUM_HWBP]; + +/** Active value of debug register 7 */ +static unsigned long dr7 = DR7_GE; + +/** + * Update debug registers + * + */ +static void gdbmach_update ( void ) { + + /* Set debug registers */ + __asm__ __volatile__ ( "mov %0, %%dr0" : : "r" ( dr[0] ) ); + __asm__ __volatile__ ( "mov %0, %%dr1" : : "r" ( dr[1] ) ); + __asm__ __volatile__ ( "mov %0, %%dr2" : : "r" ( dr[2] ) ); + __asm__ __volatile__ ( "mov %0, %%dr3" : : "r" ( dr[3] ) ); + __asm__ __volatile__ ( "mov %0, %%dr7" : : "r" ( dr7 ) ); +} + +/** + * Find reusable or available hardware breakpoint + * + * @v addr Linear address + * @v rwlen Control bits + * @ret bp Hardware breakpoint, or negative error + */ +static int gdbmach_find ( unsigned long addr, unsigned int rwlen ) { + unsigned int i; + int bp = -ENOENT; + + /* Look for a reusable or available breakpoint */ + for ( i = 0 ; i < NUM_HWBP ; i++ ) { + + /* If breakpoint is not enabled, then it is available */ + if ( ! ( dr7 & DR7_G ( i ) ) ) { + bp = i; + continue; + } + + /* If breakpoint is enabled and has the same address + * and control bits, then reuse it. + */ + if ( ( dr[i] == addr ) && + ( ( ( dr7 ^ rwlen ) & DR7_RWLEN_MASK ( i ) ) == 0 ) ) { + bp = i; + break; + } + } + + return bp; +} + +/** + * Set hardware breakpoint + * + * @v type GDB breakpoint type + * @v addr Virtual address + * @v len Length + * @v enable Enable (not disable) breakpoint + * @ret rc Return status code + */ +int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ) { + unsigned int rwlen; + unsigned long mask; + int bp; + + /* Parse breakpoint type */ + switch ( type ) { + case GDBMACH_WATCH: + rwlen = DR7_RWLEN_WRITE; + break; + case GDBMACH_AWATCH: + rwlen = DR7_RWLEN_ACCESS; + break; + default: + return -ENOTSUP; + } + + /* Parse breakpoint length */ + switch ( len ) { + case 1: + rwlen |= DR7_RWLEN_1; + break; + case 2: + rwlen |= DR7_RWLEN_2; + break; + case 4: + rwlen |= DR7_RWLEN_4; + break; + case 8: + rwlen |= DR7_RWLEN_8; + break; + default: + return -ENOTSUP; + } + + /* Convert to linear address */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + addr = virt_to_phys ( ( void * ) addr ); + + /* Find reusable or available hardware breakpoint */ + bp = gdbmach_find ( addr, rwlen ); + if ( bp < 0 ) + return ( enable ? -ENOBUFS : 0 ); + + /* Configure this breakpoint */ + DBGC ( &dr[0], "GDB bp %d at %p+%zx type %d (%sabled)\n", + bp, ( ( void * ) addr ), len, type, ( enable ? "en" : "dis" ) ); + dr[bp] = addr; + mask = DR7_RWLEN_MASK ( bp ); + dr7 = ( ( dr7 & ~mask ) | ( rwlen & mask ) ); + mask = DR7_G ( bp ); + dr7 &= ~mask; + if ( enable ) + dr7 |= mask; + + /* Update debug registers */ + gdbmach_update(); + + return 0; +} + +/** + * Handle exception + * + * @v signo GDB signal number + * @v regs Register dump + */ +__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { + unsigned long dr7_disabled = DR7_GE; + unsigned long dr6_clear = 0; + + /* Temporarily disable breakpoints */ + __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7_disabled ) ); + + /* Handle exception */ + DBGC ( &dr[0], "GDB signal %d\n", signo ); + DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) ); + gdbstub_handler ( signo, regs ); + DBGC ( &dr[0], "GDB signal %d returning\n", signo ); + DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) ); + + /* Clear breakpoint status register */ + __asm__ __volatile__ ( "mov %0, %%dr6\n" : : "r" ( dr6_clear ) ); + + /* Re-enable breakpoints */ + __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7 ) ); +} + +/** + * CPU exception vectors + * + * Note that we cannot intercept anything from INT8 (double fault) + * upwards, since these overlap by default with IRQ0-7. + */ +static void * gdbmach_vectors[] = { + gdbmach_sigfpe, /* Divide by zero */ + gdbmach_sigtrap, /* Debug trap */ + NULL, /* Non-maskable interrupt */ + gdbmach_sigtrap, /* Breakpoint */ + gdbmach_sigstkflt, /* Overflow */ + gdbmach_sigstkflt, /* Bound range exceeded */ + gdbmach_sigill, /* Invalid opcode */ +}; + +/** + * Initialise GDB + */ +void gdbmach_init ( void ) { + unsigned int i; + + /* Hook CPU exception vectors */ + for ( i = 0 ; i < ( sizeof ( gdbmach_vectors ) / + sizeof ( gdbmach_vectors[0] ) ) ; i++ ) { + if ( gdbmach_vectors[i] ) + set_interrupt_vector ( i, gdbmach_vectors[i] ); + } +} diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 0d1617d20..9eb4b5488 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_apm ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 ) #define ERRFILE_vesafb ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 ) #define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) +#define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86_64/core/gdbidt.S b/src/arch/x86_64/core/gdbidt.S new file mode 100644 index 000000000..89280bf89 --- /dev/null +++ b/src/arch/x86_64/core/gdbidt.S @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 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 + * + * GDB exception handlers + * + */ + +/* Size of a register */ +#define SIZEOF_REG 8 + +/* POSIX signal numbers for reporting traps to GDB */ +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGFPE 8 +#define SIGSTKFLT 16 + + .section ".text.gdbmach_interrupt", "ax", @progbits + .code64 + + .struct 0 +/* Register dump created for GDB stub */ +regs: +regs_rax: .space SIZEOF_REG +regs_rbx: .space SIZEOF_REG +regs_rcx: .space SIZEOF_REG +regs_rdx: .space SIZEOF_REG +regs_rsi: .space SIZEOF_REG +regs_rdi: .space SIZEOF_REG +regs_rbp: .space SIZEOF_REG +regs_rsp: .space SIZEOF_REG +regs_r8: .space SIZEOF_REG +regs_r9: .space SIZEOF_REG +regs_r10: .space SIZEOF_REG +regs_r11: .space SIZEOF_REG +regs_r12: .space SIZEOF_REG +regs_r13: .space SIZEOF_REG +regs_r14: .space SIZEOF_REG +regs_r15: .space SIZEOF_REG +regs_rip: .space SIZEOF_REG +regs_rflags: .space SIZEOF_REG +regs_cs: .space SIZEOF_REG +regs_ss: .space SIZEOF_REG +regs_ds: .space SIZEOF_REG +regs_es: .space SIZEOF_REG +regs_fs: .space SIZEOF_REG +regs_gs: .space SIZEOF_REG +regs_end: +/* GDB signal code */ +gdb: +gdb_code: .space SIZEOF_REG +gdb_end: +/* Long-mode exception frame */ +frame: +frame_rip: .space SIZEOF_REG +frame_cs: .space SIZEOF_REG +frame_rflags: .space SIZEOF_REG +frame_rsp: .space SIZEOF_REG +frame_ss: .space SIZEOF_REG +frame_end: + .previous + + .globl gdbmach_sigfpe +gdbmach_sigfpe: + push $SIGFPE + jmp gdbmach_interrupt + + .globl gdbmach_sigtrap +gdbmach_sigtrap: + push $SIGTRAP + jmp gdbmach_interrupt + + .globl gdbmach_sigstkflt +gdbmach_sigstkflt: + push $SIGSTKFLT + jmp gdbmach_interrupt + + .globl gdbmach_sigill +gdbmach_sigill: + push $SIGILL + jmp gdbmach_interrupt + +gdbmach_interrupt: + + /* Create register dump */ + pushq %gs + pushq %fs + pushq $0 /* %es unused in long mode */ + pushq $0 /* %ds unused in long mode */ + pushq ( frame_ss - regs_ss - SIZEOF_REG )(%rsp) + pushq ( frame_cs - regs_cs - SIZEOF_REG )(%rsp) + pushq ( frame_rflags - regs_rflags - SIZEOF_REG )(%rsp) + pushq ( frame_rip - regs_rip - SIZEOF_REG )(%rsp) + pushq %r15 + pushq %r14 + pushq %r13 + pushq %r12 + pushq %r11 + pushq %r10 + pushq %r9 + pushq %r8 + pushq ( frame_rsp - regs_rsp - SIZEOF_REG )(%rsp) + pushq %rbp + pushq %rdi + pushq %rsi + pushq %rdx + pushq %rcx + pushq %rbx + pushq %rax + + /* Call GDB stub exception handler */ + movq gdb_code(%rsp), %rdi + movq %rsp, %rsi + call gdbmach_handler + + /* Restore from register dump */ + popq %rax + popq %rbx + popq %rcx + popq %rdx + popq %rsi + popq %rdi + popq %rbp + popq ( frame_rsp - regs_rsp - SIZEOF_REG )(%rsp) + popq %r8 + popq %r9 + popq %r10 + popq %r11 + popq %r12 + popq %r13 + popq %r14 + popq %r15 + popq ( frame_rip - regs_rip - SIZEOF_REG )(%rsp) + popq ( frame_rflags - regs_rflags - SIZEOF_REG )(%rsp) + popq ( frame_cs - regs_cs - SIZEOF_REG )(%rsp) + popq ( frame_ss - regs_ss - SIZEOF_REG )(%rsp) + addq $( regs_fs - regs_ds ), %rsp /* skip %ds, %es */ + popq %fs + popq %gs + + /* Skip code */ + addq $( gdb_end - gdb_code ), %rsp /* skip code */ + + /* Return */ + iretq diff --git a/src/arch/x86_64/include/gdbmach.h b/src/arch/x86_64/include/gdbmach.h index 6dadbbdd3..367405fd6 100644 --- a/src/arch/x86_64/include/gdbmach.h +++ b/src/arch/x86_64/include/gdbmach.h @@ -14,16 +14,37 @@ typedef unsigned long gdbreg_t; -/* The register snapshot, this must be in sync with interrupt handler and the - * GDB protocol. */ +/* Register snapshot */ enum { - // STUB: don't expect this to work! - GDBMACH_EIP, - GDBMACH_EFLAGS, + GDBMACH_RAX, + GDBMACH_RBX, + GDBMACH_RCX, + GDBMACH_RDX, + GDBMACH_RSI, + GDBMACH_RDI, + GDBMACH_RBP, + GDBMACH_RSP, + GDBMACH_R8, + GDBMACH_R9, + GDBMACH_R10, + GDBMACH_R11, + GDBMACH_R12, + GDBMACH_R13, + GDBMACH_R14, + GDBMACH_R15, + GDBMACH_RIP, + GDBMACH_RFLAGS, + GDBMACH_CS, + GDBMACH_SS, + GDBMACH_DS, + GDBMACH_ES, + GDBMACH_FS, + GDBMACH_GS, GDBMACH_NREGS, - GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t ) }; +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + /* Breakpoint types */ enum { GDBMACH_BPMEM, @@ -33,21 +54,27 @@ enum { GDBMACH_AWATCH, }; +/* Exception vectors */ +extern void gdbmach_sigfpe ( void ); +extern void gdbmach_sigtrap ( void ); +extern void gdbmach_sigstkflt ( void ); +extern void gdbmach_sigill ( void ); + static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { - regs [ GDBMACH_EIP ] = pc; + regs[GDBMACH_RIP] = pc; } static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { - regs [ GDBMACH_EFLAGS ] &= ~( 1 << 8 ); /* Trace Flag (TF) */ - regs [ GDBMACH_EFLAGS ] |= ( step << 8 ); + regs[GDBMACH_RFLAGS] &= ~( 1 << 8 ); /* Trace Flag (TF) */ + regs[GDBMACH_RFLAGS] |= ( step << 8 ); } static inline void gdbmach_breakpoint ( void ) { __asm__ __volatile__ ( "int $3\n" ); } -extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ); - +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); extern void gdbmach_init ( void ); #endif /* GDBMACH_H */ diff --git a/src/core/gdbstub.c b/src/core/gdbstub.c index 6ad52d1a6..8b57ddf56 100644 --- a/src/core/gdbstub.c +++ b/src/core/gdbstub.c @@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); enum { POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */ - SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */ + SIZEOF_PAYLOAD = 512, /* buffer size of GDB payload data */ }; struct gdbstub { @@ -255,17 +255,20 @@ static void gdbstub_continue ( struct gdbstub *stub, int single_step ) { static void gdbstub_breakpoint ( struct gdbstub *stub ) { unsigned long args [ 3 ]; int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0; + int rc; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { gdbstub_send_errno ( stub, POSIX_EINVAL ); return; } - if ( gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], args [ 2 ], enable ) ) { - gdbstub_send_ok ( stub ); - } else { + if ( ( rc = gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], + args [ 2 ], enable ) ) != 0 ) { /* Not supported */ stub->len = 0; gdbstub_tx_packet ( stub ); + return; } + gdbstub_send_ok ( stub ); } static void gdbstub_rx_packet ( struct gdbstub *stub ) { From ab5b3abbbac98eb52109c34058e1999df7dd0b86 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 20 Mar 2016 09:16:13 +0000 Subject: [PATCH 163/591] [int13] Allow drive to be hooked using the natural drive number Interpret the maximum drive number (0xff for hard disks, 0x7f for floppy disks) as meaning "use natural drive number". Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 8 ++++++-- src/include/ipxe/sanboot.h | 2 +- src/usr/autoboot.c | 10 ++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 38880e4f0..3b3f87306 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -1588,7 +1588,7 @@ static void int13_free ( struct refcnt *refcnt ) { * * @v uri URI * @v drive Drive number - * @ret rc Return status code + * @ret drive Drive number, or negative error * * Registers the drive with the INT 13 emulation subsystem, and hooks * the INT 13 interrupt vector (if not already hooked). @@ -1603,6 +1603,10 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { int13_sync_num_drives(); natural_drive = ( ( drive & 0x80 ) ? ( num_drives | 0x80 ) : num_fdds ); + /* Use natural drive number if directed to do so */ + if ( ( drive & 0x7f ) == 0x7f ) + drive = natural_drive; + /* Check that drive number is not in use */ list_for_each_entry ( int13, &int13s, list ) { if ( int13->drive == drive ) { @@ -1661,7 +1665,7 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { int13_sync_num_drives(); free ( scratch ); - return 0; + return drive; err_guess_geometry: err_parse_iso9660: diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 57025f2c6..041e18935 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -70,7 +70,7 @@ unsigned int san_default_drive ( void ); * * @v uri URI * @v drive Drive number - * @ret rc Return status code + * @ret drive Drive number, or negative error */ int san_hook ( struct uri *uri, unsigned int drive ); diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index e93b0150d..57bf96ef2 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -127,7 +127,9 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, /* Hook SAN device, if applicable */ if ( root_path ) { - if ( ( rc = san_hook ( root_path, drive ) ) != 0 ) { + drive = san_hook ( root_path, drive ); + if ( drive < 0 ) { + rc = drive; printf ( "Could not open SAN device: %s\n", strerror ( rc ) ); goto err_san_hook; @@ -136,7 +138,7 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, } /* Describe SAN device, if applicable */ - if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) { + if ( ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) { if ( ( rc = san_describe ( drive ) ) != 0 ) { printf ( "Could not describe SAN device %#02x: %s\n", drive, strerror ( rc ) ); @@ -170,7 +172,7 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, } /* Attempt SAN boot if applicable */ - if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_BOOT ) ) { + if ( ! ( flags & URIBOOT_NO_SAN_BOOT ) ) { if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) { printf ( "Booting from SAN device %#02x\n", drive ); rc = san_boot ( drive ); @@ -188,7 +190,7 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, err_download: err_san_describe: /* Unhook SAN device, if applicable */ - if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) { + if ( ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) { if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) { san_unhook ( drive ); printf ( "Unregistered SAN device %#02x\n", drive ); From c32b07b81b6085f19af718d85300a7d2acef240b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 09:35:10 +0000 Subject: [PATCH 164/591] [int13] Allow default drive to be specified via "san-drive" setting The DHCP option 175.189 has been defined (by us) since 2006 as containing the drive number to be used for a SAN boot, but has never been automatically used as such by iPXE. Use this option (if specified) to override the default SAN drive number. Signed-off-by: Michael Brown --- src/arch/x86/include/ipxe/bios_sanboot.h | 11 --------- src/arch/x86/interface/pcbios/int13.c | 29 +++++++++++++++++++++++- src/include/ipxe/dhcp.h | 6 ++--- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/arch/x86/include/ipxe/bios_sanboot.h b/src/arch/x86/include/ipxe/bios_sanboot.h index 1a86b7d57..85d698039 100644 --- a/src/arch/x86/include/ipxe/bios_sanboot.h +++ b/src/arch/x86/include/ipxe/bios_sanboot.h @@ -15,15 +15,4 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SANBOOT_PREFIX_pcbios __pcbios_ #endif -/** - * Get default SAN drive number - * - * @ret drive Default drive number - */ -static inline __always_inline unsigned int -SANBOOT_INLINE ( pcbios, san_default_drive ) ( void ) { - /* Default to booting from first hard disk */ - return 0x80; -} - #endif /* _IPXE_BIOS_SANBOOT_H */ diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 3b3f87306..6f16904df 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -44,6 +44,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include #include #include @@ -1986,7 +1988,32 @@ static int int13_describe ( unsigned int drive ) { return 0; } -PROVIDE_SANBOOT_INLINE ( pcbios, san_default_drive ); +/** The "san-drive" setting */ +const struct setting san_drive_setting __setting ( SETTING_SANBOOT_EXTRA, + san-drive ) = { + .name = "san-drive", + .description = "SAN drive number", + .tag = DHCP_EB_SAN_DRIVE, + .type = &setting_type_uint8, +}; + +/** + * Get default SAN drive number + * + * @ret drive Default drive number + */ +static unsigned int int13_default_drive ( void ) { + unsigned long drive; + + /* Use "san-drive" setting, if specified */ + if ( fetch_uint_setting ( NULL, &san_drive_setting, &drive ) >= 0 ) + return drive; + + /* Otherwise, default to booting from first hard disk */ + return 0x80; +} + +PROVIDE_SANBOOT ( pcbios, san_default_drive, int13_default_drive ); PROVIDE_SANBOOT ( pcbios, san_hook, int13_hook ); PROVIDE_SANBOOT ( pcbios, san_unhook, int13_unhook ); PROVIDE_SANBOOT ( pcbios, san_boot, int13_boot ); diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index a11db3497..0bd7c1dca 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -403,12 +403,12 @@ struct dhcp_netdev_desc { /** Use cached network settings (obsolete; do not reuse this value) */ #define DHCP_EB_USE_CACHED DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb2 ) -/** BIOS drive number +/** SAN drive number * - * This is the drive number for a drive emulated via INT 13. 0x80 is + * This is the drive number for a SAN-hooked drive. For BIOS, 0x80 is * the first hard disk, 0x81 is the second hard disk, etc. */ -#define DHCP_EB_BIOS_DRIVE DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbd ) +#define DHCP_EB_SAN_DRIVE DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbd ) /** Username * From c640b954cdb8d1078d95e9ca04028ee9ebb4cf02 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 15:14:07 +0000 Subject: [PATCH 165/591] [3c5x9] Avoid use of sleep() in driver code Signed-off-by: Michael Brown --- src/drivers/net/3c5x9.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/3c5x9.c b/src/drivers/net/3c5x9.c index 4d9bc8d9e..d7c09f77c 100644 --- a/src/drivers/net/3c5x9.c +++ b/src/drivers/net/3c5x9.c @@ -108,7 +108,7 @@ static void t509_enable ( struct nic *nic ) { else if (connector == utp) { GO_WINDOW(nic->ioaddr,4); outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE); - sleep(2); /* Give time for media to negotiate */ + mdelay(2000); /* Give time for media to negotiate */ GO_WINDOW(nic->ioaddr,1); } From 59bae324c019d336805bb0f8818278fe6d7a62e8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 15:19:25 +0000 Subject: [PATCH 166/591] [etherfabric] Avoid use of sleep() in driver code Signed-off-by: Michael Brown --- src/drivers/net/etherfabric.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 29d117443..2cd41d4ca 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -4006,7 +4006,7 @@ efab_init_mac ( struct efab_nic *efab ) * because we want to use it, or because we're about * to reset the mac anyway */ - sleep ( 2 ); + mdelay ( 2000 ); if ( ! efab->link_up ) { EFAB_ERR ( "!\n" ); From 3ad028cf1cf8c9304c9a40051279761b0a6e0cca Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 16:09:18 +0000 Subject: [PATCH 167/591] [hermon] Fix received packet length Debugged-by: Wissam Shoukair Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 99a54a146..79d606093 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -1769,8 +1769,6 @@ static int hermon_complete ( struct ib_device *ibdev, } else { /* Set received length */ len = MLX_GET ( &cqe->normal, byte_cnt ); - assert ( len <= iob_tailroom ( iobuf ) ); - iob_put ( iobuf, len ); memset ( &recv_dest, 0, sizeof ( recv_dest ) ); recv_dest.qpn = qpn; memset ( &recv_source, 0, sizeof ( recv_source ) ); @@ -1781,6 +1779,7 @@ static int hermon_complete ( struct ib_device *ibdev, /* Locate corresponding GRH */ assert ( hermon_qp->recv.grh != NULL ); grh = &hermon_qp->recv.grh[ wqe_idx & wqe_idx_mask ]; + len -= sizeof ( *grh ); /* Construct address vector */ source = &recv_source; source->qpn = MLX_GET ( &cqe->normal, srq_rqpn ); @@ -1806,6 +1805,8 @@ static int hermon_complete ( struct ib_device *ibdev, assert ( 0 ); return -EINVAL; } + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); /* Hand off to completion handler */ ib_complete_recv ( ibdev, qp, &recv_dest, source, iobuf, rc ); } From 860d5904fb71bbf154901030712ed00e4403b79c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 16:11:58 +0000 Subject: [PATCH 168/591] [arbel] Fix received packet length Signed-off-by: Michael Brown --- src/drivers/infiniband/arbel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index 5ab701551..9671174c3 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -1646,8 +1646,6 @@ static int arbel_complete ( struct ib_device *ibdev, MLX_FILL_1 ( &recv_wqe->data[0], 0, byte_count, 0 ); MLX_FILL_1 ( &recv_wqe->data[0], 1, l_key, ARBEL_INVALID_LKEY ); - assert ( len <= iob_tailroom ( iobuf ) ); - iob_put ( iobuf, len ); memset ( &recv_dest, 0, sizeof ( recv_dest ) ); recv_dest.qpn = qpn; switch ( qp->type ) { @@ -1657,6 +1655,7 @@ static int arbel_complete ( struct ib_device *ibdev, /* Locate corresponding GRH */ assert ( arbel_recv_wq->grh != NULL ); grh = &arbel_recv_wq->grh[wqe_idx]; + len -= sizeof ( *grh ); /* Construct address vector */ source = &recv_source; memset ( source, 0, sizeof ( *source ) ); @@ -1677,6 +1676,8 @@ static int arbel_complete ( struct ib_device *ibdev, assert ( 0 ); return -EINVAL; } + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); /* Hand off to completion handler */ ib_complete_recv ( ibdev, qp, &recv_dest, source, iobuf, rc ); } From ee3122bc54631711e192c934734f3f5be40c2fa9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 16:12:32 +0000 Subject: [PATCH 169/591] [libc] Make sleep() interruptible Signed-off-by: Michael Brown --- src/core/exec.c | 16 ++-------------- src/core/timer.c | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/core/exec.c b/src/core/exec.c index 2c2ade0a5..a13884b68 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -36,10 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include -#include -#include -#include #include /** @file @@ -573,8 +569,6 @@ static struct command_descriptor sleep_cmd = static int sleep_exec ( int argc, char **argv ) { struct sleep_options opts; unsigned int seconds; - unsigned long start; - unsigned long delay; int rc; /* Parse options */ @@ -586,14 +580,8 @@ static int sleep_exec ( int argc, char **argv ) { return rc; /* Delay for specified number of seconds */ - start = currticks(); - delay = ( seconds * TICKS_PER_SEC ); - while ( ( currticks() - start ) <= delay ) { - step(); - if ( iskey() && ( getchar() == CTRL_C ) ) - return -ECANCELED; - cpu_nap(); - } + if ( sleep ( seconds ) != 0 ) + return -ECANCELED; return 0; } diff --git a/src/core/timer.c b/src/core/timer.c index dbd89f12b..ca945cfba 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -24,6 +24,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include +#include +#include /** * Delay for a fixed number of milliseconds @@ -36,12 +40,24 @@ void mdelay ( unsigned long msecs ) { } /** - * Delay for a fixed number of seconds + * Sleep (interruptibly) for a fixed number of seconds * * @v secs Number of seconds for which to delay + * @ret secs Number of seconds remaining, if interrupted */ unsigned int sleep ( unsigned int secs ) { - while ( secs-- ) - mdelay ( 1000 ); + unsigned long start = currticks(); + unsigned long now; + + for ( ; secs ; secs-- ) { + while ( ( ( now = currticks() ) - start ) < TICKS_PER_SEC ) { + step(); + if ( iskey() && ( getchar() == CTRL_C ) ) + return secs; + cpu_nap(); + } + start = now; + } + return 0; } From 3df598849b53c8bf5e87b6c300fdee6d0c0480f1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Mar 2016 17:33:21 +0000 Subject: [PATCH 170/591] [pxe] Implicitly open network device in PXENV_UDP_OPEN Some end-user configurations have been observed in which the first NBP (such as GRUB2) uses the UNDI API and then transfers control to a second NBP (such as pxelinux) which uses the UDP API. The first NBP closes the network device using PXENV_UNDI_CLOSE, which renders the UDP API unable to transmit or receive packets. The correct behaviour under these circumstances is (as often) simply not documented by the PXE specification. Testing with the Intel PXE stack suggests that PXENV_UDP_OPEN will implicitly reopen the network device if necessary, so match this behaviour. Signed-off-by: Michael Brown --- src/arch/x86/interface/pxe/pxe_udp.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/arch/x86/interface/pxe/pxe_udp.c b/src/arch/x86/interface/pxe/pxe_udp.c index 071cb59db..5a04f0865 100644 --- a/src/arch/x86/interface/pxe/pxe_udp.c +++ b/src/arch/x86/interface/pxe/pxe_udp.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -180,6 +181,15 @@ static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip; DBG ( " %s\n", inet_ntoa ( pxe_udp.local.sin_addr ) ); + /* Open network device, if necessary */ + if ( pxe_netdev && ( ! netdev_is_open ( pxe_netdev ) ) && + ( ( rc = netdev_open ( pxe_netdev ) ) != 0 ) ) { + DBG ( "PXENV_UDP_OPEN could not (implicitly) open %s: %s\n", + pxe_netdev->name, strerror ( rc ) ); + pxenv_udp_open->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + /* Open promiscuous UDP connection */ intf_restart ( &pxe_udp.xfer, 0 ); if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) { From 0a20373a2fb78622043277b4774676c55bbea22b Mon Sep 17 00:00:00 2001 From: Wissam Shoukair Date: Mon, 21 Mar 2016 15:09:13 +0200 Subject: [PATCH 171/591] [golan] Add Connect-IB, ConnectX-4 and ConnectX-4 Lx (Infiniband) support Signed-off-by: Wissam Shoukair Signed-off-by: Michael Brown --- src/Makefile | 8 + src/drivers/infiniband/CIB_PRM.h | 1168 ++++++++ src/drivers/infiniband/flexboot_nodnic.c | 1479 +++++++++ src/drivers/infiniband/flexboot_nodnic.h | 163 + src/drivers/infiniband/golan.c | 2663 +++++++++++++++++ src/drivers/infiniband/golan.h | 319 ++ .../infiniband/mlx_nodnic/include/mlx_cmd.h | 43 + .../mlx_nodnic/include/mlx_device.h | 80 + .../include/mlx_nodnic_data_structures.h | 201 ++ .../infiniband/mlx_nodnic/include/mlx_port.h | 229 ++ .../infiniband/mlx_nodnic/src/mlx_cmd.c | 77 + .../infiniband/mlx_nodnic/src/mlx_device.c | 339 +++ .../infiniband/mlx_nodnic/src/mlx_port.c | 1038 +++++++ .../include/private/mlx_memory_priv.h | 113 + .../mlx_utils/include/private/mlx_pci_priv.h | 72 + .../include/private/mlx_utils_priv.h | 68 + .../mlx_utils/include/public/mlx_bail.h | 47 + .../mlx_utils/include/public/mlx_icmd.h | 63 + .../mlx_utils/include/public/mlx_logging.h | 46 + .../mlx_utils/include/public/mlx_memory.h | 115 + .../mlx_utils/include/public/mlx_pci.h | 78 + .../mlx_utils/include/public/mlx_pci_gw.h | 81 + .../mlx_utils/include/public/mlx_types.h | 27 + .../mlx_utils/include/public/mlx_utils.h | 106 + .../mlx_lib/mlx_blink_leds/mlx_blink_leds.c | 54 + .../mlx_lib/mlx_blink_leds/mlx_blink_leds.h | 46 + .../mlx_lib/mlx_link_speed/mlx_link_speed.c | 180 ++ .../mlx_lib/mlx_link_speed/mlx_link_speed.h | 145 + .../mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c | 60 + .../mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h | 52 + .../mlx_lib/mlx_nvconfig/mlx_nvconfig.c | 295 ++ .../mlx_lib/mlx_nvconfig/mlx_nvconfig.h | 140 + .../mlx_nvconfig/mlx_nvconfig_defaults.c | 482 +++ .../mlx_nvconfig/mlx_nvconfig_defaults.h | 94 + .../mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h | 259 ++ .../mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c | 145 + .../mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h | 73 + .../mlx_lib/mlx_reg_access/mlx_reg_access.c | 90 + .../mlx_lib/mlx_reg_access/mlx_reg_access.h | 82 + .../mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c | 74 + .../mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h | 60 + .../mlx_lib/mlx_wol_rol/mlx_wol_rol.c | 84 + .../mlx_lib/mlx_wol_rol/mlx_wol_rol.h | 61 + .../src/private/uefi/mlx_logging_impl.c | 9 + .../mlx_utils/src/public/mlx_icmd.c | 371 +++ .../mlx_utils/src/public/mlx_memory.c | 238 ++ .../infiniband/mlx_utils/src/public/mlx_pci.c | 117 + .../mlx_utils/src/public/mlx_pci_gw.c | 392 +++ .../mlx_utils/src/public/mlx_utils.c | 121 + .../include/mlx_logging_priv.h | 61 + .../include/mlx_types_priv.h | 60 + .../mlx_utils_flexboot/src/mlx_memory_priv.c | 172 ++ .../mlx_utils_flexboot/src/mlx_pci_priv.c | 182 ++ .../mlx_utils_flexboot/src/mlx_utils_priv.c | 83 + src/drivers/infiniband/nodnic_prm.h | 47 + src/drivers/infiniband/nodnic_shomron_prm.h | 143 + src/include/ipxe/errfile.h | 2 + 57 files changed, 13097 insertions(+) create mode 100755 src/drivers/infiniband/CIB_PRM.h create mode 100644 src/drivers/infiniband/flexboot_nodnic.c create mode 100644 src/drivers/infiniband/flexboot_nodnic.h create mode 100755 src/drivers/infiniband/golan.c create mode 100755 src/drivers/infiniband/golan.h create mode 100644 src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h create mode 100644 src/drivers/infiniband/mlx_nodnic/include/mlx_device.h create mode 100644 src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h create mode 100644 src/drivers/infiniband/mlx_nodnic/include/mlx_port.h create mode 100644 src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c create mode 100644 src/drivers/infiniband/mlx_nodnic/src/mlx_device.c create mode 100644 src/drivers/infiniband/mlx_nodnic/src/mlx_port.c create mode 100644 src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h create mode 100644 src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h create mode 100644 src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_types.h create mode 100644 src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c create mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h create mode 100644 src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c create mode 100644 src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c create mode 100644 src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c create mode 100644 src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c create mode 100644 src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c create mode 100644 src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c create mode 100644 src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h create mode 100644 src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h create mode 100644 src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c create mode 100644 src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c create mode 100644 src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c create mode 100644 src/drivers/infiniband/nodnic_prm.h create mode 100644 src/drivers/infiniband/nodnic_shomron_prm.h diff --git a/src/Makefile b/src/Makefile index 0524bc761..58eedb11b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -84,6 +84,14 @@ SRCDIRS += drivers/block SRCDIRS += drivers/nvs SRCDIRS += drivers/bitbash SRCDIRS += drivers/infiniband +SRCDIRS += drivers/infiniband/mlx_utils_flexboot/src +SRCDIRS += drivers/infiniband/mlx_utils/src/public +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed +SRCDIRS += drivers/infiniband/mlx_nodnic/src SRCDIRS += drivers/usb SRCDIRS += interface/pxe interface/efi interface/smbios SRCDIRS += interface/bofm diff --git a/src/drivers/infiniband/CIB_PRM.h b/src/drivers/infiniband/CIB_PRM.h new file mode 100755 index 000000000..6d07c0151 --- /dev/null +++ b/src/drivers/infiniband/CIB_PRM.h @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2013-2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef __CIB_PRM__ +#define __CIB_PRM__ + +typedef unsigned long long __be64; +typedef uint32_t __be32; +typedef uint16_t __be16; + +#define GOLAN_CMD_DATA_BLOCK_SIZE (1 << 9) +#define GOLAN_CMD_PAS_CNT (GOLAN_CMD_DATA_BLOCK_SIZE / sizeof(__be64)) +#define MAILBOX_STRIDE (1 << 10) +#define MAILBOX_MASK (MAILBOX_STRIDE - 1) + +#define GOLAN_PCI_CMD_XPORT 7 +#define CMD_OWNER_HW 0x1 + +#define IB_NUM_PKEYS 0x20 + +struct health_buffer { + __be32 assert_var[5]; + __be32 rsvd0[3]; + __be32 assert_exit_ptr; + __be32 assert_callra; + __be32 rsvd1[2]; + __be32 fw_ver; + __be32 hw_id; + __be32 rsvd2; + u8 irisc_index; + u8 synd; + __be16 ext_sync; +} __attribute ( ( packed ) ); + +struct golan_hca_init_seg { + __be32 fw_rev; + __be32 cmdif_rev_fw_sub; + __be32 rsvd0[2]; + __be32 cmdq_addr_h; + __be32 cmdq_addr_l_sz; + __be32 cmd_dbell; + __be32 rsvd1[121]; + struct health_buffer health; + __be32 rsvd2[884]; + __be32 health_counter; + __be32 rsvd3[1023]; + __be64 ieee1588_clk; + __be32 ieee1588_clk_type; + __be32 clr_intx; +} __attribute ( ( packed ) ); + +enum golan_manage_pages_mode { + GOLAN_PAGES_CANT_GIVE = 0, + GOLAN_PAGES_GIVE = 1, + GOLAN_PAGES_TAKE = 2 +}; + +enum golan_qry_pages_mode { + GOLAN_BOOT_PAGES = 0x1, + GOLAN_INIT_PAGES = 0x2, + GOLAN_REG_PAGES = 0x3, +}; + +enum { + GOLAN_REG_PCAP = 0x5001, + GOLAN_REG_PMTU = 0x5003, + GOLAN_REG_PTYS = 0x5004, + GOLAN_REG_PAOS = 0x5006, + GOLAN_REG_PMAOS = 0x5012, + GOLAN_REG_PUDE = 0x5009, + GOLAN_REG_PMPE = 0x5010, + GOLAN_REG_PELC = 0x500e, + GOLAN_REG_PMLP = 0, /* TBD */ + GOLAN_REG_NODE_DESC = 0x6001, + GOLAN_REG_HOST_ENDIANESS = 0x7004, +}; + +enum { + GOLAN_CMD_OP_QUERY_HCA_CAP = 0x100, + GOLAN_CMD_OP_QUERY_ADAPTER = 0x101, + GOLAN_CMD_OP_INIT_HCA = 0x102, + GOLAN_CMD_OP_TEARDOWN_HCA = 0x103, + GOLAN_CMD_OP_ENABLE_HCA = 0x104, + GOLAN_CMD_OP_DISABLE_HCA = 0x105, + + GOLAN_CMD_OP_QUERY_PAGES = 0x107, + GOLAN_CMD_OP_MANAGE_PAGES = 0x108, + GOLAN_CMD_OP_SET_HCA_CAP = 0x109, + + GOLAN_CMD_OP_CREATE_MKEY = 0x200, + GOLAN_CMD_OP_QUERY_MKEY = 0x201, + GOLAN_CMD_OP_DESTROY_MKEY = 0x202, + GOLAN_CMD_OP_QUERY_SPECIAL_CONTEXTS = 0x203, + + GOLAN_CMD_OP_CREATE_EQ = 0x301, + GOLAN_CMD_OP_DESTROY_EQ = 0x302, + GOLAN_CMD_OP_QUERY_EQ = 0x303, + + GOLAN_CMD_OP_CREATE_CQ = 0x400, + GOLAN_CMD_OP_DESTROY_CQ = 0x401, + GOLAN_CMD_OP_QUERY_CQ = 0x402, + GOLAN_CMD_OP_MODIFY_CQ = 0x403, + + GOLAN_CMD_OP_CREATE_QP = 0x500, + GOLAN_CMD_OP_DESTROY_QP = 0x501, + GOLAN_CMD_OP_RST2INIT_QP = 0x502, + GOLAN_CMD_OP_INIT2RTR_QP = 0x503, + GOLAN_CMD_OP_RTR2RTS_QP = 0x504, + GOLAN_CMD_OP_RTS2RTS_QP = 0x505, + GOLAN_CMD_OP_SQERR2RTS_QP = 0x506, + GOLAN_CMD_OP_2ERR_QP = 0x507, + GOLAN_CMD_OP_RTS2SQD_QP = 0x508, + GOLAN_CMD_OP_SQD2RTS_QP = 0x509, + GOLAN_CMD_OP_2RST_QP = 0x50a, + GOLAN_CMD_OP_QUERY_QP = 0x50b, + GOLAN_CMD_OP_CONF_SQP = 0x50c, + GOLAN_CMD_OP_MAD_IFC = 0x50d, + GOLAN_CMD_OP_INIT2INIT_QP = 0x50e, + GOLAN_CMD_OP_SUSPEND_QP = 0x50f, + GOLAN_CMD_OP_UNSUSPEND_QP = 0x510, + GOLAN_CMD_OP_SQD2SQD_QP = 0x511, + GOLAN_CMD_OP_ALLOC_QP_COUNTER_SET = 0x512, + GOLAN_CMD_OP_DEALLOC_QP_COUNTER_SET = 0x513, + GOLAN_CMD_OP_QUERY_QP_COUNTER_SET = 0x514, + + GOLAN_CMD_OP_CREATE_PSV = 0x600, + GOLAN_CMD_OP_DESTROY_PSV = 0x601, + GOLAN_CMD_OP_QUERY_PSV = 0x602, + GOLAN_CMD_OP_QUERY_SIG_RULE_TABLE = 0x603, + GOLAN_CMD_OP_QUERY_BLOCK_SIZE_TABLE = 0x604, + + GOLAN_CMD_OP_CREATE_SRQ = 0x700, + GOLAN_CMD_OP_DESTROY_SRQ = 0x701, + GOLAN_CMD_OP_QUERY_SRQ = 0x702, + GOLAN_CMD_OP_ARM_RQ = 0x703, + GOLAN_CMD_OP_RESIZE_SRQ = 0x704, + + GOLAN_CMD_OP_QUERY_HCA_VPORT_CONTEXT = 0x762, + GOLAN_CMD_OP_QUERY_HCA_VPORT_GID = 0x764, + GOLAN_CMD_OP_QUERY_HCA_VPORT_PKEY = 0x765, + + GOLAN_CMD_OP_ALLOC_PD = 0x800, + GOLAN_CMD_OP_DEALLOC_PD = 0x801, + GOLAN_CMD_OP_ALLOC_UAR = 0x802, + GOLAN_CMD_OP_DEALLOC_UAR = 0x803, + + GOLAN_CMD_OP_ATTACH_TO_MCG = 0x806, + GOLAN_CMD_OP_DETACH_FROM_MCG = 0x807, + + + GOLAN_CMD_OP_ALLOC_XRCD = 0x80e, + GOLAN_CMD_OP_DEALLOC_XRCD = 0x80f, + + GOLAN_CMD_OP_ACCESS_REG = 0x805, +}; + +struct golan_inbox_hdr { + __be16 opcode; + u8 rsvd[4]; + __be16 opmod; +} __attribute ( ( packed ) ); + +struct golan_cmd_layout { + u8 type; + u8 rsvd0[3]; + __be32 inlen; + union { + __be64 in_ptr; + __be32 in_ptr32[2]; + }; + __be32 in[4]; + __be32 out[4]; + union { + __be64 out_ptr; + __be32 out_ptr32[2]; + }; + __be32 outlen; + u8 token; + u8 sig; + u8 rsvd1; + volatile u8 status_own; +} __attribute ( ( packed ) ); + +struct golan_outbox_hdr { + u8 status; + u8 rsvd[3]; + __be32 syndrome; +} __attribute ( ( packed ) ); + +enum { + GOLAN_DEV_CAP_FLAG_RC = 1LL << 0, + GOLAN_DEV_CAP_FLAG_UC = 1LL << 1, + GOLAN_DEV_CAP_FLAG_UD = 1LL << 2, + GOLAN_DEV_CAP_FLAG_XRC = 1LL << 3, + GOLAN_DEV_CAP_FLAG_SRQ = 1LL << 6, + GOLAN_DEV_CAP_FLAG_BAD_PKEY_CNTR = 1LL << 8, + GOLAN_DEV_CAP_FLAG_BAD_QKEY_CNTR = 1LL << 9, + GOLAN_DEV_CAP_FLAG_APM = 1LL << 17, + GOLAN_DEV_CAP_FLAG_ATOMIC = 1LL << 18, + GOLAN_DEV_CAP_FLAG_ON_DMND_PG = 1LL << 24, + GOLAN_DEV_CAP_FLAG_RESIZE_SRQ = 1LL << 32, + GOLAN_DEV_CAP_FLAG_REMOTE_FENCE = 1LL << 38, + GOLAN_DEV_CAP_FLAG_TLP_HINTS = 1LL << 39, + GOLAN_DEV_CAP_FLAG_SIG_HAND_OVER = 1LL << 40, + GOLAN_DEV_CAP_FLAG_DCT = 1LL << 41, + GOLAN_DEV_CAP_FLAG_CMDIF_CSUM = 1LL << 46, +}; + + +struct golan_hca_cap { + u8 rsvd1[16]; + u8 log_max_srq_sz; + u8 log_max_qp_sz; + u8 rsvd2; + u8 log_max_qp; + u8 log_max_strq_sz; + u8 log_max_srqs; + u8 rsvd4[2]; + u8 rsvd5; + u8 log_max_cq_sz; + u8 rsvd6; + u8 log_max_cq; + u8 log_max_eq_sz; + u8 log_max_mkey; + u8 rsvd7; + u8 log_max_eq; + u8 max_indirection; + u8 log_max_mrw_sz; + u8 log_max_bsf_list_sz; + u8 log_max_klm_list_sz; + u8 rsvd_8_0; + u8 log_max_ra_req_dc; + u8 rsvd_8_1; + u8 log_max_ra_res_dc; + u8 rsvd9; + u8 log_max_ra_req_qp; + u8 rsvd10; + u8 log_max_ra_res_qp; + u8 rsvd11[4]; + __be16 max_qp_count; + __be16 pkey_table_size; + u8 rsvd13; + u8 local_ca_ack_delay; + u8 rsvd14; + u8 num_ports; + u8 log_max_msg; + u8 rsvd15[3]; + __be16 stat_rate_support; + u8 rsvd16[2]; + __be64 flags; + u8 rsvd17; + u8 uar_sz; + u8 rsvd18; + u8 log_pg_sz; + __be16 bf_log_bf_reg_size; + u8 rsvd19[4]; + __be16 max_wqe_sz_sq; + u8 rsvd20[2]; + __be16 max_wqe_sz_rq; + u8 rsvd21[2]; + __be16 max_wqe_sz_sq_dc; + u8 rsvd22[4]; + __be16 max_qp_mcg; + u8 rsvd23; + u8 log_max_mcg; + u8 rsvd24; + u8 log_max_pd; + u8 rsvd25; + u8 log_max_xrcd; + u8 rsvd26[40]; + __be32 uar_page_sz; + u8 rsvd27[28]; + u8 log_msx_atomic_size_qp; + u8 rsvd28[2]; + u8 log_msx_atomic_size_dc; + u8 rsvd29[76]; +} __attribute ( ( packed ) ); + +struct golan_query_pages_inbox { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_query_pages_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd[2]; + __be16 func_id; + __be32 num_pages; +} __attribute ( ( packed ) ); + +struct golan_cmd_query_hca_cap_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_cmd_query_hca_cap_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; + struct golan_hca_cap hca_cap; +} __attribute ( ( packed ) ); + +struct golan_cmd_set_hca_cap_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; + struct golan_hca_cap hca_cap; +} __attribute ( ( packed ) ); + +struct golan_cmd_set_hca_cap_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_cmd_init_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[2]; + __be16 profile; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_cmd_init_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +enum golan_teardown { + GOLAN_TEARDOWN_GRACEFUL = 0x0, + GOLAN_TEARDOWN_PANIC = 0x1 +}; + +struct golan_cmd_teardown_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[2]; + __be16 profile; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_cmd_teardown_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_enable_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_enable_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_disable_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_disable_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_inbox_data { + u8 rsvd2[16]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_inbox { + struct golan_inbox_hdr hdr; + __be16 rsvd0; + __be16 func_id; + __be32 num_entries; + struct golan_manage_pages_inbox_data data; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_outbox_data { + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_outbox { + struct golan_outbox_hdr hdr; + __be32 num_entries; + __be32 rsrvd; + struct golan_manage_pages_outbox_data data; +} __attribute ( ( packed ) ); + +struct golan_reg_host_endianess { + u8 he; + u8 rsvd[15]; +} __attribute ( ( packed ) ); + +struct golan_cmd_prot_block { + union { + __be64 data[GOLAN_CMD_PAS_CNT]; + u8 bdata[GOLAN_CMD_DATA_BLOCK_SIZE]; + }; + u8 rsvd0[48]; + __be64 next; + __be32 block_num; + u8 rsvd1; + u8 token; + u8 ctrl_sig; + u8 sig; +} __attribute ( ( packed ) ); + +/* MAD IFC structures */ +#define GOLAN_MAD_SIZE 256 +#define GOLAN_MAD_IFC_NO_VALIDATION 0x3 +#define GOLAN_MAD_IFC_RLID_BIT 16 + +struct golan_mad_ifc_mbox_in { + struct golan_inbox_hdr hdr; + __be16 remote_lid; + u8 rsvd0; + u8 port; + u8 rsvd1[4]; + u8 mad[GOLAN_MAD_SIZE]; +} __attribute ( ( packed ) ); + +struct golan_mad_ifc_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; + u8 mad[GOLAN_MAD_SIZE]; +} __attribute ( ( packed ) ); + +/* UAR Structures */ +struct golan_alloc_uar_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_alloc_uar_mbox_out { + struct golan_outbox_hdr hdr; + __be32 uarn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_free_uar_mbox_in { + struct golan_inbox_hdr hdr; + __be32 uarn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_free_uar_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/* Event Queue Structures */ +enum { + GOLAN_EQ_STATE_ARMED = 0x9, + GOLAN_EQ_STATE_FIRED = 0xa, + GOLAN_EQ_STATE_ALWAYS_ARMED = 0xb, +}; + + +struct golan_eq_context { + u8 status; + u8 ec_oi; + u8 st; + u8 rsvd2[7]; + __be16 page_pffset; + __be32 log_sz_usr_page; + u8 rsvd3[7]; + u8 intr; + u8 log_page_size; + u8 rsvd4[15]; + __be32 consumer_counter; + __be32 produser_counter; + u8 rsvd5[16]; +} __attribute ( ( packed ) ); + +struct golan_create_eq_mbox_in_data { + struct golan_eq_context ctx; + u8 rsvd2[8]; + __be64 events_mask; + u8 rsvd3[176]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_eq_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[3]; + u8 input_eqn; + u8 rsvd1[4]; + struct golan_create_eq_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_eq_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[3]; + u8 eq_number; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_eq_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[3]; + u8 eqn; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_eq_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/***********************************************/ +/************** Query Vport ****************/ +struct golan_query_hca_vport_context_inbox { + struct golan_inbox_hdr hdr; + __be16 other_vport : 1; + __be16 rsvd1 : 7; + __be16 port_num : 4; + __be16 rsvd2 : 4; + __be16 vport_number; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_context_data { + __be32 field_select; + __be32 rsvd1[7]; + //**** + __be16 sm_virt_aware : 1; + __be16 has_smi : 1; + __be16 has_raw : 1; + __be16 grh_required : 1; + __be16 rsvd2 : 12; + u8 port_physical_state : 4; + u8 vport_state_policy : 4; + u8 port_state : 4; + u8 vport_state : 4; + //**** + u8 rsvd3[4]; + //**** + __be32 system_image_guid[2]; + //**** + __be32 port_guid[2]; + //**** + __be32 node_guid[2]; + //**** + __be32 cap_mask1; + __be32 cap_mask1_field_select; + __be32 cap_mask2; + __be32 cap_mask2_field_select; + u8 rsvd4[16]; + __be16 lid; + u8 rsvd5 : 4; + u8 init_type_reply : 4; + u8 lmc : 3; + u8 subnet_timeout : 5; + __be16 sm_lid; + u8 sm_sl : 4; + u8 rsvd6 : 4; + u8 rsvd7; + __be16 qkey_violation_counter; + __be16 pkey_violation_counter; + u8 rsvd8[100]; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_context_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; + struct golan_query_hca_vport_context_data context_data; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_gid_inbox { + struct golan_inbox_hdr hdr; + u8 other_vport : 1; + u8 rsvd1 : 7; + u8 port_num : 4; + u8 rsvd2 : 4; + __be16 vport_number; + __be16 rsvd3; + __be16 gid_index; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_gid_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd0[4]; + __be16 gids_num; + u8 rsvd1[2]; + __be32 gid0[4]; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_pkey_inbox { + struct golan_inbox_hdr hdr; + u8 other_vport : 1; + u8 rsvd1 : 7; + u8 port_num : 4; + u8 rsvd2 : 4; + __be16 vport_number; + __be16 rsvd3; + __be16 pkey_index; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_pkey_data { + __be16 rsvd1; + __be16 pkey0; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_pkey_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; + struct golan_query_hca_vport_pkey_data *pkey_data; +} __attribute ( ( packed ) ); + +struct golan_eqe_comp { + __be32 reserved[6]; + __be32 cqn; +} __attribute ( ( packed ) ); + +struct golan_eqe_qp_srq { + __be32 reserved[6]; + __be32 qp_srq_n; +} __attribute ( ( packed ) ); + +struct golan_eqe_cq_err { + __be32 cqn; + u8 reserved1[7]; + u8 syndrome; +} __attribute ( ( packed ) ); + +struct golan_eqe_dropped_packet { +}; + +struct golan_eqe_port_state { + u8 reserved0[8]; + u8 port; +} __attribute ( ( packed ) ); + +struct golan_eqe_gpio { + __be32 reserved0[2]; + __be64 gpio_event; +} __attribute ( ( packed ) ); + +struct golan_eqe_congestion { + u8 type; + u8 rsvd0; + u8 congestion_level; +} __attribute ( ( packed ) ); + +struct golan_eqe_stall_vl { + u8 rsvd0[3]; + u8 port_vl; +} __attribute ( ( packed ) ); + +struct golan_eqe_cmd { + __be32 vector; + __be32 rsvd[6]; +} __attribute ( ( packed ) ); + +struct golan_eqe_page_req { + u8 rsvd0[2]; + __be16 func_id; + u8 rsvd1[2]; + __be16 num_pages; + __be32 rsvd2[5]; +} __attribute ( ( packed ) ); + +union ev_data { + __be32 raw[7]; + struct golan_eqe_cmd cmd; + struct golan_eqe_comp comp; + struct golan_eqe_qp_srq qp_srq; + struct golan_eqe_cq_err cq_err; + struct golan_eqe_dropped_packet dp; + struct golan_eqe_port_state port; + struct golan_eqe_gpio gpio; + struct golan_eqe_congestion cong; + struct golan_eqe_stall_vl stall_vl; + struct golan_eqe_page_req req_pages; +} __attribute__ ((packed)); + +struct golan_eqe { + u8 rsvd0; + u8 type; + u8 rsvd1; + u8 sub_type; + __be32 rsvd2[7]; + union ev_data data; + __be16 rsvd3; + u8 signature; + u8 owner; +} __attribute__ ((packed)); + +/* Protection Domain Structures */ +struct golan_alloc_pd_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_alloc_pd_mbox_out { + struct golan_outbox_hdr hdr; + __be32 pdn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_dealloc_pd_mbox_in { + struct golan_inbox_hdr hdr; + __be32 pdn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_dealloc_pd_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/* Memory key structures */ +#define GOLAN_IB_ACCESS_LOCAL_READ (1 << 2) +#define GOLAN_IB_ACCESS_LOCAL_WRITE (1 << 3) +#define GOLAN_MKEY_LEN64 (1 << 31) +#define GOLAN_CREATE_MKEY_SEG_QPN_BIT 8 + +struct golan_mkey_seg { + /* + * This is a two bit field occupying bits 31-30. + * bit 31 is always 0, + * bit 30 is zero for regular MRs and 1 (e.g free) for UMRs that do not have tanslation + */ + u8 status; + u8 pcie_control; + u8 flags; + u8 version; + __be32 qpn_mkey7_0; + u8 rsvd1[4]; + __be32 flags_pd; + __be64 start_addr; + __be64 len; + __be32 bsfs_octo_size; + u8 rsvd2[16]; + __be32 xlt_oct_size; + u8 rsvd3[3]; + u8 log2_page_size; + u8 rsvd4[4]; +} __attribute ( ( packed ) ); + +struct golan_create_mkey_mbox_in_data { + struct golan_mkey_seg seg; + u8 rsvd1[16]; + __be32 xlat_oct_act_size; + __be32 bsf_coto_act_size; + u8 rsvd2[168]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_mkey_mbox_in { + struct golan_inbox_hdr hdr; + __be32 input_mkey_index; + u8 rsvd0[4]; + struct golan_create_mkey_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_mkey_mbox_out { + struct golan_outbox_hdr hdr; + __be32 mkey; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_mkey_mbox_in { + struct golan_inbox_hdr hdr; + __be32 mkey; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_mkey_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/* Completion Queue Structures */ +enum { + GOLAN_CQ_STATE_ARMED = 9, + GOLAN_CQ_STATE_ALWAYS_ARMED = 0xb, + GOLAN_CQ_STATE_FIRED = 0xa +}; + +enum { + GOLAN_CQE_REQ = 0, + GOLAN_CQE_RESP_WR_IMM = 1, + GOLAN_CQE_RESP_SEND = 2, + GOLAN_CQE_RESP_SEND_IMM = 3, + GOLAN_CQE_RESP_SEND_INV = 4, + GOLAN_CQE_RESIZE_CQ = 0xff, /* TBD */ + GOLAN_CQE_REQ_ERR = 13, + GOLAN_CQE_RESP_ERR = 14 +}; + +struct golan_cq_context { + u8 status; + u8 cqe_sz_flags; + u8 st; + u8 rsvd3; + u8 rsvd4[6]; + __be16 page_offset; + __be32 log_sz_usr_page; + __be16 cq_period; + __be16 cq_max_count; + __be16 rsvd20; + __be16 c_eqn; + u8 log_pg_sz; + u8 rsvd25[7]; + __be32 last_notified_index; + __be32 solicit_producer_index; + __be32 consumer_counter; + __be32 producer_counter; + u8 rsvd48[8]; + __be64 db_record_addr; +} __attribute ( ( packed ) ); + + +struct golan_create_cq_mbox_in_data { + struct golan_cq_context ctx; + u8 rsvd6[192]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_cq_mbox_in { + struct golan_inbox_hdr hdr; + __be32 input_cqn; + u8 rsvdx[4]; + struct golan_create_cq_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_cq_mbox_out { + struct golan_outbox_hdr hdr; + __be32 cqn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_cq_mbox_in { + struct golan_inbox_hdr hdr; + __be32 cqn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_cq_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_err_cqe { + u8 rsvd0[32]; + __be32 srqn; + u8 rsvd1[16]; + u8 hw_syndrom; + u8 rsvd2; + u8 vendor_err_synd; + u8 syndrome; + __be32 s_wqe_opcode_qpn; + __be16 wqe_counter; + u8 signature; + u8 op_own; +} __attribute ( ( packed ) ); + +struct golan_cqe64 { + u8 rsvd0[17]; + u8 ml_path; + u8 rsvd20[4]; + __be16 slid; + __be32 flags_rqpn; + u8 rsvd28[4]; + __be32 srqn; + __be32 imm_inval_pkey; + u8 rsvd40[4]; + __be32 byte_cnt; + __be64 timestamp; + __be32 sop_drop_qpn; + __be16 wqe_counter; + u8 signature; + u8 op_own; +} __attribute ( ( packed ) ); + +/* Queue Pair Structures */ +#define GOLAN_QP_CTX_ST_BIT 16 +#define GOLAN_QP_CTX_PM_STATE_BIT 11 +#define GOLAN_QP_CTX_FRE_BIT 11 +#define GOLAN_QP_CTX_RLKY_BIT 4 +#define GOLAN_QP_CTX_RQ_SIZE_BIT 3 +#define GOLAN_QP_CTX_SQ_SIZE_BIT 11 +#define GOLAN_QP_CTX_MTU_BIT 5 +#define GOLAN_QP_CTX_ACK_REQ_FREQ_BIT 28 + +enum { + GOLAN_QP_CTX_DONT_USE_RSRVD_LKEY = 0, + GOLAN_QP_CTX_USE_RSRVD_LKEY = 1 +}; + +enum { + GOLAN_IB_ACK_REQ_FREQ = 8, +}; + +enum golan_qp_optpar { + GOLAN_QP_PARAM_ALT_ADDR_PATH = 1 << 0, + GOLAN_QP_PARAM_RRE = 1 << 1, + GOLAN_QP_PARAM_RAE = 1 << 2, + GOLAN_QP_PARAM_RWE = 1 << 3, + GOLAN_QP_PARAM_PKEY_INDEX = 1 << 4, + GOLAN_QP_PARAM_Q_KEY = 1 << 5, + GOLAN_QP_PARAM_RNR_TIMEOUT = 1 << 6, + GOLAN_QP_PARAM_PRIMARY_ADDR_PATH = 1 << 7, + GOLAN_QP_PARAM_SRA_MAX = 1 << 8, + GOLAN_QP_PARAM_RRA_MAX = 1 << 9, + GOLAN_QP_PARAM_PM_STATE = 1 << 10, + GOLAN_QP_PARAM_RETRY_COUNT = 1 << 12, + GOLAN_QP_PARAM_RNR_RETRY = 1 << 13, + GOLAN_QP_PARAM_ACK_TIMEOUT = 1 << 14, + GOLAN_QP_PARAM_PRI_PORT = 1 << 16, + GOLAN_QP_PARAM_SRQN = 1 << 18, + GOLAN_QP_PARAM_CQN_RCV = 1 << 19, + GOLAN_QP_PARAM_DC_HS = 1 << 20, + GOLAN_QP_PARAM_DC_KEY = 1 << 21 +}; + +#define GOLAN_QP_PARAMS_INIT2RTR_MASK (GOLAN_QP_PARAM_PKEY_INDEX |\ + GOLAN_QP_PARAM_Q_KEY |\ + GOLAN_QP_PARAM_RWE |\ + GOLAN_QP_PARAM_RRE) + +#define GOLAN_QP_PARAMS_RTR2RTS_MASK (GOLAN_QP_PARAM_PM_STATE |\ + GOLAN_QP_PARAM_RNR_TIMEOUT |\ + GOLAN_QP_PARAM_Q_KEY |\ + GOLAN_QP_PARAM_RWE |\ + GOLAN_QP_PARAM_RRE) + + +enum { + GOLAN_QP_ST_RC = 0x0, + GOLAN_QP_ST_UC = 0x1, + GOLAN_QP_ST_UD = 0x2, + GOLAN_QP_ST_XRC = 0x3, + GOLAN_QP_ST_MLX = 0x4, + GOLAN_QP_ST_DC = 0x5, + GOLAN_QP_ST_QP0 = 0x7, + GOLAN_QP_ST_QP1 = 0x8, + GOLAN_QP_ST_RAW_ETHERTYPE = 0x9, + GOLAN_QP_ST_RAW_IPV6 = 0xa, + GOLAN_QP_ST_SNIFFER = 0xb, + GOLAN_QP_ST_SYNC_UMR = 0xe, + GOLAN_QP_ST_PTP_1588 = 0xd, + GOLAN_QP_ST_REG_UMR = 0xc, + GOLAN_QP_ST_MAX +}; + +enum { + GOLAN_QP_PM_MIGRATED = 0x3, + GOLAN_QP_PM_ARMED = 0x0, + GOLAN_QP_PM_REARM = 0x1 +}; + +enum { + GOLAN_QP_LAT_SENSITIVE = 1 << 28, + GOLAN_QP_ENABLE_SIG = 1 << 31 +}; + + +struct golan_qp_db { + u8 rsvd0[2]; + __be16 recv_db; + u8 rsvd1[2]; + __be16 send_db; +} __attribute ( ( packed ) ); + +enum { + GOLAN_WQE_CTRL_CQ_UPDATE = 2 << 2, /*Wissam, wtf?*/ + GOLAN_WQE_CTRL_SOLICITED = 1 << 1 +}; + +struct golan_wqe_ctrl_seg { + __be32 opmod_idx_opcode; + __be32 qpn_ds; + u8 signature; + u8 rsvd[2]; + u8 fm_ce_se; + __be32 imm; +} __attribute ( ( packed ) ); + +struct golan_av { + union { + struct { + __be32 qkey; + __be32 reserved; + } qkey; + __be64 dc_key; + } key; + __be32 dqp_dct; + u8 stat_rate_sl; + u8 fl_mlid; + __be16 rlid; + u8 reserved0[10]; + u8 tclass; + u8 hop_limit; + __be32 grh_gid_fl; + u8 rgid[16]; +} __attribute ( ( packed ) ); + +struct golan_wqe_data_seg { + __be32 byte_count; + __be32 lkey; + __be64 addr; +} __attribute ( ( packed ) ); + +struct golan_wqe_signature_seg { + u8 rsvd0[4]; + u8 signature; + u8 rsvd1[11]; +} __attribute ( ( packed ) ); + +struct golan_wqe_inline_seg { + __be32 byte_count; +} __attribute ( ( packed ) ); + +struct golan_qp_path { + u8 fl; + u8 rsvd3; + u8 free_ar; + u8 pkey_index; + u8 rsvd0; + u8 grh_mlid; + __be16 rlid; + u8 ackto_lt; + u8 mgid_index; + u8 static_rate; + u8 hop_limit; + __be32 tclass_flowlabel; + u8 rgid[16]; + u8 rsvd1[4]; + u8 sl; + u8 port; + u8 rsvd2[6]; +} __attribute ( ( packed ) ); + +struct golan_qp_context { + __be32 flags; + __be32 flags_pd; + u8 mtu_msgmax; + u8 rq_size_stride; + __be16 sq_crq_size; + __be32 qp_counter_set_usr_page; + __be32 wire_qpn; + __be32 log_pg_sz_remote_qpn; + struct golan_qp_path pri_path; + struct golan_qp_path alt_path; + __be32 params1; + u8 reserved2[4]; + __be32 next_send_psn; + __be32 cqn_send; + u8 reserved3[8]; + __be32 last_acked_psn; + __be32 ssn; + __be32 params2; + __be32 rnr_nextrecvpsn; + __be32 xrcd; + __be32 cqn_recv; + __be64 db_rec_addr; + __be32 qkey; + __be32 rq_type_srqn; + __be32 rmsn; + __be16 hw_sq_wqe_counter; + __be16 sw_sq_wqe_counter; + __be16 hw_rcyclic_byte_counter; + __be16 hw_rq_counter; + __be16 sw_rcyclic_byte_counter; + __be16 sw_rq_counter; + u8 rsvd0[5]; + u8 cgs; + u8 cs_req; + u8 cs_res; + __be64 dc_access_key; + u8 rsvd1[24]; +} __attribute ( ( packed ) ); + +struct golan_create_qp_mbox_in_data { + __be32 opt_param_mask; + u8 rsvd1[4]; + struct golan_qp_context ctx; + u8 rsvd3[16]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_qp_mbox_in { + struct golan_inbox_hdr hdr; + __be32 input_qpn; + u8 rsvd0[4]; + struct golan_create_qp_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_qp_mbox_out { + struct golan_outbox_hdr hdr; + __be32 qpn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_qp_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_qp_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_modify_qp_mbox_in_data { + __be32 optparam; + u8 rsvd0[4]; + struct golan_qp_context ctx; +} __attribute ( ( packed ) ); + +struct golan_modify_qp_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + u8 rsvd1[4]; + struct golan_modify_qp_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_modify_qp_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_attach_mcg_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + __be32 rsvd; + u8 gid[16]; +} __attribute ( ( packed ) ); + +struct golan_attach_mcg_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvf[8]; +} __attribute ( ( packed ) ); + +struct golan_detach_mcg_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + __be32 rsvd; + u8 gid[16]; +} __attribute ( ( packed ) ); + +struct golan_detach_mcg_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvf[8]; +} __attribute ( ( packed ) ); + + +#define MAILBOX_SIZE sizeof(struct golan_cmd_prot_block) + +#endif /* __CIB_PRM__ */ diff --git a/src/drivers/infiniband/flexboot_nodnic.c b/src/drivers/infiniband/flexboot_nodnic.c new file mode 100644 index 000000000..dea19ca69 --- /dev/null +++ b/src/drivers/infiniband/flexboot_nodnic.c @@ -0,0 +1,1479 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flexboot_nodnic.h" +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" +#include "mlx_utils/include/public/mlx_pci_gw.h" +#include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h" +#include "mlx_utils/include/public/mlx_types.h" +#include "mlx_utils/include/public/mlx_utils.h" +#include "mlx_utils/include/public/mlx_bail.h" +#include "mlx_nodnic/include/mlx_cmd.h" +#include "mlx_utils/include/public/mlx_memory.h" +#include "mlx_utils/include/public/mlx_pci.h" +#include "mlx_nodnic/include/mlx_device.h" +#include "mlx_nodnic/include/mlx_port.h" + +/*************************************************************************** + * + * Completion queue operations + * + *************************************************************************** + */ +static int flexboot_nodnic_arm_cq ( struct flexboot_nodnic_port *port ) { +#ifndef DEVICE_CX3 + mlx_uint32 val = ( port->eth_cq->next_idx & 0xffff ); + if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) { + MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); + return MLX_FAILED; + } +#else + mlx_utils *utils = port->port_priv.device->utils; + nodnic_port_data_flow_gw *ptr = port->port_priv.data_flow_gw; + mlx_uint32 data = 0; + mlx_uint32 val = 0; + + if ( port->port_priv.device->device_cap.crspace_doorbells == 0 ) { + val = ( port->eth_cq->next_idx & 0xffff ); + if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) { + MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); + return MLX_FAILED; + } + } else { + /* Arming the CQ with CQ CI should be with this format - + * 16 bit - CQ CI - same endianness as the FW (don't swap bytes) + * 15 bit - reserved + * 1 bit - arm CQ - must correct the endianness with the reserved above */ + data = ( ( ( port->eth_cq->next_idx & 0xffff ) << 16 ) | 0x0080 ); + /* Write the new index and update FW that new data was submitted */ + mlx_pci_mem_write ( utils, MlxPciWidthUint32, 0, + ( mlx_uint64 ) & ( ptr->armcq_cq_ci_dword ), 1, &data ); + } +#endif + return 0; +} + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int flexboot_nodnic_create_cq ( struct ib_device *ibdev , + struct ib_completion_queue *cq ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq; + mlx_status status = MLX_SUCCESS; + + flexboot_nodnic_cq = (struct flexboot_nodnic_completion_queue *) + zalloc(sizeof(*flexboot_nodnic_cq)); + if ( flexboot_nodnic_cq == NULL ) { + status = MLX_OUT_OF_RESOURCES; + goto qp_alloc_err; + } + + status = nodnic_port_create_cq(&port->port_priv, + cq->num_cqes * + flexboot_nodnic->callbacks->get_cqe_size(), + &flexboot_nodnic_cq->nodnic_completion_queue + ); + MLX_FATAL_CHECK_STATUS(status, create_err, + "nodnic_port_create_cq failed"); + flexboot_nodnic->callbacks->cqe_set_owner( + flexboot_nodnic_cq->nodnic_completion_queue->cq_virt, + cq->num_cqes); + + + ib_cq_set_drvdata ( cq, flexboot_nodnic_cq ); + return status; +create_err: + free(flexboot_nodnic_cq); +qp_alloc_err: + return status; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void flexboot_nodnic_destroy_cq ( struct ib_device *ibdev , + struct ib_completion_queue *cq ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq ); + + nodnic_port_destroy_cq(&port->port_priv, + flexboot_nodnic_cq->nodnic_completion_queue); + + free(flexboot_nodnic_cq); +} + +static +struct ib_work_queue * flexboot_nodnic_find_wq ( struct ib_device *ibdev , + struct ib_completion_queue *cq, + unsigned long qpn, int is_send ) { + struct ib_work_queue *wq; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct nodnic_ring *ring; + mlx_uint32 out_qpn; + list_for_each_entry ( wq, &cq->work_queues, list ) { + flexboot_nodnic_qp = ib_qp_get_drvdata ( wq->qp ); + if( wq->is_send == is_send && wq->is_send == TRUE ) { + ring = &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring; + } else if( wq->is_send == is_send && wq->is_send == FALSE ) { + ring = &flexboot_nodnic_qp->nodnic_queue_pair->receive.nodnic_ring; + } else { + continue; + } + nodnic_port_get_qpn(&port->port_priv, ring, &out_qpn); + if ( out_qpn == qpn ) + return wq; + } + return NULL; +} + +/** + * Handle completion + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @v cqe Hardware completion queue entry + * @ret rc Return status code + */ +static int flexboot_nodnic_complete ( struct ib_device *ibdev, + struct ib_completion_queue *cq, + struct cqe_data *cqe_data ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq; + struct ib_queue_pair *qp; + struct io_buffer *iobuf; + struct ib_address_vector recv_dest; + struct ib_address_vector recv_source; + unsigned long qpn; + unsigned long wqe_idx; + unsigned long wqe_idx_mask; + size_t len; + int rc = 0; + + /* Parse completion */ + qpn = cqe_data->qpn; + + if ( cqe_data->is_error == TRUE ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx syndrome %x vendor %x\n", + flexboot_nodnic, cq->cqn, cqe_data->syndrome, + cqe_data->vendor_err_syndrome ); + rc = -EIO; + /* Don't return immediately; propagate error to completer */ + } + + /* Identify work queue */ + wq = flexboot_nodnic_find_wq( ibdev, cq, qpn, cqe_data->is_send ); + if ( wq == NULL ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p CQN %#lx unknown %s QPN %#lx\n", + flexboot_nodnic, cq->cqn, + ( cqe_data->is_send ? "send" : "recv" ), qpn ); + return -EIO; + } + qp = wq->qp; + + /* Identify work queue entry */ + wqe_idx = cqe_data->wqe_counter; + wqe_idx_mask = ( wq->num_wqes - 1 ); + DBGCP ( flexboot_nodnic, + "NODNIC %p CQN %#lx QPN %#lx %s WQE %#lx completed:\n", + flexboot_nodnic, cq->cqn, qp->qpn, + ( cqe_data->is_send ? "send" : "recv" ), + wqe_idx ); + + /* Identify I/O buffer */ + iobuf = wq->iobufs[wqe_idx & wqe_idx_mask]; + if ( iobuf == NULL ) { + DBGC ( flexboot_nodnic, + "NODNIC %p CQN %#lx QPN %#lx empty %s WQE %#lx\n", + flexboot_nodnic, cq->cqn, qp->qpn, + ( cqe_data->is_send ? "send" : "recv" ), wqe_idx ); + return -EIO; + } + wq->iobufs[wqe_idx & wqe_idx_mask] = NULL; + + if ( cqe_data->is_send == TRUE ) { + /* Hand off to completion handler */ + ib_complete_send ( ibdev, qp, iobuf, rc ); + } else if ( rc != 0 ) { + /* Propagate error to receive completion handler */ + ib_complete_recv ( ibdev, qp, NULL, NULL, iobuf, rc ); + } else { + /* Set received length */ + len = cqe_data->byte_cnt; + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); + memset ( &recv_dest, 0, sizeof ( recv_dest ) ); + recv_dest.qpn = qpn; + memset ( &recv_source, 0, sizeof ( recv_source ) ); + switch ( qp->type ) { + case IB_QPT_SMI: + case IB_QPT_GSI: + case IB_QPT_UD: + case IB_QPT_RC: + break; + case IB_QPT_ETH: + break; + default: + assert ( 0 ); + return -EINVAL; + } + /* Hand off to completion handler */ + ib_complete_recv ( ibdev, qp, &recv_dest, + &recv_source, iobuf, rc ); + } + + return rc; +} +/** + * Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queues + */ +static void flexboot_nodnic_poll_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq ); + void *cqe; + mlx_size cqe_size; + struct cqe_data cqe_data; + unsigned int cqe_idx_mask; + int rc; + + cqe_size = flexboot_nodnic->callbacks->get_cqe_size(); + while ( TRUE ) { + /* Look for completion entry */ + cqe_idx_mask = ( cq->num_cqes - 1 ); + cqe = ((uint8_t *)flexboot_nodnic_cq->nodnic_completion_queue->cq_virt) + + cqe_size * (cq->next_idx & cqe_idx_mask); + + /* TODO: check fill_completion */ + flexboot_nodnic->callbacks->fill_completion(cqe, &cqe_data); + if ( cqe_data.owner ^ + ( ( cq->next_idx & cq->num_cqes ) ? 1 : 0 ) ) { + /* Entry still owned by hardware; end of poll */ + break; + } + /* Handle completion */ + rc = flexboot_nodnic_complete ( ibdev, cq, &cqe_data ); + if ( rc != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx failed to complete: %s\n", + flexboot_nodnic, cq->cqn, strerror ( rc ) ); + DBGC_HDA ( flexboot_nodnic, virt_to_phys ( cqe ), + cqe, sizeof ( *cqe ) ); + } + + /* Update completion queue's index */ + cq->next_idx++; + } +} +/*************************************************************************** + * + * Queue pair operations + * + *************************************************************************** + */ + + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int flexboot_nodnic_create_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp; + mlx_status status = MLX_SUCCESS; + + flexboot_nodnic_qp = (struct flexboot_nodnic_queue_pair *)zalloc(sizeof(*flexboot_nodnic_qp)); + if ( flexboot_nodnic_qp == NULL ) { + status = MLX_OUT_OF_RESOURCES; + goto qp_alloc_err; + } + + status = nodnic_port_create_qp(&port->port_priv, qp->type, + qp->send.num_wqes * sizeof(struct nodnic_send_wqbb), + qp->send.num_wqes, + qp->recv.num_wqes * sizeof(struct nodnic_recv_wqe), + qp->recv.num_wqes, + &flexboot_nodnic_qp->nodnic_queue_pair); + MLX_FATAL_CHECK_STATUS(status, create_err, + "nodnic_port_create_qp failed"); + ib_qp_set_drvdata ( qp, flexboot_nodnic_qp ); + return status; +create_err: + free(flexboot_nodnic_qp); +qp_alloc_err: + return status; +} + +/** + * Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int flexboot_nodnic_modify_qp ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp __unused) { + /*not needed*/ + return 0; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void flexboot_nodnic_destroy_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); + + nodnic_port_destroy_qp(&port->port_priv, qp->type, + flexboot_nodnic_qp->nodnic_queue_pair); + + free(flexboot_nodnic_qp); +} + +/*************************************************************************** + * + * Work request operations + * + *************************************************************************** + */ + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int flexboot_nodnic_post_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf) { + + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct ib_work_queue *wq = &qp->send; + struct nodnic_send_wqbb *wqbb; + nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; + struct nodnic_send_ring *send_ring = &nodnic_qp->send; + mlx_status status = MLX_SUCCESS; + unsigned int wqe_idx_mask; + unsigned long wqe_idx; + + if ( ( port->port_priv.dma_state == FALSE ) || + ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic DMA disabled\n"); + status = -ENETDOWN; + goto post_send_done; + } + + /* Allocate work queue entry */ + wqe_idx = wq->next_idx; + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wqe_idx & wqe_idx_mask] ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx send queue full\n", + flexboot_nodnic, qp->qpn ); + status = -ENOBUFS; + goto post_send_done; + } + wqbb = &send_ring->wqe_virt[wqe_idx & wqe_idx_mask]; + wq->iobufs[wqe_idx & wqe_idx_mask] = iobuf; + + assert ( flexboot_nodnic->callbacks-> + fill_send_wqe[qp->type] != NULL ); + status = flexboot_nodnic->callbacks-> + fill_send_wqe[qp->type] ( ibdev, qp, av, iobuf, + wqbb, wqe_idx ); + if ( status != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx fill send wqe failed\n", + flexboot_nodnic, qp->qpn ); + goto post_send_done; + } + + wq->next_idx++; + + status = port->port_priv.send_doorbell ( &port->port_priv, + &send_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); + if ( status != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring send doorbell failed\n", flexboot_nodnic ); + } + +post_send_done: + return status; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int flexboot_nodnic_post_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct ib_work_queue *wq = &qp->recv; + nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; + struct nodnic_recv_ring *recv_ring = &nodnic_qp->receive; + struct nodnic_recv_wqe *wqe; + unsigned int wqe_idx_mask; + mlx_status status = MLX_SUCCESS; + + /* Allocate work queue entry */ + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p QPN %#lx receive queue full\n", + flexboot_nodnic, qp->qpn ); + status = -ENOBUFS; + goto post_recv_done; + } + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = &((struct nodnic_recv_wqe*)recv_ring->wqe_virt)[wq->next_idx & wqe_idx_mask]; + + MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( &wqe->data[0], 1, l_key, flexboot_nodnic->device_priv.lkey ); + MLX_FILL_H ( &wqe->data[0], 2, + local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( &wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); + + wq->next_idx++; + + status = port->port_priv.recv_doorbell ( &port->port_priv, + &recv_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); + if ( status != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring receive doorbell failed\n", flexboot_nodnic ); + } +post_recv_done: + return status; +} + +/*************************************************************************** + * + * Event queues + * + *************************************************************************** + */ + +static void flexboot_nodnic_poll_eq ( struct ib_device *ibdev ) { + struct flexboot_nodnic *flexboot_nodnic; + struct flexboot_nodnic_port *port; + struct net_device *netdev; + nodnic_port_state state = 0; + mlx_status status; + + if ( ! ibdev ) { + DBG ( "%s: ibdev = NULL!!!\n", __FUNCTION__ ); + return; + } + + flexboot_nodnic = ib_get_drvdata ( ibdev ); + port = &flexboot_nodnic->port[ibdev->port - 1]; + netdev = port->netdev; + + if ( ! netdev_is_open ( netdev ) ) { + DBG2( "%s: port %d is closed\n", __FUNCTION__, port->ibdev->port ); + return; + } + + /* we don't poll EQ. Just poll link status if it's not active */ + if ( ! netdev_link_ok ( netdev ) ) { + status = nodnic_port_get_state ( &port->port_priv, &state ); + MLX_FATAL_CHECK_STATUS(status, state_err, "nodnic_port_get_state failed"); + + if ( state == nodnic_port_state_active ) { + DBG( "%s: port %d physical link is up\n", __FUNCTION__, + port->ibdev->port ); + port->type->state_change ( flexboot_nodnic, port, 1 ); + } + } +state_err: + return; +} + +/*************************************************************************** + * + * Multicast group operations + * + *************************************************************************** + */ +static int flexboot_nodnic_mcast_attach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + mlx_mac_address mac; + mlx_status status = MLX_SUCCESS; + + switch (qp->type) { + case IB_QPT_ETH: + memcpy(&mac, &gid, sizeof(mac)); + status = nodnic_port_add_mac_filter(&port->port_priv, mac); + MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err, + "nodnic_port_add_mac_filter failed"); + break; + default: + break; + } +mac_err: + return status; +} +static void flexboot_nodnic_mcast_detach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + mlx_mac_address mac; + mlx_status status = MLX_SUCCESS; + + switch (qp->type) { + case IB_QPT_ETH: + memcpy(&mac, &gid, sizeof(mac)); + status = nodnic_port_remove_mac_filter(&port->port_priv, mac); + MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err, + "nodnic_port_remove_mac_filter failed"); + break; + default: + break; + } +mac_err: + return; +} +/*************************************************************************** + * + * Infiniband link-layer operations + * + *************************************************************************** + */ + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int flexboot_nodnic_ib_open ( struct ib_device *ibdev __unused) { + int rc = 0; + + /*TODO: add implementation*/ + return rc; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void flexboot_nodnic_ib_close ( struct ib_device *ibdev __unused) { + /*TODO: add implementation*/ +} + +/** + * Inform embedded subnet management agent of a received MAD + * + * @v ibdev Infiniband device + * @v mad MAD + * @ret rc Return status code + */ +static int flexboot_nodnic_inform_sma ( struct ib_device *ibdev __unused, + union ib_mad *mad __unused) { + /*TODO: add implementation*/ + return 0; +} + +/** flexboot_nodnic Infiniband operations */ +static struct ib_device_operations flexboot_nodnic_ib_operations = { + .create_cq = flexboot_nodnic_create_cq, + .destroy_cq = flexboot_nodnic_destroy_cq, + .create_qp = flexboot_nodnic_create_qp, + .modify_qp = flexboot_nodnic_modify_qp, + .destroy_qp = flexboot_nodnic_destroy_qp, + .post_send = flexboot_nodnic_post_send, + .post_recv = flexboot_nodnic_post_recv, + .poll_cq = flexboot_nodnic_poll_cq, + .poll_eq = flexboot_nodnic_poll_eq, + .open = flexboot_nodnic_ib_open, + .close = flexboot_nodnic_ib_close, + .mcast_attach = flexboot_nodnic_mcast_attach, + .mcast_detach = flexboot_nodnic_mcast_detach, + .set_port_info = flexboot_nodnic_inform_sma, + .set_pkey_table = flexboot_nodnic_inform_sma, +}; +/*************************************************************************** + * + * + * + *************************************************************************** + */ + +#define FLEX_NODNIC_TX_POLL_TOUT 500000 +#define FLEX_NODNIC_TX_POLL_USLEEP 10 + +static void flexboot_nodnic_complete_all_tx ( struct flexboot_nodnic_port *port ) { + struct ib_device *ibdev = port->ibdev; + struct ib_completion_queue *cq; + struct ib_work_queue *wq; + int keep_polling = 0; + int timeout = FLEX_NODNIC_TX_POLL_TOUT; + + list_for_each_entry ( cq, &ibdev->cqs, list ) { + do { + ib_poll_cq ( ibdev, cq ); + keep_polling = 0; + list_for_each_entry ( wq, &cq->work_queues, list ) { + if ( wq->is_send ) + keep_polling += ( wq->fill > 0 ); + } + udelay ( FLEX_NODNIC_TX_POLL_USLEEP ); + } while ( keep_polling && ( timeout-- > 0 ) ); + } +} + +static void flexboot_nodnic_port_disable_dma ( struct flexboot_nodnic_port *port ) { + nodnic_port_priv *port_priv = & ( port->port_priv ); + mlx_status status; + + if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) ) + return; + + port_priv->port_state |= NODNIC_PORT_DISABLING_DMA; + flexboot_nodnic_complete_all_tx ( port ); + if ( ( status = nodnic_port_disable_dma ( port_priv ) ) ) { + MLX_DEBUG_WARN ( port, "Failed to disable DMA %d\n", status ); + } + + port_priv->port_state &= ~NODNIC_PORT_DISABLING_DMA; +} + +/*************************************************************************** + * + * Ethernet operation + * + *************************************************************************** + */ + +/** Number of flexboot_nodnic Ethernet send work queue entries */ +#define FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES 64 + +/** Number of flexboot_nodnic Ethernet receive work queue entries */ +#define FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES 64 +/** flexboot nodnic Ethernet queue pair operations */ +static struct ib_queue_pair_operations flexboot_nodnic_eth_qp_op = { + .alloc_iob = alloc_iob, +}; + +/** + * Transmit packet via flexboot_nodnic Ethernet device + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int flexboot_nodnic_eth_transmit ( struct net_device *netdev, + struct io_buffer *iobuf) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + int rc; + + rc = ib_post_send ( ibdev, port->eth_qp, NULL, iobuf); + /* Transmit packet */ + if ( rc != 0) { + DBGC ( flexboot_nodnic, "NODNIC %p port %d could not transmit: %s\n", + flexboot_nodnic, ibdev->port, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle flexboot_nodnic Ethernet device send completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void flexboot_nodnic_eth_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, + int rc) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** + * Handle flexboot_nodnic Ethernet device receive completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void flexboot_nodnic_eth_complete_recv ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct ib_address_vector *dest __unused, + struct ib_address_vector *source, + struct io_buffer *iobuf, + int rc) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + + if ( rc != 0 ) { + DBG ( "Received packet with error\n" ); + netdev_rx_err ( netdev, iobuf, rc ); + return; + } + + if ( source == NULL ) { + DBG ( "Received packet without address vector\n" ); + netdev_rx_err ( netdev, iobuf, -ENOTTY ); + return; + } + netdev_rx ( netdev, iobuf ); +} + +/** flexboot_nodnic Ethernet device completion operations */ +static struct ib_completion_queue_operations flexboot_nodnic_eth_cq_op = { + .complete_send = flexboot_nodnic_eth_complete_send, + .complete_recv = flexboot_nodnic_eth_complete_recv, +}; + +/** + * Poll flexboot_nodnic Ethernet device + * + * @v netdev Network device + */ +static void flexboot_nodnic_eth_poll ( struct net_device *netdev) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + + ib_poll_eq ( ibdev ); +} + +/** + * Open flexboot_nodnic Ethernet device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + mlx_status status = MLX_SUCCESS; + struct ib_completion_queue *dummy_cq = NULL; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = NULL; + mlx_uint64 cq_size = 0; + mlx_uint32 qpn = 0; + nodnic_port_state state = nodnic_port_state_down; + + if ( port->port_priv.port_state & NODNIC_PORT_OPENED ) { + DBGC ( flexboot_nodnic, "%s: port %d is already opened\n", + __FUNCTION__, port->ibdev->port ); + return 0; + } + + port->port_priv.port_state |= NODNIC_PORT_OPENED; + + dummy_cq = zalloc ( sizeof ( struct ib_completion_queue ) ); + if ( dummy_cq == NULL ) { + DBGC ( flexboot_nodnic, "%s: Failed to allocate dummy CQ\n", __FUNCTION__ ); + status = MLX_OUT_OF_RESOURCES; + goto err_create_dummy_cq; + } + INIT_LIST_HEAD ( &dummy_cq->work_queues ); + + port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH, + FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq, + FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq, + &flexboot_nodnic_eth_qp_op, netdev->name ); + if ( !port->eth_qp ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create queue pair\n", + flexboot_nodnic, ibdev->port ); + status = MLX_OUT_OF_RESOURCES; + goto err_create_qp; + } + + ib_qp_set_ownerdata ( port->eth_qp, netdev ); + + status = nodnic_port_get_cq_size(&port->port_priv, &cq_size); + MLX_FATAL_CHECK_STATUS(status, get_cq_size_err, + "nodnic_port_get_cq_size failed"); + + port->eth_cq = ib_create_cq ( ibdev, cq_size, + &flexboot_nodnic_eth_cq_op ); + if ( !port->eth_cq ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p port %d could not create completion queue\n", + flexboot_nodnic, ibdev->port ); + status = MLX_OUT_OF_RESOURCES; + goto err_create_cq; + } + port->eth_qp->send.cq = port->eth_cq; + list_del(&port->eth_qp->send.list); + list_add ( &port->eth_qp->send.list, &port->eth_cq->work_queues ); + port->eth_qp->recv.cq = port->eth_cq; + list_del(&port->eth_qp->recv.list); + list_add ( &port->eth_qp->recv.list, &port->eth_cq->work_queues ); + + status = nodnic_port_allocate_eq(&port->port_priv, + flexboot_nodnic->device_priv.device_cap.log_working_buffer_size); + MLX_FATAL_CHECK_STATUS(status, eq_alloc_err, + "nodnic_port_allocate_eq failed"); + + status = nodnic_port_init(&port->port_priv); + MLX_FATAL_CHECK_STATUS(status, init_err, + "nodnic_port_init failed"); + + /* update qp - qpn */ + flexboot_nodnic_qp = ib_qp_get_drvdata ( port->eth_qp ); + status = nodnic_port_get_qpn(&port->port_priv, + &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring, + &qpn); + MLX_FATAL_CHECK_STATUS(status, qpn_err, + "nodnic_port_get_qpn failed"); + port->eth_qp->qpn = qpn; + + /* Fill receive rings */ + ib_refill_recv ( ibdev, port->eth_qp ); + + status = nodnic_port_enable_dma(&port->port_priv); + MLX_FATAL_CHECK_STATUS(status, dma_err, + "nodnic_port_enable_dma failed"); + + if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) { + status = nodnic_port_set_promisc(&port->port_priv, TRUE); + MLX_FATAL_CHECK_STATUS(status, promisc_err, + "nodnic_port_set_promisc failed"); + } + + status = nodnic_port_get_state(&port->port_priv, &state); + MLX_FATAL_CHECK_STATUS(status, state_err, + "nodnic_port_get_state failed"); + + port->type->state_change ( + flexboot_nodnic, port, state == nodnic_port_state_active ); + + DBGC ( flexboot_nodnic, "%s: port %d opened (link is %s)\n", + __FUNCTION__, port->ibdev->port, + ( ( state == nodnic_port_state_active ) ? "Up" : "Down" ) ); + + free(dummy_cq); + return 0; +state_err: +promisc_err: +dma_err: +qpn_err: + nodnic_port_close(&port->port_priv); +init_err: + nodnic_port_free_eq(&port->port_priv); +eq_alloc_err: +err_create_cq: +get_cq_size_err: + ib_destroy_qp(ibdev, port->eth_qp ); +err_create_qp: + free(dummy_cq); +err_create_dummy_cq: + port->port_priv.port_state &= ~NODNIC_PORT_OPENED; + return status; +} + +/** + * Close flexboot_nodnic Ethernet device + * + * @v netdev Network device + */ +static void flexboot_nodnic_eth_close ( struct net_device *netdev) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + mlx_status status = MLX_SUCCESS; + + if ( ! ( port->port_priv.port_state & NODNIC_PORT_OPENED ) ) { + DBGC ( flexboot_nodnic, "%s: port %d is already closed\n", + __FUNCTION__, port->ibdev->port ); + return; + } + + if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) { + if ( ( status = nodnic_port_set_promisc( &port->port_priv, FALSE ) ) ) { + DBGC ( flexboot_nodnic, + "nodnic_port_set_promisc failed (status = %d)\n", status ); + } + } + + flexboot_nodnic_port_disable_dma ( port ); + + port->port_priv.port_state &= ~NODNIC_PORT_OPENED; + + port->type->state_change ( flexboot_nodnic, port, FALSE ); + + /* Close port */ + status = nodnic_port_close(&port->port_priv); + if ( status != MLX_SUCCESS ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not close port: %s\n", + flexboot_nodnic, ibdev->port, strerror ( status ) ); + /* Nothing we can do about this */ + } + + ib_destroy_qp ( ibdev, port->eth_qp ); + port->eth_qp = NULL; + ib_destroy_cq ( ibdev, port->eth_cq ); + port->eth_cq = NULL; + + nodnic_port_free_eq(&port->port_priv); + + DBGC ( flexboot_nodnic, "%s: port %d closed\n", __FUNCTION__, port->ibdev->port ); +} + +void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ) { + struct flexboot_nodnic_port *port = netdev->priv; + + if ( enable ) { + if ( ( port->port_priv.port_state & NODNIC_PORT_OPENED ) && + ! ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) { + flexboot_nodnic_arm_cq ( port ); + } else { + /* do nothing */ + } + } else { + nodnic_device_clear_int( port->port_priv.device ); + } +} + +/** flexboot_nodnic Ethernet network device operations */ +static struct net_device_operations flexboot_nodnic_eth_operations = { + .open = flexboot_nodnic_eth_open, + .close = flexboot_nodnic_eth_close, + .transmit = flexboot_nodnic_eth_transmit, + .poll = flexboot_nodnic_eth_poll, +}; + +/** + * Register flexboot_nodnic Ethernet device + */ +static int flexboot_nodnic_register_netdev ( struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port) { + mlx_status status = MLX_SUCCESS; + struct net_device *netdev; + struct ib_device *ibdev = port->ibdev; + union { + uint8_t bytes[8]; + uint32_t dwords[2]; + } mac; + + /* Allocate network devices */ + netdev = alloc_etherdev ( 0 ); + if ( netdev == NULL ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not allocate net device\n", + flexboot_nodnic, ibdev->port ); + status = MLX_OUT_OF_RESOURCES; + goto alloc_err; + } + port->netdev = netdev; + netdev_init ( netdev, &flexboot_nodnic_eth_operations ); + netdev->dev = ibdev->dev; + netdev->priv = port; + + status = nodnic_port_query(&port->port_priv, + nodnic_port_option_mac_high, + &mac.dwords[0]); + MLX_FATAL_CHECK_STATUS(status, mac_err, + "failed to query mac high"); + status = nodnic_port_query(&port->port_priv, + nodnic_port_option_mac_low, + &mac.dwords[1]); + MLX_FATAL_CHECK_STATUS(status, mac_err, + "failed to query mac low"); + mac.dwords[0] = htonl(mac.dwords[0]); + mac.dwords[1] = htonl(mac.dwords[1]); + memcpy ( netdev->hw_addr, + &mac.bytes[2], ETH_ALEN); + /* Register network device */ + status = register_netdev ( netdev ); + if ( status != MLX_SUCCESS ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p port %d could not register network device: %s\n", + flexboot_nodnic, ibdev->port, strerror ( status ) ); + goto reg_err; + } + return status; +reg_err: +mac_err: + netdev_put ( netdev ); +alloc_err: + return status; +} + +/** + * Handle flexboot_nodnic Ethernet device port state change + */ +static void flexboot_nodnic_state_change_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused, + struct flexboot_nodnic_port *port, + int link_up ) { + struct net_device *netdev = port->netdev; + + if ( link_up ) + netdev_link_up ( netdev ); + else + netdev_link_down ( netdev ); + +} + +/** + * Unregister flexboot_nodnic Ethernet device + */ +static void flexboot_nodnic_unregister_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused, + struct flexboot_nodnic_port *port ) { + struct net_device *netdev = port->netdev; + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** flexboot_nodnic Ethernet port type */ +static struct flexboot_nodnic_port_type flexboot_nodnic_port_type_eth = { + .register_dev = flexboot_nodnic_register_netdev, + .state_change = flexboot_nodnic_state_change_netdev, + .unregister_dev = flexboot_nodnic_unregister_netdev, +}; + +/*************************************************************************** + * + * PCI interface helper functions + * + *************************************************************************** + */ +static +mlx_status +flexboot_nodnic_allocate_infiniband_devices( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + struct pci_device *pci = flexboot_nodnic_priv->pci; + struct ib_device *ibdev = NULL; + unsigned int i = 0; + + /* Allocate Infiniband devices */ + for (; i < device_priv->device_cap.num_ports; i++) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + ibdev = alloc_ibdev(0); + if (ibdev == NULL) { + status = MLX_OUT_OF_RESOURCES; + goto err_alloc_ibdev; + } + flexboot_nodnic_priv->port[i].ibdev = ibdev; + ibdev->op = &flexboot_nodnic_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = ( FLEXBOOT_NODNIC_PORT_BASE + i); + ib_set_drvdata(ibdev, flexboot_nodnic_priv); + } + return status; +err_alloc_ibdev: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) + ibdev_put ( flexboot_nodnic_priv->port[i].ibdev ); + return status; +} + +static +mlx_status +flexboot_nodnic_thin_init_ports( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + nodnic_port_priv *port_priv = NULL; + unsigned int i = 0; + + for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port_priv = &flexboot_nodnic_priv->port[i].port_priv; + status = nodnic_port_thin_init( device_priv, port_priv, i ); + MLX_FATAL_CHECK_STATUS(status, thin_init_err, + "flexboot_nodnic_thin_init_ports failed"); + } +thin_init_err: + return status; +} + + +static +mlx_status +flexboot_nodnic_set_ports_type ( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + nodnic_port_priv *port_priv = NULL; + nodnic_port_type type = NODNIC_PORT_TYPE_UNKNOWN; + unsigned int i = 0; + + for ( i = 0 ; i < device_priv->device_cap.num_ports ; i++ ) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port_priv = &flexboot_nodnic_priv->port[i].port_priv; + status = nodnic_port_get_type(port_priv, &type); + MLX_FATAL_CHECK_STATUS(status, type_err, + "nodnic_port_get_type failed"); + switch ( type ) { + case NODNIC_PORT_TYPE_ETH: + DBGC ( flexboot_nodnic_priv, "Port %d type is Ethernet\n", i ); + flexboot_nodnic_priv->port[i].type = &flexboot_nodnic_port_type_eth; + break; + case NODNIC_PORT_TYPE_IB: + DBGC ( flexboot_nodnic_priv, "Port %d type is Infiniband\n", i ); + status = MLX_UNSUPPORTED; + goto type_err; + default: + DBGC ( flexboot_nodnic_priv, "Port %d type is unknown\n", i ); + status = MLX_UNSUPPORTED; + goto type_err; + } + } +type_err: + return status; +} + +static +mlx_status +flexboot_nodnic_ports_register_dev( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + struct flexboot_nodnic_port *port = NULL; + unsigned int i = 0; + + for (; i < device_priv->device_cap.num_ports; i++) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port = &flexboot_nodnic_priv->port[i]; + status = port->type->register_dev ( flexboot_nodnic_priv, port ); + MLX_FATAL_CHECK_STATUS(status, reg_err, + "port register_dev failed"); + } +reg_err: + return status; +} + +static +mlx_status +flexboot_nodnic_ports_unregister_dev ( struct flexboot_nodnic *flexboot_nodnic_priv ) { + struct flexboot_nodnic_port *port; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + int i = (device_priv->device_cap.num_ports - 1); + + for (; i >= 0; i--) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port = &flexboot_nodnic_priv->port[i]; + port->type->unregister_dev(flexboot_nodnic_priv, port); + ibdev_put(flexboot_nodnic_priv->port[i].ibdev); + } + return MLX_SUCCESS; +} + +/*************************************************************************** + * + * flexboot nodnic interface + * + *************************************************************************** + */ +__unused static void flexboot_nodnic_enable_dma ( struct flexboot_nodnic *nodnic ) { + nodnic_port_priv *port_priv; + mlx_status status; + int i; + + for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) { + if ( ! ( nodnic->port_mask & ( i + 1 ) ) ) + continue; + port_priv = & ( nodnic->port[i].port_priv ); + if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) ) + continue; + + if ( ( status = nodnic_port_enable_dma ( port_priv ) ) ) { + MLX_DEBUG_WARN ( nodnic, "Failed to enable DMA %d\n", status ); + } + } +} + +__unused static void flexboot_nodnic_disable_dma ( struct flexboot_nodnic *nodnic ) { + int i; + + for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) { + if ( ! ( nodnic->port_mask & ( i + 1 ) ) ) + continue; + flexboot_nodnic_port_disable_dma ( & ( nodnic->port[i] ) ); + } +} + +int flexboot_nodnic_is_supported ( struct pci_device *pci ) { + mlx_utils utils; + mlx_pci_gw_buffer buffer; + mlx_status status; + int is_supported = 0; + + DBG ( "%s: start\n", __FUNCTION__ ); + + memset ( &utils, 0, sizeof ( utils ) ); + + status = mlx_utils_init ( &utils, pci ); + MLX_CHECK_STATUS ( pci, status, utils_init_err, "mlx_utils_init failed" ); + + status = mlx_pci_gw_init ( &utils ); + MLX_CHECK_STATUS ( pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" ); + + status = mlx_pci_gw_read ( &utils, PCI_GW_SPACE_NODNIC, + NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, &buffer ); + + if ( status == MLX_SUCCESS ) { + buffer >>= NODNIC_NIC_INTERFACE_SUPPORTED_BIT; + is_supported = ( buffer & 0x1 ); + } + + mlx_pci_gw_teardown( &utils ); + +pci_gw_init_err: +utils_init_err: + DBG ( "%s: NODNIC is %s supported (status = %d)\n", + __FUNCTION__, ( is_supported ? "": "not" ), status ); + return is_supported; +} + +void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, + uint16_t high_byte ) { + union mac_addr { + struct { + uint32_t low_byte; + uint16_t high_byte; + }; + uint8_t mac_addr[ETH_ALEN]; + } mac_addr_aux; + + mac_addr_aux.high_byte = high_byte; + mac_addr_aux.low_byte = low_byte; + + mac_addr[0] = mac_addr_aux.mac_addr[5]; + mac_addr[1] = mac_addr_aux.mac_addr[4]; + mac_addr[2] = mac_addr_aux.mac_addr[3]; + mac_addr[3] = mac_addr_aux.mac_addr[2]; + mac_addr[4] = mac_addr_aux.mac_addr[1]; + mac_addr[5] = mac_addr_aux.mac_addr[0]; +} + +static mlx_status flexboot_nodnic_get_factory_mac ( + struct flexboot_nodnic *flexboot_nodnic_priv, uint8_t port __unused ) { + struct mlx_vmac_query_virt_mac virt_mac; + mlx_status status; + + memset ( & virt_mac, 0, sizeof ( virt_mac ) ); + status = mlx_vmac_query_virt_mac ( flexboot_nodnic_priv->device_priv.utils, + &virt_mac ); + if ( ! status ) { + DBGC ( flexboot_nodnic_priv, "NODNIC %p Failed to set the virtual MAC\n", + flexboot_nodnic_priv ); + } + + return status; +} + +/** + * Set port masking + * + * @v flexboot_nodnic nodnic device + * @ret rc Return status code + */ +static int flexboot_nodnic_set_port_masking ( struct flexboot_nodnic *flexboot_nodnic ) { + unsigned int i; + nodnic_device_priv *device_priv = &flexboot_nodnic->device_priv; + + flexboot_nodnic->port_mask = 0; + for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { + flexboot_nodnic->port_mask |= (i + 1); + } + + if ( ! flexboot_nodnic->port_mask ) { + /* No port was enabled */ + DBGC ( flexboot_nodnic, "NODNIC %p No port was enabled for " + "booting\n", flexboot_nodnic ); + return -ENETUNREACH; + } + + return 0; +} + +int flexboot_nodnic_probe ( struct pci_device *pci, + struct flexboot_nodnic_callbacks *callbacks, + void *drv_priv __unused ) { + mlx_status status = MLX_SUCCESS; + struct flexboot_nodnic *flexboot_nodnic_priv = NULL; + nodnic_device_priv *device_priv = NULL; + int i = 0; + + if ( ( pci == NULL ) || ( callbacks == NULL ) ) { + DBGC ( flexboot_nodnic_priv, "%s: Bad Parameter\n", __FUNCTION__ ); + return -EINVAL; + } + + flexboot_nodnic_priv = zalloc( sizeof ( *flexboot_nodnic_priv ) ); + if ( flexboot_nodnic_priv == NULL ) { + DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate priv data\n", __FUNCTION__ ); + status = MLX_OUT_OF_RESOURCES; + goto device_err_alloc; + } + + /* Register settings + * Note that pci->priv will be the device private data */ + flexboot_nodnic_priv->pci = pci; + flexboot_nodnic_priv->callbacks = callbacks; + pci_set_drvdata ( pci, flexboot_nodnic_priv ); + + device_priv = &flexboot_nodnic_priv->device_priv; + device_priv->utils = (mlx_utils *)zalloc( sizeof ( mlx_utils ) ); + if ( device_priv->utils == NULL ) { + DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate utils\n", __FUNCTION__ ); + status = MLX_OUT_OF_RESOURCES; + goto utils_err_alloc; + } + + status = mlx_utils_init( device_priv->utils, pci ); + MLX_FATAL_CHECK_STATUS(status, utils_init_err, + "mlx_utils_init failed"); + + /* nodnic init*/ + status = mlx_pci_gw_init( device_priv->utils ); + MLX_FATAL_CHECK_STATUS(status, cmd_init_err, + "mlx_pci_gw_init failed"); + + /* init device */ + status = nodnic_device_init( device_priv ); + MLX_FATAL_CHECK_STATUS(status, device_init_err, + "nodnic_device_init failed"); + + status = nodnic_device_get_cap( device_priv ); + MLX_FATAL_CHECK_STATUS(status, get_cap_err, + "nodnic_device_get_cap failed"); + + status = flexboot_nodnic_set_port_masking ( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, err_set_masking, + "flexboot_nodnic_set_port_masking failed"); + + status = flexboot_nodnic_allocate_infiniband_devices( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, err_alloc_ibdev, + "flexboot_nodnic_allocate_infiniband_devices failed"); + + /* port init */ + status = flexboot_nodnic_thin_init_ports( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, err_thin_init_ports, + "flexboot_nodnic_thin_init_ports failed"); + + /* device reg */ + status = flexboot_nodnic_set_ports_type( flexboot_nodnic_priv ); + MLX_CHECK_STATUS( flexboot_nodnic_priv, status, err_set_ports_types, + "flexboot_nodnic_set_ports_type failed"); + + status = flexboot_nodnic_ports_register_dev( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, reg_err, + "flexboot_nodnic_ports_register_dev failed"); + + for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + flexboot_nodnic_get_factory_mac ( flexboot_nodnic_priv, i ); + } + + /* Update ETH operations with IRQ function if supported */ + DBGC ( flexboot_nodnic_priv, "%s: %s IRQ function\n", + __FUNCTION__, ( callbacks->irq ? "Valid" : "No" ) ); + flexboot_nodnic_eth_operations.irq = callbacks->irq; + return 0; + + flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); +reg_err: +err_set_ports_types: +err_thin_init_ports: +err_alloc_ibdev: +err_set_masking: +get_cap_err: + nodnic_device_teardown ( device_priv ); +device_init_err: + mlx_pci_gw_teardown ( device_priv->utils ); +cmd_init_err: +utils_init_err: + free ( device_priv->utils ); +utils_err_alloc: + free ( flexboot_nodnic_priv ); +device_err_alloc: + return status; +} + +void flexboot_nodnic_remove ( struct pci_device *pci ) +{ + struct flexboot_nodnic *flexboot_nodnic_priv = pci_get_drvdata ( pci ); + nodnic_device_priv *device_priv = & ( flexboot_nodnic_priv->device_priv ); + + flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); + nodnic_device_teardown( device_priv ); + mlx_pci_gw_teardown( device_priv->utils ); + free( device_priv->utils ); + free( flexboot_nodnic_priv ); +} diff --git a/src/drivers/infiniband/flexboot_nodnic.h b/src/drivers/infiniband/flexboot_nodnic.h new file mode 100644 index 000000000..80272296c --- /dev/null +++ b/src/drivers/infiniband/flexboot_nodnic.h @@ -0,0 +1,163 @@ +#ifndef SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_H_ +#define SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_nodnic/include/mlx_nodnic_data_structures.h" +#include "nodnic_prm.h" +#include +#include +#include + +/* + * If defined, use interrupts in NODNIC driver + */ +#define NODNIC_IRQ_ENABLED + +#define FLEXBOOT_NODNIC_MAX_PORTS 2 +#define FLEXBOOT_NODNIC_PORT_BASE 1 + +#define FLEXBOOT_NODNIC_OPCODE_SEND 0xa + +/* Port protocol */ +enum flexboot_nodnic_protocol { + FLEXBOOT_NODNIC_PROT_IB_IPV6 = 0, + FLEXBOOT_NODNIC_PROT_ETH, + FLEXBOOT_NODNIC_PROT_IB_IPV4, + FLEXBOOT_NODNIC_PROT_FCOE +}; + +/** A flexboot nodnic port */ +struct flexboot_nodnic_port { + /** Infiniband device */ + struct ib_device *ibdev; + /** Network device */ + struct net_device *netdev; + /** nodic port */ + nodnic_port_priv port_priv; + /** Port type */ + struct flexboot_nodnic_port_type *type; + /** Ethernet completion queue */ + struct ib_completion_queue *eth_cq; + /** Ethernet queue pair */ + struct ib_queue_pair *eth_qp; +}; + + +/** A flexboot nodnic queue pair */ +struct flexboot_nodnic_queue_pair { + nodnic_qp *nodnic_queue_pair; +}; + +/** A flexboot nodnic cq */ +struct flexboot_nodnic_completion_queue { + nodnic_cq *nodnic_completion_queue; +}; + +/** A flexboot_nodnic device */ +struct flexboot_nodnic { + /** PCI device */ + struct pci_device *pci; + /** nic specific data*/ + struct flexboot_nodnic_callbacks *callbacks; + /**nodnic device*/ + nodnic_device_priv device_priv; + /**flexboot_nodnic ports*/ + struct flexboot_nodnic_port port[FLEXBOOT_NODNIC_MAX_PORTS]; + /** Device open request counter */ + unsigned int open_count; + /** Port masking */ + u16 port_mask; + /** device private data */ + void *priv_data; +}; + +/** A flexboot_nodnic port type */ +struct flexboot_nodnic_port_type { + /** Register port + * + * @v flexboot_nodnic flexboot_nodnic device + * @v port flexboot_nodnic port + * @ret mlx_status Return status code + */ + mlx_status ( * register_dev ) ( + struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port + ); + /** Port state changed + * + * @v flexboot_nodnic flexboot_nodnic device + * @v port flexboot_nodnic port + * @v link_up Link is up + */ + void ( * state_change ) ( + struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port, + int link_up + ); + /** Unregister port + * + * @v flexboot_nodnic flexboot_nodnic device + * @v port flexboot_nodnic port + */ + void ( * unregister_dev ) ( + struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port + ); +}; + +struct cqe_data{ + mlx_boolean owner; + mlx_uint32 qpn; + mlx_uint32 is_send; + mlx_uint32 is_error; + mlx_uint32 syndrome; + mlx_uint32 vendor_err_syndrome; + mlx_uint32 wqe_counter; + mlx_uint32 byte_cnt; +}; + +struct flexboot_nodnic_callbacks { + mlx_status ( * fill_completion ) ( void *cqe, struct cqe_data *cqe_data ); + mlx_status ( * cqe_set_owner ) ( void *cq, unsigned int num_cqes ); + mlx_size ( * get_cqe_size ) (); + mlx_status ( * fill_send_wqe[5] ) ( + struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, + struct nodnic_send_wqbb *wqbb, + unsigned long wqe_idx + ); + void ( * irq ) ( struct net_device *netdev, int enable ); +}; + +int flexboot_nodnic_probe ( struct pci_device *pci, + struct flexboot_nodnic_callbacks *callbacks, + void *drv_priv ); +void flexboot_nodnic_remove ( struct pci_device *pci ); +void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ); +int flexboot_nodnic_is_supported ( struct pci_device *pci ); +void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, + uint16_t high_byte ); + +#endif /* SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_H_ */ diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c new file mode 100755 index 000000000..9225c187f --- /dev/null +++ b/src/drivers/infiniband/golan.c @@ -0,0 +1,2663 @@ +/* + * Copyright (C) 2013-2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flexboot_nodnic.h" +#include "nodnic_shomron_prm.h" +#include "golan.h" +#include "mlx_utils/include/public/mlx_bail.h" +#include "mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h" +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "mlx_utils/include/public/mlx_pci_gw.h" +#include "mlx_nodnic/include/mlx_port.h" + +/******************************************************************************/ +/************* Very simple memory management for umalloced pages **************/ +/******* Temporary solution until full memory management is implemented *******/ +/******************************************************************************/ +#define GOLAN_PAGES 20 +struct golan_page { + struct list_head list; + userptr_t addr; +}; + +static void golan_free_pages ( struct list_head *head ) { + struct golan_page *page, *tmp; + list_for_each_entry_safe ( page, tmp, head, list ) { + list_del ( &page->list ); + ufree ( page->addr ); + free ( page ); + } +} + +static int golan_init_pages ( struct list_head *head ) { + struct golan_page *new_entry; + int rc, i; + + if ( !head ) { + rc = -EINVAL; + goto err_golan_init_pages_bad_param; + } + + INIT_LIST_HEAD ( head ); + + for ( i = 0; i < GOLAN_PAGES; i++ ) { + new_entry = zalloc ( sizeof ( *new_entry ) ); + if ( new_entry == NULL ) { + rc = -ENOMEM; + goto err_golan_init_pages_alloc_page; + } + new_entry->addr = umalloc ( GOLAN_PAGE_SIZE ); + if ( new_entry->addr == UNULL ) { + free ( new_entry ); + rc = -ENOMEM; + goto err_golan_init_pages_alloc_page; + } + list_add ( &new_entry->list, head ); + } + + return 0; + +err_golan_init_pages_alloc_page: + golan_free_pages ( head ); +err_golan_init_pages_bad_param: + return rc; +} + +static userptr_t golan_get_page ( struct list_head *head ) { + struct golan_page *page; + userptr_t addr; + + if ( list_empty ( head ) ) + return UNULL; + + page = list_first_entry ( head, struct golan_page, list ); + list_del ( &page->list ); + addr = page->addr; + free ( page ); + return addr; +} + +/******************************************************************************/ + +const char *golan_qp_state_as_string[] = { + "RESET", + "INIT", + "RTR", + "RTS", + "SQD", + "SQE", + "ERR" +}; + +inline int golan_check_rc_and_cmd_status ( struct golan_cmd_layout *cmd, int rc ) { + struct golan_outbox_hdr *out_hdr = ( struct golan_outbox_hdr * ) ( cmd->out ); + if ( rc == -EBUSY ) { + DBG ( "HCA is busy (rc = -EBUSY)\n" ); + return rc; + } else if ( out_hdr->status ) { + DBG("%s status = 0x%x - syndrom = 0x%x\n", __FUNCTION__, + out_hdr->status, be32_to_cpu(out_hdr->syndrome)); + return out_hdr->status; + } + return 0; +} + +#define GOLAN_CHECK_RC_AND_CMD_STATUS(_lable) \ + do { \ + if ( ( rc = golan_check_rc_and_cmd_status ( cmd, rc ) ) ) \ + goto _lable; \ + } while (0) + +#define GOLAN_PRINT_RC_AND_CMD_STATUS golan_check_rc_and_cmd_status ( cmd, rc ) + + +struct mbox { + union { + struct golan_cmd_prot_block mblock; + u8 data[MAILBOX_STRIDE]; + __be64 qdata[MAILBOX_STRIDE >> 3]; + }; +}; + +static inline uint32_t ilog2(uint32_t mem) +{ + return ( fls ( mem ) - 1 ); +} + +#define CTRL_SIG_SZ (sizeof(mailbox->mblock) - sizeof(mailbox->mblock.bdata) - 2) + +static inline u8 xor8_buf(void *buf, int len) +{ + u8 sum = 0; + int i; + u8 *ptr = buf; + + for (i = 0; i < len; ++i) + sum ^= ptr[i]; + + return sum; +} + +static inline int verify_block_sig(struct golan_cmd_prot_block *block) +{ + if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff) + return -EINVAL; + + if (xor8_buf(block, sizeof(*block)) != 0xff) + return -EINVAL; + return 0; +} + +static inline const char *cmd_status_str(u8 status) +{ + switch (status) { + case 0x0: return "OK"; + case 0x1: return "internal error"; + case 0x2: return "bad operation"; + case 0x3: return "bad parameter"; + case 0x4: return "bad system state"; + case 0x5: return "bad resource"; + case 0x6: return "resource busy"; + case 0x8: return "limits exceeded"; + case 0x9: return "bad resource state"; + case 0xa: return "bad index"; + case 0xf: return "no resources"; + case 0x50: return "bad input length"; + case 0x51: return "bad output length"; + case 0x10: return "bad QP state"; + case 0x30: return "bad packet (discarded)"; + case 0x40: return "bad size too many outstanding CQEs"; + case 0xff: return "Command Timed Out"; + default: return "unknown status"; + } +} + +static inline uint16_t fw_rev_maj(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->fw_rev)) & 0xffff; +} + +static inline u16 fw_rev_min(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->fw_rev)) >> 16; +} + +static inline u16 fw_rev_sub(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->cmdif_rev_fw_sub)) & 0xffff; +} + +static inline u16 cmdif_rev(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->cmdif_rev_fw_sub)) >> 16; +} + + +static inline struct golan_cmd_layout *get_cmd( struct golan *golan, int idx ) +{ + return golan->cmd.addr + (idx << golan->cmd.log_stride); +} + +static inline void golan_calc_sig(struct golan *golan, uint32_t cmd_idx, + uint32_t inbox_idx, uint32_t outbox_idx) +{ + struct golan_cmd_layout *cmd = get_cmd(golan, cmd_idx); + struct mbox *mailbox = NULL; + + if (inbox_idx != NO_MBOX) { + mailbox = GET_INBOX(golan, inbox_idx); + mailbox->mblock.token = cmd->token; + mailbox->mblock.ctrl_sig = ~xor8_buf(mailbox->mblock.rsvd0, + CTRL_SIG_SZ); + } + if (outbox_idx != NO_MBOX) { + mailbox = GET_OUTBOX(golan, outbox_idx); + mailbox->mblock.token = cmd->token; + mailbox->mblock.ctrl_sig = ~xor8_buf(mailbox->mblock.rsvd0, + CTRL_SIG_SZ); + } + cmd->sig = ~xor8_buf(cmd, sizeof(*cmd)); +} + +/** + * Get Golan FW + */ +static int fw_ver_and_cmdif ( struct golan *golan ) { + DBGC (golan ,"\n[%x:%x]rev maj.min.submin = %x.%x.%x cmdif = %x\n", + golan->iseg->fw_rev, + golan->iseg->cmdif_rev_fw_sub, + fw_rev_maj ( golan ), fw_rev_min ( golan ), + fw_rev_sub ( golan ), cmdif_rev ( golan)); + + if (cmdif_rev ( golan) != PXE_CMDIF_REF) { + DBGC (golan ,"CMDIF %d not supported current is %d\n", + cmdif_rev ( golan ), PXE_CMDIF_REF); + return 1; + } + return 0; +} + +static inline void show_out_status(uint32_t *out) +{ + DBG("%x\n", be32_to_cpu(out[0])); + DBG("%x\n", be32_to_cpu(out[1])); + DBG("%x\n", be32_to_cpu(out[2])); + DBG("%x\n", be32_to_cpu(out[3])); +} +/** + * Check if CMD has finished. + */ +static inline uint32_t is_command_finished( struct golan *golan, int idx) +{ + wmb(); + return !(get_cmd( golan , idx )->status_own & CMD_OWNER_HW); +} + +/** + * Wait for Golan command completion + * + * @v golan Golan device + * @ret rc Return status code + */ +static inline int golan_cmd_wait(struct golan *golan, int idx, const char *command) +{ + unsigned int wait; + int rc = -EBUSY; + + for ( wait = GOLAN_HCR_MAX_WAIT_MS ; wait ; --wait ) { + if (is_command_finished(golan, idx)) { + rc = CMD_STATUS(golan, idx); + rmb(); + break; + } else { + mdelay ( 1 ); + } + } + if (rc) { + DBGC (golan ,"[%s]RC is %s[%x]\n", command, cmd_status_str(rc), rc); + } + + golan->cmd_bm &= ~(1 << idx); + return rc; +} + +/** + * Notify the HW that commands are ready + */ +static inline void send_command(struct golan *golan) +{ + wmb(); //Make sure the command is visible in "memory". + writel(cpu_to_be32(golan->cmd_bm) , &golan->iseg->cmd_dbell); +} + +static inline int send_command_and_wait(struct golan *golan, uint32_t cmd_idx, + uint32_t inbox_idx, uint32_t outbox_idx, const char *command) +{ + golan_calc_sig(golan, cmd_idx, inbox_idx, outbox_idx); + send_command(golan); + return golan_cmd_wait(golan, cmd_idx, command); +} + +/** + * Prepare a FW command, + * In - comamnd idx (Must be valid) + * writes the command parameters. + */ +static inline struct golan_cmd_layout *write_cmd(struct golan *golan, int idx, + uint16_t opcode, uint16_t opmod, + uint16_t inbox_idx, + uint16_t outbox_idx, uint16_t inlen, + uint16_t outlen) +{ + struct golan_cmd_layout *cmd = get_cmd(golan , idx); + struct golan_inbox_hdr *hdr = (struct golan_inbox_hdr *)cmd->in; + static uint8_t token; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->type = GOLAN_PCI_CMD_XPORT; + cmd->status_own = CMD_OWNER_HW; + cmd->outlen = cpu_to_be32(outlen); + cmd->inlen = cpu_to_be32(inlen); + hdr->opcode = cpu_to_be16(opcode); + hdr->opmod = cpu_to_be16(opmod); + + if (inbox_idx != NO_MBOX) { + memset(GET_INBOX(golan, inbox_idx), 0, MAILBOX_SIZE); + cmd->in_ptr = VIRT_2_BE64_BUS(GET_INBOX(golan, inbox_idx)); + cmd->token = ++token; + } + if (outbox_idx != NO_MBOX) { + memset(GET_OUTBOX(golan, outbox_idx), 0, MAILBOX_SIZE); + cmd->out_ptr = VIRT_2_BE64_BUS(GET_OUTBOX(golan, outbox_idx)); + } + + golan->cmd_bm |= 1 << idx; + + assert ( cmd != NULL ); + return cmd; +} + +static inline int golan_core_enable_hca(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ENABLE_HCA, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_enable_hca_mbox_in), + sizeof(struct golan_enable_hca_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + return rc; +} + +static inline void golan_disable_hca(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DISABLE_HCA, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_disable_hca_mbox_in), + sizeof(struct golan_disable_hca_mbox_out)); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; +} + +static inline int golan_set_hca_cap(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_SET_HCA_CAP, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_cmd_set_hca_cap_mbox_in), + sizeof(struct golan_cmd_set_hca_cap_mbox_out)); + + golan->caps.flags &= ~GOLAN_DEV_CAP_FLAG_CMDIF_CSUM; + DBGC( golan , "%s caps.uar_sz = %d\n", __FUNCTION__, golan->caps.uar_sz); + DBGC( golan , "%s caps.log_pg_sz = %d\n", __FUNCTION__, golan->caps.log_pg_sz); + DBGC( golan , "%s caps.log_uar_sz = %d\n", __FUNCTION__, be32_to_cpu(golan->caps.uar_page_sz)); + golan->caps.uar_page_sz = 0; + + + memcpy(((struct golan_hca_cap *)GET_INBOX(golan, GEN_MBOX)), + &(golan->caps), + sizeof(struct golan_hca_cap)); + + //if command failed we should reset the caps in golan->caps + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + return rc; +} + +static inline int golan_qry_hca_cap(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc = 0; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_CAP, 0x1, + NO_MBOX, GEN_MBOX, + sizeof(struct golan_cmd_query_hca_cap_mbox_in), + sizeof(struct golan_cmd_query_hca_cap_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, GEN_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_hca_cap ); + + memcpy(&(golan->caps), + ((struct golan_hca_cap *)GET_OUTBOX(golan, GEN_MBOX)), + sizeof(struct golan_hca_cap)); +err_query_hca_cap: + return rc; +} + +static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 func_id ) { + uint32_t out_num_entries = 0; + int size_ibox = sizeof(struct golan_manage_pages_inbox); + int size_obox = sizeof(struct golan_manage_pages_outbox); + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + while ( pages > 0 ) { + uint32_t pas_num = min(pages, MAX_PASE_MBOX); + unsigned i; + struct golan_cmd_layout *cmd; + struct golan_manage_pages_inbox *in; + struct golan_manage_pages_outbox_data *out; + + size_ibox += (pas_num * GOLAN_PAS_SIZE); + size_obox += (pas_num * GOLAN_PAS_SIZE); + + cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_TAKE, + MEM_MBOX, MEM_MBOX, + size_ibox, + size_obox); + + in = (struct golan_manage_pages_inbox *)cmd->in; /* Warning (WE CANT USE THE LAST 2 FIELDS) */ + + in->func_id = func_id; /* Already BE */ + in->num_entries = cpu_to_be32(pas_num); + + if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) { + out = (struct golan_manage_pages_outbox_data *)GET_OUTBOX(golan, MEM_MBOX); + out_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries); + for (i = 0; i < out_num_entries; ++i) { + ufree(BE64_BUS_2_USR(out->pas[i])); + } + } else { + if ( rc == -EBUSY ) { + DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" ); + } else { + DBGC (golan ,"%s: rc =0x%x[%s]<%x> syn 0x%x[0x%x] for %d pages\n", + __FUNCTION__, rc, cmd_status_str(rc), + CMD_SYND(golan, MEM_CMD_IDX), + get_cmd( golan , MEM_CMD_IDX )->status_own, + be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num); + } + return rc; + } + + pages -= out_num_entries; + } + DBGC( golan , "%s Pages handled\n", __FUNCTION__); + return 0; +} + +static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __be16 func_id ) { + struct mbox *mailbox; + int size_ibox = sizeof(struct golan_manage_pages_inbox); + int size_obox = sizeof(struct golan_manage_pages_outbox); + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + while ( pages > 0 ) { + uint32_t pas_num = min(pages, MAX_PASE_MBOX); + unsigned i, j; + struct golan_cmd_layout *cmd; + struct golan_manage_pages_inbox *in; + userptr_t addr = 0; + + mailbox = GET_INBOX(golan, MEM_MBOX); + size_ibox += (pas_num * GOLAN_PAS_SIZE); + size_obox += (pas_num * GOLAN_PAS_SIZE); + + cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_GIVE, + MEM_MBOX, MEM_MBOX, + size_ibox, + size_obox); + + in = (struct golan_manage_pages_inbox *)cmd->in; /* Warning (WE CANT USE THE LAST 2 FIELDS) */ + + in->func_id = func_id; /* Already BE */ + in->num_entries = cpu_to_be32(pas_num); + + for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j ) { + if (!(addr = umalloc(GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + DBGC (golan ,"Couldnt allocated page \n"); + goto malloc_dma_failed; + } + if (GOLAN_PAGE_MASK & user_to_phys(addr, 0)) { + DBGC (golan ,"Addr not Page alligned [%lx %lx]\n", user_to_phys(addr, 0), addr); + } + mailbox->mblock.data[j] = USR_2_BE64_BUS(addr); + } + + if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) { + pages -= pas_num; + golan->total_dma_pages += pas_num; + } else { + if ( rc == -EBUSY ) { + DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" ); + } else { + DBGC (golan ,"%s: rc =0x%x[%s]<%x> syn 0x%x[0x%x] for %d pages\n", + __FUNCTION__, rc, cmd_status_str(rc), + CMD_SYND(golan, MEM_CMD_IDX), + get_cmd( golan , MEM_CMD_IDX )->status_own, + be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num); + } + ufree ( addr ); + goto err_send_command; + } + } + DBGC( golan , "%s Pages handled\n", __FUNCTION__); + return 0; + +err_send_command: +malloc_dma_failed: + /* Go over In box and free pages */ + /* Send Error to FW */ + /* What is next - Disable HCA? */ + DBGC (golan ,"%s Failed (rc = 0x%x)\n", __FUNCTION__, rc); + return rc; +} + +static inline int golan_handle_pages(struct golan *golan, + enum golan_qry_pages_mode qry, + enum golan_manage_pages_mode mode) +{ + struct golan_cmd_layout *cmd; + + int rc = 0; + int32_t pages; + uint16_t total_pages; + __be16 func_id; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_QUERY_PAGES, qry, + NO_MBOX, NO_MBOX, + sizeof(struct golan_query_pages_inbox), + sizeof(struct golan_query_pages_outbox)); + + rc = send_command_and_wait(golan, MEM_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_handle_pages_query ); + + pages = be32_to_cpu(QRY_PAGES_OUT(golan, MEM_CMD_IDX)->num_pages); + + DBGC( golan , "%s pages needed: %d\n", __FUNCTION__, pages); + + func_id = QRY_PAGES_OUT(golan, MEM_CMD_IDX)->func_id; + + total_pages = (( pages >= 0 ) ? pages : ( pages * ( -1 ) )); + + if ( mode == GOLAN_PAGES_GIVE ) { + rc = golan_provide_pages(golan, total_pages, func_id); + } else { + rc = golan_take_pages(golan, golan->total_dma_pages, func_id); + golan->total_dma_pages = 0; + } + + if ( rc ) { + DBGC (golan , "Failed to %s pages (rc = %d) - DMA pages allocated = %d\n", + ( ( mode == GOLAN_PAGES_GIVE ) ? "give" : "take" ), rc , golan->total_dma_pages ); + return rc; + } + + return 0; + +err_handle_pages_query: + DBGC (golan ,"%s Qyery pages failed (rc = 0x%x)\n", __FUNCTION__, rc); + return rc; +} + +static inline int golan_set_access_reg ( struct golan *golan __attribute__ (( unused )), uint32_t reg __attribute__ (( unused ))) +{ +#if 0 + write_cmd(golan, _CMD_IDX, GOLAN_CMD_OP_QUERY_PAGES, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_reg_host_endianess), + sizeof(struct golan_reg_host_endianess)); + in->arg = cpu_to_be32(arg); + in->register_id = cpu_to_be16(reg_num); +#endif + DBGC (golan ," %s Not implemented yet\n", __FUNCTION__); + return 0; +} + +static inline void golan_cmd_uninit ( struct golan *golan ) +{ + free_dma(golan->mboxes.outbox, GOLAN_PAGE_SIZE); + free_dma(golan->mboxes.inbox, GOLAN_PAGE_SIZE); + free_dma(golan->cmd.addr, GOLAN_PAGE_SIZE); +} + +/** + * Initialise Golan Command Q parameters + * -- Alocate a 4kb page for the Command Q + * -- Read the stride and log num commands available + * -- Write the address to cmdq_phy_addr in iseg + * @v golan Golan device + */ +static inline int golan_cmd_init ( struct golan *golan ) +{ + int rc = 0; + uint32_t addr_l_sz; + + if (!(golan->cmd.addr = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_failed; + } + if (!(golan->mboxes.inbox = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_inbox_failed; + } + if (!(golan->mboxes.outbox = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_outbox_failed; + } + addr_l_sz = be32_to_cpu(readl(&golan->iseg->cmdq_addr_l_sz)); + + golan->cmd.log_stride = addr_l_sz & 0xf; + golan->cmd.size = 1 << (( addr_l_sz >> 4 ) & 0xf); + + addr_l_sz = virt_to_bus(golan->cmd.addr); + writel(0 /* cpu_to_be32(golan->cmd.addr) >> 32 */, &golan->iseg->cmdq_addr_h); + writel(cpu_to_be32(addr_l_sz), &golan->iseg->cmdq_addr_l_sz); + wmb(); //Make sure the addr is visible in "memory". + + addr_l_sz = be32_to_cpu(readl(&golan->iseg->cmdq_addr_l_sz)); + + DBGC( golan , "%s Command interface was initialized\n", __FUNCTION__); + return 0; + +malloc_dma_outbox_failed: + free_dma(golan->mboxes.inbox, GOLAN_PAGE_SIZE); +malloc_dma_inbox_failed: + free_dma(golan->cmd.addr, GOLAN_PAGE_SIZE); +malloc_dma_failed: + DBGC (golan ,"%s Failed to initialize command interface (rc = 0x%x)\n", + __FUNCTION__, rc); + return rc; +} + +static inline int golan_hca_init(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_INIT_HCA, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_cmd_init_hca_mbox_in), + sizeof(struct golan_cmd_init_hca_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + return rc; +} + +static inline void golan_teardown_hca(struct golan *golan, enum golan_teardown op_mod) +{ + struct golan_cmd_layout *cmd; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_TEARDOWN_HCA, op_mod, + NO_MBOX, NO_MBOX, + sizeof(struct golan_cmd_teardown_hca_mbox_in), + sizeof(struct golan_cmd_teardown_hca_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + DBGC (golan, "%s HCA teardown compleated\n", __FUNCTION__); +} + +static inline int golan_alloc_uar(struct golan *golan) +{ + struct golan_uar *uar = &golan->uar; + struct golan_cmd_layout *cmd; + struct golan_alloc_uar_mbox_out *out; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ALLOC_UAR, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_alloc_uar_mbox_in), + sizeof(struct golan_alloc_uar_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_alloc_uar_cmd ); + out = (struct golan_alloc_uar_mbox_out *) ( cmd->out ); + + uar->index = be32_to_cpu(out->uarn) & 0xffffff; + + uar->phys = (pci_bar_start(golan->pci, GOLAN_HCA_BAR) + (uar->index << GOLAN_PAGE_SHIFT)); + uar->virt = (void *)(ioremap(uar->phys, GOLAN_PAGE_SIZE)); + + DBGC( golan , "%s: UAR allocated with index 0x%x\n", __FUNCTION__, uar->index); + return 0; + +err_alloc_uar_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_dealloc_uar(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + uint32_t uar_index = golan->uar.index; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DEALLOC_UAR, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_free_uar_mbox_in), + sizeof(struct golan_free_uar_mbox_out)); + + ((struct golan_free_uar_mbox_in *)(cmd->in))->uarn = cpu_to_be32(uar_index); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + golan->uar.index = 0; + + DBGC (golan, "%s UAR (0x%x) was destroyed\n", __FUNCTION__, uar_index); +} + +static void golan_eq_update_ci(struct golan_event_queue *eq, int arm) +{ + __be32 *addr = eq->doorbell + (arm ? 0 : 2); + u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24); + writel(cpu_to_be32(val) , addr); + /* We still want ordering, just not swabbing, so add a barrier */ + wmb(); +} + +static int golan_create_eq(struct golan *golan) +{ + struct golan_event_queue *eq = &golan->eq; + struct golan_create_eq_mbox_in_data *in; + struct golan_cmd_layout *cmd; + struct golan_create_eq_mbox_out *out; + int rc, i; + userptr_t addr; + + eq->cons_index = 0; + eq->size = GOLAN_NUM_EQES * sizeof(eq->eqes[0]); + addr = golan_get_page ( &golan->pages ); + if (!addr) { + rc = -ENOMEM; + goto err_create_eq_eqe_alloc; + } + eq->eqes = (struct golan_eqe *)user_to_virt(addr, 0); + + /* Set EQEs ownership bit to HW ownership */ + for (i = 0; i < GOLAN_NUM_EQES; ++i) { + eq->eqes[i].owner = GOLAN_EQE_HW_OWNERSHIP; + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_EQ, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_eq_mbox_in) + GOLAN_PAS_SIZE, + sizeof(struct golan_create_eq_mbox_out)); + + in = (struct golan_create_eq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + /* Fill the physical address of the page */ + in->pas[0] = USR_2_BE64_BUS(addr); + in->ctx.log_sz_usr_page = cpu_to_be32((ilog2(GOLAN_NUM_EQES)) << 24 | golan->uar.index); + DBGC( golan , "UAR idx %x (BE %x)\n", golan->uar.index, in->ctx.log_sz_usr_page); + in->events_mask = cpu_to_be64(1 << GOLAN_EVENT_TYPE_PORT_CHANGE); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_eq_cmd ); + out = (struct golan_create_eq_mbox_out *)cmd->out; + + eq->eqn = out->eq_number; + eq->doorbell = ((void *)golan->uar.virt) + GOLAN_EQ_DOORBELL_OFFSET; + + /* EQs are created in ARMED state */ + golan_eq_update_ci(eq, GOLAN_EQ_UNARMED); + + DBGC( golan , "%s: Event queue created (EQN = 0x%x)\n", __FUNCTION__, eq->eqn); + return 0; + +err_create_eq_cmd: + ufree(virt_to_user(golan->eq.eqes)); +err_create_eq_eqe_alloc: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_destory_eq(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + uint8_t eqn = golan->eq.eqn; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_EQ, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_eq_mbox_in), + sizeof(struct golan_destroy_eq_mbox_out)); + + ((struct golan_destroy_eq_mbox_in *)(cmd->in))->eqn = eqn; + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + ufree(virt_to_user(golan->eq.eqes)); + golan->eq.eqn = 0; + + DBGC( golan, "%s Event queue (0x%x) was destroyed\n", __FUNCTION__, eqn); +} + +static int golan_alloc_pd(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + struct golan_alloc_pd_mbox_out *out; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ALLOC_PD, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_alloc_pd_mbox_in), + sizeof(struct golan_alloc_pd_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_alloc_pd_cmd ); + out = (struct golan_alloc_pd_mbox_out *) ( cmd->out ); + + golan->pdn = (be32_to_cpu(out->pdn) & 0xffffff); + DBGC( golan , "%s: Protection domain created (PDN = 0x%x)\n", __FUNCTION__, + golan->pdn); + return 0; + +err_alloc_pd_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_dealloc_pd(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + uint32_t pdn = golan->pdn; + int rc; + + DBGC (golan,"%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DEALLOC_PD, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_alloc_pd_mbox_in), + sizeof(struct golan_alloc_pd_mbox_out)); + + ((struct golan_dealloc_pd_mbox_in *)(cmd->in))->pdn = cpu_to_be32(pdn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + golan->pdn = 0; + + DBGC (golan ,"%s Protection domain (0x%x) was destroyed\n", __FUNCTION__, pdn); +} + +static int golan_create_mkey(struct golan *golan) +{ + struct golan_create_mkey_mbox_in_data *in; + struct golan_cmd_layout *cmd; + struct golan_create_mkey_mbox_out *out; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_MKEY, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_mkey_mbox_in), + sizeof(struct golan_create_mkey_mbox_out)); + + in = (struct golan_create_mkey_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + in->seg.flags = GOLAN_IB_ACCESS_LOCAL_WRITE | GOLAN_IB_ACCESS_LOCAL_READ; + in->seg.flags_pd = cpu_to_be32(golan->pdn | GOLAN_MKEY_LEN64); + in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << GOLAN_CREATE_MKEY_SEG_QPN_BIT); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_mkey_cmd ); + out = (struct golan_create_mkey_mbox_out *) ( cmd->out ); + + golan->mkey = ((be32_to_cpu(out->mkey) & 0xffffff) << 8); + DBGC( golan , "%s: Got DMA Key for local access read/write (MKEY = 0x%x)\n", + __FUNCTION__, golan->mkey); + return 0; +err_create_mkey_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_destroy_mkey(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + u32 mkey = golan->mkey; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_MKEY, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_mkey_mbox_in), + sizeof(struct golan_destroy_mkey_mbox_out)); + ((struct golan_destroy_mkey_mbox_in *)(cmd->in))->mkey = cpu_to_be32(mkey >> 8); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + golan->mkey = 0; + + DBGC( golan , "%s DMA Key (0x%x) for local access write was destroyed\n" + , __FUNCTION__, mkey); +} + + +/** + * Initialise Golan PCI parameters + * + * @v golan Golan device + */ +static inline void golan_pci_init(struct golan *golan) +{ + struct pci_device *pci = golan->pci; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Get HCA BAR */ + golan->iseg = ioremap ( pci_bar_start ( pci, GOLAN_HCA_BAR), + GOLAN_PCI_CONFIG_BAR_SIZE ); +} + +static inline struct golan *golan_alloc() +{ + void *golan = zalloc(sizeof(struct golan)); + if ( !golan ) + goto err_zalloc; + + return golan; + +err_zalloc: + return NULL; +} + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int golan_create_cq(struct ib_device *ibdev, + struct ib_completion_queue *cq) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_completion_queue *golan_cq; + struct golan_cmd_layout *cmd; + struct golan_create_cq_mbox_in_data *in; + struct golan_create_cq_mbox_out *out; + int rc; + unsigned int i; + userptr_t addr; + + golan_cq = zalloc(sizeof(*golan_cq)); + if (!golan_cq) { + rc = -ENOMEM; + goto err_create_cq; + } + golan_cq->size = sizeof(golan_cq->cqes[0]) * cq->num_cqes; + golan_cq->doorbell_record = malloc_dma(GOLAN_CQ_DB_RECORD_SIZE, + GOLAN_CQ_DB_RECORD_SIZE); + if (!golan_cq->doorbell_record) { + rc = -ENOMEM; + goto err_create_cq_db_alloc; + } + + addr = golan_get_page ( &golan->pages ); + if (!addr) { + rc = -ENOMEM; + goto err_create_cq_cqe_alloc; + } + golan_cq->cqes = (struct golan_cqe64 *)user_to_virt(addr, 0); + + /* Set CQEs ownership bit to HW ownership */ + for (i = 0; i < cq->num_cqes; ++i) { + golan_cq->cqes[i].op_own = ((GOLAN_CQE_OPCODE_NOT_VALID << + GOLAN_CQE_OPCODE_BIT) | + GOLAN_CQE_HW_OWNERSHIP); + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_CQ, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_cq_mbox_in) + GOLAN_PAS_SIZE, + sizeof(struct golan_create_cq_mbox_out)); + + in = (struct golan_create_cq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + /* Fill the physical address of the page */ + in->pas[0] = USR_2_BE64_BUS(addr); + in->ctx.cqe_sz_flags = GOLAN_CQE_SIZE_64 << 5; + in->ctx.log_sz_usr_page = cpu_to_be32(((ilog2(cq->num_cqes)) << 24) | golan->uar.index); + in->ctx.c_eqn = cpu_to_be16(golan->eq.eqn); + in->ctx.db_record_addr = VIRT_2_BE64_BUS(golan_cq->doorbell_record); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_cq_cmd ); + out = (struct golan_create_cq_mbox_out *) ( cmd->out ); + + cq->cqn = (be32_to_cpu(out->cqn) & 0xffffff); + + ib_cq_set_drvdata(cq, golan_cq); + + DBGC( golan , "%s CQ created successfully (CQN = 0x%lx)\n", __FUNCTION__, cq->cqn); + return 0; + +err_create_cq_cmd: + ufree(virt_to_user(golan_cq->cqes)); +err_create_cq_cqe_alloc: + free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); +err_create_cq_db_alloc: + free ( golan_cq ); +err_create_cq: + DBGC (golan ,"%s out rc = 0x%x\n", __FUNCTION__, rc); + return rc; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void golan_destroy_cq(struct ib_device *ibdev, + struct ib_completion_queue *cq) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_completion_queue *golan_cq = ib_cq_get_drvdata(cq); + struct golan_cmd_layout *cmd; + uint32_t cqn = cq->cqn; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_CQ, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_cq_mbox_in), + sizeof(struct golan_destroy_cq_mbox_out)); + ((struct golan_destroy_cq_mbox_in *)(cmd->in))->cqn = cpu_to_be32(cqn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + cq->cqn = 0; + + ib_cq_set_drvdata(cq, NULL); + ufree(virt_to_user(golan_cq->cqes)); + free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); + free(golan_cq); + + DBGC (golan, "%s CQ number 0x%x was destroyed\n", __FUNCTION__, cqn); +} + +static void golan_cq_clean(struct ib_completion_queue *cq) +{ + ib_poll_cq(cq->ibdev, cq); +} + +static int golan_qp_type_to_st(enum ib_queue_pair_type type) +{ + int qpt = type; + + switch (qpt) { + case IB_QPT_RC: + return GOLAN_QP_ST_RC; + case IB_QPT_UD: + return GOLAN_QP_ST_UD; + case IB_QPT_SMI: + return GOLAN_QP_ST_QP0; + case IB_QPT_GSI: + return GOLAN_QP_ST_QP1; + case IB_QPT_ETH: + default: + return -EINVAL; + } +} +#if 0 +static int golan_is_special_qp(enum ib_queue_pair_type type) +{ + return (type == IB_QPT_GSI || type == IB_QPT_SMI); +} +#endif +static int golan_create_qp_aux(struct ib_device *ibdev, + struct ib_queue_pair *qp, + int *qpn) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp; + struct golan_create_qp_mbox_in_data *in; + struct golan_cmd_layout *cmd; + struct golan_wqe_data_seg *data; + struct golan_create_qp_mbox_out *out; + userptr_t addr; + uint32_t wqe_size_in_bytes; + uint32_t max_qp_size_in_wqes; + unsigned int i; + int rc; + + golan_qp = zalloc(sizeof(*golan_qp)); + if (!golan_qp) { + rc = -ENOMEM; + goto err_create_qp; + } + + if ( ( qp->type == IB_QPT_SMI ) || ( qp->type == IB_QPT_GSI ) || + ( qp->type == IB_QPT_UD ) ) { + golan_qp->rq.grh_size = ( qp->recv.num_wqes * + sizeof ( golan_qp->rq.grh[0] )); + } + + /* Calculate receive queue size */ + golan_qp->rq.size = qp->recv.num_wqes * GOLAN_RECV_WQE_SIZE; + if (GOLAN_RECV_WQE_SIZE > be16_to_cpu(golan->caps.max_wqe_sz_rq)) { + DBGC (golan ,"%s receive wqe size [%zd] > max wqe size [%d]\n", __FUNCTION__, + GOLAN_RECV_WQE_SIZE, be16_to_cpu(golan->caps.max_wqe_sz_rq)); + rc = -EINVAL; + goto err_create_qp_rq_size; + } + + wqe_size_in_bytes = sizeof(golan_qp->sq.wqes[0]); + /* Calculate send queue size */ + if (wqe_size_in_bytes > be16_to_cpu(golan->caps.max_wqe_sz_sq)) { + DBGC (golan ,"%s send WQE size [%d] > max WQE size [%d]\n", __FUNCTION__, + wqe_size_in_bytes, + be16_to_cpu(golan->caps.max_wqe_sz_sq)); + rc = -EINVAL; + goto err_create_qp_sq_wqe_size; + } + golan_qp->sq.size = (qp->send.num_wqes * wqe_size_in_bytes); + max_qp_size_in_wqes = (1 << ((uint32_t)(golan->caps.log_max_qp_sz))); + if (qp->send.num_wqes > max_qp_size_in_wqes) { + DBGC (golan ,"%s send wq size [%d] > max wq size [%d]\n", __FUNCTION__, + golan_qp->sq.size, max_qp_size_in_wqes); + rc = -EINVAL; + goto err_create_qp_sq_size; + } + + golan_qp->size = golan_qp->sq.size + golan_qp->rq.size; + + /* allocate dma memory for WQEs (1 page is enough) - should change it */ + addr = golan_get_page ( &golan->pages ); + if (!addr) { + rc = -ENOMEM; + goto err_create_qp_wqe_alloc; + } + golan_qp->wqes = user_to_virt(addr, 0); + golan_qp->rq.wqes = golan_qp->wqes; + golan_qp->sq.wqes = golan_qp->wqes + golan_qp->rq.size;//(union golan_send_wqe *)& + //(((struct golan_recv_wqe_ud *)(golan_qp->wqes))[qp->recv.num_wqes]); + + if ( golan_qp->rq.grh_size ) { + golan_qp->rq.grh = ( golan_qp->wqes + + golan_qp->sq.size + + golan_qp->rq.size ); + } + + /* Invalidate all WQEs */ + data = &golan_qp->rq.wqes[0].data[0]; + for ( i = 0 ; i < ( golan_qp->rq.size / sizeof ( *data ) ); i++ ){ + data->lkey = cpu_to_be32 ( GOLAN_INVALID_LKEY ); + data++; + } + + golan_qp->doorbell_record = malloc_dma(sizeof(struct golan_qp_db), + sizeof(struct golan_qp_db)); + if (!golan_qp->doorbell_record) { + rc = -ENOMEM; + goto err_create_qp_db_alloc; + } + memset(golan_qp->doorbell_record, 0, sizeof(struct golan_qp_db)); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_QP, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_qp_mbox_in) + GOLAN_PAS_SIZE, + sizeof(struct golan_create_qp_mbox_out)); + + in = (struct golan_create_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + /* Fill the physical address of the page */ + in->pas[0] = USR_2_BE64_BUS(addr); + in->ctx.qp_counter_set_usr_page = cpu_to_be32(golan->uar.index); + + in->ctx.flags_pd = cpu_to_be32(golan->pdn); + in->ctx.flags = cpu_to_be32((golan_qp_type_to_st(qp->type) + << GOLAN_QP_CTX_ST_BIT) | + (GOLAN_QP_PM_MIGRATED << + GOLAN_QP_CTX_PM_STATE_BIT)); +// cgs set to 0, initialy. +// atomic mode + in->ctx.rq_size_stride = ((ilog2(qp->recv.num_wqes) << + GOLAN_QP_CTX_RQ_SIZE_BIT) | + (sizeof(golan_qp->rq.wqes[0]) / GOLAN_RECV_WQE_SIZE)); + in->ctx.sq_crq_size = cpu_to_be16(ilog2(golan_qp->sq.size / GOLAN_SEND_WQE_BB_SIZE) + << GOLAN_QP_CTX_SQ_SIZE_BIT); + in->ctx.cqn_send = cpu_to_be32(qp->send.cq->cqn); + in->ctx.cqn_recv = cpu_to_be32(qp->recv.cq->cqn); + in->ctx.db_rec_addr = VIRT_2_BE64_BUS(golan_qp->doorbell_record); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_qp_cmd ); + out = (struct golan_create_qp_mbox_out *)cmd->out; + + *qpn = (be32_to_cpu(out->qpn) & 0xffffff); + /* + * Hardware wants QPN written in big-endian order (after + * shifting) for send doorbell. Precompute this value to save + * a little bit when posting sends. + */ + golan_qp->doorbell_qpn = cpu_to_be32(*qpn << 8); + golan_qp->state = GOLAN_IB_QPS_RESET; + + ib_qp_set_drvdata(qp, golan_qp); + + return 0; + +err_create_qp_cmd: + free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); +err_create_qp_db_alloc: + ufree((userptr_t)golan_qp->wqes); +err_create_qp_wqe_alloc: +err_create_qp_sq_size: +err_create_qp_sq_wqe_size: +err_create_qp_rq_size: + free ( golan_qp ); +err_create_qp: + return rc; +} + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int golan_create_qp(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + int rc, qpn = -1; + + switch (qp->type) { + case IB_QPT_UD: + case IB_QPT_SMI: + case IB_QPT_GSI: + rc = golan_create_qp_aux(ibdev, qp, &qpn); + if (rc) { + DBG ( "%s Failed to create QP (rc = 0x%x)\n", __FUNCTION__, rc); + return rc; + } + qp->qpn = qpn; + + break; + case IB_QPT_ETH: + case IB_QPT_RC: + default: + DBG ( "%s unsupported QP type (0x%x)\n", __FUNCTION__, qp->type); + return -EINVAL; + } + + return 0; +} + +static int golan_modify_qp_rst_to_init(struct ib_device *ibdev, + struct ib_queue_pair *qp __unused, + struct golan_modify_qp_mbox_in_data *in) +{ + int rc = 0; + + in->ctx.qkey = cpu_to_be32((uint32_t)(qp->qkey)); + + in->ctx.pri_path.port = ibdev->port; + in->ctx.flags |= cpu_to_be32(GOLAN_QP_PM_MIGRATED << GOLAN_QP_CTX_PM_STATE_BIT); + in->ctx.pri_path.pkey_index = 0; /* default index */ + /* QK is 0 */ + /* QP cntr set 0 */ + return rc; +} + +static int golan_modify_qp_init_to_rtr(struct ib_device *ibdev __unused, + struct ib_queue_pair *qp __unused, + struct golan_modify_qp_mbox_in_data *in) +{ + int rc = 0; + + in->optparam = 0; + return rc; +} + +static int golan_modify_qp_rtr_to_rts(struct ib_device *ibdev __unused, + struct ib_queue_pair *qp __unused, + struct golan_modify_qp_mbox_in_data *in __unused) +{ + int rc = 0; + + in->optparam = 0; + /* In good flow psn in 0 */ + return rc; +} + +static int golan_modify_qp_to_rst(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_cmd_layout *cmd; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_2RST_QP, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_modify_qp_mbox_in), + sizeof(struct golan_modify_qp_mbox_out)); + ((struct golan_modify_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_modify_qp_2rst_cmd ); + + golan_qp->state = GOLAN_IB_QPS_RESET; + DBGC( golan , "%s QP number 0x%lx was modified to RESET\n", + __FUNCTION__, qp->qpn); + + return 0; + +err_modify_qp_2rst_cmd: + DBGC (golan ,"%s Failed to modify QP number 0x%lx (rc = 0x%x)\n", + __FUNCTION__, qp->qpn, rc); + return rc; +} + +static int (*golan_modify_qp_methods[])(struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct golan_modify_qp_mbox_in_data *in) = { + + [GOLAN_IB_QPS_RESET] = golan_modify_qp_rst_to_init, + [GOLAN_IB_QPS_INIT] = golan_modify_qp_init_to_rtr, + [GOLAN_IB_QPS_RTR] = golan_modify_qp_rtr_to_rts +}; + +static int golan_modify_qp(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_modify_qp_mbox_in_data *in; + struct golan_cmd_layout *cmd; + enum golan_ib_qp_state prev_state; + int rc; + int modify_cmd[] = {GOLAN_CMD_OP_RST2INIT_QP, + GOLAN_CMD_OP_INIT2RTR_QP, + GOLAN_CMD_OP_RTR2RTS_QP}; + + while (golan_qp->state < GOLAN_IB_QPS_RTS) { + prev_state = golan_qp->state; + cmd = write_cmd(golan, DEF_CMD_IDX, modify_cmd[golan_qp->state], 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_modify_qp_mbox_in), + sizeof(struct golan_modify_qp_mbox_out)); + + in = (struct golan_modify_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + ((struct golan_modify_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + rc = golan_modify_qp_methods[golan_qp->state](ibdev, qp, in); + if (rc) { + goto err_modify_qp_fill_inbox; + } +// in->ctx.qp_counter_set_usr_page = cpu_to_be32(golan->uar.index); + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_modify_qp_cmd ); + + ++(golan_qp->state); + + DBGC( golan , "%s QP number 0x%lx was modified from %s to %s\n", + __FUNCTION__, qp->qpn, golan_qp_state_as_string[prev_state], + golan_qp_state_as_string[golan_qp->state]); + } + + DBGC( golan , "%s QP number 0x%lx is ready to receive/send packets.\n", + __FUNCTION__, qp->qpn); + return 0; + +err_modify_qp_cmd: +err_modify_qp_fill_inbox: + DBGC (golan ,"%s Failed to modify QP number 0x%lx (rc = 0x%x)\n", + __FUNCTION__, qp->qpn, rc); + return rc; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void golan_destroy_qp(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_cmd_layout *cmd; + unsigned long qpn = qp->qpn; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + if (golan_qp->state != GOLAN_IB_QPS_RESET) { + if (golan_modify_qp_to_rst(ibdev, qp)) { + DBGC (golan ,"%s Failed to modify QP 0x%lx to RESET\n", __FUNCTION__, + qp->qpn); + } + } + + if (qp->recv.cq) { + golan_cq_clean(qp->recv.cq); + } + if (qp->send.cq && (qp->send.cq != qp->recv.cq)) { + golan_cq_clean(qp->send.cq); + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_QP, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_qp_mbox_in), + sizeof(struct golan_destroy_qp_mbox_out)); + ((struct golan_destroy_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qpn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + qp->qpn = 0; + + ib_qp_set_drvdata(qp, NULL); + free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); + ufree((userptr_t)golan_qp->wqes); + free(golan_qp); + + DBGC( golan ,"%s QP 0x%lx was destroyed\n", __FUNCTION__, qpn); +} + +/** + * Calculate transmission rate + * + * @v av Address vector + * @ret golan_rate Golan rate + */ +static unsigned int golan_rate(enum ib_rate rate) { + return (((rate >= IB_RATE_2_5) && (rate <= IB_RATE_120)) ? (rate + 5) : 0); +} + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int golan_post_send(struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_send_wqe_ud *wqe = NULL; + struct golan_av *datagram = NULL; + unsigned long wqe_idx_mask; + unsigned long wqe_idx; + struct golan_wqe_data_seg *data = NULL; + struct golan_wqe_ctrl_seg *ctrl = NULL; +// static uint8_t toggle = 0; + + + wqe_idx_mask = (qp->send.num_wqes - 1); + wqe_idx = (qp->send.next_idx & wqe_idx_mask); + if (qp->send.iobufs[wqe_idx]) { + DBGC (golan ,"%s Send queue of QPN 0x%lx is full\n", __FUNCTION__, qp->qpn); + return -ENOMEM; + } + + qp->send.iobufs[wqe_idx] = iobuf; + + // change to this + //wqe_size_in_octa_words = golan_qp->sq.wqe_size_in_wqebb >> 4; + + wqe = &golan_qp->sq.wqes[wqe_idx].ud; + + //CHECK HW OWNERSHIP BIT ??? + + memset(wqe, 0, sizeof(*wqe)); + + ctrl = &wqe->ctrl; + ctrl->opmod_idx_opcode = cpu_to_be32(GOLAN_SEND_OPCODE | + ((u32)(golan_qp->sq.next_idx) << + GOLAN_WQE_CTRL_WQE_IDX_BIT)); + ctrl->qpn_ds = cpu_to_be32(GOLAN_SEND_UD_WQE_SIZE >> 4) | + golan_qp->doorbell_qpn; + ctrl->fm_ce_se = 0x8;//10 - 0 - 0 + data = &wqe->data; + data->byte_count = cpu_to_be32(iob_len(iobuf)); + data->lkey = cpu_to_be32(golan->mkey); + data->addr = VIRT_2_BE64_BUS(iobuf->data); + + datagram = &wqe->datagram; + datagram->key.qkey.qkey = cpu_to_be32(av->qkey); + datagram->dqp_dct = cpu_to_be32((1 << 31) | av->qpn); + datagram->stat_rate_sl = ((golan_rate(av->rate) << 4) | av->sl); + datagram->fl_mlid = (ibdev->lid & 0x007f); /* take only the 7 low bits of the LID */ + datagram->rlid = cpu_to_be16(av->lid); + datagram->grh_gid_fl = cpu_to_be32(av->gid_present << 30); + memcpy(datagram->rgid, av->gid.bytes, 16 /* sizeof(datagram->rgid) */); + + /* + * Make sure that descriptors are written before + * updating doorbell record and ringing the doorbell + */ + ++(qp->send.next_idx); + golan_qp->sq.next_idx = (golan_qp->sq.next_idx + GOLAN_WQEBBS_PER_SEND_UD_WQE); + golan_qp->doorbell_record->send_db = cpu_to_be16(golan_qp->sq.next_idx); + wmb(); + writeq(*((__be64 *)ctrl), golan->uar.virt + 0x800);// + +// ((toggle++ & 0x1) ? 0x100 : 0x0)); + return 0; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int golan_post_recv(struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct ib_work_queue *wq = &qp->recv; + struct golan_recv_wqe_ud *wqe; + struct ib_global_route_header *grh; + struct golan_wqe_data_seg *data; + unsigned int wqe_idx_mask; + + /* Allocate work queue entry */ + wqe_idx_mask = (wq->num_wqes - 1); + if (wq->iobufs[wq->next_idx & wqe_idx_mask]) { + DBGC (golan ,"%s Receive queue of QPN 0x%lx is full\n", __FUNCTION__, qp->qpn); + return -ENOMEM; + } + + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = & golan_qp->rq.wqes[wq->next_idx & wqe_idx_mask]; + + memset(wqe, 0, sizeof(*wqe)); + data = &wqe->data[0]; + if ( golan_qp->rq.grh ) { + grh = &golan_qp->rq.grh[wq->next_idx & wqe_idx_mask]; + data->byte_count = cpu_to_be32 ( sizeof ( *grh ) ); + data->lkey = cpu_to_be32 ( golan->mkey ); + data->addr = VIRT_2_BE64_BUS ( grh ); + data++; + } + + data->byte_count = cpu_to_be32(iob_tailroom(iobuf)); + data->lkey = cpu_to_be32(golan->mkey); + data->addr = VIRT_2_BE64_BUS(iobuf->data); + + ++wq->next_idx; + + /* + * Make sure that descriptors are written before + * updating doorbell record and ringing the doorbell + */ + wmb(); + golan_qp->doorbell_record->recv_db = cpu_to_be16(qp->recv.next_idx & 0xffff); + + return 0; +} + +static int golan_query_vport_context ( struct ib_device *ibdev ) { + struct golan *golan = ib_get_drvdata ( ibdev ); + struct golan_cmd_layout *cmd; + struct golan_query_hca_vport_context_data *context_data; + int rc; + + cmd = write_cmd ( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_CONTEXT, + 0x0, GEN_MBOX, GEN_MBOX, + sizeof(struct golan_query_hca_vport_context_inbox), + sizeof(struct golan_query_hca_vport_context_outbox) ); + + ((struct golan_query_hca_vport_context_inbox *)(cmd->in))->port_num = (u8)ibdev->port; + + rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_context_cmd ); + + context_data = (struct golan_query_hca_vport_context_data *)( GET_OUTBOX ( golan, GEN_MBOX ) ); + + ibdev->node_guid.dwords[0] = context_data->node_guid[0]; + ibdev->node_guid.dwords[1] = context_data->node_guid[1]; + ibdev->lid = be16_to_cpu( context_data->lid ); + ibdev->sm_lid = be16_to_cpu( context_data->sm_lid ); + ibdev->sm_sl = context_data->sm_sl; + ibdev->port_state = context_data->port_state; + + return 0; +err_query_vport_context_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + + +static int golan_query_vport_gid ( struct ib_device *ibdev ) { + struct golan *golan = ib_get_drvdata( ibdev ); + struct golan_cmd_layout *cmd; + union ib_gid *ib_gid; + int rc; + + cmd = write_cmd( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_GID, + 0x0, GEN_MBOX, GEN_MBOX, + sizeof(struct golan_query_hca_vport_gid_inbox), + sizeof(struct golan_query_hca_vport_gid_outbox) ); + + ((struct golan_query_hca_vport_gid_inbox *)(cmd->in))->port_num = (u8)ibdev->port; + ((struct golan_query_hca_vport_gid_inbox *)(cmd->in))->gid_index = 0; + rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_gid_cmd ); + + ib_gid = (union ib_gid *)( GET_OUTBOX ( golan, GEN_MBOX ) ); + + memcpy ( &ibdev->gid, ib_gid, sizeof(ibdev->gid) ); + + return 0; +err_query_vport_gid_cmd: + DBGC ( golan, "%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static int golan_query_vport_pkey ( struct ib_device *ibdev ) { + struct golan *golan = ib_get_drvdata ( ibdev ); + struct golan_cmd_layout *cmd; + //struct golan_query_hca_vport_pkey_data *pkey_table; + int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size)); + int rc; + + cmd = write_cmd ( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_PKEY, + 0x0, GEN_MBOX, GEN_MBOX, + sizeof(struct golan_query_hca_vport_pkey_inbox), + sizeof(struct golan_outbox_hdr) + 8 + + sizeof(struct golan_query_hca_vport_pkey_data) * pkey_table_size_in_entries ); + + ((struct golan_query_hca_vport_pkey_inbox *)(cmd->in))->port_num = (u8)ibdev->port; + ((struct golan_query_hca_vport_pkey_inbox *)(cmd->in))->pkey_index = 0xffff; + rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_pkey_cmd ); + + //pkey_table = (struct golan_query_hca_vport_pkey_data *)( GET_OUTBOX ( golan, GEN_MBOX ) ); + + return 0; +err_query_vport_pkey_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static int golan_get_ib_info ( struct ib_device *ibdev ) { + int rc; + + rc = golan_query_vport_context ( ibdev ); + if ( rc != 0 ) { + DBG ( "golan_get_ib_info: golan_query_vport_context Failed (rc = %d)\n",rc ); + goto err_query_vport_context; + } + + rc = golan_query_vport_gid ( ibdev ); + if ( rc != 0 ) { + DBG ( "golan_get_ib_info: golan_query_vport_gid Failed (rc = %d)\n",rc ); + goto err_query_vport_gid; + } + + rc = golan_query_vport_pkey ( ibdev ); + if ( rc != 0 ) { + DBG ( "golan_get_ib_info: golan_query_vport_pkey Failed (rc = %d)\n",rc ); + goto err_query_vport_pkey; + } + return rc; +err_query_vport_pkey: +err_query_vport_gid: +err_query_vport_context: + DBG ( "%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static int golan_complete(struct ib_device *ibdev, + struct ib_completion_queue *cq, + struct golan_cqe64 *cqe64) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct ib_work_queue *wq; + struct golan_queue_pair *golan_qp; + struct ib_queue_pair *qp; + struct io_buffer *iobuf = NULL; + struct ib_address_vector recv_dest; + struct ib_address_vector recv_source; + struct ib_global_route_header *grh; + struct golan_err_cqe *err_cqe64; + int gid_present, idx; + u16 wqe_ctr; + uint8_t opcode; + static int error_state; + uint32_t qpn = be32_to_cpu(cqe64->sop_drop_qpn) & 0xffffff; + int is_send = 0; + size_t len; + + opcode = cqe64->op_own >> GOLAN_CQE_OPCODE_BIT; + DBGC2( golan , "%s completion with opcode 0x%x\n", __FUNCTION__, opcode); + + if (opcode == GOLAN_CQE_REQ || opcode == GOLAN_CQE_REQ_ERR) { + is_send = 1; + } else { + is_send = 0; + } + if (opcode == GOLAN_CQE_REQ_ERR || opcode == GOLAN_CQE_RESP_ERR) { + err_cqe64 = (struct golan_err_cqe *)cqe64; + int i = 0; + if (!error_state++) { + DBGC (golan ,"\n"); + for ( i = 0 ; i < 16 ; i += 2 ) { + DBGC (golan ,"%x %x\n", + be32_to_cpu(((uint32_t *)(err_cqe64))[i]), + be32_to_cpu(((uint32_t *)(err_cqe64))[i + 1])); + } + DBGC (golan ,"CQE with error: Syndrome(0x%x), VendorSynd(0x%x), HW_SYN(0x%x)\n", + err_cqe64->syndrome, err_cqe64->vendor_err_synd, + err_cqe64->hw_syndrom); + } + } + /* Identify work queue */ + wq = ib_find_wq(cq, qpn, is_send); + if (!wq) { + DBGC (golan ,"%s unknown %s QPN 0x%x in CQN 0x%lx\n", + __FUNCTION__, (is_send ? "send" : "recv"), qpn, cq->cqn); + return -EINVAL; + } + + qp = wq->qp; + golan_qp = ib_qp_get_drvdata ( qp ); + + wqe_ctr = be16_to_cpu(cqe64->wqe_counter); + if (is_send) { + wqe_ctr &= ((GOLAN_WQEBBS_PER_SEND_UD_WQE * wq->num_wqes) - 1); + idx = wqe_ctr / GOLAN_WQEBBS_PER_SEND_UD_WQE; + } else { + idx = wqe_ctr & (wq->num_wqes - 1); + } + + iobuf = wq->iobufs[idx]; + if (!iobuf) { + DBGC (golan ,"%s IO Buffer 0x%x not found in QPN 0x%x\n", + __FUNCTION__, idx, qpn); + return -EINVAL; + } + wq->iobufs[idx] = NULL; + + if (is_send) { + ib_complete_send(ibdev, qp, iobuf, (opcode == GOLAN_CQE_REQ_ERR)); + } else { + len = be32_to_cpu(cqe64->byte_cnt); + memset(&recv_dest, 0, sizeof(recv_dest)); + recv_dest.qpn = qpn; + /* Construct address vector */ + memset(&recv_source, 0, sizeof(recv_source)); + switch (qp->type) { + case IB_QPT_SMI: + case IB_QPT_GSI: + case IB_QPT_UD: + /* Locate corresponding GRH */ + assert ( golan_qp->rq.grh != NULL ); + grh = &golan_qp->rq.grh[ idx ]; + + recv_source.qpn = be32_to_cpu(cqe64->flags_rqpn) & 0xffffff; + recv_source.lid = be16_to_cpu(cqe64->slid); + recv_source.sl = (be32_to_cpu(cqe64->flags_rqpn) >> 24) & 0xf; + gid_present = (be32_to_cpu(cqe64->flags_rqpn) >> 28) & 3; + if (!gid_present) { + recv_dest.gid_present = recv_source.gid_present = 0; + } else { + recv_dest.gid_present = recv_source.gid_present = 1; + //if (recv_source.gid_present == 0x1) { + memcpy(&recv_source.gid, &grh->sgid, sizeof(recv_source.gid)); + memcpy(&recv_dest.gid, &grh->dgid, sizeof(recv_dest.gid)); + //} else { // recv_source.gid_present = 0x3 + /* GRH is located in the upper 64 byte of the CQE128 + * currently not supported */ + //; + //} + } + len -= sizeof ( *grh ); + break; + case IB_QPT_RC: + case IB_QPT_ETH: + default: + DBGC (golan ,"%s Unsupported QP type (0x%x)\n", __FUNCTION__, qp->type); + return -EINVAL; + } + assert(len <= iob_tailroom(iobuf)); + iob_put(iobuf, len); + ib_complete_recv(ibdev, qp, &recv_dest, &recv_source, iobuf, (opcode == GOLAN_CQE_RESP_ERR)); + } + return 0; +} + +static int golan_is_hw_ownership(struct ib_completion_queue *cq, + struct golan_cqe64 *cqe64) +{ + return ((cqe64->op_own & GOLAN_CQE_OWNER_MASK) != + ((cq->next_idx >> ilog2(cq->num_cqes)) & 1)); +} +static void golan_poll_cq(struct ib_device *ibdev, + struct ib_completion_queue *cq) +{ + unsigned int i; + int rc = 0; + unsigned int cqe_idx_mask; + struct golan_cqe64 *cqe64; + struct golan_completion_queue *golan_cq = ib_cq_get_drvdata(cq); + struct golan *golan = ib_get_drvdata(ibdev); + + for (i = 0; i < cq->num_cqes; ++i) { + /* Look for completion entry */ + cqe_idx_mask = (cq->num_cqes - 1); + cqe64 = &golan_cq->cqes[cq->next_idx & cqe_idx_mask]; + /* temporary valid only for 64 byte CQE */ + if (golan_is_hw_ownership(cq, cqe64) || + ((cqe64->op_own >> GOLAN_CQE_OPCODE_BIT) == + GOLAN_CQE_OPCODE_NOT_VALID)) { + break; /* HW ownership */ + } + + DBGC2( golan , "%s CQN 0x%lx [%ld] \n", __FUNCTION__, cq->cqn, cq->next_idx); + /* + * Make sure we read CQ entry contents after we've checked the + * ownership bit. (PRM - 6.5.3.2) + */ + rmb(); + rc = golan_complete(ibdev, cq, cqe64); + if (rc != 0) { + DBGC (golan ,"%s CQN 0x%lx failed to complete\n", __FUNCTION__, cq->cqn); + } + + /* Update completion queue's index */ + cq->next_idx++; + + /* Update doorbell record */ + *(golan_cq->doorbell_record) = cpu_to_be32(cq->next_idx & 0xffffff); + } +} + +static const char *golan_eqe_type_str(u8 type) +{ + switch (type) { + case GOLAN_EVENT_TYPE_COMP: + return "GOLAN_EVENT_TYPE_COMP"; + case GOLAN_EVENT_TYPE_PATH_MIG: + return "GOLAN_EVENT_TYPE_PATH_MIG"; + case GOLAN_EVENT_TYPE_COMM_EST: + return "GOLAN_EVENT_TYPE_COMM_EST"; + case GOLAN_EVENT_TYPE_SQ_DRAINED: + return "GOLAN_EVENT_TYPE_SQ_DRAINED"; + case GOLAN_EVENT_TYPE_SRQ_LAST_WQE: + return "GOLAN_EVENT_TYPE_SRQ_LAST_WQE"; + case GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT: + return "GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT"; + case GOLAN_EVENT_TYPE_CQ_ERROR: + return "GOLAN_EVENT_TYPE_CQ_ERROR"; + case GOLAN_EVENT_TYPE_WQ_CATAS_ERROR: + return "GOLAN_EVENT_TYPE_WQ_CATAS_ERROR"; + case GOLAN_EVENT_TYPE_PATH_MIG_FAILED: + return "GOLAN_EVENT_TYPE_PATH_MIG_FAILED"; + case GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR: + return "GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR"; + case GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR: + return "GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR"; + case GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR: + return "GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR"; + case GOLAN_EVENT_TYPE_INTERNAL_ERROR: + return "GOLAN_EVENT_TYPE_INTERNAL_ERROR"; + case GOLAN_EVENT_TYPE_PORT_CHANGE: + return "GOLAN_EVENT_TYPE_PORT_CHANGE"; + case GOLAN_EVENT_TYPE_GPIO_EVENT: + return "GOLAN_EVENT_TYPE_GPIO_EVENT"; + case GOLAN_EVENT_TYPE_REMOTE_CONFIG: + return "GOLAN_EVENT_TYPE_REMOTE_CONFIG"; + case GOLAN_EVENT_TYPE_DB_BF_CONGESTION: + return "GOLAN_EVENT_TYPE_DB_BF_CONGESTION"; + case GOLAN_EVENT_TYPE_STALL_EVENT: + return "GOLAN_EVENT_TYPE_STALL_EVENT"; + case GOLAN_EVENT_TYPE_CMD: + return "GOLAN_EVENT_TYPE_CMD"; + case GOLAN_EVENT_TYPE_PAGE_REQUEST: + return "GOLAN_EVENT_TYPE_PAGE_REQUEST"; + default: + return "Unrecognized event"; + } +} + +static const char *golan_eqe_port_subtype_str(u8 subtype) +{ + switch (subtype) { + case GOLAN_PORT_CHANGE_SUBTYPE_DOWN: + return "GOLAN_PORT_CHANGE_SUBTYPE_DOWN"; + case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE: + return "GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE"; + case GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED: + return "GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED"; + case GOLAN_PORT_CHANGE_SUBTYPE_LID: + return "GOLAN_PORT_CHANGE_SUBTYPE_LID"; + case GOLAN_PORT_CHANGE_SUBTYPE_PKEY: + return "GOLAN_PORT_CHANGE_SUBTYPE_PKEY"; + case GOLAN_PORT_CHANGE_SUBTYPE_GUID: + return "GOLAN_PORT_CHANGE_SUBTYPE_GUID"; + case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG: + return "GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG"; + default: + return "Unrecognized event"; + } +} + +/** + * Update Infiniband parameters using Commands + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int golan_ib_update ( struct ib_device *ibdev ) { + int rc; + + /* Get IB parameters */ + if ( ( rc = golan_get_ib_info ( ibdev ) ) != 0 ) + return rc; + + /* Notify Infiniband core of potential link state change */ + ib_link_state_changed ( ibdev ); + + return 0; +} + +static inline void golan_handle_port_event(struct golan *golan, struct golan_eqe *eqe) +{ + struct ib_device *ibdev; + u8 port; + + port = (eqe->data.port.port >> 4) & 0xf; + ibdev = golan->ports[port - 1].ibdev; + + if ( ! ib_is_open ( ibdev ) ) + return; + + switch (eqe->sub_type) { + case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG: + case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE: + golan_ib_update ( ibdev ); + case GOLAN_PORT_CHANGE_SUBTYPE_DOWN: + case GOLAN_PORT_CHANGE_SUBTYPE_LID: + case GOLAN_PORT_CHANGE_SUBTYPE_PKEY: + case GOLAN_PORT_CHANGE_SUBTYPE_GUID: + case GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED: + DBGC( golan , "%s event %s(%d) (sub event %s(%d))arrived on port %d\n", + __FUNCTION__, golan_eqe_type_str(eqe->type), eqe->type, + golan_eqe_port_subtype_str(eqe->sub_type), + eqe->sub_type, port); + break; + default: + DBGC (golan ,"%s Port event with unrecognized subtype: port %d, sub_type %d\n", + __FUNCTION__, port, eqe->sub_type); + } +} + +static struct golan_eqe *golan_next_eqe_sw(struct golan_event_queue *eq) +{ + uint32_t entry = (eq->cons_index & (GOLAN_NUM_EQES - 1)); + struct golan_eqe *eqe = &(eq->eqes[entry]); + return ((eqe->owner != ((eq->cons_index >> ilog2(GOLAN_NUM_EQES)) & 1)) ? NULL : eqe); +} + + +/** + * Poll event queue + * + * @v ibdev Infiniband device + */ +static void golan_poll_eq(struct ib_device *ibdev) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_event_queue *eq = &(golan->eq); + struct golan_eqe *eqe; + u32 cqn; + int counter = 0; + + while ((eqe = golan_next_eqe_sw(eq)) && (counter < GOLAN_NUM_EQES)) { + /* + * Make sure we read EQ entry contents after we've + * checked the ownership bit. + */ + rmb(); + + DBGC( golan , "%s eqn %d, eqe type %s\n", __FUNCTION__, eq->eqn, + golan_eqe_type_str(eqe->type)); + switch (eqe->type) { + case GOLAN_EVENT_TYPE_COMP: + /* We dont need to handle completion events since we + * poll all the CQs after polling the EQ */ + break; + case GOLAN_EVENT_TYPE_PATH_MIG: + case GOLAN_EVENT_TYPE_COMM_EST: + case GOLAN_EVENT_TYPE_SQ_DRAINED: + case GOLAN_EVENT_TYPE_SRQ_LAST_WQE: + case GOLAN_EVENT_TYPE_WQ_CATAS_ERROR: + case GOLAN_EVENT_TYPE_PATH_MIG_FAILED: + case GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR: + case GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR: + case GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT: + case GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR: + DBGC( golan , "%s event %s(%d) arrived\n", __FUNCTION__, + golan_eqe_type_str(eqe->type), eqe->type); + break; + case GOLAN_EVENT_TYPE_CMD: +// golan_cmd_comp_handler(be32_to_cpu(eqe->data.cmd.vector)); + break; + case GOLAN_EVENT_TYPE_PORT_CHANGE: + golan_handle_port_event(golan, eqe); + break; + case GOLAN_EVENT_TYPE_CQ_ERROR: + cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff; + DBGC (golan ,"CQ error on CQN 0x%x, syndrom 0x%x\n", + cqn, eqe->data.cq_err.syndrome); +// mlx5_cq_event(dev, cqn, eqe->type); + break; + case GOLAN_EVENT_TYPE_PAGE_REQUEST: + { + /* we should check if we get this event while we + * waiting for a command */ + u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id); + s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages); + + DBGC (golan ,"%s page request for func 0x%x, napges %d\n", + __FUNCTION__, func_id, npages); + golan_provide_pages(golan, npages, func_id); + } + break; + default: + DBGC (golan ,"%s Unhandled event 0x%x on EQ 0x%x\n", __FUNCTION__, + eqe->type, eq->eqn); + break; + } + + ++eq->cons_index; + golan_eq_update_ci(eq, GOLAN_EQ_UNARMED); + ++counter; + } +} + +/** + * Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static int golan_mcast_attach(struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_cmd_layout *cmd; + int rc; + + if ( qp == NULL ) { + DBGC( golan, "%s: Invalid pointer, could not attach QPN to MCG\n", + __FUNCTION__ ); + return -EFAULT; + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ATTACH_TO_MCG, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_attach_mcg_mbox_in), + sizeof(struct golan_attach_mcg_mbox_out)); + ((struct golan_attach_mcg_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + + memcpy(GET_INBOX(golan, GEN_MBOX), gid, sizeof(*gid)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_attach_to_mcg_cmd ); + + DBGC( golan , "%s: QPN 0x%lx was attached to MCG\n", __FUNCTION__, qp->qpn); + return 0; +err_attach_to_mcg_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +/** + * Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static void golan_mcast_detach(struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_cmd_layout *cmd; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DETACH_FROM_MCG, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_detach_mcg_mbox_in), + sizeof(struct golan_detach_mcg_mbox_out)); + ((struct golan_detach_mcg_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + + memcpy(GET_INBOX(golan, GEN_MBOX), gid, sizeof(*gid)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + DBGC( golan , "%s: QPN 0x%lx was detached from MCG\n", __FUNCTION__, qp->qpn); +} + +/** + * Inform embedded subnet management agent of a received MAD + * + * @v ibdev Infiniband device + * @v mad MAD + * @ret rc Return status code + */ +static int golan_inform_sma(struct ib_device *ibdev, + union ib_mad *mad) +{ + if (!ibdev || !mad) { + return 1; + } + + return 0; +} + +static int golan_register_ibdev(struct golan_port *port) +{ + struct ib_device *ibdev = port->ibdev; + int rc; + + golan_get_ib_info ( ibdev ); + /* Register Infiniband device */ + if ((rc = register_ibdev(ibdev)) != 0) { + DBG ( "%s port %d could not register IB device: (rc = %d)\n", + __FUNCTION__, ibdev->port, rc); + return rc; + } + + port->netdev = ipoib_netdev( ibdev ); + + return 0; +} + +static inline void golan_bring_down(struct golan *golan) +{ + + DBGC(golan, "%s: start\n", __FUNCTION__); + + if (~golan->flags & GOLAN_OPEN) { + DBGC(golan, "%s: end (already closed)\n", __FUNCTION__); + return; + } + + golan_destroy_mkey(golan); + golan_dealloc_pd(golan); + golan_destory_eq(golan); + golan_dealloc_uar(golan); + golan_teardown_hca(golan, GOLAN_TEARDOWN_GRACEFUL); + golan_handle_pages(golan, GOLAN_REG_PAGES , GOLAN_PAGES_TAKE); + golan_disable_hca(golan); + golan_cmd_uninit(golan); + golan->flags &= ~GOLAN_OPEN; + DBGC(golan, "%s: end\n", __FUNCTION__); +} + +static int golan_set_link_speed ( struct golan *golan ){ + mlx_utils utils; + mlx_status status; + int i = 0; + + memset ( &utils, 0, sizeof ( utils ) ); + + status = mlx_utils_init ( &utils, golan->pci ); + MLX_CHECK_STATUS ( golan->pci, status, utils_init_err, "mlx_utils_init failed" ); + + status = mlx_pci_gw_init ( &utils ); + MLX_CHECK_STATUS ( golan->pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" ); + + for ( i = 0; i < golan->caps.num_ports; ++i ) { + status = mlx_set_link_speed( &utils, i + 1, LINK_SPEED_IB, LINK_SPEED_SDR ); + MLX_CHECK_STATUS ( golan->pci, status, set_link_speed_err, "mlx_set_link_speed failed" ); + } + +set_link_speed_err: + mlx_pci_gw_teardown( &utils ); +pci_gw_init_err: +utils_init_err: + return status; +} + +static inline int golan_bring_up(struct golan *golan) +{ + int rc = 0; + DBGC(golan, "%s\n", __FUNCTION__); + + if (golan->flags & GOLAN_OPEN) + return 0; + + if (( rc = golan_cmd_init(golan) )) + goto out; + + if (( rc = golan_core_enable_hca(golan) )) + goto cmd_uninit; + + /* Query for need for boot pages */ + if (( rc = golan_handle_pages(golan, GOLAN_BOOT_PAGES, GOLAN_PAGES_GIVE) )) + goto disable; + + if (( rc = golan_qry_hca_cap(golan) )) + goto pages; + + if (( rc = golan_set_hca_cap(golan) )) + goto pages; + + if (( rc = golan_handle_pages(golan, GOLAN_INIT_PAGES, GOLAN_PAGES_GIVE) )) + goto pages; + + if (( rc = golan_set_link_speed ( golan ) )) + goto pages_teardown; + + //Reg Init? + if (( rc = golan_hca_init(golan) )) + goto pages_2; + + if (( rc = golan_alloc_uar(golan) )) + goto teardown; + + if (( rc = golan_create_eq(golan) )) + goto de_uar; + + if (( rc = golan_alloc_pd(golan) )) + goto de_eq; + + if (( rc = golan_create_mkey(golan) )) + goto de_pd; + + golan->flags |= GOLAN_OPEN; + return 0; + + golan_destroy_mkey(golan); +de_pd: + golan_dealloc_pd(golan); +de_eq: + golan_destory_eq(golan); +de_uar: + golan_dealloc_uar(golan); +teardown: + golan_teardown_hca(golan, GOLAN_TEARDOWN_GRACEFUL); +pages_2: +pages_teardown: + golan_handle_pages(golan, GOLAN_INIT_PAGES, GOLAN_PAGES_TAKE); +pages: + golan_handle_pages(golan, GOLAN_BOOT_PAGES, GOLAN_PAGES_TAKE); +disable: + golan_disable_hca(golan); +cmd_uninit: + golan_cmd_uninit(golan); +out: + return rc; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void golan_ib_close ( struct ib_device *ibdev __unused ) {} + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int golan_ib_open ( struct ib_device *ibdev ) { + DBG ( "%s start\n", __FUNCTION__ ); + + if ( ! ibdev ) + return -EINVAL; + + golan_ib_update ( ibdev ); + + DBG ( "%s end\n", __FUNCTION__ ); + return 0; +} + +/** Golan Infiniband operations */ +static struct ib_device_operations golan_ib_operations = { + .create_cq = golan_create_cq, + .destroy_cq = golan_destroy_cq, + .create_qp = golan_create_qp, + .modify_qp = golan_modify_qp, + .destroy_qp = golan_destroy_qp, + .post_send = golan_post_send, + .post_recv = golan_post_recv, + .poll_cq = golan_poll_cq, + .poll_eq = golan_poll_eq, + .open = golan_ib_open, + .close = golan_ib_close, + .mcast_attach = golan_mcast_attach, + .mcast_detach = golan_mcast_detach, + .set_port_info = golan_inform_sma, + .set_pkey_table = golan_inform_sma, +}; + +static int golan_probe_normal ( struct pci_device *pci ) { + struct golan *golan; + struct ib_device *ibdev; + struct golan_port *port; + int i; + int rc = 0; + + golan = golan_alloc(); + if ( !golan ) { + rc = -ENOMEM; + goto err_golan_alloc; + } + + if ( golan_init_pages( &golan->pages ) ) { + rc = -ENOMEM; + goto err_golan_golan_init_pages; + } + + /* Setup PCI bus and HCA BAR */ + pci_set_drvdata( pci, golan ); + golan->pci = pci; + golan_pci_init( golan ); + /* config command queues */ + if ( fw_ver_and_cmdif( golan ) ) { + rc = -1; + goto err_fw_ver_cmdif; + } + + if ( golan_bring_up( golan ) ) { + DBGC (golan ,"golan bringup failed\n"); + rc = -1; + goto err_golan_bringup; + } + + /* Allocate Infiniband devices */ + for (i = 0; i < golan->caps.num_ports; ++i) { + ibdev = alloc_ibdev( 0 ); + if ( !ibdev ) { + rc = -ENOMEM; + goto err_golan_probe_alloc_ibdev; + } + golan->ports[i].ibdev = ibdev; + golan->ports[i].vep_number = 0; + ibdev->op = &golan_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = (GOLAN_PORT_BASE + i); + ib_set_drvdata( ibdev, golan ); + } + + /* Register devices */ + for ( i = 0; i < golan->caps.num_ports; ++i ) { + port = &golan->ports[i]; + if ((rc = golan_register_ibdev ( port ) ) != 0 ) + goto err_golan_probe_register_ibdev; + } + + return 0; + + i = golan->caps.num_ports; +err_golan_probe_register_ibdev: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) + unregister_ibdev ( golan->ports[i].ibdev ); + + i = golan->caps.num_ports; +err_golan_probe_alloc_ibdev: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) + ibdev_put ( golan->ports[i].ibdev ); + + golan_bring_down ( golan ); +err_golan_bringup: +err_fw_ver_cmdif: + golan_free_pages( &golan->pages ); +err_golan_golan_init_pages: + free ( golan ); +err_golan_alloc: + DBGC (golan ,"%s rc = %d\n", __FUNCTION__, rc); + return rc; +} + +static void golan_remove_normal ( struct pci_device *pci ) { + struct golan *golan = pci_get_drvdata(pci); + struct golan_port *port; + int i; + + DBGC(golan, "%s\n", __FUNCTION__); + + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + port = &golan->ports[i]; + unregister_ibdev ( port->ibdev ); + } + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + netdev_nullify ( golan->ports[i].netdev ); + netdev_put ( golan->ports[i].netdev ); + } + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + ibdev_put ( golan->ports[i].ibdev ); + } + + golan_bring_down(golan); + + golan_free_pages( &golan->pages ); + free(golan); +} + +/*************************************************************************** + * NODNIC operations + **************************************************************************/ +static mlx_status shomron_fill_eth_send_wqe ( struct ib_device *ibdev, + struct ib_queue_pair *qp, struct ib_address_vector *av __unused, + struct io_buffer *iobuf, struct nodnic_send_wqbb *wqbb, + unsigned long wqe_index ) { + mlx_status status = MLX_SUCCESS; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct shomron_nodnic_eth_send_wqe *eth_wqe = NULL; + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = + ib_qp_get_drvdata ( qp ); + nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; + struct nodnic_send_ring *send_ring = &nodnic_qp->send; + mlx_uint32 qpn = 0; + + eth_wqe = (struct shomron_nodnic_eth_send_wqe *)wqbb; + memset ( ( ( ( void * ) eth_wqe ) ), 0, + ( sizeof ( *eth_wqe ) ) ); + + status = nodnic_port_get_qpn(&port->port_priv, &send_ring->nodnic_ring, + &qpn); + if ( status != MLX_SUCCESS ) { + DBG("nodnic_port_get_qpn failed\n"); + goto err; + } + +#define SHOMRON_GENERATE_CQE 0x3 +#define SHOMRON_INLINE_HEADERS_SIZE 18 +#define SHOMRON_INLINE_HEADERS_OFFSET 32 + MLX_FILL_2 ( ð_wqe->ctrl, 0, opcode, FLEXBOOT_NODNIC_OPCODE_SEND, + wqe_index, wqe_index & 0xFFFF); + MLX_FILL_2 ( ð_wqe->ctrl, 1, ds, 0x4 , qpn, qpn ); + MLX_FILL_1 ( ð_wqe->ctrl, 2, + ce, SHOMRON_GENERATE_CQE /* generate completion */ + ); + MLX_FILL_2 ( ð_wqe->ctrl, 7, + inline_headers1, + cpu_to_be16(*(mlx_uint16 *)iobuf->data), + inline_headers_size, SHOMRON_INLINE_HEADERS_SIZE + ); + memcpy((void *)ð_wqe->ctrl + SHOMRON_INLINE_HEADERS_OFFSET, + iobuf->data + 2, SHOMRON_INLINE_HEADERS_SIZE - 2); + iob_pull(iobuf, SHOMRON_INLINE_HEADERS_SIZE); + MLX_FILL_1 ( ð_wqe->data[0], 0, + byte_count, iob_len ( iobuf ) ); + MLX_FILL_1 ( ð_wqe->data[0], 1, l_key, + flexboot_nodnic->device_priv.lkey ); + MLX_FILL_H ( ð_wqe->data[0], 2, + local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( ð_wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); +err: + return status; +} + +static mlx_status shomron_fill_completion( void *cqe, struct cqe_data *cqe_data ) { + union shomronprm_completion_entry *cq_entry; + uint32_t opcode; + + cq_entry = (union shomronprm_completion_entry *)cqe; + cqe_data->owner = MLX_GET ( &cq_entry->normal, owner ); + opcode = MLX_GET ( &cq_entry->normal, opcode ); +#define FLEXBOOT_NODNIC_OPCODE_CQ_SEND 0 +#define FLEXBOOT_NODNIC_OPCODE_CQ_RECV 2 +#define FLEXBOOT_NODNIC_OPCODE_CQ_SEND_ERR 13 +#define FLEXBOOT_NODNIC_OPCODE_CQ_RECV_ERR 14 + cqe_data->is_error = + ( opcode >= FLEXBOOT_NODNIC_OPCODE_CQ_RECV_ERR); + if ( cqe_data->is_error ) { + cqe_data->syndrome = MLX_GET ( &cq_entry->error, syndrome ); + cqe_data->vendor_err_syndrome = + MLX_GET ( &cq_entry->error, vendor_error_syndrome ); + cqe_data->is_send = + (opcode == FLEXBOOT_NODNIC_OPCODE_CQ_SEND_ERR); + } else { + cqe_data->is_send = + (opcode == FLEXBOOT_NODNIC_OPCODE_CQ_SEND); + cqe_data->wqe_counter = MLX_GET ( &cq_entry->normal, wqe_counter ); + cqe_data->byte_cnt = MLX_GET ( &cq_entry->normal, byte_cnt ); + + } + if ( cqe_data->is_send == TRUE ) + cqe_data->qpn = MLX_GET ( &cq_entry->normal, qpn ); + else + cqe_data->qpn = MLX_GET ( &cq_entry->normal, srqn ); + + return 0; +} + +static mlx_status shomron_cqe_set_owner ( void *cq, unsigned int num_cqes ) { + unsigned int i = 0; + union shomronprm_completion_entry *cq_list; + + cq_list = (union shomronprm_completion_entry *)cq; + for ( ; i < num_cqes ; i++ ) + MLX_FILL_1 ( &cq_list[i].normal, 15, owner, 1 ); + return 0; +} + +static mlx_size shomron_get_cqe_size () { + return sizeof ( union shomronprm_completion_entry ); +} + +struct flexboot_nodnic_callbacks shomron_nodnic_callbacks = { + .get_cqe_size = shomron_get_cqe_size, + .fill_send_wqe[IB_QPT_ETH] = shomron_fill_eth_send_wqe, + .fill_completion = shomron_fill_completion, + .cqe_set_owner = shomron_cqe_set_owner, + .irq = flexboot_nodnic_eth_irq, +}; + +static int shomron_nodnic_supported = 0; + +static int shomron_nodnic_is_supported ( struct pci_device *pci ) { + if ( pci->device == 0x1011 ) + return 0; + + return flexboot_nodnic_is_supported ( pci ); +} +/**************************************************************************/ + +static int golan_probe ( struct pci_device *pci ) { + int rc = -ENOTSUP; + + DBG ( "%s: start\n", __FUNCTION__ ); + + if ( ! pci ) { + DBG ( "%s: PCI is NULL\n", __FUNCTION__ ); + rc = -EINVAL; + goto probe_done; + } + + shomron_nodnic_supported = shomron_nodnic_is_supported ( pci ); + if ( shomron_nodnic_supported ) { + rc = flexboot_nodnic_probe ( pci, &shomron_nodnic_callbacks, NULL ); + if ( rc == 0 ) { + DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ ); + goto probe_done; + } + shomron_nodnic_supported = 0; + } + + if ( ! shomron_nodnic_supported ) { + DBG ( "%s: Using normal driver\n", __FUNCTION__ ); + rc = golan_probe_normal ( pci ); + } + +probe_done: + DBG ( "%s: rc = %d\n", __FUNCTION__, rc ); + return rc; +} + +static void golan_remove ( struct pci_device *pci ) { + DBG ( "%s: start\n", __FUNCTION__ ); + + if ( ! shomron_nodnic_supported ) { + DBG ( "%s: Using normal driver remove\n", __FUNCTION__ ); + golan_remove_normal ( pci ); + return; + } + + DBG ( "%s: Using NODNIC driver remove\n", __FUNCTION__ ); + + flexboot_nodnic_remove ( pci ); + + DBG ( "%s: end\n", __FUNCTION__ ); +} + +static struct pci_device_id golan_nics[] = { + PCI_ROM ( 0x15b3, 0x1011, "ConnectIB", "ConnectIB HCA driver: DevID 4113", 0 ), + PCI_ROM ( 0x15b3, 0x1013, "ConnectX-4", "ConnectX-4 HCA driver, DevID 4115", 0 ), + PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ), +}; + +struct pci_driver golan_driver __pci_driver = { + .ids = golan_nics, + .id_count = (sizeof(golan_nics) / sizeof(golan_nics[0])), + .probe = golan_probe, + .remove = golan_remove, +}; diff --git a/src/drivers/infiniband/golan.h b/src/drivers/infiniband/golan.h new file mode 100755 index 000000000..6e96f7508 --- /dev/null +++ b/src/drivers/infiniband/golan.h @@ -0,0 +1,319 @@ +#ifndef _GOLAN_H_ +#define _GOLAN_H_ + +/* + * Copyright (C) 2013-2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include "CIB_PRM.h" + +#define GOLAN_PCI_CONFIG_BAR_SIZE 0x100000//HERMON_PCI_CONFIG_BAR_SIZE //TODO: What is the BAR size? + +#define GOLAN_PAS_SIZE sizeof(uint64_t) + +#define GOLAN_INVALID_LKEY 0x00000100UL + +#define GOLAN_MAX_PORTS 2 +#define GOLAN_PORT_BASE 1 + +#define MELLANOX_VID 0x15b3 +#define GOLAN_HCA_BAR PCI_BASE_ADDRESS_0 //BAR 0 + +#define GOLAN_HCR_MAX_WAIT_MS 10000 + +#define min(a,b) ((a)<(b)?(a):(b)) + +#define GOLAN_PAGE_SHIFT 12 +#define GOLAN_PAGE_SIZE (1 << GOLAN_PAGE_SHIFT) +#define GOLAN_PAGE_MASK (GOLAN_PAGE_SIZE - 1) + +#define MAX_MBOX ( GOLAN_PAGE_SIZE / MAILBOX_STRIDE ) +#define DEF_CMD_IDX 1 +#define MEM_CMD_IDX 0 +#define NO_MBOX 0xffff +#define MEM_MBOX MEM_CMD_IDX +#define GEN_MBOX DEF_CMD_IDX + +#define CMD_IF_REV 4 + +#define MAX_PASE_MBOX ((GOLAN_CMD_PAS_CNT) - 2) + +#define CMD_STATUS( golan , idx ) ((struct golan_outbox_hdr *)(get_cmd( (golan) , (idx) )->out))->status +#define CMD_SYND( golan , idx ) ((struct golan_outbox_hdr *)(get_cmd( (golan) , (idx) )->out))->syndrome +#define QRY_PAGES_OUT( golan, idx ) ((struct golan_query_pages_outbox *)(get_cmd( (golan) , (idx) )->out)) + +#define VIRT_2_BE64_BUS( addr ) cpu_to_be64(((unsigned long long )virt_to_bus(addr))) +#define BE64_BUS_2_VIRT( addr ) bus_to_virt(be64_to_cpu(addr)) +#define USR_2_BE64_BUS( addr ) cpu_to_be64(((unsigned long long )user_to_phys(addr, 0))) +#define BE64_BUS_2_USR( addr ) be64_to_cpu(phys_to_user(addr)) + +#define GET_INBOX(golan, idx) (&(((struct mbox *)(golan->mboxes.inbox))[idx])) +#define GET_OUTBOX(golan, idx) (&(((struct mbox *)(golan->mboxes.outbox))[idx])) + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +/* Fw status fields */ +typedef enum { + NO_ERRORS = 0x0, + SIGNATURE_ERROR = 0x1, + TOKEN_ERROR = 0x2, + BAD_BLOCK_NUMBER = 0x3, + BAD_OUTPUT_POINTER = 0x4, // pointer not align to mailbox size + BAD_INPUT_POINTER = 0x5, // pointer not align to mailbox size + INTERNAL_ERROR = 0x6, + INPUT_LEN_ERROR = 0x7, // input length less than 0x8. + OUTPUT_LEN_ERROR = 0x8, // output length less than 0x8. + RESERVE_NOT_ZERO = 0x9, + BAD_CMD_TYPE = 0x10, +} return_hdr_t; + +struct golan_cmdq_md { + void *addr; + u16 log_stride; + u16 size; +}; + +struct golan_uar { + uint32_t index; + void *virt; + unsigned long phys; +}; + +/* Queue Pair */ +#define GOLAN_SEND_WQE_BB_SIZE 64 +#define GOLAN_SEND_UD_WQE_SIZE sizeof(struct golan_send_wqe_ud) +#define GOLAN_RECV_WQE_SIZE sizeof(struct golan_recv_wqe_ud) +#define GOLAN_WQEBBS_PER_SEND_UD_WQE DIV_ROUND_UP(GOLAN_SEND_UD_WQE_SIZE, GOLAN_SEND_WQE_BB_SIZE) +#define GOLAN_SEND_OPCODE 0x0a +#define GOLAN_WQE_CTRL_WQE_IDX_BIT 8 + +enum golan_ib_qp_state { + GOLAN_IB_QPS_RESET, + GOLAN_IB_QPS_INIT, + GOLAN_IB_QPS_RTR, + GOLAN_IB_QPS_RTS, + GOLAN_IB_QPS_SQD, + GOLAN_IB_QPS_SQE, + GOLAN_IB_QPS_ERR +}; + +struct golan_send_wqe_ud { + struct golan_wqe_ctrl_seg ctrl; + struct golan_av datagram; + struct golan_wqe_data_seg data; +}; + +union golan_send_wqe { + struct golan_send_wqe_ud ud; + uint8_t pad[GOLAN_WQEBBS_PER_SEND_UD_WQE * GOLAN_SEND_WQE_BB_SIZE]; +}; + +struct golan_recv_wqe_ud { + struct golan_wqe_data_seg data[2]; +}; + +struct golan_recv_wq { + struct golan_recv_wqe_ud *wqes; + /* WQ size in bytes */ + int size; + /* In SQ, it will be increased in wqe_size (number of WQEBBs per WQE) */ + u16 next_idx; + /** GRH buffers (if applicable) */ + struct ib_global_route_header *grh; + /** Size of GRH buffers */ + size_t grh_size; +}; + +struct golan_send_wq { + union golan_send_wqe *wqes; + /* WQ size in bytes */ + int size; + /* In SQ, it will be increased in wqe_size (number of WQEBBs per WQE) */ + u16 next_idx; +}; + +struct golan_queue_pair { + void *wqes; + int size; + struct golan_recv_wq rq; + struct golan_send_wq sq; + struct golan_qp_db *doorbell_record; + u32 doorbell_qpn; + enum golan_ib_qp_state state; +}; + +/* Completion Queue */ +#define GOLAN_CQE_OPCODE_NOT_VALID 0x0f +#define GOLAN_CQE_OPCODE_BIT 4 +#define GOLAN_CQ_DB_RECORD_SIZE sizeof(uint64_t) +#define GOLAN_CQE_OWNER_MASK 1 + +#define MANAGE_PAGES_PSA_OFFSET 0 +#define PXE_CMDIF_REF 5 + +enum { + GOLAN_CQE_SW_OWNERSHIP = 0x0, + GOLAN_CQE_HW_OWNERSHIP = 0x1 +}; + +enum { + GOLAN_CQE_SIZE_64 = 0, + GOLAN_CQE_SIZE_128 = 1 +}; + +struct golan_completion_queue { + struct golan_cqe64 *cqes; + int size; + __be64 *doorbell_record; +}; + + +/* Event Queue */ +#define GOLAN_EQE_SIZE sizeof(struct golan_eqe) +#define GOLAN_NUM_EQES 8 +#define GOLAN_EQ_DOORBELL_OFFSET 0x40 + +#define GOLAN_EQ_MAP_ALL_EVENTS \ + ((1 << GOLAN_EVENT_TYPE_PATH_MIG )| \ + (1 << GOLAN_EVENT_TYPE_COMM_EST )| \ + (1 << GOLAN_EVENT_TYPE_SQ_DRAINED )| \ + (1 << GOLAN_EVENT_TYPE_SRQ_LAST_WQE )| \ + (1 << GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT )| \ + (1 << GOLAN_EVENT_TYPE_CQ_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_WQ_CATAS_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_PATH_MIG_FAILED )| \ + (1 << GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_INTERNAL_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_PORT_CHANGE )| \ + (1 << GOLAN_EVENT_TYPE_GPIO_EVENT )| \ + (1 << GOLAN_EVENT_TYPE_CLIENT_RE_REGISTER )| \ + (1 << GOLAN_EVENT_TYPE_REMOTE_CONFIG )| \ + (1 << GOLAN_EVENT_TYPE_DB_BF_CONGESTION )| \ + (1 << GOLAN_EVENT_TYPE_STALL_EVENT )| \ + (1 << GOLAN_EVENT_TYPE_PACKET_DROPPED )| \ + (1 << GOLAN_EVENT_TYPE_CMD )| \ + (1 << GOLAN_EVENT_TYPE_PAGE_REQUEST )) + +enum golan_event { + GOLAN_EVENT_TYPE_COMP = 0x0, + + GOLAN_EVENT_TYPE_PATH_MIG = 0x01, + GOLAN_EVENT_TYPE_COMM_EST = 0x02, + GOLAN_EVENT_TYPE_SQ_DRAINED = 0x03, + GOLAN_EVENT_TYPE_SRQ_LAST_WQE = 0x13, + GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT = 0x14, + + GOLAN_EVENT_TYPE_CQ_ERROR = 0x04, + GOLAN_EVENT_TYPE_WQ_CATAS_ERROR = 0x05, + GOLAN_EVENT_TYPE_PATH_MIG_FAILED = 0x07, + GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR = 0x10, + GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR = 0x11, + GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR = 0x12, + + GOLAN_EVENT_TYPE_INTERNAL_ERROR = 0x08, + GOLAN_EVENT_TYPE_PORT_CHANGE = 0x09, + GOLAN_EVENT_TYPE_GPIO_EVENT = 0x15, +// GOLAN_EVENT_TYPE_CLIENT_RE_REGISTER = 0x16, + GOLAN_EVENT_TYPE_REMOTE_CONFIG = 0x19, + + GOLAN_EVENT_TYPE_DB_BF_CONGESTION = 0x1a, + GOLAN_EVENT_TYPE_STALL_EVENT = 0x1b, + + GOLAN_EVENT_TYPE_PACKET_DROPPED = 0x1f, + + GOLAN_EVENT_TYPE_CMD = 0x0a, + GOLAN_EVENT_TYPE_PAGE_REQUEST = 0x0b, + GOLAN_EVENT_TYPE_PAGE_FAULT = 0x0C, +}; + +enum golan_port_sub_event { + GOLAN_PORT_CHANGE_SUBTYPE_DOWN = 1, + GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE = 4, + GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED = 5, + GOLAN_PORT_CHANGE_SUBTYPE_LID = 6, + GOLAN_PORT_CHANGE_SUBTYPE_PKEY = 7, + GOLAN_PORT_CHANGE_SUBTYPE_GUID = 8, + GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG = 9 +}; + + +enum { + GOLAN_EQE_SW_OWNERSHIP = 0x0, + GOLAN_EQE_HW_OWNERSHIP = 0x1 +}; + +enum { + GOLAN_EQ_UNARMED = 0, + GOLAN_EQ_ARMED = 1, +}; + +struct golan_event_queue { + uint8_t eqn; + uint64_t mask; + struct golan_eqe *eqes; + int size; + __be32 *doorbell; + uint32_t cons_index; +}; + +struct golan_port { + /** Infiniband device */ + struct ib_device *ibdev; + /** Network device */ + struct net_device *netdev; + /** VEP number */ + u8 vep_number; +}; + +struct golan_mboxes { + void *inbox; + void *outbox; +}; + +#define GOLAN_OPEN 0x1 + +struct golan { + struct pci_device *pci; + struct golan_hca_init_seg *iseg; + struct golan_cmdq_md cmd; + struct golan_hca_cap caps; /* stored as big indian*/ + struct golan_mboxes mboxes; + struct list_head pages; + uint32_t cmd_bm; + uint32_t total_dma_pages; + struct golan_uar uar; + struct golan_event_queue eq; + uint32_t pdn; + u32 mkey; + u32 flags; + + struct golan_port ports[GOLAN_MAX_PORTS]; +}; + +#endif /* _GOLAN_H_*/ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h new file mode 100644 index 000000000..e1e89b4c3 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h @@ -0,0 +1,43 @@ +#ifndef NODNIC_CMD_H_ +#define NODNIC_CMD_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_nodnic_data_structures.h" +#include "../../mlx_utils/include/public/mlx_utils.h" +#include "../../mlx_utils/include/public/mlx_pci_gw.h" + +mlx_status +nodnic_cmd_read( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ); + +mlx_status +nodnic_cmd_write( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ); + +#endif /* STUB_NODNIC_CMD_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_device.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_device.h new file mode 100644 index 000000000..b0cc7f723 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_device.h @@ -0,0 +1,80 @@ +#ifndef NODNIC_DEVICE_H_ +#define NODNIC_DEVICE_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_nodnic_data_structures.h" + +#define NODIC_SUPPORTED_REVISION 1 +//Initialization segment +#define NODNIC_CMDQ_PHY_ADDR_HIGH_OFFSET 0x10 +#define NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET 0x14 +#define NODNIC_NIC_INTERFACE_OFFSET 0x14 +#define NODNIC_INITIALIZING_OFFSET 0x1fc +#define NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET 0x1fc +#define NODNIC_LOCATION_OFFSET 0x240 + +#define NODNIC_CMDQ_PHY_ADDR_LOW_MASK 0xFFFFE000 +#define NODNIC_NIC_INTERFACE_SUPPORTED_MASK 0x4000000 + +#define NODNIC_NIC_INTERFACE_BIT 9 +#define NODNIC_DISABLE_INTERFACE_BIT 8 +#define NODNIC_NIC_INTERFACE_SUPPORTED_BIT 26 +#define NODNIC_INITIALIZING_BIT 31 + +#define NODNIC_NIC_DISABLE_INT_OFFSET 0x100c + +//nodnic segment +#define NODNIC_REVISION_OFFSET 0x0 +#define NODNIC_HARDWARE_FORMAT_OFFSET 0x0 + + + +mlx_status +nodnic_device_init( + IN nodnic_device_priv *device_priv + ); + +mlx_status +nodnic_device_teardown( + IN nodnic_device_priv *device_priv + ); + + +mlx_status +nodnic_device_get_cap( + IN nodnic_device_priv *device_priv + ); + +mlx_status +nodnic_device_clear_int ( + IN nodnic_device_priv *device_priv + ); + +mlx_status +nodnic_device_get_fw_version( + IN nodnic_device_priv *device_priv, + OUT mlx_uint16 *fw_ver_minor, + OUT mlx_uint16 *fw_ver_sub_minor, + OUT mlx_uint16 *fw_ver_major + ); +#endif /* STUB_NODNIC_DEVICE_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h new file mode 100644 index 000000000..f58213b98 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h @@ -0,0 +1,201 @@ +#ifndef NODNIC_NODNICDATASTRUCTURES_H_ +#define NODNIC_NODNICDATASTRUCTURES_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_utils/include/public/mlx_utils.h" + +/* todo: fix coding convention */ +#define NODNIC_MEMORY_ALIGN 0x1000 + +#define NODNIC_MAX_MAC_FILTERS 5 +#define NODNIC_MAX_MGID_FILTERS 4 + +typedef struct _nodnic_device_priv nodnic_device_priv; +typedef struct _nodnic_port_priv nodnic_port_priv; +typedef struct _nodnic_device_capabilites nodnic_device_capabilites; +typedef struct _nodnic_qp nodnic_qp; +typedef struct _nodnic_cq nodnic_cq; +typedef struct _nodnic_eq nodnic_eq; + +/* NODNIC Port states + * Bit 0 - port open/close + * Bit 1 - port is [not] in disabling DMA + * 0 - closed and not disabling DMA + * 1 - opened and not disabling DMA + * 3 - opened and disabling DMA + */ +#define NODNIC_PORT_OPENED 0b00000001 +#define NODNIC_PORT_DISABLING_DMA 0b00000010 + +typedef enum { + ConnectX3 = 0, + Connectx4 +}nodnic_hardware_format; + + +typedef enum { + NODNIC_QPT_SMI, + NODNIC_QPT_GSI, + NODNIC_QPT_UD, + NODNIC_QPT_RC, + NODNIC_QPT_ETH, +}nodnic_queue_pair_type; +typedef enum { + NODNIC_PORT_TYPE_IB = 0, + NODNIC_PORT_TYPE_ETH, + NODNIC_PORT_TYPE_UNKNOWN, +}nodnic_port_type; + + +#define RECV_WQE_SIZE 16 +#define NODNIC_WQBB_SIZE 64 +/** A nodnic send wqbb */ +struct nodnic_send_wqbb { + mlx_uint8 force_align[NODNIC_WQBB_SIZE]; +}; +struct nodnic_ring { + mlx_uint32 offset; + /** Work queue entries */ + /* TODO: add to memory entity */ + mlx_physical_address wqe_physical; + mlx_void *map; + /** Size of work queue */ + mlx_size wq_size; + /** Next work queue entry index + * + * This is the index of the next entry to be filled (i.e. the + * first empty entry). This value is not bounded by num_wqes; + * users must logical-AND with (num_wqes-1) to generate an + * array index. + */ + mlx_uint32 num_wqes; + mlx_uint32 qpn; + mlx_uint32 next_idx; + mlx_uint32 ring_pi; +}; + +struct nodnic_send_ring{ + struct nodnic_ring nodnic_ring; + struct nodnic_send_wqbb *wqe_virt; +}; + + +struct nodnic_recv_ring{ + struct nodnic_ring nodnic_ring; + void *wqe_virt; +}; +struct _nodnic_qp{ + nodnic_queue_pair_type type; + struct nodnic_send_ring send; + struct nodnic_recv_ring receive; +}; + +struct _nodnic_cq{ + /** cq entries */ + mlx_void *cq_virt; + mlx_physical_address cq_physical; + mlx_void *map; + /** cq */ + mlx_size cq_size; +}; + +struct _nodnic_eq{ + mlx_void *eq_virt; + mlx_physical_address eq_physical; + mlx_void *map; + mlx_size eq_size; +}; +struct _nodnic_device_capabilites{ + mlx_boolean support_mac_filters; + mlx_boolean support_promisc_filter; + mlx_boolean support_promisc_multicast_filter; + mlx_uint8 log_working_buffer_size; + mlx_uint8 log_pkey_table_size; + mlx_boolean num_ports; // 0 - single port, 1 - dual port + mlx_uint8 log_max_ring_size; +#ifdef DEVICE_CX3 + mlx_uint8 crspace_doorbells; +#endif +}; + +#ifdef DEVICE_CX3 +/* This is the structure of the data in the scratchpad + * Read/Write data from/to its field using PCI accesses only */ +typedef struct _nodnic_port_data_flow_gw nodnic_port_data_flow_gw; +struct _nodnic_port_data_flow_gw { + mlx_uint32 send_doorbell; + mlx_uint32 recv_doorbell; + mlx_uint32 reserved2[2]; + mlx_uint32 armcq_cq_ci_dword; + mlx_uint32 dma_en; +} __attribute__ ((packed)); +#endif + +struct _nodnic_device_priv{ + mlx_boolean is_initiailzied; + mlx_utils *utils; + + //nodnic structure offset in init segment + mlx_uint32 device_offset; + + nodnic_device_capabilites device_cap; + + mlx_uint8 nodnic_revision; + nodnic_hardware_format hardware_format; + mlx_uint32 pd; + mlx_uint32 lkey; + mlx_uint64 device_guid; + nodnic_port_priv *ports; +#ifdef DEVICE_CX3 + mlx_void *crspace_clear_int; +#endif +}; + +struct _nodnic_port_priv{ + nodnic_device_priv *device; + mlx_uint32 port_offset; + mlx_uint8 port_state; + mlx_boolean network_state; + mlx_boolean dma_state; + nodnic_port_type port_type; + mlx_uint8 port_num; + nodnic_eq eq; + mlx_mac_address mac_filters[5]; + mlx_status (*send_doorbell)( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index); + mlx_status (*recv_doorbell)( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index); + mlx_status (*set_dma)( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value); +#ifdef DEVICE_CX3 + nodnic_port_data_flow_gw *data_flow_gw; +#endif +}; + + +#endif /* STUB_NODNIC_NODNICDATASTRUCTURES_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h new file mode 100644 index 000000000..4fd96a6da --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h @@ -0,0 +1,229 @@ +#ifndef NODNIC_PORT_H_ +#define NODNIC_PORT_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_nodnic_data_structures.h" + +#define NODNIC_PORT_MAC_FILTERS_OFFSET 0x10 + +typedef enum { + nodnic_port_option_link_type = 0, + nodnic_port_option_mac_low, + nodnic_port_option_mac_high, + nodnic_port_option_log_cq_size, + nodnic_port_option_reset_needed, + nodnic_port_option_mac_filters_en, + nodnic_port_option_port_state, + nodnic_port_option_network_en, + nodnic_port_option_dma_en, + nodnic_port_option_eq_addr_low, + nodnic_port_option_eq_addr_high, + nodnic_port_option_cq_addr_low, + nodnic_port_option_cq_addr_high, + nodnic_port_option_port_management_change_event, + nodnic_port_option_port_promisc_en, + nodnic_port_option_arm_cq, + nodnic_port_option_port_promisc_multicast_en, +#ifdef DEVICE_CX3 + nodnic_port_option_crspace_en, +#endif +}nodnic_port_option; + +struct nodnic_port_data_entry{ + nodnic_port_option option; + mlx_uint32 offset; + mlx_uint8 align; + mlx_uint32 mask; +}; + +struct nodnic_qp_data_entry{ + nodnic_queue_pair_type type; + mlx_uint32 send_offset; + mlx_uint32 recv_offset; +}; + + +typedef enum { + nodnic_port_state_down = 0, + nodnic_port_state_initialize, + nodnic_port_state_armed, + nodnic_port_state_active, +}nodnic_port_state; + +mlx_status +nodnic_port_get_state( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_state *state + ); + +mlx_status +nodnic_port_get_type( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_type *type + ); + +mlx_status +nodnic_port_query( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + OUT mlx_uint32 *out + ); + +mlx_status +nodnic_port_set( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + IN mlx_uint32 in + ); + +mlx_status +nodnic_port_create_cq( + IN nodnic_port_priv *port_priv, + IN mlx_size cq_size, + OUT nodnic_cq **cq + ); + +mlx_status +nodnic_port_destroy_cq( + IN nodnic_port_priv *port_priv, + IN nodnic_cq *cq + ); + +mlx_status +nodnic_port_create_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type, + IN mlx_size send_wq_size, + IN mlx_uint32 send_wqe_num, + IN mlx_size receive_wq_size, + IN mlx_uint32 recv_wqe_num, + OUT nodnic_qp **qp + ); + +mlx_status +nodnic_port_destroy_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type, + IN nodnic_qp *qp + ); +mlx_status +nodnic_port_get_qpn( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + OUT mlx_uint32 *qpn + ); +mlx_status +nodnic_port_update_ring_doorbell( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ); +mlx_status +nodnic_port_get_cq_size( + IN nodnic_port_priv *port_priv, + OUT mlx_uint64 *cq_size + ); + +mlx_status +nodnic_port_allocate_eq( + IN nodnic_port_priv *port_priv, + IN mlx_uint8 log_eq_size + ); +mlx_status +nodnic_port_free_eq( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_add_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); + +mlx_status +nodnic_port_remove_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); +mlx_status +nodnic_port_add_mgid_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); + +mlx_status +nodnic_port_remove_mgid_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); +mlx_status +nodnic_port_thin_init( + IN nodnic_device_priv *device_priv, + IN nodnic_port_priv *port_priv, + IN mlx_uint8 port_index + ); + +mlx_status +nodnic_port_set_promisc( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ); + +mlx_status +nodnic_port_set_promisc_multicast( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ); + +mlx_status +nodnic_port_init( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_close( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_enable_dma( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_disable_dma( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_read_reset_needed( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *reset_needed + ); + +mlx_status +nodnic_port_read_port_management_change_event( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *change_event + ); +#endif /* STUB_NODNIC_PORT_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c new file mode 100644 index 000000000..69f85358b --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../include/mlx_cmd.h" +#include "../../mlx_utils/include/public/mlx_pci_gw.h" +#include "../../mlx_utils/include/public/mlx_bail.h" +#include "../../mlx_utils/include/public/mlx_pci.h" +#include "../../mlx_utils/include/public/mlx_logging.h" + +mlx_status +nodnic_cmd_read( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_utils *utils = NULL; + + if ( device_priv == NULL || buffer == NULL ) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + utils = device_priv->utils; + + status = mlx_pci_gw_read(utils, PCI_GW_SPACE_NODNIC, address, buffer); + MLX_CHECK_STATUS(device_priv, status, read_error,"mlx_pci_gw_read failed"); + +read_error: +bad_param: + return status; +} + +mlx_status +nodnic_cmd_write( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_utils *utils = NULL; + + + if ( device_priv == NULL ) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + utils = device_priv->utils; + + + status = mlx_pci_gw_write(utils, PCI_GW_SPACE_NODNIC, address, buffer); + MLX_CHECK_STATUS(device_priv, status, write_error,"mlx_pci_gw_write failed"); +write_error: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c new file mode 100644 index 000000000..4acc94fa6 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../include/mlx_device.h" +#include "../include/mlx_cmd.h" +#include "../../mlx_utils/include/public/mlx_bail.h" +#include "../../mlx_utils/include/public/mlx_pci.h" +#include "../../mlx_utils/include/public/mlx_memory.h" +#include "../../mlx_utils/include/public/mlx_logging.h" + +#define CHECK_BIT(field, offset) (((field) & ((mlx_uint32)1 << (offset))) != 0) + +static +mlx_status +check_nodnic_interface_supported( + IN nodnic_device_priv* device_priv, + OUT mlx_boolean *out + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 output = 0; + status = nodnic_cmd_read(device_priv, NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, + &output); + MLX_FATAL_CHECK_STATUS(status, read_error, "failed to read nic_interface_supported"); + *out = CHECK_BIT(output, NODNIC_NIC_INTERFACE_SUPPORTED_BIT); +read_error: + return status; +} + +static +mlx_status +wait_for_device_initialization( + IN nodnic_device_priv* device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 try = 0; + mlx_uint32 buffer = 0; + +#define CHECK_DEVICE_INIT_TRIES 10 + for( ; try < CHECK_DEVICE_INIT_TRIES ; try++){ + status = nodnic_cmd_read(device_priv, NODNIC_INITIALIZING_OFFSET, &buffer); + MLX_CHECK_STATUS(device_priv, status, read_error, "failed to read initializing"); + if( !CHECK_BIT(buffer, NODNIC_INITIALIZING_BIT)){ + goto init_done; + } + mlx_utils_delay_in_ms(100); + } + status = MLX_FAILED; +read_error: +init_done: + return status; +} + +static +mlx_status +disable_nodnic_inteface( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + + buffer = (1 << NODNIC_DISABLE_INTERFACE_BIT); + status = nodnic_cmd_write(device_priv, NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET, buffer); + MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface"); + + status = wait_for_device_initialization(device_priv); + MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device"); +init_err: +write_err: + return status; +} +static +mlx_status +nodnic_device_start_nodnic( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + mlx_boolean nodnic_supported = 0; + + status = wait_for_device_initialization(device_priv); + MLX_FATAL_CHECK_STATUS(status, wait_for_fw_err, "failed to initialize device"); + + status = check_nodnic_interface_supported(device_priv, &nodnic_supported); + MLX_FATAL_CHECK_STATUS(status, read_err,"failed to check nic_interface_supported"); + + if( nodnic_supported == 0 ){ + status = MLX_UNSUPPORTED; + goto nodnic_unsupported; + } + buffer = (1 << NODNIC_NIC_INTERFACE_BIT); + status = nodnic_cmd_write(device_priv, NODNIC_NIC_INTERFACE_OFFSET, buffer); + MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface"); + + status = wait_for_device_initialization(device_priv); + MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device"); +init_err: +read_err: +write_err: +nodnic_unsupported: +wait_for_fw_err: + return status; +} + +static +mlx_status +nodnic_device_get_nodnic_data( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + + status = nodnic_cmd_read(device_priv, NODNIC_LOCATION_OFFSET, &device_priv->device_offset); + MLX_FATAL_CHECK_STATUS(status, nodnic_offset_read_err, "failed to read nodnic offset"); + + status = nodnic_cmd_read(device_priv, + device_priv->device_offset + NODNIC_REVISION_OFFSET, &buffer); + MLX_FATAL_CHECK_STATUS(status, nodnic_revision_read_err, "failed to read nodnic revision"); + + device_priv->nodnic_revision = (buffer >> 24) & 0xFF; + if( device_priv->nodnic_revision != NODIC_SUPPORTED_REVISION ){ + MLX_DEBUG_ERROR(device_priv, "nodnic revision not supported\n"); + status = MLX_UNSUPPORTED; + goto unsupported_revision; + } + + status = nodnic_cmd_read(device_priv, + device_priv->device_offset + NODNIC_HARDWARE_FORMAT_OFFSET, &buffer); + MLX_FATAL_CHECK_STATUS(status, nodnic_hardware_format_read_err, "failed to read nodnic revision"); + device_priv->hardware_format = (buffer >> 16) & 0xFF; + + return status; + +unsupported_revision: +nodnic_hardware_format_read_err: +nodnic_offset_read_err: +nodnic_revision_read_err: + disable_nodnic_inteface(device_priv); + return status; +} + +mlx_status +nodnic_device_clear_int ( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 disable = 1; +#ifndef DEVICE_CX3 + status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable); + MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit"); +#else + mlx_utils *utils = device_priv->utils; + mlx_uint64 clear_int = (mlx_uint64)(device_priv->crspace_clear_int); + mlx_uint32 swapped = 0; + + if (device_priv->device_cap.crspace_doorbells == 0) { + status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable); + MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit"); + } else { + /* Write the new index and update FW that new data was submitted */ + disable = 0x80000000; + mlx_memory_cpu_to_be32(utils, disable, &swapped); + mlx_pci_mem_write (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped); + mlx_pci_mem_read (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped); + } +#endif +clear_int_done: + return status; +} + +mlx_status +nodnic_device_init( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( device_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto parm_err; + } + status = nodnic_device_start_nodnic(device_priv); + MLX_FATAL_CHECK_STATUS(status, start_nodnic_err, "nodnic_device_start_nodnic failed"); + + status = nodnic_device_get_nodnic_data(device_priv); + MLX_FATAL_CHECK_STATUS(status, data_err, "nodnic_device_get_nodnic_data failed"); + return status; +data_err: +start_nodnic_err: +parm_err: + return status; +} + +mlx_status +nodnic_device_teardown( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + status = disable_nodnic_inteface(device_priv); + MLX_FATAL_CHECK_STATUS(status, disable_failed, "failed to disable nodnic interface"); +disable_failed: + return status; +} + +mlx_status +nodnic_device_get_cap( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_capabilites *device_cap = NULL; + mlx_uint32 buffer = 0; + mlx_uint64 guid_l = 0; + mlx_uint64 guid_h = 0; + if( device_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto parm_err; + } + + device_cap = &device_priv->device_cap; + + //get device capabilities + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x0, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic first dword"); + +#define NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET 15 +#define NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET 14 +#define NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET 13 +#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET 8 +#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK 0x7 +#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET 4 +#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK 0xF +#define NODNIC_DEVICE_NUM_PORTS_OFFSET 0 + device_cap->support_mac_filters = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET); + + device_cap->support_promisc_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET); + + device_cap->support_promisc_multicast_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET); + + device_cap->log_working_buffer_size = + (buffer >> NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET) & NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK; + + device_cap->log_pkey_table_size = + (buffer >> NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK; + + device_cap->num_ports = CHECK_BIT(buffer, NODNIC_DEVICE_NUM_PORTS_OFFSET) + 1; + +#ifdef DEVICE_CX3 +#define NODNIC_DEVICE_CRSPACE_DB_OFFSET 12 + device_cap->crspace_doorbells = CHECK_BIT(buffer, NODNIC_DEVICE_CRSPACE_DB_OFFSET); +#endif + + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x4, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic second dword"); + +#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET 24 +#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK 0x3F +#define NODNIC_DEVICE_PD_MASK 0xFFFFFF + device_cap->log_max_ring_size = + (buffer >> NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET) & NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK; + + //get device magic numbers + device_priv->pd = buffer & NODNIC_DEVICE_PD_MASK; + + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x8, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic third dword"); + device_priv->lkey = buffer; + +#ifdef DEVICE_CX3 + if ( device_cap->crspace_doorbells ) { + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x18, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic_crspace_clear_int address"); + device_priv->crspace_clear_int = device_priv->utils->config + buffer; + } +#endif + + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x10, (mlx_uint32*)&guid_h); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_h"); + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x14, (mlx_uint32*)&guid_l); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_l"); + device_priv->device_guid = guid_l | (guid_h << 32); +read_err: +parm_err: + return status; +} + +mlx_status +nodnic_device_get_fw_version( + IN nodnic_device_priv *device_priv, + OUT mlx_uint16 *fw_ver_minor, + OUT mlx_uint16 *fw_ver_sub_minor, + OUT mlx_uint16 *fw_ver_major + ){ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + + if( device_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto parm_err; + } + + status = nodnic_cmd_read(device_priv, 0x0, &buffer); + MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision major and minor"); + + *fw_ver_minor = (mlx_uint16)(buffer >> 16); + *fw_ver_major = (mlx_uint16)buffer; + + status = nodnic_cmd_read(device_priv, 0x4, &buffer); + MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision sub minor"); + + *fw_ver_sub_minor = (mlx_uint16)buffer; +read_err: +parm_err: + return status; +} diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c new file mode 100644 index 000000000..a7afdab65 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c @@ -0,0 +1,1038 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../include/mlx_port.h" +#include "../include/mlx_cmd.h" +#include "../../mlx_utils/include/public/mlx_memory.h" +#include "../../mlx_utils/include/public/mlx_pci.h" +#include "../../mlx_utils/include/public/mlx_bail.h" + +#define PortDataEntry( _option, _offset, _align, _mask) { \ + .option = _option, \ + .offset = _offset, \ + .align = _align, \ + .mask = _mask, \ + } + +#define QpDataEntry( _type, _send_offset, _recv_offset) { \ + .type = _type, \ + .send_offset = _send_offset, \ + .recv_offset = _recv_offset, \ + } + + +struct nodnic_port_data_entry nodnic_port_data_table[] = { + PortDataEntry(nodnic_port_option_link_type, 0x0, 4, 0x1), + PortDataEntry(nodnic_port_option_mac_low, 0xc, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_mac_high, 0x8, 0, 0xFFFF), + PortDataEntry(nodnic_port_option_log_cq_size, 0x6c, 0, 0x3F), + PortDataEntry(nodnic_port_option_reset_needed, 0x0, 31, 0x1), + PortDataEntry(nodnic_port_option_mac_filters_en, 0x4, 0, 0x1F), + PortDataEntry(nodnic_port_option_port_state, 0x0, 0, 0xF), + PortDataEntry(nodnic_port_option_network_en, 0x4, 31, 0x1), + PortDataEntry(nodnic_port_option_dma_en, 0x4, 30, 0x1), + PortDataEntry(nodnic_port_option_eq_addr_low, 0x74, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_eq_addr_high, 0x70, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_cq_addr_low, 0x6c, 12, 0xFFFFF), + PortDataEntry(nodnic_port_option_cq_addr_high, 0x68, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_port_management_change_event, 0x0, 30, 0x1), + PortDataEntry(nodnic_port_option_port_promisc_en, 0x4, 29, 0x1), + PortDataEntry(nodnic_port_option_arm_cq, 0x78, 8, 0xffff), + PortDataEntry(nodnic_port_option_port_promisc_multicast_en, 0x4, 28, 0x1), +#ifdef DEVICE_CX3 + PortDataEntry(nodnic_port_option_crspace_en, 0x4, 27, 0x1), +#endif +}; + +#define MAX_QP_DATA_ENTRIES 5 +struct nodnic_qp_data_entry nodnic_qp_data_teable[MAX_QP_DATA_ENTRIES] = { + QpDataEntry(NODNIC_QPT_SMI, 0, 0), + QpDataEntry(NODNIC_QPT_GSI, 0, 0), + QpDataEntry(NODNIC_QPT_UD, 0, 0), + QpDataEntry(NODNIC_QPT_RC, 0, 0), + QpDataEntry(NODNIC_QPT_ETH, 0x80, 0xC0), +}; + +#define MAX_NODNIC_PORTS 2 +int nodnic_port_offset_table[MAX_NODNIC_PORTS] = { + 0x100, //port 1 offset + 0x280, //port 1 offset +}; + +mlx_status +nodnic_port_get_state( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_state *state + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + + status = nodnic_port_query(port_priv, + nodnic_port_option_port_state, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + *state = (nodnic_port_state)out; +query_err: + return status; +} +mlx_status +nodnic_port_get_type( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_type *type + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + + if ( port_priv->port_type == NODNIC_PORT_TYPE_UNKNOWN){ + status = nodnic_port_query(port_priv, + nodnic_port_option_link_type, &out); + MLX_FATAL_CHECK_STATUS(status, query_err, + "nodnic_port_query failed"); + port_priv->port_type = (nodnic_port_type)out; + } + *type = port_priv->port_type; +query_err: + return status; +} + +mlx_status +nodnic_port_query( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + OUT mlx_uint32 *out + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + struct nodnic_port_data_entry *data_entry; + mlx_uint32 buffer = 0; + if( port_priv == NULL || out == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + device_priv = port_priv->device; + + data_entry = &nodnic_port_data_table[option]; + + status = nodnic_cmd_read(device_priv, + port_priv->port_offset + data_entry->offset , &buffer); + MLX_CHECK_STATUS(device_priv, status, read_err, + "nodnic_cmd_read failed"); + *out = (buffer >> data_entry->align) & data_entry->mask; +read_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_set( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + IN mlx_uint32 in + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + struct nodnic_port_data_entry *data_entry; + mlx_uint32 buffer = 0; + + if( port_priv == NULL ){ + MLX_DEBUG_FATAL_ERROR("port_priv is NULL\n"); + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + device_priv = port_priv->device; + data_entry = &nodnic_port_data_table[option]; + + if( in > data_entry->mask ){ + MLX_DEBUG_FATAL_ERROR("in > data_entry->mask (%d > %d)\n", + in, data_entry->mask); + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + status = nodnic_cmd_read(device_priv, + port_priv->port_offset + data_entry->offset, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, + "nodnic_cmd_read failed"); + buffer = buffer & ~(data_entry->mask << data_entry->align); + buffer = buffer | (in << data_entry->align); + status = nodnic_cmd_write(device_priv, + port_priv->port_offset + data_entry->offset, buffer); + MLX_FATAL_CHECK_STATUS(status, write_err, + "nodnic_cmd_write failed"); +write_err: +read_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_read_reset_needed( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *reset_needed + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + status = nodnic_port_query(port_priv, + nodnic_port_option_reset_needed, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + *reset_needed = (mlx_boolean)out; +query_err: + return status; +} + +mlx_status +nodnic_port_read_port_management_change_event( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *change_event + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + status = nodnic_port_query(port_priv, + nodnic_port_option_port_management_change_event, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + *change_event = (mlx_boolean)out; +query_err: + return status; +} + +mlx_status +nodnic_port_create_cq( + IN nodnic_port_priv *port_priv, + IN mlx_size cq_size, + OUT nodnic_cq **cq + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + mlx_uint64 address = 0; + if( port_priv == NULL || cq == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + + status = mlx_memory_zalloc(device_priv->utils, + sizeof(nodnic_cq),(mlx_void **)cq); + MLX_FATAL_CHECK_STATUS(status, alloc_err, + "cq priv allocation error"); + + (*cq)->cq_size = cq_size; + status = mlx_memory_alloc_dma(device_priv->utils, + (*cq)->cq_size, NODNIC_MEMORY_ALIGN, + &(*cq)->cq_virt); + MLX_FATAL_CHECK_STATUS(status, dma_alloc_err, + "cq allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + (*cq)->cq_virt, + (*cq)->cq_size, + &(*cq)->cq_physical, + &(*cq)->map); + MLX_FATAL_CHECK_STATUS(status, cq_map_err, + "cq map error"); + + /* update cq address */ +#define NODIC_CQ_ADDR_HIGH 0x68 +#define NODIC_CQ_ADDR_LOW 0x6c + address = (mlx_uint64)(*cq)->cq_physical; + nodnic_port_set(port_priv, nodnic_port_option_cq_addr_low, + (mlx_uint32)(address >> 12)); + address = address >> 32; + nodnic_port_set(port_priv, nodnic_port_option_cq_addr_high, + (mlx_uint32)address); + + return status; + mlx_memory_ummap_dma(device_priv->utils, (*cq)->map); +cq_map_err: + mlx_memory_free_dma(device_priv->utils, (*cq)->cq_size, + (void **)&((*cq)->cq_virt)); +dma_alloc_err: + mlx_memory_free(device_priv->utils, (void **)cq); +alloc_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_destroy_cq( + IN nodnic_port_priv *port_priv, + IN nodnic_cq *cq + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || cq == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + device_priv = port_priv->device; + + mlx_memory_ummap_dma(device_priv->utils, cq->map); + + mlx_memory_free_dma(device_priv->utils, cq->cq_size, + (void **)&(cq->cq_virt)); + + mlx_memory_free(device_priv->utils, (void **)&cq); +invalid_parm: + return status; +} +mlx_status +nodnic_port_create_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type, + IN mlx_size send_wq_size, + IN mlx_uint32 send_wqe_num, + IN mlx_size receive_wq_size, + IN mlx_uint32 recv_wqe_num, + OUT nodnic_qp **qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + mlx_uint32 max_ring_size = 0; + mlx_uint64 address = 0; + mlx_uint32 log_size = 0; + if( port_priv == NULL || qp == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + max_ring_size = (1 << device_priv->device_cap.log_max_ring_size); + if( send_wq_size > max_ring_size || + receive_wq_size > max_ring_size ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + status = mlx_memory_zalloc(device_priv->utils, + sizeof(nodnic_qp),(mlx_void **)qp); + MLX_FATAL_CHECK_STATUS(status, alloc_err, + "qp allocation error"); + + if( nodnic_qp_data_teable[type].send_offset == 0 || + nodnic_qp_data_teable[type].recv_offset == 0){ + status = MLX_INVALID_PARAMETER; + goto invalid_type; + } + + (*qp)->send.nodnic_ring.offset = port_priv->port_offset + + nodnic_qp_data_teable[type].send_offset; + (*qp)->receive.nodnic_ring.offset = port_priv->port_offset + + nodnic_qp_data_teable[type].recv_offset; + + status = mlx_memory_alloc_dma(device_priv->utils, + send_wq_size, NODNIC_MEMORY_ALIGN, + (void*)&(*qp)->send.wqe_virt); + MLX_FATAL_CHECK_STATUS(status, send_alloc_err, + "send wq allocation error"); + + status = mlx_memory_alloc_dma(device_priv->utils, + receive_wq_size, NODNIC_MEMORY_ALIGN, + &(*qp)->receive.wqe_virt); + MLX_FATAL_CHECK_STATUS(status, receive_alloc_err, + "receive wq allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + (*qp)->send.wqe_virt, + send_wq_size, + &(*qp)->send.nodnic_ring.wqe_physical, + &(*qp)->send.nodnic_ring.map); + MLX_FATAL_CHECK_STATUS(status, send_map_err, + "send wq map error"); + + status = mlx_memory_map_dma(device_priv->utils, + (*qp)->receive.wqe_virt, + receive_wq_size, + &(*qp)->receive.nodnic_ring.wqe_physical, + &(*qp)->receive.nodnic_ring.map); + MLX_FATAL_CHECK_STATUS(status, receive_map_err, + "receive wq map error"); + + (*qp)->send.nodnic_ring.wq_size = send_wq_size; + (*qp)->send.nodnic_ring.num_wqes = send_wqe_num; + (*qp)->receive.nodnic_ring.wq_size = receive_wq_size; + (*qp)->receive.nodnic_ring.num_wqes = recv_wqe_num; + + /* Set Ownership bit in Send/receive queue (0 - recv ; 1 - send) */ + mlx_memory_set(device_priv->utils, (*qp)->send.wqe_virt, 0xff, send_wq_size ); + mlx_memory_set(device_priv->utils, (*qp)->receive.wqe_virt, 0, recv_wqe_num ); + + /* update send ring */ +#define NODIC_RING_QP_ADDR_HIGH 0x0 +#define NODIC_RING_QP_ADDR_LOW 0x4 + address = (mlx_uint64)(*qp)->send.nodnic_ring.wqe_physical; + status = nodnic_cmd_write(device_priv, (*qp)->send.nodnic_ring.offset + + NODIC_RING_QP_ADDR_HIGH, + (mlx_uint32)(address >> 32)); + MLX_FATAL_CHECK_STATUS(status, write_send_addr_err, + "send address write error 1"); + mlx_utils_ilog2((*qp)->send.nodnic_ring.wq_size, &log_size); + address = address | log_size; + status = nodnic_cmd_write(device_priv, (*qp)->send.nodnic_ring.offset + + NODIC_RING_QP_ADDR_LOW, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, write_send_addr_err, + "send address write error 2"); + /* update receive ring */ + address = (mlx_uint64)(*qp)->receive.nodnic_ring.wqe_physical; + status = nodnic_cmd_write(device_priv, (*qp)->receive.nodnic_ring.offset + + NODIC_RING_QP_ADDR_HIGH, + (mlx_uint32)(address >> 32)); + MLX_FATAL_CHECK_STATUS(status, write_recv_addr_err, + "receive address write error 1"); + mlx_utils_ilog2((*qp)->receive.nodnic_ring.wq_size, &log_size); + address = address | log_size; + status = nodnic_cmd_write(device_priv, (*qp)->receive.nodnic_ring.offset + + NODIC_RING_QP_ADDR_LOW, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, write_recv_addr_err, + "receive address write error 2"); + + return status; +write_recv_addr_err: +write_send_addr_err: + mlx_memory_ummap_dma(device_priv->utils, (*qp)->receive.nodnic_ring.map); +receive_map_err: + mlx_memory_ummap_dma(device_priv->utils, (*qp)->send.nodnic_ring.map); +send_map_err: + mlx_memory_free_dma(device_priv->utils, receive_wq_size, + &((*qp)->receive.wqe_virt)); +receive_alloc_err: + mlx_memory_free_dma(device_priv->utils, send_wq_size, + (void **)&((*qp)->send.wqe_virt)); +send_alloc_err: +invalid_type: + mlx_memory_free(device_priv->utils, (void **)qp); +alloc_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_destroy_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type __attribute__((unused)), + IN nodnic_qp *qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = port_priv->device; + + status = mlx_memory_ummap_dma(device_priv->utils, + qp->receive.nodnic_ring.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_ummap_dma(device_priv->utils, qp->send.nodnic_ring.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + qp->receive.nodnic_ring.wq_size, + (void **)&(qp->receive.wqe_virt)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + status = mlx_memory_free_dma(device_priv->utils, + qp->send.nodnic_ring.wq_size, + (void **)&(qp->send.wqe_virt)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + status = mlx_memory_free(device_priv->utils, (void **)&qp); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free failed (Status = %d)\n", status); + } + return status; +} + +mlx_status +nodnic_port_get_qpn( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + OUT mlx_uint32 *qpn + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + if( ring == NULL || qpn == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + if( ring->qpn != 0 ){ + *qpn = ring->qpn; + goto success; + } +#define NODNIC_RING_QPN_OFFSET 0xc +#define NODNIC_RING_QPN_MASK 0xFFFFFF + status = nodnic_cmd_read(port_priv->device, + ring->offset + NODNIC_RING_QPN_OFFSET, + &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, + "nodnic_cmd_read failed"); + ring->qpn = buffer & NODNIC_RING_QPN_MASK; + *qpn = ring->qpn; +read_err: +success: +bad_param: + return status; +} + +#ifdef DEVICE_CX3 +static +mlx_status +nodnic_port_send_db_connectx3( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring __attribute__((unused)), + IN mlx_uint16 index + ) +{ + nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; + mlx_uint32 index32 = index; + mlx_pci_mem_write(port_priv->device->utils, MlxPciWidthUint32, 0, + (mlx_uint64)&(ptr->send_doorbell), 1, &index32); + return MLX_SUCCESS; +} + +static +mlx_status +nodnic_port_recv_db_connectx3( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring __attribute__((unused)), + IN mlx_uint16 index + ) +{ + nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; + mlx_uint32 index32 = index; + mlx_pci_mem_write(port_priv->device->utils, MlxPciWidthUint32, 0, + (mlx_uint64)&(ptr->recv_doorbell), 1, &index32); + return MLX_SUCCESS; +} +#endif + +mlx_status +nodnic_port_update_ring_doorbell( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + if( ring == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } +#define NODNIC_RING_RING_OFFSET 0x8 + buffer = (mlx_uint32)((index & 0xFFFF)<< 8); + status = nodnic_cmd_write(port_priv->device, + ring->offset + NODNIC_RING_RING_OFFSET, + buffer); + MLX_CHECK_STATUS(port_priv->device, status, write_err, + "nodnic_cmd_write failed"); +write_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_get_cq_size( + IN nodnic_port_priv *port_priv, + OUT mlx_uint64 *cq_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + status = nodnic_port_query(port_priv, nodnic_port_option_log_cq_size, &out); + MLX_FATAL_CHECK_STATUS(status, query_err, + "nodnic_port_query failed"); + *cq_size = 1 << out; +query_err: + return status; +} + +mlx_status +nodnic_port_allocate_eq( + IN nodnic_port_priv *port_priv, + IN mlx_uint8 log_eq_size + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + mlx_uint64 address = 0; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + device_priv = port_priv->device; + port_priv->eq.eq_size = ( ( 1 << log_eq_size ) * 1024 ); /* Size is in KB */ + status = mlx_memory_alloc_dma(device_priv->utils, + port_priv->eq.eq_size, + NODNIC_MEMORY_ALIGN, + &port_priv->eq.eq_virt); + MLX_FATAL_CHECK_STATUS(status, alloc_err, + "eq allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + port_priv->eq.eq_virt, + port_priv->eq.eq_size, + &port_priv->eq.eq_physical, + &port_priv->eq.map); + MLX_FATAL_CHECK_STATUS(status, map_err, + "eq map error"); + + address = port_priv->eq.eq_physical; + status = nodnic_port_set(port_priv, nodnic_port_option_eq_addr_low, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set eq addr low"); + address = (address >> 32); + status = nodnic_port_set(port_priv, nodnic_port_option_eq_addr_high, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set eq addr high"); + return status; +set_err: + mlx_memory_ummap_dma(device_priv->utils, port_priv->eq.map); +map_err: + mlx_memory_free_dma(device_priv->utils, + port_priv->eq.eq_size, + (void **)&(port_priv->eq.eq_virt)); +alloc_err: +bad_param: + return status; +} +mlx_status +nodnic_port_free_eq( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + device_priv = port_priv->device; + mlx_memory_ummap_dma(device_priv->utils, port_priv->eq.map); + + mlx_memory_free_dma(device_priv->utils, + port_priv->eq.eq_size, + (void **)&(port_priv->eq.eq_virt)); + +bad_param: + return status; +} + +mlx_status +nodnic_port_add_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device= NULL;; + mlx_uint8 index = 0; + mlx_uint32 out = 0; + mlx_uint32 mac_filters_en = 0; + mlx_uint32 address = 0; + mlx_mac_address zero_mac; + mlx_utils *utils = NULL; + + if( port_priv == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + memset(&zero_mac, 0, sizeof(zero_mac)); + + device = port_priv->device; + utils = device->utils; + + /* check if mac already exists */ + for( ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { + mlx_memory_cmp(utils, &port_priv->mac_filters[index], &mac, + sizeof(mac), &out); + if ( out == 0 ){ + status = MLX_FAILED; + goto already_exists; + } + } + + /* serch for available mac filter slot */ + for (index = 0 ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { + mlx_memory_cmp(utils, &port_priv->mac_filters[index], &zero_mac, + sizeof(zero_mac), &out); + if ( out == 0 ){ + break; + } + } + if ( index >= NODNIC_MAX_MAC_FILTERS ){ + status = MLX_FAILED; + goto mac_list_full; + } + + status = nodnic_port_query(port_priv, nodnic_port_option_mac_filters_en, + &mac_filters_en); + MLX_CHECK_STATUS(device, status , query_err, + "nodnic_port_query failed"); + if(mac_filters_en & (1 << index)){ + status = MLX_FAILED; + goto mac_list_full; + } + port_priv->mac_filters[index] = mac; + + // set mac filter + address = port_priv->port_offset + NODNIC_PORT_MAC_FILTERS_OFFSET + + (0x8 * index); + + status = nodnic_cmd_write(device, address, mac.high ); + MLX_CHECK_STATUS(device, status, write_err, "set mac high failed"); + status = nodnic_cmd_write(device, address + 0x4, mac.low ); + MLX_CHECK_STATUS(device, status, write_err, "set mac low failed"); + + // enable mac filter + mac_filters_en = mac_filters_en | (1 << index); + status = nodnic_port_set(port_priv, nodnic_port_option_mac_filters_en, + mac_filters_en); + MLX_CHECK_STATUS(device, status , set_err, + "nodnic_port_set failed"); +set_err: +write_err: +query_err: +mac_list_full: +already_exists: +bad_param: + return status; +} + +mlx_status +nodnic_port_remove_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device= NULL;; + mlx_uint8 index = 0; + mlx_uint32 out = 0; + mlx_uint32 mac_filters_en = 0; + mlx_mac_address zero_mac; + mlx_utils *utils = NULL; + + if( port_priv == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + memset(&zero_mac, 0, sizeof(zero_mac)); + + device = port_priv->device; + utils = device->utils; + + /* serch for mac filter */ + for( ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { + mlx_memory_cmp(utils, &port_priv->mac_filters[index], &mac, + sizeof(mac), &out); + if ( out == 0 ){ + break; + } + } + if ( index == NODNIC_MAX_MAC_FILTERS ){ + status = MLX_FAILED; + goto mac_not_found; + } + + status = nodnic_port_query(port_priv, nodnic_port_option_mac_filters_en, + &mac_filters_en); + MLX_CHECK_STATUS(device, status , query_err, + "nodnic_port_query failed"); + if((mac_filters_en & (1 << index)) == 0){ + status = MLX_FAILED; + goto mac_not_en; + } + port_priv->mac_filters[index] = zero_mac; + + // disable mac filter + mac_filters_en = mac_filters_en & ~(1 << index); + status = nodnic_port_set(port_priv, nodnic_port_option_mac_filters_en, + mac_filters_en); + MLX_CHECK_STATUS(device, status , set_err, + "nodnic_port_set failed"); +set_err: +query_err: +mac_not_en: +mac_not_found: +bad_param: + return status; +} + +static +mlx_status +nodnic_port_set_network( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + mlx_status status = MLX_SUCCESS; + /*mlx_uint32 network_valid = 0; + mlx_uint8 try = 0;*/ + + status = nodnic_port_set(port_priv, nodnic_port_option_network_en, value); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); + port_priv->network_state = value; +set_err: + return status; +} + +#ifdef DEVICE_CX3 +static +mlx_status +nodnic_port_set_dma_connectx3( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + mlx_utils *utils = port_priv->device->utils; + nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; + mlx_uint32 data = (value ? 0xffffffff : 0x0); + mlx_pci_mem_write(utils, MlxPciWidthUint32, 0, + (mlx_uint64)&(ptr->dma_en), 1, &data); + return MLX_SUCCESS; +} +#endif + +static +mlx_status +nodnic_port_set_dma( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + return nodnic_port_set(port_priv, nodnic_port_option_dma_en, value); +} + +static +mlx_status +nodnic_port_check_and_set_dma( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + mlx_status status = MLX_SUCCESS; + if ( port_priv->dma_state == value ) { + MLX_DEBUG_WARN(port_priv->device, + "nodnic_port_check_and_set_dma: already %s\n", + (value ? "enabled" : "disabled")); + status = MLX_SUCCESS; + goto set_out; + } + + status = port_priv->set_dma(port_priv, value); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); + port_priv->dma_state = value; +set_err: +set_out: + return status; +} + + +mlx_status +nodnic_port_set_promisc( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ){ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = value; + + status = nodnic_port_set(port_priv, nodnic_port_option_port_promisc_en, buffer); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); +set_err: + return status; +} + +mlx_status +nodnic_port_set_promisc_multicast( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ){ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = value; + + status = nodnic_port_set(port_priv, nodnic_port_option_port_promisc_multicast_en, buffer); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); +set_err: + return status; +} + +mlx_status +nodnic_port_init( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_set_network(port_priv, TRUE); + MLX_FATAL_CHECK_STATUS(status, set_err, + "nodnic_port_set_network failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_close( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_set_network(port_priv, FALSE); + MLX_FATAL_CHECK_STATUS(status, set_err, + "nodnic_port_set_network failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_enable_dma( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_check_and_set_dma(port_priv, TRUE); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_check_and_set_dma failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_disable_dma( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_check_and_set_dma(port_priv, FALSE); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_check_and_set_dma failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_thin_init( + IN nodnic_device_priv *device_priv, + IN nodnic_port_priv *port_priv, + IN mlx_uint8 port_index + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_boolean reset_needed = 0; +#ifdef DEVICE_CX3 + mlx_uint32 offset; +#endif + + if( device_priv == NULL || port_priv == NULL || port_index > 1){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + port_priv->device = device_priv; + + port_priv->port_offset = device_priv->device_offset + + nodnic_port_offset_table[port_index]; + + port_priv->port_num = port_index + 1; + + port_priv->send_doorbell = nodnic_port_update_ring_doorbell; + port_priv->recv_doorbell = nodnic_port_update_ring_doorbell; + port_priv->set_dma = nodnic_port_set_dma; +#ifdef DEVICE_CX3 + if (device_priv->device_cap.crspace_doorbells) { + status = nodnic_cmd_read(device_priv, (port_priv->port_offset + 0x100), + &offset); + if (status != MLX_SUCCESS) { + return status; + } else { + port_priv->data_flow_gw = (nodnic_port_data_flow_gw *) + (device_priv->utils->config + offset); + } + if ( nodnic_port_set ( port_priv, nodnic_port_option_crspace_en, 1 ) ) { + return MLX_FAILED; + } + port_priv->send_doorbell = nodnic_port_send_db_connectx3; + port_priv->recv_doorbell = nodnic_port_recv_db_connectx3; + port_priv->set_dma = nodnic_port_set_dma_connectx3; + } +#endif + /* clear reset_needed */ + nodnic_port_read_reset_needed(port_priv, &reset_needed); + + port_priv->port_type = NODNIC_PORT_TYPE_UNKNOWN; +invalid_parm: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h new file mode 100644 index 000000000..1f8ba89ea --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h @@ -0,0 +1,113 @@ +#ifndef MLXUTILS_INCLUDE_PRIVATE_MEMORYPRIV_H_ +#define MLXUTILS_INCLUDE_PRIVATE_MEMORYPRIV_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../../mlx_utils/include/public/mlx_utils.h" + +mlx_status +mlx_memory_alloc_priv( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_zalloc_priv( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free_priv( + IN mlx_utils *utils, + IN mlx_void *ptr + ); +mlx_status +mlx_memory_alloc_dma_priv( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free_dma_priv( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_void *ptr + ); +mlx_status +mlx_memory_map_dma_priv( + IN mlx_utils *utils, + IN mlx_void *addr , + IN mlx_size number_of_bytes, + OUT mlx_physical_address *phys_addr, + OUT mlx_void **mapping + ); + +mlx_status +mlx_memory_ummap_dma_priv( + IN mlx_utils *utils, + IN mlx_void *mapping + ); + +mlx_status +mlx_memory_cmp_priv( + IN mlx_utils *utils, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ); + +mlx_status +mlx_memory_set_priv( + IN mlx_utils *utils, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ); + +mlx_status +mlx_memory_cpy_priv( + IN mlx_utils *utils, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ); + +mlx_status +mlx_memory_cpu_to_be32_priv( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); + +mlx_status +mlx_memory_be32_to_cpu_priv( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); +#endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_MEMORYPRIV_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h new file mode 100644 index 000000000..89cad75eb --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h @@ -0,0 +1,72 @@ +#ifndef STUB_MLXUTILS_INCLUDE_PRIVATE_PCIPRIV_H_ +#define STUB_MLXUTILS_INCLUDE_PRIVATE_PCIPRIV_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../include/public/mlx_pci.h" +#include "../../include/public/mlx_utils.h" + +mlx_status +mlx_pci_init_priv( + IN mlx_utils *utils + ); + +mlx_status +mlx_pci_read_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_write_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_read_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_write_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + + +#endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_PCIPRIV_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h new file mode 100644 index 000000000..268b76fad --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h @@ -0,0 +1,68 @@ +#ifndef SRC_DRIVERS_INFINIBAND_MLX_UTILS_INCLUDE_PRIVATE_MLX_UTILS_PRIV_H_ +#define SRC_DRIVERS_INFINIBAND_MLX_UTILS_INCLUDE_PRIVATE_MLX_UTILS_PRIV_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../include/public/mlx_utils.h" + +mlx_status +mlx_utils_delay_in_ms_priv( + IN mlx_uint32 msecs + ); + +mlx_status +mlx_utils_delay_in_us_priv( + IN mlx_uint32 usecs + ); + +mlx_status +mlx_utils_ilog2_priv( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ); + +mlx_status +mlx_utils_init_lock_priv( + OUT void **lock + ); + +mlx_status +mlx_utils_free_lock_priv( + IN void *lock + ); + +mlx_status +mlx_utils_acquire_lock_priv ( + IN void *lock + ); + +mlx_status +mlx_utils_release_lock_priv ( + IN void *lock + ); + +mlx_status +mlx_utils_rand_priv ( + IN mlx_utils *utils, + OUT mlx_uint32 *rand_num + ); +#endif /* SRC_DRIVERS_INFINIBAND_MLX_UTILS_INCLUDE_PRIVATE_MLX_UTILS_PRIV_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h new file mode 100644 index 000000000..a4f4b37b1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h @@ -0,0 +1,47 @@ +#ifndef INCLUDE_PUBLIC_MLXBAIL_H_ +#define INCLUDE_PUBLIC_MLXBAIL_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_types.h" + +#define MLX_BAIL_ERROR(id, status,message) MLX_CHECK_STATUS(id, status, bail, message) + +#define MLX_FATAL_CHECK_STATUS(status, label, message) \ + do { \ + if (status != MLX_SUCCESS) { \ + MLX_DEBUG_FATAL_ERROR(message " (Status = %d)\n", status); \ + goto label; \ + } \ + } while (0) + +#define MLX_CHECK_STATUS(id, status, label, message) \ + do { \ + if (status != MLX_SUCCESS) { \ + MLX_DEBUG_ERROR(id, message " (Status = %d)\n", status);\ + goto label; \ + } \ + } while (0) + + + +#endif /* INCLUDE_PUBLIC_MLXBAIL_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h new file mode 100644 index 000000000..1ed423daf --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h @@ -0,0 +1,63 @@ +#ifndef MLXUTILS_INCLUDE_PUBLIC_MLX_ICMD_H_ +#define MLXUTILS_INCLUDE_PUBLIC_MLX_ICMD_H_ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_utils.h" + +#define MLX_ICMD_MB_ADDR 0x100000 +#define MLX_ICMD_MB_SIZE_ADDR 0x1000 +#define MLX_ICMD_CTRL_ADDR 0x0 + +#define MLX_ICMD_SEMAPHORE_ADDR 0x0 + +#define MLX_ICMD_SEMAPHORE_ID 1234 + +enum { + FLASH_REG_ACCESS = 0x9001, + GET_FW_INFO = 0x8007, + QUERY_VIRTUAL_MAC = 0x9003, + SET_VIRTUAL_MAC = 0x9004, + QUERY_WOL_ROL = 0x9005, + SET_WOL_ROL = 0x9006, + OCBB_INIT = 0x9007, + OCBB_QUERY_HEADER_STATUS = 0x9008, + OCBB_QUERY_ETOC_STATUS = 0x9009, + OCBB_QUERY_SET_EVENT = 0x900A, + OCSD_INIT = 0xf004, +}; + +struct mlx_icmd_ocsd { + mlx_uint32 reserved; + mlx_uint64 address; +}; + +mlx_status +mlx_icmd_send_command( + IN mlx_utils *utils, + IN mlx_uint16 opcode, + IN OUT mlx_void* data, + IN mlx_uint32 write_data_size, + IN mlx_uint32 read_data_size + ); + +#endif /* MLXUTILS_INCLUDE_PUBLIC_MLX_ICMD_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h new file mode 100644 index 000000000..7b7b852d1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h @@ -0,0 +1,46 @@ +#ifndef PUBLIC_INCLUDE_MLX_LOGGER_H_ +#define PUBLIC_INCLUDE_MLX_LOGGER_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../../mlx_utils_flexboot/include/mlx_logging_priv.h" + +#define MLX_DEBUG_FATAL_ERROR(...) MLX_DEBUG_FATAL_ERROR_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_ERROR(...) MLX_DEBUG_ERROR_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_WARN(...) MLX_DEBUG_WARN_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_INFO1(...) MLX_DEBUG_INFO1_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_INFO2(...) MLX_DEBUG_INFO2_PRIVATE(__VA_ARGS__) +#define MLX_DBG_ERROR(...) MLX_DBG_ERROR_PRIVATE(__VA_ARGS__) +#define MLX_DBG_WARN(...) MLX_DBG_WARN_PRIVATE(__VA_ARGS__) +#define MLX_DBG_INFO1(...) MLX_DBG_INFO1_PRIVATE(__VA_ARGS__) +#define MLX_DBG_INFO2(...) MLX_DBG_INFO2_PRIVATE(__VA_ARGS__) + +#define MLX_TRACE_1_START() MLX_DBG_INFO1_PRIVATE("Start\n") +#define MLX_TRACE_1_END() MLX_DBG_INFO1_PRIVATE("End\n") +#define MLX_TRACE_1_END_STATUS(status) MLX_DBG_INFO1_PRIVATE("End (%s=%d)\n", #status,status) +#define MLX_TRACE_2_START() MLX_DBG_INFO2_PRIVATE("Start\n") +#define MLX_TRACE_2_END() MLX_DBG_INFO2_PRIVATE("End\n") +#define MLX_TRACE_2_END_STATUS(status) MLX_DBG_INFO2_PRIVATE("End (%s=%d)\n", #status,status) + + + +#endif /* PUBLIC_INCLUDE_MLX_LOGGER_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h new file mode 100644 index 000000000..056756666 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h @@ -0,0 +1,115 @@ +#ifndef MLXUTILS_INCLUDE_PUBLIC_MEMORY_H_ +#define MLXUTILS_INCLUDE_PUBLIC_MEMORY_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_utils.h" + + +mlx_status +mlx_memory_alloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_zalloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free( + IN mlx_utils *utils, + IN mlx_void **ptr + ); +mlx_status +mlx_memory_alloc_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_void **ptr + ); +mlx_status +mlx_memory_map_dma( + IN mlx_utils *utils, + IN mlx_void *Addr , + IN mlx_size NumberOfBytes, + OUT mlx_physical_address *PhysAddr, + OUT mlx_void **Mapping + ); + +mlx_status +mlx_memory_ummap_dma( + IN mlx_utils *utils, + IN mlx_void *Mapping + ); + +mlx_status +mlx_memory_cmp( + IN mlx_utils *utils, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ); + +mlx_status +mlx_memory_set( + IN mlx_utils *utils, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ); + +mlx_status +mlx_memory_cpy( + IN mlx_utils *utils, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ); + +mlx_status +mlx_memory_cpu_to_be32( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); + +mlx_status +mlx_memory_be32_to_cpu( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); + +#endif /* STUB_MLXUTILS_INCLUDE_PUBLIC_MEMORY_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h new file mode 100644 index 000000000..416bdb66b --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h @@ -0,0 +1,78 @@ +#ifndef STUB_MLXUTILS_INCLUDE_PUBLIC_PCI_H_ +#define STUB_MLXUTILS_INCLUDE_PUBLIC_PCI_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_utils.h" + +typedef enum { + MlxPciWidthUint8 = 0, + MlxPciWidthUint16, + MlxPciWidthUint32, + MlxPciWidthUint64, +} mlx_pci_width; + +mlx_status +mlx_pci_init( + IN mlx_utils *utils + ); + +mlx_status +mlx_pci_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + + +#endif /* STUB_MLXUTILS_INCLUDE_PUBLIC_PCI_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h new file mode 100644 index 000000000..c074a22e5 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h @@ -0,0 +1,81 @@ +#ifndef INCLUDE_PUBLIC_MLX_PCI_GW_H_ +#define INCLUDE_PUBLIC_MLX_PCI_GW_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_utils.h" + +#define PCI_GW_FIRST_CAPABILITY_POINTER_OFFSET 0x34 + +#define PCI_GW_CAPABILITY_ID 0x9 + +#define PCI_GW_CAPABILITY_ID_OFFSET 0x0 +#define PCI_GW_CAPABILITY_NEXT_POINTER_OFFSET 0x1 +#define PCI_GW_CAPABILITY_SPACE_OFFSET 0x4 +#define PCI_GW_CAPABILITY_STATUS_OFFSET 0x7 +#define PCI_GW_CAPABILITY_COUNTER_OFFSET 0x8 +#define PCI_GW_CAPABILITY_SEMAPHORE_OFFSET 0xC +#define PCI_GW_CAPABILITY_ADDRESS_OFFSET 0x10 +#define PCI_GW_CAPABILITY_FLAG_OFFSET 0x10 +#define PCI_GW_CAPABILITY_DATA_OFFSET 0x14 + +#define PCI_GW_SEMPHORE_TRIES 3000000 +#define PCI_GW_GET_OWNERSHIP_TRIES 5000 +#define PCI_GW_READ_FLAG_TRIES 3000000 + +#define PCI_GW_WRITE_FLAG 0x80000000 + +#define PCI_GW_SPACE_NODNIC 0x4 +#define PCI_GW_SPACE_ALL_ICMD 0x3 +#define PCI_GW_SPACE_SEMAPHORE 0xa +#define PCI_GW_SPACE_CR0 0x2 + +typedef mlx_uint32 mlx_pci_gw_buffer; + + +mlx_status +mlx_pci_gw_init( + IN mlx_utils *utils + ); +mlx_status +mlx_pci_gw_teardown( + IN mlx_utils *utils + ); +mlx_status +mlx_pci_gw_read( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ); + +mlx_status +mlx_pci_gw_write( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ); + + + +#endif /* INCLUDE_PUBLIC_MLX_PCI_GW_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_types.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_types.h new file mode 100644 index 000000000..9c66567a3 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_types.h @@ -0,0 +1,27 @@ +#ifndef INCLUDE_PUBLIC_MLXTYPES_H_ +#define INCLUDE_PUBLIC_MLXTYPES_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../../mlx_utils_flexboot/include/mlx_types_priv.h" + +#endif /* INCLUDE_PUBLIC_MLXBAIL_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h new file mode 100644 index 000000000..46ad97c3b --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h @@ -0,0 +1,106 @@ +#ifndef MLXUTILS_INCLUDE_PUBLIC_MLXUTILS_H_ +#define MLXUTILS_INCLUDE_PUBLIC_MLXUTILS_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_logging.h" +#include "mlx_types.h" + +#define IN +#define OUT + +typedef mlx_uint16 mlx_pci_gw_space; + +typedef struct{ + mlx_uint32 pci_cmd_offset; + mlx_pci_gw_space space; +} __attribute__ (( packed )) mlx_pci_gw; + +typedef struct { + mlx_boolean icmd_opened; + mlx_boolean took_semaphore; + mlx_uint32 max_cmd_size; +} __attribute__ (( packed )) mlx_icmd ; + +typedef struct{ + mlx_pci *pci; + mlx_pci_gw pci_gw; + mlx_icmd icmd; + void *lock; +#ifdef DEVICE_CX3 + /* ACCESS to BAR0 */ + void *config; +#endif +} __attribute__ (( packed )) mlx_utils; + +mlx_status +mlx_utils_init( + IN mlx_utils *utils, + IN mlx_pci *pci + ); + +mlx_status +mlx_utils_teardown( + IN mlx_utils *utils + ); +mlx_status +mlx_utils_delay_in_ms( + IN mlx_uint32 msecs + ); + +mlx_status +mlx_utils_delay_in_us( + IN mlx_uint32 usecs + ); + +mlx_status +mlx_utils_ilog2( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ); + +mlx_status +mlx_utils_init_lock( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_free_lock( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_acquire_lock ( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_release_lock ( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_rand ( + IN mlx_utils *utils, + OUT mlx_uint32 *rand_num + ); +#endif /* STUB_MLXUTILS_INCLUDE_PUBLIC_MLXUTILS_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c new file mode 100644 index 000000000..ba56e72f2 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_blink_leds/mlx_blink_leds.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_blink_leds( + IN mlx_utils *utils, + IN mlx_uint16 secs + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_led_control led_control; + mlx_uint32 reg_status; + + if (utils == NULL ) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + mlx_memory_set(utils, &led_control, 0, sizeof(led_control)); + led_control.beacon_duration = secs; + status = mlx_reg_access(utils, REG_ID_MLCR, REG_ACCESS_WRITE, &led_control, sizeof(led_control), + ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } +reg_err: +bad_param: + return status; +} + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h new file mode 100644 index 000000000..886645fe2 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h @@ -0,0 +1,46 @@ +#ifndef MLX_BLINK_LEDS_H_ +#define MLX_BLINK_LEDS_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_utils.h" + +struct mlx_led_control { + mlx_uint32 reserved1 :16; + mlx_uint32 port :8; + mlx_uint32 bla :8; +/* -------------- */ + mlx_uint32 beacon_duration :16; + mlx_uint32 reserved2 :16; +/* -------------- */ + mlx_uint32 beacon_remain :16; + mlx_uint32 reserved3 :16; +}; + +mlx_status +mlx_blink_leds( + IN mlx_utils *utils, + IN mlx_uint16 secs + ); + +#endif /* MLX_NVCONFIG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c new file mode 100644 index 000000000..d31553024 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_link_speed/mlx_link_speed.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_set_link_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + IN LINK_SPEED speed + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_link_speed link_speed; + mlx_uint32 reg_status; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &link_speed, 0, sizeof(link_speed)); + + link_speed.loacl_port = port_num; + link_speed.proto_mask = 1 << type; + + status = mlx_reg_access(utils, REG_ID_PTYS, REG_ACCESS_READ, &link_speed, + sizeof(link_speed), ®_status); + + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } + switch (speed) { + case LINK_SPEED_1GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_1GB_MASK; + break; + case LINK_SPEED_10GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_10GB_MASK; + break; + case LINK_SPEED_40GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_40GB_MASK; + break; + case LINK_SPEED_100GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_100GB_MASK; + break; + case LINK_SPEED_SDR: + link_speed.ib_proto_admin = link_speed.ib_proto_capability & LINK_SPEED_SDR_MASK; + break; + case LINK_SPEED_DEFAULT: + if (type == LINK_SPEED_ETH) { + link_speed.eth_proto_admin = link_speed.eth_proto_capability; + } else { + link_speed.ib_proto_admin = link_speed.ib_proto_capability; + } + break; + } + status = mlx_reg_access(utils, REG_ID_PTYS, REG_ACCESS_WRITE, &link_speed, + sizeof(link_speed), ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } +reg_err: +bad_param: + return status; +} + +mlx_status +mlx_get_max_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + OUT mlx_uint64 *speed + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_link_speed link_speed; + mlx_uint32 reg_status; + mlx_uint64 speed_giga = 0; + mlx_uint8 lanes_number = 1; + + *speed = 0; + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &link_speed, 0, sizeof(link_speed)); + + link_speed.loacl_port = port_num; + link_speed.proto_mask = 1 << type; + + status = mlx_reg_access(utils, REG_ID_PTYS, REG_ACCESS_READ, &link_speed, + sizeof(link_speed), ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } + + if ( type == LINK_SPEED_ETH ) { + if ( link_speed.eth_proto_capability & LINK_SPEED_100GB_MASK ) { + speed_giga = 100; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_56GB_MASK ) { + speed_giga = 56; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_50GB_MASK ) { + speed_giga = 50; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_40GB_MASK ) { + speed_giga = 40; + } else if (link_speed.eth_proto_capability & LINK_SPEED_25GB_MASK) { + speed_giga = 25; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_20GB_MASK ) { + speed_giga = 20; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_10GB_MASK) { + speed_giga = 10; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_1GB_MASK ) { + speed_giga = 1; + } + } else { + if ( link_speed.ib_proto_capability & LINK_SPEED_EDR_MASK ) { + speed_giga = 25; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_EDR20_MASK ) { + speed_giga = 20; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_FDR_MASK ) { + speed_giga = 14; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_QDR_MASK ) { + speed_giga = 10; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_DDR_MASK ) { + speed_giga = 5; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_SDR_MASK ) { + speed_giga = 2.5; + } + if ( link_speed.ib_link_width_capability & LINK_SPEED_WITDH_12_MASK ) { + lanes_number = 12; + } else if ( link_speed.ib_link_width_capability & LINK_SPEED_WITDH_8_MASK ) { + lanes_number = 8; + } else if (link_speed.ib_link_width_capability & LINK_SPEED_WITDH_4_MASK ) { + lanes_number = 4; + } else if (link_speed.ib_link_width_capability & LINK_SPEED_WITDH_2_MASK ) { + lanes_number = 2; + } else if (link_speed.ib_link_width_capability & LINK_SPEED_WITDH_1_MASK ) { + lanes_number = 1; + } + speed_giga = speed_giga * lanes_number; + } + // Return data in bits + *speed = speed_giga * GIGA_TO_BIT; +reg_err: +bad_param: + return status; +} + + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h new file mode 100644 index 000000000..15b28f57a --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h @@ -0,0 +1,145 @@ +#ifndef MLX_LINK_SPEED_H_ +#define MLX_LINK_SPEED_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_utils.h" + +#define LINK_SPEED_100GB_MASK (ETH_SPEED_ENABLE_MASK_100GBASECR4 | ETH_SPEED_ENABLE_MASK_100GBASESR4 | ETH_SPEED_ENABLE_MASK_100GBASEKR4 | ETH_SPEED_ENABLE_MASK_100GBASELR4) +#define LINK_SPEED_56GB_MASK (ETH_SPEED_ENABLE_MASK_56GBASER4) +#define LINK_SPEED_50GB_MASK (ETH_SPEED_ENABLE_MASK_50GBASECR2 | ETH_SPEED_ENABLE_MASK_50GBASEKR2) +#define LINK_SPEED_40GB_MASK (ETH_SPEED_ENABLE_MASK_40GBASECR4 | ETH_SPEED_ENABLE_MASK_40GBASEKR4 | ETH_SPEED_ENABLE_MASK_40GBASESR4 | ETH_SPEED_ENABLE_MASK_40GBASELR4) +#define LINK_SPEED_25GB_MASK (ETH_SPEED_ENABLE_MASK_25GBASECR | ETH_SPEED_ENABLE_MASK_25GBASEKR | ETH_SPEED_ENABLE_MASK_25GBASESR) +#define LINK_SPEED_20GB_MASK (ETH_SPEED_ENABLE_MASK_20GBASER2) +#define LINK_SPEED_10GB_MASK (ETH_SPEED_ENABLE_MASK_10GBASECR | ETH_SPEED_ENABLE_MASK_10GBASESR | ETH_SPEED_ENABLE_MASK_10GBASELR | ETH_SPEED_ENABLE_MASK_10GBASEKR) +#define LINK_SPEED_1GB_MASK (ETH_SPEED_ENABLE_MASK_1000BASECX | ETH_SPEED_ENABLE_MASK_1000BASEKX | ETH_SPEED_ENABLE_MASK_100BaseTX | ETH_SPEED_ENABLE_MASK_1000BASET) + +#define LINK_SPEED_SDR_MASK 0x1 +#define LINK_SPEED_DDR_MASK 0x2 +#define LINK_SPEED_QDR_MASK 0xC +#define LINK_SPEED_FDR_MASK 0x10 +#define LINK_SPEED_EDR20_MASK 0x200 +#define LINK_SPEED_EDR_MASK 0x20 + +#define LINK_SPEED_WITDH_1_MASK 0x1 +#define LINK_SPEED_WITDH_2_MASK 0x2 +#define LINK_SPEED_WITDH_4_MASK 0x4 +#define LINK_SPEED_WITDH_8_MASK 0x8 +#define LINK_SPEED_WITDH_12_MASK 0x10 + +#define GIGA_TO_BIT 0x40000000 + +enum { + ETH_SPEED_ENABLE_MASK_1000BASECX = 0x0001, + ETH_SPEED_ENABLE_MASK_1000BASEKX = 0x0002, + ETH_SPEED_ENABLE_MASK_10GBASECX4 = 0x0004, + ETH_SPEED_ENABLE_MASK_10GBASEKX4 = 0x0008, + ETH_SPEED_ENABLE_MASK_10GBASEKR = 0x0010, + ETH_SPEED_ENABLE_MASK_20GBASER2 = 0x0020, + ETH_SPEED_ENABLE_MASK_40GBASECR4 = 0x0040, + ETH_SPEED_ENABLE_MASK_40GBASEKR4 = 0x0080, + ETH_SPEED_ENABLE_MASK_56GBASER4 = 0x0100, + ETH_SPEED_ENABLE_MASK_10GBASECR = 0x1000, + ETH_SPEED_ENABLE_MASK_10GBASESR = 0x2000, + ETH_SPEED_ENABLE_MASK_10GBASELR = 0x4000, + ETH_SPEED_ENABLE_MASK_40GBASESR4 = 0x8000, + ETH_SPEED_ENABLE_MASK_40GBASELR4 = 0x10000, + ETH_SPEED_ENABLE_MASK_50GBASEKR4 = 0x80000, + ETH_SPEED_ENABLE_MASK_100GBASECR4 = 0x100000, + ETH_SPEED_ENABLE_MASK_100GBASESR4 = 0x200000, + ETH_SPEED_ENABLE_MASK_100GBASEKR4 = 0x400000, + ETH_SPEED_ENABLE_MASK_100GBASELR4 = 0x800000, + ETH_SPEED_ENABLE_MASK_100BaseTX = 0x1000000, + ETH_SPEED_ENABLE_MASK_1000BASET = 0x2000000, + ETH_SPEED_ENABLE_MASK_10GBASET = 0x4000000, + ETH_SPEED_ENABLE_MASK_25GBASECR = 0x8000000, + ETH_SPEED_ENABLE_MASK_25GBASEKR = 0x10000000, + ETH_SPEED_ENABLE_MASK_25GBASESR = 0x20000000, + ETH_SPEED_ENABLE_MASK_50GBASECR2 = 0x40000000, + ETH_SPEED_ENABLE_MASK_50GBASEKR2 = 0x80000000, + ETH_SPEED_ENABLE_MASK_BAD = 0xffff, +}; + + +typedef enum { + LINK_SPEED_IB = 0, + LINK_SPEED_FC, + LINK_SPEED_ETH, +} LINK_SPEED_TYPE; + +typedef enum { + LINK_SPEED_1GB = 0, + LINK_SPEED_10GB, + LINK_SPEED_40GB, + LINK_SPEED_100GB, + LINK_SPEED_SDR, + LINK_SPEED_DEFAULT, +} LINK_SPEED; + +struct mlx_link_speed { + mlx_uint32 proto_mask :3; + mlx_uint32 reserved1 :13; + mlx_uint32 loacl_port :8; + mlx_uint32 reserved2 :8; + /* -------------- */ + mlx_uint32 reserved3 :32; + /* -------------- */ + mlx_uint32 reserved4 :32; + /* -------------- */ + mlx_uint32 eth_proto_capability :32; + /* -------------- */ + mlx_uint32 ib_proto_capability :16; + mlx_uint32 ib_link_width_capability :16; + /* -------------- */ + mlx_uint32 reserved5 :32; + /* -------------- */ + mlx_uint32 eth_proto_admin :32; + /* -------------- */ + mlx_uint32 ib_proto_admin :16; + mlx_uint32 ib_link_width_admin :16; + /* -------------- */ + mlx_uint32 reserved6 :32; + /* -------------- */ + mlx_uint32 eth_proto_oper :32; + /* -------------- */ + mlx_uint32 ib_proto_oper :16; + mlx_uint32 ib_link_width_oper :16; +}; + +mlx_status +mlx_set_link_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + IN LINK_SPEED speed + ); + +mlx_status +mlx_get_max_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + OUT mlx_uint64 *speed + ); + +#endif /* MLX_LINK_SPEED_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c new file mode 100644 index 000000000..755730284 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_mtu.h" +#include "mlx_memory.h" +#include "mlx_bail.h" + +mlx_status +mlx_get_max_mtu( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + OUT mlx_uint32 *max_mtu + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_mtu mtu; + mlx_uint32 reg_status; + *max_mtu = 0; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &mtu, 0, sizeof(mtu)); + + mtu.local_port = port_num; + + status = mlx_reg_access(utils, REG_ID_PMTU, REG_ACCESS_READ, &mtu, + sizeof(mtu), ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } + // Return data in bits + *max_mtu = mtu.max_mtu * BYTE_TO_BIT; +reg_err: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h new file mode 100644 index 000000000..c6222625c --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h @@ -0,0 +1,52 @@ +#ifndef MLX_MTU_H_ +#define MLX_MTU_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_reg_access.h" +#include "mlx_utils.h" + +#define BYTE_TO_BIT 0x8 + +struct mlx_mtu { + mlx_uint32 reserved1 :16; + mlx_uint32 local_port :8; + mlx_uint32 reserved2 :8; + /* -------------- */ + mlx_uint32 reserved3 :16; + mlx_uint32 max_mtu :16; + /* -------------- */ + mlx_uint32 reserved4 :16; + mlx_uint32 admin_mtu :16; + /* -------------- */ + mlx_uint32 reserved5 :16; + mlx_uint32 oper_mtu :16; +}; + +mlx_status +mlx_get_max_mtu( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + OUT mlx_uint32 *max_mtu + ); + +#endif /* MLX_MTU_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c new file mode 100644 index 000000000..2277e0c76 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +#define TlvMappingEntry( _tlv_type, _real_tlv_type, _class_code, _fw_reset_needed) { \ + .tlv_type = _tlv_type, \ + .real_tlv_type = _real_tlv_type, \ + .class_code = _class_code, \ + .fw_reset_needed = _fw_reset_needed, \ + } + +struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { + TlvMappingEntry(0x10, 0x10, NVRAM_TLV_CLASS_HOST, TRUE), + TlvMappingEntry(0x12, 0x12, NVRAM_TLV_CLASS_PHYSICAL_PORT, TRUE), + TlvMappingEntry(0x80, 0x80, NVRAM_TLV_CLASS_GLOBAL, TRUE), + TlvMappingEntry(0x81, 0x81, NVRAM_TLV_CLASS_GLOBAL, TRUE), + TlvMappingEntry(0x100, 0x100, NVRAM_TLV_CLASS_GLOBAL, TRUE), + TlvMappingEntry(0x2001, 0x195, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2010, 0x210, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2011, 0x211, NVRAM_TLV_CLASS_GLOBAL, FALSE), + TlvMappingEntry(0x2020, 0x2020, NVRAM_TLV_CLASS_PHYSICAL_PORT, FALSE), + TlvMappingEntry(0x2021, 0x221, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2023, 0x223, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2100, 0x230, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2101, 0x231, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2102, 0x232, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2103, 0x233, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2104, 0x234, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2105, 0x235, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2106, 0x236, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2107, 0x237, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2108, 0x238, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2109, 0x239, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x210A, 0x23A, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2200, 0x240, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2201, 0x241, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2202, 0x242, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2203, 0x243, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2204, 0x244, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2205, 0x245, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2207, 0x247, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0, 0, 0, 0), +}; + +static +mlx_status +nvconfig_set_fw_reset_level( + IN mlx_utils *utils, + IN mlx_uint16 tlv_type + ) +{ +#define WARM_REBOOT_RESET ((mlx_uint64)0x1 << 38) + mlx_status status = MLX_SUCCESS; + mlx_uint32 reg_status; + mlx_uint64 mfrl = WARM_REBOOT_RESET ; + mlx_uint8 index = 0; + mlx_boolean reset_needed = FALSE; + + for (index = 0 ; nvconfig_tlv_mapping[index].tlv_type != 0 ; index++) { + if (nvconfig_tlv_mapping[index].tlv_type == tlv_type) { + reset_needed = nvconfig_tlv_mapping[index].fw_reset_needed; + } + } + + if (reset_needed == FALSE) { + goto no_fw_reset_needed; + } + status = mlx_reg_access(utils, REG_ID_MFRL, REG_ACCESS_WRITE, &mfrl, sizeof(mfrl), + ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"nvconfig_set_fw_reset_level failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } +reg_err: +no_fw_reset_needed: + return status; +} + + +static +mlx_status +nvconfig_get_tlv_type_and_class( + IN mlx_uint16 tlv_type, + OUT mlx_uint16 *real_tlv_type, + OUT NVRAM_CLASS_CODE *class_code + ) +{ + mlx_uint8 index = 0; + for ( ; nvconfig_tlv_mapping[index].tlv_type != 0 ; index ++) { + if ( nvconfig_tlv_mapping[index].tlv_type == tlv_type) { + *real_tlv_type = nvconfig_tlv_mapping[index].real_tlv_type; + *class_code = nvconfig_tlv_mapping[index].class_code; + return MLX_SUCCESS; + } + } + return MLX_NOT_FOUND; +} +static +void +nvconfig_fill_tlv_type( + IN mlx_uint8 port, + IN NVRAM_CLASS_CODE class_code, + IN mlx_uint16 tlv_type, + OUT union nvconfig_tlv_type *nvconfig_tlv_type + ) +{ + switch (class_code) { + case NVRAM_TLV_CLASS_GLOBAL: + nvconfig_tlv_type->global.param_class = NVRAM_TLV_CLASS_GLOBAL; + nvconfig_tlv_type->global.param_idx = tlv_type; + break; + case NVRAM_TLV_CLASS_HOST: + nvconfig_tlv_type->per_host.param_class = NVRAM_TLV_CLASS_HOST; + nvconfig_tlv_type->per_host.param_idx = tlv_type; + break; + case NVRAM_TLV_CLASS_PHYSICAL_PORT: + nvconfig_tlv_type->per_port.param_class = NVRAM_TLV_CLASS_PHYSICAL_PORT; + nvconfig_tlv_type->per_port.param_idx = tlv_type; + nvconfig_tlv_type->per_port.port = port; + break; + } +} +mlx_status +nvconfig_query_capability( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + OUT mlx_boolean *read_supported, + OUT mlx_boolean *write_supported + ) +{ + mlx_status status = MLX_SUCCESS; + struct nvconfig_nvqc nvqc; + mlx_uint32 reg_status; + NVRAM_CLASS_CODE class_code; + mlx_uint16 real_tlv_type; + + if (utils == NULL || read_supported == NULL || write_supported == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nvconfig_get_tlv_type_and_class(tlv_type, &real_tlv_type, &class_code); + MLX_CHECK_STATUS(utils, status, tlv_not_supported, "tlv not supported"); + + mlx_memory_set(utils, &nvqc, 0, sizeof(nvqc)); + nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nvqc.tlv_type); + + status = mlx_reg_access(utils, REG_ID_NVQC, REG_ACCESS_READ, &nvqc, sizeof(nvqc), + ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } + *read_supported = nvqc.support_rd; + *write_supported = nvqc.support_wr; +reg_err: +tlv_not_supported: +bad_param: + return status; +} + +mlx_status +nvconfig_nvdata_invalidate( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type + ) +{ + mlx_status status = MLX_SUCCESS; + struct nvconfig_header nv_header; + mlx_uint32 reg_status; + NVRAM_CLASS_CODE class_code; + mlx_uint16 real_tlv_type; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nvconfig_get_tlv_type_and_class(tlv_type, &real_tlv_type, &class_code); + MLX_CHECK_STATUS(utils, status, tlv_not_supported, "tlv not supported"); + + mlx_memory_set(utils, &nv_header, 0, sizeof(nv_header)); + nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nv_header.tlv_type); + + status = mlx_reg_access(utils, REG_ID_NVDI, REG_ACCESS_WRITE, &nv_header, sizeof(nv_header), + ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } +reg_err: +tlv_not_supported: +bad_param: + return status; +} + +mlx_status +nvconfig_nvdata_access( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + IN REG_ACCESS_OPT opt, + IN mlx_size data_size, + IN NV_DEFAULT_OPT def_en, + IN OUT mlx_uint8 *version, + IN OUT mlx_void *data + ) +{ + mlx_status status = MLX_SUCCESS; + struct nvconfig_nvda nvda; + mlx_uint32 reg_status; + mlx_uint32 real_size_to_read; + mlx_uint32 index; + NVRAM_CLASS_CODE class_code; + mlx_uint16 real_tlv_type; + mlx_size data_size_align_to_dword; + + if (utils == NULL || data == NULL || data_size > NVCONFIG_MAX_TLV_SIZE) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nvconfig_get_tlv_type_and_class(tlv_type, &real_tlv_type, &class_code); + MLX_CHECK_STATUS(utils, status, tlv_not_supported, "tlv not supported"); + + data_size_align_to_dword = ((data_size + 3) / sizeof(mlx_uint32)) * sizeof(mlx_uint32); + mlx_memory_set(utils, &nvda, 0, sizeof(nvda)); + nvda.nv_header.length = data_size_align_to_dword; + nvda.nv_header.rd_en = 0; + nvda.nv_header.def_en = def_en; + nvda.nv_header.over_en = 1; + nvda.nv_header.version = *version; + + nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nvda.nv_header.tlv_type); + + mlx_memory_cpy(utils, nvda.data, data, data_size); + for (index = 0 ; index * 4 < NVCONFIG_MAX_TLV_SIZE ; index++) { + mlx_memory_be32_to_cpu(utils,(((mlx_uint32 *)nvda.data)[index]), ((mlx_uint32 *)nvda.data) + index); + } + status = mlx_reg_access(utils, REG_ID_NVDA, opt, &nvda, + data_size_align_to_dword + sizeof(nvda.nv_header), ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } + for (index = 0 ; index * 4 < NVCONFIG_MAX_TLV_SIZE ; index++) { + mlx_memory_cpu_to_be32(utils,(((mlx_uint32 *)nvda.data)[index]), ((mlx_uint32 *)nvda.data) + index); + } + if (opt == REG_ACCESS_READ) { + real_size_to_read = (nvda.nv_header.length > data_size) ? data_size : + nvda.nv_header.length; + mlx_memory_cpy(utils, data, nvda.data, real_size_to_read); + *version = nvda.nv_header.version; + } else { + nvconfig_set_fw_reset_level(utils, tlv_type); + } +reg_err: +tlv_not_supported: +bad_param: + return status; +} + + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h new file mode 100644 index 000000000..8333e8368 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h @@ -0,0 +1,140 @@ +#ifndef MLX_NVCONFIG_H_ +#define MLX_NVCONFIG_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_utils.h" + +typedef enum { + NVRAM_TLV_CLASS_GLOBAL = 0, + NVRAM_TLV_CLASS_PHYSICAL_PORT = 1, + NVRAM_TLV_CLASS_HOST = 3, +} NVRAM_CLASS_CODE; + +struct nvconfig_tlv_type_per_port { + mlx_uint32 param_idx :16; + mlx_uint32 port :8; + mlx_uint32 param_class :8; +}; + +struct nvconfig_tlv_type_per_host { + mlx_uint32 param_idx :10; + mlx_uint32 function :8; + mlx_uint32 host :6; + mlx_uint32 param_class :8; +}; + +struct nvconfig_tlv_type_global { + mlx_uint32 param_idx :24; + mlx_uint32 param_class :8; +}; + +struct nvconfig_tlv_mapping{ + mlx_uint16 tlv_type; + mlx_uint16 real_tlv_type; + NVRAM_CLASS_CODE class_code; + mlx_boolean fw_reset_needed; +}; + +union nvconfig_tlv_type { + struct nvconfig_tlv_type_per_port per_port; + struct nvconfig_tlv_type_per_host per_host; + struct nvconfig_tlv_type_global global; +}; + + +struct nvconfig_nvqc { + union nvconfig_tlv_type tlv_type; +/* -------------- */ + mlx_uint32 support_rd :1; /*the configuration item is supported and can be read */ + mlx_uint32 support_wr :1; /*the configuration item is supported and can be updated */ + mlx_uint32 reserved1 :2; + mlx_uint32 version :4; /*The maximum version of the configuration item currently supported by the firmware. */ + mlx_uint32 reserved2 :24; +}; + + +struct nvconfig_header { + mlx_uint32 length :9; /*Size of configuration item data in bytes between 0..256 */ + mlx_uint32 reserved0 :3; + mlx_uint32 version :4; /* Configuration item version */ + mlx_uint32 reserved1 :7; + + mlx_uint32 def_en :1; /*Choose whether to access the default value or the user-defined value. + 0x0 Read or write the user-defined value. + 0x1 Read the default value (only valid for reads).*/ + + mlx_uint32 rd_en :1; /*enables reading the TLV by lower priorities + 0 - TLV can be read by the subsequent lifecycle priorities. + 1 - TLV cannot be read by the subsequent lifecycle priorities. */ + mlx_uint32 over_en :1; /*enables overwriting the TLV by lower priorities + 0 - Can only be overwritten by the current lifecycle priority + 1 - Allowed to be overwritten by subsequent lifecycle priorities */ + mlx_uint32 header_type :2; + mlx_uint32 priority :2; + mlx_uint32 valid :2; +/* -------------- */ + union nvconfig_tlv_type tlv_type;; +/* -------------- */ + mlx_uint32 crc :16; + mlx_uint32 reserved :16; +}; + +#define NVCONFIG_MAX_TLV_SIZE 256 + +struct nvconfig_nvda { + struct nvconfig_header nv_header; + mlx_uint8 data[NVCONFIG_MAX_TLV_SIZE]; +}; + + +mlx_status +nvconfig_query_capability( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + OUT mlx_boolean *read_supported, + OUT mlx_boolean *write_supported + ); + + +mlx_status +nvconfig_nvdata_invalidate( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type + ); + +mlx_status +nvconfig_nvdata_access( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + IN REG_ACCESS_OPT opt, + IN mlx_size data_size, + IN NV_DEFAULT_OPT def_en, + IN OUT mlx_uint8 *version, + IN OUT mlx_void *data + ); + +#endif /* MLX_NVCONFIG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c new file mode 100644 index 000000000..77eda8a5c --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE( GPL2_OR_LATER); + +#include "../../mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" +#include "../../mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" + +struct tlv_default { + mlx_uint16 tlv_type; + mlx_size data_size; + mlx_status (*set_defaults)( IN void *data, IN int status, + OUT void *def_struct); +}; + +#define TlvDefaultEntry( _tlv_type, _data_size, _set_defaults) { \ + .tlv_type = _tlv_type, \ + .data_size = sizeof ( _data_size ), \ + .set_defaults = _set_defaults, \ + } + +static +mlx_status +nvconfig_get_boot_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_nic_boot_conf *nic_boot_conf = + (union mlx_nvconfig_nic_boot_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + /* boot_option_rom_en is deprecated - enabled always */ + port_conf_def->boot_option_rom_en = DEFAULT_OPTION_ROM_EN; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + port_conf_def->boot_vlan = nic_boot_conf->vlan_id; + port_conf_def->boot_protocol = nic_boot_conf->legacy_boot_prot; + port_conf_def->boot_retry_count = nic_boot_conf->boot_retry_count; + port_conf_def->boot_vlan_en = nic_boot_conf->en_vlan; + + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->boot_vlan = DEFAULT_BOOT_VLAN; + port_conf_def->boot_protocol = DEFAULT_BOOT_PROTOCOL; + + return status; +} + +static +mlx_status +nvconfig_get_boot_ext_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_nic_boot_ext_conf *nic_boot_ext_conf = + (union mlx_nvconfig_nic_boot_ext_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + port_conf_def->linkup_timeout = nic_boot_ext_conf->linkup_timeout; + port_conf_def->ip_ver = nic_boot_ext_conf->ip_ver; + + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->linkup_timeout = DEFAULT_BOOT_LINK_UP_TO; + port_conf_def->ip_ver = DEFAULT_BOOT_IP_VER; + + return status; +} + +static +mlx_status +nvconfig_get_iscsi_init_dhcp_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_iscsi_init_dhcp_conf *iscsi_init_dhcp_conf = + (union mlx_nvconfig_iscsi_init_dhcp_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + port_conf_def->iscsi_dhcp_params_en = iscsi_init_dhcp_conf->dhcp_iscsi_en; + port_conf_def->iscsi_ipv4_dhcp_en = iscsi_init_dhcp_conf->ipv4_dhcp_en; + + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->iscsi_dhcp_params_en = DEFAULT_ISCSI_DHCP_PARAM_EN; + port_conf_def->iscsi_ipv4_dhcp_en = DEFAULT_ISCSI_IPV4_DHCP_EN; + + return status; +} + +static +mlx_status +nvconfig_get_ib_boot_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_nic_ib_boot_conf *ib_boot_conf = + (union mlx_nvconfig_nic_ib_boot_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->boot_pkey = ib_boot_conf->boot_pkey; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_wol_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_wol_conf *wol_conf = (union mlx_nvconfig_wol_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->en_wol_magic = wol_conf->en_wol_magic; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_iscsi_gen_default_conf( + IN void *data, + IN int status, + OUT void *def_struct) +{ + union mlx_nvconfig_iscsi_general *iscsi_gen = + (union mlx_nvconfig_iscsi_general *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->iscsi_boot_to_target = iscsi_gen->boot_to_target; + port_conf_def->iscsi_vlan_en = iscsi_gen->vlan_en; + port_conf_def->iscsi_tcp_timestamps_en = iscsi_gen->tcp_timestamps_en; + port_conf_def->iscsi_chap_mutual_auth_en = iscsi_gen->chap_mutual_auth_en; + port_conf_def->iscsi_chap_auth_en = iscsi_gen->chap_auth_en; + port_conf_def->iscsi_lun_busy_retry_count = iscsi_gen->lun_busy_retry_count; + port_conf_def->iscsi_link_up_delay_time = iscsi_gen->link_up_delay_time; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_ib_dhcp_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_ib_dhcp_conf *ib_dhcp = + (union mlx_nvconfig_ib_dhcp_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->client_identifier = ib_dhcp->client_identifier; + port_conf_def->mac_admin_bit = ib_dhcp->mac_admin_bit; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_ocsd_ocbb_default_conf( IN void *data, + IN int status, OUT void *def_struct) { + union mlx_nvconfig_ocsd_ocbb_conf *ocsd_ocbb = + (union mlx_nvconfig_ocsd_ocbb_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->ocsd_ocbb_en = ocsd_ocbb->ocsd_ocbb_en; + + return MLX_SUCCESS; + +nvdata_access_err: + conf_def->ocsd_ocbb_en = DEFAULT_OCSD_OCBB_EN; + + return status; +} + +static +mlx_status +nvconfig_get_vpi_link_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_vpi_link_conf *vpi_link = + (union mlx_nvconfig_vpi_link_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->network_link_type = vpi_link->network_link_type; + port_conf_def->default_link_type = vpi_link->default_link_type; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_rom_banner_to_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_rom_banner_timeout_conf *rom_banner_timeout_conf = + (union mlx_nvconfig_rom_banner_timeout_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->flexboot_menu_to = rom_banner_timeout_conf->rom_banner_to; + + return MLX_SUCCESS; + +nvdata_access_err: + conf_def->flexboot_menu_to = DEFAULT_FLEXBOOT_MENU_TO; + + return status; +} + +static +mlx_status +nvconfig_get_nv_virt_caps_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_virt_caps *nv_virt_caps = + (union mlx_nvconfig_virt_caps *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->max_vfs = nv_virt_caps->max_vfs_per_pf; + + return MLX_SUCCESS; + +nvdata_access_err: + conf_def->max_vfs = DEFAULT_MAX_VFS; + + return status; +} + +static +mlx_status +nvconfig_get_nv_virt_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_virt_conf *nv_virt_conf = + (union mlx_nvconfig_virt_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + conf_def->total_vfs = nv_virt_conf->num_of_vfs; + conf_def->sriov_en = nv_virt_conf->virt_mode; + +nvdata_access_err: + return status; +} + +static struct tlv_default tlv_port_defaults[] = { + TlvDefaultEntry(BOOT_SETTINGS_TYPE, union mlx_nvconfig_nic_boot_conf, &nvconfig_get_boot_default_conf), + TlvDefaultEntry(BOOT_SETTINGS_EXT_TYPE, union mlx_nvconfig_nic_boot_ext_conf, &nvconfig_get_boot_ext_default_conf), + TlvDefaultEntry(ISCSI_INITIATOR_DHCP_CONF_TYPE, union mlx_nvconfig_iscsi_init_dhcp_conf, &nvconfig_get_iscsi_init_dhcp_default_conf), + TlvDefaultEntry(IB_BOOT_SETTING_TYPE, union mlx_nvconfig_nic_ib_boot_conf, &nvconfig_get_ib_boot_default_conf), + TlvDefaultEntry(WAKE_ON_LAN_TYPE, union mlx_nvconfig_wol_conf, &nvconfig_get_wol_default_conf), + TlvDefaultEntry(ISCSI_GENERAL_SETTINGS_TYPE, union mlx_nvconfig_iscsi_general, &nvconfig_get_iscsi_gen_default_conf), + TlvDefaultEntry(IB_DHCP_SETTINGS_TYPE, union mlx_nvconfig_ib_dhcp_conf, &nvconfig_get_ib_dhcp_default_conf), + TlvDefaultEntry(VPI_LINK_TYPE, union mlx_nvconfig_vpi_link_conf, &nvconfig_get_vpi_link_default_conf), +}; + +static struct tlv_default tlv_general_defaults[] = { + TlvDefaultEntry(BANNER_TO_TYPE, union mlx_nvconfig_rom_banner_timeout_conf, &nvconfig_get_rom_banner_to_default_conf), + TlvDefaultEntry(GLOPAL_PCI_CAPS_TYPE, union mlx_nvconfig_virt_caps, &nvconfig_get_nv_virt_caps_default_conf), + TlvDefaultEntry(GLOPAL_PCI_SETTINGS_TYPE, union mlx_nvconfig_virt_conf, &nvconfig_get_nv_virt_default_conf), + TlvDefaultEntry(OCSD_OCBB_TYPE, union mlx_nvconfig_ocsd_ocbb_conf, &nvconfig_get_ocsd_ocbb_default_conf), +}; + +static +mlx_status +nvconfig_nvdata_default_access( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + IN mlx_size data_size, + OUT mlx_void *data + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 index; + mlx_uint8 version = 0; + + status = nvconfig_nvdata_access(utils, port, tlv_type, REG_ACCESS_READ, + data_size, TLV_ACCESS_DEFAULT_EN, &version, data); + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_access failed "); + for (index = 0; index * 4 < data_size; index++) { + mlx_memory_be32_to_cpu(utils, (((mlx_uint32 *) data)[index]), + ((mlx_uint32 *) data) + index); + } + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_nvdata_read_default_value( + IN mlx_utils *utils, + IN mlx_uint8 modifier, + IN struct tlv_default *def, + OUT void *def_struct + ) +{ + mlx_status status = MLX_SUCCESS; + void *data = NULL; + + status = mlx_memory_zalloc(utils, def->data_size,&data); + MLX_CHECK_STATUS(utils, status, memory_err, + "mlx_memory_zalloc failed "); + status = nvconfig_nvdata_default_access(utils, modifier, def->tlv_type, + def->data_size, data); + def->set_defaults(data, status, def_struct); + mlx_memory_free(utils, &data); + +memory_err: + return status; +} + +static +void +nvconfig_nvdata_read_default_values( + IN mlx_utils *utils, + IN mlx_uint8 modifier, + IN struct tlv_default defaults_table[], + IN mlx_uint8 defaults_table_size, + OUT void *def_strct + ) +{ + struct tlv_default *defs; + unsigned int i; + + for (i = 0; i < defaults_table_size; i++) { + defs = &defaults_table[i]; + nvconfig_nvdata_read_default_value(utils, modifier, defs, def_strct); + } +} + +mlx_status +nvconfig_read_port_default_values( + IN mlx_utils *utils, + IN mlx_uint8 port, + OUT struct mlx_nvconfig_port_conf_defaults *port_conf_def + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL || port_conf_def == NULL) { + status = MLX_INVALID_PARAMETER; + MLX_DEBUG_ERROR(utils,"bad params."); + goto bad_param; + } + mlx_memory_set(utils, port_conf_def, 0, sizeof(*port_conf_def)); + nvconfig_nvdata_read_default_values(utils, port, tlv_port_defaults, + (sizeof(tlv_port_defaults)/sizeof(tlv_port_defaults[0])), + port_conf_def); + +bad_param: + return status; +} + +mlx_status +nvconfig_read_general_default_values( + IN mlx_utils *utils, + OUT struct mlx_nvconfig_conf_defaults *conf_def + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL || conf_def == NULL) { + status = MLX_INVALID_PARAMETER; + MLX_DEBUG_ERROR(utils,"bad params."); + goto bad_param; + } + mlx_memory_set(utils, conf_def, 0, sizeof(*conf_def)); + nvconfig_nvdata_read_default_values(utils, 0, tlv_general_defaults, + (sizeof(tlv_general_defaults)/sizeof(tlv_general_defaults[0])), + conf_def); + +bad_param: + return status; +} + +mlx_status +nvconfig_read_rom_ini_values( + IN mlx_utils *utils, + OUT struct mlx_nvcofnig_romini *rom_ini + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL || rom_ini == NULL) { + status = MLX_INVALID_PARAMETER; + MLX_DEBUG_ERROR(utils,"bad params."); + goto bad_param; + } + mlx_memory_set(utils, rom_ini, 0, sizeof(*rom_ini)); + + status = nvconfig_nvdata_default_access(utils, 0, GLOBAL_ROM_INI_TYPE, + sizeof(*rom_ini), rom_ini); +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h new file mode 100644 index 000000000..163c2a35f --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h @@ -0,0 +1,94 @@ +#ifndef MLX_NVCONFIG_DEFAULTS_H_ +#define MLX_NVCONFIG_DEFAULTS_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); +#include "mlx_nvconfig_prm.h" +/* + * Default values + */ +#define DEFAULT_FLEXBOOT_MENU_TO 4 +#define DEFAULT_MAX_VFS 8 +#define DEFAULT_BOOT_PROTOCOL 1 +#define DEFAULT_OPTION_ROM_EN 1 +#define DEFAULT_BOOT_VLAN 1 +#define DEFAULT_ISCSI_DHCP_PARAM_EN 1 +#define DEFAULT_ISCSI_IPV4_DHCP_EN 1 +#define DEFAULT_OCSD_OCBB_EN 1 +#define DEFAULT_BOOT_IP_VER 0 +#define DEFAULT_BOOT_LINK_UP_TO 0 + +struct mlx_nvconfig_port_conf_defaults { + mlx_uint8 pptx; + mlx_uint8 pprx; + mlx_boolean boot_option_rom_en; + mlx_boolean boot_vlan_en; + mlx_uint8 boot_retry_count; + mlx_uint8 boot_protocol; + mlx_uint8 boot_vlan; + mlx_uint8 boot_pkey; + mlx_boolean en_wol_magic; + mlx_uint8 network_link_type; + mlx_uint8 iscsi_boot_to_target; + mlx_boolean iscsi_vlan_en; + mlx_boolean iscsi_tcp_timestamps_en; + mlx_boolean iscsi_chap_mutual_auth_en; + mlx_boolean iscsi_chap_auth_en; + mlx_boolean iscsi_dhcp_params_en; + mlx_boolean iscsi_ipv4_dhcp_en; + mlx_uint8 iscsi_lun_busy_retry_count; + mlx_uint8 iscsi_link_up_delay_time; + mlx_uint8 client_identifier; + mlx_uint8 mac_admin_bit; + mlx_uint8 default_link_type; + mlx_uint8 linkup_timeout; + mlx_uint8 ip_ver; +}; + +struct mlx_nvconfig_conf_defaults { + mlx_uint8 max_vfs; + mlx_uint8 total_vfs; + mlx_uint8 sriov_en; + mlx_uint8 maximum_uar_bar_size; + mlx_uint8 uar_bar_size; + mlx_uint8 flexboot_menu_to; + mlx_boolean ocsd_ocbb_en; +}; + +mlx_status +nvconfig_read_port_default_values( + IN mlx_utils *utils, + IN mlx_uint8 port, + OUT struct mlx_nvconfig_port_conf_defaults *port_conf_def + ); + +mlx_status +nvconfig_read_general_default_values( + IN mlx_utils *utils, + OUT struct mlx_nvconfig_conf_defaults *conf_def + ); + +mlx_status +nvconfig_read_rom_ini_values( + IN mlx_utils *utils, + OUT struct mlx_nvcofnig_romini *rom_ini + ); +#endif /* MLX_NVCONFIG_DEFAULTS_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h new file mode 100644 index 000000000..5b3af1e78 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h @@ -0,0 +1,259 @@ +#ifndef MLX_NVCONFIG_PRM_H_ +#define MLX_NVCONFIG_PRM_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../include/public/mlx_types.h" + +enum { + WAKE_ON_LAN_TYPE = 0x10, + VIRTUALIZATION_TYPE = 0x11, + VPI_LINK_TYPE = 0x12, + BOOT_SETTINGS_EXT_TYPE = 0x2001, + BANNER_TO_TYPE = 0x2010, + OCSD_OCBB_TYPE = 0x2011, + FLOW_CONTROL_TYPE = 0x2020, + BOOT_SETTINGS_TYPE = 0x2021, + ISCSI_GENERAL_SETTINGS_TYPE = 0x2100, + IB_BOOT_SETTING_TYPE = 0x2022, + IB_DHCP_SETTINGS_TYPE = 0x2023, + GLOPAL_PCI_SETTINGS_TYPE = 0x80, + GLOPAL_PCI_CAPS_TYPE = 0x81, + GLOBAL_ROM_INI_TYPE = 0x100, + + // Types for iSCSI strings + DHCP_VEND_ID = 0x2101, + ISCSI_INITIATOR_IPV4_ADDR = 0x2102, + ISCSI_INITIATOR_SUBNET = 0x2103, + ISCSI_INITIATOR_IPV4_GATEWAY = 0x2104, + ISCSI_INITIATOR_IPV4_PRIM_DNS = 0x2105, + ISCSI_INITIATOR_IPV4_SECDNS = 0x2106, + ISCSI_INITIATOR_NAME = 0x2107, + ISCSI_INITIATOR_CHAP_ID = 0x2108, + ISCSI_INITIATOR_CHAP_PWD = 0x2109, + ISCSI_INITIATOR_DHCP_CONF_TYPE = 0x210a, + + CONNECT_FIRST_TGT = 0x2200, + FIRST_TGT_IP_ADDRESS = 0x2201, + FIRST_TGT_TCP_PORT = 0x2202, + FIRST_TGT_BOOT_LUN = 0x2203, + FIRST_TGT_ISCSI_NAME = 0x2204, + FIRST_TGT_CHAP_ID = 0x2205, + FIRST_TGT_CHAP_PWD = 0x2207, +}; + +union mlx_nvconfig_nic_boot_conf { + struct { + mlx_uint32 vlan_id : 12; + mlx_uint32 link_speed : 4; + mlx_uint32 legacy_boot_prot : 8; + mlx_uint32 boot_retry_count : 3; + mlx_uint32 boot_strap_type : 3; + mlx_uint32 en_vlan : 1; + mlx_uint32 en_option_rom : 1; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_nic_boot_ext_conf { + struct { + mlx_uint32 linkup_timeout : 8; + mlx_uint32 ip_ver : 2; + mlx_uint32 reserved0 : 22; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_rom_banner_timeout_conf { + struct { + mlx_uint32 rom_banner_to : 4; + mlx_uint32 reserved : 28; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_virt_conf { + struct { + mlx_uint32 reserved0 :24; + mlx_uint32 pf_bar_size_valid :1; + mlx_uint32 vf_bar_size_valid :1; + mlx_uint32 num_pf_msix_valid :1; + mlx_uint32 num_vf_msix_valid :1; + mlx_uint32 num_pfs_valid :1; + mlx_uint32 fpp_valid :1; + mlx_uint32 full_vf_qos_valid :1; + mlx_uint32 sriov_valid :1; + /*-------------------*/ + mlx_uint32 num_of_vfs :16; + mlx_uint32 num_of_pfs :4; + mlx_uint32 reserved1 :9; + mlx_uint32 fpp_en :1; + mlx_uint32 full_vf_qos :1; + mlx_uint32 virt_mode :1; //sriov_en + /*-------------------*/ + mlx_uint32 log_pf_uar_bar_size :6; + mlx_uint32 log_vf_uar_bar_size :6; + mlx_uint32 num_pf_msix :10; + mlx_uint32 num_vf_msix :10; + }; + mlx_uint32 dword[3]; +}; + +union mlx_nvconfig_virt_caps { + struct { + mlx_uint32 reserved0 :24; + mlx_uint32 max_vfs_per_pf_valid :1; + mlx_uint32 max_total_msix_valid :1; + mlx_uint32 max_total_bar_valid :1; + mlx_uint32 num_pfs_supported :1; + mlx_uint32 num_vf_msix_supported :1; + mlx_uint32 num_pf_msix_supported :1; + mlx_uint32 vf_bar_size_supported :1; + mlx_uint32 pf_bar_size_supported :1; + /*-------------------*/ + mlx_uint32 max_vfs_per_pf :16; + mlx_uint32 max_num_pfs :4; + mlx_uint32 reserved1 :9; + mlx_uint32 fpp_support :1; + mlx_uint32 vf_qos_control_support :1; + mlx_uint32 sriov_support :1; + /*-------------------*/ + mlx_uint32 max_log_pf_uar_bar_size :6; + mlx_uint32 max_log_vf_uar_bar_size :6; + mlx_uint32 max_num_pf_msix :10; + mlx_uint32 max_num_vf_msix :10; + /*-------------------*/ + mlx_uint32 max_total_msix; + /*-------------------*/ + mlx_uint32 max_total_bar; + }; + mlx_uint32 dword[5]; +}; + +union mlx_nvconfig_iscsi_init_dhcp_conf { + struct { + mlx_uint32 reserved0 :30; + mlx_uint32 dhcp_iscsi_en :1; + mlx_uint32 ipv4_dhcp_en :1; + + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_nic_ib_boot_conf { + struct { + mlx_uint32 boot_pkey : 16; + mlx_uint32 reserved0 : 16; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_wol_conf { + struct { + mlx_uint32 reserved0 :9; + mlx_uint32 en_wol_passwd :1; + mlx_uint32 en_wol_magic :1; + mlx_uint32 reserved1 :21; + mlx_uint32 reserved2 :32; + }; + mlx_uint32 dword[2]; +}; + +union mlx_nvconfig_iscsi_general { + struct { + mlx_uint32 reserved0 :22; + mlx_uint32 boot_to_target :2; + mlx_uint32 reserved1 :2; + mlx_uint32 vlan_en :1; + mlx_uint32 tcp_timestamps_en :1; + mlx_uint32 chap_mutual_auth_en :1; + mlx_uint32 chap_auth_en :1; + mlx_uint32 reserved2 :2; + /*-------------------*/ + mlx_uint32 vlan :12; + mlx_uint32 reserved3 :20; + /*-------------------*/ + mlx_uint32 lun_busy_retry_count:8; + mlx_uint32 link_up_delay_time :8; + mlx_uint32 reserved4 :16; + }; + mlx_uint32 dword[3]; +}; + +union mlx_nvconfig_ib_dhcp_conf { + struct { + mlx_uint32 reserved :24; + mlx_uint32 client_identifier :4; + mlx_uint32 mac_admin_bit :4; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_ocsd_ocbb_conf { + struct { + mlx_uint32 reserved :31; + mlx_uint32 ocsd_ocbb_en :1; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_vpi_link_conf { + struct { + mlx_uint32 network_link_type :2; + mlx_uint32 default_link_type :2; + mlx_uint32 reserved :28; + }; + mlx_uint32 dword; +}; + +struct mlx_nvcofnig_romini { + mlx_uint32 reserved0 :1; + mlx_uint32 shared_memory_en :1; + mlx_uint32 hii_vpi_en :1; + mlx_uint32 tech_enum :1; + mlx_uint32 reserved1 :4; + mlx_uint32 static_component_name_string :1; + mlx_uint32 hii_iscsi_configuration :1; + mlx_uint32 hii_ibm_aim :1; + mlx_uint32 hii_platform_setup :1; + mlx_uint32 hii_bdf_decimal :1; + mlx_uint32 hii_read_only :1; + mlx_uint32 reserved2 :10; + mlx_uint32 mac_enum :1; + mlx_uint32 port_enum :1; + mlx_uint32 flash_en :1; + mlx_uint32 fmp_en :1; + mlx_uint32 bofm_en :1; + mlx_uint32 platform_to_driver_en :1; + mlx_uint32 hii_en :1; + mlx_uint32 undi_en :1; + /* -------------- */ + mlx_uint64 dhcp_user_class; + /* -------------- */ + mlx_uint32 reserved3 :22; + mlx_uint32 uri_boot_retry_delay :4; + mlx_uint32 uri_boot_retry :4; + mlx_uint32 option_rom_debug :1; + mlx_uint32 promiscuous_vlan :1; +}; + +#endif /* MLX_NVCONFIG_PRM_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c new file mode 100644 index 000000000..3852efbf1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_ocbb.h" +#include "mlx_icmd.h" +#include "mlx_bail.h" + +mlx_status +mlx_ocbb_init ( + IN mlx_utils *utils, + IN mlx_uint64 address + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_ocbb_init ocbb_init; + ocbb_init.address_hi = (mlx_uint32)(address >> 32); + ocbb_init.address_lo = (mlx_uint32)address; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + OCBB_INIT, + &ocbb_init, + sizeof(ocbb_init), + 0 + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} + +mlx_status +mlx_ocbb_query_header_status ( + IN mlx_utils *utils, + OUT mlx_uint8 *ocbb_status + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_ocbb_query_status ocbb_query_status; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + OCBB_QUERY_HEADER_STATUS, + &ocbb_query_status, + 0, + sizeof(ocbb_query_status) + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); + *ocbb_status = ocbb_query_status.status; +icmd_err: +bad_param: + return status; +} + +mlx_status +mlx_ocbb_query_etoc_status ( + IN mlx_utils *utils, + OUT mlx_uint8 *ocbb_status + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_ocbb_query_status ocbb_query_status; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + OCBB_QUERY_ETOC_STATUS, + &ocbb_query_status, + 0, + sizeof(ocbb_query_status) + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); + *ocbb_status = ocbb_query_status.status; +icmd_err: +bad_param: + return status; +} + +mlx_status +mlx_ocbb_set_event ( + IN mlx_utils *utils, + IN mlx_uint64 event_data, + IN mlx_uint8 event_number, + IN mlx_uint8 event_length, + IN mlx_uint8 data_length, + IN mlx_uint8 data_start_offset + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_ocbb_set_event ocbb_event; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + ocbb_event.data_length = data_length; + ocbb_event.data_start_offset = data_start_offset; + ocbb_event.event_number = event_number; + ocbb_event.event_data = event_data; + ocbb_event.event_length = event_length; + status = mlx_icmd_send_command( + utils, + OCBB_QUERY_SET_EVENT, + &ocbb_event, + sizeof(ocbb_event), + 0 + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h new file mode 100644 index 000000000..49312b98f --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h @@ -0,0 +1,73 @@ +#ifndef MLX_OCBB_H_ +#define MLX_OCBB_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_utils.h" + +#define MLX_OCBB_EVENT_DATA_SIZE 2 +struct mlx_ocbb_init { + mlx_uint32 address_hi; + mlx_uint32 address_lo; +}; + +struct mlx_ocbb_query_status { + mlx_uint32 reserved :24; + mlx_uint32 status :8; +}; + +struct mlx_ocbb_set_event { + mlx_uint64 event_data; + mlx_uint32 event_number :8; + mlx_uint32 event_length :8; + mlx_uint32 data_length :8; + mlx_uint32 data_start_offset :8; +}; + +mlx_status +mlx_ocbb_init ( + IN mlx_utils *utils, + IN mlx_uint64 address + ); + +mlx_status +mlx_ocbb_query_header_status ( + IN mlx_utils *utils, + OUT mlx_uint8 *ocbb_status + ); + +mlx_status +mlx_ocbb_query_etoc_status ( + IN mlx_utils *utils, + OUT mlx_uint8 *ocbb_status + ); + +mlx_status +mlx_ocbb_set_event ( + IN mlx_utils *utils, + IN mlx_uint64 EventData, + IN mlx_uint8 EventNumber, + IN mlx_uint8 EventLength, + IN mlx_uint8 DataLength, + IN mlx_uint8 DataStartOffset + ); +#endif /* MLX_OCBB_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c new file mode 100644 index 000000000..143ab1b0e --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_icmd.h" +#include "../../include/public/mlx_bail.h" +#include "../../include/public/mlx_memory.h" + +static +mlx_status +init_operation_tlv( + IN struct mail_box_tlv *mail_box_tlv, + IN mlx_uint16 reg_id, + IN REG_ACCESS_OPT reg_opt + ) +{ +#define TLV_OPERATION 1 + mail_box_tlv->operation_tlv.Type = TLV_OPERATION; +#define MAD_CLASS_REG_ACCESS 1 + mail_box_tlv->operation_tlv.cls = MAD_CLASS_REG_ACCESS; +#define TLV_OPERATION_SIZE 4 + mail_box_tlv->operation_tlv.len = TLV_OPERATION_SIZE; + mail_box_tlv->operation_tlv.method = reg_opt; + mail_box_tlv->operation_tlv.register_id = reg_id; + return MLX_SUCCESS; +} + +mlx_status +mlx_reg_access( + IN mlx_utils *utils, + IN mlx_uint16 reg_id, + IN REG_ACCESS_OPT reg_opt, + IN OUT mlx_void *reg_data, + IN mlx_size reg_size, + OUT mlx_uint32 *reg_status + ) +{ + mlx_status status = MLX_SUCCESS; + struct mail_box_tlv mail_box_tlv; + + if (utils == NULL || reg_data == NULL || reg_status == NULL + || reg_size > REG_ACCESS_MAX_REG_SIZE) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &mail_box_tlv, 0, sizeof(mail_box_tlv)); + + init_operation_tlv(&mail_box_tlv, reg_id, reg_opt); + +#define REG_ACCESS_TLV_REG 3 +#define REG_TLV_HEADER_LEN 4 +#define OP_TLV_SIZE 16 + mail_box_tlv.reg_tlv.Type = REG_ACCESS_TLV_REG; + mail_box_tlv.reg_tlv.len = ((reg_size + REG_TLV_HEADER_LEN + 3) >> 2); // length is in dwords round up + mlx_memory_cpy(utils, &mail_box_tlv.reg_tlv.data, reg_data, reg_size); + + reg_size += OP_TLV_SIZE + REG_TLV_HEADER_LEN; + + status = mlx_icmd_send_command(utils, FLASH_REG_ACCESS, &mail_box_tlv, reg_size, reg_size); + MLX_CHECK_STATUS(utils, status, icmd_err, "failed to send icmd"); + + mlx_memory_cpy(utils, reg_data, &mail_box_tlv.reg_tlv.data, + reg_size - (OP_TLV_SIZE + REG_TLV_HEADER_LEN)); + + *reg_status = mail_box_tlv.operation_tlv.status; +icmd_err: +bad_param: + return status; +} + + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h new file mode 100644 index 000000000..9fbf51631 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef MLX_REG_ACCESS_H_ +#define MLX_REG_ACCESS_H_ + +#include "../../include/public/mlx_icmd.h" + +#define REG_ACCESS_MAX_REG_SIZE 236 + +typedef enum { + REG_ACCESS_READ = 1, + REG_ACCESS_WRITE = 2, +} REG_ACCESS_OPT; + +typedef enum { + TLV_ACCESS_DEFAULT_DIS = 0, + TLV_ACCESS_DEFAULT_EN = 1, +} NV_DEFAULT_OPT; + +#define REG_ID_NVDA 0x9024 +#define REG_ID_NVDI 0x9025 +#define REG_ID_NVIA 0x9029 +#define REG_ID_MLCR 0x902b +#define REG_ID_NVQC 0x9030 +#define REG_ID_MFRL 0x9028 +#define REG_ID_PTYS 0x5004 +#define REG_ID_PMTU 0x5003 + +struct operation_tlv { + mlx_uint32 reserved0 :8; /* bit_offset:0 */ /* element_size: 8 */ + mlx_uint32 status :7; /* bit_offset:8 */ /* element_size: 7 */ + mlx_uint32 dr :1; /* bit_offset:15 */ /* element_size: 1 */ + mlx_uint32 len :11; /* bit_offset:16 */ /* element_size: 11 */ + mlx_uint32 Type :5; /* bit_offset:27 */ /* element_size: 5 */ + mlx_uint32 cls :8; /* bit_offset:32 */ /* element_size: 8 */ + mlx_uint32 method :7; /* bit_offset:40 */ /* element_size: 7 */ + mlx_uint32 r :1; /* bit_offset:47 */ /* element_size: 1 */ + mlx_uint32 register_id :16; /* bit_offset:48 */ /* element_size: 16 */ + mlx_uint64 tid ; /* bit_offset:64 */ /* element_size: 64 */ +}; + +struct reg_tlv { + mlx_uint32 reserved0 :16; /* bit_offset:0 */ /* element_size: 16 */ + mlx_uint32 len :11; /* bit_offset:16 */ /* element_size: 11 */ + mlx_uint32 Type :5; /* bit_offset:27 */ /* element_size: 5 */ + mlx_uint8 data[REG_ACCESS_MAX_REG_SIZE]; +}; + +struct mail_box_tlv { + struct operation_tlv operation_tlv; + struct reg_tlv reg_tlv; +}; +mlx_status +mlx_reg_access( + IN mlx_utils *utils, + IN mlx_uint16 reg_id, + IN REG_ACCESS_OPT reg_opt, + IN OUT mlx_void *reg_data, + IN mlx_size reg_size, + OUT mlx_uint32 *reg_status + ); + +#endif /* MLX_REG_ACCESS_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c new file mode 100644 index 000000000..65d04c967 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../mlx_lib/mlx_vmac/mlx_vmac.h" +#include "../../include/public/mlx_icmd.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_vmac_query_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_query_virt_mac *virt_mac + ) +{ + mlx_status status = MLX_SUCCESS; + if (utils == NULL || virt_mac == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + QUERY_VIRTUAL_MAC, + virt_mac, + 0, + sizeof(*virt_mac) + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} + +mlx_status +mlx_vmac_set_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_set_virt_mac *virt_mac + ) +{ + mlx_status status = MLX_SUCCESS; + if (utils == NULL || virt_mac == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + SET_VIRTUAL_MAC, + virt_mac, + sizeof(*virt_mac), + 0 + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h new file mode 100644 index 000000000..2214d9189 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h @@ -0,0 +1,60 @@ +#ifndef MLX_VMAC_H_ +#define MLX_VMAC_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../include/public/mlx_utils.h" + +struct mlx_vmac_query_virt_mac { + mlx_uint32 reserved0 :30; + mlx_uint32 mac_aux_v :1; + mlx_uint32 virtual_mac_en :1; + mlx_uint32 parmanent_mac_high :16; + mlx_uint32 reserved1 :16; + mlx_uint32 parmanent_mac_low :32; + mlx_uint32 virtual_mac_high :16; + mlx_uint32 Reserved2 :16; + mlx_uint32 virtual_mac_low :32; +}; + +struct mlx_vmac_set_virt_mac { + mlx_uint32 Reserved0 :30; + mlx_uint32 mac_aux_v :1; + mlx_uint32 virtual_mac_en :1; + mlx_uint32 reserved1 :32; + mlx_uint32 reserved2 :32; + mlx_uint32 virtual_mac_high; + mlx_uint32 virtual_mac_low; +}; + +mlx_status +mlx_vmac_query_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_query_virt_mac *virt_mac + ); + +mlx_status +mlx_vmac_set_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_set_virt_mac *virt_mac + ); +#endif /* MLX_VMAC_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c new file mode 100644 index 000000000..a6c23c4a1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "mlx_wol_rol.h" +#include "mlx_icmd.h" +#include "mlx_memory.h" +#include "mlx_bail.h" + +mlx_status +mlx_set_wol ( + IN mlx_utils *utils, + IN mlx_uint8 wol_mask + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_wol_rol wol_rol; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &wol_rol, 0, sizeof(wol_rol)); + wol_rol.wol_mode_valid = TRUE; + wol_rol.wol_mode = wol_mask; + status = mlx_icmd_send_command( + utils, + SET_WOL_ROL, + &wol_rol, + sizeof(wol_rol), + 0 + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} + +mlx_status +mlx_query_wol ( + IN mlx_utils *utils, + OUT mlx_uint8 *wol_mask + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_wol_rol wol_rol; + + if (utils == NULL || wol_mask == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &wol_rol, 0, sizeof(wol_rol)); + status = mlx_icmd_send_command( + utils, + QUERY_WOL_ROL, + &wol_rol, + 0, + sizeof(wol_rol) + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); + *wol_mask = wol_rol.wol_mode; +icmd_err: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h new file mode 100644 index 000000000..610419d5d --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h @@ -0,0 +1,61 @@ +#ifndef MLX_WOL_ROL_H_ +#define MLX_WOL_ROL_H_ + +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + + +#include "mlx_utils.h" + +typedef enum { + WOL_MODE_DISABLE = 0x0, + WOL_MODE_SECURE = 0x2, + WOL_MODE_MAGIC = 0x4, + WOL_MODE_ARP = 0x8, + WOL_MODE_BC = 0x10, + WOL_MODE_MC = 0x20, + WOL_MODE_UC = 0x40, + WOL_MODE_PHY = 0x80, +} WOL_MODE; + +struct mlx_wol_rol { + mlx_uint32 reserved0 :32; + mlx_uint32 reserved1 :32; + mlx_uint32 wol_mode :8; + mlx_uint32 rol_mode :8; + mlx_uint32 reserved3 :14; + mlx_uint32 wol_mode_valid :1; + mlx_uint32 rol_mode_valid :1; +}; + +mlx_status +mlx_set_wol ( + IN mlx_utils *utils, + IN mlx_uint8 wol_mask + ); + +mlx_status +mlx_query_wol ( + IN mlx_utils *utils, + OUT mlx_uint8 *wol_mask + ); + +#endif /* MLX_WOL_ROL_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c b/src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c new file mode 100644 index 000000000..4386ad9b9 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c @@ -0,0 +1,9 @@ +MlxDebugLogImpl() + { + DBGC((DEBUG),""); + } +MlxInfoLogImpl() +{ + DBGC((INFO),""); + } +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c new file mode 100644 index 000000000..e42067393 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../include/public/mlx_bail.h" +#include "../../include/public/mlx_icmd.h" +#include "../../include/public/mlx_pci_gw.h" +#include "../../include/public/mlx_utils.h" + +static +mlx_status +mlx_icmd_get_semaphore( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 retries = 0; + mlx_uint32 semaphore_id; + mlx_uint32 buffer; + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_utils_rand(utils, &semaphore_id); + MLX_CHECK_STATUS(utils, status, rand_err, "failed to get random number"); +#define ICMD_GET_SEMAPHORE_TRIES 2560 + for (retries = 0 ; retries < ICMD_GET_SEMAPHORE_TRIES ; retries++) { + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd semaphore"); + if (buffer != 0) { + mlx_utils_delay_in_ms(10); + continue; + } + mlx_pci_gw_write( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, semaphore_id); + MLX_CHECK_STATUS(utils, status, set_err, "failed to set icmd semaphore"); + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd semaphore"); + if (semaphore_id == buffer) { + status = MLX_SUCCESS; + utils->icmd.took_semaphore = TRUE; + break; + } + mlx_utils_delay_in_ms(10); + } + if (semaphore_id != buffer) { + status = MLX_FAILED; + } +read_err: +set_err: +rand_err: +invalid_param: + return status; +} +static +mlx_status +mlx_icmd_clear_semaphore( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + if (utils->icmd.took_semaphore == FALSE) { + goto semaphore_not_taken; + } + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, 0); + MLX_CHECK_STATUS(utils, status, read_err, "failed to clear icmd semaphore"); + + utils->icmd.took_semaphore = FALSE; +read_err: +semaphore_not_taken: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_init( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + if (utils->icmd.icmd_opened == TRUE) { + goto already_opened; + } + + utils->icmd.took_semaphore = FALSE; + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_MB_SIZE_ADDR, &utils->icmd.max_cmd_size); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd mail box size"); + + utils->icmd.icmd_opened = TRUE; +read_err: +already_opened: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_set_opcode( + IN mlx_utils *utils, + IN mlx_uint16 opcode + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + +#define MLX_ICMD_OPCODE_ALIGN 16 +#define MLX_ICMD_OPCODE_MASK 0xffff + + buffer = buffer & ~(MLX_ICMD_OPCODE_MASK << MLX_ICMD_OPCODE_ALIGN); + buffer = buffer | (opcode << MLX_ICMD_OPCODE_ALIGN); + + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, buffer); + MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd ctrl"); +write_err: +read_err: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_go( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer; + mlx_uint32 busy; + mlx_uint32 wait_iteration = 0; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + +#define MLX_ICMD_BUSY_ALIGN 0 +#define MLX_ICMD_BUSY_MASK 0x1 + + busy = (buffer >> MLX_ICMD_BUSY_ALIGN) & MLX_ICMD_BUSY_MASK; + if (busy != 0) { + status = MLX_FAILED; + goto already_busy; + } + + buffer = buffer | (1 << MLX_ICMD_BUSY_ALIGN); + + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, buffer); + MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd ctrl"); + +#define MLX_ICMD_BUSY_MAX_ITERATIONS 1024 + do { + if (++wait_iteration > MLX_ICMD_BUSY_MAX_ITERATIONS) { + status = MLX_FAILED; + MLX_DEBUG_ERROR(utils, "ICMD time out"); + goto busy_timeout; + } + + mlx_utils_delay_in_ms(10); + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + busy = (buffer >> MLX_ICMD_BUSY_ALIGN) & MLX_ICMD_BUSY_MASK; + } while (busy != 0); + +busy_timeout: +write_err: +already_busy: +read_err: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_get_status( + IN mlx_utils *utils, + OUT mlx_uint32 *out_status + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer; + + if (utils == NULL || out_status == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + +#define MLX_ICMD_STATUS_ALIGN 8 +#define MLX_ICMD_STATUS_MASK 0xff + + *out_status = (buffer >> MLX_ICMD_STATUS_ALIGN) & MLX_ICMD_STATUS_MASK; + +read_err: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_write_buffer( + IN mlx_utils *utils, + IN mlx_void* data, + IN mlx_uint32 data_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 data_offset = 0; + mlx_size dword_size = sizeof(mlx_uint32); + + if (utils == NULL || data == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + for (data_offset = 0 ; data_offset*dword_size < data_size ; data_offset++) { + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_MB_ADDR + data_offset*dword_size, + ((mlx_uint32*)data)[data_offset]); + MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd MB"); + } +write_err: +invalid_param: + return status; +} + + +static +mlx_status +mlx_icmd_read_buffer( + IN mlx_utils *utils, + OUT mlx_void* data, + IN mlx_uint32 data_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 data_offset = 0; + mlx_size dword_size = sizeof(mlx_uint32); + + if (utils == NULL || data == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + for (data_offset = 0 ; data_offset*dword_size < data_size ; data_offset++) { + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_MB_ADDR + data_offset*dword_size, + (mlx_uint32*)data + data_offset); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd MB"); + } +read_err: +invalid_param: + return status; +} +mlx_status +mlx_icmd_send_command( + IN mlx_utils *utils, + IN mlx_uint16 opcode, + IN OUT mlx_void* data, + IN mlx_uint32 write_data_size, + IN mlx_uint32 read_data_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 icmd_status; + + if (utils == NULL || data == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + status = mlx_icmd_init(utils); + MLX_CHECK_STATUS(utils, status, open_err, "failed to open icmd"); + + if (write_data_size > utils->icmd.max_cmd_size || + read_data_size > utils->icmd.max_cmd_size) { + status = MLX_INVALID_PARAMETER; + goto size_err; + } + + status = mlx_icmd_get_semaphore(utils); + MLX_CHECK_STATUS(utils, status, semaphore_err, "failed to get icmd semaphore"); + + status = mlx_icmd_set_opcode(utils, opcode); + MLX_CHECK_STATUS(utils, status, opcode_err, "failed to set icmd opcode"); + + if (write_data_size != 0) { + status = mlx_icmd_write_buffer(utils, data, write_data_size); + MLX_CHECK_STATUS(utils, status, opcode_err, "failed to write icmd MB"); + } + + status = mlx_icmd_go(utils); + MLX_CHECK_STATUS(utils, status, go_err, "failed to activate icmd"); + + status = mlx_icmd_get_status(utils, &icmd_status); + MLX_CHECK_STATUS(utils, status, get_status_err, "failed to set icmd opcode"); + + if (icmd_status != 0) { + MLX_DEBUG_ERROR(utils, "icmd failed with status = %d\n", icmd_status); + status = MLX_FAILED; + goto icmd_failed; + } + if (read_data_size != 0) { + status = mlx_icmd_read_buffer(utils, data, read_data_size); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd MB"); + } +read_err: +icmd_failed: +get_status_err: +go_err: +opcode_err: + mlx_icmd_clear_semaphore(utils); +semaphore_err: +size_err: +open_err: +invalid_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c new file mode 100644 index 000000000..5aa5a53d2 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include "../../include/private/mlx_memory_priv.h" +#include "../../include/public/mlx_memory.h" + +mlx_status +mlx_memory_alloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = NULL; + if ( utils == NULL || size == 0 || *ptr != NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_alloc_priv(utils, size, ptr); +bad_param: + return status; +} + +mlx_status +mlx_memory_zalloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = NULL; + if ( utils == NULL || size == 0 || *ptr != NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_zalloc_priv(utils, size, ptr); +bad_param: + return status; +} + +mlx_status +mlx_memory_free( + IN mlx_utils *utils, + IN mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || ptr == NULL || *ptr == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_free_priv(utils, *ptr); + *ptr = NULL; +bad_param: + return status; +} +mlx_status +mlx_memory_alloc_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = NULL; + if ( utils == NULL || size == 0 || *ptr != NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_alloc_dma_priv(utils, size, align, ptr); +bad_param: + return status; +} + +mlx_status +mlx_memory_free_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || size == 0 || ptr == NULL || *ptr == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_free_dma_priv(utils, size, *ptr); + *ptr = NULL; +bad_param: + return status; +} + +mlx_status +mlx_memory_map_dma( + IN mlx_utils *utils, + IN mlx_void *addr , + IN mlx_size number_of_bytes, + OUT mlx_physical_address *phys_addr, + OUT mlx_void **mapping + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || phys_addr == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_map_dma_priv(utils, addr, number_of_bytes, phys_addr, mapping); +bad_param: + return status; +} + +mlx_status +mlx_memory_ummap_dma( + IN mlx_utils *utils, + IN mlx_void *mapping + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_ummap_dma_priv(utils, mapping); +bad_param: + return status; +} + +mlx_status +mlx_memory_cmp( + IN mlx_utils *utils, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || first_block == NULL || second_block == NULL || + out == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_cmp_priv(utils, first_block, second_block, size, out); +bad_param: + return status; +} + +mlx_status +mlx_memory_set( + IN mlx_utils *utils, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || block == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_set_priv(utils, block, value, size); +bad_param: + return status; +} + +mlx_status +mlx_memory_cpy( + IN mlx_utils *utils, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || destination_buffer == NULL || source_buffer == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_cpy_priv(utils, destination_buffer, source_buffer, length); +bad_param: + return status; +} + +mlx_status +mlx_memory_cpu_to_be32( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || destination == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_cpu_to_be32_priv(utils, source, destination); +bad_param: + return status; +} + +mlx_status +mlx_memory_be32_to_cpu( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || destination == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_be32_to_cpu_priv(utils, source, destination); +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c new file mode 100644 index 000000000..91c44d991 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include "../../include/private/mlx_pci_priv.h" +#include "../../include/public/mlx_pci.h" + +mlx_status +mlx_pci_init( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_init_priv(utils); +bail: + return status; +} + +mlx_status +mlx_pci_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_read_priv(utils, width, offset, count, buffer); +bail: + return status; +} + +mlx_status +mlx_pci_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_write_priv(utils, width, offset, count, buffer); +bail: + return status; +} + +mlx_status +mlx_pci_mem_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_mem_read_priv(utils, bar_index, width, offset, count, buffer); +bail: + return status; +} + +mlx_status +mlx_pci_mem_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_mem_write_priv(utils, width, bar_index, offset, count, buffer); +bail: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c new file mode 100644 index 000000000..30c1e644e --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "../../include/public/mlx_pci_gw.h" +#include "../../include/public/mlx_bail.h" +#include "../../include/public/mlx_pci.h" +#include "../../include/public/mlx_logging.h" + +/* Lock/unlock GW on each VSEC access */ +#undef VSEC_DEBUG + +static +mlx_status +mlx_pci_gw_check_capability_id( + IN mlx_utils *utils, + IN mlx_uint8 cap_pointer, + OUT mlx_boolean *bool + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 offset = cap_pointer + PCI_GW_CAPABILITY_ID_OFFSET; + mlx_uint8 id = 0; + status = mlx_pci_read(utils, MlxPciWidthUint8, offset, + 1, &id); + MLX_CHECK_STATUS(utils, status, read_err,"failed to read capability id"); + *bool = ( id == PCI_GW_CAPABILITY_ID ); +read_err: + return status; +} + +static +mlx_status +mlx_pci_gw_get_ownership( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset; + mlx_uint32 semaphore = 0; + mlx_uint32 counter = 0; + mlx_uint32 get_semaphore_try = 0; + mlx_uint32 get_ownership_try = 0; + + for( ; get_ownership_try < PCI_GW_GET_OWNERSHIP_TRIES; get_ownership_try ++){ + for( ; get_semaphore_try <= PCI_GW_SEMPHORE_TRIES ; get_semaphore_try++){ + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &semaphore); + MLX_CHECK_STATUS(utils, status, read_err,"failed to read semaphore"); + if( semaphore == 0 ){ + break; + } + mlx_utils_delay_in_us(10); + } + if( semaphore != 0 ){ + status = MLX_FAILED; + goto semaphore_err; + } + + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_COUNTER_OFFSET, + 1, &counter); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read counter"); + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &counter); + MLX_CHECK_STATUS(utils, status, write_err,"failed to write semaphore"); + + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &semaphore); + MLX_CHECK_STATUS(utils, status, read_err,"failed to read semaphore"); + if( counter == semaphore ){ + break; + } + } + if( counter != semaphore ){ + status = MLX_FAILED; + } +write_err: +read_err: +semaphore_err: + return status; +} + +static +mlx_status +mlx_pci_gw_free_ownership( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset; + mlx_uint32 value = 0; + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &value); + MLX_CHECK_STATUS(utils, status, write_err,"failed to write semaphore"); +write_err: + return status; +} + +static +mlx_status +mlx_pci_gw_set_space( + IN mlx_utils *utils, + IN mlx_pci_gw_space space + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset;; + mlx_uint8 space_status = 0; + + /* set nodnic*/ + status = mlx_pci_write(utils, MlxPciWidthUint16, cap_offset + PCI_GW_CAPABILITY_SPACE_OFFSET, 1, &space); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability space"); + + status = mlx_pci_read(utils, MlxPciWidthUint8, cap_offset + PCI_GW_CAPABILITY_STATUS_OFFSET, 1, &space_status); + MLX_CHECK_STATUS(utils, status, read_error,"failed to read capability status"); + if( (space_status & 0x20) == 0){ + status = MLX_FAILED; + goto space_unsupported; + } +read_error: +space_unsupported: + return status; +} + +static +mlx_status +mlx_pci_gw_wait_for_flag_value( + IN mlx_utils *utils, + IN mlx_boolean value + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 try = 0; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset; + mlx_uint32 flag = 0; + + for(; try < PCI_GW_READ_FLAG_TRIES ; try ++ ) { + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_FLAG_OFFSET, 1, &flag); + MLX_CHECK_STATUS(utils, status, read_error, "failed to read capability flag"); + if( ((flag & 0x80000000) != 0) == value ){ + goto flag_valid; + } + mlx_utils_delay_in_us(10); + } + status = MLX_FAILED; +flag_valid: +read_error: + return status; +} +static +mlx_status +mlx_pci_gw_search_capability( + IN mlx_utils *utils, + OUT mlx_uint32 *cap_offset + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 cap_pointer = 0; + mlx_boolean is_capability = FALSE; + + if( cap_offset == NULL || utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + //get first capability pointer + status = mlx_pci_read(utils, MlxPciWidthUint8, PCI_GW_FIRST_CAPABILITY_POINTER_OFFSET, + 1, &cap_pointer); + MLX_CHECK_STATUS(utils, status, read_err, + "failed to read capability pointer"); + + //search the right capability + while( cap_pointer != 0 ){ + status = mlx_pci_gw_check_capability_id(utils, cap_pointer, &is_capability); + MLX_CHECK_STATUS(utils, status, check_err + ,"failed to check capability id"); + + if( is_capability == TRUE ){ + *cap_offset = cap_pointer; + break; + } + + status = mlx_pci_read(utils, MlxPciWidthUint8, cap_pointer + + PCI_GW_CAPABILITY_NEXT_POINTER_OFFSET , + 1, &cap_pointer); + MLX_CHECK_STATUS(utils, status, read_err, + "failed to read capability pointer"); + } + if( is_capability != TRUE ){ + status = MLX_NOT_FOUND; + } +check_err: +read_err: +bad_param: + return status; +} + +mlx_status +mlx_pci_gw_init( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_pci_gw *pci_gw = NULL; + + if( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + pci_gw = &utils->pci_gw; + + status = mlx_pci_gw_search_capability(utils, &pci_gw->pci_cmd_offset); + MLX_CHECK_STATUS(utils, status, cap_err, + "mlx_pci_gw_search_capability failed"); + +#if ! defined ( VSEC_DEBUG ) + status = mlx_pci_gw_get_ownership(utils); + MLX_CHECK_STATUS(utils, status, ownership_err,"failed to get ownership"); +ownership_err: +#endif +cap_err: +bad_param: + return status; +} + +mlx_status +mlx_pci_gw_teardown( + IN mlx_utils *utils __attribute__ ((unused)) + ) +{ +#if ! defined ( VSEC_DEBUG ) + mlx_pci_gw_free_ownership(utils); +#endif + return MLX_SUCCESS; +} + +mlx_status +mlx_pci_gw_read( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_pci_gw *pci_gw = NULL; + mlx_uint32 cap_offset = 0; + + if (utils == NULL || buffer == NULL || utils->pci_gw.pci_cmd_offset == 0) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_utils_acquire_lock(utils); + + pci_gw = &utils->pci_gw; + cap_offset = pci_gw->pci_cmd_offset; + +#if ! defined ( VSEC_DEBUG ) + if (pci_gw->space != space) { + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; + } +#else + status = mlx_pci_gw_get_ownership(utils); + MLX_CHECK_STATUS(utils, status, ownership_err,"failed to get ownership"); + + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; +#endif + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_ADDRESS_OFFSET, 1, &address); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability address"); + +#if defined ( DEVICE_CX3 ) + /* WA for PCI issue (race) */ + mlx_utils_delay_in_us ( 10 ); +#endif + + status = mlx_pci_gw_wait_for_flag_value(utils, TRUE); + MLX_CHECK_STATUS(utils, status, read_error, "flag failed to change"); + + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_DATA_OFFSET, 1, buffer); + MLX_CHECK_STATUS(utils, status, read_error,"failed to read capability data"); + +#if defined ( VSEC_DEBUG ) + status = mlx_pci_gw_free_ownership(utils); + MLX_CHECK_STATUS(utils, status, free_err, + "mlx_pci_gw_free_ownership failed"); +free_err: + mlx_utils_release_lock(utils); + return status; +#endif +read_error: +space_error: +#if defined ( VSEC_DEBUG ) + mlx_pci_gw_free_ownership(utils); +ownership_err: +#endif +mlx_utils_release_lock(utils); +bad_param: + return status; +} + +mlx_status +mlx_pci_gw_write( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_pci_gw *pci_gw = NULL; + mlx_uint32 cap_offset = 0; + mlx_uint32 fixed_address = address | PCI_GW_WRITE_FLAG; + + if (utils == NULL || utils->pci_gw.pci_cmd_offset == 0) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_utils_acquire_lock(utils); + + pci_gw = &utils->pci_gw; + cap_offset = pci_gw->pci_cmd_offset; + +#if ! defined ( VSEC_DEBUG ) + if (pci_gw->space != space) { + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; + } +#else + status = mlx_pci_gw_get_ownership(utils); + MLX_CHECK_STATUS(utils, status, ownership_err,"failed to get ownership"); + + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; +#endif + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_DATA_OFFSET, 1, &buffer); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability data"); + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_ADDRESS_OFFSET, 1, &fixed_address); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability address"); + + status = mlx_pci_gw_wait_for_flag_value(utils, FALSE); + MLX_CHECK_STATUS(utils, status, read_error, "flag failed to change"); +#if defined ( VSEC_DEBUG ) + status = mlx_pci_gw_free_ownership(utils); + MLX_CHECK_STATUS(utils, status, free_err, + "mlx_pci_gw_free_ownership failed"); +free_err: +mlx_utils_release_lock(utils); + return status; +#endif +read_error: +space_error: +#if defined ( VSEC_DEBUG ) + mlx_pci_gw_free_ownership(utils); +ownership_err: +#endif +mlx_utils_release_lock(utils); +bad_param: + return status; +} + + + diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c new file mode 100644 index 000000000..c824b17e9 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include "../../include/private/mlx_utils_priv.h" +#include "../../include/public/mlx_pci.h" +#include "../../include/public/mlx_utils.h" + +mlx_status +mlx_utils_init( + IN mlx_utils *utils, + IN mlx_pci *pci + ) +{ + mlx_status status = MLX_SUCCESS; + if( pci == NULL || utils == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + utils->pci = pci; + status = mlx_pci_init(utils); + status = mlx_utils_init_lock(utils); +bail: + return status; +} + +mlx_status +mlx_utils_teardown( + IN mlx_utils *utils __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_utils_free_lock(utils); + return status; +} + +mlx_status +mlx_utils_delay_in_ms( + IN mlx_uint32 msecs + ) +{ + mlx_utils_delay_in_ms_priv(msecs); + return MLX_SUCCESS; +} +mlx_status +mlx_utils_delay_in_us( + IN mlx_uint32 usecs + ) +{ + mlx_utils_delay_in_us_priv(usecs); + return MLX_SUCCESS; +} +mlx_status +mlx_utils_ilog2( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ) +{ + mlx_utils_ilog2_priv(i, log); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_init_lock( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_init_lock_priv(&(utils->lock)); + +} + +mlx_status +mlx_utils_free_lock( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_free_lock_priv(utils->lock); +} + +mlx_status +mlx_utils_acquire_lock ( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_acquire_lock_priv(utils->lock); +} + +mlx_status +mlx_utils_release_lock ( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_release_lock_priv(utils->lock); +} + +mlx_status +mlx_utils_rand ( + IN mlx_utils *utils, + OUT mlx_uint32 *rand_num + ) +{ + return mlx_utils_rand_priv(utils, rand_num); +} diff --git a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h new file mode 100644 index 000000000..af7e86f44 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h @@ -0,0 +1,61 @@ +/* + * DebugPriv.h + * + * Created on: Jan 19, 2015 + * Author: maord + */ + +#ifndef STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ +#define STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ + +#include +#include + +#define MLX_DEBUG_FATAL_ERROR_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_ERROR_PRIVATE(id, ...) do { \ + DBGC(id, "%s: ",__func__); \ + DBGC(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_WARN_PRIVATE(id, ...) do { \ + DBGC(id, "%s: ",__func__); \ + DBGC(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_INFO1_PRIVATE(id, ...) do { \ + DBGC(id, "%s: ",__func__); \ + DBGC(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_INFO2_PRIVATE(id, ...) do { \ + DBGC2(id, "%s: ",__func__); \ + DBGC2(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_ERROR_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_WARN_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_INFO1_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_INFO2_PRIVATE(...) do { \ + DBG2("%s: ",__func__); \ + DBG2(__VA_ARGS__); \ + } while ( 0 ) + + + +#endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h new file mode 100644 index 000000000..feaeae679 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h @@ -0,0 +1,60 @@ +/* + * types.h + * + * Created on: Jan 18, 2015 + * Author: maord + */ + +#ifndef A_MLXUTILS_INCLUDE_PUBLIC_TYPES_H_ +#define A_MLXUTILS_INCLUDE_PUBLIC_TYPES_H_ +#include +//#include +#include + +#define MLX_SUCCESS 0 +#define MLX_OUT_OF_RESOURCES (-1) +//(-ENOMEM) +#define MLX_INVALID_PARAMETER (-2) +//(-EINVAL) +#define MLX_UNSUPPORTED (-3) +//(-ENOSYS) +#define MLX_NOT_FOUND (-4) + +#define MLX_FAILED (-5) + +#undef TRUE +#define TRUE 1 +#undef FALSE +#define FALSE !TRUE + +typedef int mlx_status; + +typedef uint8_t mlx_uint8; +typedef uint16_t mlx_uint16; +typedef uint32_t mlx_uint32; +typedef uint64_t mlx_uint64; +typedef uint32_t mlx_uintn; + +typedef int8_t mlx_int8; +typedef int16_t mlx_int16;; +typedef int32_t mlx_int32; +typedef int64_t mlx_int64; +typedef uint8_t mlx_boolean; + +typedef struct pci_device mlx_pci; + +typedef size_t mlx_size; + +typedef void mlx_void; + +#define MAC_ADDR_LEN 6 +typedef unsigned long mlx_physical_address; +typedef union { + struct { + uint32_t low; + uint32_t high; + } __attribute__ (( packed )); + uint8_t addr[MAC_ADDR_LEN]; +} mlx_mac_address; + +#endif /* A_MLXUTILS_INCLUDE_PUBLIC_TYPES_H_ */ diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c new file mode 100644 index 000000000..cb9e759bf --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c @@ -0,0 +1,172 @@ +/* + * MemoryPriv.c + * + * Created on: Jan 21, 2015 + * Author: maord + */ + +#include +#include +#include +#include +#include "../../mlx_utils/include/private/mlx_memory_priv.h" + + +mlx_status +mlx_memory_alloc_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = malloc(size); + if(*ptr == NULL){ + status = MLX_OUT_OF_RESOURCES; + } + return status; +} + +mlx_status +mlx_memory_zalloc_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = zalloc(size); + if(*ptr == NULL){ + status = MLX_OUT_OF_RESOURCES; + } + return status; +} + +mlx_status +mlx_memory_free_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_void *ptr + ) +{ + mlx_status status = MLX_SUCCESS; + free(ptr); + return status; +} +mlx_status +mlx_memory_alloc_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = malloc_dma(size, align); + if (*ptr == NULL) { + status = MLX_OUT_OF_RESOURCES; + } else { + memset(*ptr, 0, size); + } + return status; +} + +mlx_status +mlx_memory_free_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size , + IN mlx_void *ptr + ) +{ + mlx_status status = MLX_SUCCESS; + free_dma(ptr, size); + return status; +} +mlx_status +mlx_memory_map_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_void *addr , + IN mlx_size number_of_bytes __attribute__ ((unused)), + OUT mlx_physical_address *phys_addr, + OUT mlx_void **mapping __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; + *phys_addr = virt_to_bus(addr); + return status; +} + +mlx_status +mlx_memory_ummap_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_void *mapping __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; + return status; +} + +mlx_status +mlx_memory_cmp_priv( + IN mlx_utils *utils __unused, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ) +{ + mlx_status status = MLX_SUCCESS; + *out = memcmp(first_block, second_block, size); + return status; +} + +mlx_status +mlx_memory_set_priv( + IN mlx_utils *utils __unused, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ) +{ + mlx_status status = MLX_SUCCESS; + memset(block, value, size); + return status; +} + +mlx_status +mlx_memory_cpy_priv( + IN mlx_utils *utils __unused, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ) +{ + mlx_status status = MLX_SUCCESS; + memcpy(destination_buffer, source_buffer, length); + return status; +} + +mlx_status +mlx_memory_cpu_to_be32_priv( + IN mlx_utils *utils __unused, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + *destination = cpu_to_be32(source); + return status; +} + + +mlx_status +mlx_memory_be32_to_cpu_priv( + IN mlx_utils *utils __unused, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + *destination = be32_to_cpu(source); + return status; +} + diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c new file mode 100644 index 000000000..f8caefdce --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c @@ -0,0 +1,182 @@ +/* + * MlxPciPriv.c + * + * Created on: Jan 21, 2015 + * Author: maord + */ + +#include +#include "../../mlx_utils/include/private/mlx_pci_priv.h" + + +static +mlx_status +mlx_pci_config_byte( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_uint32 offset, + IN OUT mlx_uint8 *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if (read) { + status = pci_read_config_byte(utils->pci, offset, buffer); + }else { + status = pci_write_config_byte(utils->pci, offset, *buffer); + } + return status; +} + +static +mlx_status +mlx_pci_config_word( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_uint32 offset, + IN OUT mlx_uint16 *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if (read) { + status = pci_read_config_word(utils->pci, offset, buffer); + }else { + status = pci_write_config_word(utils->pci, offset, *buffer); + } + return status; +} + +static +mlx_status +mlx_pci_config_dword( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_uint32 offset, + IN OUT mlx_uint32 *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if (read) { + status = pci_read_config_dword(utils->pci, offset, buffer); + }else { + status = pci_write_config_dword(utils->pci, offset, *buffer); + } + return status; +} +static +mlx_status +mlx_pci_config( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 *tmp = (mlx_uint8*)buffer; + mlx_uintn iteration = 0; + if( width == MlxPciWidthUint64) { + width = MlxPciWidthUint32; + count = count * 2; + } + + for(;iteration < count ; iteration++) { + switch (width){ + case MlxPciWidthUint8: + status = mlx_pci_config_byte(utils, read , offset++, tmp++); + break; + case MlxPciWidthUint16: + status = mlx_pci_config_word(utils, read , offset, (mlx_uint16*)tmp); + tmp += 2; + offset += 2; + break; + case MlxPciWidthUint32: + status = mlx_pci_config_dword(utils, read , offset, (mlx_uint32*)tmp); + tmp += 4; + offset += 4; + break; + default: + status = MLX_INVALID_PARAMETER; + } + if(status != MLX_SUCCESS) { + goto config_error; + } + } +config_error: + return status; +} +mlx_status +mlx_pci_init_priv( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + adjust_pci_device ( utils->pci ); +#ifdef DEVICE_CX3 + utils->config = ioremap ( pci_bar_start ( utils->pci, PCI_BASE_ADDRESS_0), + 0x100000 ); +#endif + return status; +} + +mlx_status +mlx_pci_read_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + status = mlx_pci_config(utils, TRUE, width, offset, count, buffer); + return status; +} + +mlx_status +mlx_pci_write_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + status = mlx_pci_config(utils, FALSE, width, offset, count, buffer); + return status; +} + +mlx_status +mlx_pci_mem_read_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_pci_width width __attribute__ ((unused)), + IN mlx_uint8 bar_index __attribute__ ((unused)), + IN mlx_uint64 offset, + IN mlx_uintn count __attribute__ ((unused)), + OUT mlx_void *buffer + ) +{ + if (buffer == NULL || width != MlxPciWidthUint32) + return MLX_INVALID_PARAMETER; + *((mlx_uint32 *)buffer) = readl(offset); + return MLX_SUCCESS; +} + +mlx_status +mlx_pci_mem_write_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_pci_width width __attribute__ ((unused)), + IN mlx_uint8 bar_index __attribute__ ((unused)), + IN mlx_uint64 offset, + IN mlx_uintn count __attribute__ ((unused)), + IN mlx_void *buffer + ) +{ + if (buffer == NULL || width != MlxPciWidthUint32) + return MLX_INVALID_PARAMETER; + barrier(); + writel(*((mlx_uint32 *)buffer), offset); + return MLX_SUCCESS; +} diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c new file mode 100644 index 000000000..5fca406fc --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c @@ -0,0 +1,83 @@ +/* + * MlxUtilsPriv.c + * + * Created on: Jan 25, 2015 + * Author: maord + */ + +#include +#include +#include +#include "../../mlx_utils/include/private/mlx_utils_priv.h" + +mlx_status +mlx_utils_delay_in_ms_priv( + IN mlx_uint32 msecs + ) +{ + mdelay(msecs); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_delay_in_us_priv( + IN mlx_uint32 usecs + ) +{ + udelay(usecs); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_ilog2_priv( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ) +{ + *log = ( fls ( i ) - 1 ); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_init_lock_priv( + OUT void **lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_free_lock_priv( + IN void *lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_acquire_lock_priv ( + IN void *lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_release_lock_priv ( + IN void *lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_rand_priv ( + IN mlx_utils *utils __unused, + OUT mlx_uint32 *rand_num + ) +{ + do { + *rand_num = rand(); + } while ( *rand_num == 0 ); + return MLX_SUCCESS; +} diff --git a/src/drivers/infiniband/nodnic_prm.h b/src/drivers/infiniband/nodnic_prm.h new file mode 100644 index 000000000..5e0fa9890 --- /dev/null +++ b/src/drivers/infiniband/nodnic_prm.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_PRM_H_ +#define SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_PRM_H_ + +#include "mlx_bitops.h" + +struct nodnic_wqe_segment_data_ptr_st { /* Little Endian */ + pseudo_bit_t byte_count[0x0001f]; + pseudo_bit_t always0[0x00001]; +/* -------------- */ + pseudo_bit_t l_key[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_h[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_l[0x00020]; +/* -------------- */ +}; + +struct MLX_DECLARE_STRUCT ( nodnic_wqe_segment_data_ptr ); + +#define HERMON_MAX_SCATTER 1 + +struct nodnic_recv_wqe { + struct nodnic_wqe_segment_data_ptr data[HERMON_MAX_SCATTER]; +} __attribute__ (( packed )); + +#endif /* SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_PRM_H_ */ diff --git a/src/drivers/infiniband/nodnic_shomron_prm.h b/src/drivers/infiniband/nodnic_shomron_prm.h new file mode 100644 index 000000000..85cd97187 --- /dev/null +++ b/src/drivers/infiniband/nodnic_shomron_prm.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_SHOMRON_PRM_H_ +#define SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_SHOMRON_PRM_H_ + + + +#include "nodnic_prm.h" + + +#define SHOMRON_MAX_GATHER 1 + +/* Send wqe segment ctrl */ + +struct shomronprm_wqe_segment_ctrl_send_st { /* Little Endian */ + pseudo_bit_t opcode[0x00008]; + pseudo_bit_t wqe_index[0x00010]; + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t ds[0x00006]; /* descriptor (wqe) size in 16bytes chunk */ + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t qpn[0x00018]; +/* -------------- */ + pseudo_bit_t reserved3[0x00002]; + pseudo_bit_t ce[0x00002]; + pseudo_bit_t reserved4[0x0001c]; +/* -------------- */ + pseudo_bit_t reserved5[0x00040]; +/* -------------- */ + pseudo_bit_t mss[0x0000e]; + pseudo_bit_t reserved6[0x0000e]; + pseudo_bit_t cs13_inner[0x00001]; + pseudo_bit_t cs14_inner[0x00001]; + pseudo_bit_t cs13[0x00001]; + pseudo_bit_t cs14[0x00001]; +/* -------------- */ + pseudo_bit_t reserved7[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers1[0x00010]; + pseudo_bit_t inline_headers_size[0x0000a]; //sum size of inline_hdr1+inline_hdrs (0x10) + pseudo_bit_t reserved8[0x00006]; +/* -------------- */ + pseudo_bit_t inline_headers2[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers3[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers4[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers5[0x00020]; +}; + + + +/* Completion Queue Entry Format #### michal - fixed by gdror */ + +struct shomronprm_completion_queue_entry_st { /* Little Endian */ + + pseudo_bit_t reserved1[0x00080]; +/* -------------- */ + pseudo_bit_t reserved2[0x00010]; + pseudo_bit_t ml_path[0x00007]; + pseudo_bit_t reserved3[0x00009]; +/* -------------- */ + pseudo_bit_t slid[0x00010]; + pseudo_bit_t reserved4[0x00010]; +/* -------------- */ + pseudo_bit_t rqpn[0x00018]; + pseudo_bit_t sl[0x00004]; + pseudo_bit_t l3_hdr[0x00002]; + pseudo_bit_t reserved5[0x00002]; +/* -------------- */ + pseudo_bit_t reserved10[0x00020]; +/* -------------- */ + pseudo_bit_t srqn[0x00018]; + pseudo_bit_t reserved11[0x0008]; +/* -------------- */ + pseudo_bit_t pkey_index[0x00020]; +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t byte_cnt[0x00020]; +/* -------------- */ + pseudo_bit_t reserved7[0x00040]; +/* -------------- */ + pseudo_bit_t qpn[0x00018]; + pseudo_bit_t rx_drop_counter[0x00008]; +/* -------------- */ + pseudo_bit_t owner[0x00001]; + pseudo_bit_t reserved8[0x00003]; + pseudo_bit_t opcode[0x00004]; + pseudo_bit_t reserved9[0x00008]; + pseudo_bit_t wqe_counter[0x00010]; +}; + + +/* Completion with Error CQE #### michal - gdror fixed */ + +struct shomronprm_completion_with_error_st { /* Little Endian */ + pseudo_bit_t reserved1[0x001a0]; + /* -------------- */ + pseudo_bit_t syndrome[0x00008]; + pseudo_bit_t vendor_error_syndrome[0x00008]; + pseudo_bit_t reserved2[0x00010]; + /* -------------- */ + pseudo_bit_t reserved3[0x00040]; +}; + + +struct MLX_DECLARE_STRUCT ( shomronprm_wqe_segment_ctrl_send ); +struct MLX_DECLARE_STRUCT ( shomronprm_completion_queue_entry ); +struct MLX_DECLARE_STRUCT ( shomronprm_completion_with_error ); + +struct shomron_nodnic_eth_send_wqe { + struct shomronprm_wqe_segment_ctrl_send ctrl; + struct nodnic_wqe_segment_data_ptr data[SHOMRON_MAX_GATHER]; +} __attribute__ (( packed )); + +union shomronprm_completion_entry { + struct shomronprm_completion_queue_entry normal; + struct shomronprm_completion_with_error error; +} __attribute__ (( packed )); + + +#endif /* SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_SHOMRON_PRM_H_ */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 638931578..338ebddc2 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -186,6 +186,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_smsc95xx ( ERRFILE_DRIVER | 0x007a0000 ) #define ERRFILE_acm ( ERRFILE_DRIVER | 0x007b0000 ) #define ERRFILE_eoib ( ERRFILE_DRIVER | 0x007c0000 ) +#define ERRFILE_golan ( ERRFILE_DRIVER | 0x007d0000 ) +#define ERRFILE_flexboot_nodnic ( ERRFILE_DRIVER | 0x007e0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 05027a7a125eee751e7aa2cd93f3a95f683f901a Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 22 Mar 2016 21:26:06 +0100 Subject: [PATCH 172/591] [golan] Fix build error on some versions of gcc Some versions of gcc complain that "'__bswap_variable_32' is static but used in inline function 'golan_check_rc_and_cmd_status' which is not static". Fix by making golan_check_rc_and_cmd_status() a static inline. Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/infiniband/golan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 9225c187f..64c75526b 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -120,7 +120,7 @@ const char *golan_qp_state_as_string[] = { "ERR" }; -inline int golan_check_rc_and_cmd_status ( struct golan_cmd_layout *cmd, int rc ) { +static inline int golan_check_rc_and_cmd_status ( struct golan_cmd_layout *cmd, int rc ) { struct golan_outbox_hdr *out_hdr = ( struct golan_outbox_hdr * ) ( cmd->out ); if ( rc == -EBUSY ) { DBG ( "HCA is busy (rc = -EBUSY)\n" ); From c4e8c40227ebb11361e066be96ed93927eedfbf4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2016 13:41:17 +0000 Subject: [PATCH 173/591] [prefix] Use CRC32 to verify each block prior to decompression Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 99 ++++++++++++++++++++++----------- src/arch/x86/prefix/unlzma.S | 54 +++++++++++++++++- src/util/zbin.c | 52 +++++++++++++---- 3 files changed, 160 insertions(+), 45 deletions(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 425f51484..533be981e 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -341,6 +341,7 @@ zero_bytes: * Returns: * %esi : next source physical address * %edi : next destination physical address + * CF : as returned by memcpy()-like function * Corrupts: * None **************************************************************************** @@ -356,6 +357,7 @@ process_bytes: pushl %ebp /* Construct GDT on stack (since .prefix may not be writable) */ + .equ GDT_LEN, 0x20 .equ PM_DS, 0x18 /* Flat data segment */ pushl $0x00cf9300 pushl $0x0000ffff @@ -369,7 +371,7 @@ process_bytes: pushw $0xffff pushl $0 /* Base and length */ pushw %ss - pushw $0x1f + pushw $( GDT_LEN - 1 ) movzwl %sp, %ebp shll $4, 0x02(%bp) addl %ebp, 0x02(%bp) @@ -407,7 +409,9 @@ process_bytes: /* Return to (flat) real mode */ movl %cr0, %eax + pushfw andb $0!CR0_PE, %al + popfw movl %eax, %cr0 lret 2: /* lret will ljmp to here */ @@ -433,7 +437,7 @@ process_bytes: /* Restore GDT */ data32 lgdt -8(%bp) - addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp + leaw GDT_LEN(%bp), %sp /* Restore registers and return */ popl %ebp @@ -461,6 +465,7 @@ process_bytes: call *%bx /* Convert %ds:esi and %es:edi back to physical addresses */ + pushfw xorl %eax, %eax movw %ds, %ax shll $4, %eax @@ -469,6 +474,7 @@ process_bytes: movw %es, %ax shll $4, %eax addl %eax, %edi + popfw /* Restore registers and return */ popw %es @@ -493,6 +499,7 @@ process_bytes: * Returns: * %esi : next source physical address (will be a multiple of 16) * %edi : next destination physical address (will be a multiple of 16) + * CF set on failure * Corrupts: * none **************************************************************************** @@ -511,6 +518,7 @@ install_block: movw $copy_bytes, %bx #endif call process_bytes + jc 99f /* Zero .bss portion */ negl %ecx @@ -522,9 +530,9 @@ install_block: addl $0xf, %esi andl $~0xf, %esi addl $0xf, %edi - andl $~0xf, %edi + andl $~0xf, %edi /* Will also clear CF */ - /* Restore registers and return */ +99: /* Restore registers and return */ popw %bx popl %ecx ret @@ -730,6 +738,7 @@ install_prealloc: movl $_text16_early_filesz, %ecx movl $_text16_early_memsz, %edx call install_block /* .text16.early */ + jc install_block_death popl %ecx /* Calculate offset to next block */ subl %esi, %ecx negl %ecx @@ -748,17 +757,8 @@ install_prealloc: pushw $access_highmem lret 1: /* Die if we could not access high memory */ - jnc 3f - movw $a20_death_message, %si - xorw %di, %di - call print_message -2: jmp 2b - .section ".prefix.data.a20_death_message", "aw", @progbits -a20_death_message: - .asciz "\nHigh memory inaccessible - cannot continue\n" - .size a20_death_message, . - a20_death_message - .previous -3: + jc access_highmem_death + #endif /* Open payload (which may not yet be in memory) */ @@ -769,25 +769,7 @@ a20_death_message: pushw $open_payload lret 1: /* Die if we could not access the payload */ - jnc 3f - xorw %di, %di - movl %esi, %eax - call print_hex_dword - call print_space - movl %ecx, %eax - call print_hex_dword - movw $payload_death_message, %si - call print_message -2: /* Halt system */ - cli - hlt - jmp 2b - .section ".prefix.data.payload_death_message", "aw", @progbits -payload_death_message: - .asciz "\nPayload inaccessible - cannot continue\n" - .size payload_death_message, . - payload_death_message - .previous -3: + jc open_payload_death /* Calculate physical address of payload (i.e. first source) */ testl %esi, %esi @@ -801,12 +783,14 @@ payload_death_message: movl $_text16_late_filesz, %ecx movl $_text16_late_memsz, %edx call install_block /* .text16.late */ + jc install_block_death progress " .data16\n" movzwl %bx, %edi shll $4, %edi movl $_data16_filesz, %ecx movl $_data16_filesz, %edx /* do not zero our temporary stack */ call install_block /* .data16 */ + jc install_block_death /* Set up %ds for access to .data16 */ movw %bx, %ds @@ -846,6 +830,7 @@ payload_death_message: movl $_textdata_filesz, %ecx movl $_textdata_memsz, %edx call install_block + jc install_block_death popl %edi #endif /* KEEP_IT_REAL */ @@ -960,6 +945,52 @@ close_payload: .size open_payload, . - open_payload .size close_payload, . - close_payload + /* Report installation failure */ + .section ".prefix.install_death", "ax", @progbits +install_death: + pushw %cs + popw %ds + xorw %di, %di + call print_hex_dword + call print_space + movl %esi, %eax + call print_hex_dword + call print_space + movl %ecx, %eax + call print_hex_dword + movw $install_death_message, %si + call print_message +2: /* Halt system */ + cli + hlt + jmp 2b + .size install_death, . - install_death + .section ".prefix.data.install_death_message", "aw", @progbits +install_death_message: + .asciz "\nInstallation failed - cannot continue\n" + .size install_death_message, . - install_death_message + + /* Report failure to access high memory */ + .section ".prefix.install_block_death", "ax", @progbits +install_block_death: + movl $0x1b101b10, %eax + jmp install_death + .size install_block_death, . - install_block_death + + /* Report failure to access high memory */ + .section ".prefix.access_highmem_death", "ax", @progbits +access_highmem_death: + movl $0x0a200a20, %eax + jmp install_death + .size access_highmem_death, . - access_highmem_death + + /* Report failure to open payload */ + .section ".prefix.open_payload_death", "ax", @progbits +open_payload_death: + xorl %eax, %eax + jmp install_death + .size open_payload_death, . - open_payload_death + /**************************************************************************** * uninstall * diff --git a/src/arch/x86/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S index 8d4b3c1a8..ce18c756f 100644 --- a/src/arch/x86/prefix/unlzma.S +++ b/src/arch/x86/prefix/unlzma.S @@ -58,6 +58,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); .code32 #endif /* CODE16 */ +#define CRCPOLY 0xedb88320 +#define CRCSEED 0xffffffff + /**************************************************************************** * Debugging **************************************************************************** @@ -862,6 +865,44 @@ bcj_filter: ret .size bcj_filter, . - bcj_filter +/**************************************************************************** + * Verify CRC32 + * + * Parameters: + * %ds:%esi : Start of compressed input data + * %edx : Length of compressed input data (including CRC) + * Returns: + * CF clear if CRC32 is zero + * All other registers are preserved + * Corrupts: + * %eax + * %ebx + * %ecx + * %edx + * %esi + **************************************************************************** + */ +verify_crc32: + /* Calculate CRC */ + addl %esi, %edx + movl $CRCSEED, %ebx +1: ADDR32 lodsb + xorb %al, %bl + movw $8, %cx +2: rcrl %ebx + jnc 3f + xorl $CRCPOLY, %ebx +3: ADDR16 loop 2b + cmpl %esi, %edx + jne 1b + /* Set CF if result is nonzero */ + testl %ebx, %ebx + jz 1f + stc +1: /* Return */ + ret + .size verify_crc32, . - verify_crc32 + /**************************************************************************** * decompress (real-mode or 16/32-bit protected-mode near call) * @@ -873,6 +914,7 @@ bcj_filter: * Returns: * %ds:%esi - End of compressed input data * %es:%edi - End of decompressed output data + * CF set if CRC32 was incorrect * All other registers are preserved * * NOTE: It would be possible to build a smaller version of the @@ -888,6 +930,13 @@ decompress: pushl %ecx pushl %edx pushl %ebp + /* Verify CRC32 */ + ADDR32 lodsl + movl %eax, %edx + pushl %esi + call verify_crc32 + popl %esi + jc 99f /* Allocate parameter block */ subl $sizeof__lzma_dec, %esp movl %esp, %ebp @@ -928,8 +977,11 @@ decompress: movl out_start(%ebp), %esi call bcj_filter popl %esi - /* Restore registers and return */ + /* Skip CRC */ + ADDR32 lodsl + /* Free parameter block (and clear CF) */ addl $sizeof__lzma_dec, %esp +99: /* Restore registers and return */ popl %ebp popl %edx popl %ecx diff --git a/src/util/zbin.c b/src/util/zbin.c index 1862a3827..75fba583f 100644 --- a/src/util/zbin.c +++ b/src/util/zbin.c @@ -144,6 +144,7 @@ static int read_zinfo_file ( const char *filename, static int alloc_output_file ( size_t max_len, struct output_file *output ) { output->len = 0; + output->hdr_len = 0; output->max_len = ( max_len ); output->buf = malloc ( max_len ); if ( ! output->buf ) { @@ -241,19 +242,41 @@ static void bcj_filter ( void *data, size_t len ) { }; } +#define CRCPOLY 0xedb88320 +#define CRCSEED 0xffffffff + +static uint32_t crc32_le ( uint32_t crc, const void *data, size_t len ) { + const uint8_t *src = data; + uint32_t mult; + unsigned int i; + + while ( len-- ) { + crc ^= *(src++); + for ( i = 0 ; i < 8 ; i++ ) { + mult = ( ( crc & 1 ) ? CRCPOLY : 0 ); + crc = ( ( crc >> 1 ) ^ mult ); + } + } + return crc; +} + static int process_zinfo_pack ( struct input_file *input, struct output_file *output, union zinfo_record *zinfo ) { struct zinfo_pack *pack = &zinfo->pack; size_t offset = pack->offset; size_t len = pack->len; + size_t start_len; size_t packed_len = 0; - size_t remaining = ( output->max_len - output->len ); + size_t remaining; lzma_options_lzma options; const lzma_filter filters[] = { { .id = LZMA_FILTER_LZMA1, .options = &options }, { .id = LZMA_VLI_UNKNOWN } }; + void *packed; + uint32_t *len32; + uint32_t *crc32; if ( ( offset + len ) > input->len ) { fprintf ( stderr, "Input buffer overrun on pack\n" ); @@ -261,6 +284,9 @@ static int process_zinfo_pack ( struct input_file *input, } output->len = align ( output->len, pack->align ); + start_len = output->len; + len32 = ( output->buf + output->len ); + output->len += sizeof ( *len32 ); if ( output->len > output->max_len ) { fprintf ( stderr, "Output buffer overrun on pack\n" ); return -1; @@ -268,28 +294,34 @@ static int process_zinfo_pack ( struct input_file *input, bcj_filter ( ( input->buf + offset ), len ); + packed = ( output->buf + output->len ); + remaining = ( output->max_len - output->len ); lzma_lzma_preset ( &options, LZMA_PRESET ); options.lc = LZMA_LC; options.lp = LZMA_LP; options.pb = LZMA_PB; if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ), - len, ( output->buf + output->len ), - &packed_len, remaining ) != LZMA_OK ) { + len, packed, &packed_len, + remaining ) != LZMA_OK ) { fprintf ( stderr, "Compression failure\n" ); return -1; } - - if ( DEBUG ) { - fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", - offset, ( offset + len ), output->len, - ( output->len + packed_len ) ); - } - output->len += packed_len; + + crc32 = ( output->buf + output->len ); + output->len += sizeof ( *crc32 ); if ( output->len > output->max_len ) { fprintf ( stderr, "Output buffer overrun on pack\n" ); return -1; } + *len32 = ( packed_len + sizeof ( *crc32 ) ); + *crc32 = crc32_le ( CRCSEED, packed, packed_len ); + + if ( DEBUG ) { + fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx) crc %#08x\n", + offset, ( offset + len ), start_len, output->len, + *crc32 ); + } return 0; } From f8e1678b84fc3119ce515e0c1a84881bb9ce1c36 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 24 Mar 2016 19:25:03 +0000 Subject: [PATCH 174/591] [crypto] Allow cross-certificate source to be configured at build time Provide a build option CROSSCERT in config/crypto.h to allow the default cross-signed certificate source to be configured at build time. The ${crosscert} setting may still be used to reconfigure the cross-signed certificate source at runtime. Signed-off-by: Michael Brown --- src/config/crypto.h | 8 ++++++++ src/net/validator.c | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/crypto.h b/src/config/crypto.h index bccfc04b8..8f885c554 100644 --- a/src/config/crypto.h +++ b/src/config/crypto.h @@ -50,6 +50,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define TIMESTAMP_ERROR_MARGIN ( ( 12 * 60 + 30 ) * 60 ) +/** Default cross-signed certificate source + * + * This is the default location from which iPXE will attempt to + * download cross-signed certificates in order to complete a + * certificate chain. + */ +#define CROSSCERT "http://ca.ipxe.org/auto" + #include #include NAMED_CONFIG(crypto.h) #include diff --git a/src/net/validator.c b/src/net/validator.c index db968398a..57ad0e7b6 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -41,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** @file * @@ -133,7 +134,7 @@ const struct setting crosscert_setting __setting ( SETTING_CRYPTO, crosscert )={ }; /** Default cross-signed certificate source */ -static const char crosscert_default[] = "http://ca.ipxe.org/auto"; +static const char crosscert_default[] = CROSSCERT; /** * Append cross-signing certificates to certificate chain From 97c3f6e55a941a3caf3a0a36346d2e89f5a7b283 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 29 Mar 2016 19:38:18 +0100 Subject: [PATCH 175/591] [iscsi] Include DHCP server address in iBFT Signed-off-by: Michael Brown --- src/drivers/block/ibft.c | 1 + src/include/ipxe/settings.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c index 6aabd766a..91a808d85 100644 --- a/src/drivers/block/ibft.c +++ b/src/drivers/block/ibft.c @@ -260,6 +260,7 @@ static int ibft_fill_nic ( struct ibft_nic *nic, ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting, ( sizeof ( nic->dns ) / sizeof ( nic->dns[0] ) ) ); + ibft_set_ipaddr_setting ( parent, &nic->dhcp, &dhcp_server_setting, 1 ); DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) ); DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) ); if ( ( rc = ibft_set_string_setting ( NULL, strings, &nic->hostname, diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 4f56f3e9e..b44794af2 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -462,6 +462,8 @@ extern const struct setting asset_setting __setting ( SETTING_HOST_EXTRA, asset ); extern const struct setting board_serial_setting __setting ( SETTING_HOST_EXTRA, board-serial ); +extern const struct setting dhcp_server_setting __setting ( SETTING_MISC, + dhcp-server ); /** * Initialise a settings block From ef1c4b1c9031adcb4aee01bd628d96fc0c676b94 Mon Sep 17 00:00:00 2001 From: Christian Nilsson Date: Tue, 29 Mar 2016 18:44:30 +0200 Subject: [PATCH 176/591] [intel] Add PCI device ID for another I219-V Signed-off-by: Christian Nilsson Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index dc06466a7..a64dd1913 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1067,6 +1067,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", 0 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; From 70509e6a03e8e307c579ca5ebafa591f6741db6f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 29 Mar 2016 20:57:03 +0100 Subject: [PATCH 177/591] [netdevice] Return ENOENT for an unknown bus type It is possible for the preloaded UNDI device to end up with no specified bus type, since it may not be recognised as either a PCI or an ISAPnP device. This will result in a bus type value of zero, which currently results in NULL being treated as a string pointer by netdev_fetch_bustype(). Fix by returning ENOENT if an unknown bus type is specified. Reported-by: Todd Stansell Signed-off-by: Michael Brown --- src/net/netdev_settings.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index c4fd36941..7d893a126 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -141,7 +141,8 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data, assert ( desc->bus_type < ( sizeof ( bustypes ) / sizeof ( bustypes[0] ) ) ); bustype = bustypes[desc->bus_type]; - assert ( bustype != NULL ); + if ( ! bustype ) + return -ENOENT; strncpy ( data, bustype, len ); return strlen ( bustype ); } From c9af896314e33885fc91e86f531bea7e7dd1f9f3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 30 Mar 2016 07:27:09 +0100 Subject: [PATCH 178/591] [linda] Validate payload length There is no way for the hardware to give us an invalid length in the LRH, since it must have parsed this length field in order to perform header splitting. However, this is difficult to prove conclusively. Add an unnecessary length check to explicitly reject any packets larger than the posted receive I/O buffer. Signed-off-by: Michael Brown --- src/drivers/infiniband/linda.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/drivers/infiniband/linda.c b/src/drivers/infiniband/linda.c index 391fff429..77d50d110 100644 --- a/src/drivers/infiniband/linda.c +++ b/src/drivers/infiniband/linda.c @@ -1271,8 +1271,15 @@ static void linda_complete_recv ( struct ib_device *ibdev, /* Completing the eager buffer described in * this header entry. */ - iob_put ( iobuf, payload_len ); - rc = ( err ? -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + if ( payload_len <= iob_tailroom ( iobuf ) ) { + iob_put ( iobuf, payload_len ); + rc = ( err ? + -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + } else { + DBGC ( linda, "Linda %p bad payload len %zd\n", + linda, payload_len ); + rc = -EPROTO; + } /* Redirect to target QP if necessary */ if ( qp != intended_qp ) { DBGC ( linda, "Linda %p redirecting QPN %ld " @@ -1283,7 +1290,7 @@ static void linda_complete_recv ( struct ib_device *ibdev, intended_qp->recv.fill++; } ib_complete_recv ( ibdev, intended_qp, &dest, &source, - iobuf, rc); + iobuf, rc ); } else { /* Completing on a skipped-over eager buffer */ ib_complete_recv ( ibdev, qp, &dest, &source, iobuf, From 597521ef531b2d7f8ed264f33cba7517e11ab05e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 30 Mar 2016 07:31:51 +0100 Subject: [PATCH 179/591] [qib7322] Validate payload length There is no way for the hardware to give us an invalid length in the LRH, since it must have parsed this length field in order to perform header splitting. However, this is difficult to prove conclusively. Add an unnecessary length check to explicitly reject any packets larger than the posted receive I/O buffer. Signed-off-by: Michael Brown --- src/drivers/infiniband/qib7322.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c index 3bd587ec0..af7006e04 100644 --- a/src/drivers/infiniband/qib7322.c +++ b/src/drivers/infiniband/qib7322.c @@ -1507,8 +1507,15 @@ static void qib7322_complete_recv ( struct ib_device *ibdev, /* Completing the eager buffer described in * this header entry. */ - iob_put ( iobuf, payload_len ); - rc = ( err ? -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + if ( payload_len <= iob_tailroom ( iobuf ) ) { + iob_put ( iobuf, payload_len ); + rc = ( err ? + -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + } else { + DBGC ( qib7322, "QIB7322 %p bad payload len " + "%zd\n", qib7322, payload_len ); + rc = -EPROTO; + } /* Redirect to target QP if necessary */ if ( qp != intended_qp ) { DBGC2 ( qib7322, "QIB7322 %p redirecting QPN " @@ -1519,7 +1526,7 @@ static void qib7322_complete_recv ( struct ib_device *ibdev, intended_qp->recv.fill++; } ib_complete_recv ( ibdev, intended_qp, &dest, &source, - iobuf, rc); + iobuf, rc ); } else { /* Completing on a skipped-over eager buffer */ ib_complete_recv ( ibdev, qp, &dest, &source, iobuf, From 320488d0f921c0e13cb1ef2e31c23cd087d148e2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 12 Apr 2016 11:45:58 +0100 Subject: [PATCH 180/591] [test] Update snprintf_ok() to use okx() Signed-off-by: Michael Brown --- src/tests/vsprintf_test.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/tests/vsprintf_test.c b/src/tests/vsprintf_test.c index 0ad4f1c56..ad732a0d2 100644 --- a/src/tests/vsprintf_test.c +++ b/src/tests/vsprintf_test.c @@ -39,21 +39,32 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Report an snprintf() test result * + * @v len Buffer length + * @v expected Expected result + * @v file Test code file + * @v line Test code line + * @v format Format string + * @v ... Arguments */ -#define snprintf_ok( len, result, format, ... ) do { \ - char actual[ (len) ]; \ - const char expected[] = result; \ - size_t actual_len; \ - \ - actual_len = snprintf ( actual, sizeof ( actual ), \ - format, ##__VA_ARGS__ ); \ - ok ( actual_len >= strlen ( result ) ); \ - ok ( strcmp ( actual, expected ) == 0 ); \ - if ( strcmp ( actual, expected ) != 0 ) { \ - DBG ( "SNPRINTF expected \"%s\", got \"%s\"\n", \ - expected, actual ); \ - } \ - } while ( 0 ) +static void snprintf_okx ( size_t len, const char *expected, const char *file, + unsigned int line, const char *fmt, ... ) { + char actual[len]; + size_t actual_len; + va_list args; + + va_start ( args, fmt ); + actual_len = vsnprintf ( actual, sizeof ( actual ), fmt, args ); + va_end ( args ); + okx ( actual_len >= strlen ( expected ), file, line ); + okx ( strcmp ( actual, expected ) == 0, file, line ); + if ( strcmp ( actual, expected ) != 0 ) { + DBG ( "SNPRINTF expected \"%s\", got \"%s\"\n", + expected, actual ); + } +} +#define snprintf_ok( len, result, format, ... ) \ + snprintf_okx ( len, result, __FILE__, __LINE__, format, \ + ##__VA_ARGS__ ) /** * Perform vsprintf() self-tests From cc8824ad4e9486b9fa64f1b1d078ff1963f71219 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 12 Apr 2016 11:51:05 +0100 Subject: [PATCH 181/591] [libc] Print "" for wide-character NULL strings The existing code intends to print NULL strings as "" (for the sake of debug messages), but the logic is incorrect when handling wide-character strings. Fix the logic and add applicable unit tests. Signed-off-by: Michael Brown --- src/core/vsprintf.c | 6 ++++-- src/tests/vsprintf_test.c | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c index cb3bec5dd..9d3a97c2d 100644 --- a/src/core/vsprintf.c +++ b/src/core/vsprintf.c @@ -257,11 +257,13 @@ size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) { } else if ( *fmt == 's' ) { if ( length < &type_sizes[LONG_LEN] ) { ptr = va_arg ( args, char * ); + if ( ! ptr ) + ptr = ""; } else { wptr = va_arg ( args, wchar_t * ); + if ( ! wptr ) + ptr = ""; } - if ( ( ptr == NULL ) && ( wptr == NULL ) ) - ptr = ""; } else if ( *fmt == 'p' ) { intptr_t ptrval; diff --git a/src/tests/vsprintf_test.c b/src/tests/vsprintf_test.c index ad732a0d2..f388b3ded 100644 --- a/src/tests/vsprintf_test.c +++ b/src/tests/vsprintf_test.c @@ -108,6 +108,10 @@ static void vsprintf_test_exec ( void ) { snprintf_ok ( 64, "PCI 00:1f.3", "PCI %02x:%02x.%x", 0x00, 0x1f, 0x03 ); snprintf_ok ( 64, "Region [1000000,3f000000)", "Region [%llx,%llx)", 0x1000000ULL, 0x3f000000ULL ); + + /* Null string (used for debug messages) */ + snprintf_ok ( 16, "", "%s", ( ( char * ) NULL ) ); + snprintf_ok ( 16, "", "%ls", ( ( wchar_t * ) NULL ) ); } /** vsprintf() self-test */ From 5238c85b623200fa0f44a46db93965080053f745 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 12 Apr 2016 11:59:31 +0100 Subject: [PATCH 182/591] [efi] Work around broken EFI HII specification The EFI_HII_CONFIG_ACCESS_PROTOCOL's ExtractConfig() method is passed a request string which includes the parameters being queried plus an apparently meaningless blob of information (the ConfigHdr), and is expected to include this same meaningless blob of information in the results string. Neither the specification nor the existing EDK2 code (including the nominal reference implementation in the DriverSampleDxe driver) provide any reason for the existence of this meaningless blob of information. It appears to be consumed in its entirety by the EFI_HII_CONFIG_ROUTING_PROTOCOL, and to contain zero bits of information by the time it reaches an EFI_HII_CONFIG_ACCESS_PROTOCOL instance. It would potentially allow for multiple configuration data sets to be handled by a single EFI_HII_CONFIG_ACCESS_PROTOCOL instance, in a style alien to the rest of the UEFI specification (which implicitly assumes that the instance pointer is always sufficient to uniquely identify the instance). iPXE currently handles this by simply copying the ConfigHdr from the request string to the results string, and otherwise ignoring it. This approach is also used by some code in EDK2, such as OVMF's PlatformDxe driver. As of EDK2 commit 8a45f80 ("MdeModulePkg: Make HII configuration settings available to OS runtime"), this causes an assertion failure inside EDK2. The failure arises when iPXE is handled a NULL request string, and responds (as per the specification) with a results string including all settings. Since there is no meaningless blob to copy from the request string, there is no corresponding meaningless blob in the results string. This now causes an assertion failure in HiiDatabaseDxe's HiiConfigRoutingExportConfig(). The same failure does not affect the OVMF PlatformDxe driver, which simply passes the request string to the HII BlockToConfig() utility function. The BlockToConfig() function returns EFI_INVALID_PARAMETER when passed a null request string, and PlatformDxe propagates this error directly to the caller. Fix by matching the behaviour of OVMF's PlatformDxe driver: explicitly return EFI_INVALID_PARAMETER if the request string is NULL or empty. This violates the specification (insofar as it is feasible to determine what the specification actually requires), but causes correct behaviour with the EDK2 codebase. Signed-off-by: Michael Brown --- src/interface/efi/efi_snp_hii.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 720402bdb..1e87ea15a 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -546,6 +546,13 @@ efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii, /* Initialise results */ *results = NULL; + /* Work around apparently broken UEFI specification */ + if ( ! ( request && request[0] ) ) { + DBGC ( snpdev, "SNPDEV %p ExtractConfig ignoring malformed " + "request\n", snpdev ); + return EFI_INVALID_PARAMETER; + } + /* Process all request fragments */ for ( pos = *progress = request ; *progress && **progress ; pos = *progress + 1 ) { From ffd959a1d6a0e93d57bb6c0f33bbc2792c296e65 Mon Sep 17 00:00:00 2001 From: Wissam Shoukair Date: Tue, 5 Apr 2016 13:39:08 +0300 Subject: [PATCH 183/591] [mlx_icmd] Fix compilation error in GCC versions newer than 4.6.4 Signed-off-by: Wissam Shoukair Signed-off-by: Michael Brown --- src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c index e42067393..f7d365dee 100644 --- a/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c @@ -316,7 +316,7 @@ mlx_icmd_send_command( ) { mlx_status status = MLX_SUCCESS; - mlx_uint32 icmd_status; + mlx_uint32 icmd_status = MLX_FAILED; if (utils == NULL || data == NULL) { status = MLX_INVALID_PARAMETER; From 0eea8b5c3bd269f2911001fddb7a2823a3658d10 Mon Sep 17 00:00:00 2001 From: Wissam Shoukair Date: Thu, 24 Mar 2016 18:42:59 +0200 Subject: [PATCH 184/591] [golan] Add missing iounmap() Signed-off-by: Wissam Shoukair Signed-off-by: Michael Brown --- src/drivers/infiniband/golan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 64c75526b..ba5a52495 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -2446,6 +2446,7 @@ err_golan_probe_alloc_ibdev: golan_bring_down ( golan ); err_golan_bringup: err_fw_ver_cmdif: + iounmap( golan->iseg ); golan_free_pages( &golan->pages ); err_golan_golan_init_pages: free ( golan ); @@ -2474,7 +2475,7 @@ static void golan_remove_normal ( struct pci_device *pci ) { } golan_bring_down(golan); - + iounmap( golan->iseg ); golan_free_pages( &golan->pages ); free(golan); } From 4afb75842314c454980c748586764afb187cef7c Mon Sep 17 00:00:00 2001 From: Suresh Sundriyal Date: Tue, 12 Apr 2016 14:18:17 +0100 Subject: [PATCH 185/591] [pool] Fix check for reopenable pooled connections Signed-off-by: Michael Brown --- src/include/ipxe/pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/ipxe/pool.h b/src/include/ipxe/pool.h index 27066e9b3..81ff57d75 100644 --- a/src/include/ipxe/pool.h +++ b/src/include/ipxe/pool.h @@ -112,7 +112,7 @@ pool_is_reopenable ( struct pooled_connection *pool ) { /* A connection is reopenable if it has been recycled but is * not yet known to be alive. */ - return ( ( pool->flags & POOL_RECYCLED ) & + return ( ( pool->flags & POOL_RECYCLED ) && ( ! ( pool->flags & POOL_ALIVE ) ) ); } From 5e5450c2d04e6f976ea4cef5db50e136d4a06282 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 14 Apr 2016 16:48:41 +0100 Subject: [PATCH 186/591] [comboot] Support COMBOOT in 64-bit builds Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 2 - src/arch/x86/Makefile | 1 + src/arch/{i386 => x86}/image/com32.c | 72 +++++++------ src/arch/{i386 => x86}/image/comboot.c | 12 +-- src/arch/{i386 => x86}/include/comboot.h | 6 +- .../interface/syslinux/com32_call.c | 17 ++- .../interface/syslinux/com32_wrapper.S | 101 +++++++++--------- .../interface/syslinux/comboot_call.c | 4 +- .../interface/syslinux/comboot_resolv.c | 0 .../tests/comboot/shuffle-simple.asm | 1 - .../{i386 => x86}/tests/comboot/version.asm | 0 src/arch/x86/transitions/librm.S | 66 +++++++++++- 12 files changed, 180 insertions(+), 102 deletions(-) rename src/arch/{i386 => x86}/image/com32.c (77%) rename src/arch/{i386 => x86}/image/comboot.c (99%) rename src/arch/{i386 => x86}/include/comboot.h (96%) rename src/arch/{i386 => x86}/interface/syslinux/com32_call.c (91%) rename src/arch/{i386 => x86}/interface/syslinux/com32_wrapper.S (51%) rename src/arch/{i386 => x86}/interface/syslinux/comboot_call.c (99%) rename src/arch/{i386 => x86}/interface/syslinux/comboot_resolv.c (100%) rename src/arch/{i386 => x86}/tests/comboot/shuffle-simple.asm (99%) rename src/arch/{i386 => x86}/tests/comboot/version.asm (100%) diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index a52986048..fe3adc9ce 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -83,9 +83,7 @@ endif # i386-specific directories containing source files # SRCDIRS += arch/i386/core -SRCDIRS += arch/i386/image SRCDIRS += arch/i386/tests -SRCDIRS += arch/i386/interface/syslinux # Include common x86 Makefile # diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index e933f4a4f..368c29f6d 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -16,6 +16,7 @@ SRCDIRS += arch/x86/interface/pxe SRCDIRS += arch/x86/interface/pxeparent SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/interface/vmware +SRCDIRS += arch/x86/interface/syslinux SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen diff --git a/src/arch/i386/image/com32.c b/src/arch/x86/image/com32.c similarity index 77% rename from src/arch/i386/image/com32.c rename to src/arch/x86/image/com32.c index ff64fd1a1..016652877 100644 --- a/src/arch/i386/image/com32.c +++ b/src/arch/x86/image/com32.c @@ -76,8 +76,6 @@ static int com32_exec_loop ( struct image *image ) { assert ( avail_mem_top != 0 ); - com32_external_esp = phys_to_virt ( avail_mem_top ); - /* Hook COMBOOT API interrupts */ hook_comboot_interrupts(); @@ -88,34 +86,44 @@ static int com32_exec_loop ( struct image *image ) { */ unregister_image ( image ); - __asm__ __volatile__ ( - "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ - "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ - "call _virt_to_phys\n\t" /* Switch to flat physical address space */ - "sti\n\t" /* Enable interrupts */ - "pushl %0\n\t" /* Pointer to CDECL helper function */ - "pushl %1\n\t" /* Pointer to FAR call helper function */ - "pushl %2\n\t" /* Size of low memory bounce buffer */ - "pushl %3\n\t" /* Pointer to low memory bounce buffer */ - "pushl %4\n\t" /* Pointer to INT call helper function */ - "pushl %5\n\t" /* Pointer to the command line arguments */ - "pushl $6\n\t" /* Number of additional arguments */ - "call *%6\n\t" /* Execute image */ - "cli\n\t" /* Disable interrupts */ - "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ - "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ - : - : - /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), - /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), - /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), - /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), - /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), - /* %5 */ "r" ( virt_to_phys ( image->cmdline ? - image->cmdline : "" ) ), - /* %6 */ "r" ( COM32_START_PHYS ) - : - "memory" ); + __asm__ __volatile__ ( PHYS_CODE ( + /* Preserve registers */ + "pushal\n\t" + /* Preserve stack pointer */ + "subl $4, %k0\n\t" + "movl %%esp, (%k0)\n\t" + /* Switch to COM32 stack */ + "movl %k0, %%esp\n\t" + /* Enable interrupts */ + "sti\n\t" + /* Construct stack frame */ + "pushl %k1\n\t" + "pushl %k2\n\t" + "pushl %k3\n\t" + "pushl %k4\n\t" + "pushl %k5\n\t" + "pushl %k6\n\t" + "pushl $6\n\t" + /* Call COM32 entry point */ + "movl %k7, %k0\n\t" + "call *%k0\n\t" + /* Disable interrupts */ + "cli\n\t" + /* Restore stack pointer */ + "movl 24(%%esp), %%esp\n\t" + /* Restore registers */ + "popal\n\t" ) + : + : "r" ( avail_mem_top ), + "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), + "r" ( virt_to_phys ( com32_farcall_wrapper ) ), + "r" ( get_fbms() * 1024 - ( COM32_BOUNCE_SEG << 4 ) ), + "i" ( COM32_BOUNCE_SEG << 4 ), + "r" ( virt_to_phys ( com32_intcall_wrapper ) ), + "r" ( virt_to_phys ( image->cmdline ? + image->cmdline : "" ) ), + "i" ( COM32_START_PHYS ) + : "memory" ); DBGC ( image, "COM32 %p: returned\n", image ); break; @@ -147,7 +155,7 @@ static int com32_exec_loop ( struct image *image ) { /** * Check image name extension - * + * * @v image COM32 image * @ret rc Return status code */ @@ -155,7 +163,7 @@ static int com32_identify ( struct image *image ) { const char *ext; static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; uint8_t buf[5]; - + if ( image->len >= 5 ) { /* Check for magic number * mov eax,21cd4cffh diff --git a/src/arch/i386/image/comboot.c b/src/arch/x86/image/comboot.c similarity index 99% rename from src/arch/i386/image/comboot.c rename to src/arch/x86/image/comboot.c index 20b5ae1e7..9a847f0ff 100644 --- a/src/arch/i386/image/comboot.c +++ b/src/arch/x86/image/comboot.c @@ -64,7 +64,7 @@ struct comboot_psp { /** * Copy command line to PSP - * + * * @v image COMBOOT image */ static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) { @@ -97,7 +97,7 @@ static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) /** * Initialize PSP - * + * * @v image COMBOOT image * @v seg_userptr segment to initialize */ @@ -213,7 +213,7 @@ static int comboot_exec_loop ( struct image *image ) { /** * Check image name extension - * + * * @v image COMBOOT image * @ret rc Return status code */ @@ -254,7 +254,7 @@ static int comboot_prepare_segment ( struct image *image ) seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); /* Allow etra 0x100 bytes before image for PSP */ - filesz = image->len + 0x100; + filesz = image->len + 0x100; /* Ensure the entire 64k segment is free */ memsz = 0xFFFF; @@ -289,7 +289,7 @@ static int comboot_probe ( struct image *image ) { /* Check if this is a COMBOOT image */ if ( ( rc = comboot_identify ( image ) ) != 0 ) { - + return rc; } @@ -304,7 +304,7 @@ static int comboot_probe ( struct image *image ) { */ static int comboot_exec ( struct image *image ) { int rc; - + /* Sanity check for filesize */ if( image->len >= 0xFF00 ) { DBGC( image, "COMBOOT %p: image too large\n", diff --git a/src/arch/i386/include/comboot.h b/src/arch/x86/include/comboot.h similarity index 96% rename from src/arch/i386/include/comboot.h rename to src/arch/x86/include/comboot.h index 5cb1ba54c..69c6ef024 100644 --- a/src/arch/i386/include/comboot.h +++ b/src/arch/x86/include/comboot.h @@ -29,7 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define COMBOOT_FEATURE_LOCAL_BOOT (1 << 0) #define COMBOOT_FEATURE_IDLE_LOOP (1 << 1) -/** Maximum number of shuffle descriptors for +/** Maximum number of shuffle descriptors for * shuffle and boot functions * (INT 22h AX=0012h, 001Ah, 001Bh) */ @@ -102,7 +102,7 @@ typedef struct { extern void hook_comboot_interrupts ( ); extern void unhook_comboot_interrupts ( ); -/* These are not the correct prototypes, but it doens't matter, +/* These are not the correct prototypes, but it doens't matter, * as we only ever get the address of these functions; * they are only called from COM32 code running in PHYS_CODE */ @@ -116,8 +116,6 @@ extern int comboot_resolv ( const char *name, struct in_addr *address ); /* setjmp/longjmp context buffer used to return after loading an image */ extern rmjmp_buf comboot_return; -extern void *com32_external_esp; - #define COMBOOT_EXIT 1 #define COMBOOT_EXIT_RUN_KERNEL 2 #define COMBOOT_EXIT_COMMAND 3 diff --git a/src/arch/i386/interface/syslinux/com32_call.c b/src/arch/x86/interface/syslinux/com32_call.c similarity index 91% rename from src/arch/i386/interface/syslinux/com32_call.c rename to src/arch/x86/interface/syslinux/com32_call.c index 75dcc238f..19fdbaff9 100644 --- a/src/arch/i386/interface/syslinux/com32_call.c +++ b/src/arch/x86/interface/syslinux/com32_call.c @@ -46,6 +46,9 @@ uint16_t __bss16 ( com32_saved_sp ); */ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + DBGC ( &com32_regs, "COM32 INT%x in %#08lx out %#08lx\n", + interrupt, inregs_phys, outregs_phys ); + memcpy_user ( virt_to_user( &com32_regs ), 0, phys_to_user ( inregs_phys ), 0, sizeof(com32sys_t) ); @@ -76,7 +79,7 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad /* patch INT instruction */ "pushw %%ax\n\t" "movb %%ss:(com32_int_vector), %%al\n\t" - "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" + "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" /* perform a jump to avoid problems with cache * consistency in self-modifying code on some CPUs (486) */ @@ -106,7 +109,7 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad if ( outregs_phys ) { memcpy_user ( phys_to_user ( outregs_phys ), 0, - virt_to_user( &com32_regs ), 0, + virt_to_user( &com32_regs ), 0, sizeof(com32sys_t) ); } } @@ -116,6 +119,9 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad */ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + DBGC ( &com32_regs, "COM32 farcall %04x:%04x in %#08lx out %#08lx\n", + ( proc >> 16 ), ( proc & 0xffff ), inregs_phys, outregs_phys ); + memcpy_user ( virt_to_user( &com32_regs ), 0, phys_to_user ( inregs_phys ), 0, sizeof(com32sys_t) ); @@ -165,7 +171,7 @@ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t if ( outregs_phys ) { memcpy_user ( phys_to_user ( outregs_phys ), 0, - virt_to_user( &com32_regs ), 0, + virt_to_user( &com32_regs ), 0, sizeof(com32sys_t) ); } } @@ -176,13 +182,16 @@ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz ) { int32_t eax; + DBGC ( &com32_regs, "COM32 cfarcall %04x:%04x params %#08lx+%#zx\n", + ( proc >> 16 ), ( proc & 0xffff ), stack, stacksz ); + copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz ); com32_farcall_proc = proc; __asm__ __volatile__ ( REAL_CODE ( "lcall *%%ss:(com32_farcall_proc)\n\t" ) : "=a" (eax) - : + : : "ecx", "edx" ); remove_user_from_rm_stack ( 0, stacksz ); diff --git a/src/arch/i386/interface/syslinux/com32_wrapper.S b/src/arch/x86/interface/syslinux/com32_wrapper.S similarity index 51% rename from src/arch/i386/interface/syslinux/com32_wrapper.S rename to src/arch/x86/interface/syslinux/com32_wrapper.S index c9d1452b4..d59a3392c 100644 --- a/src/arch/i386/interface/syslinux/com32_wrapper.S +++ b/src/arch/x86/interface/syslinux/com32_wrapper.S @@ -19,79 +19,82 @@ FILE_LICENCE ( GPL2_OR_LATER ) - .text - .arch i386 - .code32 +#include "librm.h" + .text + + .code32 .globl com32_farcall_wrapper com32_farcall_wrapper: + movl $VIRTUAL(com32_farcall), %eax + jmp com32_wrapper - movl $com32_farcall, %eax - jmp com32_wrapper - - + .code32 .globl com32_cfarcall_wrapper com32_cfarcall_wrapper: + movl $VIRTUAL(com32_cfarcall), %eax + jmp com32_wrapper - movl $com32_cfarcall, %eax - jmp com32_wrapper - - + .code32 .globl com32_intcall_wrapper com32_intcall_wrapper: + movl $VIRTUAL(com32_intcall), %eax + /* fall through */ - movl $com32_intcall, %eax - /*jmp com32_wrapper*/ /* fall through */ - + .code32 com32_wrapper: + + /* Disable interrupts */ cli /* Switch to internal virtual address space */ - call _phys_to_virt + call _phys_to_virt - mov %eax, (com32_helper_function) +#ifdef __x86_64__ - /* Save external COM32 stack pointer */ - movl %esp, (com32_external_esp) + .code64 - /* Copy arguments to caller-save registers */ - movl 12(%esp), %eax - movl 8(%esp), %ecx - movl 4(%esp), %edx + /* Preserve registers which are callee-save for COM32 (i386 API) */ + pushq %rdi + pushq %rsi + pushq %rbp - /* Switch to internal stack */ - movl (com32_internal_esp), %esp + /* Extract parameters from stack */ + movl 28(%rsp), %edi + movl 32(%rsp), %esi + movl 36(%rsp), %edx - /* Copy arguments to internal stack */ - pushl %eax - pushl %ecx - pushl %edx + /* Align stack pointer */ + movq %rsp, %rbp + andq $~0x07, %rsp - call *(com32_helper_function) + /* Call helper function */ + movslq %eax, %rax + call *%rax - /* Clean up stack */ - addl $12, %esp + /* Restore stack pointer */ + movq %rbp, %rsp - /* Save internal stack pointer and restore external stack pointer */ - movl %esp, (com32_internal_esp) - movl (com32_external_esp), %esp + /* Restore registers */ + popq %rbp + popq %rsi + popq %rdi + +#else /* _x86_64 */ + + /* Call helper function */ + pushl 12(%esp) + pushl 12(%esp) + pushl 12(%esp) + call *%eax + addl $12, %esp + +#endif /* _x86_64 */ /* Switch to external flat physical address space */ - call _virt_to_phys + call _virt_to_phys + .code32 + /* Reenable interrupts and return */ sti ret - - - .data - -/* Internal iPXE virtual address space %esp */ -.globl com32_internal_esp -.lcomm com32_internal_esp, 4 - -/* External flat physical address space %esp */ -.globl com32_external_esp -.lcomm com32_external_esp, 4 - -/* Function pointer of helper to call */ -.lcomm com32_helper_function, 4 diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/x86/interface/syslinux/comboot_call.c similarity index 99% rename from src/arch/i386/interface/syslinux/comboot_call.c rename to src/arch/x86/interface/syslinux/comboot_call.c index 565977811..2f5c252c1 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/x86/interface/syslinux/comboot_call.c @@ -489,7 +489,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { struct in_addr addr; copy_from_user ( hostname, hostname_u, 0, len + 1 ); - + /* TODO: * "If the hostname does not contain a dot (.), the * local domain name is automatically appended." @@ -519,7 +519,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { /* Jump to real-mode entry point */ __asm__ __volatile__ ( - REAL_CODE ( + REAL_CODE ( "pushw %0\n\t" "popw %%ds\n\t" "pushl %1\n\t" diff --git a/src/arch/i386/interface/syslinux/comboot_resolv.c b/src/arch/x86/interface/syslinux/comboot_resolv.c similarity index 100% rename from src/arch/i386/interface/syslinux/comboot_resolv.c rename to src/arch/x86/interface/syslinux/comboot_resolv.c diff --git a/src/arch/i386/tests/comboot/shuffle-simple.asm b/src/arch/x86/tests/comboot/shuffle-simple.asm similarity index 99% rename from src/arch/i386/tests/comboot/shuffle-simple.asm rename to src/arch/x86/tests/comboot/shuffle-simple.asm index 8ede8d097..fa574bd72 100644 --- a/src/arch/i386/tests/comboot/shuffle-simple.asm +++ b/src/arch/x86/tests/comboot/shuffle-simple.asm @@ -37,4 +37,3 @@ source: dd 0 dd shuffle_len num_shuffle_descriptors equ 1 - diff --git a/src/arch/i386/tests/comboot/version.asm b/src/arch/x86/tests/comboot/version.asm similarity index 100% rename from src/arch/i386/tests/comboot/version.asm rename to src/arch/x86/tests/comboot/version.asm diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index 3a585a921..f633b352b 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -567,9 +567,10 @@ phys_to_prot: popl %eax ret - /* Expose as _phys_to_virt for use by COMBOOT */ +.if32 /* Expose as _phys_to_virt for use by COMBOOT, if applicable */ .globl _phys_to_virt .equ _phys_to_virt, phys_to_prot +.endif /**************************************************************************** * prot_to_phys (protected-mode near call, 32-bit virtual return address) @@ -615,9 +616,10 @@ prot_to_phys: popl %eax ret - /* Expose as _virt_to_phys for use by COMBOOT */ +.if32 /* Expose as _virt_to_phys for use by COMBOOT, if applicable */ .globl _virt_to_phys .equ _virt_to_phys, prot_to_phys +.endif /**************************************************************************** * intr_to_prot (protected-mode near call, 32-bit virtual return address) @@ -1202,6 +1204,66 @@ phys_call: /* Return and discard function parameters */ ret $( PHC_OFFSET_END - PHC_OFFSET_PARAMS ) +/**************************************************************************** + * phys_to_long (protected-mode near call, 32-bit physical return address) + * + * Used by COMBOOT. + * + **************************************************************************** + */ + .if64 + + .section ".text.phys_to_long", "ax", @progbits + .code32 +phys_to_long: + + /* Switch to virtual addresses */ + call phys_to_prot + + /* Convert to 32-bit virtual return address */ + pushl %eax + movl VIRTUAL(virt_offset), %eax + subl %eax, 4(%esp) + popl %eax + + /* Switch to long mode and return */ + jmp prot_to_long + + /* Expose as _phys_to_virt for use by COMBOOT */ + .globl _phys_to_virt + .equ _phys_to_virt, phys_to_long + + .endif + +/**************************************************************************** + * long_to_phys (long-mode near call, 64-bit virtual return address) + * + * Used by COMBOOT. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_to_phys", "ax", @progbits + .code64 +long_to_phys: + + /* Switch to protected mode */ + call long_to_prot + .code32 + + /* Convert to 32-bit virtual return address */ + popl (%esp) + + /* Switch to physical addresses and return */ + jmp prot_to_phys + + /* Expose as _virt_to_phys for use by COMBOOT */ + .globl _virt_to_phys + .equ _virt_to_phys, long_to_phys + + .endif + /**************************************************************************** * flatten_real_mode (real-mode near call) * From 237949491860bf1ca6f704e586bf723b7d8001e7 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 11 Apr 2016 11:26:56 +0200 Subject: [PATCH 187/591] [pci] Add pci_find_next_capability() PCI devices may support more capabilities of the same type (for example PCI_CAP_ID_VNDR) and there was no way to discover all of them. This commit adds a new API pci_find_next_capability which provides this functionality. It would typically be used like so: for (pos = pci_find_capability(pci, PCI_CAP_ID_VNDR); pos > 0; pos = pci_find_next_capability(pci, pos, PCI_CAP_ID_VNDR)) { ... } Signed-off-by: Ladi Prosek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael Brown --- src/drivers/bus/pciextra.c | 54 +++++++++++++++++++++++++++++--------- src/include/ipxe/pci.h | 2 ++ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 82287fb86..3082d8a3d 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -3,6 +3,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +static int pci_find_capability_common ( struct pci_device *pci, + uint8_t pos, int cap ) { + uint8_t id; + int ttl = 48; + + while ( ttl-- && pos >= 0x40 ) { + pos &= ~3; + pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id ); + DBG ( "PCI Capability: %d\n", id ); + if ( id == 0xff ) + break; + if ( id == cap ) + return pos; + pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos ); + } + return 0; +} + /** * Look for a PCI capability * @@ -17,9 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int pci_find_capability ( struct pci_device *pci, int cap ) { uint16_t status; - uint8_t pos, id; + uint8_t pos; uint8_t hdr_type; - int ttl = 48; pci_read_config_word ( pci, PCI_STATUS, &status ); if ( ! ( status & PCI_STATUS_CAP_LIST ) ) @@ -36,17 +53,28 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos ); break; } - while ( ttl-- && pos >= 0x40 ) { - pos &= ~3; - pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id ); - DBG ( "PCI Capability: %d\n", id ); - if ( id == 0xff ) - break; - if ( id == cap ) - return pos; - pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos ); - } - return 0; + return pci_find_capability_common ( pci, pos, cap ); +} + +/** + * Look for another PCI capability + * + * @v pci PCI device to query + * @v pos Address of the current capability + * @v cap Capability code + * @ret address Address of capability, or 0 if not found + * + * Determine whether or not a device supports a given PCI capability + * starting the search at a given address within the device's PCI + * configuration space. Returns the address of the next capability + * structure within the device's PCI configuration space, or 0 if the + * device does not support another such capability. + */ +int pci_find_next_capability ( struct pci_device *pci, int pos, int cap ) { + uint8_t new_pos; + + pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &new_pos ); + return pci_find_capability_common ( pci, new_pos, cap ); } /** diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 89d9d8040..0c6bc7ed6 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -286,6 +286,8 @@ 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 ); 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 ); /** From 7b499f849edc79f30b27dfe8143c04e90e3154c9 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 11 Apr 2016 11:26:57 +0200 Subject: [PATCH 188/591] [virtio] Add virtio 1.0 constants and data structures Virtio 1.0 introduces new constants and data structures, common to all devices as well as specific to virtio-net. This commit adds a subset of these to be able to drive the virtio-net 1.0 network device. Signed-off-by: Ladi Prosek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael Brown --- src/drivers/net/virtio-net.h | 16 +++++++++ src/include/ipxe/virtio-pci.h | 60 ++++++++++++++++++++++++++++++++++ src/include/ipxe/virtio-ring.h | 8 +++++ 3 files changed, 84 insertions(+) diff --git a/src/drivers/net/virtio-net.h b/src/drivers/net/virtio-net.h index 3abef28ea..c2b4a17cc 100644 --- a/src/drivers/net/virtio-net.h +++ b/src/drivers/net/virtio-net.h @@ -14,6 +14,12 @@ #define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ #define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ #define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Driver can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* Configuration status field is available. */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel is available. */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support. */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering. */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Driver can send gratuitous packets. */ struct virtio_net_config { @@ -41,4 +47,14 @@ struct virtio_net_hdr uint16_t csum_start; uint16_t csum_offset; }; + +/* Virtio 1.0 version of the first element of the scatter-gather list. */ +struct virtio_net_hdr_modern +{ + struct virtio_net_hdr legacy; + + /* Used only if VIRTIO_NET_F_MRG_RXBUF: */ + uint16_t num_buffers; +}; + #endif /* _VIRTIO_NET_H_ */ diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index a09c46316..8076f20c8 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -37,6 +37,66 @@ /* Virtio ABI version, this must match exactly */ #define VIRTIO_PCI_ABI_VERSION 0 +/* PCI capability types: */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 /* Common configuration */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 /* Notifications */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 /* ISR access */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 /* Device specific configuration */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 /* PCI configuration access */ + +#define __u8 uint8_t +#define __le16 uint16_t +#define __le32 uint32_t +#define __le64 uint64_t + +/* This is the PCI capability header: */ +struct virtio_pci_cap { + __u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + __u8 cap_next; /* Generic PCI field: next ptr. */ + __u8 cap_len; /* Generic PCI field: capability length */ + __u8 cfg_type; /* Identifies the structure. */ + __u8 bar; /* Where to find it. */ + __u8 padding[3]; /* Pad to full dword. */ + __le32 offset; /* Offset within bar. */ + __le32 length; /* Length of the structure, in bytes. */ +}; + +struct virtio_pci_notify_cap { + struct virtio_pci_cap cap; + __le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ +}; + +struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; + __u8 pci_cfg_data[4]; /* Data for BAR access. */ +}; + +/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */ +struct virtio_pci_common_cfg { + /* About the whole device. */ + __le32 device_feature_select; /* read-write */ + __le32 device_feature; /* read-only */ + __le32 guest_feature_select; /* read-write */ + __le32 guest_feature; /* read-write */ + __le16 msix_config; /* read-write */ + __le16 num_queues; /* read-only */ + __u8 device_status; /* read-write */ + __u8 config_generation; /* read-only */ + + /* About a specific virtqueue. */ + __le16 queue_select; /* read-write */ + __le16 queue_size; /* read-write, power of 2. */ + __le16 queue_msix_vector; /* read-write */ + __le16 queue_enable; /* read-write */ + __le16 queue_notify_off; /* read-only */ + __le32 queue_desc_lo; /* read-write */ + __le32 queue_desc_hi; /* read-write */ + __le32 queue_avail_lo; /* read-write */ + __le32 queue_avail_hi; /* read-write */ + __le32 queue_used_lo; /* read-write */ + __le32 queue_used_hi; /* read-write */ +}; + static inline u32 vp_get_features(unsigned int ioaddr) { return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index c687acab7..e44d13c05 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -8,9 +8,17 @@ #define VIRTIO_CONFIG_S_DRIVER 2 /* Driver has used its parts of the config, and is happy */ #define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* Driver has finished configuring features */ +#define VIRTIO_CONFIG_S_FEATURES_OK 8 /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 +/* Virtio feature flags used to negotiate device and driver features. */ +/* Can the device handle any descriptor layout? */ +#define VIRTIO_F_ANY_LAYOUT 27 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + #define MAX_QUEUE_NUM (256) #define VRING_DESC_F_NEXT 1 From 8a055a2a707f8cb92e7b62512391e03b4e25cae2 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 11 Apr 2016 11:26:58 +0200 Subject: [PATCH 189/591] [virtio] Add virtio 1.0 PCI support This commit adds support for driving virtio 1.0 PCI devices. In addition to various helpers, a number of vpm_ functions are introduced to be used instead of their legacy vp_ counterparts when accessing virtio 1.0 (aka modern) devices. Signed-off-by: Ladi Prosek Reviewed-by: Michael S. Tsirkin Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/bus/virtio-pci.c | 353 ++++++++++++++++++++++++++++++++- src/drivers/bus/virtio-ring.c | 17 +- src/drivers/net/virtio-net.c | 5 +- src/include/ipxe/errfile.h | 1 + src/include/ipxe/virtio-pci.h | 147 ++++++++++++++ src/include/ipxe/virtio-ring.h | 6 +- 6 files changed, 517 insertions(+), 12 deletions(-) diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index fbef067bc..a1c805a87 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -11,10 +11,15 @@ * */ +#include "errno.h" +#include "byteswap.h" #include "etherboot.h" #include "ipxe/io.h" -#include "ipxe/virtio-ring.h" +#include "ipxe/iomap.h" +#include "ipxe/pci.h" +#include "ipxe/reboot.h" #include "ipxe/virtio-pci.h" +#include "ipxe/virtio-ring.h" int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq) @@ -30,19 +35,19 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); if (!num) { - printf("ERROR: queue size is 0\n"); + DBG("VIRTIO-PCI ERROR: queue size is 0\n"); return -1; } if (num > MAX_QUEUE_NUM) { - printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); + DBG("VIRTIO-PCI ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); return -1; } /* check if the queue is already active */ if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { - printf("ERROR: queue already active\n"); + DBG("VIRTIO-PCI ERROR: queue already active\n"); return -1; } @@ -62,3 +67,343 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, return num; } + +#define CFG_POS(vdev, field) \ + (vdev->cfg_cap_pos + offsetof(struct virtio_pci_cfg_cap, field)) + +static void prep_pci_cfg_cap(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, + size_t offset, u32 length) +{ + pci_write_config_byte(vdev->pci, CFG_POS(vdev, cap.bar), region->bar); + pci_write_config_dword(vdev->pci, CFG_POS(vdev, cap.length), length); + pci_write_config_dword(vdev->pci, CFG_POS(vdev, cap.offset), + (intptr_t)(region->base + offset)); +} + +void vpm_iowrite8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u8 data, size_t offset) +{ + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + writeb(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + outb(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 1); + pci_write_config_byte(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); + break; + default: + assert(0); + break; + } +} + +void vpm_iowrite16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u16 data, size_t offset) +{ + data = cpu_to_le16(data); + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + writew(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + outw(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 2); + pci_write_config_word(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); + break; + default: + assert(0); + break; + } +} + +void vpm_iowrite32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u32 data, size_t offset) +{ + data = cpu_to_le32(data); + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + writel(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + outl(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 4); + pci_write_config_dword(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); + break; + default: + assert(0); + break; + } +} + +u8 vpm_ioread8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset) +{ + uint8_t data; + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + data = readb(region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + data = inb(region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 1); + pci_read_config_byte(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); + break; + default: + assert(0); + data = 0; + break; + } + return data; +} + +u16 vpm_ioread16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset) +{ + uint16_t data; + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + data = readw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + data = inw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 2); + pci_read_config_word(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); + break; + default: + assert(0); + data = 0; + break; + } + return le16_to_cpu(data); +} + +u32 vpm_ioread32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset) +{ + uint32_t data; + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + data = readw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + data = inw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 4); + pci_read_config_dword(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); + break; + default: + assert(0); + data = 0; + break; + } + return le32_to_cpu(data); +} + +int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type) +{ + int pos; + uint8_t type, bar; + + for (pos = pci_find_capability(pci, PCI_CAP_ID_VNDR); + pos > 0; + pos = pci_find_next_capability(pci, pos, PCI_CAP_ID_VNDR)) { + + pci_read_config_byte(pci, pos + offsetof(struct virtio_pci_cap, + cfg_type), &type); + pci_read_config_byte(pci, pos + offsetof(struct virtio_pci_cap, + bar), &bar); + + /* Ignore structures with reserved BAR values */ + if (bar > 0x5) { + continue; + } + + if (type == cfg_type) { + return pos; + } + } + return 0; +} + +int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, + u32 align, u32 start, u32 size, + struct virtio_pci_region *region) +{ + u8 bar; + u32 offset, length, base_raw; + unsigned long base; + + pci_read_config_byte(pci, cap + offsetof(struct virtio_pci_cap, bar), &bar); + pci_read_config_dword(pci, cap + offsetof(struct virtio_pci_cap, offset), + &offset); + pci_read_config_dword(pci, cap + offsetof(struct virtio_pci_cap, length), + &length); + + if (length <= start) { + DBG("VIRTIO-PCI bad capability len %u (>%u expected)\n", length, start); + return -EINVAL; + } + if (length - start < minlen) { + DBG("VIRTIO-PCI bad capability len %u (>=%zu expected)\n", length, minlen); + return -EINVAL; + } + length -= start; + if (start + offset < offset) { + DBG("VIRTIO-PCI map wrap-around %u+%u\n", start, offset); + return -EINVAL; + } + offset += start; + if (offset & (align - 1)) { + DBG("VIRTIO-PCI offset %u not aligned to %u\n", offset, align); + return -EINVAL; + } + if (length > size) { + length = size; + } + + if (minlen + offset < minlen || + minlen + offset > pci_bar_size(pci, PCI_BASE_ADDRESS(bar))) { + DBG("VIRTIO-PCI map virtio %zu@%u out of range on bar %i length %lu\n", + minlen, offset, + bar, (unsigned long)pci_bar_size(pci, PCI_BASE_ADDRESS(bar))); + return -EINVAL; + } + + region->base = NULL; + region->length = length; + region->bar = bar; + + base = pci_bar_start(pci, PCI_BASE_ADDRESS(bar)); + if (base) { + pci_read_config_dword(pci, PCI_BASE_ADDRESS(bar), &base_raw); + + if (base_raw & PCI_BASE_ADDRESS_SPACE_IO) { + /* Region accessed using port I/O */ + region->base = (void *)(base + offset); + region->flags = VIRTIO_PCI_REGION_PORT; + } else { + /* Region mapped into memory space */ + region->base = ioremap(base + offset, length); + region->flags = VIRTIO_PCI_REGION_MEMORY; + } + } + if (!region->base) { + /* Region accessed via PCI config space window */ + region->base = (void *)(intptr_t)offset; + region->flags = VIRTIO_PCI_REGION_PCI_CONFIG; + } + return 0; +} + +void virtio_pci_unmap_capability(struct virtio_pci_region *region) +{ + unsigned region_type = region->flags & VIRTIO_PCI_REGION_TYPE_MASK; + if (region_type == VIRTIO_PCI_REGION_MEMORY) { + iounmap(region->base); + } +} + +void vpm_notify(struct virtio_pci_modern_device *vdev, + struct vring_virtqueue *vq) +{ + vpm_iowrite16(vdev, &vq->notification, (u16)vq->queue_index, 0); +} + +int vpm_find_vqs(struct virtio_pci_modern_device *vdev, + unsigned nvqs, struct vring_virtqueue *vqs) +{ + unsigned i; + struct vring_virtqueue *vq; + u16 size, off; + u32 notify_offset_multiplier; + int err; + + if (nvqs > vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(num_queues))) { + return -ENOENT; + } + + /* Read notify_off_multiplier from config space. */ + pci_read_config_dword(vdev->pci, + vdev->notify_cap_pos + offsetof(struct virtio_pci_notify_cap, + notify_off_multiplier), + ¬ify_offset_multiplier); + + for (i = 0; i < nvqs; i++) { + /* Select the queue we're interested in */ + vpm_iowrite16(vdev, &vdev->common, (u16)i, COMMON_OFFSET(queue_select)); + + /* Check if queue is either not available or already active. */ + size = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_size)); + /* QEMU has a bug where queues don't revert to inactive on device + * reset. Skip checking the queue_enable field until it is fixed. + */ + if (!size /*|| vpm_ioread16(vdev, &vdev->common.queue_enable)*/) + return -ENOENT; + + if (size & (size - 1)) { + DBG("VIRTIO-PCI %p: bad queue size %u", vdev, size); + return -EINVAL; + } + + vq = &vqs[i]; + vq->queue_index = i; + + /* get offset of notification word for this vq */ + off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off)); + vq->vring.num = size; + + vring_init(&vq->vring, size, (unsigned char *)vq->queue); + + /* activate the queue */ + vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size)); + + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.desc), + COMMON_OFFSET(queue_desc_lo), + COMMON_OFFSET(queue_desc_hi)); + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.avail), + COMMON_OFFSET(queue_avail_lo), + COMMON_OFFSET(queue_avail_hi)); + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.used), + COMMON_OFFSET(queue_used_lo), + COMMON_OFFSET(queue_used_hi)); + + err = virtio_pci_map_capability(vdev->pci, + vdev->notify_cap_pos, 2, 2, + off * notify_offset_multiplier, 2, + &vq->notification); + if (err) { + goto err_map_notify; + } + } + + /* Select and activate all queues. Has to be done last: once we do + * this, there's no way to go back except reset. + */ + for (i = 0; i < nvqs; i++) { + vq = &vqs[i]; + vpm_iowrite16(vdev, &vdev->common, (u16)vq->queue_index, + COMMON_OFFSET(queue_select)); + vpm_iowrite16(vdev, &vdev->common, 1, COMMON_OFFSET(queue_enable)); + } + return 0; + +err_map_notify: + /* Undo the virtio_pci_map_capability calls. */ + while (i-- > 0) { + virtio_pci_unmap_capability(&vqs[i].notification); + } + return err; +} diff --git a/src/drivers/bus/virtio-ring.c b/src/drivers/bus/virtio-ring.c index e55b6d0ed..98e787e16 100644 --- a/src/drivers/bus/virtio-ring.c +++ b/src/drivers/bus/virtio-ring.c @@ -18,8 +18,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "etherboot.h" #include "ipxe/io.h" -#include "ipxe/virtio-ring.h" #include "ipxe/virtio-pci.h" +#include "ipxe/virtio-ring.h" #define BUG() do { \ printf("BUG: failure at %s:%d/%s()!\n", \ @@ -122,7 +122,8 @@ void vring_add_buf(struct vring_virtqueue *vq, wmb(); } -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +void vring_kick(struct virtio_pci_modern_device *vdev, unsigned int ioaddr, + struct vring_virtqueue *vq, int num_added) { struct vring *vr = &vq->vring; @@ -130,7 +131,13 @@ void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) vr->avail->idx += num_added; mb(); - if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) - vp_notify(ioaddr, vq->queue_index); + if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) { + if (vdev) { + /* virtio 1.0 */ + vpm_notify(vdev, vq); + } else { + /* legacy virtio */ + vp_notify(ioaddr, vq->queue_index); + } + } } - diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 533ccb0c6..446bbd68a 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -24,14 +24,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include #include #include #include -#include #include +#include #include "virtio-net.h" /* @@ -135,7 +136,7 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, virtnet, iobuf, vq_idx ); vring_add_buf ( vq, list, out, in, iobuf, 0 ); - vring_kick ( virtnet->ioaddr, vq, 1 ); + vring_kick ( NULL, virtnet->ioaddr, vq, 1 ); } /** Try to keep rx virtqueue filled with iobufs diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 338ebddc2..4681567b9 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -188,6 +188,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_eoib ( ERRFILE_DRIVER | 0x007c0000 ) #define ERRFILE_golan ( ERRFILE_DRIVER | 0x007d0000 ) #define ERRFILE_flexboot_nodnic ( ERRFILE_DRIVER | 0x007e0000 ) +#define ERRFILE_virtio_pci ( ERRFILE_DRIVER | 0x007f0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index 8076f20c8..c7452c82f 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -97,6 +97,44 @@ struct virtio_pci_common_cfg { __le32 queue_used_hi; /* read-write */ }; +/* Virtio 1.0 PCI region descriptor. We support memory mapped I/O, port I/O, + * and PCI config space access via the cfg PCI capability as a fallback. */ +struct virtio_pci_region { + void *base; + size_t length; + u8 bar; + +/* How to interpret the base field */ +#define VIRTIO_PCI_REGION_TYPE_MASK 0x00000003 +/* The base field is a memory address */ +#define VIRTIO_PCI_REGION_MEMORY 0x00000000 +/* The base field is a port address */ +#define VIRTIO_PCI_REGION_PORT 0x00000001 +/* The base field is an offset within the PCI bar */ +#define VIRTIO_PCI_REGION_PCI_CONFIG 0x00000002 + unsigned flags; +}; + +/* Virtio 1.0 device state */ +struct virtio_pci_modern_device { + struct pci_device *pci; + + /* VIRTIO_PCI_CAP_PCI_CFG position */ + int cfg_cap_pos; + + /* VIRTIO_PCI_CAP_COMMON_CFG data */ + struct virtio_pci_region common; + + /* VIRTIO_PCI_CAP_DEVICE_CFG data */ + struct virtio_pci_region device; + + /* VIRTIO_PCI_CAP_ISR_CFG data */ + struct virtio_pci_region isr; + + /* VIRTIO_PCI_CAP_NOTIFY_CFG data */ + int notify_cap_pos; +}; + static inline u32 vp_get_features(unsigned int ioaddr) { return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); @@ -156,6 +194,115 @@ static inline void vp_del_vq(unsigned int ioaddr, int queue_index) outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); } +struct vring_virtqueue; + int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq); + +/* Virtio 1.0 I/O routines abstract away the three possible HW access + * mechanisms - memory, port I/O, and PCI cfg space access. Also built-in + * are endianness conversions - to LE on write and from LE on read. */ + +void vpm_iowrite8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u8 data, size_t offset); + +void vpm_iowrite16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u16 data, size_t offset); + +void vpm_iowrite32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u32 data, size_t offset); + +static inline void vpm_iowrite64(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, + u64 data, size_t offset_lo, size_t offset_hi) +{ + vpm_iowrite32(vdev, region, (u32)data, offset_lo); + vpm_iowrite32(vdev, region, data >> 32, offset_hi); +} + +u8 vpm_ioread8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset); + +u16 vpm_ioread16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset); + +u32 vpm_ioread32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset); + +/* Virtio 1.0 device manipulation routines */ + +#define COMMON_OFFSET(field) offsetof(struct virtio_pci_common_cfg, field) + +static inline void vpm_reset(struct virtio_pci_modern_device *vdev) +{ + vpm_iowrite8(vdev, &vdev->common, 0, COMMON_OFFSET(device_status)); + while (vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status))) + mdelay(1); +} + +static inline u8 vpm_get_status(struct virtio_pci_modern_device *vdev) +{ + return vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status)); +} + +static inline void vpm_add_status(struct virtio_pci_modern_device *vdev, + u8 status) +{ + u8 curr_status = vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status)); + vpm_iowrite8(vdev, &vdev->common, + curr_status | status, COMMON_OFFSET(device_status)); +} + +static inline u64 vpm_get_features(struct virtio_pci_modern_device *vdev) +{ + u32 features_lo, features_hi; + + vpm_iowrite32(vdev, &vdev->common, 0, COMMON_OFFSET(device_feature_select)); + features_lo = vpm_ioread32(vdev, &vdev->common, COMMON_OFFSET(device_feature)); + vpm_iowrite32(vdev, &vdev->common, 1, COMMON_OFFSET(device_feature_select)); + features_hi = vpm_ioread32(vdev, &vdev->common, COMMON_OFFSET(device_feature)); + + return ((u64)features_hi << 32) | features_lo; +} + +static inline void vpm_set_features(struct virtio_pci_modern_device *vdev, + u64 features) +{ + u32 features_lo = (u32)features; + u32 features_hi = features >> 32; + + vpm_iowrite32(vdev, &vdev->common, 0, COMMON_OFFSET(guest_feature_select)); + vpm_iowrite32(vdev, &vdev->common, features_lo, COMMON_OFFSET(guest_feature)); + vpm_iowrite32(vdev, &vdev->common, 1, COMMON_OFFSET(guest_feature_select)); + vpm_iowrite32(vdev, &vdev->common, features_hi, COMMON_OFFSET(guest_feature)); +} + +static inline void vpm_get(struct virtio_pci_modern_device *vdev, + unsigned offset, void *buf, unsigned len) +{ + u8 *ptr = buf; + unsigned i; + + for (i = 0; i < len; i++) + ptr[i] = vpm_ioread8(vdev, &vdev->device, offset + i); +} + +static inline u8 vpm_get_isr(struct virtio_pci_modern_device *vdev) +{ + return vpm_ioread8(vdev, &vdev->isr, 0); +} + +void vpm_notify(struct virtio_pci_modern_device *vdev, + struct vring_virtqueue *vq); + +int vpm_find_vqs(struct virtio_pci_modern_device *vdev, + unsigned nvqs, struct vring_virtqueue *vqs); + +int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type); + +int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, + u32 align, u32 start, u32 size, + struct virtio_pci_region *region); + +void virtio_pci_unmap_capability(struct virtio_pci_region *region); #endif /* _VIRTIO_PCI_H_ */ diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index e44d13c05..6ba550b5a 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -1,6 +1,8 @@ #ifndef _VIRTIO_RING_H_ # define _VIRTIO_RING_H_ +#include + /* Status byte for guest to report progress, and synchronize features. */ /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 @@ -79,6 +81,7 @@ struct vring_virtqueue { void *vdata[MAX_QUEUE_NUM]; /* PCI */ int queue_index; + struct virtio_pci_region notification; }; struct vring_list { @@ -142,6 +145,7 @@ void *vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], unsigned int out, unsigned int in, void *index, int num_added); -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); +void vring_kick(struct virtio_pci_modern_device *vdev, unsigned int ioaddr, + struct vring_virtqueue *vq, int num_added); #endif /* _VIRTIO_RING_H_ */ From 988243c93fb87190e1867ef913136ecf139e7cb8 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 11 Apr 2016 11:26:59 +0200 Subject: [PATCH 190/591] [virtio] Add virtio-net 1.0 support This commit makes virtio-net support devices with VEN 0x1af4 and DEV 0x1041, which is how non-transitional (modern-only) virtio-net devices are exposed on the PCI bus. Transitional devices supporting both the old 0.9.5 and new 1.0 version of the virtio spec are driven using the new protocol. Legacy devices are driven using the old protocol, same as before this commit. Signed-off-by: Ladi Prosek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael Brown --- src/drivers/net/virtio-net.c | 262 +++++++++++++++++++++++++++++++++-- 1 file changed, 251 insertions(+), 11 deletions(-) diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 446bbd68a..fe0fd4b86 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -89,6 +89,12 @@ struct virtnet_nic { /** Base pio register address */ unsigned long ioaddr; + /** 0 for legacy, 1 for virtio 1.0 */ + int virtio_version; + + /** Virtio 1.0 device data */ + struct virtio_pci_modern_device vdev; + /** RX/TX virtqueues */ struct vring_virtqueue *virtqueue; @@ -99,7 +105,7 @@ struct virtnet_nic { unsigned int rx_num_iobufs; /** Virtio net packet header, we only need one */ - struct virtio_net_hdr empty_header; + struct virtio_net_hdr_modern empty_header; }; /** Add an iobuf to a virtqueue @@ -116,6 +122,9 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx]; unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0; unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2; + size_t header_len = virtnet->virtio_version + ? sizeof ( virtnet->empty_header ) + : sizeof ( virtnet->empty_header.legacy ); struct vring_list list[] = { { /* Share a single zeroed virtio net header between all @@ -124,7 +133,7 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, * header fields get used. */ .addr = ( char* ) &virtnet->empty_header, - .length = sizeof ( virtnet->empty_header ), + .length = header_len, }, { .addr = ( char* ) iobuf->data, @@ -136,7 +145,8 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, virtnet, iobuf, vq_idx ); vring_add_buf ( vq, list, out, in, iobuf, 0 ); - vring_kick ( NULL, virtnet->ioaddr, vq, 1 ); + vring_kick ( virtnet->virtio_version ? &virtnet->vdev : NULL, + virtnet->ioaddr, vq, 1 ); } /** Try to keep rx virtqueue filled with iobufs @@ -165,12 +175,12 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { } } -/** Open network device +/** Open network device, legacy virtio 0.9.5 * * @v netdev Network device * @ret rc Return status code */ -static int virtnet_open ( struct net_device *netdev ) { +static int virtnet_open_legacy ( struct net_device *netdev ) { struct virtnet_nic *virtnet = netdev->priv; unsigned long ioaddr = virtnet->ioaddr; u32 features; @@ -211,6 +221,81 @@ static int virtnet_open ( struct net_device *netdev ) { return 0; } +/** Open network device, modern virtio 1.0 + * + * @v netdev Network device + * @ret rc Return status code + */ +static int virtnet_open_modern ( struct net_device *netdev ) { + struct virtnet_nic *virtnet = netdev->priv; + u64 features; + u8 status; + + /* Negotiate features */ + features = vpm_get_features ( &virtnet->vdev ); + if ( ! ( features & VIRTIO_F_VERSION_1 ) ) { + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -EINVAL; + } + vpm_set_features ( &virtnet->vdev, features & ( + ( 1ULL << VIRTIO_NET_F_MAC ) | + ( 1ULL << VIRTIO_F_VERSION_1 ) | + ( 1ULL << VIRTIO_F_ANY_LAYOUT ) ) ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FEATURES_OK ); + + status = vpm_get_status ( &virtnet->vdev ); + if ( ! ( status & VIRTIO_CONFIG_S_FEATURES_OK ) ) { + DBGC ( virtnet, "VIRTIO-NET %p device didn't accept features\n", + virtnet ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -EINVAL; + } + + /* Allocate virtqueues */ + virtnet->virtqueue = zalloc ( QUEUE_NB * + sizeof ( *virtnet->virtqueue ) ); + if ( ! virtnet->virtqueue ) { + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -ENOMEM; + } + + /* Initialize rx/tx virtqueues */ + if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) { + DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n", + virtnet ); + free ( virtnet->virtqueue ); + virtnet->virtqueue = NULL; + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -ENOENT; + } + + /* Disable interrupts before starting */ + netdev_irq ( netdev, 0 ); + + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER_OK ); + + /* Initialize rx packets */ + INIT_LIST_HEAD ( &virtnet->rx_iobufs ); + virtnet->rx_num_iobufs = 0; + virtnet_refill_rx_virtqueue ( netdev ); + return 0; +} + +/** Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int virtnet_open ( struct net_device *netdev ) { + struct virtnet_nic *virtnet = netdev->priv; + + if ( virtnet->virtio_version ) { + return virtnet_open_modern ( netdev ); + } else { + return virtnet_open_legacy ( netdev ); + } +} + /** Close network device * * @v netdev Network device @@ -219,10 +304,19 @@ static void virtnet_close ( struct net_device *netdev ) { struct virtnet_nic *virtnet = netdev->priv; struct io_buffer *iobuf; struct io_buffer *next_iobuf; + int i; - vp_reset ( virtnet->ioaddr ); + if ( virtnet->virtio_version ) { + vpm_reset ( &virtnet->vdev ); + } else { + vp_reset ( virtnet->ioaddr ); + } /* Virtqueues can be freed now that NIC is reset */ + for ( i = 0 ; i < QUEUE_NB ; i++ ) { + virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification ); + } + free ( virtnet->virtqueue ); virtnet->virtqueue = NULL; @@ -303,10 +397,14 @@ static void virtnet_poll ( struct net_device *netdev ) { /* Acknowledge interrupt. This is necessary for UNDI operation and * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being - * set (that flag is just a hint and the hypervisor not not have to + * set (that flag is just a hint and the hypervisor does not have to * honor it). */ - vp_get_isr ( virtnet->ioaddr ); + if ( virtnet->virtio_version ) { + vpm_get_isr ( &virtnet->vdev ); + } else { + vp_get_isr ( virtnet->ioaddr ); + } virtnet_process_tx_packets ( netdev ); virtnet_process_rx_packets ( netdev ); @@ -339,13 +437,12 @@ static struct net_device_operations virtnet_operations = { }; /** - * Probe PCI device + * Probe PCI device, legacy virtio 0.9.5 * * @v pci PCI device - * @v id PCI ID * @ret rc Return status code */ -static int virtnet_probe ( struct pci_device *pci ) { +static int virtnet_probe_legacy ( struct pci_device *pci ) { unsigned long ioaddr = pci->ioaddr; struct net_device *netdev; struct virtnet_nic *virtnet; @@ -395,6 +492,143 @@ static int virtnet_probe ( struct pci_device *pci ) { return rc; } +/** + * Probe PCI device, modern virtio 1.0 + * + * @v pci PCI device + * @v found_dev Set to non-zero if modern device was found (probe may still fail) + * @ret rc Return status code + */ +static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { + struct net_device *netdev; + struct virtnet_nic *virtnet; + u64 features; + int rc, common, isr, notify, config, device; + + common = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_COMMON_CFG ); + if ( ! common ) { + DBG ( "Common virtio capability not found!\n" ); + return -ENODEV; + } + *found_dev = 1; + + isr = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_ISR_CFG ); + notify = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_NOTIFY_CFG ); + config = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_PCI_CFG ); + if ( ! isr || ! notify || ! config ) { + DBG ( "Missing virtio capabilities %i/%i/%i/%i\n", + common, isr, notify, config ); + return -EINVAL; + } + device = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_DEVICE_CFG ); + + /* Allocate and hook up net device */ + netdev = alloc_etherdev ( sizeof ( *virtnet ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &virtnet_operations ); + virtnet = netdev->priv; + + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + + DBGC ( virtnet, "VIRTIO-NET modern %p busaddr=%s irq=%d\n", + virtnet, pci->dev.name, pci->irq ); + + virtnet->vdev.pci = pci; + rc = virtio_pci_map_capability ( pci, common, + sizeof ( struct virtio_pci_common_cfg ), 4, + 0, sizeof ( struct virtio_pci_common_cfg ), + &virtnet->vdev.common ); + if ( rc ) + goto err_map_common; + + rc = virtio_pci_map_capability ( pci, isr, sizeof ( u8 ), 1, + 0, 1, + &virtnet->vdev.isr ); + if ( rc ) + goto err_map_isr; + + virtnet->vdev.notify_cap_pos = notify; + virtnet->vdev.cfg_cap_pos = config; + + /* Map the device capability */ + if ( device ) { + rc = virtio_pci_map_capability ( pci, device, + 0, 4, 0, sizeof ( struct virtio_net_config ), + &virtnet->vdev.device ); + if ( rc ) + goto err_map_device; + } + + /* Enable the PCI device */ + adjust_pci_device ( pci ); + + /* Reset the device and set initial status bits */ + vpm_reset ( &virtnet->vdev ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER ); + + /* Load MAC address */ + if ( device ) { + features = vpm_get_features ( &virtnet->vdev ); + if ( features & ( 1ULL << VIRTIO_NET_F_MAC ) ) { + vpm_get ( &virtnet->vdev, + offsetof ( struct virtio_net_config, mac ), + netdev->hw_addr, ETH_ALEN ); + DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet, + eth_ntoa ( netdev->hw_addr ) ); + } + } + + /* We need a valid MAC address */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + rc = -EADDRNOTAVAIL; + goto err_mac_address; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Mark link as up, control virtqueue is not used */ + netdev_link_up ( netdev ); + + virtnet->virtio_version = 1; + return 0; + + unregister_netdev ( netdev ); +err_register_netdev: +err_mac_address: + vpm_reset ( &virtnet->vdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + + virtio_pci_unmap_capability ( &virtnet->vdev.device ); +err_map_device: + virtio_pci_unmap_capability ( &virtnet->vdev.isr ); +err_map_isr: + virtio_pci_unmap_capability ( &virtnet->vdev.common ); +err_map_common: + return rc; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int virtnet_probe ( struct pci_device *pci ) { + int found_modern = 0; + int rc = virtnet_probe_modern ( pci, &found_modern ); + if ( ! found_modern && pci->device < 0x1040 ) { + /* fall back to the legacy probe */ + rc = virtnet_probe_legacy ( pci ); + } + return rc; +} + /** * Remove device * @@ -402,6 +636,11 @@ static int virtnet_probe ( struct pci_device *pci ) { */ static void virtnet_remove ( struct pci_device *pci ) { struct net_device *netdev = pci_get_drvdata ( pci ); + struct virtnet_nic *virtnet = netdev->priv; + + virtio_pci_unmap_capability ( &virtnet->vdev.device ); + virtio_pci_unmap_capability ( &virtnet->vdev.isr ); + virtio_pci_unmap_capability ( &virtnet->vdev.common ); unregister_netdev ( netdev ); netdev_nullify ( netdev ); @@ -410,6 +649,7 @@ static void virtnet_remove ( struct pci_device *pci ) { static struct pci_device_id virtnet_nics[] = { PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0), +PCI_ROM(0x1af4, 0x1041, "virtio-net", "Virtio Network Interface 1.0", 0), }; struct pci_driver virtnet_driver __pci_driver = { From 40a8a5294c0cb0f8fe6f608600dc8ed8d1f2b0c5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 18 Apr 2016 10:08:46 +0100 Subject: [PATCH 191/591] [ethernet] Make LACP support configurable at build time Add a build configuration option NET_PROTO_LACP to control whether or not LACP support is included for Ethernet devices. Signed-off-by: Michael Brown --- src/config/config_ethernet.c | 3 +++ src/config/general.h | 1 + src/net/ethernet.c | 3 --- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index de7a07c57..b5f7ddc9d 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -43,3 +43,6 @@ REQUIRE_OBJECT ( fcoe ); #ifdef NET_PROTO_STP REQUIRE_OBJECT ( stp ); #endif +#ifdef NET_PROTO_LACP +REQUIRE_OBJECT ( eth_slow ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 675c11e07..a21501500 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef NET_PROTO_IPV6 /* IPv6 protocol */ #undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */ #define NET_PROTO_STP /* Spanning Tree protocol */ +#define NET_PROTO_LACP /* Link Aggregation control protocol */ /* * PXE support diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 6ddf05344..26fdedea8 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -278,6 +278,3 @@ REQUIRING_SYMBOL ( ethernet_protocol ); /* Drag in Ethernet configuration */ REQUIRE_OBJECT ( config_ethernet ); - -/* Drag in Ethernet slow protocols */ -REQUIRE_OBJECT ( eth_slow ); From 91aa188fbbcbf572ec11ff977ac206c984dad991 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Apr 2016 16:30:49 +0100 Subject: [PATCH 192/591] [libc] Allow CPU architectures to use unoptimised string functions Signed-off-by: Michael Brown --- src/include/string.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/include/string.h b/src/include/string.h index 0fab6c74b..0f4182001 100644 --- a/src/include/string.h +++ b/src/include/string.h @@ -10,6 +10,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include + +extern void * generic_memset ( void *dest, int character, + size_t len ) __nonnull; +extern void * generic_memcpy ( void *dest, const void *src, + size_t len ) __nonnull; +extern void * generic_memmove ( void *dest, const void *src, + size_t len ) __nonnull; + #include /* Architecture-specific code is expected to provide these functions, @@ -18,12 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); void * memset ( void *dest, int character, size_t len ) __nonnull; void * memcpy ( void *dest, const void *src, size_t len ) __nonnull; void * memmove ( void *dest, const void *src, size_t len ) __nonnull; -extern void * generic_memset ( void *dest, int character, - size_t len ) __nonnull; -extern void * generic_memcpy ( void *dest, const void *src, - size_t len ) __nonnull; -extern void * generic_memmove ( void *dest, const void *src, - size_t len ) __nonnull; extern int __pure memcmp ( const void *first, const void *second, size_t len ) __nonnull; From 55e409b14fdfc6bcd51cdcdaf1ee20ad5258215d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 20 Apr 2016 16:43:34 +0100 Subject: [PATCH 193/591] [libgcc] Provide symbol to handle gcc's implicit calls to memset() On some architectures (such as ARM), gcc will insert implicit calls to memset(). Handle these using the same mechanism as for the implicit calls to memcpy() used by x86. Signed-off-by: Michael Brown --- src/libgcc/implicit.c | 26 ++++++++++++++++++++++++++ src/libgcc/memcpy.c | 18 ------------------ 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 src/libgcc/implicit.c delete mode 100644 src/libgcc/memcpy.c diff --git a/src/libgcc/implicit.c b/src/libgcc/implicit.c new file mode 100644 index 000000000..645ae6d22 --- /dev/null +++ b/src/libgcc/implicit.c @@ -0,0 +1,26 @@ +/** @file + * + * gcc sometimes likes to insert implicit calls to memcpy() and + * memset(). Unfortunately, there doesn't seem to be any way to + * prevent it from doing this, or to force it to use the optimised + * versions as seen by C code; it insists on inserting symbol + * references to "memcpy" and "memset". We therefore include wrapper + * functions just to keep gcc happy. + * + */ + +#include + +void * gcc_implicit_memcpy ( void *dest, const void *src, + size_t len ) asm ( "memcpy" ); + +void * gcc_implicit_memcpy ( void *dest, const void *src, size_t len ) { + return memcpy ( dest, src, len ); +} + +void * gcc_implicit_memset ( void *dest, int character, + size_t len ) asm ( "memset" ); + +void * gcc_implicit_memset ( void *dest, int character, size_t len ) { + return memset ( dest, character, len ); +} diff --git a/src/libgcc/memcpy.c b/src/libgcc/memcpy.c deleted file mode 100644 index e98b78384..000000000 --- a/src/libgcc/memcpy.c +++ /dev/null @@ -1,18 +0,0 @@ -/** @file - * - * gcc sometimes likes to insert implicit calls to memcpy(). - * Unfortunately, there doesn't seem to be any way to prevent it from - * doing this, or to force it to use the optimised memcpy() as seen by - * C code; it insists on inserting a symbol reference to "memcpy". We - * therefore include wrapper functions just to keep gcc happy. - * - */ - -#include - -void * gcc_implicit_memcpy ( void *dest, const void *src, - size_t len ) asm ( "memcpy" ); - -void * gcc_implicit_memcpy ( void *dest, const void *src, size_t len ) { - return memcpy ( dest, src, len ); -} From b696a5063e80aa68cb064a8ac0098edd31632555 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Apr 2016 12:12:50 +0100 Subject: [PATCH 194/591] [image] Skip misleading "format not recognised" error message Return success (rather than failure) after an image format has been correctly identified. This has no practical effect, since the return value from image_probe() is deliberately never used, but avoids a somewhat surprising and misleading "format not recognised" error message when debugging is enabled. Signed-off-by: Michael Brown --- src/core/image.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/image.c b/src/core/image.c index 4a4e9c2a0..a185b82f4 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -191,7 +191,7 @@ static int image_probe ( struct image *image ) { image->type = type; DBGC ( image, "IMAGE %s is %s\n", image->name, type->name ); - break; + return 0; } DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, type->name, strerror ( rc ) ); From 2d42d3cff6e941c4e04d15dc29ea670f2fdb7b0c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Apr 2016 11:33:13 +0100 Subject: [PATCH 195/591] [librm] Reduce real-mode stack consumption in virt_call() Some PXE NBPs are known to make PXE API calls with very little space available on the real-mode stack. For example, the Rembo-ia32 NBP from some versions of IBM's Tivoli Provisioning Manager for Operating System Deployment (TPMfOSD) will issue calls with the real-mode stack placed at 0000:03d2; this is at the end of the interrupt vector table and leaves only 498 bytes of stack space available before overwriting the hardware IRQ vectors. This limits the amount of state that we can preserve before transitioning to protected mode. Work around these challenging conditions by preserving everything other than the initial register dump in a temporary static buffer within our real-mode data segment, and copying the contents of this buffer to the protected-mode stack. Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 162 ++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 59 deletions(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index f633b352b..a082526bd 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -180,17 +180,49 @@ gdt_end: * to us. **************************************************************************** */ - .section ".bss.rm_sp", "aw", @nobits + .section ".bss.rm_ss_sp", "aw", @nobits .globl rm_sp rm_sp: .word 0 - - .section ".bss.rm_ss", "aw", @nobits .globl rm_ss rm_ss: .word 0 .section ".data.pm_esp", "aw", @progbits pm_esp: .long VIRTUAL(_estack) +/**************************************************************************** + * Temporary static data buffer + * + * This is used to reduce the amount of real-mode stack space consumed + * during mode transitions, since we are sometimes called with very + * little real-mode stack space available. + **************************************************************************** + */ + /* Temporary static buffer usage by virt_call */ + .struct 0 +VC_TMP_GDT: .space 6 +VC_TMP_IDT: .space 6 +VC_TMP_PAD: .space 4 /* for alignment */ +.if64 +VC_TMP_CR3: .space 4 +VC_TMP_CR4: .space 4 +VC_TMP_EMER: .space 8 +.endif +VC_TMP_END: + .previous + + /* Temporary static buffer usage by real_call */ + .struct 0 +RC_TMP_FUNCTION: .space 4 +RC_TMP_END: + .previous + + /* Shared temporary static buffer */ + .section ".bss16.rm_tmpbuf", "aw", @nobits + .align 16 +rm_tmpbuf: + .space VC_TMP_END + .size rm_tmpbuf, . - rm_tmpbuf + /**************************************************************************** * Virtual address offsets * @@ -341,6 +373,7 @@ set_seg_base: * * Parameters: * %ecx : number of bytes to move from RM stack to PM stack + * %edx : number of bytes to copy from RM temporary buffer to PM stack * **************************************************************************** */ @@ -361,14 +394,19 @@ real_to_prot: /* Add protected-mode return address to length of data to be copied */ addw $4, %cx /* %ecx must be less than 64kB anyway */ - /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */ - xorl %ebp, %ebp - movw %ss, %bp - movzwl %sp, %edx - movl %ebp, %eax + /* Real-mode %ss:%sp => %ebp and virtual address => %esi */ + xorl %eax, %eax + movw %ss, %ax shll $4, %eax - addr32 leal (%eax,%edx), %esi + movzwl %sp, %ebp + addr32 leal (%eax,%ebp), %esi subl rm_virt_offset, %esi + shll $12, %eax + orl %eax, %ebp + + /* Real-mode data segment virtual address => %ebx */ + movl rm_data16, %ebx +.if64 ; subl rm_virt_offset, %ebx ; .endif /* Load protected-mode global descriptor table */ data32 lgdt gdtr @@ -407,15 +445,20 @@ r2p_pmode: lidt VIRTUAL(idtr32) /* Record real-mode %ss:sp (after removal of data) */ - movw %bp, VIRTUAL(rm_ss) - addl %ecx, %edx - movw %dx, VIRTUAL(rm_sp) + addl %ecx, %ebp + movl %ebp, VIRTUAL(rm_sp) /* Move data from RM stack to PM stack */ + subl %edx, %esp subl %ecx, %esp movl %esp, %edi rep movsb + /* Copy data from RM temporary buffer to PM stack */ + leal rm_tmpbuf(%ebx), %esi + movl %edx, %ecx + rep movsb + /* Return to virtual address */ ret @@ -435,6 +478,7 @@ r2p_pmode: * * Parameters: * %ecx : number of bytes to move from PM stack to RM stack + * %edx : number of bytes to move from PM stack to RM temporary buffer * %esi : real-mode global and interrupt descriptor table registers * **************************************************************************** @@ -455,19 +499,26 @@ prot_to_real: /* Add return address to data to be moved to RM stack */ addl $4, %ecx - /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */ - movzwl VIRTUAL(rm_ss), %ebp - movzwl VIRTUAL(rm_sp), %edx - subl %ecx, %edx - movl %ebp, %eax + /* Real-mode %ss:sp => %ebp and virtual address => %edi */ + movl VIRTUAL(rm_sp), %ebp + subl %ecx, %ebp + movzwl VIRTUAL(rm_ss), %eax shll $4, %eax - leal (%eax,%edx), %edi + movzwl %bp, %edi + addl %eax, %edi subl VIRTUAL(virt_offset), %edi /* Move data from PM stack to RM stack */ movl %esp, %esi rep movsb + /* Move data from PM stack to RM temporary buffer */ + movl VIRTUAL(data16), %edi +.if64 ; subl VIRTUAL(virt_offset), %edi ; .endif + addl $rm_tmpbuf, %edi + movl %edx, %ecx + rep movsb + /* Record protected-mode %esp (after removal of data) */ movl %esi, VIRTUAL(pm_esp) @@ -497,8 +548,10 @@ p2r_ljmp_rm_cs: movw %ax, %es movw %ax, %fs movw %ax, %gs - movw %bp, %ss - movl %edx, %esp + movl %ebp, %eax + shrl $16, %eax + movw %ax, %ss + movzwl %bp, %esp /* Return to real-mode address */ data32 ret @@ -921,14 +974,6 @@ long_restore_regs: **************************************************************************** */ .struct 0 -VC_OFFSET_GDT: .space 6 -VC_OFFSET_IDT: .space 6 -.if64 -VC_OFFSET_PADDING64: .space 4 /* for alignment */ -VC_OFFSET_CR3: .space 4 -VC_OFFSET_CR4: .space 4 -VC_OFFSET_EMER: .space 8 -.endif VC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS VC_OFFSET_PADDING: .space 2 /* for alignment */ VC_OFFSET_RETADDR: .space 2 @@ -941,7 +986,7 @@ VC_OFFSET_END: .code16 .globl virt_call virt_call: - /* Preserve registers, flags and GDT on external RM stack */ + /* Preserve registers and flags on external RM stack */ pushw %ss /* padding */ pushfl pushal @@ -951,34 +996,38 @@ virt_call: pushw %ds pushw %ss pushw %cs - subw $VC_OFFSET_IX86, %sp - movw %sp, %bp - sidt VC_OFFSET_IDT(%bp) - sgdt VC_OFFSET_GDT(%bp) + + /* Claim ownership of temporary static buffer */ + cli + + /* Preserve GDT and IDT in temporary static buffer */ + movw %cs:rm_ds, %ds + sidt ( rm_tmpbuf + VC_TMP_IDT ) + sgdt ( rm_tmpbuf + VC_TMP_GDT ) .if64 ; /* Preserve control registers, if applicable */ movl $MSR_EFER, %ecx rdmsr - movl %eax, (VC_OFFSET_EMER+0)(%bp) - movl %edx, (VC_OFFSET_EMER+4)(%bp) + movl %eax, ( rm_tmpbuf + VC_TMP_EMER + 0 ) + movl %edx, ( rm_tmpbuf + VC_TMP_EMER + 4 ) movl %cr4, %eax - movl %eax, VC_OFFSET_CR4(%bp) + movl %eax, ( rm_tmpbuf + VC_TMP_CR4 ) movl %cr3, %eax - movl %eax, VC_OFFSET_CR3(%bp) + movl %eax, ( rm_tmpbuf + VC_TMP_CR3 ) .endif /* For sanity's sake, clear the direction flag as soon as possible */ cld /* Switch to protected mode and move register dump to PM stack */ movl $VC_OFFSET_END, %ecx + movl $VC_TMP_END, %edx pushl $VIRTUAL(vc_pmode) vc_jmp: jmp real_to_prot .section ".text.virt_call", "ax", @progbits .code32 vc_pmode: /* Call function (in protected mode) */ - leal VC_OFFSET_IX86(%esp), %eax - pushl %eax + pushl %esp call *(VC_OFFSET_FUNCTION+4)(%esp) popl %eax /* discard */ @@ -989,11 +1038,9 @@ vc_lmode: .code64 /* Call function (in long mode) */ - leaq VC_OFFSET_IX86(%rsp), %rdi - pushq %rdi - movslq (VC_OFFSET_FUNCTION+8)(%rsp), %rax + movq %rsp, %rdi + movslq VC_OFFSET_FUNCTION(%rsp), %rax callq *%rax - popq %rdi /* discard */ /* Switch to protected mode */ call long_to_prot @@ -1001,7 +1048,8 @@ vc_lmode: .endif /* Switch to real mode and move register dump back to RM stack */ movl $VC_OFFSET_END, %ecx - movl %esp, %esi + movl $VC_TMP_END, %edx + leal VC_TMP_GDT(%esp, %ecx), %esi pushl $vc_rmode jmp prot_to_real .section ".text16.virt_call", "ax", @progbits @@ -1009,17 +1057,17 @@ vc_lmode: vc_rmode: .if64 ; /* Restore control registers, if applicable */ movw %sp, %bp - movl VC_OFFSET_CR3(%bp), %eax + movl ( rm_tmpbuf + VC_TMP_CR3 ), %eax movl %eax, %cr3 - movl VC_OFFSET_CR4(%bp), %eax + movl ( rm_tmpbuf + VC_TMP_CR4 ), %eax movl %eax, %cr4 - movl (VC_OFFSET_EMER+0)(%bp), %eax - movl (VC_OFFSET_EMER+4)(%bp), %edx + movl ( rm_tmpbuf + VC_TMP_EMER + 0 ), %eax + movl ( rm_tmpbuf + VC_TMP_EMER + 4 ), %edx movl $MSR_EFER, %ecx wrmsr .endif /* Restore registers and flags and return */ - addw $( VC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp + popl %eax /* skip %cs and %ss */ popw %ds popw %es popw %fs @@ -1067,6 +1115,7 @@ vc_rmode: .struct 0 RC_OFFSET_REGS: .space SIZEOF_I386_REGS RC_OFFSET_REGS_END: +RC_OFFSET_FUNCTION_COPY:.space 4 .if64 RC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS RC_OFFSET_LREG_RETADDR: .space SIZEOF_ADDR @@ -1087,11 +1136,12 @@ real_call: .code32 .endif /* Create register dump and function pointer copy on PM stack */ + pushl ( RC_OFFSET_FUNCTION - RC_OFFSET_FUNCTION_COPY - 4 )(%esp) pushal - pushl RC_OFFSET_FUNCTION(%esp) /* Switch to real mode and move register dump to RM stack */ - movl $( RC_OFFSET_REGS_END + 4 /* function pointer copy */ ), %ecx + movl $RC_OFFSET_REGS_END, %ecx + movl $RC_TMP_END, %edx pushl $rc_rmode movl $VIRTUAL(rm_default_gdtr_idtr), %esi jmp prot_to_real @@ -1099,9 +1149,8 @@ real_call: .code16 rc_rmode: /* Call real-mode function */ - popl rc_function popal - call *rc_function + call *( rm_tmpbuf + RC_TMP_FUNCTION ) pushal /* For sanity's sake, clear the direction flag as soon as possible */ @@ -1109,6 +1158,7 @@ rc_rmode: /* Switch to protected mode and move register dump back to PM stack */ movl $RC_OFFSET_REGS_END, %ecx + xorl %edx, %edx pushl $VIRTUAL(rc_pmode) jmp real_to_prot .section ".text.real_call", "ax", @progbits @@ -1126,12 +1176,6 @@ rc_pmode: ret $( RC_OFFSET_END - RC_OFFSET_PARAMS ) - /* Function vector, used because "call xx(%sp)" is not a valid - * 16-bit expression. - */ - .section ".bss16.rc_function", "aw", @nobits -rc_function: .word 0, 0 - /* Default real-mode global and interrupt descriptor table registers */ .section ".data.rm_default_gdtr_idtr", "aw", @progbits rm_default_gdtr_idtr: From fe62f3c8312c3747c1a6c375b502cffa65d60f63 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 2 May 2016 13:20:26 +0100 Subject: [PATCH 196/591] [tg3] Fix _tg3_flag() for 64-bit builds Commit 86f96a4 ("[tg3] Remove x86-specific inline assembly") introduced a regression in _tg3_flag() in 64-bit builds, since any flags in the upper 32 bits of a 64-bit unsigned long would be discarded when truncating to a 32-bit int. Debugged-by: Shane Thompson Tested-by: Shane Thompson Signed-off-by: Michael Brown --- src/drivers/net/tg3/tg3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index d29523a83..0c3d23bb7 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -3324,7 +3324,7 @@ static inline int _tg3_flag(enum TG3_FLAGS flag, unsigned long *bits) { unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); - return ( bits[index] & ( 1UL << bit ) ); + return ( !! ( bits[index] & ( 1UL << bit ) ) ); } static inline void _tg3_flag_set(enum TG3_FLAGS flag, unsigned long *bits) From 71560d185475117b10994d839afe059577e7768c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 27 Apr 2016 11:03:18 +0100 Subject: [PATCH 197/591] [librm] Preserve FPU, MMX and SSE state across calls to virt_call() The IBM Tivoli Provisioning Manager for OS Deployment (also known as TPMfOSD, Rembo-ia32, or Rembo Auto-Deploy) has a serious bug in some older versions (observed with v5.1.1.0, apparently fixed by v7.1.1.0) which can lead to arbitrary data corruption. As mentioned in commit 87723a0 ("[libflat] Test A20 gate without switching to flat real mode"), Tivoli's NBP sets up a VMM and makes calls to the PXE stack in VM86 mode. This appears to be some kind of attempt to run PXE API calls inside a sandbox. The VMM is fairly sophisticated: for example, it handles our attempts to switch into protected mode and patches our GDT so that our protected-mode code runs in ring 1 instead of ring 0. However, it neglects to apply any memory protections. In particular, it does not enable paging and leaves us with 4GB segment limits. We can therefore trivially break out of the sandbox by simply overwriting the GDT (or by modifying any of Tivoli's VMM code or data structures). When we attempt to execute privileged instructions (such as "lidt"), the CPU raises an exception and control is passed to the Tivoli VMM. This may result in a call to Tivoli's memcpy() function. Tivoli's memcpy() function includes optimisations which use the SSE registers %xmm0-%xmm3 to speed up aligned memory copies. Unfortunately, the Tivoli VMM's exception handler does not save or restore %xmm0-%xmm3. The net effect of this bug in the Tivoli VMM is that any privileged instruction (such as "lidt") issued by iPXE may result in unexpected corruption of the %xmm0-%xmm3 registers. Even more unfortunately, this problem affects the code path taken in response to a hardware interrupt from the NIC, since that code path will call PXENV_UNDI_ISR. The net effect therefore becomes that any NIC hardware interrupt (e.g. due to a received packet) may result in unexpected corruption of the %xmm0-%xmm3 registers. If a packet arrives while Tivoli is in the middle of using its memcpy() function, then the unexpected corruption of the %xmm0-%xmm3 registers will result in unexpected corruption in the destination buffer. The net effect therefore becomes that any received packet may result in a 16-byte block of corruption somewhere in any data that Tivoli copied using its memcpy() function. We can work around this bug in the Tivoli VMM by saving and restoring the %xmm0-%xmm3 registers across calls to virt_call(). To work around the problem, we need to save registers before attempting to execute any privileged instructions, and ensure that we attempt no further privileged instructions after restoring the registers. This is less simple than it may sound. We can use the "movups" instruction to save and restore individual registers, but this will itself generate an undefined opcode exception if SSE is not currently enabled according to the flags in %cr0 and %cr4. We can't access %cr0 or %cr4 before attempting the "movups" instruction, because access a control register is itself a privileged instruction (which may therefore trigger corruption of the registers that we're trying to save). The best solution seems to be to use the "fxsave" and "fxrstor" instructions. If SSE is not enabled, then these instructions may fail to save and restore the SSE register contents, but will not generate an undefined opcode exception. (If SSE is not enabled, then we don't really care about preserving the SSE register contents anyway.) The use of "fxsave" and "fxrstor" introduces an implicit assumption that the CPU supports SSE instructions (even though we make no assumption about whether or not SSE is currently enabled). SSE was introduced in 1999 with the Pentium III (and added by AMD in 2001), and is an architectural requirement for x86_64. Experimentation with current versions of gcc suggest that it may generate SSE instructions even when using "-m32", unless an explicit "-march=i386" or "-mno-sse" is used to inhibit this. It therefore seems reasonable to assume that SSE will be supported on any hardware that might realistically be used with new iPXE builds. As a side benefit of this change, the MMX register %mm0 will now be preserved across virt_call() even in an i386 build of iPXE using a driver that requires readq()/writeq(), and the SSE registers %xmm0-%xmm5 will now be preserved across virt_call() even in an x86_64 build of iPXE using the Hyper-V netvsc driver. Experimentation suggests that this change adds around 10% to the number of cycles required for a do-nothing virt_call(), most of which are due to the extra bytes copied using "rep movsb". Since the number of bytes copied is a compile-time constant local to librm.S, we could potentially reduce this impact by ensuring that we always copy a whole number of dwords and so can use "rep movsl" instead of "rep movsb". Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index a082526bd..e91ede372 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -207,6 +207,7 @@ VC_TMP_CR3: .space 4 VC_TMP_CR4: .space 4 VC_TMP_EMER: .space 8 .endif +VC_TMP_FXSAVE: .space 512 VC_TMP_END: .previous @@ -1000,8 +1001,11 @@ virt_call: /* Claim ownership of temporary static buffer */ cli - /* Preserve GDT and IDT in temporary static buffer */ + /* Preserve FPU, MMX and SSE state in temporary static buffer */ movw %cs:rm_ds, %ds + fxsave ( rm_tmpbuf + VC_TMP_FXSAVE ) + + /* Preserve GDT and IDT in temporary static buffer */ sidt ( rm_tmpbuf + VC_TMP_IDT ) sgdt ( rm_tmpbuf + VC_TMP_GDT ) @@ -1066,6 +1070,9 @@ vc_rmode: movl $MSR_EFER, %ecx wrmsr .endif + /* Restore FPU, MMX and SSE state from temporary static buffer */ + fxrstor ( rm_tmpbuf + VC_TMP_FXSAVE ) + /* Restore registers and flags and return */ popl %eax /* skip %cs and %ss */ popw %ds From efd5cf9aadcaf36f45db5d1c3059197a8479567c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 2 May 2016 20:36:54 +0100 Subject: [PATCH 198/591] [efi] Eliminate use of libbfd Parse the intermediate ELF file directly instead of using libbfd, in order to allow for cross-compiled ELF objects. As a side bonus, this eliminates libbfd as a build requirement. Signed-off-by: Michael Brown --- src/Makefile | 2 - src/Makefile.housekeeping | 10 +- src/util/elf2efi.c | 469 ++++++++++++++++++++++++-------------- 3 files changed, 301 insertions(+), 180 deletions(-) diff --git a/src/Makefile b/src/Makefile index 58eedb11b..b7becdb43 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,8 +53,6 @@ EINFO := ./util/einfo GENKEYMAP := ./util/genkeymap.pl DOXYGEN := doxygen LCAB := lcab -BINUTILS_DIR := /usr -BFD_DIR := $(BINUTILS_DIR) ZLIB_DIR := /usr ############################################################################### diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 4280f398e..4ba1421bf 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1303,20 +1303,18 @@ CLEANUP += $(ZBIN) # # The EFI image converter # -ELF2EFI_CFLAGS := -I$(BINUTILS_DIR)/include -I$(BFD_DIR)/include \ - -I$(ZLIB_DIR)/include -idirafter include -ELF2EFI_LDFLAGS := -L$(BINUTILS_DIR)/lib -L$(BFD_DIR)/lib -L$(ZLIB_DIR)/lib \ - -lbfd -ldl -lz -Wl,--no-warn-search-mismatch +ELF2EFI_CFLAGS := -I$(ZLIB_DIR)/include -idirafter include +ELF2EFI_LDFLAGS := -L$(ZLIB_DIR)/lib -lz -Wl,--no-warn-search-mismatch $(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET_IA32 $< \ + $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET32 $< \ $(ELF2EFI_LDFLAGS) -o $@ CLEANUP += $(ELF2EFI32) $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET_X64 $< \ + $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET64 $< \ $(ELF2EFI_LDFLAGS) -o $@ CLEANUP += $(ELF2EFI64) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index dee70ee7a..5e1050e3d 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -17,9 +17,6 @@ * 02110-1301, USA. */ -#define _GNU_SOURCE -#define PACKAGE "elf2efi" -#define PACKAGE_VERSION "1" #define FILE_LICENCE(...) extern void __file_licence ( void ) #include #include @@ -30,15 +27,65 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include #include #include #define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) +#ifdef EFI_TARGET32 + +#define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS32 +#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC +#define EFI_IMAGE_FILE_MACHINE EFI_IMAGE_FILE_32BIT_MACHINE +#define ELFCLASS ELFCLASS32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Addr Elf32_Addr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE ELF32_R_TYPE +#define ELF_R_SYM ELF32_R_SYM + +#elif defined(EFI_TARGET64) + +#define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS64 +#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC +#define EFI_IMAGE_FILE_MACHINE 0 +#define ELFCLASS ELFCLASS64 +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Addr Elf64_Addr +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_R_TYPE ELF64_R_TYPE +#define ELF_R_SYM ELF64_R_SYM + +#endif + #define EFI_FILE_ALIGN 0x20 +struct elf_machine { + unsigned int pe_machine; + unsigned int r_none; + unsigned int r_abs; + unsigned int r_pcrel; +}; + +struct elf_file { + void *data; + size_t len; + const Elf_Ehdr *ehdr; + struct elf_machine *machine; +}; + struct pe_section { struct pe_section *next; EFI_IMAGE_SECTION_HEADER hdr; @@ -57,11 +104,7 @@ struct pe_relocs { struct pe_header { EFI_IMAGE_DOS_HEADER dos; uint8_t padding[128]; -#if defined(EFI_TARGET_IA32) - EFI_IMAGE_NT_HEADERS32 nt; -#elif defined(EFI_TARGET_X64) - EFI_IMAGE_NT_HEADERS64 nt; -#endif + EFI_IMAGE_NT_HEADERS nt; }; static struct pe_header efi_pe_header = { @@ -72,26 +115,15 @@ static struct pe_header efi_pe_header = { .nt = { .Signature = EFI_IMAGE_NT_SIGNATURE, .FileHeader = { -#if defined(EFI_TARGET_IA32) - .Machine = EFI_IMAGE_MACHINE_IA32, -#elif defined(EFI_TARGET_X64) - .Machine = EFI_IMAGE_MACHINE_X64, -#endif .TimeDateStamp = 0x10d1a884, .SizeOfOptionalHeader = sizeof ( efi_pe_header.nt.OptionalHeader ), .Characteristics = ( EFI_IMAGE_FILE_DLL | -#if defined(EFI_TARGET_IA32) - EFI_IMAGE_FILE_32BIT_MACHINE | -#endif + EFI_IMAGE_FILE_MACHINE | EFI_IMAGE_FILE_EXECUTABLE_IMAGE ), }, .OptionalHeader = { -#if defined(EFI_TARGET_IA32) - .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC, -#elif defined(EFI_TARGET_X64) - .Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC, -#endif + .Magic = EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC, .MajorLinkerVersion = 42, .MinorLinkerVersion = 42, .SectionAlignment = EFI_FILE_ALIGN, @@ -104,6 +136,20 @@ static struct pe_header efi_pe_header = { }, }; +static struct elf_machine machine_i386 = { + .pe_machine = EFI_IMAGE_MACHINE_IA32, + .r_none = R_386_NONE, + .r_abs = R_386_32, + .r_pcrel = R_386_PC32, +}; + +static struct elf_machine machine_x86_64 = { + .pe_machine = EFI_IMAGE_MACHINE_X64, + .r_none = R_X86_64_NONE, + .r_abs = R_X86_64_64, + .r_pcrel = R_X86_64_PC32, +}; + /** Command-line options */ struct options { unsigned int subsystem; @@ -235,110 +281,155 @@ static size_t output_pe_reltab ( struct pe_relocs *pe_reltab, } /** - * Open input BFD file + * Read input ELF file * - * @v filename File name - * @ret ibfd BFD file + * @v name File name + * @v elf ELF file */ -static bfd * open_input_bfd ( const char *filename ) { - bfd *bfd; +static void read_elf_file ( const char *name, struct elf_file *elf ) { + static const unsigned char ident[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS, ELFDATA2LSB + }; + struct stat stat; + const Elf_Ehdr *ehdr; + const Elf_Shdr *shdr; + void *data; + size_t offset; + unsigned int i; + int fd; - /* Open the file */ - bfd = bfd_openr ( filename, NULL ); - if ( ! bfd ) { - eprintf ( "Cannot open %s: ", filename ); - bfd_perror ( NULL ); + /* Open file */ + fd = open ( name, O_RDONLY ); + if ( fd < 0 ) { + eprintf ( "Could not open %s: %s\n", name, strerror ( errno ) ); exit ( 1 ); } - /* The call to bfd_check_format() must be present, otherwise - * we get a segfault from later BFD calls. - */ - if ( ! bfd_check_format ( bfd, bfd_object ) ) { - eprintf ( "%s is not an object file: ", filename ); - bfd_perror ( NULL ); + /* Get file size */ + if ( fstat ( fd, &stat ) < 0 ) { + eprintf ( "Could not get size of %s: %s\n", + name, strerror ( errno ) ); exit ( 1 ); } + elf->len = stat.st_size; - return bfd; + /* Map file */ + data = mmap ( NULL, elf->len, PROT_READ, MAP_SHARED, fd, 0 ); + if ( data == MAP_FAILED ) { + eprintf ( "Could not map %s: %s\n", name, strerror ( errno ) ); + exit ( 1 ); + } + elf->data = data; + + /* Close file */ + close ( fd ); + + /* Check header */ + ehdr = elf->data; + if ( ( elf->len < sizeof ( *ehdr ) ) || + ( memcmp ( ident, ehdr->e_ident, sizeof ( ident ) ) != 0 ) ) { + eprintf ( "Invalid ELF header in %s\n", name ); + exit ( 1 ); + } + elf->ehdr = ehdr; + + /* Check section headers */ + for ( i = 0 ; i < ehdr->e_shnum ; i++ ) { + offset = ( ehdr->e_shoff + ( i * ehdr->e_shentsize ) ); + if ( elf->len < ( offset + sizeof ( *shdr ) ) ) { + eprintf ( "ELF section header outside file in %s\n", + name ); + exit ( 1 ); + } + shdr = ( data + offset ); + if ( ( shdr->sh_type != SHT_NOBITS ) && + ( ( elf->len < shdr->sh_offset ) || + ( ( ( elf->len - shdr->sh_offset ) < shdr->sh_size ) ))){ + eprintf ( "ELF section %d outside file in %s\n", + i, name ); + exit ( 1 ); + } + if ( shdr->sh_link >= ehdr->e_shnum ) { + eprintf ( "ELF section %d link section %d out of " + "range\n", i, shdr->sh_link ); + exit ( 1 ); + } + } + + /* Identify architecture */ + switch ( ehdr->e_machine ) { + case EM_386: + elf->machine = &machine_i386; + break; + case EM_X86_64: + elf->machine = &machine_x86_64; + break; + default: + eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); + exit ( 1 ); + } } /** - * Read symbol table + * Get ELF string * - * @v bfd BFD file + * @v elf ELF file + * @v section String table section number + * @v offset String table offset + * @ret string ELF string */ -static asymbol ** read_symtab ( bfd *bfd ) { - long symtab_size; - asymbol **symtab; - long symcount; +static const char * elf_string ( struct elf_file *elf, unsigned int section, + size_t offset ) { + const Elf_Ehdr *ehdr = elf->ehdr; + const Elf_Shdr *shdr; + char *string; + char *last; - /* Get symbol table size */ - symtab_size = bfd_get_symtab_upper_bound ( bfd ); - if ( symtab_size < 0 ) { - bfd_perror ( "Could not get symbol table upper bound" ); + /* Locate section header */ + if ( section >= ehdr->e_shnum ) { + eprintf ( "Invalid ELF string section %d\n", section ); + exit ( 1 ); + } + shdr = ( elf->data + ehdr->e_shoff + ( section * ehdr->e_shentsize ) ); + + /* Sanity check section */ + if ( shdr->sh_type != SHT_STRTAB ) { + eprintf ( "ELF section %d (type %d) is not a string table\n", + section, shdr->sh_type ); + exit ( 1 ); + } + last = ( elf->data + shdr->sh_offset + shdr->sh_size - 1 ); + if ( *last != '\0' ) { + eprintf ( "ELF section %d is not NUL-terminated\n", section ); exit ( 1 ); } - /* Allocate and read symbol table */ - symtab = xmalloc ( symtab_size ); - symcount = bfd_canonicalize_symtab ( bfd, symtab ); - if ( symcount < 0 ) { - bfd_perror ( "Cannot read symbol table" ); + /* Locate string */ + if ( offset >= shdr->sh_size ) { + eprintf ( "Invalid ELF string offset %zd in section %d\n", + offset, section ); exit ( 1 ); } + string = ( elf->data + shdr->sh_offset + offset ); - return symtab; -} - -/** - * Read relocation table - * - * @v bfd BFD file - * @v symtab Symbol table - * @v section Section - * @v symtab Symbol table - * @ret reltab Relocation table - */ -static arelent ** read_reltab ( bfd *bfd, asymbol **symtab, - asection *section ) { - long reltab_size; - arelent **reltab; - long numrels; - - /* Get relocation table size */ - reltab_size = bfd_get_reloc_upper_bound ( bfd, section ); - if ( reltab_size < 0 ) { - bfd_perror ( "Could not get relocation table upper bound" ); - exit ( 1 ); - } - - /* Allocate and read relocation table */ - reltab = xmalloc ( reltab_size ); - numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab ); - if ( numrels < 0 ) { - bfd_perror ( "Cannot read relocation table" ); - exit ( 1 ); - } - - return reltab; + return string; } /** * Process section * - * @v bfd BFD file + * @v elf ELF file + * @v shdr ELF section header * @v pe_header PE file header - * @v section Section * @ret new New PE section */ -static struct pe_section * process_section ( bfd *bfd, - struct pe_header *pe_header, - asection *section ) { +static struct pe_section * process_section ( struct elf_file *elf, + const Elf_Shdr *shdr, + struct pe_header *pe_header ) { struct pe_section *new; + const char *name; size_t section_memsz; size_t section_filesz; - unsigned long flags = bfd_get_section_flags ( bfd, section ); unsigned long code_start; unsigned long code_end; unsigned long data_start; @@ -349,12 +440,15 @@ static struct pe_section * process_section ( bfd *bfd, unsigned long *applicable_start; unsigned long *applicable_end; + /* Get section name */ + name = elf_string ( elf, elf->ehdr->e_shstrndx, shdr->sh_name ); + /* Extract current RVA limits from file header */ code_start = pe_header->nt.OptionalHeader.BaseOfCode; code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode ); -#if defined(EFI_TARGET_IA32) +#if defined(EFI_TARGET32) data_start = pe_header->nt.OptionalHeader.BaseOfData; -#elif defined(EFI_TARGET_X64) +#elif defined(EFI_TARGET64) data_start = code_end; #endif data_mid = ( data_start + @@ -363,21 +457,21 @@ static struct pe_section * process_section ( bfd *bfd, pe_header->nt.OptionalHeader.SizeOfUninitializedData ); /* Allocate PE section */ - section_memsz = bfd_section_size ( bfd, section ); - section_filesz = ( ( flags & SEC_LOAD ) ? + section_memsz = shdr->sh_size; + section_filesz = ( ( shdr->sh_type == SHT_PROGBITS ) ? efi_file_align ( section_memsz ) : 0 ); new = xmalloc ( sizeof ( *new ) + section_filesz ); memset ( new, 0, sizeof ( *new ) + section_filesz ); /* Fill in section header details */ - strncpy ( ( char * ) new->hdr.Name, section->name, - sizeof ( new->hdr.Name ) ); + strncpy ( ( char * ) new->hdr.Name, name, sizeof ( new->hdr.Name ) ); new->hdr.Misc.VirtualSize = section_memsz; - new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section ); + new->hdr.VirtualAddress = shdr->sh_addr; new->hdr.SizeOfRawData = section_filesz; /* Fill in section characteristics and update RVA limits */ - if ( flags & SEC_CODE ) { + if ( ( shdr->sh_type == SHT_PROGBITS ) && + ( shdr->sh_flags & SHF_EXECINSTR ) ) { /* .text-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_CODE | @@ -386,7 +480,8 @@ static struct pe_section * process_section ( bfd *bfd, EFI_IMAGE_SCN_MEM_READ ); applicable_start = &code_start; applicable_end = &code_end; - } else if ( flags & SEC_DATA ) { + } else if ( ( shdr->sh_type == SHT_PROGBITS ) && + ( shdr->sh_flags & SHF_WRITE ) ) { /* .data-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | @@ -395,7 +490,7 @@ static struct pe_section * process_section ( bfd *bfd, EFI_IMAGE_SCN_MEM_WRITE ); applicable_start = &data_start; applicable_end = &data_mid; - } else if ( flags & SEC_READONLY ) { + } else if ( shdr->sh_type == SHT_PROGBITS ) { /* .rodata-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | @@ -403,7 +498,7 @@ static struct pe_section * process_section ( bfd *bfd, EFI_IMAGE_SCN_MEM_READ ); applicable_start = &data_start; applicable_end = &data_mid; - } else if ( ! ( flags & SEC_LOAD ) ) { + } else if ( shdr->sh_type == SHT_NOBITS ) { /* .bss-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA | @@ -413,19 +508,15 @@ static struct pe_section * process_section ( bfd *bfd, applicable_start = &data_mid; applicable_end = &data_end; } else { - eprintf ( "Unrecognised characteristics %#lx for section %s\n", - flags, section->name ); + eprintf ( "Unrecognised characteristics for section %s\n", + name ); exit ( 1 ); } /* Copy in section contents */ - if ( flags & SEC_LOAD ) { - if ( ! bfd_get_section_contents ( bfd, section, new->contents, - 0, section_memsz ) ) { - eprintf ( "Cannot read section %s: ", section->name ); - bfd_perror ( NULL ); - exit ( 1 ); - } + if ( shdr->sh_type == SHT_PROGBITS ) { + memcpy ( new->contents, ( elf->data + shdr->sh_offset ), + shdr->sh_size ); } /* Update RVA limits */ @@ -445,7 +536,7 @@ static struct pe_section * process_section ( bfd *bfd, /* Write RVA limits back to file header */ pe_header->nt.OptionalHeader.BaseOfCode = code_start; pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start ); -#if defined(EFI_TARGET_IA32) +#if defined(EFI_TARGET32) pe_header->nt.OptionalHeader.BaseOfData = data_start; #endif pe_header->nt.OptionalHeader.SizeOfInitializedData = @@ -465,46 +556,76 @@ static struct pe_section * process_section ( bfd *bfd, /** * Process relocation record * - * @v bfd BFD file - * @v section Section - * @v rel Relocation entry + * @v elf ELF file + * @v shdr ELF section header + * @v syms Symbol table + * @v nsyms Number of symbol table entries + * @v rel Relocation record * @v pe_reltab PE relocation table to fill in */ -static void process_reloc ( bfd *bfd __attribute__ (( unused )), - asection *section, arelent *rel, - struct pe_relocs **pe_reltab ) { - reloc_howto_type *howto = rel->howto; - asymbol *sym = *(rel->sym_ptr_ptr); - unsigned long offset = ( bfd_get_section_vma ( bfd, section ) + - rel->address ); +static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, + const Elf_Sym *syms, unsigned int nsyms, + const Elf_Rel *rel, struct pe_relocs **pe_reltab ) { + unsigned int type = ELF_R_TYPE ( rel->r_info ); + unsigned int sym = ELF_R_SYM ( rel->r_info ); + size_t offset = ( shdr->sh_addr + rel->r_offset ); - if ( bfd_is_abs_section ( sym->section ) ) { + /* Look up symbol and process relocation */ + if ( sym >= nsyms ) { + eprintf ( "Symbol out of range\n" ); + exit ( 1 ); + } + if ( syms[sym].st_shndx == SHN_ABS ) { /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ - } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) { + } else if ( type == elf->machine->r_none ) { /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ - } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { - /* Generate an 8-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, 8 ); - } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) { - /* Generate a 4-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, 4 ); - } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { - /* Generate a 2-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, 2 ); - } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) { + } else if ( type == elf->machine->r_abs ) { + /* Generate an 8-byte or 4-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, sizeof ( Elf_Addr ) ); + } else if ( type == elf->machine->r_pcrel ) { /* Skip PC-relative relocations; all relative offsets * remain unaltered when the object is loaded. */ } else { - eprintf ( "Unrecognised relocation type %s\n", howto->name ); + eprintf ( "Unrecognised relocation type %d\n", type ); exit ( 1 ); } } +/** + * Process relocation records + * + * @v elf ELF file + * @v shdr ELF section header + * @v stride Relocation record size + * @v pe_reltab PE relocation table to fill in + */ +static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr, + size_t stride, struct pe_relocs **pe_reltab ) { + const Elf_Shdr *symtab; + const Elf_Sym *syms; + const Elf_Rel *rel; + unsigned int nsyms; + unsigned int nrels; + unsigned int i; + + /* Identify symbol table */ + symtab = ( elf->data + elf->ehdr->e_shoff + + ( shdr->sh_link * elf->ehdr->e_shentsize ) ); + syms = ( elf->data + symtab->sh_offset ); + nsyms = ( symtab->sh_size / sizeof ( syms[0] ) ); + + /* Process each relocation */ + rel = ( elf->data + shdr->sh_offset ); + nrels = ( shdr->sh_size / stride ); + for ( i = 0 ; i < nrels ; i++ ) { + process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab ); + rel = ( ( ( const void * ) rel ) + stride ); + } +} + /** * Create relocations section * @@ -696,53 +817,60 @@ static void write_pe_file ( struct pe_header *pe_header, static void elf2pe ( const char *elf_name, const char *pe_name, struct options *opts ) { char pe_name_tmp[ strlen ( pe_name ) + 1 ]; - bfd *bfd; - asymbol **symtab; - asection *section; - arelent **reltab; - arelent **rel; struct pe_relocs *pe_reltab = NULL; struct pe_section *pe_sections = NULL; struct pe_section **next_pe_section = &pe_sections; struct pe_header pe_header; + struct elf_file elf; + const Elf_Shdr *shdr; + size_t offset; + unsigned int i; FILE *pe; /* Create a modifiable copy of the PE name */ memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) ); - /* Open the file */ - bfd = open_input_bfd ( elf_name ); - symtab = read_symtab ( bfd ); + /* Read ELF file */ + read_elf_file ( elf_name, &elf ); /* Initialise the PE header */ memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); - pe_header.nt.OptionalHeader.AddressOfEntryPoint = - bfd_get_start_address ( bfd ); + pe_header.nt.FileHeader.Machine = elf.machine->pe_machine; + pe_header.nt.OptionalHeader.AddressOfEntryPoint = elf.ehdr->e_entry; pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; - /* For each input section, build an output section and create - * the appropriate relocation records - */ - for ( section = bfd->sections ; section ; section = section->next ) { - /* Discard non-allocatable sections */ - if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) ) - continue; - /* Create output section */ - *(next_pe_section) = process_section ( bfd, &pe_header, - section ); - next_pe_section = &(*next_pe_section)->next; - /* Add relocations from this section */ - reltab = read_reltab ( bfd, symtab, section ); - for ( rel = reltab ; *rel ; rel++ ) - process_reloc ( bfd, section, *rel, &pe_reltab ); - free ( reltab ); + /* Process input sections */ + for ( i = 0 ; i < elf.ehdr->e_shnum ; i++ ) { + offset = ( elf.ehdr->e_shoff + ( i * elf.ehdr->e_shentsize ) ); + shdr = ( elf.data + offset ); + + /* Process section */ + if ( shdr->sh_flags & SHF_ALLOC ) { + + /* Create output section */ + *(next_pe_section) = process_section ( &elf, shdr, + &pe_header ); + next_pe_section = &(*next_pe_section)->next; + + } else if ( shdr->sh_type == SHT_REL ) { + + /* Process .rel relocations */ + process_relocs ( &elf, shdr, sizeof ( Elf_Rel ), + &pe_reltab ); + + } else if ( shdr->sh_type == SHT_RELA ) { + + /* Process .rela relocations */ + process_relocs ( &elf, shdr, sizeof ( Elf_Rela ), + &pe_reltab ); + } } /* Create the .reloc section */ *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); next_pe_section = &(*next_pe_section)->next; - /* Create the .reloc section */ + /* Create the .debug section */ *(next_pe_section) = create_debug_section ( &pe_header, basename ( pe_name_tmp ) ); next_pe_section = &(*next_pe_section)->next; @@ -757,8 +885,8 @@ static void elf2pe ( const char *elf_name, const char *pe_name, write_pe_file ( &pe_header, pe_sections, pe ); fclose ( pe ); - /* Close BFD file */ - bfd_close ( bfd ); + /* Unmap ELF file */ + munmap ( elf.data, elf.len ); } /** @@ -825,9 +953,6 @@ int main ( int argc, char **argv ) { const char *infile; const char *outfile; - /* Initialise libbfd */ - bfd_init(); - /* Parse command-line arguments */ infile_index = parse_options ( argc, argv, &opts ); if ( argc != ( infile_index + 2 ) ) { From 9f91df422b9ec7f1db493ea2fe333d8e609b58f9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 2 May 2016 23:09:49 +0100 Subject: [PATCH 199/591] [build] Remove unnecessary dependency on zlib The dependency on zlib seems to have been introduced in commit 3dd7ce1 ("[efi] Allow building with non-system libbfd") as an indirect requirement of either libbfd or libiberty when building on Mac OS X. Since we no longer use either of these libraries, remove the unnecessary link against zlib. Signed-off-by: Michael Brown --- src/Makefile | 1 - src/Makefile.housekeeping | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index b7becdb43..582ffe81c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,7 +53,6 @@ EINFO := ./util/einfo GENKEYMAP := ./util/genkeymap.pl DOXYGEN := doxygen LCAB := lcab -ZLIB_DIR := /usr ############################################################################### # diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 4ba1421bf..ceff81410 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1303,19 +1303,15 @@ CLEANUP += $(ZBIN) # # The EFI image converter # -ELF2EFI_CFLAGS := -I$(ZLIB_DIR)/include -idirafter include -ELF2EFI_LDFLAGS := -L$(ZLIB_DIR)/lib -lz -Wl,--no-warn-search-mismatch $(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET32 $< \ - $(ELF2EFI_LDFLAGS) -o $@ + $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET32 $< -o $@ CLEANUP += $(ELF2EFI32) $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET64 $< \ - $(ELF2EFI_LDFLAGS) -o $@ + $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET64 $< -o $@ CLEANUP += $(ELF2EFI64) $(EFIROM) : util/efirom.c $(MAKEDEPS) From 1e066431a45a67321abcd134e888205fc633bd41 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 13:30:37 +0100 Subject: [PATCH 200/591] [tcpip] Do not fall back to using unoptimised TCP/IP checksumming Require architecture-specific code to make a deliberate choice to use the unoptimised generic_tcpip_continue_chksum() function, if there is no optimised version available. Signed-off-by: Michael Brown --- src/arch/x86/core/x86_tcpip.c | 4 ++-- src/arch/x86/include/bits/tcpip.h | 6 ++---- src/include/ipxe/tcpip.h | 13 ++++--------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/arch/x86/core/x86_tcpip.c b/src/arch/x86/core/x86_tcpip.c index 88042f5f7..ed323d5d0 100644 --- a/src/arch/x86/core/x86_tcpip.c +++ b/src/arch/x86/core/x86_tcpip.c @@ -42,8 +42,8 @@ extern char x86_tcpip_loop_end[]; * @v len Length of data buffer * @ret cksum Updated checksum, in network byte order */ -uint16_t x86_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ) { +uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data, + size_t len ) { unsigned long sum = ( ( ~partial ) & 0xffff ); unsigned long initial_word_count; unsigned long loop_count; diff --git a/src/arch/x86/include/bits/tcpip.h b/src/arch/x86/include/bits/tcpip.h index 5c2baffcf..0ac55b1a0 100644 --- a/src/arch/x86/include/bits/tcpip.h +++ b/src/arch/x86/include/bits/tcpip.h @@ -9,9 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -extern uint16_t x86_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ); - -#define tcpip_continue_chksum x86_tcpip_continue_chksum +extern uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data, + size_t len ); #endif /* _BITS_TCPIP_H */ diff --git a/src/include/ipxe/tcpip.h b/src/include/ipxe/tcpip.h index be4ac838b..414daad53 100644 --- a/src/include/ipxe/tcpip.h +++ b/src/include/ipxe/tcpip.h @@ -13,6 +13,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include + +extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial, + const void *data, size_t len ); + #include struct io_buffer; @@ -195,17 +199,8 @@ extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip, extern struct tcpip_net_protocol * tcpip_net_protocol ( sa_family_t sa_family ); extern struct net_device * tcpip_netdev ( struct sockaddr_tcpip *st_dest ); extern size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ); -extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ); extern uint16_t tcpip_chksum ( const void *data, size_t len ); extern int tcpip_bind ( struct sockaddr_tcpip *st_local, int ( * available ) ( int port ) ); -/* Use generic_tcpip_continue_chksum() if no architecture-specific - * version is available - */ -#ifndef tcpip_continue_chksum -#define tcpip_continue_chksum generic_tcpip_continue_chksum -#endif - #endif /* _IPXE_TCPIP_H */ From 757ab983811ac8d3f65efb65b8309738bd33bea3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 13:04:33 +0100 Subject: [PATCH 201/591] [efi] Use a timer event to generate the currticks() timer We currently use the EFI_CPU_ARCH_PROTOCOL's GetTimerValue() method to generate the currticks() timer, calibrated against a 1ms delay from the boot services Stall() method. This does not work on ARM platforms, where GetTimerValue() is an empty stub which just returns EFI_UNSUPPORTED. Fix by instead creating a periodic timer event, and using this event to increment a current tick counter. Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/Cpu.h | 302 ---------------------------- src/include/ipxe/efi/efi_timer.h | 18 ++ src/interface/efi/efi_timer.c | 122 ++++++----- 3 files changed, 88 insertions(+), 354 deletions(-) delete mode 100644 src/include/ipxe/efi/Protocol/Cpu.h diff --git a/src/include/ipxe/efi/Protocol/Cpu.h b/src/include/ipxe/efi/Protocol/Cpu.h deleted file mode 100644 index 665924e88..000000000 --- a/src/include/ipxe/efi/Protocol/Cpu.h +++ /dev/null @@ -1,302 +0,0 @@ -/** @file - CPU Architectural Protocol as defined in PI spec Volume 2 DXE - - This code abstracts the DXE core from processor implementation details. - - Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
- This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#ifndef __ARCH_PROTOCOL_CPU_H__ -#define __ARCH_PROTOCOL_CPU_H__ - -FILE_LICENCE ( BSD3 ); - -#include - -#define EFI_CPU_ARCH_PROTOCOL_GUID \ - { 0x26baccb1, 0x6f42, 0x11d4, {0xbc, 0xe7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } } - -typedef struct _EFI_CPU_ARCH_PROTOCOL EFI_CPU_ARCH_PROTOCOL; - -/// -/// The type of flush operation -/// -typedef enum { - EfiCpuFlushTypeWriteBackInvalidate, - EfiCpuFlushTypeWriteBack, - EfiCpuFlushTypeInvalidate, - EfiCpuMaxFlushType -} EFI_CPU_FLUSH_TYPE; - -/// -/// The type of processor INIT. -/// -typedef enum { - EfiCpuInit, - EfiCpuMaxInitType -} EFI_CPU_INIT_TYPE; - -/** - EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. - - @param InterruptType Defines the type of interrupt or exception that - occurred on the processor.This parameter is processor architecture specific. - @param SystemContext A pointer to the processor context when - the interrupt occurred on the processor. - - @return None - -**/ -typedef -VOID -(EFIAPI *EFI_CPU_INTERRUPT_HANDLER)( - IN CONST EFI_EXCEPTION_TYPE InterruptType, - IN CONST EFI_SYSTEM_CONTEXT SystemContext - ); - -/** - This function flushes the range of addresses from Start to Start+Length - from the processor's data cache. If Start is not aligned to a cache line - boundary, then the bytes before Start to the preceding cache line boundary - are also flushed. If Start+Length is not aligned to a cache line boundary, - then the bytes past Start+Length to the end of the next cache line boundary - are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be - supported. If the data cache is fully coherent with all DMA operations, then - this function can just return EFI_SUCCESS. If the processor does not support - flushing a range of the data cache, then the entire data cache can be flushed. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param Start The beginning physical address to flush from the processor's data - cache. - @param Length The number of bytes to flush from the processor's data cache. This - function may flush more bytes than Length specifies depending upon - the granularity of the flush operation that the processor supports. - @param FlushType Specifies the type of flush operation to perform. - - @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from - the processor's data cache. - @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified - by FlushType. - @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed - from the processor's data cache. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_FLUSH_DATA_CACHE)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_PHYSICAL_ADDRESS Start, - IN UINT64 Length, - IN EFI_CPU_FLUSH_TYPE FlushType - ); - - -/** - This function enables interrupt processing by the processor. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - - @retval EFI_SUCCESS Interrupts are enabled on the processor. - @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_ENABLE_INTERRUPT)( - IN EFI_CPU_ARCH_PROTOCOL *This - ); - - -/** - This function disables interrupt processing by the processor. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - - @retval EFI_SUCCESS Interrupts are disabled on the processor. - @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_DISABLE_INTERRUPT)( - IN EFI_CPU_ARCH_PROTOCOL *This - ); - - -/** - This function retrieves the processor's current interrupt state a returns it in - State. If interrupts are currently enabled, then TRUE is returned. If interrupts - are currently disabled, then FALSE is returned. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param State A pointer to the processor's current interrupt state. Set to TRUE if - interrupts are enabled and FALSE if interrupts are disabled. - - @retval EFI_SUCCESS The processor's current interrupt state was returned in State. - @retval EFI_INVALID_PARAMETER State is NULL. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_GET_INTERRUPT_STATE)( - IN EFI_CPU_ARCH_PROTOCOL *This, - OUT BOOLEAN *State - ); - - -/** - This function generates an INIT on the processor. If this function succeeds, then the - processor will be reset, and control will not be returned to the caller. If InitType is - not supported by this processor, or the processor cannot programmatically generate an - INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error - occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param InitType The type of processor INIT to perform. - - @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen. - @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported - by this processor. - @retval EFI_DEVICE_ERROR The processor INIT failed. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_INIT)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_CPU_INIT_TYPE InitType - ); - - -/** - This function registers and enables the handler specified by InterruptHandler for a processor - interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the - handler for the processor interrupt or exception type specified by InterruptType is uninstalled. - The installed handler is called once for each processor interrupt or exception. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts - are enabled and FALSE if interrupts are disabled. - @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called - when a processor interrupt occurs. If this parameter is NULL, then the handler - will be uninstalled. - - @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. - @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was - previously installed. - @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not - previously installed. - @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_REGISTER_INTERRUPT_HANDLER)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_EXCEPTION_TYPE InterruptType, - IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler - ); - - -/** - This function reads the processor timer specified by TimerIndex and returns it in TimerValue. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param TimerIndex Specifies which processor timer is to be returned in TimerValue. This parameter - must be between 0 and NumberOfTimers-1. - @param TimerValue Pointer to the returned timer value. - @param TimerPeriod A pointer to the amount of time that passes in femtoseconds for each increment - of TimerValue. If TimerValue does not increment at a predictable rate, then 0 is - returned. This parameter is optional and may be NULL. - - @retval EFI_SUCCESS The processor timer value specified by TimerIndex was returned in TimerValue. - @retval EFI_DEVICE_ERROR An error occurred attempting to read one of the processor's timers. - @retval EFI_INVALID_PARAMETER TimerValue is NULL or TimerIndex is not valid. - @retval EFI_UNSUPPORTED The processor does not have any readable timers. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_GET_TIMER_VALUE)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN UINT32 TimerIndex, - OUT UINT64 *TimerValue, - OUT UINT64 *TimerPeriod OPTIONAL - ); - - -/** - This function modifies the attributes for the memory region specified by BaseAddress and - Length from their current attributes to the attributes specified by Attributes. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param BaseAddress The physical address that is the start address of a memory region. - @param Length The size in bytes of the memory region. - @param Attributes The bit mask of attributes to set for the memory region. - - @retval EFI_SUCCESS The attributes were set for the memory region. - @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by - BaseAddress and Length cannot be modified. - @retval EFI_INVALID_PARAMETER Length is zero. - Attributes specified an illegal combination of attributes that - cannot be set together. - @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of - the memory resource range. - @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory - resource range specified by BaseAddress and Length. - The bit mask of attributes is not support for the memory resource - range specified by BaseAddress and Length. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_SET_MEMORY_ATTRIBUTES)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length, - IN UINT64 Attributes - ); - - -/// -/// The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE -/// Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt -/// vectors and exception vectors, reading internal processor timers, resetting the processor, and -/// determining the processor frequency. -/// -struct _EFI_CPU_ARCH_PROTOCOL { - EFI_CPU_FLUSH_DATA_CACHE FlushDataCache; - EFI_CPU_ENABLE_INTERRUPT EnableInterrupt; - EFI_CPU_DISABLE_INTERRUPT DisableInterrupt; - EFI_CPU_GET_INTERRUPT_STATE GetInterruptState; - EFI_CPU_INIT Init; - EFI_CPU_REGISTER_INTERRUPT_HANDLER RegisterInterruptHandler; - EFI_CPU_GET_TIMER_VALUE GetTimerValue; - EFI_CPU_SET_MEMORY_ATTRIBUTES SetMemoryAttributes; - /// - /// The number of timers that are available in a processor. The value in this - /// field is a constant that must not be modified after the CPU Architectural - /// Protocol is installed. All consumers must treat this as a read-only field. - /// - UINT32 NumberOfTimers; - /// - /// The size, in bytes, of the alignment required for DMA buffer allocations. - /// This is typically the size of the largest data cache line in the platform. - /// The value in this field is a constant that must not be modified after the - /// CPU Architectural Protocol is installed. All consumers must treat this as - /// a read-only field. - /// - UINT32 DmaBufferAlignment; -}; - -extern EFI_GUID gEfiCpuArchProtocolGuid; - -#endif diff --git a/src/include/ipxe/efi/efi_timer.h b/src/include/ipxe/efi/efi_timer.h index c03765393..c49875988 100644 --- a/src/include/ipxe/efi/efi_timer.h +++ b/src/include/ipxe/efi/efi_timer.h @@ -15,4 +15,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define TIMER_PREFIX_efi __efi_ #endif +/** + * Number of ticks per second + * + * This is a policy decision. + */ +#define EFI_TICKS_PER_SEC 20 + +/** + * Get number of ticks per second + * + * @ret ticks_per_sec Number of ticks per second + */ +static inline __attribute__ (( always_inline )) unsigned long +TIMER_INLINE ( efi, ticks_per_sec ) ( void ) { + + return EFI_TICKS_PER_SEC; +} + #endif /* _IPXE_EFI_TIMER_H */ diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 81620c92c..a574e2043 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -25,12 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -#include -#include #include #include +#include #include -#include /** @file * @@ -38,19 +36,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Scale factor to apply to CPU timer 0 - * - * The timer is scaled down in order to ensure that reasonable values - * for "number of ticks" don't exceed the size of an unsigned long. - */ -#define EFI_TIMER0_SHIFT 12 +/** Current tick count */ +static unsigned long efi_jiffies; -/** Calibration time */ -#define EFI_CALIBRATE_DELAY_MS 1 +/** Timer tick event */ +static EFI_EVENT efi_tick_event; -/** CPU protocol */ -static EFI_CPU_ARCH_PROTOCOL *cpu_arch; -EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch ); +/** Colour for debug messages */ +#define colour &efi_jiffies /** * Delay for a fixed number of microseconds @@ -64,8 +57,8 @@ static void efi_udelay ( unsigned long usecs ) { if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) { rc = -EEFI ( efirc ); - DBG ( "EFI could not delay for %ldus: %s\n", - usecs, strerror ( rc ) ); + DBGC ( colour, "EFI could not delay for %ldus: %s\n", + usecs, strerror ( rc ) ); /* Probably screwed */ } } @@ -76,53 +69,78 @@ static void efi_udelay ( unsigned long usecs ) { * @ret ticks Current time, in ticks */ static unsigned long efi_currticks ( void ) { - UINT64 time; - EFI_STATUS efirc; - int rc; - /* Read CPU timer 0 (TSC) */ - if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBG ( "EFI could not read CPU timer: %s\n", strerror ( rc ) ); - /* Probably screwed */ - return -1UL; - } - - return ( time >> EFI_TIMER0_SHIFT ); + return efi_jiffies; } /** - * Get number of ticks per second + * Timer tick * - * @ret ticks_per_sec Number of ticks per second + * @v event Timer tick event + * @v context Event context */ -static unsigned long efi_ticks_per_sec ( void ) { - static unsigned long ticks_per_sec = 0; +static EFIAPI void efi_tick ( EFI_EVENT event __unused, + void *context __unused ) { - /* Calibrate timer, if necessary. EFI does nominally provide - * the timer speed via the (optional) TimerPeriod parameter to - * the GetTimerValue() call, but it gets the speed slightly - * wrong. By up to three orders of magnitude. Not helpful. - */ - if ( ! ticks_per_sec ) { - unsigned long start; - unsigned long elapsed; + /* Increment tick count */ + efi_jiffies++; +} - DBG ( "Calibrating EFI timer with a %d ms delay\n", - EFI_CALIBRATE_DELAY_MS ); - start = currticks(); - mdelay ( EFI_CALIBRATE_DELAY_MS ); - elapsed = ( currticks() - start ); - ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS )); - DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld " - "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS, - ticks_per_sec ); +/** + * Start timer tick + * + */ +static void efi_tick_startup ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Create timer tick event */ + if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ), + TPL_CALLBACK, efi_tick, NULL, + &efi_tick_event ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not create timer tick: %s\n", + strerror ( rc ) ); + /* Nothing we can do about it */ + return; } - return ticks_per_sec; + /* Start timer tick */ + if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic, + ( 10000000 / EFI_TICKS_PER_SEC ) ) ) !=0){ + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not start timer tick: %s\n", + strerror ( rc ) ); + /* Nothing we can do about it */ + return; + } + DBGC ( colour, "EFI timer started at %d ticks per second\n", + EFI_TICKS_PER_SEC ); } +/** + * Stop timer tick + * + * @v booting System is shutting down in order to boot + */ +static void efi_tick_shutdown ( int booting __unused ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Stop timer tick */ + bs->SetTimer ( efi_tick_event, TimerCancel, 0 ); + DBGC ( colour, "EFI timer stopped\n" ); + + /* Destroy timer tick event */ + bs->CloseEvent ( efi_tick_event ); +} + +/** Timer tick startup function */ +struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .startup = efi_tick_startup, + .shutdown = efi_tick_shutdown, +}; + PROVIDE_TIMER ( efi, udelay, efi_udelay ); PROVIDE_TIMER ( efi, currticks, efi_currticks ); -PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec ); +PROVIDE_TIMER_INLINE ( efi, ticks_per_sec ); From 57d0ea7c461d4ba2c8befa9506472c81129c30b3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 14:13:44 +0100 Subject: [PATCH 202/591] [efi] Generalise EFI entropy generation to non-x86 CPUs Signed-off-by: Michael Brown --- src/arch/x86/include/bits/errfile.h | 1 - src/include/ipxe/errfile.h | 1 + src/{arch/x86 => }/interface/efi/efi_entropy.c | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/{arch/x86 => }/interface/efi/efi_entropy.c (96%) diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 9eb4b5488..42792242d 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -53,7 +53,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) -#define ERRFILE_efi_entropy ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00020000 ) /** @} */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 4681567b9..2cd9fb5b1 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -351,6 +351,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_usb ( ERRFILE_OTHER | 0x004b0000 ) #define ERRFILE_efi_fbcon ( ERRFILE_OTHER | 0x004c0000 ) #define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) +#define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) /** @} */ diff --git a/src/arch/x86/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c similarity index 96% rename from src/arch/x86/interface/efi/efi_entropy.c rename to src/interface/efi/efi_entropy.c index a54bd12e6..881c4c9a2 100644 --- a/src/arch/x86/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -104,13 +105,12 @@ static void efi_entropy_disable ( void ) { /** * Wait for a timer tick * - * @ret low TSC low-order bits, or negative error + * @ret low CPU profiling low-order bits, or negative error */ static int efi_entropy_tick ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; UINTN index; uint16_t low; - uint32_t discard_d; EFI_STATUS efirc; int rc; @@ -129,8 +129,8 @@ static int efi_entropy_tick ( void ) { return rc; } - /* Get current TSC low-order bits */ - __asm__ __volatile__ ( "rdtsc" : "=a" ( low ), "=d" ( discard_d ) ); + /* Get current CPU profiling timestamp low-order bits */ + low = profile_timestamp(); return low; } From e2f14c2f8c10674dbbd4f1228d79dc4c9be213b5 Mon Sep 17 00:00:00 2001 From: Vinson Lee Date: Fri, 15 Apr 2016 22:38:35 +0000 Subject: [PATCH 203/591] [mucurses] Fix GCC 6 nonnull-compare errors Remove null checks for arguments declared as nonnull. Signed-off-by: Vinson Lee Signed-off-by: Michael Brown --- src/hci/mucurses/windows.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/hci/mucurses/windows.c b/src/hci/mucurses/windows.c index 7f39bdea2..5f5d1f4e2 100644 --- a/src/hci/mucurses/windows.c +++ b/src/hci/mucurses/windows.c @@ -18,9 +18,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret rc return status code */ int delwin ( WINDOW *win ) { - if ( win == NULL ) - return ERR; - /* I think we should blank the region covered by the window - ncurses doesn't do this, but they have a buffer, so they may just be deleting from an offscreen context whereas we @@ -51,8 +48,6 @@ int delwin ( WINDOW *win ) { WINDOW *derwin ( WINDOW *parent, int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *child; - if ( parent == NULL ) - return NULL; if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) return NULL; if ( ( (unsigned)ncols > parent->width ) || @@ -75,8 +70,6 @@ WINDOW *derwin ( WINDOW *parent, int nlines, int ncols, */ WINDOW *dupwin ( WINDOW *orig ) { WINDOW *copy; - if ( orig == NULL ) - return NULL; if ( ( copy = malloc( sizeof( WINDOW ) ) ) == NULL ) return NULL; copy->scr = orig->scr; @@ -99,8 +92,6 @@ WINDOW *dupwin ( WINDOW *orig ) { * @ret rc return status code */ int mvwin ( WINDOW *win, int y, int x ) { - if ( win == NULL ) - return ERR; if ( ( ( (unsigned)y + win->height ) > LINES ) || ( ( (unsigned)x + win->width ) > COLS ) ) return ERR; @@ -149,8 +140,6 @@ WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *subwin ( WINDOW *parent, int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *child; - if ( parent == NULL ) - return NULL; if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) return NULL; child = newwin( nlines, ncols, begin_y, begin_x ); From 65b32a0b7000f70a5bb1d33190d40f9b04c93172 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 15:53:52 +0100 Subject: [PATCH 204/591] [sis190] Fix building with GCC 6 Signed-off-by: Michael Brown --- src/drivers/net/sis190.c | 6 ------ src/drivers/net/sis190.h | 7 ------- 2 files changed, 13 deletions(-) diff --git a/src/drivers/net/sis190.c b/src/drivers/net/sis190.c index 991c30f9e..81f3d9844 100644 --- a/src/drivers/net/sis190.c +++ b/src/drivers/net/sis190.c @@ -72,12 +72,6 @@ struct pci_driver sis190_isa_bridge_driver __pci_driver = { static const u32 sis190_intr_mask = RxQEmpty | RxQInt | TxQ1Int | TxQ0Int | RxHalt | TxHalt | LinkChange; -/* - * Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - * The chips use a 64 element hash table based on the Ethernet CRC. - */ -static const int multicast_filter_limit = 32; - static void __mdio_cmd(void *ioaddr, u32 ctl) { unsigned int i; diff --git a/src/drivers/net/sis190.h b/src/drivers/net/sis190.h index 0551333d5..79f94d2d9 100644 --- a/src/drivers/net/sis190.h +++ b/src/drivers/net/sis190.h @@ -297,13 +297,6 @@ static struct mii_chip_info { { NULL, { 0x00, 0x00 }, 0, 0 } }; -static const struct { - const char *name; -} sis_chip_info[] = { - { "SiS 190 PCI Fast Ethernet adapter" }, - { "SiS 191 PCI Gigabit Ethernet adapter" }, -}; - static void sis190_phy_task(struct sis190_private *tp); static void sis190_free(struct net_device *dev); static inline void sis190_init_rxfilter(struct net_device *dev); From 76ec2a0540b25dbd183b9ce185583a4b24278cf1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 15:54:10 +0100 Subject: [PATCH 205/591] [skge] Fix building with GCC 6 Signed-off-by: Michael Brown --- src/drivers/net/skge.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/drivers/net/skge.c b/src/drivers/net/skge.c index 6384e7647..c3264225b 100755 --- a/src/drivers/net/skge.c +++ b/src/drivers/net/skge.c @@ -84,9 +84,6 @@ static struct net_device_operations skge_operations = { /* Avoid conditionals by using array */ static const int txqaddr[] = { Q_XA1, Q_XA2 }; static const int rxqaddr[] = { Q_R1, Q_R2 }; -static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F }; -static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F }; -static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F }; static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 }; /* Determine supported/advertised modes based on hardware. @@ -1922,8 +1919,6 @@ static void skge_tx_clean(struct net_device *dev) skge->tx_ring.to_clean = e; } -static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 }; - static inline u16 phy_length(const struct skge_hw *hw, u32 status) { if (hw->chip_id == CHIP_ID_GENESIS) From 08230599ef7715133a951bacb4b1169b2930c0b6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 15:57:14 +0100 Subject: [PATCH 206/591] [golan] Fix building with GCC 6 Signed-off-by: Michael Brown --- src/drivers/infiniband/golan.c | 20 ++++++++++++++------ src/drivers/infiniband/golan.h | 7 +++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index ba5a52495..d410fdfb9 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -843,6 +843,7 @@ err_create_eq_eqe_alloc: static void golan_destory_eq(struct golan *golan) { struct golan_cmd_layout *cmd; + struct golan_destroy_eq_mbox_in *in; uint8_t eqn = golan->eq.eqn; int rc; @@ -853,7 +854,8 @@ static void golan_destory_eq(struct golan *golan) sizeof(struct golan_destroy_eq_mbox_in), sizeof(struct golan_destroy_eq_mbox_out)); - ((struct golan_destroy_eq_mbox_in *)(cmd->in))->eqn = eqn; + in = GOLAN_MBOX_IN ( cmd, in ); + in->eqn = eqn; rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); GOLAN_PRINT_RC_AND_CMD_STATUS; @@ -1630,6 +1632,7 @@ static int golan_post_recv(struct ib_device *ibdev, static int golan_query_vport_context ( struct ib_device *ibdev ) { struct golan *golan = ib_get_drvdata ( ibdev ); struct golan_cmd_layout *cmd; + struct golan_query_hca_vport_context_inbox *in; struct golan_query_hca_vport_context_data *context_data; int rc; @@ -1638,7 +1641,8 @@ static int golan_query_vport_context ( struct ib_device *ibdev ) { sizeof(struct golan_query_hca_vport_context_inbox), sizeof(struct golan_query_hca_vport_context_outbox) ); - ((struct golan_query_hca_vport_context_inbox *)(cmd->in))->port_num = (u8)ibdev->port; + in = GOLAN_MBOX_IN ( cmd, in ); + in->port_num = (u8)ibdev->port; rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_context_cmd ); @@ -1662,6 +1666,7 @@ err_query_vport_context_cmd: static int golan_query_vport_gid ( struct ib_device *ibdev ) { struct golan *golan = ib_get_drvdata( ibdev ); struct golan_cmd_layout *cmd; + struct golan_query_hca_vport_gid_inbox *in; union ib_gid *ib_gid; int rc; @@ -1670,8 +1675,9 @@ static int golan_query_vport_gid ( struct ib_device *ibdev ) { sizeof(struct golan_query_hca_vport_gid_inbox), sizeof(struct golan_query_hca_vport_gid_outbox) ); - ((struct golan_query_hca_vport_gid_inbox *)(cmd->in))->port_num = (u8)ibdev->port; - ((struct golan_query_hca_vport_gid_inbox *)(cmd->in))->gid_index = 0; + in = GOLAN_MBOX_IN ( cmd, in ); + in->port_num = (u8)ibdev->port; + in->gid_index = 0; rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_gid_cmd ); @@ -1688,6 +1694,7 @@ err_query_vport_gid_cmd: static int golan_query_vport_pkey ( struct ib_device *ibdev ) { struct golan *golan = ib_get_drvdata ( ibdev ); struct golan_cmd_layout *cmd; + struct golan_query_hca_vport_pkey_inbox *in; //struct golan_query_hca_vport_pkey_data *pkey_table; int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size)); int rc; @@ -1698,8 +1705,9 @@ static int golan_query_vport_pkey ( struct ib_device *ibdev ) { sizeof(struct golan_outbox_hdr) + 8 + sizeof(struct golan_query_hca_vport_pkey_data) * pkey_table_size_in_entries ); - ((struct golan_query_hca_vport_pkey_inbox *)(cmd->in))->port_num = (u8)ibdev->port; - ((struct golan_query_hca_vport_pkey_inbox *)(cmd->in))->pkey_index = 0xffff; + in = GOLAN_MBOX_IN ( cmd, in ); + in->port_num = (u8)ibdev->port; + in->pkey_index = 0xffff; rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_pkey_cmd ); diff --git a/src/drivers/infiniband/golan.h b/src/drivers/infiniband/golan.h index 6e96f7508..a6cb4e744 100755 --- a/src/drivers/infiniband/golan.h +++ b/src/drivers/infiniband/golan.h @@ -74,6 +74,13 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define GET_INBOX(golan, idx) (&(((struct mbox *)(golan->mboxes.inbox))[idx])) #define GET_OUTBOX(golan, idx) (&(((struct mbox *)(golan->mboxes.outbox))[idx])) +#define GOLAN_MBOX_IN( cmd_ptr, in_ptr ) ( { \ + union { \ + __be32 raw[4]; \ + typeof ( *(in_ptr) ) cooked; \ + } *u = container_of ( &(cmd_ptr)->in[0], typeof ( *u ), raw[0] ); \ + &u->cooked; } ) + #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) /* Fw status fields */ From 63037bdce4a325e5e1da85ffcdf27b77ac670c01 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 15:57:44 +0100 Subject: [PATCH 207/591] [ath] Fix building with GCC 6 Signed-off-by: Michael Brown --- src/drivers/net/ath/ath.h | 2 - src/drivers/net/ath/ath5k/ath5k.c | 40 ----------- src/drivers/net/ath/ath5k/ath5k_phy.c | 6 +- src/drivers/net/ath/ath5k/ath5k_reset.c | 8 --- src/drivers/net/ath/ath9k/ar9002_initvals.h | 72 +++++++++---------- .../net/ath/ath9k/ar9003_2p2_initvals.h | 40 +++++------ src/drivers/net/ath/ath9k/ar9340_initvals.h | 36 +++++----- src/drivers/net/ath/ath9k/ar9485_initvals.h | 44 ++++++------ src/drivers/net/ath/ath9k/ath9k_init.c | 3 +- 9 files changed, 101 insertions(+), 150 deletions(-) diff --git a/src/drivers/net/ath/ath.h b/src/drivers/net/ath/ath.h index 65b97f6aa..d6a037394 100644 --- a/src/drivers/net/ath/ath.h +++ b/src/drivers/net/ath/ath.h @@ -101,8 +101,6 @@ static inline u32 get_unaligned_le32(const void *p) */ #define ATH_KEYMAX 128 /* max key cache size we handle */ -static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - struct ath_ani { int caldone; unsigned int longcal_timer; diff --git a/src/drivers/net/ath/ath5k/ath5k.c b/src/drivers/net/ath/ath5k/ath5k.c index 92c4ffdf4..a6a65a2e9 100644 --- a/src/drivers/net/ath/ath5k/ath5k.c +++ b/src/drivers/net/ath/ath5k/ath5k.c @@ -85,46 +85,6 @@ static struct pci_device_id ath5k_nics[] = { PCI_ROM(0x168c, 0x001d, "ath2417", "Atheros 2417 Nala", AR5K_AR5212), }; -/* Known SREVs */ -static const struct ath5k_srev_name srev_names[] = { - { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 }, - { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 }, - { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A }, - { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B }, - { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 }, - { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 }, - { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 }, - { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A }, - { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 }, - { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 }, - { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 }, - { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 }, - { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 }, - { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 }, - { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 }, - { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 }, - { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 }, - { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 }, - { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN }, - { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 }, - { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 }, - { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A }, - { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 }, - { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 }, - { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A }, - { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B }, - { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 }, - { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A }, - { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B }, - { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 }, - { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 }, - { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 }, - { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 }, - { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 }, - { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 }, - { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, -}; - #define ATH5K_SPMBL_NO 1 #define ATH5K_SPMBL_YES 2 #define ATH5K_SPMBL_BOTH 3 diff --git a/src/drivers/net/ath/ath5k/ath5k_phy.c b/src/drivers/net/ath/ath5k/ath5k_phy.c index 7891d39ea..c2a66a4d3 100644 --- a/src/drivers/net/ath/ath5k/ath5k_phy.c +++ b/src/drivers/net/ath/ath5k/ath5k_phy.c @@ -1219,12 +1219,12 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, /* Update radio registers */ ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | - AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); + AR5K_REG_SM(-1U, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | AR5K_PHY_AGCCOARSE_LO)) | - AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | - AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); + AR5K_REG_SM(-1U, AR5K_PHY_AGCCOARSE_HI) | + AR5K_REG_SM(-127U, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | AR5K_PHY_ADCSAT_THR)) | diff --git a/src/drivers/net/ath/ath5k/ath5k_reset.c b/src/drivers/net/ath/ath5k/ath5k_reset.c index 2f36a4e9a..73765a7b0 100644 --- a/src/drivers/net/ath/ath5k/ath5k_reset.c +++ b/src/drivers/net/ath/ath5k/ath5k_reset.c @@ -134,14 +134,6 @@ static int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, return 0; } - -/* - * index into rates for control rates, we can set it up like this because - * this is only used for AR5212 and we know it supports G mode - */ -static const unsigned int control_rates[] = - { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 }; - /** * ath5k_hw_write_rate_duration - fill rate code to duration table * diff --git a/src/drivers/net/ath/ath9k/ar9002_initvals.h b/src/drivers/net/ath/ath9k/ar9002_initvals.h index d7a5ac09f..f9a92c9b7 100644 --- a/src/drivers/net/ath/ath9k/ar9002_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9002_initvals.h @@ -16,7 +16,7 @@ FILE_LICENCE ( BSD2 ); -static const u32 ar9280Modes_9280_2[][6] = { +static __unused const u32 ar9280Modes_9280_2[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180}, @@ -65,7 +65,7 @@ static const u32 ar9280Modes_9280_2[][6] = { {0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000}, }; -static const u32 ar9280Common_9280_2[][2] = { +static __unused const u32 ar9280Common_9280_2[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, @@ -409,7 +409,7 @@ static const u32 ar9280Common_9280_2[][2] = { {0x00007898, 0x2a850160}, }; -static const u32 ar9280Modes_fast_clock_9280_2[][3] = { +static __unused const u32 ar9280Modes_fast_clock_9280_2[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -426,7 +426,7 @@ static const u32 ar9280Modes_fast_clock_9280_2[][3] = { {0x00009918, 0x0000000b, 0x00000016}, }; -static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { +static __unused const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290}, {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304}, @@ -559,7 +559,7 @@ static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { {0x0000a848, 0x00001066, 0x00001066, 0x00001055, 0x00001055, 0x00001055}, }; -static const u32 ar9280Modes_original_rxgain_9280_2[][6] = { +static __unused const u32 ar9280Modes_original_rxgain_9280_2[][6] = { {0x00009a00, 0x00008184, 0x00008184, 0x00008000, 0x00008000, 0x00008000}, {0x00009a04, 0x00008188, 0x00008188, 0x00008000, 0x00008000, 0x00008000}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00008000, 0x00008000, 0x00008000}, @@ -692,7 +692,7 @@ static const u32 ar9280Modes_original_rxgain_9280_2[][6] = { {0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063}, }; -static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { +static __unused const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290}, {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304}, @@ -825,7 +825,7 @@ static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { {0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a}, }; -static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { +static __unused const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { {0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652}, {0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce}, {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -859,7 +859,7 @@ static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { {0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480}, }; -static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { +static __unused const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { {0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652}, {0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce}, {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -893,7 +893,7 @@ static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { {0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480}, }; -static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { +static __unused const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -907,7 +907,7 @@ static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { +static __unused const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -921,7 +921,7 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -935,7 +935,7 @@ static const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -949,7 +949,7 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285Modes_9285_1_2[][6] = { +static __unused const u32 ar9285Modes_9285_1_2[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180}, @@ -1254,7 +1254,7 @@ static const u32 ar9285Modes_9285_1_2[][6] = { {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e}, }; -static const u32 ar9285Common_9285_1_2[][2] = { +static __unused const u32 ar9285Common_9285_1_2[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020045}, @@ -1574,7 +1574,7 @@ static const u32 ar9285Common_9285_1_2[][2] = { {0x00007870, 0x10142c00}, }; -static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { +static __unused const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000}, @@ -1614,7 +1614,7 @@ static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; -static const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = { +static __unused const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000}, @@ -1654,7 +1654,7 @@ static const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = { {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, }; -static const u32 ar9285Modes_XE2_0_normal_power[][6] = { +static __unused const u32 ar9285Modes_XE2_0_normal_power[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000}, @@ -1694,7 +1694,7 @@ static const u32 ar9285Modes_XE2_0_normal_power[][6] = { {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, }; -static const u32 ar9285Modes_XE2_0_high_power[][6] = { +static __unused const u32 ar9285Modes_XE2_0_high_power[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000}, @@ -1734,7 +1734,7 @@ static const u32 ar9285Modes_XE2_0_high_power[][6] = { {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; -static const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -1748,7 +1748,7 @@ static const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -1762,7 +1762,7 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9287Modes_9287_1_1[][6] = { +static __unused const u32 ar9287Modes_9287_1_1[][6] = { {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38, 0x00001180}, @@ -1808,7 +1808,7 @@ static const u32 ar9287Modes_9287_1_1[][6] = { {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9287Common_9287_1_1[][2] = { +static __unused const u32 ar9287Common_9287_1_1[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, @@ -2177,21 +2177,21 @@ static const u32 ar9287Common_9287_1_1[][2] = { {0x000078b8, 0x2a850160}, }; -static const u32 ar9287Common_normal_cck_fir_coeff_9287_1_1[][2] = { +static __unused const u32 ar9287Common_normal_cck_fir_coeff_9287_1_1[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00fffeff}, {0x0000a1f8, 0x00f5f9ff}, {0x0000a1fc, 0xb79f6427}, }; -static const u32 ar9287Common_japan_2484_cck_fir_coeff_9287_1_1[][2] = { +static __unused const u32 ar9287Common_japan_2484_cck_fir_coeff_9287_1_1[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00000000}, {0x0000a1f8, 0xefff0301}, {0x0000a1fc, 0xca9228ee}, }; -static const u32 ar9287Modes_tx_gain_9287_1_1[][6] = { +static __unused const u32 ar9287Modes_tx_gain_9287_1_1[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002, 0x00004002}, {0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004, 0x00008004}, @@ -2239,7 +2239,7 @@ static const u32 ar9287Modes_tx_gain_9287_1_1[][6] = { {0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000, 0x0a1aa000}, }; -static const u32 ar9287Modes_rx_gain_9287_1_1[][6] = { +static __unused const u32 ar9287Modes_rx_gain_9287_1_1[][6] = { {0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120}, {0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124}, {0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128}, @@ -2500,7 +2500,7 @@ static const u32 ar9287Modes_rx_gain_9287_1_1[][6] = { {0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067}, }; -static const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { +static __unused const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -2514,7 +2514,7 @@ static const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { +static __unused const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -2528,7 +2528,7 @@ static const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9271Modes_9271[][6] = { +static __unused const u32 ar9271Modes_9271[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180}, @@ -2834,7 +2834,7 @@ static const u32 ar9271Modes_9271[][6] = { {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e}, }; -static const u32 ar9271Common_9271[][2] = { +static __unused const u32 ar9271Common_9271[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020045}, @@ -3163,26 +3163,26 @@ static const u32 ar9271Common_9271[][2] = { {0x0000d384, 0xf3307ff0}, }; -static const u32 ar9271Common_normal_cck_fir_coeff_9271[][2] = { +static __unused const u32 ar9271Common_normal_cck_fir_coeff_9271[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00fffeff}, {0x0000a1f8, 0x00f5f9ff}, {0x0000a1fc, 0xb79f6427}, }; -static const u32 ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { +static __unused const u32 ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00000000}, {0x0000a1f8, 0xefff0301}, {0x0000a1fc, 0xca9228ee}, }; -static const u32 ar9271Modes_9271_1_0_only[][6] = { +static __unused const u32 ar9271Modes_9271_1_0_only[][6] = { {0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, }; -static const u32 ar9271Modes_9271_ANI_reg[][6] = { +static __unused const u32 ar9271Modes_9271_ANI_reg[][6] = { {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2}, {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e}, {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e}, @@ -3193,7 +3193,7 @@ static const u32 ar9271Modes_9271_ANI_reg[][6] = { {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, }; -static const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = { +static __unused const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000}, @@ -3229,7 +3229,7 @@ static const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = { {0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd}, }; -static const u32 ar9271Modes_high_power_tx_gain_9271[][6] = { +static __unused const u32 ar9271Modes_high_power_tx_gain_9271[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000}, diff --git a/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h b/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h index e8ac70da5..b1303bbaa 100644 --- a/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h @@ -19,7 +19,7 @@ /* AR9003 2.2 */ -static const u32 ar9300_2p2_radio_postamble[][5] = { +static __unused const u32 ar9300_2p2_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31}, {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800}, @@ -32,7 +32,7 @@ static const u32 ar9300_2p2_radio_postamble[][5] = { {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; -static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, @@ -138,7 +138,7 @@ static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9300Modes_fast_clock_2p2[][3] = { +static __unused const u32 ar9300Modes_fast_clock_2p2[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -151,7 +151,7 @@ static const u32 ar9300Modes_fast_clock_2p2[][3] = { {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9300_2p2_radio_core[][2] = { +static __unused const u32 ar9300_2p2_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, @@ -295,7 +295,7 @@ static const u32 ar9300_2p2_radio_core[][2] = { {0x00016bd4, 0x00000000}, }; -static const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { +static __unused const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x02000101}, {0x0000a004, 0x02000102}, @@ -555,7 +555,7 @@ static const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { {0x0000b1fc, 0x00000776}, }; -static const u32 ar9300_2p2_mac_postamble[][5] = { +static __unused const u32 ar9300_2p2_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, @@ -567,12 +567,12 @@ static const u32 ar9300_2p2_mac_postamble[][5] = { {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -static const u32 ar9300_2p2_soc_postamble[][5] = { +static __unused const u32 ar9300_2p2_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; -static const u32 ar9200_merlin_2p2_radio_core[][2] = { +static __unused const u32 ar9200_merlin_2p2_radio_core[][2] = { /* Addr allmodes */ {0x00007800, 0x00040000}, {0x00007804, 0xdb005012}, @@ -614,7 +614,7 @@ static const u32 ar9200_merlin_2p2_radio_core[][2] = { {0x00007894, 0x5a108000}, }; -static const u32 ar9300_2p2_baseband_postamble[][5] = { +static __unused const u32 ar9300_2p2_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, @@ -670,7 +670,7 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = { {0x0000c284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; -static const u32 ar9300_2p2_baseband_core[][2] = { +static __unused const u32 ar9300_2p2_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, @@ -833,7 +833,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = { {0x0000c420, 0x00000000}, }; -static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, @@ -939,7 +939,7 @@ static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { {0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c}, }; -static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, @@ -1045,7 +1045,7 @@ static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9300Common_rx_gain_table_2p2[][2] = { +static __unused const u32 ar9300Common_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1305,7 +1305,7 @@ static const u32 ar9300Common_rx_gain_table_2p2[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, @@ -1411,7 +1411,7 @@ static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9300_2p2_mac_core[][2] = { +static __unused const u32 ar9300_2p2_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, @@ -1570,7 +1570,7 @@ static const u32 ar9300_2p2_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { +static __unused const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1830,7 +1830,7 @@ static const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9300_2p2_soc_preamble[][2] = { +static __unused const u32 ar9300_2p2_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007008, 0x00000000}, @@ -1840,21 +1840,21 @@ static const u32 ar9300_2p2_soc_preamble[][2] = { {0x00007048, 0x00000008}, }; -static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = { +static __unused const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x0821265e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; -static const u32 ar9300PciePhy_clkreq_enable_L1_2p2[][2] = { +static __unused const u32 ar9300PciePhy_clkreq_enable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x08253e5e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; -static const u32 ar9300PciePhy_clkreq_disable_L1_2p2[][2] = { +static __unused const u32 ar9300PciePhy_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x08213e5e}, {0x00004040, 0x0008003b}, diff --git a/src/drivers/net/ath/ath9k/ar9340_initvals.h b/src/drivers/net/ath/ath9k/ar9340_initvals.h index 815a8af1b..784080b16 100644 --- a/src/drivers/net/ath/ath9k/ar9340_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9340_initvals.h @@ -17,7 +17,7 @@ #ifndef INITVALS_9340_H #define INITVALS_9340_H -static const u32 ar9340_1p0_radio_postamble[][5] = { +static __unused const u32 ar9340_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646800, 0xa4646800, 0xa4646800, 0xa4646800}, {0x0001610c, 0x08000000, 0x08000000, 0x00000000, 0x00000000}, @@ -26,7 +26,7 @@ static const u32 ar9340_1p0_radio_postamble[][5] = { {0x00016540, 0x10804000, 0x10804000, 0x50804000, 0x50804000}, }; -static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -99,7 +99,7 @@ static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_fast_clock_1p0[][3] = { +static __unused const u32 ar9340Modes_fast_clock_1p0[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -112,7 +112,7 @@ static const u32 ar9340Modes_fast_clock_1p0[][3] = { {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9340_1p0_radio_core[][2] = { +static __unused const u32 ar9340_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, @@ -218,13 +218,13 @@ static const u32 ar9340_1p0_radio_core[][2] = { {0x000167d4, 0x00000000}, }; -static const u32 ar9340_1p0_radio_core_40M[][2] = { +static __unused const u32 ar9340_1p0_radio_core_40M[][2] = { {0x0001609c, 0x02566f3a}, {0x000160ac, 0xa4647c00}, {0x000160b0, 0x01885f5a}, }; -static const u32 ar9340_1p0_mac_postamble[][5] = { +static __unused const u32 ar9340_1p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, @@ -236,12 +236,12 @@ static const u32 ar9340_1p0_mac_postamble[][5] = { {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -static const u32 ar9340_1p0_soc_postamble[][5] = { +static __unused const u32 ar9340_1p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; -static const u32 ar9340_1p0_baseband_postamble[][5] = { +static __unused const u32 ar9340_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a022e, 0x206a022e}, @@ -288,7 +288,7 @@ static const u32 ar9340_1p0_baseband_postamble[][5] = { {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; -static const u32 ar9340_1p0_baseband_core[][2] = { +static __unused const u32 ar9340_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, @@ -464,7 +464,7 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000b420, 0x00000000}, }; -static const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, @@ -537,7 +537,7 @@ static const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, @@ -609,7 +609,7 @@ static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { {0x00016444, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, {0x00016448, 0x8e481266, 0x8e481266, 0x8e481266, 0x8e481266}, }; -static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, @@ -683,7 +683,7 @@ static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { }; -static const u32 ar9340Common_rx_gain_table_1p0[][2] = { +static __unused const u32 ar9340Common_rx_gain_table_1p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -943,7 +943,7 @@ static const u32 ar9340Common_rx_gain_table_1p0[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1016,7 +1016,7 @@ static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1089,7 +1089,7 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24927266, 0x24927266, 0x8e482266, 0x8e482266}, }; -static const u32 ar9340_1p0_mac_core[][2] = { +static __unused const u32 ar9340_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, @@ -1253,7 +1253,7 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { +static __unused const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1513,7 +1513,7 @@ static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9340_1p0_soc_preamble[][2] = { +static __unused const u32 ar9340_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007008, 0x00000000}, diff --git a/src/drivers/net/ath/ath9k/ar9485_initvals.h b/src/drivers/net/ath/ath9k/ar9485_initvals.h index 611ea6ce8..c854398aa 100644 --- a/src/drivers/net/ath/ath9k/ar9485_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9485_initvals.h @@ -17,7 +17,7 @@ #ifndef INITVALS_9485_H #define INITVALS_9485_H -static const u32 ar9485_1_1_mac_core[][2] = { +static __unused const u32 ar9485_1_1_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, @@ -179,7 +179,7 @@ static const u32 ar9485_1_1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9485_1_1_baseband_core[][2] = { +static __unused const u32 ar9485_1_1_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, @@ -316,7 +316,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x0000a7dc, 0x00000000}, }; -static const u32 ar9485Common_1_1[][2] = { +static __unused const u32 ar9485Common_1_1[][2] = { /* Addr allmodes */ {0x00007010, 0x00000022}, {0x00007020, 0x00000000}, @@ -324,7 +324,7 @@ static const u32 ar9485Common_1_1[][2] = { {0x00007038, 0x000004c2}, }; -static const u32 ar9485_1_1_baseband_postamble[][5] = { +static __unused const u32 ar9485_1_1_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, @@ -369,7 +369,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -442,7 +442,7 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -515,7 +515,7 @@ static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_1_1_radio_postamble[][2] = { +static __unused const u32 ar9485_1_1_radio_postamble[][2] = { /* Addr allmodes */ {0x0001609c, 0x0b283f31}, {0x000160ac, 0x24611800}, @@ -524,7 +524,7 @@ static const u32 ar9485_1_1_radio_postamble[][2] = { {0x00016140, 0x10804008}, }; -static const u32 ar9485_1_1_mac_postamble[][5] = { +static __unused const u32 ar9485_1_1_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, @@ -536,7 +536,7 @@ static const u32 ar9485_1_1_mac_postamble[][5] = { {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -static const u32 ar9485_1_1_radio_core[][2] = { +static __unused const u32 ar9485_1_1_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, @@ -601,14 +601,14 @@ static const u32 ar9485_1_1_radio_core[][2] = { {0x00016c44, 0x12000000}, }; -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10052e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { +static __unused const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -681,7 +681,7 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_1_1[][2] = { +static __unused const u32 ar9485_1_1[][2] = { /* Addr allmodes */ {0x0000a580, 0x00000000}, {0x0000a584, 0x00000000}, @@ -701,7 +701,7 @@ static const u32 ar9485_1_1[][2] = { {0x0000a5bc, 0x00000000}, }; -static const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -774,14 +774,14 @@ static const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10013e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485_1_1_soc_preamble[][2] = { +static __unused const u32 ar9485_1_1_soc_preamble[][2] = { /* Addr allmodes */ {0x00004014, 0xba280400}, {0x00004090, 0x00aa10aa}, @@ -793,14 +793,14 @@ static const u32 ar9485_1_1_soc_preamble[][2] = { {0x00007048, 0x00000002}, }; -static const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = { +static __unused const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = { /* Addr allmodes */ {0x0000a398, 0x00000000}, {0x0000a39c, 0x6f7f0301}, {0x0000a3a0, 0xca9228ee}, }; -static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -873,21 +873,21 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { +static __unused const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { /* Addr 5G_HT2 5G_HT40 */ {0x00009e00, 0x03721821, 0x03721821}, {0x0000a230, 0x0000400b, 0x00004016}, {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10012e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485_common_rx_gain_1_1[][2] = { +static __unused const u32 ar9485_common_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1019,14 +1019,14 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a1fc, 0x00000296}, }; -static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10053e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { +static __unused const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, diff --git a/src/drivers/net/ath/ath9k/ath9k_init.c b/src/drivers/net/ath/ath9k/ath9k_init.c index 03de7701a..98a0d6d59 100644 --- a/src/drivers/net/ath/ath9k/ath9k_init.c +++ b/src/drivers/net/ath/ath9k/ath9k_init.c @@ -22,6 +22,7 @@ FILE_LICENCE ( BSD2 ); #include #include #include +#include #include "ath9k.h" @@ -349,7 +350,7 @@ static void ath9k_init_misc(struct ath_softc *sc) ath9k_hw_set_diversity(sc->sc_ah, 1); sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); - memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + memcpy(common->bssidmask, eth_broadcast, ETH_ALEN); } static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, From a5885fbc19c4b60dc1a21624d1a9d1b77a93504e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 15:58:14 +0100 Subject: [PATCH 208/591] [legacy] Fix building with GCC 6 Signed-off-by: Michael Brown --- src/include/nic.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/include/nic.h b/src/include/nic.h index 4c91f57a6..8b06e88f4 100644 --- a/src/include/nic.h +++ b/src/include/nic.h @@ -209,7 +209,8 @@ static inline void * legacy_isa_get_drvdata ( void *hwdev ) { #undef DRIVER #define DRIVER(_name_text,_unused2,_unused3,_name,_probe,_disable) \ - static const char _name ## _text[] = _name_text; \ + static __attribute__ (( unused )) const char \ + _name ## _text[] = _name_text; \ static inline int \ _name ## _probe ( struct nic *nic, void *hwdev ) { \ return _probe ( nic, hwdev ); \ From 67f539fa80753877d7d7615c32b5ebdee5169f54 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 4 May 2016 19:13:31 +0100 Subject: [PATCH 209/591] [libgcc] Provide __divmoddi4() Signed-off-by: Michael Brown --- src/libgcc/__divdi3.c | 18 +----------------- src/libgcc/__divmoddi4.c | 25 +++++++++++++++++++++++++ src/libgcc/__moddi3.c | 15 +-------------- src/libgcc/libgcc.h | 1 + 4 files changed, 28 insertions(+), 31 deletions(-) create mode 100644 src/libgcc/__divmoddi4.c diff --git a/src/libgcc/__divdi3.c b/src/libgcc/__divdi3.c index 7097b11e1..224bb69c7 100644 --- a/src/libgcc/__divdi3.c +++ b/src/libgcc/__divdi3.c @@ -6,21 +6,5 @@ __libgcc int64_t __divdi3(int64_t num, int64_t den) { - int minus = 0; - int64_t v; - - if ( num < 0 ) { - num = -num; - minus = 1; - } - if ( den < 0 ) { - den = -den; - minus ^= 1; - } - - v = __udivmoddi4(num, den, NULL); - if ( minus ) - v = -v; - - return v; + return __divmoddi4(num, den, NULL); } diff --git a/src/libgcc/__divmoddi4.c b/src/libgcc/__divmoddi4.c new file mode 100644 index 000000000..95e328d06 --- /dev/null +++ b/src/libgcc/__divmoddi4.c @@ -0,0 +1,25 @@ +#include "libgcc.h" + +__libgcc int64_t __divmoddi4(int64_t num, int64_t den, int64 *rem_p) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, (uint64_t *)rem_p); + if ( minus ) { + v = -v; + if ( rem_p ) + *rem_p = -(*rem_p); + } + + return v; +} diff --git a/src/libgcc/__moddi3.c b/src/libgcc/__moddi3.c index d671bbc4d..ea6fd6f7b 100644 --- a/src/libgcc/__moddi3.c +++ b/src/libgcc/__moddi3.c @@ -6,21 +6,8 @@ __libgcc int64_t __moddi3(int64_t num, int64_t den) { - int minus = 0; int64_t v; - if ( num < 0 ) { - num = -num; - minus = 1; - } - if ( den < 0 ) { - den = -den; - minus ^= 1; - } - - (void) __udivmoddi4(num, den, (uint64_t *)&v); - if ( minus ) - v = -v; - + (void) __divmoddi4(num, den, &v); return v; } diff --git a/src/libgcc/libgcc.h b/src/libgcc/libgcc.h index d3e9bdd73..eb7c68ec5 100644 --- a/src/libgcc/libgcc.h +++ b/src/libgcc/libgcc.h @@ -8,6 +8,7 @@ extern __libgcc uint64_t __udivmoddi4 ( uint64_t num, uint64_t den, uint64_t *rem ); extern __libgcc uint64_t __udivdi3 (uint64_t num, uint64_t den ); extern __libgcc uint64_t __umoddi3 ( uint64_t num, uint64_t den ); +extern __libgcc int64_t __divmoddi4 ( int64_t num, int64_t den, int64_t *rem ); extern __libgcc int64_t __divdi3 ( int64_t num, int64_t den ); extern __libgcc int64_t __moddi3 ( int64_t num, int64_t den ); From 49a5bcfba626a8fc01612d0b3f50b56c51529618 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 5 May 2016 14:46:40 +0100 Subject: [PATCH 210/591] [bitops] Fix typo in test case Signed-off-by: Michael Brown --- src/tests/bitops_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/bitops_test.c b/src/tests/bitops_test.c index a689b949f..f29fc6801 100644 --- a/src/tests/bitops_test.c +++ b/src/tests/bitops_test.c @@ -60,7 +60,7 @@ static void bitops_test_exec ( void ) { /* Test clear_bit() */ clear_bit ( 0, bits ); - ok ( bits[5] == 0x00 ); + ok ( bits[0] == 0x00 ); bits[5] = 0xff; clear_bit ( 42, bits ); ok ( bits[5] == 0xfb ); From 1a16f67a28c6e8b9875b07e15c7c379cfc147e69 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 19 Oct 2015 20:01:19 +0100 Subject: [PATCH 211/591] [arm] Add support for 32-bit ARM Signed-off-by: Michael Brown --- src/arch/arm/Makefile | 25 ++ src/arch/arm/Makefile.efi | 14 + src/arch/arm/core/arm_bigint.c | 102 +++++++ src/arch/arm/core/arm_io.c | 88 ++++++ src/arch/arm/core/setjmp.S | 32 +++ src/arch/arm/include/bits/bigint.h | 316 +++++++++++++++++++++ src/arch/arm/include/bits/bitops.h | 100 +++++++ src/arch/arm/include/bits/byteswap.h | 52 ++++ src/arch/arm/include/bits/compiler.h | 16 ++ src/arch/arm/include/bits/endian.h | 13 + src/arch/arm/include/bits/entropy.h | 12 + src/arch/arm/include/bits/errfile.h | 19 ++ src/arch/arm/include/bits/hyperv.h | 12 + src/arch/arm/include/bits/io.h | 14 + src/arch/arm/include/bits/iomap.h | 12 + src/arch/arm/include/bits/nap.h | 14 + src/arch/arm/include/bits/pci_io.h | 14 + src/arch/arm/include/bits/profile.h | 30 ++ src/arch/arm/include/bits/reboot.h | 12 + src/arch/arm/include/bits/sanboot.h | 12 + src/arch/arm/include/bits/smbios.h | 12 + src/arch/arm/include/bits/stdint.h | 23 ++ src/arch/arm/include/bits/string.h | 60 ++++ src/arch/arm/include/bits/strings.h | 85 ++++++ src/arch/arm/include/bits/tcpip.h | 19 ++ src/arch/arm/include/bits/time.h | 12 + src/arch/arm/include/bits/timer.h | 12 + src/arch/arm/include/bits/uaccess.h | 12 + src/arch/arm/include/bits/uart.h | 12 + src/arch/arm/include/bits/umalloc.h | 12 + src/arch/arm/include/bits/xen.h | 149 ++++++++++ src/arch/arm/include/efi/ipxe/dhcp_arch.h | 46 +++ src/arch/arm/include/gdbmach.h | 45 +++ src/arch/arm/include/ipxe/arm_io.h | 89 ++++++ src/arch/arm/include/ipxe/efi/efiarm_nap.h | 18 ++ src/arch/arm/include/limits.h | 61 ++++ src/arch/arm/include/setjmp.h | 38 +++ src/arch/arm/interface/efi/efiarm_nap.c | 53 ++++ src/arch/arm/libgcc/lldivmod.S | 50 ++++ src/arch/arm/libgcc/llshift.S | 88 ++++++ src/config/defaults/efi.h | 12 +- src/util/efirom.c | 1 + src/util/elf2efi.c | 110 ++++--- 43 files changed, 1879 insertions(+), 49 deletions(-) create mode 100644 src/arch/arm/Makefile create mode 100644 src/arch/arm/Makefile.efi create mode 100644 src/arch/arm/core/arm_bigint.c create mode 100644 src/arch/arm/core/arm_io.c create mode 100644 src/arch/arm/core/setjmp.S create mode 100644 src/arch/arm/include/bits/bigint.h create mode 100644 src/arch/arm/include/bits/bitops.h create mode 100644 src/arch/arm/include/bits/byteswap.h create mode 100644 src/arch/arm/include/bits/compiler.h create mode 100644 src/arch/arm/include/bits/endian.h create mode 100644 src/arch/arm/include/bits/entropy.h create mode 100644 src/arch/arm/include/bits/errfile.h create mode 100644 src/arch/arm/include/bits/hyperv.h create mode 100644 src/arch/arm/include/bits/io.h create mode 100644 src/arch/arm/include/bits/iomap.h create mode 100644 src/arch/arm/include/bits/nap.h create mode 100644 src/arch/arm/include/bits/pci_io.h create mode 100644 src/arch/arm/include/bits/profile.h create mode 100644 src/arch/arm/include/bits/reboot.h create mode 100644 src/arch/arm/include/bits/sanboot.h create mode 100644 src/arch/arm/include/bits/smbios.h create mode 100644 src/arch/arm/include/bits/stdint.h create mode 100644 src/arch/arm/include/bits/string.h create mode 100644 src/arch/arm/include/bits/strings.h create mode 100644 src/arch/arm/include/bits/tcpip.h create mode 100644 src/arch/arm/include/bits/time.h create mode 100644 src/arch/arm/include/bits/timer.h create mode 100644 src/arch/arm/include/bits/uaccess.h create mode 100644 src/arch/arm/include/bits/uart.h create mode 100644 src/arch/arm/include/bits/umalloc.h create mode 100644 src/arch/arm/include/bits/xen.h create mode 100644 src/arch/arm/include/efi/ipxe/dhcp_arch.h create mode 100644 src/arch/arm/include/gdbmach.h create mode 100644 src/arch/arm/include/ipxe/arm_io.h create mode 100644 src/arch/arm/include/ipxe/efi/efiarm_nap.h create mode 100644 src/arch/arm/include/limits.h create mode 100644 src/arch/arm/include/setjmp.h create mode 100644 src/arch/arm/interface/efi/efiarm_nap.c create mode 100644 src/arch/arm/libgcc/lldivmod.S create mode 100644 src/arch/arm/libgcc/llshift.S diff --git a/src/arch/arm/Makefile b/src/arch/arm/Makefile new file mode 100644 index 000000000..f883a64d6 --- /dev/null +++ b/src/arch/arm/Makefile @@ -0,0 +1,25 @@ +# Assembler section type character +# +ASM_TCHAR := % +ASM_TCHAR_OPS := %% + +# ARM-specific directories containing source files +# +SRCDIRS += arch/arm/core +SRCDIRS += arch/arm/libgcc +SRCDIRS += arch/arm/interface/efi + +# ARM-specific flags +# +CFLAGS += -mthumb -mcpu=cortex-a15 -mabi=aapcs -mfloat-abi=soft +CFLAGS += -mword-relocations +ASFLAGS += -mthumb -mcpu=cortex-a15 + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# Include platform-specific Makefile +# +MAKEDEPS += arch/arm/Makefile.$(PLATFORM) +include arch/arm/Makefile.$(PLATFORM) diff --git a/src/arch/arm/Makefile.efi b/src/arch/arm/Makefile.efi new file mode 100644 index 000000000..909c8d21b --- /dev/null +++ b/src/arch/arm/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI32) + +# Specify EFI boot file +# +EFI_BOOT_FILE = bootarm.efi + +# Include generic EFI Makefile +# +MAKEDEPS += Makefile.efi +include Makefile.efi diff --git a/src/arch/arm/core/arm_bigint.c b/src/arch/arm/core/arm_bigint.c new file mode 100644 index 000000000..839bead18 --- /dev/null +++ b/src/arch/arm/core/arm_bigint.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include + +/** @file + * + * Big integer support + */ + +/** + * Multiply big integers + * + * @v multiplicand0 Element 0 of big integer to be multiplied + * @v multiplier0 Element 0 of big integer to be multiplied + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements + */ +void bigint_multiply_raw ( const uint32_t *multiplicand0, + const uint32_t *multiplier0, + uint32_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = + ( ( const void * ) multiplicand0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = + ( ( const void * ) multiplier0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result = + ( ( void * ) result0 ); + unsigned int i; + unsigned int j; + uint32_t multiplicand_element; + uint32_t multiplier_element; + uint32_t *result_elements; + uint32_t discard_low; + uint32_t discard_high; + uint32_t discard_temp; + + /* Zero result */ + memset ( result, 0, sizeof ( *result ) ); + + /* Multiply integers one element at a time */ + for ( i = 0 ; i < size ; i++ ) { + multiplicand_element = multiplicand->element[i]; + for ( j = 0 ; j < size ; j++ ) { + multiplier_element = multiplier->element[j]; + result_elements = &result->element[ i + j ]; + /* Perform a single multiply, and add the + * resulting double-element into the result, + * carrying as necessary. The carry can + * never overflow beyond the end of the + * result, since: + * + * a < 2^{n}, b < 2^{n} => ab < 2^{2n} + */ + __asm__ __volatile__ ( "umull %1, %2, %5, %6\n\t" + "ldr %3, [%0]\n\t" + "adds %3, %1\n\t" + "stmia %0!, {%3}\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, %2\n\t" + "stmia %0!, {%3}\n\t" + "bcc 2f\n\t" + "\n1:\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, #0\n\t" + "stmia %0!, {%3}\n\t" + "bcs 1b\n\t" + "\n2:\n\t" + : "+l" ( result_elements ), + "=l" ( discard_low ), + "=l" ( discard_high ), + "=l" ( discard_temp ), + "+m" ( *result ) + : "l" ( multiplicand_element ), + "l" ( multiplier_element ) + : "cc" ); + } + } +} diff --git a/src/arch/arm/core/arm_io.c b/src/arch/arm/core/arm_io.c new file mode 100644 index 000000000..804014c9a --- /dev/null +++ b/src/arch/arm/core/arm_io.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include + +/** @file + * + * iPXE I/O API for ARM + * + */ + +/** An ARM I/O qword */ +union arm_io_qword { + uint64_t qword; + uint32_t dword[2]; +}; + +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + * + * This is not atomic for ARM. + */ +static uint64_t arm_readq ( volatile uint64_t *io_addr ) { + volatile union arm_io_qword *ptr = + container_of ( io_addr, union arm_io_qword, qword ); + union arm_io_qword tmp; + + tmp.dword[0] = readl ( &ptr->dword[0] ); + tmp.dword[1] = readl ( &ptr->dword[1] ); + return tmp.qword; +} + +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + * + * This is not atomic for ARM. + */ +static void arm_writeq ( uint64_t data, volatile uint64_t *io_addr ) { + volatile union arm_io_qword *ptr = + container_of ( io_addr, union arm_io_qword, qword ); + union arm_io_qword tmp; + + tmp.qword = data; + writel ( tmp.dword[0], &ptr->dword[0] ); + writel ( tmp.dword[1], &ptr->dword[1] ); +} + +PROVIDE_IOAPI_INLINE ( arm, phys_to_bus ); +PROVIDE_IOAPI_INLINE ( arm, bus_to_phys ); +PROVIDE_IOAPI_INLINE ( arm, readb ); +PROVIDE_IOAPI_INLINE ( arm, readw ); +PROVIDE_IOAPI_INLINE ( arm, readl ); +PROVIDE_IOAPI_INLINE ( arm, writeb ); +PROVIDE_IOAPI_INLINE ( arm, writew ); +PROVIDE_IOAPI_INLINE ( arm, writel ); +PROVIDE_IOAPI_INLINE ( arm, iodelay ); +PROVIDE_IOAPI_INLINE ( arm, mb ); +PROVIDE_IOAPI ( arm, readq, arm_readq ); +PROVIDE_IOAPI ( arm, writeq, arm_writeq ); diff --git a/src/arch/arm/core/setjmp.S b/src/arch/arm/core/setjmp.S new file mode 100644 index 000000000..7e7b0fe58 --- /dev/null +++ b/src/arch/arm/core/setjmp.S @@ -0,0 +1,32 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arm + +/* + * Save stack context for non-local goto + */ + .globl setjmp + .type setjmp, %function +setjmp: + /* Store registers */ + stmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr } + /* Return 0 when returning as setjmp() */ + mov r0, #0 + bx lr + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp + .type longjmp, %function +longjmp: + /* Restore registers */ + ldmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr } + /* Force result to non-zero */ + movs r0, r1 + moveq r0, #1 + /* Return to setjmp() caller */ + bx lr + .size longjmp, . - longjmp diff --git a/src/arch/arm/include/bits/bigint.h b/src/arch/arm/include/bits/bigint.h new file mode 100644 index 000000000..103c6c489 --- /dev/null +++ b/src/arch/arm/include/bits/bigint.h @@ -0,0 +1,316 @@ +#ifndef _BITS_BIGINT_H +#define _BITS_BIGINT_H + +/** @file + * + * Big integer support + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Element of a big integer */ +typedef uint32_t bigint_element_t; + +/** + * Initialise big integer + * + * @v value0 Element 0 of big integer to initialise + * @v size Number of elements + * @v data Raw data + * @v len Length of raw data + */ +static inline __attribute__ (( always_inline )) void +bigint_init_raw ( uint32_t *value0, unsigned int size, + const void *data, size_t len ) { + size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + uint8_t *value_byte = ( ( void * ) value0 ); + const uint8_t *data_byte = ( data + len ); + + /* Copy raw data in reverse order, padding with zeros */ + while ( len-- ) + *(value_byte++) = *(--data_byte); + while ( pad_len-- ) + *(value_byte++) = 0; +} + +/** + * Add big integers + * + * @v addend0 Element 0 of big integer to add + * @v value0 Element 0 of big integer to be added to + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_addend; + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_addend_i; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %2, %0, %8, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldmia %0!, {%3}\n\t" + "ldr %4, [%1]\n\t" + "adcs %4, %3\n\t" + "stmia %1!, {%4}\n\t" + "teq %0, %2\n\t" + "bne 1b\n\t" + : "=l" ( discard_addend ), + "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_addend_i ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( addend0 ), "1" ( value0 ), "l" ( size ) + : "cc" ); +} + +/** + * Subtract big integers + * + * @v subtrahend0 Element 0 of big integer to subtract + * @v value0 Element 0 of big integer to be subtracted from + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_subtrahend; + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_subtrahend_i; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "add %2, %0, %8, lsl #2\n\t" + "cmp %2, %0\n\t" /* set CF */ + "\n1:\n\t" + "ldmia %0!, {%3}\n\t" + "ldr %4, [%1]\n\t" + "sbcs %4, %3\n\t" + "stmia %1!, {%4}\n\t" + "teq %0, %2\n\t" + "bne 1b\n\t" + : "=l" ( discard_subtrahend ), + "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_subtrahend_i ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( subtrahend0 ), "1" ( value0 ), + "l" ( size ) + : "cc" ); +} + +/** + * Rotate big integer left + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_rol_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %2, [%0]\n\t" + "adcs %2, %2\n\t" + "stmia %0!, {%2}\n\t" + "teq %0, %1\n\t" + "bne 1b\n\t" + : "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Rotate big integer right + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_ror_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldmdb %1!, {%2}\n\t" + "rrxs %2, %2\n\t" + "str %2, [%1]\n\t" + "teq %0, %1\n\t" + "bne 1b\n\t" + : "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Test if big integer is equal to zero + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret is_zero Big integer is equal to zero + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_zero_raw ( const uint32_t *value0, unsigned int size ) { + const uint32_t *value = value0; + uint32_t value_i; + + do { + value_i = *(value++); + if ( value_i ) + break; + } while ( --size ); + + return ( value_i == 0 ); +} + +/** + * Compare big integers + * + * @v value0 Element 0 of big integer + * @v reference0 Element 0 of reference big integer + * @v size Number of elements + * @ret geq Big integer is greater than or equal to the reference + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_geq_raw ( const uint32_t *value0, const uint32_t *reference0, + unsigned int size ) { + const uint32_t *value = ( value0 + size ); + const uint32_t *reference = ( reference0 + size ); + uint32_t value_i; + uint32_t reference_i; + + do { + value_i = *(--value); + reference_i = *(--reference); + if ( value_i != reference_i ) + break; + } while ( --size ); + + return ( value_i >= reference_i ); +} + +/** + * Test if bit is set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to test + * @ret is_set Bit is set + */ +static inline __attribute__ (( always_inline )) int +bigint_bit_is_set_raw ( const uint32_t *value0, unsigned int size, + unsigned int bit ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( const void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + return ( value->element[index] & ( 1 << subindex ) ); +} + +/** + * Find highest bit set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret max_bit Highest bit set + 1 (or 0 if no bits set) + */ +static inline __attribute__ (( always_inline )) int +bigint_max_set_bit_raw ( const uint32_t *value0, unsigned int size ) { + const uint32_t *value = ( value0 + size ); + int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) ); + uint32_t value_i; + + do { + value_i = *(--value); + max_bit -= ( 32 - fls ( value_i ) ); + if ( value_i ) + break; + } while ( --size ); + + return max_bit; +} + +/** + * Grow big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, + uint32_t *dest0, unsigned int dest_size ) { + unsigned int pad_size = ( dest_size - source_size ); + + memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) ); + memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) ); +} + +/** + * Shrink big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, + uint32_t *dest0, unsigned int dest_size ) { + + memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) ); +} + +/** + * Finalise big integer + * + * @v value0 Element 0 of big integer to finalise + * @v size Number of elements + * @v out Output buffer + * @v len Length of output buffer + */ +static inline __attribute__ (( always_inline )) void +bigint_done_raw ( const uint32_t *value0, unsigned int size __unused, + void *out, size_t len ) { + const uint8_t *value_byte = ( ( const void * ) value0 ); + uint8_t *out_byte = ( out + len ); + + /* Copy raw data in reverse order */ + while ( len-- ) + *(--out_byte) = *(value_byte++); +} + +extern void bigint_multiply_raw ( const uint32_t *multiplicand0, + const uint32_t *multiplier0, + uint32_t *value0, unsigned int size ); + +#endif /* _BITS_BIGINT_H */ diff --git a/src/arch/arm/include/bits/bitops.h b/src/arch/arm/include/bits/bitops.h new file mode 100644 index 000000000..9a5fe14c2 --- /dev/null +++ b/src/arch/arm/include/bits/bitops.h @@ -0,0 +1,100 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * ARM bit operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 32 ); + unsigned int offset = ( bit % 32 ); + volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index ); + uint32_t mask = ( 1UL << offset ); + uint32_t old; + uint32_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldrex %0, %3\n\t" + "orr %1, %0, %4\n\t" + "strex %2, %1, %3\n\t" + "tst %2, %2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ), + "+Q" ( *dword ) + : "r" ( mask ) + : "cc" ); + + return ( old & mask ); +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 32 ); + unsigned int offset = ( bit % 32 ); + volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index ); + uint32_t mask = ( 1UL << offset ); + uint32_t old; + uint32_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldrex %0, %3\n\t" + "bic %1, %0, %4\n\t" + "strex %2, %1, %3\n\t" + "tst %2, %2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ), + "+Q" ( *dword ) + : "r" ( mask ) + : "cc" ); + + return ( old & mask ); +} + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + + test_and_set_bit ( bit, bits ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + + test_and_clear_bit ( bit, bits ); +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/arch/arm/include/bits/byteswap.h b/src/arch/arm/include/bits/byteswap.h new file mode 100644 index 000000000..1fc884bd8 --- /dev/null +++ b/src/arch/arm/include/bits/byteswap.h @@ -0,0 +1,52 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +/** @file + * + * Byte-order swapping functions + * + */ + +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "rev16 %0, %1" : "=l" ( x ) : "l" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_16s ( uint16_t *x ) { + *x = __bswap_variable_16 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "rev %0, %1" : "=l" ( x ) : "l" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_32s ( uint32_t *x ) { + *x = __bswap_variable_32 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + uint32_t in_high = ( x >> 32 ); + uint32_t in_low = ( x & 0xffffffffUL ); + uint32_t out_high = __bswap_variable_32 ( in_low ); + uint32_t out_low = __bswap_variable_32 ( in_high ); + + return ( ( ( ( uint64_t ) out_high ) << 32 ) | + ( ( uint64_t ) out_low ) ); +} + +static inline __attribute__ (( always_inline )) void +__bswap_64s ( uint64_t *x ) { + *x = __bswap_variable_64 ( *x ); +} + +#endif diff --git a/src/arch/arm/include/bits/compiler.h b/src/arch/arm/include/bits/compiler.h new file mode 100644 index 000000000..e420cf922 --- /dev/null +++ b/src/arch/arm/include/bits/compiler.h @@ -0,0 +1,16 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_ARM_NONE + +#ifndef ASSEMBLY + +#define __asmcall +#define __libgcc + +#endif /* ASSEMBLY */ + +#endif /*_BITS_COMPILER_H */ diff --git a/src/arch/arm/include/bits/endian.h b/src/arch/arm/include/bits/endian.h new file mode 100644 index 000000000..4506711ad --- /dev/null +++ b/src/arch/arm/include/bits/endian.h @@ -0,0 +1,13 @@ +#ifndef _BITS_ENDIAN_H +#define _BITS_ENDIAN_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* ARM may be either little-endian or big-endian */ +#ifdef __ARM_BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#endif /* _BITS_ENDIAN_H */ diff --git a/src/arch/arm/include/bits/entropy.h b/src/arch/arm/include/bits/entropy.h new file mode 100644 index 000000000..75fdc90ea --- /dev/null +++ b/src/arch/arm/include/bits/entropy.h @@ -0,0 +1,12 @@ +#ifndef _BITS_ENTROPY_H +#define _BITS_ENTROPY_H + +/** @file + * + * ARM-specific entropy API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_ENTROPY_H */ diff --git a/src/arch/arm/include/bits/errfile.h b/src/arch/arm/include/bits/errfile.h new file mode 100644 index 000000000..65f7f719b --- /dev/null +++ b/src/arch/arm/include/bits/errfile.h @@ -0,0 +1,19 @@ +#ifndef _BITS_ERRFILE_H +#define _BITS_ERRFILE_H + +/** @file + * + * ARM-specific error file identifiers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @addtogroup errfile Error file identifiers + * @{ + */ + +/** @} */ + +#endif /* _BITS_ERRFILE_H */ diff --git a/src/arch/arm/include/bits/hyperv.h b/src/arch/arm/include/bits/hyperv.h new file mode 100644 index 000000000..f0e0c8793 --- /dev/null +++ b/src/arch/arm/include/bits/hyperv.h @@ -0,0 +1,12 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_HYPERV_H */ diff --git a/src/arch/arm/include/bits/io.h b/src/arch/arm/include/bits/io.h new file mode 100644 index 000000000..90f6455ec --- /dev/null +++ b/src/arch/arm/include/bits/io.h @@ -0,0 +1,14 @@ +#ifndef _BITS_IO_H +#define _BITS_IO_H + +/** @file + * + * ARM-specific I/O API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_IO_H */ diff --git a/src/arch/arm/include/bits/iomap.h b/src/arch/arm/include/bits/iomap.h new file mode 100644 index 000000000..ae953c450 --- /dev/null +++ b/src/arch/arm/include/bits/iomap.h @@ -0,0 +1,12 @@ +#ifndef _BITS_IOMAP_H +#define _BITS_IOMAP_H + +/** @file + * + * ARM-specific I/O mapping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_IOMAP_H */ diff --git a/src/arch/arm/include/bits/nap.h b/src/arch/arm/include/bits/nap.h new file mode 100644 index 000000000..e30a7146b --- /dev/null +++ b/src/arch/arm/include/bits/nap.h @@ -0,0 +1,14 @@ +#ifndef _BITS_NAP_H +#define _BITS_NAP_H + +/** @file + * + * ARM-specific CPU sleeping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_MAP_H */ diff --git a/src/arch/arm/include/bits/pci_io.h b/src/arch/arm/include/bits/pci_io.h new file mode 100644 index 000000000..fba0eb979 --- /dev/null +++ b/src/arch/arm/include/bits/pci_io.h @@ -0,0 +1,14 @@ +#ifndef _BITS_PCI_IO_H +#define _BITS_PCI_IO_H + +/** @file + * + * ARM PCI I/O API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_PCI_IO_H */ diff --git a/src/arch/arm/include/bits/profile.h b/src/arch/arm/include/bits/profile.h new file mode 100644 index 000000000..2b15d1604 --- /dev/null +++ b/src/arch/arm/include/bits/profile.h @@ -0,0 +1,30 @@ +#ifndef _BITS_PROFILE_H +#define _BITS_PROFILE_H + +/** @file + * + * Profiling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) uint64_t +profile_timestamp ( void ) { + uint32_t cycles; + + /* Read cycle counter */ + __asm__ __volatile__ ( "mcr p15, 0, %1, c9, c12, 0\n\t" + "mrc p15, 0, %0, c9, c13, 0\n\t" + : "=r" ( cycles ) : "r" ( 1 ) ); + return cycles; +} + +#endif /* _BITS_PROFILE_H */ diff --git a/src/arch/arm/include/bits/reboot.h b/src/arch/arm/include/bits/reboot.h new file mode 100644 index 000000000..88c50250c --- /dev/null +++ b/src/arch/arm/include/bits/reboot.h @@ -0,0 +1,12 @@ +#ifndef _BITS_REBOOT_H +#define _BITS_REBOOT_H + +/** @file + * + * ARM-specific reboot API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_REBOOT_H */ diff --git a/src/arch/arm/include/bits/sanboot.h b/src/arch/arm/include/bits/sanboot.h new file mode 100644 index 000000000..abd4c79a5 --- /dev/null +++ b/src/arch/arm/include/bits/sanboot.h @@ -0,0 +1,12 @@ +#ifndef _BITS_SANBOOT_H +#define _BITS_SANBOOT_H + +/** @file + * + * ARM-specific sanboot API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_SANBOOT_H */ diff --git a/src/arch/arm/include/bits/smbios.h b/src/arch/arm/include/bits/smbios.h new file mode 100644 index 000000000..d94218116 --- /dev/null +++ b/src/arch/arm/include/bits/smbios.h @@ -0,0 +1,12 @@ +#ifndef _BITS_SMBIOS_H +#define _BITS_SMBIOS_H + +/** @file + * + * ARM-specific SMBIOS API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_SMBIOS_H */ diff --git a/src/arch/arm/include/bits/stdint.h b/src/arch/arm/include/bits/stdint.h new file mode 100644 index 000000000..fe1f9946a --- /dev/null +++ b/src/arch/arm/include/bits/stdint.h @@ -0,0 +1,23 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +typedef __SIZE_TYPE__ size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/src/arch/arm/include/bits/string.h b/src/arch/arm/include/bits/string.h new file mode 100644 index 000000000..5b1c1505d --- /dev/null +++ b/src/arch/arm/include/bits/string.h @@ -0,0 +1,60 @@ +#ifndef BITS_STRING_H +#define BITS_STRING_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * String functions + * + */ + +/** + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int character, size_t len ) { + + /* Not yet optimised */ + generic_memset ( dest, character, len ); + return dest; +} + +/** + * Copy memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memcpy ( void *dest, const void *src, size_t len ) { + + /* Not yet optimised */ + generic_memcpy ( dest, src, len ); + return dest; +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memmove ( void *dest, const void *src, size_t len ) { + + /* Not yet optimised */ + generic_memmove ( dest, src, len ); + return dest; +} + +#endif /* BITS_STRING_H */ diff --git a/src/arch/arm/include/bits/strings.h b/src/arch/arm/include/bits/strings.h new file mode 100644 index 000000000..adbd5f4b4 --- /dev/null +++ b/src/arch/arm/include/bits/strings.h @@ -0,0 +1,85 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + unsigned long bits = value; + unsigned long lsb; + unsigned int lz; + + /* Extract least significant set bit */ + lsb = ( bits & -bits ); + + /* Count number of leading zeroes before LSB */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) ); + + return ( 32 - lz ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( low ) { + return ( __ffsl ( low ) ); + } else if ( high ) { + return ( 32 + __ffsl ( high ) ); + } else { + return 0; + } +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + unsigned int lz; + + /* Count number of leading zeroes */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) ); + + return ( 32 - lz ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( high ) { + return ( 32 + __flsl ( high ) ); + } else if ( low ) { + return ( __flsl ( low ) ); + } else { + return 0; + } +} + +#endif /* _BITS_STRINGS_H */ diff --git a/src/arch/arm/include/bits/tcpip.h b/src/arch/arm/include/bits/tcpip.h new file mode 100644 index 000000000..fc3c5b3ff --- /dev/null +++ b/src/arch/arm/include/bits/tcpip.h @@ -0,0 +1,19 @@ +#ifndef _BITS_TCPIP_H +#define _BITS_TCPIP_H + +/** @file + * + * Transport-network layer interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline )) uint16_t +tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ) { + + /* Not yet optimised */ + return generic_tcpip_continue_chksum ( partial, data, len ); +} + +#endif /* _BITS_TCPIP_H */ diff --git a/src/arch/arm/include/bits/time.h b/src/arch/arm/include/bits/time.h new file mode 100644 index 000000000..724d8b932 --- /dev/null +++ b/src/arch/arm/include/bits/time.h @@ -0,0 +1,12 @@ +#ifndef _BITS_TIME_H +#define _BITS_TIME_H + +/** @file + * + * ARM-specific time API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_TIME_H */ diff --git a/src/arch/arm/include/bits/timer.h b/src/arch/arm/include/bits/timer.h new file mode 100644 index 000000000..64e7d31df --- /dev/null +++ b/src/arch/arm/include/bits/timer.h @@ -0,0 +1,12 @@ +#ifndef _BITS_TIMER_H +#define _BITS_TIMER_H + +/** @file + * + * ARM-specific timer API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_TIMER_H */ diff --git a/src/arch/arm/include/bits/uaccess.h b/src/arch/arm/include/bits/uaccess.h new file mode 100644 index 000000000..87f11509c --- /dev/null +++ b/src/arch/arm/include/bits/uaccess.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UACCESS_H +#define _BITS_UACCESS_H + +/** @file + * + * ARM-specific user access API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UACCESS_H */ diff --git a/src/arch/arm/include/bits/uart.h b/src/arch/arm/include/bits/uart.h new file mode 100644 index 000000000..6f85975f7 --- /dev/null +++ b/src/arch/arm/include/bits/uart.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UART_H +#define _BITS_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UART_H */ diff --git a/src/arch/arm/include/bits/umalloc.h b/src/arch/arm/include/bits/umalloc.h new file mode 100644 index 000000000..27970d7b2 --- /dev/null +++ b/src/arch/arm/include/bits/umalloc.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UMALLOC_H +#define _BITS_UMALLOC_H + +/** @file + * + * ARM-specific user memory allocation API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UMALLOC_H */ diff --git a/src/arch/arm/include/bits/xen.h b/src/arch/arm/include/bits/xen.h new file mode 100644 index 000000000..c3808a733 --- /dev/null +++ b/src/arch/arm/include/bits/xen.h @@ -0,0 +1,149 @@ +#ifndef _BITS_XEN_H +#define _BITS_XEN_H + +/** @file + * + * Xen interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Hypercall registers */ +#define XEN_HC "r12" +#define XEN_REG1 "r0" +#define XEN_REG2 "r1" +#define XEN_REG3 "r2" +#define XEN_REG4 "r3" +#define XEN_REG5 "r4" + +/** + * Issue hypercall with one argument + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_1 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + + __asm__ __volatile__ ( "hvc %1" + : "+r" ( reg1 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with two arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_2 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + + __asm__ __volatile__ ( "hvc %2" + : "+r" ( reg1 ), "+r" ( reg2 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with three arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_3 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + + __asm__ __volatile__ ( "hvc %3" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with four arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_4 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + + __asm__ __volatile__ ( "hvc %4" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ), + "+r" ( reg4 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with five arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @v arg5 Fifth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_5 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + register unsigned long reg5 asm ( XEN_REG5 ) = arg5; + + __asm__ __volatile__ ( "hvc %5" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ), + "+r" ( reg4 ), "+r" ( reg5 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +#endif /* _BITS_XEN_H */ diff --git a/src/arch/arm/include/efi/ipxe/dhcp_arch.h b/src/arch/arm/include/efi/ipxe/dhcp_arch.h new file mode 100644 index 000000000..f403d4ce8 --- /dev/null +++ b/src/arch/arm/include/efi/ipxe/dhcp_arch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#define DHCP_ARCH_VENDOR_CLASS_ID \ + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) + +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_EFI ) + +#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) + +#endif diff --git a/src/arch/arm/include/gdbmach.h b/src/arch/arm/include/gdbmach.h new file mode 100644 index 000000000..cd152eedd --- /dev/null +++ b/src/arch/arm/include/gdbmach.h @@ -0,0 +1,45 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include + +typedef unsigned long gdbreg_t; + +/* Register snapshot */ +enum { + /* Not yet implemented */ + GDBMACH_NREGS, +}; + +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) step; +} + +static inline void gdbmach_breakpoint ( void ) { + /* Not yet implemented */ +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); +extern void gdbmach_init ( void ); + +#endif /* GDBMACH_H */ diff --git a/src/arch/arm/include/ipxe/arm_io.h b/src/arch/arm/include/ipxe/arm_io.h new file mode 100644 index 000000000..e13de15f4 --- /dev/null +++ b/src/arch/arm/include/ipxe/arm_io.h @@ -0,0 +1,89 @@ +#ifndef _IPXE_ARM_IO_H +#define _IPXE_ARM_IO_H + +/** @file + * + * iPXE I/O API for ARM + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOAPI_ARM +#define IOAPI_PREFIX_arm +#else +#define IOAPI_PREFIX_arm __arm_ +#endif + +/* + * Memory space mappings + * + */ + +/** Page shift */ +#define PAGE_SHIFT 12 + +/* + * Physical<->Bus address mappings + * + */ + +static inline __always_inline unsigned long +IOAPI_INLINE ( arm, phys_to_bus ) ( unsigned long phys_addr ) { + return phys_addr; +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) { + return bus_addr; +} + +/* + * MMIO reads and writes up to native word size + * + */ + +#define ARM_READX( _api_func, _type, _insn_suffix ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( arm, _api_func ) ( volatile _type *io_addr ) { \ + _type data; \ + __asm__ __volatile__ ( "ldr" _insn_suffix " %0, %1" \ + : "=r" ( data ) : "Qo" ( *io_addr ) ); \ + return data; \ +} +ARM_READX ( readb, uint8_t, "b" ); +ARM_READX ( readw, uint16_t, "h" ); +ARM_READX ( readl, uint32_t, "" ); + +#define ARM_WRITEX( _api_func, _type, _insn_suffix ) \ +static inline __always_inline void \ +IOAPI_INLINE ( arm, _api_func ) ( _type data, \ + volatile _type *io_addr ) { \ + __asm__ __volatile__ ( "str" _insn_suffix " %0, %1" \ + : : "r" ( data ), "Qo" ( *io_addr ) ); \ +} +ARM_WRITEX ( writeb, uint8_t, "b" ); +ARM_WRITEX ( writew, uint16_t, "h" ); +ARM_WRITEX ( writel, uint32_t, "" ); + +/* + * Slow down I/O + * + */ + +static inline __always_inline void +IOAPI_INLINE ( arm, iodelay ) ( void ) { + /* Nothing to do */ +} + +/* + * Memory barrier + * + */ + +static inline __always_inline void +IOAPI_INLINE ( arm, mb ) ( void ) { + __asm__ __volatile__ ( "dmb" ); +} + +#endif /* _IPXE_ARM_IO_H */ diff --git a/src/arch/arm/include/ipxe/efi/efiarm_nap.h b/src/arch/arm/include/ipxe/efi/efiarm_nap.h new file mode 100644 index 000000000..dcbdd3e20 --- /dev/null +++ b/src/arch/arm/include/ipxe/efi/efiarm_nap.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFIARM_NAP_H +#define _IPXE_EFIARM_NAP_H + +/** @file + * + * EFI CPU sleeping + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef NAP_EFIARM +#define NAP_PREFIX_efiarm +#else +#define NAP_PREFIX_efiarm __efiarm_ +#endif + +#endif /* _IPXE_EFIARM_NAP_H */ diff --git a/src/arch/arm/include/limits.h b/src/arch/arm/include/limits.h new file mode 100644 index 000000000..bb48b75ab --- /dev/null +++ b/src/arch/arm/include/limits.h @@ -0,0 +1,61 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 4294967295UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/arm/include/setjmp.h b/src/arch/arm/include/setjmp.h new file mode 100644 index 000000000..4828b47a2 --- /dev/null +++ b/src/arch/arm/include/setjmp.h @@ -0,0 +1,38 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A jump buffer */ +typedef struct { + /** Saved r4 */ + uint32_t r4; + /** Saved r5 */ + uint32_t r5; + /** Saved r6 */ + uint32_t r6; + /** Saved r7 */ + uint32_t r7; + /** Saved r8 */ + uint32_t r8; + /** Saved r9 */ + uint32_t r9; + /** Saved r10 */ + uint32_t r10; + /** Saved frame pointer (r11) */ + uint32_t fp; + /** Saved stack pointer (r13) */ + uint32_t sp; + /** Saved link register (r14) */ + uint32_t lr; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/src/arch/arm/interface/efi/efiarm_nap.c b/src/arch/arm/interface/efi/efiarm_nap.c new file mode 100644 index 000000000..9ed638e9a --- /dev/null +++ b/src/arch/arm/interface/efi/efiarm_nap.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include + +/** @file + * + * iPXE CPU sleeping API for EFI + * + */ + +/** + * Sleep until next interrupt + * + */ +static void efiarm_cpu_nap ( void ) { + /* + * I can't find any EFI API that allows us to put the CPU to + * sleep. The CpuSleep() function is defined in CpuLib.h, but + * isn't part of any exposed protocol so we have no way to + * call it. + * + * The EFI shell doesn't seem to bother sleeping the CPU; it + * just sits there idly burning power. + * + */ + __asm__ __volatile__ ( "wfi" ); +} + +PROVIDE_NAP ( efiarm, cpu_nap, efiarm_cpu_nap ); diff --git a/src/arch/arm/libgcc/lldivmod.S b/src/arch/arm/libgcc/lldivmod.S new file mode 100644 index 000000000..910be4b78 --- /dev/null +++ b/src/arch/arm/libgcc/lldivmod.S @@ -0,0 +1,50 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .thumb + +/** + * Unsigned long long division + * + * @v r1:r0 Dividend + * @v r3:r2 Divisor + * @ret r1:r0 Quotient + * @ret r3:r2 Remainder + */ + .section ".text.__aeabi_uldivmod", "ax", %progbits + .globl __aeabi_uldivmod + .type __aeabi_uldivmod, %function +__aeabi_uldivmod: + /* Allocate stack space for remainder and pointer to remainder */ + push {r0, r1, r2, r3, r4, lr} + /* Call __udivmoddi4() */ + add r4, sp, #8 + str r4, [sp] + bl __udivmoddi4 + /* Retrieve remainder and return */ + add sp, sp, #8 + pop {r2, r3, r4, pc} + .size __aeabi_uldivmod, . - __aeabi_uldivmod + +/** + * Signed long long division + * + * @v r1:r0 Dividend + * @v r3:r2 Divisor + * @ret r1:r0 Quotient + * @ret r3:r2 Remainder + */ + .section ".text.__aeabi_ldivmod", "ax", %progbits + .globl __aeabi_ldivmod + .type __aeabi_ldivmod, %function +__aeabi_ldivmod: + /* Allocate stack space for remainder and pointer to remainder */ + push {r0, r1, r2, r3, r4, lr} + /* Call __divmoddi4() */ + add r4, sp, #8 + str r4, [sp] + bl __divmoddi4 + /* Retrieve remainder and return */ + add sp, sp, #8 + pop {r2, r3, r4, pc} + .size __aeabi_ldivmod, . - __aeabi_ldivmod diff --git a/src/arch/arm/libgcc/llshift.S b/src/arch/arm/libgcc/llshift.S new file mode 100644 index 000000000..cc16e2615 --- /dev/null +++ b/src/arch/arm/libgcc/llshift.S @@ -0,0 +1,88 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arm + +/** + * Logical shift left + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_llsl", "ax", %progbits + .globl __aeabi_llsl + .type __aeabi_llsl, %function +__aeabi_llsl: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * high = ( low << ( shift - 32 ) ) + */ + movpl r1, r0, lsl r3 + /* If shift < 32, then + * high = ( ( high << shift ) | ( low >> ( 32 - shift ) ) ) + */ + movmi r1, r1, lsl r2 + rsbmi r3, r2, #32 + orrmi r1, r1, r0, lsr r3 + /* low = ( low << shift ) */ + mov r0, r0, lsl r2 + bx lr + .size __aeabi_llsl, . - __aeabi_llsl + +/** + * Logical shift right + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_llsr", "ax", %progbits + .globl __aeabi_llsr + .type __aeabi_llsr, %function +__aeabi_llsr: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * low = ( high >> ( shift - 32 ) ) + */ + movpl r0, r1, lsr r3 + /* If shift < 32, then + * low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) ) + */ + movmi r0, r0, lsr r2 + rsbmi r3, r2, #32 + orrmi r0, r0, r1, lsl r3 + /* high = ( high >> shift ) */ + mov r1, r1, lsr r2 + bx lr + .size __aeabi_llsr, . - __aeabi_llsr + +/** + * Arithmetic shift right + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_lasr", "ax", %progbits + .globl __aeabi_lasr + .type __aeabi_lasr, %function +__aeabi_lasr: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * low = ( high >> ( shift - 32 ) ) + */ + movpl r0, r1, asr r3 + /* If shift < 32, then + * low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) ) + */ + movmi r0, r0, lsr r2 + rsbmi r3, r2, #32 + orrmi r0, r0, r1, lsl r3 + /* high = ( high >> shift ) */ + mov r1, r1, asr r2 + bx lr + .size __aeabi_lasr, . - __aeabi_lasr diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index ea9c31e27..8d3a8bf23 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -10,12 +10,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_EFI -#define IOAPI_X86 #define IOMAP_VIRT #define PCIAPI_EFI #define CONSOLE_EFI #define TIMER_EFI -#define NAP_EFIX86 #define UMALLOC_EFI #define SMBIOS_EFI #define SANBOOT_NULL @@ -35,6 +33,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define USB_EFI /* Provide EFI_USB_IO_PROTOCOL interface */ #define REBOOT_CMD /* Reboot command */ + +#if defined ( __i386__ ) || defined ( __x86_64__ ) +#define IOAPI_X86 +#define NAP_EFIX86 #define CPUID_CMD /* x86 CPU feature detection command */ +#endif + +#if defined ( __arm__ ) +#define IOAPI_ARM +#define NAP_EFIARM +#endif #endif /* CONFIG_DEFAULTS_EFI_H */ diff --git a/src/util/efirom.c b/src/util/efirom.c index a982c19ae..72829cbd4 100644 --- a/src/util/efirom.c +++ b/src/util/efirom.c @@ -81,6 +81,7 @@ static void read_pe_info ( void *pe, uint16_t *machine, *machine = nt->nt32.FileHeader.Machine; switch ( *machine ) { case EFI_IMAGE_MACHINE_IA32: + case EFI_IMAGE_MACHINE_ARMTHUMB_MIXED: *subsystem = nt->nt32.OptionalHeader.Subsystem; break; case EFI_IMAGE_MACHINE_X64: diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 5e1050e3d..01ddfd681 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -70,20 +70,14 @@ #endif -#define EFI_FILE_ALIGN 0x20 +#define ELF_MREL( mach, type ) ( (mach) | ( (type) << 16 ) ) -struct elf_machine { - unsigned int pe_machine; - unsigned int r_none; - unsigned int r_abs; - unsigned int r_pcrel; -}; +#define EFI_FILE_ALIGN 0x20 struct elf_file { void *data; size_t len; const Elf_Ehdr *ehdr; - struct elf_machine *machine; }; struct pe_section { @@ -136,20 +130,6 @@ static struct pe_header efi_pe_header = { }, }; -static struct elf_machine machine_i386 = { - .pe_machine = EFI_IMAGE_MACHINE_IA32, - .r_none = R_386_NONE, - .r_abs = R_386_32, - .r_pcrel = R_386_PC32, -}; - -static struct elf_machine machine_x86_64 = { - .pe_machine = EFI_IMAGE_MACHINE_X64, - .r_none = R_X86_64_NONE, - .r_abs = R_X86_64_64, - .r_pcrel = R_X86_64_PC32, -}; - /** Command-line options */ struct options { unsigned int subsystem; @@ -355,19 +335,6 @@ static void read_elf_file ( const char *name, struct elf_file *elf ) { exit ( 1 ); } } - - /* Identify architecture */ - switch ( ehdr->e_machine ) { - case EM_386: - elf->machine = &machine_i386; - break; - case EM_X86_64: - elf->machine = &machine_x86_64; - break; - default: - eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); - exit ( 1 ); - } } /** @@ -415,6 +382,36 @@ static const char * elf_string ( struct elf_file *elf, unsigned int section, return string; } +/** + * Set machine architecture + * + * @v elf ELF file + * @v pe_header PE file header + */ +static void set_machine ( struct elf_file *elf, struct pe_header *pe_header ) { + const Elf_Ehdr *ehdr = elf->ehdr; + uint16_t machine; + + /* Identify machine architecture */ + switch ( ehdr->e_machine ) { + case EM_386: + machine = EFI_IMAGE_MACHINE_IA32; + break; + case EM_X86_64: + machine = EFI_IMAGE_MACHINE_X64; + break; + case EM_ARM: + machine = EFI_IMAGE_MACHINE_ARMTHUMB_MIXED; + break; + default: + eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); + exit ( 1 ); + } + + /* Set machine architecture */ + pe_header->nt.FileHeader.Machine = machine; +} + /** * Process section * @@ -568,6 +565,7 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, const Elf_Rel *rel, struct pe_relocs **pe_reltab ) { unsigned int type = ELF_R_TYPE ( rel->r_info ); unsigned int sym = ELF_R_SYM ( rel->r_info ); + unsigned int mrel = ELF_MREL ( elf->ehdr->e_machine, type ); size_t offset = ( shdr->sh_addr + rel->r_offset ); /* Look up symbol and process relocation */ @@ -579,18 +577,36 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ - } else if ( type == elf->machine->r_none ) { - /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ - } else if ( type == elf->machine->r_abs ) { - /* Generate an 8-byte or 4-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, sizeof ( Elf_Addr ) ); - } else if ( type == elf->machine->r_pcrel ) { - /* Skip PC-relative relocations; all relative offsets - * remain unaltered when the object is loaded. - */ } else { - eprintf ( "Unrecognised relocation type %d\n", type ); - exit ( 1 ); + switch ( mrel ) { + case ELF_MREL ( EM_386, R_386_NONE ) : + case ELF_MREL ( EM_ARM, R_ARM_NONE ) : + case ELF_MREL ( EM_X86_64, R_X86_64_NONE ) : + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ + break; + case ELF_MREL ( EM_386, R_386_32 ) : + case ELF_MREL ( EM_ARM, R_ARM_ABS32 ) : + /* Generate a 4-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 4 ); + break; + case ELF_MREL ( EM_X86_64, R_X86_64_64 ) : + /* Generate an 8-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 8 ); + break; + case ELF_MREL ( EM_386, R_386_PC32 ) : + case ELF_MREL ( EM_ARM, R_ARM_CALL ) : + case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) : + case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) : + case ELF_MREL ( EM_X86_64, R_X86_64_PC32 ) : + /* Skip PC-relative relocations; all relative + * offsets remain unaltered when the object is + * loaded. + */ + break; + default: + eprintf ( "Unrecognised relocation type %d\n", type ); + exit ( 1 ); + } } } @@ -835,7 +851,7 @@ static void elf2pe ( const char *elf_name, const char *pe_name, /* Initialise the PE header */ memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); - pe_header.nt.FileHeader.Machine = elf.machine->pe_machine; + set_machine ( &elf, &pe_header ); pe_header.nt.OptionalHeader.AddressOfEntryPoint = elf.ehdr->e_entry; pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; From 2a187f480ecc9d7943a53658e748bb5e60104788 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 7 May 2016 23:55:28 +0100 Subject: [PATCH 212/591] [arm] Avoid instruction references to symbols defined via ".equ" When building for 64-bit ARM, some symbol references may be resolved via an "adrp" instruction (to obtain the start of the 4kB page containing the symbol) and a separate 12-bit offset. For example (taken from the GNU assembler documentation): adrp x0, foo ldr x0, [x0, #:lo12:foo] We occasionally refer to symbols defined via mechanisms that are not directly visible to gcc. For example: extern char some_magic_symbol[]; __asm__ ( ".equ some_magic_symbol, some_magic_expression" ); The subsequent use of the ":lo12:" prefix on such magically-defined symbols triggers an assertion failure in the assembler. This problem seems to affect only "private_key_len" in the current codebase. Fix by storing this value as static data; this avoids the need to provide the value as a literal within the instruction stream, and so avoids the problematic use of the ":lo12:" prefix. Signed-off-by: Michael Brown --- src/crypto/privkey.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/crypto/privkey.c b/src/crypto/privkey.c index 0b10e9cf8..7ef04880f 100644 --- a/src/crypto/privkey.c +++ b/src/crypto/privkey.c @@ -69,6 +69,12 @@ struct asn1_cursor private_key = { .len = ( ( size_t ) private_key_len ), }; +/** Default private key */ +static struct asn1_cursor default_private_key = { + .data = private_key_data, + .len = ( ( size_t ) private_key_len ), +}; + /** Private key setting */ static struct setting privkey_setting __setting ( SETTING_CRYPTO, privkey ) = { .name = "privkey", @@ -92,8 +98,8 @@ static int privkey_apply_settings ( void ) { if ( ALLOW_KEY_OVERRIDE ) { /* Restore default private key */ - private_key.data = private_key_data; - private_key.len = ( ( size_t ) private_key_len ); + memcpy ( &private_key, &default_private_key, + sizeof ( private_key ) ); /* Fetch new private key, if any */ free ( key_data ); From edea3a434ccae8dc980c715949287c9ba63babf5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 8 May 2016 00:18:35 +0100 Subject: [PATCH 213/591] [arm] Split out 32-bit-specific code to arch/arm32 Signed-off-by: Michael Brown --- src/arch/arm/Makefile | 21 +++------------ src/arch/arm/Makefile.efi | 8 ------ src/arch/arm/core/arm_io.c | 26 +++++++++---------- src/arch/arm32/Makefile | 23 ++++++++++++++++ src/arch/arm32/Makefile.efi | 14 ++++++++++ .../core/arm32_bigint.c} | 0 src/arch/{arm => arm32}/core/setjmp.S | 0 src/arch/{arm => arm32}/include/bits/bigint.h | 0 src/arch/{arm => arm32}/include/bits/bitops.h | 0 .../{arm => arm32}/include/bits/byteswap.h | 0 .../{arm => arm32}/include/bits/compiler.h | 0 .../{arm => arm32}/include/bits/profile.h | 0 src/arch/{arm => arm32}/include/bits/stdint.h | 0 .../{arm => arm32}/include/bits/strings.h | 0 .../include/efi/ipxe/dhcp_arch.h | 0 src/arch/{arm => arm32}/include/gdbmach.h | 0 src/arch/{arm => arm32}/include/limits.h | 0 src/arch/{arm => arm32}/include/setjmp.h | 0 src/arch/{arm => arm32}/libgcc/lldivmod.S | 0 src/arch/{arm => arm32}/libgcc/llshift.S | 0 20 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 src/arch/arm32/Makefile create mode 100644 src/arch/arm32/Makefile.efi rename src/arch/{arm/core/arm_bigint.c => arm32/core/arm32_bigint.c} (100%) rename src/arch/{arm => arm32}/core/setjmp.S (100%) rename src/arch/{arm => arm32}/include/bits/bigint.h (100%) rename src/arch/{arm => arm32}/include/bits/bitops.h (100%) rename src/arch/{arm => arm32}/include/bits/byteswap.h (100%) rename src/arch/{arm => arm32}/include/bits/compiler.h (100%) rename src/arch/{arm => arm32}/include/bits/profile.h (100%) rename src/arch/{arm => arm32}/include/bits/stdint.h (100%) rename src/arch/{arm => arm32}/include/bits/strings.h (100%) rename src/arch/{arm => arm32}/include/efi/ipxe/dhcp_arch.h (100%) rename src/arch/{arm => arm32}/include/gdbmach.h (100%) rename src/arch/{arm => arm32}/include/limits.h (100%) rename src/arch/{arm => arm32}/include/setjmp.h (100%) rename src/arch/{arm => arm32}/libgcc/lldivmod.S (100%) rename src/arch/{arm => arm32}/libgcc/llshift.S (100%) diff --git a/src/arch/arm/Makefile b/src/arch/arm/Makefile index f883a64d6..3cee5f3ac 100644 --- a/src/arch/arm/Makefile +++ b/src/arch/arm/Makefile @@ -3,23 +3,10 @@ ASM_TCHAR := % ASM_TCHAR_OPS := %% +# Include common ARM headers +# +INCDIRS += arch/arm/include + # ARM-specific directories containing source files # -SRCDIRS += arch/arm/core -SRCDIRS += arch/arm/libgcc SRCDIRS += arch/arm/interface/efi - -# ARM-specific flags -# -CFLAGS += -mthumb -mcpu=cortex-a15 -mabi=aapcs -mfloat-abi=soft -CFLAGS += -mword-relocations -ASFLAGS += -mthumb -mcpu=cortex-a15 - -# EFI requires -fshort-wchar, and nothing else currently uses wchar_t -# -CFLAGS += -fshort-wchar - -# Include platform-specific Makefile -# -MAKEDEPS += arch/arm/Makefile.$(PLATFORM) -include arch/arm/Makefile.$(PLATFORM) diff --git a/src/arch/arm/Makefile.efi b/src/arch/arm/Makefile.efi index 909c8d21b..f04be425b 100644 --- a/src/arch/arm/Makefile.efi +++ b/src/arch/arm/Makefile.efi @@ -1,13 +1,5 @@ # -*- makefile -*- : Force emacs to use Makefile mode -# Specify EFI image builder -# -ELF2EFI = $(ELF2EFI32) - -# Specify EFI boot file -# -EFI_BOOT_FILE = bootarm.efi - # Include generic EFI Makefile # MAKEDEPS += Makefile.efi diff --git a/src/arch/arm/core/arm_io.c b/src/arch/arm/core/arm_io.c index 804014c9a..4bc47a5c6 100644 --- a/src/arch/arm/core/arm_io.c +++ b/src/arch/arm/core/arm_io.c @@ -33,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** An ARM I/O qword */ -union arm_io_qword { +union arm32_io_qword { uint64_t qword; uint32_t dword[2]; }; @@ -44,12 +44,12 @@ union arm_io_qword { * @v io_addr I/O address * @ret data Value read * - * This is not atomic for ARM. + * This is not atomic for ARM32. */ -static uint64_t arm_readq ( volatile uint64_t *io_addr ) { - volatile union arm_io_qword *ptr = - container_of ( io_addr, union arm_io_qword, qword ); - union arm_io_qword tmp; +static uint64_t arm32_readq ( volatile uint64_t *io_addr ) { + volatile union arm32_io_qword *ptr = + container_of ( io_addr, union arm32_io_qword, qword ); + union arm32_io_qword tmp; tmp.dword[0] = readl ( &ptr->dword[0] ); tmp.dword[1] = readl ( &ptr->dword[1] ); @@ -62,12 +62,12 @@ static uint64_t arm_readq ( volatile uint64_t *io_addr ) { * @v data Value to write * @v io_addr I/O address * - * This is not atomic for ARM. + * This is not atomic for ARM32. */ -static void arm_writeq ( uint64_t data, volatile uint64_t *io_addr ) { - volatile union arm_io_qword *ptr = - container_of ( io_addr, union arm_io_qword, qword ); - union arm_io_qword tmp; +static void arm32_writeq ( uint64_t data, volatile uint64_t *io_addr ) { + volatile union arm32_io_qword *ptr = + container_of ( io_addr, union arm32_io_qword, qword ); + union arm32_io_qword tmp; tmp.qword = data; writel ( tmp.dword[0], &ptr->dword[0] ); @@ -84,5 +84,5 @@ PROVIDE_IOAPI_INLINE ( arm, writew ); PROVIDE_IOAPI_INLINE ( arm, writel ); PROVIDE_IOAPI_INLINE ( arm, iodelay ); PROVIDE_IOAPI_INLINE ( arm, mb ); -PROVIDE_IOAPI ( arm, readq, arm_readq ); -PROVIDE_IOAPI ( arm, writeq, arm_writeq ); +PROVIDE_IOAPI ( arm, readq, arm32_readq ); +PROVIDE_IOAPI ( arm, writeq, arm32_writeq ); diff --git a/src/arch/arm32/Makefile b/src/arch/arm32/Makefile new file mode 100644 index 000000000..3a7c09230 --- /dev/null +++ b/src/arch/arm32/Makefile @@ -0,0 +1,23 @@ +# ARM32-specific directories containing source files +# +SRCDIRS += arch/arm32/core +SRCDIRS += arch/arm32/libgcc + +# ARM32-specific flags +# +CFLAGS += -mthumb -mcpu=cortex-a15 -mabi=aapcs -mfloat-abi=soft +CFLAGS += -mword-relocations +ASFLAGS += -mthumb -mcpu=cortex-a15 + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# Include common ARM Makefile +MAKEDEPS += arch/arm/Makefile +include arch/arm/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/arm32/Makefile.$(PLATFORM) +include arch/arm32/Makefile.$(PLATFORM) diff --git a/src/arch/arm32/Makefile.efi b/src/arch/arm32/Makefile.efi new file mode 100644 index 000000000..a06354f1d --- /dev/null +++ b/src/arch/arm32/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI32) + +# Specify EFI boot file +# +EFI_BOOT_FILE = bootarm.efi + +# Include generic EFI Makefile +# +MAKEDEPS += arch/arm/Makefile.efi +include arch/arm/Makefile.efi diff --git a/src/arch/arm/core/arm_bigint.c b/src/arch/arm32/core/arm32_bigint.c similarity index 100% rename from src/arch/arm/core/arm_bigint.c rename to src/arch/arm32/core/arm32_bigint.c diff --git a/src/arch/arm/core/setjmp.S b/src/arch/arm32/core/setjmp.S similarity index 100% rename from src/arch/arm/core/setjmp.S rename to src/arch/arm32/core/setjmp.S diff --git a/src/arch/arm/include/bits/bigint.h b/src/arch/arm32/include/bits/bigint.h similarity index 100% rename from src/arch/arm/include/bits/bigint.h rename to src/arch/arm32/include/bits/bigint.h diff --git a/src/arch/arm/include/bits/bitops.h b/src/arch/arm32/include/bits/bitops.h similarity index 100% rename from src/arch/arm/include/bits/bitops.h rename to src/arch/arm32/include/bits/bitops.h diff --git a/src/arch/arm/include/bits/byteswap.h b/src/arch/arm32/include/bits/byteswap.h similarity index 100% rename from src/arch/arm/include/bits/byteswap.h rename to src/arch/arm32/include/bits/byteswap.h diff --git a/src/arch/arm/include/bits/compiler.h b/src/arch/arm32/include/bits/compiler.h similarity index 100% rename from src/arch/arm/include/bits/compiler.h rename to src/arch/arm32/include/bits/compiler.h diff --git a/src/arch/arm/include/bits/profile.h b/src/arch/arm32/include/bits/profile.h similarity index 100% rename from src/arch/arm/include/bits/profile.h rename to src/arch/arm32/include/bits/profile.h diff --git a/src/arch/arm/include/bits/stdint.h b/src/arch/arm32/include/bits/stdint.h similarity index 100% rename from src/arch/arm/include/bits/stdint.h rename to src/arch/arm32/include/bits/stdint.h diff --git a/src/arch/arm/include/bits/strings.h b/src/arch/arm32/include/bits/strings.h similarity index 100% rename from src/arch/arm/include/bits/strings.h rename to src/arch/arm32/include/bits/strings.h diff --git a/src/arch/arm/include/efi/ipxe/dhcp_arch.h b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h similarity index 100% rename from src/arch/arm/include/efi/ipxe/dhcp_arch.h rename to src/arch/arm32/include/efi/ipxe/dhcp_arch.h diff --git a/src/arch/arm/include/gdbmach.h b/src/arch/arm32/include/gdbmach.h similarity index 100% rename from src/arch/arm/include/gdbmach.h rename to src/arch/arm32/include/gdbmach.h diff --git a/src/arch/arm/include/limits.h b/src/arch/arm32/include/limits.h similarity index 100% rename from src/arch/arm/include/limits.h rename to src/arch/arm32/include/limits.h diff --git a/src/arch/arm/include/setjmp.h b/src/arch/arm32/include/setjmp.h similarity index 100% rename from src/arch/arm/include/setjmp.h rename to src/arch/arm32/include/setjmp.h diff --git a/src/arch/arm/libgcc/lldivmod.S b/src/arch/arm32/libgcc/lldivmod.S similarity index 100% rename from src/arch/arm/libgcc/lldivmod.S rename to src/arch/arm32/libgcc/lldivmod.S diff --git a/src/arch/arm/libgcc/llshift.S b/src/arch/arm32/libgcc/llshift.S similarity index 100% rename from src/arch/arm/libgcc/llshift.S rename to src/arch/arm32/libgcc/llshift.S From 17c6f322eef5e0a2250a89b140486cf07598d2fa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 8 May 2016 00:20:20 +0100 Subject: [PATCH 214/591] [arm] Add support for 64-bit ARM (Aarch64) Signed-off-by: Michael Brown --- src/arch/arm/core/arm_io.c | 5 + src/arch/arm/include/bits/xen.h | 9 + src/arch/arm/include/ipxe/arm_io.h | 44 ++- src/arch/arm64/Makefile | 22 ++ src/arch/arm64/Makefile.efi | 14 + src/arch/arm64/core/arm64_bigint.c | 103 +++++++ src/arch/arm64/core/setjmp.S | 56 ++++ src/arch/arm64/include/bits/bigint.h | 317 ++++++++++++++++++++ src/arch/arm64/include/bits/bitops.h | 100 ++++++ src/arch/arm64/include/bits/byteswap.h | 47 +++ src/arch/arm64/include/bits/compiler.h | 16 + src/arch/arm64/include/bits/profile.h | 30 ++ src/arch/arm64/include/bits/stdint.h | 21 ++ src/arch/arm64/include/bits/strings.h | 69 +++++ src/arch/arm64/include/efi/ipxe/dhcp_arch.h | 46 +++ src/arch/arm64/include/gdbmach.h | 45 +++ src/arch/arm64/include/limits.h | 59 ++++ src/arch/arm64/include/setjmp.h | 44 +++ src/config/defaults/efi.h | 2 +- src/util/efirom.c | 1 + src/util/elf2efi.c | 20 ++ 21 files changed, 1055 insertions(+), 15 deletions(-) create mode 100644 src/arch/arm64/Makefile create mode 100644 src/arch/arm64/Makefile.efi create mode 100644 src/arch/arm64/core/arm64_bigint.c create mode 100644 src/arch/arm64/core/setjmp.S create mode 100644 src/arch/arm64/include/bits/bigint.h create mode 100644 src/arch/arm64/include/bits/bitops.h create mode 100644 src/arch/arm64/include/bits/byteswap.h create mode 100644 src/arch/arm64/include/bits/compiler.h create mode 100644 src/arch/arm64/include/bits/profile.h create mode 100644 src/arch/arm64/include/bits/stdint.h create mode 100644 src/arch/arm64/include/bits/strings.h create mode 100644 src/arch/arm64/include/efi/ipxe/dhcp_arch.h create mode 100644 src/arch/arm64/include/gdbmach.h create mode 100644 src/arch/arm64/include/limits.h create mode 100644 src/arch/arm64/include/setjmp.h diff --git a/src/arch/arm/core/arm_io.c b/src/arch/arm/core/arm_io.c index 4bc47a5c6..1ef571fc1 100644 --- a/src/arch/arm/core/arm_io.c +++ b/src/arch/arm/core/arm_io.c @@ -84,5 +84,10 @@ PROVIDE_IOAPI_INLINE ( arm, writew ); PROVIDE_IOAPI_INLINE ( arm, writel ); PROVIDE_IOAPI_INLINE ( arm, iodelay ); PROVIDE_IOAPI_INLINE ( arm, mb ); +#ifdef __aarch64__ +PROVIDE_IOAPI_INLINE ( arm, readq ); +PROVIDE_IOAPI_INLINE ( arm, writeq ); +#else PROVIDE_IOAPI ( arm, readq, arm32_readq ); PROVIDE_IOAPI ( arm, writeq, arm32_writeq ); +#endif diff --git a/src/arch/arm/include/bits/xen.h b/src/arch/arm/include/bits/xen.h index c3808a733..34f647903 100644 --- a/src/arch/arm/include/bits/xen.h +++ b/src/arch/arm/include/bits/xen.h @@ -10,12 +10,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Hypercall registers */ +#ifdef __aarch64__ +#define XEN_HC "x16" +#define XEN_REG1 "x0" +#define XEN_REG2 "x1" +#define XEN_REG3 "x2" +#define XEN_REG4 "x3" +#define XEN_REG5 "x4" +#else #define XEN_HC "r12" #define XEN_REG1 "r0" #define XEN_REG2 "r1" #define XEN_REG3 "r2" #define XEN_REG4 "r3" #define XEN_REG5 "r4" +#endif /** * Issue hypercall with one argument diff --git a/src/arch/arm/include/ipxe/arm_io.h b/src/arch/arm/include/ipxe/arm_io.h index e13de15f4..f8765af75 100644 --- a/src/arch/arm/include/ipxe/arm_io.h +++ b/src/arch/arm/include/ipxe/arm_io.h @@ -43,34 +43,46 @@ IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) { * */ -#define ARM_READX( _api_func, _type, _insn_suffix ) \ +#define ARM_READX( _api_func, _type, _insn_suffix, _reg_prefix ) \ static inline __always_inline _type \ IOAPI_INLINE ( arm, _api_func ) ( volatile _type *io_addr ) { \ _type data; \ - __asm__ __volatile__ ( "ldr" _insn_suffix " %0, %1" \ + __asm__ __volatile__ ( "ldr" _insn_suffix " %" _reg_prefix "0, %1" \ : "=r" ( data ) : "Qo" ( *io_addr ) ); \ return data; \ } -ARM_READX ( readb, uint8_t, "b" ); -ARM_READX ( readw, uint16_t, "h" ); -ARM_READX ( readl, uint32_t, "" ); +#ifdef __aarch64__ +ARM_READX ( readb, uint8_t, "b", "w" ); +ARM_READX ( readw, uint16_t, "h", "w" ); +ARM_READX ( readl, uint32_t, "", "w" ); +ARM_READX ( readq, uint64_t, "", "" ); +#else +ARM_READX ( readb, uint8_t, "b", "" ); +ARM_READX ( readw, uint16_t, "h", "" ); +ARM_READX ( readl, uint32_t, "", "" ); +#endif -#define ARM_WRITEX( _api_func, _type, _insn_suffix ) \ +#define ARM_WRITEX( _api_func, _type, _insn_suffix, _reg_prefix ) \ static inline __always_inline void \ -IOAPI_INLINE ( arm, _api_func ) ( _type data, \ - volatile _type *io_addr ) { \ - __asm__ __volatile__ ( "str" _insn_suffix " %0, %1" \ +IOAPI_INLINE ( arm, _api_func ) ( _type data, volatile _type *io_addr ) { \ + __asm__ __volatile__ ( "str" _insn_suffix " %" _reg_prefix "0, %1" \ : : "r" ( data ), "Qo" ( *io_addr ) ); \ } -ARM_WRITEX ( writeb, uint8_t, "b" ); -ARM_WRITEX ( writew, uint16_t, "h" ); -ARM_WRITEX ( writel, uint32_t, "" ); +#ifdef __aarch64__ +ARM_WRITEX ( writeb, uint8_t, "b", "w" ); +ARM_WRITEX ( writew, uint16_t, "h", "w" ); +ARM_WRITEX ( writel, uint32_t, "", "w" ); +ARM_WRITEX ( writeq, uint64_t, "", "" ); +#else +ARM_WRITEX ( writeb, uint8_t, "b", "" ); +ARM_WRITEX ( writew, uint16_t, "h", "" ); +ARM_WRITEX ( writel, uint32_t, "", "" ); +#endif /* * Slow down I/O * */ - static inline __always_inline void IOAPI_INLINE ( arm, iodelay ) ( void ) { /* Nothing to do */ @@ -80,10 +92,14 @@ IOAPI_INLINE ( arm, iodelay ) ( void ) { * Memory barrier * */ - static inline __always_inline void IOAPI_INLINE ( arm, mb ) ( void ) { + +#ifdef __aarch64__ + __asm__ __volatile__ ( "dmb sy" ); +#else __asm__ __volatile__ ( "dmb" ); +#endif } #endif /* _IPXE_ARM_IO_H */ diff --git a/src/arch/arm64/Makefile b/src/arch/arm64/Makefile new file mode 100644 index 000000000..d121871f7 --- /dev/null +++ b/src/arch/arm64/Makefile @@ -0,0 +1,22 @@ +# ARM64-specific directories containing source files +# +SRCDIRS += arch/arm64/core + +# ARM64-specific flags +# +CFLAGS += -mabi=lp64 -mlittle-endian -mcmodel=small +CFLAGS += -fomit-frame-pointer +ASFLAGS += -mabi=lp64 -EL + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# Include common ARM Makefile +MAKEDEPS += arch/arm/Makefile +include arch/arm/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/arm64/Makefile.$(PLATFORM) +include arch/arm64/Makefile.$(PLATFORM) diff --git a/src/arch/arm64/Makefile.efi b/src/arch/arm64/Makefile.efi new file mode 100644 index 000000000..998a64d03 --- /dev/null +++ b/src/arch/arm64/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI64) + +# Specify EFI boot file +# +EFI_BOOT_FILE = bootaa64.efi + +# Include generic EFI Makefile +# +MAKEDEPS += arch/arm/Makefile.efi +include arch/arm/Makefile.efi diff --git a/src/arch/arm64/core/arm64_bigint.c b/src/arch/arm64/core/arm64_bigint.c new file mode 100644 index 000000000..bc4ee9a00 --- /dev/null +++ b/src/arch/arm64/core/arm64_bigint.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include + +/** @file + * + * Big integer support + */ + +/** + * Multiply big integers + * + * @v multiplicand0 Element 0 of big integer to be multiplied + * @v multiplier0 Element 0 of big integer to be multiplied + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements + */ +void bigint_multiply_raw ( const uint64_t *multiplicand0, + const uint64_t *multiplier0, + uint64_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = + ( ( const void * ) multiplicand0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = + ( ( const void * ) multiplier0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result = + ( ( void * ) result0 ); + unsigned int i; + unsigned int j; + uint64_t multiplicand_element; + uint64_t multiplier_element; + uint64_t *result_elements; + uint64_t discard_low; + uint64_t discard_high; + uint64_t discard_temp_low; + uint64_t discard_temp_high; + + /* Zero result */ + memset ( result, 0, sizeof ( *result ) ); + + /* Multiply integers one element at a time */ + for ( i = 0 ; i < size ; i++ ) { + multiplicand_element = multiplicand->element[i]; + for ( j = 0 ; j < size ; j++ ) { + multiplier_element = multiplier->element[j]; + result_elements = &result->element[ i + j ]; + /* Perform a single multiply, and add the + * resulting double-element into the result, + * carrying as necessary. The carry can + * never overflow beyond the end of the + * result, since: + * + * a < 2^{n}, b < 2^{n} => ab < 2^{2n} + */ + __asm__ __volatile__ ( "mul %1, %6, %7\n\t" + "umulh %2, %6, %7\n\t" + "ldp %3, %4, [%0]\n\t" + "adds %3, %3, %1\n\t" + "adcs %4, %4, %2\n\t" + "stp %3, %4, [%0], #16\n\t" + "bcc 2f\n\t" + "\n1:\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, %3, xzr\n\t" + "str %3, [%0], #8\n\t" + "bcs 1b\n\t" + "\n2:\n\t" + : "+r" ( result_elements ), + "=&r" ( discard_low ), + "=&r" ( discard_high ), + "=r" ( discard_temp_low ), + "=r" ( discard_temp_high ), + "+m" ( *result ) + : "r" ( multiplicand_element ), + "r" ( multiplier_element ) + : "cc" ); + } + } +} diff --git a/src/arch/arm64/core/setjmp.S b/src/arch/arm64/core/setjmp.S new file mode 100644 index 000000000..fa47aa0af --- /dev/null +++ b/src/arch/arm64/core/setjmp.S @@ -0,0 +1,56 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + + /* Must match jmp_buf structure layout */ + .struct 0 +env_x19_x20: .quad 0, 0 +env_x21_x22: .quad 0, 0 +env_x23_x24: .quad 0, 0 +env_x25_x26: .quad 0, 0 +env_x27_x28: .quad 0, 0 +env_x29_x30: .quad 0, 0 +env_sp: .quad 0 + .previous + +/* + * Save stack context for non-local goto + */ + .globl setjmp + .type setjmp, %function +setjmp: + /* Store registers */ + stp x19, x20, [x0, #env_x19_x20] + stp x21, x22, [x0, #env_x21_x22] + stp x23, x24, [x0, #env_x23_x24] + stp x25, x26, [x0, #env_x25_x26] + stp x27, x28, [x0, #env_x27_x28] + stp x29, x30, [x0, #env_x29_x30] + mov x16, sp + str x16, [x0, #env_sp] + /* Return 0 when returning as setjmp() */ + mov x0, #0 + ret + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp + .type longjmp, %function +longjmp: + /* Restore registers */ + ldp x19, x20, [x0, #env_x19_x20] + ldp x21, x22, [x0, #env_x21_x22] + ldp x23, x24, [x0, #env_x23_x24] + ldp x25, x26, [x0, #env_x25_x26] + ldp x27, x28, [x0, #env_x27_x28] + ldp x29, x30, [x0, #env_x29_x30] + ldr x16, [x0, #env_sp] + mov sp, x16 + /* Force result to non-zero */ + cmp w1, #0 + csinc w0, w1, w1, ne + /* Return to setjmp() caller */ + br x30 + .size longjmp, . - longjmp diff --git a/src/arch/arm64/include/bits/bigint.h b/src/arch/arm64/include/bits/bigint.h new file mode 100644 index 000000000..79983b410 --- /dev/null +++ b/src/arch/arm64/include/bits/bigint.h @@ -0,0 +1,317 @@ +#ifndef _BITS_BIGINT_H +#define _BITS_BIGINT_H + +/** @file + * + * Big integer support + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Element of a big integer */ +typedef uint64_t bigint_element_t; + +/** + * Initialise big integer + * + * @v value0 Element 0 of big integer to initialise + * @v size Number of elements + * @v data Raw data + * @v len Length of raw data + */ +static inline __attribute__ (( always_inline )) void +bigint_init_raw ( uint64_t *value0, unsigned int size, + const void *data, size_t len ) { + size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + uint8_t *value_byte = ( ( void * ) value0 ); + const uint8_t *data_byte = ( data + len ); + + /* Copy raw data in reverse order, padding with zeros */ + while ( len-- ) + *(value_byte++) = *(--data_byte); + while ( pad_len-- ) + *(value_byte++) = 0; +} + +/** + * Add big integers + * + * @v addend0 Element 0 of big integer to add + * @v value0 Element 0 of big integer to be added to + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_add_raw ( const uint64_t *addend0, uint64_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_addend; + uint64_t *discard_value; + uint64_t discard_addend_i; + uint64_t discard_value_i; + unsigned int discard_size; + + __asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %3, [%0], #8\n\t" + "ldr %4, [%1]\n\t" + "adcs %4, %4, %3\n\t" + "str %4, [%1], #8\n\t" + "sub %w2, %w2, #1\n\t" + "cbnz %w2, 1b\n\t" + : "=r" ( discard_addend ), + "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_addend_i ), + "=r" ( discard_value_i ), + "+m" ( *value ) + : "0" ( addend0 ), "1" ( value0 ), "2" ( size ) + : "cc" ); +} + +/** + * Subtract big integers + * + * @v subtrahend0 Element 0 of big integer to subtract + * @v value0 Element 0 of big integer to be subtracted from + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_subtrahend; + uint64_t *discard_value; + uint64_t discard_subtrahend_i; + uint64_t discard_value_i; + unsigned int discard_size; + + __asm__ __volatile__ ( "cmp xzr, xzr\n\t" /* set CF */ + "\n1:\n\t" + "ldr %3, [%0], #8\n\t" + "ldr %4, [%1]\n\t" + "sbcs %4, %4, %3\n\t" + "str %4, [%1], #8\n\t" + "sub %w2, %w2, #1\n\t" + "cbnz %w2, 1b\n\t" + : "=r" ( discard_subtrahend ), + "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_subtrahend_i ), + "=r" ( discard_value_i ), + "+m" ( *value ) + : "0" ( subtrahend0 ), "1" ( value0 ), + "2" ( size ) + : "cc" ); +} + +/** + * Rotate big integer left + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_rol_raw ( uint64_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_value; + uint64_t discard_value_i; + unsigned int discard_size; + + __asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %2, [%0]\n\t" + "adcs %2, %2, %2\n\t" + "str %2, [%0], #8\n\t" + "sub %w1, %w1, #1\n\t" + "cbnz %w1, 1b\n\t" + : "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Rotate big integer right + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_ror_raw ( uint64_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_value; + uint64_t discard_value_i; + uint64_t discard_value_j; + unsigned int discard_size; + + __asm__ __volatile__ ( "mov %3, #0\n\t" + "\n1:\n\t" + "sub %w1, %w1, #1\n\t" + "ldr %2, [%0, %1, lsl #3]\n\t" + "extr %3, %3, %2, #1\n\t" + "str %3, [%0, %1, lsl #3]\n\t" + "mov %3, %2\n\t" + "cbnz %w1, 1b\n\t" + : "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_value_i ), + "=r" ( discard_value_j ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) ); +} + +/** + * Test if big integer is equal to zero + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret is_zero Big integer is equal to zero + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_zero_raw ( const uint64_t *value0, unsigned int size ) { + const uint64_t *value = value0; + uint64_t value_i; + + do { + value_i = *(value++); + if ( value_i ) + break; + } while ( --size ); + + return ( value_i == 0 ); +} + +/** + * Compare big integers + * + * @v value0 Element 0 of big integer + * @v reference0 Element 0 of reference big integer + * @v size Number of elements + * @ret geq Big integer is greater than or equal to the reference + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_geq_raw ( const uint64_t *value0, const uint64_t *reference0, + unsigned int size ) { + const uint64_t *value = ( value0 + size ); + const uint64_t *reference = ( reference0 + size ); + uint64_t value_i; + uint64_t reference_i; + + do { + value_i = *(--value); + reference_i = *(--reference); + if ( value_i != reference_i ) + break; + } while ( --size ); + + return ( value_i >= reference_i ); +} + +/** + * Test if bit is set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to test + * @ret is_set Bit is set + */ +static inline __attribute__ (( always_inline )) int +bigint_bit_is_set_raw ( const uint64_t *value0, unsigned int size, + unsigned int bit ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( const void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + return ( !! ( value->element[index] & ( 1UL << subindex ) ) ); +} + +/** + * Find highest bit set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret max_bit Highest bit set + 1 (or 0 if no bits set) + */ +static inline __attribute__ (( always_inline )) int +bigint_max_set_bit_raw ( const uint64_t *value0, unsigned int size ) { + const uint64_t *value = ( value0 + size ); + int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) ); + uint64_t value_i; + + do { + value_i = *(--value); + max_bit -= ( 64 - fls ( value_i ) ); + if ( value_i ) + break; + } while ( --size ); + + return max_bit; +} + +/** + * Grow big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_grow_raw ( const uint64_t *source0, unsigned int source_size, + uint64_t *dest0, unsigned int dest_size ) { + unsigned int pad_size = ( dest_size - source_size ); + + memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) ); + memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) ); +} + +/** + * Shrink big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_shrink_raw ( const uint64_t *source0, unsigned int source_size __unused, + uint64_t *dest0, unsigned int dest_size ) { + + memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) ); +} + +/** + * Finalise big integer + * + * @v value0 Element 0 of big integer to finalise + * @v size Number of elements + * @v out Output buffer + * @v len Length of output buffer + */ +static inline __attribute__ (( always_inline )) void +bigint_done_raw ( const uint64_t *value0, unsigned int size __unused, + void *out, size_t len ) { + const uint8_t *value_byte = ( ( const void * ) value0 ); + uint8_t *out_byte = ( out + len ); + + /* Copy raw data in reverse order */ + while ( len-- ) + *(--out_byte) = *(value_byte++); +} + +extern void bigint_multiply_raw ( const uint64_t *multiplicand0, + const uint64_t *multiplier0, + uint64_t *value0, unsigned int size ); + +#endif /* _BITS_BIGINT_H */ diff --git a/src/arch/arm64/include/bits/bitops.h b/src/arch/arm64/include/bits/bitops.h new file mode 100644 index 000000000..4350f622a --- /dev/null +++ b/src/arch/arm64/include/bits/bitops.h @@ -0,0 +1,100 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * ARM bit operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 64 ); + unsigned int offset = ( bit % 64 ); + volatile uint64_t *qword = ( ( ( volatile uint64_t * ) bits ) + index ); + uint64_t mask = ( 1UL << offset ); + uint64_t old; + uint64_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldxr %0, %3\n\t" + "orr %1, %0, %4\n\t" + "stxr %w2, %1, %3\n\t" + "tst %w2, %w2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&r" ( flag ), + "+Q" ( *qword ) + : "r" ( mask ) + : "cc" ); + + return ( !! ( old & mask ) ); +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 64 ); + unsigned int offset = ( bit % 64 ); + volatile uint64_t *qword = ( ( ( volatile uint64_t * ) bits ) + index ); + uint64_t mask = ( 1UL << offset ); + uint64_t old; + uint64_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldxr %0, %3\n\t" + "bic %1, %0, %4\n\t" + "stxr %w2, %1, %3\n\t" + "tst %w2, %w2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&r" ( flag ), + "+Q" ( *qword ) + : "r" ( mask ) + : "cc" ); + + return ( !! ( old & mask ) ); +} + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + + test_and_set_bit ( bit, bits ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + + test_and_clear_bit ( bit, bits ); +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/arch/arm64/include/bits/byteswap.h b/src/arch/arm64/include/bits/byteswap.h new file mode 100644 index 000000000..169d6c20e --- /dev/null +++ b/src/arch/arm64/include/bits/byteswap.h @@ -0,0 +1,47 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +/** @file + * + * Byte-order swapping functions + * + */ + +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "rev16 %0, %1" : "=r" ( x ) : "r" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_16s ( uint16_t *x ) { + *x = __bswap_variable_16 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "rev32 %0, %1" : "=r" ( x ) : "r" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_32s ( uint32_t *x ) { + *x = __bswap_variable_32 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + __asm__ ( "rev %0, %1" : "=r" ( x ) : "r" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_64s ( uint64_t *x ) { + *x = __bswap_variable_64 ( *x ); +} + +#endif diff --git a/src/arch/arm64/include/bits/compiler.h b/src/arch/arm64/include/bits/compiler.h new file mode 100644 index 000000000..3b129c2fd --- /dev/null +++ b/src/arch/arm64/include/bits/compiler.h @@ -0,0 +1,16 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_AARCH64_NULL + +#ifndef ASSEMBLY + +#define __asmcall +#define __libgcc + +#endif /* ASSEMBLY */ + +#endif /*_BITS_COMPILER_H */ diff --git a/src/arch/arm64/include/bits/profile.h b/src/arch/arm64/include/bits/profile.h new file mode 100644 index 000000000..cad02ea4b --- /dev/null +++ b/src/arch/arm64/include/bits/profile.h @@ -0,0 +1,30 @@ +#ifndef _BITS_PROFILE_H +#define _BITS_PROFILE_H + +/** @file + * + * Profiling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) uint64_t +profile_timestamp ( void ) { + uint64_t cycles; + + /* Read cycle counter */ + __asm__ __volatile__ ( "msr PMCR_EL0, %1\n\t" + "mrs %0, PMCCNTR_EL0\n\t" + : "=r" ( cycles ) : "r" ( 1 ) ); + return cycles; +} + +#endif /* _BITS_PROFILE_H */ diff --git a/src/arch/arm64/include/bits/stdint.h b/src/arch/arm64/include/bits/stdint.h new file mode 100644 index 000000000..9eb72e9c4 --- /dev/null +++ b/src/arch/arm64/include/bits/stdint.h @@ -0,0 +1,21 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +typedef __SIZE_TYPE__ size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/src/arch/arm64/include/bits/strings.h b/src/arch/arm64/include/bits/strings.h new file mode 100644 index 000000000..d5340f484 --- /dev/null +++ b/src/arch/arm64/include/bits/strings.h @@ -0,0 +1,69 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long long bits = value; + unsigned long long lsb; + unsigned int lz; + + /* Extract least significant set bit */ + lsb = ( bits & -bits ); + + /* Count number of leading zeroes before LSB */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) ); + + return ( 64 - lz ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + + return __ffsll ( value ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned int lz; + + /* Count number of leading zeroes */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) ); + + return ( 64 - lz ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + + return __flsll ( value ); +} + +#endif /* _BITS_STRINGS_H */ diff --git a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h new file mode 100644 index 000000000..f403d4ce8 --- /dev/null +++ b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#define DHCP_ARCH_VENDOR_CLASS_ID \ + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) + +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_EFI ) + +#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) + +#endif diff --git a/src/arch/arm64/include/gdbmach.h b/src/arch/arm64/include/gdbmach.h new file mode 100644 index 000000000..cd152eedd --- /dev/null +++ b/src/arch/arm64/include/gdbmach.h @@ -0,0 +1,45 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include + +typedef unsigned long gdbreg_t; + +/* Register snapshot */ +enum { + /* Not yet implemented */ + GDBMACH_NREGS, +}; + +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) step; +} + +static inline void gdbmach_breakpoint ( void ) { + /* Not yet implemented */ +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); +extern void gdbmach_init ( void ); + +#endif /* GDBMACH_H */ diff --git a/src/arch/arm64/include/limits.h b/src/arch/arm64/include/limits.h new file mode 100644 index 000000000..8cf87b471 --- /dev/null +++ b/src/arch/arm64/include/limits.h @@ -0,0 +1,59 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 9223372036854775807L +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 18446744073709551615UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/arm64/include/setjmp.h b/src/arch/arm64/include/setjmp.h new file mode 100644 index 000000000..85a7a9cad --- /dev/null +++ b/src/arch/arm64/include/setjmp.h @@ -0,0 +1,44 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A jump buffer */ +typedef struct { + /** Saved x19 */ + uint64_t x19; + /** Saved x20 */ + uint64_t x20; + /** Saved x21 */ + uint64_t x21; + /** Saved x22 */ + uint64_t x22; + /** Saved x23 */ + uint64_t x23; + /** Saved x24 */ + uint64_t x24; + /** Saved x25 */ + uint64_t x25; + /** Saved x26 */ + uint64_t x26; + /** Saved x27 */ + uint64_t x27; + /** Saved x28 */ + uint64_t x28; + /** Saved frame pointer (x29) */ + uint64_t x29; + /** Saved link register (x30) */ + uint64_t x30; + /** Saved stack pointer (x31) */ + uint64_t sp; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 8d3a8bf23..ba4eed936 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CPUID_CMD /* x86 CPU feature detection command */ #endif -#if defined ( __arm__ ) +#if defined ( __arm__ ) || defined ( __aarch64__ ) #define IOAPI_ARM #define NAP_EFIARM #endif diff --git a/src/util/efirom.c b/src/util/efirom.c index 72829cbd4..943a66910 100644 --- a/src/util/efirom.c +++ b/src/util/efirom.c @@ -85,6 +85,7 @@ static void read_pe_info ( void *pe, uint16_t *machine, *subsystem = nt->nt32.OptionalHeader.Subsystem; break; case EFI_IMAGE_MACHINE_X64: + case EFI_IMAGE_MACHINE_AARCH64: *subsystem = nt->nt64.OptionalHeader.Subsystem; break; default: diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 01ddfd681..a93010dc7 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -72,6 +72,11 @@ #define ELF_MREL( mach, type ) ( (mach) | ( (type) << 16 ) ) +/* Seems to be missing from elf.h */ +#ifndef R_AARCH64_NULL +#define R_AARCH64_NULL 256 +#endif + #define EFI_FILE_ALIGN 0x20 struct elf_file { @@ -403,6 +408,9 @@ static void set_machine ( struct elf_file *elf, struct pe_header *pe_header ) { case EM_ARM: machine = EFI_IMAGE_MACHINE_ARMTHUMB_MIXED; break; + case EM_AARCH64: + machine = EFI_IMAGE_MACHINE_AARCH64; + break; default: eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); exit ( 1 ); @@ -582,6 +590,8 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, case ELF_MREL ( EM_386, R_386_NONE ) : case ELF_MREL ( EM_ARM, R_ARM_NONE ) : case ELF_MREL ( EM_X86_64, R_X86_64_NONE ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_NONE ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_NULL ) : /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ break; case ELF_MREL ( EM_386, R_386_32 ) : @@ -590,6 +600,7 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, generate_pe_reloc ( pe_reltab, offset, 4 ); break; case ELF_MREL ( EM_X86_64, R_X86_64_64 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ABS64 ) : /* Generate an 8-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 8 ); break; @@ -598,6 +609,15 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) : case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) : case ELF_MREL ( EM_X86_64, R_X86_64_PC32 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_CALL26 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_JUMP26 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ADR_PREL_LO21 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ADR_PREL_PG_HI21 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ADD_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST8_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST16_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST32_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST64_ABS_LO12_NC ) : /* Skip PC-relative relocations; all relative * offsets remain unaltered when the object is * loaded. From 45cd68c0fb45bc8d83adc3832a6ed08a02bee822 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 9 May 2016 16:16:43 +0100 Subject: [PATCH 215/591] [efi] Allow for building with older versions of elf.h system header Reported-by: Ahmad Mahagna Signed-off-by: Michael Brown --- src/util/elf2efi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index a93010dc7..152bf5333 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -72,6 +72,28 @@ #define ELF_MREL( mach, type ) ( (mach) | ( (type) << 16 ) ) +/* Allow for building with older versions of elf.h */ +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_NONE 0 +#define R_AARCH64_ABS64 257 +#define R_AARCH64_CALL26 283 +#define R_AARCH64_JUMP26 282 +#define R_AARCH64_ADR_PREL_LO21 274 +#define R_AARCH64_ADR_PREL_PG_HI21 275 +#define R_AARCH64_ADD_ABS_LO12_NC 277 +#define R_AARCH64_LDST8_ABS_LO12_NC 278 +#define R_AARCH64_LDST16_ABS_LO12_NC 284 +#define R_AARCH64_LDST32_ABS_LO12_NC 285 +#define R_AARCH64_LDST64_ABS_LO12_NC 286 +#endif /* EM_AARCH64 */ +#ifndef R_ARM_CALL +#define R_ARM_CALL 28 +#endif +#ifndef R_ARM_THM_JUMP24 +#define R_ARM_THM_JUMP24 30 +#endif + /* Seems to be missing from elf.h */ #ifndef R_AARCH64_NULL #define R_AARCH64_NULL 256 From a966570dce690a5eba139fd941e12e4c6d445e22 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 9 May 2016 16:01:06 +0100 Subject: [PATCH 216/591] [libc] Avoid implicit assumptions about potentially-optimised memcpy() Do not assume that an architecture-specific optimised memcpy() will have the same properties as generic_memcpy() in terms of handling overlapping regions. Signed-off-by: Michael Brown --- src/core/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/string.c b/src/core/string.c index 3e658e54e..5a185e635 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -81,7 +81,7 @@ void * generic_memmove ( void *dest, const void *src, size_t len ) { uint8_t *dest_bytes = ( dest + len ); if ( dest < src ) - return memcpy ( dest, src, len ); + return generic_memcpy ( dest, src, len ); while ( len-- ) *(--dest_bytes) = *(--src_bytes); return dest; From 95716ece91a29f1d122741ec3dd307765d96e314 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 9 May 2016 16:03:19 +0100 Subject: [PATCH 217/591] [arm] Add optimised string functions for 64-bit ARM Signed-off-by: Michael Brown --- src/arch/{arm => arm32}/include/bits/string.h | 0 src/arch/arm64/core/arm64_string.c | 249 ++++++++++++++++++ src/arch/arm64/include/bits/string.h | 106 ++++++++ 3 files changed, 355 insertions(+) rename src/arch/{arm => arm32}/include/bits/string.h (100%) create mode 100644 src/arch/arm64/core/arm64_string.c create mode 100644 src/arch/arm64/include/bits/string.h diff --git a/src/arch/arm/include/bits/string.h b/src/arch/arm32/include/bits/string.h similarity index 100% rename from src/arch/arm/include/bits/string.h rename to src/arch/arm32/include/bits/string.h diff --git a/src/arch/arm64/core/arm64_string.c b/src/arch/arm64/core/arm64_string.c new file mode 100644 index 000000000..28a2b73bc --- /dev/null +++ b/src/arch/arm64/core/arm64_string.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2016 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 + * + * Optimised string operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Copy memory area + * + * @v dest Destination address + * @v src Source address + * @v len Length + * @ret dest Destination address + */ +void arm64_memcpy ( void *dest, const void *src, size_t len ) { + void *discard_dest; + void *discard_end; + const void *discard_src; + size_t discard_offset; + unsigned long discard_data; + unsigned long discard_low; + unsigned long discard_high; + + /* If length is too short for an "ldp"/"stp" instruction pair, + * then just copy individual bytes. + */ + if ( len < 16 ) { + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "ldrb %w1, [%3, %0]\n\t" + "strb %w1, [%2, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ), + "=&r" ( discard_data ) + : "r" ( dest ), "r" ( src ), "0" ( len ) + : "memory" ); + return; + } + + /* Use "ldp"/"stp" to copy 16 bytes at a time: one initial + * potentially unaligned access, multiple destination-aligned + * accesses, one final potentially unaligned access. + */ + __asm__ __volatile__ ( "ldp %3, %4, [%1], #16\n\t" + "stp %3, %4, [%0], #16\n\t" + "and %3, %0, #15\n\t" + "sub %0, %0, %3\n\t" + "sub %1, %1, %3\n\t" + "bic %2, %5, #15\n\t" + "b 2f\n\t" + "\n1:\n\t" + "ldp %3, %4, [%1], #16\n\t" + "stp %3, %4, [%0], #16\n\t" + "\n2:\n\t" + "cmp %0, %2\n\t" + "bne 1b\n\t" + "ldp %3, %4, [%6, #-16]\n\t" + "stp %3, %4, [%5, #-16]\n\t" + : "=&r" ( discard_dest ), + "=&r" ( discard_src ), + "=&r" ( discard_end ), + "=&r" ( discard_low ), + "=&r" ( discard_high ) + : "r" ( dest + len ), "r" ( src + len ), + "0" ( dest ), "1" ( src ) + : "memory", "cc" ); +} + +/** + * Zero memory region + * + * @v dest Destination region + * @v len Length + */ +void arm64_bzero ( void *dest, size_t len ) { + size_t discard_offset; + void *discard_dest; + void *discard_end; + + /* If length is too short for an "stp" instruction, then just + * zero individual bytes. + */ + if ( len < 16 ) { + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "strb wzr, [%1, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ) + : "r" ( dest ), "0" ( len ) + : "memory" ); + return; + } + + /* Use "stp" to zero 16 bytes at a time: one initial + * potentially unaligned access, multiple aligned accesses, + * one final potentially unaligned access. + */ + __asm__ __volatile__ ( "stp xzr, xzr, [%0], #16\n\t" + "bic %0, %0, #15\n\t" + "bic %1, %2, #15\n\t" + "b 2f\n\t" + "\n1:\n\t" + "stp xzr, xzr, [%0], #16\n\t" + "\n2:\n\t" + "cmp %0, %1\n\t" + "bne 1b\n\t" + "stp xzr, xzr, [%2, #-16]\n\t" + : "=&r" ( discard_dest ), + "=&r" ( discard_end ) + : "r" ( dest + len ), "0" ( dest ) + : "memory", "cc" ); +} + +/** + * Fill memory region + * + * @v dest Destination region + * @v len Length + * @v character Fill character + * + * The unusual parameter order is to allow for more efficient + * tail-calling to arm64_memset() when zeroing a region. + */ +void arm64_memset ( void *dest, size_t len, int character ) { + size_t discard_offset; + + /* Use optimised zeroing code if applicable */ + if ( character == 0 ) { + arm64_bzero ( dest, len ); + return; + } + + /* Fill one byte at a time. Calling memset() with a non-zero + * value is relatively rare and unlikely to be + * performance-critical. + */ + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "strb %w2, [%1, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ) + : "r" ( dest ), "r" ( character ), "0" ( len ) + : "memory" ); +} + +/** + * Copy (possibly overlapping) memory region forwards + * + * @v dest Destination region + * @v src Source region + * @v len Length + */ +void arm64_memmove_forwards ( void *dest, const void *src, size_t len ) { + void *discard_dest; + const void *discard_src; + unsigned long discard_data; + + /* Assume memmove() is not performance-critical, and perform a + * bytewise copy for simplicity. + */ + __asm__ __volatile__ ( "b 2f\n\t" + "\n1:\n\t" + "ldrb %w2, [%1], #1\n\t" + "strb %w2, [%0], #1\n\t" + "\n2:\n\t" + "cmp %0, %3\n\t" + "bne 1b\n\t" + : "=&r" ( discard_dest ), + "=&r" ( discard_src ), + "=&r" ( discard_data ) + : "r" ( dest + len ), "0" ( dest ), "1" ( src ) + : "memory" ); +} + +/** + * Copy (possibly overlapping) memory region backwards + * + * @v dest Destination region + * @v src Source region + * @v len Length + */ +void arm64_memmove_backwards ( void *dest, const void *src, size_t len ) { + size_t discard_offset; + unsigned long discard_data; + + /* Assume memmove() is not performance-critical, and perform a + * bytewise copy for simplicity. + */ + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "ldrb %w1, [%3, %0]\n\t" + "strb %w1, [%2, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ), + "=&r" ( discard_data ) + : "r" ( dest ), "r" ( src ), "0" ( len ) + : "memory" ); +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + */ +void arm64_memmove ( void *dest, const void *src, size_t len ) { + + if ( dest <= src ) { + arm64_memmove_forwards ( dest, src, len ); + } else { + arm64_memmove_backwards ( dest, src, len ); + } +} diff --git a/src/arch/arm64/include/bits/string.h b/src/arch/arm64/include/bits/string.h new file mode 100644 index 000000000..c05fbe346 --- /dev/null +++ b/src/arch/arm64/include/bits/string.h @@ -0,0 +1,106 @@ +#ifndef BITS_STRING_H +#define BITS_STRING_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * String functions + * + */ + +extern void arm64_bzero ( void *dest, size_t len ); +extern void arm64_memset ( void *dest, size_t len, int character ); +extern void arm64_memcpy ( void *dest, const void *src, size_t len ); +extern void arm64_memmove_forwards ( void *dest, const void *src, size_t len ); +extern void arm64_memmove_backwards ( void *dest, const void *src, size_t len ); +extern void arm64_memmove ( void *dest, const void *src, size_t len ); + +/** + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int character, size_t len ) { + + /* Allow gcc to generate inline "stX xzr" instructions for + * small, constant lengths. + */ + if ( __builtin_constant_p ( character ) && ( character == 0 ) && + __builtin_constant_p ( len ) && ( len <= 64 ) ) { + __builtin_memset ( dest, 0, len ); + return dest; + } + + /* For zeroing larger or non-constant lengths, use the + * optimised variable-length zeroing code. + */ + if ( __builtin_constant_p ( character ) && ( character == 0 ) ) { + arm64_bzero ( dest, len ); + return dest; + } + + /* Not necessarily zeroing: use basic variable-length code */ + arm64_memset ( dest, len, character ); + return dest; +} + +/** + * Copy memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memcpy ( void *dest, const void *src, size_t len ) { + + /* Allow gcc to generate inline "ldX"/"stX" instructions for + * small, constant lengths. + */ + if ( __builtin_constant_p ( len ) && ( len <= 64 ) ) { + __builtin_memcpy ( dest, src, len ); + return dest; + } + + /* Otherwise, use variable-length code */ + arm64_memcpy ( dest, src, len ); + return dest; +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memmove ( void *dest, const void *src, size_t len ) { + ssize_t offset = ( dest - src ); + + /* If required direction of copy is known at build time, then + * use the appropriate forwards/backwards copy directly. + */ + if ( __builtin_constant_p ( offset ) ) { + if ( offset <= 0 ) { + arm64_memmove_forwards ( dest, src, len ); + return dest; + } else { + arm64_memmove_backwards ( dest, src, len ); + return dest; + } + } + + /* Otherwise, use ambidirectional copy */ + arm64_memmove ( dest, src, len ); + return dest; +} + +#endif /* BITS_STRING_H */ From 47931a4de53ccdeda061c59aa0919f152cf0dfdf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 10 May 2016 17:13:05 +0100 Subject: [PATCH 218/591] [arm] Add optimised TCP/IP checksumming for 64-bit ARM Signed-off-by: Michael Brown --- src/arch/{arm => arm32}/include/bits/tcpip.h | 0 src/arch/arm64/core/arm64_tcpip.c | 175 +++++++++++++++++++ src/arch/arm64/include/bits/tcpip.h | 15 ++ 3 files changed, 190 insertions(+) rename src/arch/{arm => arm32}/include/bits/tcpip.h (100%) create mode 100644 src/arch/arm64/core/arm64_tcpip.c create mode 100644 src/arch/arm64/include/bits/tcpip.h diff --git a/src/arch/arm/include/bits/tcpip.h b/src/arch/arm32/include/bits/tcpip.h similarity index 100% rename from src/arch/arm/include/bits/tcpip.h rename to src/arch/arm32/include/bits/tcpip.h diff --git a/src/arch/arm64/core/arm64_tcpip.c b/src/arch/arm64/core/arm64_tcpip.c new file mode 100644 index 000000000..0ef04ea42 --- /dev/null +++ b/src/arch/arm64/core/arm64_tcpip.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 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 ); + +/** @file + * + * TCP/IP checksum + * + */ + +#include +#include + +/** Alignment used by main checksumming loop */ +#define TCPIP_CHKSUM_ALIGN 16 + +/** Number of steps in each iteration of the unrolled main checksumming loop */ +#define TCPIP_CHKSUM_UNROLL 4 + +/** + * Calculate continued TCP/IP checkum + * + * @v sum Checksum of already-summed data, in network byte order + * @v data Data buffer + * @v len Length of data buffer + * @ret sum Updated checksum, in network byte order + */ +uint16_t tcpip_continue_chksum ( uint16_t sum, const void *data, + size_t len ) { + intptr_t start; + intptr_t end; + intptr_t mid; + unsigned int pre; + unsigned int post; + unsigned int first; + uint64_t discard_low; + uint64_t discard_high; + + /* Avoid potentially undefined shift operation */ + if ( len == 0 ) + return sum; + + /* Find maximally-aligned midpoint. For short blocks of data, + * this may be aligned to fewer than 16 bytes. + */ + start = ( ( intptr_t ) data ); + end = ( start + len ); + mid = ( end & + ~( ( ~( 1UL << 63 ) ) >> ( 64 - flsl ( start ^ end ) ) ) ); + + /* Calculate pre- and post-alignment lengths */ + pre = ( ( mid - start ) & ( TCPIP_CHKSUM_ALIGN - 1 ) ); + post = ( ( end - mid ) & ( TCPIP_CHKSUM_ALIGN - 1 ) ); + + /* Calculate number of steps in first iteration of unrolled loop */ + first = ( ( ( len - pre - post ) / TCPIP_CHKSUM_ALIGN ) & + ( TCPIP_CHKSUM_UNROLL - 1 ) ); + + /* Calculate checksum */ + __asm__ ( /* Invert sum */ + "eor %w0, %w0, #0xffff\n\t" + /* Clear carry flag */ + "cmn xzr, xzr\n\t" + /* Byteswap and sum pre-alignment byte, if applicable */ + "tbz %w4, #0, 1f\n\t" + "ldrb %w2, [%1], #1\n\t" + "rev16 %w0, %w0\n\t" + "rev16 %w2, %w2\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum pre-alignment halfword, if applicable */ + "tbz %w4, #1, 1f\n\t" + "ldrh %w2, [%1], #2\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum pre-alignment word, if applicable */ + "tbz %w4, #2, 1f\n\t" + "ldr %w2, [%1], #4\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum pre-alignment doubleword, if applicable */ + "tbz %w4, #3, 1f\n\t" + "ldr %2, [%1], #8\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Jump into unrolled (x4) main loop */ + "adr %2, 2f\n\t" + "sub %2, %2, %5, lsl #3\n\t" + "sub %2, %2, %5, lsl #2\n\t" + "br %2\n\t" + "\n1:\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "\n2:\n\t" + "sub %2, %1, %6\n\t" + "cbnz %2, 1b\n\t" + /* Sum post-alignment doubleword, if applicable */ + "tbz %w7, #3, 1f\n\t" + "ldr %2, [%1], #8\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum post-alignment word, if applicable */ + "tbz %w7, #2, 1f\n\t" + "ldr %w2, [%1], #4\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum post-alignment halfword, if applicable */ + "tbz %w7, #1, 1f\n\t" + "ldrh %w2, [%1], #2\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum post-alignment byte, if applicable */ + "tbz %w7, #0, 1f\n\t" + "ldrb %w2, [%1], #1\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Fold down to a uint32_t plus carry flag */ + "lsr %2, %0, #32\n\t" + "adcs %w0, %w0, %w2\n\t" + /* Fold down to a uint16_t plus carry in bit 16 */ + "ubfm %2, %0, #0, #15\n\t" + "ubfm %3, %0, #16, #31\n\t" + "adc %w0, %w2, %w3\n\t" + /* Fold down to a uint16_t */ + "tbz %w0, #16, 1f\n\t" + "mov %w2, #0xffff\n\t" + "sub %w0, %w0, %w2\n\t" + "tbz %w0, #16, 1f\n\t" + "sub %w0, %w0, %w2\n\t" + "\n1:\n\t" + /* Byteswap back, if applicable */ + "tbz %w4, #0, 1f\n\t" + "rev16 %w0, %w0\n\t" + "\n1:\n\t" + /* Invert sum */ + "eor %w0, %w0, #0xffff\n\t" + : "+r" ( sum ), "+r" ( data ), "=&r" ( discard_low ), + "=&r" ( discard_high ) + : "r" ( pre ), "r" ( first ), "r" ( end - post ), + "r" ( post ) + : "cc" ); + + return sum; +} diff --git a/src/arch/arm64/include/bits/tcpip.h b/src/arch/arm64/include/bits/tcpip.h new file mode 100644 index 000000000..68686534e --- /dev/null +++ b/src/arch/arm64/include/bits/tcpip.h @@ -0,0 +1,15 @@ +#ifndef _BITS_TCPIP_H +#define _BITS_TCPIP_H + +/** @file + * + * Transport-network layer interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern uint16_t tcpip_continue_chksum ( uint16_t sum, const void *data, + size_t len ); + +#endif /* _BITS_TCPIP_H */ From 6164741f81fbb80d25ba3877251f4b31de3ed6a0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 11 May 2016 22:02:26 +0100 Subject: [PATCH 219/591] [efi] Guard against GetStatus() failing to return a NULL TX buffer The UEFI specification requires the EFI_SIMPLE_NETWORK_PROTOCOL GetStatus() method to set TxBuf to NULL if there are no transmit buffers to recycle. Some implementations (observed with Lan9118Dxe in EDK2) fill in TxBuf only when there is a transmit buffer to recycle, which leads to large numbers of "spurious TX completion" errors. Work around this problem by initialising TxBuf to NULL before calling the GetStatus() method. Signed-off-by: Michael Brown --- src/drivers/net/efi/snpnet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c index 0d876b636..88474b0be 100644 --- a/src/drivers/net/efi/snpnet.c +++ b/src/drivers/net/efi/snpnet.c @@ -191,6 +191,7 @@ static void snpnet_poll_tx ( struct net_device *netdev ) { int rc; /* Get status */ + txbuf = NULL; if ( ( efirc = snp->snp->GetStatus ( snp->snp, &irq, &txbuf ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not get status: %s\n", From 601706688bbb5502538627feb92752760d0d9f24 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 11 May 2016 21:44:23 +0100 Subject: [PATCH 220/591] [arm] Use CNTVCT_EL0 as profiling timestamp The raw cycle counter at PMCCNTR_EL0 works in qemu but seems to always read as zero on physical hardware (tested on Juno r1 and Cavium ThunderX), even after ensuring that PMCR_EL0.E and PMCNTENSET_EL0.C are both enabled. Use CNTVCT_EL0 instead; this seems to count at a lower resolution (tens of CPU cycles), but is usable for profiling. Signed-off-by: Michael Brown --- src/arch/arm64/include/bits/profile.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/arch/arm64/include/bits/profile.h b/src/arch/arm64/include/bits/profile.h index cad02ea4b..62ffa3772 100644 --- a/src/arch/arm64/include/bits/profile.h +++ b/src/arch/arm64/include/bits/profile.h @@ -21,9 +21,7 @@ profile_timestamp ( void ) { uint64_t cycles; /* Read cycle counter */ - __asm__ __volatile__ ( "msr PMCR_EL0, %1\n\t" - "mrs %0, PMCCNTR_EL0\n\t" - : "=r" ( cycles ) : "r" ( 1 ) ); + __asm__ __volatile__ ( "mrs %0, CNTVCT_EL0\n\t" : "=r" ( cycles ) ); return cycles; } From 858f56e68b7089a3dc8473bb94dd198554a24eeb Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Sat, 7 May 2016 21:20:37 +0200 Subject: [PATCH 221/591] [ath9k] Fix buffer overrun for ar9287 This backport is from linux kernel upstream commit 83d6f1f ("ath9k: fix buffer overrun for ar9287"). Signed-off-by: Christian Hesse Signed-off-by: Michael Brown --- src/drivers/net/ath/ath9k/ath9k_eeprom.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/drivers/net/ath/ath9k/ath9k_eeprom.c b/src/drivers/net/ath/ath9k/ath9k_eeprom.c index f552acaa3..a20423790 100644 --- a/src/drivers/net/ath/ath9k/ath9k_eeprom.c +++ b/src/drivers/net/ath/ath9k/ath9k_eeprom.c @@ -368,10 +368,9 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, if (match) { if (AR_SREV_9287(ah)) { - /* FIXME: array overrun? */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_9287[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_9287[idxL].pwrPdg[i], data_9287[idxL].vpdPdg[i], @@ -381,7 +380,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, } else if (eeprom_4k) { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_4k[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_4k[idxL].pwrPdg[i], data_4k[idxL].vpdPdg[i], @@ -391,7 +390,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, } else { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_def[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_def[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_def[idxL].pwrPdg[i], data_def[idxL].vpdPdg[i], From 276d7c31c5c4fbfe77a09c74f89ec9e06d5fb703 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 13 May 2016 13:22:06 +0100 Subject: [PATCH 222/591] [undi] Work around broken HP EliteBook 745 G3 PXE ROM Reported-by: Arturino Mazzei Tested-by: Arturino Mazzei Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undinet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index e273ae0a2..091ef9254 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -591,6 +591,8 @@ static const struct undinet_irq_broken undinet_irq_broken_list[] = { /* HP XX70x laptops */ { .pci_vendor = 0x8086, .pci_device = 0x1502 }, { .pci_vendor = 0x8086, .pci_device = 0x1503 }, + /* HP 745 G3 laptop */ + { .pci_vendor = 0x14e4, .pci_device = 0x1687 }, }; /** From 6d2bdc4ea3bfefb061a69f0667d6a70fbb862523 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 14 May 2016 18:34:08 +0100 Subject: [PATCH 223/591] [pci] Add support for PCI Enhanced Allocation Some embedded devices have immovable BARs, which are described via a PCI Enhanced Allocation capability. Signed-off-by: Michael Brown --- src/drivers/bus/pciea.c | 149 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/pci.h | 1 + src/include/ipxe/pciea.h | 70 +++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 src/drivers/bus/pciea.c create mode 100644 src/include/ipxe/pciea.h diff --git a/src/drivers/bus/pciea.c b/src/drivers/bus/pciea.c new file mode 100644 index 000000000..aaa69cf4c --- /dev/null +++ b/src/drivers/bus/pciea.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** @file + * + * PCI Enhanced Allocation + * + */ + +/** + * Locate PCI Enhanced Allocation BAR equivalent entry + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @ret offset PCI Enhanced Allocation entry offset, or negative error + */ +static int pciea_offset ( struct pci_device *pci, unsigned int bei ) { + uint8_t entries; + uint32_t desc; + unsigned int i; + int offset; + + /* Locate Enhanced Allocation capability */ + offset = pci_find_capability ( pci, PCI_CAP_ID_EA ); + if ( offset < 0 ) + return offset; + + /* Get number of entries */ + pci_read_config_byte ( pci, ( offset + PCIEA_ENTRIES ), &entries ); + entries &= PCIEA_ENTRIES_MASK; + + /* Locate first entry */ + offset += PCIEA_FIRST; + + /* Search for a matching entry */ + for ( i = 0 ; i < entries ; i++ ) { + + /* Read entry descriptor */ + pci_read_config_dword ( pci, offset, &desc ); + + /* Check for a matching entry */ + if ( ( desc & PCIEA_DESC_ENABLED ) && + ( bei == PCIEA_DESC_BEI ( desc ) ) ) + return offset; + + /* Move to next entry */ + offset += ( ( PCIEA_DESC_SIZE ( desc ) + 1 ) << 2 ); + } + + return -ENOENT; +} + +/** + * Read PCI Enhanced Allocation BAR equivalent value + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @v low_offset Offset to low dword of value + * @ret value BAR equivalent value + */ +static unsigned long pciea_bar_value ( struct pci_device *pci, unsigned int bei, + unsigned int low_offset ) { + uint32_t low; + uint32_t high; + int offset; + + /* Locate Enhanced Allocation offset for this BEI */ + offset = pciea_offset ( pci, bei ); + if ( offset < 0 ) + return 0; + + /* Read BAR equivalent */ + offset += low_offset; + pci_read_config_dword ( pci, offset, &low ); + if ( low & PCIEA_LOW_ATTR_64BIT ) { + offset += PCIEA_LOW_HIGH; + pci_read_config_dword ( pci, offset, &high ); + if ( high ) { + if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { + return ( ( ( uint64_t ) high << 32 ) | low ); + } else { + DBGC ( pci, PCI_FMT " unhandled 64-bit EA BAR " + "%08x%08x\n", + PCI_ARGS ( pci ), high, low ); + return 0; + } + } + } + return low; +} + +/** + * Find the start of a PCI Enhanced Allocation BAR equivalent + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @ret start BAR start address + * + * If the address exceeds the size of an unsigned long (i.e. if a + * 64-bit BAR has a non-zero high dword on a 32-bit machine), the + * return value will be zero. + */ +unsigned long pciea_bar_start ( struct pci_device *pci, unsigned int bei ) { + unsigned long base; + + base = pciea_bar_value ( pci, bei, PCIEA_LOW_BASE ); + return ( base & ~PCIEA_LOW_ATTR_MASK ); +} + +/** + * Find the size of a PCI Enhanced Allocation BAR equivalent + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @ret size BAR size + */ +unsigned long pciea_bar_size ( struct pci_device *pci, unsigned int bei ) { + unsigned long limit; + + limit = pciea_bar_value ( pci, bei, PCIEA_LOW_LIMIT ); + return ( limit ? ( ( limit | PCIEA_LOW_ATTR_MASK ) + 1 ) : 0 ); +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2cd9fb5b1..0c95e7a89 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -189,6 +189,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_golan ( ERRFILE_DRIVER | 0x007d0000 ) #define ERRFILE_flexboot_nodnic ( ERRFILE_DRIVER | 0x007e0000 ) #define ERRFILE_virtio_pci ( ERRFILE_DRIVER | 0x007f0000 ) +#define ERRFILE_pciea ( ERRFILE_DRIVER | 0x00c00000 ) #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 0c6bc7ed6..bf0e81dfa 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -94,6 +94,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_CAP_ID_VPD 0x03 /**< Vital product data */ #define PCI_CAP_ID_VNDR 0x09 /**< Vendor-specific */ #define PCI_CAP_ID_EXP 0x10 /**< PCI Express */ +#define PCI_CAP_ID_EA 0x14 /**< Enhanced Allocation */ /** Next capability */ #define PCI_CAP_NEXT 0x01 diff --git a/src/include/ipxe/pciea.h b/src/include/ipxe/pciea.h new file mode 100644 index 000000000..941c94ed5 --- /dev/null +++ b/src/include/ipxe/pciea.h @@ -0,0 +1,70 @@ +#ifndef _IPXE_PCIEA_H +#define _IPXE_PCIEA_H + +/** @file + * + * PCI Enhanced Allocation + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Number of entries */ +#define PCIEA_ENTRIES 2 +#define PCIEA_ENTRIES_MASK 0x3f + +/** First entry */ +#define PCIEA_FIRST 4 + +/** Entry descriptor */ +#define PCIEA_DESC 0 + +/** Entry size */ +#define PCIEA_DESC_SIZE(desc) ( ( (desc) >> 0 ) & 0x7 ) + +/** BAR equivalent indicator */ +#define PCIEA_DESC_BEI(desc) ( ( (desc) >> 4 ) & 0xf ) + +/** BAR equivalent indicators */ +enum pciea_bei { + PCIEA_BEI_BAR_0 = 0, /**< Standard BAR 0 */ + PCIEA_BEI_BAR_1 = 1, /**< Standard BAR 1 */ + PCIEA_BEI_BAR_2 = 2, /**< Standard BAR 2 */ + PCIEA_BEI_BAR_3 = 3, /**< Standard BAR 3 */ + PCIEA_BEI_BAR_4 = 4, /**< Standard BAR 4 */ + PCIEA_BEI_BAR_5 = 5, /**< Standard BAR 5 */ + PCIEA_BEI_ROM = 8, /**< Expansion ROM BAR */ + PCIEA_BEI_VF_BAR_0 = 9, /**< Virtual function BAR 0 */ + PCIEA_BEI_VF_BAR_1 = 10, /**< Virtual function BAR 1 */ + PCIEA_BEI_VF_BAR_2 = 11, /**< Virtual function BAR 2 */ + PCIEA_BEI_VF_BAR_3 = 12, /**< Virtual function BAR 3 */ + PCIEA_BEI_VF_BAR_4 = 13, /**< Virtual function BAR 4 */ + PCIEA_BEI_VF_BAR_5 = 14, /**< Virtual function BAR 5 */ +}; + +/** Entry is enabled */ +#define PCIEA_DESC_ENABLED 0x80000000UL + +/** Base address low dword */ +#define PCIEA_LOW_BASE 4 + +/** Limit low dword */ +#define PCIEA_LOW_LIMIT 8 + +/** BAR is 64-bit */ +#define PCIEA_LOW_ATTR_64BIT 0x00000002UL + +/** Low dword attribute bit mask */ +#define PCIEA_LOW_ATTR_MASK 0x00000003UL + +/** Offset to high dwords */ +#define PCIEA_LOW_HIGH 8 + +extern unsigned long pciea_bar_start ( struct pci_device *pci, + unsigned int bei ); +extern unsigned long pciea_bar_size ( struct pci_device *pci, + unsigned int bei ); + +#endif /* _IPXE_PCIEA_H */ From 56c0147deb4c36bf28fa4ac7f3cefdad726346e4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 20 May 2016 13:05:39 +0100 Subject: [PATCH 224/591] [settings] Extend numerical setting tags to "unsigned long" Signed-off-by: Michael Brown --- src/core/memmap_settings.c | 2 +- src/core/settings.c | 4 ++-- src/include/ipxe/settings.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c index fab3e5f3a..1098bd756 100644 --- a/src/core/memmap_settings.c +++ b/src/core/memmap_settings.c @@ -145,7 +145,7 @@ static int memmap_settings_fetch ( struct settings *settings, unsigned int i; unsigned int count; - DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n", + DBGC ( settings, "MEMMAP start %ld count %ld %s%s%s%s scale %ld\n", MEMMAP_START ( setting->tag ), MEMMAP_COUNT ( setting->tag ), ( MEMMAP_INCLUDE_START ( setting->tag ) ? "start" : "" ), ( ( MEMMAP_INCLUDE_START ( setting->tag ) && diff --git a/src/core/settings.c b/src/core/settings.c index f6f62d226..757555781 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1474,9 +1474,9 @@ struct setting * find_setting ( const char *name ) { * @v name Name * @ret tag Tag number, or 0 if not a valid number */ -static unsigned int parse_setting_tag ( const char *name ) { +static unsigned long parse_setting_tag ( const char *name ) { char *tmp = ( ( char * ) name ); - unsigned int tag = 0; + unsigned long tag = 0; while ( 1 ) { tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) ); diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index b44794af2..6534c25b6 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -40,7 +40,7 @@ struct setting { * (such as a DHCP option number, or an SMBIOS structure and * field number). */ - unsigned int tag; + unsigned long tag; /** Setting scope (or NULL) * * For historic reasons, a NULL scope with a non-zero tag From 231adda40f55610efd1c6028a300a3f46db23103 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 20 May 2016 20:43:58 +0100 Subject: [PATCH 225/591] [netdevice] Fix failure path in register_netdev() Signed-off-by: Michael Brown --- src/net/netdevice.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net/netdevice.c b/src/net/netdevice.c index b834c3cd9..9df21196c 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -733,6 +733,8 @@ int register_netdev ( struct net_device *netdev ) { clear_settings ( netdev_settings ( netdev ) ); unregister_settings ( netdev_settings ( netdev ) ); err_register_settings: + list_del ( &netdev->list ); + netdev_put ( netdev ); err_duplicate: return rc; } From 80dd6cbcc4fd8c013969e205ee410344d9180b27 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 20 May 2016 20:57:18 +0100 Subject: [PATCH 226/591] [lotest] Add option to use broadcast packets for loopback testing Signed-off-by: Michael Brown --- src/hci/commands/lotest_cmd.c | 7 ++++++- src/include/usr/lotest.h | 3 ++- src/usr/lotest.c | 14 ++++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/hci/commands/lotest_cmd.c b/src/hci/commands/lotest_cmd.c index a989932d4..393b3c36e 100644 --- a/src/hci/commands/lotest_cmd.c +++ b/src/hci/commands/lotest_cmd.c @@ -43,12 +43,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct lotest_options { /** MTU */ unsigned int mtu; + /** Broadcast */ + int broadcast; }; /** "lotest" option list */ static struct option_descriptor lotest_opts[] = { OPTION_DESC ( "mtu", 'm', required_argument, struct lotest_options, mtu, parse_integer ), + OPTION_DESC ( "broadcast", 'b', no_argument, + struct lotest_options, broadcast, parse_flag ), }; /** "lotest" command descriptor */ @@ -86,7 +90,8 @@ static int lotest_exec ( int argc, char **argv ) { opts.mtu = ETH_MAX_MTU; /* Perform loopback test */ - if ( ( rc = loopback_test ( sender, receiver, opts.mtu ) ) != 0 ) { + if ( ( rc = loopback_test ( sender, receiver, opts.mtu, + opts.broadcast ) ) != 0 ) { printf ( "Test failed: %s\n", strerror ( rc ) ); return rc; } diff --git a/src/include/usr/lotest.h b/src/include/usr/lotest.h index ce0fe5eda..bd66f4a44 100644 --- a/src/include/usr/lotest.h +++ b/src/include/usr/lotest.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int loopback_test ( struct net_device *sender, - struct net_device *receiver, size_t mtu ); + struct net_device *receiver, + size_t mtu, int broadcast ); #endif /* _USR_LOTEST_H */ diff --git a/src/usr/lotest.c b/src/usr/lotest.c index 6b328713c..6b75b5048 100644 --- a/src/usr/lotest.c +++ b/src/usr/lotest.c @@ -188,13 +188,15 @@ static int loopback_wait ( void *data, size_t len ) { * @v sender Sending network device * @v receiver Received network device * @v mtu Packet size (excluding link-layer headers) + * @v broadcast Use broadcast link-layer address * @ret rc Return status code */ int loopback_test ( struct net_device *sender, struct net_device *receiver, - size_t mtu ) { + size_t mtu, int broadcast ) { uint8_t *buf; uint32_t *seq; struct io_buffer *iobuf; + const void *ll_dest; unsigned int i; unsigned int successes; int rc; @@ -219,9 +221,13 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, return -ENOMEM; seq = ( ( void * ) buf ); + /* Determine destination address */ + ll_dest = ( broadcast ? sender->ll_broadcast : receiver->ll_addr ); + /* Print initial statistics */ - printf ( "Performing loopback test from %s to %s with %zd byte MTU\n", - sender->name, receiver->name, mtu ); + printf ( "Performing %sloopback test from %s to %s with %zd byte MTU\n", + ( broadcast ? "broadcast " : "" ), sender->name, + receiver->name, mtu ); ifstat ( sender ); ifstat ( receiver ); @@ -250,7 +256,7 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, /* Transmit packet */ if ( ( rc = net_tx ( iob_disown ( iobuf ), sender, - &lotest_protocol, receiver->ll_addr, + &lotest_protocol, ll_dest, sender->ll_addr ) ) != 0 ) { printf ( "\nFailed to transmit packet: %s", strerror ( rc ) ); From f42b2585fe81903488c3c1560089457e860241d4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 May 2016 15:27:50 +0100 Subject: [PATCH 227/591] [http] Ignore unrecognised "Connection" header tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some HTTP/2 servers send the header "Connection: upgrade, close". This currently causes iPXE to fail due to the unrecognised "upgrade" token. Fix by ignoring any unrecognised tokens in the "Connection" header. Reported-by: Ján ONDREJ (SAL) Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index d40633aaf..57b897620 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -189,8 +189,8 @@ char * http_token ( char **line, char **value ) { if ( value ) *value = NULL; - /* Skip any initial whitespace */ - while ( isspace ( **line ) ) + /* Skip any initial whitespace or commas */ + while ( ( isspace ( **line ) ) || ( **line == ',' ) ) (*line)++; /* Check for end of line and record token position */ @@ -201,8 +201,8 @@ char * http_token ( char **line, char **value ) { /* Scan for end of token */ while ( ( c = **line ) ) { - /* Terminate if we hit an unquoted whitespace */ - if ( isspace ( c ) && ! quote ) + /* Terminate if we hit an unquoted whitespace or comma */ + if ( ( isspace ( c ) || ( c == ',' ) ) && ! quote ) break; /* Terminate if we hit a closing quote */ @@ -1315,19 +1315,17 @@ http_response_transfer_encoding __http_response_header = { * @ret rc Return status code */ static int http_parse_connection ( struct http_transaction *http, char *line ) { + char *token; /* Check for known connection intentions */ - if ( strcasecmp ( line, "keep-alive" ) == 0 ) { - http->response.flags |= HTTP_RESPONSE_KEEPALIVE; - return 0; - } - if ( strcasecmp ( line, "close" ) == 0 ) { - http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; - return 0; + while ( ( token = http_token ( &line, NULL ) ) ) { + if ( strcasecmp ( token, "keep-alive" ) == 0 ) + http->response.flags |= HTTP_RESPONSE_KEEPALIVE; + if ( strcasecmp ( token, "close" ) == 0 ) + http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; } - DBGC ( http, "HTTP %p unrecognised Connection \"%s\"\n", http, line ); - return -ENOTSUP_CONNECTION; + return 0; } /** HTTP "Connection" header */ From 8dd39b95723af0aac20d16316d16f306d8394af2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 May 2016 15:51:36 +0100 Subject: [PATCH 228/591] [efi] Work around broken UEFI keyboard drivers Some UEFI keyboard drivers are blissfully unaware of the existence of either Ctrl key, and will report "Ctrl-" as just "". This breaks substantial portions of the iPXE user interface. Work around these broken UEFI drivers by allowing "ESC " to be used as a substitute for "Ctrl-". Tested-by: Dreamcat4 Signed-off-by: Michael Brown --- src/core/getkey.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/getkey.c b/src/core/getkey.c index 0f0f8b7c3..0c280d23b 100644 --- a/src/core/getkey.c +++ b/src/core/getkey.c @@ -76,9 +76,14 @@ int getkey ( unsigned long timeout ) { if ( character != ESC ) return character; + character = getchar_timeout ( GETKEY_TIMEOUT ); + if ( character < 0 ) + return ESC; + + if ( isalpha ( character ) ) + return ( toupper ( character ) - 'A' + 1 ); + while ( ( character = getchar_timeout ( GETKEY_TIMEOUT ) ) >= 0 ) { - if ( character == '[' ) - continue; if ( isdigit ( character ) ) { n = ( ( n * 10 ) + ( character - '0' ) ); continue; From ee5dfb75aaf43bca24cec4910a74563249016f79 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 24 May 2016 00:23:10 +0100 Subject: [PATCH 229/591] [axge] Add driver for ASIX 10/100/1000 USB Ethernet NICs Add driver for the AX88178A (USB2) and AX88179 (USB3) 10/100/1000 Ethernet NICs. Signed-off-by: Michael Brown --- src/drivers/net/axge.c | 798 +++++++++++++++++++++++++++++++++++++ src/drivers/net/axge.h | 174 ++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 973 insertions(+) create mode 100644 src/drivers/net/axge.c create mode 100644 src/drivers/net/axge.h diff --git a/src/drivers/net/axge.c b/src/drivers/net/axge.c new file mode 100644 index 000000000..ab59a8be7 --- /dev/null +++ b/src/drivers/net/axge.c @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2016 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 "axge.h" + +/** @file + * + * Asix 10/100/1000 USB Ethernet driver + * + * Large chunks of functionality are undocumented in the available + * datasheets. The gaps are deduced from combinations of the Linux + * driver, the FreeBSD driver, and experimentation with the hardware. + */ + +/** Interrupt completion profiler */ +static struct profiler axge_intr_profiler __profiler = + { .name = "axge.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler axge_in_profiler __profiler = + { .name = "axge.in" }; + +/** Bulk OUT profiler */ +static struct profiler axge_out_profiler __profiler = + { .name = "axge.out" }; + +/** Default bulk IN configuration + * + * The Linux and FreeBSD drivers have set of magic constants which are + * chosen based on both the Ethernet and USB link speeds. + * + * Experimentation shows that setting the "timer" value to zero seems + * to prevent the device from ever coalescing multiple packets into a + * single bulk IN transfer. This allows us to get away with using a + * 2kB receive I/O buffer and a zerocopy receive path. + */ +static struct axge_bulk_in_control axge_bicr = { + .ctrl = 7, + .timer = cpu_to_le16 ( 0 ), + .size = 0, + .ifg = 0, +}; + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Read register + * + * @v asix AXGE device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline int axge_read_register ( struct axge_device *axge, + unsigned int offset, void *data, + size_t len ) { + + return usb_control ( axge->usb, AXGE_READ_MAC_REGISTER, + offset, len, data, len ); +} + +/** + * Read one-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value to fill in + * @ret rc Return status code + */ +static inline int axge_read_byte ( struct axge_device *axge, + unsigned int offset, uint8_t *value ) { + + return axge_read_register ( axge, offset, value, sizeof ( *value ) ); +} + +/** + * Read two-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value to fill in + * @ret rc Return status code + */ +static inline int axge_read_word ( struct axge_device *axge, + unsigned int offset, uint16_t *value ) { + + return axge_read_register ( axge, offset, value, sizeof ( *value ) ); +} + +/** + * Read four-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value to fill in + * @ret rc Return status code + */ +static inline int axge_read_dword ( struct axge_device *axge, + unsigned int offset, uint32_t *value ) { + + return axge_read_register ( axge, offset, value, sizeof ( *value ) ); +} + +/** + * Write register + * + * @v asix AXGE device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline int axge_write_register ( struct axge_device *axge, + unsigned int offset, void *data, + size_t len ) { + + return usb_control ( axge->usb, AXGE_WRITE_MAC_REGISTER, + offset, len, data, len ); +} + +/** + * Write one-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value + * @ret rc Return status code + */ +static inline int axge_write_byte ( struct axge_device *axge, + unsigned int offset, uint8_t value ) { + + return axge_write_register ( axge, offset, &value, sizeof ( value )); +} + +/** + * Write two-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value + * @ret rc Return status code + */ +static inline int axge_write_word ( struct axge_device *axge, + unsigned int offset, uint16_t value ) { + + return axge_write_register ( axge, offset, &value, sizeof ( value )); +} + +/** + * Write one-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value + * @ret rc Return status code + */ +static inline int axge_write_dword ( struct axge_device *axge, + unsigned int offset, uint32_t value ) { + + return axge_write_register ( axge, offset, &value, sizeof ( value )); +} + +/****************************************************************************** + * + * Link status + * + ****************************************************************************** + */ + +/** + * Get link status + * + * @v asix AXGE device + * @ret rc Return status code + */ +static int axge_check_link ( struct axge_device *axge ) { + struct net_device *netdev = axge->netdev; + uint8_t plsr; + int rc; + + /* Read physical link status register */ + if ( ( rc = axge_read_byte ( axge, AXGE_PLSR, &plsr ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not read PLSR: %s\n", + axge, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + if ( plsr & AXGE_PLSR_EPHY_ANY ) { + DBGC ( axge, "AXGE %p link up (PLSR %02x)\n", axge, plsr ); + netdev_link_up ( netdev ); + } else { + DBGC ( axge, "AXGE %p link down (PLSR %02x)\n", axge, plsr ); + netdev_link_down ( netdev ); + } + + return 0; +} + +/****************************************************************************** + * + * AXGE communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void axge_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct axge_device *axge = container_of ( ep, struct axge_device, + usbnet.intr ); + struct net_device *netdev = axge->netdev; + struct axge_interrupt *intr; + size_t len = iob_len ( iobuf ); + unsigned int link_ok; + + /* Profile completions */ + profile_start ( &axge_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Drop packets with errors */ + if ( rc != 0 ) { + DBGC ( axge, "AXGE %p interrupt failed: %s\n", + axge, strerror ( rc ) ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( len < sizeof ( *intr ) ) { + DBGC ( axge, "AXGE %p underlength interrupt:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + intr = iobuf->data; + + /* Check magic signature */ + if ( intr->magic != cpu_to_le16 ( AXGE_INTR_MAGIC ) ) { + DBGC ( axge, "AXGE %p malformed interrupt:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + + /* Extract link status */ + link_ok = ( intr->link & cpu_to_le16 ( AXGE_INTR_LINK_PPLS ) ); + if ( link_ok && ! netdev_link_ok ( netdev ) ) { + DBGC ( axge, "AXGE %p link up\n", axge ); + netdev_link_up ( netdev ); + } else if ( netdev_link_ok ( netdev ) && ! link_ok ) { + DBGC ( axge, "AXGE %p link down\n", axge ); + netdev_link_down ( netdev ); + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &axge_intr_profiler ); + + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations axge_intr_operations = { + .complete = axge_intr_complete, +}; + +/****************************************************************************** + * + * AXGE data interface + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void axge_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct axge_device *axge = container_of ( ep, struct axge_device, + usbnet.in ); + struct net_device *netdev = axge->netdev; + struct axge_rx_footer *ftr; + struct axge_rx_descriptor *desc; + struct io_buffer *pkt; + unsigned int count; + unsigned int offset; + size_t len; + size_t padded_len; + + /* Profile receive completions */ + profile_start ( &axge_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( axge, "AXGE %p bulk IN failed: %s\n", + axge, strerror ( rc ) ); + goto error; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *ftr ) ) { + DBGC ( axge, "AXGE %p underlength bulk IN:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + + /* Parse ftr, strip ftr and descriptors */ + iob_unput ( iobuf, sizeof ( *ftr ) ); + ftr = ( iobuf->data + iob_len ( iobuf ) ); + count = le16_to_cpu ( ftr->count ); + if ( count == 0 ) { + DBGC ( axge, "AXGE %p zero-packet bulk IN:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + offset = le16_to_cpu ( ftr->offset ); + if ( ( iob_len ( iobuf ) < offset ) || + ( ( iob_len ( iobuf ) - offset ) < ( count * sizeof ( *desc ) ) )){ + DBGC ( axge, "AXGE %p malformed bulk IN footer:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + desc = ( iobuf->data + offset ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - offset ) ); + + /* Process packets */ + for ( ; count-- ; desc++ ) { + + /* Parse descriptor */ + len = ( le16_to_cpu ( desc->len_flags ) & AXGE_RX_LEN_MASK ); + padded_len = ( ( len + AXGE_RX_LEN_PAD_ALIGN - 1 ) & + ~( AXGE_RX_LEN_PAD_ALIGN - 1 ) ); + if ( iob_len ( iobuf ) < padded_len ) { + DBGC ( axge, "AXGE %p malformed bulk IN descriptor:\n", + axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + + /* Check for previous dropped packets */ + if ( desc->len_flags & cpu_to_le16 ( AXGE_RX_CRC_ERROR ) ) + netdev_rx_err ( netdev, NULL, -EIO ); + if ( desc->len_flags & cpu_to_le16 ( AXGE_RX_DROP_ERROR ) ) + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + + /* Allocate new I/O buffer, if applicable */ + if ( count ) { + + /* More packets remain: allocate a new buffer */ + pkt = alloc_iob ( AXGE_IN_RESERVE + len ); + if ( ! pkt ) { + /* Record error and continue */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + iob_pull ( iobuf, padded_len ); + continue; + } + iob_reserve ( pkt, AXGE_IN_RESERVE ); + memcpy ( iob_put ( pkt, len ), iobuf->data, len ); + iob_pull ( iobuf, padded_len ); + + } else { + + /* This is the last (or only) packet: use this buffer */ + iob_unput ( iobuf, ( padded_len - len ) ); + pkt = iob_disown ( iobuf ); + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( pkt ) ); + } + + assert ( iobuf == NULL ); + profile_stop ( &axge_in_profiler ); + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations axge_in_operations = { + .complete = axge_in_complete, +}; + +/** + * Transmit packet + * + * @v asix AXGE device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int axge_out_transmit ( struct axge_device *axge, + struct io_buffer *iobuf ) { + struct axge_tx_header *hdr; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile transmissions */ + profile_start ( &axge_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *hdr ) ) ) != 0 ) + return rc; + hdr = iob_push ( iobuf, sizeof ( *hdr ) ); + hdr->len = cpu_to_le32 ( len ); + hdr->wtf = 0; + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &axge->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &axge_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void axge_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct axge_device *axge = container_of ( ep, struct axge_device, + usbnet.out ); + struct net_device *netdev = axge->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations axge_out_operations = { + .complete = axge_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int axge_open ( struct net_device *netdev ) { + struct axge_device *axge = netdev->priv; + uint16_t rcr; + int rc; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &axge->usbnet ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not open: %s\n", + axge, strerror ( rc ) ); + goto err_open; + } + + /* Set MAC address */ + if ( ( rc = axge_write_register ( axge, AXGE_NIDR, + netdev->ll_addr, ETH_ALEN ) ) !=0){ + DBGC ( axge, "AXGE %p could not set MAC address: %s\n", + axge, strerror ( rc ) ); + goto err_write_mac; + } + + /* Enable receiver */ + rcr = cpu_to_le16 ( AXGE_RCR_PRO | AXGE_RCR_AMALL | + AXGE_RCR_AB | AXGE_RCR_SO ); + if ( ( rc = axge_write_word ( axge, AXGE_RCR, rcr ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not write RCR: %s\n", + axge, strerror ( rc ) ); + goto err_write_rcr; + } + + /* Update link status */ + axge_check_link ( axge ); + + return 0; + + axge_write_word ( axge, AXGE_RCR, 0 ); + err_write_rcr: + err_write_mac: + usbnet_close ( &axge->usbnet ); + err_open: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void axge_close ( struct net_device *netdev ) { + struct axge_device *axge = netdev->priv; + + /* Disable receiver */ + axge_write_word ( axge, AXGE_RCR, 0 ); + + /* Close USB network device */ + usbnet_close ( &axge->usbnet ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int axge_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct axge_device *axge = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = axge_out_transmit ( axge, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void axge_poll ( struct net_device *netdev ) { + struct axge_device *axge = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( axge->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &axge->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** AXGE network device operations */ +static struct net_device_operations axge_operations = { + .open = axge_open, + .close = axge_close, + .transmit = axge_transmit, + .poll = axge_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int axge_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct axge_device *axge; + uint16_t epprcr; + uint16_t msr; + uint8_t csr; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *axge ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &axge_operations ); + netdev->dev = &func->dev; + axge = netdev->priv; + memset ( axge, 0, sizeof ( *axge ) ); + axge->usb = usb; + axge->bus = usb->port->hub->bus; + axge->netdev = netdev; + usbnet_init ( &axge->usbnet, func, &axge_intr_operations, + &axge_in_operations, &axge_out_operations ); + usb_refill_init ( &axge->usbnet.intr, 0, 0, AXGE_INTR_MAX_FILL ); + usb_refill_init ( &axge->usbnet.in, AXGE_IN_RESERVE, + AXGE_IN_MTU, AXGE_IN_MAX_FILL ); + DBGC ( axge, "AXGE %p on %s\n", axge, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &axge->usbnet, config ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not describe: %s\n", + axge, strerror ( rc ) ); + goto err_describe; + } + + /* Fetch MAC address */ + if ( ( rc = axge_read_register ( axge, AXGE_NIDR, netdev->hw_addr, + ETH_ALEN ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not fetch MAC address: %s\n", + axge, strerror ( rc ) ); + goto err_read_mac; + } + + /* Power up PHY */ + if ( ( rc = axge_write_word ( axge, AXGE_EPPRCR, 0 ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not write EPPRCR: %s\n", + axge, strerror ( rc ) ); + goto err_write_epprcr_off; + } + epprcr = cpu_to_le16 ( AXGE_EPPRCR_IPRL ); + if ( ( rc = axge_write_word ( axge, AXGE_EPPRCR, epprcr ) ) != 0){ + DBGC ( axge, "AXGE %p could not write EPPRCR: %s\n", + axge, strerror ( rc ) ); + goto err_write_epprcr_on; + } + mdelay ( AXGE_EPPRCR_DELAY_MS ); + + /* Select clocks */ + csr = ( AXGE_CSR_BCS | AXGE_CSR_ACS ); + if ( ( rc = axge_write_byte ( axge, AXGE_CSR, csr ) ) != 0){ + DBGC ( axge, "AXGE %p could not write CSR: %s\n", + axge, strerror ( rc ) ); + goto err_write_csr; + } + mdelay ( AXGE_CSR_DELAY_MS ); + + /* Configure bulk IN pipeline */ + if ( ( rc = axge_write_register ( axge, AXGE_BICR, &axge_bicr, + sizeof ( axge_bicr ) ) ) != 0 ){ + DBGC ( axge, "AXGE %p could not write BICR: %s\n", + axge, strerror ( rc ) ); + goto err_write_bicr; + } + + /* Set medium status */ + msr = cpu_to_le16 ( AXGE_MSR_GM | AXGE_MSR_FD | AXGE_MSR_RFC | + AXGE_MSR_TFC | AXGE_MSR_RE ); + if ( ( rc = axge_write_word ( axge, AXGE_MSR, msr ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not write MSR: %s\n", + axge, strerror ( rc ) ); + goto err_write_msr; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + /* Update link status */ + axge_check_link ( axge ); + + usb_func_set_drvdata ( func, axge ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_write_msr: + err_write_bicr: + err_write_csr: + err_write_epprcr_on: + err_write_epprcr_off: + err_read_mac: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void axge_remove ( struct usb_function *func ) { + struct axge_device *axge = usb_func_get_drvdata ( func ); + struct net_device *netdev = axge->netdev; + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** AXGE device IDs */ +static struct usb_device_id axge_ids[] = { + { + .name = "ax88179", + .vendor = 0x0b95, + .product = 0x1790, + }, + { + .name = "ax88178a", + .vendor = 0x0b95, + .product = 0x178a, + }, + { + .name = "dub1312", + .vendor = 0x2001, + .product = 0x4a00, + }, + { + .name = "axge-sitecom", + .vendor = 0x0df6, + .product = 0x0072, + }, + { + .name = "axge-samsung", + .vendor = 0x04e8, + .product = 0xa100, + }, + { + .name = "onelinkdock", + .vendor = 0x17ef, + .product = 0x304b, + }, +}; + +/** AXGE driver */ +struct usb_driver axge_driver __usb_driver = { + .ids = axge_ids, + .id_count = ( sizeof ( axge_ids ) / sizeof ( axge_ids[0] ) ), + .class = USB_CLASS_ID ( USB_ANY_ID, USB_ANY_ID, USB_ANY_ID ), + .score = USB_SCORE_NORMAL, + .probe = axge_probe, + .remove = axge_remove, +}; diff --git a/src/drivers/net/axge.h b/src/drivers/net/axge.h new file mode 100644 index 000000000..65bf911c5 --- /dev/null +++ b/src/drivers/net/axge.h @@ -0,0 +1,174 @@ +#ifndef _AXGE_H +#define _AXGE_H + +/** @file + * + * Asix 10/100/1000 USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** Read MAC register */ +#define AXGE_READ_MAC_REGISTER \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + +/** Write MAC register */ +#define AXGE_WRITE_MAC_REGISTER \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + +/** Physical Link Status Register */ +#define AXGE_PLSR 0x02 +#define AXGE_PLSR_EPHY_10 0x10 /**< Ethernet at 10Mbps */ +#define AXGE_PLSR_EPHY_100 0x20 /**< Ethernet at 100Mbps */ +#define AXGE_PLSR_EPHY_1000 0x40 /**< Ethernet at 1000Mbps */ +#define AXGE_PLSR_EPHY_ANY \ + ( AXGE_PLSR_EPHY_10 | \ + AXGE_PLSR_EPHY_100 | \ + AXGE_PLSR_EPHY_1000 ) + +/** RX Control Register */ +#define AXGE_RCR 0x0b +#define AXGE_RCR_PRO 0x0001 /**< Promiscuous mode */ +#define AXGE_RCR_AMALL 0x0002 /**< Accept all multicasts */ +#define AXGE_RCR_AB 0x0008 /**< Accept broadcasts */ +#define AXGE_RCR_SO 0x0080 /**< Start operation */ + +/** Node ID Register */ +#define AXGE_NIDR 0x10 + +/** Medium Status Register */ +#define AXGE_MSR 0x22 +#define AXGE_MSR_GM 0x0001 /**< Gigabit mode */ +#define AXGE_MSR_FD 0x0002 /**< Full duplex */ +#define AXGE_MSR_RFC 0x0010 /**< RX flow control enable */ +#define AXGE_MSR_TFC 0x0020 /**< TX flow control enable */ +#define AXGE_MSR_RE 0x0100 /**< Receive enable */ + +/** Ethernet PHY Power and Reset Control Register */ +#define AXGE_EPPRCR 0x26 +#define AXGE_EPPRCR_IPRL 0x0020 /**< Undocumented */ + +/** Delay after initialising EPPRCR */ +#define AXGE_EPPRCR_DELAY_MS 200 + +/** Bulk IN Control Register (undocumented) */ +#define AXGE_BICR 0x2e + +/** Bulk IN Control (undocumented) */ +struct axge_bulk_in_control { + /** Control */ + uint8_t ctrl; + /** Timer */ + uint16_t timer; + /** Size */ + uint8_t size; + /** Inter-frame gap */ + uint8_t ifg; +} __attribute__ (( packed )); + +/** Clock Select Register (undocumented) */ +#define AXGE_CSR 0x33 +#define AXGE_CSR_BCS 0x01 /**< Undocumented */ +#define AXGE_CSR_ACS 0x02 /**< Undocumented */ + +/** Delay after initialising CSR */ +#define AXGE_CSR_DELAY_MS 100 + +/** Transmit packet header */ +struct axge_tx_header { + /** Packet length */ + uint32_t len; + /** Answers on a postcard, please */ + uint32_t wtf; +} __attribute__ (( packed )); + +/** Receive packet footer */ +struct axge_rx_footer { + /** Packet count */ + uint16_t count; + /** Header offset */ + uint16_t offset; +} __attribute__ (( packed )); + +/** Receive packet descriptor */ +struct axge_rx_descriptor { + /** Checksum information */ + uint16_t check; + /** Length and error flags */ + uint16_t len_flags; +} __attribute__ (( packed )); + +/** Receive packet length mask */ +#define AXGE_RX_LEN_MASK 0x1fff + +/** Receive packet length alignment */ +#define AXGE_RX_LEN_PAD_ALIGN 8 + +/** Receive packet CRC error */ +#define AXGE_RX_CRC_ERROR 0x2000 + +/** Receive packet dropped error */ +#define AXGE_RX_DROP_ERROR 0x8000 + +/** Interrupt data */ +struct axge_interrupt { + /** Magic signature */ + uint16_t magic; + /** Link state */ + uint16_t link; + /** PHY register MR01 */ + uint16_t mr01; + /** PHY register MR05 */ + uint16_t mr05; +} __attribute__ (( packed )); + +/** Interrupt magic signature */ +#define AXGE_INTR_MAGIC 0x00a1 + +/** Link is up */ +#define AXGE_INTR_LINK_PPLS 0x0001 + +/** An AXGE network device */ +struct axge_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; +}; + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define AXGE_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define AXGE_IN_MAX_FILL 8 + +/** Bulk IN buffer size + * + * This is a policy decision. + */ +#define AXGE_IN_MTU 2048 + +/** Amount of space to reserve at start of bulk IN buffers + * + * This is required to allow for protocols such as ARP which may reuse + * a received I/O buffer for transmission. + */ +#define AXGE_IN_RESERVE sizeof ( struct axge_tx_header ) + +#endif /* _AXGE_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 0c95e7a89..4dae35b4c 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -190,6 +190,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_flexboot_nodnic ( ERRFILE_DRIVER | 0x007e0000 ) #define ERRFILE_virtio_pci ( ERRFILE_DRIVER | 0x007f0000 ) #define ERRFILE_pciea ( ERRFILE_DRIVER | 0x00c00000 ) +#define ERRFILE_axge ( ERRFILE_DRIVER | 0x00c10000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 31d4a7b8db1bd186e3a03be4b616dfb185f3f1ae Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 May 2016 13:43:33 +0100 Subject: [PATCH 230/591] [arm] Use correct DHCP client architecture values Signed-off-by: Michael Brown --- src/arch/arm32/include/efi/ipxe/dhcp_arch.h | 2 +- src/arch/arm64/include/efi/ipxe/dhcp_arch.h | 2 +- src/include/ipxe/dhcp.h | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h index f403d4ce8..f9baab4fa 100644 --- a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h @@ -39,7 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) #define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_EFI ) + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_ARM32 ) #define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) diff --git a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h index f403d4ce8..48a36d052 100644 --- a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h @@ -39,7 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) #define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_EFI ) + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_ARM64 ) #define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index 0bd7c1dca..a154bba1c 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -272,6 +272,10 @@ enum dhcp_client_architecture_values { DHCP_CLIENT_ARCHITECTURE_XSCALE = 0x0008, /** EFI x86-64 */ DHCP_CLIENT_ARCHITECTURE_X86_64 = 0x0009, + /** EFI 32-bit ARM */ + DHCP_CLIENT_ARCHITECTURE_ARM32 = 0x000a, + /** EFI 64-bit ARM */ + DHCP_CLIENT_ARCHITECTURE_ARM64 = 0x000b, }; /** Client network device interface */ From af9afd0a86aeac1eed28b5028c3de669515fc7fc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 May 2016 13:58:37 +0100 Subject: [PATCH 231/591] [dhcp] Fix definitions for x86_64 and EFI BC client architectures There has been a longstanding disagreement between RFC4578 and the IANA "Processor Architecture Types" registry. RFC4578 section 2.1 defines type 7 as "EFI BC" and type 9 as "EFI x86-64"; the IANA registry quotes RFC4578 as its source but has these values erroneously swapped. The EDK2 codebase uses the IANA values. As of March 2016, RFC4578 has been modified by an errata to match the values as recorded in the IANA registry. Fix our definitions to match the consensus values. Signed-off-by: Michael Brown --- src/include/ipxe/dhcp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index a154bba1c..693aa7e73 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -266,12 +266,12 @@ enum dhcp_client_architecture_values { DHCP_CLIENT_ARCHITECTURE_LC = 0x0005, /** EFI IA32 */ DHCP_CLIENT_ARCHITECTURE_IA32 = 0x0006, - /** EFI BC */ - DHCP_CLIENT_ARCHITECTURE_EFI = 0x0007, + /** EFI x86-64 */ + DHCP_CLIENT_ARCHITECTURE_X86_64 = 0x0007, /** EFI Xscale */ DHCP_CLIENT_ARCHITECTURE_XSCALE = 0x0008, - /** EFI x86-64 */ - DHCP_CLIENT_ARCHITECTURE_X86_64 = 0x0009, + /** EFI BC */ + DHCP_CLIENT_ARCHITECTURE_EFI = 0x0009, /** EFI 32-bit ARM */ DHCP_CLIENT_ARCHITECTURE_ARM32 = 0x000a, /** EFI 64-bit ARM */ From aa4b038c70ca3e23c3126fda4318e9bbe77f3ea2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 May 2016 13:04:26 +0100 Subject: [PATCH 232/591] [efi] Expose DHCP packets via the Apple NetBoot protocol Mac OS X uses non-standard EFI protocols to obtain the DHCP packets from the UEFI firmware. Originally-implemented-by: Michael Kuron Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/AppleNetBoot.h | 46 ++++++++++ src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_debug.c | 2 + src/interface/efi/efi_guid.c | 5 ++ src/interface/efi/efi_pxe.c | 94 +++++++++++++++++++- 5 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 src/include/ipxe/efi/Protocol/AppleNetBoot.h diff --git a/src/include/ipxe/efi/Protocol/AppleNetBoot.h b/src/include/ipxe/efi/Protocol/AppleNetBoot.h new file mode 100644 index 000000000..144beff1c --- /dev/null +++ b/src/include/ipxe/efi/Protocol/AppleNetBoot.h @@ -0,0 +1,46 @@ +#ifndef __EFI_APPLE_NET_BOOT_PROTOCOL_H__ +#define __EFI_APPLE_NET_BOOT_PROTOCOL_H__ + +/** @file + * + * Apple Net Boot Protocol + * + */ + +FILE_LICENCE ( BSD3 ); + +#define EFI_APPLE_NET_BOOT_PROTOCOL_GUID \ + { 0x78ee99fb, 0x6a5e, 0x4186, \ + { 0x97, 0xde, 0xcd, 0x0a, 0xba, 0x34, 0x5a, 0x74 } } + +typedef struct _EFI_APPLE_NET_BOOT_PROTOCOL EFI_APPLE_NET_BOOT_PROTOCOL; + +/** + Get a DHCP packet obtained by the firmware during NetBoot. + + @param This A pointer to the APPLE_NET_BOOT_PROTOCOL instance. + @param BufferSize A pointer to the size of the buffer in bytes. + @param DataBuffer The memory buffer to copy the packet to. If it is + NULL, then the size of the packet is returned + in BufferSize. + @retval EFI_SUCCESS The packet was copied. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the + current packet. BufferSize has been + updated with the size needed to + complete the request. +**/ +typedef +EFI_STATUS +(EFIAPI *GET_DHCP_RESPONSE) ( + IN EFI_APPLE_NET_BOOT_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *DataBuffer + ); + +struct _EFI_APPLE_NET_BOOT_PROTOCOL +{ + GET_DHCP_RESPONSE GetDhcpResponse; + GET_DHCP_RESPONSE GetBsdpResponse; +}; + +#endif /*__EFI_APPLE_NET_BOOT_PROTOCOL_H__ */ diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 390d4e6e6..db9943a42 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -154,6 +154,7 @@ struct efi_config_table { #define EEFI( efirc ) EPLATFORM ( EINFO_EPLATFORM, efirc ) extern EFI_GUID efi_absolute_pointer_protocol_guid; +extern EFI_GUID efi_apple_net_boot_protocol_guid; extern EFI_GUID efi_arp_protocol_guid; extern EFI_GUID efi_arp_service_binding_protocol_guid; extern EFI_GUID efi_block_io_protocol_guid; diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 84160d643..19531fdc1 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -71,6 +71,8 @@ struct efi_well_known_guid { static struct efi_well_known_guid efi_well_known_guids[] = { { &efi_absolute_pointer_protocol_guid, "AbsolutePointer" }, + { &efi_apple_net_boot_protocol_guid, + "AppleNetBoot" }, { &efi_arp_protocol_guid, "Arp" }, { &efi_arp_service_binding_protocol_guid, diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 39da5efe0..62ee5a517 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -84,6 +85,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); EFI_GUID efi_absolute_pointer_protocol_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; +/** Apple NetBoot protocol GUID */ +EFI_GUID efi_apple_net_boot_protocol_guid + = EFI_APPLE_NET_BOOT_PROTOCOL_GUID; + /** ARP protocol GUID */ EFI_GUID efi_arp_protocol_guid = EFI_ARP_PROTOCOL_GUID; diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index 1847e3fd1..a1f81df59 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -80,6 +81,8 @@ struct efi_pxe { EFI_PXE_BASE_CODE_PROTOCOL base; /** PXE base code mode */ EFI_PXE_BASE_CODE_MODE mode; + /** Apple NetBoot protocol */ + EFI_APPLE_NET_BOOT_PROTOCOL apple; /** TCP/IP network-layer protocol */ struct tcpip_net_protocol *tcpip; @@ -1498,6 +1501,83 @@ static EFI_PXE_BASE_CODE_PROTOCOL efi_pxe_base_code_protocol = { .SetPackets = efi_pxe_set_packets, }; +/****************************************************************************** + * + * Apple NetBoot protocol + * + ****************************************************************************** + */ + +/** + * Get DHCP/BSDP response + * + * @v packet Packet + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_apple_get_response ( EFI_PXE_BASE_CODE_PACKET *packet, UINTN *len, + VOID *data ) { + + /* Check length */ + if ( *len < sizeof ( *packet ) ) { + *len = sizeof ( *packet ); + return EFI_BUFFER_TOO_SMALL; + } + + /* Copy packet */ + memcpy ( data, packet, sizeof ( *packet ) ); + *len = sizeof ( *packet ); + + return EFI_SUCCESS; +} + +/** + * Get DHCP response + * + * @v apple Apple NetBoot protocol + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_apple_get_dhcp_response ( EFI_APPLE_NET_BOOT_PROTOCOL *apple, + UINTN *len, VOID *data ) { + struct efi_pxe *pxe = container_of ( apple, struct efi_pxe, apple ); + + return efi_apple_get_response ( &pxe->mode.DhcpAck, len, data ); +} + +/** + * Get BSDP response + * + * @v apple Apple NetBoot protocol + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_apple_get_bsdp_response ( EFI_APPLE_NET_BOOT_PROTOCOL *apple, + UINTN *len, VOID *data ) { + struct efi_pxe *pxe = container_of ( apple, struct efi_pxe, apple ); + + return efi_apple_get_response ( &pxe->mode.PxeReply, len, data ); +} + +/** Apple NetBoot protocol */ +static EFI_APPLE_NET_BOOT_PROTOCOL efi_apple_net_boot_protocol = { + .GetDhcpResponse = efi_apple_get_dhcp_response, + .GetBsdpResponse = efi_apple_get_bsdp_response, +}; + +/****************************************************************************** + * + * Installer + * + ****************************************************************************** + */ + /** * Install PXE base code protocol * @@ -1526,6 +1606,8 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { pxe->handle = handle; memcpy ( &pxe->base, &efi_pxe_base_code_protocol, sizeof ( pxe->base )); pxe->base.Mode = &pxe->mode; + memcpy ( &pxe->apple, &efi_apple_net_boot_protocol, + sizeof ( pxe->apple ) ); pxe->buf.op = &efi_pxe_buf_operations; intf_init ( &pxe->tftp, &efi_pxe_tftp_desc, &pxe->refcnt ); intf_init ( &pxe->udp, &efi_pxe_udp_desc, &pxe->refcnt ); @@ -1545,7 +1627,9 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { /* Install PXE base code protocol */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &handle, &efi_pxe_base_code_protocol_guid, &pxe->base, + &handle, + &efi_pxe_base_code_protocol_guid, &pxe->base, + &efi_apple_net_boot_protocol_guid, &pxe->apple, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pxe, "PXE %s could not install base code protocol: %s\n", @@ -1560,7 +1644,9 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { return 0; bs->UninstallMultipleProtocolInterfaces ( - handle, &efi_pxe_base_code_protocol_guid, &pxe->base, + handle, + &efi_pxe_base_code_protocol_guid, &pxe->base, + &efi_apple_net_boot_protocol_guid, &pxe->apple, NULL ); err_install_protocol: ref_put ( &pxe->refcnt ); @@ -1590,7 +1676,9 @@ void efi_pxe_uninstall ( EFI_HANDLE handle ) { /* Uninstall PXE base code protocol */ bs->UninstallMultipleProtocolInterfaces ( - handle, &efi_pxe_base_code_protocol_guid, &pxe->base, + handle, + &efi_pxe_base_code_protocol_guid, &pxe->base, + &efi_apple_net_boot_protocol_guid, &pxe->apple, NULL ); /* Remove from list and drop list's reference */ From f6e8b800be5d8656aa2b10827cf7ea93cfcf5048 Mon Sep 17 00:00:00 2001 From: Vinson Lee Date: Fri, 3 Jun 2016 18:09:54 +0100 Subject: [PATCH 233/591] [build] Remove nested "my" declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix build error with perl >= 5.23.2: Can't redeclare "my" in "my" at ./util/parserom.pl line 160 Signed-off-by: Vinson Lee Reviewed-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/util/parserom.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/parserom.pl b/src/util/parserom.pl index 28df60652..5a849a540 100755 --- a/src/util/parserom.pl +++ b/src/util/parserom.pl @@ -157,7 +157,7 @@ sub process_isa_rom { # Output Makefile rules for the specified ROM declarations sub print_make_rules { - my ( $state, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; + my ( $state, $image, $desc, $vendor, $device, $dup ) = @_; unless ( $state->{'is_header_printed'} ) { print "# NIC\t\n"; print "# NIC\tfamily\t$state->{family}\n"; From 2c197517f2a82970ab6866e197f06a3099418324 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Jun 2016 08:39:25 +0100 Subject: [PATCH 234/591] [libc] Always use a non-zero seed for the (non-crypto) RNG The non-cryptographic RNG implemented by random() has the property that a seed value of zero will result in a generated sequence of all-zero values. This situation can arise if currticks() returns zero at start of day. Work around this problem by falling back to a fixed non-zero seed if necessary. This has no effect on the separate DRBG used by cryptographic code. Signed-off-by: Michael Brown --- src/core/random.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/random.c b/src/core/random.c index a74175a79..975a03cf5 100644 --- a/src/core/random.c +++ b/src/core/random.c @@ -18,6 +18,8 @@ static int32_t rnd_seed = 0; */ void srandom ( unsigned int seed ) { rnd_seed = seed; + if ( ! rnd_seed ) + rnd_seed = 4; /* Chosen by fair dice roll */ } /** From f76210961c76a3f54f673d668e21e30d2eed9612 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Jun 2016 09:36:28 +0100 Subject: [PATCH 235/591] [pci] Support systems with multiple PCI root bridges Extend the 16-bit PCI bus:dev.fn address to a 32-bit seg:bus:dev.fn address, assuming a segment value of zero in contexts where multiple segments are unsupported by the underlying data structures (e.g. in the iBFT or BOFM tables). Signed-off-by: Michael Brown --- src/arch/x86/core/pcidirect.c | 6 +- src/core/settings.c | 13 ++- src/drivers/bus/pci.c | 8 +- src/drivers/bus/pci_settings.c | 2 +- src/drivers/net/phantom/phantom.c | 12 +-- src/include/ipxe/pci.h | 16 ++-- src/interface/bofm/bofm.c | 4 +- src/interface/efi/efi_pci.c | 137 ++++++++++++++++++++++++++---- src/tests/settings_test.c | 4 +- 9 files changed, 160 insertions(+), 42 deletions(-) diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 9b8e6b1d9..0d09be84b 100644 --- a/src/arch/x86/core/pcidirect.c +++ b/src/arch/x86/core/pcidirect.c @@ -36,10 +36,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Prepare for Type 1 PCI configuration space access * * @v pci PCI device - * @v where Location within PCI configuration space + * @v where Location within PCI configuration space */ void pcidirect_prepare ( struct pci_device *pci, int where ) { - outl ( ( 0x80000000 | ( pci->busdevfn << 8 ) | ( where & ~3 ) ), + uint16_t busdevfn = ( pci->busdevfn & 0xffff ); + + outl ( ( 0x80000000 | ( busdevfn << 8 ) | ( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS ); } diff --git a/src/core/settings.c b/src/core/settings.c index 757555781..9cae0cae3 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -2232,6 +2232,10 @@ static int format_busdevfn_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { unsigned long busdevfn; + unsigned int seg; + unsigned int bus; + unsigned int slot; + unsigned int func; int check_len; /* Extract numeric value */ @@ -2240,9 +2244,14 @@ static int format_busdevfn_setting ( const struct setting_type *type __unused, return check_len; assert ( check_len == ( int ) raw_len ); + /* Extract PCI address components */ + seg = PCI_SEG ( busdevfn ); + bus = PCI_BUS ( busdevfn ); + slot = PCI_SLOT ( busdevfn ); + func = PCI_FUNC ( busdevfn ); + /* Format value */ - return snprintf ( buf, len, "%02lx:%02lx.%lx", PCI_BUS ( busdevfn ), - PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) ); + return snprintf ( buf, len, "%04x:%02x:%02x.%x", seg, bus, slot, func ); } /** PCI bus:dev.fn setting type */ diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 6fbedd940..06b36a770 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -175,7 +175,7 @@ void adjust_pci_device ( struct pci_device *pci ) { * @ret rc Return status code */ int pci_read_config ( struct pci_device *pci ) { - uint16_t busdevfn; + uint32_t busdevfn; uint8_t hdrtype; uint32_t tmp; @@ -203,8 +203,8 @@ int pci_read_config ( struct pci_device *pci ) { pci_read_bases ( pci ); /* Initialise generic device component */ - snprintf ( pci->dev.name, sizeof ( pci->dev.name ), - "PCI%02x:%02x.%x", PCI_BUS ( pci->busdevfn ), + snprintf ( pci->dev.name, sizeof ( pci->dev.name ), "%04x:%02x:%02x.%x", + PCI_SEG ( pci->busdevfn ), PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ) ); pci->dev.desc.bus_type = BUS_TYPE_PCI; pci->dev.desc.location = pci->busdevfn; @@ -232,7 +232,7 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { /* Determine number of PCI buses */ if ( ! end ) - end = PCI_BUSDEVFN ( pci_num_bus(), 0, 0 ); + end = PCI_BUSDEVFN ( 0, pci_num_bus(), 0, 0 ); /* Find next PCI device, if any */ for ( ; busdevfn < end ; busdevfn++ ) { diff --git a/src/drivers/bus/pci_settings.c b/src/drivers/bus/pci_settings.c index 1cb9fa5a3..98005559d 100644 --- a/src/drivers/bus/pci_settings.c +++ b/src/drivers/bus/pci_settings.c @@ -70,7 +70,7 @@ static int pci_settings_fetch ( struct settings *settings __unused, unsigned int i; /* Extract busdevfn, offset, and length from tag */ - tag_busdevfn = ( ( setting->tag >> 16 ) & 0xffff ); + tag_busdevfn = ( setting->tag >> 16 ); tag_offset = ( ( setting->tag >> 8 ) & 0xff ); tag_len = ( ( setting->tag >> 0 ) & 0xff ); diff --git a/src/drivers/net/phantom/phantom.c b/src/drivers/net/phantom/phantom.c index 38b66743c..781049ff4 100644 --- a/src/drivers/net/phantom/phantom.c +++ b/src/drivers/net/phantom/phantom.c @@ -2060,6 +2060,7 @@ static int phantom_probe ( struct pci_device *pci ) { struct net_device *netdev; struct phantom_nic *phantom; struct settings *parent_settings; + unsigned int busdevfn; int rc; /* Allocate Phantom device */ @@ -2090,19 +2091,20 @@ static int phantom_probe ( struct pci_device *pci ) { * B2 will have this fixed; remove this hack when B1 is no * longer in use. */ - if ( PCI_FUNC ( pci->busdevfn ) == 0 ) { + busdevfn = pci->busdevfn; + if ( PCI_FUNC ( busdevfn ) == 0 ) { unsigned int i; for ( i = 0 ; i < 8 ; i++ ) { uint32_t temp; pci->busdevfn = - PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), - PCI_SLOT ( pci->busdevfn ), i ); + PCI_BUSDEVFN ( PCI_SEG ( busdevfn ), + PCI_BUS ( busdevfn ), + PCI_SLOT ( busdevfn ), i ); pci_read_config_dword ( pci, 0xc8, &temp ); pci_read_config_dword ( pci, 0xc8, &temp ); pci_write_config_dword ( pci, 0xc8, 0xf1000 ); } - pci->busdevfn = PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), - PCI_SLOT ( pci->busdevfn ), 0 ); + pci->busdevfn = busdevfn; } /* Initialise the command PEG */ diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index bf0e81dfa..ddd8c8d1e 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -195,8 +195,8 @@ struct pci_device { uint32_t class; /** Interrupt number */ uint8_t irq; - /** Bus, device, and function (bus:dev.fn) number */ - uint16_t busdevfn; + /** Segment, bus, device, and function (bus:dev.fn) number */ + uint32_t busdevfn; /** Driver for this device */ struct pci_driver *driver; /** Driver-private data @@ -241,11 +241,13 @@ struct pci_driver { /** Declare a fallback PCI driver */ #define __pci_driver_fallback __table_entry ( PCI_DRIVERS, 02 ) +#define PCI_SEG( busdevfn ) ( ( (busdevfn) >> 16 ) & 0xffff ) #define PCI_BUS( busdevfn ) ( ( (busdevfn) >> 8 ) & 0xff ) #define PCI_SLOT( busdevfn ) ( ( (busdevfn) >> 3 ) & 0x1f ) #define PCI_FUNC( busdevfn ) ( ( (busdevfn) >> 0 ) & 0x07 ) -#define PCI_BUSDEVFN( bus, slot, func ) \ - ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) +#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 ) @@ -271,12 +273,12 @@ struct pci_driver { PCI_ID( _vendor, _device, _name, _description, _data ) /** PCI device debug message format */ -#define PCI_FMT "PCI %02x:%02x.%x" +#define PCI_FMT "%04x:%02x:%02x.%x" /** PCI device debug message arguments */ #define PCI_ARGS( pci ) \ - PCI_BUS ( (pci)->busdevfn ), PCI_SLOT ( (pci)->busdevfn ), \ - PCI_FUNC ( (pci)->busdevfn ) + PCI_SEG ( (pci)->busdevfn ), PCI_BUS ( (pci)->busdevfn ), \ + PCI_SLOT ( (pci)->busdevfn ), PCI_FUNC ( (pci)->busdevfn ) extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, diff --git a/src/interface/bofm/bofm.c b/src/interface/bofm/bofm.c index 545088dc6..54039193a 100644 --- a/src/interface/bofm/bofm.c +++ b/src/interface/bofm/bofm.c @@ -313,12 +313,12 @@ int bofm ( userptr_t bofmtab, struct pci_device *pci ) { } DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n", en.slot, ( en.port + 1 ), - ( ( en.slot || en.port ) ? "" : "(?)" ), + ( ( en.slot || en.port ) ? "" : "(?)" ), 0, PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), PCI_FUNC ( en.busdevfn ), en.mport ); bofm = bofm_find_busdevfn ( en.busdevfn ); if ( ! bofm ) { - DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", + DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0, PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), PCI_FUNC ( en.busdevfn ), en.mport ); continue; diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index be305ba6c..9f0851a56 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -61,58 +61,157 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ -/** PCI root bridge I/O protocol */ -static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci; -EFI_REQUEST_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); +/** + * Locate EFI PCI root bridge I/O protocol + * + * @v pci PCI device + * @ret handle EFI PCI root bridge handle + * @ret root EFI PCI root bridge I/O protocol, or NULL if not found + * @ret rc Return status code + */ +static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *handles; + UINTN num_handles; + union { + void *interface; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + } u; + EFI_STATUS efirc; + UINTN i; + int rc; + /* Enumerate all handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, + &efi_pci_root_bridge_io_protocol_guid, + NULL, &num_handles, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI cannot locate root bridges: %s\n", + strerror ( rc ) ); + goto err_locate; + } + + /* Look for matching root bridge I/O protocol */ + for ( i = 0 ; i < num_handles ; i++ ) { + *handle = handles[i]; + if ( ( efirc = bs->OpenProtocol ( *handle, + &efi_pci_root_bridge_io_protocol_guid, + &u.interface, efi_image_handle, *handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI cannot open %s: %s\n", + efi_handle_name ( *handle ), strerror ( rc ) ); + continue; + } + if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) { + *root = u.root; + bs->FreePool ( handles ); + return 0; + } + bs->CloseProtocol ( *handle, + &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, *handle ); + } + DBGC ( pci, "EFIPCI found no root bridge for " PCI_FMT "\n", + PCI_ARGS ( pci ) ); + rc = -ENOENT; + + bs->FreePool ( handles ); + err_locate: + return rc; +} + +/** + * Calculate EFI PCI configuration space address + * + * @v pci PCI device + * @v location Encoded offset and width + * @ret address EFI PCI address + */ static unsigned long efipci_address ( struct pci_device *pci, unsigned long location ) { + return EFI_PCI_ADDRESS ( PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ), EFIPCI_OFFSET ( location ) ); } +/** + * Read from PCI configuration space + * + * @v pci PCI device + * @v location Encoded offset and width + * @ret value Value + * @ret rc Return status code + */ int efipci_read ( struct pci_device *pci, unsigned long location, void *value ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; EFI_STATUS efirc; int rc; - if ( ! efipci ) - return -ENOTSUP; + /* Identify root bridge */ + if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + goto err_root; - if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ), - efipci_address ( pci, location ), 1, - value ) ) != 0 ) { + /* Read from configuration space */ + if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + value ) ) != 0 ) { rc = -EEFI ( efirc ); DBG ( "EFIPCI config read from " PCI_FMT " offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); - return -EIO; + goto err_read; } - return 0; + err_read: + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); + err_root: + return rc; } +/** + * Write to PCI configuration space + * + * @v pci PCI device + * @v location Encoded offset and width + * @v value Value + * @ret rc Return status code + */ int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; EFI_STATUS efirc; int rc; - if ( ! efipci ) - return -ENOTSUP; + /* Identify root bridge */ + if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + goto err_root; - if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), - efipci_address ( pci, location ), 1, - &value ) ) != 0 ) { + /* Read from configuration space */ + if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + &value ) ) != 0 ) { rc = -EEFI ( efirc ); DBG ( "EFIPCI config write to " PCI_FMT " offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); - return -EIO; + goto err_write; } - return 0; + err_write: + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); + err_root: + return rc; } PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus ); @@ -146,6 +245,7 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, void *interface; } pci_io; UINTN pci_segment, pci_bus, pci_dev, pci_fn; + unsigned int busdevfn; EFI_STATUS efirc; int rc; @@ -190,7 +290,8 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ - pci_init ( pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); + busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); + pci_init ( pci, busdevfn ); if ( ( rc = pci_read_config ( pci ) ) != 0 ) { DBGC ( device, "EFIPCI %s cannot read PCI configuration: %s\n", efi_handle_name ( device ), strerror ( rc ) ); diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 89203d42d..828901b06 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -422,7 +422,9 @@ static void settings_test_exec ( void ) { /* "busdevfn" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_busdevfn_setting, - RAW ( 0x03, 0x45 ), "03:08.5" ); + RAW ( 0x03, 0x45 ), "0000:03:08.5" ); + fetchf_ok ( &test_settings, &test_busdevfn_setting, + RAW ( 0x00, 0x02, 0x0a, 0x21 ), "0002:0a:04.1" ); /* Clear and unregister test settings block */ clear_settings ( &test_settings ); From b42e71921fb8169f2c5a409bd095f10bb3c0f1ac Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Jun 2016 12:20:35 +0100 Subject: [PATCH 236/591] [http] Accept headers with no whitespace following the colon Reported-by: Raphael Cohn Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index 57b897620..b1f74bc4f 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -1201,13 +1201,17 @@ static int http_parse_header ( struct http_transaction *http, char *line ) { DBGC2 ( http, "HTTP %p RX %s\n", http, line ); /* Extract header name */ - sep = strstr ( line, ": " ); + sep = strchr ( line, ':' ); if ( ! sep ) { DBGC ( http, "HTTP %p malformed header \"%s\"\n", http, line ); return -EINVAL_HEADER; } *sep = '\0'; - line = ( sep + 2 /* ": " */ ); + + /* Extract remainder of line */ + line = ( sep + 1 ); + while ( isspace ( *line ) ) + line++; /* Process header, if recognised */ for_each_table_entry ( header, HTTP_RESPONSE_HEADERS ) { From 5c2a959a72e999b5aedf34b416960260d272a9df Mon Sep 17 00:00:00 2001 From: Leendert van Doorn Date: Fri, 10 Jun 2016 08:54:25 -0500 Subject: [PATCH 237/591] [tg3] Fix address truncation bug on 64-bit machines Signed-off-by: Leendert van Doorn Signed-off-by: Michael Brown --- src/drivers/net/tg3/tg3.c | 2 +- src/drivers/net/tg3/tg3.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/tg3/tg3.c b/src/drivers/net/tg3/tg3.c index 42bfa2d99..824c9b1c0 100644 --- a/src/drivers/net/tg3/tg3.c +++ b/src/drivers/net/tg3/tg3.c @@ -545,7 +545,7 @@ static int tg3_test_dma(struct tg3 *tp) goto out_nofree; } buf_dma = virt_to_bus(buf); - DBGC2(tp->dev, "dma test buffer, virt: %p phys: %#08x\n", buf, buf_dma); + DBGC2(tp->dev, "dma test buffer, virt: %p phys: %#016lx\n", buf, buf_dma); if (tg3_flag(tp, 57765_PLUS)) { tp->dma_rwctrl = DMA_RWCTRL_DIS_CACHE_ALIGNMENT; diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index 0c3d23bb7..be02c5719 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -2788,7 +2788,7 @@ struct tg3_hw_stats { u8 __reserved4[0xb00-0x9c8]; }; -typedef u32 dma_addr_t; +typedef unsigned long dma_addr_t; /* 'mapping' is superfluous as the chip does not write into * the tx/rx post rings so we could just fetch it from there. From 188789eb3cb83496bd48847da59c74e3f06d413e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Jun 2016 17:27:06 +0100 Subject: [PATCH 238/591] [tcp] Send TCP keepalives on idle established connections In some circumstances, intermediate devices may lose state in a way that temporarily prevents the successful delivery of packets from a TCP peer. For example, a firewall may drop a NAT forwarding table entry. Since iPXE spends most of its time downloading files (and hence purely receiving data, sending only TCP ACKs), this can easily happen in a situation in which there is no reason for iPXE's TCP stack to generate any retransmissions. The temporary loss of connectivity can therefore effectively become permanent. Work around this problem by sending TCP keepalives after a period of inactivity on an established connection. TCP keepalives usually send a single garbage byte in sequence number space that has already been ACKed by the peer. Since we do not need to elicit a response from the peer, we instead send pure ACKs (with no garbage data) in order to keep the transmit code path simple. Originally-implemented-by: Ladi Prosek Debugged-by: Ladi Prosek Signed-off-by: Michael Brown --- src/include/ipxe/tcp.h | 8 ++++++++ src/net/tcp.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h index 9d9aecd07..21be3ca8a 100644 --- a/src/include/ipxe/tcp.h +++ b/src/include/ipxe/tcp.h @@ -378,6 +378,14 @@ struct tcp_options { */ #define TCP_MSL ( 2 * 60 * TICKS_PER_SEC ) +/** + * TCP keepalive period + * + * We send keepalive ACKs after this period of inactivity has elapsed + * on an established connection. + */ +#define TCP_KEEPALIVE_DELAY ( 15 * TICKS_PER_SEC ) + /** * TCP maximum header length * diff --git a/src/net/tcp.c b/src/net/tcp.c index 68128e84a..37a202ef1 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -113,6 +113,8 @@ struct tcp_connection { struct process process; /** Retransmission timer */ struct retry_timer timer; + /** Keepalive timer */ + struct retry_timer keepalive; /** Shutdown (TIME_WAIT) timer */ struct retry_timer wait; @@ -177,6 +179,7 @@ static struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.xfer" }; static struct process_descriptor tcp_process_desc; static struct interface_descriptor tcp_xfer_desc; static void tcp_expired ( struct retry_timer *timer, int over ); +static void tcp_keepalive_expired ( struct retry_timer *timer, int over ); static void tcp_wait_expired ( struct retry_timer *timer, int over ); static struct tcp_connection * tcp_demux ( unsigned int local_port ); static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, @@ -284,6 +287,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt ); process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt ); timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt ); + timer_init ( &tcp->keepalive, tcp_keepalive_expired, &tcp->refcnt ); timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt ); tcp->prev_tcp_state = TCP_CLOSED; tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); @@ -380,6 +384,7 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { /* Remove from list and drop reference */ process_del ( &tcp->process ); stop_timer ( &tcp->timer ); + stop_timer ( &tcp->keepalive ); stop_timer ( &tcp->wait ); list_del ( &tcp->list ); ref_put ( &tcp->refcnt ); @@ -394,6 +399,9 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 ); + /* Stop keepalive timer */ + stop_timer ( &tcp->keepalive ); + /* If we have no data remaining to send, start sending FIN */ if ( list_empty ( &tcp->tx_queue ) && ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) { @@ -801,6 +809,32 @@ static void tcp_expired ( struct retry_timer *timer, int over ) { } } +/** + * Keepalive timer expired + * + * @v timer Keepalive timer + * @v over Failure indicator + */ +static void tcp_keepalive_expired ( struct retry_timer *timer, + int over __unused ) { + struct tcp_connection *tcp = + container_of ( timer, struct tcp_connection, keepalive ); + + DBGC ( tcp, "TCP %p sending keepalive\n", tcp ); + + /* Reset keepalive timer */ + start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY ); + + /* Send keepalive. We do this only to preserve or restore + * state in intermediate devices (e.g. firewall NAT tables); + * we don't actually care about eliciting a response to verify + * that the peer is still alive. We therefore send just a + * pure ACK, to keep our transmit path simple. + */ + tcp->flags |= TCP_ACK_PENDING; + tcp_xmit ( tcp ); +} + /** * Shutdown timer expired * @@ -1105,6 +1139,10 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, /* Update window size */ tcp->snd_win = win; + /* Hold off (or start) the keepalive timer, if applicable */ + if ( ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) + start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY ); + /* Ignore ACKs that don't actually acknowledge any new data. * (In particular, do not stop the retransmission timer; this * avoids creating a sorceror's apprentice syndrome when a From 02d5cfff22e3796f3ecf0b4b93519f952223fd89 Mon Sep 17 00:00:00 2001 From: Leendert van Doorn Date: Mon, 13 Jun 2016 08:14:42 -0500 Subject: [PATCH 239/591] [tg3] Add missing memory barrier ARM64 has a weaker memory order model than x86. The missing memory barrier caused phy initialization notification to be delayed beyond the link-wait timeout (15 secs). Signed-off-by: Leendert van Doorn Signed-off-by: Michael Brown --- src/drivers/net/tg3/tg3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/drivers/net/tg3/tg3.c b/src/drivers/net/tg3/tg3.c index 824c9b1c0..1bed06649 100644 --- a/src/drivers/net/tg3/tg3.c +++ b/src/drivers/net/tg3/tg3.c @@ -486,6 +486,8 @@ static void tg3_poll(struct net_device *dev) */ tp->hw_status->status &= ~SD_STATUS_UPDATED; + mb(); + tg3_poll_link(tp); tg3_tx_complete(dev); tg3_rx_complete(dev); From e6111c151794c4c15a0988e259666ef5be24ffcc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 13 Jun 2016 15:29:05 +0100 Subject: [PATCH 240/591] [time] Allow system clock to be adjusted at runtime Provide a mechanism to allow an arbitrary adjustment to be applied to all subsequent calls to time(). Note that the underlying clock source (e.g. the RTC clock) will not be changed; only the time as reported within iPXE will be affected. Signed-off-by: Michael Brown --- src/core/time.c | 3 +++ src/include/ipxe/time.h | 15 ++++++++++++++- src/include/time.h | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/core/time.c b/src/core/time.c index 29a924ebe..c353ac5bd 100644 --- a/src/core/time.c +++ b/src/core/time.c @@ -43,6 +43,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * 400. */ +/** Current system clock offset */ +signed long time_offset; + /** Days of week (for debugging) */ static const char *weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" diff --git a/src/include/ipxe/time.h b/src/include/ipxe/time.h index 4c5bb2a00..89bf90e03 100644 --- a/src/include/ipxe/time.h +++ b/src/include/ipxe/time.h @@ -50,11 +50,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Include all architecture-dependent time API headers */ #include +extern signed long time_offset; + /** - * Get current time in seconds + * Get current time in seconds (ignoring system clock offset) * * @ret time Time, in seconds */ time_t time_now ( void ); +/** + * Adjust system clock + * + * @v delta Clock adjustment, in seconds + */ +static inline __attribute__ (( always_inline )) void +time_adjust ( signed long delta ) { + + time_offset += delta; +} + #endif /* _IPXE_TIME_H */ diff --git a/src/include/time.h b/src/include/time.h index 462ac6999..ab93a3dbb 100644 --- a/src/include/time.h +++ b/src/include/time.h @@ -39,10 +39,10 @@ struct tm { * @v t Time to fill in, or NULL * @ret time Current time */ -static inline time_t time ( time_t *t ) { +static inline __attribute__ (( always_inline )) time_t time ( time_t *t ) { time_t now; - now = time_now(); + now = ( time_now() + time_offset ); if ( t ) *t = now; return now; From fce6117ad983a9c53087860058769dc65fb09d73 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 13 Jun 2016 15:55:49 +0100 Subject: [PATCH 241/591] [ntp] Add simple NTP client Signed-off-by: Michael Brown --- src/include/ipxe/errfile.h | 1 + src/include/ipxe/ntp.h | 109 +++++++++++++++ src/net/udp/ntp.c | 275 +++++++++++++++++++++++++++++++++++++ 3 files changed, 385 insertions(+) create mode 100644 src/include/ipxe/ntp.h create mode 100644 src/net/udp/ntp.c diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 4dae35b4c..cbab452bb 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -265,6 +265,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_peerblk ( ERRFILE_NET | 0x00460000 ) #define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) #define ERRFILE_xsigo ( ERRFILE_NET | 0x00480000 ) +#define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/ntp.h b/src/include/ipxe/ntp.h new file mode 100644 index 000000000..f5b3d2326 --- /dev/null +++ b/src/include/ipxe/ntp.h @@ -0,0 +1,109 @@ +#ifndef _IPXE_NTP_H +#define _IPXE_NTP_H + +/** @file + * + * Network Time Protocol + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** NTP port */ +#define NTP_PORT 123 + +/** An NTP short-format timestamp */ +struct ntp_short { + /** Seconds */ + uint16_t seconds; + /** Fraction of a second */ + uint16_t fraction; +} __attribute__ (( packed )); + +/** An NTP timestamp */ +struct ntp_timestamp { + /** Seconds */ + uint32_t seconds; + /** Fraction of a second */ + uint32_t fraction; +} __attribute__ (( packed )); + +/** An NTP reference identifier */ +union ntp_id { + /** Textual identifier */ + char text[4]; + /** IPv4 address */ + struct in_addr in; + /** Opaque integer */ + uint32_t opaque; +}; + +/** An NTP header */ +struct ntp_header { + /** Flags */ + uint8_t flags; + /** Stratum */ + uint8_t stratum; + /** Polling rate */ + int8_t poll; + /** Precision */ + int8_t precision; + /** Root delay */ + struct ntp_short delay; + /** Root dispersion */ + struct ntp_short dispersion; + /** Reference clock identifier */ + union ntp_id id; + /** Reference timestamp */ + struct ntp_timestamp reference; + /** Originate timestamp */ + struct ntp_timestamp originate; + /** Receive timestamp */ + struct ntp_timestamp receive; + /** Transmit timestamp */ + struct ntp_timestamp transmit; +} __attribute__ (( packed )); + +/** Leap second indicator: unknown */ +#define NTP_FL_LI_UNKNOWN 0xc0 + +/** NTP version: 1 */ +#define NTP_FL_VN_1 0x20 + +/** NTP mode: client */ +#define NTP_FL_MODE_CLIENT 0x03 + +/** NTP mode: server */ +#define NTP_FL_MODE_SERVER 0x04 + +/** NTP mode mask */ +#define NTP_FL_MODE_MASK 0x07 + +/** NTP timestamp for start of Unix epoch */ +#define NTP_EPOCH 2208988800UL + +/** NTP fraction of a second magic value + * + * This is a policy decision. + */ +#define NTP_FRACTION_MAGIC 0x69505845UL + +/** NTP minimum retransmission timeout + * + * This is a policy decision. + */ +#define NTP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC ) + +/** NTP maximum retransmission timeout + * + * This is a policy decision. + */ +#define NTP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) + +extern int start_ntp ( struct interface *job, const char *hostname ); + +#endif /* _IPXE_NTP_H */ diff --git a/src/net/udp/ntp.c b/src/net/udp/ntp.c new file mode 100644 index 000000000..11f8ccc00 --- /dev/null +++ b/src/net/udp/ntp.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2016 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 +#include +#include +#include + +/** @file + * + * Network Time Protocol + * + */ + +/** An NTP client */ +struct ntp_client { + /** Reference count */ + struct refcnt refcnt; + /** Job control interface */ + struct interface job; + /** Data transfer interface */ + struct interface xfer; + /** Retransmission timer */ + struct retry_timer timer; +}; + +/** + * Close NTP client + * + * @v ntp NTP client + * @v rc Reason for close + */ +static void ntp_close ( struct ntp_client *ntp, int rc ) { + + /* Stop timer */ + stop_timer ( &ntp->timer ); + + /* Shut down interfaces */ + intf_shutdown ( &ntp->xfer, rc ); + intf_shutdown ( &ntp->job, rc ); +} + +/** + * Send NTP request + * + * @v ntp NTP client + * @ret rc Return status code + */ +static int ntp_request ( struct ntp_client *ntp ) { + struct ntp_header hdr; + int rc; + + DBGC ( ntp, "NTP %p sending request\n", ntp ); + + /* Construct header */ + memset ( &hdr, 0, sizeof ( hdr ) ); + hdr.flags = ( NTP_FL_LI_UNKNOWN | NTP_FL_VN_1 | NTP_FL_MODE_CLIENT ); + hdr.transmit.seconds = htonl ( time ( NULL ) + NTP_EPOCH ); + hdr.transmit.fraction = htonl ( NTP_FRACTION_MAGIC ); + + /* Send request */ + if ( ( rc = xfer_deliver_raw ( &ntp->xfer, &hdr, + sizeof ( hdr ) ) ) != 0 ) { + DBGC ( ntp, "NTP %p could not send request: %s\n", + ntp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle NTP response + * + * @v ntp NTP client + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int ntp_deliver ( struct ntp_client *ntp, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct ntp_header *hdr; + struct sockaddr_tcpip *st_src; + int32_t delta; + int rc; + + /* Check source port */ + st_src = ( ( struct sockaddr_tcpip * ) meta->src ); + if ( st_src->st_port != htons ( NTP_PORT ) ) { + DBGC ( ntp, "NTP %p received non-NTP packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + + /* Check packet length */ + if ( iob_len ( iobuf ) < sizeof ( *hdr ) ) { + DBGC ( ntp, "NTP %p received malformed packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + hdr = iobuf->data; + + /* Check mode */ + if ( ( hdr->flags & NTP_FL_MODE_MASK ) != NTP_FL_MODE_SERVER ) { + DBGC ( ntp, "NTP %p received non-server packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + + /* Check magic value */ + if ( hdr->originate.fraction != htonl ( NTP_FRACTION_MAGIC ) ) { + DBGC ( ntp, "NTP %p received unrecognised packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + + /* Check for Kiss-o'-Death packets */ + if ( ! hdr->stratum ) { + DBGC ( ntp, "NTP %p received kiss-o'-death:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + goto close; + } + + /* Calculate clock delta */ + delta = ( ntohl ( hdr->receive.seconds ) - + ntohl ( hdr->originate.seconds ) ); + DBGC ( ntp, "NTP %p delta %d seconds\n", ntp, delta ); + + /* Adjust system clock */ + time_adjust ( delta ); + + /* Success */ + rc = 0; + + close: + ntp_close ( ntp, rc ); + ignore: + free_iob ( iobuf ); + return 0; +} + +/** + * Handle data transfer window change + * + * @v ntp NTP client + */ +static void ntp_window_changed ( struct ntp_client *ntp ) { + + /* Start timer to send initial request */ + start_timer_nodelay ( &ntp->timer ); +} + +/** Data transfer interface operations */ +static struct interface_operation ntp_xfer_op[] = { + INTF_OP ( xfer_deliver, struct ntp_client *, ntp_deliver ), + INTF_OP ( xfer_window_changed, struct ntp_client *, + ntp_window_changed ), + INTF_OP ( intf_close, struct ntp_client *, ntp_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor ntp_xfer_desc = + INTF_DESC_PASSTHRU ( struct ntp_client, xfer, ntp_xfer_op, job ); + +/** Job control interface operations */ +static struct interface_operation ntp_job_op[] = { + INTF_OP ( intf_close, struct ntp_client *, ntp_close ), +}; + +/** Job control interface descriptor */ +static struct interface_descriptor ntp_job_desc = + INTF_DESC_PASSTHRU ( struct ntp_client, job, ntp_job_op, xfer ); + +/** + * Handle NTP timer expiry + * + * @v timer Retransmission timer + * @v fail Failure indicator + */ +static void ntp_expired ( struct retry_timer *timer, int fail ) { + struct ntp_client *ntp = + container_of ( timer, struct ntp_client, timer ); + + /* Shut down client if we have failed */ + if ( fail ) { + ntp_close ( ntp, -ETIMEDOUT ); + return; + } + + /* Otherwise, restart timer and (re)transmit request */ + start_timer ( &ntp->timer ); + ntp_request ( ntp ); +} + +/** + * Start NTP client + * + * @v job Job control interface + * @v hostname NTP server + * @ret rc Return status code + */ +int start_ntp ( struct interface *job, const char *hostname ) { + struct ntp_client *ntp; + union { + struct sockaddr_tcpip st; + struct sockaddr sa; + } server; + int rc; + + /* Allocate and initialise structure*/ + ntp = zalloc ( sizeof ( *ntp ) ); + if ( ! ntp ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &ntp->refcnt, NULL ); + intf_init ( &ntp->job, &ntp_job_desc, &ntp->refcnt ); + intf_init ( &ntp->xfer, &ntp_xfer_desc, &ntp->refcnt ); + timer_init ( &ntp->timer, ntp_expired, &ntp->refcnt ); + set_timer_limits ( &ntp->timer, NTP_MIN_TIMEOUT, NTP_MAX_TIMEOUT ); + + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st.st_port = htons ( NTP_PORT ); + if ( ( rc = xfer_open_named_socket ( &ntp->xfer, SOCK_DGRAM, &server.sa, + hostname, NULL ) ) != 0 ) { + DBGC ( ntp, "NTP %p could not open socket: %s\n", + ntp, strerror ( rc ) ); + goto err_open; + } + + /* Attach parent interface, mortalise self, and return */ + intf_plug_plug ( &ntp->job, job ); + ref_put ( &ntp->refcnt ); + return 0; + + err_open: + ntp_close ( ntp, rc ); + ref_put ( &ntp->refcnt ); + err_alloc: + return rc; +} From 3c61e11fe1a653b159ad472469fa74a3b2d0f116 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 13 Jun 2016 15:57:16 +0100 Subject: [PATCH 242/591] [cmdline] Add "ntp" command Signed-off-by: Michael Brown --- src/config/config.c | 3 ++ src/config/general.h | 1 + src/hci/commands/ntp_cmd.c | 81 ++++++++++++++++++++++++++++++++++++++ src/include/usr/ntpmgmt.h | 14 +++++++ src/usr/ntpmgmt.c | 57 +++++++++++++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 src/hci/commands/ntp_cmd.c create mode 100644 src/include/usr/ntpmgmt.h create mode 100644 src/usr/ntpmgmt.c diff --git a/src/config/config.c b/src/config/config.c index 6f9e66cd4..e24cfe0d0 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -278,6 +278,9 @@ REQUIRE_OBJECT ( ipstat_cmd ); #ifdef PROFSTAT_CMD REQUIRE_OBJECT ( profstat_cmd ); #endif +#ifdef NTP_CMD +REQUIRE_OBJECT ( ntp_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/config/general.h b/src/config/general.h index a21501500..a71ba726f 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -147,6 +147,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define CONSOLE_CMD /* Console command */ //#define IPSTAT_CMD /* IP statistics commands */ //#define PROFSTAT_CMD /* Profiling commands */ +//#define NTP_CMD /* NTP commands */ /* * ROM-specific options diff --git a/src/hci/commands/ntp_cmd.c b/src/hci/commands/ntp_cmd.c new file mode 100644 index 000000000..8f741a512 --- /dev/null +++ b/src/hci/commands/ntp_cmd.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * NTP commands + * + */ + +/** "ntp" options */ +struct ntp_options {}; + +/** "ntp" option list */ +static struct option_descriptor ntp_opts[] = {}; + +/** "ntp" command descriptor */ +static struct command_descriptor ntp_cmd = + COMMAND_DESC ( struct ntp_options, ntp_opts, 1, 1, "" ); + +/** + * "ntp" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int ntp_exec ( int argc, char **argv ) { + struct ntp_options opts; + const char *hostname; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &ntp_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse hostname */ + hostname = argv[optind]; + + /* Get time and date via NTP */ + if ( ( rc = ntp ( hostname ) ) != 0 ) { + printf ( "Could not get time and date: %s\n", strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** NTP command */ +struct command ntp_command __command = { + .name = "ntp", + .exec = ntp_exec, +}; diff --git a/src/include/usr/ntpmgmt.h b/src/include/usr/ntpmgmt.h new file mode 100644 index 000000000..284e668e6 --- /dev/null +++ b/src/include/usr/ntpmgmt.h @@ -0,0 +1,14 @@ +#ifndef _USR_NTPMGMT_H +#define _USR_NTPMGMT_H + +/** @file + * + * NTP management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int ntp ( const char *hostname ); + +#endif /* _USR_NTPMGMT_H */ diff --git a/src/usr/ntpmgmt.c b/src/usr/ntpmgmt.c new file mode 100644 index 000000000..765c6dc9e --- /dev/null +++ b/src/usr/ntpmgmt.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * NTP management + * + */ + +/** + * Get time and date via NTP + * + * @v hostname Hostname + * @ret rc Return status code + */ +int ntp ( const char *hostname ) { + int rc; + + /* Start NTP client */ + if ( ( rc = start_ntp ( &monojob, hostname ) ) != 0 ) + return rc; + + /* Wait for NTP to complete */ + if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 ) + return rc; + + return 0; +} From 4775dd383505c5ebef13f5195d885aeca186aa98 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 13 Jun 2016 18:41:26 +0100 Subject: [PATCH 243/591] [thunderx] Add driver for Cavium ThunderX SoC NICs Signed-off-by: Michael Brown --- src/drivers/net/thunderx.c | 1668 ++++++++++++++++++++++++++++++++++++ src/drivers/net/thunderx.h | 949 ++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 2618 insertions(+) create mode 100644 src/drivers/net/thunderx.c create mode 100644 src/drivers/net/thunderx.h diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c new file mode 100644 index 000000000..fce153e5f --- /dev/null +++ b/src/drivers/net/thunderx.c @@ -0,0 +1,1668 @@ +/* + * Copyright (C) 2016 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 +#include +#include +#include +#include +#include +#include "thunderx.h" + +/** @file + * + * Cavium ThunderX Ethernet driver + * + */ + +/** List of BGX Ethernet interfaces */ +static LIST_HEAD ( txnic_bgxs ); + +/** List of physical functions */ +static LIST_HEAD ( txnic_pfs ); + +/** Debug colour for physical function and BGX messages */ +#define TXNICCOL(x) ( &txnic_pfs + (x)->node ) + +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Show virtual NIC diagnostics (for debugging) + * + * @v vnic Virtual NIC + */ +static __attribute__ (( unused )) void txnic_diag ( struct txnic *vnic ) { + + DBGC ( vnic, "TXNIC %s SQ %05zx(%05llx)/%05zx(%05llx) %08llx\n", + vnic->name, + ( ( vnic->sq.prod % TXNIC_SQES ) * TXNIC_SQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_SQ_TAIL(0) ), + ( ( vnic->sq.cons % TXNIC_SQES ) * TXNIC_SQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_SQ_HEAD(0) ), + readq ( vnic->regs + TXNIC_QS_SQ_STATUS(0) ) ); + DBGC ( vnic, "TXNIC %s RQ %05zx(%05llx)/%05zx(%05llx) %016llx\n", + vnic->name, + ( ( vnic->rq.prod % TXNIC_RQES ) * TXNIC_RQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_RBDR_TAIL(0) ), + ( ( vnic->rq.cons % TXNIC_RQES ) * TXNIC_RQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_RBDR_HEAD(0) ), + readq ( vnic->regs + TXNIC_QS_RBDR_STATUS0(0) ) ); + DBGC ( vnic, "TXNIC %s CQ xxxxx(%05llx)/%05x(%05llx) %08llx:%08llx\n", + vnic->name, readq ( vnic->regs + TXNIC_QS_CQ_TAIL(0) ), + ( ( vnic->cq.cons % TXNIC_CQES ) * TXNIC_CQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_CQ_HEAD(0) ), + readq ( vnic->regs + TXNIC_QS_CQ_STATUS(0) ), + readq ( vnic->regs + TXNIC_QS_CQ_STATUS2(0) ) ); +} + +/****************************************************************************** + * + * Send queue + * + ****************************************************************************** + */ + +/** + * Create send queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_create_sq ( struct txnic *vnic ) { + + /* Reset send queue */ + vnic->sq.prod = 0; + vnic->sq.cons = 0; + writeq ( TXNIC_QS_SQ_CFG_RESET, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); + + /* Configure and enable send queue */ + writeq ( user_to_phys ( vnic->sq.sqe, 0 ), + ( vnic->regs + TXNIC_QS_SQ_BASE(0) ) ); + writeq ( ( TXNIC_QS_SQ_CFG_ENA | TXNIC_QS_SQ_CFG_QSIZE_1K ), + ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); + + DBGC ( vnic, "TXNIC %s SQ at [%08lx,%08lx)\n", + vnic->name, user_to_phys ( vnic->sq.sqe, 0 ), + user_to_phys ( vnic->sq.sqe, TXNIC_SQ_SIZE ) ); + return 0; +} + +/** + * Disable send queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_disable_sq ( struct txnic *vnic ) { + uint64_t status; + unsigned int i; + + /* Disable send queue */ + writeq ( 0, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); + + /* Wait for send queue to be stopped */ + for ( i = 0 ; i < TXNIC_SQ_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if send queue is stopped */ + status = readq ( vnic->regs + TXNIC_QS_SQ_STATUS(0) ); + if ( status & TXNIC_QS_SQ_STATUS_STOPPED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( vnic, "TXNIC %s SQ disable timed out\n", vnic->name ); + return -ETIMEDOUT; +} + +/** + * Destroy send queue + * + * @v vnic Virtual NIC + */ +static void txnic_destroy_sq ( struct txnic *vnic ) { + int rc; + + /* Disable send queue */ + if ( ( rc = txnic_disable_sq ( vnic ) ) != 0 ) { + /* Nothing else we can do */ + return; + } + + /* Reset send queue */ + writeq ( TXNIC_QS_SQ_CFG_RESET, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); +} + +/** + * Send packet + * + * @v vnic Virtual NIC + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int txnic_send ( struct txnic *vnic, struct io_buffer *iobuf ) { + struct txnic_sqe sqe; + unsigned int sq_idx; + size_t offset; + size_t len; + + /* Get next send queue entry */ + if ( ( vnic->sq.prod - vnic->sq.cons ) >= TXNIC_SQ_FILL ) { + DBGC ( vnic, "TXNIC %s out of send queue entries\n", + vnic->name ); + return -ENOBUFS; + } + sq_idx = ( vnic->sq.prod++ % TXNIC_SQES ); + offset = ( sq_idx * TXNIC_SQ_STRIDE ); + + /* Populate send descriptor */ + len = iob_len ( iobuf ); + memset ( &sqe, 0, sizeof ( sqe ) ); + sqe.hdr.total = cpu_to_le32 ( ( len >= ETH_ZLEN ) ? len : ETH_ZLEN ); + sqe.hdr.subdcnt = ( TXNIC_SQE_SUBDESCS - 1 ); + sqe.hdr.flags = TXNIC_SEND_HDR_FLAGS; + sqe.gather.size = cpu_to_le16 ( len ); + sqe.gather.flags = TXNIC_SEND_GATHER_FLAGS; + sqe.gather.addr = cpu_to_le64 ( virt_to_bus ( iobuf->data ) ); + DBGC2 ( vnic, "TXNIC %s SQE %#03x is [%08lx,%08lx)\n", + vnic->name, sq_idx, virt_to_bus ( iobuf->data ), + ( virt_to_bus ( iobuf->data ) + len ) ); + + /* Copy send descriptor to ring */ + copy_to_user ( vnic->sq.sqe, offset, &sqe, sizeof ( sqe ) ); + + /* Ring doorbell */ + wmb(); + writeq ( TXNIC_SQE_SUBDESCS, ( vnic->regs + TXNIC_QS_SQ_DOOR(0) ) ); + + return 0; +} + +/** + * Complete send queue entry + * + * @v vnic Virtual NIC + * @v cqe Send completion queue entry + */ +static void txnic_complete_sqe ( struct txnic *vnic, + struct txnic_cqe_send *cqe ) { + struct net_device *netdev = vnic->netdev; + unsigned int sq_idx; + unsigned int status; + + /* Parse completion */ + sq_idx = ( le16_to_cpu ( cqe->sqe_ptr ) / TXNIC_SQE_SUBDESCS ); + status = cqe->send_status; + + /* Sanity check */ + assert ( sq_idx == ( vnic->sq.cons % TXNIC_SQES ) ); + + /* Free send queue entry */ + vnic->sq.cons++; + + /* Complete transmission */ + if ( status ) { + DBGC ( vnic, "TXNIC %s SQE %#03x complete (status %#02x)\n", + vnic->name, sq_idx, status ); + netdev_tx_complete_next_err ( netdev, -EIO ); + } else { + DBGC2 ( vnic, "TXNIC %s SQE %#03x complete\n", + vnic->name, sq_idx ); + netdev_tx_complete_next ( netdev ); + } +} + +/****************************************************************************** + * + * Receive queue + * + ****************************************************************************** + */ + +/** + * Create receive queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_create_rq ( struct txnic *vnic ) { + + /* Reset receive buffer descriptor ring */ + vnic->rq.prod = 0; + vnic->rq.cons = 0; + writeq ( TXNIC_QS_RBDR_CFG_RESET, + ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Configure and enable receive buffer descriptor ring */ + writeq ( user_to_phys ( vnic->rq.rqe, 0 ), + ( vnic->regs + TXNIC_QS_RBDR_BASE(0) ) ); + writeq ( ( TXNIC_QS_RBDR_CFG_ENA | TXNIC_QS_RBDR_CFG_QSIZE_8K | + TXNIC_QS_RBDR_CFG_LINES ( TXNIC_RQE_SIZE / + TXNIC_LINE_SIZE ) ), + ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Enable receive queue */ + writeq ( TXNIC_QS_RQ_CFG_ENA, ( vnic->regs + TXNIC_QS_RQ_CFG(0) ) ); + + DBGC ( vnic, "TXNIC %s RQ at [%08lx,%08lx)\n", + vnic->name, user_to_phys ( vnic->rq.rqe, 0 ), + user_to_phys ( vnic->rq.rqe, TXNIC_RQ_SIZE ) ); + return 0; +} + +/** + * Disable receive queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_disable_rq ( struct txnic *vnic ) { + uint64_t cfg; + unsigned int i; + + /* Disable receive queue */ + writeq ( 0, ( vnic->regs + TXNIC_QS_RQ_CFG(0) ) ); + + /* Wait for receive queue to be disabled */ + for ( i = 0 ; i < TXNIC_RQ_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if receive queue is disabled */ + cfg = readq ( vnic->regs + TXNIC_QS_RQ_CFG(0) ); + if ( ! ( cfg & TXNIC_QS_RQ_CFG_ENA ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( vnic, "TXNIC %s RQ disable timed out\n", vnic->name ); + return -ETIMEDOUT; +} + +/** + * Destroy receive queue + * + * @v vnic Virtual NIC + */ +static void txnic_destroy_rq ( struct txnic *vnic ) { + unsigned int i; + int rc; + + /* Disable receive queue */ + if ( ( rc = txnic_disable_rq ( vnic ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Disable receive buffer descriptor ring */ + writeq ( 0, ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Reset receive buffer descriptor ring */ + writeq ( TXNIC_QS_RBDR_CFG_RESET, + ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Free any unused I/O buffers */ + for ( i = 0 ; i < TXNIC_RQ_FILL ; i++ ) { + if ( vnic->rq.iobuf[i] ) + free_iob ( vnic->rq.iobuf[i] ); + vnic->rq.iobuf[i] = NULL; + } +} + +/** + * Refill receive queue + * + * @v vnic Virtual NIC + */ +static void txnic_refill_rq ( struct txnic *vnic ) { + struct io_buffer *iobuf; + struct txnic_rqe rqe; + unsigned int rq_idx; + unsigned int rq_iobuf_idx; + unsigned int refilled = 0; + size_t offset; + + /* Refill ring */ + while ( ( vnic->rq.prod - vnic->rq.cons ) < TXNIC_RQ_FILL ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( TXNIC_RQE_SIZE ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rq_idx = ( vnic->rq.prod++ % TXNIC_RQES ); + offset = ( rq_idx * TXNIC_RQ_STRIDE ); + + /* Populate receive descriptor */ + rqe.rbdre.addr = cpu_to_le64 ( virt_to_bus ( iobuf->data ) ); + DBGC2 ( vnic, "TXNIC %s RQE %#03x is [%08lx,%08lx)\n", + vnic->name, rq_idx, virt_to_bus ( iobuf->data ), + ( virt_to_bus ( iobuf->data ) + TXNIC_RQE_SIZE ) ); + + /* Copy receive descriptor to ring */ + copy_to_user ( vnic->rq.rqe, offset, &rqe, sizeof ( rqe ) ); + refilled++; + + /* Record I/O buffer */ + rq_iobuf_idx = ( rq_idx % TXNIC_RQ_FILL ); + assert ( vnic->rq.iobuf[rq_iobuf_idx] == NULL ); + vnic->rq.iobuf[rq_iobuf_idx] = iobuf; + } + + /* Ring doorbell */ + wmb(); + writeq ( refilled, ( vnic->regs + TXNIC_QS_RBDR_DOOR(0) ) ); +} + +/** + * Complete receive queue entry + * + * @v vnic Virtual NIC + * @v cqe Receive completion queue entry + */ +static void txnic_complete_rqe ( struct txnic *vnic, + struct txnic_cqe_rx *cqe ) { + struct net_device *netdev = vnic->netdev; + struct io_buffer *iobuf; + unsigned int errop; + unsigned int rq_idx; + unsigned int rq_iobuf_idx; + size_t apad_len; + size_t len; + + /* Parse completion */ + errop = cqe->errop; + apad_len = TXNIC_CQE_RX_APAD_LEN ( cqe->apad ); + len = le16_to_cpu ( cqe->len ); + + /* Get next receive I/O buffer */ + rq_idx = ( vnic->rq.cons++ % TXNIC_RQES ); + rq_iobuf_idx = ( rq_idx % TXNIC_RQ_FILL ); + iobuf = vnic->rq.iobuf[rq_iobuf_idx]; + vnic->rq.iobuf[rq_iobuf_idx] = NULL; + + /* Populate I/O buffer */ + iob_reserve ( iobuf, apad_len ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( errop ) { + DBGC ( vnic, "TXNIC %s RQE %#03x error (length %zd, errop " + "%#02x)\n", vnic->name, rq_idx, len, errop ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } else { + DBGC2 ( vnic, "TXNIC %s RQE %#03x complete (length %zd)\n", + vnic->name, rq_idx, len ); + netdev_rx ( netdev, iobuf ); + } +} + +/****************************************************************************** + * + * Completion queue + * + ****************************************************************************** + */ + +/** + * Create completion queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_create_cq ( struct txnic *vnic ) { + + /* Reset completion queue */ + vnic->cq.cons = 0; + writeq ( TXNIC_QS_CQ_CFG_RESET, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); + + /* Configure and enable completion queue */ + writeq ( user_to_phys ( vnic->cq.cqe, 0 ), + ( vnic->regs + TXNIC_QS_CQ_BASE(0) ) ); + writeq ( ( TXNIC_QS_CQ_CFG_ENA | TXNIC_QS_CQ_CFG_QSIZE_256 ), + ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); + + DBGC ( vnic, "TXNIC %s CQ at [%08lx,%08lx)\n", + vnic->name, user_to_phys ( vnic->cq.cqe, 0 ), + user_to_phys ( vnic->cq.cqe, TXNIC_CQ_SIZE ) ); + return 0; +} + +/** + * Disable completion queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_disable_cq ( struct txnic *vnic ) { + uint64_t cfg; + unsigned int i; + + /* Disable completion queue */ + writeq ( 0, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); + + /* Wait for completion queue to be disabled */ + for ( i = 0 ; i < TXNIC_CQ_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if completion queue is disabled */ + cfg = readq ( vnic->regs + TXNIC_QS_CQ_CFG(0) ); + if ( ! ( cfg & TXNIC_QS_CQ_CFG_ENA ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( vnic, "TXNIC %s CQ disable timed out\n", vnic->name ); + return -ETIMEDOUT; +} + +/** + * Destroy completion queue + * + * @v vnic Virtual NIC + */ +static void txnic_destroy_cq ( struct txnic *vnic ) { + int rc; + + /* Disable completion queue */ + if ( ( rc = txnic_disable_cq ( vnic ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Reset completion queue */ + writeq ( TXNIC_QS_CQ_CFG_RESET, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); +} + +/** + * Poll completion queue + * + * @v vnic Virtual NIC + */ +static void txnic_poll_cq ( struct txnic *vnic ) { + union txnic_cqe cqe; + uint64_t status; + size_t offset; + unsigned int qcount; + unsigned int cq_idx; + unsigned int i; + + /* Get number of completions */ + status = readq ( vnic->regs + TXNIC_QS_CQ_STATUS(0) ); + qcount = TXNIC_QS_CQ_STATUS_QCOUNT ( status ); + if ( ! qcount ) + return; + + /* Process completion queue entries */ + for ( i = 0 ; i < qcount ; i++ ) { + + /* Get completion queue entry */ + cq_idx = ( vnic->cq.cons++ % TXNIC_CQES ); + offset = ( cq_idx * TXNIC_CQ_STRIDE ); + copy_from_user ( &cqe, vnic->cq.cqe, offset, sizeof ( cqe ) ); + + /* Process completion queue entry */ + switch ( cqe.common.cqe_type ) { + case TXNIC_CQE_TYPE_SEND: + txnic_complete_sqe ( vnic, &cqe.send ); + break; + case TXNIC_CQE_TYPE_RX: + txnic_complete_rqe ( vnic, &cqe.rx ); + break; + default: + DBGC ( vnic, "TXNIC %s unknown completion type %d\n", + vnic->name, cqe.common.cqe_type ); + DBGC_HDA ( vnic, user_to_phys ( vnic->cq.cqe, offset ), + &cqe, sizeof ( cqe ) ); + break; + } + } + + /* Ring doorbell */ + writeq ( qcount, ( vnic->regs + TXNIC_QS_CQ_DOOR(0) ) ); +} + +/****************************************************************************** + * + * Virtual NIC + * + ****************************************************************************** + */ + +/** + * Open virtual NIC + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_open ( struct txnic *vnic ) { + int rc; + + /* Create completion queue */ + if ( ( rc = txnic_create_cq ( vnic ) ) != 0 ) + goto err_create_cq; + + /* Create send queue */ + if ( ( rc = txnic_create_sq ( vnic ) ) != 0 ) + goto err_create_sq; + + /* Create receive queue */ + if ( ( rc = txnic_create_rq ( vnic ) ) != 0 ) + goto err_create_rq; + + /* Refill receive queue */ + txnic_refill_rq ( vnic ); + + return 0; + + txnic_destroy_rq ( vnic ); + err_create_rq: + txnic_destroy_sq ( vnic ); + err_create_sq: + txnic_destroy_cq ( vnic ); + err_create_cq: + return rc; +} + +/** + * Close virtual NIC + * + * @v vnic Virtual NIC + */ +static void txnic_close ( struct txnic *vnic ) { + + /* Destroy receive queue */ + txnic_destroy_rq ( vnic ); + + /* Destroy send queue */ + txnic_destroy_sq ( vnic ); + + /* Destroy completion queue */ + txnic_destroy_cq ( vnic ); +} + +/** + * Poll virtual NIC + * + * @v vnic Virtual NIC + */ +static void txnic_poll ( struct txnic *vnic ) { + + /* Poll completion queue */ + txnic_poll_cq ( vnic ); + + /* Refill receive queue */ + txnic_refill_rq ( vnic ); +} + +/** + * Allocate virtual NIC + * + * @v dev Underlying device + * @v membase Register base address + * @ret vnic Virtual NIC, or NULL on failure + */ +static struct txnic * txnic_alloc ( struct device *dev, + unsigned long membase ) { + struct net_device *netdev; + struct txnic *vnic; + + /* Allocate network device */ + netdev = alloc_etherdev ( sizeof ( *vnic ) ); + if ( ! netdev ) + goto err_alloc_netdev; + netdev->dev = dev; + vnic = netdev->priv; + vnic->netdev = netdev; + vnic->name = dev->name; + + /* Allow caller to reuse netdev->priv. (The generic virtual + * NIC code never assumes that netdev->priv==vnic.) + */ + netdev->priv = NULL; + + /* Allocate completion queue */ + vnic->cq.cqe = umalloc ( TXNIC_CQ_SIZE ); + if ( ! vnic->cq.cqe ) + goto err_alloc_cq; + + /* Allocate send queue */ + vnic->sq.sqe = umalloc ( TXNIC_SQ_SIZE ); + if ( ! vnic->sq.sqe ) + goto err_alloc_sq; + + /* Allocate receive queue */ + vnic->rq.rqe = umalloc ( TXNIC_RQ_SIZE ); + if ( ! vnic->rq.rqe ) + goto err_alloc_rq; + + /* Map registers */ + vnic->regs = ioremap ( membase, TXNIC_VF_BAR_SIZE ); + if ( ! vnic->regs ) + goto err_ioremap; + + return vnic; + + iounmap ( vnic->regs ); + err_ioremap: + ufree ( vnic->rq.rqe ); + err_alloc_rq: + ufree ( vnic->sq.sqe ); + err_alloc_sq: + ufree ( vnic->cq.cqe ); + err_alloc_cq: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc_netdev: + return NULL; +} + +/** + * Free virtual NIC + * + * @v vnic Virtual NIC + */ +static void txnic_free ( struct txnic *vnic ) { + struct net_device *netdev = vnic->netdev; + + /* Unmap registers */ + iounmap ( vnic->regs ); + + /* Free receive queue */ + ufree ( vnic->rq.rqe ); + + /* Free send queue */ + ufree ( vnic->sq.sqe ); + + /* Free completion queue */ + ufree ( vnic->cq.cqe ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/****************************************************************************** + * + * Logical MAC virtual NICs + * + ****************************************************************************** + */ + +/** + * Show LMAC diagnostics (for debugging) + * + * @v lmac Logical MAC + */ +static __attribute__ (( unused )) void +txnic_lmac_diag ( struct txnic_lmac *lmac ) { + struct txnic *vnic = lmac->vnic; + uint64_t status1; + uint64_t status2; + uint64_t br_status1; + uint64_t br_status2; + uint64_t br_algn_status; + uint64_t br_pmd_status; + uint64_t an_status; + + /* Read status (clearing latching bits) */ + writeq ( BGX_SPU_STATUS1_RCV_LNK, ( lmac->regs + BGX_SPU_STATUS1 ) ); + writeq ( BGX_SPU_STATUS2_RCVFLT, ( lmac->regs + BGX_SPU_STATUS2 ) ); + status1 = readq ( lmac->regs + BGX_SPU_STATUS1 ); + status2 = readq ( lmac->regs + BGX_SPU_STATUS2 ); + DBGC ( vnic, "TXNIC %s SPU %02llx:%04llx%s%s%s\n", + vnic->name, status1, status2, + ( ( status1 & BGX_SPU_STATUS1_FLT ) ? " FLT" : "" ), + ( ( status1 & BGX_SPU_STATUS1_RCV_LNK ) ? " RCV_LNK" : "" ), + ( ( status2 & BGX_SPU_STATUS2_RCVFLT ) ? " RCVFLT" : "" ) ); + + /* Read BASE-R status (clearing latching bits) */ + writeq ( ( BGX_SPU_BR_STATUS2_LATCHED_LOCK | + BGX_SPU_BR_STATUS2_LATCHED_BER ), + ( lmac->regs + BGX_SPU_BR_STATUS2 ) ); + br_status1 = readq ( lmac->regs + BGX_SPU_BR_STATUS1 ); + br_status2 = readq ( lmac->regs + BGX_SPU_BR_STATUS2 ); + DBGC ( vnic, "TXNIC %s BR %04llx:%04llx%s%s%s%s%s\n", + vnic->name, br_status2, br_status2, + ( ( br_status1 & BGX_SPU_BR_STATUS1_RCV_LNK ) ? " RCV_LNK" : ""), + ( ( br_status1 & BGX_SPU_BR_STATUS1_HI_BER ) ? " HI_BER" : "" ), + ( ( br_status1 & BGX_SPU_BR_STATUS1_BLK_LOCK ) ? + " BLK_LOCK" : "" ), + ( ( br_status2 & BGX_SPU_BR_STATUS2_LATCHED_LOCK ) ? + " LATCHED_LOCK" : "" ), + ( ( br_status2 & BGX_SPU_BR_STATUS2_LATCHED_BER ) ? + " LATCHED_BER" : "" ) ); + + /* Read BASE-R alignment status */ + br_algn_status = readq ( lmac->regs + BGX_SPU_BR_ALGN_STATUS ); + DBGC ( vnic, "TXNIC %s BR ALGN %016llx%s\n", vnic->name, br_algn_status, + ( ( br_algn_status & BGX_SPU_BR_ALGN_STATUS_ALIGND ) ? + " ALIGND" : "" ) ); + + /* Read BASE-R link training status */ + br_pmd_status = readq ( lmac->regs + BGX_SPU_BR_PMD_STATUS ); + DBGC ( vnic, "TXNIC %s BR PMD %04llx\n", vnic->name, br_pmd_status ); + + /* Read autonegotiation status (clearing latching bits) */ + writeq ( ( BGX_SPU_AN_STATUS_PAGE_RX | BGX_SPU_AN_STATUS_LINK_STATUS ), + ( lmac->regs + BGX_SPU_AN_STATUS ) ); + an_status = readq ( lmac->regs + BGX_SPU_AN_STATUS ); + DBGC ( vnic, "TXNIC %s BR AN %04llx%s%s%s%s%s\n", vnic->name, an_status, + ( ( an_status & BGX_SPU_AN_STATUS_XNP_STAT ) ? " XNP_STAT" : ""), + ( ( an_status & BGX_SPU_AN_STATUS_PAGE_RX ) ? " PAGE_RX" : "" ), + ( ( an_status & BGX_SPU_AN_STATUS_AN_COMPLETE ) ? + " AN_COMPLETE" : "" ), + ( ( an_status & BGX_SPU_AN_STATUS_LINK_STATUS ) ? + " LINK_STATUS" : "" ), + ( ( an_status & BGX_SPU_AN_STATUS_LP_AN_ABLE ) ? + " LP_AN_ABLE" : "" ) ); + + /* Read transmit statistics */ + DBGC ( vnic, "TXNIC %s TXF xc %#llx xd %#llx mc %#llx sc %#llx ok " + "%#llx bc %#llx mc %#llx un %#llx pa %#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_TX_STAT0 ), + readq ( lmac->regs + BGX_CMR_TX_STAT1 ), + readq ( lmac->regs + BGX_CMR_TX_STAT2 ), + readq ( lmac->regs + BGX_CMR_TX_STAT3 ), + readq ( lmac->regs + BGX_CMR_TX_STAT5 ), + readq ( lmac->regs + BGX_CMR_TX_STAT14 ), + readq ( lmac->regs + BGX_CMR_TX_STAT15 ), + readq ( lmac->regs + BGX_CMR_TX_STAT16 ), + readq ( lmac->regs + BGX_CMR_TX_STAT17 ) ); + DBGC ( vnic, "TXNIC %s TXB ok %#llx hist %#llx:%#llx:%#llx:%#llx:" + "%#llx:%#llx:%#llx:%#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_TX_STAT4 ), + readq ( lmac->regs + BGX_CMR_TX_STAT6 ), + readq ( lmac->regs + BGX_CMR_TX_STAT7 ), + readq ( lmac->regs + BGX_CMR_TX_STAT8 ), + readq ( lmac->regs + BGX_CMR_TX_STAT9 ), + readq ( lmac->regs + BGX_CMR_TX_STAT10 ), + readq ( lmac->regs + BGX_CMR_TX_STAT11 ), + readq ( lmac->regs + BGX_CMR_TX_STAT12 ), + readq ( lmac->regs + BGX_CMR_TX_STAT13 ) ); + + /* Read receive statistics */ + DBGC ( vnic, "TXNIC %s RXF ok %#llx pa %#llx nm %#llx ov %#llx er " + "%#llx nc %#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_RX_STAT0 ), + readq ( lmac->regs + BGX_CMR_RX_STAT2 ), + readq ( lmac->regs + BGX_CMR_RX_STAT4 ), + readq ( lmac->regs + BGX_CMR_RX_STAT6 ), + readq ( lmac->regs + BGX_CMR_RX_STAT8 ), + readq ( lmac->regs + BGX_CMR_RX_STAT9 ) ); + DBGC ( vnic, "TXNIC %s RXB ok %#llx pa %#llx nm %#llx ov %#llx nc " + "%#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_RX_STAT1 ), + readq ( lmac->regs + BGX_CMR_RX_STAT3 ), + readq ( lmac->regs + BGX_CMR_RX_STAT5 ), + readq ( lmac->regs + BGX_CMR_RX_STAT7 ), + readq ( lmac->regs + BGX_CMR_RX_STAT10 ) ); +} + +/** + * Update LMAC link state + * + * @v lmac Logical MAC + */ +static void txnic_lmac_update_link ( struct txnic_lmac *lmac ) { + struct txnic *vnic = lmac->vnic; + struct net_device *netdev = vnic->netdev; + uint64_t status1; + + /* Read status (clearing latching bits) */ + writeq ( BGX_SPU_STATUS1_RCV_LNK, ( lmac->regs + BGX_SPU_STATUS1 ) ); + status1 = readq ( lmac->regs + BGX_SPU_STATUS1 ); + + /* Report link status */ + if ( status1 & BGX_SPU_STATUS1_RCV_LNK ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/** + * Poll LMAC link state + * + * @v lmac Logical MAC + */ +static void txnic_lmac_poll_link ( struct txnic_lmac *lmac ) { + struct txnic *vnic = lmac->vnic; + uint64_t intr; + + /* Get interrupt status */ + intr = readq ( lmac->regs + BGX_SPU_INT ); + if ( ! intr ) + return; + DBGC ( vnic, "TXNIC %s INT %04llx%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + vnic->name, intr, + ( ( intr & BGX_SPU_INT_TRAINING_FAIL ) ? " TRAINING_FAIL" : "" ), + ( ( intr & BGX_SPU_INT_TRAINING_DONE ) ? " TRAINING_DONE" : "" ), + ( ( intr & BGX_SPU_INT_AN_COMPLETE ) ? " AN_COMPLETE" : "" ), + ( ( intr & BGX_SPU_INT_AN_LINK_GOOD ) ? " AN_LINK_GOOD" : "" ), + ( ( intr & BGX_SPU_INT_AN_PAGE_RX ) ? " AN_PAGE_RX" : "" ), + ( ( intr & BGX_SPU_INT_FEC_UNCORR ) ? " FEC_UNCORR" : "" ), + ( ( intr & BGX_SPU_INT_FEC_CORR ) ? " FEC_CORR" : "" ), + ( ( intr & BGX_SPU_INT_BIP_ERR ) ? " BIP_ERR" : "" ), + ( ( intr & BGX_SPU_INT_DBG_SYNC ) ? " DBG_SYNC" : "" ), + ( ( intr & BGX_SPU_INT_ALGNLOS ) ? " ALGNLOS" : "" ), + ( ( intr & BGX_SPU_INT_SYNLOS ) ? " SYNLOS" : "" ), + ( ( intr & BGX_SPU_INT_BITLCKLS ) ? " BITLCKLS" : "" ), + ( ( intr & BGX_SPU_INT_ERR_BLK ) ? " ERR_BLK" : "" ), + ( ( intr & BGX_SPU_INT_RX_LINK_DOWN ) ? " RX_LINK_DOWN" : "" ), + ( ( intr & BGX_SPU_INT_RX_LINK_UP ) ? " RX_LINK_UP" : "" ) ); + + /* Clear interrupt status */ + writeq ( intr, ( lmac->regs + BGX_SPU_INT ) ); + + /* Update link state */ + txnic_lmac_update_link ( lmac ); +} + +/** + * Reset LMAC + * + * @v lmac Logical MAC + */ +static void txnic_lmac_reset ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + void *qsregs = ( pf->regs + TXNIC_PF_QS ( lmac->idx ) ); + + /* There is no reset available for the physical function + * aspects of a virtual NIC; we have to explicitly reload a + * sensible set of default values. + */ + writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_CFG(0) ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_DROP_CFG(0) ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_BP_CFG(0) ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_SQ_CFG(0) ) ); +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int txnic_lmac_open ( struct net_device *netdev ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + struct txnic *vnic = lmac->vnic; + unsigned int vnic_idx = lmac->idx; + unsigned int chan_idx = TXNIC_CHAN_IDX ( vnic_idx ); + unsigned int tl4_idx = TXNIC_TL4_IDX ( vnic_idx ); + unsigned int tl3_idx = TXNIC_TL3_IDX ( vnic_idx ); + unsigned int tl2_idx = TXNIC_TL2_IDX ( vnic_idx ); + void *lmregs = ( pf->regs + TXNIC_PF_LMAC ( vnic_idx ) ); + void *chregs = ( pf->regs + TXNIC_PF_CHAN ( vnic_idx ) ); + void *qsregs = ( pf->regs + TXNIC_PF_QS ( vnic_idx ) ); + size_t max_pkt_size; + int rc; + + /* Configure channel/match parse indices */ + writeq ( ( TXNIC_PF_MPI_CFG_VNIC ( vnic_idx ) | + TXNIC_PF_MPI_CFG_RSSI_BASE ( vnic_idx ) ), + ( TXNIC_PF_MPI_CFG ( vnic_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_RSSI_RQ_RQ_QS ( vnic_idx ) ), + ( TXNIC_PF_RSSI_RQ ( vnic_idx ) + pf->regs ) ); + + /* Configure LMAC */ + max_pkt_size = ( netdev->max_pkt_len + 4 /* possible VLAN */ ); + writeq ( ( TXNIC_PF_LMAC_CFG_ADJUST_DEFAULT | + TXNIC_PF_LMAC_CFG_MIN_PKT_SIZE ( ETH_ZLEN ) ), + ( TXNIC_PF_LMAC_CFG + lmregs ) ); + writeq ( ( TXNIC_PF_LMAC_CFG2_MAX_PKT_SIZE ( max_pkt_size ) ), + ( TXNIC_PF_LMAC_CFG2 + lmregs ) ); + writeq ( ( TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT_DEFAULT | + TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT_DEFAULT | + TXNIC_PF_LMAC_CREDIT_CC_ENABLE ), + ( TXNIC_PF_LMAC_CREDIT + lmregs ) ); + + /* Configure channels */ + writeq ( ( TXNIC_PF_CHAN_TX_CFG_BP_ENA ), + ( TXNIC_PF_CHAN_TX_CFG + chregs ) ); + writeq ( ( TXNIC_PF_CHAN_RX_CFG_CPI_BASE ( vnic_idx ) ), + ( TXNIC_PF_CHAN_RX_CFG + chregs ) ); + writeq ( ( TXNIC_PF_CHAN_RX_BP_CFG_ENA | + TXNIC_PF_CHAN_RX_BP_CFG_BPID ( vnic_idx ) ), + ( TXNIC_PF_CHAN_RX_BP_CFG + chregs ) ); + + /* Configure traffic limiters */ + writeq ( ( TXNIC_PF_TL2_CFG_RR_QUANTUM_DEFAULT ), + ( TXNIC_PF_TL2_CFG ( tl2_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_TL3_CFG_RR_QUANTUM_DEFAULT ), + ( TXNIC_PF_TL3_CFG ( tl3_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_TL3_CHAN_CHAN ( chan_idx ) ), + ( TXNIC_PF_TL3_CHAN ( tl3_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_TL4_CFG_SQ_QS ( vnic_idx ) | + TXNIC_PF_TL4_CFG_RR_QUANTUM_DEFAULT ), + ( TXNIC_PF_TL4_CFG ( tl4_idx ) + pf->regs ) ); + + /* Configure send queue */ + writeq ( ( TXNIC_PF_QS_SQ_CFG_CQ_QS ( vnic_idx ) ), + ( TXNIC_PF_QS_SQ_CFG(0) + qsregs ) ); + writeq ( ( TXNIC_PF_QS_SQ_CFG2_TL4 ( tl4_idx ) ), + ( TXNIC_PF_QS_SQ_CFG2(0) + qsregs ) ); + + /* Configure receive queue */ + writeq ( ( TXNIC_PF_QS_RQ_CFG_CACHING_ALL | + TXNIC_PF_QS_RQ_CFG_CQ_QS ( vnic_idx ) | + TXNIC_PF_QS_RQ_CFG_RBDR_CONT_QS ( vnic_idx ) | + TXNIC_PF_QS_RQ_CFG_RBDR_STRT_QS ( vnic_idx ) ), + ( TXNIC_PF_QS_RQ_CFG(0) + qsregs ) ); + writeq ( ( TXNIC_PF_QS_RQ_BP_CFG_RBDR_BP_ENA | + TXNIC_PF_QS_RQ_BP_CFG_CQ_BP_ENA | + TXNIC_PF_QS_RQ_BP_CFG_BPID ( vnic_idx ) ), + ( TXNIC_PF_QS_RQ_BP_CFG(0) + qsregs ) ); + + /* Enable queue set */ + writeq ( ( TXNIC_PF_QS_CFG_ENA | TXNIC_PF_QS_CFG_VNIC ( vnic_idx ) ), + ( TXNIC_PF_QS_CFG + qsregs ) ); + + /* Open virtual NIC */ + if ( ( rc = txnic_open ( vnic ) ) != 0 ) + goto err_open; + + /* Update link state */ + txnic_lmac_update_link ( lmac ); + + return 0; + + txnic_close ( vnic ); + err_open: + writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void txnic_lmac_close ( struct net_device *netdev ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + struct txnic *vnic = lmac->vnic; + void *qsregs = ( pf->regs + TXNIC_PF_QS ( lmac->idx ) ); + + /* Close virtual NIC */ + txnic_close ( vnic ); + + /* Disable queue set */ + writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int txnic_lmac_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic *vnic = lmac->vnic; + + return txnic_send ( vnic, iobuf ); +} + +/** + * Poll network device + * + * @v netdev Network device + */ +static void txnic_lmac_poll ( struct net_device *netdev ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic *vnic = lmac->vnic; + + /* Poll virtual NIC */ + txnic_poll ( vnic ); + + /* Poll link state */ + txnic_lmac_poll_link ( lmac ); +} + +/** Network device operations */ +static struct net_device_operations txnic_lmac_operations = { + .open = txnic_lmac_open, + .close = txnic_lmac_close, + .transmit = txnic_lmac_transmit, + .poll = txnic_lmac_poll, +}; + +/** + * Probe logical MAC virtual NIC + * + * @v lmac Logical MAC + * @ret rc Return status code + */ +static int txnic_lmac_probe ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + struct txnic *vnic; + struct net_device *netdev; + unsigned long membase; + int rc; + + /* Sanity check */ + assert ( lmac->vnic == NULL ); + + /* Calculate register base address */ + membase = ( pf->vf_membase + ( lmac->idx * pf->vf_stride ) ); + + /* Allocate and initialise network device */ + vnic = txnic_alloc ( &bgx->pci->dev, membase ); + if ( ! vnic ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev = vnic->netdev; + netdev_init ( netdev, &txnic_lmac_operations ); + netdev->priv = lmac; + lmac->vnic = vnic; + + /* Reset device */ + txnic_lmac_reset ( lmac ); + + /* Set MAC address */ + memcpy ( netdev->hw_addr, lmac->mac.raw, ETH_ALEN ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + vnic->name = netdev->name; + DBGC ( TXNICCOL ( pf ), "TXNIC %d/%d/%d is %s (%s)\n", pf->node, + bgx->idx, lmac->idx, vnic->name, eth_ntoa ( lmac->mac.raw ) ); + + /* Update link state */ + txnic_lmac_update_link ( lmac ); + + return 0; + + unregister_netdev ( netdev ); + err_register: + txnic_lmac_reset ( lmac ); + txnic_free ( vnic ); + lmac->vnic = NULL; + err_alloc: + return rc; +} + +/** + * Remove logical MAC virtual NIC + * + * @v lmac Logical MAC + */ +static void txnic_lmac_remove ( struct txnic_lmac *lmac ) { + + /* Sanity check */ + assert ( lmac->vnic != NULL ); + + /* Unregister network device */ + unregister_netdev ( lmac->vnic->netdev ); + + /* Reset device */ + txnic_lmac_reset ( lmac ); + + /* Free virtual NIC */ + txnic_free ( lmac->vnic ); + lmac->vnic = NULL; +} + +/** + * Probe all LMACs on a BGX Ethernet interface + * + * @v pf Physical function + * @v bgx BGX Ethernet interface + * @ret rc Return status code + */ +static int txnic_lmac_probe_all ( struct txnic_pf *pf, struct txnic_bgx *bgx ) { + unsigned int bgx_idx; + int lmac_idx; + int count; + int rc; + + /* Sanity checks */ + bgx_idx = bgx->idx; + assert ( pf->node == bgx->node ); + assert ( pf->bgx[bgx_idx] == NULL ); + assert ( bgx->pf == NULL ); + + /* Associate BGX with physical function */ + pf->bgx[bgx_idx] = bgx; + bgx->pf = pf; + + /* Probe all LMACs */ + count = bgx->count; + for ( lmac_idx = 0 ; lmac_idx < count ; lmac_idx++ ) { + if ( ( rc = txnic_lmac_probe ( &bgx->lmac[lmac_idx] ) ) != 0 ) + goto err_probe; + } + + return 0; + + lmac_idx = count; + err_probe: + for ( lmac_idx-- ; lmac_idx >= 0 ; lmac_idx-- ) + txnic_lmac_remove ( &bgx->lmac[lmac_idx] ); + pf->bgx[bgx_idx] = NULL; + bgx->pf = NULL; + return rc; +} + +/** + * Remove all LMACs on a BGX Ethernet interface + * + * @v pf Physical function + * @v bgx BGX Ethernet interface + */ +static void txnic_lmac_remove_all ( struct txnic_pf *pf, + struct txnic_bgx *bgx ) { + unsigned int lmac_idx; + + /* Sanity checks */ + assert ( pf->bgx[bgx->idx] == bgx ); + assert ( bgx->pf == pf ); + + /* Remove all LMACs */ + for ( lmac_idx = 0 ; lmac_idx < bgx->count ; lmac_idx++ ) + txnic_lmac_remove ( &bgx->lmac[lmac_idx] ); + + /* Disassociate BGX from physical function */ + pf->bgx[bgx->idx] = NULL; + bgx->pf = NULL; +} + +/****************************************************************************** + * + * NIC physical function interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int txnic_pf_probe ( struct pci_device *pci ) { + struct txnic_pf *pf; + struct txnic_bgx *bgx; + unsigned long membase; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + pf = zalloc ( sizeof ( *pf ) ); + if ( ! pf ) { + rc = -ENOMEM; + goto err_alloc; + } + pf->pci = pci; + pci_set_drvdata ( pci, pf ); + + /* Get base addresses */ + membase = pciea_bar_start ( pci, PCIEA_BEI_BAR_0 ); + pf->vf_membase = pciea_bar_start ( pci, PCIEA_BEI_VF_BAR_0 ); + pf->vf_stride = pciea_bar_size ( pci, PCIEA_BEI_VF_BAR_0 ); + + /* Calculate node ID */ + pf->node = txnic_address_node ( membase ); + DBGC ( TXNICCOL ( pf ), "TXNIC %d/*/* PF %s at %#lx (VF %#lx+%#lx)\n", + pf->node, pci->dev.name, membase, pf->vf_membase, pf->vf_stride); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + pf->regs = ioremap ( membase, TXNIC_PF_BAR_SIZE ); + if ( ! pf->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Configure physical function */ + writeq ( TXNIC_PF_CFG_ENA, ( pf->regs + TXNIC_PF_CFG ) ); + writeq ( ( TXNIC_PF_BP_CFG_BP_POLL_ENA | + TXNIC_PF_BP_CFG_BP_POLL_DLY_DEFAULT ), + ( pf->regs + TXNIC_PF_BP_CFG ) ); + for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) { + writeq ( ( TXNIC_PF_INTF_SEND_CFG_BLOCK_BGX | + TXNIC_PF_INTF_SEND_CFG_BLOCK ( i ) ), + ( pf->regs + TXNIC_PF_INTF_SEND_CFG ( i ) ) ); + writeq ( ( TXNIC_PF_INTF_BP_CFG_BP_ENA | + TXNIC_PF_INTF_BP_CFG_BP_ID_BGX | + TXNIC_PF_INTF_BP_CFG_BP_ID ( i ) ), + ( pf->regs + TXNIC_PF_INTF_BP_CFG ( i ) ) ); + } + writeq ( ( TXNIC_PF_PKIND_CFG_LENERR_EN | + TXNIC_PF_PKIND_CFG_MAXLEN_DISABLE | + TXNIC_PF_PKIND_CFG_MINLEN_DISABLE ), + ( pf->regs + TXNIC_PF_PKIND_CFG(0) ) ); + + /* Add to list of physical functions */ + list_add_tail ( &pf->list, &txnic_pfs ); + + /* Probe all LMACs, if applicable */ + list_for_each_entry ( bgx, &txnic_bgxs, list ) { + if ( bgx->node != pf->node ) + continue; + if ( ( rc = txnic_lmac_probe_all ( pf, bgx ) ) != 0 ) + goto err_probe; + } + + return 0; + + err_probe: + for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) { + if ( pf->bgx[i] ) + txnic_lmac_remove_all ( pf, pf->bgx[i] ); + } + list_del ( &pf->list ); + writeq ( 0, ( pf->regs + TXNIC_PF_CFG ) ); + iounmap ( pf->regs ); + err_ioremap: + free ( pf ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void txnic_pf_remove ( struct pci_device *pci ) { + struct txnic_pf *pf = pci_get_drvdata ( pci ); + unsigned int i; + + /* Remove all LMACs, if applicable */ + for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) { + if ( pf->bgx[i] ) + txnic_lmac_remove_all ( pf, pf->bgx[i] ); + } + + /* Remove from list of physical functions */ + list_del ( &pf->list ); + + /* Disable physical function */ + writeq ( 0, ( pf->regs + TXNIC_PF_CFG ) ); + + /* Unmap registers */ + iounmap ( pf->regs ); + + /* Free physical function */ + free ( pf ); +} + +/** NIC physical function PCI device IDs */ +static struct pci_device_id txnic_pf_ids[] = { + PCI_ROM ( 0x177d, 0xa01e, "thunder-pf", "ThunderX NIC PF", 0 ), +}; + +/** NIC physical function PCI driver */ +struct pci_driver txnic_pf_driver __pci_driver = { + .ids = txnic_pf_ids, + .id_count = ( sizeof ( txnic_pf_ids ) / sizeof ( txnic_pf_ids[0] ) ), + .probe = txnic_pf_probe, + .remove = txnic_pf_remove, +}; + +/****************************************************************************** + * + * BGX interface + * + ****************************************************************************** + */ + +/** LMAC types */ +static struct txnic_lmac_type txnic_lmac_types[] = { + [TXNIC_LMAC_XAUI] = { + .name = "XAUI", + .count = 1, + .lane_to_sds = 0xe4, + }, + [TXNIC_LMAC_RXAUI] = { + .name = "RXAUI", + .count = 2, + .lane_to_sds = 0x0e04, + }, + [TXNIC_LMAC_10G_R] = { + .name = "10GBASE-R", + .count = 4, + .lane_to_sds = 0x00000000, + }, + [TXNIC_LMAC_40G_R] = { + .name = "40GBASE-R", + .count = 1, + .lane_to_sds = 0xe4, + }, +}; + +/** + * Detect BGX Ethernet interface LMAC type + * + * @v bgx BGX Ethernet interface + * @ret type LMAC type, or negative error + */ +static int txnic_bgx_detect ( struct txnic_bgx *bgx ) { + uint64_t config; + uint64_t br_pmd_control; + uint64_t rx_lmacs; + unsigned int type; + + /* We assume that the early (pre-UEFI) firmware will have + * configured at least the LMAC 0 type and use of link + * training, and may have overridden the number of LMACs. + */ + + /* Determine type from LMAC 0 */ + config = readq ( bgx->regs + BGX_CMR_CONFIG ); + type = BGX_CMR_CONFIG_LMAC_TYPE_GET ( config ); + if ( ( type >= ( sizeof ( txnic_lmac_types ) / + sizeof ( txnic_lmac_types[0] ) ) ) || + ( txnic_lmac_types[type].count == 0 ) ) { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* BGX unknown type %d\n", + bgx->node, bgx->idx, type ); + return -ENOTTY; + } + bgx->type = &txnic_lmac_types[type]; + + /* Check whether link training is required */ + br_pmd_control = readq ( bgx->regs + BGX_SPU_BR_PMD_CONTROL ); + bgx->training = + ( !! ( br_pmd_control & BGX_SPU_BR_PMD_CONTROL_TRAIN_EN ) ); + + /* Determine number of LMACs */ + rx_lmacs = readq ( bgx->regs + BGX_CMR_RX_LMACS ); + bgx->count = BGX_CMR_RX_LMACS_LMACS_GET ( rx_lmacs ); + if ( ( bgx->count == TXNIC_NUM_LMAC ) && + ( bgx->type->count != TXNIC_NUM_LMAC ) ) { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* assuming %d LMACs\n", + bgx->node, bgx->idx, bgx->type->count ); + bgx->count = bgx->type->count; + } + + return type; +} + +/** + * Initialise BGX Ethernet interface + * + * @v bgx BGX Ethernet interface + * @v type LMAC type + */ +static void txnic_bgx_init ( struct txnic_bgx *bgx, unsigned int type ) { + uint64_t global_config; + uint32_t lane_to_sds; + unsigned int i; + + /* Set number of LMACs */ + writeq ( BGX_CMR_RX_LMACS_LMACS_SET ( bgx->count ), + ( bgx->regs + BGX_CMR_RX_LMACS ) ); + writeq ( BGX_CMR_TX_LMACS_LMACS_SET ( bgx->count ), + ( bgx->regs + BGX_CMR_TX_LMACS ) ); + + /* Set LMAC types and lane mappings, and disable all LMACs */ + lane_to_sds = bgx->type->lane_to_sds; + for ( i = 0 ; i < bgx->count ; i++ ) { + writeq ( ( BGX_CMR_CONFIG_LMAC_TYPE_SET ( type ) | + BGX_CMR_CONFIG_LANE_TO_SDS ( lane_to_sds ) ), + ( bgx->regs + BGX_LMAC ( i ) + BGX_CMR_CONFIG ) ); + lane_to_sds >>= 8; + } + + /* Reset all MAC address filtering */ + for ( i = 0 ; i < TXNIC_NUM_DMAC ; i++ ) + writeq ( 0, ( bgx->regs + BGX_CMR_RX_DMAC_CAM ( i ) ) ); + + /* Reset NCSI steering */ + for ( i = 0 ; i < TXNIC_NUM_STEERING ; i++ ) + writeq ( 0, ( bgx->regs + BGX_CMR_RX_STEERING ( i ) ) ); + + /* Enable backpressure to all channels */ + writeq ( BGX_CMR_CHAN_MSK_AND_ALL ( bgx->count ), + ( bgx->regs + BGX_CMR_CHAN_MSK_AND ) ); + + /* Strip FCS */ + global_config = readq ( bgx->regs + BGX_CMR_GLOBAL_CONFIG ); + global_config |= BGX_CMR_GLOBAL_CONFIG_FCS_STRIP; + writeq ( global_config, ( bgx->regs + BGX_CMR_GLOBAL_CONFIG ) ); +} + +/** + * Initialise Super PHY Unit (SPU) + * + * @v lmac Logical MAC + */ +static void txnic_bgx_spu_init ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + + /* Reset PHY */ + writeq ( BGX_SPU_CONTROL1_RESET, ( lmac->regs + BGX_SPU_CONTROL1 ) ); + mdelay ( BGX_SPU_RESET_DELAY_MS ); + + /* Power down PHY */ + writeq ( BGX_SPU_CONTROL1_LO_PWR, ( lmac->regs + BGX_SPU_CONTROL1 ) ); + + /* Configure training, if applicable */ + if ( bgx->training ) { + writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LP_CUP ) ); + writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LD_CUP ) ); + writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LD_REP ) ); + writeq ( BGX_SPU_BR_PMD_CONTROL_TRAIN_EN, + ( lmac->regs + BGX_SPU_BR_PMD_CONTROL ) ); + } + + /* Disable forward error correction */ + writeq ( 0, ( lmac->regs + BGX_SPU_FEC_CONTROL ) ); + + /* Disable autonegotiation */ + writeq ( 0, ( lmac->regs + BGX_SPU_AN_CONTROL ) ); + + /* Power up PHY */ + writeq ( 0, ( lmac->regs + BGX_SPU_CONTROL1 ) ); +} + +/** + * Initialise LMAC + * + * @v bgx BGX Ethernet interface + * @v lmac_idx LMAC index + */ +static void txnic_bgx_lmac_init ( struct txnic_bgx *bgx, + unsigned int lmac_idx ) { + struct txnic_lmac *lmac = &bgx->lmac[lmac_idx]; + uint64_t config; + + /* Record associated BGX */ + lmac->bgx = bgx; + + /* Set register base address (already mapped) */ + lmac->regs = ( bgx->regs + BGX_LMAC ( lmac_idx ) ); + + /* Calculate virtual NIC index */ + lmac->idx = TXNIC_VNIC_IDX ( bgx->idx, lmac_idx ); + + /* Set MAC address */ + eth_random_addr ( lmac->mac.raw ); + + /* Initialise PHY */ + txnic_bgx_spu_init ( lmac ); + + /* Accept all multicasts and broadcasts */ + writeq ( ( BGX_CMR_RX_DMAC_CTL_MCST_MODE_ACCEPT | + BGX_CMR_RX_DMAC_CTL_BCST_ACCEPT ), + ( lmac->regs + BGX_CMR_RX_DMAC_CTL ) ); + + /* Enable LMAC */ + config = readq ( lmac->regs + BGX_CMR_CONFIG ); + config |= ( BGX_CMR_CONFIG_ENABLE | + BGX_CMR_CONFIG_DATA_PKT_RX_EN | + BGX_CMR_CONFIG_DATA_PKT_TX_EN ); + writeq ( config, ( lmac->regs + BGX_CMR_CONFIG ) ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int txnic_bgx_probe ( struct pci_device *pci ) { + struct txnic_bgx *bgx; + struct txnic_pf *pf; + unsigned long membase; + unsigned int i; + int type; + int rc; + + /* Allocate and initialise structure */ + bgx = zalloc ( sizeof ( *bgx ) ); + if ( ! bgx ) { + rc = -ENOMEM; + goto err_alloc; + } + bgx->pci = pci; + pci_set_drvdata ( pci, bgx ); + + /* Get base address */ + membase = pciea_bar_start ( pci, PCIEA_BEI_BAR_0 ); + + /* Calculate node ID and index */ + bgx->node = txnic_address_node ( membase ); + bgx->idx = txnic_address_bgx ( membase ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + bgx->regs = ioremap ( membase, TXNIC_BGX_BAR_SIZE ); + if ( ! bgx->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Detect LMAC type */ + if ( ( type = txnic_bgx_detect ( bgx ) ) < 0 ) { + rc = type; + goto err_detect; + } + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* BGX %s at %#lx %dx %s%s\n", + bgx->node, bgx->idx, pci->dev.name, membase, bgx->count, + bgx->type->name, ( bgx->training ? "(training)" : "" ) ); + + /* Initialise interface */ + txnic_bgx_init ( bgx, type ); + + /* Initialise all LMACs */ + for ( i = 0 ; i < bgx->count ; i++ ) + txnic_bgx_lmac_init ( bgx, i ); + + /* Add to list of BGX devices */ + list_add_tail ( &bgx->list, &txnic_bgxs ); + + /* Probe all LMACs, if applicable */ + list_for_each_entry ( pf, &txnic_pfs, list ) { + if ( pf->node != bgx->node ) + continue; + if ( ( rc = txnic_lmac_probe_all ( pf, bgx ) ) != 0 ) + goto err_probe; + } + + return 0; + + if ( bgx->pf ) + txnic_lmac_remove_all ( bgx->pf, bgx ); + list_del ( &bgx->list ); + err_probe: + err_detect: + iounmap ( bgx->regs ); + err_ioremap: + free ( bgx ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void txnic_bgx_remove ( struct pci_device *pci ) { + struct txnic_bgx *bgx = pci_get_drvdata ( pci ); + + /* Remove all LMACs, if applicable */ + if ( bgx->pf ) + txnic_lmac_remove_all ( bgx->pf, bgx ); + + /* Remove from list of BGX devices */ + list_del ( &bgx->list ); + + /* Unmap registers */ + iounmap ( bgx->regs ); + + /* Free BGX device */ + free ( bgx ); +} + +/** BGX PCI device IDs */ +static struct pci_device_id txnic_bgx_ids[] = { + PCI_ROM ( 0x177d, 0xa026, "thunder-bgx", "ThunderX BGX", 0 ), +}; + +/** BGX PCI driver */ +struct pci_driver txnic_bgx_driver __pci_driver = { + .ids = txnic_bgx_ids, + .id_count = ( sizeof ( txnic_bgx_ids ) / sizeof ( txnic_bgx_ids[0] ) ), + .probe = txnic_bgx_probe, + .remove = txnic_bgx_remove, +}; diff --git a/src/drivers/net/thunderx.h b/src/drivers/net/thunderx.h new file mode 100644 index 000000000..410daf6e2 --- /dev/null +++ b/src/drivers/net/thunderx.h @@ -0,0 +1,949 @@ +#ifndef _THUNDERX_H +#define _THUNDERX_H + +/** @file + * + * Cavium ThunderX Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/****************************************************************************** + * + * Address space + * + ****************************************************************************** + */ + +/** Size of a cache line */ +#define TXNIC_LINE_SIZE 128 + +/** Virtual function BAR size */ +#define TXNIC_VF_BAR_SIZE 0x200000UL + +/** Physical function BAR size */ +#define TXNIC_PF_BAR_SIZE 0x40000000UL + +/** BGX BAR size */ +#define TXNIC_BGX_BAR_SIZE 0x400000UL + +/** Maximum number of BGX Ethernet interfaces (per node) */ +#define TXNIC_NUM_BGX 2 + +/** Maximum number of Logical MACs (per BGX) */ +#define TXNIC_NUM_LMAC 4 + +/** Maximum number of destination MAC addresses (per BGX) */ +#define TXNIC_NUM_DMAC 32 + +/** Maximum number of steering rules (per BGX) */ +#define TXNIC_NUM_STEERING 8 + +/** + * Calculate node ID + * + * @v addr PCI BAR base address + * @ret node Node ID + */ +static inline unsigned int txnic_address_node ( uint64_t addr ) { + + /* Node ID is in bits [45:44] of the hardcoded BAR address */ + return ( ( addr >> 44 ) & 0x3 ); +} + +/** + * Calculate BGX Ethernet interface index + * + * @v addr PCI BAR base address + * @ret index Index + */ +static inline unsigned int txnic_address_bgx ( uint64_t addr ) { + + /* Index is in bit 24 of the hardcoded BAR address */ + return ( ( addr >> 24 ) & 0x1 ); +} + +/****************************************************************************** + * + * Send queue + * + ****************************************************************************** + */ + +/** Send queue configuration */ +#define TXNIC_QS_SQ_CFG(q) ( ( (q) << 18 ) | 0x010800 ) +#define TXNIC_QS_SQ_CFG_ENA ( 1ULL << 19 ) +#define TXNIC_QS_SQ_CFG_RESET ( 1ULL << 17 ) +#define TXNIC_QS_SQ_CFG_QSIZE(sz) ( ( ( uint64_t ) (sz) ) << 8 ) +#define TXNIC_QS_SQ_CFG_QSIZE_1K \ + TXNIC_QS_SQ_CFG_QSIZE ( 0 ) + +/** Send queue base address */ +#define TXNIC_QS_SQ_BASE(q) ( ( (q) << 18 ) | 0x010820 ) + +/** Send queue head pointer */ +#define TXNIC_QS_SQ_HEAD(q) ( ( (q) << 18 ) | 0x010828 ) + +/** Send queue tail pointer */ +#define TXNIC_QS_SQ_TAIL(q) ( ( (q) << 18 ) | 0x010830 ) + +/** Send queue doorbell */ +#define TXNIC_QS_SQ_DOOR(q) ( ( (q) << 18 ) | 0x010838 ) + +/** Send queue status */ +#define TXNIC_QS_SQ_STATUS(q) ( ( (q) << 18 ) | 0x010840 ) +#define TXNIC_QS_SQ_STATUS_STOPPED ( 1ULL << 21 ) + +/** Maximum time to wait for a send queue to stop + * + * This is a policy decision. + */ +#define TXNIC_SQ_STOP_MAX_WAIT_MS 100 + +/** A send header subdescriptor */ +struct txnic_send_header { + /** Total length */ + uint32_t total; + /** Unused */ + uint8_t unused_a[2]; + /** Subdescriptor count */ + uint8_t subdcnt; + /** Flags */ + uint8_t flags; + /** Unused */ + uint8_t unused_b[8]; +} __attribute__ (( packed )); + +/** Flags for send header subdescriptor + * + * These comprise SUBDC=0x1 and PNC=0x1. + */ +#define TXNIC_SEND_HDR_FLAGS 0x14 + +/** A send gather subdescriptor */ +struct txnic_send_gather { + /** Size */ + uint16_t size; + /** Unused */ + uint8_t unused[5]; + /** Flags */ + uint8_t flags; + /** Address */ + uint64_t addr; +} __attribute__ (( packed )); + +/** Flags for send gather subdescriptor + * + * These comprise SUBDC=0x4 and LD_TYPE=0x0. + */ +#define TXNIC_SEND_GATHER_FLAGS 0x40 + +/** A send queue entry + * + * Each send queue entry comprises a single send header subdescriptor + * and a single send gather subdescriptor. + */ +struct txnic_sqe { + /** Send header descriptor */ + struct txnic_send_header hdr; + /** Send gather descriptor */ + struct txnic_send_gather gather; +} __attribute__ (( packed )); + +/** Number of subdescriptors per send queue entry */ +#define TXNIC_SQE_SUBDESCS ( sizeof ( struct txnic_sqe ) / \ + sizeof ( struct txnic_send_header ) ) + +/** Number of send queue entries + * + * The minimum send queue size is 1024 entries. + */ +#define TXNIC_SQES ( 1024 / TXNIC_SQE_SUBDESCS ) + +/** Send queue maximum fill level + * + * This is a policy decision. + */ +#define TXNIC_SQ_FILL 32 + +/** Send queue alignment */ +#define TXNIC_SQ_ALIGN TXNIC_LINE_SIZE + +/** Send queue stride */ +#define TXNIC_SQ_STRIDE sizeof ( struct txnic_sqe ) + +/** Send queue size */ +#define TXNIC_SQ_SIZE ( TXNIC_SQES * TXNIC_SQ_STRIDE ) + +/** A send queue */ +struct txnic_sq { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Send queue entries */ + userptr_t sqe; +}; + +/****************************************************************************** + * + * Receive queue + * + ****************************************************************************** + */ + +/** Receive queue configuration */ +#define TXNIC_QS_RQ_CFG(q) ( ( (q) << 18 ) | 0x010600 ) +#define TXNIC_QS_RQ_CFG_ENA ( 1ULL << 1 ) + +/** Maximum time to wait for a receive queue to disable + * + * This is a policy decision. + */ +#define TXNIC_RQ_DISABLE_MAX_WAIT_MS 100 + +/** Receive buffer descriptor ring configuration */ +#define TXNIC_QS_RBDR_CFG(q) ( ( (q) << 18 ) | 0x010c00 ) +#define TXNIC_QS_RBDR_CFG_ENA ( 1ULL << 44 ) +#define TXNIC_QS_RBDR_CFG_RESET ( 1ULL << 43 ) +#define TXNIC_QS_RBDR_CFG_QSIZE(sz) ( ( ( uint64_t ) (sz) ) << 32 ) +#define TXNIC_QS_RBDR_CFG_QSIZE_8K \ + TXNIC_QS_RBDR_CFG_QSIZE ( 0 ) +#define TXNIC_QS_RBDR_CFG_LINES(sz) ( ( ( uint64_t ) (sz) ) << 0 ) + +/** Receive buffer descriptor ring base address */ +#define TXNIC_QS_RBDR_BASE(q) ( ( (q) << 18 ) | 0x010c20 ) + +/** Receive buffer descriptor ring head pointer */ +#define TXNIC_QS_RBDR_HEAD(q) ( ( (q) << 18 ) | 0x010c28 ) + +/** Receive buffer descriptor ring tail pointer */ +#define TXNIC_QS_RBDR_TAIL(q) ( ( (q) << 18 ) | 0x010c30 ) + +/** Receive buffer descriptor ring doorbell */ +#define TXNIC_QS_RBDR_DOOR(q) ( ( (q) << 18 ) | 0x010c38 ) + +/** Receive buffer descriptor ring status 0 */ +#define TXNIC_QS_RBDR_STATUS0(q) ( ( (q) << 18 ) | 0x010c40 ) + +/** A receive buffer descriptor ring entry */ +struct txnic_rbdr_entry { + /** Address */ + uint64_t addr; +} __attribute__ (( packed )); + +/** A receive queue entry */ +struct txnic_rqe { + /** Receive buffer descriptor ring entry */ + struct txnic_rbdr_entry rbdre; +} __attribute__ (( packed )); + +/** Number of receive queue entries + * + * The minimum receive queue size is 8192 entries. + */ +#define TXNIC_RQES 8192 + +/** Receive queue maximum fill level + * + * This is a policy decision. Must not exceed TXNIC_RQES. + */ +#define TXNIC_RQ_FILL 32 + +/** Receive queue entry size + * + * This is a policy decision. + */ +#define TXNIC_RQE_SIZE ( ( ETH_DATA_ALIGN + ETH_FRAME_LEN + \ + 4 /* VLAN */ + TXNIC_LINE_SIZE - 1 ) \ + & ~( TXNIC_LINE_SIZE - 1 ) ) + +/** Receive queue alignment */ +#define TXNIC_RQ_ALIGN TXNIC_LINE_SIZE + +/** Receive queue stride */ +#define TXNIC_RQ_STRIDE sizeof ( struct txnic_rqe ) + +/** Receive queue size */ +#define TXNIC_RQ_SIZE ( TXNIC_RQES * TXNIC_RQ_STRIDE ) + +/** A receive queue */ +struct txnic_rq { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Receive queue entries */ + userptr_t rqe; + /** I/O buffers */ + struct io_buffer *iobuf[TXNIC_RQ_FILL]; +}; + +/****************************************************************************** + * + * Completion queue + * + ****************************************************************************** + */ + +/** Completion queue configuration */ +#define TXNIC_QS_CQ_CFG(q) ( ( (q) << 18 ) | 0x010400 ) +#define TXNIC_QS_CQ_CFG_ENA ( 1ULL << 42 ) +#define TXNIC_QS_CQ_CFG_RESET ( 1ULL << 41 ) +#define TXNIC_QS_CQ_CFG_QSIZE(sz) ( ( ( uint64_t ) (sz) ) << 32 ) +#define TXNIC_QS_CQ_CFG_QSIZE_256 \ + TXNIC_QS_CQ_CFG_QSIZE ( 7 ) + +/** Maximum time to wait for a completion queue to disable + * + * This is a policy decision. + */ +#define TXNIC_CQ_DISABLE_MAX_WAIT_MS 100 + +/** Completion queue base address */ +#define TXNIC_QS_CQ_BASE(q) ( ( (q) << 18 ) | 0x010420 ) + +/** Completion queue head pointer */ +#define TXNIC_QS_CQ_HEAD(q) ( ( (q) << 18 ) | 0x010428 ) + +/** Completion queue tail pointer */ +#define TXNIC_QS_CQ_TAIL(q) ( ( (q) << 18 ) | 0x010430 ) + +/** Completion queue doorbell */ +#define TXNIC_QS_CQ_DOOR(q) ( ( (q) << 18 ) | 0x010438 ) + +/** Completion queue status */ +#define TXNIC_QS_CQ_STATUS(q) ( ( (q) << 18 ) | 0x010440 ) +#define TXNIC_QS_CQ_STATUS_QCOUNT(status) \ + ( ( (status) >> 0 ) & 0xffff ) + +/** Completion queue status 2 */ +#define TXNIC_QS_CQ_STATUS2(q) ( ( (q) << 18 ) | 0x010448 ) + +/** A send completion queue entry */ +struct txnic_cqe_send { + /** Status */ + uint8_t send_status; + /** Unused */ + uint8_t unused[4]; + /** Send queue entry pointer */ + uint16_t sqe_ptr; + /** Type */ + uint8_t cqe_type; +} __attribute__ (( packed )); + +/** Send completion queue entry type */ +#define TXNIC_CQE_TYPE_SEND 0x80 + +/** A receive completion queue entry */ +struct txnic_cqe_rx { + /** Error opcode */ + uint8_t errop; + /** Unused */ + uint8_t unused_a[6]; + /** Type */ + uint8_t cqe_type; + /** Unused */ + uint8_t unused_b[1]; + /** Padding */ + uint8_t apad; + /** Unused */ + uint8_t unused_c[4]; + /** Length */ + uint16_t len; +} __attribute__ (( packed )); + +/** Receive completion queue entry type */ +#define TXNIC_CQE_TYPE_RX 0x20 + +/** Applied padding */ +#define TXNIC_CQE_RX_APAD_LEN( apad ) ( (apad) >> 5 ) + +/** Completion queue entry common fields */ +struct txnic_cqe_common { + /** Unused */ + uint8_t unused_a[7]; + /** Type */ + uint8_t cqe_type; +} __attribute__ (( packed )); + +/** A completion queue entry */ +union txnic_cqe { + /** Common fields */ + struct txnic_cqe_common common; + /** Send completion */ + struct txnic_cqe_send send; + /** Receive completion */ + struct txnic_cqe_rx rx; +}; + +/** Number of completion queue entries + * + * The minimum completion queue size is 256 entries. + */ +#define TXNIC_CQES 256 + +/** Completion queue alignment */ +#define TXNIC_CQ_ALIGN 512 + +/** Completion queue stride */ +#define TXNIC_CQ_STRIDE 512 + +/** Completion queue size */ +#define TXNIC_CQ_SIZE ( TXNIC_CQES * TXNIC_CQ_STRIDE ) + +/** A completion queue */ +struct txnic_cq { + /** Consumer counter */ + unsigned int cons; + /** Completion queue entries */ + userptr_t cqe; +}; + +/****************************************************************************** + * + * Virtual NIC + * + ****************************************************************************** + */ + +/** A virtual NIC */ +struct txnic { + /** Registers */ + void *regs; + /** Device name (for debugging) */ + const char *name; + /** Network device */ + struct net_device *netdev; + + /** Send queue */ + struct txnic_sq sq; + /** Receive queue */ + struct txnic_rq rq; + /** Completion queue */ + struct txnic_cq cq; +}; + +/****************************************************************************** + * + * Physical function + * + ****************************************************************************** + */ + +/** Physical function configuration */ +#define TXNIC_PF_CFG 0x000000 +#define TXNIC_PF_CFG_ENA ( 1ULL << 0 ) + +/** Backpressure configuration */ +#define TXNIC_PF_BP_CFG 0x000080 +#define TXNIC_PF_BP_CFG_BP_POLL_ENA ( 1ULL << 6 ) +#define TXNIC_PF_BP_CFG_BP_POLL_DLY(dl) ( ( ( uint64_t ) (dl) ) << 0 ) +#define TXNIC_PF_BP_CFG_BP_POLL_DLY_DEFAULT \ + TXNIC_PF_BP_CFG_BP_POLL_DLY ( 3 ) + +/** Interface send configuration */ +#define TXNIC_PF_INTF_SEND_CFG(in) ( ( (in) << 8 ) | 0x000200 ) +#define TXNIC_PF_INTF_SEND_CFG_BLOCK_BGX ( 1ULL << 3 ) +#define TXNIC_PF_INTF_SEND_CFG_BLOCK(bl) ( ( ( uint64_t ) (bl) ) << 0 ) + +/** Interface backpressure configuration */ +#define TXNIC_PF_INTF_BP_CFG(in) ( ( (in) << 8 ) | 0x000208 ) +#define TXNIC_PF_INTF_BP_CFG_BP_ENA ( 1ULL << 63 ) +#define TXNIC_PF_INTF_BP_CFG_BP_ID_BGX ( 1ULL << 3 ) +#define TXNIC_PF_INTF_BP_CFG_BP_ID(bp) ( ( ( uint64_t ) (bp) ) << 0 ) + +/** Port kind configuration */ +#define TXNIC_PF_PKIND_CFG(pk) ( ( (pk) << 3 ) | 0x000600 ) +#define TXNIC_PF_PKIND_CFG_LENERR_EN ( 1ULL << 33 ) +#define TXNIC_PF_PKIND_CFG_MAXLEN(ct) ( ( ( uint64_t ) (ct) ) << 16 ) +#define TXNIC_PF_PKIND_CFG_MAXLEN_DISABLE \ + TXNIC_PF_PKIND_CFG_MAXLEN ( 0xffff ) +#define TXNIC_PF_PKIND_CFG_MINLEN(ct) ( ( ( uint64_t ) (ct) ) << 0 ) +#define TXNIC_PF_PKIND_CFG_MINLEN_DISABLE \ + TXNIC_PF_PKIND_CFG_MINLEN ( 0x0000 ) + +/** Match parse index configuration */ +#define TXNIC_PF_MPI_CFG(ix) ( ( (ix) << 3 ) | 0x210000 ) +#define TXNIC_PF_MPI_CFG_VNIC(vn) ( ( ( uint64_t ) (vn) ) << 24 ) +#define TXNIC_PF_MPI_CFG_RSSI_BASE(ix) ( ( ( uint64_t ) (ix) ) << 0 ) + +/** RSS indirection receive queue */ +#define TXNIC_PF_RSSI_RQ(ix) ( ( (ix) << 3 ) | 0x220000 ) +#define TXNIC_PF_RSSI_RQ_RQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 3 ) + +/** LMAC registers */ +#define TXNIC_PF_LMAC(lm) ( ( (lm) << 3 ) | 0x240000 ) + +/** LMAC configuration */ +#define TXNIC_PF_LMAC_CFG 0x000000 +#define TXNIC_PF_LMAC_CFG_ADJUST(ad) ( ( ( uint64_t ) (ad) ) << 8 ) +#define TXNIC_PF_LMAC_CFG_ADJUST_DEFAULT \ + TXNIC_PF_LMAC_CFG_ADJUST ( 6 ) +#define TXNIC_PF_LMAC_CFG_MIN_PKT_SIZE(sz) ( ( ( uint64_t ) (sz) ) << 0 ) + +/** LMAC configuration 2 */ +#define TXNIC_PF_LMAC_CFG2 0x000100 +#define TXNIC_PF_LMAC_CFG2_MAX_PKT_SIZE(sz) ( ( ( uint64_t ) (sz) ) << 0 ) + +/** LMAC credit */ +#define TXNIC_PF_LMAC_CREDIT 0x004000 +#define TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT(ct) ( ( ( uint64_t ) (ct) ) << 12 ) +#define TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT_DEFAULT \ + TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT ( 192 ) +#define TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT(ct) ( ( ( uint64_t ) (ct) ) << 2 ) +#define TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT_DEFAULT \ + TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT ( 511 ) +#define TXNIC_PF_LMAC_CREDIT_CC_ENABLE ( 1ULL << 1 ) + +/** Channel registers */ +#define TXNIC_PF_CHAN(ch) ( ( (ch) << 3 ) | 0x400000 ) + +/** Channel transmit configuration */ +#define TXNIC_PF_CHAN_TX_CFG 0x000000 +#define TXNIC_PF_CHAN_TX_CFG_BP_ENA ( 1ULL << 0 ) + +/** Channel receive configuration */ +#define TXNIC_PF_CHAN_RX_CFG 0x020000 +#define TXNIC_PF_CHAN_RX_CFG_CPI_BASE(ix) ( ( ( uint64_t ) (ix) ) << 48 ) + +/** Channel receive backpressure configuration */ +#define TXNIC_PF_CHAN_RX_BP_CFG 0x080000 +#define TXNIC_PF_CHAN_RX_BP_CFG_ENA ( 1ULL << 63 ) +#define TXNIC_PF_CHAN_RX_BP_CFG_BPID(bp) ( ( ( uint64_t ) (bp) ) << 0 ) + +/** Traffic limiter 2 configuration */ +#define TXNIC_PF_TL2_CFG(tl) ( ( (tl) << 3 ) | 0x500000 ) +#define TXNIC_PF_TL2_CFG_RR_QUANTUM(rr) ( ( ( uint64_t ) (rr) ) << 0 ) +#define TXNIC_PF_TL2_CFG_RR_QUANTUM_DEFAULT \ + TXNIC_PF_TL2_CFG_RR_QUANTUM ( 0x905 ) + +/** Traffic limiter 3 configuration */ +#define TXNIC_PF_TL3_CFG(tl) ( ( (tl) << 3 ) | 0x600000 ) +#define TXNIC_PF_TL3_CFG_RR_QUANTUM(rr) ( ( ( uint64_t ) (rr) ) << 0 ) +#define TXNIC_PF_TL3_CFG_RR_QUANTUM_DEFAULT \ + TXNIC_PF_TL3_CFG_RR_QUANTUM ( 0x905 ) + +/** Traffic limiter 3 channel mapping */ +#define TXNIC_PF_TL3_CHAN(tl) ( ( (tl) << 3 ) | 0x620000 ) +#define TXNIC_PF_TL3_CHAN_CHAN(ch) ( ( (ch) & 0x7f ) << 0 ) + +/** Traffic limiter 4 configuration */ +#define TXNIC_PF_TL4_CFG(tl) ( ( (tl) << 3 ) | 0x800000 ) +#define TXNIC_PF_TL4_CFG_SQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 27 ) +#define TXNIC_PF_TL4_CFG_RR_QUANTUM(rr) ( ( ( uint64_t ) (rr) ) << 0 ) +#define TXNIC_PF_TL4_CFG_RR_QUANTUM_DEFAULT \ + TXNIC_PF_TL4_CFG_RR_QUANTUM ( 0x905 ) + +/** Queue set registers */ +#define TXNIC_PF_QS(qs) ( ( (qs) << 21 ) | 0x20000000UL ) + +/** Queue set configuration */ +#define TXNIC_PF_QS_CFG 0x010000 +#define TXNIC_PF_QS_CFG_ENA ( 1ULL << 31 ) +#define TXNIC_PF_QS_CFG_VNIC(vn) ( ( ( uint64_t ) (vn) ) << 0 ) + +/** Receive queue configuration */ +#define TXNIC_PF_QS_RQ_CFG(q) ( ( (q) << 18 ) | 0x010400 ) +#define TXNIC_PF_QS_RQ_CFG_CACHING(cx) ( ( ( uint64_t ) (cx) ) << 26 ) +#define TXNIC_PF_QS_RQ_CFG_CACHING_ALL \ + TXNIC_PF_QS_RQ_CFG_CACHING ( 1 ) +#define TXNIC_PF_QS_RQ_CFG_CQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 19 ) +#define TXNIC_PF_QS_RQ_CFG_RBDR_CONT_QS(qs) ( ( ( uint64_t ) (qs) ) << 9 ) +#define TXNIC_PF_QS_RQ_CFG_RBDR_STRT_QS(qs) ( ( ( uint64_t ) (qs) ) << 1 ) + +/** Receive queue drop configuration */ +#define TXNIC_PF_QS_RQ_DROP_CFG(q) ( ( (q) << 18 ) | 0x010420 ) + +/** Receive queue backpressure configuration */ +#define TXNIC_PF_QS_RQ_BP_CFG(q) ( ( (q) << 18 ) | 0x010500 ) +#define TXNIC_PF_QS_RQ_BP_CFG_RBDR_BP_ENA ( 1ULL << 63 ) +#define TXNIC_PF_QS_RQ_BP_CFG_CQ_BP_ENA ( 1ULL << 62 ) +#define TXNIC_PF_QS_RQ_BP_CFG_BPID(bp) ( ( ( uint64_t ) (bp) ) << 0 ) + +/** Send queue configuration */ +#define TXNIC_PF_QS_SQ_CFG(q) ( ( (q) << 18 ) | 0x010c00 ) +#define TXNIC_PF_QS_SQ_CFG_CQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 3 ) + +/** Send queue configuration 2 */ +#define TXNIC_PF_QS_SQ_CFG2(q) ( ( (q) << 18 ) | 0x010c08 ) +#define TXNIC_PF_QS_SQ_CFG2_TL4(tl) ( ( ( uint64_t ) (tl) ) << 0 ) + +/** A physical function */ +struct txnic_pf { + /** Registers */ + void *regs; + /** PCI device */ + struct pci_device *pci; + /** Node ID */ + unsigned int node; + + /** Virtual function BAR base */ + unsigned long vf_membase; + /** Virtual function BAR stride */ + unsigned long vf_stride; + + /** List of physical functions */ + struct list_head list; + /** BGX Ethernet interfaces (if known) */ + struct txnic_bgx *bgx[TXNIC_NUM_BGX]; +}; + +/** + * Calculate virtual NIC index + * + * @v bgx_idx BGX Ethernet interface index + * @v lmac_idx Logical MAC index + * @ret vnic_idx Virtual NIC index + */ +#define TXNIC_VNIC_IDX( bgx_idx, lmac_idx ) \ + ( ( (bgx_idx) * TXNIC_NUM_LMAC ) + (lmac_idx) ) + +/** + * Calculate BGX Ethernet interface index + * + * @v vnic_idx Virtual NIC index + * @ret bgx_idx BGX Ethernet interface index + */ +#define TXNIC_BGX_IDX( vnic_idx ) ( (vnic_idx) / TXNIC_NUM_LMAC ) + +/** + * Calculate logical MAC index + * + * @v vnic_idx Virtual NIC index + * @ret lmac_idx Logical MAC index + */ +#define TXNIC_LMAC_IDX( vnic_idx ) ( (vnic_idx) % TXNIC_NUM_LMAC ) + +/** + * Calculate traffic limiter 2 index + * + * @v vnic_idx Virtual NIC index + * @v tl2_idx Traffic limiter 2 index + */ +#define TXNIC_TL2_IDX( vnic_idx ) ( (vnic_idx) << 3 ) + +/** + * Calculate traffic limiter 3 index + * + * @v vnic_idx Virtual NIC index + * @v tl3_idx Traffic limiter 3 index + */ +#define TXNIC_TL3_IDX( vnic_idx ) ( (vnic_idx) << 5 ) + +/** + * Calculate traffic limiter 4 index + * + * @v vnic_idx Virtual NIC index + * @v tl4_idx Traffic limiter 4 index + */ +#define TXNIC_TL4_IDX( vnic_idx ) ( (vnic_idx) << 7 ) + +/** + * Calculate channel index + * + * @v vnic_idx Virtual NIC index + * @v chan_idx Channel index + */ +#define TXNIC_CHAN_IDX( vnic_idx ) ( ( TXNIC_BGX_IDX (vnic_idx) << 7 ) | \ + ( TXNIC_LMAC_IDX (vnic_idx) << 4 ) ) + +/****************************************************************************** + * + * BGX Ethernet interface + * + ****************************************************************************** + */ + +/** Per-LMAC registers */ +#define BGX_LMAC(lm) ( ( (lm) << 20 ) | 0x00000000UL ) + +/** CMR configuration */ +#define BGX_CMR_CONFIG 0x000000 +#define BGX_CMR_CONFIG_ENABLE ( 1ULL << 15 ) +#define BGX_CMR_CONFIG_DATA_PKT_RX_EN ( 1ULL << 14 ) +#define BGX_CMR_CONFIG_DATA_PKT_TX_EN ( 1ULL << 13 ) +#define BGX_CMR_CONFIG_LMAC_TYPE_GET(config) \ + ( ( (config) >> 8 ) & 0x7 ) +#define BGX_CMR_CONFIG_LMAC_TYPE_SET(ty) ( ( ( uint64_t ) (ty) ) << 8 ) +#define BGX_CMR_CONFIG_LANE_TO_SDS(ls) ( ( ( uint64_t ) (ls) ) << 0 ) + +/** CMR global configuration */ +#define BGX_CMR_GLOBAL_CONFIG 0x000008 +#define BGX_CMR_GLOBAL_CONFIG_FCS_STRIP ( 1ULL << 6 ) + +/** CMR receive statistics 0 */ +#define BGX_CMR_RX_STAT0 0x000070 + +/** CMR receive statistics 1 */ +#define BGX_CMR_RX_STAT1 0x000078 + +/** CMR receive statistics 2 */ +#define BGX_CMR_RX_STAT2 0x000080 + +/** CMR receive statistics 3 */ +#define BGX_CMR_RX_STAT3 0x000088 + +/** CMR receive statistics 4 */ +#define BGX_CMR_RX_STAT4 0x000090 + +/** CMR receive statistics 5 */ +#define BGX_CMR_RX_STAT5 0x000098 + +/** CMR receive statistics 6 */ +#define BGX_CMR_RX_STAT6 0x0000a0 + +/** CMR receive statistics 7 */ +#define BGX_CMR_RX_STAT7 0x0000a8 + +/** CMR receive statistics 8 */ +#define BGX_CMR_RX_STAT8 0x0000b0 + +/** CMR receive statistics 9 */ +#define BGX_CMR_RX_STAT9 0x0000b8 + +/** CMR receive statistics 10 */ +#define BGX_CMR_RX_STAT10 0x0000c0 + +/** CMR destination MAC control */ +#define BGX_CMR_RX_DMAC_CTL 0x0000e8 +#define BGX_CMR_RX_DMAC_CTL_MCST_MODE(md) ( ( ( uint64_t ) (md) ) << 1 ) +#define BGX_CMR_RX_DMAC_CTL_MCST_MODE_ACCEPT \ + BGX_CMR_RX_DMAC_CTL_MCST_MODE ( 1 ) +#define BGX_CMR_RX_DMAC_CTL_BCST_ACCEPT ( 1ULL << 0 ) + +/** CMR destination MAC CAM */ +#define BGX_CMR_RX_DMAC_CAM(i) ( ( (i) << 3 ) | 0x000200 ) + +/** CMR receive steering */ +#define BGX_CMR_RX_STEERING(i) ( ( (i) << 3 ) | 0x000300 ) + +/** CMR backpressure channel mask AND */ +#define BGX_CMR_CHAN_MSK_AND 0x000450 +#define BGX_CMR_CHAN_MSK_AND_ALL(count) \ + ( 0xffffffffffffffffULL >> ( 16 * ( 4 - (count) ) ) ) + +/** CMR transmit statistics 0 */ +#define BGX_CMR_TX_STAT0 0x000600 + +/** CMR transmit statistics 1 */ +#define BGX_CMR_TX_STAT1 0x000608 + +/** CMR transmit statistics 2 */ +#define BGX_CMR_TX_STAT2 0x000610 + +/** CMR transmit statistics 3 */ +#define BGX_CMR_TX_STAT3 0x000618 + +/** CMR transmit statistics 4 */ +#define BGX_CMR_TX_STAT4 0x000620 + +/** CMR transmit statistics 5 */ +#define BGX_CMR_TX_STAT5 0x000628 + +/** CMR transmit statistics 6 */ +#define BGX_CMR_TX_STAT6 0x000630 + +/** CMR transmit statistics 7 */ +#define BGX_CMR_TX_STAT7 0x000638 + +/** CMR transmit statistics 8 */ +#define BGX_CMR_TX_STAT8 0x000640 + +/** CMR transmit statistics 9 */ +#define BGX_CMR_TX_STAT9 0x000648 + +/** CMR transmit statistics 10 */ +#define BGX_CMR_TX_STAT10 0x000650 + +/** CMR transmit statistics 11 */ +#define BGX_CMR_TX_STAT11 0x000658 + +/** CMR transmit statistics 12 */ +#define BGX_CMR_TX_STAT12 0x000660 + +/** CMR transmit statistics 13 */ +#define BGX_CMR_TX_STAT13 0x000668 + +/** CMR transmit statistics 14 */ +#define BGX_CMR_TX_STAT14 0x000670 + +/** CMR transmit statistics 15 */ +#define BGX_CMR_TX_STAT15 0x000678 + +/** CMR transmit statistics 16 */ +#define BGX_CMR_TX_STAT16 0x000680 + +/** CMR transmit statistics 17 */ +#define BGX_CMR_TX_STAT17 0x000688 + +/** CMR receive logical MACs */ +#define BGX_CMR_RX_LMACS 0x000468 +#define BGX_CMR_RX_LMACS_LMACS_GET(lmacs) \ + ( ( (lmacs) >> 0 ) & 0x7 ) +#define BGX_CMR_RX_LMACS_LMACS_SET(ct) ( ( ( uint64_t ) (ct) ) << 0 ) + +/** CMR transmit logical MACs */ +#define BGX_CMR_TX_LMACS 0x001000 +#define BGX_CMR_TX_LMACS_LMACS_GET(lmacs) \ + ( ( (lmacs) >> 0 ) & 0x7 ) +#define BGX_CMR_TX_LMACS_LMACS_SET(ct) ( ( ( uint64_t ) (ct) ) << 0 ) + +/** SPU control 1 */ +#define BGX_SPU_CONTROL1 0x010000 +#define BGX_SPU_CONTROL1_RESET ( 1ULL << 15 ) +#define BGX_SPU_CONTROL1_LO_PWR ( 1ULL << 11 ) + +/** SPU reset delay */ +#define BGX_SPU_RESET_DELAY_MS 10 + +/** SPU status 1 */ +#define BGX_SPU_STATUS1 0x010008 +#define BGX_SPU_STATUS1_FLT ( 1ULL << 7 ) +#define BGX_SPU_STATUS1_RCV_LNK ( 1ULL << 2 ) + +/** SPU status 2 */ +#define BGX_SPU_STATUS2 0x010020 +#define BGX_SPU_STATUS2_RCVFLT ( 1ULL << 10 ) + +/** SPU BASE-R status 1 */ +#define BGX_SPU_BR_STATUS1 0x010030 +#define BGX_SPU_BR_STATUS1_RCV_LNK ( 1ULL << 12 ) +#define BGX_SPU_BR_STATUS1_HI_BER ( 1ULL << 1 ) +#define BGX_SPU_BR_STATUS1_BLK_LOCK ( 1ULL << 0 ) + +/** SPU BASE-R status 2 */ +#define BGX_SPU_BR_STATUS2 0x010038 +#define BGX_SPU_BR_STATUS2_LATCHED_LOCK ( 1ULL << 15 ) +#define BGX_SPU_BR_STATUS2_LATCHED_BER ( 1ULL << 14 ) + +/** SPU BASE-R alignment status */ +#define BGX_SPU_BR_ALGN_STATUS 0x010050 +#define BGX_SPU_BR_ALGN_STATUS_ALIGND ( 1ULL << 12 ) + +/** SPU BASE-R link training control */ +#define BGX_SPU_BR_PMD_CONTROL 0x010068 +#define BGX_SPU_BR_PMD_CONTROL_TRAIN_EN ( 1ULL << 1 ) + +/** SPU BASE-R link training status */ +#define BGX_SPU_BR_PMD_STATUS 0x010070 + +/** SPU link partner coefficient update */ +#define BGX_SPU_BR_PMD_LP_CUP 0x010078 + +/** SPU local device coefficient update */ +#define BGX_SPU_BR_PMD_LD_CUP 0x010088 + +/** SPU local device status report */ +#define BGX_SPU_BR_PMD_LD_REP 0x010090 + +/** SPU forward error correction control */ +#define BGX_SPU_FEC_CONTROL 0x0100a0 + +/** SPU autonegotation control */ +#define BGX_SPU_AN_CONTROL 0x0100c8 + +/** SPU autonegotiation status */ +#define BGX_SPU_AN_STATUS 0x0100d0 +#define BGX_SPU_AN_STATUS_XNP_STAT ( 1ULL << 7 ) +#define BGX_SPU_AN_STATUS_PAGE_RX ( 1ULL << 6 ) +#define BGX_SPU_AN_STATUS_AN_COMPLETE ( 1ULL << 5 ) +#define BGX_SPU_AN_STATUS_LINK_STATUS ( 1ULL << 2 ) +#define BGX_SPU_AN_STATUS_LP_AN_ABLE ( 1ULL << 0 ) + +/** SPU interrupt */ +#define BGX_SPU_INT 0x010220 +#define BGX_SPU_INT_TRAINING_FAIL ( 1ULL << 14 ) +#define BGX_SPU_INT_TRAINING_DONE ( 1ULL << 13 ) +#define BGX_SPU_INT_AN_COMPLETE ( 1ULL << 12 ) +#define BGX_SPU_INT_AN_LINK_GOOD ( 1ULL << 11 ) +#define BGX_SPU_INT_AN_PAGE_RX ( 1ULL << 10 ) +#define BGX_SPU_INT_FEC_UNCORR ( 1ULL << 9 ) +#define BGX_SPU_INT_FEC_CORR ( 1ULL << 8 ) +#define BGX_SPU_INT_BIP_ERR ( 1ULL << 7 ) +#define BGX_SPU_INT_DBG_SYNC ( 1ULL << 6 ) +#define BGX_SPU_INT_ALGNLOS ( 1ULL << 5 ) +#define BGX_SPU_INT_SYNLOS ( 1ULL << 4 ) +#define BGX_SPU_INT_BITLCKLS ( 1ULL << 3 ) +#define BGX_SPU_INT_ERR_BLK ( 1ULL << 2 ) +#define BGX_SPU_INT_RX_LINK_DOWN ( 1ULL << 1 ) +#define BGX_SPU_INT_RX_LINK_UP ( 1ULL << 0 ) + +/** LMAC types */ +enum txnic_lmac_types { + TXNIC_LMAC_SGMII = 0x0, /**< SGMII/1000BASE-X */ + TXNIC_LMAC_XAUI = 0x1, /**< 10GBASE-X/XAUI or DXAUI */ + TXNIC_LMAC_RXAUI = 0x2, /**< Reduced XAUI */ + TXNIC_LMAC_10G_R = 0x3, /**< 10GBASE-R */ + TXNIC_LMAC_40G_R = 0x4, /**< 40GBASE-R */ +}; + +/** An LMAC type */ +struct txnic_lmac_type { + /** Name */ + const char *name; + /** Number of LMACs */ + uint8_t count; + /** Lane-to-SDS mapping */ + uint32_t lane_to_sds; +}; + +/** An LMAC address */ +union txnic_lmac_address { + struct { + uint8_t pad[2]; + uint8_t raw[ETH_ALEN]; + } __attribute__ (( packed )); + uint64_t be64; +}; + +/** A Logical MAC (LMAC) */ +struct txnic_lmac { + /** Registers */ + void *regs; + /** Containing BGX Ethernet interface */ + struct txnic_bgx *bgx; + /** Virtual NIC index */ + unsigned int idx; + + /** MAC address */ + union txnic_lmac_address mac; + + /** Virtual NIC (if applicable) */ + struct txnic *vnic; +}; + +/** A BGX Ethernet interface */ +struct txnic_bgx { + /** Registers */ + void *regs; + /** PCI device */ + struct pci_device *pci; + /** Node ID */ + unsigned int node; + /** BGX index */ + unsigned int idx; + + /** LMAC type */ + struct txnic_lmac_type *type; + /** Number of LMACs */ + unsigned int count; + /** Link training is in use */ + int training; + + /** List of BGX Ethernet interfaces */ + struct list_head list; + /** Physical function (if known) */ + struct txnic_pf *pf; + + /** Logical MACs */ + struct txnic_lmac lmac[TXNIC_NUM_LMAC]; +}; + +#endif /* _THUNDERX_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index cbab452bb..f743dae6f 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -191,6 +191,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_virtio_pci ( ERRFILE_DRIVER | 0x007f0000 ) #define ERRFILE_pciea ( ERRFILE_DRIVER | 0x00c00000 ) #define ERRFILE_axge ( ERRFILE_DRIVER | 0x00c10000 ) +#define ERRFILE_thunderx ( ERRFILE_DRIVER | 0x00c20000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From cf52436c71a96af241ae329eea7e06a3d4b73fc2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 15 Jun 2016 20:29:38 +0100 Subject: [PATCH 244/591] [thunderx] Fix channel configuration for VNICs 1-7 Signed-off-by: Michael Brown --- src/drivers/net/thunderx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c index fce153e5f..63aa23ef0 100644 --- a/src/drivers/net/thunderx.c +++ b/src/drivers/net/thunderx.c @@ -939,7 +939,7 @@ static int txnic_lmac_open ( struct net_device *netdev ) { unsigned int tl3_idx = TXNIC_TL3_IDX ( vnic_idx ); unsigned int tl2_idx = TXNIC_TL2_IDX ( vnic_idx ); void *lmregs = ( pf->regs + TXNIC_PF_LMAC ( vnic_idx ) ); - void *chregs = ( pf->regs + TXNIC_PF_CHAN ( vnic_idx ) ); + void *chregs = ( pf->regs + TXNIC_PF_CHAN ( chan_idx ) ); void *qsregs = ( pf->regs + TXNIC_PF_QS ( vnic_idx ) ); size_t max_pkt_size; int rc; From ec992b97c27743e2fbfafc57db5403cf9541cb31 Mon Sep 17 00:00:00 2001 From: Christian Nilsson Date: Thu, 16 Jun 2016 11:41:40 +0100 Subject: [PATCH 245/591] [intel] Add PCI device ID for another I219-LM Tested-by: Kuniyasu Suzaki Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index a64dd1913..127f5c7e7 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1067,6 +1067,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", 0 ), PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", 0 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), From 8f0bec434695d2c07647adda5a5a04cd5fe79b25 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 18 Jun 2016 18:45:18 +0100 Subject: [PATCH 246/591] [efi] Include VLAN in SNP device path if applicable Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 4508967e6..a6e94c00b 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -1545,8 +1546,10 @@ static int efi_snp_probe ( struct net_device *netdev ) { struct efi_snp_device *snpdev; EFI_DEVICE_PATH_PROTOCOL *path_end; MAC_ADDR_DEVICE_PATH *macpath; + VLAN_DEVICE_PATH *vlanpath; size_t path_prefix_len = 0; unsigned int ifcnt; + unsigned int tag; void *interface; EFI_STATUS efirc; int rc; @@ -1634,7 +1637,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { /* Allocate the new device path */ path_prefix_len = efi_devpath_len ( efidev->path ); snpdev->path = zalloc ( path_prefix_len + sizeof ( *macpath ) + - sizeof ( *path_end ) ); + sizeof ( *vlanpath ) + sizeof ( *path_end ) ); if ( ! snpdev->path ) { rc = -ENOMEM; goto err_alloc_device_path; @@ -1643,7 +1646,6 @@ static int efi_snp_probe ( struct net_device *netdev ) { /* Populate the device path */ memcpy ( snpdev->path, efidev->path, path_prefix_len ); macpath = ( ( ( void * ) snpdev->path ) + path_prefix_len ); - path_end = ( ( void * ) ( macpath + 1 ) ); memset ( macpath, 0, sizeof ( *macpath ) ); macpath->Header.Type = MESSAGING_DEVICE_PATH; macpath->Header.SubType = MSG_MAC_ADDR_DP; @@ -1651,6 +1653,17 @@ static int efi_snp_probe ( struct net_device *netdev ) { memcpy ( &macpath->MacAddress, netdev->ll_addr, sizeof ( macpath->MacAddress ) ); macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); + if ( ( tag = vlan_tag ( netdev ) ) ) { + vlanpath = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); + memset ( vlanpath, 0, sizeof ( *vlanpath ) ); + vlanpath->Header.Type = MESSAGING_DEVICE_PATH; + vlanpath->Header.SubType = MSG_VLAN_DP; + vlanpath->Header.Length[0] = sizeof ( *vlanpath ); + vlanpath->VlanId = tag; + path_end = ( ( ( void * ) vlanpath ) + sizeof ( *vlanpath ) ); + } else { + path_end = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); + } memset ( path_end, 0, sizeof ( *path_end ) ); path_end->Type = END_DEVICE_PATH_TYPE; path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; From 25ae251dd918fd427e7e01d2befc0ac34aabcaa8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 13 Jun 2016 18:41:33 +0100 Subject: [PATCH 247/591] [thunderx] Retrieve base MAC address via EFI_THUNDER_CONFIG_PROTOCOL Signed-off-by: Michael Brown --- src/drivers/net/thunderx.c | 40 ++++++- src/drivers/net/thunderxcfg.h | 211 ++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 src/drivers/net/thunderxcfg.h diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c index 63aa23ef0..306adc459 100644 --- a/src/drivers/net/thunderx.c +++ b/src/drivers/net/thunderx.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include "thunderx.h" +#include "thunderxcfg.h" /** @file * @@ -56,6 +57,10 @@ static LIST_HEAD ( txnic_pfs ); /** Debug colour for physical function and BGX messages */ #define TXNICCOL(x) ( &txnic_pfs + (x)->node ) +/** Board configuration protocol */ +static EFI_THUNDER_CONFIG_PROTOCOL *txcfg; +EFI_REQUEST_PROTOCOL ( EFI_THUNDER_CONFIG_PROTOCOL, &txcfg ); + /****************************************************************************** * * Diagnostics @@ -1478,6 +1483,39 @@ static void txnic_bgx_init ( struct txnic_bgx *bgx, unsigned int type ) { writeq ( global_config, ( bgx->regs + BGX_CMR_GLOBAL_CONFIG ) ); } +/** + * Get MAC address + * + * @v lmac Logical MAC + */ +static void txnic_bgx_mac ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + BOARD_CFG *boardcfg; + NODE_CFG *nodecfg; + BGX_CFG *bgxcfg; + LMAC_CFG *lmaccfg; + + /* Extract MAC from Board Configuration protocol, if available */ + if ( txcfg ) { + boardcfg = txcfg->BoardConfig; + nodecfg = &boardcfg->Node[ bgx->node % MAX_NODES ]; + bgxcfg = &nodecfg->BgxCfg[ bgx->idx % BGX_PER_NODE_COUNT ]; + lmaccfg = &bgxcfg->Lmacs[ lmac->idx % LMAC_PER_BGX_COUNT ]; + lmac->mac.be64 = cpu_to_be64 ( lmaccfg->MacAddress ); + } else { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no board " + "configuration protocol\n", bgx->node, bgx->idx, + lmac->idx ); + } + + /* Use random MAC address if none available */ + if ( ! lmac->mac.be64 ) { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no MAC address\n", + bgx->node, bgx->idx, lmac->idx ); + eth_random_addr ( lmac->mac.raw ); + } +} + /** * Initialise Super PHY Unit (SPU) * @@ -1533,7 +1571,7 @@ static void txnic_bgx_lmac_init ( struct txnic_bgx *bgx, lmac->idx = TXNIC_VNIC_IDX ( bgx->idx, lmac_idx ); /* Set MAC address */ - eth_random_addr ( lmac->mac.raw ); + txnic_bgx_mac ( lmac ); /* Initialise PHY */ txnic_bgx_spu_init ( lmac ); diff --git a/src/drivers/net/thunderxcfg.h b/src/drivers/net/thunderxcfg.h new file mode 100644 index 000000000..661175862 --- /dev/null +++ b/src/drivers/net/thunderxcfg.h @@ -0,0 +1,211 @@ +#ifndef _THUNDERXCFG_H +#define _THUNDERXCFG_H + +/** @file + * + * Cavium ThunderX Board Configuration + * + * The definitions in this section are extracted from BSD-licensed + * (but non-public) portions of ThunderPkg. + * + */ + +FILE_LICENCE ( BSD2 ); + +#include + +/****************************************************************************** + * + * From ThunderxBoardConfig.h + * + ****************************************************************************** + * + * Header file for Cavium ThunderX Board Configurations + * Copyright (c) 2015, Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define MAX_NODES 2 +#define CLUSTER_COUNT 3 +#define CORE_PER_CLUSTER_COUNT 16 +#define CORE_COUNT (CLUSTER_COUNT*CORE_PER_CLUSTER_COUNT) +#define BGX_PER_NODE_COUNT 2 +#define LMAC_PER_BGX_COUNT 4 +#define PEM_PER_NODE_COUNT 6 +#define LMC_PER_NODE_COUNT 4 +#define DIMM_PER_LMC_COUNT 2 + +#define THUNDERX_CPU_ID(node, cluster, core) (((node) << 16) | ((cluster) << 8) | (core)) + +//TODO: Put common type definitions in separate common include file +typedef enum { + BGX_MODE_SGMII, /* 1 lane, 1.250 Gbaud */ + BGX_MODE_XAUI, /* 4 lanes, 3.125 Gbaud */ + BGX_MODE_DXAUI, /* 4 lanes, 6.250 Gbaud */ + BGX_MODE_RXAUI, /* 2 lanes, 6.250 Gbaud */ + BGX_MODE_XFI, /* 1 lane, 10.3125 Gbaud */ + BGX_MODE_XLAUI, /* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_10G_KR,/* 1 lane, 10.3125 Gbaud */ + BGX_MODE_40G_KR,/* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_UNKNOWN +} BGX_MODE_T; + +typedef enum { + EBB8800, + EBB8804, + CRB_1S, + CRB_2S, + ASIANCAT, + GBT_MT60, + INVENTEC_P3E003, + BOARD_MAX +} BOARD_TYPE; + +typedef struct { + BOOLEAN Enabled; + UINT64 LaneToSds; + UINT64 MacAddress; +} LMAC_CFG; + +typedef struct { + BOOLEAN BgxEnabled; + BGX_MODE_T BgxMode; + UINTN LmacCount; //Maximum number of LMAcs + UINT64 BaseAddress; + UINT64 LmacType; + /* Bit mask of QLMs connected to this BGX */ + UINT64 QlmMask; + UINT64 QlmFreq; + BOOLEAN UseTraining; + LMAC_CFG Lmacs[LMAC_PER_BGX_COUNT]; +} BGX_CFG; + +typedef struct { + BOOLEAN PemUsable; +} PEM_CFG; + +typedef struct { + enum { NotPresent, Empty, Available } Status; + UINT8 Type; + UINT8 SubType; + UINT8 Rank; + UINT16 Mfg; + UINTN SizeMb; + UINTN Speed; + CHAR8 Serial[16]; + CHAR8 PartNo[24]; +} DIMM_CFG; + +typedef struct { + DIMM_CFG DimmCfg[DIMM_PER_LMC_COUNT]; +} LMC_CFG; + +typedef struct { + BOOLEAN Core[CORE_COUNT]; + BGX_CFG BgxCfg[BGX_PER_NODE_COUNT]; + PEM_CFG PemCfg[PEM_PER_NODE_COUNT]; + LMC_CFG LmcCfg[LMC_PER_NODE_COUNT]; + UINT64 RamStart; + UINT64 RamReserve; + UINT64 RamSize; + UINTN CPUSpeed; + UINTN CPUVersion; +} NODE_CFG; + +#define MAX_SERIAL 32 +#define MAX_REVISION 32 +typedef struct { + BOARD_TYPE BoardType; + CHAR8 Serial[MAX_SERIAL]; + CHAR8 Revision[MAX_REVISION]; + UINTN NumNodes; + UINTN BmcBootTwsiBus; + UINTN BmcBootTwsiAddr; + UINTN RtcTwsiBus; + UINTN RtcTwsiAddr; + /* IPMI support*/ + UINTN BmcIpmiTwsiBus; + UINTN BmcIpmiTwsiAddr; + NODE_CFG Node[MAX_NODES]; + UINT16 CpuClusterCount; + UINT16 CpuPerClusterCount; + UINT16 PcieSegmentCount; + UINT64 MacAddrRangeStart; + UINTN DdrSpeed; + UINT64 AcpiOemTableId; +} BOARD_CFG; + +/****************************************************************************** + * + * From ThunderConfigProtocol.h + * + ****************************************************************************** + * + * Thunder board Configuration Protocol + * + * Copyright (c) 2015, Cavium Inc. All rights reserved.
+ * + * This program and the accompanying materials are licensed and made + * available under the terms and conditions of the BSD License which + * accompanies this distribution. The full text of the license may + * be found at http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" + * BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER + * EXPRESS OR IMPLIED. + * + */ + +#define EFI_THUNDER_CONFIG_PROTOCOL_GUID \ + {0xb75a0608, 0x99ff, 0x11e5, {0x9b, 0xeb, 0x00, 0x14, 0xd1, 0xfa, 0x23, 0x5c}} + +/// +/// Forward declaration +/// +typedef struct _EFI_THUNDER_CONFIG_PROTOCOL EFI_THUNDER_CONFIG_PROTOCOL; + +/// +/// Function prototypes +/// +typedef +EFI_STATUS +(EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG)( + IN EFI_THUNDER_CONFIG_PROTOCOL *This, + OUT BOARD_CFG** cfg + ); + +/// +/// Protocol structure +/// +typedef struct _EFI_THUNDER_CONFIG_PROTOCOL { + EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG GetConfig; + BOARD_CFG* BoardConfig; +} EFI_THUNDER_CONFIG_PROTOCOL; + +#endif /* _THUNDERXCFG_H */ From fbbc895442847b4d3baa05fe1be66b510c949381 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 2 May 2016 13:46:39 +0200 Subject: [PATCH 248/591] [virtio] Renumber virtio_pci_region flags Some of the regions may end up being unmapped, either because they are optional or because the attempt to map them has failed. Region types starting at 0 didn't make it easy to test for this condition. This commit bumps all valid region types up by 1 with 0 having the implicit 'unmapped' meaning. Signed-off-by: Ladi Prosek Reviewed-by: Marcel Apfelbaum Signed-off-by: Michael Brown --- src/include/ipxe/virtio-pci.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index c7452c82f..f3c9b17ca 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -107,11 +107,11 @@ struct virtio_pci_region { /* How to interpret the base field */ #define VIRTIO_PCI_REGION_TYPE_MASK 0x00000003 /* The base field is a memory address */ -#define VIRTIO_PCI_REGION_MEMORY 0x00000000 +#define VIRTIO_PCI_REGION_MEMORY 0x00000001 /* The base field is a port address */ -#define VIRTIO_PCI_REGION_PORT 0x00000001 +#define VIRTIO_PCI_REGION_PORT 0x00000002 /* The base field is an offset within the PCI bar */ -#define VIRTIO_PCI_REGION_PCI_CONFIG 0x00000002 +#define VIRTIO_PCI_REGION_PCI_CONFIG 0x00000003 unsigned flags; }; From 040aa980d630e45cb6d9e5fbaf5719b814accd5f Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Tue, 31 May 2016 10:12:12 +0200 Subject: [PATCH 249/591] [virtio] Fix virtio-pci logging iPXE debug logging doesn't support %u. This commit replaces it with %d in virtio-pci debug format strings. Signed-off-by: Ladi Prosek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael Brown --- src/drivers/bus/virtio-pci.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index a1c805a87..3311595fb 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -253,21 +253,21 @@ int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, &length); if (length <= start) { - DBG("VIRTIO-PCI bad capability len %u (>%u expected)\n", length, start); + DBG("VIRTIO-PCI bad capability len %d (>%d expected)\n", length, start); return -EINVAL; } if (length - start < minlen) { - DBG("VIRTIO-PCI bad capability len %u (>=%zu expected)\n", length, minlen); + DBG("VIRTIO-PCI bad capability len %d (>=%zd expected)\n", length, minlen); return -EINVAL; } length -= start; if (start + offset < offset) { - DBG("VIRTIO-PCI map wrap-around %u+%u\n", start, offset); + DBG("VIRTIO-PCI map wrap-around %d+%d\n", start, offset); return -EINVAL; } offset += start; if (offset & (align - 1)) { - DBG("VIRTIO-PCI offset %u not aligned to %u\n", offset, align); + DBG("VIRTIO-PCI offset %d not aligned to %d\n", offset, align); return -EINVAL; } if (length > size) { @@ -276,9 +276,9 @@ int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, if (minlen + offset < minlen || minlen + offset > pci_bar_size(pci, PCI_BASE_ADDRESS(bar))) { - DBG("VIRTIO-PCI map virtio %zu@%u out of range on bar %i length %lu\n", + DBG("VIRTIO-PCI map virtio %zd@%d out of range on bar %i length %ld\n", minlen, offset, - bar, (unsigned long)pci_bar_size(pci, PCI_BASE_ADDRESS(bar))); + bar, pci_bar_size(pci, PCI_BASE_ADDRESS(bar))); return -EINVAL; } @@ -354,7 +354,7 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, return -ENOENT; if (size & (size - 1)) { - DBG("VIRTIO-PCI %p: bad queue size %u", vdev, size); + DBG("VIRTIO-PCI %p: bad queue size %d", vdev, size); return -EINVAL; } From c9176878ef9edee1cf5a8a1ca1d9b9afdf2edfcd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 20 Jun 2016 14:07:41 +0100 Subject: [PATCH 250/591] [smsc75xx] Allow up to 100ms for reset to complete Signed-off-by: Michael Brown --- src/drivers/net/smsc75xx.c | 23 ++++++++++++++--------- src/drivers/net/smsc75xx.h | 4 ++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/drivers/net/smsc75xx.c b/src/drivers/net/smsc75xx.c index 9a9634600..4ce98ac80 100644 --- a/src/drivers/net/smsc75xx.c +++ b/src/drivers/net/smsc75xx.c @@ -511,6 +511,7 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { */ static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { uint32_t hw_cfg; + unsigned int i; int rc; /* Reset device */ @@ -519,18 +520,22 @@ static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { return rc; /* Wait for reset to complete */ - udelay ( SMSC75XX_RESET_DELAY_US ); + for ( i = 0 ; i < SMSC75XX_RESET_MAX_WAIT_MS ; i++ ) { - /* Check that reset has completed */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG, - &hw_cfg ) ) != 0 ) - return rc; - if ( hw_cfg & SMSC75XX_HW_CFG_LRST ) { - DBGC ( smsc75xx, "SMSC75XX %p failed to reset\n", smsc75xx ); - return -ETIMEDOUT; + /* Check if reset has completed */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( ! ( hw_cfg & SMSC75XX_HW_CFG_LRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); } - return 0; + DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for reset\n", + smsc75xx ); + return -ETIMEDOUT; } /****************************************************************************** diff --git a/src/drivers/net/smsc75xx.h b/src/drivers/net/smsc75xx.h index 2463b72a1..ae81fc168 100644 --- a/src/drivers/net/smsc75xx.h +++ b/src/drivers/net/smsc75xx.h @@ -280,8 +280,8 @@ struct smsc75xx_device { uint32_t int_sts; }; -/** Reset delay (in microseconds) */ -#define SMSC75XX_RESET_DELAY_US 2 +/** Maximum time to wait for reset (in milliseconds) */ +#define SMSC75XX_RESET_MAX_WAIT_MS 100 /** Maximum time to wait for EEPROM (in milliseconds) */ #define SMSC75XX_EEPROM_MAX_WAIT_MS 100 From 694c18addc0dfdf51369f6d598dd0c8ca4bf2861 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 20 Jun 2016 16:08:17 +0100 Subject: [PATCH 251/591] [efi] Report failures to stop the EFI timer tick event Signed-off-by: Michael Brown --- src/interface/efi/efi_timer.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index a574e2043..da064120a 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -126,13 +126,27 @@ static void efi_tick_startup ( void ) { */ static void efi_tick_shutdown ( int booting __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; /* Stop timer tick */ - bs->SetTimer ( efi_tick_event, TimerCancel, 0 ); + if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerCancel, 0 ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not stop timer tick: %s\n", + strerror ( rc ) ); + /* Self-destruct initiated */ + return; + } DBGC ( colour, "EFI timer stopped\n" ); /* Destroy timer tick event */ - bs->CloseEvent ( efi_tick_event ); + if ( ( efirc = bs->CloseEvent ( efi_tick_event ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not destroy timer tick: %s\n", + strerror ( rc ) ); + /* Probably non-fatal */ + return; + } } /** Timer tick startup function */ From 632e57f0f36d9b48f574db273a19e26bf592fc99 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jun 2016 09:07:20 +0100 Subject: [PATCH 252/591] [efi] Do not copy garbage bytes into SNP device path MAC address The SNP device path includes the network device's MAC address within the MAC_ADDR_DEVICE_PATH.MacAddress field. We check that the link-layer address will fit within this field, and then perform the copy using the length of the destination buffer. At 32 bytes, the MacAddress field is actually larger than the current maximum iPXE link-layer address. The copy therefore overflows the source buffer, resulting in trailing garbage bytes being appended to the device path's MacAddress. This is invisible in debug messages, since the DevicePathToText protocol will render only the length implied by the interface type. Fix by copying only the actual length of the link-layer address (which we have already verified will not overflow the destination buffer). Debugged-by: Laszlo Ersek Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index a6e94c00b..e6388bf67 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -1651,7 +1651,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { macpath->Header.SubType = MSG_MAC_ADDR_DP; macpath->Header.Length[0] = sizeof ( *macpath ); memcpy ( &macpath->MacAddress, netdev->ll_addr, - sizeof ( macpath->MacAddress ) ); + netdev->ll_protocol->ll_addr_len ); macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); if ( ( tag = vlan_tag ( netdev ) ) ) { vlanpath = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); From 04186319181298083ef28695a8309028b26fe83c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jun 2016 12:04:50 +0100 Subject: [PATCH 253/591] [thunderx] Fix compilation with older versions of gcc Remove redundant duplicate typedef which causes a build failure on older gcc versions. Signed-off-by: Michael Brown --- src/drivers/net/thunderxcfg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/thunderxcfg.h b/src/drivers/net/thunderxcfg.h index 661175862..235c54319 100644 --- a/src/drivers/net/thunderxcfg.h +++ b/src/drivers/net/thunderxcfg.h @@ -203,9 +203,9 @@ EFI_STATUS /// /// Protocol structure /// -typedef struct _EFI_THUNDER_CONFIG_PROTOCOL { +struct _EFI_THUNDER_CONFIG_PROTOCOL { EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG GetConfig; BOARD_CFG* BoardConfig; -} EFI_THUNDER_CONFIG_PROTOCOL; +}; #endif /* _THUNDERXCFG_H */ From c9f6a8605955926017cdbe2fa99a4b72fd0985a2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Jun 2016 15:13:35 +0100 Subject: [PATCH 254/591] [efi] Fix uninitialised data in HII IFR structures The HII IFR structures are allocated via realloc() rather than zalloc(), and so are not automatically zeroed. This results in the presence of uninitialised and invalid data, causing crashes elsewhere in the UEFI firmware. Fix by explicitly zeroing the newly allocated portion of any IFR structure in efi_ifr_op(). Debugged-by: Laszlo Ersek Debugged-by: Gary Lin Signed-off-by: Michael Brown --- src/interface/efi/efi_hii.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interface/efi/efi_hii.c b/src/interface/efi/efi_hii.c index 0ea970e67..506fc8869 100644 --- a/src/interface/efi/efi_hii.c +++ b/src/interface/efi/efi_hii.c @@ -117,6 +117,7 @@ static void * efi_ifr_op ( struct efi_ifr_builder *ifr, unsigned int opcode, ifr->ops_len = new_ops_len; /* Fill in opcode header */ + memset ( op, 0, len ); op->OpCode = opcode; op->Length = len; From c22da4b8ba09b60f8cfce9ac7136351c25588c65 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jul 2016 12:50:19 +0100 Subject: [PATCH 255/591] [bios] Do not enable interrupts when printing to the console There seems to be no reason for the sti/cli pair used around each call to INT 10. Remove these instructions, so that printing debug messages from within an ISR does not temporarily reenable interrupts. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/bios_console.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index c081a41e6..81e3a7d7e 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -98,9 +98,7 @@ static void bios_handle_cup ( struct ansiesc_context *ctx __unused, if ( cy < 0 ) cy = 0; - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0200 ), "b" ( 1 ), "d" ( ( cy << 8 ) | cx ) ); } @@ -118,9 +116,7 @@ static void bios_handle_ed ( struct ansiesc_context *ctx __unused, /* We assume that we always clear the whole screen */ assert ( params[0] == ANSIESC_ED_ALL ); - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ), "c" ( 0 ), "d" ( ( ( console_height - 1 ) << 8 ) | @@ -188,9 +184,7 @@ static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused, /* Get character height */ get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT ); - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0100 ), "c" ( ( ( height - 2 ) << 8 ) | ( height - 1 ) ) ); @@ -207,9 +201,7 @@ static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused, unsigned int count __unused, int params[] __unused ) { - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0100 ), "c" ( 0x2000 ) ); } @@ -243,7 +235,6 @@ static void bios_putchar ( int character ) { /* Print character with attribute */ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ - "sti\n\t" /* Skip non-printable characters */ "cmpb $0x20, %%al\n\t" "jb 1f\n\t" @@ -264,7 +255,6 @@ static void bios_putchar ( int character ) { "xorw %%bx, %%bx\n\t" "movb $0x0e, %%ah\n\t" "int $0x10\n\t" - "cli\n\t" "popl %%ebp\n\t" /* gcc bug */ ) : "=a" ( discard_a ), "=b" ( discard_b ), "=c" ( discard_c ) From 3bb61c33c2d77ac9a1a512d809576f3444b6b1ed Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jul 2016 12:52:20 +0100 Subject: [PATCH 256/591] [pxe] Disable interrupts on the PIC before starting NBP Some BIOSes (observed with an HP Gen9) seem to spuriously enable interrupts at the PIC. This causes problems with NBPs such as GRUB which use the UNDI API (thereby enabling interrupts on the NIC) without first hooking an interrupt service routine. In this situation, the interrupt will end up being handled by the default BIOS ISR, which will typically just send an EOI and return. Since nothing in this handler causes the NIC to deassert the interrupt, this will result in an interrupt storm. Entertainingly, some BIOSes are immune to this problem because the default ISR sends the EOI only to the slave PIC; this effectively disables the interrupt. Work around this problem by disabling the interrupt on the PIC before invoking the PXE NBP. An NBP that expects to make use of interrupts will need to be configuring the PIC anyway, so it is probably safe to assume that it will explicitly reenable the interrupt. Signed-off-by: Michael Brown --- src/arch/x86/image/pxe_image.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/arch/x86/image/pxe_image.c b/src/arch/x86/image/pxe_image.c index 297a618b8..b6bcb18b4 100644 --- a/src/arch/x86/image/pxe_image.c +++ b/src/arch/x86/image/pxe_image.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -87,6 +88,10 @@ static int pxe_exec ( struct image *image ) { /* Reset console since PXE NBP will probably use it */ console_reset(); + /* Disable IRQ, if applicable */ + if ( netdev_irq_supported ( netdev ) && netdev->dev->desc.irq ) + disable_irq ( netdev->dev->desc.irq ); + /* Start PXE NBP */ rc = pxe_start_nbp(); From 3d9f094022854ca06d02f34c688896abde9e1b20 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 13:15:05 +0100 Subject: [PATCH 257/591] [dhcp] Allow for variable encapsulation of architecture-specific options DHCPv4 and DHCPv6 share some values in common for the architecture- specific options (such as the client system architecture type), but use different encapsulations: DHCPv4 has a single byte for the option length while DHCPv6 has a 16-bit field for the option length. Move the containing DHCP_OPTION() and related wrappers from the individual dhcp_arch.h files to dhcp.c, thus allowing for the architecture-specific values to be reused in dhcpv6.c. Signed-off-by: Michael Brown --- src/arch/arm32/include/efi/ipxe/dhcp_arch.h | 13 ++++++------- src/arch/arm64/include/efi/ipxe/dhcp_arch.h | 13 ++++++------- src/arch/i386/include/efi/ipxe/dhcp_arch.h | 13 ++++++------- src/arch/i386/include/pcbios/ipxe/dhcp_arch.h | 13 ++++++------- src/arch/x86_64/include/efi/ipxe/dhcp_arch.h | 13 ++++++------- src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h | 13 ++++++------- src/net/udp/dhcp.c | 6 +++--- 7 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h index f9baab4fa..e971955ba 100644 --- a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h @@ -33,14 +33,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) +#define DHCP_ARCH_VENDOR_CLASS_ID \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_ARM32 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM32 -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ #endif diff --git a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h index 48a36d052..4ffea7f44 100644 --- a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h @@ -33,14 +33,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) +#define DHCP_ARCH_VENDOR_CLASS_ID \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_ARM64 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM64 -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ #endif diff --git a/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/src/arch/i386/include/efi/ipxe/dhcp_arch.h index c17c1ea5e..74027928d 100644 --- a/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -33,14 +33,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '6', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) +#define DHCP_ARCH_VENDOR_CLASS_ID \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '6', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_IA32 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_IA32 -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ #endif diff --git a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h index e07e4c192..0a7a2f7cd 100644 --- a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -33,14 +33,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ) +#define DHCP_ARCH_VENDOR_CLASS_ID \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ #endif diff --git a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h index 6511c1ad3..b35818efd 100644 --- a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h @@ -33,14 +33,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '9', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) +#define DHCP_ARCH_VENDOR_CLASS_ID \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '9', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86_64 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86_64 -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ #endif diff --git a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h index e07e4c192..0a7a2f7cd 100644 --- a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h +++ b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h @@ -33,14 +33,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ) +#define DHCP_ARCH_VENDOR_CLASS_ID \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ #endif diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 9342ad21e..0c6cea0f1 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -82,9 +82,9 @@ static uint8_t dhcp_request_options_data[] = { DHCP_MESSAGE_TYPE, DHCP_BYTE ( 0 ), DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ), - DHCP_CLIENT_ARCHITECTURE, DHCP_ARCH_CLIENT_ARCHITECTURE, - DHCP_CLIENT_NDI, DHCP_ARCH_CLIENT_NDI, - DHCP_VENDOR_CLASS_ID, DHCP_ARCH_VENDOR_CLASS_ID, + DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), + DHCP_CLIENT_NDI, DHCP_OPTION ( DHCP_ARCH_CLIENT_NDI ), + DHCP_VENDOR_CLASS_ID, DHCP_STRING ( DHCP_ARCH_VENDOR_CLASS_ID ), DHCP_USER_CLASS_ID, DHCP_STRING ( 'i', 'P', 'X', 'E' ), DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, From fda8916c83f320de3f12c9880d177d0f9fa40904 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 13:18:49 +0100 Subject: [PATCH 258/591] [dhcpv6] Include RFC5970 client architecture options in DHCPv6 requests RFC5970 defines DHCPv6 options 61 (client system architecture type) and 62 (client network interface identifier), with contents equivalent to DHCPv4 options 93 and 94 respectively. Signed-off-by: Michael Brown --- src/include/ipxe/dhcpv6.h | 33 +++++++++++++++++++++++++++++++++ src/net/udp/dhcpv6.c | 36 +++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/include/ipxe/dhcpv6.h b/src/include/ipxe/dhcpv6.h index 9307b6cae..48cb76337 100644 --- a/src/include/ipxe/dhcpv6.h +++ b/src/include/ipxe/dhcpv6.h @@ -157,6 +157,12 @@ struct dhcpv6_user_class_option { /** DHCPv6 bootfile parameters option */ #define DHCPV6_BOOTFILE_PARAM 60 +/** DHCPv6 client system architecture option */ +#define DHCPV6_CLIENT_ARCHITECTURE 61 + +/** DHCPv6 client network interface identifier option */ +#define DHCPV6_CLIENT_NDI 62 + /** DHCPv6 syslog server option * * This option code has not yet been assigned by IANA. Please update @@ -164,6 +170,33 @@ struct dhcpv6_user_class_option { */ #define DHCPV6_LOG_SERVERS 0xffffffffUL +/** Construct a DHCPv6 option code */ +#define DHCPV6_CODE( code ) \ + ( ( (code) >> 8 ) & 0xff ), ( ( (code) >> 0 ) & 0xff ) + +/** Construct a DHCPv6 option length */ +#define DHCPV6_LEN( len ) \ + ( ( (len) >> 8 ) & 0xff ), ( ( (len) >> 0 ) & 0xff ) + +/** Construct a DHCPv6 option from a list of bytes */ +#define DHCPV6_OPTION( ... ) \ + DHCPV6_LEN ( VA_ARG_COUNT ( __VA_ARGS__ ) ), __VA_ARGS__ + +/** Construct a DHCPv6 option from a list of characters */ +#define DHCPV6_STRING( ... ) DHCPV6_OPTION ( __VA_ARGS__ ) + +/** Construct a byte-valued DHCPv6 option */ +#define DHCPV6_BYTE( value ) DHCPV6_OPTION ( value ) + +/** Construct a word-valued DHCPv6 option */ +#define DHCPV6_WORD( value ) DHCPV6_OPTION ( ( ( (value) >> 8 ) & 0xff ), \ + ( ( (value) >> 0 ) & 0xff ) ) +/** Construct a dword-valued DHCPv6 option */ +#define DHCPV6_DWORD( value ) DHCPV6_OPTION ( ( ( (value) >> 24 ) & 0xff ), \ + ( ( (value) >> 16 ) & 0xff ), \ + ( ( (value) >> 8 ) & 0xff ), \ + ( ( (value) >> 0 ) & 0xff ) ) + /** * Any DHCPv6 option * diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index a63543775..8b216e9a2 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -364,10 +365,17 @@ static int dhcpv6_register ( struct dhcpv6_option_list *options, * */ -/** Options to be requested */ -static uint16_t dhcpv6_requested_options[] = { - htons ( DHCPV6_DNS_SERVERS ), htons ( DHCPV6_DOMAIN_LIST ), - htons ( DHCPV6_BOOTFILE_URL ), htons ( DHCPV6_BOOTFILE_PARAM ), +/** Raw option data for options common to all DHCPv6 requests */ +static uint8_t dhcpv6_request_options_data[] = { + DHCPV6_CODE ( DHCPV6_OPTION_REQUEST ), + DHCPV6_OPTION ( DHCPV6_CODE ( DHCPV6_DNS_SERVERS ), + DHCPV6_CODE ( DHCPV6_DOMAIN_LIST ), + DHCPV6_CODE ( DHCPV6_BOOTFILE_URL ), + DHCPV6_CODE ( DHCPV6_BOOTFILE_PARAM ) ), + DHCPV6_CODE ( DHCPV6_CLIENT_ARCHITECTURE ), + DHCPV6_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), + DHCPV6_CODE ( DHCPV6_CLIENT_NDI ), + DHCPV6_OPTION ( DHCP_ARCH_CLIENT_NDI ) }; /** @@ -565,15 +573,14 @@ static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { struct dhcpv6_duid_option *server_id; struct dhcpv6_ia_na_option *ia_na; struct dhcpv6_iaaddr_option *iaaddr; - struct dhcpv6_option_request_option *option_request; struct dhcpv6_user_class_option *user_class; struct dhcpv6_elapsed_time_option *elapsed; struct dhcpv6_header *dhcphdr; struct io_buffer *iobuf; + void *options; size_t client_id_len; size_t server_id_len; size_t ia_na_len; - size_t option_request_len; size_t user_class_string_len; size_t user_class_len; size_t elapsed_len; @@ -592,16 +599,14 @@ static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { } else { ia_na_len = 0; } - option_request_len = ( sizeof ( *option_request ) + - sizeof ( dhcpv6_requested_options ) ); user_class_string_len = dhcpv6_user_class ( NULL, 0 ); user_class_len = ( sizeof ( *user_class ) + sizeof ( user_class->user_class[0] ) + user_class_string_len ); elapsed_len = sizeof ( *elapsed ); total_len = ( sizeof ( *dhcphdr ) + client_id_len + server_id_len + - ia_na_len + option_request_len + user_class_len + - elapsed_len ); + ia_na_len + sizeof ( dhcpv6_request_options_data ) + + user_class_len + elapsed_len ); /* Allocate packet */ iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len ); @@ -652,13 +657,10 @@ static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { } } - /* Construct option request */ - option_request = iob_put ( iobuf, option_request_len ); - option_request->header.code = htons ( DHCPV6_OPTION_REQUEST ); - option_request->header.len = htons ( option_request_len - - sizeof ( option_request->header )); - memcpy ( option_request->requested, dhcpv6_requested_options, - sizeof ( dhcpv6_requested_options ) ); + /* Construct fixed request options */ + options = iob_put ( iobuf, sizeof ( dhcpv6_request_options_data ) ); + memcpy ( options, dhcpv6_request_options_data, + sizeof ( dhcpv6_request_options_data ) ); /* Construct user class */ user_class = iob_put ( iobuf, user_class_len ); From d7f1834b5e57649d003d89bd4c4bd7d7061b62d0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 14:08:26 +0100 Subject: [PATCH 259/591] [dhcpv6] Include vendor class identifier option in DHCPv6 requests RFC3315 defines DHCPv6 option 16 (vendor class identifier) but does not define any direct relationship with the roughly equivalent DHCPv4 option 60. The PXE specification predates IPv6, and the UEFI specification is expectedly vague on the subject. Examination of the reference EDK2 codebase suggests that the DHCPv6 vendor class identifier will be formatted in accordance with RFC3315, using a single vendor-class-data item in which the opaque-data field is the string as would appear in DHCPv4 option 60. RFC3315 requires the vendor class identifier to specify an IANA enterprise number, as a way of disambiguating the vendor-class-data namespace. The EDK2 code uses the value 343, described as: // TODO: IANA TBD: temporarily using Intel's Since this "TODO" has been present since at least 2010, it is probably safe to assume that it has now become a de facto standard. Signed-off-by: Michael Brown --- src/include/ipxe/dhcpv6.h | 43 +++++++++++++++++++++++++++++---------- src/net/udp/dhcpv6.c | 3 +++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/include/ipxe/dhcpv6.h b/src/include/ipxe/dhcpv6.h index 48cb76337..6e70f7e63 100644 --- a/src/include/ipxe/dhcpv6.h +++ b/src/include/ipxe/dhcpv6.h @@ -145,6 +145,21 @@ struct dhcpv6_user_class_option { /** DHCPv6 user class option */ #define DHCPV6_USER_CLASS 15 +/** DHCPv6 vendor class option */ +#define DHCPV6_VENDOR_CLASS 16 + +/** DHCPv6 PXE vendor class + * + * The DHCPv6 vendor class includes a field for an IANA enterprise + * number. The EDK2 codebase uses the value 343, with the comment: + * + * TODO: IANA TBD: temporarily using Intel's + * + * Since this "temporarily" has applied since at least 2010, we assume + * that it has become a de facto standard. + */ +#define DHCPV6_VENDOR_CLASS_PXE 343 + /** DHCPv6 DNS recursive name server option */ #define DHCPV6_DNS_SERVERS 23 @@ -170,13 +185,22 @@ struct dhcpv6_user_class_option { */ #define DHCPV6_LOG_SERVERS 0xffffffffUL +/** Construct a DHCPv6 byte value */ +#define DHCPV6_BYTE_VALUE( value ) ( (value) & 0xff ) + +/** Construct a DHCPv6 word value */ +#define DHCPV6_WORD_VALUE( value ) \ + DHCPV6_BYTE_VALUE ( (value) >> 8 ), DHCPV6_BYTE_VALUE ( (value) >> 0 ) + +/** Construct a DHCPv6 dword value */ +#define DHCPV6_DWORD_VALUE( value ) \ + DHCPV6_WORD_VALUE ( (value) >> 16 ), DHCPV6_WORD_VALUE ( (value) >> 0 ) + /** Construct a DHCPv6 option code */ -#define DHCPV6_CODE( code ) \ - ( ( (code) >> 8 ) & 0xff ), ( ( (code) >> 0 ) & 0xff ) +#define DHCPV6_CODE( code ) DHCPV6_WORD_VALUE ( code ) /** Construct a DHCPv6 option length */ -#define DHCPV6_LEN( len ) \ - ( ( (len) >> 8 ) & 0xff ), ( ( (len) >> 0 ) & 0xff ) +#define DHCPV6_LEN( len ) DHCPV6_WORD_VALUE ( len ) /** Construct a DHCPv6 option from a list of bytes */ #define DHCPV6_OPTION( ... ) \ @@ -186,16 +210,13 @@ struct dhcpv6_user_class_option { #define DHCPV6_STRING( ... ) DHCPV6_OPTION ( __VA_ARGS__ ) /** Construct a byte-valued DHCPv6 option */ -#define DHCPV6_BYTE( value ) DHCPV6_OPTION ( value ) +#define DHCPV6_BYTE( value ) DHCPV6_OPTION ( DHCPV6_BYTE_VALUE ( value ) ) /** Construct a word-valued DHCPv6 option */ -#define DHCPV6_WORD( value ) DHCPV6_OPTION ( ( ( (value) >> 8 ) & 0xff ), \ - ( ( (value) >> 0 ) & 0xff ) ) +#define DHCPV6_WORD( value ) DHCPV6_OPTION ( DHCPV6_WORD_VALUE ( value ) ) + /** Construct a dword-valued DHCPv6 option */ -#define DHCPV6_DWORD( value ) DHCPV6_OPTION ( ( ( (value) >> 24 ) & 0xff ), \ - ( ( (value) >> 16 ) & 0xff ), \ - ( ( (value) >> 8 ) & 0xff ), \ - ( ( (value) >> 0 ) & 0xff ) ) +#define DHCPV6_DWORD( value ) DHCPV6_OPTION ( DHCPV6_DWORD_VALUE ( value ) ) /** * Any DHCPv6 option diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index 8b216e9a2..ef07f54e3 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -372,6 +372,9 @@ static uint8_t dhcpv6_request_options_data[] = { DHCPV6_CODE ( DHCPV6_DOMAIN_LIST ), DHCPV6_CODE ( DHCPV6_BOOTFILE_URL ), DHCPV6_CODE ( DHCPV6_BOOTFILE_PARAM ) ), + DHCPV6_CODE ( DHCPV6_VENDOR_CLASS ), + DHCPV6_OPTION ( DHCPV6_DWORD_VALUE ( DHCPV6_VENDOR_CLASS_PXE ), + DHCPV6_STRING ( DHCP_ARCH_VENDOR_CLASS_ID ) ), DHCPV6_CODE ( DHCPV6_CLIENT_ARCHITECTURE ), DHCPV6_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), DHCPV6_CODE ( DHCPV6_CLIENT_NDI ), From aeb62038119f1988b06fa6a55a74d3db65c64d79 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 15:07:05 +0100 Subject: [PATCH 260/591] [dhcp] Automatically generate vendor class identifier string The vendor class identifier strings in DHCP_ARCH_VENDOR_CLASS_ID are out of sync with the (correct) client architecture values in DHCP_ARCH_CLIENT_ARCHITECTURE. Fix by removing all definitions of DHCP_ARCH_VENDOR_CLASS_ID, and instead generating the vendor class identifier string automatically based on DHCP_ARCH_CLIENT_ARCHITECTURE and DHCP_ARCH_CLIENT_NDI. Signed-off-by: Michael Brown --- src/arch/arm32/include/efi/ipxe/dhcp_arch.h | 5 ---- src/arch/arm64/include/efi/ipxe/dhcp_arch.h | 5 ---- src/arch/i386/include/efi/ipxe/dhcp_arch.h | 5 ---- src/arch/i386/include/pcbios/ipxe/dhcp_arch.h | 5 ---- src/arch/x86_64/include/efi/ipxe/dhcp_arch.h | 5 ---- .../x86_64/include/pcbios/ipxe/dhcp_arch.h | 5 ---- src/include/ipxe/dhcp.h | 23 +++++++++++++++++++ src/net/udp/dhcp.c | 4 +++- src/net/udp/dhcpv6.c | 4 +++- 9 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h index e971955ba..29a235944 100644 --- a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h @@ -33,11 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' - #define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM32 #define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ diff --git a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h index 4ffea7f44..bb26aae4d 100644 --- a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h @@ -33,11 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' - #define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM64 #define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ diff --git a/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/src/arch/i386/include/efi/ipxe/dhcp_arch.h index 74027928d..cf3dbe499 100644 --- a/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -33,11 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '6', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' - #define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_IA32 #define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ diff --git a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h index 0a7a2f7cd..e22f50b37 100644 --- a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -33,11 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' - #define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 #define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ diff --git a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h index b35818efd..fb85b4405 100644 --- a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h @@ -33,11 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '9', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' - #define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86_64 #define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ diff --git a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h index 0a7a2f7cd..e22f50b37 100644 --- a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h +++ b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h @@ -33,11 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' - #define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 #define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index 693aa7e73..b699b31f0 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -210,6 +210,29 @@ struct dhcp_pxe_boot_menu_item { /** Vendor class identifier */ #define DHCP_VENDOR_CLASS_ID 60 +/** Vendor class identifier for PXE clients */ +#define DHCP_VENDOR_PXECLIENT( arch, ndi ) \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', DHCP_VENDOR_PXECLIENT_ARCH ( arch ), \ + ':', 'U', 'N', 'D', 'I', ':', DHCP_VENDOR_PXECLIENT_UNDI ( ndi ) + +/** Vendor class identifier architecture for PXE clients */ +#define DHCP_VENDOR_PXECLIENT_ARCH( arch ) \ + ( '0' + ( ( (arch) / 10000 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 1000 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 100 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 10 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 1 ) % 10 ) ) + +/** Vendor class identifier UNDI version for PXE clients */ +#define DHCP_VENDOR_PXECLIENT_UNDI( type, major, minor ) \ + DHCP_VENDOR_PXECLIENT_UNDI_VERSION ( major ), \ + DHCP_VENDOR_PXECLIENT_UNDI_VERSION ( minor ) +#define DHCP_VENDOR_PXECLIENT_UNDI_VERSION( version ) \ + ( '0' + ( ( (version) / 100 ) % 10 ) ), \ + ( '0' + ( ( (version) / 10 ) % 10 ) ), \ + ( '0' + ( ( (version) / 1 ) % 10 ) ) + /** Client identifier */ #define DHCP_CLIENT_ID 61 diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 0c6cea0f1..b9c1fd90c 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -84,7 +84,9 @@ static uint8_t dhcp_request_options_data[] = { DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ), DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), DHCP_CLIENT_NDI, DHCP_OPTION ( DHCP_ARCH_CLIENT_NDI ), - DHCP_VENDOR_CLASS_ID, DHCP_STRING ( DHCP_ARCH_VENDOR_CLASS_ID ), + DHCP_VENDOR_CLASS_ID, + DHCP_STRING ( DHCP_VENDOR_PXECLIENT ( DHCP_ARCH_CLIENT_ARCHITECTURE, + DHCP_ARCH_CLIENT_NDI ) ), DHCP_USER_CLASS_ID, DHCP_STRING ( 'i', 'P', 'X', 'E' ), DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index ef07f54e3..be1ed4ec0 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -374,7 +374,9 @@ static uint8_t dhcpv6_request_options_data[] = { DHCPV6_CODE ( DHCPV6_BOOTFILE_PARAM ) ), DHCPV6_CODE ( DHCPV6_VENDOR_CLASS ), DHCPV6_OPTION ( DHCPV6_DWORD_VALUE ( DHCPV6_VENDOR_CLASS_PXE ), - DHCPV6_STRING ( DHCP_ARCH_VENDOR_CLASS_ID ) ), + DHCPV6_STRING ( + DHCP_VENDOR_PXECLIENT ( DHCP_ARCH_CLIENT_ARCHITECTURE, + DHCP_ARCH_CLIENT_NDI ) ) ), DHCPV6_CODE ( DHCPV6_CLIENT_ARCHITECTURE ), DHCPV6_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), DHCPV6_CODE ( DHCPV6_CLIENT_NDI ), From 5e2a7481ada168ab8b5f58bbe099ef51b7b09098 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 16:10:45 +0100 Subject: [PATCH 261/591] [xfer] Send intf_close() if redirection fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A redirection failure is fatal, but provides no opportunity for the caller of xfer_[v]redirect() to report the failure since the interface will already have been disconnected. Fix by sending intf_close() from within the default xfer_vredirect() handler. Debugged-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/core/xfer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/xfer.c b/src/core/xfer.c index 112fee1bf..3a2f174d0 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -81,12 +81,17 @@ int xfer_vredirect ( struct interface *intf, int type, va_list args ) { * xfer_vreopen(), we create a temporary interface in * order to be able to send xfer_window_changed() to * the parent. + * + * If redirection fails, then send intf_close() to the + * parent interface. */ intf_plug ( &tmp, dest ); rc = xfer_vreopen ( dest, type, args ); if ( rc == 0 ) { xfer_window_changed ( dest ); xfer_window_changed ( &tmp ); + } else { + intf_close ( &tmp, rc ); } intf_unplug ( &tmp ); } From 89c6db838fc795c113e0584de0230cf86225135b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 16:13:25 +0100 Subject: [PATCH 262/591] [downloader] Treat redirection failures as fatal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debugged-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/core/downloader.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/downloader.c b/src/core/downloader.c index ba678f868..35b5b0ac6 100644 --- a/src/core/downloader.c +++ b/src/core/downloader.c @@ -190,14 +190,18 @@ static int downloader_vredirect ( struct downloader *downloader, int type, /* Set image URI */ if ( ( rc = image_set_uri ( downloader->image, uri ) ) != 0 ) - return rc; + goto err; } /* Redirect to new location */ if ( ( rc = xfer_vreopen ( &downloader->xfer, type, args ) ) != 0 ) - return rc; + goto err; return 0; + + err: + downloader_finished ( downloader, rc ); + return rc; } /** Downloader data transfer interface operations */ From 55f7a675d61f8b83478e71d4f9efb3a1b789eb08 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Jul 2016 16:14:22 +0100 Subject: [PATCH 263/591] [iscsi] Treat redirection failures as fatal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debugged-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 019a4c14e..ec004e4ba 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1710,6 +1710,7 @@ static int iscsi_vredirect ( struct iscsi_session *iscsi, int type, va_list args ) { va_list tmp; struct sockaddr *peer; + int rc; /* Intercept redirects to a LOCATION_SOCKET and record the IP * address for the iBFT. This is a bit of a hack, but avoids @@ -1725,7 +1726,15 @@ static int iscsi_vredirect ( struct iscsi_session *iscsi, int type, va_end ( tmp ); } - return xfer_vreopen ( &iscsi->socket, type, args ); + /* Redirect to new location */ + if ( ( rc = xfer_vreopen ( &iscsi->socket, type, args ) ) != 0 ) + goto err; + + return 0; + + err: + iscsi_close ( iscsi, rc ); + return rc; } /** iSCSI socket interface operations */ From e2c0a20d60cac7e29f8aa6ed8231338eb34e9736 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jul 2016 10:10:35 +0100 Subject: [PATCH 264/591] [debug] Allow per-object runtime enabling/disabling of debug messages The DBG_ENABLE() and DBG_DISABLE() macros currently affect the debug level of all objects that were built with debugging enabled. This is undesirable, since it is common to use different debug levels in each object. Make the debug level mask a per-object variable. DBG_ENABLE() and DBG_DISABLE() now control only the debug level for the containing object (which is consistent with the intended usage across the existing codebase). DBG_ENABLE_OBJECT() and DBG_DISABLE_OBJECT() may be used to control the debug level for a specified object. For example: // Enable DBG() messages from tcpip.c DBG_ENABLE_OBJECT ( tcpip, DBGLVL_LOG ); Note that the existence of debug messages continues to be gated by the DEBUG=... list specified on the build command line. If an object was built without the relevant debug level, then DBG_ENABLE_OBJECT() will have no effect on that object at runtime (other than to explicitly drag in the object via a symbol reference). Signed-off-by: Michael Brown --- src/include/compiler.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/include/compiler.h b/src/include/compiler.h index 32afb64cf..eae62c518 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -285,14 +285,23 @@ extern void dbg_pause ( void ); extern void dbg_more ( void ); /* Allow for selective disabling of enabled debug levels */ +#define __debug_disable( object ) _C2 ( __debug_disable_, object ) +char __debug_disable(OBJECT); +#define DBG_DISABLE_OBJECT( object, level ) do { \ + extern char __debug_disable(object); \ + __debug_disable(object) |= (level); \ + } while ( 0 ) +#define DBG_ENABLE_OBJECT( object, level ) do { \ + extern char __debug_disable(object); \ + __debug_disable(object) &= ~(level); \ + } while ( 0 ) #if DBGLVL_MAX -int __debug_disable; -#define DBGLVL ( DBGLVL_MAX & ~__debug_disable ) +#define DBGLVL ( DBGLVL_MAX & ~__debug_disable(OBJECT) ) #define DBG_DISABLE( level ) do { \ - __debug_disable |= (level); \ + __debug_disable(OBJECT) |= ( (level) & DBGLVL_MAX ); \ } while ( 0 ) #define DBG_ENABLE( level ) do { \ - __debug_disable &= ~(level); \ + __debug_disable(OBJECT) &= ~( (level) & DBGLVL_MAX ); \ } while ( 0 ) #else #define DBGLVL 0 From 6e1ce52d146fe8197c1b58e2e3b0ebcf9ccda349 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jul 2016 10:19:36 +0100 Subject: [PATCH 265/591] [debug] Allow debug messages to be initially disabled at runtime Extend the DEBUG=... syntax to allow debug messages to be compiled in but disabled by default. For example: make bin/undionly.kpxe DEBUG=netdevice:3:1 would compile in the messages as for DEBUG=netdevice:3, but would set the debug level mask so that only the DEBUG=netdevice:1 messages would be displayed. This allows for external code to selectively enable the additional debug messages at runtime, without being overwhelmed by unwanted initial noise. For example, a developer of a new protocol may want to temporarily enable tracing of all packets received: this can be done by building with DEBUG=netdevice:3:1 and using // temporarily enable per-packet messages DBG_ENABLE_OBJECT ( netdevice, DBGLVL_EXTRA ); ... // disable per-packet messages DBG_DISABLE_OBJECT ( netdevice, DBGLVL_EXTRA ); Note that unlike the usual DBG_ENABLE() and DBG_DISABLE() macros, DBG_ENABLE_OBJECT() and DBG_DISABLE_OBJECT() will not be removed via dead code elimination if debugging is disabled in the specified object. In particular, this means that using either of these macros will always result in a symbol reference to the specified object. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 20 ++++++++++++++------ src/include/compiler.h | 6 +++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index ceff81410..264b9d0f9 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -523,19 +523,25 @@ POST_O := POST_O_DEPS := endif +# Debug level calculations +# +DBGLVL_MAX = -DDBGLVL_MAX=$(firstword $(subst ., ,$(1))) +DBGLVL_DFLT = -DDBGLVL_DFLT=$(lastword $(subst ., ,$(1))) +DBGLVL = $(call DBGLVL_MAX,$(1)) $(call DBGLVL_DFLT,$(1)) + # Rules for specific object types. # COMPILE_c = $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS) RULE_c = $(Q)$(COMPILE_c) -c $< -o $@ $(POST_O) RULE_c_to_ids.o = $(Q)$(ECHO_E) '$(OBJ_IDS_ASM_NL)' | $(ASSEMBLE_S) -o $@ -RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -DDBGLVL_MAX=$* -c $< -o $@ $(POST_O) +RULE_c_to_dbg%.o= $(Q)$(COMPILE_c) $(call DBGLVL,$*) -c $< -o $@ $(POST_O) RULE_c_to_c = $(Q)$(COMPILE_c) -E -c $< > $@ RULE_c_to_s = $(Q)$(COMPILE_c) -S -g0 -c $< -o $@ PREPROCESS_S = $(CPP) $(CFLAGS) $(CFLAGS_S) $(OBJ_CFLAGS) ASSEMBLE_S = $(AS) $(ASFLAGS) RULE_S = $(Q)$(PREPROCESS_S) $< | $(ASSEMBLE_S) -o $@ -RULE_S_to_dbg%.o = $(Q)$(PREPROCESS_S) -DDBGLVL_MAX=$* $< | $(ASSEMBLE_S) -o $@ +RULE_S_to_dbg%.o= $(Q)$(PREPROCESS_S) $(call DBGLVL,$*) $< | $(ASSEMBLE_S) -o $@ RULE_S_to_s = $(Q)$(PREPROCESS_S) $< > $@ GENERIC_TARGETS += ids.o dbg%.o c s @@ -1019,10 +1025,12 @@ TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_ENTRY) $(TGT_LD_DRIVERS) \ # the target. # DEBUG_LIST = $(subst $(COMMA), ,$(DEBUG)) -DEBUG_OBJ_LEVEL = $(firstword $(word 2,$(subst :, ,$(1))) 1) -DEBUG_OBJ_BASE = $(word 1,$(subst :, ,$(1))).dbg$(call DEBUG_OBJ_LEVEL,$(1)) -DEBUG_OBJ = $(BIN)/$(call DEBUG_OBJ_BASE,$(1)).o -DEBUG_ORIG_OBJ = $(BIN)/$(word 1,$(subst :, ,$(1))).o +DEBUG_MAX = $(firstword $(word 2,$(subst :, ,$(1))) 1) +DEBUG_DFLT = $(if $(word 3,$(subst :, ,$(1))),.$(word 3,$(subst :, ,$(1)))) +DEBUG_LEVEL = $(call DEBUG_MAX,$(1))$(call DEBUG_DFLT,$(1)) +DEBUG_BASE = $(firstword $(subst :, ,$(1))).dbg$(call DEBUG_LEVEL,$(1)) +DEBUG_OBJ = $(BIN)/$(call DEBUG_BASE,$(1)).o +DEBUG_ORIG_OBJ = $(BIN)/$(firstword $(subst :, ,$(1))).o DEBUG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_OBJ,$(D))) DEBUG_ORIG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_ORIG_OBJ,$(D))) BLIB_OBJS = $(DEBUG_OBJS) $(filter-out $(DEBUG_ORIG_OBJS),$(BOBJS)) diff --git a/src/include/compiler.h b/src/include/compiler.h index eae62c518..4924c7ef5 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -270,6 +270,10 @@ PROVIDE_SYMBOL ( OBJECT_SYMBOL ); #define DBGLVL_MAX 0 #endif +#ifndef DBGLVL_DFLT +#define DBGLVL_DFLT DBGLVL_MAX +#endif + #ifndef ASSEMBLY /** printf() for debugging */ @@ -286,7 +290,7 @@ extern void dbg_more ( void ); /* Allow for selective disabling of enabled debug levels */ #define __debug_disable( object ) _C2 ( __debug_disable_, object ) -char __debug_disable(OBJECT); +char __debug_disable(OBJECT) = ( DBGLVL_MAX & ~DBGLVL_DFLT ); #define DBG_DISABLE_OBJECT( object, level ) do { \ extern char __debug_disable(object); \ __debug_disable(object) |= (level); \ From 46719f2264fc63deaa712f4dc164d3b33c66e05f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jul 2016 13:28:51 +0100 Subject: [PATCH 266/591] [libc] Allow assertions to be globally enabled or disabled Assertions are enabled for objects built with any debug level (including an explicit debug level of zero). It is sometimes useful to be able to enable assertions across all objects; this currently requires manually hacking include/assert.h. Allow assertions to be globally enabled by adding ASSERT=1 to the build command line. For example: make bin/8086100e.mrom ASSERT=1 Similarly, allow assertions to be globally disabled by adding ASSERT=0 to the build command line. If no ASSERT=... is specified on the build command line, then only objects mentioned in DEBUG=... will have assertions enabled (as is currently the case). Note than globally enabling assertions imposes a relatively heavy runtime penalty, primarily due to the various sanity checks performed by list_add(), list_for_each_entry(), etc. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 27 +++++++++++++++++++++++++++ src/include/assert.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 264b9d0f9..a02acc8dd 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -720,6 +720,33 @@ config/named.h : $(CONFIG_LIST) .PRECIOUS : config/named.h +# (Single-element) list of assertion configuration +# +ASSERT_LIST := $(BIN)/.assert.list +ifeq ($(wildcard $(ASSERT_LIST)),) +ASSERT_OLD := +else +ASSERT_OLD := $(shell cat $(ASSERT_LIST)) +endif +ifneq ($(ASSERT_OLD),$(ASSERT)) +$(shell $(ECHO) "$(ASSERT)" > $(ASSERT_LIST)) +endif + +$(ASSERT_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(ASSERT_LIST) + +# Assertion configuration +# +ifneq ($(ASSERT),) +CFLAGS += -DASSERTING=$(ASSERT) +endif + +include/assert.h : $(ASSERT_LIST) + $(Q)$(TOUCH) $@ + +.PRECIOUS : include/assert.h + # These files use .incbin inline assembly to include a binary file. # Unfortunately ccache does not detect this dependency and caches # builds even when the binary file has changed. diff --git a/src/include/assert.h b/src/include/assert.h index 07f3ecb8c..dd71fa713 100644 --- a/src/include/assert.h +++ b/src/include/assert.h @@ -12,11 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#ifndef ASSERTING #ifdef NDEBUG #define ASSERTING 0 #else #define ASSERTING 1 #endif +#endif extern unsigned int assertion_failures; From 5430465185ba037c1f9c06cd3ec486b56dafb539 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jul 2016 13:45:54 +0100 Subject: [PATCH 267/591] [profile] Allow profiling to be globally enabled or disabled As with assertions, profiling is enabled for objects built with any debug level (including an explicit debug level of zero). Allow profiling to be globally enabled or disabled by adding PROFILE=1 or PROFILE=0 respectively to the build command line. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 27 +++++++++++++++++++++++++++ src/include/ipxe/profile.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index a02acc8dd..f09db3728 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -747,6 +747,33 @@ include/assert.h : $(ASSERT_LIST) .PRECIOUS : include/assert.h +# (Single-element) list of profiling configuration +# +PROFILE_LIST := $(BIN)/.profile.list +ifeq ($(wildcard $(PROFILE_LIST)),) +PROFILE_OLD := +else +PROFILE_OLD := $(shell cat $(PROFILE_LIST)) +endif +ifneq ($(PROFILE_OLD),$(PROFILE)) +$(shell $(ECHO) "$(PROFILE)" > $(PROFILE_LIST)) +endif + +$(PROFILE_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(PROFILE_LIST) + +# Profiling configuration +# +ifneq ($(PROFILE),) +CFLAGS += -DPROFILING=$(PROFILE) +endif + +include/ipxe/profile.h : $(PROFILE_LIST) + $(Q)$(TOUCH) $@ + +.PRECIOUS : include/ipxe/profile.h + # These files use .incbin inline assembly to include a binary file. # Unfortunately ccache does not detect this dependency and caches # builds even when the binary file has changed. diff --git a/src/include/ipxe/profile.h b/src/include/ipxe/profile.h index b6d2b19e0..2c69e1208 100644 --- a/src/include/ipxe/profile.h +++ b/src/include/ipxe/profile.h @@ -12,11 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#ifndef PROFILING #ifdef NDEBUG #define PROFILING 0 #else #define PROFILING 1 #endif +#endif /** * A data structure for storing profiling information From d6817943d187c18869a4d69e2499caf453162bca Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 30 Jun 2016 14:37:36 +0200 Subject: [PATCH 268/591] [efi] Install the HII config access protocol on a child of the SNP handle In edk2, there are several drivers that associate HII forms (and corresponding config access protocol instances) with each individual network device. (In this context, "network device" means the EFI handle on which the SNP protocol is installed, and on which the device path ending with the MAC() node is installed also.) Such edk2 drivers are, for example: Ip4Dxe, HttpBootDxe, VlanConfigDxe. In UEFI, any given handle can carry at most one instance of a specific protocol (see e.g. the specification of the InstallProtocolInterface() boot service). This implies that the class of drivers mentioned above can't install their EFI_HII_CONFIG_ACCESS_PROTOCOL instances on the SNP handle directly -- they would conflict with each other. Accordingly, each of those edk2 drivers creates a "private" child handle under the SNP handle, and installs its config access protocol (and corresponding HII package list) on its child handle. The device path for the child handle is traditionally derived by appending a Hardware Vendor Device Path node after the MAC() node. The VenHw() nodes in question consist of a GUID (by definition), and no trailing data (by choice). The purpose of these VenHw() nodes is only that all the child nodes can be uniquely identified by device path. At the moment iPXE does not follow this pattern. It doesn't run into a conflict when it installs its EFI_HII_CONFIG_ACCESS_PROTOCOL directly on the SNP handle, but that's only because iPXE is the sole driver not following the pattern. This behavior seems risky (one might call it a "latent bug"); better align iPXE with the edk2 custom. Cc: Michael Brown Cc: Gary Lin Cc: Ladi Prosek Ref: http://thread.gmane.org/gmane.comp.bios.edk2.devel/13494/focus=13532 Signed-off-by: Laszlo Ersek Reviewed-by: Ladi Prosek Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_snp.h | 4 ++ src/interface/efi/efi_snp_hii.c | 75 +++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/include/ipxe/efi/efi_snp.h b/src/include/ipxe/efi/efi_snp.h index 4c5461ec4..a9f67cfcb 100644 --- a/src/include/ipxe/efi/efi_snp.h +++ b/src/include/ipxe/efi/efi_snp.h @@ -57,6 +57,10 @@ struct efi_snp_device { EFI_HII_CONFIG_ACCESS_PROTOCOL hii; /** HII package list */ EFI_HII_PACKAGE_LIST_HEADER *package_list; + /** EFI child handle for HII association */ + EFI_HANDLE hii_child_handle; + /** Device path of HII child handle */ + EFI_DEVICE_PATH_PROTOCOL *hii_child_path; /** HII handle */ EFI_HII_HANDLE hii_handle; /** Device name */ diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 1e87ea15a..651bef040 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -63,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** EFI platform setup formset GUID */ @@ -653,6 +654,9 @@ static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = { */ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + VENDOR_DEVICE_PATH *vendor_path; + EFI_DEVICE_PATH_PROTOCOL *path_end; + size_t path_prefix_len; int efirc; int rc; @@ -674,9 +678,46 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { goto err_build_package_list; } + /* Allocate the new device path */ + path_prefix_len = efi_devpath_len ( snpdev->path ); + snpdev->hii_child_path = zalloc ( path_prefix_len + + sizeof ( *vendor_path ) + + sizeof ( *path_end ) ); + if ( ! snpdev->hii_child_path ) { + DBGC ( snpdev, + "SNPDEV %p could not allocate HII child device path\n", + snpdev ); + rc = -ENOMEM; + goto err_alloc_child_path; + } + + /* Populate the device path */ + memcpy ( snpdev->hii_child_path, snpdev->path, path_prefix_len ); + vendor_path = ( ( ( void * ) snpdev->hii_child_path ) + + path_prefix_len ); + vendor_path->Header.Type = HARDWARE_DEVICE_PATH; + vendor_path->Header.SubType = HW_VENDOR_DP; + vendor_path->Header.Length[0] = sizeof ( *vendor_path ); + efi_snp_hii_random_guid ( &vendor_path->Guid ); + path_end = ( ( void * ) ( vendor_path + 1 ) ); + path_end->Type = END_DEVICE_PATH_TYPE; + path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + path_end->Length[0] = sizeof ( *path_end ); + + /* Create device path and child handle for HII association */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &snpdev->hii_child_handle, + &efi_device_path_protocol_guid, snpdev->hii_child_path, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( snpdev, "SNPDEV %p could not create HII child handle: " + "%s\n", snpdev, strerror ( rc ) ); + goto err_hii_child_handle; + } + /* Add HII packages */ if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list, - snpdev->handle, + snpdev->hii_child_handle, &snpdev->hii_handle ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n", @@ -686,7 +727,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { /* Install HII protocol */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &snpdev->handle, + &snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); @@ -695,15 +736,34 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { goto err_install_protocol; } + /* Add as child of handle with SNP instance */ + if ( ( rc = efi_child_add ( snpdev->handle, + snpdev->hii_child_handle ) ) != 0 ) { + DBGC ( snpdev, + "SNPDEV %p could not adopt HII child handle: %s\n", + snpdev, strerror ( rc ) ); + goto err_efi_child_add; + } + return 0; + efi_child_del ( snpdev->handle, snpdev->hii_child_handle ); + err_efi_child_add: bs->UninstallMultipleProtocolInterfaces ( - snpdev->handle, + snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, NULL ); err_install_protocol: efihii->RemovePackageList ( efihii, snpdev->hii_handle ); err_new_package_list: + bs->UninstallMultipleProtocolInterfaces ( + snpdev->hii_child_handle, + &efi_device_path_protocol_guid, snpdev->hii_child_path, + NULL ); + err_hii_child_handle: + free ( snpdev->hii_child_path ); + snpdev->hii_child_path = NULL; + err_alloc_child_path: free ( snpdev->package_list ); snpdev->package_list = NULL; err_build_package_list: @@ -724,11 +784,18 @@ void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) { return; /* Uninstall protocols and remove package list */ + efi_child_del ( snpdev->handle, snpdev->hii_child_handle ); bs->UninstallMultipleProtocolInterfaces ( - snpdev->handle, + snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, NULL ); efihii->RemovePackageList ( efihii, snpdev->hii_handle ); + bs->UninstallMultipleProtocolInterfaces ( + snpdev->hii_child_handle, + &efi_device_path_protocol_guid, snpdev->hii_child_path, + NULL ); + free ( snpdev->hii_child_path ); + snpdev->hii_child_path = NULL; free ( snpdev->package_list ); snpdev->package_list = NULL; } From 74222cd2c19e71a9474529813075dfacbaa75b34 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Jul 2016 20:36:53 +0100 Subject: [PATCH 269/591] [rng] Check for functioning RTC interrupt On some platforms (observed in a small subset of Microsoft Azure (Hyper-V) virtual machines), the RTC appears to be incapable of generating an interrupt via the legacy PIC. The RTC status registers show that a periodic interrupt has been asserted, but the PIC IRR shows that IRQ8 remains inactive. On such systems, iPXE will currently freeze during the "iPXE initialising devices..." message. Work around this problem by checking that RTC interrupts are being raised before returning from rtc_entropy_enable(). If no interrupt is seen within 100ms, then we assume that the RTC interrupt mechanism is broken. In these circumstances, iPXE will continue to initialise but any subsequent attempt to generate entropy will fail. In particular, HTTPS connections will fail with an error indicating that no entropy is available. Signed-off-by: Michael Brown --- src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86/interface/pcbios/rtc_entropy.c | 56 +++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 42792242d..79b6f882e 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_vesafb ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 ) #define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) #define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 ) +#define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c index 83c2445f8..e9e6baa59 100644 --- a/src/arch/x86/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -31,17 +31,26 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include +#include #include #include #include #include +/** Maximum time to wait for an RTC interrupt, in milliseconds */ +#define RTC_MAX_WAIT_MS 100 + /** RTC interrupt handler */ extern void rtc_isr ( void ); /** Previous RTC interrupt handler */ static struct segoff rtc_old_handler; +/** Flag set by RTC interrupt handler */ +extern volatile uint8_t __text16 ( rtc_flag ); +#define rtc_flag __use_text16 ( rtc_flag ) + /** * Hook RTC interrupt handler * @@ -96,6 +105,10 @@ static void rtc_unhook_isr ( void ) { static void rtc_enable_int ( void ) { uint8_t status_b; + /* Clear any stale pending interrupts via status register C */ + outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS ); + inb ( CMOS_DATA ); + /* Set Periodic Interrupt Enable bit in status register B */ outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS ); status_b = inb ( CMOS_DATA ); @@ -125,18 +138,60 @@ static void rtc_disable_int ( void ) { inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */ } +/** + * Check that entropy gathering is functional + * + * @ret rc Return status code + */ +static int rtc_entropy_check ( void ) { + unsigned int i; + + /* Check that RTC interrupts are working */ + rtc_flag = 0; + for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) { + + /* Allow interrupts to occur */ + __asm__ __volatile__ ( "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" ); + + /* Check for RTC interrupt flag */ + if ( rtc_flag ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" ); + return -ETIMEDOUT; +} + /** * Enable entropy gathering * * @ret rc Return status code */ static int rtc_entropy_enable ( void ) { + int rc; + /* Hook ISR and enable RTC interrupts */ rtc_hook_isr(); enable_irq ( RTC_IRQ ); rtc_enable_int(); + /* Check that RTC interrupts are working */ + if ( ( rc = rtc_entropy_check() ) != 0 ) + goto err_check; + return 0; + + err_check: + rtc_disable_int(); + disable_irq ( RTC_IRQ ); + rtc_unhook_isr(); + return rc; } /** @@ -145,6 +200,7 @@ static int rtc_entropy_enable ( void ) { */ static void rtc_entropy_disable ( void ) { + /* Disable RTC interrupts and unhook ISR */ rtc_disable_int(); disable_irq ( RTC_IRQ ); rtc_unhook_isr(); From e19c0a8fd2bc8c2331c5fabe17ea56e7c35d1900 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Jul 2016 19:25:26 +0100 Subject: [PATCH 270/591] [acpi] Add support for ACPI power off Signed-off-by: Michael Brown --- src/arch/x86/include/bios.h | 1 + src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86/include/ipxe/acpipwr.h | 14 + src/arch/x86/include/ipxe/apm.h | 14 + src/arch/x86/interface/pcbios/acpipwr.c | 116 +++++++++ src/arch/x86/interface/pcbios/apm.c | 6 +- src/arch/x86/interface/pcbios/bios_reboot.c | 22 ++ src/core/acpi.c | 274 ++++++++++++++++++++ src/include/ipxe/acpi.h | 75 ++++++ 9 files changed, 519 insertions(+), 4 deletions(-) create mode 100644 src/arch/x86/include/ipxe/acpipwr.h create mode 100644 src/arch/x86/include/ipxe/apm.h create mode 100644 src/arch/x86/interface/pcbios/acpipwr.c diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index 988bbc62b..a5a5d887c 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -4,6 +4,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 +#define BDA_EBDA 0x000e #define BDA_EQUIPMENT_WORD 0x0010 #define BDA_FBMS 0x0013 #define BDA_REBOOT 0x0072 diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 79b6f882e..f4816e62a 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) #define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 ) #define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 ) +#define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86/include/ipxe/acpipwr.h b/src/arch/x86/include/ipxe/acpipwr.h new file mode 100644 index 000000000..93da09429 --- /dev/null +++ b/src/arch/x86/include/ipxe/acpipwr.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_ACPIPWR_H +#define _IPXE_ACPIPWR_H + +/** @file + * + * ACPI power off + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int acpi_poweroff ( void ); + +#endif /* _IPXE_ACPIPWR_H */ diff --git a/src/arch/x86/include/ipxe/apm.h b/src/arch/x86/include/ipxe/apm.h new file mode 100644 index 000000000..21d913ac4 --- /dev/null +++ b/src/arch/x86/include/ipxe/apm.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_APM_H +#define _IPXE_APM_H + +/** @file + * + * Advanced Power Management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int apm_poweroff ( void ); + +#endif /* _IPXE_APM_H */ diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c new file mode 100644 index 000000000..63b986b68 --- /dev/null +++ b/src/arch/x86/interface/pcbios/acpipwr.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * ACPI power off + * + */ + +/** Colour for debug messages */ +#define colour FADT_SIGNATURE + +/** _S5_ signature */ +#define S5_SIGNATURE ACPI_SIGNATURE ( '_', 'S', '5', '_' ) + +/** + * Power off the computer using ACPI + * + * @ret rc Return status code + */ +int acpi_poweroff ( void ) { + struct acpi_fadt fadtab; + uint16_t ebda; + userptr_t rsdt; + userptr_t fadt; + unsigned int pm1a_cnt_blk; + unsigned int pm1b_cnt_blk; + unsigned int pm1a_cnt; + unsigned int pm1b_cnt; + unsigned int slp_typa; + unsigned int slp_typb; + int s5; + int rc; + + /* Locate EBDA */ + get_real ( ebda, BDA_SEG, BDA_EBDA ); + + /* Locate RSDT */ + rsdt = acpi_find_rsdt ( real_to_user ( ebda, 0 ) ); + if ( ! rsdt ) { + DBGC ( colour, "ACPI could not find RSDT (EBDA %04x)\n", ebda ); + return -ENOENT; + } + + /* Locate FADT */ + fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 ); + if ( ! fadt ) { + DBGC ( colour, "ACPI could not find FADT\n" ); + return -ENOENT; + } + + /* Read FADT */ + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + pm1a_cnt_blk = le32_to_cpu ( fadtab.pm1a_cnt_blk ); + pm1b_cnt_blk = le32_to_cpu ( fadtab.pm1b_cnt_blk ); + pm1a_cnt = ( pm1a_cnt_blk + ACPI_PM1_CNT ); + pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT ); + + /* Extract \_S5 from DSDT or any SSDT */ + s5 = acpi_sx ( rsdt, S5_SIGNATURE ); + if ( s5 < 0 ) { + rc = s5; + DBGC ( colour, "ACPI could not extract \\_S5: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Power off system */ + if ( pm1a_cnt_blk ) { + slp_typa = ( ( s5 >> 0 ) & 0xff ); + DBGC ( colour, "ACPI PM1a sleep type %#x => %04x\n", + slp_typa, pm1a_cnt ); + outw ( ( ACPI_PM1_CNT_SLP_TYP ( slp_typa ) | + ACPI_PM1_CNT_SLP_EN ), pm1a_cnt ); + } + if ( pm1b_cnt_blk ) { + slp_typb = ( ( s5 >> 8 ) & 0xff ); + DBGC ( colour, "ACPI PM1b sleep type %#x => %04x\n", + slp_typb, pm1b_cnt ); + outw ( ( ACPI_PM1_CNT_SLP_TYP ( slp_typb ) | + ACPI_PM1_CNT_SLP_EN ), pm1b_cnt ); + } + + DBGC ( colour, "ACPI power off failed\n" ); + return -EPROTO; +} diff --git a/src/arch/x86/interface/pcbios/apm.c b/src/arch/x86/interface/pcbios/apm.c index 50b19cb81..680dbb16a 100644 --- a/src/arch/x86/interface/pcbios/apm.c +++ b/src/arch/x86/interface/pcbios/apm.c @@ -32,14 +32,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -#include +#include /** * Power off the computer using APM * * @ret rc Return status code */ -static int apm_poweroff ( void ) { +int apm_poweroff ( void ) { uint16_t apm_version; uint16_t apm_signature; uint16_t apm_flags; @@ -108,5 +108,3 @@ static int apm_poweroff ( void ) { /* Should never happen */ return -ECANCELED; } - -PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff ); diff --git a/src/arch/x86/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c index ed18dde0b..c6c5a5a91 100644 --- a/src/arch/x86/interface/pcbios/bios_reboot.c +++ b/src/arch/x86/interface/pcbios/bios_reboot.c @@ -32,6 +32,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** * Reboot system @@ -49,4 +51,24 @@ static void bios_reboot ( int warm ) { __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) ); } +/** + * Power off system + * + * @ret rc Return status code + */ +static int bios_poweroff ( void ) { + int rc; + + /* Try APM */ + if ( ( rc = apm_poweroff() ) != 0 ) + DBG ( "APM power off failed: %s\n", strerror ( rc ) ); + + /* Try ACPI */ + if ( ( rc = acpi_poweroff() ) != 0 ) + DBG ( "ACPI power off failed: %s\n", strerror ( rc ) ); + + return rc; +} + PROVIDE_REBOOT ( pcbios, reboot, bios_reboot ); +PROVIDE_REBOOT ( pcbios, poweroff, bios_poweroff ); diff --git a/src/core/acpi.c b/src/core/acpi.c index b0ccfa78d..955637e00 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include #include #include @@ -40,6 +42,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ +/** + * Transcribe ACPI table signature (for debugging) + * + * @v signature ACPI table signature + * @ret name ACPI table signature name + */ +static const char * acpi_name ( uint32_t signature ) { + static union { + uint32_t signature; + char name[5]; + } u; + + u.signature = cpu_to_le32 ( signature ); + return u.name; +} + /** * Fix up ACPI table checksum * @@ -55,6 +73,262 @@ void acpi_fix_checksum ( struct acpi_description_header *acpi ) { acpi->checksum -= sum; } +/** + * Locate ACPI root system description table within a memory range + * + * @v start Start address to search + * @v len Length to search + * @ret rsdt ACPI root system description table, or UNULL + */ +static userptr_t acpi_find_rsdt_range ( userptr_t start, size_t len ) { + static const char signature[8] = RSDP_SIGNATURE; + struct acpi_rsdp rsdp; + userptr_t rsdt; + size_t offset; + uint8_t sum; + unsigned int i; + + /* Search for RSDP */ + for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ; + offset += RSDP_STRIDE ) { + + /* Check signature and checksum */ + copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) ); + if ( memcmp ( rsdp.signature, signature, + sizeof ( signature ) ) != 0 ) + continue; + for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ ) + sum += *( ( ( uint8_t * ) &rsdp ) + i ); + if ( sum != 0 ) + continue; + + /* Extract RSDT */ + rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) ); + DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n", + user_to_phys ( rsdt, 0 ), + user_to_phys ( start, offset ) ); + return rsdt; + } + + return UNULL; +} + +/** + * Locate ACPI root system description table + * + * @v ebda Extended BIOS data area, or UNULL + * @ret rsdt ACPI root system description table, or UNULL + */ +userptr_t acpi_find_rsdt ( userptr_t ebda ) { + userptr_t rsdt; + + /* Search EBDA, if applicable */ + if ( ebda ) { + rsdt = acpi_find_rsdt_range ( ebda, RSDP_EBDA_LEN ); + if ( rsdt ) + return rsdt; + } + + /* Search fixed BIOS area */ + rsdt = acpi_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ), + RSDP_BIOS_LEN ); + if ( rsdt ) + return rsdt; + + return UNULL; +} + +/** + * Locate ACPI table + * + * @v rsdt ACPI root system description table + * @v signature Requested table signature + * @v index Requested index of table with this signature + * @ret table Table, or UNULL if not found + */ +userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) { + struct acpi_description_header acpi; + struct acpi_rsdt *rsdtab; + typeof ( rsdtab->entry[0] ) entry; + userptr_t table; + size_t len; + unsigned int count; + unsigned int i; + + /* Read RSDT header */ + copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) ); + if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) { + DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n", + user_to_phys ( rsdt, 0 ) ); + DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi, + sizeof ( acpi ) ); + return UNULL; + } + len = le32_to_cpu ( acpi.length ); + if ( len < sizeof ( rsdtab->acpi ) ) { + DBGC ( rsdt, "RSDT %#08lx has invalid length:\n", + user_to_phys ( rsdt, 0 ) ); + DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi, + sizeof ( acpi ) ); + return UNULL; + } + + /* Calculate number of entries */ + count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) ); + + /* Search through entries */ + for ( i = 0 ; i < count ; i++ ) { + + /* Get table address */ + copy_from_user ( &entry, rsdt, + offsetof ( typeof ( *rsdtab ), entry[i] ), + sizeof ( entry ) ); + + /* Read table header */ + table = phys_to_user ( entry ); + copy_from_user ( &acpi.signature, table, 0, + sizeof ( acpi.signature ) ); + + /* Check table signature */ + if ( acpi.signature != cpu_to_le32 ( signature ) ) + continue; + + /* Check index */ + if ( index-- ) + continue; + + DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n", + user_to_phys ( rsdt, 0 ), acpi_name ( signature ), + user_to_phys ( table, 0 ) ); + return table; + } + + DBGC ( rsdt, "RSDT %#08lx could not find %s\n", + user_to_phys ( rsdt, 0 ), acpi_name ( signature ) ); + return UNULL; +} + +/** + * Extract \_Sx value from DSDT/SSDT + * + * @v zsdt DSDT or SSDT + * @v signature Signature (e.g. "_S5_") + * @ret sx \_Sx value, or negative error + * + * In theory, extracting the \_Sx value from the DSDT/SSDT requires a + * full ACPI parser plus some heuristics to work around the various + * broken encodings encountered in real ACPI implementations. + * + * In practice, we can get the same result by scanning through the + * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first + * four bytes, removing any bytes with bit 3 set, and treating + * whatever is left as a little-endian value. This is one of the + * uglier hacks I have ever implemented, but it's still prettier than + * the ACPI specification itself. + */ +static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) { + struct acpi_description_header acpi; + union { + uint32_t dword; + uint8_t byte[4]; + } buf; + size_t offset; + size_t len; + unsigned int sx; + uint8_t *byte; + + /* Read table header */ + copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) ); + len = le32_to_cpu ( acpi.length ); + + /* Locate signature */ + for ( offset = sizeof ( acpi ) ; + ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */ + + sizeof ( buf ) /* value */ ) < len ) ; + offset++ ) { + + /* Check signature */ + copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) ); + if ( buf.dword != cpu_to_le32 ( signature ) ) + continue; + DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n", + user_to_phys ( zsdt, 0 ), acpi_name ( signature ), + offset ); + offset += sizeof ( buf ); + + /* Read first four bytes of value */ + copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ), + sizeof ( buf ) ); + DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing " + "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ), + acpi_name ( signature ), buf.byte[0], buf.byte[1], + buf.byte[2], buf.byte[3] ); + + /* Extract \Sx value. There are three potential + * encodings that we might encounter: + * + * - SLP_TYPa, SLP_TYPb, rsvd, rsvd + * + * - , SLP_TYPa, , SLP_TYPb, ... + * + * - , SLP_TYPa, SLP_TYPb, 0, 0 + * + * Since and both have bit + * 3 set, and valid SLP_TYPx must have bit 3 clear + * (since SLP_TYPx is a 3-bit field), we can just skip + * any bytes with bit 3 set. + */ + byte = &buf.byte[0]; + if ( *byte & 0x08 ) + byte++; + sx = *(byte++); + if ( *byte & 0x08 ) + byte++; + sx |= ( *byte << 8 ); + return sx; + } + + return -ENOENT; +} + +/** + * Extract \_Sx value from DSDT/SSDT + * + * @v rsdt ACPI root system description table + * @v signature Signature (e.g. "_S5_") + * @ret sx \_Sx value, or negative error + */ +int acpi_sx ( userptr_t rsdt, uint32_t signature ) { + struct acpi_fadt fadtab; + userptr_t fadt; + userptr_t dsdt; + userptr_t ssdt; + unsigned int i; + int sx; + + /* Try DSDT first */ + fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 ); + if ( fadt ) { + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + dsdt = phys_to_user ( fadtab.dsdt ); + if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 ) + return sx; + } + + /* Try all SSDTs */ + for ( i = 0 ; ; i++ ) { + ssdt = acpi_find ( rsdt, SSDT_SIGNATURE, i ); + if ( ! ssdt ) + break; + if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 ) + return sx; + } + + DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n", + user_to_phys ( rsdt, 0 ), acpi_name ( signature ) ); + return -ENOENT; +} + /****************************************************************************** * * Interface methods diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index 2ccd691ed..17d29b9df 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include /** * An ACPI description header @@ -51,6 +52,76 @@ struct acpi_description_header { #define ACPI_SIGNATURE( a, b, c, d ) \ ( ( (a) << 0 ) | ( (b) << 8 ) | ( (c) << 16 ) | ( (d) << 24 ) ) +/** Root System Description Pointer signature */ +#define RSDP_SIGNATURE { 'R', 'S', 'D', ' ', 'P', 'T', 'R', ' ' } + +/** Root System Description Pointer */ +struct acpi_rsdp { + /** Signature */ + char signature[8]; + /** To make sum of entire table == 0 */ + uint8_t checksum; + /** OEM identification */ + char oem_id[6]; + /** Revision */ + uint8_t revision; + /** Physical address of RSDT */ + uint32_t rsdt; +} __attribute__ (( packed )); + +/** EBDA RSDP length */ +#define RSDP_EBDA_LEN 0x400 + +/** Fixed BIOS area RSDP start address */ +#define RSDP_BIOS_START 0xe0000 + +/** Fixed BIOS area RSDP length */ +#define RSDP_BIOS_LEN 0x20000 + +/** Stride at which to search for RSDP */ +#define RSDP_STRIDE 16 + +/** Root System Description Table (RSDT) signature */ +#define RSDT_SIGNATURE ACPI_SIGNATURE ( 'R', 'S', 'D', 'T' ) + +/** ACPI Root System Description Table (RSDT) */ +struct acpi_rsdt { + /** ACPI header */ + struct acpi_description_header acpi; + /** ACPI table entries */ + uint32_t entry[0]; +} __attribute__ (( packed )); + +/** Fixed ACPI Description Table (FADT) signature */ +#define FADT_SIGNATURE ACPI_SIGNATURE ( 'F', 'A', 'C', 'P' ) + +/** Fixed ACPI Description Table (FADT) */ +struct acpi_fadt { + /** ACPI header */ + struct acpi_description_header acpi; + /** Physical address of FACS */ + uint32_t facs; + /** Physical address of DSDT */ + uint32_t dsdt; + /** Unused by iPXE */ + uint8_t unused[20]; + /** PM1a Control Register Block */ + uint32_t pm1a_cnt_blk; + /** PM1b Control Register Block */ + uint32_t pm1b_cnt_blk; +} __attribute__ (( packed )); + +/** ACPI PM1 Control Register (within PM1a_CNT_BLK or PM1A_CNT_BLK) */ +#define ACPI_PM1_CNT 0 +#define ACPI_PM1_CNT_SLP_TYP(x) ( (x) << 10 ) /**< Sleep type */ +#define ACPI_PM1_CNT_SLP_EN ( 1 << 13 ) /**< Sleep enable */ + +/** Differentiated System Description Table (DSDT) signature */ +#define DSDT_SIGNATURE ACPI_SIGNATURE ( 'D', 'S', 'D', 'T' ) + +/** Secondary System Description Table (SSDT) signature */ +#define SSDT_SIGNATURE ACPI_SIGNATURE ( 'S', 'S', 'D', 'T' ) + extern int acpi_describe ( struct interface *interface, struct acpi_description_header *acpi, size_t len ); #define acpi_describe_TYPE( object_type ) \ @@ -59,5 +130,9 @@ extern int acpi_describe ( struct interface *interface, size_t len ) ) extern void acpi_fix_checksum ( struct acpi_description_header *acpi ); +extern userptr_t acpi_find_rsdt ( userptr_t ebda ); +extern userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, + unsigned int index ); +extern int acpi_sx ( userptr_t rsdt, uint32_t signature ); #endif /* _IPXE_ACPI_H */ From df85901768622b8aea94d39a28effdbf90f7d4f0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 11 Jul 2016 21:23:03 +0100 Subject: [PATCH 271/591] [acpi] Allow time for ACPI power off to take effect The ACPI power off sequence may not take effect immediately. Delay for one second, to eliminate potentially confusing log messages such as "Could not power off: Error 0x43902001 (http://ipx". Reported-by: Leonid Vasetsky Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/acpipwr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c index 63b986b68..d19f972d5 100644 --- a/src/arch/x86/interface/pcbios/acpipwr.c +++ b/src/arch/x86/interface/pcbios/acpipwr.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include #include #include @@ -111,6 +112,11 @@ int acpi_poweroff ( void ) { ACPI_PM1_CNT_SLP_EN ), pm1b_cnt ); } + /* On some systems, execution will continue briefly. Delay to + * avoid potentially confusing log messages. + */ + mdelay ( 1000 ); + DBGC ( colour, "ACPI power off failed\n" ); return -EPROTO; } From 23c275bd1e1ddbb7c03254812b938f1992d66b9e Mon Sep 17 00:00:00 2001 From: Lukas Grossar Date: Mon, 11 Jul 2016 17:06:01 +0200 Subject: [PATCH 272/591] [intel] Add PCI device ID for I219-V/LM Signed-off-by: Lukas Grossar Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 127f5c7e7..4f8a4cbb4 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1062,6 +1062,8 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), + PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", 0 ), + PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", 0 ), PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), From 45dd627689b73b7e80789d73c85d4a1080a7b8ea Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 12 Jul 2016 08:47:27 +0100 Subject: [PATCH 273/591] [ipv4] Send gratuitous ARPs whenever a new IPv4 address is applied In a busy network (such as a public cloud), IPv4 addresses may be recycled rapidly. When this happens, unidirectional traffic (such as UDP syslog) will succeed, but bidirectional traffic (such as TCP connections) may fail due to stale ARP cache entries on other nodes. The remote ARP cache expiry timeout is likely to exceed iPXE's connection timeout, meaning that boot attempts can fail before the problem is automatically resolved. Fix by sending gratuitous ARPs whenever an IPv4 address is changed, to attempt to update stale remote ARP cache entries. Note that this is not a guaranteed fix, since ARP is an unreliable protocol. We avoid sending gratuitous ARPs unconditionally, since otherwise any unrelated settings change (e.g. "set dns 192.168.0.1") would cause unexpected gratuitous ARPs to be sent. Signed-off-by: Michael Brown --- src/net/ipv4.c | 100 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 20 deletions(-) diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 8eb04a65f..865cfb60f 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -79,11 +79,11 @@ static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" }; * @v address IPv4 address * @v netmask Subnet mask * @v gateway Gateway address (if any) - * @ret miniroute Routing table entry, or NULL + * @ret rc Return status code */ -static struct ipv4_miniroute * __malloc -add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, - struct in_addr netmask, struct in_addr gateway ) { +static int add_ipv4_miniroute ( struct net_device *netdev, + struct in_addr address, struct in_addr netmask, + struct in_addr gateway ) { struct ipv4_miniroute *miniroute; DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) ); @@ -96,7 +96,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, miniroute = malloc ( sizeof ( *miniroute ) ); if ( ! miniroute ) { DBGC ( netdev, "IPv4 could not add miniroute\n" ); - return NULL; + return -ENOMEM; } /* Record routing information */ @@ -114,7 +114,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, list_add ( &miniroute->list, &ipv4_miniroutes ); } - return miniroute; + return 0; } /** @@ -812,33 +812,69 @@ const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = { }; /** - * Create IPv4 routing table based on configured settings + * Send gratuitous ARP, if applicable * + * @v netdev Network device + * @v address IPv4 address + * @v netmask Subnet mask + * @v gateway Gateway address (if any) * @ret rc Return status code */ -static int ipv4_create_routes ( void ) { - struct ipv4_miniroute *miniroute; - struct ipv4_miniroute *tmp; +static int ipv4_gratuitous_arp ( struct net_device *netdev, + struct in_addr address, + struct in_addr netmask __unused, + struct in_addr gateway __unused ) { + int rc; + + /* Do nothing if network device already has this IPv4 address */ + if ( ipv4_has_addr ( netdev, address ) ) + return 0; + + /* Transmit gratuitous ARP */ + DBGC ( netdev, "IPv4 sending gratuitous ARP for %s via %s\n", + inet_ntoa ( address ), netdev->name ); + if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, &address, + &address ) ) != 0 ) { + DBGC ( netdev, "IPv4 could not transmit gratuitous ARP: %s\n", + strerror ( rc ) ); + /* Treat failures as non-fatal */ + } + + return 0; +} + +/** + * Process IPv4 network device settings + * + * @v apply Application method + * @ret rc Return status code + */ +static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev, + struct in_addr address, + struct in_addr netmask, + struct in_addr gateway ) ) { struct net_device *netdev; struct settings *settings; struct in_addr address = { 0 }; struct in_addr netmask = { 0 }; struct in_addr gateway = { 0 }; + int rc; - /* Delete all existing routes */ - list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) - del_ipv4_miniroute ( miniroute ); - - /* Create a route for each configured network device */ + /* Process settings for each network device */ for_each_netdev ( netdev ) { + + /* Get network device settings */ settings = netdev_settings ( netdev ); + /* Get IPv4 address */ address.s_addr = 0; fetch_ipv4_setting ( settings, &ip_setting, &address ); if ( ! address.s_addr ) continue; + /* Get subnet mask */ fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); + /* Calculate default netmask, if necessary */ if ( ! netmask.s_addr ) { if ( IN_IS_CLASSA ( address.s_addr ) ) { @@ -849,18 +885,42 @@ static int ipv4_create_routes ( void ) { netmask.s_addr = INADDR_NET_CLASSC; } } + /* Get default gateway, if present */ fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); - /* Configure route */ - miniroute = add_ipv4_miniroute ( netdev, address, - netmask, gateway ); - if ( ! miniroute ) - return -ENOMEM; + + /* Apply settings */ + if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 ) + return rc; } return 0; } +/** + * Create IPv4 routing table based on configured settings + * + * @ret rc Return status code + */ +static int ipv4_create_routes ( void ) { + struct ipv4_miniroute *miniroute; + struct ipv4_miniroute *tmp; + int rc; + + /* Send gratuitous ARPs for any new IPv4 addresses */ + ipv4_settings ( ipv4_gratuitous_arp ); + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) + del_ipv4_miniroute ( miniroute ); + + /* Create a route for each configured network device */ + if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 ) + return rc; + + return 0; +} + /** IPv4 settings applicator */ struct settings_applicator ipv4_settings_applicator __settings_applicator = { .apply = ipv4_create_routes, From db3443608fe32fffb4f6ad467bfc035a824bff52 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 11 Jul 2016 17:14:14 +0100 Subject: [PATCH 274/591] [intel] Strip spurious VLAN tags received by virtual function NICs The physical function may be configured to transparently insert a VLAN tag into all transmitted packets. Unfortunately, it does not equivalently strip this same VLAN tag from all received packets. This behaviour may be observed in some Amazon EC2 instances with Enhanced Networking enabled: transmissions work as expected but all packets received by iPXE appear to have a spurious VLAN tag. We can configure the receive queue to strip VLAN tags via the RXDCTL.VME bit. We need to find out from the PF driver whether or not we should do so. There exists a "get queue configuration" mailbox message which contains a field labelled IXGBE_VF_TRANS_VLAN in the Linux driver. A comment in the Linux PF driver describes this field as "notify VF of need for VLAN tag stripping, and correct queue". It will be filled with a non-zero value if the PF is enforcing the use of a single VLAN tag. It will also be filled with a non-zero value if the PF is using multiple traffic classes. The Linux VF driver seems to treat this field as being simply the number of traffic classes, and gives it no VLAN-related interpretation. The Linux VF driver instead handles the VLAN tag stripping by simply assuming that any unrecognised VLAN tag ought to be silently dropped. We choose to strip and ignore the VLAN tag if the IXGBE_VF_TRANS_VLAN field has a non-zero value. Reported-by: Leonid Vasetsky Tested-by: Leonid Vasetsky Signed-off-by: Michael Brown --- src/drivers/net/intelvf.c | 41 +++++++++++++++++++++++++++ src/drivers/net/intelvf.h | 43 ++++++++++++++++++++++++++++ src/drivers/net/intelx.h | 3 ++ src/drivers/net/intelxvf.c | 57 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) diff --git a/src/drivers/net/intelvf.c b/src/drivers/net/intelvf.c index ac6fea745..537e0fbb1 100644 --- a/src/drivers/net/intelvf.c +++ b/src/drivers/net/intelvf.c @@ -338,3 +338,44 @@ int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) { return 0; } + +/** + * Get queue configuration + * + * @v intel Intel device + * @v vlan_thing VLAN hand-waving thing to fill in + * @ret rc Return status code + */ +int intelvf_mbox_queues ( struct intel_nic *intel, int *vlan_thing ) { + union intelvf_msg msg; + int rc; + + /* Send queue configuration message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_GET_QUEUES; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p get queue configuration failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) !=INTELVF_MSG_TYPE_GET_QUEUES){ + DBGC ( intel, "INTEL %p get queue configuration unexpected " + "response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to get the queue configuration */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p get queue configuration refused\n", + intel ); + return -EPERM; + } + + /* Extract VLAN hand-waving thing */ + *vlan_thing = msg.queues.vlan_thing; + + return 0; +} diff --git a/src/drivers/net/intelvf.h b/src/drivers/net/intelvf.h index d2f98d874..ab404698f 100644 --- a/src/drivers/net/intelvf.h +++ b/src/drivers/net/intelvf.h @@ -37,6 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Set MTU mailbox message */ #define INTELVF_MSG_TYPE_SET_MTU 0x00000005UL +/** Get queue configuration message */ +#define INTELVF_MSG_TYPE_GET_QUEUES 0x00000009UL + /** Control ("ping") mailbox message */ #define INTELVF_MSG_TYPE_CONTROL 0x00000100UL @@ -78,6 +81,44 @@ struct intelvf_msg_mtu { uint32_t mtu; } __attribute__ (( packed )); +/** Queue configuration mailbox message (API v1.1+ only) */ +struct intelvf_msg_queues { + /** Message header */ + uint32_t hdr; + /** Maximum number of transmit queues */ + uint32_t tx; + /** Maximum number of receive queues */ + uint32_t rx; + /** VLAN hand-waving thing + * + * This is labelled IXGBE_VF_TRANS_VLAN in the Linux driver. + * + * A comment in the Linux PF driver describes it as "notify VF + * of need for VLAN tag stripping, and correct queue". It + * will be filled with a non-zero value if the PF is enforcing + * the use of a single VLAN tag. It will also be filled with + * a non-zero value if the PF is using multiple traffic + * classes. + * + * The Linux VF driver seems to treat this field as being + * simply the number of traffic classes, and gives it no + * VLAN-related interpretation. + * + * If the PF is enforcing the use of a single VLAN tag for the + * VF, then the VLAN tag will be transparently inserted in + * transmitted packets (via the PFVMVIR register) but will + * still be visible in received packets. The Linux VF driver + * handles this unexpected VLAN tag by simply ignoring any + * unrecognised VLAN tags. + * + * We choose to strip and ignore the VLAN tag if this field + * has a non-zero value. + */ + uint32_t vlan_thing; + /** Default queue */ + uint32_t dflt; +} __attribute__ (( packed )); + /** Mailbox message */ union intelvf_msg { /** Message header */ @@ -88,6 +129,8 @@ union intelvf_msg { struct intelvf_msg_version version; /** MTU message */ struct intelvf_msg_mtu mtu; + /** Queue configuration message */ + struct intelvf_msg_queues queues; /** Raw dwords */ uint32_t dword[0]; }; diff --git a/src/drivers/net/intelx.h b/src/drivers/net/intelx.h index 6383dfcad..d7f3b78e8 100644 --- a/src/drivers/net/intelx.h +++ b/src/drivers/net/intelx.h @@ -71,6 +71,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Receive Descriptor register block */ #define INTELX_RD 0x01000UL +/** Receive Descriptor Control Register */ +#define INTELX_RXDCTL_VME 0x40000000UL /**< Strip VLAN tag */ + /** Split Receive Control Register */ #define INTELX_SRRCTL 0x02100UL #define INTELX_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */ diff --git a/src/drivers/net/intelxvf.c b/src/drivers/net/intelxvf.c index 05e34c127..91a10b10f 100644 --- a/src/drivers/net/intelxvf.c +++ b/src/drivers/net/intelxvf.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include "intelx.h" #include "intelxvf.h" /** @file @@ -156,6 +157,47 @@ static int intelxvf_mbox_version ( struct intel_nic *intel, return 0; } +/** + * Get queue configuration + * + * @v intel Intel device + * @v vlan_thing VLAN hand-waving thing to fill in + * @ret rc Return status code + */ +static int intelxvf_mbox_queues ( struct intel_nic *intel, int *vlan_thing ) { + union intelvf_msg msg; + int rc; + + /* Send queue configuration message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_GET_QUEUES; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p get queue configuration failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) !=INTELVF_MSG_TYPE_GET_QUEUES){ + DBGC ( intel, "INTEL %p get queue configuration unexpected " + "response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to get the queue configuration */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p get queue configuration refused\n", + intel ); + return -EPERM; + } + + /* Extract VLAN hand-waving thing */ + *vlan_thing = msg.queues.vlan_thing; + + return 0; +} + /****************************************************************************** * * Network device interface @@ -171,8 +213,10 @@ static int intelxvf_mbox_version ( struct intel_nic *intel, */ static int intelxvf_open ( struct net_device *netdev ) { struct intel_nic *intel = netdev->priv; + uint32_t rxdctl; uint32_t srrctl; uint32_t dca_rxctrl; + int vlan_thing; int rc; /* Reset the function */ @@ -208,6 +252,19 @@ static int intelxvf_open ( struct net_device *netdev ) { goto err_mbox_set_mtu; } + /* Get queue configuration. Ignore failures, since the host + * may not support this message. + */ + vlan_thing = 0; + intelxvf_mbox_queues ( intel, &vlan_thing ); + if ( vlan_thing ) { + DBGC ( intel, "INTEL %p stripping VLAN tags (thing=%d)\n", + intel, vlan_thing ); + rxdctl = readl ( intel->regs + INTELXVF_RD + INTEL_xDCTL ); + rxdctl |= INTELX_RXDCTL_VME; + writel ( rxdctl, intel->regs + INTELXVF_RD + INTEL_xDCTL ); + } + /* Create transmit descriptor ring */ if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 ) goto err_create_tx; From 517d2340317245d88d8261d9d28e38c32abab63b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 14 Jul 2016 13:51:07 +0100 Subject: [PATCH 275/591] [intel] Remove duplicate intelvf_mbox_queues() function Commit db34436 ("[intel] Strip spurious VLAN tags received by virtual function NICs") accidentally introduced two copies of the intel[x]vf_mbox_queues() function. Remove the unintended copy. Signed-off-by: Michael Brown --- src/drivers/net/intelvf.c | 41 --------------------------------------- 1 file changed, 41 deletions(-) diff --git a/src/drivers/net/intelvf.c b/src/drivers/net/intelvf.c index 537e0fbb1..ac6fea745 100644 --- a/src/drivers/net/intelvf.c +++ b/src/drivers/net/intelvf.c @@ -338,44 +338,3 @@ int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) { return 0; } - -/** - * Get queue configuration - * - * @v intel Intel device - * @v vlan_thing VLAN hand-waving thing to fill in - * @ret rc Return status code - */ -int intelvf_mbox_queues ( struct intel_nic *intel, int *vlan_thing ) { - union intelvf_msg msg; - int rc; - - /* Send queue configuration message */ - memset ( &msg, 0, sizeof ( msg ) ); - msg.hdr = INTELVF_MSG_TYPE_GET_QUEUES; - if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { - DBGC ( intel, "INTEL %p get queue configuration failed: %s\n", - intel, strerror ( rc ) ); - return rc; - } - - /* Check response */ - if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) !=INTELVF_MSG_TYPE_GET_QUEUES){ - DBGC ( intel, "INTEL %p get queue configuration unexpected " - "response:\n", intel ); - DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); - return -EPROTO; - } - - /* Check that we were allowed to get the queue configuration */ - if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { - DBGC ( intel, "INTEL %p get queue configuration refused\n", - intel ); - return -EPERM; - } - - /* Extract VLAN hand-waving thing */ - *vlan_thing = msg.queues.vlan_thing; - - return 0; -} From c53a209a4253a99a18fe3a9330fd85c9ce2b91a6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Jul 2016 15:49:24 +0100 Subject: [PATCH 276/591] [ipv6] Perform SLAAC only during autoconfiguration We currently perform IPv6 stateless address autoconfiguration (SLAAC) in response to any router advertisement with the relevant flags set. This can result in the local IPv6 source address changing midway through a TCP connection, since our connections bind only to a local port number and do not store a local network address. In addition, this behaviour for SLAAC is inconsistent with that for DHCPv4 and stateful DHCPv6, both of which will be performed only as a result of an explicit autoconfiguration action (e.g. via the default autoboot sequence, or the "ifconf" command). Fix by ignoring router advertisements arriving outside the context of an ongoing autoconfiguration attempt. Signed-off-by: Michael Brown --- src/net/ndp.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/net/ndp.c b/src/net/ndp.c index e62f7d5cb..0a68ec783 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ); static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, struct ndp_router_advertisement_header *radv, @@ -341,6 +342,7 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, struct ndp_prefix_information_option *prefix_opt = &option->prefix; struct in6_addr *router = &sin6_src->sin6_addr; struct in6_addr address; + struct ipv6conf *ipv6conf; int prefix_len; int rc; @@ -350,14 +352,21 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, "short at %zd bytes\n", netdev->name, len ); return -EINVAL; } + + /* Identify IPv6 configurator, if any */ + ipv6conf = ipv6conf_demux ( netdev ); DBGC ( netdev, "NDP %s found %sdefault router %s ", netdev->name, ( radv->lifetime ? "" : "non-" ), inet6_ntoa ( &sin6_src->sin6_addr ) ); - DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n", + DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d%s\n", ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ), ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ), inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len ); + prefix_opt->prefix_len, ( ipv6conf ? "" : " (ignored)" ) ); + + /* Do nothing unless IPv6 autoconfiguration is in progress */ + if ( ! ipv6conf ) + return 0; /* Ignore off-link prefixes */ if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ) @@ -915,13 +924,10 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev, /* Identify IPv6 configurator, if any */ ipv6conf = ipv6conf_demux ( netdev ); - if ( ! ipv6conf ) { - /* Not an error; router advertisements are processed - * as a background activity even when no explicit - * autoconfiguration is taking place. - */ + + /* Do nothing unless IPv6 autoconfiguration is in progress */ + if ( ! ipv6conf ) return 0; - } /* If this is not the first solicited router advertisement, ignore it */ if ( ! timer_running ( &ipv6conf->timer ) ) From ecfc81d76f584a9e5878d940b376d1a0e7a8aa66 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Jul 2016 16:52:47 +0100 Subject: [PATCH 277/591] [settings] Create space for IPv6 in settings display order Signed-off-by: Michael Brown --- src/include/ipxe/settings.h | 36 ++++++++++++++++++++---------------- src/net/ipv4.c | 6 +++--- src/net/ndp.c | 2 +- src/net/udp/dns.c | 4 ++-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 6534c25b6..29853edc4 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -62,18 +62,22 @@ struct setting { #define SETTING_NETDEV 01 /**< Network device settings */ #define SETTING_NETDEV_EXTRA 02 /**< Network device additional settings */ -#define SETTING_IP 03 /**< IPv4 settings */ -#define SETTING_IP_EXTRA 04 /**< IPv4 additional settings */ -#define SETTING_BOOT 05 /**< Generic boot settings */ -#define SETTING_BOOT_EXTRA 06 /**< Generic boot additional settings */ -#define SETTING_SANBOOT 07 /**< SAN boot settings */ -#define SETTING_SANBOOT_EXTRA 08 /**< SAN boot additional settings */ -#define SETTING_HOST 09 /**< Host identity settings */ -#define SETTING_HOST_EXTRA 10 /**< Host identity additional settings */ -#define SETTING_AUTH 11 /**< Authentication settings */ -#define SETTING_AUTH_EXTRA 12 /**< Authentication additional settings */ -#define SETTING_CRYPTO 13 /**< Cryptography settings */ -#define SETTING_MISC 14 /**< Miscellaneous settings */ +#define SETTING_IP4 03 /**< IPv4 settings */ +#define SETTING_IP4_EXTRA 04 /**< IPv4 additional settings */ +#define SETTING_IP6 05 /**< IPv6 settings */ +#define SETTING_IP6_EXTRA 06 /**< IPv6 additional settings */ +#define SETTING_IP 07 /**< IPv4 settings */ +#define SETTING_IP_EXTRA 08 /**< IPv4 additional settings */ +#define SETTING_BOOT 09 /**< Generic boot settings */ +#define SETTING_BOOT_EXTRA 10 /**< Generic boot additional settings */ +#define SETTING_SANBOOT 11 /**< SAN boot settings */ +#define SETTING_SANBOOT_EXTRA 12 /**< SAN boot additional settings */ +#define SETTING_HOST 13 /**< Host identity settings */ +#define SETTING_HOST_EXTRA 14 /**< Host identity additional settings */ +#define SETTING_AUTH 15 /**< Authentication settings */ +#define SETTING_AUTH_EXTRA 16 /**< Authentication additional settings */ +#define SETTING_CRYPTO 17 /**< Cryptography settings */ +#define SETTING_MISC 18 /**< Miscellaneous settings */ /** @} */ @@ -421,13 +425,13 @@ extern const struct setting_type setting_type_busdevfn __setting_type; extern const struct setting_type setting_type_dnssl __setting_type; extern const struct setting -ip_setting __setting ( SETTING_IP, ip ); +ip_setting __setting ( SETTING_IP4, ip ); extern const struct setting -netmask_setting __setting ( SETTING_IP, netmask ); +netmask_setting __setting ( SETTING_IP4, netmask ); extern const struct setting -gateway_setting __setting ( SETTING_IP, gateway ); +gateway_setting __setting ( SETTING_IP4, gateway ); extern const struct setting -dns_setting __setting ( SETTING_IP_EXTRA, dns ); +dns_setting __setting ( SETTING_IP4_EXTRA, dns ); extern const struct setting hostname_setting __setting ( SETTING_HOST, hostname ); extern const struct setting diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 865cfb60f..b4148d8e0 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -788,7 +788,7 @@ int format_ipv4_setting ( const struct setting_type *type __unused, } /** IPv4 address setting */ -const struct setting ip_setting __setting ( SETTING_IP, ip ) = { +const struct setting ip_setting __setting ( SETTING_IP4, ip ) = { .name = "ip", .description = "IP address", .tag = DHCP_EB_YIADDR, @@ -796,7 +796,7 @@ const struct setting ip_setting __setting ( SETTING_IP, ip ) = { }; /** IPv4 subnet mask setting */ -const struct setting netmask_setting __setting ( SETTING_IP, netmask ) = { +const struct setting netmask_setting __setting ( SETTING_IP4, netmask ) = { .name = "netmask", .description = "Subnet mask", .tag = DHCP_SUBNET_MASK, @@ -804,7 +804,7 @@ const struct setting netmask_setting __setting ( SETTING_IP, netmask ) = { }; /** Default gateway setting */ -const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = { +const struct setting gateway_setting __setting ( SETTING_IP4, gateway ) = { .name = "gateway", .description = "Default gateway", .tag = DHCP_ROUTERS, diff --git a/src/net/ndp.c b/src/net/ndp.c index 0a68ec783..fb6f32319 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -785,7 +785,7 @@ static int ndp_register_settings ( struct net_device *netdev, } /** DNS server setting */ -const struct setting ndp_dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { +const struct setting ndp_dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .name = "dns6", .description = "DNS server", .tag = NDP_TAG ( NDP_OPT_RDNSS, diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index 2d77477f6..5a9836cb2 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -1048,7 +1048,7 @@ const struct setting_type setting_type_dnssl __setting_type = { }; /** IPv4 DNS server setting */ -const struct setting dns_setting __setting ( SETTING_IP_EXTRA, dns ) = { +const struct setting dns_setting __setting ( SETTING_IP4_EXTRA, dns ) = { .name = "dns", .description = "DNS server", .tag = DHCP_DNS_SERVERS, @@ -1056,7 +1056,7 @@ const struct setting dns_setting __setting ( SETTING_IP_EXTRA, dns ) = { }; /** IPv6 DNS server setting */ -const struct setting dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { +const struct setting dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .name = "dns6", .description = "DNS server", .tag = DHCPV6_DNS_SERVERS, From 129206f476262b8b071135d0a1e020811472dc78 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 16 Jul 2016 12:42:08 +0100 Subject: [PATCH 278/591] [ipv6] Rename ipv6_scope to dhcpv6_scope The settings scope ipv6_scope refers specifically to IPv6 settings that have a corresponding DHCPv6 option. Rename to dhcpv6_scope to more accurately reflect this purpose. Signed-off-by: Michael Brown --- src/core/settings.c | 2 +- src/include/ipxe/settings.h | 2 +- src/net/udp/dhcpv6.c | 8 ++++---- src/net/udp/dns.c | 2 +- src/net/udp/syslog.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 9cae0cae3..1361a10ed 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1784,7 +1784,7 @@ const struct setting_type setting_type_ipv6 __setting_type = { }; /** IPv6 settings scope */ -const struct settings_scope ipv6_scope; +const struct settings_scope dhcpv6_scope; /** * Integer setting type indices diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 29853edc4..64ffe655b 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -284,7 +284,7 @@ struct builtin_setting { extern const struct settings_scope builtin_scope; /** IPv6 setting scope */ -extern const struct settings_scope ipv6_scope; +extern const struct settings_scope dhcpv6_scope; /** * A generic settings block diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index be1ed4ec0..f57ea7b8a 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -280,7 +280,7 @@ struct dhcpv6_settings { static int dhcpv6_applies ( struct settings *settings __unused, const struct setting *setting ) { - return ( setting->scope == &ipv6_scope ); + return ( setting->scope == &dhcpv6_scope ); } /** @@ -341,7 +341,7 @@ static int dhcpv6_register ( struct dhcpv6_option_list *options, } ref_init ( &dhcpv6set->refcnt, NULL ); settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations, - &dhcpv6set->refcnt, &ipv6_scope ); + &dhcpv6set->refcnt, &dhcpv6_scope ); data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) ); len = options->len; memcpy ( data, options->data, len ); @@ -987,7 +987,7 @@ const struct setting filename6_setting __setting ( SETTING_BOOT, filename ) = { .description = "Boot filename", .tag = DHCPV6_BOOTFILE_URL, .type = &setting_type_string, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; /** DNS search list setting */ @@ -996,5 +996,5 @@ const struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { .description = "DNS search list", .tag = DHCPV6_DOMAIN_LIST, .type = &setting_type_dnssl, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index 5a9836cb2..e849472de 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -1061,7 +1061,7 @@ const struct setting dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .description = "DNS server", .tag = DHCPV6_DNS_SERVERS, .type = &setting_type_ipv6, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; /** DNS search list */ diff --git a/src/net/udp/syslog.c b/src/net/udp/syslog.c index b6eee6036..a45fc459d 100644 --- a/src/net/udp/syslog.c +++ b/src/net/udp/syslog.c @@ -213,7 +213,7 @@ const struct setting syslog6_setting __setting ( SETTING_MISC, syslog6 ) = { .description = "Syslog server", .tag = DHCPV6_LOG_SERVERS, .type = &setting_type_ipv6, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; /** From 0ac874242bfe6d623a9fcff13852fa995a379b8d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 18 Jul 2016 23:52:40 +0100 Subject: [PATCH 279/591] [settings] Correctly mortalise autovivified child settings blocks Signed-off-by: Michael Brown --- src/core/settings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/settings.c b/src/core/settings.c index 1361a10ed..42bb5e53f 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -331,6 +331,7 @@ struct settings * autovivify_child_settings ( struct settings *parent, &new_child->autovivified.refcnt ); settings = &new_child->autovivified.generic.settings; register_settings ( settings, parent, new_child->name ); + ref_put ( settings->refcnt ); return settings; } From ee54ab5be6c46adae18fc215b66d629796328b04 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 18 Jul 2016 14:37:04 +0100 Subject: [PATCH 280/591] [ipv6] Allow settings to comprise arbitrary subsets of NDP options Signed-off-by: Michael Brown --- src/net/ndp.c | 98 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 33 deletions(-) diff --git a/src/net/ndp.c b/src/net/ndp.c index fb6f32319..0876efd94 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -636,7 +636,7 @@ struct ndp_settings { /** Length of NDP options */ size_t len; /** NDP options */ - union ndp_option option[0]; + union ndp_option options[0]; }; /** NDP settings scope */ @@ -647,9 +647,11 @@ static const struct settings_scope ndp_settings_scope; * * @v type NDP option type * @v offset Starting offset of data + * @v len Length of data (or 0 to use all remaining data) * @ret tag NDP tag */ -#define NDP_TAG( type, offset ) ( ( (offset) << 8 ) | (type) ) +#define NDP_TAG( type, offset, len ) \ + ( ( (len) << 16 ) | ( (offset) << 8 ) | (type) ) /** * Extract NDP tag type @@ -657,7 +659,7 @@ static const struct settings_scope ndp_settings_scope; * @v tag NDP tag * @ret type NDP option type */ -#define NDP_TAG_TYPE( tag ) ( (tag) & 0xff ) +#define NDP_TAG_TYPE( tag ) ( ( (tag) >> 0 ) & 0xff ) /** * Extract NDP tag offset @@ -665,7 +667,23 @@ static const struct settings_scope ndp_settings_scope; * @v tag NDP tag * @ret offset Starting offset of data */ -#define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 ) +#define NDP_TAG_OFFSET( tag ) ( ( (tag) >> 8 ) & 0xff ) + +/** + * Extract NDP tag length + * + * @v tag NDP tag + * @ret len Length of data (or 0 to use all remaining data) + */ +#define NDP_TAG_LEN( tag ) ( ( (tag) >> 16 ) & 0xff ) + +/** + * Extract NDP tag instance + * + * @v tag NDP tag + * @ret instance Instance + */ +#define NDP_TAG_INSTANCE( tag ) ( ( (tag) >> 24 ) & 0xff ) /** * Check applicability of NDP setting @@ -698,44 +716,58 @@ static int ndp_fetch ( struct settings *settings, container_of ( settings->parent, struct net_device, settings.settings ); union ndp_option *option; - unsigned int type = NDP_TAG_TYPE ( setting->tag ); - unsigned int offset = NDP_TAG_OFFSET ( setting->tag ); - size_t remaining; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + unsigned int tag_instance; + size_t offset; size_t option_len; - size_t payload_len; + void *option_data; + + /* Parse setting tag */ + tag_type = NDP_TAG_TYPE ( setting->tag ); + tag_offset = NDP_TAG_OFFSET ( setting->tag ); + tag_len = NDP_TAG_LEN ( setting->tag ); + tag_instance = NDP_TAG_INSTANCE ( setting->tag ); /* Scan through NDP options for requested type. We can assume * that the options are well-formed, otherwise they would have * been rejected prior to being stored. */ - option = ndpset->option; - remaining = ndpset->len; - while ( remaining ) { + for ( offset = 0 ; offset < ndpset->len ; offset += option_len ) { /* Calculate option length */ + option = ( ( ( void * ) ndpset->options ) + offset ); option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - /* If this is the requested option, return it */ - if ( option->header.type == type ) { + /* Skip options that do not match this tag */ + if ( option->header.type != tag_type ) + continue; - /* Sanity check */ - if ( offset > option_len ) { - DBGC ( netdev, "NDP %s option %d too short\n", - netdev->name, type ); - return -EINVAL; - } - payload_len = ( option_len - offset ); + /* Skip previous instances of this option */ + if ( tag_instance-- != 0 ) + continue; - /* Copy data to output buffer */ - if ( len > payload_len ) - len = payload_len; - memcpy ( data, ( ( ( void * ) option ) + offset ), len); - return payload_len; + /* Sanity check */ + if ( ( tag_offset + tag_len ) > option_len ) { + DBGC ( netdev, "NDP %s option %d too short\n", + netdev->name, tag_type ); + return -EINVAL; } + if ( ! tag_len ) + tag_len = ( option_len - tag_offset ); + option_data = ( ( ( void * ) option ) + tag_offset ); - /* Move to next option */ - option = ( ( ( void * ) option ) + option_len ); - remaining -= option_len; + /* Copy data to output buffer */ + if ( len > tag_len ) + len = tag_len; + memcpy ( data, option_data, len ); + + /* Default to hex if no type is specified */ + if ( ! setting->type ) + setting->type = &setting_type_hex; + + return tag_len; } return -ENOENT; @@ -751,12 +783,12 @@ static struct settings_operations ndp_settings_operations = { * Register NDP settings * * @v netdev Network device - * @v option NDP options + * @v options NDP options * @v len Length of options * @ret rc Return status code */ static int ndp_register_settings ( struct net_device *netdev, - union ndp_option *option, size_t len ) { + union ndp_option *options, size_t len ) { struct settings *parent = netdev_settings ( netdev ); struct ndp_settings *ndpset; int rc; @@ -771,7 +803,7 @@ static int ndp_register_settings ( struct net_device *netdev, settings_init ( &ndpset->settings, &ndp_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); ndpset->len = len; - memcpy ( ndpset->option, option, len ); + memcpy ( ndpset->options, options, len ); /* Register settings */ if ( ( rc = register_settings ( &ndpset->settings, parent, @@ -789,7 +821,7 @@ const struct setting ndp_dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .name = "dns6", .description = "DNS server", .tag = NDP_TAG ( NDP_OPT_RDNSS, - offsetof ( struct ndp_rdnss_option, addresses ) ), + offsetof ( struct ndp_rdnss_option, addresses ), 0 ), .type = &setting_type_ipv6, .scope = &ndp_settings_scope, }; @@ -799,7 +831,7 @@ const struct setting ndp_dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { .name = "dnssl", .description = "DNS search list", .tag = NDP_TAG ( NDP_OPT_DNSSL, - offsetof ( struct ndp_dnssl_option, names ) ), + offsetof ( struct ndp_dnssl_option, names ), 0 ), .type = &setting_type_dnssl, .scope = &ndp_settings_scope, }; From 3b783d7fd2be053438c4fa968359f01b3c7ece8b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 18 Jul 2016 15:13:10 +0100 Subject: [PATCH 281/591] [ipv6] Expose IPv6 settings acquired through NDP Expose the IPv6 address (or prefix) as ${ip6}, the prefix length as ${len6}, and the router address as ${gateway6}. Originally-implemented-by: Hannes Reinecke Originally-implemented-by: Marin Hannache Signed-off-by: Michael Brown --- src/include/ipxe/settings.h | 9 ++ src/net/ipv6.c | 27 ++++ src/net/ndp.c | 251 +++++++++++++++++++++++++++++++++++- 3 files changed, 282 insertions(+), 5 deletions(-) diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 64ffe655b..07ebaa620 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -284,6 +284,9 @@ struct builtin_setting { extern const struct settings_scope builtin_scope; /** IPv6 setting scope */ +extern const struct settings_scope ipv6_scope; + +/** DHCPv6 setting scope */ extern const struct settings_scope dhcpv6_scope; /** @@ -433,6 +436,12 @@ gateway_setting __setting ( SETTING_IP4, gateway ); extern const struct setting dns_setting __setting ( SETTING_IP4_EXTRA, dns ); extern const struct setting +ip6_setting __setting ( SETTING_IP6, ip6 ); +extern const struct setting +len6_setting __setting ( SETTING_IP6, len6 ); +extern const struct setting +gateway6_setting __setting ( SETTING_IP6, gateway6 ); +extern const struct setting hostname_setting __setting ( SETTING_HOST, hostname ); extern const struct setting domain_setting __setting ( SETTING_IP_EXTRA, domain ); diff --git a/src/net/ipv6.c b/src/net/ipv6.c index bbc00d33e..78d61369f 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1061,6 +1061,33 @@ int format_ipv6_setting ( const struct setting_type *type __unused, return snprintf ( buf, len, "%s", inet6_ntoa ( ipv6 ) ); } +/** IPv6 settings scope */ +const struct settings_scope ipv6_scope; + +/** IPv6 address setting */ +const struct setting ip6_setting __setting ( SETTING_IP6, ip6 ) = { + .name = "ip6", + .description = "IPv6 address", + .type = &setting_type_ipv6, + .scope = &ipv6_scope, +}; + +/** IPv6 prefix length setting */ +const struct setting len6_setting __setting ( SETTING_IP6, len6 ) = { + .name = "len6", + .description = "IPv6 prefix length", + .type = &setting_type_int8, + .scope = &ipv6_scope, +}; + +/** Default gateway setting */ +const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = { + .name = "gateway6", + .description = "IPv6 gateway", + .type = &setting_type_ipv6, + .scope = &ipv6_scope, +}; + /** * Create IPv6 network device * diff --git a/src/net/ndp.c b/src/net/ndp.c index 0876efd94..c488acc73 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include @@ -41,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ); static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, + struct in6_addr *router, struct ndp_router_advertisement_header *radv, size_t len ); @@ -585,6 +587,7 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, struct sockaddr_in6 *sin6_dest __unused ) { union ndp_header *ndp = iobuf->data; struct ndp_router_advertisement_header *radv = &ndp->radv; + struct in6_addr *router = &sin6_src->sin6_addr; size_t len = iob_len ( iobuf ); int rc; @@ -595,8 +598,8 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, goto err_options; /* Pass to IPv6 autoconfiguration */ - if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv, - len ) ) != 0 ) + if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, router, + radv, len ) ) != 0 ) goto err_ipv6conf; err_ipv6conf: @@ -627,12 +630,26 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = { * */ +/** An NDP prefix settings block */ +struct ndp_prefix_settings { + /** Settings interface */ + struct settings settings; + /** Name */ + char name[4]; + /** Prefix information option */ + struct ndp_prefix_information_option *prefix; +}; + /** An NDP settings block */ struct ndp_settings { /** Reference counter */ struct refcnt refcnt; /** Settings interface */ struct settings settings; + /** Router address */ + struct in6_addr router; + /** Router lifetime */ + unsigned int lifetime; /** Length of NDP options */ size_t len; /** NDP options */ @@ -779,22 +796,207 @@ static struct settings_operations ndp_settings_operations = { .fetch = ndp_fetch, }; +/** + * Check applicability of NDP per-prefix setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @ret applies Setting applies within this settings block + */ +static int ndp_prefix_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &ipv6_scope ); +} + +/** + * Fetch value of NDP IPv6 address setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data, + size_t len ) { + struct ndp_prefix_settings *prefset = + container_of ( settings, struct ndp_prefix_settings, settings ); + struct ndp_settings *ndpset = + container_of ( settings->parent, struct ndp_settings, settings); + struct net_device *netdev = + container_of ( ndpset->settings.parent, struct net_device, + settings.settings ); + struct ndp_prefix_information_option *prefix = prefset->prefix; + struct in6_addr ip6; + int prefix_len; + + /* Skip dead prefixes */ + if ( ! prefix->valid ) + return -ENOENT; + + /* Construct IPv6 address via SLAAC, if applicable */ + memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) ); + if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) { + prefix_len = ipv6_eui64 ( &ip6, netdev ); + if ( prefix_len < 0 ) + return prefix_len; + if ( prefix_len != prefix->prefix_len ) + return -EINVAL; + } + + /* Fill in IPv6 address */ + if ( len > sizeof ( ip6 ) ) + len = sizeof ( ip6 ); + memcpy ( data, &ip6, len ); + + return sizeof ( ip6 ); +} + +/** + * Fetch value of NDP prefix length setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_len6 ( struct settings *settings, void *data, + size_t len ) { + struct ndp_prefix_settings *prefset = + container_of ( settings, struct ndp_prefix_settings, settings ); + struct ndp_prefix_information_option *prefix = prefset->prefix; + uint8_t *len6; + + /* Fill in prefix length */ + if ( len >= sizeof ( *len6 ) ) { + /* We treat an off-link prefix as having a prefix + * length covering the entire IPv6 address. + */ + len6 = data; + *len6 = ( ( prefix->flags & NDP_PREFIX_ON_LINK ) ? + prefix->prefix_len : -1UL ); + } + + return sizeof ( *len6 ); +} + +/** + * Fetch value of NDP router address setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_gateway6 ( struct settings *settings, + void *data, size_t len ) { + struct ndp_settings *ndpset = + container_of ( settings->parent, struct ndp_settings, settings); + + /* Treat non-routing router as non-existent */ + if ( ! ndpset->lifetime ) + return -ENOENT; + + /* Fill in router address */ + if ( len > sizeof ( ndpset->router ) ) + len = sizeof ( ndpset->router ); + memcpy ( data, &ndpset->router, len ); + + return sizeof ( ndpset->router ); +} + +/** An NDP per-prefix setting operation */ +struct ndp_prefix_operation { + /** Generic setting */ + const struct setting *setting; + /** + * Fetch value of setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ + int ( * fetch ) ( struct settings *settings, void *data, size_t len ); +}; + +/** NDP per-prefix settings operations */ +static struct ndp_prefix_operation ndp_prefix_operations[] = { + { &ip6_setting, ndp_prefix_fetch_ip6 }, + { &len6_setting, ndp_prefix_fetch_len6 }, + { &gateway6_setting, ndp_prefix_fetch_gateway6 }, +}; + +/** + * Fetch value of NDP pre-prefix setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct ndp_prefix_operation *op; + unsigned int i; + + /* Handle per-prefix settings */ + for ( i = 0 ; i < ( sizeof ( ndp_prefix_operations ) / + sizeof ( ndp_prefix_operations[0] ) ) ; i++ ) { + op = &ndp_prefix_operations[i]; + if ( setting_cmp ( setting, op->setting ) == 0 ) + return op->fetch ( settings, data, len ); + } + + return -ENOENT; +} + +/** NDP per-prefix settings operations */ +static struct settings_operations ndp_prefix_settings_operations = { + .applies = ndp_prefix_applies, + .fetch = ndp_prefix_fetch, +}; + /** * Register NDP settings * * @v netdev Network device + * @v router Router address + * @v lifetime Router lifetime * @v options NDP options * @v len Length of options * @ret rc Return status code */ static int ndp_register_settings ( struct net_device *netdev, + struct in6_addr *router, + unsigned int lifetime, union ndp_option *options, size_t len ) { struct settings *parent = netdev_settings ( netdev ); + union ndp_option *option; struct ndp_settings *ndpset; + struct ndp_prefix_settings *prefset; + size_t offset; + size_t option_len; + unsigned int prefixes; + unsigned int instance; int rc; + /* Count number of prefix options. We can assume that the + * options are well-formed, otherwise they would have been + * rejected prior to being stored. + */ + for ( prefixes = 0, offset = 0 ; offset < len ; offset += option_len ) { + option = ( ( ( void * ) options ) + offset ); + option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); + if ( option->header.type == NDP_OPT_PREFIX ) + prefixes++; + } + /* Allocate and initialise structure */ - ndpset = zalloc ( sizeof ( *ndpset ) + len ); + ndpset = zalloc ( sizeof ( *ndpset ) + len + + ( prefixes * sizeof ( *prefset ) ) ); if ( ! ndpset ) { rc = -ENOMEM; goto err_alloc; @@ -802,14 +1004,50 @@ static int ndp_register_settings ( struct net_device *netdev, ref_init ( &ndpset->refcnt, NULL ); settings_init ( &ndpset->settings, &ndp_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); + memcpy ( &ndpset->router, router, sizeof ( ndpset->router ) ); + ndpset->lifetime = lifetime; ndpset->len = len; memcpy ( ndpset->options, options, len ); + prefset = ( ( ( void * ) ndpset->options ) + len ); /* Register settings */ if ( ( rc = register_settings ( &ndpset->settings, parent, NDP_SETTINGS_NAME ) ) != 0 ) goto err_register; + /* Construct and register per-prefix settings */ + for ( instance = 0, offset = 0 ; offset < len ; offset += option_len ) { + + /* Skip non-prefix options */ + option = ( ( ( void * ) ndpset->options ) + offset ); + option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); + if ( option->header.type != NDP_OPT_PREFIX ) + continue; + + /* Initialise structure */ + settings_init ( &prefset->settings, + &ndp_prefix_settings_operations, + &ndpset->refcnt, &ndp_settings_scope ); + prefset->prefix = &option->prefix; + snprintf ( prefset->name, sizeof ( prefset->name ), "%d", + instance++ ); + + /* Register settings */ + if ( ( rc = register_settings ( &prefset->settings, + &ndpset->settings, + prefset->name ) ) != 0 ) + goto err_register_prefix; + + /* Move to next per-prefix settings */ + prefset++; + } + assert ( instance == prefixes ); + + ref_put ( &ndpset->refcnt ); + return 0; + + err_register_prefix: + unregister_settings ( &ndpset->settings ); err_register: ref_put ( &ndpset->refcnt ); err_alloc: @@ -938,6 +1176,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { * Handle router advertisement during IPv6 autoconfiguration * * @v netdev Network device + * @v router Router address * @v radv Router advertisement * @v len Length of router advertisement * @ret rc Return status code @@ -947,6 +1186,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { */ static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, + struct in6_addr *router, struct ndp_router_advertisement_header *radv, size_t len ) { struct ipv6conf *ipv6conf; @@ -970,8 +1210,9 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev, /* Register NDP settings */ option_len = ( len - offsetof ( typeof ( *radv ), option ) ); - if ( ( rc = ndp_register_settings ( netdev, radv->option, - option_len ) ) != 0 ) + if ( ( rc = ndp_register_settings ( netdev, router, + ntohl ( radv->lifetime ), + radv->option, option_len ) ) != 0 ) return rc; /* Start DHCPv6 if required */ From 03d19cf14d19a80aee60126f1b0099c8f201aca7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 01:18:30 +0100 Subject: [PATCH 282/591] [dhcpv6] Expose IPv6 address setting acquired through DHCPv6 Originally-implemented-by: Hannes Reinecke Originally-implemented-by: Marin Hannache Signed-off-by: Michael Brown --- src/net/udp/dhcpv6.c | 69 ++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index f57ea7b8a..ac7010422 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -266,6 +266,8 @@ struct dhcpv6_settings { struct refcnt refcnt; /** Settings block */ struct settings settings; + /** Leased address */ + struct in6_addr lease; /** Option list */ struct dhcpv6_option_list options; }; @@ -280,7 +282,32 @@ struct dhcpv6_settings { static int dhcpv6_applies ( struct settings *settings __unused, const struct setting *setting ) { - return ( setting->scope == &dhcpv6_scope ); + return ( ( setting->scope == &dhcpv6_scope ) || + ( setting_cmp ( setting, &ip6_setting ) == 0 ) ); +} + +/** + * Fetch value of DHCPv6 leased address + * + * @v dhcpset DHCPv6 settings + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int dhcpv6_fetch_lease ( struct dhcpv6_settings *dhcpv6set, + void *data, size_t len ) { + struct in6_addr *lease = &dhcpv6set->lease; + + /* Do nothing unless a leased address exists */ + if ( IN6_IS_ADDR_UNSPECIFIED ( lease ) ) + return -ENOENT; + + /* Copy leased address */ + if ( len > sizeof ( *lease ) ) + len = sizeof ( *lease ); + memcpy ( data, lease, len ); + + return sizeof ( *lease ); } /** @@ -300,6 +327,10 @@ static int dhcpv6_fetch ( struct settings *settings, const union dhcpv6_any_option *option; size_t option_len; + /* Handle leased address */ + if ( setting_cmp ( setting, &ip6_setting ) == 0 ) + return dhcpv6_fetch_lease ( dhcpv6set, data, len ); + /* Find option */ option = dhcpv6_option ( &dhcpv6set->options, setting->tag ); if ( ! option ) @@ -322,11 +353,13 @@ static struct settings_operations dhcpv6_settings_operations = { /** * Register DHCPv6 options as network device settings * + * @v lease DHCPv6 leased address * @v options DHCPv6 option list * @v parent Parent settings block * @ret rc Return status code */ -static int dhcpv6_register ( struct dhcpv6_option_list *options, +static int dhcpv6_register ( struct in6_addr *lease, + struct dhcpv6_option_list *options, struct settings *parent ) { struct dhcpv6_settings *dhcpv6set; void *data; @@ -347,6 +380,7 @@ static int dhcpv6_register ( struct dhcpv6_option_list *options, memcpy ( data, options->data, len ); dhcpv6set->options.data = data; dhcpv6set->options.len = len; + memcpy ( &dhcpv6set->lease, lease, sizeof ( dhcpv6set->lease ) ); /* Register settings */ if ( ( rc = register_settings ( &dhcpv6set->settings, parent, @@ -848,28 +882,25 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6, } } - /* Transition to next state or complete DHCPv6, as applicable */ + /* Transition to next state, if applicable */ if ( dhcpv6->state->next ) { - - /* Transition to next state */ dhcpv6_set_state ( dhcpv6, dhcpv6->state->next ); rc = 0; - - } else { - - /* Register settings */ - if ( ( rc = dhcpv6_register ( &options, parent ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not register " - "settings: %s\n", dhcpv6->netdev->name, - strerror ( rc ) ); - goto done; - } - - /* Mark as complete */ - dhcpv6_finished ( dhcpv6, 0 ); - DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name ); + goto done; } + /* Register settings */ + if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &options, + parent ) ) != 0 ) { + DBGC ( dhcpv6, "DHCPv6 %s could not register settings: %s\n", + dhcpv6->netdev->name, strerror ( rc ) ); + goto done; + } + + /* Mark as complete */ + dhcpv6_finished ( dhcpv6, 0 ); + DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name ); + done: free_iob ( iobuf ); return rc; From 1fdc7da4358bda233b0f1a9cb8cd27c92830f6b9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 14:16:51 +0100 Subject: [PATCH 283/591] [ipv6] Expose IPv6 link-local address settings Originally-implemented-by: Hannes Reinecke Originally-implemented-by: Marin Hannache Signed-off-by: Michael Brown --- src/include/ipxe/ipv6.h | 3 ++ src/net/ipv6.c | 113 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index b500382c1..86c8ff924 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -238,6 +238,9 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) { addr->s6_addr[15] = 2; } +/** IPv6 link-local address settings block name */ +#define IPV6_SETTINGS_NAME "link" + extern struct list_head ipv6_miniroutes; extern struct net_protocol ipv6_protocol __net_protocol; diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 78d61369f..cdd6640c7 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1088,6 +1088,115 @@ const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = { .scope = &ipv6_scope, }; +/** + * Check applicability of IPv6 link-local address setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @ret applies Setting applies within this settings block + */ +static int ipv6_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &ipv6_scope ); +} + +/** + * Fetch IPv6 link-local address setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ipv6_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct net_device *netdev = + container_of ( settings->parent, struct net_device, + settings.settings ); + struct in6_addr ip6; + uint8_t *len6; + int prefix_len; + int rc; + + /* Construct link-local address from EUI-64 as per RFC 2464 */ + memset ( &ip6, 0, sizeof ( ip6 ) ); + prefix_len = ipv6_link_local ( &ip6, netdev ); + if ( prefix_len < 0 ) { + rc = prefix_len; + return rc; + } + + /* Handle setting */ + if ( setting_cmp ( setting, &ip6_setting ) == 0 ) { + + /* Return link-local ip6 */ + if ( len > sizeof ( ip6 ) ) + len = sizeof ( ip6 ); + memcpy ( data, &ip6, len ); + return sizeof ( ip6 ); + + } else if ( setting_cmp ( setting, &len6_setting ) == 0 ) { + + /* Return prefix length */ + if ( len ) { + len6 = data; + *len6 = prefix_len; + } + return sizeof ( *len6 ); + + } + + return -ENOENT; +} + +/** IPv6 link-local address settings operations */ +static struct settings_operations ipv6_settings_operations = { + .applies = ipv6_applies, + .fetch = ipv6_fetch, +}; + +/** IPv6 link-local address settings */ +struct ipv6_settings { + /** Reference counter */ + struct refcnt refcnt; + /** Settings interface */ + struct settings settings; +}; + +/** + * Register IPv6 link-local address settings + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ipv6_register_settings ( struct net_device *netdev ) { + struct settings *parent = netdev_settings ( netdev ); + struct ipv6_settings *ipv6set; + int rc; + + /* Allocate and initialise structure */ + ipv6set = zalloc ( sizeof ( *ipv6set ) ); + if ( ! ipv6set ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &ipv6set->refcnt, NULL ); + settings_init ( &ipv6set->settings, &ipv6_settings_operations, + &ipv6set->refcnt, &ipv6_scope ); + + /* Register settings */ + if ( ( rc = register_settings ( &ipv6set->settings, parent, + IPV6_SETTINGS_NAME ) ) != 0 ) + goto err_register; + + err_register: + ref_put ( &ipv6set->refcnt ); + err_alloc: + return rc; +} + /** * Create IPv6 network device * @@ -1116,6 +1225,10 @@ static int ipv6_probe ( struct net_device *netdev ) { if ( ! miniroute ) return -ENOMEM; + /* Register link-local address settings */ + if ( ( rc = ipv6_register_settings ( netdev ) ) != 0 ) + return rc; + return 0; } From f5cf4f706ec27e995b8431f885b61b5cdbcef968 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 16:44:18 +0100 Subject: [PATCH 284/591] [settings] Allow settings blocks to specify a sibling ordering Allow settings blocks to provide an explicit default ordering between siblings, with lower precedence than the existing ${priority} setting. Signed-off-by: Michael Brown --- src/core/settings.c | 2 ++ src/include/ipxe/settings.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/core/settings.c b/src/core/settings.c index 42bb5e53f..c306054d6 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -452,6 +452,8 @@ static void reprioritise_settings ( struct settings *settings ) { tmp_priority = fetch_intz_setting ( tmp, &priority_setting ); if ( priority > tmp_priority ) break; + if ( settings->order > tmp->order ) + break; } list_add_tail ( &settings->siblings, &tmp->siblings ); diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 07ebaa620..8cc0b6bb7 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -144,6 +144,8 @@ struct settings { struct settings_operations *op; /** Default scope for numerical settings constructed for this block */ const struct settings_scope *default_scope; + /** Sibling ordering */ + int order; }; /** From 4ad3c73b3099cfe3b7f1c79ddfe9061809e4ac6d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 16:57:32 +0100 Subject: [PATCH 285/591] [ipv6] Match user expectations for IPv6 settings priorities A reasonable user expectation is that ${net0/ip6} should show the "highest-priority" of the IPv6 addresses, even when multiple IPv6 addresses are active. The expected order of priority is likely to be manually-assigned addresses first, then stateful DHCPv6 addresses, then SLAAC addresses, and lastly link-local addresses. Using ${priority} to enforce an ordering is undesirable since that would affect the priority assigned to each of the net blocks as a whole, so use the sibling ordering capability instead. Signed-off-by: Michael Brown --- src/include/ipxe/ipv6.h | 12 ++++++++++++ src/net/ipv6.c | 1 + src/net/ndp.c | 19 +++++++++++++++++-- src/net/udp/dhcpv6.c | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index 86c8ff924..4dd03f058 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -238,6 +238,18 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) { addr->s6_addr[15] = 2; } +/** IPv6 settings sibling order */ +enum ipv6_settings_order { + /** No address */ + IPV6_ORDER_PREFIX_ONLY = -4, + /** Link-local address */ + IPV6_ORDER_LINK_LOCAL = -3, + /** Address assigned via SLAAC */ + IPV6_ORDER_SLAAC = -2, + /** Address assigned via DHCPv6 */ + IPV6_ORDER_DHCPV6 = -1, +}; + /** IPv6 link-local address settings block name */ #define IPV6_SETTINGS_NAME "link" diff --git a/src/net/ipv6.c b/src/net/ipv6.c index cdd6640c7..04ba3d8bb 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1185,6 +1185,7 @@ static int ipv6_register_settings ( struct net_device *netdev ) { ref_init ( &ipv6set->refcnt, NULL ); settings_init ( &ipv6set->settings, &ipv6_settings_operations, &ipv6set->refcnt, &ipv6_scope ); + ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL; /* Register settings */ if ( ( rc = register_settings ( &ipv6set->settings, parent, diff --git a/src/net/ndp.c b/src/net/ndp.c index c488acc73..a35a1219e 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -981,17 +981,28 @@ static int ndp_register_settings ( struct net_device *netdev, size_t option_len; unsigned int prefixes; unsigned int instance; + int order; int rc; /* Count number of prefix options. We can assume that the * options are well-formed, otherwise they would have been * rejected prior to being stored. */ + order = IPV6_ORDER_PREFIX_ONLY; for ( prefixes = 0, offset = 0 ; offset < len ; offset += option_len ) { + + /* Skip non-prefix options */ option = ( ( ( void * ) options ) + offset ); option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - if ( option->header.type == NDP_OPT_PREFIX ) - prefixes++; + if ( option->header.type != NDP_OPT_PREFIX ) + continue; + + /* Count number of prefixes */ + prefixes++; + + /* Increase overall order if we have SLAAC addresses */ + if ( option->prefix.flags & NDP_PREFIX_AUTONOMOUS ) + order = IPV6_ORDER_SLAAC; } /* Allocate and initialise structure */ @@ -1004,6 +1015,7 @@ static int ndp_register_settings ( struct net_device *netdev, ref_init ( &ndpset->refcnt, NULL ); settings_init ( &ndpset->settings, &ndp_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); + ndpset->settings.order = order; memcpy ( &ndpset->router, router, sizeof ( ndpset->router ) ); ndpset->lifetime = lifetime; ndpset->len = len; @@ -1028,6 +1040,9 @@ static int ndp_register_settings ( struct net_device *netdev, settings_init ( &prefset->settings, &ndp_prefix_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); + prefset->settings.order = + ( ( option->prefix.flags & NDP_PREFIX_AUTONOMOUS ) ? + IPV6_ORDER_SLAAC : IPV6_ORDER_PREFIX_ONLY ); prefset->prefix = &option->prefix; snprintf ( prefset->name, sizeof ( prefset->name ), "%d", instance++ ); diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index ac7010422..a2c69aaae 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -375,6 +375,7 @@ static int dhcpv6_register ( struct in6_addr *lease, ref_init ( &dhcpv6set->refcnt, NULL ); settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations, &dhcpv6set->refcnt, &dhcpv6_scope ); + dhcpv6set->settings.order = IPV6_ORDER_DHCPV6; data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) ); len = options->len; memcpy ( data, options->data, len ); From c34d1518eb446d596087ed2b9dda33a513f7e980 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 17:49:50 +0100 Subject: [PATCH 286/591] [ipv6] Create routing table based on IPv6 settings Use the IPv6 settings to construct the routing table, in a matter analogous to the construction of the IPv4 routing table. This allows for manual assignment of IPv6 addresses via e.g. set net0/ip6 2001:ba8:0:1d4::6950:5845 set net0/len6 64 set net0/gateway6 fe80::226:bff:fedd:d3c0 The prefix length ("len6") may be omitted, in which case a default prefix length of 64 will be assumed. Multiple IPv6 addresses may be assigned manually by implicitly creating child settings blocks. For example: set net0/ip6 2001:ba8:0:1d4::6950:5845 set net0.ula/ip6 fda4:2496:e992::6950:5845 Signed-off-by: Michael Brown --- src/include/ipxe/ipv6.h | 10 +- src/net/ipv6.c | 255 +++++++++++++++++++++------------------- src/net/ndp.c | 55 +-------- src/net/udp/dhcpv6.c | 17 +-- 4 files changed, 142 insertions(+), 195 deletions(-) diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index 4dd03f058..0e5292fba 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -25,6 +25,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** IPv6 maximum hop limit */ #define IPV6_HOP_LIMIT 0xff +/** IPv6 default prefix length */ +#define IPV6_DEFAULT_PREFIX_LEN 64 + +/** IPv6 maximum prefix length */ +#define IPV6_MAX_PREFIX_LEN 128 + /** IPv6 header */ struct ipv6_header { /** Version (4 bits), Traffic class (8 bits), Flow label (20 bits) */ @@ -258,10 +264,6 @@ extern struct list_head ipv6_miniroutes; extern struct net_protocol ipv6_protocol __net_protocol; extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ); -extern int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ); -extern int ipv6_set_address ( struct net_device *netdev, - struct in6_addr *address ); extern int parse_ipv6_setting ( const struct setting_type *type, const char *value, void *buf, size_t len ); extern int format_ipv6_setting ( const struct setting_type *type, diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 04ba3d8bb..d2a173120 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -164,107 +164,85 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, * @v netdev Network device * @v address IPv6 address (or prefix) * @v prefix_len Prefix length - * @v flags Flags - * @ret miniroute Routing table entry, or NULL on failure + * @v router Router address (if any) + * @ret rc Return status code */ -static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev, - struct in6_addr *address, - unsigned int prefix_len, - unsigned int flags ) { +static int ipv6_add_miniroute ( struct net_device *netdev, + struct in6_addr *address, + unsigned int prefix_len, + struct in6_addr *router ) { struct ipv6_miniroute *miniroute; uint8_t *prefix_mask; - - /* Create routing table entry */ - miniroute = zalloc ( sizeof ( *miniroute ) ); - if ( ! miniroute ) - return NULL; - miniroute->netdev = netdev_get ( netdev ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->prefix_len = prefix_len; - assert ( prefix_len <= ( 8 * sizeof ( miniroute->prefix_mask ) ) ); - for ( prefix_mask = miniroute->prefix_mask.s6_addr ; prefix_len >= 8 ; - prefix_mask++, prefix_len -= 8 ) { - *prefix_mask = 0xff; - } - if ( prefix_len ) - *prefix_mask <<= ( 8 - prefix_len ); - miniroute->flags = flags; - list_add ( &miniroute->list, &ipv6_miniroutes ); - ipv6_dump_miniroute ( miniroute ); - - return miniroute; -} - -/** - * Define IPv6 on-link prefix - * - * @v netdev Network device - * @v prefix IPv6 address prefix - * @v prefix_len Prefix length - * @v router Router address (or NULL) - * @ret rc Return status code - */ -int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ) { - struct ipv6_miniroute *miniroute; - int changed; + unsigned int remaining; + unsigned int i; /* Find or create routing table entry */ - miniroute = ipv6_miniroute ( netdev, prefix ); - if ( ! miniroute ) - miniroute = ipv6_add_miniroute ( netdev, prefix, prefix_len, 0); - if ( ! miniroute ) - return -ENOMEM; + miniroute = ipv6_miniroute ( netdev, address ); + if ( ! miniroute ) { - /* Record router and add to start or end of list as appropriate */ - list_del ( &miniroute->list ); - if ( router ) { - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ROUTER ) ) || - ( memcmp ( &miniroute->router, router, - sizeof ( miniroute->router ) ) != 0 ) ); - miniroute->flags |= IPV6_HAS_ROUTER; - memcpy ( &miniroute->router, router, - sizeof ( miniroute->router ) ); - list_add_tail ( &miniroute->list, &ipv6_miniroutes ); - } else { - changed = ( miniroute->flags & IPV6_HAS_ROUTER ); - miniroute->flags &= ~IPV6_HAS_ROUTER; + /* Create new routing table entry */ + miniroute = zalloc ( sizeof ( *miniroute ) ); + if ( ! miniroute ) + return -ENOMEM; + miniroute->netdev = netdev_get ( netdev ); + memcpy ( &miniroute->address, address, + sizeof ( miniroute->address ) ); + + /* Default to prefix length of 64 if none specified */ + if ( ! prefix_len ) + prefix_len = IPV6_DEFAULT_PREFIX_LEN; + miniroute->prefix_len = prefix_len; + assert ( prefix_len <= IPV6_MAX_PREFIX_LEN ); + + /* Construct prefix mask */ + remaining = prefix_len; + for ( prefix_mask = miniroute->prefix_mask.s6_addr ; + remaining >= 8 ; prefix_mask++, remaining -= 8 ) { + *prefix_mask = 0xff; + } + if ( remaining ) + *prefix_mask <<= ( 8 - remaining ); + + /* Add to list of routes */ list_add ( &miniroute->list, &ipv6_miniroutes ); } - if ( changed ) - ipv6_dump_miniroute ( miniroute ); + /* Set or update address, if applicable */ + for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / + sizeof ( address->s6_addr32[0] ) ) ; i++ ) { + if ( ( address->s6_addr32[i] & + ~miniroute->prefix_mask.s6_addr32[i] ) != 0 ) { + memcpy ( &miniroute->address, address, + sizeof ( miniroute->address ) ); + miniroute->flags |= IPV6_HAS_ADDRESS; + } + } + if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN ) + miniroute->flags |= IPV6_HAS_ADDRESS; + + /* Set or update router, if applicable */ + if ( router ) { + memcpy ( &miniroute->router, router, + sizeof ( miniroute->router ) ); + miniroute->flags |= IPV6_HAS_ROUTER; + list_del ( &miniroute->list ); + list_add_tail ( &miniroute->list, &ipv6_miniroutes ); + } + + ipv6_dump_miniroute ( miniroute ); return 0; } /** - * Add IPv6 on-link address + * Delete IPv6 minirouting table entry * - * @v netdev Network device - * @v address IPv6 address - * @ret rc Return status code - * - * An on-link prefix for the address must already exist. + * @v miniroute Routing table entry */ -int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) { - struct ipv6_miniroute *miniroute; - int changed; +static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) { - /* Find routing table entry */ - miniroute = ipv6_miniroute ( netdev, address ); - if ( ! miniroute ) - return -EADDRNOTAVAIL; - - /* Record address */ - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) || - ( memcmp ( &miniroute->address, address, - sizeof ( miniroute->address ) ) != 0 ) ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->flags |= IPV6_HAS_ADDRESS; - if ( changed ) - ipv6_dump_miniroute ( miniroute ); - - return 0; + netdev_put ( miniroute->netdev ); + list_del ( &miniroute->list ); + free ( miniroute ); } /** @@ -1198,65 +1176,98 @@ static int ipv6_register_settings ( struct net_device *netdev ) { return rc; } +/** IPv6 network device driver */ +struct net_driver ipv6_driver __net_driver = { + .name = "IPv6", + .probe = ipv6_register_settings, +}; + /** - * Create IPv6 network device + * Create IPv6 routing table based on configured settings * * @v netdev Network device + * @v settings Settings block * @ret rc Return status code */ -static int ipv6_probe ( struct net_device *netdev ) { - struct ipv6_miniroute *miniroute; - struct in6_addr address; - int prefix_len; +static int ipv6_create_routes ( struct net_device *netdev, + struct settings *settings ) { + struct settings *child; + struct settings *origin; + struct in6_addr ip6_buf; + struct in6_addr gateway6_buf; + struct in6_addr *ip6 = &ip6_buf; + struct in6_addr *gateway6 = &gateway6_buf; + uint8_t len6; + size_t len; int rc; - /* Construct link-local address from EUI-64 as per RFC 2464 */ - memset ( &address, 0, sizeof ( address ) ); - prefix_len = ipv6_link_local ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "IPv6 %s could not construct link-local " - "address: %s\n", netdev->name, strerror ( rc ) ); + /* First, create routing table for any child settings. We do + * this depth-first and in reverse order so that the end + * result reflects the relative priorities of the settings + * blocks. + */ + list_for_each_entry_reverse ( child, &settings->children, siblings ) + ipv6_create_routes ( netdev, child ); + + /* Fetch IPv6 address, if any */ + len = fetch_setting ( settings, &ip6_setting, &origin, NULL, + ip6, sizeof ( *ip6 ) ); + if ( ( len != sizeof ( *ip6 ) ) || ( origin != settings ) ) + return 0; + + /* Fetch prefix length, if defined */ + len = fetch_setting ( settings, &len6_setting, &origin, NULL, + &len6, sizeof ( len6 ) ); + if ( ( len != sizeof ( len6 ) ) || ( origin != settings ) ) + len6 = 0; + if ( len6 > IPV6_MAX_PREFIX_LEN ) + len6 = IPV6_MAX_PREFIX_LEN; + + /* Fetch gateway, if defined */ + len = fetch_setting ( settings, &gateway6_setting, &origin, NULL, + gateway6, sizeof ( *gateway6 ) ); + if ( ( len != sizeof ( *gateway6 ) ) || ( origin != settings ) ) + gateway6 = NULL; + + /* Create or update route */ + if ( ( rc = ipv6_add_miniroute ( netdev, ip6, len6, gateway6 ) ) != 0){ + DBGC ( netdev, "IPv6 %s could not add route: %s\n", + netdev->name, strerror ( rc ) ); return rc; } - /* Create link-local address for this network device */ - miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len, - IPV6_HAS_ADDRESS ); - if ( ! miniroute ) - return -ENOMEM; - - /* Register link-local address settings */ - if ( ( rc = ipv6_register_settings ( netdev ) ) != 0 ) - return rc; - return 0; } /** - * Destroy IPv6 network device + * Create IPv6 routing table based on configured settings * - * @v netdev Network device + * @ret rc Return status code */ -static void ipv6_remove ( struct net_device *netdev ) { +static int ipv6_create_all_routes ( void ) { struct ipv6_miniroute *miniroute; struct ipv6_miniroute *tmp; + struct net_device *netdev; + struct settings *settings; + int rc; - /* Delete all miniroutes for this network device */ - list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) { - if ( miniroute->netdev == netdev ) { - netdev_put ( miniroute->netdev ); - list_del ( &miniroute->list ); - free ( miniroute ); - } + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) + ipv6_del_miniroute ( miniroute ); + + /* Create routes for each configured network device */ + for_each_netdev ( netdev ) { + settings = netdev_settings ( netdev ); + if ( ( rc = ipv6_create_routes ( netdev, settings ) ) != 0 ) + return rc; } + + return 0; } -/** IPv6 network device driver */ -struct net_driver ipv6_driver __net_driver = { - .name = "IPv6", - .probe = ipv6_probe, - .remove = ipv6_remove, +/** IPv6 settings applicator */ +struct settings_applicator ipv6_settings_applicator __settings_applicator = { + .apply = ipv6_create_all_routes, }; /* Drag in objects via ipv6_protocol */ diff --git a/src/net/ndp.c b/src/net/ndp.c index a35a1219e..21bbd9995 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -342,11 +342,6 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, union ndp_option *option, size_t len ) { struct ndp_router_advertisement_header *radv = &ndp->radv; struct ndp_prefix_information_option *prefix_opt = &option->prefix; - struct in6_addr *router = &sin6_src->sin6_addr; - struct in6_addr address; - struct ipv6conf *ipv6conf; - int prefix_len; - int rc; /* Sanity check */ if ( sizeof ( *prefix_opt ) > len ) { @@ -355,59 +350,13 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, return -EINVAL; } - /* Identify IPv6 configurator, if any */ - ipv6conf = ipv6conf_demux ( netdev ); DBGC ( netdev, "NDP %s found %sdefault router %s ", netdev->name, ( radv->lifetime ? "" : "non-" ), inet6_ntoa ( &sin6_src->sin6_addr ) ); - DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d%s\n", + DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n", ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ), ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ), - inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len, ( ipv6conf ? "" : " (ignored)" ) ); - - /* Do nothing unless IPv6 autoconfiguration is in progress */ - if ( ! ipv6conf ) - return 0; - - /* Ignore off-link prefixes */ - if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ) - return 0; - - /* Define prefix */ - if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix, - prefix_opt->prefix_len, - ( radv->lifetime ? - router : NULL ) ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n", - netdev->name, inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len, strerror ( rc ) ); - return rc; - } - - /* Perform stateless address autoconfiguration, if applicable */ - if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) { - memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) ); - prefix_len = ipv6_eui64 ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "NDP %s could not construct SLAAC " - "address: %s\n", netdev->name, strerror ( rc ) ); - return rc; - } - if ( prefix_len != prefix_opt->prefix_len ) { - DBGC ( netdev, "NDP %s incorrect SLAAC prefix length " - "%d (expected %d)\n", netdev->name, - prefix_opt->prefix_len, prefix_len ); - return -EINVAL; - } - if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not set address %s: %s\n", - netdev->name, inet6_ntoa ( &address ), - strerror ( rc ) ); - return rc; - } - } + inet6_ntoa ( &prefix_opt->prefix ), prefix_opt->prefix_len ); return 0; } diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index a2c69aaae..253032e4e 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -462,8 +462,6 @@ enum dhcpv6_session_state_flags { DHCPV6_RX_RECORD_SERVER_ID = 0x04, /** Record received IPv6 address */ DHCPV6_RX_RECORD_IAADDR = 0x08, - /** Apply received IPv6 address */ - DHCPV6_RX_APPLY_IAADDR = 0x10, }; /** DHCPv6 request state */ @@ -471,7 +469,7 @@ static struct dhcpv6_session_state dhcpv6_request = { .tx_type = DHCPV6_REQUEST, .rx_type = DHCPV6_REPLY, .flags = ( DHCPV6_TX_IA_NA | DHCPV6_TX_IAADDR | - DHCPV6_RX_RECORD_IAADDR | DHCPV6_RX_APPLY_IAADDR ), + DHCPV6_RX_RECORD_IAADDR ), .next = NULL, }; @@ -870,19 +868,6 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6, dhcpv6->server_duid_len ); } - /* Apply identity association address, if applicable */ - if ( dhcpv6->state->flags & DHCPV6_RX_APPLY_IAADDR ) { - if ( ( rc = ipv6_set_address ( dhcpv6->netdev, - &dhcpv6->lease ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not apply %s: %s\n", - dhcpv6->netdev->name, - inet6_ntoa ( &dhcpv6->lease ), strerror ( rc ) ); - /* This is plausibly the error we want to return */ - dhcpv6->rc = rc; - goto done; - } - } - /* Transition to next state, if applicable */ if ( dhcpv6->state->next ) { dhcpv6_set_state ( dhcpv6, dhcpv6->state->next ); From daa1a59310c0ae05773ac7f81417dfeebeb1455b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Jul 2016 15:46:51 +0100 Subject: [PATCH 287/591] [ipv6] Rename ipv6_scope to ipv6_settings_scope Signed-off-by: Michael Brown --- src/include/ipxe/settings.h | 2 +- src/net/ipv6.c | 12 ++++++------ src/net/ndp.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 8cc0b6bb7..341fc3c7f 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -286,7 +286,7 @@ struct builtin_setting { extern const struct settings_scope builtin_scope; /** IPv6 setting scope */ -extern const struct settings_scope ipv6_scope; +extern const struct settings_scope ipv6_settings_scope; /** DHCPv6 setting scope */ extern const struct settings_scope dhcpv6_scope; diff --git a/src/net/ipv6.c b/src/net/ipv6.c index d2a173120..c4e1f7088 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1040,14 +1040,14 @@ int format_ipv6_setting ( const struct setting_type *type __unused, } /** IPv6 settings scope */ -const struct settings_scope ipv6_scope; +const struct settings_scope ipv6_settings_scope; /** IPv6 address setting */ const struct setting ip6_setting __setting ( SETTING_IP6, ip6 ) = { .name = "ip6", .description = "IPv6 address", .type = &setting_type_ipv6, - .scope = &ipv6_scope, + .scope = &ipv6_settings_scope, }; /** IPv6 prefix length setting */ @@ -1055,7 +1055,7 @@ const struct setting len6_setting __setting ( SETTING_IP6, len6 ) = { .name = "len6", .description = "IPv6 prefix length", .type = &setting_type_int8, - .scope = &ipv6_scope, + .scope = &ipv6_settings_scope, }; /** Default gateway setting */ @@ -1063,7 +1063,7 @@ const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = { .name = "gateway6", .description = "IPv6 gateway", .type = &setting_type_ipv6, - .scope = &ipv6_scope, + .scope = &ipv6_settings_scope, }; /** @@ -1076,7 +1076,7 @@ const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = { static int ipv6_applies ( struct settings *settings __unused, const struct setting *setting ) { - return ( setting->scope == &ipv6_scope ); + return ( setting->scope == &ipv6_settings_scope ); } /** @@ -1162,7 +1162,7 @@ static int ipv6_register_settings ( struct net_device *netdev ) { } ref_init ( &ipv6set->refcnt, NULL ); settings_init ( &ipv6set->settings, &ipv6_settings_operations, - &ipv6set->refcnt, &ipv6_scope ); + &ipv6set->refcnt, &ipv6_settings_scope ); ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL; /* Register settings */ diff --git a/src/net/ndp.c b/src/net/ndp.c index 21bbd9995..f28e71cbd 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -755,7 +755,7 @@ static struct settings_operations ndp_settings_operations = { static int ndp_prefix_applies ( struct settings *settings __unused, const struct setting *setting ) { - return ( setting->scope == &ipv6_scope ); + return ( setting->scope == &ipv6_settings_scope ); } /** From a454baaf11d254d518dc84cc2a67735758687263 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 25 Jul 2016 13:44:16 +0100 Subject: [PATCH 288/591] [test] Update IPv6 tests to use okx() Signed-off-by: Michael Brown --- src/tests/ipv6_test.c | 64 ++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c index 772eb1b82..42b72961d 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -70,22 +70,27 @@ static const struct in6_addr sample_multicast = { * * @v addr IPv6 address * @v text Expected textual representation + * @v file Test code file + * @v line Test code line */ +static void inet6_ntoa_okx ( const struct in6_addr *addr, const char *text, + const char *file, unsigned int line ) { + char *actual; + + actual = inet6_ntoa ( addr ); + DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) " + "= %s\n", ntohs ( addr->s6_addr16[0] ), + ntohs ( addr->s6_addr16[1] ), ntohs ( addr->s6_addr16[2] ), + ntohs ( addr->s6_addr16[3] ), ntohs ( addr->s6_addr16[4] ), + ntohs ( addr->s6_addr16[5] ), ntohs ( addr->s6_addr16[6] ), + ntohs ( addr->s6_addr16[7] ), actual ); + okx ( strcmp ( actual, text ) == 0, file, line ); +} #define inet6_ntoa_ok( addr, text ) do { \ static const struct in6_addr in = { \ .s6_addr = addr, \ }; \ - static const char expected[] = text; \ - char *actual; \ - \ - actual = inet6_ntoa ( &in ); \ - DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) " \ - "= %s\n", ntohs ( in.s6_addr16[0] ), \ - ntohs ( in.s6_addr16[1] ), ntohs ( in.s6_addr16[2] ), \ - ntohs ( in.s6_addr16[3] ), ntohs ( in.s6_addr16[4] ), \ - ntohs ( in.s6_addr16[5] ), ntohs ( in.s6_addr16[6] ), \ - ntohs ( in.s6_addr16[7] ), actual ); \ - ok ( strcmp ( actual, expected ) == 0 ); \ + inet6_ntoa_okx ( &in, text, __FILE__, __LINE__ ); \ } while ( 0 ) /** @@ -93,31 +98,40 @@ static const struct in6_addr sample_multicast = { * * @v text Textual representation * @v addr Expected IPv6 address + * @v file Test code file + * @v line Test code line */ +static void inet6_aton_okx ( const char *text, const struct in6_addr *addr, + const char *file, unsigned int line ) { + struct in6_addr actual; + + okx ( inet6_aton ( text, &actual ) == 0, file, line ); + DBG ( "inet6_aton ( \"%s\" ) = %s\n", text, inet6_ntoa ( &actual ) ); + okx ( memcmp ( &actual, addr, sizeof ( actual ) ) == 0, + file, line ); +} #define inet6_aton_ok( text, addr ) do { \ - static const char string[] = text; \ - static const struct in6_addr expected = { \ + static const struct in6_addr in = { \ .s6_addr = addr, \ }; \ - struct in6_addr actual; \ - \ - ok ( inet6_aton ( string, &actual ) == 0 ); \ - DBG ( "inet6_aton ( \"%s\" ) = %s\n", string, \ - inet6_ntoa ( &actual ) ); \ - ok ( memcmp ( &actual, &expected, sizeof ( actual ) ) == 0 ); \ + inet6_aton_okx ( text, &in, __FILE__, __LINE__ ); \ } while ( 0 ) /** * Report an inet6_aton() failure test result * * @v text Textual representation + * @v file Test code file + * @v line Test code line */ -#define inet6_aton_fail_ok( text ) do { \ - static const char string[] = text; \ - struct in6_addr dummy; \ - \ - ok ( inet6_aton ( string, &dummy ) != 0 ); \ - } while ( 0 ) +static void inet6_aton_fail_okx ( const char *text, const char *file, + unsigned int line ) { + struct in6_addr dummy; + + okx ( inet6_aton ( text, &dummy ) != 0, file, line ); +} +#define inet6_aton_fail_ok( text ) \ + inet6_aton_fail_okx ( text, __FILE__, __LINE__ ) /** * Perform IPv6 self-tests From a4c4f72297bea6902001ce813aaf432bd49d382d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 25 Jul 2016 15:20:22 +0100 Subject: [PATCH 289/591] [ipv6] Allow for multiple routers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Select the IPv6 source address and corresponding router (if any) using a very simplified version of the algorithm from RFC6724: - Ignore any source address that has a smaller scope than the destination address. For example, do not use a link-local source address when sending to a global destination address. - If we have a source address which is on the same link as the destination address, then use that source address. - If we are left with multiple possible source addresses, then choose the address with the smallest scope. For example, if we are sending to a site-local destination address and we have both a global source address and a site-local source address, then use the site-local source address. - If we are still left with multiple possible source addresses, then choose the address with the longest matching prefix. For the purposes of this algorithm, we treat RFC4193 Unique Local Addresses as having organisation-local scope. Since we use only link-local scope for our multicast transmissions, this approximation should remain valid in all practical situations. Originally-implemented-by: Thomas Bächler Signed-off-by: Michael Brown --- src/include/ipxe/in.h | 8 +- src/include/ipxe/ipv6.h | 39 ++++++ src/net/ipv6.c | 173 ++++++++++++++++++------- src/tests/ipv6_test.c | 281 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 451 insertions(+), 50 deletions(-) diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index 0ebf441c2..3044d6316 100644 --- a/src/include/ipxe/in.h +++ b/src/include/ipxe/in.h @@ -69,8 +69,12 @@ struct in6_addr { ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ htons ( 0xfe80 ) ) -#define IN6_IS_ADDR_NONGLOBAL( addr ) \ - ( IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MULTICAST (addr) ) +#define IN6_IS_ADDR_SITELOCAL( addr ) \ + ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ + htons ( 0xfec0 ) ) + +#define IN6_IS_ADDR_ULA( addr ) \ + ( ( *( ( const uint8_t * ) (addr) ) & 0xfe ) == 0xfc ) /** * IPv4 socket address diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index 0e5292fba..4dd43f16d 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -158,6 +158,24 @@ struct ipv6_pseudo_header { uint8_t next_header; } __attribute__ (( packed )); +/** IPv6 address scopes */ +enum ipv6_address_scope { + /** Interface-local address scope */ + IPV6_SCOPE_INTERFACE_LOCAL = 0x1, + /** Link-local address scope */ + IPV6_SCOPE_LINK_LOCAL = 0x2, + /** Admin-local address scope */ + INV6_SCOPE_ADMIN_LOCAL = 0x4, + /** Site-local address scope */ + IPV6_SCOPE_SITE_LOCAL = 0x5, + /** Organisation-local address scope */ + IPV6_SCOPE_ORGANISATION_LOCAL = 0x8, + /** Global address scope */ + IPV6_SCOPE_GLOBAL = 0xe, + /** Maximum scope */ + IPV6_SCOPE_MAX = 0xf, +}; + /** An IPv6 address/routing table entry */ struct ipv6_miniroute { /** List of miniroutes */ @@ -174,6 +192,8 @@ struct ipv6_miniroute { struct in6_addr prefix_mask; /** Router address */ struct in6_addr router; + /** Scope */ + unsigned int scope; /** Flags */ unsigned int flags; }; @@ -244,6 +264,18 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) { addr->s6_addr[15] = 2; } +/** + * Get multicast address scope + * + * @v addr Multicast address + * @ret scope Address scope + */ +static inline unsigned int +ipv6_multicast_scope ( const struct in6_addr *addr ) { + + return ( addr->s6_addr[1] & 0x0f ); +} + /** IPv6 settings sibling order */ enum ipv6_settings_order { /** No address */ @@ -264,6 +296,13 @@ extern struct list_head ipv6_miniroutes; extern struct net_protocol ipv6_protocol __net_protocol; extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ); +extern int ipv6_add_miniroute ( struct net_device *netdev, + struct in6_addr *address, + unsigned int prefix_len, + struct in6_addr *router ); +extern void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ); +extern struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, + struct in6_addr **dest ); extern int parse_ipv6_setting ( const struct setting_type *type, const char *value, void *buf, size_t len ); extern int format_ipv6_setting ( const struct setting_type *type, diff --git a/src/net/ipv6.c b/src/net/ipv6.c index c4e1f7088..4b2c33eb4 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -78,6 +79,40 @@ static uint32_t ipv6col ( struct in6_addr *in ) { return crc32_le ( 0, in, sizeof ( *in ) ); } +/** + * Determine IPv6 address scope + * + * @v addr IPv6 address + * @ret scope Address scope + */ +static unsigned int ipv6_scope ( const struct in6_addr *addr ) { + + /* Multicast addresses directly include a scope field */ + if ( IN6_IS_ADDR_MULTICAST ( addr ) ) + return ipv6_multicast_scope ( addr ); + + /* Link-local addresses have link-local scope */ + if ( IN6_IS_ADDR_LINKLOCAL ( addr ) ) + return IPV6_SCOPE_LINK_LOCAL; + + /* Site-local addresses have site-local scope */ + if ( IN6_IS_ADDR_SITELOCAL ( addr ) ) + return IPV6_SCOPE_SITE_LOCAL; + + /* Unique local addresses do not directly map to a defined + * scope. They effectively have a scope which is wider than + * link-local but narrower than global. Since the only + * multicast packets that we transmit are link-local, we can + * simply choose an arbitrary scope between link-local and + * global. + */ + if ( IN6_IS_ADDR_ULA ( addr ) ) + return IPV6_SCOPE_ORGANISATION_LOCAL; + + /* All other addresses are assumed to be global */ + return IPV6_SCOPE_GLOBAL; +} + /** * Dump IPv6 routing table entry * @@ -119,23 +154,32 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) { } /** - * Check if IPv6 address is within a routing table entry's local network + * Count matching bits of an IPv6 routing table entry prefix * * @v miniroute Routing table entry * @v address IPv6 address - * @ret is_on_link Address is within this entry's local network + * @ret match_len Number of matching prefix bits */ -static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute, - struct in6_addr *address ) { +static unsigned int ipv6_match_len ( struct ipv6_miniroute *miniroute, + struct in6_addr *address ) { + unsigned int match_len = 0; unsigned int i; + uint32_t diff; for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / sizeof ( address->s6_addr32[0] ) ) ; i++ ) { - if ( (( address->s6_addr32[i] ^ miniroute->address.s6_addr32[i]) - & miniroute->prefix_mask.s6_addr32[i] ) != 0 ) - return 0; + + diff = ntohl ( ~( ( ~( address->s6_addr32[i] ^ + miniroute->address.s6_addr32[i] ) ) + & miniroute->prefix_mask.s6_addr32[i] ) ); + match_len += 32; + if ( diff ) { + match_len -= flsl ( diff ); + break; + } } - return 1; + + return match_len; } /** @@ -148,12 +192,15 @@ static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute, static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, struct in6_addr *address ) { struct ipv6_miniroute *miniroute; + unsigned int match_len; list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { - if ( ( miniroute->netdev == netdev ) && - ipv6_is_on_link ( miniroute, address ) ) { - return miniroute; - } + if ( miniroute->netdev != netdev ) + continue; + match_len = ipv6_match_len ( miniroute, address ); + if ( match_len < miniroute->prefix_len ) + continue; + return miniroute; } return NULL; } @@ -167,10 +214,8 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, * @v router Router address (if any) * @ret rc Return status code */ -static int ipv6_add_miniroute ( struct net_device *netdev, - struct in6_addr *address, - unsigned int prefix_len, - struct in6_addr *router ) { +int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address, + unsigned int prefix_len, struct in6_addr *router ) { struct ipv6_miniroute *miniroute; uint8_t *prefix_mask; unsigned int remaining; @@ -178,7 +223,12 @@ static int ipv6_add_miniroute ( struct net_device *netdev, /* Find or create routing table entry */ miniroute = ipv6_miniroute ( netdev, address ); - if ( ! miniroute ) { + if ( miniroute ) { + + /* Remove from existing position in routing table */ + list_del ( &miniroute->list ); + + } else { /* Create new routing table entry */ miniroute = zalloc ( sizeof ( *miniroute ) ); @@ -202,11 +252,11 @@ static int ipv6_add_miniroute ( struct net_device *netdev, } if ( remaining ) *prefix_mask <<= ( 8 - remaining ); - - /* Add to list of routes */ - list_add ( &miniroute->list, &ipv6_miniroutes ); } + /* Add to start of routing table */ + list_add ( &miniroute->list, &ipv6_miniroutes ); + /* Set or update address, if applicable */ for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / sizeof ( address->s6_addr32[0] ) ) ; i++ ) { @@ -220,13 +270,14 @@ static int ipv6_add_miniroute ( struct net_device *netdev, if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN ) miniroute->flags |= IPV6_HAS_ADDRESS; + /* Update scope */ + miniroute->scope = ipv6_scope ( &miniroute->address ); + /* Set or update router, if applicable */ if ( router ) { memcpy ( &miniroute->router, router, sizeof ( miniroute->router ) ); miniroute->flags |= IPV6_HAS_ROUTER; - list_del ( &miniroute->list ); - list_add_tail ( &miniroute->list, &ipv6_miniroutes ); } ipv6_dump_miniroute ( miniroute ); @@ -238,7 +289,7 @@ static int ipv6_add_miniroute ( struct net_device *netdev, * * @v miniroute Routing table entry */ -static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) { +void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) { netdev_put ( miniroute->netdev ); list_del ( &miniroute->list ); @@ -253,9 +304,17 @@ static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) { * @ret dest Next hop destination address * @ret miniroute Routing table entry to use, or NULL if no route */ -static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, - struct in6_addr **dest ) { +struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, + struct in6_addr **dest ) { struct ipv6_miniroute *miniroute; + struct ipv6_miniroute *chosen = NULL; + unsigned int best = 0; + unsigned int match_len; + unsigned int score; + unsigned int scope; + + /* Calculate destination address scope */ + scope = ipv6_scope ( *dest ); /* Find first usable route in routing table */ list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { @@ -264,37 +323,54 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, if ( ! netdev_is_open ( miniroute->netdev ) ) continue; - /* Skip routing table entries with no usable source address */ + /* Skip entries with no usable source address */ if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) continue; - if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) { + /* Skip entries with a non-matching scope ID, if + * destination specifies a scope ID. + */ + if ( scope_id && ( miniroute->netdev->index != scope_id ) ) + continue; - /* If destination is non-global, and the scope ID - * matches this network device, then use this route. - */ - if ( miniroute->netdev->index == scope_id ) - return miniroute; + /* Skip entries that are out of scope */ + if ( miniroute->scope < scope ) + continue; - } else { + /* Calculate match length */ + match_len = ipv6_match_len ( miniroute, *dest ); - /* If destination is an on-link global - * address, then use this route. - */ - if ( ipv6_is_on_link ( miniroute, *dest ) ) - return miniroute; + /* If destination is on-link, then use this route */ + if ( match_len >= miniroute->prefix_len ) + return miniroute; - /* If destination is an off-link global - * address, and we have a default gateway, - * then use this route. - */ - if ( miniroute->flags & IPV6_HAS_ROUTER ) { - *dest = &miniroute->router; - return miniroute; - } + /* If destination is unicast, then skip off-link + * entries with no router. + */ + if ( ! ( IN6_IS_ADDR_MULTICAST ( *dest ) || + ( miniroute->flags & IPV6_HAS_ROUTER ) ) ) + continue; + + /* Choose best route, defined as being the route with + * the smallest viable scope. If two routes both have + * the same scope, then prefer the route with the + * longest match length. + */ + score = ( ( ( IPV6_SCOPE_MAX + 1 - miniroute->scope ) << 8 ) + + match_len ); + if ( score > best ) { + chosen = miniroute; + best = score; } } + /* Return chosen route, if any */ + if ( chosen ) { + if ( ! IN6_IS_ADDR_MULTICAST ( *dest ) ) + *dest = &chosen->router; + return chosen; + } + return NULL; } @@ -880,7 +956,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { const char *netdev_name; /* Identify network device, if applicable */ - if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) { + if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) { netdev = find_netdev_by_index ( sin6->sin6_scope_id ); netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); } else { @@ -946,7 +1022,8 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { } sin6->sin6_scope_id = netdev->index; - } else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) { + } else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) || + IN6_IS_ADDR_MULTICAST ( &in ) ) { /* If no network device is explicitly specified for a * link-local or multicast address, default to using diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c index 42b72961d..9b3a744df 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -41,6 +41,38 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Define inline IPv6 address */ #define IPV6(...) { __VA_ARGS__ } +/** An IPv6 test routing table entry */ +struct ipv6_test_route { + /** Local address */ + const char *address; + /** Prefix length */ + unsigned int prefix_len; + /** Router address (if any) */ + const char *router; +}; + +/** An IPv6 test routing table */ +struct ipv6_test_table { + /** Test routing table entries */ + const struct ipv6_test_route *routes; + /** Number of table entries */ + unsigned int count; + /** Constructed routing table */ + struct list_head list; +}; + +/** Define a test routing table */ +#define TABLE( name, ... ) \ + static const struct ipv6_test_route name ## _routes[] = { \ + __VA_ARGS__ \ + }; \ + static struct ipv6_test_table name = { \ + .routes = name ## _routes, \ + .count = ( sizeof ( name ## _routes ) / \ + sizeof ( name ## _routes[0] ) ), \ + .list = LIST_HEAD_INIT ( name.list ), \ + }; + /** The unspecified IPv6 address */ static const struct in6_addr sample_unspecified = { .s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -53,6 +85,18 @@ static const struct in6_addr sample_link_local = { 0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ), }; +/** A sample site-local IPv6 address */ +static const struct in6_addr sample_site_local = { + .s6_addr = IPV6 ( 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ), +}; + +/** A sample ULA IPv6 address */ +static const struct in6_addr sample_ula = { + .s6_addr = IPV6 ( 0xfd, 0x44, 0x91, 0x12, 0x64, 0x42, 0x00, 0x00, + 0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ), +}; + /** A sample global IPv6 address */ static const struct in6_addr sample_global = { .s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4, @@ -65,6 +109,31 @@ static const struct in6_addr sample_multicast = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), }; +/** Dummy network device used for routing tests */ +static struct net_device ipv6_test_netdev = { + .refcnt = REF_INIT ( ref_no_free ), + .index = 42, + .state = NETDEV_OPEN, +}; + +/** Routing table with only a link-local address */ +TABLE ( table_link_local, + { "fe80::69ff:fe50:5845", 64, NULL } ); + +/** Routing table with a global address */ +TABLE ( table_normal, + { "fe80::69ff:fe50:5845", 64, NULL }, + { "2001:db8:3::1", 64, "fe80::1" } ); + +/** Routing table with multiple addresses and routers */ +TABLE ( table_multi, + { "fe80::69ff:fe50:5845", 64, NULL }, + { "2001:db8:3::1", 64, "fe80::1" }, + { "2001:db8:5::1", 64, NULL }, + { "2001:db8:42::1", 64, "fe80::2" }, + { "fd44:9112:6442::69ff:fe50:5845", 64, "fe80::1" }, + { "fd70:6ba9:50ae::69ff:fe50:5845", 64, "fe80::3" } ); + /** * Report an inet6_ntoa() test result * @@ -133,6 +202,148 @@ static void inet6_aton_fail_okx ( const char *text, const char *file, #define inet6_aton_fail_ok( text ) \ inet6_aton_fail_okx ( text, __FILE__, __LINE__ ) +/** + * Create test routing table + * + * @v table Test routing table + * @v file Test code file + * @v line Test code line + */ +static void ipv6_table_okx ( struct ipv6_test_table *table, const char *file, + unsigned int line ) { + const struct ipv6_test_route *route; + struct in6_addr address; + struct in6_addr router; + struct list_head saved; + unsigned int i; + + /* Sanity check */ + okx ( list_empty ( &table->list ), file, line ); + + /* Save existing routing table */ + INIT_LIST_HEAD ( &saved ); + list_splice_init ( &ipv6_miniroutes, &saved ); + + /* Construct routing table */ + for ( i = 0 ; i < table->count ; i++ ) { + + /* Parse address and router (if applicable) */ + route = &table->routes[i]; + okx ( inet6_aton ( route->address, &address ) == 0, + file, line ); + if ( route->router ) { + okx ( inet6_aton ( route->router, &router ) == 0, + file, line ); + } + + /* Add routing table entry */ + okx ( ipv6_add_miniroute ( &ipv6_test_netdev, &address, + route->prefix_len, + ( route->router ? + &router : NULL ) ) == 0, + file, line ); + } + + /* Save constructed routing table */ + list_splice_init ( &ipv6_miniroutes, &table->list ); + + /* Restore original routing table */ + list_splice ( &saved, &ipv6_miniroutes ); +} +#define ipv6_table_ok( table ) \ + ipv6_table_okx ( table, __FILE__, __LINE__ ) + +/** + * Report an ipv6_route() test result + * + * @v table Test routing table + * @v dest Destination address + * @v src Expected source address, or NULL to expect failure + * @v next Expected next hop address, or NULL to expect destination + * @v file Test code file + * @v line Test code line + */ +static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest, + const char *src, const char *next, + const char *file, unsigned int line ) { + struct in6_addr in_dest; + struct in6_addr in_src; + struct in6_addr in_next; + struct in6_addr *actual; + struct ipv6_miniroute *miniroute; + struct list_head saved; + + /* Switch to test routing table */ + INIT_LIST_HEAD ( &saved ); + list_splice_init ( &ipv6_miniroutes, &saved ); + list_splice_init ( &table->list, &ipv6_miniroutes ); + + /* Parse addresses */ + okx ( inet6_aton ( dest, &in_dest ) == 0, file, line ); + if ( src ) + okx ( inet6_aton ( src, &in_src ) == 0, file, line ); + if ( next ) { + okx ( inet6_aton ( next, &in_next ) == 0, file, line ); + } else { + memcpy ( &in_next, &in_dest, sizeof ( in_next ) ); + } + + /* Perform routing */ + actual = &in_dest; + miniroute = ipv6_route ( ipv6_test_netdev.index, &actual ); + + /* Validate result */ + if ( src ) { + + /* Check that a route was found */ + okx ( miniroute != NULL, file, line ); + DBG ( "ipv6_route ( %s ) = %s", dest, inet6_ntoa ( actual ) ); + DBG ( " from %s\n", inet6_ntoa ( &miniroute->address ) ); + + /* Check that expected source address was used */ + okx ( memcmp ( &miniroute->address, &in_src, + sizeof ( in_src ) ) == 0, file, line ); + + /* Check that expected next hop address was used */ + okx ( memcmp ( actual, &in_next, sizeof ( *actual ) ) == 0, + file, line ); + + } else { + + /* Routing is expected to fail */ + okx ( miniroute == NULL, file, line ); + } + + /* Restore original routing table */ + list_splice_init ( &ipv6_miniroutes, &table->list ); + list_splice ( &saved, &ipv6_miniroutes ); +} +#define ipv6_route_ok( table, dest, src, next ) \ + ipv6_route_okx ( table, dest, src, next, __FILE__, __LINE__ ) + +/** + * Destroy test routing table + * + * @v table Test routing table + */ +static void ipv6_table_del ( struct ipv6_test_table *table ) { + struct ipv6_miniroute *miniroute; + struct ipv6_miniroute *tmp; + struct list_head saved; + + /* Switch to test routing table */ + INIT_LIST_HEAD ( &saved ); + list_splice_init ( &ipv6_miniroutes, &saved ); + list_splice_init ( &table->list, &ipv6_miniroutes ); + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) + ipv6_del_miniroute ( miniroute ); + + /* Restore original routing table */ + list_splice ( &saved, &ipv6_miniroutes ); +} + /** * Perform IPv6 self-tests * @@ -142,16 +353,34 @@ static void ipv6_test_exec ( void ) { /* Address testing macros */ ok ( IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) ); ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_ula ) ); ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) ); ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) ); ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) ); ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_ula ) ); ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) ); ok ( IN6_IS_ADDR_MULTICAST ( &sample_multicast ) ); ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) ); ok ( IN6_IS_ADDR_LINKLOCAL ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_ula ) ); ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) ); ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_multicast ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_unspecified ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_link_local ) ); + ok ( IN6_IS_ADDR_SITELOCAL ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_ula ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_global ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_multicast ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_unspecified ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_site_local ) ); + ok ( IN6_IS_ADDR_ULA ( &sample_ula ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_global ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_multicast ) ); /* inet6_ntoa() tests */ inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4, @@ -228,6 +457,58 @@ static void ipv6_test_exec ( void ) { inet6_aton_fail_ok ( "2001:db8::1::2" ); inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" ); inet6_aton_fail_ok ( ":::" ); + + /* Create test routing tables */ + ipv6_table_ok ( &table_link_local ); + ipv6_table_ok ( &table_normal ); + ipv6_table_ok ( &table_multi ); + + /* Routing table with only a link-local address */ + ipv6_route_ok ( &table_link_local, "fe80::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_link_local, "2001:db8:1::1", + NULL, NULL ); + ipv6_route_ok ( &table_link_local, "ff02::1", + "fe80::69ff:fe50:5845", NULL ); + + /** Routing table with a global address */ + ipv6_route_ok ( &table_normal, "fe80::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_normal, "2001:db8:3::42", + "2001:db8:3::1", NULL ); + ipv6_route_ok ( &table_normal, "2001:ba8:0:1d4::6950:5845", + "2001:db8:3::1", "fe80::1" ); + ipv6_route_ok ( &table_normal, "ff02::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_normal, "ff0e::1", + "2001:db8:3::1", NULL ); + + /** Routing table with multiple addresses and routers */ + ipv6_route_ok ( &table_multi, "fe80::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:3::17", + "2001:db8:3::1", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:5::92", + "2001:db8:5::1", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:42::17", + "2001:db8:42::1", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:5:1::17", + "2001:db8:3::1", "fe80::1" ); + ipv6_route_ok ( &table_multi, "fd44:9112:6442::1", + "fd44:9112:6442::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_multi, "fd70:6ba9:50ae::1", + "fd70:6ba9:50ae::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_multi, "fd40::3", + "fd44:9112:6442::69ff:fe50:5845", "fe80::1" ); + ipv6_route_ok ( &table_multi, "fd70::2", + "fd70:6ba9:50ae::69ff:fe50:5845", "fe80::3" ); + ipv6_route_ok ( &table_multi, "ff02::1", + "fe80::69ff:fe50:5845", NULL ); + + /* Destroy test routing tables */ + ipv6_table_del ( &table_link_local ); + ipv6_table_del ( &table_normal ); + ipv6_table_del ( &table_multi ); } /** IPv6 self-test */ From 145aae39987b2736ed829bf5ffccaa09b37605fe Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 26 Jul 2016 16:18:53 +0100 Subject: [PATCH 290/591] [hyperv] Use instance UUID in device name The Windows drivers for VMBus devices are enumerated using the instance UUID rather than the channel number. Include the instance UUID within the iPXE device name to allow an iPXE network device to be more easily associated with the corresponding Windows network device when debugging. Signed-off-by: Michael Brown --- src/include/ipxe/device.h | 2 +- src/interface/hyperv/vmbus.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h index d81417e8e..d4ba001b0 100644 --- a/src/include/ipxe/device.h +++ b/src/include/ipxe/device.h @@ -72,7 +72,7 @@ struct device_description { /** A hardware device */ struct device { /** Name */ - char name[32]; + char name[40]; /** Driver name */ const char *driver_name; /** Device description */ diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index fd809dda4..e269aee29 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -1121,6 +1121,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, const struct vmbus_message_header *header = &vmbus->message->header; const struct vmbus_offer_channel *offer = &vmbus->message->offer; const union uuid *type; + union uuid instance; struct vmbus_driver *driver; struct vmbus_device *vmdev; struct vmbus_device *tmp; @@ -1165,8 +1166,11 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, rc = -ENOMEM; goto err_alloc_vmdev; } + memcpy ( &instance, &offer->instance, + sizeof ( instance ) ); + uuid_mangle ( &instance ); snprintf ( vmdev->dev.name, sizeof ( vmdev->dev.name ), - "vmbus:%02x", channel ); + "{%s}", uuid_ntoa ( &instance ) ); vmdev->dev.desc.bus_type = BUS_TYPE_HV; INIT_LIST_HEAD ( &vmdev->dev.children ); list_add_tail ( &vmdev->dev.siblings, From 5846ce2e9e567a216167fa389117011879fb9f8a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jul 2016 15:00:26 +0100 Subject: [PATCH 291/591] [crypto] Remove obsolete extern declaration for asn1_invalidate_cursor() Signed-off-by: Michael Brown --- src/include/ipxe/asn1.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 2e635b48a..70dd3eae7 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -337,7 +337,6 @@ asn1_type ( const struct asn1_cursor *cursor ) { return ( ( cursor->len >= sizeof ( *type ) ) ? *type : ASN1_END ); } -extern void asn1_invalidate_cursor ( struct asn1_cursor *cursor ); extern int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ); extern int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ); From 296670a6481ab8b4090c3e53fa4b8dc9518e7c69 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jul 2016 15:02:15 +0100 Subject: [PATCH 292/591] [crypto] Allow for parsing of partial ASN.1 cursors Allow code to create a partial ASN.1 cursor containing only the type and length bytes, so that asn1_start() may be used to determine the length of a large ASN.1 blob without first allocating memory to hold the entire blob. Signed-off-by: Michael Brown --- src/crypto/asn1.c | 13 +++++++------ src/include/ipxe/asn1.h | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index 9c71ffe10..03eb18f7b 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -86,6 +86,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @v cursor ASN.1 object cursor * @v type Expected type, or ASN1_ANY + * @v extra Additional length not present within partial cursor * @ret len Length of object body, or negative error * * The object cursor will be updated to point to the start of the @@ -93,7 +94,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * the length of the object body (i.e. the number of bytes until the * following object tag, if any) is returned. */ -static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { +int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { unsigned int len_len; unsigned int len; @@ -135,9 +136,9 @@ static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { cursor->data++; cursor->len--; } - if ( cursor->len < len ) { + if ( ( cursor->len + extra ) < len ) { DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n", - cursor, len, cursor->len ); + cursor, len, ( cursor->len + extra ) ); return -EINVAL_ASN1_LEN; } @@ -158,7 +159,7 @@ static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { int len; - len = asn1_start ( cursor, type ); + len = asn1_start ( cursor, type, 0 ); if ( len < 0 ) { asn1_invalidate_cursor ( cursor ); return len; @@ -185,7 +186,7 @@ int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) { int len; - len = asn1_start ( cursor, type ); + len = asn1_start ( cursor, type, 0 ); if ( len < 0 ) return len; @@ -242,7 +243,7 @@ int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) { /* Find end of object */ memcpy ( &temp, cursor, sizeof ( temp ) ); - len = asn1_start ( &temp, type ); + len = asn1_start ( &temp, type, 0 ); if ( len < 0 ) { asn1_invalidate_cursor ( cursor ); return len; diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 70dd3eae7..b0a82c00c 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -337,6 +337,8 @@ asn1_type ( const struct asn1_cursor *cursor ) { return ( ( cursor->len >= sizeof ( *type ) ) ? *type : ASN1_END ); } +extern int asn1_start ( struct asn1_cursor *cursor, unsigned int type, + size_t extra ); extern int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ); extern int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ); From ef50608029d9f5821dd4567ee8d9aa78294e3091 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jul 2016 16:16:55 +0100 Subject: [PATCH 293/591] [image] Add image_asn1() to extract ASN.1 objects from image Signed-off-by: Michael Brown --- src/core/image.c | 35 +++++++++++++++++++++++++++++++++++ src/include/ipxe/image.h | 16 ++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/core/image.c b/src/core/image.c index a185b82f4..b4785269b 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -505,3 +505,38 @@ int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { return 0; } + +/** + * Extract ASN.1 object from image + * + * @v image Image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +int image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Sanity check */ + assert ( offset <= image->len ); + + /* Check that this image can be used to extract an ASN.1 object */ + if ( ! ( image->type && image->type->asn1 ) ) + return -ENOTSUP; + + /* Try creating ASN.1 cursor */ + next = image->type->asn1 ( image, offset, cursor ); + if ( next < 0 ) { + rc = next; + DBGC ( image, "IMAGE %s could not extract ASN.1 object: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index f33feddad..2e7eb4cee 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -17,6 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct uri; struct pixel_buffer; +struct asn1_cursor; struct image_type; /** An executable image */ @@ -99,6 +100,19 @@ struct image_type { * @ret rc Return status code */ int ( * pixbuf ) ( struct image *image, struct pixel_buffer **pixbuf ); + /** + * Extract ASN.1 object from image + * + * @v image Image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on + * the allocated ASN.1 cursor. + */ + int ( * asn1 ) ( struct image *image, size_t offset, + struct asn1_cursor **cursor ); }; /** @@ -170,6 +184,8 @@ extern int image_select ( struct image *image ); extern struct image * image_find_selected ( void ); extern int image_set_trust ( int require_trusted, int permanent ); extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ); +extern int image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ); /** * Increment reference count on an image From eb7188d04b30dcbc47ac1af621b738cc0923ae38 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jul 2016 16:18:23 +0100 Subject: [PATCH 294/591] [crypto] Add DER image format Add DER-encoded ASN.1 as an image format. There is no fixed signature for DER files. We treat an image as DER if it comprises a single valid SEQUENCE object covering the entire length of the image. Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/general.h | 1 + src/image/der.c | 120 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/der.h | 16 +++++ src/include/ipxe/errfile.h | 1 + src/tests/asn1_test.c | 97 ++++++++++++++++++++++++++++++ src/tests/asn1_test.h | 73 ++++++++++++++++++++++ src/tests/der_test.c | 84 ++++++++++++++++++++++++++ src/tests/tests.c | 1 + 9 files changed, 396 insertions(+) create mode 100644 src/image/der.c create mode 100644 src/include/ipxe/der.h create mode 100644 src/tests/asn1_test.c create mode 100644 src/tests/asn1_test.h create mode 100644 src/tests/der_test.c diff --git a/src/config/config.c b/src/config/config.c index e24cfe0d0..acdbebaac 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -188,6 +188,9 @@ REQUIRE_OBJECT ( pnm ); #ifdef IMAGE_PNG REQUIRE_OBJECT ( png ); #endif +#ifdef IMAGE_DER +REQUIRE_OBJECT ( der ); +#endif /* * Drag in all requested commands diff --git a/src/config/general.h b/src/config/general.h index a71ba726f..6ff4b74a4 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -112,6 +112,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define IMAGE_SDI /* SDI image support */ //#define IMAGE_PNM /* PNM image support */ //#define IMAGE_PNG /* PNG image support */ +//#define IMAGE_DER /* DER image support */ /* * Command-line commands to include diff --git a/src/image/der.c b/src/image/der.c new file mode 100644 index 000000000..fa17e5659 --- /dev/null +++ b/src/image/der.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DER-encoded ASN.1 data + * + */ + +/** + * Extract ASN.1 object from image + * + * @v image DER image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int der_asn1 ( struct image *image, size_t offset __unused, + struct asn1_cursor **cursor ) { + void *data; + + /* Allocate cursor and data buffer */ + *cursor = malloc ( sizeof ( **cursor ) + image->len ); + if ( ! *cursor ) + return -ENOMEM; + data = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); + + /* Populate cursor and data buffer */ + (*cursor)->data = data; + (*cursor)->len = image->len; + copy_from_user ( data, image->data, 0, image->len ); + + return image->len; +} + +/** + * Probe DER image + * + * @v image DER image + * @ret rc Return status code + */ +static int der_probe ( struct image *image ) { + struct asn1_cursor cursor; + uint8_t buf[8]; + size_t extra; + size_t total; + int len; + int rc; + + /* Sanity check: no realistic DER image can be smaller than this */ + if ( image->len < sizeof ( buf ) ) + return -ENOEXEC; + + /* Prepare partial cursor */ + cursor.data = buf; + cursor.len = sizeof ( buf ); + copy_from_user ( buf, image->data, 0, sizeof ( buf ) ); + extra = ( image->len - sizeof ( buf ) ); + + /* Get length of ASN.1 sequence */ + len = asn1_start ( &cursor, ASN1_SEQUENCE, extra ); + if ( len < 0 ) { + rc = len; + DBGC ( image, "DER %s is not valid ASN.1: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + /* Add length of tag and length bytes consumed by asn1_start() */ + total = ( len + ( cursor.data - ( ( void * ) buf ) ) ); + assert ( total <= image->len ); + + /* Check that image comprises a single well-formed ASN.1 object */ + if ( total != image->len ) { + DBGC ( image, "DER %s is not single ASN.1\n", image->name ); + return -ENOEXEC; + } + + return 0; +} + +/** DER image type */ +struct image_type der_image_type __image_type ( PROBE_NORMAL ) = { + .name = "DER", + .probe = der_probe, + .asn1 = der_asn1, +}; diff --git a/src/include/ipxe/der.h b/src/include/ipxe/der.h new file mode 100644 index 000000000..c63bd9751 --- /dev/null +++ b/src/include/ipxe/der.h @@ -0,0 +1,16 @@ +#ifndef _IPXE_DER_H +#define _IPXE_DER_H + +/** @file + * + * DER image format + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern struct image_type der_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_DER_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index f743dae6f..61e208a52 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -276,6 +276,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_embedded ( ERRFILE_IMAGE | 0x00050000 ) #define ERRFILE_pnm ( ERRFILE_IMAGE | 0x00060000 ) #define ERRFILE_png ( ERRFILE_IMAGE | 0x00070000 ) +#define ERRFILE_der ( ERRFILE_IMAGE | 0x00080000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/tests/asn1_test.c b/src/tests/asn1_test.c new file mode 100644 index 000000000..df3f01b63 --- /dev/null +++ b/src/tests/asn1_test.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 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 + * + * ASN.1 self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include "asn1_test.h" + +/** + * Report ASN.1 test result + * + * @v test ASN.1 test + * @v file Test code file + * @v line Test code line + */ +void asn1_okx ( struct asn1_test *test, const char *file, unsigned int line ) { + struct digest_algorithm *digest = &asn1_test_digest_algorithm; + struct asn1_cursor *cursor; + uint8_t ctx[digest->ctxsize]; + uint8_t out[ASN1_TEST_DIGEST_SIZE]; + unsigned int i; + size_t offset; + int next; + + /* Sanity check */ + assert ( sizeof ( out ) == digest->digestsize ); + + /* Correct image data pointer */ + test->image->data = virt_to_user ( ( void * ) test->image->data ); + + /* Check that image is detected as correct type */ + okx ( register_image ( test->image ) == 0, file, line ); + okx ( test->image->type == test->type, file, line ); + + /* Check that all ASN.1 objects can be extracted */ + for ( offset = 0, i = 0 ; i < test->count ; offset = next, i++ ) { + + /* Extract ASN.1 object */ + next = image_asn1 ( test->image, offset, &cursor ); + okx ( next >= 0, file, line ); + okx ( ( ( size_t ) next ) > offset, file, line ); + if ( next > 0 ) { + + /* Calculate digest of ASN.1 object */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, cursor->data, + cursor->len ); + digest_final ( digest, ctx, out ); + + /* Compare against expected digest */ + okx ( memcmp ( out, test->expected[i].digest, + sizeof ( out ) ) == 0, file, line ); + + /* Free ASN.1 object */ + free ( cursor ); + } + } + + /* Check that we have reached the end of the image */ + okx ( offset == test->image->len, file, line ); + + /* Unregister image */ + unregister_image ( test->image ); +} diff --git a/src/tests/asn1_test.h b/src/tests/asn1_test.h new file mode 100644 index 000000000..c8167ed36 --- /dev/null +++ b/src/tests/asn1_test.h @@ -0,0 +1,73 @@ +#ifndef _ASN1_TEST_H +#define _ASN1_TEST_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** Digest algorithm used for ASN.1 tests */ +#define asn1_test_digest_algorithm sha1_algorithm + +/** Digest size used for ASN.1 tests */ +#define ASN1_TEST_DIGEST_SIZE SHA1_DIGEST_SIZE + +/** An ASN.1 test digest */ +struct asn1_test_digest { + /** Digest value */ + uint8_t digest[ASN1_TEST_DIGEST_SIZE]; +}; + +/** An ASN.1 test */ +struct asn1_test { + /** Image type */ + struct image_type *type; + /** Source image */ + struct image *image; + /** Expected digests of ASN.1 objects */ + struct asn1_test_digest *expected; + /** Number of ASN.1 objects */ + unsigned int count; +}; + +/** + * Define an ASN.1 test + * + * @v _name Test name + * @v _type Test image file type + * @v _file Test image file data + * @v ... Expected ASN.1 object digests + * @ret test ASN.1 test + */ +#define ASN1( _name, _type, _file, ... ) \ + static const char _name ## __file[] = _file; \ + static struct image _name ## __image = { \ + .refcnt = REF_INIT ( ref_no_free ), \ + .name = #_name, \ + .data = ( userptr_t ) ( _name ## __file ), \ + .len = sizeof ( _name ## __file ), \ + }; \ + static struct asn1_test_digest _name ## _expected[] = { \ + __VA_ARGS__ \ + }; \ + static struct asn1_test _name = { \ + .type = _type, \ + .image = & _name ## __image, \ + .expected = _name ## _expected, \ + .count = ( sizeof ( _name ## _expected ) / \ + sizeof ( _name ## _expected[0] ) ), \ + }; + +extern void asn1_okx ( struct asn1_test *test, const char *file, + unsigned int line ); + +/** + * Report ASN.1 test result + * + * @v test ASN.1 test + */ +#define asn1_ok( test ) asn1_okx ( test, __FILE__, __LINE__ ) + +#endif /* _ASN1_TEST_H */ diff --git a/src/tests/der_test.c b/src/tests/der_test.c new file mode 100644 index 000000000..00cc644f4 --- /dev/null +++ b/src/tests/der_test.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 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 + * + * DER self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "asn1_test.h" + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define inline expected digest */ +#define DIGEST(...) { { __VA_ARGS__ } } + +/** 32-bit RSA private key */ +ASN1 ( rsa32, &der_image_type, + DATA ( 0x30, 0x2c, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0xb7, 0x56, + 0x5c, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x04, 0x66, + 0xa4, 0xc4, 0x35, 0x02, 0x03, 0x00, 0xda, 0x9f, 0x02, 0x03, + 0x00, 0xd6, 0xaf, 0x02, 0x02, 0x01, 0x59, 0x02, 0x02, 0x4e, + 0xe1, 0x02, 0x03, 0x00, 0xa6, 0x5a ), + DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed, + 0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ) ); + +/** 64-bit RSA private key */ +ASN1 ( rsa64, &der_image_type, + DATA ( 0x30, 0x3e, 0x02, 0x01, 0x00, 0x02, 0x09, 0x00, 0xa1, 0xba, + 0xb5, 0x70, 0x00, 0x89, 0xc0, 0x43, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x08, 0x43, 0x98, 0xc6, 0x3c, 0x5f, 0xdc, 0x98, + 0x01, 0x02, 0x05, 0x00, 0xcf, 0x91, 0x1c, 0x5d, 0x02, 0x05, + 0x00, 0xc7, 0x77, 0x85, 0x1f, 0x02, 0x05, 0x00, 0xbc, 0xb3, + 0x33, 0x91, 0x02, 0x04, 0x1b, 0xf9, 0x38, 0x13, 0x02, 0x04, + 0x19, 0xf2, 0x58, 0x86 ), + DIGEST ( 0xee, 0x17, 0x32, 0x31, 0xf0, 0x3d, 0xfd, 0xaa, 0x9b, 0x47, + 0xaf, 0x7b, 0x4b, 0x52, 0x0b, 0xb1, 0xab, 0x25, 0x3f, 0x11 ) ); + +/** + * Perform DER self-test + * + */ +static void der_test_exec ( void ) { + + /* Perform tests */ + asn1_ok ( &rsa32 ); + asn1_ok ( &rsa64 ); +} + +/** DER self-test */ +struct self_test der_test __self_test = { + .name = "der", + .exec = der_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 0ec885f4f..b9679b49e 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -69,3 +69,4 @@ REQUIRE_OBJECT ( pccrc_test ); REQUIRE_OBJECT ( linebuf_test ); REQUIRE_OBJECT ( iobuf_test ); REQUIRE_OBJECT ( bitops_test ); +REQUIRE_OBJECT ( der_test ); From 84add97ce9e094e9299db181d53ba8859f4a3e67 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jul 2016 22:51:50 +0100 Subject: [PATCH 295/591] [crypto] Add PEM image format Add PEM-encoded ASN.1 as an image format. We accept as PEM any image containing a line starting with a "-----BEGIN" boundary marker. We allow for PEM files containing multiple ASN.1 objects, such as a certificate chain produced by concatenating individual certificate files. Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/general.h | 1 + src/image/pem.c | 208 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/pem.h | 22 ++++ src/tests/pem_test.c | 107 +++++++++++++++++++ src/tests/tests.c | 1 + 7 files changed, 343 insertions(+) create mode 100644 src/image/pem.c create mode 100644 src/include/ipxe/pem.h create mode 100644 src/tests/pem_test.c diff --git a/src/config/config.c b/src/config/config.c index acdbebaac..c24b58d5b 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -191,6 +191,9 @@ REQUIRE_OBJECT ( png ); #ifdef IMAGE_DER REQUIRE_OBJECT ( der ); #endif +#ifdef IMAGE_PEM +REQUIRE_OBJECT ( pem ); +#endif /* * Drag in all requested commands diff --git a/src/config/general.h b/src/config/general.h index 6ff4b74a4..efded483a 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -113,6 +113,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define IMAGE_PNM /* PNM image support */ //#define IMAGE_PNG /* PNG image support */ //#define IMAGE_DER /* DER image support */ +//#define IMAGE_PEM /* PEM image support */ /* * Command-line commands to include diff --git a/src/image/pem.c b/src/image/pem.c new file mode 100644 index 000000000..721b11ec6 --- /dev/null +++ b/src/image/pem.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * PEM-encoded ASN.1 data + * + */ + +/** + * Locate next line + * + * @v image PEM image + * @v offset Starting offset + * @ret next Offset to next line + */ +static size_t pem_next ( struct image *image, size_t offset ) { + off_t eol; + + /* Find and skip next newline character, if any */ + eol = memchr_user ( image->data, offset, '\n', ( image->len - offset )); + if ( eol < 0 ) + return image->len; + return ( eol + 1 ); +} + +/** + * Locate boundary marker line + * + * @v image PEM image + * @v offset Starting offset + * @v marker Boundary marker + * @ret offset Offset to boundary marker line, or negative error + */ +static int pem_marker ( struct image *image, size_t offset, + const char *marker ) { + char buf[ strlen ( marker ) ]; + + /* Sanity check */ + assert ( offset <= image->len ); + + /* Scan for marker at start of line */ + while ( offset < image->len ) { + + /* Check for marker */ + if ( ( image->len - offset ) < sizeof ( buf ) ) + break; + copy_from_user ( buf, image->data, offset, sizeof ( buf ) ); + if ( memcmp ( buf, marker, sizeof ( buf ) ) == 0 ) + return offset; + + /* Move to next line */ + offset = pem_next ( image, offset ); + assert ( offset <= image->len ); + } + + return -ENOENT; +} + +/** + * Extract ASN.1 object from image + * + * @v image PEM image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int pem_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + size_t encoded_len; + size_t decoded_max_len; + char *encoded; + void *decoded; + int begin; + int end; + int len; + int rc; + + /* Locate and skip BEGIN marker */ + begin = pem_marker ( image, offset, PEM_BEGIN ); + if ( begin < 0 ) { + rc = begin; + DBGC ( image, "PEM %s [%#zx,%#zx) missing BEGIN marker: %s\n", + image->name, offset, image->len, strerror ( rc ) ); + goto err_begin; + } + begin = pem_next ( image, begin ); + + /* Locate and skip END marker */ + end = pem_marker ( image, begin, PEM_END ); + if ( end < 0 ) { + rc = end; + DBGC ( image, "PEM %s [%#zx,%#zx) missing END marker: %s\n", + image->name, offset, image->len, strerror ( rc ) ); + goto err_end; + } + encoded_len = ( end - begin ); + end = pem_next ( image, end ); + + /* Extract Base64-encoded data */ + encoded = malloc ( encoded_len + 1 /* NUL */ ); + if ( ! encoded ) { + rc = -ENOMEM; + goto err_alloc_encoded; + } + copy_from_user ( encoded, image->data, begin, encoded_len ); + encoded[encoded_len] = '\0'; + + /* Allocate cursor and data buffer */ + decoded_max_len = base64_decoded_max_len ( encoded ); + *cursor = malloc ( sizeof ( **cursor ) + decoded_max_len ); + if ( ! *cursor ) { + rc = -ENOMEM; + goto err_alloc_decoded; + } + decoded = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); + + /* Decode Base64-encoded data */ + len = base64_decode ( encoded, decoded, decoded_max_len ); + if ( len < 0 ) { + rc = len; + DBGC ( image, "PEM %s could not decode: %s\n", + image->name, strerror ( rc ) ); + goto err_decode; + } + (*cursor)->data = decoded; + (*cursor)->len = len; + assert ( (*cursor)->len <= decoded_max_len ); + + /* Free Base64-encoded data */ + free ( encoded ); + + /* Update offset and skip any unencapsulated trailer */ + offset = end; + if ( pem_marker ( image, offset, PEM_BEGIN ) < 0 ) + offset = image->len; + + return offset; + + err_decode: + free ( decoded ); + err_alloc_decoded: + free ( encoded ); + err_alloc_encoded: + err_end: + err_begin: + return rc; +} + +/** + * Probe PEM image + * + * @v image PEM image + * @ret rc Return status code + */ +static int pem_probe ( struct image *image ) { + int rc; + + /* Check that image contains a BEGIN marker */ + if ( ( rc = pem_marker ( image, 0, PEM_BEGIN ) ) < 0 ) { + DBGC ( image, "PEM %s has no BEGIN marker: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** PEM image type */ +struct image_type pem_image_type __image_type ( PROBE_NORMAL ) = { + .name = "PEM", + .probe = pem_probe, + .asn1 = pem_asn1, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 61e208a52..f28e58948 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -277,6 +277,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_pnm ( ERRFILE_IMAGE | 0x00060000 ) #define ERRFILE_png ( ERRFILE_IMAGE | 0x00070000 ) #define ERRFILE_der ( ERRFILE_IMAGE | 0x00080000 ) +#define ERRFILE_pem ( ERRFILE_IMAGE | 0x00090000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/include/ipxe/pem.h b/src/include/ipxe/pem.h new file mode 100644 index 000000000..1276f94ad --- /dev/null +++ b/src/include/ipxe/pem.h @@ -0,0 +1,22 @@ +#ifndef _IPXE_PEM_H +#define _IPXE_PEM_H + +/** @file + * + * PEM image format + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Pre-encapsulation boundary marker */ +#define PEM_BEGIN "-----BEGIN" + +/** Post-encapsulation boundary marker */ +#define PEM_END "-----END" + +extern struct image_type pem_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_PEM_H */ diff --git a/src/tests/pem_test.c b/src/tests/pem_test.c new file mode 100644 index 000000000..df47ad501 --- /dev/null +++ b/src/tests/pem_test.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 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 + * + * PEM self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "asn1_test.h" + +/** Define inline expected digest */ +#define DIGEST(...) { { __VA_ARGS__ } } + +/** Single RSA private key */ +ASN1 ( single, &pem_image_type, + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC6loItAgMBAAECBCqhYIkCAwDyVwIDAMUbAgMAr9kCAmr9AgIaWQ==\n" + "-----END RSA PRIVATE KEY-----\n", + DIGEST ( 0xb9, 0x38, 0x83, 0xcd, 0xf4, 0x58, 0xa9, 0xa2, 0x84, 0x11, + 0xfa, 0x0b, 0x6f, 0xdc, 0x3e, 0xa3, 0x7c, 0x90, 0x7c, 0x2d ) ); + +/** Three concatenated RSA private keys */ +ASN1 ( multiple, &pem_image_type, + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQDtbjyVAgMBAAECBQCEOtJxAgMA+xsCAwDyDwICLGsCAgqTAgIxVQ==\n" + "-----END RSA PRIVATE KEY-----\n" + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC3VlyxAgMBAAECBGakxDUCAwDanwIDANavAgIBWQICTuECAwCmWg==\n" + "-----END RSA PRIVATE KEY-----\n" + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC89dS1AgMBAAECBQCxjnLBAgMA3qcCAwDZQwICP3cCAgpRAgI57A==\n" + "-----END RSA PRIVATE KEY-----\n", + DIGEST ( 0x9c, 0xb2, 0xc1, 0xa0, 0x9c, 0xcb, 0x11, 0xbf, 0x80, 0xd0, + 0x8c, 0xe5, 0xda, 0xf2, 0x3b, 0x2c, 0xca, 0x64, 0x25, 0x8a ), + DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed, + 0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ), + DIGEST ( 0x01, 0xd2, 0x8a, 0x74, 0x42, 0x08, 0x0f, 0xb0, 0x03, 0x82, + 0xcd, 0xa3, 0xdc, 0x78, 0xfe, 0xd7, 0xa3, 0x28, 0xfc, 0x29 ) ); + +/** Two RSA private keys with various bits of noise added */ +ASN1 ( noisy, &pem_image_type, + "Hello world! This is uninteresting stuff before the actual data.\n" + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC3VlyxAgMBAAECBGakxDUCAwDanwIDANavAgIBWQICTuECAwCmWg==\n" + "-----END RSA PRIVATE KEY-----\n" + "Here is some more uninteresting stuff.\n" + "Followed by what is actually another RSA private key, but with " + "extra whitespace added, and the description change to pretend " + "it's a certificate\n" + "-----BEGIN CERTIFICATE-----\n" + " MCwCAQACBQC6loItAgMBAAECBCqhYIkCAwD\r\n" + " yVwIDAMUbAgMAr9kCAmr9AgIaWQ== \r\n" + "-----END CERTIFICATE-----\n" + "and some trailing garbage as well\n" + "and more garbage with no final newline", + DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed, + 0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ), + DIGEST ( 0xb9, 0x38, 0x83, 0xcd, 0xf4, 0x58, 0xa9, 0xa2, 0x84, 0x11, + 0xfa, 0x0b, 0x6f, 0xdc, 0x3e, 0xa3, 0x7c, 0x90, 0x7c, 0x2d ) ); + +/** + * Perform PEM self-test + * + */ +static void pem_test_exec ( void ) { + + /* Perform tests */ + asn1_ok ( &single ); + asn1_ok ( &multiple ); + asn1_ok ( &noisy ); +} + +/** PEM self-test */ +struct self_test pem_test __self_test = { + .name = "pem", + .exec = pem_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index b9679b49e..39c5136ea 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -70,3 +70,4 @@ REQUIRE_OBJECT ( linebuf_test ); REQUIRE_OBJECT ( iobuf_test ); REQUIRE_OBJECT ( bitops_test ); REQUIRE_OBJECT ( der_test ); +REQUIRE_OBJECT ( pem_test ); From 829fedafcb107d41fb0753361acae5efee376c58 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Jul 2016 16:22:08 +0100 Subject: [PATCH 296/591] [image] Use image_asn1() to extract data from CMS signature images Signed-off-by: Michael Brown --- src/hci/commands/image_trust_cmd.c | 1 + src/usr/imgtrust.c | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index f9d6b5b3e..03e3e4431 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -181,3 +181,4 @@ REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); REQUIRE_OBJECT ( sha1 ); REQUIRE_OBJECT ( sha256 ); +REQUIRE_OBJECT ( der ); diff --git a/src/usr/imgtrust.c b/src/usr/imgtrust.c index a269833a6..595ea6b25 100644 --- a/src/usr/imgtrust.c +++ b/src/usr/imgtrust.c @@ -50,30 +50,28 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int imgverify ( struct image *image, struct image *signature, const char *name ) { - size_t len; - void *data; + struct asn1_cursor *data; struct cms_signature *sig; struct cms_signer_info *info; time_t now; + int next; int rc; /* Mark image as untrusted */ image_untrust ( image ); - /* Copy signature to internal memory */ - len = signature->len; - data = malloc ( len ); - if ( ! data ) { - rc = -ENOMEM; - goto err_alloc; + /* Get raw signature data */ + next = image_asn1 ( signature, 0, &data ); + if ( next < 0 ) { + rc = next; + goto err_asn1; } - copy_from_user ( data, signature->data, 0, len ); /* Parse signature */ - if ( ( rc = cms_signature ( data, len, &sig ) ) != 0 ) + if ( ( rc = cms_signature ( data->data, data->len, &sig ) ) != 0 ) goto err_parse; - /* Free internal copy of signature */ + /* Free raw signature data */ free ( data ); data = NULL; @@ -107,7 +105,7 @@ int imgverify ( struct image *image, struct image *signature, cms_put ( sig ); err_parse: free ( data ); - err_alloc: + err_asn1: syslog ( LOG_ERR, "Image \"%s\" signature bad: %s\n", image->name, strerror ( rc ) ); return rc; From b7e43b033556106ef3e8b51e6c51710f1d3c999c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Jul 2016 15:18:35 +0100 Subject: [PATCH 297/591] [build] Remove obsolete explicit object requirements As of commit b1caa48 ("[crypto] Support SHA-{224,384,512} in X.509 certificates"), the list of supported cryptographic algorithms is controlled by config/crypto.h. Signed-off-by: Michael Brown --- src/hci/commands/image_trust_cmd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index 03e3e4431..d9d37d1d6 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -177,8 +177,4 @@ struct command image_trust_commands[] __command = { REQUIRING_SYMBOL ( image_trust_commands ); /* Drag in objects typically required for signature verification */ -REQUIRE_OBJECT ( rsa ); -REQUIRE_OBJECT ( md5 ); -REQUIRE_OBJECT ( sha1 ); -REQUIRE_OBJECT ( sha256 ); REQUIRE_OBJECT ( der ); From 942b798c8d143042fc17c7fadea528fee5cbebc2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Jul 2016 15:40:39 +0100 Subject: [PATCH 298/591] [crypto] Enable both DER and PEM formats by default Enable both IMAGE_DER and IMAGE_PEM by default, and drag in the relevant objects only when image_asn1() is present in the binary. This allows "imgverify" to transparently use either DER or PEM signature files. Signed-off-by: Michael Brown --- src/config/config.c | 6 ------ src/config/config_asn1.c | 39 +++++++++++++++++++++++++++++++++++++ src/config/general.h | 4 ++-- src/core/image.c | 35 --------------------------------- src/crypto/asn1.c | 42 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 43 deletions(-) create mode 100644 src/config/config_asn1.c diff --git a/src/config/config.c b/src/config/config.c index c24b58d5b..e24cfe0d0 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -188,12 +188,6 @@ REQUIRE_OBJECT ( pnm ); #ifdef IMAGE_PNG REQUIRE_OBJECT ( png ); #endif -#ifdef IMAGE_DER -REQUIRE_OBJECT ( der ); -#endif -#ifdef IMAGE_PEM -REQUIRE_OBJECT ( pem ); -#endif /* * Drag in all requested commands diff --git a/src/config/config_asn1.c b/src/config/config_asn1.c new file mode 100644 index 000000000..c4419d04d --- /dev/null +++ b/src/config/config_asn1.c @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * ASN.1 file format configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +#ifdef IMAGE_DER +REQUIRE_OBJECT ( der ); +#endif +#ifdef IMAGE_PEM +REQUIRE_OBJECT ( pem ); +#endif diff --git a/src/config/general.h b/src/config/general.h index efded483a..38d629ccd 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -112,8 +112,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define IMAGE_SDI /* SDI image support */ //#define IMAGE_PNM /* PNM image support */ //#define IMAGE_PNG /* PNG image support */ -//#define IMAGE_DER /* DER image support */ -//#define IMAGE_PEM /* PEM image support */ +#define IMAGE_DER /* DER image support */ +#define IMAGE_PEM /* PEM image support */ /* * Command-line commands to include diff --git a/src/core/image.c b/src/core/image.c index b4785269b..a185b82f4 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -505,38 +505,3 @@ int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { return 0; } - -/** - * Extract ASN.1 object from image - * - * @v image Image - * @v offset Offset within image - * @v cursor ASN.1 cursor to fill in - * @ret next Offset to next image, or negative error - * - * The caller is responsible for eventually calling free() on the - * allocated ASN.1 cursor. - */ -int image_asn1 ( struct image *image, size_t offset, - struct asn1_cursor **cursor ) { - int next; - int rc; - - /* Sanity check */ - assert ( offset <= image->len ); - - /* Check that this image can be used to extract an ASN.1 object */ - if ( ! ( image->type && image->type->asn1 ) ) - return -ENOTSUP; - - /* Try creating ASN.1 cursor */ - next = image->type->asn1 ( image, offset, cursor ); - if ( next < 0 ) { - rc = next; - DBGC ( image, "IMAGE %s could not extract ASN.1 object: %s\n", - image->name, strerror ( rc ) ); - return rc; - } - - return next; -} diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index 03eb18f7b..ff56e1f3b 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -838,3 +839,44 @@ int asn1_wrap ( struct asn1_builder *builder, unsigned int type ) { return 0; } + +/** + * Extract ASN.1 object from image + * + * @v image Image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +int image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Sanity check */ + assert ( offset <= image->len ); + + /* Check that this image can be used to extract an ASN.1 object */ + if ( ! ( image->type && image->type->asn1 ) ) + return -ENOTSUP; + + /* Try creating ASN.1 cursor */ + next = image->type->asn1 ( image, offset, cursor ); + if ( next < 0 ) { + rc = next; + DBGC ( image, "IMAGE %s could not extract ASN.1 object: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} + +/* Drag in objects via image_asn1() */ +REQUIRING_SYMBOL ( image_asn1 ); + +/* Drag in ASN.1 image formats */ +REQUIRE_OBJECT ( config_asn1 ); From 1090839b94bcfec75b87438f154071908108927b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Jul 2016 15:56:10 +0100 Subject: [PATCH 299/591] [build] Remove more obsolete explicit object requirements Signed-off-by: Michael Brown --- src/hci/commands/image_trust_cmd.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index d9d37d1d6..b34378f93 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -172,9 +172,3 @@ struct command image_trust_commands[] __command = { .exec = imgverify_exec, }, }; - -/* Drag in objects via command list */ -REQUIRING_SYMBOL ( image_trust_commands ); - -/* Drag in objects typically required for signature verification */ -REQUIRE_OBJECT ( der ); From 2afd66eb55996500499eb3bcc39c66ff042679c8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Jul 2016 15:58:59 +0100 Subject: [PATCH 300/591] [pixbuf] Enable PNG format by default Enable IMAGE_PNG (but not IMAGE_PNM) by default, and drag in the relevant objects only when image_pixbuf() is present in the binary. Signed-off-by: Michael Brown --- src/config/config.c | 6 ------ src/config/config_pixbuf.c | 39 ++++++++++++++++++++++++++++++++++++++ src/config/general.h | 2 +- src/core/image.c | 24 ----------------------- src/core/pixbuf.c | 32 +++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 6 files changed, 73 insertions(+), 31 deletions(-) create mode 100644 src/config/config_pixbuf.c diff --git a/src/config/config.c b/src/config/config.c index e24cfe0d0..c6683261f 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -182,12 +182,6 @@ REQUIRE_OBJECT ( efi_image ); #ifdef IMAGE_SDI REQUIRE_OBJECT ( sdi ); #endif -#ifdef IMAGE_PNM -REQUIRE_OBJECT ( pnm ); -#endif -#ifdef IMAGE_PNG -REQUIRE_OBJECT ( png ); -#endif /* * Drag in all requested commands diff --git a/src/config/config_pixbuf.c b/src/config/config_pixbuf.c new file mode 100644 index 000000000..f8ff59daf --- /dev/null +++ b/src/config/config_pixbuf.c @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Pixel buffer file format configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +#ifdef IMAGE_PNM +REQUIRE_OBJECT ( pnm ); +#endif +#ifdef IMAGE_PNG +REQUIRE_OBJECT ( png ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 38d629ccd..4b9904898 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -111,7 +111,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define IMAGE_EFI /* EFI image support */ //#define IMAGE_SDI /* SDI image support */ //#define IMAGE_PNM /* PNM image support */ -//#define IMAGE_PNG /* PNG image support */ +#define IMAGE_PNG /* PNG image support */ #define IMAGE_DER /* DER image support */ #define IMAGE_PEM /* PEM image support */ diff --git a/src/core/image.c b/src/core/image.c index a185b82f4..078ce1bb9 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -481,27 +481,3 @@ int image_set_trust ( int require_trusted, int permanent ) { return 0; } - -/** - * Create pixel buffer from image - * - * @v image Image - * @v pixbuf Pixel buffer to fill in - * @ret rc Return status code - */ -int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { - int rc; - - /* Check that this image can be used to create a pixel buffer */ - if ( ! ( image->type && image->type->pixbuf ) ) - return -ENOTSUP; - - /* Try creating pixel buffer */ - if ( ( rc = image->type->pixbuf ( image, pixbuf ) ) != 0 ) { - DBGC ( image, "IMAGE %s could not create pixel buffer: %s\n", - image->name, strerror ( rc ) ); - return rc; - } - - return 0; -} diff --git a/src/core/pixbuf.c b/src/core/pixbuf.c index c12bd3c06..4742d285d 100644 --- a/src/core/pixbuf.c +++ b/src/core/pixbuf.c @@ -30,7 +30,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include +#include #include +#include #include /** @@ -82,3 +84,33 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { err_alloc_pixbuf: return NULL; } + +/** + * Create pixel buffer from image + * + * @v image Image + * @v pixbuf Pixel buffer to fill in + * @ret rc Return status code + */ +int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { + int rc; + + /* Check that this image can be used to create a pixel buffer */ + if ( ! ( image->type && image->type->pixbuf ) ) + return -ENOTSUP; + + /* Try creating pixel buffer */ + if ( ( rc = image->type->pixbuf ( image, pixbuf ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not create pixel buffer: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/* Drag in objects via image_pixbuf() */ +REQUIRING_SYMBOL ( image_pixbuf ); + +/* Drag in pixel buffer image formats */ +REQUIRE_OBJECT ( config_pixbuf ); diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index f28e58948..9ec22fb01 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -70,6 +70,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_ansicoldef ( ERRFILE_CORE | 0x001e0000 ) #define ERRFILE_fault ( ERRFILE_CORE | 0x001f0000 ) #define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) +#define ERRFILE_pixbuf ( ERRFILE_CORE | 0x00210000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) From e564a4e7d6b5aa0dca94399c695f2d7cac949648 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 25 Aug 2016 15:35:44 +0100 Subject: [PATCH 301/591] [crypto] Add image_x509() to extract X.509 certificates from image Signed-off-by: Michael Brown --- src/crypto/x509.c | 42 +++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/x509.h | 4 ++++ 2 files changed, 46 insertions(+) diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 43a4ca17a..28267191e 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -1766,6 +1767,47 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time, return -EACCES_USELESS; } +/** + * Extract X.509 certificate object from image + * + * @v image Image + * @v offset Offset within image + * @ret cert X.509 certificate + * @ret next Offset to next image, or negative error + * + * On success, the caller holds a reference to the X.509 certificate, + * and is responsible for ultimately calling x509_put(). + */ +int image_x509 ( struct image *image, size_t offset, + struct x509_certificate **cert ) { + struct asn1_cursor *cursor; + int next; + int rc; + + /* Get ASN.1 object */ + next = image_asn1 ( image, offset, &cursor ); + if ( next < 0 ) { + rc = next; + goto err_asn1; + } + + /* Parse certificate */ + if ( ( rc = x509_certificate ( cursor->data, cursor->len, + cert ) ) != 0 ) + goto err_certificate; + + /* Free ASN.1 object */ + free ( cursor ); + + return next; + + x509_put ( *cert ); + err_certificate: + free ( cursor ); + err_asn1: + return rc; +} + /* Drag in objects via x509_validate() */ REQUIRING_SYMBOL ( x509_validate ); diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index 0daaf5e59..80c2e3c66 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -16,6 +16,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +struct image; + /** An X.509 serial number */ struct x509_serial { /** Raw serial number */ @@ -358,6 +360,8 @@ extern int x509_auto_append ( struct x509_chain *chain, extern int x509_validate_chain ( struct x509_chain *chain, time_t time, struct x509_chain *store, struct x509_root *root ); +extern int image_x509 ( struct image *image, size_t offset, + struct x509_certificate **cert ); /* Functions exposed only for unit testing */ extern int x509_check_issuer ( struct x509_certificate *cert, From ff28b22568ebc2cb885beae5d0c95ddcf94dca8a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 25 Aug 2016 15:41:57 +0100 Subject: [PATCH 302/591] [crypto] Generalise X.509 "valid" field to a "flags" field Signed-off-by: Michael Brown --- src/crypto/ocsp.c | 2 +- src/crypto/x509.c | 8 ++++---- src/include/ipxe/x509.h | 21 ++++++++++++++++++--- src/net/validator.c | 2 +- src/tests/ocsp_test.c | 2 +- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index e7adcdba9..b83f4c035 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -282,7 +282,7 @@ int ocsp_check ( struct x509_certificate *cert, /* Sanity checks */ assert ( cert != NULL ); assert ( issuer != NULL ); - assert ( issuer->valid ); + assert ( x509_is_valid ( issuer ) ); /* Allocate and initialise check */ *ocsp = zalloc ( sizeof ( **ocsp ) ); diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 28267191e..4d9515092 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -1320,7 +1320,7 @@ int x509_validate ( struct x509_certificate *cert, root = &root_certificates; /* Return success if certificate has already been validated */ - if ( cert->valid ) + if ( x509_is_valid ( cert ) ) return 0; /* Fail if certificate is invalid at specified time */ @@ -1329,7 +1329,7 @@ int x509_validate ( struct x509_certificate *cert, /* Succeed if certificate is a trusted root certificate */ if ( x509_check_root ( cert, root ) == 0 ) { - cert->valid = 1; + cert->flags |= X509_FL_VALIDATED; cert->path_remaining = ( cert->extensions.basic.path_len + 1 ); return 0; } @@ -1342,7 +1342,7 @@ int x509_validate ( struct x509_certificate *cert, } /* Fail unless issuer has already been validated */ - if ( ! issuer->valid ) { + if ( ! x509_is_valid ( issuer ) ) { DBGC ( cert, "X509 %p \"%s\" ", cert, x509_name ( cert ) ); DBGC ( cert, "issuer %p \"%s\" has not yet been validated\n", issuer, x509_name ( issuer ) ); @@ -1376,7 +1376,7 @@ int x509_validate ( struct x509_certificate *cert, cert->path_remaining = max_path_remaining; /* Mark certificate as valid */ - cert->valid = 1; + cert->flags |= X509_FL_VALIDATED; DBGC ( cert, "X509 %p \"%s\" successfully validated using ", cert, x509_name ( cert ) ); diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index 80c2e3c66..58f91c01f 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -189,8 +189,8 @@ struct x509_certificate { /** Link in certificate store */ struct x509_link store; - /** Certificate has been validated */ - int valid; + /** Flags */ + unsigned int flags; /** Maximum number of subsequent certificates in chain */ unsigned int path_remaining; @@ -216,6 +216,12 @@ struct x509_certificate { struct x509_extensions extensions; }; +/** X.509 certificate flags */ +enum x509_flags { + /** Certificate has been validated */ + X509_FL_VALIDATED = 0x0001, +}; + /** * Get reference to X.509 certificate * @@ -373,13 +379,22 @@ extern int x509_check_root ( struct x509_certificate *cert, struct x509_root *root ); extern int x509_check_time ( struct x509_certificate *cert, time_t time ); +/** + * Check if X.509 certificate is valid + * + * @v cert X.509 certificate + */ +static inline int x509_is_valid ( struct x509_certificate *cert ) { + return ( cert->flags & X509_FL_VALIDATED ); +} + /** * Invalidate X.509 certificate * * @v cert X.509 certificate */ static inline void x509_invalidate ( struct x509_certificate *cert ) { - cert->valid = 0; + cert->flags &= ~X509_FL_VALIDATED; cert->path_remaining = 0; } diff --git a/src/net/validator.c b/src/net/validator.c index 57ad0e7b6..52845b6ed 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -478,7 +478,7 @@ static void validator_step ( struct validator *validator ) { issuer = link->cert; if ( ! cert ) continue; - if ( ! issuer->valid ) + if ( ! x509_is_valid ( issuer ) ) continue; /* The issuer is valid, but this certificate is not * yet valid. If OCSP is applicable, start it. diff --git a/src/tests/ocsp_test.c b/src/tests/ocsp_test.c index c6d458596..a3349346a 100644 --- a/src/tests/ocsp_test.c +++ b/src/tests/ocsp_test.c @@ -110,7 +110,7 @@ static void ocsp_prepare_test ( struct ocsp_test *test ) { x509_invalidate ( cert ); /* Force-validate issuer certificate */ - issuer->valid = 1; + issuer->flags |= X509_FL_VALIDATED; issuer->path_remaining = ( issuer->extensions.basic.path_len + 1 ); } From 161c80af5bacf52a9f488551361c25be601eabb9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 31 Aug 2016 15:05:22 +0100 Subject: [PATCH 303/591] [list] Add list_next_entry() and list_prev_entry() Signed-off-by: Michael Brown --- src/include/ipxe/list.h | 28 ++++++++++++++++++++++++++++ src/tests/list_test.c | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/include/ipxe/list.h b/src/include/ipxe/list.h index 6a9b76f91..274fb64c6 100644 --- a/src/include/ipxe/list.h +++ b/src/include/ipxe/list.h @@ -348,6 +348,34 @@ extern void extern_list_splice_tail_init ( struct list_head *list, ( type * ) NULL : \ list_entry ( (list)->prev, type, member ) ) +/** + * Get the container of the next entry in a list + * + * @v pos Current list entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret next Next list entry, or NULL at end of list + */ +#define list_next_entry( pos, head, member ) ( { \ + typeof (pos) next = list_entry ( (pos)->member.next, \ + typeof ( *(pos) ), \ + member ); \ + ( ( &next->member == (head) ) ? NULL : next ); } ) + +/** + * Get the container of the previous entry in a list + * + * @v pos Current list entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret next Next list entry, or NULL at end of list + */ +#define list_prev_entry( pos, head, member ) ( { \ + typeof (pos) prev = list_entry ( (pos)->member.prev, \ + typeof ( *(pos) ), \ + member ); \ + ( ( &prev->member == (head) ) ? NULL : prev ); } ) + /** * Iterate over a list * diff --git a/src/tests/list_test.c b/src/tests/list_test.c index 352c87da0..f016a32eb 100644 --- a/src/tests/list_test.c +++ b/src/tests/list_test.c @@ -396,6 +396,29 @@ static void list_test_exec ( void ) { ok ( list_first_entry ( list, struct list_test, list ) == NULL ); ok ( list_last_entry ( list, struct list_test, list ) == NULL ); + /* Test list_next_entry() and list_prev_entry() */ + INIT_LIST_HEAD ( list ); + list_add_tail ( &list_tests[5].list, list ); + list_add_tail ( &list_tests[3].list, list ); + list_add_tail ( &list_tests[1].list, list ); + list_add_tail ( &list_tests[7].list, list ); + ok ( list_prev_entry ( &list_tests[5], list, list ) == NULL ); + ok ( list_next_entry ( &list_tests[5], list, list ) == &list_tests[3] ); + ok ( list_prev_entry ( &list_tests[3], list, list ) == &list_tests[5] ); + ok ( list_next_entry ( &list_tests[3], list, list ) == &list_tests[1] ); + ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[3] ); + ok ( list_next_entry ( &list_tests[1], list, list ) == &list_tests[7] ); + ok ( list_prev_entry ( &list_tests[7], list, list ) == &list_tests[1] ); + ok ( list_next_entry ( &list_tests[7], list, list ) == NULL ); + list_del ( &list_tests[7].list ); + ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[3] ); + ok ( list_next_entry ( &list_tests[1], list, list ) == NULL ); + list_del ( &list_tests[3].list ); + ok ( list_prev_entry ( &list_tests[5], list, list ) == NULL ); + ok ( list_next_entry ( &list_tests[5], list, list ) == &list_tests[1] ); + ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[5] ); + ok ( list_next_entry ( &list_tests[1], list, list ) == NULL ); + /* Test list_for_each() */ INIT_LIST_HEAD ( list ); list_add_tail ( &list_tests[6].list, list ); From 534eae4d92ba6e9b2378db22c65992f136b499c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 31 Aug 2016 15:16:43 +0100 Subject: [PATCH 304/591] [crypto] Expose certstore_del() to explicitly remove stored certificates Signed-off-by: Michael Brown --- src/crypto/certstore.c | 19 +++++++++++++++---- src/include/ipxe/certstore.h | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index e62d8330b..9809413a0 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -145,6 +145,20 @@ void certstore_add ( struct x509_certificate *cert ) { x509_name ( cert ) ); } +/** + * Remove certificate from store + * + * @v cert X.509 certificate + */ +void certstore_del ( struct x509_certificate *cert ) { + + /* Remove certificate from store */ + DBGC ( &certstore, "CERTSTORE removed certificate %s\n", + x509_name ( cert ) ); + list_del ( &cert->store.list ); + x509_put ( cert ); +} + /** * Discard a stored certificate * @@ -158,10 +172,7 @@ static unsigned int certstore_discard ( void ) { */ list_for_each_entry_reverse ( cert, &certstore.links, store.list ) { if ( cert->refcnt.count == 0 ) { - DBGC ( &certstore, "CERTSTORE discarded certificate " - "%s\n", x509_name ( cert ) ); - list_del ( &cert->store.list ); - x509_put ( cert ); + certstore_del ( cert ); return 1; } } diff --git a/src/include/ipxe/certstore.h b/src/include/ipxe/certstore.h index 49b3b512c..e4c789cfd 100644 --- a/src/include/ipxe/certstore.h +++ b/src/include/ipxe/certstore.h @@ -17,5 +17,6 @@ extern struct x509_chain certstore; extern struct x509_certificate * certstore_find ( struct asn1_cursor *raw ); extern struct x509_certificate * certstore_find_key ( struct asn1_cursor *key ); extern void certstore_add ( struct x509_certificate *cert ); +extern void certstore_del ( struct x509_certificate *cert ); #endif /* _IPXE_CERTSTORE_H */ From 9a1a42f2830ac797070cb6f807869872d7e7c19a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 25 Aug 2016 15:38:58 +0100 Subject: [PATCH 305/591] [crypto] Allow certificates to be marked as having been added explicitly Allow certificates to be marked as having been added explicitly at run time. Such certificates will not be discarded via the certificate store cache discarder. Signed-off-by: Michael Brown --- src/crypto/certstore.c | 23 +++++++++++++++++++---- src/include/ipxe/x509.h | 4 ++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index 9809413a0..d0ef5c5d5 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -152,6 +152,10 @@ void certstore_add ( struct x509_certificate *cert ) { */ void certstore_del ( struct x509_certificate *cert ) { + /* Ignore attempts to remove permanent certificates */ + if ( cert->flags & X509_FL_PERMANENT ) + return; + /* Remove certificate from store */ DBGC ( &certstore, "CERTSTORE removed certificate %s\n", x509_name ( cert ) ); @@ -171,11 +175,22 @@ static unsigned int certstore_discard ( void ) { * only reference is held by the store itself. */ list_for_each_entry_reverse ( cert, &certstore.links, store.list ) { - if ( cert->refcnt.count == 0 ) { - certstore_del ( cert ); - return 1; - } + + /* Skip certificates for which another reference is held */ + if ( cert->refcnt.count > 0 ) + continue; + + /* Skip certificates that were added at build time or + * added explicitly at run time. + */ + if ( cert->flags & ( X509_FL_PERMANENT | X509_FL_EXPLICIT ) ) + continue; + + /* Discard certificate */ + certstore_del ( cert ); + return 1; } + return 0; } diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index 58f91c01f..78eeafbfb 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -220,6 +220,10 @@ struct x509_certificate { enum x509_flags { /** Certificate has been validated */ X509_FL_VALIDATED = 0x0001, + /** Certificate was added at build time */ + X509_FL_PERMANENT = 0x0002, + /** Certificate was added explicitly at run time */ + X509_FL_EXPLICIT = 0x0004, }; /** From 1e277ab062d23ffa1b35bd078f9fba5c9b4e6495 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 25 Aug 2016 15:39:43 +0100 Subject: [PATCH 306/591] [crypto] Add certstat() to display basic certificate information Signed-off-by: Michael Brown --- src/crypto/x509.c | 4 +-- src/include/usr/certmgmt.h | 16 ++++++++++ src/usr/certmgmt.c | 63 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/include/usr/certmgmt.h create mode 100644 src/usr/certmgmt.c diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 4d9515092..76ace0313 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -122,10 +122,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x0b, "No usable certificates" ) /** - * Get X.509 certificate name (for debugging) + * Get X.509 certificate display name * * @v cert X.509 certificate - * @ret name Name (for debugging) + * @ret name Display name */ const char * x509_name ( struct x509_certificate *cert ) { struct asn1_cursor *common_name = &cert->subject.common_name; diff --git a/src/include/usr/certmgmt.h b/src/include/usr/certmgmt.h new file mode 100644 index 000000000..4363b03e1 --- /dev/null +++ b/src/include/usr/certmgmt.h @@ -0,0 +1,16 @@ +#ifndef _USR_CERTMGMT_H +#define _USR_CERTMGMT_H + +/** @file + * + * Certificate management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern void certstat ( struct x509_certificate *cert ); + +#endif /* _USR_CERTMGMT_H */ diff --git a/src/usr/certmgmt.c b/src/usr/certmgmt.c new file mode 100644 index 000000000..2f233fe4f --- /dev/null +++ b/src/usr/certmgmt.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Certificate management + * + */ + +/** + * Display status of a certificate + * + * @v cert X.509 certificate + */ +void certstat ( struct x509_certificate *cert ) { + struct digest_algorithm *digest = &sha1_algorithm; + uint8_t fingerprint[ digest->digestsize ]; + char buf[ base16_encoded_len ( sizeof ( fingerprint ) ) + 1 /* NUL */ ]; + + /* Generate fingerprint */ + x509_fingerprint ( cert, digest, fingerprint ); + base16_encode ( fingerprint, sizeof ( fingerprint ), + buf, sizeof ( buf ) ); + + /* Print certificate status */ + printf ( "%s : %s", x509_name ( cert ), buf ); + if ( cert->flags & X509_FL_PERMANENT ) + printf ( " [PERMANENT]" ); + if ( cert->flags & X509_FL_EXPLICIT ) + printf ( " [EXPLICIT]" ); + if ( x509_is_valid ( cert ) ) + printf ( " [VALIDATED]" ); + printf ( "\n" ); +} From eed12580388950d53e5f3436de484e4e170a2c5f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 25 Aug 2016 15:40:27 +0100 Subject: [PATCH 307/591] [cmdline] Add certificate management commands Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/general.h | 1 + src/hci/commands/cert_cmd.c | 304 ++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 4 files changed, 309 insertions(+) create mode 100644 src/hci/commands/cert_cmd.c diff --git a/src/config/config.c b/src/config/config.c index c6683261f..faf9aefa8 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -275,6 +275,9 @@ REQUIRE_OBJECT ( profstat_cmd ); #ifdef NTP_CMD REQUIRE_OBJECT ( ntp_cmd ); #endif +#ifdef CERT_CMD +REQUIRE_OBJECT ( cert_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/config/general.h b/src/config/general.h index 4b9904898..d2bbeb067 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -150,6 +150,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define IPSTAT_CMD /* IP statistics commands */ //#define PROFSTAT_CMD /* Profiling commands */ //#define NTP_CMD /* NTP commands */ +//#define CERT_CMD /* Certificate management commands */ /* * ROM-specific options diff --git a/src/hci/commands/cert_cmd.c b/src/hci/commands/cert_cmd.c new file mode 100644 index 000000000..24b18bf5c --- /dev/null +++ b/src/hci/commands/cert_cmd.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2016 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Certificate management commands + * + */ + +/** "cert" options */ +struct cert_options { + /** Certificate subject name */ + char *name; + /** Keep certificate file after parsing */ + int keep; +}; + +/** "cert" option list */ +static union { + /* "certstore" takes both options */ + struct option_descriptor certstore[2]; + /* "certstat" takes only --subject */ + struct option_descriptor certstat[1]; + /* "certfree" takes only --subject */ + struct option_descriptor certfree[1]; +} opts = { + .certstore = { + OPTION_DESC ( "subject", 's', required_argument, + struct cert_options, name, parse_string ), + OPTION_DESC ( "keep", 'k', no_argument, + struct cert_options, keep, parse_flag ), + }, +}; + +/** A "cert" command descriptor */ +struct cert_command_descriptor { + /** Command descriptor */ + struct command_descriptor cmd; + /** Payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ + int ( * payload ) ( struct x509_certificate *cert ); +}; + +/** + * Construct "cert" command descriptor + * + * @v _struct Options structure type + * @v _options Option descriptor array + * @v _min_args Minimum number of non-option arguments + * @v _max_args Maximum number of non-option arguments + * @v _usage Command usage + * @v _payload Payload method + * @ret _command Command descriptor + */ +#define CERT_COMMAND_DESC( _struct, _options, _min_args, _max_args, \ + _usage, _payload ) \ + { \ + .cmd = COMMAND_DESC ( _struct, _options, _min_args, \ + _max_args, _usage ), \ + .payload = _payload, \ + } + +/** + * Execute "cert" command + * + * @v argc Argument count + * @v argv Argument list + * @v certcmd Command descriptor + * @ret rc Return status code + */ +static int cert_exec ( int argc, char **argv, + struct cert_command_descriptor *certcmd ) { + struct command_descriptor *cmd = &certcmd->cmd; + struct cert_options opts; + struct image *image = NULL; + struct x509_certificate *cert; + struct x509_certificate *tmp; + unsigned int count = 0; + size_t offset = 0; + int next; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, cmd, &opts ) ) != 0 ) + goto err_parse; + + /* Acquire image, if applicable */ + if ( ( optind < argc ) && + ( ( rc = imgacquire ( argv[optind], 0, &image ) ) != 0 ) ) + goto err_acquire; + + /* Get first entry in certificate store */ + tmp = list_first_entry ( &certstore.links, struct x509_certificate, + store.list ); + + /* Iterate over certificates */ + while ( 1 ) { + + /* Get next certificate from image or store as applicable */ + if ( image ) { + + /* Get next certificate from image */ + if ( offset >= image->len ) + break; + next = image_x509 ( image, offset, &cert ); + if ( next < 0 ) { + rc = next; + printf ( "Could not parse certificate: %s\n", + strerror ( rc ) ); + goto err_x509; + } + offset = next; + + } else { + + /* Get next certificate from store */ + cert = tmp; + if ( ! cert ) + break; + tmp = list_next_entry ( tmp, &certstore.links, + store.list ); + x509_get ( cert ); + } + + /* Skip non-matching names, if a name was specified */ + if ( opts.name && ( x509_check_name ( cert, opts.name ) != 0 )){ + x509_put ( cert ); + continue; + } + + /* Execute payload */ + if ( ( rc = certcmd->payload ( cert ) ) != 0 ) { + x509_put ( cert ); + goto err_payload; + } + + /* Count number of certificates processed */ + count++; + + /* Drop reference to certificate */ + x509_put ( cert ); + } + + /* Fail if a name was specified and no matching certificates + * were found. + */ + if ( opts.name && ( count == 0 ) ) { + printf ( "\"%s\" : no such certificate\n", opts.name ); + rc = -ENOENT; + goto err_none; + } + + err_none: + err_payload: + err_x509: + if ( image && ( ! opts.keep ) ) + unregister_image ( image ); + err_acquire: + err_parse: + return rc; +} + +/** + * "certstat" payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ +static int certstat_payload ( struct x509_certificate *cert ) { + + certstat ( cert ); + return 0; +} + +/** "certstat" command descriptor */ +static struct cert_command_descriptor certstat_cmd = + CERT_COMMAND_DESC ( struct cert_options, opts.certstat, 0, 0, NULL, + certstat_payload ); + +/** + * The "certstat" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int certstat_exec ( int argc, char **argv ) { + + return cert_exec ( argc, argv, &certstat_cmd ); +} + +/** + * "certstore" payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ +static int certstore_payload ( struct x509_certificate *cert ) { + + /* Mark certificate as having been added explicitly */ + cert->flags |= X509_FL_EXPLICIT; + + return 0; +} + +/** "certstore" command descriptor */ +static struct cert_command_descriptor certstore_cmd = + CERT_COMMAND_DESC ( struct cert_options, opts.certstore, 0, 1, + "[]", certstore_payload ); + +/** + * The "certstore" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int certstore_exec ( int argc, char **argv ) { + + return cert_exec ( argc, argv, &certstore_cmd ); +} + +/** + * "certfree" payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ +static int certfree_payload ( struct x509_certificate *cert ) { + + /* Remove from certificate store */ + certstore_del ( cert ); + + return 0; +} + +/** "certfree" command descriptor */ +static struct cert_command_descriptor certfree_cmd = + CERT_COMMAND_DESC ( struct cert_options, opts.certfree, 0, 0, NULL, + certfree_payload ); + +/** + * The "certfree" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int certfree_exec ( int argc, char **argv ) { + + return cert_exec ( argc, argv, &certfree_cmd ); +} + +/** Certificate management commands */ +struct command certmgmt_commands[] __command = { + { + .name = "certstat", + .exec = certstat_exec, + }, + { + .name = "certstore", + .exec = certstore_exec, + }, + { + .name = "certfree", + .exec = certfree_exec, + }, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 9ec22fb01..08e166ce7 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -359,6 +359,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_fbcon ( ERRFILE_OTHER | 0x004c0000 ) #define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) #define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) +#define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) /** @} */ From 827dd1bfee67daa683935ce65316f7e0f057fe1c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 31 Aug 2016 17:23:42 +0100 Subject: [PATCH 308/591] [crypto] Mark permanent certificates as permanent Signed-off-by: Michael Brown --- src/crypto/certstore.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index d0ef5c5d5..cdf6fb4dd 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -240,6 +240,7 @@ static void certstore_init ( void ) { * permanent reference to it. */ certstore_add ( cert ); + cert->flags |= X509_FL_PERMANENT; DBGC ( &certstore, "CERTSTORE permanent certificate %d is %s\n", i, x509_name ( cert ) ); } From 54dcfed3759c53526c10744e3d7cf095db0ffcaa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 16 Oct 2016 17:04:30 +0100 Subject: [PATCH 309/591] [efi] Mark AppleNetBoot.h as a native iPXE header AppleNetBoot.h is not taken from the EDK2 codebase and so cannot be imported using include/ipxe/efi/import.pl. Mark as a native iPXE header (by changing the include guard) to avoid breaking the import process. Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/AppleNetBoot.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include/ipxe/efi/Protocol/AppleNetBoot.h b/src/include/ipxe/efi/Protocol/AppleNetBoot.h index 144beff1c..5946524fd 100644 --- a/src/include/ipxe/efi/Protocol/AppleNetBoot.h +++ b/src/include/ipxe/efi/Protocol/AppleNetBoot.h @@ -1,5 +1,5 @@ -#ifndef __EFI_APPLE_NET_BOOT_PROTOCOL_H__ -#define __EFI_APPLE_NET_BOOT_PROTOCOL_H__ +#ifndef _IPXE_EFI_APPLE_NET_BOOT_PROTOCOL_H +#define _IPXE_EFI_APPLE_NET_BOOT_PROTOCOL_H /** @file * @@ -43,4 +43,4 @@ struct _EFI_APPLE_NET_BOOT_PROTOCOL GET_DHCP_RESPONSE GetBsdpResponse; }; -#endif /*__EFI_APPLE_NET_BOOT_PROTOCOL_H__ */ +#endif /*_IPXE_EFI_APPLE_NET_BOOT_PROTOCOL_H */ From f796d5b6b63d3b986996458b4dbed4679e1abb3a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 16 Oct 2016 16:32:49 +0100 Subject: [PATCH 310/591] [efi] Update to current EDK2 headers Signed-off-by: Michael Brown --- src/include/ipxe/efi/AArch64/ProcessorBind.h | 2 +- src/include/ipxe/efi/Arm/ProcessorBind.h | 9 +- src/include/ipxe/efi/Base.h | 151 +++++++++++++++++- src/include/ipxe/efi/Ia32/ProcessorBind.h | 4 +- src/include/ipxe/efi/IndustryStandard/Pci22.h | 56 ++++--- src/include/ipxe/efi/Library/BaseLib.h | 143 ++++++++++++++++- src/include/ipxe/efi/Pi/PiHob.h | 6 +- src/include/ipxe/efi/Protocol/DevicePath.h | 15 +- src/include/ipxe/efi/Protocol/HiiImage.h | 4 +- src/include/ipxe/efi/Protocol/Mtftp4.h | 4 +- src/include/ipxe/efi/Protocol/SimpleTextOut.h | 2 +- .../efi/Uefi/UefiInternalFormRepresentation.h | 24 ++- src/include/ipxe/efi/Uefi/UefiPxe.h | 2 +- src/include/ipxe/efi/Uefi/UefiSpec.h | 8 +- src/include/ipxe/efi/X64/ProcessorBind.h | 17 +- 15 files changed, 401 insertions(+), 46 deletions(-) diff --git a/src/include/ipxe/efi/AArch64/ProcessorBind.h b/src/include/ipxe/efi/AArch64/ProcessorBind.h index d4301726f..858aefe3a 100644 --- a/src/include/ipxe/efi/AArch64/ProcessorBind.h +++ b/src/include/ipxe/efi/AArch64/ProcessorBind.h @@ -34,7 +34,7 @@ FILE_LICENCE ( BSD3 ); #if _MSC_EXTENSIONS // - // use Microsoft* C complier dependent integer width types + // use Microsoft* C compiler dependent integer width types // typedef unsigned __int64 UINT64; typedef __int64 INT64; diff --git a/src/include/ipxe/efi/Arm/ProcessorBind.h b/src/include/ipxe/efi/Arm/ProcessorBind.h index 51a727145..fac183237 100644 --- a/src/include/ipxe/efi/Arm/ProcessorBind.h +++ b/src/include/ipxe/efi/Arm/ProcessorBind.h @@ -30,9 +30,16 @@ FILE_LICENCE ( BSD3 ); #pragma pack() #endif +// +// RVCT does not support the __builtin_unreachable() macro +// +#ifdef __ARMCC_VERSION +#define UNREACHABLE() +#endif + #if _MSC_EXTENSIONS // - // use Microsoft* C complier dependent integer width types + // use Microsoft* C compiler dependent integer width types // typedef unsigned __int64 UINT64; typedef __int64 INT64; diff --git a/src/include/ipxe/efi/Base.h b/src/include/ipxe/efi/Base.h index 8a047aef0..b241e6279 100644 --- a/src/include/ipxe/efi/Base.h +++ b/src/include/ipxe/efi/Base.h @@ -86,6 +86,117 @@ VERIFY_SIZE_OF (CHAR16, 2); #define GLOBAL_REMOVE_IF_UNREFERENCED #endif +// +// Should be used in combination with NORETURN to avoid 'noreturn' returns +// warnings. +// +#ifndef UNREACHABLE + #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4) + /// + /// Signal compilers and analyzers that this call is not reachable. It is + /// up to the compiler to remove any code past that point. + /// Not implemented by GCC 4.4 or earlier. + /// + #define UNREACHABLE() __builtin_unreachable () + #elif defined (__has_feature) + #if __has_builtin (__builtin_unreachable) + /// + /// Signal compilers and analyzers that this call is not reachable. It is + /// up to the compiler to remove any code past that point. + /// + #define UNREACHABLE() __builtin_unreachable () + #endif + #endif + + #ifndef UNREACHABLE + /// + /// Signal compilers and analyzers that this call is not reachable. It is + /// up to the compiler to remove any code past that point. + /// + #define UNREACHABLE() + #endif +#endif + +// +// Signaling compilers and analyzers that a certain function cannot return may +// remove all following code and thus lead to better optimization and less +// false positives. +// +#ifndef NORETURN + #if defined (__GNUC__) || defined (__clang__) + /// + /// Signal compilers and analyzers that the function cannot return. + /// It is up to the compiler to remove any code past a call to functions + /// flagged with this attribute. + /// + #define NORETURN __attribute__((noreturn)) + #elif defined(_MSC_EXTENSIONS) && !defined(MDE_CPU_EBC) + /// + /// Signal compilers and analyzers that the function cannot return. + /// It is up to the compiler to remove any code past a call to functions + /// flagged with this attribute. + /// + #define NORETURN __declspec(noreturn) + #else + /// + /// Signal compilers and analyzers that the function cannot return. + /// It is up to the compiler to remove any code past a call to functions + /// flagged with this attribute. + /// + #define NORETURN + #endif +#endif + +// +// Should be used in combination with ANALYZER_NORETURN to avoid 'noreturn' +// returns warnings. +// +#ifndef ANALYZER_UNREACHABLE + #ifdef __clang_analyzer__ + #if __has_builtin (__builtin_unreachable) + /// + /// Signal the analyzer that this call is not reachable. + /// This excludes compilers. + /// + #define ANALYZER_UNREACHABLE() __builtin_unreachable () + #endif + #endif + + #ifndef ANALYZER_UNREACHABLE + /// + /// Signal the analyzer that this call is not reachable. + /// This excludes compilers. + /// + #define ANALYZER_UNREACHABLE() + #endif +#endif + +// +// Static Analyzers may issue errors about potential NULL-dereferences when +// dereferencing a pointer, that has been checked before, outside of a +// NULL-check. This may lead to false positives, such as when using ASSERT() +// for verification. +// +#ifndef ANALYZER_NORETURN + #ifdef __has_feature + #if __has_feature (attribute_analyzer_noreturn) + /// + /// Signal analyzers that the function cannot return. + /// This excludes compilers. + /// + #define ANALYZER_NORETURN __attribute__((analyzer_noreturn)) + #endif + #endif + + #ifndef ANALYZER_NORETURN + /// + /// Signal the analyzer that the function cannot return. + /// This excludes compilers. + /// + #define ANALYZER_NORETURN + #endif +#endif + // // For symbol name in assembly code, an extra "_" is sometimes necessary // @@ -193,7 +304,7 @@ struct _LIST_ENTRY { // // UEFI specification claims 1 and 0. We are concerned about the -// complier portability so we did it this way. +// compiler portability so we did it this way. // /// @@ -480,7 +591,31 @@ struct _LIST_ENTRY { #define VA_COPY(Dest, Start) __va_copy (Dest, Start) -#elif defined(__GNUC__) && !defined(NO_BUILTIN_VA_FUNCS) +#elif defined(__GNUC__) + +#if defined(MDE_CPU_X64) && !defined(NO_MSABI_VA_FUNCS) +// +// X64 only. Use MS ABI version of GCC built-in macros for variable argument lists. +// +/// +/// Both GCC and LLVM 3.8 for X64 support new variable argument intrinsics for Microsoft ABI +/// + +/// +/// Variable used to traverse the list of arguments. This type can vary by +/// implementation and could be an array or structure. +/// +typedef __builtin_ms_va_list VA_LIST; + +#define VA_START(Marker, Parameter) __builtin_ms_va_start (Marker, Parameter) + +#define VA_ARG(Marker, TYPE) ((sizeof (TYPE) < sizeof (UINTN)) ? (TYPE)(__builtin_va_arg (Marker, UINTN)) : (TYPE)(__builtin_va_arg (Marker, TYPE))) + +#define VA_END(Marker) __builtin_ms_va_end (Marker) + +#define VA_COPY(Dest, Start) __builtin_ms_va_copy (Dest, Start) + +#else // // Use GCC built-in macros for variable argument lists. // @@ -499,6 +634,8 @@ typedef __builtin_va_list VA_LIST; #define VA_COPY(Dest, Start) __builtin_va_copy (Dest, Start) +#endif + #else /// /// Variable used to traverse the list of arguments. This type can vary by @@ -1038,7 +1175,7 @@ typedef UINTN RETURN_STATUS; #if defined(_MSC_EXTENSIONS) && !defined (__INTEL_COMPILER) && !defined (MDE_CPU_EBC) #pragma intrinsic(_ReturnAddress) /** - Get the return address of the calling funcation. + Get the return address of the calling function. Based on intrinsic function _ReturnAddress that provides the address of the instruction in the calling function that will be executed after @@ -1046,27 +1183,27 @@ typedef UINTN RETURN_STATUS; @param L Return Level. - @return The return address of the calling funcation or 0 if L != 0. + @return The return address of the calling function or 0 if L != 0. **/ #define RETURN_ADDRESS(L) ((L == 0) ? _ReturnAddress() : (VOID *) 0) #elif defined(__GNUC__) void * __builtin_return_address (unsigned int level); /** - Get the return address of the calling funcation. + Get the return address of the calling function. Based on built-in Function __builtin_return_address that returns the return address of the current function, or of one of its callers. @param L Return Level. - @return The return address of the calling funcation. + @return The return address of the calling function. **/ #define RETURN_ADDRESS(L) __builtin_return_address (L) #else /** - Get the return address of the calling funcation. + Get the return address of the calling function. @param L Return Level. diff --git a/src/include/ipxe/efi/Ia32/ProcessorBind.h b/src/include/ipxe/efi/Ia32/ProcessorBind.h index 375ff2d92..82e1d46ac 100644 --- a/src/include/ipxe/efi/Ia32/ProcessorBind.h +++ b/src/include/ipxe/efi/Ia32/ProcessorBind.h @@ -81,7 +81,7 @@ FILE_LICENCE ( BSD3 ); #pragma warning ( disable : 4057 ) // -// ASSERT(FALSE) or while (TRUE) are legal constructes so supress this warning +// ASSERT(FALSE) or while (TRUE) are legal constructs so suppress this warning // #pragma warning ( disable : 4127 ) @@ -121,7 +121,7 @@ FILE_LICENCE ( BSD3 ); #if defined(_MSC_EXTENSIONS) // - // use Microsoft C complier dependent integer width types + // use Microsoft C compiler dependent integer width types // /// diff --git a/src/include/ipxe/efi/IndustryStandard/Pci22.h b/src/include/ipxe/efi/IndustryStandard/Pci22.h index f0f2ae922..d585e8e70 100644 --- a/src/include/ipxe/efi/IndustryStandard/Pci22.h +++ b/src/include/ipxe/efi/IndustryStandard/Pci22.h @@ -5,11 +5,10 @@ PCI Local Bus Specification, 2.2 PCI-to-PCI Bridge Architecture Specification, Revision 1.2 PC Card Standard, 8.0 + PCI Power Management Interface Specifiction, Revision 1.2 - - - Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
- Copyright (c) 2014 - 2105, Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2014 - 2015, Hewlett-Packard Development Company, L.P.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -638,6 +637,7 @@ typedef union { #define EFI_PCI_CAPABILITY_ID_SLOTID 0x04 #define EFI_PCI_CAPABILITY_ID_MSI 0x05 #define EFI_PCI_CAPABILITY_ID_HOTPLUG 0x06 +#define EFI_PCI_CAPABILITY_ID_SHPC 0x0C /// /// Capabilities List Header @@ -648,18 +648,6 @@ typedef struct { UINT8 NextItemPtr; } EFI_PCI_CAPABILITY_HDR; -/// -/// Power Management Register Block Definition -/// Section 3.2, PCI Power Management Interface Specifiction, Revision 1.2 -/// -typedef struct { - EFI_PCI_CAPABILITY_HDR Hdr; - UINT16 PMC; - UINT16 PMCSR; - UINT8 BridgeExtention; - UINT8 Data; -} EFI_PCI_CAPABILITY_PMI; - /// /// PMC - Power Management Capabilities /// Section 3.2.3, PCI Power Management Interface Specifiction, Revision 1.2 @@ -668,7 +656,7 @@ typedef union { struct { UINT16 Version : 3; UINT16 PmeClock : 1; - UINT16 : 1; + UINT16 Reserved : 1; UINT16 DeviceSpecificInitialization : 1; UINT16 AuxCurrent : 3; UINT16 D1Support : 1; @@ -687,7 +675,9 @@ typedef union { typedef union { struct { UINT16 PowerState : 2; - UINT16 : 6; + UINT16 ReservedForPciExpress : 1; + UINT16 NoSoftReset : 1; + UINT16 Reserved : 4; UINT16 PmeEnable : 1; UINT16 DataSelect : 4; UINT16 DataScale : 2; @@ -696,6 +686,36 @@ typedef union { UINT16 Data; } EFI_PCI_PMCSR; +#define PCI_POWER_STATE_D0 0 +#define PCI_POWER_STATE_D1 1 +#define PCI_POWER_STATE_D2 2 +#define PCI_POWER_STATE_D3_HOT 3 + +/// +/// PMCSR_BSE - PMCSR PCI-to-PCI Bridge Support Extensions +/// Section 3.2.5, PCI Power Management Interface Specifiction, Revision 1.2 +/// +typedef union { + struct { + UINT8 Reserved : 6; + UINT8 B2B3 : 1; + UINT8 BusPowerClockControl : 1; + } Bits; + UINT8 Uint8; +} EFI_PCI_PMCSR_BSE; + +/// +/// Power Management Register Block Definition +/// Section 3.2, PCI Power Management Interface Specifiction, Revision 1.2 +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + EFI_PCI_PMC PMC; + EFI_PCI_PMCSR PMCSR; + EFI_PCI_PMCSR_BSE BridgeExtention; + UINT8 Data; +} EFI_PCI_CAPABILITY_PMI; + /// /// A.G.P Capability /// Section 6.1.4, Accelerated Graphics Port Interface Specification, Revision 1.0 diff --git a/src/include/ipxe/efi/Library/BaseLib.h b/src/include/ipxe/efi/Library/BaseLib.h index a45a20d70..a254ed2d0 100644 --- a/src/include/ipxe/efi/Library/BaseLib.h +++ b/src/include/ipxe/efi/Library/BaseLib.h @@ -2,7 +2,7 @@ Provides string functions, linked list functions, math functions, synchronization functions, file path functions, and CPU architecture-specific functions. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -189,6 +189,8 @@ typedef struct { /** Returns the length of a Null-terminated Unicode string. + This function is similar as strlen_s defined in C11. + If String is not aligned on a 16-bit boundary, then ASSERT(). @param String A pointer to a Null-terminated Unicode string. @@ -211,10 +213,14 @@ StrnLenS ( Copies the string pointed to by Source (including the terminating null char) to the array pointed to by Destination. + This function is similar as strcpy_s defined in C11. + If Destination is not aligned on a 16-bit boundary, then ASSERT(). If Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -243,10 +249,14 @@ StrCpyS ( Source to the array pointed to by Destination. If no null char is copied from Source, then Destination[Length] is always set to null. + This function is similar as strncpy_s defined in C11. + If Length > 0 and Destination is not aligned on a 16-bit boundary, then ASSERT(). If Length > 0 and Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -277,10 +287,14 @@ StrnCpyS ( Appends a copy of the string pointed to by Source (including the terminating null char) to the end of the string pointed to by Destination. + This function is similar as strcat_s defined in C11. + If Destination is not aligned on a 16-bit boundary, then ASSERT(). If Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -313,10 +327,14 @@ StrCatS ( copied from Source, then Destination[StrLen(Destination) + Length] is always set to null. + This function is similar as strncat_s defined in C11. + If Destination is not aligned on a 16-bit boundary, then ASSERT(). If Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -348,6 +366,8 @@ StrnCatS ( /** Returns the length of a Null-terminated Ascii string. + This function is similar as strlen_s defined in C11. + @param String A pointer to a Null-terminated Ascii string. @param MaxSize The maximum number of Destination Ascii char, including terminating null char. @@ -368,8 +388,12 @@ AsciiStrnLenS ( Copies the string pointed to by Source (including the terminating null char) to the array pointed to by Destination. + This function is similar as strcpy_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -398,8 +422,12 @@ AsciiStrCpyS ( Source to the array pointed to by Destination. If no null char is copied from Source, then Destination[Length] is always set to null. + This function is similar as strncpy_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -430,8 +458,12 @@ AsciiStrnCpyS ( Appends a copy of the string pointed to by Source (including the terminating null char) to the end of the string pointed to by Destination. + This function is similar as strcat_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -464,8 +496,12 @@ AsciiStrCatS ( copied from Source, then Destination[StrLen(Destination) + Length] is always set to null. + This function is similar as strncat_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -986,7 +1022,11 @@ StrHexToUint64 ( IN CONST CHAR16 *String ); +#ifndef DISABLE_NEW_DEPRECATED_INTERFACES + /** + [ATTENTION] This function is deprecated for security reason. + Convert a Null-terminated Unicode string to a Null-terminated ASCII string and returns the ASCII string. @@ -1026,6 +1066,56 @@ UnicodeStrToAsciiStr ( OUT CHAR8 *Destination ); +#endif + +/** + Convert a Null-terminated Unicode string to a Null-terminated + ASCII string. + + This function is similar to AsciiStrCpyS. + + This function converts the content of the Unicode string Source + to the ASCII string Destination by copying the lower 8 bits of + each Unicode character. The function terminates the ASCII string + Destination by appending a Null-terminator character at the end. + + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((StrLen (Source) + 1) * sizeof (CHAR8)) in bytes. + + If any Unicode characters in Source contain non-zero value in + the upper 8 bits, then ASSERT(). + + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then the Destination is unmodified. + + @param Source The pointer to a Null-terminated Unicode string. + @param Destination The pointer to a Null-terminated ASCII string. + @param DestMax The maximum number of Destination Ascii + char, including terminating null char. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than StrLen(Source). + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If PcdMaximumAsciiStringLength is not zero, + and DestMax is greater than + PcdMaximumAsciiStringLength. + If PcdMaximumUnicodeStringLength is not zero, + and DestMax is greater than + PcdMaximumUnicodeStringLength. + If DestMax is 0. + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +UnicodeStrToAsciiStrS ( + IN CONST CHAR16 *Source, + OUT CHAR8 *Destination, + IN UINTN DestMax + ); #ifndef DISABLE_NEW_DEPRECATED_INTERFACES @@ -1529,8 +1619,11 @@ AsciiStrHexToUint64 ( IN CONST CHAR8 *String ); +#ifndef DISABLE_NEW_DEPRECATED_INTERFACES /** + [ATTENTION] This function is deprecated for security reason. + Convert one Null-terminated ASCII string to a Null-terminated Unicode string and returns the Unicode string. @@ -1564,6 +1657,52 @@ AsciiStrToUnicodeStr ( OUT CHAR16 *Destination ); +#endif + +/** + Convert one Null-terminated ASCII string to a Null-terminated + Unicode string. + + This function is similar to StrCpyS. + + This function converts the contents of the ASCII string Source to the Unicode + string Destination. The function terminates the Unicode string Destination by + appending a Null-terminator character at the end. + + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((AsciiStrLen (Source) + 1) * sizeof (CHAR16)) in bytes. + + If Destination is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then the Destination is unmodified. + + @param Source The pointer to a Null-terminated ASCII string. + @param Destination The pointer to a Null-terminated Unicode string. + @param DestMax The maximum number of Destination Unicode + char, including terminating null char. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than StrLen(Source). + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If PcdMaximumUnicodeStringLength is not zero, + and DestMax is greater than + PcdMaximumUnicodeStringLength. + If PcdMaximumAsciiStringLength is not zero, + and DestMax is greater than + PcdMaximumAsciiStringLength. + If DestMax is 0. + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToUnicodeStrS ( + IN CONST CHAR8 *Source, + OUT CHAR16 *Destination, + IN UINTN DestMax + ); /** Converts an 8-bit value to an 8-bit BCD value. @@ -1635,7 +1774,7 @@ PathRemoveLastItem( @param[in] Path The pointer to the string containing the path. - @return Returns Path, otherwise returns NULL to indicate that an error has occured. + @return Returns Path, otherwise returns NULL to indicate that an error has occurred. **/ CHAR16* EFIAPI diff --git a/src/include/ipxe/efi/Pi/PiHob.h b/src/include/ipxe/efi/Pi/PiHob.h index 121748dec..2663b0525 100644 --- a/src/include/ipxe/efi/Pi/PiHob.h +++ b/src/include/ipxe/efi/Pi/PiHob.h @@ -1,7 +1,7 @@ /** @file HOB related definitions in PI. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -11,7 +11,7 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. @par Revision Reference: - PI Version 1.4 + PI Version 1.4a **/ @@ -295,7 +295,7 @@ typedef UINT32 EFI_RESOURCE_ATTRIBUTE_TYPE; #define EFI_RESOURCE_ATTRIBUTE_PERSISTABLE 0x01000000 #define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED 0x00040000 -#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00800000 +#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00080000 // // Physical memory relative reliability attribute. This diff --git a/src/include/ipxe/efi/Protocol/DevicePath.h b/src/include/ipxe/efi/Protocol/DevicePath.h index d35b65fa9..d406b2868 100644 --- a/src/include/ipxe/efi/Protocol/DevicePath.h +++ b/src/include/ipxe/efi/Protocol/DevicePath.h @@ -5,7 +5,7 @@ from a software point of view. The path must persist from boot to boot, so it can not contain things like PCI bus numbers that change from boot to boot. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -511,7 +511,7 @@ typedef struct { UINT16 HBAPortNumber; /// /// The Port multiplier port number that facilitates the connection - /// to the device. Bit 15 should be set if the device is directly + /// to the device. Must be set to 0xFFFF if the device is directly /// connected to the HBA. /// UINT16 PortMultiplierPortNumber; @@ -856,6 +856,15 @@ typedef struct { UINT8 SlotNumber; } SD_DEVICE_PATH; +/// +/// EMMC (Embedded MMC) Device Path SubType. +/// +#define MSG_EMMC_DP 0x1D +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 SlotNumber; +} EMMC_DEVICE_PATH; + /// /// iSCSI Device Path SubType /// @@ -1241,6 +1250,7 @@ typedef union { WIFI_DEVICE_PATH WiFi; UFS_DEVICE_PATH Ufs; SD_DEVICE_PATH Sd; + EMMC_DEVICE_PATH Emmc; HARDDRIVE_DEVICE_PATH HardDrive; CDROM_DEVICE_PATH CD; @@ -1297,6 +1307,7 @@ typedef union { WIFI_DEVICE_PATH *WiFi; UFS_DEVICE_PATH *Ufs; SD_DEVICE_PATH *Sd; + EMMC_DEVICE_PATH *Emmc; HARDDRIVE_DEVICE_PATH *HardDrive; CDROM_DEVICE_PATH *CD; diff --git a/src/include/ipxe/efi/Protocol/HiiImage.h b/src/include/ipxe/efi/Protocol/HiiImage.h index b18d51a61..ba934a9f9 100644 --- a/src/include/ipxe/efi/Protocol/HiiImage.h +++ b/src/include/ipxe/efi/Protocol/HiiImage.h @@ -1,7 +1,7 @@ /** @file The file provides services to access to images in the images database. - Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -17,6 +17,8 @@ FILE_LICENCE ( BSD3 ); +#include + #define EFI_HII_IMAGE_PROTOCOL_GUID \ { 0x31a6406a, 0x6bdf, 0x4e46, { 0xb2, 0xa2, 0xeb, 0xaa, 0x89, 0xc4, 0x9, 0x20 } } diff --git a/src/include/ipxe/efi/Protocol/Mtftp4.h b/src/include/ipxe/efi/Protocol/Mtftp4.h index 0e961cfd4..bc0a8396d 100644 --- a/src/include/ipxe/efi/Protocol/Mtftp4.h +++ b/src/include/ipxe/efi/Protocol/Mtftp4.h @@ -1,5 +1,5 @@ /** @file - EFI Multicast Trivial File Tranfer Protocol Definition + EFI Multicast Trivial File Transfer Protocol Definition Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under @@ -214,7 +214,7 @@ EFI_STATUS ); /** - Timeout callback funtion. + Timeout callback function. @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. @param Token The token that is provided in the diff --git a/src/include/ipxe/efi/Protocol/SimpleTextOut.h b/src/include/ipxe/efi/Protocol/SimpleTextOut.h index 8aa36c35a..54d38b393 100644 --- a/src/include/ipxe/efi/Protocol/SimpleTextOut.h +++ b/src/include/ipxe/efi/Protocol/SimpleTextOut.h @@ -162,7 +162,7 @@ typedef EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SIMPLE_TEXT_OUTPUT_INTERFACE; Reset the text output device hardware and optionaly run diagnostics @param This The protocol instance pointer. - @param ExtendedVerification Driver may perform more exhaustive verfication + @param ExtendedVerification Driver may perform more exhaustive verification operation of the device during reset. @retval EFI_SUCCESS The text output device was reset. diff --git a/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h b/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h index 49ea24ff9..88c026201 100644 --- a/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h +++ b/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h @@ -3,7 +3,8 @@ IFR is primarily consumed by the EFI presentation engine, and produced by EFI internal application and drivers as well as all add-in card option-ROM drivers -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -211,6 +212,7 @@ typedef struct _EFI_HII_FONT_PACKAGE_HDR { #define EFI_HII_GIBT_GLYPHS 0x11 #define EFI_HII_GIBT_GLYPH_DEFAULT 0x12 #define EFI_HII_GIBT_GLYPHS_DEFAULT 0x13 +#define EFI_HII_GIBT_GLYPH_VARIABILITY 0x14 #define EFI_HII_GIBT_DUPLICATE 0x20 #define EFI_HII_GIBT_SKIP2 0x21 #define EFI_HII_GIBT_SKIP1 0x22 @@ -283,6 +285,13 @@ typedef struct _EFI_HII_GIBT_GLYPHS_DEFAULT_BLOCK { UINT8 BitmapData[1]; } EFI_HII_GIBT_GLYPHS_DEFAULT_BLOCK; +typedef struct _EFI_HII_GIBT_VARIABILITY_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + EFI_HII_GLYPH_INFO Cell; + UINT8 GlyphPackInBits; + UINT8 BitmapData [1]; +} EFI_HII_GIBT_VARIABILITY_BLOCK; + typedef struct _EFI_HII_GIBT_SKIP1_BLOCK { EFI_HII_GLYPH_BLOCK Header; UINT8 SkipCount; @@ -491,6 +500,7 @@ typedef struct _EFI_HII_IMAGE_BLOCK { #define EFI_HII_IIBT_IMAGE_24BIT 0x16 #define EFI_HII_IIBT_IMAGE_24BIT_TRANS 0x17 #define EFI_HII_IIBT_IMAGE_JPEG 0x18 +#define EFI_HII_IIBT_IMAGE_PNG 0x19 #define EFI_HII_IIBT_DUPLICATE 0x20 #define EFI_HII_IIBT_SKIP2 0x21 #define EFI_HII_IIBT_SKIP1 0x22 @@ -611,6 +621,12 @@ typedef struct _EFI_HII_IIBT_JPEG_BLOCK { UINT8 Data[1]; } EFI_HII_IIBT_JPEG_BLOCK; +typedef struct _EFI_HII_IIBT_PNG_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT32 Size; + UINT8 Data[1]; +} EFI_HII_IIBT_PNG_BLOCK; + typedef struct _EFI_HII_IIBT_SKIP1_BLOCK { EFI_HII_IMAGE_BLOCK Header; UINT8 SkipCount; @@ -2112,4 +2128,10 @@ typedef struct _EFI_HII_AIBT_SKIP2_BLOCK { /// #define STRING_TOKEN(t) t +/// +/// IMAGE_TOKEN is not defined in UEFI specification. But it is placed +/// here for the easy access by C files and VFR source files. +/// +#define IMAGE_TOKEN(t) t + #endif diff --git a/src/include/ipxe/efi/Uefi/UefiPxe.h b/src/include/ipxe/efi/Uefi/UefiPxe.h index 13be21aae..6ed5c9a22 100644 --- a/src/include/ipxe/efi/Uefi/UefiPxe.h +++ b/src/include/ipxe/efi/Uefi/UefiPxe.h @@ -1081,7 +1081,7 @@ typedef struct s_pxe_cpb_start_31 { /// /// protocol driver can provide anything for this Unique_ID, UNDI remembers - /// that as just a 64bit value assocaited to the interface specified by + /// that as just a 64bit value associated to the interface specified by /// the ifnum and gives it back as a parameter to all the call-back routines /// when calling for that interface! /// diff --git a/src/include/ipxe/efi/Uefi/UefiSpec.h b/src/include/ipxe/efi/Uefi/UefiSpec.h index 98ac8765f..27edf43ac 100644 --- a/src/include/ipxe/efi/Uefi/UefiSpec.h +++ b/src/include/ipxe/efi/Uefi/UefiSpec.h @@ -1006,11 +1006,15 @@ EFI_STATUS @param[in] ResetType The type of reset to perform. @param[in] ResetStatus The status code for the reset. - @param[in] DataSize The size, in bytes, of WatchdogData. + @param[in] DataSize The size, in bytes, of ResetData. @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or EfiResetShutdown the data buffer starts with a Null-terminated string, optionally followed by additional binary data. - + The string is a description that the caller may use to further + indicate the reason for the system reset. ResetData is only + valid if ResetStatus is something other than EFI_SUCCESS + unless the ResetType is EfiResetPlatformSpecific + where a minimum amount of ResetData is always required. **/ typedef VOID diff --git a/src/include/ipxe/efi/X64/ProcessorBind.h b/src/include/ipxe/efi/X64/ProcessorBind.h index b64c25c0f..ad7ae81a5 100644 --- a/src/include/ipxe/efi/X64/ProcessorBind.h +++ b/src/include/ipxe/efi/X64/ProcessorBind.h @@ -29,6 +29,19 @@ FILE_LICENCE ( BSD3 ); #pragma pack() #endif +#if defined(__GNUC__) && defined(__pic__) && !defined(USING_LTO) +// +// Mark all symbol declarations and references as hidden, meaning they will +// not be subject to symbol preemption. This allows the compiler to refer to +// symbols directly using relative references rather than via the GOT, which +// contains absolute symbol addresses that are subject to runtime relocation. +// +// The LTO linker will not emit GOT based relocations when all symbol +// references can be resolved locally, and so there is no need to set the +// pragma in that case (and doing so will cause other issues). +// +#pragma GCC visibility push (hidden) +#endif #if defined(__INTEL_COMPILER) // @@ -82,7 +95,7 @@ FILE_LICENCE ( BSD3 ); #pragma warning ( disable : 4057 ) // -// ASSERT(FALSE) or while (TRUE) are legal constructes so supress this warning +// ASSERT(FALSE) or while (TRUE) are legal constructs so suppress this warning // #pragma warning ( disable : 4127 ) @@ -121,7 +134,7 @@ FILE_LICENCE ( BSD3 ); #if defined(_MSC_EXTENSIONS) // - // use Microsoft C complier dependent integer width types + // use Microsoft C compiler dependent integer width types // /// From 0be77e959e813773cac10f049cb62c83258f81ff Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 16 Oct 2016 23:27:50 +0100 Subject: [PATCH 311/591] [efi] Add EFI_BLOCK_IO2_PROTOCOL header and GUID definition Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/BlockIo2.h | 208 +++++++++++++++++++++++ src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_debug.c | 2 + src/interface/efi/efi_guid.c | 5 + 4 files changed, 216 insertions(+) create mode 100644 src/include/ipxe/efi/Protocol/BlockIo2.h diff --git a/src/include/ipxe/efi/Protocol/BlockIo2.h b/src/include/ipxe/efi/Protocol/BlockIo2.h new file mode 100644 index 000000000..0b9cf8eb1 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/BlockIo2.h @@ -0,0 +1,208 @@ +/** @file + Block IO2 protocol as defined in the UEFI 2.3.1 specification. + + The Block IO2 protocol defines an extension to the Block IO protocol which + enables the ability to read and write data at a block level in a non-blocking + manner. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __BLOCK_IO2_H__ +#define __BLOCK_IO2_H__ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_BLOCK_IO2_PROTOCOL_GUID \ + { \ + 0xa77b2472, 0xe282, 0x4e9f, {0xa2, 0x45, 0xc2, 0xc0, 0xe2, 0x7b, 0xbc, 0xc1} \ + } + +typedef struct _EFI_BLOCK_IO2_PROTOCOL EFI_BLOCK_IO2_PROTOCOL; + +/** + The struct of Block IO2 Token. +**/ +typedef struct { + + /// + /// If Event is NULL, then blocking I/O is performed.If Event is not NULL and + /// non-blocking I/O is supported, then non-blocking I/O is performed, and + /// Event will be signaled when the read request is completed. + /// + EFI_EVENT Event; + + /// + /// Defines whether or not the signaled event encountered an error. + /// + EFI_STATUS TransactionStatus; +} EFI_BLOCK_IO2_TOKEN; + + +/** + Reset the block device hardware. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Indicates that the driver may perform a more + exhausive verification operation of the device + during reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_RESET_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + This function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and + non-blocking I/O is being used, the Event associated with this request will + not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is + replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The + caller is responsible for either having implicit or + explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Token->Event is + not NULL.The data was read correctly from the + device if the Token->Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_READ_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA LBA, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + This function writes the requested number of blocks to the device. All blocks + are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA, + EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is + being used, the Event associated with this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The write request was queued if Event is not NULL. + The data was written correctly to the device if + the Event is NULL. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_WRITE_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA LBA, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED + is returned and non-blocking I/O is being used, the Event associated with + this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in,out] Token A pointer to the token associated with the transaction + + @retval EFI_SUCCESS The flush request was queued if Event is not NULL. + All outstanding data was written correctly to the + device if the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while writting back + the data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_FLUSH_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ); + +/// +/// The Block I/O2 protocol defines an extension to the Block I/O protocol which +/// enables the ability to read and write data at a block level in a non-blocking +// manner. +/// +struct _EFI_BLOCK_IO2_PROTOCOL { + /// + /// A pointer to the EFI_BLOCK_IO_MEDIA data for this device. + /// Type EFI_BLOCK_IO_MEDIA is defined in BlockIo.h. + /// + EFI_BLOCK_IO_MEDIA *Media; + + EFI_BLOCK_RESET_EX Reset; + EFI_BLOCK_READ_EX ReadBlocksEx; + EFI_BLOCK_WRITE_EX WriteBlocksEx; + EFI_BLOCK_FLUSH_EX FlushBlocksEx; +}; + +extern EFI_GUID gEfiBlockIo2ProtocolGuid; + +#endif + diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index db9943a42..d34155321 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -158,6 +158,7 @@ extern EFI_GUID efi_apple_net_boot_protocol_guid; extern EFI_GUID efi_arp_protocol_guid; extern EFI_GUID efi_arp_service_binding_protocol_guid; extern EFI_GUID efi_block_io_protocol_guid; +extern EFI_GUID efi_block_io2_protocol_guid; extern EFI_GUID efi_bus_specific_driver_override_protocol_guid; extern EFI_GUID efi_component_name_protocol_guid; extern EFI_GUID efi_component_name2_protocol_guid; diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 19531fdc1..dc9ed85c4 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -79,6 +79,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "ArpSb" }, { &efi_block_io_protocol_guid, "BlockIo" }, + { &efi_block_io2_protocol_guid, + "BlockIo2" }, { &efi_bus_specific_driver_override_protocol_guid, "BusSpecificDriverOverride" }, { &efi_component_name_protocol_guid, diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 62ee5a517..cd81876fc 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -101,6 +102,10 @@ EFI_GUID efi_arp_service_binding_protocol_guid EFI_GUID efi_block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID; +/** Block I/O version 2 protocol GUID */ +EFI_GUID efi_block_io2_protocol_guid + = EFI_BLOCK_IO2_PROTOCOL_GUID; + /** Bus specific driver override protocol GUID */ EFI_GUID efi_bus_specific_driver_override_protocol_guid = EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID; From aa11f5deda29f91f5b8592965ed00f4a65002eff Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 29 Oct 2016 00:08:48 +0100 Subject: [PATCH 312/591] [bzimage] Fix page alignment of initrd images The initrd_addr_max field represents the highest byte address that may be used to hold initrd images, and is therefore almost certainly not aligned to a page boundary: a typical value might be 0x7fffffff. Fix the address calculations to ensure that the initrd images are always aligned to a page boundary. Reported-by: Sitsofe Wheeler Signed-off-by: Michael Brown --- src/arch/x86/image/bzimage.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index d9b5ddc07..e3c4cb83d 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -522,10 +522,12 @@ static void bzimage_load_initrds ( struct image *image, /* Find highest usable address */ top = userptr_add ( highest->data, bzimage_align ( highest->len ) ); - if ( user_to_phys ( top, 0 ) > bzimg->mem_limit ) - top = phys_to_user ( bzimg->mem_limit ); + if ( user_to_phys ( top, -1 ) > bzimg->mem_limit ) { + top = phys_to_user ( ( bzimg->mem_limit + 1 ) & + ~( INITRD_ALIGN - 1 ) ); + } DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n", - image, user_to_phys ( top, 0 ) ); + image, user_to_phys ( top, -1 ) ); /* Load initrds in order */ for_each_image ( initrd ) { From dd9a14de35d2c4e308170ad2b51722d448d04e76 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 27 Oct 2016 00:13:50 +0200 Subject: [PATCH 313/591] [librm] Conditionalize the workaround for the Tivoli VMM's SSE garbling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 71560d1 ("[librm] Preserve FPU, MMX and SSE state across calls to virt_call()") added FXSAVE and FXRSTOR instructions to iPXE. In KVM virtual machines, these instructions execute fine as long as the host CPU supports the "unrestricted_guest" feature (that is, it can virtualize big real mode natively). On older host CPUs however, KVM has to emulate big real mode, and it currently doesn't implement FXSAVE emulation. Upstream QEMU rebuilt iPXE at commit 0418631 ("[thunderx] Fix compilation with older versions of gcc") which is a descendant of commit 71560d1 (see above). This was done in QEMU commit ffdc5a2 ("ipxe: update submodule from 4e03af8ec to 041863191"). The resultant binaries were bundled with the QEMU v2.7.0 release; see QEMU commit c52125a ("ipxe: update prebuilt binaries"). This distributed the iPXE workaround for the Tivoli VMM bug to a number of KVM users with old host CPUs, causing KVM emulation failures (guest crashes) for them while netbooting. Make the FXSAVE and FXRSTOR instructions conditional on a new feature test macro called TIVOLI_VMM_WORKAROUND. Define the macro by default. There is prior art for an assembly file including config/general.h: see arch/x86/prefix/romprefix.S. Also, TIVOLI_VMM_WORKAROUND seems to be a good fit for the "Obscure configuration options" section in config/general.h. Cc: Bandan Das Cc: Gerd Hoffmann Cc: Greg Cc: Michael Brown Cc: Michael Prokop Cc: Paolo Bonzini Cc: Peter Pickford Cc: Radim Krčmář Ref: https://bugs.archlinux.org/task/50778 Ref: https://bugs.launchpad.net/qemu/+bug/1623276 Ref: https://bugzilla.proxmox.com/show_bug.cgi?id=1182 Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1356762 Signed-off-by: Laszlo Ersek Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm.S | 16 ++++++++++++---- src/config/general.h | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index e91ede372..c31daad84 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -7,6 +7,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +/* Drag in general configuration */ +#include + /* Drag in local definitions */ #include "librm.h" @@ -207,7 +210,9 @@ VC_TMP_CR3: .space 4 VC_TMP_CR4: .space 4 VC_TMP_EMER: .space 8 .endif +#ifdef TIVOLI_VMM_WORKAROUND VC_TMP_FXSAVE: .space 512 +#endif VC_TMP_END: .previous @@ -1000,11 +1005,12 @@ virt_call: /* Claim ownership of temporary static buffer */ cli - - /* Preserve FPU, MMX and SSE state in temporary static buffer */ movw %cs:rm_ds, %ds - fxsave ( rm_tmpbuf + VC_TMP_FXSAVE ) +#ifdef TIVOLI_VMM_WORKAROUND + /* Preserve FPU, MMX and SSE state in temporary static buffer */ + fxsave ( rm_tmpbuf + VC_TMP_FXSAVE ) +#endif /* Preserve GDT and IDT in temporary static buffer */ sidt ( rm_tmpbuf + VC_TMP_IDT ) sgdt ( rm_tmpbuf + VC_TMP_GDT ) @@ -1070,9 +1076,11 @@ vc_rmode: movl $MSR_EFER, %ecx wrmsr .endif + +#ifdef TIVOLI_VMM_WORKAROUND /* Restore FPU, MMX and SSE state from temporary static buffer */ fxrstor ( rm_tmpbuf + VC_TMP_FXSAVE ) - +#endif /* Restore registers and flags and return */ popl %eax /* skip %cs and %ss */ popw %ds diff --git a/src/config/general.h b/src/config/general.h index d2bbeb067..be0845f68 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -190,6 +190,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef GDBUDP /* Remote GDB debugging over UDP * (both may be set) */ //#define EFI_DOWNGRADE_UX /* Downgrade UEFI user experience */ +#define TIVOLI_VMM_WORKAROUND /* Work around the Tivoli VMM's garbling of SSE + * registers when iPXE traps to it due to + * privileged instructions */ #include #include NAMED_CONFIG(general.h) From b991c67c1d91574ef22336cc3a5944d1e63230c9 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 27 Oct 2016 00:13:51 +0200 Subject: [PATCH 314/591] [build] Disable TIVOLI_VMM_WORKAROUND in the qemu configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prevents KVM guests from crashing that run iPXE on host CPUs without "unrestricted_guest" support. Once KVM gets the FXSAVE / FXRSTOR emulation feature (*), and the feature becomes widely available to users, we can back out this change from iPXE. (*) Already in progress by Radim: [PATCH 0/2] KVM: x86: emulate fxsave and fxrstor https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1258895.html https://www.spinics.net/lists/kernel/msg2370327.html Cc: Bandan Das Cc: Gerd Hoffmann Cc: Greg Cc: Michael Brown Cc: Michael Prokop Cc: Paolo Bonzini Cc: Peter Pickford Cc: Radim Krčmář Ref: https://bugs.archlinux.org/task/50778 Ref: https://bugs.launchpad.net/qemu/+bug/1623276 Ref: https://bugzilla.proxmox.com/show_bug.cgi?id=1182 Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1356762 Signed-off-by: Laszlo Ersek Signed-off-by: Michael Brown --- src/config/qemu/general.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/qemu/general.h b/src/config/qemu/general.h index 30f60d3f7..a08449735 100644 --- a/src/config/qemu/general.h +++ b/src/config/qemu/general.h @@ -8,3 +8,8 @@ /* Work around missing EFI_PXE_BASE_CODE_PROTOCOL */ #define EFI_DOWNGRADE_UX + +/* The Tivoli VMM workaround causes a KVM emulation failure on hosts + * without unrestricted_guest support + */ +#undef TIVOLI_VMM_WORKAROUND From daa8ed9274d91a157dc049f00792f62c98b0a11a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Nov 2016 22:22:13 +0000 Subject: [PATCH 315/591] [interface] Provide intf_reinit() to reinitialise nullified interfaces Provide an abstraction intf_reinit() to restore the descriptor of a previously nullified interface. Signed-off-by: Michael Brown --- src/core/interface.c | 3 +-- src/include/ipxe/interface.h | 18 ++++++++++++++++++ src/net/tcp/httpcore.c | 5 +---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/core/interface.c b/src/core/interface.c index ba148c13d..948fa5c54 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -295,7 +295,6 @@ void intf_shutdown ( struct interface *intf, int rc ) { * blocked during shutdown. */ void intf_restart ( struct interface *intf, int rc ) { - struct interface_descriptor *desc = intf->desc; /* Shut down the interface */ intf_shutdown ( intf, rc ); @@ -309,7 +308,7 @@ void intf_restart ( struct interface *intf, int rc ) { * infinite loop as the intf_close() operations on each side * of the link call each other recursively. */ - intf->desc = desc; + intf_reinit ( intf ); } /** diff --git a/src/include/ipxe/interface.h b/src/include/ipxe/interface.h index a8d823775..ebb1b6911 100644 --- a/src/include/ipxe/interface.h +++ b/src/include/ipxe/interface.h @@ -123,6 +123,11 @@ struct interface { struct refcnt *refcnt; /** Interface descriptor */ struct interface_descriptor *desc; + /** Original interface descriptor + * + * Used by intf_reinit(). + */ + struct interface_descriptor *original; }; extern void intf_plug ( struct interface *intf, struct interface *dest ); @@ -166,6 +171,7 @@ static inline void intf_init ( struct interface *intf, intf->dest = &null_intf; intf->refcnt = refcnt; intf->desc = desc; + intf->original = desc; } /** @@ -177,6 +183,7 @@ static inline void intf_init ( struct interface *intf, .dest = &null_intf, \ .refcnt = NULL, \ .desc = &(descriptor), \ + .original = &(descriptor), \ } /** @@ -236,4 +243,15 @@ static inline void intf_init ( struct interface *intf, */ #define INTF_INTF_DBG( intf, dest ) INTF_DBG ( intf ), INTF_DBG ( dest ) +/** + * Reinitialise an object interface + * + * @v intf Object interface + */ +static inline void intf_reinit ( struct interface *intf ) { + + /* Restore original interface descriptor */ + intf->desc = intf->original; +} + #endif /* _IPXE_INTERFACE_H */ diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index b1f74bc4f..27cc50653 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -787,12 +787,9 @@ static int http_transfer_complete ( struct http_transaction *http ) { /* Restart content decoding interfaces (which may be attached * to the same object). */ - intf_nullify ( &http->content ); - intf_nullify ( &http->transfer ); + intf_nullify ( &http->transfer ); /* avoid potential loops */ intf_restart ( &http->content, http->response.rc ); intf_restart ( &http->transfer, http->response.rc ); - http->content.desc = &http_content_desc; - http->transfer.desc = &http_transfer_desc; intf_plug_plug ( &http->transfer, &http->content ); http->len = 0; assert ( http->remaining == 0 ); From 81fceaec6eea05efb942a188c3d92dd73a1a8aa0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Nov 2016 23:00:57 +0000 Subject: [PATCH 316/591] [iscsi] Avoid potential infinite loops during shutdown The command and data interfaces may be connected to the same object. Nullify the data interface before shutting down the control interface to avoid potential infinite loops. Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index ec004e4ba..d6f80084d 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -231,6 +231,7 @@ static void iscsi_close ( struct iscsi_session *iscsi, int rc ) { process_del ( &iscsi->process ); /* Shut down interfaces */ + intf_nullify ( &iscsi->data ); /* avoid potential loops */ intf_shutdown ( &iscsi->socket, rc ); intf_shutdown ( &iscsi->control, rc ); intf_shutdown ( &iscsi->data, rc ); From fd95c780b6ad39ab55344c0e4b9c2125c2c2f5ad Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Nov 2016 22:22:22 +0000 Subject: [PATCH 317/591] [efi] Add basic EFI SAN booting capability Signed-off-by: Michael Brown --- src/config/defaults/efi.h | 7 +- src/include/ipxe/efi/efi_block.h | 27 + src/include/ipxe/errfile.h | 1 + src/include/ipxe/sanboot.h | 1 + src/interface/efi/efi_block.c | 1062 ++++++++++++++++++++++++++++++ 5 files changed, 1097 insertions(+), 1 deletion(-) create mode 100644 src/include/ipxe/efi/efi_block.h create mode 100644 src/interface/efi/efi_block.c diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index ba4eed936..2e4c28329 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -16,7 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define TIMER_EFI #define UMALLOC_EFI #define SMBIOS_EFI -#define SANBOOT_NULL +#define SANBOOT_EFI #define BOFM_EFI #define ENTROPY_EFI #define TIME_EFI @@ -27,6 +27,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_EFI /* EFI image support */ #define IMAGE_SCRIPT /* iPXE script image support */ +#define SANBOOT_PROTO_ISCSI /* iSCSI protocol */ +#define SANBOOT_PROTO_AOE /* AoE protocol */ +#define SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ +#define SANBOOT_PROTO_FCP /* Fibre Channel protocol */ + #define USB_HCD_XHCI /* xHCI USB host controller */ #define USB_HCD_EHCI /* EHCI USB host controller */ #define USB_HCD_UHCI /* UHCI USB host controller */ diff --git a/src/include/ipxe/efi/efi_block.h b/src/include/ipxe/efi/efi_block.h new file mode 100644 index 000000000..ea28230bf --- /dev/null +++ b/src/include/ipxe/efi/efi_block.h @@ -0,0 +1,27 @@ +#ifndef _IPXE_EFI_BLOCK_H +#define _IPXE_EFI_BLOCK_H + +/** @block + * + * EFI block device protocols + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef SANBOOT_EFI +#define SANBOOT_PREFIX_efi +#else +#define SANBOOT_PREFIX_efi __efi_ +#endif + +static inline __always_inline unsigned int +SANBOOT_INLINE ( efi, san_default_drive ) ( void ) { + /* Drive numbers don't exist as a concept under EFI. We + * arbitarily choose to use drive 0x80 to minimise differences + * with a standard BIOS. + */ + return 0x80; +} + +#endif /* _IPXE_EFI_BLOCK_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 08e166ce7..d0b93d02f 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -71,6 +71,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_fault ( ERRFILE_CORE | 0x001f0000 ) #define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) #define ERRFILE_pixbuf ( ERRFILE_CORE | 0x00210000 ) +#define ERRFILE_efi_block ( ERRFILE_CORE | 0x00220000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 041e18935..c651364cb 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -54,6 +54,7 @@ struct uri; /* Include all architecture-independent sanboot API headers */ #include +#include /* Include all architecture-dependent sanboot API headers */ #include diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c new file mode 100644 index 000000000..ab2309431 --- /dev/null +++ b/src/interface/efi/efi_block.c @@ -0,0 +1,1062 @@ +/* + * Copyright (C) 2016 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 + * + * EFI block device protocols + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Timeout for EFI block device commands (in ticks) */ +#define EFI_BLOCK_TIMEOUT ( 15 * TICKS_PER_SEC ) + +/** Boot filename */ +static wchar_t efi_block_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; + +/** iPXE EFI block device vendor device path GUID */ +#define IPXE_BLOCK_DEVICE_PATH_GUID \ + { 0x8998b594, 0xf531, 0x4e87, \ + { 0x8b, 0xdf, 0x8f, 0x88, 0x54, 0x3e, 0x99, 0xd4 } } + +/** iPXE EFI block device vendor device path GUID */ +static EFI_GUID ipxe_block_device_path_guid + = IPXE_BLOCK_DEVICE_PATH_GUID; + +/** An iPXE EFI block device vendor device path */ +struct efi_block_vendor_path { + /** Generic vendor device path */ + VENDOR_DEVICE_PATH vendor; + /** Block device URI */ + CHAR16 uri[0]; +} __attribute__ (( packed )); + +/** An EFI block device */ +struct efi_block { + /** Reference count */ + struct refcnt refcnt; + /** List of all registered block devices */ + struct list_head list; + + /** Block device URI */ + struct uri *uri; + /** Drive number */ + unsigned int drive; + /** Underlying block device interface */ + struct interface intf; + + /** Current device status */ + int block_rc; + /** Raw block device capacity */ + struct block_device_capacity capacity; + /** Block size shift + * + * To allow for emulation of CD-ROM access, this represents + * the left-shift required to translate from EFI I/O blocks to + * underlying blocks. + * + * Note that the LogicalBlocksPerPhysicalBlock field in + * EFI_BLOCK_IO_MEDIA is unable to encapsulate this + * information, since it does not allow us to describe a + * situation in which there are multiple "physical" + * (i.e. underlying) blocks per logical (i.e. exposed) block. + */ + unsigned int blksize_shift; + + /** EFI handle */ + EFI_HANDLE handle; + /** Media descriptor */ + EFI_BLOCK_IO_MEDIA media; + /** Block I/O protocol */ + EFI_BLOCK_IO_PROTOCOL block_io; + /** Device path protocol */ + EFI_DEVICE_PATH_PROTOCOL *path; + + /** Command interface */ + struct interface command; + /** Command timeout timer */ + struct retry_timer timer; + /** Command status */ + int command_rc; +}; + +/** + * Free EFI block device + * + * @v refcnt Reference count + */ +static void efi_block_free ( struct refcnt *refcnt ) { + struct efi_block *block = + container_of ( refcnt, struct efi_block, refcnt ); + + assert ( ! timer_running ( &block->timer ) ); + uri_put ( block->uri ); + free ( block->path ); + free ( block ); +} + +/** List of EFI block devices */ +static LIST_HEAD ( efi_block_devices ); + +/** + * Find EFI block device + * + * @v drive Drive number + * @ret block Block device, or NULL if not found + */ +static struct efi_block * efi_block_find ( unsigned int drive ) { + struct efi_block *block; + + list_for_each_entry ( block, &efi_block_devices, list ) { + if ( block->drive == drive ) + return block; + } + + return NULL; +} + +/** + * Close EFI block device command + * + * @v block Block device + * @v rc Reason for close + */ +static void efi_block_cmd_close ( struct efi_block *block, int rc ) { + + /* Stop timer */ + stop_timer ( &block->timer ); + + /* Restart interface */ + intf_restart ( &block->command, rc ); + + /* Record command status */ + block->command_rc = rc; +} + +/** + * Record EFI block device capacity + * + * @v block Block device + * @v capacity Block device capacity + */ +static void efi_block_cmd_capacity ( struct efi_block *block, + struct block_device_capacity *capacity ) { + + /* Record raw capacity information */ + memcpy ( &block->capacity, capacity, sizeof ( block->capacity ) ); +} + +/** EFI block device command interface operations */ +static struct interface_operation efi_block_cmd_op[] = { + INTF_OP ( intf_close, struct efi_block *, efi_block_cmd_close ), + INTF_OP ( block_capacity, struct efi_block *, efi_block_cmd_capacity ), +}; + +/** EFI block device command interface descriptor */ +static struct interface_descriptor efi_block_cmd_desc = + INTF_DESC ( struct efi_block, command, efi_block_cmd_op ); + +/** + * Handle EFI block device command timeout + * + * @v retry Retry timer + */ +static void efi_block_cmd_expired ( struct retry_timer *timer, + int over __unused ) { + struct efi_block *block = + container_of ( timer, struct efi_block, timer ); + + efi_block_cmd_close ( block, -ETIMEDOUT ); +} + +/** + * Restart EFI block device interface + * + * @v block Block device + * @v rc Reason for restart + */ +static void efi_block_restart ( struct efi_block *block, int rc ) { + + /* Restart block device interface */ + intf_nullify ( &block->command ); /* avoid potential loops */ + intf_restart ( &block->intf, rc ); + + /* Close any outstanding command */ + efi_block_cmd_close ( block, rc ); + + /* Record device error */ + block->block_rc = rc; +} + +/** + * (Re)open EFI block device + * + * @v block Block device + * @ret rc Return status code + * + * This function will block until the device is available. + */ +static int efi_block_reopen ( struct efi_block *block ) { + int rc; + + /* Close any outstanding command and restart interface */ + efi_block_restart ( block, -ECONNRESET ); + + /* Mark device as being not yet open */ + block->block_rc = -EINPROGRESS; + + /* Open block device interface */ + if ( ( rc = xfer_open_uri ( &block->intf, block->uri ) ) != 0 ) { + DBGC ( block, "EFIBLK %#02x could not (re)open URI: %s\n", + block->drive, strerror ( rc ) ); + return rc; + } + + /* Wait for device to become available */ + while ( block->block_rc == -EINPROGRESS ) { + step(); + if ( xfer_window ( &block->intf ) != 0 ) { + block->block_rc = 0; + return 0; + } + } + + DBGC ( block, "EFIBLK %#02x never became available: %s\n", + block->drive, strerror ( block->block_rc ) ); + return block->block_rc; +} + +/** + * Handle closure of underlying block device interface + * + * @v block Block device + * @ret rc Reason for close + */ +static void efi_block_close ( struct efi_block *block, int rc ) { + + /* Any closure is an error from our point of view */ + if ( rc == 0 ) + rc = -ENOTCONN; + DBGC ( block, "EFIBLK %#02x went away: %s\n", + block->drive, strerror ( rc ) ); + + /* Close any outstanding command and restart interface */ + efi_block_restart ( block, rc ); +} + +/** + * Check EFI block device flow control window + * + * @v block Block device + */ +static size_t efi_block_window ( struct efi_block *block __unused ) { + + /* We are never ready to receive data via this interface. + * This prevents objects that support both block and stream + * interfaces from attempting to send us stream data. + */ + return 0; +} + +/** EFI block device interface operations */ +static struct interface_operation efi_block_op[] = { + INTF_OP ( intf_close, struct efi_block *, efi_block_close ), + INTF_OP ( xfer_window, struct efi_block *, efi_block_window ), +}; + +/** EFI block device interface descriptor */ +static struct interface_descriptor efi_block_desc = + INTF_DESC ( struct efi_block, intf, efi_block_op ); + +/** EFI block device command context */ +struct efi_block_command_context { + /** Starting LBA (using EFI block numbering) */ + uint64_t lba; + /** Data buffer */ + void *data; + /** Length of data buffer */ + size_t len; + /** Block device read/write operation (if any) */ + int ( * block_rw ) ( struct interface *control, struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ); +}; + +/** + * Initiate EFI block device command + * + * @v block Block device + * @v op Command operation + * @v context Command context, or NULL if not required + * @ret rc Return status code + */ +static int efi_block_command ( struct efi_block *block, + int ( * op ) ( struct efi_block *block, + struct efi_block_command_context + *context ), + struct efi_block_command_context *context ) { + int rc; + + /* Sanity check */ + assert ( ! timer_running ( &block->timer ) ); + + /* Reopen block device if applicable */ + if ( ( block->block_rc != 0 ) && + ( ( rc = efi_block_reopen ( block ) ) != 0 ) ) { + goto err_reopen; + } + + /* Start expiry timer */ + start_timer_fixed ( &block->timer, EFI_BLOCK_TIMEOUT ); + + /* Initiate block device operation */ + if ( ( rc = op ( block, context ) ) != 0 ) + goto err_op; + + /* Wait for command to complete */ + while ( timer_running ( &block->timer ) ) + step(); + + /* Collect return status */ + rc = block->command_rc; + + return rc; + + err_op: + stop_timer ( &block->timer ); + err_reopen: + return rc; +} + +/** + * Initiate EFI block device read/write command + * + * @v block Block device + * @v context Command context + * @ret rc Return status code + */ +static int efi_block_cmd_rw ( struct efi_block *block, + struct efi_block_command_context *context ) { + uint64_t lba; + unsigned int count; + int rc; + + /* Calculate underlying starting LBA and block count */ + if ( block->capacity.blksize == 0 ) { + DBGC ( block, "EFIBLK %#02x has zero block size\n", + block->drive ); + return -EINVAL; + } + lba = ( context->lba << block->blksize_shift ); + count = ( context->len / block->capacity.blksize ); + if ( ( count * block->capacity.blksize ) != context->len ) { + DBGC ( block, "EFIBLK %#02x invalid read/write length %#zx\n", + block->drive, context->len ); + return -EINVAL; + } + + /* Initiate read/write command */ + if ( ( rc = context->block_rw ( &block->intf, &block->command, lba, + count, virt_to_user ( context->data ), + context->len ) ) != 0 ) { + DBGC ( block, "EFIBLK %#02x could not initiate read/write: " + "%s\n", block->drive, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Initiate EFI block device read capacity command + * + * @v block Block device + * @v context Command context + * @ret rc Return status code + */ +static int efi_block_cmd_read_capacity ( struct efi_block *block, + struct efi_block_command_context + *context __unused ) { + int rc; + + /* Initiate read capacity command */ + if ( ( rc = block_read_capacity ( &block->intf, + &block->command ) ) != 0 ) { + DBGC ( block, "EFIBLK %#02x could not read capacity: %s\n", + block->drive, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Reset EFI block device + * + * @v block_io Block I/O protocol + * @v verify Perform extended verification + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *block_io, + BOOLEAN verify __unused ) { + struct efi_block *block = + container_of ( block_io, struct efi_block, block_io ); + int rc; + + DBGC2 ( block, "EFIBLK %#02x reset\n", block->drive ); + + /* Claim network devices for use by iPXE */ + efi_snp_claim(); + + /* Reopen block device */ + if ( ( rc = efi_block_reopen ( block ) ) != 0 ) + goto err_reopen; + + err_reopen: + efi_snp_release(); + return EFIRC ( rc ); +} + +/** + * Read from EFI block device + * + * @v block_io Block I/O protocol + * @v media Media identifier + * @v lba Starting LBA + * @v len Size of buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, + EFI_LBA lba, UINTN len, VOID *data ) { + struct efi_block *block = + container_of ( block_io, struct efi_block, block_io ); + struct efi_block_command_context context = { + .lba = lba, + .data = data, + .len = len, + .block_rw = block_read, + }; + int rc; + + DBGC2 ( block, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", + block->drive, context.lba, context.data, context.len ); + + /* Claim network devices for use by iPXE */ + efi_snp_claim(); + + /* Issue read command */ + if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, + &context ) ) != 0 ) + goto err_command; + + err_command: + efi_snp_release(); + return EFIRC ( rc ); +} + +/** + * Write to EFI block device + * + * @v block_io Block I/O protocol + * @v media Media identifier + * @v lba Starting LBA + * @v len Size of buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, + EFI_LBA lba, UINTN len, VOID *data ) { + struct efi_block *block = + container_of ( block_io, struct efi_block, block_io ); + struct efi_block_command_context context = { + .lba = lba, + .data = data, + .len = len, + .block_rw = block_write, + }; + int rc; + + DBGC2 ( block, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", + block->drive, context.lba, context.data, context.len ); + + /* Claim network devices for use by iPXE */ + efi_snp_claim(); + + /* Issue write command */ + if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, + &context ) ) != 0 ) + goto err_command; + + err_command: + efi_snp_release(); + return EFIRC ( rc ); +} + +/** + * Flush data to EFI block device + * + * @v block_io Block I/O protocol + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_block_io_flush ( EFI_BLOCK_IO_PROTOCOL *block_io ) { + struct efi_block *block = + container_of ( block_io, struct efi_block, block_io ); + + DBGC2 ( block, "EFIBLK %#02x flush\n", block->drive ); + + /* Nothing to do */ + return 0; +} + +/** + * Create device path for EFI block device + * + * @v uri Block device URI + * @v parent Parent device path + * @ret path Device path, or NULL on failure + * + * The caller must eventually free() the device path. + */ +static EFI_DEVICE_PATH_PROTOCOL * +efi_block_path ( struct uri *uri, EFI_DEVICE_PATH_PROTOCOL *parent ) { + EFI_DEVICE_PATH_PROTOCOL *path; + struct efi_block_vendor_path *vendor; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t prefix_len; + size_t uri_len; + size_t vendor_len; + size_t len; + char *uri_buf; + + /* Calculate device path lengths */ + end = efi_devpath_end ( parent ); + prefix_len = ( ( void * ) end - ( void * ) parent ); + uri_len = format_uri ( uri, NULL, 0 ); + vendor_len = ( sizeof ( *vendor ) + + ( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) ); + len = ( prefix_len + vendor_len + sizeof ( *end ) ); + + /* Allocate device path and space for URI buffer */ + path = zalloc ( len + uri_len + 1 /* NUL */ ); + if ( ! path ) + return NULL; + uri_buf = ( ( ( void * ) path ) + len ); + + /* Construct device path */ + memcpy ( path, parent, prefix_len ); + vendor = ( ( ( void * ) path ) + prefix_len ); + vendor->vendor.Header.Type = HARDWARE_DEVICE_PATH; + vendor->vendor.Header.SubType = HW_VENDOR_DP; + vendor->vendor.Header.Length[0] = ( vendor_len & 0xff ); + vendor->vendor.Header.Length[1] = ( vendor_len >> 8 ); + memcpy ( &vendor->vendor.Guid, &ipxe_block_device_path_guid, + sizeof ( vendor->vendor.Guid ) ); + format_uri ( uri, uri_buf, ( uri_len + 1 /* NUL */ ) ); + efi_snprintf ( vendor->uri, ( uri_len + 1 /* NUL */ ), "%s", uri_buf ); + end = ( ( ( void * ) vendor ) + vendor_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + return path; +} + +/** + * Configure EFI block device as a CD-ROM, if applicable + * + * @v block Block device + * @v scratch Scratch data area + * @ret rc Return status code + * + * The EDK2 code will ignore CD-ROM devices with a block size other + * than 2048. While we could require the user to configure the block + * size appropriately, this is non-trivial and would impose a + * substantial learning effort on the user. Instead, we perform + * essentially the same auto-detection as under a BIOS SAN boot; if + * the ISO9660 primary volume descriptor is present then we force a + * block size of 2048 and map read/write requests appropriately. + */ +static int efi_block_parse_iso9660 ( struct efi_block *block, void *scratch ) { + static const struct iso9660_primary_descriptor_fixed primary_check = { + .type = ISO9660_TYPE_PRIMARY, + .id = ISO9660_ID, + }; + struct iso9660_primary_descriptor *primary = scratch; + struct efi_block_command_context context; + unsigned int blksize; + unsigned int blksize_shift; + int rc; + + /* Calculate required blocksize shift for potential CD-ROM access */ + blksize = block->capacity.blksize; + blksize_shift = 0; + while ( blksize < ISO9660_BLKSIZE ) { + blksize <<= 1; + blksize_shift++; + } + if ( blksize > ISO9660_BLKSIZE ) { + /* Cannot be a CD-ROM */ + return 0; + } + + /* Read primary volume descriptor */ + memset ( &context, 0, sizeof ( context ) ); + context.lba = ( ISO9660_PRIMARY_LBA << blksize_shift ); + context.data = primary; + context.len = ISO9660_BLKSIZE; + context.block_rw = block_read; + if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, + &context ) ) != 0 ) { + DBGC ( block, "EFIBLK %#02x could not read ISO9660 primary " + "volume descriptor: %s\n", + block->drive, strerror ( rc ) ); + return rc; + } + + /* Do nothing unless this is an ISO image */ + if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) != 0 ) + return 0; + DBGC ( block, "EFIBLK %#02x contains an ISO9660 filesystem; treating " + "as CD-ROM\n", block->drive ); + block->blksize_shift = blksize_shift; + + return 0; +} + +/** + * Determing EFI block device capacity and block size + * + * @v block Block device + * @ret rc Return status code + */ +static int efi_block_capacity ( struct efi_block *block ) { + size_t scratch_len; + void *scratch; + int rc; + + /* Read read block capacity */ + if ( ( rc = efi_block_command ( block, efi_block_cmd_read_capacity, + NULL ) ) != 0 ) + goto err_read_capacity; + block->blksize_shift = 0; + + /* Allocate scratch area */ + scratch_len = ( block->capacity.blksize ); + if ( scratch_len < ISO9660_BLKSIZE ) + scratch_len = ISO9660_BLKSIZE; + scratch = malloc ( scratch_len ); + if ( ! scratch ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Configure as a CD-ROM, if applicable */ + if ( ( rc = efi_block_parse_iso9660 ( block, scratch ) ) != 0 ) + goto err_parse_iso9660; + + /* Update media descriptor */ + block->media.BlockSize = + ( block->capacity.blksize << block->blksize_shift ); + block->media.LastBlock = + ( ( block->capacity.blocks >> block->blksize_shift ) - 1 ); + + /* Success */ + rc = 0; + + err_parse_iso9660: + free ( scratch ); + err_alloc: + err_read_capacity: + return rc; +} + +/** + * Connect all possible drivers to EFI block device + * + * @v block Block device + */ +static void efi_block_connect ( struct efi_block *block ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Try to connect all possible drivers to this block device */ + if ( ( efirc = bs->ConnectController ( block->handle, NULL, + NULL, 1 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( block, "EFIBLK %#02x could not connect drivers: %s\n", + block->drive, strerror ( rc ) ); + /* May not be an error; may already be connected */ + } + DBGC2 ( block, "EFIBLK %#02x supports protocols:\n", block->drive ); + DBGC2_EFI_PROTOCOLS ( block, block->handle ); +} + +/** + * Hook EFI block device + * + * @v uri URI + * @v drive Drive number + * @ret drive Drive number, or negative error + */ +static int efi_block_hook ( struct uri *uri, unsigned int drive ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_snp_device *snpdev; + struct efi_block *block; + EFI_STATUS efirc; + int rc; + + /* Check that drive number is not already in use */ + if ( efi_block_find ( drive ) ) { + rc = -EADDRINUSE; + goto err_in_use; + } + + /* Allocate and initialise structure */ + block = zalloc ( sizeof ( *block ) ); + if ( ! block ) { + rc = -ENOMEM; + goto err_zalloc; + } + ref_init ( &block->refcnt, efi_block_free ); + intf_init ( &block->intf, &efi_block_desc, &block->refcnt ); + block->uri = uri_get ( uri ); + block->drive = drive; + block->block_rc = -EINPROGRESS; + block->media.MediaPresent = 1; + block->media.LogicalBlocksPerPhysicalBlock = 1; + block->block_io.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + block->block_io.Media = &block->media; + block->block_io.Reset = efi_block_io_reset; + block->block_io.ReadBlocks = efi_block_io_read; + block->block_io.WriteBlocks = efi_block_io_write; + block->block_io.FlushBlocks = efi_block_io_flush; + intf_init ( &block->command, &efi_block_cmd_desc, &block->refcnt ); + timer_init ( &block->timer, efi_block_cmd_expired, &block->refcnt ); + + /* Find an appropriate parent device handle */ + snpdev = last_opened_snpdev(); + if ( ! snpdev ) { + DBGC ( block, "EFIBLK %#02x could not identify SNP device\n", + block->drive ); + rc = -ENODEV; + goto err_no_snpdev; + } + + /* Construct device path */ + block->path = efi_block_path ( block->uri, snpdev->path ); + if ( ! block->path ) { + rc = -ENOMEM; + goto err_path; + } + DBGC ( block, "EFIBLK %#02x has device path %s\n", + block->drive, efi_devpath_text ( block->path ) ); + + /* Add to list of block devices */ + list_add ( &block->list, &efi_block_devices ); + + /* Open block device interface */ + if ( ( rc = efi_block_reopen ( block ) ) != 0 ) + goto err_reopen; + + /* Determine capacity and block size */ + if ( ( rc = efi_block_capacity ( block ) ) != 0 ) + goto err_capacity; + + /* Install protocols */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &block->handle, + &efi_block_io_protocol_guid, &block->block_io, + &efi_device_path_protocol_guid, block->path, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( block, "EFIBLK %#02x could not install protocols: %s\n", + block->drive, strerror ( rc ) ); + goto err_install; + } + + /* Connect all possible protocols */ + efi_block_connect ( block ); + + return drive; + + bs->UninstallMultipleProtocolInterfaces ( + block->handle, + &efi_block_io_protocol_guid, &block->block_io, + &efi_device_path_protocol_guid, block->path, NULL ); + err_install: + err_capacity: + efi_block_restart ( block, rc ); + intf_shutdown ( &block->intf, rc ); + err_reopen: + list_del ( &block->list ); + err_no_snpdev: + err_path: + ref_put ( &block->refcnt ); + err_zalloc: + err_in_use: + return rc; +} + +/** + * Unhook EFI block device + * + * @v drive Drive number + */ +static void efi_block_unhook ( unsigned int drive ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block *block; + + /* Find block device */ + block = efi_block_find ( drive ); + if ( ! block ) { + DBG ( "EFIBLK cannot find drive %#02x\n", drive ); + return; + } + + /* Uninstall protocols */ + bs->UninstallMultipleProtocolInterfaces ( + block->handle, + &efi_block_io_protocol_guid, &block->block_io, + &efi_device_path_protocol_guid, block->path, NULL ); + + /* Close any outstanding commands and shut down interface */ + efi_block_restart ( block, 0 ); + intf_shutdown ( &block->intf, 0 ); + + /* Remove from list of block devices */ + list_del ( &block->list ); + + /* Drop list's reference to drive */ + ref_put ( &block->refcnt ); +} + +/** + * Describe EFI block device + * + * @v drive Drive number + * @ret rc Return status code + */ +static int efi_block_describe ( unsigned int drive ) { + struct efi_block *block; + + /* Find block device */ + block = efi_block_find ( drive ); + if ( ! block ) { + DBG ( "EFIBLK cannot find drive %#02x\n", drive ); + return -ENODEV; + } + + return 0; +} + +/** + * Try booting from child device of EFI block device + * + * @v block Block device + * @v handle EFI handle + * @ret rc Return status code + */ +static int efi_block_boot_image ( struct efi_block *block, + EFI_HANDLE handle, EFI_HANDLE *image ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; + EFI_DEVICE_PATH_PROTOCOL *boot_path; + FILEPATH_DEVICE_PATH *filepath; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t prefix_len; + size_t filepath_len; + size_t boot_path_len; + EFI_STATUS efirc; + int rc; + + /* Identify device path */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_device_path_protocol_guid, + &path.interface, efi_image_handle, + handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + DBGC ( block, "EFIBLK %#02x found filesystem with no device " + "path??", block->drive ); + rc = -EEFI ( efirc ); + goto err_open_device_path; + } + + /* Check if this device is a child of our block device */ + end = efi_devpath_end ( block->path ); + prefix_len = ( ( ( void * ) end ) - ( ( void * ) block->path ) ); + if ( memcmp ( path.path, block->path, prefix_len ) != 0 ) { + /* Not a child device */ + rc = -ENOTTY; + goto err_not_child; + } + DBGC ( block, "EFIBLK %#02x found child device %s\n", + block->drive, efi_devpath_text ( path.path ) ); + + /* Construct device path for boot image */ + end = efi_devpath_end ( path.path ); + prefix_len = ( ( ( void * ) end ) - ( ( void * ) path.path ) ); + filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH + + sizeof ( efi_block_boot_filename ) ); + boot_path_len = ( prefix_len + filepath_len + sizeof ( *end ) ); + boot_path = zalloc ( boot_path_len ); + if ( ! boot_path ) { + rc = -ENOMEM; + goto err_alloc_path; + } + memcpy ( boot_path, path.path, prefix_len ); + filepath = ( ( ( void * ) boot_path ) + prefix_len ); + filepath->Header.Type = MEDIA_DEVICE_PATH; + filepath->Header.SubType = MEDIA_FILEPATH_DP; + filepath->Header.Length[0] = ( filepath_len & 0xff ); + filepath->Header.Length[1] = ( filepath_len >> 8 ); + memcpy ( filepath->PathName, efi_block_boot_filename, + sizeof ( efi_block_boot_filename ) ); + end = ( ( ( void * ) filepath ) + filepath_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + DBGC ( block, "EFIBLK %#02x trying to load %s\n", + block->drive, efi_devpath_text ( boot_path ) ); + + /* Try loading boot image from this device */ + if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path, + NULL, 0, image ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( block, "EFIBLK %#02x could not load image: %s\n", + block->drive, strerror ( rc ) ); + goto err_load_image; + } + + /* Success */ + rc = 0; + + err_load_image: + free ( boot_path ); + err_alloc_path: + err_not_child: + err_open_device_path: + return rc; +} + +/** + * Boot from EFI block device + * + * @v drive Drive number + * @ret rc Return status code + */ +static int efi_block_boot ( unsigned int drive ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block *block; + EFI_HANDLE *handles; + EFI_HANDLE image = NULL; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Find block device */ + block = efi_block_find ( drive ); + if ( ! block ) { + DBG ( "EFIBLK cannot find drive %#02x\n", drive ); + rc = -ENODEV; + goto err_block_find; + } + + /* Connect all possible protocols */ + efi_block_connect ( block ); + + /* Locate all handles supporting the Simple File System protocol */ + if ( ( efirc = bs->LocateHandleBuffer ( + ByProtocol, &efi_simple_file_system_protocol_guid, + NULL, &count, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( block, "EFIBLK %#02x cannot locate file systems: %s\n", + block->drive, strerror ( rc ) ); + goto err_locate_file_systems; + } + + /* Try booting from any available child device containing a + * suitable boot image. This is something of a wild stab in + * the dark, but should end up conforming to user expectations + * most of the time. + */ + rc = -ENOENT; + for ( i = 0 ; i < count ; i++ ) { + if ( ( rc = efi_block_boot_image ( block, handles[i], + &image ) ) != 0 ) + continue; + DBGC ( block, "EFIBLK %#02x found boot image\n", block->drive ); + efirc = bs->StartImage ( image, NULL, NULL ); + rc = ( efirc ? -EEFI ( efirc ) : 0 ); + bs->UnloadImage ( image ); + DBGC ( block, "EFIBLK %#02x boot image returned: %s\n", + block->drive, strerror ( rc ) ); + break; + } + + bs->FreePool ( handles ); + err_locate_file_systems: + err_block_find: + return rc; +} + +PROVIDE_SANBOOT_INLINE ( efi, san_default_drive ); +PROVIDE_SANBOOT ( efi, san_hook, efi_block_hook ); +PROVIDE_SANBOOT ( efi, san_unhook, efi_block_unhook ); +PROVIDE_SANBOOT ( efi, san_describe, efi_block_describe ); +PROVIDE_SANBOOT ( efi, san_boot, efi_block_boot ); From 8138ea190d8f281a9bd3cbfff2b83daee3478987 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 5 Dec 2016 08:50:03 +0000 Subject: [PATCH 318/591] [undi] Allocate base memory before calling UNDI loader entry point Allocate base memory (by decreasing the free base memory counter) before calling the UNDI loader entry point, to minimise surprises for the UNDI loader code. Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undiload.c | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/arch/x86/drivers/net/undiload.c b/src/arch/x86/drivers/net/undiload.c index 7160ee384..492dae4ba 100644 --- a/src/arch/x86/drivers/net/undiload.c +++ b/src/arch/x86/drivers/net/undiload.c @@ -72,7 +72,8 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { /* Only one UNDI instance may be loaded at any given time */ if ( undi_loader_entry.segment ) { DBG ( "UNDI %p cannot load multiple instances\n", undi ); - return -EBUSY; + rc = -EBUSY; + goto err_multiple; } /* Set up START_UNDI parameters */ @@ -90,10 +91,15 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { undi_loader.UNDI_CS = fbms_seg; fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 ); undi_loader.UNDI_DS = fbms_seg; + undi->fbms = ( fbms_seg >> 6 ); + set_fbms ( undi->fbms ); + DBGC ( undi, "UNDI %p allocated [%d,%d) kB of base memory\n", + undi, undi->fbms, undi->restore_fbms ); /* Debug info */ - DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ", - undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS ); + DBGC ( undi, "UNDI %p loading ROM %p to CS %04x:%04zx DS %04x:%04zx " + "for ", undi, undirom, undi_loader.UNDI_CS, undirom->code_size, + undi_loader.UNDI_DS, undirom->data_size ); if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { unsigned int bus = ( undi->pci_busdevfn >> 8 ); unsigned int devfn = ( undi->pci_busdevfn & 0xff ); @@ -116,15 +122,11 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { : "=a" ( exit ) : "a" ( __from_data16 ( &undi_loader ) ) : "ebx", "ecx", "edx", "esi", "edi" ); - if ( exit != PXENV_EXIT_SUCCESS ) { - /* Clear entry point */ - memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) ); - rc = -EUNDILOAD ( undi_loader.Status ); DBGC ( undi, "UNDI %p loader failed: %s\n", undi, strerror ( rc ) ); - return rc; + goto err_loader; } /* Populate PXE device structure */ @@ -138,13 +140,13 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset, undi->entry.segment, undi->entry.offset ); - /* Update free base memory counter */ - undi->fbms = ( fbms_seg >> 6 ); - set_fbms ( undi->fbms ); - DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n", - undi, undi->fbms, undi->restore_fbms ); - return 0; + + err_loader: + set_fbms ( undi->restore_fbms ); + memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) ); + err_multiple: + return rc; } /** From cc40fcbf8b0dedc18431b9281f793c20c6276f96 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 5 Dec 2016 14:11:00 +0000 Subject: [PATCH 319/591] [romprefix] Avoid using PMM-allocated memory in UNDI loader entry point The UNDI loader entry point is very likely to be called after POST, when there is a high chance that the PMM-allocated image source area and decompression area have been reused by something else. In particular, using an iPXE .iso to test a separate iPXE ROM's UNDI loader entry point in a qemu VM is likely to crash. SeaBIOS allocates PMM blocks from close to the top of memory and so these blocks have a high chance of colliding with the runtime addresses subsequently chosen by the non-ROM iPXE by scanning the INT 15,e820 memory map. The standard romprefix.S has no choice about relying on the PMM-allocated image source area, since it has no other way to retrieve its compressed payload. In mromprefix.S, the image source area functions only as an optional buffer used to avoid repeated reads from the (potentially slow) expansion ROM BAR by the decompression code. We can therefore always set %esi=0 when calling install_prealloc from the UNDI loader entry point, and simply fall back to reading directly from the expansion ROM BAR. We can always set %edi=0 when calling install_prealloc from the UNDI loader entry point. This will behave as though the decompression area PMM allocation failed, and will therefore use INT 15,88 to find a temporary decompression area somewhere close to 64MB. This is by no means guaranteed to be safe from collisions, but it's probably safer on balance than the PMM-allocated address. Signed-off-by: Michael Brown --- src/arch/x86/prefix/mromprefix.S | 18 ++++++++++++++++++ src/arch/x86/prefix/undiloader.S | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/arch/x86/prefix/mromprefix.S b/src/arch/x86/prefix/mromprefix.S index b636b92af..568d1c006 100644 --- a/src/arch/x86/prefix/mromprefix.S +++ b/src/arch/x86/prefix/mromprefix.S @@ -456,6 +456,24 @@ pci_set_mem_access: ret .size pci_set_mem_access, . - pci_set_mem_access +/* Update image source address for UNDI loader + * + * Parameters: + * %esi : Image source address + * Returns: + * %esi : Image source address + */ + .section ".prefix", "ax", @progbits + .globl undiloader_source +undiloader_source: + /* Always use expansion ROM BAR directly when installing via + * the UNDI loader entry point, since the PMM-allocated block + * may collide with whatever is calling the UNDI loader entry + * point. + */ + xorl %esi, %esi + ret + /* Payload prefix * * We include a dummy ROM header to cover the "hidden" portion of the diff --git a/src/arch/x86/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S index 530b48e8a..1d77110e7 100644 --- a/src/arch/x86/prefix/undiloader.S +++ b/src/arch/x86/prefix/undiloader.S @@ -35,7 +35,8 @@ undiloader: movw %es:12(%di), %bx movw %es:14(%di), %ax movl image_source, %esi - movl decompress_to, %edi + call undiloader_source + xorl %edi, %edi orl $0xffffffff, %ebp /* Allow arbitrary relocation */ call install_prealloc popw %di @@ -57,3 +58,16 @@ undiloader: popl %edi popl %esi lret + +/* Update image source address for UNDI loader + * + * Parameters: + * %esi : Image source address + * Returns: + * %esi : Image source address + */ + .section ".prefix", "ax", @progbits + .globl undiloader_source + .weak undiloader_source +undiloader_source: + ret From 6997d3c2fab72732d8af0955c9c760f7f5c4562b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 5 Dec 2016 15:45:17 +0000 Subject: [PATCH 320/591] [undi] Clean up driver and device name information Fix the driver name reported by "ifstat" when using the undipci driver (due to the unnecessary extra device node inserted as a child of the PCI device). Remove the "UNDI-" prefix from device names since the driver name is also now visible via "ifstat", and tidy up the device name to match the format used by standard PCI devices. The output from "ifstat" now resembles: iPXE> ifstat net0: 52:54:00:12:34:56 using undipci on 0000:00:03.0 iPXE> ifstat net0: 52:54:00:12:34:56 using undionly on 0000:00:03.0 Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undi.c | 12 +--------- src/arch/x86/drivers/net/undinet.c | 17 ++++++------- src/arch/x86/drivers/net/undionly.c | 37 ++++++++++++++++------------- src/arch/x86/include/undi.h | 2 -- src/arch/x86/include/undinet.h | 3 ++- 5 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/arch/x86/drivers/net/undi.c b/src/arch/x86/drivers/net/undi.c index 9820cf629..87c93c3b4 100644 --- a/src/arch/x86/drivers/net/undi.c +++ b/src/arch/x86/drivers/net/undi.c @@ -94,23 +94,14 @@ static int undipci_probe ( struct pci_device *pci ) { } } - /* Add to device hierarchy */ - snprintf ( undi->dev.name, sizeof ( undi->dev.name ), - "UNDI-%s", pci->dev.name ); - memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) ); - undi->dev.parent = &pci->dev; - INIT_LIST_HEAD ( &undi->dev.children ); - list_add ( &undi->dev.siblings, &pci->dev.children ); - /* Create network device */ - if ( ( rc = undinet_probe ( undi ) ) != 0 ) + if ( ( rc = undinet_probe ( undi, &pci->dev ) ) != 0 ) goto err_undinet_probe; return 0; err_undinet_probe: undi_unload ( undi ); - list_del ( &undi->dev.siblings ); err_find_rom: err_load_pci: free ( undi ); @@ -128,7 +119,6 @@ static void undipci_remove ( struct pci_device *pci ) { undinet_remove ( undi ); undi_unload ( undi ); - list_del ( &undi->dev.siblings ); free ( undi ); pci_set_drvdata ( pci, NULL ); } diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index 091ef9254..2afffa251 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -598,19 +598,19 @@ static const struct undinet_irq_broken undinet_irq_broken_list[] = { /** * Check for devices with broken support for generating interrupts * - * @v undi UNDI device + * @v desc Device description * @ret irq_is_broken Interrupt support is broken; no interrupts are generated */ -static int undinet_irq_is_broken ( struct undi_device *undi ) { +static int undinet_irq_is_broken ( struct device_description *desc ) { const struct undinet_irq_broken *broken; unsigned int i; for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) / sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) { broken = &undinet_irq_broken_list[i]; - if ( ( undi->dev.desc.bus_type == BUS_TYPE_PCI ) && - ( undi->dev.desc.vendor == broken->pci_vendor ) && - ( undi->dev.desc.device == broken->pci_device ) ) { + if ( ( desc->bus_type == BUS_TYPE_PCI ) && + ( desc->vendor == broken->pci_vendor ) && + ( desc->device == broken->pci_device ) ) { return 1; } } @@ -621,9 +621,10 @@ static int undinet_irq_is_broken ( struct undi_device *undi ) { * Probe UNDI device * * @v undi UNDI device + * @v dev Underlying generic device * @ret rc Return status code */ -int undinet_probe ( struct undi_device *undi ) { +int undinet_probe ( struct undi_device *undi, struct device *dev ) { struct net_device *netdev; struct undi_nic *undinic; struct s_PXENV_START_UNDI start_undi; @@ -644,7 +645,7 @@ int undinet_probe ( struct undi_device *undi ) { netdev_init ( netdev, &undinet_operations ); undinic = netdev->priv; undi_set_drvdata ( undi, netdev ); - netdev->dev = &undi->dev; + netdev->dev = dev; memset ( undinic, 0, sizeof ( *undinic ) ); undinet_entry = undi->entry; DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi ); @@ -733,7 +734,7 @@ int undinet_probe ( struct undi_device *undi ) { undinic ); undinic->hacks |= UNDI_HACK_EB54; } - if ( undinet_irq_is_broken ( undi ) ) { + if ( undinet_irq_is_broken ( &dev->desc ) ) { DBGC ( undinic, "UNDINIC %p forcing polling mode due to " "broken interrupts\n", undinic ); undinic->irq_supported = 0; diff --git a/src/arch/x86/drivers/net/undionly.c b/src/arch/x86/drivers/net/undionly.c index 70dbe4bfd..9c9ca1274 100644 --- a/src/arch/x86/drivers/net/undionly.c +++ b/src/arch/x86/drivers/net/undionly.c @@ -50,6 +50,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * addition to the UNDI driver, build e.g. "bin/undi.dsk". */ +/** UNDI root bus device */ +static struct device undibus_dev; + /** * Probe UNDI root bus * @@ -60,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ static int undibus_probe ( struct root_device *rootdev ) { struct undi_device *undi = &preloaded_undi; + struct device *dev = &undibus_dev; int rc; /* Check for a valie preloaded UNDI device */ @@ -69,34 +73,32 @@ static int undibus_probe ( struct root_device *rootdev ) { } /* Add to device hierarchy */ - undi->dev.driver_name = "undionly"; + dev->driver_name = "undionly"; if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { - undi->dev.desc.bus_type = BUS_TYPE_PCI; - undi->dev.desc.location = undi->pci_busdevfn; - undi->dev.desc.vendor = undi->pci_vendor; - undi->dev.desc.device = undi->pci_device; - snprintf ( undi->dev.name, sizeof ( undi->dev.name ), - "UNDI-PCI%02x:%02x.%x", - PCI_BUS ( undi->pci_busdevfn ), + dev->desc.bus_type = BUS_TYPE_PCI; + dev->desc.location = undi->pci_busdevfn; + dev->desc.vendor = undi->pci_vendor; + dev->desc.device = undi->pci_device; + snprintf ( dev->name, sizeof ( dev->name ), + "0000:%02x:%02x.%x", PCI_BUS ( undi->pci_busdevfn ), PCI_SLOT ( undi->pci_busdevfn ), PCI_FUNC ( undi->pci_busdevfn ) ); } else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) { - undi->dev.desc.bus_type = BUS_TYPE_ISAPNP; - snprintf ( undi->dev.name, sizeof ( undi->dev.name ), - "UNDI-ISAPNP" ); + dev->desc.bus_type = BUS_TYPE_ISAPNP; + snprintf ( dev->name, sizeof ( dev->name ), "ISAPNP" ); } - undi->dev.parent = &rootdev->dev; - list_add ( &undi->dev.siblings, &rootdev->dev.children); - INIT_LIST_HEAD ( &undi->dev.children ); + dev->parent = &rootdev->dev; + list_add ( &dev->siblings, &rootdev->dev.children); + INIT_LIST_HEAD ( &dev->children ); /* Create network device */ - if ( ( rc = undinet_probe ( undi ) ) != 0 ) + if ( ( rc = undinet_probe ( undi, dev ) ) != 0 ) goto err; return 0; err: - list_del ( &undi->dev.siblings ); + list_del ( &dev->siblings ); return rc; } @@ -107,9 +109,10 @@ static int undibus_probe ( struct root_device *rootdev ) { */ static void undibus_remove ( struct root_device *rootdev __unused ) { struct undi_device *undi = &preloaded_undi; + struct device *dev = &undibus_dev; undinet_remove ( undi ); - list_del ( &undi->dev.siblings ); + list_del ( &dev->siblings ); } /** UNDI bus root device driver */ diff --git a/src/arch/x86/include/undi.h b/src/arch/x86/include/undi.h index 7a5624f93..adf0c01e0 100644 --- a/src/arch/x86/include/undi.h +++ b/src/arch/x86/include/undi.h @@ -53,8 +53,6 @@ struct undi_device { */ UINT16_t flags; - /** Generic device */ - struct device dev; /** Driver-private data * * Use undi_set_drvdata() and undi_get_drvdata() to access this diff --git a/src/arch/x86/include/undinet.h b/src/arch/x86/include/undinet.h index 2798c4466..04fdd6000 100644 --- a/src/arch/x86/include/undinet.h +++ b/src/arch/x86/include/undinet.h @@ -10,8 +10,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct undi_device; +struct device; -extern int undinet_probe ( struct undi_device *undi ); +extern int undinet_probe ( struct undi_device *undi, struct device *dev ); extern void undinet_remove ( struct undi_device *undi ); #endif /* _UNDINET_H */ From ce81601181f741e0ab63a77405d2eddec98576e9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Dec 2016 07:36:33 +0000 Subject: [PATCH 321/591] [prefix] Remove impossible progress message The "progress" macro can be used only from within the .prefix section. At the point of calling relocate(), we are running in .text16 and so the near call to print_message() will end up calling a random function somewhere in .text16. Interestingly, this problem has remained unnoticed for some time. It is rare to build with DEBUG=libprefix. In the few cases that it has been used during development, the randomly selected function in .text16 seems to have been a harmless no-op with no visible side-effects (beyond the unnoticed failure to print the "relocate" progress message). Fix by removing the futile attempt to print a progress message before calling relocate(). Reported-by: Raed Salem Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 1 - 1 file changed, 1 deletion(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 533be981e..7c678fa87 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -873,7 +873,6 @@ install_prealloc: * relocate() will return with %esi, %edi and %ecx set up * ready for the copy to the new location. */ - progress " relocate\n" virtcall relocate /* Jump back to .prefix segment */ From 80c482c0ed674b0077db6fc5a7dbd706f00c63bf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Dec 2016 09:38:33 +0000 Subject: [PATCH 322/591] [prefix] Include diagnostic information within progress messages Include some relevant diagnostic infomation within the progress messages generated via DEBUG=libprefix. Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 52 ++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 7c678fa87..ffb211058 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -36,10 +36,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Allow for DBG()-style messages within libprefix */ #ifdef NDEBUG - .macro progress message + .macro progress message, regs:vararg .endm #else - .macro progress message + .macro dumpreg reg, others:vararg + pushl %eax + movl \reg, %eax + pushw %di + xorw %di, %di + call print_space + call print_hex_dword + popw %di + popl %eax + .ifnb \others + dumpreg \others + .endif + .endm + + .macro progress message, regs:vararg pushfl pushw %ds pushw %si @@ -51,6 +65,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) call print_message popw %di popw %si + .ifnb \regs + dumpreg \regs + .endif + pushw %di + pushw %ax + xorw %di, %di + movb $( '\n' ), %al + call print_character + popw %ax + popw %di popw %ds popfl .section ".prefix.data", "aw", @progbits @@ -659,7 +683,7 @@ hooked_bios_interrupts: .code16 .globl install install: - progress "install:\n" + progress "\ninstall:" /* Preserve registers */ pushl %esi pushl %edi @@ -702,7 +726,7 @@ install: .code16 .globl install_prealloc install_prealloc: - progress "install_prealloc:\n" + progress "\ninstall_prealloc:", %eax, %ebx, %esi, %edi, %ebp /* Save registers on external stack */ pushal pushw %ds @@ -726,7 +750,6 @@ install_prealloc: pushl %edi /* Install .text16.early and calculate %ecx as offset to next block */ - progress " .text16.early\n" pushl %esi xorl %esi, %esi movw %cs, %si @@ -737,6 +760,7 @@ install_prealloc: shll $4, %edi movl $_text16_early_filesz, %ecx movl $_text16_early_memsz, %edx + progress " .text16.early ", %esi, %edi, %ecx, %edx call install_block /* .text16.early */ jc install_block_death popl %ecx /* Calculate offset to next block */ @@ -750,7 +774,7 @@ install_prealloc: * already have 4GB segment limits as a result of calling * install_block.) */ - progress " access_highmem\n" + progress " access_highmem" pushw %cs pushw $1f pushw %ax @@ -762,7 +786,7 @@ install_prealloc: #endif /* Open payload (which may not yet be in memory) */ - progress " open_payload\n" + progress " open_payload ", %esi, %ecx pushw %cs pushw $1f pushw %ax @@ -779,16 +803,16 @@ install_prealloc: 1: addl %ecx, %esi /* Install .text16.late and .data16 */ - progress " .text16.late\n" movl $_text16_late_filesz, %ecx movl $_text16_late_memsz, %edx + progress " .text16.late ", %esi, %edi, %ecx, %edx call install_block /* .text16.late */ jc install_block_death - progress " .data16\n" movzwl %bx, %edi shll $4, %edi movl $_data16_filesz, %ecx movl $_data16_filesz, %edx /* do not zero our temporary stack */ + progress " .data16 ", %esi, %edi, %ecx, %edx call install_block /* .data16 */ jc install_block_death @@ -825,10 +849,10 @@ install_prealloc: * prior to reading the E820 memory map and relocating * properly. */ - progress " .textdata\n" pushl %edi movl $_textdata_filesz, %ecx movl $_textdata_memsz, %edx + progress " .textdata ", %esi, %edi, %ecx, %edx call install_block jc install_block_death popl %edi @@ -850,7 +874,7 @@ install_prealloc: #ifndef KEEP_IT_REAL /* Initialise librm at current location */ - progress " init_librm\n" + progress " init_librm ", %eax, %ebx, %edi movw %ax, (init_librm_vector+2) lcall *init_librm_vector @@ -881,7 +905,7 @@ install_prealloc: .section ".prefix.install_prealloc", "awx", @progbits 1: /* Copy code to new location */ - progress " copy\n" + progress " copy ", %esi, %edi, %ecx pushl %edi pushw %bx movw $copy_bytes, %bx @@ -890,7 +914,7 @@ install_prealloc: popl %edi /* Initialise librm at new location */ - progress " init_librm\n" + progress " init_librm ", %eax, %ebx, %edi lcall *init_librm_vector #else /* KEEP_IT_REAL */ @@ -902,7 +926,7 @@ install_prealloc: #endif /* KEEP_IT_REAL */ /* Close access to payload */ - progress " close_payload\n" + progress " close_payload" movw %ax, (close_payload_vector+2) lcall *close_payload_vector From e09331a4c6731b771c60f554f33cf0fdf5775124 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 7 Dec 2016 07:25:44 +0000 Subject: [PATCH 323/591] [undi] Try matching UNDI ROMs in BIOS enumeration order When searching for an UNDI ROM to match against a PCI device, search in order of increasing ROM address (within the 128kB BIOS option ROM area). This is likely (though not guaranteed) to match the order of the original enumeration performed by the BIOS, which is in turn likely to match the order of enumeration on the PCI bus. Since we load at most one UNDI ROM, the net result is that we increase our chances of loading the ROM corresponding to the selected PCI device (rather than loading a ROM corresponding to a higher-numbered PCI device with the same vendor and device IDs.) Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undirom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/x86/drivers/net/undirom.c b/src/arch/x86/drivers/net/undirom.c index b54c6170f..257b12411 100644 --- a/src/arch/x86/drivers/net/undirom.c +++ b/src/arch/x86/drivers/net/undirom.c @@ -168,7 +168,7 @@ static int undirom_probe ( unsigned int rom_segment ) { /* Add to UNDI ROM list and return */ DBGC ( undirom, "UNDIROM %p registered\n", undirom ); - list_add ( &undirom->list, &undiroms ); + list_add_tail ( &undirom->list, &undiroms ); return 0; err: From 5cf5ffea2874434ffdc64c3242f2c53ed7ec1d40 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 7 Dec 2016 13:41:06 +0000 Subject: [PATCH 324/591] [efi] Work around temporal anomaly encountered during ExitBootServices() EFI provides no clean way for device drivers to shut down in preparation for handover to a booted operating system. The platform firmware simply doesn't bother to call the drivers' Stop() methods. Instead, drivers must register an EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when ExitBootServices() is called, and clean up without any reference to the EFI driver model. Unfortunately, all timers silently stop working when ExitBootServices() is called. Even more unfortunately, and for no discernible reason, this happens before any EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled. The net effect of this entertaining design choice is that any timeout loops on the shutdown path (e.g. for gracefully closing outstanding TCP connections) may wait indefinitely. There is no way to report failure from currticks(), since the API lazily assumes that the host system continues to travel through time in the usual direction. Work around EFI's violation of this assumption by falling back to a simple free-running monotonic counter. Debugged-by: Maor Dickman Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_init.c | 10 ++++++++++ src/interface/efi/efi_timer.c | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index d34155321..48ec0968a 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -214,6 +214,7 @@ extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; extern EFI_SYSTEM_TABLE *efi_systab; +extern int efi_shutdown_in_progress; extern const __attribute__ (( pure )) char * efi_guid_ntoa ( EFI_GUID *guid ); extern const __attribute__ (( pure )) char * diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 93ada21d4..cfaff606e 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -35,6 +35,9 @@ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; /** System table passed to entry point */ EFI_SYSTEM_TABLE *efi_systab; +/** EFI shutdown is in progress */ +int efi_shutdown_in_progress; + /** Event used to signal shutdown */ static EFI_EVENT efi_shutdown_event; @@ -50,6 +53,13 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); */ static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, void *context __unused ) { + + /* Mark shutdown as being in progress, to indicate that large + * parts of the system (e.g. timers) are no longer functional. + */ + efi_shutdown_in_progress = 1; + + /* Shut down iPXE */ shutdown_boot(); } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index da064120a..1fd9971e9 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -70,6 +70,31 @@ static void efi_udelay ( unsigned long usecs ) { */ static unsigned long efi_currticks ( void ) { + /* EFI provides no clean way for device drivers to shut down + * in preparation for handover to a booted operating system. + * The platform firmware simply doesn't bother to call the + * drivers' Stop() methods. Instead, drivers must register an + * EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when + * ExitBootServices() is called, and clean up without any + * reference to the EFI driver model. + * + * Unfortunately, all timers silently stop working when + * ExitBootServices() is called. Even more unfortunately, and + * for no discernible reason, this happens before any + * EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled. The + * net effect of this entertaining design choice is that any + * timeout loops on the shutdown path (e.g. for gracefully + * closing outstanding TCP connections) may wait indefinitely. + * + * There is no way to report failure from currticks(), since + * the API lazily assumes that the host system continues to + * travel through time in the usual direction. Work around + * EFI's violation of this assumption by falling back to a + * simple free-running monotonic counter. + */ + if ( efi_shutdown_in_progress ) + efi_jiffies++; + return efi_jiffies; } From 26050fd4c87c50503d5bd573b2ec91703676e211 Mon Sep 17 00:00:00 2001 From: Raed Salem Date: Thu, 8 Dec 2016 11:01:51 +0200 Subject: [PATCH 325/591] [golan] Update Connect-IB, ConnectX-4 and ConnectX-4 Lx (Infiniband) support Updates: - Nodnic: Support for arm cq doorbell via the UAR BAR - Ensure hardware is quiescent when no interface is open - WinPE WA - Support for clear interrupt via BAR - Nodnic: Support for send TX doorbells via the UAR BAR - Added ConnectX-5EX device - Added ConnectX-5 device Signed-off-by: Raed Salem Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/infiniband/flexboot_nodnic.c | 166 ++++++-- src/drivers/infiniband/flexboot_nodnic.h | 28 +- src/drivers/infiniband/golan.c | 206 ++++++---- src/drivers/infiniband/golan.h | 12 +- .../include/mlx_nodnic_data_structures.h | 32 +- .../infiniband/mlx_nodnic/include/mlx_port.h | 13 + .../infiniband/mlx_nodnic/src/mlx_device.c | 34 +- .../infiniband/mlx_nodnic/src/mlx_port.c | 354 +++++++++++++++++- .../mlx_utils/include/private/mlx_pci_priv.h | 5 + .../mlx_utils/include/public/mlx_logging.h | 1 + .../mlx_utils/include/public/mlx_pci.h | 5 + .../mlx_lib/mlx_link_speed/mlx_link_speed.h | 5 + .../mlx_lib/mlx_nvconfig/mlx_nvconfig.c | 7 + .../mlx_lib/mlx_nvconfig/mlx_nvconfig.h | 16 + .../mlx_nvconfig/mlx_nvconfig_defaults.c | 30 +- .../mlx_nvconfig/mlx_nvconfig_defaults.h | 6 + .../mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h | 94 ++++- .../mlx_utils/src/public/mlx_icmd.c | 2 +- .../infiniband/mlx_utils/src/public/mlx_pci.c | 16 + .../mlx_utils/src/public/mlx_utils.c | 5 +- .../include/mlx_logging_priv.h | 5 +- .../include/mlx_types_priv.h | 2 +- .../mlx_utils_flexboot/src/mlx_pci_priv.c | 13 + 23 files changed, 903 insertions(+), 154 deletions(-) diff --git a/src/drivers/infiniband/flexboot_nodnic.c b/src/drivers/infiniband/flexboot_nodnic.c index dea19ca69..1ee10f54b 100644 --- a/src/drivers/infiniband/flexboot_nodnic.c +++ b/src/drivers/infiniband/flexboot_nodnic.c @@ -22,7 +22,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include #include #include #include @@ -31,10 +30,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include "flexboot_nodnic.h" -#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" -#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" -#include "mlx_utils/include/public/mlx_pci_gw.h" -#include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h" #include "mlx_utils/include/public/mlx_types.h" #include "mlx_utils/include/public/mlx_utils.h" #include "mlx_utils/include/public/mlx_bail.h" @@ -43,6 +38,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "mlx_utils/include/public/mlx_pci.h" #include "mlx_nodnic/include/mlx_device.h" #include "mlx_nodnic/include/mlx_port.h" +#include +#include +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" +#include "mlx_utils/include/public/mlx_pci_gw.h" +#include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h" /*************************************************************************** * @@ -52,10 +53,27 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ static int flexboot_nodnic_arm_cq ( struct flexboot_nodnic_port *port ) { #ifndef DEVICE_CX3 - mlx_uint32 val = ( port->eth_cq->next_idx & 0xffff ); - if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) { - MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); - return MLX_FAILED; + mlx_uint32 val32 = 0; + union arm_cq_uar cq_uar; + +#define ARM_CQ_UAR_CQ_CI_MASK 0xffffff +#define ARM_CQ_UAR_CMDSN_MASK 3 +#define ARM_CQ_UAR_CMDSN_OFFSET 28 +#define ARM_CQ_UAR_CQ_CI_OFFSET 0x20 + if ( port->port_priv.device->device_cap.support_bar_cq_ctrl ) { + cq_uar.dword[0] = cpu_to_be32((port->eth_cq->next_idx & ARM_CQ_UAR_CQ_CI_MASK) | + ((port->cmdsn++ & ARM_CQ_UAR_CMDSN_MASK) << ARM_CQ_UAR_CMDSN_OFFSET)); + cq_uar.dword[1] = cpu_to_be32(port->eth_cq->cqn); + wmb(); + writeq(cq_uar.qword, port->port_priv.device->uar.virt + ARM_CQ_UAR_CQ_CI_OFFSET); + port->port_priv.arm_cq_doorbell_record->dword[0] = cq_uar.dword[1]; + port->port_priv.arm_cq_doorbell_record->dword[1] = cq_uar.dword[0]; + } else { + val32 = ( port->eth_cq->next_idx & 0xffffff ); + if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val32 ) ) { + MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); + return MLX_FAILED; + } } #else mlx_utils *utils = port->port_priv.device->utils; @@ -77,7 +95,7 @@ static int flexboot_nodnic_arm_cq ( struct flexboot_nodnic_port *port ) { data = ( ( ( port->eth_cq->next_idx & 0xffff ) << 16 ) | 0x0080 ); /* Write the new index and update FW that new data was submitted */ mlx_pci_mem_write ( utils, MlxPciWidthUint32, 0, - ( mlx_uint64 ) & ( ptr->armcq_cq_ci_dword ), 1, &data ); + ( mlx_uintn ) & ( ptr->armcq_cq_ci_dword ), 1, &data ); } #endif return 0; @@ -96,6 +114,7 @@ static int flexboot_nodnic_create_cq ( struct ib_device *ibdev , struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq; mlx_status status = MLX_SUCCESS; + mlx_uint32 cqn; flexboot_nodnic_cq = (struct flexboot_nodnic_completion_queue *) zalloc(sizeof(*flexboot_nodnic_cq)); @@ -114,10 +133,18 @@ static int flexboot_nodnic_create_cq ( struct ib_device *ibdev , flexboot_nodnic->callbacks->cqe_set_owner( flexboot_nodnic_cq->nodnic_completion_queue->cq_virt, cq->num_cqes); - + if ( flexboot_nodnic->device_priv.device_cap.support_bar_cq_ctrl ) { + status = nodnic_port_query(&port->port_priv, + nodnic_port_option_cq_n_index, + (mlx_uint32 *)&cqn ); + MLX_FATAL_CHECK_STATUS(status, read_cqn_err, + "failed to query cqn"); + cq->cqn = cqn; + } ib_cq_set_drvdata ( cq, flexboot_nodnic_cq ); return status; +read_cqn_err: create_err: free(flexboot_nodnic_cq); qp_alloc_err: @@ -450,6 +477,9 @@ static int flexboot_nodnic_post_send ( struct ib_device *ibdev, status = port->port_priv.send_doorbell ( &port->port_priv, &send_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); + if ( flexboot_nodnic->callbacks->tx_uar_send_doorbell_fn ) { + flexboot_nodnic->callbacks->tx_uar_send_doorbell_fn ( ibdev, wqbb ); + } if ( status != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring send doorbell failed\n", flexboot_nodnic ); } @@ -1293,12 +1323,14 @@ int flexboot_nodnic_is_supported ( struct pci_device *pci ) { mlx_pci_gw_teardown( &utils ); pci_gw_init_err: + mlx_utils_teardown(&utils); utils_init_err: DBG ( "%s: NODNIC is %s supported (status = %d)\n", __FUNCTION__, ( is_supported ? "": "not" ), status ); return is_supported; } + void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, uint16_t high_byte ) { union mac_addr { @@ -1329,13 +1361,14 @@ static mlx_status flexboot_nodnic_get_factory_mac ( status = mlx_vmac_query_virt_mac ( flexboot_nodnic_priv->device_priv.utils, &virt_mac ); if ( ! status ) { - DBGC ( flexboot_nodnic_priv, "NODNIC %p Failed to set the virtual MAC\n", - flexboot_nodnic_priv ); + DBGC ( flexboot_nodnic_priv, "NODNIC %p Failed to set the virtual MAC\n" + ,flexboot_nodnic_priv ); } return status; } + /** * Set port masking * @@ -1361,6 +1394,79 @@ static int flexboot_nodnic_set_port_masking ( struct flexboot_nodnic *flexboot_n return 0; } +int init_mlx_utils ( mlx_utils **utils, struct pci_device *pci ) { + int rc = 0; + + *utils = ( mlx_utils * ) zalloc ( sizeof ( mlx_utils ) ); + if ( *utils == NULL ) { + DBGC ( utils, "%s: Failed to allocate utils\n", __FUNCTION__ ); + rc = -1; + goto err_utils_alloc; + } + if ( mlx_utils_init ( *utils, pci ) ) { + DBGC ( utils, "%s: mlx_utils_init failed\n", __FUNCTION__ ); + rc = -1; + goto err_utils_init; + } + if ( mlx_pci_gw_init ( *utils ) ){ + DBGC ( utils, "%s: mlx_pci_gw_init failed\n", __FUNCTION__ ); + rc = -1; + goto err_cmd_init; + } + + return 0; + + mlx_pci_gw_teardown ( *utils ); +err_cmd_init: + mlx_utils_teardown ( *utils ); +err_utils_init: + free ( *utils ); +err_utils_alloc: + *utils = NULL; + + return rc; +} + +void free_mlx_utils ( mlx_utils **utils ) { + + mlx_pci_gw_teardown ( *utils ); + mlx_utils_teardown ( *utils ); + free ( *utils ); + *utils = NULL; +} + +/** + * Initialise Nodnic PCI parameters + * + * @v hermon Nodnic device + */ +static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) { + mlx_status status = MLX_SUCCESS; + struct pci_device *pci = flexboot_nodnic->pci; + nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar; + + if ( ! flexboot_nodnic->device_priv.utils ) { + uar->virt = NULL; + DBGC ( flexboot_nodnic, "%s: mlx_utils is not initialized \n", __FUNCTION__ ); + return -EINVAL; + } + + if ( ! flexboot_nodnic->device_priv.device_cap.support_uar_tx_db ) { + DBGC ( flexboot_nodnic, "%s: tx db using uar is not supported \n", __FUNCTION__ ); + return -ENOTSUP; + } + /* read uar offset then allocate */ + if ( ( status = nodnic_port_set_send_uar_offset ( &flexboot_nodnic->port[0].port_priv ) ) ) { + DBGC ( flexboot_nodnic, "%s: nodnic_port_set_send_uar_offset failed," + "status = %d\n", __FUNCTION__, status ); + return -EINVAL; + } + uar->phys = ( pci_bar_start ( pci, FLEXBOOT_NODNIC_HCA_BAR ) + (mlx_uint32)uar->offset ); + uar->virt = ( void * )( ioremap ( uar->phys, FLEXBOOT_NODNIC_PAGE_SIZE ) ); + + return status; +} + int flexboot_nodnic_probe ( struct pci_device *pci, struct flexboot_nodnic_callbacks *callbacks, void *drv_priv __unused ) { @@ -1388,21 +1494,10 @@ int flexboot_nodnic_probe ( struct pci_device *pci, pci_set_drvdata ( pci, flexboot_nodnic_priv ); device_priv = &flexboot_nodnic_priv->device_priv; - device_priv->utils = (mlx_utils *)zalloc( sizeof ( mlx_utils ) ); - if ( device_priv->utils == NULL ) { - DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate utils\n", __FUNCTION__ ); - status = MLX_OUT_OF_RESOURCES; - goto utils_err_alloc; - } - - status = mlx_utils_init( device_priv->utils, pci ); - MLX_FATAL_CHECK_STATUS(status, utils_init_err, - "mlx_utils_init failed"); - - /* nodnic init*/ - status = mlx_pci_gw_init( device_priv->utils ); - MLX_FATAL_CHECK_STATUS(status, cmd_init_err, - "mlx_pci_gw_init failed"); + /* init mlx utils */ + status = init_mlx_utils ( & device_priv->utils, pci ); + MLX_FATAL_CHECK_STATUS(status, err_utils_init, + "init_mlx_utils failed"); /* init device */ status = nodnic_device_init( device_priv ); @@ -1426,6 +1521,11 @@ int flexboot_nodnic_probe ( struct pci_device *pci, MLX_FATAL_CHECK_STATUS(status, err_thin_init_ports, "flexboot_nodnic_thin_init_ports failed"); + if ( ( status = flexboot_nodnic_alloc_uar ( flexboot_nodnic_priv ) ) ) { + DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_pci_init failed" + " ( status = %d )\n",__FUNCTION__, status ); + } + /* device reg */ status = flexboot_nodnic_set_ports_type( flexboot_nodnic_priv ); MLX_CHECK_STATUS( flexboot_nodnic_priv, status, err_set_ports_types, @@ -1456,11 +1556,8 @@ err_set_masking: get_cap_err: nodnic_device_teardown ( device_priv ); device_init_err: - mlx_pci_gw_teardown ( device_priv->utils ); -cmd_init_err: -utils_init_err: - free ( device_priv->utils ); -utils_err_alloc: + free_mlx_utils ( & device_priv->utils ); +err_utils_init: free ( flexboot_nodnic_priv ); device_err_alloc: return status; @@ -1473,7 +1570,6 @@ void flexboot_nodnic_remove ( struct pci_device *pci ) flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); nodnic_device_teardown( device_priv ); - mlx_pci_gw_teardown( device_priv->utils ); - free( device_priv->utils ); + free_mlx_utils ( & device_priv->utils ); free( flexboot_nodnic_priv ); } diff --git a/src/drivers/infiniband/flexboot_nodnic.h b/src/drivers/infiniband/flexboot_nodnic.h index 80272296c..3020f7455 100644 --- a/src/drivers/infiniband/flexboot_nodnic.h +++ b/src/drivers/infiniband/flexboot_nodnic.h @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" /* * If defined, use interrupts in NODNIC driver @@ -37,6 +38,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define FLEXBOOT_NODNIC_PORT_BASE 1 #define FLEXBOOT_NODNIC_OPCODE_SEND 0xa +#define FLEXBOOT_NODNIC_HCA_BAR PCI_BASE_ADDRESS_0 //BAR 0 +#define FLEXBOOT_NODNIC_PAGE_SHIFT 12 +#define FLEXBOOT_NODNIC_PAGE_SIZE (1 << FLEXBOOT_NODNIC_PAGE_SHIFT) +#define FLEXBOOT_NODNIC_PAGE_MASK (FLEXBOOT_NODNIC_PAGE_SIZE - 1) /* Port protocol */ enum flexboot_nodnic_protocol { @@ -60,6 +65,7 @@ struct flexboot_nodnic_port { struct ib_completion_queue *eth_cq; /** Ethernet queue pair */ struct ib_queue_pair *eth_qp; + mlx_uint8 cmdsn; }; @@ -136,6 +142,21 @@ struct cqe_data{ mlx_uint32 byte_cnt; }; +union arm_cq_uar { + struct { + //big endian + mlx_uint32 reserved0 :2; + mlx_uint32 cmdn :2; + mlx_uint32 reserved1 :3; + mlx_uint32 cmd :1; + mlx_uint32 cq_ci :24; + mlx_uint32 reserved2 :8; + mlx_uint32 cq_n :24; + }; + mlx_uint32 dword[2]; + mlx_uint64 qword; +}; + struct flexboot_nodnic_callbacks { mlx_status ( * fill_completion ) ( void *cqe, struct cqe_data *cqe_data ); mlx_status ( * cqe_set_owner ) ( void *cq, unsigned int num_cqes ); @@ -149,6 +170,10 @@ struct flexboot_nodnic_callbacks { unsigned long wqe_idx ); void ( * irq ) ( struct net_device *netdev, int enable ); + mlx_status ( * tx_uar_send_doorbell_fn ) ( + struct ib_device *ibdev, + struct nodnic_send_wqbb *wqbb + ); }; int flexboot_nodnic_probe ( struct pci_device *pci, @@ -159,5 +184,6 @@ void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ); int flexboot_nodnic_is_supported ( struct pci_device *pci ); void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, uint16_t high_byte ); - +int init_mlx_utils ( mlx_utils **utils, struct pci_device *pci ); +void free_mlx_utils ( mlx_utils **utils ); #endif /* SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_H_ */ diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index d410fdfb9..b704a939f 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -21,31 +21,32 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -#include #include #include #include #include #include #include +#include "flexboot_nodnic.h" #include #include +#include #include +#include +#include "mlx_utils/include/public/mlx_pci_gw.h" +#include #include -#include "flexboot_nodnic.h" +#include "mlx_nodnic/include/mlx_port.h" #include "nodnic_shomron_prm.h" #include "golan.h" #include "mlx_utils/include/public/mlx_bail.h" #include "mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h" -#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" -#include "mlx_utils/include/public/mlx_pci_gw.h" -#include "mlx_nodnic/include/mlx_port.h" +#define DEVICE_IS_CIB( device ) ( device == 0x1011 ) /******************************************************************************/ /************* Very simple memory management for umalloced pages **************/ /******* Temporary solution until full memory management is implemented *******/ /******************************************************************************/ -#define GOLAN_PAGES 20 struct golan_page { struct list_head list; userptr_t addr; @@ -61,8 +62,7 @@ static void golan_free_pages ( struct list_head *head ) { } static int golan_init_pages ( struct list_head *head ) { - struct golan_page *new_entry; - int rc, i; + int rc = 0; if ( !head ) { rc = -EINVAL; @@ -70,26 +70,8 @@ static int golan_init_pages ( struct list_head *head ) { } INIT_LIST_HEAD ( head ); + return rc; - for ( i = 0; i < GOLAN_PAGES; i++ ) { - new_entry = zalloc ( sizeof ( *new_entry ) ); - if ( new_entry == NULL ) { - rc = -ENOMEM; - goto err_golan_init_pages_alloc_page; - } - new_entry->addr = umalloc ( GOLAN_PAGE_SIZE ); - if ( new_entry->addr == UNULL ) { - free ( new_entry ); - rc = -ENOMEM; - goto err_golan_init_pages_alloc_page; - } - list_add ( &new_entry->list, head ); - } - - return 0; - -err_golan_init_pages_alloc_page: - golan_free_pages ( head ); err_golan_init_pages_bad_param: return rc; } @@ -98,16 +80,42 @@ static userptr_t golan_get_page ( struct list_head *head ) { struct golan_page *page; userptr_t addr; - if ( list_empty ( head ) ) - return UNULL; - - page = list_first_entry ( head, struct golan_page, list ); - list_del ( &page->list ); - addr = page->addr; - free ( page ); + if ( list_empty ( head ) ) { + addr = umalloc ( GOLAN_PAGE_SIZE ); + if ( addr == UNULL ) { + goto err_golan_iget_page_alloc_page; + } + } else { + page = list_first_entry ( head, struct golan_page, list ); + list_del ( &page->list ); + addr = page->addr; + free ( page ); + } +err_golan_iget_page_alloc_page: return addr; } +static int golan_return_page ( struct list_head *head, + userptr_t addr ) { + struct golan_page *new_entry; + int rc = 0; + + if ( ! head ) { + rc = -EINVAL; + goto err_golan_return_page_bad_param; + } + new_entry = zalloc ( sizeof ( *new_entry ) ); + if ( new_entry == NULL ) { + rc = -ENOMEM; + goto err_golan_return_page_alloc_page; + } + new_entry->addr = addr; + list_add_tail( &new_entry->list, head ); + +err_golan_return_page_alloc_page: +err_golan_return_page_bad_param: + return rc; +} /******************************************************************************/ const char *golan_qp_state_as_string[] = { @@ -450,8 +458,8 @@ err_query_hca_cap: static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 func_id ) { uint32_t out_num_entries = 0; - int size_ibox = sizeof(struct golan_manage_pages_inbox); - int size_obox = sizeof(struct golan_manage_pages_outbox); + int size_ibox = 0; + int size_obox = 0; int rc = 0; DBGC(golan, "%s\n", __FUNCTION__); @@ -463,8 +471,8 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 struct golan_manage_pages_inbox *in; struct golan_manage_pages_outbox_data *out; - size_ibox += (pas_num * GOLAN_PAS_SIZE); - size_obox += (pas_num * GOLAN_PAS_SIZE); + size_ibox = sizeof(struct golan_manage_pages_inbox) + (pas_num * GOLAN_PAS_SIZE); + size_obox = sizeof(struct golan_manage_pages_outbox) + (pas_num * GOLAN_PAS_SIZE); cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_TAKE, MEM_MBOX, MEM_MBOX, @@ -480,7 +488,7 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 out = (struct golan_manage_pages_outbox_data *)GET_OUTBOX(golan, MEM_MBOX); out_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries); for (i = 0; i < out_num_entries; ++i) { - ufree(BE64_BUS_2_USR(out->pas[i])); + golan_return_page ( &golan->pages, ( BE64_BUS_2_USR( out->pas[i] ) ) ); } } else { if ( rc == -EBUSY ) { @@ -503,8 +511,8 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __be16 func_id ) { struct mbox *mailbox; - int size_ibox = sizeof(struct golan_manage_pages_inbox); - int size_obox = sizeof(struct golan_manage_pages_outbox); + int size_ibox = 0; + int size_obox = 0; int rc = 0; DBGC(golan, "%s\n", __FUNCTION__); @@ -517,8 +525,8 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __ userptr_t addr = 0; mailbox = GET_INBOX(golan, MEM_MBOX); - size_ibox += (pas_num * GOLAN_PAS_SIZE); - size_obox += (pas_num * GOLAN_PAS_SIZE); + size_ibox = sizeof(struct golan_manage_pages_inbox) + (pas_num * GOLAN_PAS_SIZE); + size_obox = sizeof(struct golan_manage_pages_outbox) + (pas_num * GOLAN_PAS_SIZE); cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_GIVE, MEM_MBOX, MEM_MBOX, @@ -531,7 +539,7 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __ in->num_entries = cpu_to_be32(pas_num); for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j ) { - if (!(addr = umalloc(GOLAN_PAGE_SIZE))) { + if ( ! ( addr = golan_get_page ( & golan->pages ) ) ) { rc = -ENOMEM; DBGC (golan ,"Couldnt allocated page \n"); goto malloc_dma_failed; @@ -555,7 +563,7 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __ get_cmd( golan , MEM_CMD_IDX )->status_own, be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num); } - ufree ( addr ); + golan_return_page ( &golan->pages ,addr ); goto err_send_command; } } @@ -834,7 +842,7 @@ static int golan_create_eq(struct golan *golan) return 0; err_create_eq_cmd: - ufree(virt_to_user(golan->eq.eqes)); + golan_return_page ( & golan->pages, virt_to_user ( eq->eqes ) ); err_create_eq_eqe_alloc: DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); return rc; @@ -859,7 +867,7 @@ static void golan_destory_eq(struct golan *golan) rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); GOLAN_PRINT_RC_AND_CMD_STATUS; - ufree(virt_to_user(golan->eq.eqes)); + golan_return_page ( &golan->pages, virt_to_user ( golan->eq.eqes ) ); golan->eq.eqn = 0; DBGC( golan, "%s Event queue (0x%x) was destroyed\n", __FUNCTION__, eqn); @@ -1063,7 +1071,7 @@ static int golan_create_cq(struct ib_device *ibdev, return 0; err_create_cq_cmd: - ufree(virt_to_user(golan_cq->cqes)); + golan_return_page ( & golan->pages, virt_to_user ( golan_cq->cqes ) ); err_create_cq_cqe_alloc: free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); err_create_cq_db_alloc: @@ -1100,7 +1108,7 @@ static void golan_destroy_cq(struct ib_device *ibdev, cq->cqn = 0; ib_cq_set_drvdata(cq, NULL); - ufree(virt_to_user(golan_cq->cqes)); + golan_return_page ( & golan->pages, virt_to_user ( golan_cq->cqes ) ); free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); free(golan_cq); @@ -1272,7 +1280,7 @@ static int golan_create_qp_aux(struct ib_device *ibdev, err_create_qp_cmd: free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); err_create_qp_db_alloc: - ufree((userptr_t)golan_qp->wqes); + golan_return_page ( & golan->pages, ( userptr_t ) golan_qp->wqes ); err_create_qp_wqe_alloc: err_create_qp_sq_size: err_create_qp_sq_wqe_size: @@ -1326,7 +1334,7 @@ static int golan_modify_qp_rst_to_init(struct ib_device *ibdev, in->ctx.pri_path.port = ibdev->port; in->ctx.flags |= cpu_to_be32(GOLAN_QP_PM_MIGRATED << GOLAN_QP_CTX_PM_STATE_BIT); - in->ctx.pri_path.pkey_index = 0; /* default index */ + in->ctx.pri_path.pkey_index = 0; /* QK is 0 */ /* QP cntr set 0 */ return rc; @@ -1480,7 +1488,7 @@ static void golan_destroy_qp(struct ib_device *ibdev, ib_qp_set_drvdata(qp, NULL); free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); - ufree((userptr_t)golan_qp->wqes); + golan_return_page ( & golan->pages, ( userptr_t ) golan_qp->wqes ); free(golan_qp); DBGC( golan ,"%s QP 0x%lx was destroyed\n", __FUNCTION__, qpn); @@ -1694,8 +1702,8 @@ err_query_vport_gid_cmd: static int golan_query_vport_pkey ( struct ib_device *ibdev ) { struct golan *golan = ib_get_drvdata ( ibdev ); struct golan_cmd_layout *cmd; - struct golan_query_hca_vport_pkey_inbox *in; //struct golan_query_hca_vport_pkey_data *pkey_table; + struct golan_query_hca_vport_pkey_inbox *in; int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size)); int rc; @@ -2244,26 +2252,24 @@ static inline void golan_bring_down(struct golan *golan) } static int golan_set_link_speed ( struct golan *golan ){ - mlx_utils utils; mlx_status status; int i = 0; + int utils_inited = 0; - memset ( &utils, 0, sizeof ( utils ) ); - - status = mlx_utils_init ( &utils, golan->pci ); - MLX_CHECK_STATUS ( golan->pci, status, utils_init_err, "mlx_utils_init failed" ); - - status = mlx_pci_gw_init ( &utils ); - MLX_CHECK_STATUS ( golan->pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" ); + if ( ! golan->utils ) { + utils_inited = 1; + status = init_mlx_utils ( & golan->utils, golan->pci ); + MLX_CHECK_STATUS ( golan->pci, status, utils_init_err, "mlx_utils_init failed" ); + } for ( i = 0; i < golan->caps.num_ports; ++i ) { - status = mlx_set_link_speed( &utils, i + 1, LINK_SPEED_IB, LINK_SPEED_SDR ); + status = mlx_set_link_speed ( golan->utils, i + 1, LINK_SPEED_IB, LINK_SPEED_SDR ); MLX_CHECK_STATUS ( golan->pci, status, set_link_speed_err, "mlx_set_link_speed failed" ); } set_link_speed_err: - mlx_pci_gw_teardown( &utils ); -pci_gw_init_err: +if ( utils_inited ) + free_mlx_utils ( & golan->utils ); utils_init_err: return status; } @@ -2344,7 +2350,16 @@ out: * * @v ibdev Infiniband device */ -static void golan_ib_close ( struct ib_device *ibdev __unused ) {} +static void golan_ib_close ( struct ib_device *ibdev ) { + struct golan *golan = NULL; + + DBG ( "%s start\n", __FUNCTION__ ); + if ( ! ibdev ) + return; + golan = ib_get_drvdata ( ibdev ); + golan_bring_down ( golan ); + DBG ( "%s end\n", __FUNCTION__ ); +} /** * Initialise Infiniband link @@ -2353,11 +2368,13 @@ static void golan_ib_close ( struct ib_device *ibdev __unused ) {} * @ret rc Return status code */ static int golan_ib_open ( struct ib_device *ibdev ) { + struct golan *golan = NULL; DBG ( "%s start\n", __FUNCTION__ ); if ( ! ibdev ) return -EINVAL; - + golan = ib_get_drvdata ( ibdev ); + golan_bring_up ( golan ); golan_ib_update ( ibdev ); DBG ( "%s end\n", __FUNCTION__ ); @@ -2417,6 +2434,12 @@ static int golan_probe_normal ( struct pci_device *pci ) { goto err_golan_bringup; } + if ( ! DEVICE_IS_CIB ( pci->device ) ) { + if ( init_mlx_utils ( & golan->utils, pci ) ) { + rc = -1; + goto err_utils_init; + } + } /* Allocate Infiniband devices */ for (i = 0; i < golan->caps.num_ports; ++i) { ibdev = alloc_ibdev( 0 ); @@ -2435,10 +2458,13 @@ static int golan_probe_normal ( struct pci_device *pci ) { /* Register devices */ for ( i = 0; i < golan->caps.num_ports; ++i ) { port = &golan->ports[i]; - if ((rc = golan_register_ibdev ( port ) ) != 0 ) + if ((rc = golan_register_ibdev ( port ) ) != 0 ) { goto err_golan_probe_register_ibdev; + } } + golan_bring_down ( golan ); + return 0; i = golan->caps.num_ports; @@ -2450,7 +2476,10 @@ err_golan_probe_register_ibdev: err_golan_probe_alloc_ibdev: for ( i-- ; ( signed int ) i >= 0 ; i-- ) ibdev_put ( golan->ports[i].ibdev ); - + if ( ! DEVICE_IS_CIB ( pci->device ) ) { + free_mlx_utils ( & golan->utils ); + } +err_utils_init: golan_bring_down ( golan ); err_golan_bringup: err_fw_ver_cmdif: @@ -2476,13 +2505,13 @@ static void golan_remove_normal ( struct pci_device *pci ) { } for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { netdev_nullify ( golan->ports[i].netdev ); - netdev_put ( golan->ports[i].netdev ); } for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { ibdev_put ( golan->ports[i].ibdev ); } - - golan_bring_down(golan); + if ( ! DEVICE_IS_CIB ( pci->device ) ) { + free_mlx_utils ( & golan->utils ); + } iounmap( golan->iseg ); golan_free_pages( &golan->pages ); free(golan); @@ -2491,6 +2520,26 @@ static void golan_remove_normal ( struct pci_device *pci ) { /*************************************************************************** * NODNIC operations **************************************************************************/ +static mlx_status shomron_tx_uar_send_db ( struct ib_device *ibdev, + struct nodnic_send_wqbb *wqbb ) { + mlx_status status = MLX_SUCCESS; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct shomron_nodnic_eth_send_wqe *eth_wqe = + ( struct shomron_nodnic_eth_send_wqe * )wqbb; + struct shomronprm_wqe_segment_ctrl_send *ctrl; + + if ( ! ibdev || ! eth_wqe || ! flexboot_nodnic->device_priv.uar.virt ) { + DBG("%s: Invalid parameters\n",__FUNCTION__); + status = MLX_FAILED; + goto err; + } + wmb(); + ctrl = & eth_wqe->ctrl; + writeq(*((__be64 *)ctrl), flexboot_nodnic->device_priv.uar.virt + 0x800); +err: + return status; +} + static mlx_status shomron_fill_eth_send_wqe ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_address_vector *av __unused, struct io_buffer *iobuf, struct nodnic_send_wqbb *wqbb, @@ -2599,12 +2648,13 @@ struct flexboot_nodnic_callbacks shomron_nodnic_callbacks = { .fill_completion = shomron_fill_completion, .cqe_set_owner = shomron_cqe_set_owner, .irq = flexboot_nodnic_eth_irq, + .tx_uar_send_doorbell_fn = shomron_tx_uar_send_db, }; static int shomron_nodnic_supported = 0; static int shomron_nodnic_is_supported ( struct pci_device *pci ) { - if ( pci->device == 0x1011 ) + if ( DEVICE_IS_CIB ( pci->device ) ) return 0; return flexboot_nodnic_is_supported ( pci ); @@ -2624,15 +2674,9 @@ static int golan_probe ( struct pci_device *pci ) { shomron_nodnic_supported = shomron_nodnic_is_supported ( pci ); if ( shomron_nodnic_supported ) { + DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ ); rc = flexboot_nodnic_probe ( pci, &shomron_nodnic_callbacks, NULL ); - if ( rc == 0 ) { - DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ ); - goto probe_done; - } - shomron_nodnic_supported = 0; - } - - if ( ! shomron_nodnic_supported ) { + } else { DBG ( "%s: Using normal driver\n", __FUNCTION__ ); rc = golan_probe_normal ( pci ); } @@ -2662,6 +2706,8 @@ static struct pci_device_id golan_nics[] = { PCI_ROM ( 0x15b3, 0x1011, "ConnectIB", "ConnectIB HCA driver: DevID 4113", 0 ), PCI_ROM ( 0x15b3, 0x1013, "ConnectX-4", "ConnectX-4 HCA driver, DevID 4115", 0 ), PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ), + PCI_ROM ( 0x15b3, 0x1017, "ConnectX-5", "ConnectX-5 HCA driver, DevID 4119", 0 ), + PCI_ROM ( 0x15b3, 0x1019, "ConnectX-5EX", "ConnectX-5EX HCA driver, DevID 4121", 0 ), }; struct pci_driver golan_driver __pci_driver = { diff --git a/src/drivers/infiniband/golan.h b/src/drivers/infiniband/golan.h index a6cb4e744..c5227dd73 100755 --- a/src/drivers/infiniband/golan.h +++ b/src/drivers/infiniband/golan.h @@ -22,14 +22,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #include "CIB_PRM.h" +#include "mlx_utils/include/public/mlx_utils.h" #define GOLAN_PCI_CONFIG_BAR_SIZE 0x100000//HERMON_PCI_CONFIG_BAR_SIZE //TODO: What is the BAR size? @@ -319,6 +320,7 @@ struct golan { uint32_t pdn; u32 mkey; u32 flags; + mlx_utils *utils; struct golan_port ports[GOLAN_MAX_PORTS]; }; diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h index f58213b98..61f2c5736 100644 --- a/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h @@ -36,6 +36,8 @@ typedef struct _nodnic_device_capabilites nodnic_device_capabilites; typedef struct _nodnic_qp nodnic_qp; typedef struct _nodnic_cq nodnic_cq; typedef struct _nodnic_eq nodnic_eq; +typedef struct _nodnic_qp_db nodnic_qp_db; +typedef struct _nodnic_arm_cq_db nodnic_arm_cq_db; /* NODNIC Port states * Bit 0 - port open/close @@ -73,6 +75,12 @@ typedef enum { struct nodnic_send_wqbb { mlx_uint8 force_align[NODNIC_WQBB_SIZE]; }; + +struct nodnic_doorbell { + mlx_physical_address doorbell_physical; + mlx_void *map; + nodnic_qp_db *qp_doorbell_record; +}; struct nodnic_ring { mlx_uint32 offset; /** Work queue entries */ @@ -91,7 +99,8 @@ struct nodnic_ring { mlx_uint32 num_wqes; mlx_uint32 qpn; mlx_uint32 next_idx; - mlx_uint32 ring_pi; + struct nodnic_doorbell recv_doorbell; + struct nodnic_doorbell send_doorbell; }; struct nodnic_send_ring{ @@ -117,6 +126,7 @@ struct _nodnic_cq{ mlx_void *map; /** cq */ mlx_size cq_size; + struct nodnic_doorbell arm_cq_doorbell; }; struct _nodnic_eq{ @@ -136,6 +146,10 @@ struct _nodnic_device_capabilites{ #ifdef DEVICE_CX3 mlx_uint8 crspace_doorbells; #endif + mlx_uint8 support_rx_pi_dma; + mlx_uint8 support_uar_tx_db; + mlx_uint8 support_bar_cq_ctrl; + mlx_uint8 log_uar_page_size; }; #ifdef DEVICE_CX3 @@ -151,6 +165,13 @@ struct _nodnic_port_data_flow_gw { } __attribute__ ((packed)); #endif +typedef struct _nodnic_uar_priv{ + mlx_uint8 inited; + mlx_uint64 offset; + void *virt; + unsigned long phys; +} nodnic_uar; + struct _nodnic_device_priv{ mlx_boolean is_initiailzied; mlx_utils *utils; @@ -169,6 +190,7 @@ struct _nodnic_device_priv{ #ifdef DEVICE_CX3 mlx_void *crspace_clear_int; #endif + nodnic_uar uar; }; struct _nodnic_port_priv{ @@ -181,6 +203,7 @@ struct _nodnic_port_priv{ mlx_uint8 port_num; nodnic_eq eq; mlx_mac_address mac_filters[5]; + nodnic_arm_cq_db *arm_cq_doorbell_record; mlx_status (*send_doorbell)( IN nodnic_port_priv *port_priv, IN struct nodnic_ring *ring, @@ -197,5 +220,12 @@ struct _nodnic_port_priv{ #endif }; +struct _nodnic_qp_db { + mlx_uint32 recv_db; + mlx_uint32 send_db; +} __attribute ( ( packed ) ); +struct _nodnic_arm_cq_db { + mlx_uint32 dword[2]; +} __attribute ( ( packed ) ); #endif /* STUB_NODNIC_NODNICDATASTRUCTURES_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h index 4fd96a6da..bb3026729 100644 --- a/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h @@ -47,6 +47,9 @@ typedef enum { #ifdef DEVICE_CX3 nodnic_port_option_crspace_en, #endif + nodnic_port_option_send_ring0_uar_index, + nodnic_port_option_send_ring1_uar_index, + nodnic_port_option_cq_n_index, }nodnic_port_option; struct nodnic_port_data_entry{ @@ -226,4 +229,14 @@ nodnic_port_read_port_management_change_event( IN nodnic_port_priv *port_priv, OUT mlx_boolean *change_event ); +mlx_status +nodnic_port_set_send_uar_offset( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_update_tx_db_func( + IN nodnic_device_priv *device_priv, + IN nodnic_port_priv *port_priv + ); #endif /* STUB_NODNIC_PORT_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c index 4acc94fa6..f6fdacdbf 100644 --- a/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c @@ -169,11 +169,17 @@ nodnic_device_clear_int ( mlx_status status = MLX_SUCCESS; mlx_uint32 disable = 1; #ifndef DEVICE_CX3 - status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable); +#define NODNIC_CLEAR_INT_BAR_OFFSET 0x100C + if ( device_priv->device_cap.support_bar_cq_ctrl ) { + status = mlx_pci_mem_write ( device_priv->utils, MlxPciWidthUint32, 0, + ( mlx_uint64 ) ( NODNIC_CLEAR_INT_BAR_OFFSET ), 1, &disable ); + } else { + status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable); + } MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit"); #else mlx_utils *utils = device_priv->utils; - mlx_uint64 clear_int = (mlx_uint64)(device_priv->crspace_clear_int); + mlx_uint64 clear_int = (mlx_uintn)(device_priv->crspace_clear_int); mlx_uint32 swapped = 0; if (device_priv->device_cap.crspace_doorbells == 0) { @@ -303,6 +309,30 @@ nodnic_device_get_cap( status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x14, (mlx_uint32*)&guid_l); MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_l"); device_priv->device_guid = guid_l | (guid_h << 32); + +#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET 31 +#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_MASK 0x1 +#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET 29 +#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_MASK 0x1 +#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET 27 +#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_MASK 0x1 + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x1c, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic support_rx_pi_dma"); + if ( sizeof ( mlx_uintn ) == sizeof ( mlx_uint32 ) ) { + device_cap->support_rx_pi_dma = FALSE; + device_cap->support_uar_tx_db = FALSE; + device_cap->support_bar_cq_ctrl = FALSE; + } else { + device_cap->support_rx_pi_dma = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET); + device_cap->support_uar_tx_db = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET); + device_cap->support_bar_cq_ctrl = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET); + } + +#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET 0 +#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK 0xFF + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x20, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic log_uar_page_size"); + device_cap->log_uar_page_size = ( buffer >> NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK; read_err: parm_err: return status; diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c index a7afdab65..efbd8ddf7 100644 --- a/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c @@ -55,11 +55,18 @@ struct nodnic_port_data_entry nodnic_port_data_table[] = { PortDataEntry(nodnic_port_option_cq_addr_high, 0x68, 0, 0xFFFFFFFF), PortDataEntry(nodnic_port_option_port_management_change_event, 0x0, 30, 0x1), PortDataEntry(nodnic_port_option_port_promisc_en, 0x4, 29, 0x1), +#ifndef DEVICE_CX3 + PortDataEntry(nodnic_port_option_arm_cq, 0x78, 8, 0xffffff), +#else PortDataEntry(nodnic_port_option_arm_cq, 0x78, 8, 0xffff), +#endif PortDataEntry(nodnic_port_option_port_promisc_multicast_en, 0x4, 28, 0x1), #ifdef DEVICE_CX3 PortDataEntry(nodnic_port_option_crspace_en, 0x4, 27, 0x1), #endif + PortDataEntry(nodnic_port_option_send_ring0_uar_index, 0x108, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_send_ring1_uar_index, 0x10c, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_cq_n_index, 0x118, 0, 0xFFFFFF), }; #define MAX_QP_DATA_ENTRIES 5 @@ -186,6 +193,30 @@ invalid_parm: return status; } +mlx_status +nodnic_port_set_send_uar_offset( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + + if ( ! port_priv->device->device_cap.support_uar_tx_db ) { + MLX_DEBUG_INFO1 ( port_priv, "nodnic_port_set_send_uar_offset: tx db using uar is not supported \n"); + status = MLX_UNSUPPORTED; + goto uar_not_supported; + } + + status = nodnic_port_query(port_priv, + nodnic_port_option_send_ring0_uar_index, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + port_priv->device->uar.offset = out << port_priv->device->device_cap.log_uar_page_size; +uar_not_supported: +query_err: + return status; +} + mlx_status nodnic_port_read_reset_needed( IN nodnic_port_priv *port_priv, @@ -220,6 +251,111 @@ query_err: return status; } +static +mlx_status +nodnic_port_allocate_dbr_dma ( + IN nodnic_port_priv *port_priv, + IN struct nodnic_doorbell *nodnic_db, + IN mlx_uint32 dbr_addr_low_ofst, + IN mlx_uint32 dbr_addr_high_ofst, + IN void **dbr_addr, + IN mlx_size size, + IN void **map + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint64 address = 0; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || nodnic_db == NULL ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + status = mlx_memory_alloc_dma(device_priv->utils, + size, + NODNIC_MEMORY_ALIGN, + (void **)dbr_addr + ); + MLX_FATAL_CHECK_STATUS(status, alloc_db_record_err, + "doorbell record dma allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + (void *)(*dbr_addr), + size, + &nodnic_db->doorbell_physical, + map//nodnic_ring->map + ); + MLX_FATAL_CHECK_STATUS(status, map_db_record_err, + "doorbell record map dma error"); + + address = (mlx_uint64)nodnic_db->doorbell_physical; + status = nodnic_cmd_write(device_priv, + dbr_addr_low_ofst, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set doorbell addr low"); + + address = address >> 32; + status = nodnic_cmd_write(device_priv, + dbr_addr_high_ofst, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set doorbell addr high"); + + return status; + +set_err: + mlx_memory_ummap_dma(device_priv->utils, *map); +map_db_record_err: + mlx_memory_free_dma(device_priv->utils, size, + (void **)dbr_addr); +alloc_db_record_err: +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_cq_dbr_dma_init( + IN nodnic_port_priv *port_priv, + OUT nodnic_cq **cq + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + if ( ! device_priv->device_cap.support_bar_cq_ctrl ) { + status = MLX_UNSUPPORTED; + goto uar_arm_cq_db_unsupported; + } + +#define NODNIC_PORT_ARM_CQ_DBR_ADDR_LOW_OFFSET 0x114 +#define NODNIC_PORT_ARM_CQ_DBR_ADDR_HIGH_OFFSET 0x110 + + status = nodnic_port_allocate_dbr_dma ( port_priv,&(*cq)->arm_cq_doorbell, + port_priv->port_offset + NODNIC_PORT_ARM_CQ_DBR_ADDR_LOW_OFFSET, + port_priv->port_offset + NODNIC_PORT_ARM_CQ_DBR_ADDR_HIGH_OFFSET, + (void **)&port_priv->arm_cq_doorbell_record , + sizeof(nodnic_arm_cq_db), + (void **)&((*cq)->arm_cq_doorbell.map)); + MLX_FATAL_CHECK_STATUS(status, alloc_dbr_dma_err, + "failed to allocate doorbell record dma"); + return status; + +alloc_dbr_dma_err: +uar_arm_cq_db_unsupported: +invalid_parm: + return status; +} + mlx_status nodnic_port_create_cq( IN nodnic_port_priv *port_priv, @@ -257,17 +393,24 @@ nodnic_port_create_cq( MLX_FATAL_CHECK_STATUS(status, cq_map_err, "cq map error"); + status = nodnic_port_cq_dbr_dma_init(port_priv,cq); + /* update cq address */ #define NODIC_CQ_ADDR_HIGH 0x68 #define NODIC_CQ_ADDR_LOW 0x6c address = (mlx_uint64)(*cq)->cq_physical; - nodnic_port_set(port_priv, nodnic_port_option_cq_addr_low, - (mlx_uint32)(address >> 12)); + status = nodnic_port_set(port_priv, nodnic_port_option_cq_addr_low, + (mlx_uint32)(address) >> 12); + MLX_FATAL_CHECK_STATUS(status, dma_set_addr_low_err, + "cq set addr low error"); address = address >> 32; - nodnic_port_set(port_priv, nodnic_port_option_cq_addr_high, + status = nodnic_port_set(port_priv, nodnic_port_option_cq_addr_high, (mlx_uint32)address); - + MLX_FATAL_CHECK_STATUS(status, dma_set_addr_high_err, + "cq set addr high error"); return status; +dma_set_addr_high_err: +dma_set_addr_low_err: mlx_memory_ummap_dma(device_priv->utils, (*cq)->map); cq_map_err: mlx_memory_free_dma(device_priv->utils, (*cq)->cq_size, @@ -294,6 +437,21 @@ nodnic_port_destroy_cq( } device_priv = port_priv->device; + if ( device_priv->device_cap.support_bar_cq_ctrl ){ + status = mlx_memory_ummap_dma(device_priv->utils, + cq->arm_cq_doorbell.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + sizeof(nodnic_arm_cq_db), + (void **)&(port_priv->arm_cq_doorbell_record)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + } + mlx_memory_ummap_dma(device_priv->utils, cq->map); mlx_memory_free_dma(device_priv->utils, cq->cq_size, @@ -303,6 +461,126 @@ nodnic_port_destroy_cq( invalid_parm: return status; } + +static +mlx_status +nodnic_port_allocate_ring_db_dma ( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *nodnic_ring, + IN struct nodnic_doorbell *nodnic_db + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL || nodnic_ring == NULL || nodnic_db == NULL ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } +#define NODNIC_RING_DBR_ADDR_LOW_OFFSET 0x1C +#define NODNIC_RING_DBR_ADDR_HIGH_OFFSET 0x18 + status = nodnic_port_allocate_dbr_dma ( port_priv,nodnic_db, + nodnic_ring->offset + NODNIC_RING_DBR_ADDR_LOW_OFFSET, + nodnic_ring->offset + NODNIC_RING_DBR_ADDR_HIGH_OFFSET, + (void **)&nodnic_db->qp_doorbell_record, + sizeof(nodnic_qp_db), + (void **)&nodnic_ring->map ); + MLX_FATAL_CHECK_STATUS(status, alloc_dbr_dma_err, + "failed to allocate doorbell record dma"); + + return status; +alloc_dbr_dma_err: +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_rx_pi_dma_alloc( + IN nodnic_port_priv *port_priv, + OUT nodnic_qp **qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || qp == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + + if ( ! device_priv->device_cap.support_rx_pi_dma ) { + goto rx_pi_dma_unsupported; + } + + if ( device_priv->device_cap.support_rx_pi_dma ) { + status = nodnic_port_allocate_ring_db_dma(port_priv, + &(*qp)->receive.nodnic_ring,&(*qp)->receive.nodnic_ring.recv_doorbell); + MLX_FATAL_CHECK_STATUS(status, dma_alloc_err, + "rx doorbell dma allocation error"); + } + + return status; + +dma_alloc_err: +rx_pi_dma_unsupported: +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_send_db_dma( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ) +{ + mlx_uint32 swapped = 0; + mlx_uint32 index32 = index; + mlx_memory_cpu_to_be32(port_priv->device->utils, index32, &swapped); + ring->send_doorbell.qp_doorbell_record->send_db = swapped; + + return MLX_SUCCESS; +} + +static +mlx_status +nodnic_port_tx_dbr_dma_init( + IN nodnic_port_priv *port_priv, + OUT nodnic_qp **qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || qp == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + + if ( ! device_priv->device_cap.support_uar_tx_db || ! device_priv->uar.offset ) { + status = MLX_UNSUPPORTED; + goto uar_tx_db_unsupported; + } + status = nodnic_port_allocate_ring_db_dma(port_priv, + &(*qp)->send.nodnic_ring,&(*qp)->send.nodnic_ring.send_doorbell); + MLX_FATAL_CHECK_STATUS(status, dma_alloc_err, + "tx doorbell dma allocation error"); + port_priv->send_doorbell = nodnic_port_send_db_dma; + + return status; + +dma_alloc_err: +uar_tx_db_unsupported: +invalid_parm: + + return status; +} + mlx_status nodnic_port_create_qp( IN nodnic_port_priv *port_priv, @@ -376,6 +654,13 @@ nodnic_port_create_qp( MLX_FATAL_CHECK_STATUS(status, receive_map_err, "receive wq map error"); + status = nodnic_port_rx_pi_dma_alloc(port_priv,qp); + MLX_FATAL_CHECK_STATUS(status, rx_pi_dma_alloc_err, + "receive db dma error"); + + status = nodnic_port_tx_dbr_dma_init(port_priv,qp); + + (*qp)->send.nodnic_ring.wq_size = send_wq_size; (*qp)->send.nodnic_ring.num_wqes = send_wqe_num; (*qp)->receive.nodnic_ring.wq_size = receive_wq_size; @@ -420,6 +705,7 @@ nodnic_port_create_qp( write_recv_addr_err: write_send_addr_err: mlx_memory_ummap_dma(device_priv->utils, (*qp)->receive.nodnic_ring.map); +rx_pi_dma_alloc_err: receive_map_err: mlx_memory_ummap_dma(device_priv->utils, (*qp)->send.nodnic_ring.map); send_map_err: @@ -457,6 +743,36 @@ nodnic_port_destroy_qp( MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); } + if ( device_priv->device_cap.support_rx_pi_dma ){ + status = mlx_memory_ummap_dma(device_priv->utils, + qp->receive.nodnic_ring.recv_doorbell.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + sizeof(nodnic_qp_db), + (void **)&(qp->receive.nodnic_ring.recv_doorbell.qp_doorbell_record)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + } + + if ( device_priv->device_cap.support_uar_tx_db || ! device_priv->uar.offset){ + status = mlx_memory_ummap_dma(device_priv->utils, + qp->send.nodnic_ring.send_doorbell.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + sizeof(nodnic_qp_db), + (void **)&(qp->send.nodnic_ring.send_doorbell.qp_doorbell_record)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + } + status = mlx_memory_free_dma(device_priv->utils, qp->receive.nodnic_ring.wq_size, (void **)&(qp->receive.wqe_virt)); @@ -520,7 +836,7 @@ nodnic_port_send_db_connectx3( nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; mlx_uint32 index32 = index; mlx_pci_mem_write(port_priv->device->utils, MlxPciWidthUint32, 0, - (mlx_uint64)&(ptr->send_doorbell), 1, &index32); + (mlx_uintn)&(ptr->send_doorbell), 1, &index32); return MLX_SUCCESS; } @@ -535,10 +851,24 @@ nodnic_port_recv_db_connectx3( nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; mlx_uint32 index32 = index; mlx_pci_mem_write(port_priv->device->utils, MlxPciWidthUint32, 0, - (mlx_uint64)&(ptr->recv_doorbell), 1, &index32); + (mlx_uintn)&(ptr->recv_doorbell), 1, &index32); return MLX_SUCCESS; } #endif +static +mlx_status +nodnic_port_recv_db_dma( + IN nodnic_port_priv *port_priv __attribute__((unused)), + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ) +{ + mlx_uint32 swapped = 0; + mlx_uint32 index32 = index; + mlx_memory_cpu_to_be32(port_priv->device->utils, index32, &swapped); + ring->recv_doorbell.qp_doorbell_record->recv_db = swapped; + return MLX_SUCCESS; +} mlx_status nodnic_port_update_ring_doorbell( @@ -678,11 +1008,10 @@ nodnic_port_add_mac_filter( goto bad_param; } - memset(&zero_mac, 0, sizeof(zero_mac)); - device = port_priv->device; utils = device->utils; + mlx_memory_set(utils, &zero_mac, 0, sizeof(zero_mac)); /* check if mac already exists */ for( ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { mlx_memory_cmp(utils, &port_priv->mac_filters[index], &mac, @@ -759,11 +1088,10 @@ nodnic_port_remove_mac_filter( goto bad_param; } - memset(&zero_mac, 0, sizeof(zero_mac)); - device = port_priv->device; utils = device->utils; + mlx_memory_set(utils, &zero_mac, 0, sizeof(zero_mac)); /* serch for mac filter */ for( ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { mlx_memory_cmp(utils, &port_priv->mac_filters[index], &mac, @@ -832,7 +1160,7 @@ nodnic_port_set_dma_connectx3( nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; mlx_uint32 data = (value ? 0xffffffff : 0x0); mlx_pci_mem_write(utils, MlxPciWidthUint32, 0, - (mlx_uint64)&(ptr->dma_en), 1, &data); + (mlx_uintn)&(ptr->dma_en), 1, &data); return MLX_SUCCESS; } #endif @@ -1029,6 +1357,10 @@ nodnic_port_thin_init( port_priv->set_dma = nodnic_port_set_dma_connectx3; } #endif + if ( device_priv->device_cap.support_rx_pi_dma ) { + port_priv->recv_doorbell = nodnic_port_recv_db_dma; + } + /* clear reset_needed */ nodnic_port_read_reset_needed(port_priv, &reset_needed); diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h index 89cad75eb..cf35e5b73 100644 --- a/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h @@ -30,6 +30,11 @@ mlx_pci_init_priv( IN mlx_utils *utils ); +mlx_status +mlx_pci_teardown_priv( + IN mlx_utils *utils + ); + mlx_status mlx_pci_read_priv( IN mlx_utils *utils, diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h index 7b7b852d1..7ff06bbf5 100644 --- a/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "../../../mlx_utils_flexboot/include/mlx_logging_priv.h" +#define MLX_PRINT(...) MLX_PRINT_PRIVATE(__VA_ARGS__) #define MLX_DEBUG_FATAL_ERROR(...) MLX_DEBUG_FATAL_ERROR_PRIVATE(__VA_ARGS__) #define MLX_DEBUG_ERROR(...) MLX_DEBUG_ERROR_PRIVATE(__VA_ARGS__) #define MLX_DEBUG_WARN(...) MLX_DEBUG_WARN_PRIVATE(__VA_ARGS__) diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h index 416bdb66b..60eb55d52 100644 --- a/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h @@ -36,6 +36,11 @@ mlx_pci_init( IN mlx_utils *utils ); +mlx_status +mlx_pci_teardown( + IN mlx_utils *utils + ); + mlx_status mlx_pci_read( IN mlx_utils *utils, diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h index 15b28f57a..cb167d6ae 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h @@ -124,6 +124,11 @@ struct mlx_link_speed { /* -------------- */ mlx_uint32 ib_proto_oper :16; mlx_uint32 ib_link_width_oper :16; + /* -------------- */ + mlx_uint32 reserved7 :32; + /* -------------- */ + mlx_uint32 eth_proto_lp_advertise :32; + mlx_uint32 reserved[3]; }; mlx_status diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c index 2277e0c76..1ea68dd8a 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c @@ -42,6 +42,7 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { TlvMappingEntry(0x2020, 0x2020, NVRAM_TLV_CLASS_PHYSICAL_PORT, FALSE), TlvMappingEntry(0x2021, 0x221, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2023, 0x223, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2006, 0x206, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2100, 0x230, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2101, 0x231, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2102, 0x232, NVRAM_TLV_CLASS_HOST, FALSE), @@ -53,6 +54,7 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { TlvMappingEntry(0x2108, 0x238, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2109, 0x239, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x210A, 0x23A, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2022, 0x222, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2200, 0x240, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2201, 0x241, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2202, 0x242, NVRAM_TLV_CLASS_HOST, FALSE), @@ -60,6 +62,11 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { TlvMappingEntry(0x2204, 0x244, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2205, 0x245, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2207, 0x247, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2002, 0x202, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2004, 0x204, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x110, 0x110, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x192, 0x192, NVRAM_TLV_CLASS_GLOBAL, FALSE), + TlvMappingEntry(0x101, 0x101, NVRAM_TLV_CLASS_GLOBAL, TRUE), TlvMappingEntry(0, 0, 0, 0), }; diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h index 8333e8368..0a99bb1b5 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h @@ -107,6 +107,22 @@ struct nvconfig_nvda { mlx_uint8 data[NVCONFIG_MAX_TLV_SIZE]; }; +struct nv_conf_cap { + /** WOL En/Dis **/ + mlx_uint8 wol_en; + /** VPI En/Dis **/ + mlx_uint8 vpi_en; +}; + +struct mlx_nvconfig_virt_net_addr { + mlx_uint32 reserved1 :29; + mlx_uint32 erase_on_powerup:1; + mlx_uint32 reserverd2 :1; + mlx_uint32 virtual_mac_en :1; + mlx_uint32 virtual_mac_high; + mlx_uint32 virtual_mac_low; +}; + mlx_status nvconfig_query_capability( diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c index 77eda8a5c..f5b2f155f 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c @@ -86,13 +86,13 @@ nvconfig_get_boot_ext_default_conf( "TLV not found. Using hard-coded defaults "); port_conf_def->linkup_timeout = nic_boot_ext_conf->linkup_timeout; port_conf_def->ip_ver = nic_boot_ext_conf->ip_ver; - + port_conf_def->undi_network_wait_to = nic_boot_ext_conf->undi_network_wait_to; return MLX_SUCCESS; nvdata_access_err: port_conf_def->linkup_timeout = DEFAULT_BOOT_LINK_UP_TO; port_conf_def->ip_ver = DEFAULT_BOOT_IP_VER; - + port_conf_def->undi_network_wait_to = DEFAULT_BOOT_UNDI_NETWORK_WAIT_TO; return status; } @@ -185,8 +185,12 @@ nvconfig_get_iscsi_gen_default_conf( port_conf_def->iscsi_chap_auth_en = iscsi_gen->chap_auth_en; port_conf_def->iscsi_lun_busy_retry_count = iscsi_gen->lun_busy_retry_count; port_conf_def->iscsi_link_up_delay_time = iscsi_gen->link_up_delay_time; + port_conf_def->iscsi_drive_num = iscsi_gen->drive_num; + + return MLX_SUCCESS; nvdata_access_err: + port_conf_def->iscsi_drive_num = DEFAULT_ISCSI_DRIVE_NUM; return status; } @@ -327,6 +331,27 @@ nvdata_access_err: return status; } +static +mlx_status +nvconfig_get_rom_cap_default_conf( IN void *data, + IN int status, OUT void *def_struct) { + union mlx_nvconfig_rom_cap_conf *rom_cap_conf = + (union mlx_nvconfig_rom_cap_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->boot_ip_ver_en = rom_cap_conf->boot_ip_ver_en; + + return MLX_SUCCESS; + +nvdata_access_err: + rom_cap_conf->boot_ip_ver_en = DEFAULT_BOOT_IP_VERSION_EN; + + return status; +} + static struct tlv_default tlv_port_defaults[] = { TlvDefaultEntry(BOOT_SETTINGS_TYPE, union mlx_nvconfig_nic_boot_conf, &nvconfig_get_boot_default_conf), TlvDefaultEntry(BOOT_SETTINGS_EXT_TYPE, union mlx_nvconfig_nic_boot_ext_conf, &nvconfig_get_boot_ext_default_conf), @@ -343,6 +368,7 @@ static struct tlv_default tlv_general_defaults[] = { TlvDefaultEntry(GLOPAL_PCI_CAPS_TYPE, union mlx_nvconfig_virt_caps, &nvconfig_get_nv_virt_caps_default_conf), TlvDefaultEntry(GLOPAL_PCI_SETTINGS_TYPE, union mlx_nvconfig_virt_conf, &nvconfig_get_nv_virt_default_conf), TlvDefaultEntry(OCSD_OCBB_TYPE, union mlx_nvconfig_ocsd_ocbb_conf, &nvconfig_get_ocsd_ocbb_default_conf), + TlvDefaultEntry(NV_ROM_CAP_TYPE, union mlx_nvconfig_rom_cap_conf, &nvconfig_get_rom_cap_default_conf), }; static diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h index 163c2a35f..48699c358 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h @@ -32,9 +32,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DEFAULT_BOOT_VLAN 1 #define DEFAULT_ISCSI_DHCP_PARAM_EN 1 #define DEFAULT_ISCSI_IPV4_DHCP_EN 1 +#define DEFAULT_ISCSI_DRIVE_NUM 0x80 #define DEFAULT_OCSD_OCBB_EN 1 #define DEFAULT_BOOT_IP_VER 0 #define DEFAULT_BOOT_LINK_UP_TO 0 +#define DEFAULT_BOOT_UNDI_NETWORK_WAIT_TO 30 +#define DEFAULT_BOOT_IP_VERSION_EN 1 struct mlx_nvconfig_port_conf_defaults { mlx_uint8 pptx; @@ -56,11 +59,13 @@ struct mlx_nvconfig_port_conf_defaults { mlx_boolean iscsi_ipv4_dhcp_en; mlx_uint8 iscsi_lun_busy_retry_count; mlx_uint8 iscsi_link_up_delay_time; + mlx_uint8 iscsi_drive_num; mlx_uint8 client_identifier; mlx_uint8 mac_admin_bit; mlx_uint8 default_link_type; mlx_uint8 linkup_timeout; mlx_uint8 ip_ver; + mlx_uint8 undi_network_wait_to; }; struct mlx_nvconfig_conf_defaults { @@ -71,6 +76,7 @@ struct mlx_nvconfig_conf_defaults { mlx_uint8 uar_bar_size; mlx_uint8 flexboot_menu_to; mlx_boolean ocsd_ocbb_en; + mlx_boolean boot_ip_ver_en; }; mlx_status diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h index 5b3af1e78..7fd52accc 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h @@ -33,12 +33,15 @@ enum { OCSD_OCBB_TYPE = 0x2011, FLOW_CONTROL_TYPE = 0x2020, BOOT_SETTINGS_TYPE = 0x2021, + NV_ROM_FLEXBOOT_DEBUG = 0x2004, + ISCSI_GENERAL_SETTINGS_TYPE = 0x2100, IB_BOOT_SETTING_TYPE = 0x2022, IB_DHCP_SETTINGS_TYPE = 0x2023, GLOPAL_PCI_SETTINGS_TYPE = 0x80, GLOPAL_PCI_CAPS_TYPE = 0x81, GLOBAL_ROM_INI_TYPE = 0x100, + NV_VIRT_NET_ADDR = 0x110, // Types for iSCSI strings DHCP_VEND_ID = 0x2101, @@ -59,6 +62,8 @@ enum { FIRST_TGT_ISCSI_NAME = 0x2204, FIRST_TGT_CHAP_ID = 0x2205, FIRST_TGT_CHAP_PWD = 0x2207, + NV_ROM_DEBUG_LEVEL = 0x2002, + NV_ROM_CAP_TYPE = 0x101, }; union mlx_nvconfig_nic_boot_conf { @@ -78,7 +83,9 @@ union mlx_nvconfig_nic_boot_ext_conf { struct { mlx_uint32 linkup_timeout : 8; mlx_uint32 ip_ver : 2; - mlx_uint32 reserved0 : 22; + mlx_uint32 reserved0 : 6; + mlx_uint32 undi_network_wait_to : 8; + mlx_uint32 reserved1 : 8; }; mlx_uint32 dword; }; @@ -194,7 +201,8 @@ union mlx_nvconfig_iscsi_general { /*-------------------*/ mlx_uint32 lun_busy_retry_count:8; mlx_uint32 link_up_delay_time :8; - mlx_uint32 reserved4 :16; + mlx_uint32 drive_num :8; + mlx_uint32 reserved4 :8; }; mlx_uint32 dword[3]; }; @@ -226,34 +234,98 @@ union mlx_nvconfig_vpi_link_conf { }; struct mlx_nvcofnig_romini { - mlx_uint32 reserved0 :1; + mlx_uint32 reserved0 :1; mlx_uint32 shared_memory_en :1; - mlx_uint32 hii_vpi_en :1; - mlx_uint32 tech_enum :1; - mlx_uint32 reserved1 :4; + mlx_uint32 hii_vpi_en :1; + mlx_uint32 tech_enum :1; + mlx_uint32 reserved1 :4; mlx_uint32 static_component_name_string :1; mlx_uint32 hii_iscsi_configuration :1; - mlx_uint32 hii_ibm_aim :1; + mlx_uint32 hii_ibm_aim :1; mlx_uint32 hii_platform_setup :1; mlx_uint32 hii_bdf_decimal :1; mlx_uint32 hii_read_only :1; - mlx_uint32 reserved2 :10; + mlx_uint32 reserved2 :10; mlx_uint32 mac_enum :1; - mlx_uint32 port_enum :1; + mlx_uint32 port_enum :1; mlx_uint32 flash_en :1; mlx_uint32 fmp_en :1; mlx_uint32 bofm_en :1; - mlx_uint32 platform_to_driver_en :1; + mlx_uint32 platform_to_driver_en:1; mlx_uint32 hii_en :1; mlx_uint32 undi_en :1; /* -------------- */ mlx_uint64 dhcp_user_class; /* -------------- */ - mlx_uint32 reserved3 :22; + mlx_uint32 reserved3 :10; + mlx_uint32 ucm_single_port :1; + mlx_uint32 tivoli_wa_en :1; + mlx_uint32 dhcp_pxe_discovery_control_dis :1; + mlx_uint32 hii_flexaddr_override:1; + mlx_uint32 hii_flexaddr_setting :1; + mlx_uint32 guided_ops :1; + mlx_uint32 hii_type :4; + mlx_uint32 hii_mriname2 :1; + mlx_uint32 hii_aim_ucm_ver2 :1; mlx_uint32 uri_boot_retry_delay :4; mlx_uint32 uri_boot_retry :4; mlx_uint32 option_rom_debug :1; mlx_uint32 promiscuous_vlan :1; + +} __attribute__ ((packed)); + +union mlx_nvconfig_debug_conf { + struct { + mlx_uint32 dbg_log_en :1; + mlx_uint32 reserved1 :31; + /***************************************************/ + mlx_uint32 stp_dbg_lvl :2; + mlx_uint32 romprefix_dbg_lvl :2; + mlx_uint32 dhcp_dbg_lvl :2; + mlx_uint32 dhcpv6_dbg_lvl :2; + mlx_uint32 arp_dbg_lvl :2; + mlx_uint32 neighbor_dbg_lvl :2; + mlx_uint32 ndp_dbg_lvl :2; + mlx_uint32 uri_dbg_lvl :2; + mlx_uint32 driver_dbg_lvl :2; + mlx_uint32 nodnic_dbg_lvl :2; + mlx_uint32 nodnic_cmd_dbg_lvl :2; + mlx_uint32 nodnic_device_dbg_lvl :2; + mlx_uint32 nodnic_port_dbg_lvl :2; + mlx_uint32 netdevice_dbg_lvl :2; + mlx_uint32 tftp_dbg_lvl :2; + mlx_uint32 udp_dbg_lvl :2; + /***************************************************/ + mlx_uint32 tcp_dbg_lvl :2; + mlx_uint32 tcpip_dbg_lvl :2; + mlx_uint32 ipv4_dbg_lvl :2; + mlx_uint32 ipv6_dbg_lvl :2; + mlx_uint32 drv_set_dbg_lvl :2; + mlx_uint32 stat_update_dbg_lvl :2; + mlx_uint32 pxe_undi_dbg_lvl :2; + mlx_uint32 reserved2 :18; + }; + mlx_uint32 dword[3]; +}; + +union mlx_nvconfig_flexboot_debug { + struct { + mlx_uint32 reserved0 :29; + mlx_uint32 panic_behavior :2; + mlx_uint32 boot_to_shell :1; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_rom_cap_conf { + struct { + mlx_uint32 reserved0 :28; + mlx_uint32 uefi_logs_en :1; + mlx_uint32 flexboot_debug_en :1; + mlx_uint32 boot_debug_log_en :1; + mlx_uint32 boot_ip_ver_en :1; + }; + mlx_uint32 dword; }; #endif /* MLX_NVCONFIG_PRM_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c index f7d365dee..e4ab5f0ad 100644 --- a/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c @@ -316,7 +316,7 @@ mlx_icmd_send_command( ) { mlx_status status = MLX_SUCCESS; - mlx_uint32 icmd_status = MLX_FAILED; + mlx_uint32 icmd_status = 0; if (utils == NULL || data == NULL) { status = MLX_INVALID_PARAMETER; diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c index 91c44d991..f9f9b2a12 100644 --- a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include + #include "../../include/private/mlx_pci_priv.h" #include "../../include/public/mlx_pci.h" @@ -38,6 +39,21 @@ bail: return status; } +mlx_status +mlx_pci_teardown( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_teardown_priv(utils); +bail: + return status; +} + mlx_status mlx_pci_read( IN mlx_utils *utils, diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c index c824b17e9..7ae35355d 100644 --- a/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c @@ -20,10 +20,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include + #include "../../include/private/mlx_utils_priv.h" #include "../../include/public/mlx_pci.h" #include "../../include/public/mlx_utils.h" - mlx_status mlx_utils_init( IN mlx_utils *utils, @@ -44,11 +44,12 @@ bail: mlx_status mlx_utils_teardown( - IN mlx_utils *utils __attribute__ ((unused)) + IN mlx_utils *utils ) { mlx_status status = MLX_SUCCESS; mlx_utils_free_lock(utils); + mlx_pci_teardown(utils); return status; } diff --git a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h index af7e86f44..3acc1d9d8 100644 --- a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h +++ b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h @@ -12,8 +12,8 @@ #include #define MLX_DEBUG_FATAL_ERROR_PRIVATE(...) do { \ - DBG("%s: ",__func__); \ - DBG(__VA_ARGS__); \ + printf("%s: ",__func__); \ + printf(__VA_ARGS__); \ } while ( 0 ) #define MLX_DEBUG_ERROR_PRIVATE(id, ...) do { \ @@ -56,6 +56,7 @@ DBG2(__VA_ARGS__); \ } while ( 0 ) +#define MLX_PRINT_PRIVATE(...) printf(__VA_ARGS__) #endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h index feaeae679..fe0d5c056 100644 --- a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h +++ b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h @@ -33,7 +33,7 @@ typedef uint8_t mlx_uint8; typedef uint16_t mlx_uint16; typedef uint32_t mlx_uint32; typedef uint64_t mlx_uint64; -typedef uint32_t mlx_uintn; +typedef unsigned long mlx_uintn; typedef int8_t mlx_int8; typedef int16_t mlx_int16;; diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c index f8caefdce..b474a4a63 100644 --- a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c @@ -6,6 +6,7 @@ */ #include + #include "../../mlx_utils/include/private/mlx_pci_priv.h" @@ -120,6 +121,18 @@ mlx_pci_init_priv( return status; } +mlx_status +mlx_pci_teardown_priv( + IN mlx_utils *utils __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; +#ifdef DEVICE_CX3 + iounmap( utils->config ); +#endif + return status; +} + mlx_status mlx_pci_read_priv( IN mlx_utils *utils, From 43b2d8eafb2c1ac568d43be5491cc5b441c6c5e5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 22 Jan 2017 09:12:52 +0000 Subject: [PATCH 326/591] [ipv4] Accept unicast packets for the local network broadcast address The ISC Kea DHCP server transmits its DHCPOFFER as a unicast packet with a broadcast IPv4 destination address (255.255.255.255). This combination is currently rejected by iPXE. Fix by explicitly accepting the local network broadcast address (255.255.255.255) as a valid unicast destination address. Reported-by: Roy Ledochowski Tested-by: Roy Ledochowski Signed-off-by: Michael Brown --- src/net/ipv4.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/ipv4.c b/src/net/ipv4.c index b4148d8e0..b9ce5e7f7 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -552,6 +552,7 @@ static int ipv4_rx ( struct io_buffer *iobuf, /* Discard unicast packets not destined for us */ if ( ( ! ( flags & LL_MULTICAST ) ) && + ( iphdr->dest.s_addr != INADDR_BROADCAST ) && ipv4_has_any_addr ( netdev ) && ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) { DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet " From dfbbc16ae392e8f4ab62e15a99cdfb32a06af557 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 22 Jan 2017 11:22:11 +0000 Subject: [PATCH 327/591] [build] Add %.vhd target for building VM bootable disk images Signed-off-by: Michael Brown --- src/Makefile | 1 + src/arch/x86/Makefile.pcbios | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/Makefile b/src/Makefile index 582ffe81c..1a81e9152 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,6 +53,7 @@ EINFO := ./util/einfo GENKEYMAP := ./util/genkeymap.pl DOXYGEN := doxygen LCAB := lcab +QEMUIMG := qemu-img ############################################################################### # diff --git a/src/arch/x86/Makefile.pcbios b/src/arch/x86/Makefile.pcbios index f8c225352..df08e0182 100644 --- a/src/arch/x86/Makefile.pcbios +++ b/src/arch/x86/Makefile.pcbios @@ -120,6 +120,11 @@ NON_AUTO_MEDIA += usb $(QM)$(ECHO) " [FINISH] $@" $(Q)cat $^ > $@ +NON_AUTO_MEDIA += vhd +%vhd: %usb + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(QEMUIMG) convert -f raw -O vpc $< $@ + # Padded floppy image (e.g. for iLO) NON_AUTO_MEDIA += pdsk %pdsk : %dsk From e45451c6994f6e93729ee3860e45fa965483356f Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 16 Dec 2016 10:54:32 +0100 Subject: [PATCH 328/591] [virtio] Cap queue size to MAX_QUEUE_NUM vpm_find_vqs incorrectly accepted the host provided queue size with no regard to iPXE's internal limitations. Virtio 1.0 makes it possible for the driver to override the queue size to reduce memory requirements and iPXE is a great use case for this feature. Also removing the extra vq->vring.num assignment which is already handled in vring_init. Signed-off-by: Ladi Prosek Acked-by: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael Brown --- src/drivers/bus/virtio-pci.c | 8 +++++++- src/include/ipxe/virtio-ring.h | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index 3311595fb..1fcf9bae0 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -358,12 +358,18 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, return -EINVAL; } + if (size > MAX_QUEUE_NUM) { + /* iPXE networking tends to be not perf critical so there's no + * need to accept large queue sizes. + */ + size = MAX_QUEUE_NUM; + } + vq = &vqs[i]; vq->queue_index = i; /* get offset of notification word for this vq */ off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off)); - vq->vring.num = size; vring_init(&vq->vring, size, (unsigned char *)vq->queue); diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index 6ba550b5a..d2ded3007 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -95,7 +95,7 @@ static inline void vring_init(struct vring *vr, unsigned int i; unsigned long pa; - vr->num = num; + vr->num = num; /* physical address of desc must be page aligned */ @@ -103,13 +103,13 @@ static inline void vring_init(struct vring *vr, pa = (pa + PAGE_MASK) & ~PAGE_MASK; vr->desc = phys_to_virt(pa); - vr->avail = (struct vring_avail *)&vr->desc[num]; + vr->avail = (struct vring_avail *)&vr->desc[num]; /* physical address of used must be page aligned */ pa = virt_to_phys(&vr->avail->ring[num]); pa = (pa + PAGE_MASK) & ~PAGE_MASK; - vr->used = phys_to_virt(pa); + vr->used = phys_to_virt(pa); for (i = 0; i < num - 1; i++) vr->desc[i].next = i + 1; From b782a56be725569fb68f92b81a01903d1652c5b0 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 16 Dec 2016 13:31:22 +0100 Subject: [PATCH 329/591] [virtio] Simplify virtqueue shutdown This commit introduces virtnet_free_virtqueues called on all virtqueue error and shutdown paths. vpm_find_vqs no longer cleans up after itself and instead expects virtnet_free_virtqueues to be always called to undo its effect. Signed-off-by: Ladi Prosek Acked-by: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael Brown --- src/drivers/bus/virtio-pci.c | 9 +-------- src/drivers/net/virtio-net.c | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index 1fcf9bae0..c50602a31 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -391,7 +391,7 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, off * notify_offset_multiplier, 2, &vq->notification); if (err) { - goto err_map_notify; + return err; } } @@ -405,11 +405,4 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, vpm_iowrite16(vdev, &vdev->common, 1, COMMON_OFFSET(queue_enable)); } return 0; - -err_map_notify: - /* Undo the virtio_pci_map_capability calls. */ - while (i-- > 0) { - virtio_pci_unmap_capability(&vqs[i].notification); - } - return err; } diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index fe0fd4b86..de3333d2f 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -175,6 +175,22 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { } } +/** Helper to free all virtqueue memory + * + * @v netdev Network device + */ +static void virtnet_free_virtqueues ( struct net_device *netdev ) { + struct virtnet_nic *virtnet = netdev->priv; + int i; + + for ( i = 0; i < QUEUE_NB; i++ ) { + virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification ); + } + + free ( virtnet->virtqueue ); + virtnet->virtqueue = NULL; +} + /** Open network device, legacy virtio 0.9.5 * * @v netdev Network device @@ -200,8 +216,7 @@ static int virtnet_open_legacy ( struct net_device *netdev ) { if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n", virtnet, i ); - free ( virtnet->virtqueue ); - virtnet->virtqueue = NULL; + virtnet_free_virtqueues ( netdev ); return -ENOENT; } } @@ -263,8 +278,7 @@ static int virtnet_open_modern ( struct net_device *netdev ) { if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n", virtnet ); - free ( virtnet->virtqueue ); - virtnet->virtqueue = NULL; + virtnet_free_virtqueues ( netdev ); vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); return -ENOENT; } @@ -304,7 +318,6 @@ static void virtnet_close ( struct net_device *netdev ) { struct virtnet_nic *virtnet = netdev->priv; struct io_buffer *iobuf; struct io_buffer *next_iobuf; - int i; if ( virtnet->virtio_version ) { vpm_reset ( &virtnet->vdev ); @@ -313,12 +326,7 @@ static void virtnet_close ( struct net_device *netdev ) { } /* Virtqueues can be freed now that NIC is reset */ - for ( i = 0 ; i < QUEUE_NB ; i++ ) { - virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification ); - } - - free ( virtnet->virtqueue ); - virtnet->virtqueue = NULL; + virtnet_free_virtqueues ( netdev ); /* Free rx iobufs */ list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) { From fba3b39900af52e695d59bc34f28bb8d22526ddb Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 16 Dec 2016 14:07:08 +0100 Subject: [PATCH 330/591] [virtio] Remove queue size limit in legacy virtio Virtio 0.9 implementation was limited to the maximum virtqueue size of MAX_QUEUE_NUM and the virtio-net driver would fail to initialize on hosts exceeding this limit. This commit lifts the restriction by allocating the queue memory based on the actual queue size instead of using a fixed maximum. Note that virtio 1.0 still uses the MAX_QUEUE_NUM constant to cap the size (unfortunately this functionality is not available in virtio 0.9). Signed-off-by: Ladi Prosek Acked-by: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael Brown --- src/drivers/bus/virtio-pci.c | 48 +++++++++++++++++++++++++++------- src/drivers/net/virtio-net.c | 1 + src/include/ipxe/virtio-pci.h | 2 ++ src/include/ipxe/virtio-ring.h | 6 ++--- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index c50602a31..402bf4f12 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -21,11 +21,37 @@ #include "ipxe/virtio-pci.h" #include "ipxe/virtio-ring.h" +static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num) +{ + size_t queue_size = PAGE_MASK + vring_size(num); + size_t vdata_size = num * sizeof(void *); + + vq->queue = zalloc(queue_size + vdata_size); + if (!vq->queue) { + return -ENOMEM; + } + + /* vdata immediately follows the ring */ + vq->vdata = (void **)(vq->queue + queue_size); + + return 0; +} + +void vp_free_vq(struct vring_virtqueue *vq) +{ + if (vq->queue) { + free(vq->queue); + vq->queue = NULL; + vq->vdata = NULL; + } +} + int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq) { struct vring * vr = &vq->vring; u16 num; + int rc; /* select the queue */ @@ -39,11 +65,6 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, return -1; } - if (num > MAX_QUEUE_NUM) { - DBG("VIRTIO-PCI ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); - return -1; - } - /* check if the queue is already active */ if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { @@ -54,8 +75,12 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, vq->queue_index = queue_index; /* initialize the queue */ - - vring_init(vr, num, (unsigned char*)&vq->queue); + rc = vp_alloc_vq(vq, num); + if (rc) { + DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n"); + return rc; + } + vring_init(vr, num, vq->queue); /* activate the queue * @@ -354,7 +379,7 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, return -ENOENT; if (size & (size - 1)) { - DBG("VIRTIO-PCI %p: bad queue size %d", vdev, size); + DBG("VIRTIO-PCI %p: bad queue size %d\n", vdev, size); return -EINVAL; } @@ -371,7 +396,12 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, /* get offset of notification word for this vq */ off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off)); - vring_init(&vq->vring, size, (unsigned char *)vq->queue); + err = vp_alloc_vq(vq, size); + if (err) { + DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev); + return err; + } + vring_init(&vq->vring, size, vq->queue); /* activate the queue */ vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size)); diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index de3333d2f..eaf6ed382 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -185,6 +185,7 @@ static void virtnet_free_virtqueues ( struct net_device *netdev ) { for ( i = 0; i < QUEUE_NB; i++ ) { virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification ); + vp_free_vq ( &virtnet->virtqueue[i] ); } free ( virtnet->virtqueue ); diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index f3c9b17ca..f3074f14d 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -196,9 +196,11 @@ static inline void vp_del_vq(unsigned int ioaddr, int queue_index) struct vring_virtqueue; +void vp_free_vq(struct vring_virtqueue *vq); int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq); + /* Virtio 1.0 I/O routines abstract away the three possible HW access * mechanisms - memory, port I/O, and PCI cfg space access. Also built-in * are endianness conversions - to LE on write and from LE on read. */ diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index d2ded3007..e608e624f 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -71,14 +71,12 @@ struct vring { + PAGE_MASK) & ~PAGE_MASK) + \ (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) -typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)]; - struct vring_virtqueue { - virtio_queue_t queue; + unsigned char *queue; struct vring vring; u16 free_head; u16 last_used_idx; - void *vdata[MAX_QUEUE_NUM]; + void **vdata; /* PCI */ int queue_index; struct virtio_pci_region notification; From 04c7befa730b3a6bfed069d3c6f6539bddb0ee52 Mon Sep 17 00:00:00 2001 From: David Decotigny Date: Fri, 20 Jan 2017 10:29:22 -0800 Subject: [PATCH 331/591] [build] Return const char * from uuid_ntoa() Signed-off-by: Michael Brown --- src/core/uuid.c | 2 +- src/include/ipxe/uuid.h | 2 +- src/net/peerdisc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/uuid.c b/src/core/uuid.c index b8d21de17..c43d4216f 100644 --- a/src/core/uuid.c +++ b/src/core/uuid.c @@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v uuid UUID * @ret string UUID in canonical form */ -char * uuid_ntoa ( const union uuid *uuid ) { +const char * uuid_ntoa ( const union uuid *uuid ) { static char buf[37]; /* "00000000-0000-0000-0000-000000000000" */ sprintf ( buf, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", diff --git a/src/include/ipxe/uuid.h b/src/include/ipxe/uuid.h index 6c45eb9aa..24c46acaf 100644 --- a/src/include/ipxe/uuid.h +++ b/src/include/ipxe/uuid.h @@ -47,6 +47,6 @@ static inline void uuid_mangle ( union uuid *uuid ) { __bswap_16s ( &uuid->canonical.c ); } -extern char * uuid_ntoa ( const union uuid *uuid ); +extern const char * uuid_ntoa ( const union uuid *uuid ); #endif /* _IPXE_UUID_H */ diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index 5b0e98911..4c3cd2ea5 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -408,7 +408,7 @@ static struct peerdisc_segment * peerdisc_create ( const char *id ) { } random_uuid; size_t uuid_len; size_t id_len; - char *uuid; + const char *uuid; char *uuid_copy; char *id_copy; unsigned int i; From b6f524388ba5a02cf95ffa3fb3bfd5f0feae7bb6 Mon Sep 17 00:00:00 2001 From: David Decotigny Date: Fri, 20 Jan 2017 10:29:24 -0800 Subject: [PATCH 332/591] [af_packet] Add new AF_PACKET driver for Linux This code largely inspired by tap.c. Allows for testing iPXE on real NICs from within Linux. For example: make bin-x86_64-linux/af_packet.linux valgrind ./bin-x86_64-linux/af_packet.linux --net af_packet,if=eth3 Tested as x86_64 and i386 binary. Signed-off-by: Michael Brown --- src/arch/x86/core/linux/linux_api.c | 39 ++++ src/drivers/linux/af_packet.c | 325 ++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/linux_api.h | 7 + 4 files changed, 372 insertions(+) create mode 100644 src/drivers/linux/af_packet.c diff --git a/src/arch/x86/core/linux/linux_api.c b/src/arch/x86/core/linux/linux_api.c index 0bed9fd57..17b1f3fd4 100644 --- a/src/arch/x86/core/linux/linux_api.c +++ b/src/arch/x86/core/linux/linux_api.c @@ -108,3 +108,42 @@ void * linux_mremap ( void *old_address, __kernel_size_t old_size, int linux_munmap ( void *addr, __kernel_size_t length ) { return linux_syscall ( __NR_munmap, addr, length ); } + +int linux_socket ( int domain, int type_, int protocol ) { +#ifdef __NR_socket + return linux_syscall ( __NR_socket, domain, type_, protocol ); +#else +#ifndef SOCKOP_socket +# define SOCKOP_socket 1 +#endif + unsigned long sc_args[] = { domain, type_, protocol }; + return linux_syscall ( __NR_socketcall, SOCKOP_socket, sc_args ); +#endif +} + +int linux_bind ( int fd, const struct sockaddr *addr, socklen_t addrlen ) { +#ifdef __NR_bind + return linux_syscall ( __NR_bind, fd, addr, addrlen ); +#else +#ifndef SOCKOP_bind +# define SOCKOP_bind 2 +#endif + unsigned long sc_args[] = { fd, (unsigned long)addr, addrlen }; + return linux_syscall ( __NR_socketcall, SOCKOP_bind, sc_args ); +#endif +} + +ssize_t linux_sendto ( int fd, const void *buf, size_t len, int flags, + const struct sockaddr *daddr, socklen_t addrlen ) { +#ifdef __NR_sendto + return linux_syscall ( __NR_sendto, fd, buf, len, flags, + daddr, addrlen ); +#else +#ifndef SOCKOP_sendto +# define SOCKOP_sendto 11 +#endif + unsigned long sc_args[] = { fd, (unsigned long)buf, len, + flags, (unsigned long)daddr, addrlen }; + return linux_syscall ( __NR_socketcall, SOCKOP_sendto, sc_args ); +#endif +} diff --git a/src/drivers/linux/af_packet.c b/src/drivers/linux/af_packet.c new file mode 100644 index 000000000..1622c8c0e --- /dev/null +++ b/src/drivers/linux/af_packet.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2016 David Decotigny + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ +#define __GLIBC__ 2 +#include +#include +#include +#include +#undef __GLIBC__ +#include + +/* linux-specifc syscall params */ +#define LINUX_AF_PACKET 17 +#define LINUX_SOCK_RAW 3 +#define LINUX_SIOCGIFINDEX 0x8933 +#define LINUX_SIOCGIFHWADDR 0x8927 + +#define RX_BUF_SIZE 1536 + +/** @file + * + * The AF_PACKET driver. + * + * Bind to an existing linux network interface. + */ + +struct af_packet_nic { + /** Linux network interface name */ + char * ifname; + /** Packet socket descriptor */ + int fd; + /** ifindex */ + int ifindex; +}; + +/** Open the linux interface */ +static int af_packet_nic_open ( struct net_device * netdev ) +{ + struct af_packet_nic * nic = netdev->priv; + struct sockaddr_ll socket_address; + struct ifreq if_data; + int ret; + + nic->fd = linux_socket(LINUX_AF_PACKET, LINUX_SOCK_RAW, + htons(ETH_P_ALL)); + if (nic->fd < 0) { + DBGC(nic, "af_packet %p socket(AF_PACKET) = %d (%s)\n", + nic, nic->fd, linux_strerror(linux_errno)); + return nic->fd; + } + + /* resolve ifindex of ifname */ + memset(&if_data, 0, sizeof(if_data)); + strncpy(if_data.ifr_name, nic->ifname, sizeof(if_data.ifr_name)); + ret = linux_ioctl(nic->fd, LINUX_SIOCGIFINDEX, &if_data); + if (ret < 0) { + DBGC(nic, "af_packet %p ioctl(SIOCGIFINDEX) = %d (%s)\n", + nic, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + nic->ifindex = if_data.ifr_ifindex; + + /* bind to interface */ + memset(&socket_address, 0, sizeof(socket_address)); + socket_address.sll_family = LINUX_AF_PACKET; + socket_address.sll_ifindex = nic->ifindex; + socket_address.sll_protocol = htons(ETH_P_ALL); + ret = linux_bind(nic->fd, (void *) &socket_address, + sizeof(socket_address)); + if (ret == -1) { + DBGC(nic, "af_packet %p bind() = %d (%s)\n", + nic, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + /* Set nonblocking mode to make af_packet_nic_poll() easier */ + ret = linux_fcntl(nic->fd, F_SETFL, O_NONBLOCK); + if (ret != 0) { + DBGC(nic, "af_packet %p fcntl(%d, ...) = %d (%s)\n", + nic, nic->fd, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + return 0; +} + +/** Close the packet socket */ +static void af_packet_nic_close ( struct net_device *netdev ) +{ + struct af_packet_nic * nic = netdev->priv; + linux_close(nic->fd); +} + +/** + * Transmit an ethernet packet. + * + * The packet can be written to the socket and marked as complete immediately. + */ +static int af_packet_nic_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) +{ + struct af_packet_nic * nic = netdev->priv; + struct sockaddr_ll socket_address; + const struct ethhdr * eh; + int rc; + + memset(&socket_address, 0, sizeof(socket_address)); + socket_address.sll_family = LINUX_AF_PACKET; + socket_address.sll_ifindex = nic->ifindex; + socket_address.sll_halen = ETH_ALEN; + + eh = iobuf->data; + memcpy(socket_address.sll_addr, eh->h_dest, ETH_ALEN); + + rc = linux_sendto(nic->fd, iobuf->data, iobuf->tail - iobuf->data, + 0, (struct sockaddr *)&socket_address, + sizeof(socket_address)); + + DBGC2(nic, "af_packet %p wrote %d bytes\n", nic, rc); + netdev_tx_complete(netdev, iobuf); + + return 0; +} + +/** Poll for new packets */ +static void af_packet_nic_poll ( struct net_device *netdev ) +{ + struct af_packet_nic * nic = netdev->priv; + struct pollfd pfd; + struct io_buffer * iobuf; + int r; + + pfd.fd = nic->fd; + pfd.events = POLLIN; + if (linux_poll(&pfd, 1, 0) == -1) { + DBGC(nic, "af_packet %p poll failed (%s)\n", + nic, linux_strerror(linux_errno)); + return; + } + if ((pfd.revents & POLLIN) == 0) + return; + + /* At this point we know there is at least one new packet to be read */ + + iobuf = alloc_iob(RX_BUF_SIZE); + if (! iobuf) + goto allocfail; + + while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) { + DBGC2(nic, "af_packet %p read %d bytes\n", nic, r); + + iob_put(iobuf, r); + netdev_rx(netdev, iobuf); + + iobuf = alloc_iob(RX_BUF_SIZE); + if (! iobuf) + goto allocfail; + } + + free_iob(iobuf); + return; + +allocfail: + DBGC(nic, "af_packet %p alloc_iob failed\n", nic); +} + +/** + * Set irq. + * + * Not used on linux, provide a dummy implementation. + */ +static void af_packet_nic_irq ( struct net_device *netdev, int enable ) +{ + struct af_packet_nic *nic = netdev->priv; + + DBGC(nic, "af_packet %p irq enable = %d\n", nic, enable); +} + + +static int af_packet_update_properties ( struct net_device *netdev ) +{ + struct af_packet_nic *nic = netdev->priv; + struct ifreq if_data; + int ret; + + /* retrieve default MAC address */ + int fd = linux_socket(LINUX_AF_PACKET, LINUX_SOCK_RAW, 0); + if (fd < 0) { + DBGC(nic, "af_packet %p cannot create raw socket (%s)\n", + nic, linux_strerror(linux_errno)); + return fd; + } + + /* retrieve host's MAC address */ + memset(&if_data, 0, sizeof(if_data)); + strncpy(if_data.ifr_name, nic->ifname, sizeof(if_data.ifr_name)); + ret = linux_ioctl(fd, LINUX_SIOCGIFHWADDR, &if_data); + if (ret < 0) { + DBGC(nic, "af_packet %p cannot get mac addr (%s)\n", + nic, linux_strerror(linux_errno)); + linux_close(fd); + return ret; + } + + linux_close(fd); + /* struct sockaddr = { u16 family, u8 pad[14] (equiv. sa_data) }; */ + memcpy(netdev->ll_addr, if_data.ifr_hwaddr.pad, ETH_ALEN); + return 0; +} + +/** AF_PACKET operations */ +static struct net_device_operations af_packet_nic_operations = { + .open = af_packet_nic_open, + .close = af_packet_nic_close, + .transmit = af_packet_nic_transmit, + .poll = af_packet_nic_poll, + .irq = af_packet_nic_irq, +}; + +/** Handle a device request for the af_packet driver */ +static int af_packet_nic_probe ( struct linux_device *device, + struct linux_device_request *request ) +{ + struct linux_setting *if_setting; + struct net_device *netdev; + struct af_packet_nic *nic; + int rc; + + netdev = alloc_etherdev(sizeof(*nic)); + if (! netdev) + return -ENOMEM; + + netdev_init(netdev, &af_packet_nic_operations); + nic = netdev->priv; + linux_set_drvdata(device, netdev); + netdev->dev = &device->dev; + + memset(nic, 0, sizeof(*nic)); + + /* Look for the mandatory if setting */ + if_setting = linux_find_setting("if", &request->settings); + + /* No if setting */ + if (! if_setting) { + printf("af_packet missing a mandatory if setting\n"); + rc = -EINVAL; + goto err_settings; + } + + nic->ifname = if_setting->value; + snprintf ( device->dev.name, sizeof ( device->dev.name ), "%s", + nic->ifname ); + device->dev.desc.bus_type = BUS_TYPE_TAP; + af_packet_update_properties(netdev); + if_setting->applied = 1; + + /* Apply rest of the settings */ + linux_apply_settings(&request->settings, &netdev->settings.settings); + + /* Register network device */ + if ((rc = register_netdev(netdev)) != 0) + goto err_register; + + netdev_link_up(netdev); + + return 0; + +err_settings: + unregister_netdev(netdev); +err_register: + netdev_nullify(netdev); + netdev_put(netdev); + return rc; +} + +/** Remove the device */ +static void af_packet_nic_remove ( struct linux_device *device ) +{ + struct net_device *netdev = linux_get_drvdata(device); + unregister_netdev(netdev); + netdev_nullify(netdev); + netdev_put(netdev); +} + +/** AF_PACKET linux_driver */ +struct linux_driver af_packet_nic_driver __linux_driver = { + .name = "af_packet", + .probe = af_packet_nic_probe, + .remove = af_packet_nic_remove, + .can_probe = 1, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index d0b93d02f..1a037b10e 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -194,6 +194,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_pciea ( ERRFILE_DRIVER | 0x00c00000 ) #define ERRFILE_axge ( ERRFILE_DRIVER | 0x00c10000 ) #define ERRFILE_thunderx ( ERRFILE_DRIVER | 0x00c20000 ) +#define ERRFILE_af_packet ( ERRFILE_DRIVER | 0x00c30000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) diff --git a/src/include/linux_api.h b/src/include/linux_api.h index 28a3cda3b..fe9fa910f 100644 --- a/src/include/linux_api.h +++ b/src/include/linux_api.h @@ -46,6 +46,8 @@ typedef __kernel_loff_t loff_t; #include typedef unsigned long nfds_t; typedef uint32_t useconds_t; +typedef uint32_t socklen_t; +struct sockaddr; #define MAP_FAILED ( ( void * ) -1 ) #define SEEK_SET 0 @@ -68,6 +70,11 @@ extern void * linux_mmap ( void *addr, __kernel_size_t length, int prot, extern void * linux_mremap ( void *old_address, __kernel_size_t old_size, __kernel_size_t new_size, int flags ); extern int linux_munmap ( void *addr, __kernel_size_t length ); +extern int linux_socket ( int domain, int type_, int protocol ); +extern int linux_bind ( int fd, const struct sockaddr *addr, + socklen_t addrlen ); +extern ssize_t linux_sendto ( int fd, const void *buf, size_t len, int flags, + const struct sockaddr *daddr, socklen_t addrlen ); extern const char * linux_strerror ( int errnum ); From 0dc4814ca83b8b7ca226c4a9c4bdcc050af2882b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2017 13:23:31 +0000 Subject: [PATCH 333/591] [virtio] Use separate RX and TX empty header buffers Some host implementations (notably Google Compute Platform) are known to unconditionally write back VIRTIO_NET_HDR_F_DATA_VALID to header->flags for received packets, regardless of the features negotiated by the driver. This breaks the transmit datapath by effectively setting an illegal flag for all subsequent transmitted packets. Work around this problem by using separate empty header buffers for the receive and transmit queues. Debugged-by: Ladi Prosek Signed-off-by: Michael Brown --- src/drivers/net/virtio-net.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index eaf6ed382..40367b89d 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -104,8 +104,8 @@ struct virtnet_nic { /** Pending rx packet count */ unsigned int rx_num_iobufs; - /** Virtio net packet header, we only need one */ - struct virtio_net_hdr_modern empty_header; + /** Virtio net dummy packet headers */ + struct virtio_net_hdr_modern empty_header[QUEUE_NB]; }; /** Add an iobuf to a virtqueue @@ -120,19 +120,24 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, int vq_idx, struct io_buffer *iobuf ) { struct virtnet_nic *virtnet = netdev->priv; struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx]; + struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx]; unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0; unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2; - size_t header_len = virtnet->virtio_version - ? sizeof ( virtnet->empty_header ) - : sizeof ( virtnet->empty_header.legacy ); + size_t header_len = ( virtnet->virtio_version ? + sizeof ( *header ) : sizeof ( header->legacy ) ); struct vring_list list[] = { { /* Share a single zeroed virtio net header between all - * rx and tx packets. This works because this driver + * packets in a ring. This works because this driver * does not use any advanced features so none of the * header fields get used. + * + * Some host implementations (notably Google Compute + * Platform) are known to unconditionally write back + * to header->flags for received packets. Work around + * this by using separate RX and TX headers. */ - .addr = ( char* ) &virtnet->empty_header, + .addr = ( char* ) header, .length = header_len, }, { From de85336abb7861e4ea4df2e296eb33d179c7c9bd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2017 14:41:22 +0000 Subject: [PATCH 334/591] [cloud] Add ability to retrieve Google Compute Engine metadata For some unspecified "security" reason, the Google Compute Engine metadata server will refuse any requests that do not include the non-standard HTTP header "Metadata-Flavor: Google". Attempt to autodetect such requests (by comparing the hostname against "metadata.google.internal"), and add the "Metadata-Flavor: Google" header if applicable. Enable this feature in the CONFIG=cloud build, and include a sample embedded script allowing iPXE to boot from a script configured as metadata via e.g. # Create shared boot image make bin/ipxe.usb CONFIG=cloud EMBED=config/cloud/gce.ipxe # Configure per-instance boot script gcloud compute instances add-metadata \ --metadata-from-file ipxeboot=boot.ipxe Signed-off-by: Michael Brown --- src/config/cloud/gce.ipxe | 7 ++++ src/config/cloud/general.h | 4 +++ src/config/config_http.c | 3 ++ src/config/general.h | 1 + src/net/tcp/httpgce.c | 72 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 src/config/cloud/gce.ipxe create mode 100644 src/net/tcp/httpgce.c diff --git a/src/config/cloud/gce.ipxe b/src/config/cloud/gce.ipxe new file mode 100644 index 000000000..95330d718 --- /dev/null +++ b/src/config/cloud/gce.ipxe @@ -0,0 +1,7 @@ +#!ipxe + +echo Google Compute Engine - iPXE boot via metadata +ifstat || +dhcp || +route || +chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot diff --git a/src/config/cloud/general.h b/src/config/cloud/general.h index e69de29bb..99028c147 100644 --- a/src/config/cloud/general.h +++ b/src/config/cloud/general.h @@ -0,0 +1,4 @@ +/* Allow retrieval of metadata (such as an iPXE boot script) from + * Google Compute Engine metadata server. + */ +#define HTTP_HACK_GCE diff --git a/src/config/config_http.c b/src/config/config_http.c index 3f198d228..3c0e78028 100644 --- a/src/config/config_http.c +++ b/src/config/config_http.c @@ -43,3 +43,6 @@ REQUIRE_OBJECT ( httpdigest ); #ifdef HTTP_ENC_PEERDIST REQUIRE_OBJECT ( peerdist ); #endif +#ifdef HTTP_HACK_GCE +REQUIRE_OBJECT ( httpgce ); +#endif diff --git a/src/config/general.h b/src/config/general.h index be0845f68..fb1ac93f4 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -78,6 +78,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define HTTP_AUTH_BASIC /* Basic authentication */ #define HTTP_AUTH_DIGEST /* Digest authentication */ //#define HTTP_ENC_PEERDIST /* PeerDist content encoding */ +//#define HTTP_HACK_GCE /* Google Compute Engine hacks */ /* * 802.11 cryptosystems and handshaking protocols diff --git a/src/net/tcp/httpgce.c b/src/net/tcp/httpgce.c new file mode 100644 index 000000000..c5d879028 --- /dev/null +++ b/src/net/tcp/httpgce.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 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 + * + * Google Compute Engine (GCE) metadata retrieval + * + * For some unspecified "security" reason, the Google Compute Engine + * metadata server will refuse any requests that do not include the + * non-standard HTTP header "Metadata-Flavor: Google". + */ + +#include +#include +#include + +/** Metadata host name + * + * This is used to identify metadata requests, in the absence of any + * more robust mechanism. + */ +#define GCE_METADATA_HOST_NAME "metadata.google.internal" + +/** + * Construct HTTP "Metadata-Flavor" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_metadata_flavor ( struct http_transaction *http, + char *buf, size_t len ) { + + /* Do nothing unless this appears to be a Google Compute + * Engine metadata request. + */ + if ( strcasecmp ( http->request.host, GCE_METADATA_HOST_NAME ) != 0 ) + return 0; + + /* Construct host URI */ + return snprintf ( buf, len, "Google" ); +} + +/** HTTP "Metadata-Flavor" header */ +struct http_request_header http_request_metadata_flavor __http_request_header ={ + .name = "Metadata-Flavor", + .format = http_format_metadata_flavor, +}; From 4e85b2708fa0c8854c722302ff9bf00ecdbcccc8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2017 16:32:54 +0000 Subject: [PATCH 335/591] [virtio] Use host-specified MTU when available Signed-off-by: Michael Brown --- src/drivers/net/virtio-net.c | 40 ++++++++++++++++++++++++------------ src/drivers/net/virtio-net.h | 10 +++++++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 40367b89d..4151532e6 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -77,13 +77,8 @@ enum { QUEUE_NB }; -enum { - /** Max number of pending rx packets */ - NUM_RX_BUF = 8, - - /** Max Ethernet frame length, including FCS and VLAN tag */ - RX_BUF_SIZE = 1522, -}; +/** Max number of pending rx packets */ +#define NUM_RX_BUF 8 struct virtnet_nic { /** Base pio register address */ @@ -160,12 +155,13 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, */ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { struct virtnet_nic *virtnet = netdev->priv; + size_t len = ( netdev->max_pkt_len + 4 /* VLAN */ ); while ( virtnet->rx_num_iobufs < NUM_RX_BUF ) { struct io_buffer *iobuf; /* Try to allocate a buffer, stop for now if out of memory */ - iobuf = alloc_iob ( RX_BUF_SIZE ); + iobuf = alloc_iob ( len ); if ( ! iobuf ) break; @@ -173,7 +169,7 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { list_add ( &iobuf->list, &virtnet->rx_iobufs ); /* Mark packet length until we know the actual size */ - iob_put ( iobuf, RX_BUF_SIZE ); + iob_put ( iobuf, len ); virtnet_enqueue_iob ( netdev, RX_INDEX, iobuf ); virtnet->rx_num_iobufs++; @@ -237,7 +233,8 @@ static int virtnet_open_legacy ( struct net_device *netdev ) { /* Driver is ready */ features = vp_get_features ( ioaddr ); - vp_set_features ( ioaddr, features & ( 1 << VIRTIO_NET_F_MAC ) ); + vp_set_features ( ioaddr, features & ( ( 1 << VIRTIO_NET_F_MAC ) | + ( 1 << VIRTIO_NET_F_MTU ) ) ); vp_set_status ( ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK ); return 0; } @@ -260,6 +257,7 @@ static int virtnet_open_modern ( struct net_device *netdev ) { } vpm_set_features ( &virtnet->vdev, features & ( ( 1ULL << VIRTIO_NET_F_MAC ) | + ( 1ULL << VIRTIO_NET_F_MTU ) | ( 1ULL << VIRTIO_F_VERSION_1 ) | ( 1ULL << VIRTIO_F_ANY_LAYOUT ) ) ); vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FEATURES_OK ); @@ -389,7 +387,7 @@ static void virtnet_process_rx_packets ( struct net_device *netdev ) { virtnet->rx_num_iobufs--; /* Update iobuf length */ - iob_unput ( iobuf, RX_BUF_SIZE ); + iob_unput ( iobuf, iob_len ( iobuf ) ); iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) ); DBGC2 ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n", @@ -461,6 +459,7 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { struct net_device *netdev; struct virtnet_nic *virtnet; u32 features; + u16 mtu; int rc; /* Allocate and hook up net device */ @@ -480,7 +479,7 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { adjust_pci_device ( pci ); vp_reset ( ioaddr ); - /* Load MAC address */ + /* Load MAC address and MTU */ features = vp_get_features ( ioaddr ); if ( features & ( 1 << VIRTIO_NET_F_MAC ) ) { vp_get ( ioaddr, offsetof ( struct virtio_net_config, mac ), @@ -488,6 +487,12 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet, eth_ntoa ( netdev->hw_addr ) ); } + if ( features & ( 1ULL << VIRTIO_NET_F_MTU ) ) { + vp_get ( ioaddr, offsetof ( struct virtio_net_config, mtu ), + &mtu, sizeof ( mtu ) ); + DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet, mtu ); + netdev->max_pkt_len = ( mtu + ETH_HLEN ); + } /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) @@ -517,6 +522,7 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { struct net_device *netdev; struct virtnet_nic *virtnet; u64 features; + u16 mtu; int rc, common, isr, notify, config, device; common = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_COMMON_CFG ); @@ -583,7 +589,7 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE ); vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER ); - /* Load MAC address */ + /* Load MAC address and MTU */ if ( device ) { features = vpm_get_features ( &virtnet->vdev ); if ( features & ( 1ULL << VIRTIO_NET_F_MAC ) ) { @@ -593,6 +599,14 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet, eth_ntoa ( netdev->hw_addr ) ); } + if ( features & ( 1ULL << VIRTIO_NET_F_MTU ) ) { + vpm_get ( &virtnet->vdev, + offsetof ( struct virtio_net_config, mtu ), + &mtu, sizeof ( mtu ) ); + DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet, + mtu ); + netdev->max_pkt_len = ( mtu + ETH_HLEN ); + } } /* We need a valid MAC address */ diff --git a/src/drivers/net/virtio-net.h b/src/drivers/net/virtio-net.h index c2b4a17cc..ff58d3ef1 100644 --- a/src/drivers/net/virtio-net.h +++ b/src/drivers/net/virtio-net.h @@ -4,6 +4,7 @@ /* The feature bitmap for virtio net */ #define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ #define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MTU 3 /* Initial MTU advice */ #define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ #define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ #define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ @@ -25,6 +26,15 @@ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ u8 mac[6]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + u16 status; + /* Maximum number of each of transmit and receive queues; + * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. + * Legal values are between 1 and 0x8000 + */ + u16 max_virtqueue_pairs; + /* Default maximum transmit unit advice */ + u16 mtu; } __attribute__((packed)); /* This is the first element of the scatter-gather list. If you don't From 16aed6e5ce808615df95c7767b4dd8793c398d93 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2017 17:47:28 +0000 Subject: [PATCH 336/591] [netdevice] Allow MTU to be changed at runtime Provide a settings applicator to modify netdev->max_pkt_len in response to changes to the "mtu" setting (DHCP option 26). Note that as with MAC address changes, drivers are permitted to completely ignore any changes in the MTU value. The net result will be that iPXE effectively uses the smaller of either the hardware default MTU or the software configured MTU. Signed-off-by: Michael Brown --- src/include/ipxe/dhcp.h | 3 ++ src/net/netdev_settings.c | 61 +++++++++++++++++++++++++++++++++++++++ src/net/udp/dhcp.c | 6 ++-- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index b699b31f0..23f16aaa7 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -83,6 +83,9 @@ struct dhcp_packet; /** Root path */ #define DHCP_ROOT_PATH 17 +/** Maximum transmission unit */ +#define DHCP_MTU 26 + /** Vendor encapsulated options */ #define DHCP_VENDOR_ENCAP 43 diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 7d893a126..67a45bede 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -70,6 +70,12 @@ const struct setting ifname_setting __setting ( SETTING_NETDEV, ifname ) = { .description = "Interface name", .type = &setting_type_string, }; +const struct setting mtu_setting __setting ( SETTING_NETDEV, mtu ) = { + .name = "mtu", + .description = "MTU", + .type = &setting_type_int16, + .tag = DHCP_MTU, +}; /** * Store MAC address setting @@ -377,3 +383,58 @@ static void netdev_redirect_settings_init ( void ) { struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = { .initialise = netdev_redirect_settings_init, }; + +/** + * Apply network device settings + * + * @ret rc Return status code + */ +static int apply_netdev_settings ( void ) { + struct net_device *netdev; + struct settings *settings; + struct ll_protocol *ll_protocol; + size_t old_max_pkt_len; + size_t mtu; + int rc; + + /* Process settings for each network device */ + for_each_netdev ( netdev ) { + + /* Get network device settings */ + settings = netdev_settings ( netdev ); + + /* Get MTU */ + mtu = fetch_uintz_setting ( settings, &mtu_setting ); + + /* Do nothing unless MTU is specified */ + if ( ! mtu ) + continue; + + /* Update maximum packet length */ + ll_protocol = netdev->ll_protocol; + old_max_pkt_len = netdev->max_pkt_len; + netdev->max_pkt_len = ( mtu + ll_protocol->ll_header_len ); + if ( netdev->max_pkt_len != old_max_pkt_len ) { + DBGC ( netdev, "NETDEV %s MTU is %zd\n", + netdev->name, mtu ); + } + + /* Close and reopen network device if MTU has increased */ + if ( netdev_is_open ( netdev ) && + ( netdev->max_pkt_len > old_max_pkt_len ) ) { + netdev_close ( netdev ); + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + DBGC ( netdev, "NETDEV %s could not reopen: " + "%s\n", netdev->name, strerror ( rc ) ); + return rc; + } + } + } + + return 0; +} + +/** Network device settings applicator */ +struct settings_applicator netdev_applicator __settings_applicator = { + .apply = apply_netdev_settings, +}; diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index b9c1fd90c..95f80821e 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -91,9 +91,9 @@ static uint8_t dhcp_request_options_data[] = { DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, - DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, - DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, - DHCP_DOMAIN_SEARCH, + DHCP_ROOT_PATH, DHCP_MTU, DHCP_VENDOR_ENCAP, + DHCP_VENDOR_CLASS_ID, DHCP_TFTP_SERVER_NAME, + DHCP_BOOTFILE_NAME, DHCP_DOMAIN_SEARCH, 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */ DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), DHCP_END From 90fc2f273a32793e0d0413d7adbf7bc43b995914 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 24 Jan 2017 13:47:03 +0000 Subject: [PATCH 337/591] [cloud] Show CPU vendor and model in example cloud boot scripts Some problems arise only when running on a specific CPU type (e.g. non-functional timer interrupts as observed in Azure AMD instances). Include the CPU vendor and model within the sample cloud boot scripts, to assist in debugging such problems. Signed-off-by: Michael Brown --- src/config/cloud/aws.ipxe | 1 + src/config/cloud/gce.ipxe | 1 + src/config/cloud/settings.h | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/src/config/cloud/aws.ipxe b/src/config/cloud/aws.ipxe index d857d71df..2c96e3888 100644 --- a/src/config/cloud/aws.ipxe +++ b/src/config/cloud/aws.ipxe @@ -1,6 +1,7 @@ #!ipxe echo Amazon EC2 - iPXE boot via user-data +echo CPU: ${cpuvendor} ${cpumodel} ifstat || dhcp || route || diff --git a/src/config/cloud/gce.ipxe b/src/config/cloud/gce.ipxe index 95330d718..88e12b56b 100644 --- a/src/config/cloud/gce.ipxe +++ b/src/config/cloud/gce.ipxe @@ -1,6 +1,7 @@ #!ipxe echo Google Compute Engine - iPXE boot via metadata +echo CPU: ${cpuvendor} ${cpumodel} ifstat || dhcp || route || diff --git a/src/config/cloud/settings.h b/src/config/cloud/settings.h index e69de29bb..34deeb070 100644 --- a/src/config/cloud/settings.h +++ b/src/config/cloud/settings.h @@ -0,0 +1,4 @@ +/* It can often be useful to know the CPU on which a cloud instance is + * running (e.g. to isolate problems with Azure AMD instances). + */ +#define CPUID_SETTINGS From 321af68b72ef92a90f30a83f32fb1093d05c6135 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 24 Jan 2017 15:03:10 +0000 Subject: [PATCH 338/591] [hyperv] Ignore unsolicited VMBus messages In some high-end Azure instances (e.g. NC6) we may receive an unsolicited VMBUS_OFFER_CHANNEL message for a PCIe pass-through device some time after completing the bus enumeration. This currently causes apparently random failures due to unexpected VMBus message types. Fix by ignoring any unsolicited VMBus messages. Signed-off-by: Michael Brown --- src/interface/hyperv/vmbus.c | 82 +++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index e269aee29..286572ce0 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -90,12 +90,12 @@ static int vmbus_post_empty_message ( struct hv_hypervisor *hv, } /** - * Wait for received message + * Wait for received message of any type * * @v hv Hyper-V hypervisor * @ret rc Return status code */ -static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) { +static int vmbus_wait_for_any_message ( struct hv_hypervisor *hv ) { struct vmbus *vmbus = hv->vmbus; int rc; @@ -116,6 +116,38 @@ static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) { return 0; } +/** + * Wait for received message of a specified type, ignoring any others + * + * @v hv Hyper-V hypervisor + * @v type Message type + * @ret rc Return status code + */ +static int vmbus_wait_for_message ( struct hv_hypervisor *hv, + unsigned int type ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + int rc; + + /* Loop until specified message arrives, or until an error occurs */ + while ( 1 ) { + + /* Wait for message */ + if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 ) + return rc; + + /* Check for requested message type */ + if ( header->type == cpu_to_le32 ( type ) ) + return 0; + + /* Ignore any other messages (e.g. due to additional + * channels being offered at runtime). + */ + DBGC ( vmbus, "VMBUS %p ignoring message type %d (expecting " + "%d)\n", vmbus, le32_to_cpu ( header->type ), type ); + } +} + /** * Initiate contact * @@ -144,15 +176,10 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv, return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_VERSION_RESPONSE ) ) !=0) return rc; /* Check response */ - if ( version->header.type != cpu_to_le32 ( VMBUS_VERSION_RESPONSE ) ) { - DBGC ( vmbus, "VMBUS %p unexpected version response type %d\n", - vmbus, le32_to_cpu ( version->header.type ) ); - return -EPROTO; - } if ( ! version->supported ) { DBGC ( vmbus, "VMBUS %p requested version not supported\n", vmbus ); @@ -178,8 +205,6 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv, * @ret rc Return status code */ static int vmbus_unload ( struct hv_hypervisor *hv ) { - struct vmbus *vmbus = hv->vmbus; - const struct vmbus_message_header *header = &vmbus->message->header; int rc; /* Post message */ @@ -187,16 +212,9 @@ static int vmbus_unload ( struct hv_hypervisor *hv ) { return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_UNLOAD_RESPONSE ) ) != 0) return rc; - /* Check response */ - if ( header->type != cpu_to_le32 ( VMBUS_UNLOAD_RESPONSE ) ) { - DBGC ( vmbus, "VMBUS %p unexpected unload response type %d\n", - vmbus, le32_to_cpu ( header->type ) ); - return -EPROTO; - } - return 0; } @@ -290,15 +308,10 @@ int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_GPADL_CREATED ) ) != 0 ) return rc; /* Check response */ - if ( created->header.type != cpu_to_le32 ( VMBUS_GPADL_CREATED ) ) { - DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", - vmdev->dev.name, le32_to_cpu ( created->header.type ) ); - return -EPROTO; - } if ( created->channel != cpu_to_le32 ( vmdev->channel ) ) { DBGC ( vmdev, "VMBUS %s unexpected GPADL channel %d\n", vmdev->dev.name, le32_to_cpu ( created->channel ) ); @@ -346,15 +359,10 @@ int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) { return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_GPADL_TORNDOWN ) ) != 0 ) return rc; /* Check response */ - if ( torndown->header.type != cpu_to_le32 ( VMBUS_GPADL_TORNDOWN ) ) { - DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", - vmdev->dev.name, le32_to_cpu ( torndown->header.type ) ); - return -EPROTO; - } if ( torndown->gpadl != cpu_to_le32 ( gpadl ) ) { DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n", vmdev->dev.name, le32_to_cpu ( torndown->gpadl ) ); @@ -443,15 +451,11 @@ int vmbus_open ( struct vmbus_device *vmdev, return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, + VMBUS_OPEN_CHANNEL_RESULT ) ) != 0) return rc; /* Check response */ - if ( opened->header.type != cpu_to_le32 ( VMBUS_OPEN_CHANNEL_RESULT ) ){ - DBGC ( vmdev, "VMBUS %s unexpected open response type %d\n", - vmdev->dev.name, le32_to_cpu ( opened->header.type ) ); - return -EPROTO; - } if ( opened->channel != cpu_to_le32 ( vmdev->channel ) ) { DBGC ( vmdev, "VMBUS %s unexpected opened channel %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->channel ) ); @@ -1136,8 +1140,8 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, while ( 1 ) { /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) - goto err_wait_for_message; + if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 ) + goto err_wait_for_any_message; /* Handle response */ if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) { @@ -1223,7 +1227,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, } err_unexpected_offer: err_alloc_vmdev: - err_wait_for_message: + err_wait_for_any_message: /* Free any devices allocated (but potentially not yet probed) */ list_for_each_entry_safe ( vmdev, tmp, &parent->children, dev.siblings ) { From bd6255c7be152ea3451dcd6f4175e250bf755f67 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 07:32:38 +0000 Subject: [PATCH 339/591] [pic8259] Fix definitions for "read IRR" and "read ISR" commands Signed-off-by: Michael Brown --- src/arch/x86/include/pic8259.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/include/pic8259.h b/src/arch/x86/include/pic8259.h index f02e62909..dbec5fd2c 100644 --- a/src/arch/x86/include/pic8259.h +++ b/src/arch/x86/include/pic8259.h @@ -37,8 +37,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Register command values */ #define OCW3_ID 0x08 -#define OCW3_READ_IRR 0x03 -#define OCW3_READ_ISR 0x02 +#define OCW3_READ_IRR 0x02 +#define OCW3_READ_ISR 0x03 #define ICR_EOI_NON_SPECIFIC 0x20 #define ICR_EOI_NOP 0x40 #define ICR_EOI_SPECIFIC 0x60 From 941c53a3bf0f0c1c276996e2a68b0fa6263ce410 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 08:00:50 +0000 Subject: [PATCH 340/591] [efi] Fix building elf2efi.c when -fpic is enabled by default The x86_64 EDK2 headers include a #pragma to mark all subsequent symbol declarations and references as hidden if position-independent code is being generated. Since libgen.h is currently included only after the EDK2 headers, this results in __xpg_basename() being erroneously marked as having hidden visibility (if the compiler defaults to building position-independent code); this eventually results in a failure to link the elf2efi binary. Fix by including libgen.h prior to including the EDK2 headers. Originally-fixed-by: Doug Goldstein Signed-off-by: Michael Brown --- src/util/elf2efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 152bf5333..27f37d98a 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -32,9 +32,9 @@ #include #include #include +#include #include #include -#include #define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) From 8ef4e7c572d6cf4feb6578d253a0268bc603ad16 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 10:11:26 +0000 Subject: [PATCH 341/591] [interface] Avoid unnecessary reference counting in intf_unplug() The null interface does not have a reference counter, so the call to intf_get() is always redundant. Signed-off-by: Michael Brown --- src/core/interface.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/interface.c b/src/core/interface.c index 948fa5c54..faaa931f2 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -113,7 +113,10 @@ void intf_plug_plug ( struct interface *a, struct interface *b ) { * @v intf Object interface */ void intf_unplug ( struct interface *intf ) { - intf_plug ( intf, &null_intf ); + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " unplug\n", + INTF_INTF_DBG ( intf, intf->dest ) ); + intf_put ( intf->dest ); + intf->dest = &null_intf; } /** From 5ff13830ec5e2a5f0e8d82d780c264b73faf1935 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 10:17:48 +0000 Subject: [PATCH 342/591] [interface] Remove misleading comment Signed-off-by: Michael Brown --- src/core/interface.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/interface.c b/src/core/interface.c index faaa931f2..a700d2e4d 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -79,9 +79,6 @@ struct interface null_intf = INTF_INIT ( null_intf_desc ); * The reference to the existing destination interface is dropped, a * reference to the new destination interface is obtained, and the * interface is updated to point to the new destination interface. - * - * Note that there is no "unplug" call; instead you must plug the - * interface into a null interface. */ void intf_plug ( struct interface *intf, struct interface *dest ) { DBGC ( INTF_COL ( intf ), From f450c75dad04061f2d51401088f156e1226804ac Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 10:19:02 +0000 Subject: [PATCH 343/591] [interface] Unplug interface before calling intf_close() in intf_shutdown() The call to intf_close() may result in the original interface being reopened. For example: when reading the capacity of a 2TB+ disk via iSCSI, the SCSI layer will respond to the intf_close() from the READ CAPACITY (10) command by immediately issuing a READ CAPACITY (16) command. The iSCSI layer happens to reuse the same interface for the new command (since it allows only a single concurrent command). Currently, intf_shutdown() unplugs the interface after the call to intf_close() returns. In the above scenario, this results in unplugging the just-reopened interface. Fix by transferring the interface destination (and its reference) to a temporary interface, and so effectively performing the unplug before making the call to intf_close(). Signed-off-by: Michael Brown --- src/core/interface.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/interface.c b/src/core/interface.c index a700d2e4d..f7802be07 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -271,6 +271,7 @@ void intf_close ( struct interface *intf, int rc ) { * unplugs the interface. */ void intf_shutdown ( struct interface *intf, int rc ) { + struct interface tmp; DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " shutting down (%s)\n", INTF_DBG ( intf ), strerror ( rc ) ); @@ -278,11 +279,15 @@ void intf_shutdown ( struct interface *intf, int rc ) { /* Block further operations */ intf_nullify ( intf ); - /* Notify destination of close */ - intf_close ( intf, rc ); + /* Transfer destination to temporary interface */ + tmp.dest = intf->dest; + intf->dest = &null_intf; - /* Unplug interface */ - intf_unplug ( intf ); + /* Notify destination of close via temporary interface */ + intf_close ( &tmp, rc ); + + /* Unplug temporary interface */ + intf_unplug ( &tmp ); } /** From 70fc25ad6e71a99b5802eb92b95c26407acbe990 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 14:48:24 +0000 Subject: [PATCH 344/591] [netdevice] Limit MTU by hardware maximum frame length Separate out the concept of "hardware maximum supported frame length" and "configured link MTU", and limit the latter according to the former. In networks where the DHCP-supplied link MTU is inconsistent with the hardware or driver capabilities (e.g. a network using jumbo frames), this will result in iPXE advertising a TCP MSS consistent with a size that can actually be received. Note that the term "MTU" is typically used to refer to the maximum length excluding the link-layer headers; we adopt this usage. Signed-off-by: Michael Brown --- src/include/ipxe/netdevice.h | 9 ++++++++- src/net/infiniband/xsigo.c | 1 + src/net/netdev_settings.c | 22 +++++++++++++++------- src/net/netdevice.c | 6 ++++++ src/net/tcpip.c | 3 +-- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index a1d207ffc..d498ab697 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -397,9 +397,16 @@ struct net_device { struct retry_timer link_block; /** Maximum packet length * - * This length includes any link-layer headers. + * This is the maximum packet length (including any link-layer + * headers) supported by the hardware. */ size_t max_pkt_len; + /** Maximum transmission unit length + * + * This is the maximum transmission unit length (excluding any + * link-layer headers) configured for the link. + */ + size_t mtu; /** TX packet queue */ struct list_head tx_queue; /** Deferred TX packet queue */ diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c index 91b7b71f1..0ee753c38 100644 --- a/src/net/infiniband/xsigo.c +++ b/src/net/infiniband/xsigo.c @@ -323,6 +323,7 @@ static int xve_update_mtu ( struct xsigo_nic *xve, struct eoib_device *eoib, * not the EoIB header. */ netdev->max_pkt_len = ( mtu + sizeof ( struct ethhdr ) ); + netdev->mtu = mtu; DBGC ( xve, "XVE %s has MTU %zd\n", xve->name, mtu ); return 0; diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 67a45bede..c54288d4f 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -393,7 +393,8 @@ static int apply_netdev_settings ( void ) { struct net_device *netdev; struct settings *settings; struct ll_protocol *ll_protocol; - size_t old_max_pkt_len; + size_t max_mtu; + size_t old_mtu; size_t mtu; int rc; @@ -410,18 +411,25 @@ static int apply_netdev_settings ( void ) { if ( ! mtu ) continue; - /* Update maximum packet length */ + /* Limit MTU to maximum supported by hardware */ ll_protocol = netdev->ll_protocol; - old_max_pkt_len = netdev->max_pkt_len; - netdev->max_pkt_len = ( mtu + ll_protocol->ll_header_len ); - if ( netdev->max_pkt_len != old_max_pkt_len ) { + max_mtu = ( netdev->max_pkt_len - ll_protocol->ll_header_len ); + if ( mtu > max_mtu ) { + DBGC ( netdev, "NETDEV %s cannot support MTU %zd (max " + "%zd)\n", netdev->name, mtu, max_mtu ); + mtu = max_mtu; + } + + /* Update maximum packet length */ + old_mtu = netdev->mtu; + netdev->mtu = mtu; + if ( mtu != old_mtu ) { DBGC ( netdev, "NETDEV %s MTU is %zd\n", netdev->name, mtu ); } /* Close and reopen network device if MTU has increased */ - if ( netdev_is_open ( netdev ) && - ( netdev->max_pkt_len > old_max_pkt_len ) ) { + if ( netdev_is_open ( netdev ) && ( mtu > old_mtu ) ) { netdev_close ( netdev ); if ( ( rc = netdev_open ( netdev ) ) != 0 ) { DBGC ( netdev, "NETDEV %s could not reopen: " diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 9df21196c..41ece77f0 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -663,6 +663,12 @@ int register_netdev ( struct net_device *netdev ) { ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr ); } + /* Set MTU, if not already set */ + if ( ! netdev->mtu ) { + netdev->mtu = ( netdev->max_pkt_len - + ll_protocol->ll_header_len ); + } + /* Reject network devices that are already available via a * different hardware device. */ diff --git a/src/net/tcpip.c b/src/net/tcpip.c index c9e4ee789..cc7d02005 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -144,8 +144,7 @@ size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ) { return 0; /* Calculate MTU */ - mtu = ( netdev->max_pkt_len - netdev->ll_protocol->ll_header_len - - tcpip_net->header_len ); + mtu = ( netdev->mtu - tcpip_net->header_len ); return mtu; } From d37e025b81cb6fd0e7833b927ed138b42a628bc8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 20:57:18 +0000 Subject: [PATCH 345/591] [cpuid] Provide cpuid_supported() to test for supported functions Signed-off-by: Michael Brown --- src/arch/x86/core/cpuid.c | 90 +++++++++++++++++++---------- src/arch/x86/core/cpuid_settings.c | 35 ++--------- src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86/include/ipxe/cpuid.h | 8 +-- 4 files changed, 72 insertions(+), 62 deletions(-) diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c index bc5a6c68c..864c1035d 100644 --- a/src/arch/x86/core/cpuid.c +++ b/src/arch/x86/core/cpuid.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include /** @file @@ -32,15 +33,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** Colour for debug messages */ +#define colour 0x861d + /** * Check whether or not CPUID instruction is supported * - * @ret is_supported CPUID instruction is supported + * @ret rc Return status code */ -int cpuid_is_supported ( void ) { +static int cpuid_instruction_supported ( void ) { unsigned long original; unsigned long inverted; + /* Check for instruction existence via flag modifiability */ __asm__ ( "pushf\n\t" "pushf\n\t" "pop %0\n\t" @@ -53,7 +58,54 @@ int cpuid_is_supported ( void ) { "popf\n\t" : "=&r" ( original ), "=&r" ( inverted ) : "ir" ( CPUID_FLAG ) ); - return ( ( original ^ inverted ) & CPUID_FLAG ); + if ( ! ( ( original ^ inverted ) & CPUID_FLAG ) ) { + DBGC ( colour, "CPUID instruction is not supported\n" ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Check whether or not CPUID function is supported + * + * @v function CPUID function + * @ret rc Return status code + */ +int cpuid_supported ( uint32_t function ) { + uint32_t max_function; + uint32_t discard_b; + uint32_t discard_c; + uint32_t discard_d; + int rc; + + /* Check that CPUID instruction is available */ + if ( ( rc = cpuid_instruction_supported() ) != 0 ) + return rc; + + /* Find highest supported function number within this family */ + cpuid ( ( function & CPUID_EXTENDED ), &max_function, &discard_b, + &discard_c, &discard_d ); + + /* Fail if maximum function number is meaningless (e.g. if we + * are attempting to call an extended function on a CPU which + * does not support them). + */ + if ( ( max_function & CPUID_AMD_CHECK_MASK ) != + ( function & CPUID_AMD_CHECK_MASK ) ) { + DBGC ( colour, "CPUID invalid maximum function %#08x\n", + max_function ); + return -EINVAL; + } + + /* Fail if this function is not supported */ + if ( function > max_function ) { + DBGC ( colour, "CPUID function %#08x not supported\n", + function ); + return -ENOTTY; + } + + return 0; } /** @@ -62,18 +114,13 @@ int cpuid_is_supported ( void ) { * @v features x86 CPU features to fill in */ static void x86_intel_features ( struct x86_features *features ) { - uint32_t max_level; uint32_t discard_a; uint32_t discard_b; - uint32_t discard_c; - uint32_t discard_d; + int rc; /* Check that features are available via CPUID */ - cpuid ( CPUID_VENDOR_ID, &max_level, &discard_b, &discard_c, - &discard_d ); - if ( max_level < CPUID_FEATURES ) { - DBGC ( features, "CPUID has no Intel-defined features (max " - "level %08x)\n", max_level ); + if ( ( rc = cpuid_supported ( CPUID_FEATURES ) ) != 0 ) { + DBGC ( features, "CPUID has no Intel-defined features\n" ); return; } @@ -91,22 +138,13 @@ static void x86_intel_features ( struct x86_features *features ) { * @v features x86 CPU features to fill in */ static void x86_amd_features ( struct x86_features *features ) { - uint32_t max_level; uint32_t discard_a; uint32_t discard_b; - uint32_t discard_c; - uint32_t discard_d; + int rc; /* Check that features are available via CPUID */ - cpuid ( CPUID_AMD_MAX_FN, &max_level, &discard_b, &discard_c, - &discard_d ); - if ( ( max_level & CPUID_AMD_CHECK_MASK ) != CPUID_AMD_CHECK ) { - DBGC ( features, "CPUID has no extended functions\n" ); - return; - } - if ( max_level < CPUID_AMD_FEATURES ) { - DBGC ( features, "CPUID has no AMD-defined features (max " - "level %08x)\n", max_level ); + if ( ( rc = cpuid_supported ( CPUID_AMD_FEATURES ) ) != 0 ) { + DBGC ( features, "CPUID has no AMD-defined features\n" ); return; } @@ -127,12 +165,6 @@ void x86_features ( struct x86_features *features ) { /* Clear all features */ memset ( features, 0, sizeof ( *features ) ); - /* Check that CPUID instruction is available */ - if ( ! cpuid_is_supported() ) { - DBGC ( features, "CPUID instruction is not supported\n" ); - return; - } - /* Get Intel-defined features */ x86_intel_features ( features ); diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c index 08bd3918a..306dbefb8 100644 --- a/src/arch/x86/core/cpuid_settings.c +++ b/src/arch/x86/core/cpuid_settings.c @@ -149,48 +149,25 @@ static int cpuid_settings_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { uint32_t function; - uint32_t max_function; uint32_t num_functions; uint32_t registers; uint32_t num_registers; uint32_t buf[4]; uint32_t output; - uint32_t discard_b; - uint32_t discard_c; - uint32_t discard_d; size_t frag_len; size_t result_len = 0; - - /* Fail unless CPUID is supported */ - if ( ! cpuid_is_supported() ) { - DBGC ( settings, "CPUID not supported\n" ); - return -ENOTSUP; - } - - /* Find highest supported function number within this set */ - function = CPUID_FUNCTION ( setting->tag ); - cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b, - &discard_c, &discard_d ); - - /* Fail if maximum function number is meaningless (e.g. if we - * are attempting to call an extended function on a CPU which - * does not support them). - */ - if ( ( max_function & CPUID_AMD_CHECK_MASK ) != - ( function & CPUID_AMD_CHECK_MASK ) ) { - DBGC ( settings, "CPUID invalid maximum function\n" ); - return -ENOTSUP; - } + int rc; /* Call each function in turn */ + function = CPUID_FUNCTION ( setting->tag ); num_functions = CPUID_NUM_FUNCTIONS ( setting->tag ); for ( ; num_functions-- ; function++ ) { /* Fail if this function is not supported */ - if ( function > max_function ) { - DBGC ( settings, "CPUID function %#08x not supported\n", - function ); - return -ENOTSUP; + if ( ( rc = cpuid_supported ( function ) ) != 0 ) { + DBGC ( settings, "CPUID function %#08x not supported: " + "%s\n", function, strerror ( rc ) ); + return rc; } /* Issue CPUID */ diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index f4816e62a..105cdf5d2 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 ) #define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 ) #define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 ) +#define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index da85d0b88..2e2cc7c1a 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -60,22 +60,22 @@ struct x86_features { /** * Issue CPUID instruction * - * @v operation CPUID operation + * @v function CPUID function * @v eax Output via %eax * @v ebx Output via %ebx * @v ecx Output via %ecx * @v edx Output via %edx */ static inline __attribute__ (( always_inline )) void -cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, +cpuid ( uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx ) { __asm__ ( "cpuid" : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) - : "0" ( operation ) ); + : "0" ( function ) ); } -extern int cpuid_is_supported ( void ); +extern int cpuid_supported ( uint32_t function ); extern void x86_features ( struct x86_features *features ); #endif /* _IPXE_CPUID_H */ From 302f1eeb80706fb10067efedb1279fa3f85ddda2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 25 Jan 2017 20:59:15 +0000 Subject: [PATCH 346/591] [time] Allow timer to be selected at runtime Allow the active timer (providing udelay() and currticks()) to be selected at runtime based on probing during the INIT_EARLY stage of initialisation. TICKS_PER_SEC is now a fixed compile-time constant for all builds, and is independent of the underlying clock tick rate. We choose the value 1024 to allow multiplications and divisions on seconds to be converted to bit shifts. TICKS_PER_MS is defined as 1, allowing multiplications and divisions on milliseconds to be omitted entirely. The 2% inaccuracy in this definition is negligible when using the standard BIOS timer (running at around 18.2Hz). TIMER_RDTSC now checks for a constant TSC before claiming to be a usable timer. (This timer can be tested in KVM via the command-line option "-cpu host,+invtsc".) Signed-off-by: Michael Brown --- src/arch/arm/include/bits/timer.h | 12 -- src/arch/x86/core/rdtsc_timer.c | 159 ++++++++++++++++----- src/arch/x86/include/bios.h | 2 + src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86/include/bits/timer.h | 15 -- src/arch/x86/include/ipxe/bios_timer.h | 44 ------ src/arch/x86/include/ipxe/cpuid.h | 6 + src/arch/x86/include/ipxe/rdtsc_timer.h | 39 ----- src/arch/x86/interface/pcbios/bios_timer.c | 37 +++-- src/config/config_timer.c | 48 +++++++ src/core/parseopt.c | 2 +- src/core/timer.c | 62 +++++++- src/drivers/net/ath/ath5k/ath5k.c | 2 +- src/drivers/net/forcedeth.c | 2 +- src/hci/commands/time_cmd.c | 2 +- src/include/ipxe/efi/efi_timer.h | 36 ----- src/include/ipxe/linux/linux_timer.h | 18 --- src/include/ipxe/timer.h | 128 +++++++++-------- src/include/unistd.h | 12 +- src/interface/efi/efi_timer.c | 22 ++- src/interface/linux/linux_timer.c | 26 ++-- src/net/80211/net80211.c | 8 +- src/net/fcoe.c | 2 +- src/net/stp.c | 2 +- 24 files changed, 369 insertions(+), 318 deletions(-) delete mode 100644 src/arch/arm/include/bits/timer.h delete mode 100644 src/arch/x86/include/bits/timer.h delete mode 100644 src/arch/x86/include/ipxe/bios_timer.h delete mode 100644 src/arch/x86/include/ipxe/rdtsc_timer.h create mode 100644 src/config/config_timer.c delete mode 100644 src/include/ipxe/efi/efi_timer.h delete mode 100644 src/include/ipxe/linux/linux_timer.h diff --git a/src/arch/arm/include/bits/timer.h b/src/arch/arm/include/bits/timer.h deleted file mode 100644 index 64e7d31df..000000000 --- a/src/arch/arm/include/bits/timer.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_TIMER_H -#define _BITS_TIMER_H - -/** @file - * - * ARM-specific timer API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_TIMER_H */ diff --git a/src/arch/x86/core/rdtsc_timer.c b/src/arch/x86/core/rdtsc_timer.c index e720a239c..ed5151503 100644 --- a/src/arch/x86/core/rdtsc_timer.c +++ b/src/arch/x86/core/rdtsc_timer.c @@ -29,16 +29,70 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#include +#include +#include #include +#include #include -/** - * Number of TSC ticks per microsecond +/** Number of microseconds to use for TSC calibration */ +#define TSC_CALIBRATE_US 1024 + +/** TSC increment per microsecond */ +static unsigned long tsc_per_us; + +/** Minimum resolution for scaled TSC timer */ +#define TSC_SCALED_HZ 32 + +/** TSC scale (expressed as a bit shift) * - * This is calibrated on the first use of the timer. + * We use this to avoid the need for 64-bit divsion on 32-bit systems. */ -static unsigned long rdtsc_ticks_per_usec; +static unsigned int tsc_scale; + +/** Number of timer ticks per scaled TSC increment */ +static unsigned long ticks_per_scaled_tsc; + +/** Colour for debug messages */ +#define colour &tsc_per_us + +/** + * Get raw TSC value + * + * @ret tsc Raw TSC value + */ +static inline __always_inline unsigned long rdtsc_raw ( void ) { + unsigned long raw; + + __asm__ __volatile__ ( "rdtsc\n\t" : "=a" ( raw ) : : "edx" ); + return raw; +} + +/** + * Get TSC value, shifted to avoid rollover within a realistic timescale + * + * @ret tsc Scaled TSC value + */ +static inline __always_inline unsigned long rdtsc_scaled ( void ) { + unsigned long scaled; + + __asm__ __volatile__ ( "rdtsc\n\t" + "shrdl %b1, %%edx, %%eax\n\t" + : "=a" ( scaled ) : "c" ( tsc_scale ) : "edx" ); + return scaled; +} + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long rdtsc_currticks ( void ) { + unsigned long scaled; + + scaled = rdtsc_scaled(); + return ( scaled * ticks_per_scaled_tsc ); +} /** * Delay for a fixed number of microseconds @@ -48,47 +102,76 @@ static unsigned long rdtsc_ticks_per_usec; static void rdtsc_udelay ( unsigned long usecs ) { unsigned long start; unsigned long elapsed; + unsigned long threshold; - /* Sanity guard, since we may divide by this */ - if ( ! usecs ) - usecs = 1; - - start = currticks(); - if ( rdtsc_ticks_per_usec ) { - /* Already calibrated; busy-wait until done */ - do { - elapsed = ( currticks() - start ); - } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); - } else { - /* Not yet calibrated; use 8254 PIT and calibrate - * based on result. - */ - pit8254_udelay ( usecs ); - elapsed = ( currticks() - start ); - rdtsc_ticks_per_usec = ( elapsed / usecs ); - DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " - "(%ld MHz)\n", elapsed, usecs, - ( rdtsc_ticks_per_usec << TSC_SHIFT ) ); - } + start = rdtsc_raw(); + threshold = ( usecs * tsc_per_us ); + do { + elapsed = ( rdtsc_raw() - start ); + } while ( elapsed < threshold ); } /** - * Get number of ticks per second + * Probe RDTSC timer * - * @ret ticks_per_sec Number of ticks per second + * @ret rc Return status code */ -static unsigned long rdtsc_ticks_per_sec ( void ) { +static int rdtsc_probe ( void ) { + unsigned long before; + unsigned long after; + unsigned long elapsed; + uint32_t apm; + uint32_t discard_a; + uint32_t discard_b; + uint32_t discard_c; + int rc; - /* Calibrate timer, if not already done */ - if ( ! rdtsc_ticks_per_usec ) - udelay ( 1 ); + /* Check that TSC is invariant */ + if ( ( rc = cpuid_supported ( CPUID_APM ) ) != 0 ) { + DBGC ( colour, "RDTSC cannot determine APM features: %s\n", + strerror ( rc ) ); + return rc; + } + cpuid ( CPUID_APM, &discard_a, &discard_b, &discard_c, &apm ); + if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) { + DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n", + apm ); + return -ENOTTY; + } - /* Sanity check */ - assert ( rdtsc_ticks_per_usec != 0 ); + /* Calibrate udelay() timer via 8254 PIT */ + before = rdtsc_raw(); + pit8254_udelay ( TSC_CALIBRATE_US ); + after = rdtsc_raw(); + elapsed = ( after - before ); + tsc_per_us = ( elapsed / TSC_CALIBRATE_US ); + if ( ! tsc_per_us ) { + DBGC ( colour, "RDTSC has zero TSC per microsecond\n" ); + return -EIO; + } - return ( rdtsc_ticks_per_usec * 1000 * 1000 ); + /* Calibrate currticks() scaling factor */ + tsc_scale = 31; + ticks_per_scaled_tsc = ( ( 1UL << tsc_scale ) / + ( tsc_per_us * ( 1000000 / TICKS_PER_SEC ) ) ); + while ( ticks_per_scaled_tsc > ( TICKS_PER_SEC / TSC_SCALED_HZ ) ) { + tsc_scale--; + ticks_per_scaled_tsc >>= 1; + } + DBGC ( colour, "RDTSC has %ld tsc per us, %ld ticks per 2^%d tsc\n", + tsc_per_us, ticks_per_scaled_tsc, tsc_scale ); + if ( ! ticks_per_scaled_tsc ) { + DBGC ( colour, "RDTSC has zero ticks per TSC\n" ); + return -EIO; + } + + return 0; } -PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay ); -PROVIDE_TIMER_INLINE ( rdtsc, currticks ); -PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec ); +/** RDTSC timer */ +struct timer rdtsc_timer __timer ( TIMER_PREFERRED ) = { + .name = "rdtsc", + .probe = rdtsc_probe, + .currticks = rdtsc_currticks, + .udelay = rdtsc_udelay, +}; diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index a5a5d887c..14e7acbc7 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -7,6 +7,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_EBDA 0x000e #define BDA_EQUIPMENT_WORD 0x0010 #define BDA_FBMS 0x0013 +#define BDA_TICKS 0x006c +#define BDA_MIDNIGHT 0x0070 #define BDA_REBOOT 0x0072 #define BDA_REBOOT_WARM 0x1234 #define BDA_NUM_DRIVES 0x0075 diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 105cdf5d2..9d1fed7f6 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 ) #define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 ) #define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 ) +#define ERRFILE_rdtsc_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86/include/bits/timer.h b/src/arch/x86/include/bits/timer.h deleted file mode 100644 index b0ff5ee11..000000000 --- a/src/arch/x86/include/bits/timer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _BITS_TIMER_H -#define _BITS_TIMER_H - -/** @file - * - * x86-specific timer API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include - -#endif /* _BITS_TIMER_H */ diff --git a/src/arch/x86/include/ipxe/bios_timer.h b/src/arch/x86/include/ipxe/bios_timer.h deleted file mode 100644 index 6b88a623c..000000000 --- a/src/arch/x86/include/ipxe/bios_timer.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _IPXE_BIOS_TIMER_H -#define _IPXE_BIOS_TIMER_H - -/** @file - * - * BIOS timer - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_PCBIOS -#define TIMER_PREFIX_pcbios -#else -#define TIMER_PREFIX_pcbios __pcbios_ -#endif - -#include - -/** - * Delay for a fixed number of microseconds - * - * @v usecs Number of microseconds for which to delay - */ -static inline __always_inline void -TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) { - /* BIOS timer is not high-resolution enough for udelay(), so - * we use the 8254 Programmable Interval Timer. - */ - pit8254_udelay ( usecs ); -} - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -static inline __always_inline unsigned long -TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) { - /* BIOS timer ticks over at 18.2 ticks per second */ - return 18; -} - -#endif /* _IPXE_BIOS_TIMER_H */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index 2e2cc7c1a..a9df9f0de 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -57,6 +57,12 @@ struct x86_features { /** Get CPU model */ #define CPUID_MODEL 0x80000002UL +/** Get APM information */ +#define CPUID_APM 0x80000007UL + +/** Invariant TSC */ +#define CPUID_APM_EDX_TSC_INVARIANT 0x00000100UL + /** * Issue CPUID instruction * diff --git a/src/arch/x86/include/ipxe/rdtsc_timer.h b/src/arch/x86/include/ipxe/rdtsc_timer.h deleted file mode 100644 index 598f4bb08..000000000 --- a/src/arch/x86/include/ipxe/rdtsc_timer.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _IPXE_RDTSC_TIMER_H -#define _IPXE_RDTSC_TIMER_H - -/** @file - * - * RDTSC timer - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_RDTSC -#define TIMER_PREFIX_rdtsc -#else -#define TIMER_PREFIX_rdtsc __rdtsc_ -#endif - -/** - * RDTSC values can easily overflow an unsigned long. We discard the - * low-order bits in order to obtain sensibly-scaled values. - */ -#define TSC_SHIFT 8 - -/** - * Get current system time in ticks - * - * @ret ticks Current time, in ticks - */ -static inline __always_inline unsigned long -TIMER_INLINE ( rdtsc, currticks ) ( void ) { - unsigned long ticks; - - __asm__ __volatile__ ( "rdtsc\n\t" - "shrdl %1, %%edx, %%eax\n\t" - : "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" ); - return ticks; -} - -#endif /* _IPXE_RDTSC_TIMER_H */ diff --git a/src/arch/x86/interface/pcbios/bios_timer.c b/src/arch/x86/interface/pcbios/bios_timer.c index 3299c9aae..49e1d2265 100644 --- a/src/arch/x86/interface/pcbios/bios_timer.c +++ b/src/arch/x86/interface/pcbios/bios_timer.c @@ -32,6 +32,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include + +/** Number of ticks per day + * + * This seems to be the normative value, as used by e.g. SeaBIOS to + * decide when to set the midnight rollover flag. + */ +#define BIOS_TICKS_PER_DAY 0x1800b0 + +/** Number of ticks per BIOS tick */ +#define TICKS_PER_BIOS_TICK \ + ( ( TICKS_PER_SEC * 60 * 60 * 24 ) / BIOS_TICKS_PER_DAY ) /** * Get current system time in ticks @@ -43,7 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * of calling timeofday BIOS interrupt. */ static unsigned long bios_currticks ( void ) { - static int days = 0; + static uint32_t offset; uint32_t ticks; uint8_t midnight; @@ -53,18 +65,25 @@ static unsigned long bios_currticks ( void ) { "nop\n\t" "cli\n\t" ); - get_real ( ticks, BDA_SEG, 0x006c ); - get_real ( midnight, BDA_SEG, 0x0070 ); + /* Read current BIOS time of day */ + get_real ( ticks, BDA_SEG, BDA_TICKS ); + get_real ( midnight, BDA_SEG, BDA_MIDNIGHT ); + /* Handle midnight rollover */ if ( midnight ) { midnight = 0; - put_real ( midnight, BDA_SEG, 0x0070 ); - days += 0x1800b0; + put_real ( midnight, BDA_SEG, BDA_MIDNIGHT ); + offset += BIOS_TICKS_PER_DAY; } + ticks += offset; - return ( days + ticks ); + /* Convert to timer ticks */ + return ( ticks * TICKS_PER_BIOS_TICK ); } -PROVIDE_TIMER_INLINE ( pcbios, udelay ); -PROVIDE_TIMER ( pcbios, currticks, bios_currticks ); -PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec ); +/** BIOS timer */ +struct timer bios_timer __timer ( TIMER_NORMAL ) = { + .name = "bios", + .currticks = bios_currticks, + .udelay = pit8254_udelay, +}; diff --git a/src/config/config_timer.c b/src/config/config_timer.c new file mode 100644 index 000000000..a462970c2 --- /dev/null +++ b/src/config/config_timer.c @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Timer configuration options + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in timers + */ +#ifdef TIMER_PCBIOS +REQUIRE_OBJECT ( bios_timer ); +#endif +#ifdef TIMER_RDTSC +REQUIRE_OBJECT ( rdtsc_timer ); +#endif +#ifdef TIMER_EFI +REQUIRE_OBJECT ( efi_timer ); +#endif +#ifdef TIMER_LINUX +REQUIRE_OBJECT ( linux_timer ); +#endif diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 66f60158c..3ddf94f3d 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -117,7 +117,7 @@ int parse_timeout ( char *text, unsigned long *value ) { return rc; /* Convert to a number of timer ticks */ - *value = ( ( value_ms * TICKS_PER_SEC ) / 1000 ); + *value = ( value_ms * TICKS_PER_MS ); return 0; } diff --git a/src/core/timer.c b/src/core/timer.c index ca945cfba..ed7246059 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -23,11 +23,39 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include +#include +#include #include #include #include #include +#include +#include + +/** Current timer */ +static struct timer *timer; + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +unsigned long currticks ( void ) { + + assert ( timer != NULL ); + return timer->currticks(); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +void udelay ( unsigned long usecs ) { + + assert ( timer != NULL ); + timer->udelay ( usecs ); +} /** * Delay for a fixed number of milliseconds @@ -61,3 +89,35 @@ unsigned int sleep ( unsigned int secs ) { return 0; } + +/** + * Find a working timer + * + */ +static void timer_probe ( void ) { + int rc; + + /* Use first working timer */ + for_each_table_entry ( timer, TIMERS ) { + if ( ( timer->probe == NULL ) || + ( ( rc = timer->probe() ) == 0 ) ) { + DBGC ( &timer, "TIMER using %s\n", timer->name ); + return; + } + DBGC ( &timer, "TIMER could not initialise %s: %s\n", + timer->name, strerror ( rc ) ); + } + + /* This is a fatal error */ + DBGC ( &timer, "TIMER found no working timers!\n" ); + while ( 1 ) {} +} + +/** Timer initialisation function */ +struct init_fn timer_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = timer_probe, +}; + +/* Drag in timer configuration */ +REQUIRING_SYMBOL ( timer_init_fn ); +REQUIRE_OBJECT ( config_timer ); diff --git a/src/drivers/net/ath/ath5k/ath5k.c b/src/drivers/net/ath/ath5k/ath5k.c index a6a65a2e9..a500175a7 100644 --- a/src/drivers/net/ath/ath5k/ath5k.c +++ b/src/drivers/net/ath/ath5k/ath5k.c @@ -1381,7 +1381,7 @@ ath5k_poll(struct net80211_device *dev) unsigned int counter = 1000; if (currticks() - sc->last_calib_ticks > - ATH5K_CALIB_INTERVAL * ticks_per_sec()) { + ATH5K_CALIB_INTERVAL * TICKS_PER_SEC) { ath5k_calibrate(sc); sc->last_calib_ticks = currticks(); } diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c index 79938cbbb..7f044b192 100644 --- a/src/drivers/net/forcedeth.c +++ b/src/drivers/net/forcedeth.c @@ -1176,7 +1176,7 @@ nv_mgmt_get_version ( struct forcedeth_private *priv ) ioaddr + NvRegTransmitterControl ); start = currticks(); - while ( currticks() > start + 5 * ticks_per_sec() ) { + while ( currticks() > start + 5 * TICKS_PER_SEC ) { data_ready2 = readl ( ioaddr + NvRegTransmitterControl ); if ( ( data_ready & NVREG_XMITCTL_DATA_READY ) != ( data_ready2 & NVREG_XMITCTL_DATA_READY ) ) { diff --git a/src/hci/commands/time_cmd.c b/src/hci/commands/time_cmd.c index d1dd49caf..08148bf38 100644 --- a/src/hci/commands/time_cmd.c +++ b/src/hci/commands/time_cmd.c @@ -68,7 +68,7 @@ static int time_exec ( int argc, char **argv ) { start = currticks(); rc = execv ( argv[1], argv + 1 ); elapsed = ( currticks() - start ); - decisecs = ( 10 * elapsed / ticks_per_sec() ); + decisecs = ( 10 * elapsed / TICKS_PER_SEC ); printf ( "%s: %d.%ds\n", argv[0], ( decisecs / 10 ), ( decisecs % 10 ) ); diff --git a/src/include/ipxe/efi/efi_timer.h b/src/include/ipxe/efi/efi_timer.h deleted file mode 100644 index c49875988..000000000 --- a/src/include/ipxe/efi/efi_timer.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef _IPXE_EFI_TIMER_H -#define _IPXE_EFI_TIMER_H - -/** @file - * - * iPXE timer API for EFI - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_EFI -#define TIMER_PREFIX_efi -#else -#define TIMER_PREFIX_efi __efi_ -#endif - -/** - * Number of ticks per second - * - * This is a policy decision. - */ -#define EFI_TICKS_PER_SEC 20 - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -static inline __attribute__ (( always_inline )) unsigned long -TIMER_INLINE ( efi, ticks_per_sec ) ( void ) { - - return EFI_TICKS_PER_SEC; -} - -#endif /* _IPXE_EFI_TIMER_H */ diff --git a/src/include/ipxe/linux/linux_timer.h b/src/include/ipxe/linux/linux_timer.h deleted file mode 100644 index 7f46e36b2..000000000 --- a/src/include/ipxe/linux/linux_timer.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _IPXE_LINUX_TIMER_H -#define _IPXE_LINUX_TIMER_H - -/** @file - * - * iPXE timer API for Linux - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_LINUX -#define TIMER_PREFIX_linux -#else -#define TIMER_PREFIX_linux __linux_ -#endif - -#endif /* _IPXE_LINUX_TIMER_H */ diff --git a/src/include/ipxe/timer.h b/src/include/ipxe/timer.h index 82fbb6764..e6b95172e 100644 --- a/src/include/ipxe/timer.h +++ b/src/include/ipxe/timer.h @@ -3,75 +3,77 @@ /** @file * - * iPXE timer API + * iPXE timers * - * The timer API provides udelay() for fixed delays, and currticks() - * for a monotonically increasing tick counter. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include -#include - -/** - * Calculate static inline timer API function name - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @ret _subsys_func Subsystem API function - */ -#define TIMER_INLINE( _subsys, _api_func ) \ - SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func ) - -/** - * Provide a timer API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @v _func Implementing function - */ -#define PROVIDE_TIMER( _subsys, _api_func, _func ) \ - PROVIDE_SINGLE_API ( TIMER_PREFIX_ ## _subsys, _api_func, _func ) - -/** - * Provide a static inline timer API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - */ -#define PROVIDE_TIMER_INLINE( _subsys, _api_func ) \ - PROVIDE_SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func ) - -/* Include all architecture-independent I/O API headers */ -#include -#include - -/* Include all architecture-dependent I/O API headers */ -#include - -/** - * Delay for a fixed number of microseconds - * - * @v usecs Number of microseconds for which to delay - */ -void udelay ( unsigned long usecs ); - -/** - * Get current system time in ticks - * - * @ret ticks Current time, in ticks - */ -unsigned long currticks ( void ); - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -unsigned long ticks_per_sec ( void ); +#include /** Number of ticks per second */ -#define TICKS_PER_SEC ( ticks_per_sec() ) +#define TICKS_PER_SEC 1024 + +/** Number of ticks per millisecond + * + * This is (obviously) not 100% consistent with the definition of + * TICKS_PER_SEC, but it allows for multiplications and divisions to + * be elided. In any case, timer ticks are not expected to be a + * precision timing source; for example, the standard BIOS timer is + * based on an 18.2Hz clock. + */ +#define TICKS_PER_MS 1 + +/** A timer */ +struct timer { + /** Name */ + const char *name; + /** + * Probe timer + * + * @ret rc Return status code + */ + int ( * probe ) ( void ); + /** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ + unsigned long ( * currticks ) ( void ); + /** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ + void ( * udelay ) ( unsigned long usecs ); +}; + +/** Timer table */ +#define TIMERS __table ( struct timer, "timers" ) + +/** Declare a timer */ +#define __timer( order ) __table_entry ( TIMERS, order ) + +/** @defgroup timer_order Timer detection order + * + * @{ + */ + +#define TIMER_PREFERRED 01 /**< Preferred timer */ +#define TIMER_NORMAL 02 /**< Normal timer */ + +/** @} */ + +/* + * sleep() prototype is defined by POSIX.1. usleep() prototype is + * defined by 4.3BSD. udelay() and mdelay() prototypes are chosen to + * be reasonably sensible. + * + */ + +extern void udelay ( unsigned long usecs ); +extern void mdelay ( unsigned long msecs ); +extern unsigned long currticks ( void ); +extern unsigned int sleep ( unsigned int seconds ); #endif /* _IPXE_TIMER_H */ diff --git a/src/include/unistd.h b/src/include/unistd.h index d09e1ae30..6c31c0601 100644 --- a/src/include/unistd.h +++ b/src/include/unistd.h @@ -23,19 +23,9 @@ extern int execv ( const char *command, char * const argv[] ); rc; \ } ) -/* Pick up udelay() */ +/* Pick up udelay() and sleep() */ #include -/* - * sleep() prototype is defined by POSIX.1. usleep() prototype is - * defined by 4.3BSD. udelay() and mdelay() prototypes are chosen to - * be reasonably sensible. - * - */ - -extern unsigned int sleep ( unsigned int seconds ); -extern void mdelay ( unsigned long msecs ); - static inline __always_inline void usleep ( unsigned long usecs ) { udelay ( usecs ); } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 1fd9971e9..0ffe2a1a0 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -36,6 +36,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** + * Number of jiffies per second + * + * This is a policy decision. + */ +#define EFI_JIFFIES_PER_SEC 32 + /** Current tick count */ static unsigned long efi_jiffies; @@ -95,7 +102,7 @@ static unsigned long efi_currticks ( void ) { if ( efi_shutdown_in_progress ) efi_jiffies++; - return efi_jiffies; + return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) ); } /** @@ -133,7 +140,7 @@ static void efi_tick_startup ( void ) { /* Start timer tick */ if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic, - ( 10000000 / EFI_TICKS_PER_SEC ) ) ) !=0){ + ( 10000000 / EFI_JIFFIES_PER_SEC ) ))!=0){ rc = -EEFI ( efirc ); DBGC ( colour, "EFI could not start timer tick: %s\n", strerror ( rc ) ); @@ -141,7 +148,7 @@ static void efi_tick_startup ( void ) { return; } DBGC ( colour, "EFI timer started at %d ticks per second\n", - EFI_TICKS_PER_SEC ); + EFI_JIFFIES_PER_SEC ); } /** @@ -180,6 +187,9 @@ struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = { .shutdown = efi_tick_shutdown, }; -PROVIDE_TIMER ( efi, udelay, efi_udelay ); -PROVIDE_TIMER ( efi, currticks, efi_currticks ); -PROVIDE_TIMER_INLINE ( efi, ticks_per_sec ); +/** EFI timer */ +struct timer efi_timer __timer ( TIMER_NORMAL ) = { + .name = "efi", + .currticks = efi_currticks, + .udelay = efi_udelay, +}; diff --git a/src/interface/linux/linux_timer.c b/src/interface/linux/linux_timer.c index 7a994517b..9c5e96f2b 100644 --- a/src/interface/linux/linux_timer.c +++ b/src/interface/linux/linux_timer.c @@ -39,16 +39,6 @@ static void linux_udelay(unsigned long usecs) linux_usleep(usecs); } -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -static unsigned long linux_ticks_per_sec(void) -{ - return 1000; -} - /** * Get current system time in ticks * @@ -67,21 +57,25 @@ static unsigned long linux_currticks(void) { static struct timeval start; static int initialized = 0; + struct timeval now; + unsigned long ticks; if (! initialized) { linux_gettimeofday(&start, NULL); initialized = 1; } - struct timeval now; linux_gettimeofday(&now, NULL); - unsigned long ticks = (now.tv_sec - start.tv_sec) * linux_ticks_per_sec(); - ticks += now.tv_usec / (long)(1000000 / linux_ticks_per_sec()); + ticks = ( ( now.tv_sec - start.tv_sec ) * TICKS_PER_SEC ); + ticks += ( now.tv_usec / ( 1000000 / TICKS_PER_SEC ) ); return ticks; } -PROVIDE_TIMER(linux, udelay, linux_udelay); -PROVIDE_TIMER(linux, currticks, linux_currticks); -PROVIDE_TIMER(linux, ticks_per_sec, linux_ticks_per_sec); +/** Linux timer */ +struct timer linux_timer __timer ( TIMER_NORMAL ) = { + .name = "linux", + .currticks = linux_currticks, + .udelay = linux_udelay, +}; diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c index d4970ad5c..62912741f 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -1321,7 +1321,7 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev, ctx->ticks_start = currticks(); ctx->ticks_beacon = 0; ctx->ticks_channel = currticks(); - ctx->hop_time = ticks_per_sec() / ( active ? 2 : 6 ); + ctx->hop_time = TICKS_PER_SEC / ( active ? 2 : 6 ); /* * Channels on 2.4GHz overlap, and the most commonly used @@ -1363,8 +1363,8 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev, int net80211_probe_step ( struct net80211_probe_ctx *ctx ) { struct net80211_device *dev = ctx->dev; - u32 start_timeout = NET80211_PROBE_TIMEOUT * ticks_per_sec(); - u32 gather_timeout = ticks_per_sec(); + u32 start_timeout = NET80211_PROBE_TIMEOUT * TICKS_PER_SEC; + u32 gather_timeout = TICKS_PER_SEC; u32 now = currticks(); struct io_buffer *iob; int signal; @@ -2606,7 +2606,7 @@ static void net80211_rx_frag ( struct net80211_device *dev, /* start a frag cache entry */ int i, newest = -1; u32 curr_ticks = currticks(), newest_ticks = 0; - u32 timeout = ticks_per_sec() * NET80211_FRAG_TIMEOUT; + u32 timeout = TICKS_PER_SEC * NET80211_FRAG_TIMEOUT; for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) { if ( dev->frags[i].in_use == 0 ) diff --git a/src/net/fcoe.c b/src/net/fcoe.c index c3258f15e..f910eeead 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -1094,7 +1094,7 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { /* Send keepalive */ start_timer_fixed ( &fcoe->timer, - ( ( fcoe->keepalive * TICKS_PER_SEC ) / 1000 ) ); + ( fcoe->keepalive * TICKS_PER_MS ) ); fcoe_fip_tx_keepalive ( fcoe ); /* Abandon FCF if we have not seen its advertisements */ diff --git a/src/net/stp.c b/src/net/stp.c index defdaed9e..3d78400af 100644 --- a/src/net/stp.c +++ b/src/net/stp.c @@ -110,7 +110,7 @@ static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev, "forwarding\n", netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ), stp->flags ); - hello = ( ( ntohs ( stp->hello ) * TICKS_PER_SEC ) / 256 ); + hello = ( ntohs ( stp->hello ) * ( TICKS_PER_SEC / 256 ) ); netdev_link_block ( netdev, ( hello * 2 ) ); rc = -ENETUNREACH; goto done; From f3ba0fb5fdd866961b5dfc0d227af60b25753f0e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Jan 2017 08:03:11 +0000 Subject: [PATCH 347/591] [hyperv] Provide timer based on the 10MHz time reference count MSR When running on AMD platforms, the legacy hardware emulation is extremely unreliable. In particular, the IRQ0 timer interrupt is likely to simply stop working, resulting in a total failure of any code that relies on timers (such as DHCP retransmission attempts). Work around this by using the 10MHz time counter provided by Hyper-V via an MSR. (This timer can be tested in KVM via the command-line option "-cpu host,hv_time".) Signed-off-by: Michael Brown --- src/arch/x86/drivers/hyperv/hyperv.c | 113 ++++++++++++++++++++++++--- src/arch/x86/drivers/hyperv/hyperv.h | 6 ++ 2 files changed, 108 insertions(+), 11 deletions(-) diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index f73829bd5..cc6e38688 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -51,6 +52,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define HV_MESSAGE_MAX_WAIT_MS 1000 +/** Hyper-V timer frequency (fixed 10Mhz) */ +#define HV_TIMER_HZ 10000000 + +/** Hyper-V timer scale factor (used to avoid 64-bit division) */ +#define HV_TIMER_SHIFT 18 + /** * Convert a Hyper-V status code to an iPXE status code * @@ -145,22 +152,19 @@ static void hv_free_message ( struct hv_hypervisor *hv ) { /** * Check whether or not we are running in Hyper-V * - * @v hv Hyper-V hypervisor * @ret rc Return status code */ -static int hv_check_hv ( struct hv_hypervisor *hv ) { +static int hv_check_hv ( void ) { struct x86_features features; uint32_t interface_id; uint32_t discard_ebx; uint32_t discard_ecx; uint32_t discard_edx; - uint32_t available; - uint32_t permissions; /* Check for presence of a hypervisor (not necessarily Hyper-V) */ x86_features ( &features ); if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) { - DBGC ( hv, "HV %p not running in a hypervisor\n", hv ); + DBGC ( HV_INTERFACE_ID, "HV not running in a hypervisor\n" ); return -ENODEV; } @@ -168,11 +172,26 @@ static int hv_check_hv ( struct hv_hypervisor *hv ) { cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx, &discard_ecx, &discard_edx ); if ( interface_id != HV_INTERFACE_ID ) { - DBGC ( hv, "HV %p not running in Hyper-V (interface ID " - "%#08x)\n", hv, interface_id ); + DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface " + "ID %#08x)\n", interface_id ); return -ENODEV; } + return 0; +} + +/** + * Check required features + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_check_features ( struct hv_hypervisor *hv ) { + uint32_t available; + uint32_t permissions; + uint32_t discard_ecx; + uint32_t discard_edx; + /* Check that required features and privileges are available */ cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx, &discard_edx ); @@ -509,6 +528,10 @@ static int hv_probe ( struct root_device *rootdev ) { struct hv_hypervisor *hv; int rc; + /* Check we are running in Hyper-V */ + if ( ( rc = hv_check_hv() ) != 0 ) + goto err_check_hv; + /* Allocate and initialise structure */ hv = zalloc ( sizeof ( *hv ) ); if ( ! hv ) { @@ -516,9 +539,9 @@ static int hv_probe ( struct root_device *rootdev ) { goto err_alloc; } - /* Check we are running in Hyper-V */ - if ( ( rc = hv_check_hv ( hv ) ) != 0 ) - goto err_check_hv; + /* Check features */ + if ( ( rc = hv_check_features ( hv ) ) != 0 ) + goto err_check_features; /* Allocate pages */ if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message, @@ -555,9 +578,10 @@ static int hv_probe ( struct root_device *rootdev ) { hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, NULL ); err_alloc_pages: - err_check_hv: + err_check_features: free ( hv ); err_alloc: + err_check_hv: return rc; } @@ -590,6 +614,73 @@ struct root_device hv_root_device __root_device = { .driver = &hv_root_driver, }; +/** + * Probe timer + * + * @ret rc Return status code + */ +static int hv_timer_probe ( void ) { + uint32_t available; + uint32_t discard_ebx; + uint32_t discard_ecx; + uint32_t discard_edx; + int rc; + + /* Check we are running in Hyper-V */ + if ( ( rc = hv_check_hv() ) != 0 ) + return rc; + + /* Check for available reference counter */ + cpuid ( HV_CPUID_FEATURES, &available, &discard_ebx, &discard_ecx, + &discard_edx ); + if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) { + DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" ); + return -ENODEV; + } + + return 0; +} + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long hv_currticks ( void ) { + + /* Calculate time using a combination of bit shifts and + * multiplication (to avoid a 64-bit division). + */ + return ( ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) >> HV_TIMER_SHIFT ) * + ( TICKS_PER_SEC / ( HV_TIMER_HZ >> HV_TIMER_SHIFT ) ) ); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void hv_udelay ( unsigned long usecs ) { + uint32_t start; + uint32_t elapsed; + uint32_t threshold; + + /* Spin until specified number of 10MHz ticks have elapsed */ + start = rdmsr ( HV_X64_MSR_TIME_REF_COUNT ); + threshold = ( usecs * ( HV_TIMER_HZ / 1000000 ) ); + do { + elapsed = ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) - start ); + } while ( elapsed < threshold ); +} + +/** Hyper-V timer */ +struct timer hv_timer __timer ( TIMER_PREFERRED ) = { + .name = "Hyper-V", + .probe = hv_timer_probe, + .currticks = hv_currticks, + .udelay = hv_udelay, +}; + /* Drag in objects via hv_root_device */ REQUIRING_SYMBOL ( hv_root_device ); diff --git a/src/arch/x86/drivers/hyperv/hyperv.h b/src/arch/x86/drivers/hyperv/hyperv.h index 0d09beb44..08031fc6d 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.h +++ b/src/arch/x86/drivers/hyperv/hyperv.h @@ -21,6 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Get hypervisor features */ #define HV_CPUID_FEATURES 0x40000003UL +/** Time reference counter MSR is available */ +#define HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR 0x00000002UL + /** SynIC MSRs are available */ #define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL @@ -39,6 +42,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Hypercall page MSR */ #define HV_X64_MSR_HYPERCALL 0x40000001UL +/** Time reference MSR */ +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020UL + /** SynIC control MSR */ #define HV_X64_MSR_SCONTROL 0x40000080UL From fcf77515650272b9b51e18c115c66f48d33481b0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Jan 2017 09:31:40 +0000 Subject: [PATCH 348/591] [int13] Avoid potential division by zero Avoid using a zero sector count to guess the disk geometry, since that would result in a division by zero when calculating the number of cylinders. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 6f16904df..8b2e134b5 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -561,6 +561,8 @@ static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch, struct master_boot_record *mbr = scratch; struct partition_table_entry *partition; unsigned int i; + unsigned int end_head; + unsigned int end_sector; int rc; /* Default guess is xx/255/63 */ @@ -586,10 +588,12 @@ static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch, */ for ( i = 0 ; i < 4 ; i++ ) { partition = &mbr->partitions[i]; - if ( ! partition->type ) + end_head = PART_HEAD ( partition->chs_end ); + end_sector = PART_SECTOR ( partition->chs_end ); + if ( ! ( partition->type && end_head && end_sector ) ) continue; - *heads = ( PART_HEAD ( partition->chs_end ) + 1 ); - *sectors = PART_SECTOR ( partition->chs_end ); + *heads = ( end_head + 1 ); + *sectors = end_sector; DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based " "on partition %d\n", int13->drive, *heads, *sectors, ( i + 1 ) ); From f8cf3ceb0bfc3e88ae32702e90b47439be0deee4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Jan 2017 09:45:19 +0000 Subject: [PATCH 349/591] [int13] Test correct return status from INT 13 calls INT 13 calls return a status value via %ah, with CF set if %ah is non-zero (indicating an error). Our wrappers zero the whole of %ax if CF is clear, to allow C code (which has no easy access to CF) to simply test for a non-zero status to detect an error. The current code assigns the returned status to a uint8_t, effectively testing %al rather than %ah. Fix by treating the returned status as a uint16_t instead. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 8b2e134b5..3e03e8c5d 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -1751,7 +1751,7 @@ static void int13_unhook ( unsigned int drive ) { * @ret rc Return status code */ static int int13_load_mbr ( unsigned int drive, struct segoff *address ) { - uint8_t status; + uint16_t status; int discard_b, discard_c, discard_d; uint16_t magic; @@ -1775,7 +1775,7 @@ static int int13_load_mbr ( unsigned int drive, struct segoff *address ) { : "a" ( 0x0201 ), "b" ( *address ), "c" ( 1 ), "d" ( drive ) ); if ( status ) { - DBG ( "INT13 drive %02x could not read MBR (status %02x)\n", + DBG ( "INT13 drive %02x could not read MBR (status %04x)\n", drive, status ); return -EIO; } @@ -1818,7 +1818,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { struct eltorito_validation_entry valid; struct eltorito_boot_entry boot; } __attribute__ (( packed )) catalog; - uint8_t status; + uint16_t status; /* Use INT 13, 4d to read the boot catalog */ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" @@ -1833,7 +1833,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { "S" ( __from_data16 ( &eltorito_cmd ) ) ); if ( status ) { DBG ( "INT13 drive %02x could not read El Torito boot catalog " - "(status %02x)\n", drive, status ); + "(status %04x)\n", drive, status ); return -EIO; } copy_from_user ( &catalog, phys_to_user ( eltorito_cmd.buffer ), 0, @@ -1880,7 +1880,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { "S" ( __from_data16 ( &eltorito_address ) ) ); if ( status ) { DBG ( "INT13 drive %02x could not read El Torito boot image " - "(status %02x)\n", drive, status ); + "(status %04x)\n", drive, status ); return -EIO; } From 0bfe9f53426242e2b932ed51ed694f5e77fead9a Mon Sep 17 00:00:00 2001 From: Christian Nilsson Date: Wed, 21 Dec 2016 20:21:33 +0100 Subject: [PATCH 350/591] [intel] Add INTEL_NO_PHY_RST for I219-LM (2) Originally-implemented-by: Malte zu Klampen Originally-implemented-by: Richard Moore Tested-by: Esben Storgaard Nielsen Signed-off-by: Christian Nilsson Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 4f8a4cbb4..ab6defde1 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1069,7 +1069,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", 0 ), + PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", 0 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), From 41f786cc0a392c238068919709a63985134bb9b1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Jan 2017 11:39:25 +0000 Subject: [PATCH 351/591] [settings] Add "unixtime" builtin setting to expose the current time Expose the current wall-clock time (in seconds since the Epoch), since this is often useful in captured boot logs and can also be useful when checking unexpected X.509 certificate validation failures. Use a :uint32 setting to avoid Y2K38 rollover, thereby ensuring that this will eventually be somebody else's problem. Signed-off-by: Michael Brown --- src/core/settings.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/core/settings.c b/src/core/settings.c index c306054d6..e60a882a7 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -2552,6 +2553,38 @@ struct builtin_setting version_builtin_setting __builtin_setting = { .fetch = version_fetch, }; +/** + * Fetch current time setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int unixtime_fetch ( void *data, size_t len ) { + uint32_t content; + + /* Return current time */ + content = htonl ( time(NULL) ); + if ( len > sizeof ( content ) ) + len = sizeof ( content ); + memcpy ( data, &content, len ); + return sizeof ( content ); +} + +/** Current time setting */ +const struct setting unixtime_setting __setting ( SETTING_MISC, unixtime ) = { + .name = "unixtime", + .description = "Seconds since the Epoch", + .type = &setting_type_uint32, + .scope = &builtin_scope, +}; + +/** Current time built-in setting */ +struct builtin_setting unixtime_builtin_setting __builtin_setting = { + .setting = &unixtime_setting, + .fetch = unixtime_fetch, +}; + /** * Fetch built-in setting * From a8f80a75d25970f1e4e607e761a153a7d57b9a15 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 1 Feb 2017 15:30:41 +0000 Subject: [PATCH 352/591] [time] Report attempts to use timers before initialisation Signed-off-by: Michael Brown --- src/core/timer.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/core/timer.c b/src/core/timer.c index ed7246059..791cdcdbb 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -24,7 +24,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#include #include #include #include @@ -42,7 +41,14 @@ static struct timer *timer; */ unsigned long currticks ( void ) { - assert ( timer != NULL ); + /* Guard against use during early initialisation */ + if ( ! timer ) { + DBGC ( &timer, "TIMER currticks() called before initialisation " + "from %p\n", __builtin_return_address ( 0 ) ); + return 0; + } + + /* Use selected timer */ return timer->currticks(); } @@ -53,7 +59,14 @@ unsigned long currticks ( void ) { */ void udelay ( unsigned long usecs ) { - assert ( timer != NULL ); + /* Guard against use during early initialisation */ + if ( ! timer ) { + DBGC ( &timer, "TIMER udelay() called before initialisation " + "from %p\n", __builtin_return_address ( 0 ) ); + return; + } + + /* Use selected timer */ timer->udelay ( usecs ); } @@ -63,6 +76,15 @@ void udelay ( unsigned long usecs ) { * @v msecs Number of milliseconds for which to delay */ void mdelay ( unsigned long msecs ) { + + /* Guard against use during early initialisation */ + if ( ! timer ) { + DBGC ( &timer, "TIMER mdelay() called before initialisation " + "from %p\n", __builtin_return_address ( 0 ) ); + return; + } + + /* Delay for specified number of milliseconds */ while ( msecs-- ) udelay ( 1000 ); } From 23b788e5cdd7fcfa863a476a1448870ac3cf0103 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 2 Feb 2017 15:49:21 +0000 Subject: [PATCH 353/591] [interface] Provide the ability to shut down multiple interfaces Shutting down (and optionally restarting) multiple interfaces is fraught with problems if there are loops in the interface connectivity (e.g. the HTTP content-decoded and transfer-decoded interfaces, which will generally loop back to each other). Various workarounds currently exist across the codebase, generally involving preceding calls to intf_nullify() to avoid problems due to known loops. Provide intfs_shutdown() and intfs_restart() to allow all of an object's interfaces to be shut down (or restarted) in a single call, without having to worry about potential external loops. Signed-off-by: Michael Brown --- src/core/interface.c | 69 ++++++++++++++++++++++++++++++++++++ src/include/ipxe/interface.h | 5 +++ 2 files changed, 74 insertions(+) diff --git a/src/core/interface.c b/src/core/interface.c index f7802be07..402aa4541 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -290,6 +290,41 @@ void intf_shutdown ( struct interface *intf, int rc ) { intf_unplug ( &tmp ); } +/** + * Shut down multiple object interfaces + * + * @v intfs Object interfaces + * @v rc Reason for close + */ +void intfs_vshutdown ( va_list intfs, int rc ) { + struct interface *intf; + va_list tmp; + + /* Nullify all interfaces to avoid potential loops */ + va_copy ( tmp, intfs ); + while ( ( intf = va_arg ( tmp, struct interface * ) ) ) + intf_nullify ( intf ); + va_end ( tmp ); + + /* Shut down all interfaces */ + while ( ( intf = va_arg ( intfs, struct interface * ) ) ) + intf_shutdown ( intf, rc ); +} + +/** + * Shut down multiple object interfaces + * + * @v rc Reason for close + * @v ... Object interfaces + */ +void intfs_shutdown ( int rc, ... ) { + va_list intfs; + + va_start ( intfs, rc ); + intfs_vshutdown ( intfs, rc ); + va_end ( intfs ); +} + /** * Shut down and restart an object interface * @@ -316,6 +351,40 @@ void intf_restart ( struct interface *intf, int rc ) { intf_reinit ( intf ); } +/** + * Shut down and restart multiple object interfaces + * + * @v intfs Object interfaces + * @v rc Reason for close + */ +void intfs_vrestart ( va_list intfs, int rc ) { + struct interface *intf; + va_list tmp; + + /* Shut down all interfaces */ + va_copy ( tmp, intfs ); + intfs_vshutdown ( tmp, rc ); + va_end ( tmp ); + + /* Reinitialise all interfaces */ + while ( ( intf = va_arg ( intfs, struct interface * ) ) ) + intf_reinit ( intf ); +} + +/** + * Shut down and restart multiple object interfaces + * + * @v rc Reason for close + * @v ... Object interfaces + */ +void intfs_restart ( int rc, ... ) { + va_list intfs; + + va_start ( intfs, rc ); + intfs_vrestart ( intfs, rc ); + va_end ( intfs ); +} + /** * Poke an object interface * diff --git a/src/include/ipxe/interface.h b/src/include/ipxe/interface.h index ebb1b6911..b65002c80 100644 --- a/src/include/ipxe/interface.h +++ b/src/include/ipxe/interface.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include /** An object interface operation */ @@ -148,7 +149,11 @@ extern void intf_close ( struct interface *intf, int rc ); typeof ( void ( object_type, int rc ) ) extern void intf_shutdown ( struct interface *intf, int rc ); +extern void intfs_vshutdown ( va_list intfs, int rc ); +extern void intfs_shutdown ( int rc, ... ) __attribute__ (( sentinel )); extern void intf_restart ( struct interface *intf, int rc ); +extern void intfs_vrestart ( va_list intfs, int rc ); +extern void intfs_restart ( int rc, ... ) __attribute__ (( sentinel )); extern void intf_poke ( struct interface *intf, void ( type ) ( struct interface *intf ) ); From 4a4da573dd8ffabff881ee52c2d1151c15d1730e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 2 Feb 2017 16:52:55 +0000 Subject: [PATCH 354/591] [http] Cleanly shut down potentially looped interfaces Use intfs_shutdown() and intfs_restart() to cleanly shut down multiple interfaces that may loop back to the same object. This fixes a regression introduced by commit daa8ed9 ("[interface] Provide intf_reinit() to reinitialise nullified interfaces") which broke the use of HTTP Basic and Digest authentication. Reported-by: murmansk Reported-by: Brett Waldo Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index 27cc50653..ec527c64d 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -275,15 +275,9 @@ static void http_close ( struct http_transaction *http, int rc ) { /* Stop timer */ stop_timer ( &http->timer ); - /* Close all interfaces, allowing for the fact that the - * content-decoded and transfer-decoded interfaces may be - * connected to the same object. - */ - intf_shutdown ( &http->conn, rc ); - intf_nullify ( &http->transfer ); - intf_shutdown ( &http->content, rc ); - intf_shutdown ( &http->transfer, rc ); - intf_shutdown ( &http->xfer, rc ); + /* Close all interfaces */ + intfs_shutdown ( rc, &http->conn, &http->transfer, &http->content, + &http->xfer, NULL ); } /** @@ -784,12 +778,9 @@ static int http_transfer_complete ( struct http_transaction *http ) { } } - /* Restart content decoding interfaces (which may be attached - * to the same object). - */ - intf_nullify ( &http->transfer ); /* avoid potential loops */ - intf_restart ( &http->content, http->response.rc ); - intf_restart ( &http->transfer, http->response.rc ); + /* Restart content decoding interfaces */ + intfs_restart ( http->response.rc, &http->content, &http->transfer, + NULL ); intf_plug_plug ( &http->transfer, &http->content ); http->len = 0; assert ( http->remaining == 0 ); From ed864feb3aedcde41040d4a5293608ccd2d3361a Mon Sep 17 00:00:00 2001 From: Bartosz Szczepanek Date: Tue, 7 Feb 2017 12:20:18 +0100 Subject: [PATCH 355/591] [thunderx] Fix hardware deinitialization It is required to reset BGX context state for the LMAC using BGX_CMR_CONFIG register. This solves problem with network connectivity in Linux booted from iPXE. Signed-off-by: Bartosz Szczepanek Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/thunderx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c index 306adc459..901ecba11 100644 --- a/src/drivers/net/thunderx.c +++ b/src/drivers/net/thunderx.c @@ -1146,10 +1146,17 @@ static int txnic_lmac_probe ( struct txnic_lmac *lmac ) { * @v lmac Logical MAC */ static void txnic_lmac_remove ( struct txnic_lmac *lmac ) { + uint64_t config; /* Sanity check */ assert ( lmac->vnic != NULL ); + /* Disable packet receive and transmit */ + config = readq ( lmac->regs + BGX_CMR_CONFIG ); + config &= ~( BGX_CMR_CONFIG_DATA_PKT_TX_EN | + BGX_CMR_CONFIG_DATA_PKT_RX_EN ); + writeq ( config, ( lmac->regs + BGX_CMR_CONFIG ) ); + /* Unregister network device */ unregister_netdev ( lmac->vnic->netdev ); From 30f96c9f41f2596493c6ca18060bebaaaf44415b Mon Sep 17 00:00:00 2001 From: Konrad Adamczyk Date: Tue, 7 Feb 2017 12:20:19 +0100 Subject: [PATCH 356/591] [thunderx] Don't disable NIC when exiting from iPXE According to ThunderX Errata G-17560, NIC_PF_CFG[ENA] bit should not be cleared at exit. This allows other drivers to access the NIC regs correctly. Signed-off-by: Konrad Adamczyk Signed-off-by: Michael Brown --- src/drivers/net/thunderx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c index 901ecba11..c9c246cb8 100644 --- a/src/drivers/net/thunderx.c +++ b/src/drivers/net/thunderx.c @@ -1345,9 +1345,6 @@ static void txnic_pf_remove ( struct pci_device *pci ) { /* Remove from list of physical functions */ list_del ( &pf->list ); - /* Disable physical function */ - writeq ( 0, ( pf->regs + TXNIC_PF_CFG ) ); - /* Unmap registers */ iounmap ( pf->regs ); From 0e0e0321a5d1e27e55df98ebe8a2b32b73a7f547 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 6 Mar 2017 15:01:39 +0000 Subject: [PATCH 357/591] [efi] Add missing SANBOOT_PROTO_HTTP to EFI default configuration Signed-off-by: Michael Brown --- src/config/defaults/efi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 2e4c28329..a4f70a1d5 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SANBOOT_PROTO_AOE /* AoE protocol */ #define SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ #define SANBOOT_PROTO_FCP /* Fibre Channel protocol */ +#define SANBOOT_PROTO_HTTP /* HTTP SAN protocol */ #define USB_HCD_XHCI /* xHCI USB host controller */ #define USB_HCD_EHCI /* EHCI USB host controller */ From 530ec8301180de6c094e6f99369450d9a70b91af Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 3 Mar 2017 17:31:33 +0000 Subject: [PATCH 358/591] [block] Remove spurious comments Signed-off-by: Michael Brown --- src/include/ipxe/sanboot.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index c651364cb..1c68a58ca 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -7,13 +7,6 @@ * * The sanboot API provides methods for hooking, unhooking, * describing, and booting from SAN devices. - * - * The standard methods (readl()/writel() etc.) do not strictly check - * the type of the address parameter; this is because traditional - * usage does not necessarily provide the correct pointer type. For - * example, code written for ISA devices at fixed I/O addresses (such - * as the keyboard controller) tend to use plain integer constants for - * the address parameter. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); From 4adc7b029013bf93c0e5f79eddb9231ba2b50292 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 4 Mar 2017 18:43:07 +0000 Subject: [PATCH 359/591] [block] Centralise SAN device abstraction Create a central SAN device abstraction to be shared between BIOS and UEFI. Signed-off-by: Michael Brown --- src/core/sanboot.c | 563 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/sanboot.h | 124 +++++++- 3 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 src/core/sanboot.c diff --git a/src/core/sanboot.c b/src/core/sanboot.c new file mode 100644 index 000000000..da1b68b5f --- /dev/null +++ b/src/core/sanboot.c @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2017 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 + * + * SAN booting + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Timeout for block device commands (in ticks) + * + * Underlying devices should ideally never become totally stuck. + * However, if they do, then the blocking SAN APIs provide no means + * for the caller to cancel the operation, and the machine appears to + * hang. Use an overall timeout for all commands to avoid this + * problem and bounce timeout failures to the caller. + */ +#define SAN_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) + +/** List of SAN devices */ +LIST_HEAD ( san_devices ); + +/** + * Find SAN device by drive number + * + * @v drive Drive number + * @ret sandev SAN device, or NULL + */ +struct san_device * sandev_find ( unsigned int drive ) { + struct san_device *sandev; + + list_for_each_entry ( sandev, &san_devices, list ) { + if ( sandev->drive == drive ) + return sandev; + } + return NULL; +} + +/** + * Free SAN device + * + * @v refcnt Reference count + */ +static void sandev_free ( struct refcnt *refcnt ) { + struct san_device *sandev = + container_of ( refcnt, struct san_device, refcnt ); + + assert ( ! timer_running ( &sandev->timer ) ); + uri_put ( sandev->uri ); + free ( sandev ); +} + +/** + * Close SAN device command + * + * @v sandev SAN device + * @v rc Reason for close + */ +static void sandev_command_close ( struct san_device *sandev, int rc ) { + + /* Stop timer */ + stop_timer ( &sandev->timer ); + + /* Restart interface */ + intf_restart ( &sandev->command, rc ); + + /* Record command status */ + sandev->command_rc = rc; +} + +/** + * Record SAN device capacity + * + * @v sandev SAN device + * @v capacity SAN device capacity + */ +static void sandev_command_capacity ( struct san_device *sandev, + struct block_device_capacity *capacity ) { + + /* Record raw capacity information */ + memcpy ( &sandev->capacity, capacity, sizeof ( sandev->capacity ) ); +} + +/** SAN device command interface operations */ +static struct interface_operation sandev_command_op[] = { + INTF_OP ( intf_close, struct san_device *, sandev_command_close ), + INTF_OP ( block_capacity, struct san_device *, + sandev_command_capacity ), +}; + +/** SAN device command interface descriptor */ +static struct interface_descriptor sandev_command_desc = + INTF_DESC ( struct san_device, command, sandev_command_op ); + +/** + * Handle SAN device command timeout + * + * @v retry Retry timer + */ +static void sandev_command_expired ( struct retry_timer *timer, + int over __unused ) { + struct san_device *sandev = + container_of ( timer, struct san_device, timer ); + + sandev_command_close ( sandev, -ETIMEDOUT ); +} + +/** + * Restart SAN device interface + * + * @v sandev SAN device + * @v rc Reason for restart + */ +static void sandev_restart ( struct san_device *sandev, int rc ) { + + /* Restart block device interface */ + intf_nullify ( &sandev->command ); /* avoid potential loops */ + intf_restart ( &sandev->block, rc ); + + /* Close any outstanding command */ + sandev_command_close ( sandev, rc ); + + /* Record device error */ + sandev->block_rc = rc; +} + +/** + * (Re)open SAN device + * + * @v sandev SAN device + * @ret rc Return status code + * + * This function will block until the device is available. + */ +int sandev_reopen ( struct san_device *sandev ) { + int rc; + + /* Close any outstanding command and restart interface */ + sandev_restart ( sandev, -ECONNRESET ); + + /* Mark device as being not yet open */ + sandev->block_rc = -EINPROGRESS; + + /* Open block device interface */ + if ( ( rc = xfer_open_uri ( &sandev->block, sandev->uri ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x could not (re)open URI: %s\n", + sandev->drive, strerror ( rc ) ); + return rc; + } + + /* Wait for device to become available */ + while ( sandev->block_rc == -EINPROGRESS ) { + step(); + if ( xfer_window ( &sandev->block ) != 0 ) { + sandev->block_rc = 0; + return 0; + } + } + + DBGC ( sandev, "SAN %#02x never became available: %s\n", + sandev->drive, strerror ( sandev->block_rc ) ); + return sandev->block_rc; +} + +/** + * Handle closure of underlying block device interface + * + * @v sandev SAN device + * @ret rc Reason for close + */ +static void sandev_block_close ( struct san_device *sandev, int rc ) { + + /* Any closure is an error from our point of view */ + if ( rc == 0 ) + rc = -ENOTCONN; + DBGC ( sandev, "SAN %#02x went away: %s\n", + sandev->drive, strerror ( rc ) ); + + /* Close any outstanding command and restart interface */ + sandev_restart ( sandev, rc ); +} + +/** + * Check SAN device flow control window + * + * @v sandev SAN device + */ +static size_t sandev_block_window ( struct san_device *sandev __unused ) { + + /* We are never ready to receive data via this interface. + * This prevents objects that support both block and stream + * interfaces from attempting to send us stream data. + */ + return 0; +} + +/** SAN device block interface operations */ +static struct interface_operation sandev_block_op[] = { + INTF_OP ( intf_close, struct san_device *, sandev_block_close ), + INTF_OP ( xfer_window, struct san_device *, sandev_block_window ), +}; + +/** SAN device block interface descriptor */ +static struct interface_descriptor sandev_block_desc = + INTF_DESC ( struct san_device, block, sandev_block_op ); + +/** SAN device read/write command parameters */ +struct san_command_rw_params { + /** SAN device read/write operation */ + int ( * block_rw ) ( struct interface *control, struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ); + /** Data buffer */ + userptr_t buffer; + /** Starting LBA */ + uint64_t lba; + /** Block count */ + unsigned int count; +}; + +/** SAN device command parameters */ +union san_command_params { + /** Read/write command parameters */ + struct san_command_rw_params rw; +}; + +/** + * Initiate SAN device read/write command + * + * @v sandev SAN device + * @v params Command parameters + * @ret rc Return status code + */ +static int sandev_command_rw ( struct san_device *sandev, + const union san_command_params *params ) { + size_t len = ( params->rw.count * sandev->capacity.blksize ); + int rc; + + /* Initiate read/write command */ + if ( ( rc = params->rw.block_rw ( &sandev->block, &sandev->command, + params->rw.lba, params->rw.count, + params->rw.buffer, len ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x could not initiate read/write: " + "%s\n", sandev->drive, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Initiate SAN device read capacity command + * + * @v sandev SAN device + * @v params Command parameters + * @ret rc Return status code + */ +static int +sandev_command_read_capacity ( struct san_device *sandev, + const union san_command_params *params __unused){ + int rc; + + /* Initiate read capacity command */ + if ( ( rc = block_read_capacity ( &sandev->block, + &sandev->command ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x could not initiate read capacity: " + "%s\n", sandev->drive, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Execute a single SAN device command and wait for completion + * + * @v sandev SAN device + * @v command Command + * @v params Command parameters (if required) + * @ret rc Return status code + */ +static int +sandev_command ( struct san_device *sandev, + int ( * command ) ( struct san_device *sandev, + const union san_command_params *params ), + const union san_command_params *params ) { + int rc; + + /* Sanity check */ + assert ( ! timer_running ( &sandev->timer ) ); + + /* Reopen block device if applicable */ + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) { + goto err_reopen; + } + + /* Start expiry timer */ + start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); + + /* Initiate command */ + if ( ( rc = command ( sandev, params ) ) != 0 ) + goto err_op; + + /* Wait for command to complete */ + while ( timer_running ( &sandev->timer ) ) + step(); + + /* Collect return status */ + rc = sandev->command_rc; + + return rc; + + err_op: + stop_timer ( &sandev->timer ); + err_reopen: + return rc; +} + +/** + * Reset SAN device + * + * @v sandev SAN device + * @ret rc Return status code + */ +int sandev_reset ( struct san_device *sandev ) { + int rc; + + DBGC ( sandev, "SAN %#02x reset\n", sandev->drive ); + + /* Close and reopen underlying block device */ + if ( ( rc = sandev_reopen ( sandev ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read from or write to SAN device + * + * @v sandev SAN device + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v block_rw Block read/write method + * @ret rc Return status code + */ +int sandev_rw ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer, + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) ) { + union san_command_params params; + unsigned int remaining; + size_t frag_len; + int rc; + + /* Initialise command parameters */ + params.rw.block_rw = block_rw; + params.rw.buffer = buffer; + params.rw.lba = ( lba << sandev->blksize_shift ); + params.rw.count = sandev->capacity.max_count; + remaining = ( count << sandev->blksize_shift ); + + /* Read/write fragments */ + while ( remaining ) { + + /* Determine fragment length */ + if ( params.rw.count > remaining ) + params.rw.count = remaining; + + /* Execute command */ + if ( ( rc = sandev_command ( sandev, sandev_command_rw, + ¶ms ) ) != 0 ) + return rc; + + /* Move to next fragment */ + frag_len = ( sandev->capacity.blksize * params.rw.count ); + params.rw.buffer = userptr_add ( params.rw.buffer, frag_len ); + params.rw.lba += params.rw.count; + remaining -= params.rw.count; + } + + return 0; +} + +/** + * Configure SAN device as a CD-ROM, if applicable + * + * @v sandev SAN device + * @ret rc Return status code + * + * Both BIOS and UEFI require SAN devices to be accessed with a block + * size of 2048. While we could require the user to configure the + * block size appropriately, this is non-trivial and would impose a + * substantial learning effort on the user. Instead, we check for the + * presence of the ISO9660 primary volume descriptor and, if found, + * then we force a block size of 2048 and map read/write requests + * appropriately. + */ +static int sandev_parse_iso9660 ( struct san_device *sandev ) { + static const struct iso9660_primary_descriptor_fixed primary_check = { + .type = ISO9660_TYPE_PRIMARY, + .id = ISO9660_ID, + }; + struct iso9660_primary_descriptor *primary; + unsigned int blksize; + unsigned int blksize_shift; + unsigned int lba; + unsigned int count; + int rc; + + /* Calculate required blocksize shift for potential CD-ROM access */ + blksize = sandev->capacity.blksize; + blksize_shift = 0; + while ( blksize < ISO9660_BLKSIZE ) { + blksize <<= 1; + blksize_shift++; + } + if ( blksize > ISO9660_BLKSIZE ) { + /* Cannot be a CD-ROM. This is not an error. */ + rc = 0; + goto invalid_blksize; + } + lba = ( ISO9660_PRIMARY_LBA << blksize_shift ); + count = ( 1 << blksize_shift ); + + /* Allocate scratch area */ + primary = malloc ( ISO9660_BLKSIZE ); + if ( ! primary ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Read primary volume descriptor */ + if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( primary ), + block_read ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x could not read ISO9660 primary" + "volume descriptor: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_rw; + } + + /* Configure as CD-ROM if applicable */ + if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) == 0){ + DBGC ( sandev, "SAN %#02x contains an ISO9660 filesystem; " + "treating as CD-ROM\n", sandev->drive ); + sandev->blksize_shift = blksize_shift; + sandev->is_cdrom = 1; + } + + err_rw: + free ( primary ); + err_alloc: + invalid_blksize: + return rc; +} + +/** + * Allocate SAN device + * + * @ret sandev SAN device, or NULL + */ +struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size ) { + struct san_device *sandev; + + /* Allocate and initialise structure */ + sandev = zalloc ( sizeof ( *sandev ) + priv_size ); + if ( ! sandev ) + return NULL; + ref_init ( &sandev->refcnt, sandev_free ); + sandev->uri = uri_get ( uri ); + intf_init ( &sandev->block, &sandev_block_desc, &sandev->refcnt ); + sandev->block_rc = -EINPROGRESS; + intf_init ( &sandev->command, &sandev_command_desc, &sandev->refcnt ); + timer_init ( &sandev->timer, sandev_command_expired, &sandev->refcnt ); + sandev->priv = ( ( ( void * ) sandev ) + sizeof ( *sandev ) ); + + return sandev; +} + +/** + * Register SAN device + * + * @v sandev SAN device + * @ret rc Return status code + */ +int register_sandev ( struct san_device *sandev ) { + int rc; + + /* Check that drive number is not in use */ + if ( sandev_find ( sandev->drive ) != NULL ) { + DBGC ( sandev, "SAN %#02x is already in use\n", sandev->drive ); + return -EADDRINUSE; + } + + /* Read device capacity */ + if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity, + NULL ) ) != 0 ) + return rc; + + /* Configure as a CD-ROM, if applicable */ + if ( ( rc = sandev_parse_iso9660 ( sandev ) ) != 0 ) + return rc; + + /* Add to list of SAN devices */ + list_add_tail ( &sandev->list, &san_devices ); + + return 0; +} + +/** + * Unregister SAN device + * + * @v sandev SAN device + */ +void unregister_sandev ( struct san_device *sandev ) { + + /* Sanity check */ + assert ( ! timer_running ( &sandev->timer ) ); + + /* Shut down interfaces */ + intfs_shutdown ( 0, &sandev->block, &sandev->command, NULL ); + + /* Remove from list of SAN devices */ + list_del ( &sandev->list ); +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 1a037b10e..cd5c1959c 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -72,6 +72,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) #define ERRFILE_pixbuf ( ERRFILE_CORE | 0x00210000 ) #define ERRFILE_efi_block ( ERRFILE_CORE | 0x00220000 ) +#define ERRFILE_sanboot ( ERRFILE_CORE | 0x00230000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 1c68a58ca..420d4dbed 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -12,9 +12,52 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include +#include +#include +#include #include -struct uri; +/** A SAN device */ +struct san_device { + /** Reference count */ + struct refcnt refcnt; + /** List of SAN devices */ + struct list_head list; + + /** SAN device URI */ + struct uri *uri; + /** Drive number */ + unsigned int drive; + + /** Underlying block device interface */ + struct interface block; + /** Current device status */ + int block_rc; + + /** Command interface */ + struct interface command; + /** Command timeout timer */ + struct retry_timer timer; + /** Command status */ + int command_rc; + + /** Raw block device capacity */ + struct block_device_capacity capacity; + /** Block size shift + * + * To allow for emulation of CD-ROM access, this represents + * the left-shift required to translate from exposed logical + * I/O blocks to underlying blocks. + */ + unsigned int blksize_shift; + /** Drive is a CD-ROM */ + int is_cdrom; + + /** Driver private data */ + void *priv; +}; /** * Calculate static inline sanboot API function name @@ -91,4 +134,83 @@ int san_boot ( unsigned int drive ); */ int san_describe ( unsigned int drive ); +extern struct list_head san_devices; + +/** Iterate over all SAN devices */ +#define for_each_sandev( sandev ) \ + list_for_each_entry ( (sandev), &san_devices, list ) + +/** There exist some SAN devices + * + * @ret existence Existence of SAN devices + */ +static inline int have_sandevs ( void ) { + return ( ! list_empty ( &san_devices ) ); +} + +/** + * Get reference to SAN device + * + * @v sandev SAN device + * @ret sandev SAN device + */ +static inline __attribute__ (( always_inline )) struct san_device * +sandev_get ( struct san_device *sandev ) { + ref_get ( &sandev->refcnt ); + return sandev; +} + +/** + * Drop reference to SAN device + * + * @v sandev SAN device + */ +static inline __attribute__ (( always_inline )) void +sandev_put ( struct san_device *sandev ) { + ref_put ( &sandev->refcnt ); +} + +/** + * Calculate SAN device block size + * + * @v sandev SAN device + * @ret blksize Sector size + */ +static inline size_t sandev_blksize ( struct san_device *sandev ) { + return ( sandev->capacity.blksize << sandev->blksize_shift ); +} + +/** + * Calculate SAN device capacity + * + * @v sandev SAN device + * @ret blocks Number of blocks + */ +static inline uint64_t sandev_capacity ( struct san_device *sandev ) { + return ( sandev->capacity.blocks >> sandev->blksize_shift ); +} + +/** + * Check if SAN device needs to be reopened + * + * @v sandev SAN device + * @ret needs_reopen SAN device needs to be reopened + */ +static inline int sandev_needs_reopen ( struct san_device *sandev ) { + return ( sandev->block_rc != 0 ); +} + +extern struct san_device * sandev_find ( unsigned int drive ); +extern int sandev_reopen ( struct san_device *sandev ); +extern int sandev_reset ( struct san_device *sandev ); +extern int sandev_rw ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer, + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) ); +extern struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size ); +extern int register_sandev ( struct san_device *sandev ); +extern void unregister_sandev ( struct san_device *sandev ); + #endif /* _IPXE_SANBOOT_H */ From e7ee2eda4badef6ee361f089768064ae737169ed Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 6 Mar 2017 12:25:20 +0000 Subject: [PATCH 360/591] [block] Centralise "san-drive" setting The concept of the SAN drive number is meaningful only in a BIOS environment, where it represents the INT13 drive number (0x80 for the first hard disk). We retain this concept in a UEFI environment to allow for a simple way for iPXE commands to refer to SAN drives. Centralise the concept of the default drive number, since it is shared between all supported environments. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 28 ------------------- src/core/null_sanboot.c | 1 - src/core/sanboot.c | 39 +++++++++++++++++++++++++++ src/include/ipxe/efi/efi_block.h | 9 ------- src/include/ipxe/null_sanboot.h | 5 ---- src/include/ipxe/sanboot.h | 8 +----- src/interface/efi/efi_block.c | 1 - 7 files changed, 40 insertions(+), 51 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 3e03e8c5d..283e5be7d 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -44,8 +44,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include -#include #include #include #include @@ -1992,32 +1990,6 @@ static int int13_describe ( unsigned int drive ) { return 0; } -/** The "san-drive" setting */ -const struct setting san_drive_setting __setting ( SETTING_SANBOOT_EXTRA, - san-drive ) = { - .name = "san-drive", - .description = "SAN drive number", - .tag = DHCP_EB_SAN_DRIVE, - .type = &setting_type_uint8, -}; - -/** - * Get default SAN drive number - * - * @ret drive Default drive number - */ -static unsigned int int13_default_drive ( void ) { - unsigned long drive; - - /* Use "san-drive" setting, if specified */ - if ( fetch_uint_setting ( NULL, &san_drive_setting, &drive ) >= 0 ) - return drive; - - /* Otherwise, default to booting from first hard disk */ - return 0x80; -} - -PROVIDE_SANBOOT ( pcbios, san_default_drive, int13_default_drive ); PROVIDE_SANBOOT ( pcbios, san_hook, int13_hook ); PROVIDE_SANBOOT ( pcbios, san_unhook, int13_unhook ); PROVIDE_SANBOOT ( pcbios, san_boot, int13_boot ); diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c index 2f7522c6c..31a8a56b0 100644 --- a/src/core/null_sanboot.c +++ b/src/core/null_sanboot.c @@ -43,7 +43,6 @@ static int null_san_describe ( unsigned int drive __unused ) { return -EOPNOTSUPP; } -PROVIDE_SANBOOT_INLINE ( null, san_default_drive ); PROVIDE_SANBOOT ( null, san_hook, null_san_hook ); PROVIDE_SANBOOT ( null, san_unhook, null_san_unhook ); PROVIDE_SANBOOT ( null, san_boot, null_san_boot ); diff --git a/src/core/sanboot.c b/src/core/sanboot.c index da1b68b5f..42a308392 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -39,8 +39,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include +/** + * Default SAN drive number + * + * The drive number is a meaningful concept only in a BIOS + * environment, where it represents the INT13 drive number (0x80 for + * the first hard disk). We retain it in other environments to allow + * for a simple way for iPXE commands to refer to SAN drives. + */ +#define SAN_DEFAULT_DRIVE 0x80 + /** * Timeout for block device commands (in ticks) * @@ -541,6 +553,7 @@ int register_sandev ( struct san_device *sandev ) { /* Add to list of SAN devices */ list_add_tail ( &sandev->list, &san_devices ); + DBGC ( sandev, "SAN %#02x registered\n", sandev->drive ); return 0; } @@ -560,4 +573,30 @@ void unregister_sandev ( struct san_device *sandev ) { /* Remove from list of SAN devices */ list_del ( &sandev->list ); + DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive ); +} + +/** The "san-drive" setting */ +const struct setting san_drive_setting __setting ( SETTING_SANBOOT_EXTRA, + san-drive ) = { + .name = "san-drive", + .description = "SAN drive number", + .tag = DHCP_EB_SAN_DRIVE, + .type = &setting_type_uint8, +}; + +/** + * Get default SAN drive number + * + * @ret drive Default drive number + */ +unsigned int san_default_drive ( void ) { + unsigned long drive; + + /* Use "san-drive" setting, if specified */ + if ( fetch_uint_setting ( NULL, &san_drive_setting, &drive ) >= 0 ) + return drive; + + /* Otherwise, default to booting from first hard disk */ + return SAN_DEFAULT_DRIVE; } diff --git a/src/include/ipxe/efi/efi_block.h b/src/include/ipxe/efi/efi_block.h index ea28230bf..f8cf7fc13 100644 --- a/src/include/ipxe/efi/efi_block.h +++ b/src/include/ipxe/efi/efi_block.h @@ -15,13 +15,4 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SANBOOT_PREFIX_efi __efi_ #endif -static inline __always_inline unsigned int -SANBOOT_INLINE ( efi, san_default_drive ) ( void ) { - /* Drive numbers don't exist as a concept under EFI. We - * arbitarily choose to use drive 0x80 to minimise differences - * with a standard BIOS. - */ - return 0x80; -} - #endif /* _IPXE_EFI_BLOCK_H */ diff --git a/src/include/ipxe/null_sanboot.h b/src/include/ipxe/null_sanboot.h index 58f03339f..b0e36b8b0 100644 --- a/src/include/ipxe/null_sanboot.h +++ b/src/include/ipxe/null_sanboot.h @@ -15,9 +15,4 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SANBOOT_PREFIX_null __null_ #endif -static inline __always_inline unsigned int -SANBOOT_INLINE ( null, san_default_drive ) ( void ) { - return 0; -} - #endif /* _IPXE_NULL_SANBOOT_H */ diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 420d4dbed..3e7ed1c80 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -95,13 +95,6 @@ struct san_device { /* Include all architecture-dependent sanboot API headers */ #include -/** - * Get default SAN drive number - * - * @ret drive Default drive number - */ -unsigned int san_default_drive ( void ); - /** * Hook SAN device * @@ -212,5 +205,6 @@ extern int sandev_rw ( struct san_device *sandev, uint64_t lba, extern struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size ); extern int register_sandev ( struct san_device *sandev ); extern void unregister_sandev ( struct san_device *sandev ); +extern unsigned int san_default_drive ( void ); #endif /* _IPXE_SANBOOT_H */ diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index ab2309431..ee26b37d8 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -1055,7 +1055,6 @@ static int efi_block_boot ( unsigned int drive ) { return rc; } -PROVIDE_SANBOOT_INLINE ( efi, san_default_drive ); PROVIDE_SANBOOT ( efi, san_hook, efi_block_hook ); PROVIDE_SANBOOT ( efi, san_unhook, efi_block_unhook ); PROVIDE_SANBOOT ( efi, san_describe, efi_block_describe ); From e790366c7c74a4e49a29374a5fb97778d5c8a08b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 4 Mar 2017 21:02:31 +0000 Subject: [PATCH 361/591] [int13] Refactor to use centralised SAN device abstraction Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 949 +++++++------------------- 1 file changed, 264 insertions(+), 685 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 283e5be7d..23cfefca9 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -29,20 +29,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include -#include -#include -#include -#include -#include -#include #include #include #include #include -#include #include #include #include @@ -59,31 +51,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** - * Overall timeout for INT 13 commands (independent of underlying device - * - * Underlying devices should ideally never become totally stuck. - * However, if they do, then the INT 13 mechanism provides no means - * for the caller to cancel the operation, and the machine appears to - * hang. Use an overall timeout for all commands to avoid this - * problem and bounce timeout failures to the caller. - */ -#define INT13_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) - -/** An INT 13 emulated drive */ -struct int13_drive { - /** Reference count */ - struct refcnt refcnt; - /** List of all registered drives */ - struct list_head list; - - /** Block device URI */ - struct uri *uri; - /** Underlying block device interface */ - struct interface block; - - /** BIOS in-use drive number (0x00-0xff) */ - unsigned int drive; +/** INT 13 SAN device private data */ +struct int13_data { /** BIOS natural drive number (0x00-0xff) * * This is the drive number that would have been assigned by @@ -96,16 +65,6 @@ struct int13_drive { */ unsigned int natural_drive; - /** Block device capacity */ - struct block_device_capacity capacity; - /** INT 13 emulated blocksize shift - * - * To allow for emulation of CD-ROM access, this represents - * the left-shift required to translate from INT 13 blocks to - * underlying blocks. - */ - unsigned int blksize_shift; - /** Number of cylinders * * The cylinder number field in an INT 13 call is ten bits @@ -131,13 +90,8 @@ struct int13_drive { */ unsigned int sectors_per_track; - /** Drive is a CD-ROM */ - int is_cdrom; /** Address of El Torito boot catalog (if any) */ unsigned int boot_catalog; - - /** Underlying device status, if in error */ - int block_rc; /** Status of last operation */ int last_status; }; @@ -158,9 +112,6 @@ static struct int13_fdd_parameters __data16 ( int13_fdd_params ) = { }; #define int13_fdd_params __use_data16 ( int13_fdd_params ) -/** List of registered emulated drives */ -static LIST_HEAD ( int13s ); - /** * Equipment word * @@ -189,293 +140,37 @@ static uint8_t __text16 ( num_drives ); #define num_drives __use_text16 ( num_drives ) /** - * Calculate INT 13 drive sector size + * Calculate SAN device capacity (limited to 32 bits) * - * @v int13 Emulated drive - * @ret blksize Sector size - */ -static inline size_t int13_blksize ( struct int13_drive *int13 ) { - return ( int13->capacity.blksize << int13->blksize_shift ); -} - -/** - * Calculate INT 13 drive capacity - * - * @v int13 Emulated drive + * @v sandev SAN device * @ret blocks Number of blocks */ -static inline uint64_t int13_capacity ( struct int13_drive *int13 ) { - return ( int13->capacity.blocks >> int13->blksize_shift ); -} - -/** - * Calculate INT 13 drive capacity (limited to 32 bits) - * - * @v int13 Emulated drive - * @ret blocks Number of blocks - */ -static inline uint32_t int13_capacity32 ( struct int13_drive *int13 ) { - uint64_t capacity = int13_capacity ( int13 ); +static inline uint32_t int13_capacity32 ( struct san_device *sandev ) { + uint64_t capacity = sandev_capacity ( sandev ); return ( ( capacity <= 0xffffffffUL ) ? capacity : 0xffffffff ); } /** - * Test if INT 13 drive is a floppy disk drive + * Test if SAN device is a floppy disk drive * - * @v int13 Emulated drive - * @ret is_fdd Emulated drive is a floppy disk + * @v sandev SAN device + * @ret is_fdd SAN device is a floppy disk drive */ -static inline int int13_is_fdd ( struct int13_drive *int13 ) { - return ( ! ( int13->drive & 0x80 ) ); -} - -/** An INT 13 command */ -struct int13_command { - /** Status */ - int rc; - /** INT 13 drive */ - struct int13_drive *int13; - /** Underlying block device interface */ - struct interface block; - /** Command timeout timer */ - struct retry_timer timer; -}; - -/** - * Record INT 13 drive capacity - * - * @v command INT 13 command - * @v capacity Block device capacity - */ -static void int13_command_capacity ( struct int13_command *command, - struct block_device_capacity *capacity ) { - memcpy ( &command->int13->capacity, capacity, - sizeof ( command->int13->capacity ) ); +static inline int int13_is_fdd ( struct san_device *sandev ) { + return ( ! ( sandev->drive & 0x80 ) ); } /** - * Close INT 13 command + * Parse El Torito parameters * - * @v command INT 13 command - * @v rc Reason for close - */ -static void int13_command_close ( struct int13_command *command, int rc ) { - intf_restart ( &command->block, rc ); - stop_timer ( &command->timer ); - command->rc = rc; -} - -/** - * Handle INT 13 command timer expiry - * - * @v timer Timer - */ -static void int13_command_expired ( struct retry_timer *timer, - int over __unused ) { - struct int13_command *command = - container_of ( timer, struct int13_command, timer ); - - int13_command_close ( command, -ETIMEDOUT ); -} - -/** INT 13 command interface operations */ -static struct interface_operation int13_command_op[] = { - INTF_OP ( intf_close, struct int13_command *, int13_command_close ), - INTF_OP ( block_capacity, struct int13_command *, - int13_command_capacity ), -}; - -/** INT 13 command interface descriptor */ -static struct interface_descriptor int13_command_desc = - INTF_DESC ( struct int13_command, block, int13_command_op ); - -/** - * Open (or reopen) INT 13 emulated drive underlying block device - * - * @v int13 Emulated drive - * @ret rc Return status code - */ -static int int13_reopen_block ( struct int13_drive *int13 ) { - int rc; - - /* Close any existing block device */ - intf_restart ( &int13->block, -ECONNRESET ); - - /* Open block device */ - if ( ( rc = xfer_open_uri ( &int13->block, int13->uri ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not reopen block " - "device: %s\n", int13->drive, strerror ( rc ) ); - int13->block_rc = rc; - return rc; - } - - /* Clear block device error status */ - int13->block_rc = 0; - - return 0; -} - -/** - * Prepare to issue INT 13 command - * - * @v command INT 13 command - * @v int13 Emulated drive - * @ret rc Return status code - */ -static int int13_command_start ( struct int13_command *command, - struct int13_drive *int13 ) { - int rc; - - /* Sanity check */ - assert ( command->int13 == NULL ); - assert ( ! timer_running ( &command->timer ) ); - - /* Reopen block device if necessary */ - if ( ( int13->block_rc != 0 ) && - ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) ) - return rc; - - /* Initialise command */ - command->rc = -EINPROGRESS; - command->int13 = int13; - start_timer_fixed ( &command->timer, INT13_COMMAND_TIMEOUT ); - - /* Wait for block control interface to become ready */ - while ( ( command->rc == -EINPROGRESS ) && - ( xfer_window ( &int13->block ) == 0 ) ) { - step(); - } - - return ( ( command->rc == -EINPROGRESS ) ? - int13->block_rc : command->rc ); -} - -/** - * Wait for INT 13 command to complete - * - * @v command INT 13 command - * @ret rc Return status code - */ -static int int13_command_wait ( struct int13_command *command ) { - - /* Sanity check */ - assert ( timer_running ( &command->timer ) ); - - /* Wait for command to complete */ - while ( command->rc == -EINPROGRESS ) - step(); - - assert ( ! timer_running ( &command->timer ) ); - return command->rc; -} - -/** - * Terminate INT 13 command - * - * @v command INT 13 command - */ -static void int13_command_stop ( struct int13_command *command ) { - stop_timer ( &command->timer ); - command->int13 = NULL; -} - -/** The single active INT 13 command */ -static struct int13_command int13_command = { - .block = INTF_INIT ( int13_command_desc ), - .timer = TIMER_INIT ( int13_command_expired ), -}; - -/** - * Read from or write to INT 13 drive - * - * @v int13 Emulated drive - * @v lba Starting logical block address - * @v count Number of logical blocks - * @v buffer Data buffer - * @v block_rw Block read/write method - * @ret rc Return status code - */ -static int int13_rw ( struct int13_drive *int13, uint64_t lba, - unsigned int count, userptr_t buffer, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len ) ) { - struct int13_command *command = &int13_command; - unsigned int frag_count; - size_t frag_len; - int rc; - - /* Translate to underlying blocksize */ - lba <<= int13->blksize_shift; - count <<= int13->blksize_shift; - - while ( count ) { - - /* Determine fragment length */ - frag_count = count; - if ( frag_count > int13->capacity.max_count ) - frag_count = int13->capacity.max_count; - frag_len = ( int13->capacity.blksize * frag_count ); - - /* Issue command */ - if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) || - ( ( rc = block_rw ( &int13->block, &command->block, lba, - frag_count, buffer, - frag_len ) ) != 0 ) || - ( ( rc = int13_command_wait ( command ) ) != 0 ) ) { - int13_command_stop ( command ); - return rc; - } - int13_command_stop ( command ); - - /* Move to next fragment */ - lba += frag_count; - count -= frag_count; - buffer = userptr_add ( buffer, frag_len ); - } - - return 0; -} - -/** - * Read INT 13 drive capacity - * - * @v int13 Emulated drive - * @ret rc Return status code - */ -static int int13_read_capacity ( struct int13_drive *int13 ) { - struct int13_command *command = &int13_command; - int rc; - - /* Issue command */ - if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) || - ( ( rc = block_read_capacity ( &int13->block, - &command->block ) ) != 0 ) || - ( ( rc = int13_command_wait ( command ) ) != 0 ) ) { - int13_command_stop ( command ); - return rc; - } - - int13_command_stop ( command ); - return 0; -} - -/** - * Parse ISO9660 parameters - * - * @v int13 Emulated drive + * @v sandev SAN device * @v scratch Scratch area for single-sector reads * @ret rc Return status code * - * Reads and parses ISO9660 parameters, if present. + * Reads and parses El Torito parameters, if present. */ -static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) { - static const struct iso9660_primary_descriptor_fixed primary_check = { - .type = ISO9660_TYPE_PRIMARY, - .id = ISO9660_ID, - }; - struct iso9660_primary_descriptor *primary = scratch; +static int int13_parse_eltorito ( struct san_device *sandev, void *scratch ) { + struct int13_data *int13 = sandev->priv; static const struct eltorito_descriptor_fixed boot_check = { .type = ISO9660_TYPE_BOOT, .id = ISO9660_ID, @@ -483,69 +178,34 @@ static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) { .system_id = "EL TORITO SPECIFICATION", }; struct eltorito_descriptor *boot = scratch; - unsigned int blksize; - unsigned int blksize_shift; int rc; - /* Calculate required blocksize shift */ - blksize = int13_blksize ( int13 ); - blksize_shift = 0; - while ( blksize < ISO9660_BLKSIZE ) { - blksize <<= 1; - blksize_shift++; - } - if ( blksize > ISO9660_BLKSIZE ) { - /* Do nothing if the blksize is invalid for CD-ROM access */ - return 0; - } - - /* Read primary volume descriptor */ - if ( ( rc = int13_rw ( int13, - ( ISO9660_PRIMARY_LBA << blksize_shift ), 1, - virt_to_user ( primary ), block_read ) ) != 0 ){ - DBGC ( int13, "INT13 drive %02x could not read ISO9660 " - "primary volume descriptor: %s\n", - int13->drive, strerror ( rc ) ); - return rc; - } - - /* Do nothing unless this is an ISO image */ - if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) != 0 ) - return 0; - DBGC ( int13, "INT13 drive %02x contains an ISO9660 filesystem; " - "treating as CD-ROM\n", int13->drive ); - int13->is_cdrom = 1; - /* Read boot record volume descriptor */ - if ( ( rc = int13_rw ( int13, - ( ELTORITO_LBA << blksize_shift ), 1, - virt_to_user ( boot ), block_read ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not read El Torito boot " + if ( ( rc = sandev_rw ( sandev, ELTORITO_LBA, 1, + virt_to_user ( boot ), block_read ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not read El Torito boot " "record volume descriptor: %s\n", - int13->drive, strerror ( rc ) ); + sandev->drive, strerror ( rc ) ); return rc; } /* Check for an El Torito boot catalog */ if ( memcmp ( boot, &boot_check, sizeof ( boot_check ) ) == 0 ) { int13->boot_catalog = boot->sector; - DBGC ( int13, "INT13 drive %02x has an El Torito boot catalog " - "at LBA %08x\n", int13->drive, int13->boot_catalog ); + DBGC ( sandev, "INT13 drive %02x has an El Torito boot catalog " + "at LBA %08x\n", sandev->drive, int13->boot_catalog ); } else { - DBGC ( int13, "INT13 drive %02x has no El Torito boot " - "catalog\n", int13->drive ); + DBGC ( sandev, "INT13 drive %02x has no El Torito boot " + "catalog\n", sandev->drive ); } - /* Configure drive for no-emulation CD-ROM access */ - int13->blksize_shift += blksize_shift; - return 0; } /** * Guess INT 13 hard disk drive geometry * - * @v int13 Emulated drive + * @v sandev SAN device * @v scratch Scratch area for single-sector reads * @ret heads Guessed number of heads * @ret sectors Guessed number of sectors per track @@ -553,7 +213,7 @@ static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) { * * Guesses the drive geometry by inspecting the partition table. */ -static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch, +static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch, unsigned int *heads, unsigned int *sectors ) { struct master_boot_record *mbr = scratch; @@ -568,17 +228,17 @@ static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch, *sectors = 63; /* Read partition table */ - if ( ( rc = int13_rw ( int13, 0, 1, virt_to_user ( mbr ), - block_read ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not read " + if ( ( rc = sandev_rw ( sandev, 0, 1, virt_to_user ( mbr ), + block_read ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not read " "partition table to guess geometry: %s\n", - int13->drive, strerror ( rc ) ); + sandev->drive, strerror ( rc ) ); return rc; } - DBGC2 ( int13, "INT13 drive %02x has MBR:\n", int13->drive ); - DBGC2_HDA ( int13, 0, mbr, sizeof ( *mbr ) ); - DBGC ( int13, "INT13 drive %02x has signature %08x\n", - int13->drive, mbr->signature ); + DBGC2 ( sandev, "INT13 drive %02x has MBR:\n", sandev->drive ); + DBGC2_HDA ( sandev, 0, mbr, sizeof ( *mbr ) ); + DBGC ( sandev, "INT13 drive %02x has signature %08x\n", + sandev->drive, mbr->signature ); /* Scan through partition table and modify guesses for * heads and sectors_per_track if we find any used @@ -592,9 +252,9 @@ static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch, continue; *heads = ( end_head + 1 ); *sectors = end_sector; - DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based " + DBGC ( sandev, "INT13 drive %02x guessing C/H/S xx/%d/%d based " "on partition %d\n", - int13->drive, *heads, *sectors, ( i + 1 ) ); + sandev->drive, *heads, *sectors, ( i + 1 ) ); } return 0; @@ -627,17 +287,17 @@ static const struct int13_fdd_geometry int13_fdd_geometries[] = { /** * Guess INT 13 floppy disk drive geometry * - * @v int13 Emulated drive + * @v sandev SAN device * @ret heads Guessed number of heads * @ret sectors Guessed number of sectors per track * @ret rc Return status code * * Guesses the drive geometry by inspecting the disk size. */ -static int int13_guess_geometry_fdd ( struct int13_drive *int13, +static int int13_guess_geometry_fdd ( struct san_device *sandev, unsigned int *heads, unsigned int *sectors ) { - unsigned int blocks = int13_capacity ( int13 ); + unsigned int blocks = sandev_capacity ( sandev ); const struct int13_fdd_geometry *geometry; unsigned int cylinders; unsigned int i; @@ -650,8 +310,8 @@ static int int13_guess_geometry_fdd ( struct int13_drive *int13, *heads = INT13_FDD_HEADS ( geometry ); *sectors = INT13_FDD_SECTORS ( geometry ); if ( ( cylinders * (*heads) * (*sectors) ) == blocks ) { - DBGC ( int13, "INT13 drive %02x guessing C/H/S " - "%d/%d/%d based on size %dK\n", int13->drive, + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "%d/%d/%d based on size %dK\n", sandev->drive, cylinders, *heads, *sectors, ( blocks / 2 ) ); return 0; } @@ -662,36 +322,33 @@ static int int13_guess_geometry_fdd ( struct int13_drive *int13, */ *heads = 2; *sectors = 18; - DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size " - "%dK\n", int13->drive, *heads, *sectors, ( blocks / 2 ) ); + DBGC ( sandev, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size " + "%dK\n", sandev->drive, *heads, *sectors, ( blocks / 2 ) ); return 0; } /** * Guess INT 13 drive geometry * - * @v int13 Emulated drive + * @v sandev SAN device * @v scratch Scratch area for single-sector reads * @ret rc Return status code */ -static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) { +static int int13_guess_geometry ( struct san_device *sandev, void *scratch ) { + struct int13_data *int13 = sandev->priv; unsigned int guessed_heads; unsigned int guessed_sectors; unsigned int blocks; unsigned int blocks_per_cyl; int rc; - /* Don't even try when the blksize is invalid for C/H/S access */ - if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) - return 0; - /* Guess geometry according to drive type */ - if ( int13_is_fdd ( int13 ) ) { - if ( ( rc = int13_guess_geometry_fdd ( int13, &guessed_heads, + if ( int13_is_fdd ( sandev ) ) { + if ( ( rc = int13_guess_geometry_fdd ( sandev, &guessed_heads, &guessed_sectors )) != 0) return rc; } else { - if ( ( rc = int13_guess_geometry_hdd ( int13, scratch, + if ( ( rc = int13_guess_geometry_hdd ( sandev, scratch, &guessed_heads, &guessed_sectors )) != 0) return rc; @@ -704,7 +361,7 @@ static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) { int13->sectors_per_track = guessed_sectors; if ( ! int13->cylinders ) { /* Avoid attempting a 64-bit divide on a 32-bit system */ - blocks = int13_capacity32 ( int13 ); + blocks = int13_capacity32 ( sandev ); blocks_per_cyl = ( int13->heads * int13->sectors_per_track ); assert ( blocks_per_cyl != 0 ); int13->cylinders = ( blocks / blocks_per_cyl ); @@ -719,7 +376,8 @@ static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) { * Update BIOS drive count */ static void int13_sync_num_drives ( void ) { - struct int13_drive *int13; + struct san_device *sandev; + struct int13_data *int13; uint8_t *counter; uint8_t max_drive; uint8_t required; @@ -730,18 +388,19 @@ static void int13_sync_num_drives ( void ) { num_fdds = ( ( equipment_word & 0x0001 ) ? ( ( ( equipment_word >> 6 ) & 0x3 ) + 1 ) : 0 ); - /* Ensure count is large enough to cover all of our emulated drives */ - list_for_each_entry ( int13, &int13s, list ) { - counter = ( int13_is_fdd ( int13 ) ? &num_fdds : &num_drives ); - max_drive = int13->drive; + /* Ensure count is large enough to cover all of our SAN devices */ + for_each_sandev ( sandev ) { + int13 = sandev->priv; + counter = ( int13_is_fdd ( sandev ) ? &num_fdds : &num_drives ); + max_drive = sandev->drive; if ( max_drive < int13->natural_drive ) max_drive = int13->natural_drive; required = ( ( max_drive & 0x7f ) + 1 ); if ( *counter < required ) { *counter = required; - DBGC ( int13, "INT13 drive %02x added to drive count: " + DBGC ( sandev, "INT13 drive %02x added to drive count: " "%d HDDs, %d FDDs\n", - int13->drive, num_drives, num_fdds ); + sandev->drive, num_drives, num_fdds ); } } @@ -773,21 +432,17 @@ static void int13_check_num_drives ( void ) { /** * INT 13, 00 - Reset disk system * - * @v int13 Emulated drive + * @v sandev SAN device * @ret status Status code */ -static int int13_reset ( struct int13_drive *int13, +static int int13_reset ( struct san_device *sandev, struct i386_all_regs *ix86 __unused ) { int rc; - DBGC2 ( int13, "Reset drive\n" ); + DBGC2 ( sandev, "Reset drive\n" ); - /* Reopen underlying block device */ - if ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) - return -INT13_STATUS_RESET_FAILED; - - /* Check that block device is functional */ - if ( ( rc = int13_read_capacity ( int13 ) ) != 0 ) + /* Reset SAN device */ + if ( ( rc = sandev_reset ( sandev ) ) != 0 ) return -INT13_STATUS_RESET_FAILED; return 0; @@ -796,19 +451,21 @@ static int int13_reset ( struct int13_drive *int13, /** * INT 13, 01 - Get status of last operation * - * @v int13 Emulated drive + * @v sandev SAN device * @ret status Status code */ -static int int13_get_last_status ( struct int13_drive *int13, +static int int13_get_last_status ( struct san_device *sandev, struct i386_all_regs *ix86 __unused ) { - DBGC2 ( int13, "Get status of last operation\n" ); + struct int13_data *int13 = sandev->priv; + + DBGC2 ( sandev, "Get status of last operation\n" ); return int13->last_status; } /** * Read / write sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v al Number of sectors to read or write (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -819,7 +476,7 @@ static int int13_get_last_status ( struct int13_drive *int13, * @ret status Status code * @ret al Number of sectors read or written */ -static int int13_rw_sectors ( struct int13_drive *int13, +static int int13_rw_sectors ( struct san_device *sandev, struct i386_all_regs *ix86, int ( * block_rw ) ( struct interface *control, struct interface *data, @@ -827,6 +484,7 @@ static int int13_rw_sectors ( struct int13_drive *int13, unsigned int count, userptr_t buffer, size_t len ) ) { + struct int13_data *int13 = sandev->priv; unsigned int cylinder, head, sector; unsigned long lba; unsigned int count; @@ -834,10 +492,10 @@ static int int13_rw_sectors ( struct int13_drive *int13, int rc; /* Validate blocksize */ - if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) { - DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) " + if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) { + DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) " "for non-extended read/write\n", - int13->drive, int13_blksize ( int13 ) ); + sandev->drive, sandev_blksize ( sandev ) ); return -INT13_STATUS_INVALID; } @@ -848,7 +506,7 @@ static int int13_rw_sectors ( struct int13_drive *int13, if ( ( cylinder >= int13->cylinders ) || ( head >= int13->heads ) || ( sector < 1 ) || ( sector > int13->sectors_per_track ) ) { - DBGC ( int13, "C/H/S %d/%d/%d out of range for geometry " + DBGC ( sandev, "C/H/S %d/%d/%d out of range for geometry " "%d/%d/%d\n", cylinder, head, sector, int13->cylinders, int13->heads, int13->sectors_per_track ); return -INT13_STATUS_INVALID; @@ -858,14 +516,14 @@ static int int13_rw_sectors ( struct int13_drive *int13, count = ix86->regs.al; buffer = real_to_user ( ix86->segs.es, ix86->regs.bx ); - DBGC2 ( int13, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n", + DBGC2 ( sandev, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n", cylinder, head, sector, lba, ix86->segs.es, ix86->regs.bx, count ); /* Read from / write to block device */ - if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x I/O failed: %s\n", - int13->drive, strerror ( rc ) ); + if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_rw ) ) != 0 ){ + DBGC ( sandev, "INT13 drive %02x I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; } @@ -875,7 +533,7 @@ static int int13_rw_sectors ( struct int13_drive *int13, /** * INT 13, 02 - Read sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v al Number of sectors to read (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -885,16 +543,17 @@ static int int13_rw_sectors ( struct int13_drive *int13, * @ret status Status code * @ret al Number of sectors read */ -static int int13_read_sectors ( struct int13_drive *int13, +static int int13_read_sectors ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Read: " ); - return int13_rw_sectors ( int13, ix86, block_read ); + + DBGC2 ( sandev, "Read: " ); + return int13_rw_sectors ( sandev, ix86, block_read ); } /** * INT 13, 03 - Write sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v al Number of sectors to write (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -904,16 +563,17 @@ static int int13_read_sectors ( struct int13_drive *int13, * @ret status Status code * @ret al Number of sectors written */ -static int int13_write_sectors ( struct int13_drive *int13, +static int int13_write_sectors ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Write: " ); - return int13_rw_sectors ( int13, ix86, block_write ); + + DBGC2 ( sandev, "Write: " ); + return int13_rw_sectors ( sandev, ix86, block_write ); } /** * INT 13, 08 - Get drive parameters * - * @v int13 Emulated drive + * @v sandev SAN device * @ret status Status code * @ret ch Low bits of maximum cylinder number * @ret cl (bits 7:6) High bits of maximum cylinder number @@ -921,19 +581,20 @@ static int int13_write_sectors ( struct int13_drive *int13, * @ret dh Maximum head number * @ret dl Number of drives */ -static int int13_get_parameters ( struct int13_drive *int13, +static int int13_get_parameters ( struct san_device *sandev, struct i386_all_regs *ix86 ) { + struct int13_data *int13 = sandev->priv; unsigned int max_cylinder = int13->cylinders - 1; unsigned int max_head = int13->heads - 1; unsigned int max_sector = int13->sectors_per_track; /* sic */ - DBGC2 ( int13, "Get drive parameters\n" ); + DBGC2 ( sandev, "Get drive parameters\n" ); /* Validate blocksize */ - if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) { - DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) " + if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) { + DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) " "for non-extended parameters\n", - int13->drive, int13_blksize ( int13 ) ); + sandev->drive, sandev_blksize ( sandev ) ); return -INT13_STATUS_INVALID; } @@ -941,10 +602,10 @@ static int int13_get_parameters ( struct int13_drive *int13, ix86->regs.ch = ( max_cylinder & 0xff ); ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector ); ix86->regs.dh = max_head; - ix86->regs.dl = ( int13_is_fdd ( int13 ) ? num_fdds : num_drives ); + ix86->regs.dl = ( int13_is_fdd ( sandev ) ? num_fdds : num_drives ); /* Floppy-specific parameters */ - if ( int13_is_fdd ( int13 ) ) { + if ( int13_is_fdd ( sandev ) ) { ix86->regs.bl = INT13_FDD_TYPE_1M44; ix86->segs.es = rm_ds; ix86->regs.di = __from_data16 ( &int13_fdd_params ); @@ -956,21 +617,21 @@ static int int13_get_parameters ( struct int13_drive *int13, /** * INT 13, 15 - Get disk type * - * @v int13 Emulated drive + * @v sandev SAN device * @ret ah Type code * @ret cx:dx Sector count * @ret status Status code / disk type */ -static int int13_get_disk_type ( struct int13_drive *int13, +static int int13_get_disk_type ( struct san_device *sandev, struct i386_all_regs *ix86 ) { uint32_t blocks; - DBGC2 ( int13, "Get disk type\n" ); + DBGC2 ( sandev, "Get disk type\n" ); - if ( int13_is_fdd ( int13 ) ) { + if ( int13_is_fdd ( sandev ) ) { return INT13_DISK_TYPE_FDD; } else { - blocks = int13_capacity32 ( int13 ); + blocks = int13_capacity32 ( sandev ); ix86->regs.cx = ( blocks >> 16 ); ix86->regs.dx = ( blocks & 0xffff ); return INT13_DISK_TYPE_HDD; @@ -980,16 +641,17 @@ static int int13_get_disk_type ( struct int13_drive *int13, /** * INT 13, 41 - Extensions installation check * - * @v int13 Emulated drive + * @v sandev SAN device * @v bx 0x55aa * @ret bx 0xaa55 * @ret cx Extensions API support bitmap * @ret status Status code / API version */ -static int int13_extension_check ( struct int13_drive *int13 __unused, +static int int13_extension_check ( struct san_device *sandev __unused, struct i386_all_regs *ix86 ) { + if ( ix86->regs.bx == 0x55aa ) { - DBGC2 ( int13, "INT13 extensions installation check\n" ); + DBGC2 ( sandev, "INT13 extensions installation check\n" ); ix86->regs.bx = 0xaa55; ix86->regs.cx = ( INT13_EXTENSION_LINEAR | INT13_EXTENSION_EDD | @@ -1003,12 +665,12 @@ static int int13_extension_check ( struct int13_drive *int13 __unused, /** * Extended read / write * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @v block_rw Block read/write method * @ret status Status code */ -static int int13_extended_rw ( struct int13_drive *int13, +static int int13_extended_rw ( struct san_device *sandev, struct i386_all_regs *ix86, int ( * block_rw ) ( struct interface *control, struct interface *data, @@ -1027,14 +689,14 @@ static int int13_extended_rw ( struct int13_drive *int13, * ELTORITO.SYS seems to assume that we are really a CD-ROM if * we support extended reads for a floppy drive. */ - if ( int13_is_fdd ( int13 ) ) + if ( int13_is_fdd ( sandev ) ) return -INT13_STATUS_INVALID; /* Get buffer size */ get_real ( bufsize, ix86->segs.ds, ( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) ); if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) { - DBGC2 ( int13, "\n", bufsize ); + DBGC2 ( sandev, "\n", bufsize ); return -INT13_STATUS_INVALID; } @@ -1042,17 +704,17 @@ static int int13_extended_rw ( struct int13_drive *int13, memset ( &addr, 0, sizeof ( addr ) ); copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize ); lba = addr.lba; - DBGC2 ( int13, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) ); + DBGC2 ( sandev, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) ); if ( ( addr.count == 0xff ) || ( ( addr.buffer.segment == 0xffff ) && ( addr.buffer.offset == 0xffff ) ) ) { buffer = phys_to_user ( addr.buffer_phys ); - DBGC2 ( int13, "%08llx", + DBGC2 ( sandev, "%08llx", ( ( unsigned long long ) addr.buffer_phys ) ); } else { buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset ); - DBGC2 ( int13, "%04x:%04x", addr.buffer.segment, + DBGC2 ( sandev, "%04x:%04x", addr.buffer.segment, addr.buffer.offset ); } if ( addr.count <= 0x7f ) { @@ -1060,15 +722,15 @@ static int int13_extended_rw ( struct int13_drive *int13, } else if ( addr.count == 0xff ) { count = addr.long_count; } else { - DBGC2 ( int13, " \n", addr.count ); + DBGC2 ( sandev, " \n", addr.count ); return -INT13_STATUS_INVALID; } - DBGC2 ( int13, " (count %ld)\n", count ); + DBGC2 ( sandev, " (count %ld)\n", count ); /* Read from / write to block device */ - if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x extended I/O failed: %s\n", - int13->drive, strerror ( rc ) ); + if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_rw ) ) != 0 ){ + DBGC ( sandev, "INT13 drive %02x extended I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); /* Record that no blocks were transferred successfully */ addr.count = 0; put_real ( addr.count, ix86->segs.ds, @@ -1083,37 +745,39 @@ static int int13_extended_rw ( struct int13_drive *int13, /** * INT 13, 42 - Extended read * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_read ( struct int13_drive *int13, +static int int13_extended_read ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Extended read: " ); - return int13_extended_rw ( int13, ix86, block_read ); + + DBGC2 ( sandev, "Extended read: " ); + return int13_extended_rw ( sandev, ix86, block_read ); } /** * INT 13, 43 - Extended write * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_write ( struct int13_drive *int13, +static int int13_extended_write ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Extended write: " ); - return int13_extended_rw ( int13, ix86, block_write ); + + DBGC2 ( sandev, "Extended write: " ); + return int13_extended_rw ( sandev, ix86, block_write ); } /** * INT 13, 44 - Verify sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_verify ( struct int13_drive *int13, +static int int13_extended_verify ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_disk_address addr; uint64_t lba; @@ -1125,7 +789,7 @@ static int int13_extended_verify ( struct int13_drive *int13, sizeof ( addr )); lba = addr.lba; count = addr.count; - DBGC2 ( int13, "Verify: LBA %08llx (count %ld)\n", + DBGC2 ( sandev, "Verify: LBA %08llx (count %ld)\n", ( ( unsigned long long ) lba ), count ); } @@ -1136,11 +800,11 @@ static int int13_extended_verify ( struct int13_drive *int13, /** * INT 13, 44 - Extended seek * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_seek ( struct int13_drive *int13, +static int int13_extended_seek ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_disk_address addr; uint64_t lba; @@ -1152,7 +816,7 @@ static int int13_extended_seek ( struct int13_drive *int13, sizeof ( addr )); lba = addr.lba; count = addr.count; - DBGC2 ( int13, "Seek: LBA %08llx (count %ld)\n", + DBGC2 ( sandev, "Seek: LBA %08llx (count %ld)\n", ( ( unsigned long long ) lba ), count ); } @@ -1163,11 +827,11 @@ static int int13_extended_seek ( struct int13_drive *int13, /** * Build device path information * - * @v int13 Emulated drive + * @v sandev SAN device * @v dpi Device path information * @ret rc Return status code */ -static int int13_device_path_info ( struct int13_drive *int13, +static int int13_device_path_info ( struct san_device *sandev, struct edd_device_path_information *dpi ) { struct device *device; struct device_description *desc; @@ -1176,15 +840,15 @@ static int int13_device_path_info ( struct int13_drive *int13, int rc; /* Reopen block device if necessary */ - if ( ( int13->block_rc != 0 ) && - ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) ) + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) return rc; /* Get underlying hardware device */ - device = identify_device ( &int13->block ); + device = identify_device ( &sandev->block ); if ( ! device ) { - DBGC ( int13, "INT13 drive %02x cannot identify hardware " - "device\n", int13->drive ); + DBGC ( sandev, "INT13 drive %02x cannot identify hardware " + "device\n", sandev->drive ); return -ENODEV; } @@ -1199,16 +863,16 @@ static int int13_device_path_info ( struct int13_drive *int13, dpi->interface_path.pci.channel = 0xff; /* unused */ break; default: - DBGC ( int13, "INT13 drive %02x unrecognised bus type %d\n", - int13->drive, desc->bus_type ); + DBGC ( sandev, "INT13 drive %02x unrecognised bus type %d\n", + sandev->drive, desc->bus_type ); return -ENOTSUP; } /* Get EDD block device description */ - if ( ( rc = edd_describe ( &int13->block, &dpi->interface_type, + if ( ( rc = edd_describe ( &sandev->block, &dpi->interface_type, &dpi->device_path ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x cannot identify block device: " - "%s\n", int13->drive, strerror ( rc ) ); + DBGC ( sandev, "INT13 drive %02x cannot identify block device: " + "%s\n", sandev->drive, strerror ( rc ) ); return rc; } @@ -1225,12 +889,13 @@ static int int13_device_path_info ( struct int13_drive *int13, /** * INT 13, 48 - Get extended parameters * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Drive parameter table * @ret status Status code */ -static int int13_get_extended_parameters ( struct int13_drive *int13, +static int int13_get_extended_parameters ( struct san_device *sandev, struct i386_all_regs *ix86 ) { + struct int13_data *int13 = sandev->priv; struct int13_disk_parameters params; struct segoff address; size_t len = sizeof ( params ); @@ -1241,26 +906,26 @@ static int int13_get_extended_parameters ( struct int13_drive *int13, get_real ( bufsize, ix86->segs.ds, ( ix86->regs.si + offsetof ( typeof ( params ), bufsize ))); - DBGC2 ( int13, "Get extended drive parameters to %04x:%04x+%02x\n", + DBGC2 ( sandev, "Get extended drive parameters to %04x:%04x+%02x\n", ix86->segs.ds, ix86->regs.si, bufsize ); /* Build drive parameters */ memset ( ¶ms, 0, sizeof ( params ) ); params.flags = INT13_FL_DMA_TRANSPARENT; if ( ( int13->cylinders < 1024 ) && - ( int13_capacity ( int13 ) <= INT13_MAX_CHS_SECTORS ) ) { + ( sandev_capacity ( sandev ) <= INT13_MAX_CHS_SECTORS ) ) { params.flags |= INT13_FL_CHS_VALID; } params.cylinders = int13->cylinders; params.heads = int13->heads; params.sectors_per_track = int13->sectors_per_track; - params.sectors = int13_capacity ( int13 ); - params.sector_size = int13_blksize ( int13 ); + params.sectors = sandev_capacity ( sandev ); + params.sector_size = sandev_blksize ( sandev ); memset ( ¶ms.dpte, 0xff, sizeof ( params.dpte ) ); - if ( ( rc = int13_device_path_info ( int13, ¶ms.dpi ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not provide device " + if ( ( rc = int13_device_path_info ( sandev, ¶ms.dpi ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not provide device " "path information: %s\n", - int13->drive, strerror ( rc ) ); + sandev->drive, strerror ( rc ) ); len = offsetof ( typeof ( params ), dpi ); } @@ -1276,11 +941,11 @@ static int int13_get_extended_parameters ( struct int13_drive *int13, params.bufsize = offsetof ( typeof ( params ), dpi ); } - DBGC ( int13, "INT 13 drive %02x described using extended " - "parameters:\n", int13->drive ); + DBGC ( sandev, "INT 13 drive %02x described using extended " + "parameters:\n", sandev->drive ); address.segment = ix86->segs.ds; address.offset = ix86->regs.si; - DBGC_HDA ( int13, address, ¶ms, len ); + DBGC_HDA ( sandev, address, ¶ms, len ); /* Return drive parameters */ if ( len > bufsize ) @@ -1293,29 +958,29 @@ static int int13_get_extended_parameters ( struct int13_drive *int13, /** * INT 13, 4b - Get status or terminate CD-ROM emulation * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Specification packet * @ret status Status code */ -static int int13_cdrom_status_terminate ( struct int13_drive *int13, +static int int13_cdrom_status_terminate ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_cdrom_specification specification; - DBGC2 ( int13, "Get CD-ROM emulation status to %04x:%04x%s\n", + DBGC2 ( sandev, "Get CD-ROM emulation status to %04x:%04x%s\n", ix86->segs.ds, ix86->regs.si, ( ix86->regs.al ? "" : " and terminate" ) ); /* Fail if we are not a CD-ROM */ - if ( ! int13->is_cdrom ) { - DBGC ( int13, "INT13 drive %02x is not a CD-ROM\n", - int13->drive ); + if ( ! sandev->is_cdrom ) { + DBGC ( sandev, "INT13 drive %02x is not a CD-ROM\n", + sandev->drive ); return -INT13_STATUS_INVALID; } /* Build specification packet */ memset ( &specification, 0, sizeof ( specification ) ); specification.size = sizeof ( specification ); - specification.drive = int13->drive; + specification.drive = sandev->drive; /* Return specification packet */ copy_to_real ( ix86->segs.ds, ix86->regs.si, &specification, @@ -1328,33 +993,34 @@ static int int13_cdrom_status_terminate ( struct int13_drive *int13, /** * INT 13, 4d - Read CD-ROM boot catalog * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Command packet * @ret status Status code */ -static int int13_cdrom_read_boot_catalog ( struct int13_drive *int13, +static int int13_cdrom_read_boot_catalog ( struct san_device *sandev, struct i386_all_regs *ix86 ) { + struct int13_data *int13 = sandev->priv; struct int13_cdrom_boot_catalog_command command; int rc; /* Read parameters from command packet */ copy_from_real ( &command, ix86->segs.ds, ix86->regs.si, sizeof ( command ) ); - DBGC2 ( int13, "Read CD-ROM boot catalog to %08x\n", command.buffer ); + DBGC2 ( sandev, "Read CD-ROM boot catalog to %08x\n", command.buffer ); /* Fail if we have no boot catalog */ if ( ! int13->boot_catalog ) { - DBGC ( int13, "INT13 drive %02x has no boot catalog\n", - int13->drive ); + DBGC ( sandev, "INT13 drive %02x has no boot catalog\n", + sandev->drive ); return -INT13_STATUS_INVALID; } /* Read from boot catalog */ - if ( ( rc = int13_rw ( int13, ( int13->boot_catalog + command.start ), - command.count, phys_to_user ( command.buffer ), - block_read ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not read boot catalog: " - "%s\n", int13->drive, strerror ( rc ) ); + if ( ( rc = sandev_rw ( sandev, ( int13->boot_catalog + command.start ), + command.count, phys_to_user ( command.buffer ), + block_read ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not read boot catalog: " + "%s\n", sandev->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; } @@ -1368,79 +1034,81 @@ static int int13_cdrom_read_boot_catalog ( struct int13_drive *int13, static __asmcall void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; - struct int13_drive *int13; + struct san_device *sandev; + struct int13_data *int13; int status; /* Check BIOS hasn't killed off our drive */ int13_check_num_drives(); - list_for_each_entry ( int13, &int13s, list ) { + for_each_sandev ( sandev ) { - if ( bios_drive != int13->drive ) { + int13 = sandev->priv; + if ( bios_drive != sandev->drive ) { /* Remap any accesses to this drive's natural number */ if ( bios_drive == int13->natural_drive ) { - DBGC2 ( int13, "INT13,%02x (%02x) remapped to " + DBGC2 ( sandev, "INT13,%02x (%02x) remapped to " "(%02x)\n", ix86->regs.ah, - bios_drive, int13->drive ); - ix86->regs.dl = int13->drive; + bios_drive, sandev->drive ); + ix86->regs.dl = sandev->drive; return; } else if ( ( ( bios_drive & 0x7f ) == 0x7f ) && ( command == INT13_CDROM_STATUS_TERMINATE ) - && int13->is_cdrom ) { + && sandev->is_cdrom ) { /* Catch non-drive-specific CD-ROM calls */ } else { continue; } } - DBGC2 ( int13, "INT13,%02x (%02x): ", + DBGC2 ( sandev, "INT13,%02x (%02x): ", ix86->regs.ah, bios_drive ); switch ( command ) { case INT13_RESET: - status = int13_reset ( int13, ix86 ); + status = int13_reset ( sandev, ix86 ); break; case INT13_GET_LAST_STATUS: - status = int13_get_last_status ( int13, ix86 ); + status = int13_get_last_status ( sandev, ix86 ); break; case INT13_READ_SECTORS: - status = int13_read_sectors ( int13, ix86 ); + status = int13_read_sectors ( sandev, ix86 ); break; case INT13_WRITE_SECTORS: - status = int13_write_sectors ( int13, ix86 ); + status = int13_write_sectors ( sandev, ix86 ); break; case INT13_GET_PARAMETERS: - status = int13_get_parameters ( int13, ix86 ); + status = int13_get_parameters ( sandev, ix86 ); break; case INT13_GET_DISK_TYPE: - status = int13_get_disk_type ( int13, ix86 ); + status = int13_get_disk_type ( sandev, ix86 ); break; case INT13_EXTENSION_CHECK: - status = int13_extension_check ( int13, ix86 ); + status = int13_extension_check ( sandev, ix86 ); break; case INT13_EXTENDED_READ: - status = int13_extended_read ( int13, ix86 ); + status = int13_extended_read ( sandev, ix86 ); break; case INT13_EXTENDED_WRITE: - status = int13_extended_write ( int13, ix86 ); + status = int13_extended_write ( sandev, ix86 ); break; case INT13_EXTENDED_VERIFY: - status = int13_extended_verify ( int13, ix86 ); + status = int13_extended_verify ( sandev, ix86 ); break; case INT13_EXTENDED_SEEK: - status = int13_extended_seek ( int13, ix86 ); + status = int13_extended_seek ( sandev, ix86 ); break; case INT13_GET_EXTENDED_PARAMETERS: - status = int13_get_extended_parameters ( int13, ix86 ); + status = int13_get_extended_parameters ( sandev, ix86 ); break; case INT13_CDROM_STATUS_TERMINATE: - status = int13_cdrom_status_terminate ( int13, ix86 ); + status = int13_cdrom_status_terminate ( sandev, ix86 ); break; case INT13_CDROM_READ_BOOT_CATALOG: - status = int13_cdrom_read_boot_catalog ( int13, ix86 ); + status = int13_cdrom_read_boot_catalog ( sandev, ix86 ); break; default: - DBGC2 ( int13, "*** Unrecognised INT13 ***\n" ); + DBGC2 ( sandev, "*** Unrecognised INT13 ***\n" ); status = -INT13_STATUS_INVALID; break; } @@ -1451,8 +1119,8 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) { /* Negative status indicates an error */ if ( status < 0 ) { status = -status; - DBGC ( int13, "INT13,%02x (%02x) failed with status " - "%02x\n", ix86->regs.ah, int13->drive, status ); + DBGC ( sandev, "INT13,%02x (%02x) failed with status " + "%02x\n", ix86->regs.ah, sandev->drive, status ); } else { ix86->flags &= ~CF; } @@ -1529,66 +1197,7 @@ static void int13_unhook_vector ( void ) { } /** - * Check INT13 emulated drive flow control window - * - * @v int13 Emulated drive - */ -static size_t int13_block_window ( struct int13_drive *int13 __unused ) { - - /* We are never ready to receive data via this interface. - * This prevents objects that support both block and stream - * interfaces from attempting to send us stream data. - */ - return 0; -} - -/** - * Handle INT 13 emulated drive underlying block device closing - * - * @v int13 Emulated drive - * @v rc Reason for close - */ -static void int13_block_close ( struct int13_drive *int13, int rc ) { - - /* Any closing is an error from our point of view */ - if ( rc == 0 ) - rc = -ENOTCONN; - - DBGC ( int13, "INT13 drive %02x went away: %s\n", - int13->drive, strerror ( rc ) ); - - /* Record block device error code */ - int13->block_rc = rc; - - /* Shut down interfaces */ - intf_restart ( &int13->block, rc ); -} - -/** INT 13 drive interface operations */ -static struct interface_operation int13_block_op[] = { - INTF_OP ( xfer_window, struct int13_drive *, int13_block_window ), - INTF_OP ( intf_close, struct int13_drive *, int13_block_close ), -}; - -/** INT 13 drive interface descriptor */ -static struct interface_descriptor int13_block_desc = - INTF_DESC ( struct int13_drive, block, int13_block_op ); - -/** - * Free INT 13 emulated drive - * - * @v refcnt Reference count - */ -static void int13_free ( struct refcnt *refcnt ) { - struct int13_drive *int13 = - container_of ( refcnt, struct int13_drive, refcnt ); - - uri_put ( int13->uri ); - free ( int13 ); -} - -/** - * Hook INT 13 emulated drive + * Hook INT 13 SAN device * * @v uri URI * @v drive Drive number @@ -1598,9 +1207,11 @@ static void int13_free ( struct refcnt *refcnt ) { * the INT 13 interrupt vector (if not already hooked). */ static int int13_hook ( struct uri *uri, unsigned int drive ) { - struct int13_drive *int13; + struct san_device *sandev; + struct int13_data *int13; unsigned int natural_drive; void *scratch; + int need_hook = ( ! have_sandevs() ); int rc; /* Calculate natural drive number */ @@ -1611,60 +1222,49 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { if ( ( drive & 0x7f ) == 0x7f ) drive = natural_drive; - /* Check that drive number is not in use */ - list_for_each_entry ( int13, &int13s, list ) { - if ( int13->drive == drive ) { - rc = -EADDRINUSE; - goto err_in_use; - } - } - - /* Allocate and initialise structure */ - int13 = zalloc ( sizeof ( *int13 ) ); - if ( ! int13 ) { + /* Allocate SAN device */ + sandev = alloc_sandev ( uri, sizeof ( *int13 ) ); + if ( ! sandev ) { rc = -ENOMEM; - goto err_zalloc; + goto err_alloc; } - ref_init ( &int13->refcnt, int13_free ); - intf_init ( &int13->block, &int13_block_desc, &int13->refcnt ); - int13->uri = uri_get ( uri ); - int13->drive = drive; + sandev->drive = drive; + int13 = sandev->priv; int13->natural_drive = natural_drive; - /* Open block device interface */ - if ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) - goto err_reopen_block; - - /* Read device capacity */ - if ( ( rc = int13_read_capacity ( int13 ) ) != 0 ) - goto err_read_capacity; + /* Register SAN device */ + if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not register: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_register; + } /* Allocate scratch area */ - scratch = malloc ( int13_blksize ( int13 ) ); + scratch = malloc ( sandev_blksize ( sandev ) ); if ( ! scratch ) goto err_alloc_scratch; /* Parse parameters, if present */ - if ( ( rc = int13_parse_iso9660 ( int13, scratch ) ) != 0 ) - goto err_parse_iso9660; + if ( sandev->is_cdrom && + ( ( rc = int13_parse_eltorito ( sandev, scratch ) ) != 0 ) ) + goto err_parse_eltorito; - /* Give drive a default geometry */ - if ( ( rc = int13_guess_geometry ( int13, scratch ) ) != 0 ) + /* Give drive a default geometry, if applicable */ + if ( ( sandev_blksize ( sandev ) == INT13_BLKSIZE ) && + ( ( rc = int13_guess_geometry ( sandev, scratch ) ) != 0 ) ) goto err_guess_geometry; - DBGC ( int13, "INT13 drive %02x (naturally %02x) registered with C/H/S " - "geometry %d/%d/%d\n", int13->drive, int13->natural_drive, - int13->cylinders, int13->heads, int13->sectors_per_track ); + DBGC ( sandev, "INT13 drive %02x (naturally %02x) registered with " + "C/H/S geometry %d/%d/%d\n", + sandev->drive, int13->natural_drive, int13->cylinders, + int13->heads, int13->sectors_per_track ); /* Hook INT 13 vector if not already hooked */ - if ( list_empty ( &int13s ) ) { + if ( need_hook ) { int13_hook_vector(); devices_get(); } - /* Add to list of emulated drives */ - list_add ( &int13->list, &int13s ); - /* Update BIOS drive count */ int13_sync_num_drives(); @@ -1672,73 +1272,52 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { return drive; err_guess_geometry: - err_parse_iso9660: + err_parse_eltorito: free ( scratch ); err_alloc_scratch: - err_read_capacity: - err_reopen_block: - intf_shutdown ( &int13->block, rc ); - ref_put ( &int13->refcnt ); - err_zalloc: - err_in_use: + unregister_sandev ( sandev ); + err_register: + sandev_put ( sandev ); + err_alloc: return rc; } /** - * Find INT 13 emulated drive by drive number - * - * @v drive Drive number - * @ret int13 Emulated drive, or NULL - */ -static struct int13_drive * int13_find ( unsigned int drive ) { - struct int13_drive *int13; - - list_for_each_entry ( int13, &int13s, list ) { - if ( int13->drive == drive ) - return int13; - } - return NULL; -} - -/** - * Unhook INT 13 emulated drive + * Unhook INT 13 SAN device * * @v drive Drive number * * Unregisters the drive from the INT 13 emulation subsystem. If this - * is the last emulated drive, the INT 13 vector is unhooked (if + * is the last SAN device, the INT 13 vector is unhooked (if * possible). */ static void int13_unhook ( unsigned int drive ) { - struct int13_drive *int13; + struct san_device *sandev; /* Find drive */ - int13 = int13_find ( drive ); - if ( ! int13 ) { - DBG ( "INT13 cannot find emulated drive %02x\n", drive ); + sandev = sandev_find ( drive ); + if ( ! sandev ) { + DBG ( "INT13 cannot find drive %02x\n", drive ); return; } - /* Shut down interfaces */ - intf_shutdown ( &int13->block, 0 ); - - /* Remove from list of emulated drives */ - list_del ( &int13->list ); + /* Unregister SAN device */ + unregister_sandev ( sandev ); /* Should adjust BIOS drive count, but it's difficult * to do so reliably. */ - DBGC ( int13, "INT13 drive %02x unregistered\n", int13->drive ); + DBGC ( sandev, "INT13 drive %02x unregistered\n", sandev->drive ); /* Unhook INT 13 vector if no more drives */ - if ( list_empty ( &int13s ) ) { + if ( ! have_sandevs() ) { devices_put(); int13_unhook_vector(); } - /* Drop list's reference to drive */ - ref_put ( &int13->refcnt ); + /* Drop reference to drive */ + sandev_put ( sandev ); } /** @@ -1939,26 +1518,26 @@ static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) )); #define xbftab __use_data16 ( xbftab ) /** - * Describe INT 13 emulated drive for SAN-booted operating system + * Describe SAN device for SAN-booted operating system * * @v drive Drive number * @ret rc Return status code */ static int int13_describe ( unsigned int drive ) { - struct int13_drive *int13; + struct san_device *sandev; struct segoff xbft_address; int rc; /* Find drive */ - int13 = int13_find ( drive ); - if ( ! int13 ) { - DBG ( "INT13 cannot find emulated drive %02x\n", drive ); + sandev = sandev_find ( drive ); + if ( ! sandev ) { + DBG ( "INT13 cannot find drive %02x\n", drive ); return -ENODEV; } /* Reopen block device if necessary */ - if ( ( int13->block_rc != 0 ) && - ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) ) + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) return rc; /* Clear table */ @@ -1971,10 +1550,10 @@ static int int13_describe ( unsigned int drive ) { sizeof ( xbftab.acpi.oem_table_id ) ); /* Fill in remaining parameters */ - if ( ( rc = acpi_describe ( &int13->block, &xbftab.acpi, + if ( ( rc = acpi_describe ( &sandev->block, &xbftab.acpi, sizeof ( xbftab ) ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not create ACPI " - "description: %s\n", int13->drive, strerror ( rc ) ); + DBGC ( sandev, "INT13 drive %02x could not create ACPI " + "description: %s\n", sandev->drive, strerror ( rc ) ); return rc; } @@ -1982,9 +1561,9 @@ static int int13_describe ( unsigned int drive ) { acpi_fix_checksum ( &xbftab.acpi ); xbft_address.segment = rm_ds; xbft_address.offset = __from_data16 ( &xbftab ); - DBGC ( int13, "INT13 drive %02x described using boot firmware " - "table:\n", int13->drive ); - DBGC_HDA ( int13, xbft_address, &xbftab, + DBGC ( sandev, "INT13 drive %02x described using boot firmware " + "table:\n", sandev->drive ); + DBGC_HDA ( sandev, xbft_address, &xbftab, le32_to_cpu ( xbftab.acpi.length ) ); return 0; From 23d388418e8066fc5d609d57c4a0032a982a2dbc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 6 Mar 2017 14:22:51 +0000 Subject: [PATCH 362/591] [efi] Refactor to use centralised SAN device abstraction Signed-off-by: Michael Brown --- src/interface/efi/efi_block.c | 835 +++++++--------------------------- 1 file changed, 173 insertions(+), 662 deletions(-) diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index ee26b37d8..8eb300a1a 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -55,9 +55,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** Timeout for EFI block device commands (in ticks) */ -#define EFI_BLOCK_TIMEOUT ( 15 * TICKS_PER_SEC ) - /** Boot filename */ static wchar_t efi_block_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; @@ -78,38 +75,10 @@ struct efi_block_vendor_path { CHAR16 uri[0]; } __attribute__ (( packed )); -/** An EFI block device */ -struct efi_block { - /** Reference count */ - struct refcnt refcnt; - /** List of all registered block devices */ - struct list_head list; - - /** Block device URI */ - struct uri *uri; - /** Drive number */ - unsigned int drive; - /** Underlying block device interface */ - struct interface intf; - - /** Current device status */ - int block_rc; - /** Raw block device capacity */ - struct block_device_capacity capacity; - /** Block size shift - * - * To allow for emulation of CD-ROM access, this represents - * the left-shift required to translate from EFI I/O blocks to - * underlying blocks. - * - * Note that the LogicalBlocksPerPhysicalBlock field in - * EFI_BLOCK_IO_MEDIA is unable to encapsulate this - * information, since it does not allow us to describe a - * situation in which there are multiple "physical" - * (i.e. underlying) blocks per logical (i.e. exposed) block. - */ - unsigned int blksize_shift; - +/** EFI SAN device private data */ +struct efi_block_data { + /** SAN device */ + struct san_device *sandev; /** EFI handle */ EFI_HANDLE handle; /** Media descriptor */ @@ -118,319 +87,41 @@ struct efi_block { EFI_BLOCK_IO_PROTOCOL block_io; /** Device path protocol */ EFI_DEVICE_PATH_PROTOCOL *path; - - /** Command interface */ - struct interface command; - /** Command timeout timer */ - struct retry_timer timer; - /** Command status */ - int command_rc; }; /** - * Free EFI block device + * Read from or write to EFI block device * - * @v refcnt Reference count - */ -static void efi_block_free ( struct refcnt *refcnt ) { - struct efi_block *block = - container_of ( refcnt, struct efi_block, refcnt ); - - assert ( ! timer_running ( &block->timer ) ); - uri_put ( block->uri ); - free ( block->path ); - free ( block ); -} - -/** List of EFI block devices */ -static LIST_HEAD ( efi_block_devices ); - -/** - * Find EFI block device - * - * @v drive Drive number - * @ret block Block device, or NULL if not found - */ -static struct efi_block * efi_block_find ( unsigned int drive ) { - struct efi_block *block; - - list_for_each_entry ( block, &efi_block_devices, list ) { - if ( block->drive == drive ) - return block; - } - - return NULL; -} - -/** - * Close EFI block device command - * - * @v block Block device - * @v rc Reason for close - */ -static void efi_block_cmd_close ( struct efi_block *block, int rc ) { - - /* Stop timer */ - stop_timer ( &block->timer ); - - /* Restart interface */ - intf_restart ( &block->command, rc ); - - /* Record command status */ - block->command_rc = rc; -} - -/** - * Record EFI block device capacity - * - * @v block Block device - * @v capacity Block device capacity - */ -static void efi_block_cmd_capacity ( struct efi_block *block, - struct block_device_capacity *capacity ) { - - /* Record raw capacity information */ - memcpy ( &block->capacity, capacity, sizeof ( block->capacity ) ); -} - -/** EFI block device command interface operations */ -static struct interface_operation efi_block_cmd_op[] = { - INTF_OP ( intf_close, struct efi_block *, efi_block_cmd_close ), - INTF_OP ( block_capacity, struct efi_block *, efi_block_cmd_capacity ), -}; - -/** EFI block device command interface descriptor */ -static struct interface_descriptor efi_block_cmd_desc = - INTF_DESC ( struct efi_block, command, efi_block_cmd_op ); - -/** - * Handle EFI block device command timeout - * - * @v retry Retry timer - */ -static void efi_block_cmd_expired ( struct retry_timer *timer, - int over __unused ) { - struct efi_block *block = - container_of ( timer, struct efi_block, timer ); - - efi_block_cmd_close ( block, -ETIMEDOUT ); -} - -/** - * Restart EFI block device interface - * - * @v block Block device - * @v rc Reason for restart - */ -static void efi_block_restart ( struct efi_block *block, int rc ) { - - /* Restart block device interface */ - intf_nullify ( &block->command ); /* avoid potential loops */ - intf_restart ( &block->intf, rc ); - - /* Close any outstanding command */ - efi_block_cmd_close ( block, rc ); - - /* Record device error */ - block->block_rc = rc; -} - -/** - * (Re)open EFI block device - * - * @v block Block device - * @ret rc Return status code - * - * This function will block until the device is available. - */ -static int efi_block_reopen ( struct efi_block *block ) { - int rc; - - /* Close any outstanding command and restart interface */ - efi_block_restart ( block, -ECONNRESET ); - - /* Mark device as being not yet open */ - block->block_rc = -EINPROGRESS; - - /* Open block device interface */ - if ( ( rc = xfer_open_uri ( &block->intf, block->uri ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not (re)open URI: %s\n", - block->drive, strerror ( rc ) ); - return rc; - } - - /* Wait for device to become available */ - while ( block->block_rc == -EINPROGRESS ) { - step(); - if ( xfer_window ( &block->intf ) != 0 ) { - block->block_rc = 0; - return 0; - } - } - - DBGC ( block, "EFIBLK %#02x never became available: %s\n", - block->drive, strerror ( block->block_rc ) ); - return block->block_rc; -} - -/** - * Handle closure of underlying block device interface - * - * @v block Block device - * @ret rc Reason for close - */ -static void efi_block_close ( struct efi_block *block, int rc ) { - - /* Any closure is an error from our point of view */ - if ( rc == 0 ) - rc = -ENOTCONN; - DBGC ( block, "EFIBLK %#02x went away: %s\n", - block->drive, strerror ( rc ) ); - - /* Close any outstanding command and restart interface */ - efi_block_restart ( block, rc ); -} - -/** - * Check EFI block device flow control window - * - * @v block Block device - */ -static size_t efi_block_window ( struct efi_block *block __unused ) { - - /* We are never ready to receive data via this interface. - * This prevents objects that support both block and stream - * interfaces from attempting to send us stream data. - */ - return 0; -} - -/** EFI block device interface operations */ -static struct interface_operation efi_block_op[] = { - INTF_OP ( intf_close, struct efi_block *, efi_block_close ), - INTF_OP ( xfer_window, struct efi_block *, efi_block_window ), -}; - -/** EFI block device interface descriptor */ -static struct interface_descriptor efi_block_desc = - INTF_DESC ( struct efi_block, intf, efi_block_op ); - -/** EFI block device command context */ -struct efi_block_command_context { - /** Starting LBA (using EFI block numbering) */ - uint64_t lba; - /** Data buffer */ - void *data; - /** Length of data buffer */ - size_t len; - /** Block device read/write operation (if any) */ - int ( * block_rw ) ( struct interface *control, struct interface *data, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len ); -}; - -/** - * Initiate EFI block device command - * - * @v block Block device - * @v op Command operation - * @v context Command context, or NULL if not required + * @v sandev SAN device + * @v lba Starting LBA + * @v data Data buffer + * @v len Size of buffer + * @v block_rw Block read/write method * @ret rc Return status code */ -static int efi_block_command ( struct efi_block *block, - int ( * op ) ( struct efi_block *block, - struct efi_block_command_context - *context ), - struct efi_block_command_context *context ) { - int rc; - - /* Sanity check */ - assert ( ! timer_running ( &block->timer ) ); - - /* Reopen block device if applicable */ - if ( ( block->block_rc != 0 ) && - ( ( rc = efi_block_reopen ( block ) ) != 0 ) ) { - goto err_reopen; - } - - /* Start expiry timer */ - start_timer_fixed ( &block->timer, EFI_BLOCK_TIMEOUT ); - - /* Initiate block device operation */ - if ( ( rc = op ( block, context ) ) != 0 ) - goto err_op; - - /* Wait for command to complete */ - while ( timer_running ( &block->timer ) ) - step(); - - /* Collect return status */ - rc = block->command_rc; - - return rc; - - err_op: - stop_timer ( &block->timer ); - err_reopen: - return rc; -} - -/** - * Initiate EFI block device read/write command - * - * @v block Block device - * @v context Command context - * @ret rc Return status code - */ -static int efi_block_cmd_rw ( struct efi_block *block, - struct efi_block_command_context *context ) { - uint64_t lba; +static int efi_block_rw ( struct san_device *sandev, uint64_t lba, + void *data, size_t len, + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) ){ + struct efi_block_data *block = sandev->priv; unsigned int count; int rc; - /* Calculate underlying starting LBA and block count */ - if ( block->capacity.blksize == 0 ) { - DBGC ( block, "EFIBLK %#02x has zero block size\n", - block->drive ); - return -EINVAL; - } - lba = ( context->lba << block->blksize_shift ); - count = ( context->len / block->capacity.blksize ); - if ( ( count * block->capacity.blksize ) != context->len ) { - DBGC ( block, "EFIBLK %#02x invalid read/write length %#zx\n", - block->drive, context->len ); + /* Sanity check */ + count = ( len / block->media.BlockSize ); + if ( ( count * block->media.BlockSize ) != len ) { + DBGC ( sandev, "EFIBLK %#02x impossible length %#zx\n", + sandev->drive, len ); return -EINVAL; } - /* Initiate read/write command */ - if ( ( rc = context->block_rw ( &block->intf, &block->command, lba, - count, virt_to_user ( context->data ), - context->len ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not initiate read/write: " - "%s\n", block->drive, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Initiate EFI block device read capacity command - * - * @v block Block device - * @v context Command context - * @ret rc Return status code - */ -static int efi_block_cmd_read_capacity ( struct efi_block *block, - struct efi_block_command_context - *context __unused ) { - int rc; - - /* Initiate read capacity command */ - if ( ( rc = block_read_capacity ( &block->intf, - &block->command ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not read capacity: %s\n", - block->drive, strerror ( rc ) ); + /* Read from / write to block device */ + if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( data ), + block_rw ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); return rc; } @@ -446,20 +137,14 @@ static int efi_block_cmd_read_capacity ( struct efi_block *block, */ static EFI_STATUS EFIAPI efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *block_io, BOOLEAN verify __unused ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; int rc; - DBGC2 ( block, "EFIBLK %#02x reset\n", block->drive ); - - /* Claim network devices for use by iPXE */ + DBGC2 ( sandev, "EFIBLK %#02x reset\n", sandev->drive ); efi_snp_claim(); - - /* Reopen block device */ - if ( ( rc = efi_block_reopen ( block ) ) != 0 ) - goto err_reopen; - - err_reopen: + rc = sandev_reset ( sandev ); efi_snp_release(); return EFIRC ( rc ); } @@ -477,28 +162,15 @@ static EFI_STATUS EFIAPI efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *block_io, static EFI_STATUS EFIAPI efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, EFI_LBA lba, UINTN len, VOID *data ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); - struct efi_block_command_context context = { - .lba = lba, - .data = data, - .len = len, - .block_rw = block_read, - }; + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; int rc; - DBGC2 ( block, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", - block->drive, context.lba, context.data, context.len ); - - /* Claim network devices for use by iPXE */ + DBGC2 ( sandev, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", + sandev->drive, lba, data, ( ( size_t ) len ) ); efi_snp_claim(); - - /* Issue read command */ - if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, - &context ) ) != 0 ) - goto err_command; - - err_command: + rc = efi_block_rw ( sandev, lba, data, len, block_read ); efi_snp_release(); return EFIRC ( rc ); } @@ -516,28 +188,15 @@ efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, static EFI_STATUS EFIAPI efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, EFI_LBA lba, UINTN len, VOID *data ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); - struct efi_block_command_context context = { - .lba = lba, - .data = data, - .len = len, - .block_rw = block_write, - }; + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; int rc; - DBGC2 ( block, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", - block->drive, context.lba, context.data, context.len ); - - /* Claim network devices for use by iPXE */ + DBGC2 ( sandev, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", + sandev->drive, lba, data, ( ( size_t ) len ) ); efi_snp_claim(); - - /* Issue write command */ - if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, - &context ) ) != 0 ) - goto err_command; - - err_command: + rc = efi_block_rw ( sandev, lba, data, len, block_write ); efi_snp_release(); return EFIRC ( rc ); } @@ -550,52 +209,100 @@ efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, */ static EFI_STATUS EFIAPI efi_block_io_flush ( EFI_BLOCK_IO_PROTOCOL *block_io ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; - DBGC2 ( block, "EFIBLK %#02x flush\n", block->drive ); + DBGC2 ( sandev, "EFIBLK %#02x flush\n", sandev->drive ); /* Nothing to do */ return 0; } /** - * Create device path for EFI block device + * Connect all possible drivers to EFI block device * - * @v uri Block device URI - * @v parent Parent device path - * @ret path Device path, or NULL on failure - * - * The caller must eventually free() the device path. + * @v sandev SAN device */ -static EFI_DEVICE_PATH_PROTOCOL * -efi_block_path ( struct uri *uri, EFI_DEVICE_PATH_PROTOCOL *parent ) { - EFI_DEVICE_PATH_PROTOCOL *path; - struct efi_block_vendor_path *vendor; +static void efi_block_connect ( struct san_device *sandev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block_data *block = sandev->priv; + EFI_STATUS efirc; + int rc; + + /* Try to connect all possible drivers to this block device */ + if ( ( efirc = bs->ConnectController ( block->handle, NULL, + NULL, 1 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not connect drivers: %s\n", + sandev->drive, strerror ( rc ) ); + /* May not be an error; may already be connected */ + } + DBGC2 ( sandev, "EFIBLK %#02x supports protocols:\n", sandev->drive ); + DBGC2_EFI_PROTOCOLS ( sandev, block->handle ); +} + +/** + * Hook EFI block device + * + * @v uri URI + * @v drive Drive number + * @ret drive Drive number, or negative error + */ +static int efi_block_hook ( struct uri *uri, unsigned int drive ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_DEVICE_PATH_PROTOCOL *end; + struct efi_block_vendor_path *vendor; + struct efi_snp_device *snpdev; + struct san_device *sandev; + struct efi_block_data *block; size_t prefix_len; size_t uri_len; size_t vendor_len; size_t len; char *uri_buf; + EFI_STATUS efirc; + int rc; - /* Calculate device path lengths */ - end = efi_devpath_end ( parent ); - prefix_len = ( ( void * ) end - ( void * ) parent ); + /* Find an appropriate parent device handle */ + snpdev = last_opened_snpdev(); + if ( ! snpdev ) { + DBG ( "EFIBLK could not identify SNP device\n" ); + rc = -ENODEV; + goto err_no_snpdev; + } + + /* Calculate length of private data */ + prefix_len = efi_devpath_len ( snpdev->path ); uri_len = format_uri ( uri, NULL, 0 ); vendor_len = ( sizeof ( *vendor ) + ( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) ); - len = ( prefix_len + vendor_len + sizeof ( *end ) ); + len = ( sizeof ( *block ) + uri_len + 1 /* NUL */ + prefix_len + + vendor_len + sizeof ( *end ) ); - /* Allocate device path and space for URI buffer */ - path = zalloc ( len + uri_len + 1 /* NUL */ ); - if ( ! path ) - return NULL; - uri_buf = ( ( ( void * ) path ) + len ); + /* Allocate and initialise structure */ + sandev = alloc_sandev ( uri, len ); + if ( ! sandev ) { + rc = -ENOMEM; + goto err_alloc; + } + sandev->drive = drive; + block = sandev->priv; + block->sandev = sandev; + block->media.MediaPresent = 1; + block->media.LogicalBlocksPerPhysicalBlock = 1; + block->block_io.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + block->block_io.Media = &block->media; + block->block_io.Reset = efi_block_io_reset; + block->block_io.ReadBlocks = efi_block_io_read; + block->block_io.WriteBlocks = efi_block_io_write; + block->block_io.FlushBlocks = efi_block_io_flush; + uri_buf = ( ( ( void * ) block ) + sizeof ( *block ) ); + block->path = ( ( ( void * ) uri_buf ) + uri_len + 1 /* NUL */ ); /* Construct device path */ - memcpy ( path, parent, prefix_len ); - vendor = ( ( ( void * ) path ) + prefix_len ); + memcpy ( block->path, snpdev->path, prefix_len ); + vendor = ( ( ( void * ) block->path ) + prefix_len ); vendor->vendor.Header.Type = HARDWARE_DEVICE_PATH; vendor->vendor.Header.SubType = HW_VENDOR_DP; vendor->vendor.Header.Length[0] = ( vendor_len & 0xff ); @@ -608,211 +315,21 @@ efi_block_path ( struct uri *uri, EFI_DEVICE_PATH_PROTOCOL *parent ) { end->Type = END_DEVICE_PATH_TYPE; end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; end->Length[0] = sizeof ( *end ); + DBGC ( sandev, "EFIBLK %#02x has device path %s\n", + sandev->drive, efi_devpath_text ( block->path ) ); - return path; -} - -/** - * Configure EFI block device as a CD-ROM, if applicable - * - * @v block Block device - * @v scratch Scratch data area - * @ret rc Return status code - * - * The EDK2 code will ignore CD-ROM devices with a block size other - * than 2048. While we could require the user to configure the block - * size appropriately, this is non-trivial and would impose a - * substantial learning effort on the user. Instead, we perform - * essentially the same auto-detection as under a BIOS SAN boot; if - * the ISO9660 primary volume descriptor is present then we force a - * block size of 2048 and map read/write requests appropriately. - */ -static int efi_block_parse_iso9660 ( struct efi_block *block, void *scratch ) { - static const struct iso9660_primary_descriptor_fixed primary_check = { - .type = ISO9660_TYPE_PRIMARY, - .id = ISO9660_ID, - }; - struct iso9660_primary_descriptor *primary = scratch; - struct efi_block_command_context context; - unsigned int blksize; - unsigned int blksize_shift; - int rc; - - /* Calculate required blocksize shift for potential CD-ROM access */ - blksize = block->capacity.blksize; - blksize_shift = 0; - while ( blksize < ISO9660_BLKSIZE ) { - blksize <<= 1; - blksize_shift++; + /* Register SAN device */ + if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x could not register: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_register; } - if ( blksize > ISO9660_BLKSIZE ) { - /* Cannot be a CD-ROM */ - return 0; - } - - /* Read primary volume descriptor */ - memset ( &context, 0, sizeof ( context ) ); - context.lba = ( ISO9660_PRIMARY_LBA << blksize_shift ); - context.data = primary; - context.len = ISO9660_BLKSIZE; - context.block_rw = block_read; - if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, - &context ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not read ISO9660 primary " - "volume descriptor: %s\n", - block->drive, strerror ( rc ) ); - return rc; - } - - /* Do nothing unless this is an ISO image */ - if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) != 0 ) - return 0; - DBGC ( block, "EFIBLK %#02x contains an ISO9660 filesystem; treating " - "as CD-ROM\n", block->drive ); - block->blksize_shift = blksize_shift; - - return 0; -} - -/** - * Determing EFI block device capacity and block size - * - * @v block Block device - * @ret rc Return status code - */ -static int efi_block_capacity ( struct efi_block *block ) { - size_t scratch_len; - void *scratch; - int rc; - - /* Read read block capacity */ - if ( ( rc = efi_block_command ( block, efi_block_cmd_read_capacity, - NULL ) ) != 0 ) - goto err_read_capacity; - block->blksize_shift = 0; - - /* Allocate scratch area */ - scratch_len = ( block->capacity.blksize ); - if ( scratch_len < ISO9660_BLKSIZE ) - scratch_len = ISO9660_BLKSIZE; - scratch = malloc ( scratch_len ); - if ( ! scratch ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Configure as a CD-ROM, if applicable */ - if ( ( rc = efi_block_parse_iso9660 ( block, scratch ) ) != 0 ) - goto err_parse_iso9660; /* Update media descriptor */ block->media.BlockSize = - ( block->capacity.blksize << block->blksize_shift ); + ( sandev->capacity.blksize << sandev->blksize_shift ); block->media.LastBlock = - ( ( block->capacity.blocks >> block->blksize_shift ) - 1 ); - - /* Success */ - rc = 0; - - err_parse_iso9660: - free ( scratch ); - err_alloc: - err_read_capacity: - return rc; -} - -/** - * Connect all possible drivers to EFI block device - * - * @v block Block device - */ -static void efi_block_connect ( struct efi_block *block ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_STATUS efirc; - int rc; - - /* Try to connect all possible drivers to this block device */ - if ( ( efirc = bs->ConnectController ( block->handle, NULL, - NULL, 1 ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x could not connect drivers: %s\n", - block->drive, strerror ( rc ) ); - /* May not be an error; may already be connected */ - } - DBGC2 ( block, "EFIBLK %#02x supports protocols:\n", block->drive ); - DBGC2_EFI_PROTOCOLS ( block, block->handle ); -} - -/** - * Hook EFI block device - * - * @v uri URI - * @v drive Drive number - * @ret drive Drive number, or negative error - */ -static int efi_block_hook ( struct uri *uri, unsigned int drive ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_snp_device *snpdev; - struct efi_block *block; - EFI_STATUS efirc; - int rc; - - /* Check that drive number is not already in use */ - if ( efi_block_find ( drive ) ) { - rc = -EADDRINUSE; - goto err_in_use; - } - - /* Allocate and initialise structure */ - block = zalloc ( sizeof ( *block ) ); - if ( ! block ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &block->refcnt, efi_block_free ); - intf_init ( &block->intf, &efi_block_desc, &block->refcnt ); - block->uri = uri_get ( uri ); - block->drive = drive; - block->block_rc = -EINPROGRESS; - block->media.MediaPresent = 1; - block->media.LogicalBlocksPerPhysicalBlock = 1; - block->block_io.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; - block->block_io.Media = &block->media; - block->block_io.Reset = efi_block_io_reset; - block->block_io.ReadBlocks = efi_block_io_read; - block->block_io.WriteBlocks = efi_block_io_write; - block->block_io.FlushBlocks = efi_block_io_flush; - intf_init ( &block->command, &efi_block_cmd_desc, &block->refcnt ); - timer_init ( &block->timer, efi_block_cmd_expired, &block->refcnt ); - - /* Find an appropriate parent device handle */ - snpdev = last_opened_snpdev(); - if ( ! snpdev ) { - DBGC ( block, "EFIBLK %#02x could not identify SNP device\n", - block->drive ); - rc = -ENODEV; - goto err_no_snpdev; - } - - /* Construct device path */ - block->path = efi_block_path ( block->uri, snpdev->path ); - if ( ! block->path ) { - rc = -ENOMEM; - goto err_path; - } - DBGC ( block, "EFIBLK %#02x has device path %s\n", - block->drive, efi_devpath_text ( block->path ) ); - - /* Add to list of block devices */ - list_add ( &block->list, &efi_block_devices ); - - /* Open block device interface */ - if ( ( rc = efi_block_reopen ( block ) ) != 0 ) - goto err_reopen; - - /* Determine capacity and block size */ - if ( ( rc = efi_block_capacity ( block ) ) != 0 ) - goto err_capacity; + ( ( sandev->capacity.blocks >> sandev->blksize_shift ) - 1 ); /* Install protocols */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( @@ -821,13 +338,13 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { &efi_device_path_protocol_guid, block->path, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x could not install protocols: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x could not install protocols: %s\n", + sandev->drive, strerror ( rc ) ); goto err_install; } /* Connect all possible protocols */ - efi_block_connect ( block ); + efi_block_connect ( sandev ); return drive; @@ -836,16 +353,11 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { &efi_block_io_protocol_guid, &block->block_io, &efi_device_path_protocol_guid, block->path, NULL ); err_install: - err_capacity: - efi_block_restart ( block, rc ); - intf_shutdown ( &block->intf, rc ); - err_reopen: - list_del ( &block->list ); + unregister_sandev ( sandev ); + err_register: + sandev_put ( sandev ); + err_alloc: err_no_snpdev: - err_path: - ref_put ( &block->refcnt ); - err_zalloc: - err_in_use: return rc; } @@ -856,14 +368,16 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { */ static void efi_block_unhook ( unsigned int drive ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_block *block; + struct san_device *sandev; + struct efi_block_data *block; - /* Find block device */ - block = efi_block_find ( drive ); - if ( ! block ) { + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { DBG ( "EFIBLK cannot find drive %#02x\n", drive ); return; } + block = sandev->priv; /* Uninstall protocols */ bs->UninstallMultipleProtocolInterfaces ( @@ -871,15 +385,11 @@ static void efi_block_unhook ( unsigned int drive ) { &efi_block_io_protocol_guid, &block->block_io, &efi_device_path_protocol_guid, block->path, NULL ); - /* Close any outstanding commands and shut down interface */ - efi_block_restart ( block, 0 ); - intf_shutdown ( &block->intf, 0 ); + /* Unregister SAN device */ + unregister_sandev ( sandev ); - /* Remove from list of block devices */ - list_del ( &block->list ); - - /* Drop list's reference to drive */ - ref_put ( &block->refcnt ); + /* Drop reference to drive */ + sandev_put ( sandev ); } /** @@ -889,11 +399,11 @@ static void efi_block_unhook ( unsigned int drive ) { * @ret rc Return status code */ static int efi_block_describe ( unsigned int drive ) { - struct efi_block *block; + struct san_device *sandev; - /* Find block device */ - block = efi_block_find ( drive ); - if ( ! block ) { + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { DBG ( "EFIBLK cannot find drive %#02x\n", drive ); return -ENODEV; } @@ -904,13 +414,14 @@ static int efi_block_describe ( unsigned int drive ) { /** * Try booting from child device of EFI block device * - * @v block Block device + * @v sandev SAN device * @v handle EFI handle * @ret rc Return status code */ -static int efi_block_boot_image ( struct efi_block *block, - EFI_HANDLE handle, EFI_HANDLE *image ) { +static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, + EFI_HANDLE *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block_data *block = sandev->priv; union { EFI_DEVICE_PATH_PROTOCOL *path; void *interface; @@ -930,22 +441,21 @@ static int efi_block_boot_image ( struct efi_block *block, &path.interface, efi_image_handle, handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - DBGC ( block, "EFIBLK %#02x found filesystem with no device " - "path??", block->drive ); + DBGC ( sandev, "EFIBLK %#02x found filesystem with no device " + "path??", sandev->drive ); rc = -EEFI ( efirc ); goto err_open_device_path; } /* Check if this device is a child of our block device */ - end = efi_devpath_end ( block->path ); - prefix_len = ( ( ( void * ) end ) - ( ( void * ) block->path ) ); + prefix_len = efi_devpath_len ( block->path ); if ( memcmp ( path.path, block->path, prefix_len ) != 0 ) { /* Not a child device */ rc = -ENOTTY; goto err_not_child; } - DBGC ( block, "EFIBLK %#02x found child device %s\n", - block->drive, efi_devpath_text ( path.path ) ); + DBGC ( sandev, "EFIBLK %#02x found child device %s\n", + sandev->drive, efi_devpath_text ( path.path ) ); /* Construct device path for boot image */ end = efi_devpath_end ( path.path ); @@ -970,15 +480,15 @@ static int efi_block_boot_image ( struct efi_block *block, end->Type = END_DEVICE_PATH_TYPE; end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; end->Length[0] = sizeof ( *end ); - DBGC ( block, "EFIBLK %#02x trying to load %s\n", - block->drive, efi_devpath_text ( boot_path ) ); + DBGC ( sandev, "EFIBLK %#02x trying to load %s\n", + sandev->drive, efi_devpath_text ( boot_path ) ); /* Try loading boot image from this device */ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path, NULL, 0, image ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x could not load image: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x could not load image: %s\n", + sandev->drive, strerror ( rc ) ); goto err_load_image; } @@ -1001,7 +511,7 @@ static int efi_block_boot_image ( struct efi_block *block, */ static int efi_block_boot ( unsigned int drive ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_block *block; + struct san_device *sandev; EFI_HANDLE *handles; EFI_HANDLE image = NULL; UINTN count; @@ -1009,24 +519,24 @@ static int efi_block_boot ( unsigned int drive ) { EFI_STATUS efirc; int rc; - /* Find block device */ - block = efi_block_find ( drive ); - if ( ! block ) { + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { DBG ( "EFIBLK cannot find drive %#02x\n", drive ); rc = -ENODEV; - goto err_block_find; + goto err_sandev_find; } /* Connect all possible protocols */ - efi_block_connect ( block ); + efi_block_connect ( sandev ); /* Locate all handles supporting the Simple File System protocol */ if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, &efi_simple_file_system_protocol_guid, NULL, &count, &handles ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x cannot locate file systems: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x cannot locate file systems: %s\n", + sandev->drive, strerror ( rc ) ); goto err_locate_file_systems; } @@ -1037,21 +547,22 @@ static int efi_block_boot ( unsigned int drive ) { */ rc = -ENOENT; for ( i = 0 ; i < count ; i++ ) { - if ( ( rc = efi_block_boot_image ( block, handles[i], + if ( ( rc = efi_block_boot_image ( sandev, handles[i], &image ) ) != 0 ) continue; - DBGC ( block, "EFIBLK %#02x found boot image\n", block->drive ); + DBGC ( sandev, "EFIBLK %#02x found boot image\n", + sandev->drive ); efirc = bs->StartImage ( image, NULL, NULL ); rc = ( efirc ? -EEFI ( efirc ) : 0 ); bs->UnloadImage ( image ); - DBGC ( block, "EFIBLK %#02x boot image returned: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x boot image returned: %s\n", + sandev->drive, strerror ( rc ) ); break; } bs->FreePool ( handles ); err_locate_file_systems: - err_block_find: + err_sandev_find: return rc; } From d9886f1961f9970b4354442e84b98727b69cd73a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 7 Mar 2017 16:11:22 +0000 Subject: [PATCH 363/591] [block] Retry any SAN device operation The SCSI layer currently implements a retry loop in order to retry commands that fail due to spurious "error" conditions such as "power on occurred". Move this retry loop to the generic SAN device layer: this allow for retries due to other transient error conditions such as an iSCSI target having dropped the connection due to inactivity. Signed-off-by: Michael Brown --- src/core/sanboot.c | 60 +++++++++++++++++++++++++--------------- src/drivers/block/scsi.c | 25 +---------------- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 42a308392..85d0bc7f6 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -64,6 +64,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define SAN_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) +/** + * Number of times to retry commands + * + * We may need to retry commands. For example, the underlying + * connection may be closed by the SAN target due to an inactivity + * timeout, or the SAN target may return pointless "error" messages + * such as "SCSI power-on occurred". + */ +#define SAN_COMMAND_MAX_RETRIES 10 + /** List of SAN devices */ LIST_HEAD ( san_devices ); @@ -331,36 +341,42 @@ sandev_command ( struct san_device *sandev, int ( * command ) ( struct san_device *sandev, const union san_command_params *params ), const union san_command_params *params ) { + unsigned int retries; int rc; /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); - /* Reopen block device if applicable */ - if ( sandev_needs_reopen ( sandev ) && - ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) { - goto err_reopen; + /* (Re)try command */ + for ( retries = 0 ; retries < SAN_COMMAND_MAX_RETRIES ; retries++ ) { + + /* Reopen block device if applicable */ + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) { + continue; + } + + /* Start expiry timer */ + start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); + + /* Initiate command */ + if ( ( rc = command ( sandev, params ) ) != 0 ) { + stop_timer ( &sandev->timer ); + continue; + } + + /* Wait for command to complete */ + while ( timer_running ( &sandev->timer ) ) + step(); + + /* Exit on success */ + if ( ( rc = sandev->command_rc ) == 0 ) + return 0; } - /* Start expiry timer */ - start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); + /* Sanity check */ + assert ( ! timer_running ( &sandev->timer ) ); - /* Initiate command */ - if ( ( rc = command ( sandev, params ) ) != 0 ) - goto err_op; - - /* Wait for command to complete */ - while ( timer_running ( &sandev->timer ) ) - step(); - - /* Collect return status */ - rc = sandev->command_rc; - - return rc; - - err_op: - stop_timer ( &sandev->timer ); - err_reopen: return rc; } diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index fd5f82b9f..847e0d46c 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -40,9 +40,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Maximum number of command retries */ -#define SCSICMD_MAX_RETRIES 10 - /* Error numbers generated by SCSI sense data */ #define EIO_NO_SENSE __einfo_error ( EINFO_EIO_NO_SENSE ) #define EINFO_EIO_NO_SENSE \ @@ -283,9 +280,6 @@ struct scsi_command { /** Command tag */ uint32_t tag; - /** Retry count */ - unsigned int retries; - /** Private data */ uint8_t priv[0]; }; @@ -449,28 +443,11 @@ static int scsicmd_command ( struct scsi_command *scsicmd ) { * @v rc Reason for close */ static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) { - struct scsi_device *scsidev = scsicmd->scsidev; /* Restart SCSI interface */ intf_restart ( &scsicmd->scsi, rc ); - /* SCSI targets have an annoying habit of returning occasional - * pointless "error" messages such as "power-on occurred", so - * we have to be prepared to retry commands. - */ - if ( ( rc != 0 ) && ( scsicmd->retries++ < SCSICMD_MAX_RETRIES ) ) { - /* Retry command */ - DBGC ( scsidev, "SCSI %p tag %08x failed: %s\n", - scsidev, scsicmd->tag, strerror ( rc ) ); - DBGC ( scsidev, "SCSI %p tag %08x retrying (retry %d)\n", - scsidev, scsicmd->tag, scsicmd->retries ); - if ( ( rc = scsicmd_command ( scsicmd ) ) == 0 ) - return; - } - - /* If we didn't (successfully) reissue the command, hand over - * to the command completion handler. - */ + /* Hand over to the command completion handler */ scsicmd->type->done ( scsicmd, rc ); } From a29bdb3a92a5df6e30b76e74caa6e72462b3d767 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Mar 2017 12:16:15 +0000 Subject: [PATCH 364/591] [iscsi] Use intfs_shutdown() when shutting down multiple interfaces Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index d6f80084d..51d09ddc7 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -231,10 +231,8 @@ static void iscsi_close ( struct iscsi_session *iscsi, int rc ) { process_del ( &iscsi->process ); /* Shut down interfaces */ - intf_nullify ( &iscsi->data ); /* avoid potential loops */ - intf_shutdown ( &iscsi->socket, rc ); - intf_shutdown ( &iscsi->control, rc ); - intf_shutdown ( &iscsi->data, rc ); + intfs_shutdown ( rc, &iscsi->socket, &iscsi->control, &iscsi->data, + NULL ); } /** From 7ff3fc7c72effe730cc8ee0a2964efbd7fc58d2a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Mar 2017 12:16:35 +0000 Subject: [PATCH 365/591] [scsi] Use intfs_shutdown() when shutting down multiple interfaces Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index 847e0d46c..51a1dc0c2 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -394,8 +394,7 @@ static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) { } /* Shut down interfaces */ - intf_shutdown ( &scsicmd->scsi, rc ); - intf_shutdown ( &scsicmd->block, rc ); + intfs_shutdown ( rc, &scsicmd->scsi, &scsicmd->block, NULL ); } /** @@ -840,9 +839,8 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) { process_del ( &scsidev->process ); /* Shut down interfaces */ - intf_shutdown ( &scsidev->block, rc ); - intf_shutdown ( &scsidev->scsi, rc ); - intf_shutdown ( &scsidev->ready, rc ); + intfs_shutdown ( rc, &scsidev->block, &scsidev->scsi, &scsidev->ready, + NULL ); /* Shut down any remaining commands */ list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) { From 9423a85f71b14af6f69897fe5172c60a34b28c42 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Mar 2017 12:16:56 +0000 Subject: [PATCH 366/591] [block] Use intfs_shutdown() when shutting down multiple interfaces Signed-off-by: Michael Brown --- src/core/sanboot.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 85d0bc7f6..95fa5c4ee 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -171,8 +171,7 @@ static void sandev_command_expired ( struct retry_timer *timer, static void sandev_restart ( struct san_device *sandev, int rc ) { /* Restart block device interface */ - intf_nullify ( &sandev->command ); /* avoid potential loops */ - intf_restart ( &sandev->block, rc ); + intfs_restart ( rc, &sandev->command, &sandev->block, NULL ); /* Close any outstanding command */ sandev_command_close ( sandev, rc ); From 9db9221ea0e3a7a4b32cf7fbdb17a46af474ce2b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 9 Mar 2017 12:45:45 +0000 Subject: [PATCH 367/591] [scsi] Avoid duplicate calls to scsicmd_close() When a SCSI device is closed in error, the shutdown of the device's block data interface will probably lead to any outstanding commands being closed (by whichever object is currently connected to the block data interface). However, commands remain in the list of outstanding commands until the final reference is dropped. The result is that scsidev_close() will make a second call to scsicmd_close() for each command. This is harmless, but produces confusing debug messages. Fix by treating the outstanding command list as holding an explicit reference to each command, and removing the command from the list of outstanding commands in scsicmd_close(). Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index 51a1dc0c2..cb4bb94c4 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -371,8 +371,7 @@ static void scsicmd_free ( struct refcnt *refcnt ) { struct scsi_command *scsicmd = container_of ( refcnt, struct scsi_command, refcnt ); - /* Remove from list of commands */ - list_del ( &scsicmd->list ); + /* Drop reference to SCSI device */ scsidev_put ( scsicmd->scsidev ); /* Free command */ @@ -395,6 +394,10 @@ static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) { /* Shut down interfaces */ intfs_shutdown ( rc, &scsicmd->scsi, &scsicmd->block, NULL ); + + /* Remove from list of commands and drop list's reference */ + list_del ( &scsicmd->list ); + scsicmd_put ( scsicmd ); } /** @@ -733,9 +736,8 @@ static int scsidev_command ( struct scsi_device *scsidev, if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 ) goto err_command; - /* Attach to parent interface, mortalise self, and return */ + /* Attach to parent interface, transfer reference to list, and return */ intf_plug_plug ( &scsicmd->block, block ); - ref_put ( &scsicmd->refcnt ); return 0; err_command: @@ -843,11 +845,8 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) { NULL ); /* Shut down any remaining commands */ - list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) { - scsicmd_get ( scsicmd ); + list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) scsicmd_close ( scsicmd, rc ); - scsicmd_put ( scsicmd ); - } } /** SCSI device block interface operations */ From 1d049002622da6a5a9cc98c42fd4dd27dc741b96 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 9 Mar 2017 13:54:13 +0000 Subject: [PATCH 368/591] [intel] Reset all virtual function settings Some VF data is not cleared with reset, so make sure to return all the settings to default before configuring the VF. This fixes an issue where network packets would fail to be received if the VF was previously used by the linux ixgbevf driver. Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 63 ++++++++++++++++++++++++++++++++++---- src/drivers/net/intel.h | 4 +++ src/drivers/net/intelxvf.c | 23 +++++++++++--- src/drivers/net/intelxvf.h | 14 +++++++-- 4 files changed, 91 insertions(+), 13 deletions(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index ab6defde1..534252ba3 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -424,6 +424,61 @@ void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr, ****************************************************************************** */ +/** + * Disable descriptor ring + * + * @v intel Intel device + * @v reg Register block + * @ret rc Return status code + */ +static int intel_disable_ring ( struct intel_nic *intel, unsigned int reg ) { + uint32_t dctl; + unsigned int i; + + /* Disable ring */ + writel ( 0, ( intel->regs + reg + INTEL_xDCTL ) ); + + /* Wait for disable to complete */ + for ( i = 0 ; i < INTEL_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if ring is disabled */ + dctl = readl ( intel->regs + reg + INTEL_xDCTL ); + if ( ! ( dctl & INTEL_xDCTL_ENABLE ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intel, "INTEL %p ring %05x timed out waiting for disable " + "(dctl %08x)\n", intel, reg, dctl ); + return -ETIMEDOUT; +} + +/** + * Reset descriptor ring + * + * @v intel Intel device + * @v reg Register block + * @ret rc Return status code + */ +void intel_reset_ring ( struct intel_nic *intel, unsigned int reg ) { + + /* Disable ring. Ignore errors and continue to reset the ring anyway */ + intel_disable_ring ( intel, reg ); + + /* Clear ring length */ + writel ( 0, ( intel->regs + reg + INTEL_xDLEN ) ); + + /* Clear ring address */ + writel ( 0, ( intel->regs + reg + INTEL_xDBAH ) ); + writel ( 0, ( intel->regs + reg + INTEL_xDBAL ) ); + + /* Reset head and tail pointers */ + writel ( 0, ( intel->regs + reg + INTEL_xDH ) ); + writel ( 0, ( intel->regs + reg + INTEL_xDT ) ); +} + /** * Create descriptor ring * @@ -484,12 +539,8 @@ int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ) { */ void intel_destroy_ring ( struct intel_nic *intel, struct intel_ring *ring ) { - /* Clear ring length */ - writel ( 0, ( intel->regs + ring->reg + INTEL_xDLEN ) ); - - /* Clear ring address */ - writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAL ) ); - writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAH ) ); + /* Reset ring */ + intel_reset_ring ( intel, ring->reg ); /* Free descriptor ring */ free_dma ( ring->desc, ring->len ); diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index 16a72a11b..630eaf84c 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -185,6 +185,9 @@ struct intel_descriptor { #define INTEL_xDCTL 0x28 #define INTEL_xDCTL_ENABLE 0x02000000UL /**< Queue enable */ +/** Maximum time to wait for queue disable, in milliseconds */ +#define INTEL_DISABLE_MAX_WAIT_MS 100 + /** Receive Address Low */ #define INTEL_RAL0 0x05400UL @@ -330,6 +333,7 @@ extern void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr, size_t len ); extern void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr, size_t len ); +extern void intel_reset_ring ( struct intel_nic *intel, unsigned int reg ); extern int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ); extern void intel_destroy_ring ( struct intel_nic *intel, diff --git a/src/drivers/net/intelxvf.c b/src/drivers/net/intelxvf.c index 91a10b10f..2caeec27e 100644 --- a/src/drivers/net/intelxvf.c +++ b/src/drivers/net/intelxvf.c @@ -216,6 +216,7 @@ static int intelxvf_open ( struct net_device *netdev ) { uint32_t rxdctl; uint32_t srrctl; uint32_t dca_rxctrl; + unsigned int i; int vlan_thing; int rc; @@ -252,6 +253,15 @@ static int intelxvf_open ( struct net_device *netdev ) { goto err_mbox_set_mtu; } + /* Reset all descriptor rings */ + for ( i = 0 ; i < INTELXVF_NUM_RINGS ; i++ ) { + intel_reset_ring ( intel, INTELXVF_TD ( i ) ); + intel_reset_ring ( intel, INTELXVF_RD ( i ) ); + } + + /* Reset packet split receive type register */ + writel ( 0, intel->regs + INTELXVF_PSRTYPE ); + /* Get queue configuration. Ignore failures, since the host * may not support this message. */ @@ -260,9 +270,9 @@ static int intelxvf_open ( struct net_device *netdev ) { if ( vlan_thing ) { DBGC ( intel, "INTEL %p stripping VLAN tags (thing=%d)\n", intel, vlan_thing ); - rxdctl = readl ( intel->regs + INTELXVF_RD + INTEL_xDCTL ); + rxdctl = readl ( intel->regs + INTELXVF_RD(0) + INTEL_xDCTL ); rxdctl |= INTELX_RXDCTL_VME; - writel ( rxdctl, intel->regs + INTELXVF_RD + INTEL_xDCTL ); + writel ( rxdctl, intel->regs + INTELXVF_RD(0) + INTEL_xDCTL ); } /* Create transmit descriptor ring */ @@ -283,9 +293,12 @@ static int intelxvf_open ( struct net_device *netdev ) { /* Configure receive buffer sizes and set receive descriptor type */ srrctl = readl ( intel->regs + INTELXVF_SRRCTL ); srrctl &= ~( INTELXVF_SRRCTL_BSIZE_MASK | + INTELXVF_SRRCTL_BHDRSIZE_MASK | INTELXVF_SRRCTL_DESCTYPE_MASK ); srrctl |= ( INTELXVF_SRRCTL_BSIZE_DEFAULT | - INTELXVF_SRRCTL_DESCTYPE_DEFAULT ); + INTELXVF_SRRCTL_BHDRSIZE_DEFAULT | + INTELXVF_SRRCTL_DESCTYPE_DEFAULT | + INTELXVF_SRRCTL_DROP_EN ); writel ( srrctl, intel->regs + INTELXVF_SRRCTL ); /* Clear "must-be-zero" bit for direct cache access (DCA). We @@ -434,9 +447,9 @@ static int intelxvf_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intel, 0, sizeof ( *intel ) ); intel_init_mbox ( &intel->mbox, INTELXVF_MBCTRL, INTELXVF_MBMEM ); - intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD, + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD(0), intel_describe_tx_adv ); - intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD, + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD(0), intel_describe_rx ); /* Fix up PCI device */ diff --git a/src/drivers/net/intelxvf.h b/src/drivers/net/intelxvf.h index ad046a65c..4663272aa 100644 --- a/src/drivers/net/intelxvf.h +++ b/src/drivers/net/intelxvf.h @@ -55,8 +55,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Mailbox Control Register */ #define INTELXVF_MBCTRL 0x02fcUL +/** Packet Split Receive Type */ +#define INTELXVF_PSRTYPE 0x0300UL + /** Receive Descriptor register block */ -#define INTELXVF_RD 0x1000UL +#define INTELXVF_RD(n) ( 0x1000UL + ( 0x40 * (n) ) ) /** RX DCA Control Register */ #define INTELXVF_DCA_RXCTRL 0x100cUL @@ -67,9 +70,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define INTELXVF_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */ #define INTELXVF_SRRCTL_BSIZE_DEFAULT INTELXVF_SRRCTL_BSIZE ( 0x02 ) #define INTELXVF_SRRCTL_BSIZE_MASK INTELXVF_SRRCTL_BSIZE ( 0x1f ) +#define INTELXVF_SRRCTL_BHDRSIZE(kb) ( (kb) << 8 ) /**< Header size */ +#define INTELXVF_SRRCTL_BHDRSIZE_DEFAULT INTELXVF_SRRCTL_BHDRSIZE ( 0x04 ) +#define INTELXVF_SRRCTL_BHDRSIZE_MASK INTELXVF_SRRCTL_BHDRSIZE ( 0x0f ) #define INTELXVF_SRRCTL_DESCTYPE(typ) ( (typ) << 25 ) /**< Descriptor type */ #define INTELXVF_SRRCTL_DESCTYPE_DEFAULT INTELXVF_SRRCTL_DESCTYPE ( 0x00 ) #define INTELXVF_SRRCTL_DESCTYPE_MASK INTELXVF_SRRCTL_DESCTYPE ( 0x07 ) +#define INTELXVF_SRRCTL_DROP_EN 0x10000000UL /** Good Packets Received Count */ #define INTELXVF_GPRC 0x101c @@ -84,7 +91,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define INTELXVF_MPRC 0x1034 /** Transmit Descriptor register block */ -#define INTELXVF_TD 0x2000UL +#define INTELXVF_TD(n) ( 0x2000UL + ( 0x40 * (n) ) ) /** Good Packets Transmitted Count */ #define INTELXVF_GPTC 0x201c @@ -101,4 +108,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** API version 1.1 */ #define INTELXVF_MSG_VERSION_1_1 0x00000002UL +/** Number of queues */ +#define INTELXVF_NUM_RINGS 8 + #endif /* _INTELXVF_H */ From 4524cc11bfc5628e620cf4b42785ee6aee16ccb0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Abrams Date: Thu, 9 Mar 2017 14:23:22 +0000 Subject: [PATCH 369/591] [iscsi] Don't close when receiving NOP-In Some iSCSI targets send NOP-In. Rather than closing the connection when we receive one, it is more user friendly to log a debug message and keep the connection open. Eventually, it would be nice if iPXE supported replying to NOP-Ins, but we might as well keep the connection open until the target disconnects us. Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 51d09ddc7..1e6fd1f51 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -109,10 +109,6 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); __einfo_error ( EINFO_ENOTSUP_TARGET_STATUS ) #define EINFO_ENOTSUP_TARGET_STATUS \ __einfo_uniqify ( EINFO_ENOTSUP, 0x04, "Unsupported target status" ) -#define ENOTSUP_NOP_IN \ - __einfo_error ( EINFO_ENOTSUP_NOP_IN ) -#define EINFO_ENOTSUP_NOP_IN \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x05, "Unsupported NOP-In received" ) #define EPERM_INITIATOR_AUTHENTICATION \ __einfo_error ( EINFO_EPERM_INITIATOR_AUTHENTICATION ) #define EINFO_EPERM_INITIATOR_AUTHENTICATION \ @@ -620,12 +616,15 @@ static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, * sent as ping requests, but we can happily accept NOP-Ins * sent merely to update CmdSN. */ - if ( nop_in->ttt != htonl ( ISCSI_TAG_RESERVED ) ) { - DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT " - "%08x\n", iscsi, ntohl ( nop_in->ttt ) ); - return -ENOTSUP_NOP_IN; - } + if ( nop_in->ttt == htonl ( ISCSI_TAG_RESERVED ) ) + return 0; + /* Ignore any other NOP-Ins. The target may eventually + * disconnect us for failing to respond, but this minimises + * unnecessary connection closures. + */ + DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT %08x\n", + iscsi, ntohl ( nop_in->ttt ) ); return 0; } From afdebdc163268bfc31d927cdfbbf92ed1aa93a6f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Mar 2017 15:40:17 +0000 Subject: [PATCH 370/591] [build] Provide common ARRAY_SIZE() definition Several files define the ARRAY_SIZE() macro as used in Linux. Provide a common definition for this in include/compiler.h. Signed-off-by: Michael Brown --- src/drivers/net/ath/ath.h | 1 - src/drivers/net/ath/ath5k/ath5k.h | 2 -- src/drivers/net/forcedeth.h | 2 -- src/drivers/net/pcnet32.h | 2 -- src/drivers/net/rtl818x/rtl8185_rtl8225.c | 5 ++--- src/drivers/net/skge.h | 3 --- src/drivers/net/tg3/tg3.h | 2 -- src/drivers/net/vxge/vxge_config.h | 4 ---- src/include/compiler.h | 7 +++++++ 9 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/drivers/net/ath/ath.h b/src/drivers/net/ath/ath.h index d6a037394..589bb5634 100644 --- a/src/drivers/net/ath/ath.h +++ b/src/drivers/net/ath/ath.h @@ -26,7 +26,6 @@ FILE_LICENCE ( BSD2 ); #include /* This block of functions are from kernel.h v3.0.1 */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define BITS_PER_BYTE 8 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) diff --git a/src/drivers/net/ath/ath5k/ath5k.h b/src/drivers/net/ath/ath5k/ath5k.h index 30e2024c6..fa62e8ce5 100644 --- a/src/drivers/net/ath/ath5k/ath5k.h +++ b/src/drivers/net/ath/ath5k/ath5k.h @@ -34,8 +34,6 @@ FILE_LICENCE ( MIT ); #undef ERRFILE #define ERRFILE ERRFILE_ath5k -#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) - /* RX/TX descriptor hw structs */ #include "desc.h" diff --git a/src/drivers/net/forcedeth.h b/src/drivers/net/forcedeth.h index e1cf6f71a..3a372dc1b 100644 --- a/src/drivers/net/forcedeth.h +++ b/src/drivers/net/forcedeth.h @@ -36,8 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #ifndef _FORCEDETH_H_ #define _FORCEDETH_H_ -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - struct ring_desc { u32 buf; u32 flaglen; diff --git a/src/drivers/net/pcnet32.h b/src/drivers/net/pcnet32.h index 5e4492ef9..f06d7fd00 100644 --- a/src/drivers/net/pcnet32.h +++ b/src/drivers/net/pcnet32.h @@ -23,8 +23,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #ifndef _PCNET32_H_ #define _PCNET32_H_ -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - /* * Set the number of Tx and Rx buffers, using Log_2(# buffers). * Set default values to 16 Tx buffers and 32 Rx buffers. diff --git a/src/drivers/net/rtl818x/rtl8185_rtl8225.c b/src/drivers/net/rtl818x/rtl8185_rtl8225.c index ae92531ce..31a740e64 100644 --- a/src/drivers/net/rtl818x/rtl8185_rtl8225.c +++ b/src/drivers/net/rtl818x/rtl8185_rtl8225.c @@ -30,7 +30,6 @@ FILE_LICENCE(GPL2_ONLY); #define RTL8225_ANAPARAM2_OFF 0x840dec11 #define min(a,b) (((a)<(b))?(a):(b)) -#define ARRAY_SIZE(a) (int)(sizeof(a)/sizeof((a)[0])) static inline void rtl8225_write_phy_ofdm(struct net80211_device *dev, u8 addr, u8 data) @@ -323,7 +322,7 @@ static void rtl8225_rf_set_tx_power(struct net80211_device *dev, int channel) static void rtl8225_rf_init(struct net80211_device *dev) { struct rtl818x_priv *priv = dev->priv; - int i; + unsigned int i; rtl818x_set_anaparam(priv, RTL8225_ANAPARAM_ON); @@ -552,7 +551,7 @@ static const u16 rtl8225z2_rxgain[] = { static void rtl8225z2_rf_init(struct net80211_device *dev) { struct rtl818x_priv *priv = dev->priv; - int i; + unsigned int i; rtl818x_set_anaparam(priv, RTL8225_ANAPARAM_ON); diff --git a/src/drivers/net/skge.h b/src/drivers/net/skge.h index d9e91457a..b13013bba 100755 --- a/src/drivers/net/skge.h +++ b/src/drivers/net/skge.h @@ -65,9 +65,6 @@ FILE_LICENCE ( GPL2_ONLY ); #define SUPPORTED_TP (1 << 7) #define SUPPORTED_FIBRE (1 << 10) -/* from kernel.h */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - /* ----------------------------------- */ #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index be02c5719..05d516344 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -3284,8 +3284,6 @@ struct tg3 { u16 subsystem_device; }; -#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) ) - #define TG3_TX_RING_SIZE 512 #define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1) diff --git a/src/drivers/net/vxge/vxge_config.h b/src/drivers/net/vxge/vxge_config.h index bf25134ad..59e8a7966 100644 --- a/src/drivers/net/vxge/vxge_config.h +++ b/src/drivers/net/vxge/vxge_config.h @@ -27,10 +27,6 @@ FILE_LICENCE(GPL2_ONLY); #define WAIT_FACTOR 1 -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif - #define VXGE_HW_MAC_MAX_WIRE_PORTS 2 #define VXGE_HW_MAC_MAX_AGGR_PORTS 2 #define VXGE_HW_MAC_MAX_PORTS 3 diff --git a/src/include/compiler.h b/src/include/compiler.h index 4924c7ef5..56a5f63d5 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -655,6 +655,13 @@ char __debug_disable(OBJECT) = ( DBGLVL_MAX & ~DBGLVL_DFLT ); #define barrier() __asm__ __volatile__ ( "" : : : "memory" ) #endif /* ASSEMBLY */ +/** + * Array size + */ +#ifndef ASSEMBLY +#define ARRAY_SIZE(array) ( sizeof (array) / sizeof ( (array)[0] ) ) +#endif /* ASSEMBLY */ + /** * @defgroup licences Licence declarations * From 0463ec32c717c10e56b1e0bbecc1b02756df7fcc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Mar 2017 21:18:03 +0000 Subject: [PATCH 371/591] [efi] Update to current EDK2 headers Signed-off-by: Michael Brown --- src/include/ipxe/efi/AArch64/ProcessorBind.h | 6 + src/include/ipxe/efi/Arm/ProcessorBind.h | 6 + src/include/ipxe/efi/Base.h | 57 +- src/include/ipxe/efi/Ia32/ProcessorBind.h | 6 + .../ipxe/efi/IndustryStandard/Acpi51.h | 8 +- .../ipxe/efi/IndustryStandard/Acpi60.h | 12 +- src/include/ipxe/efi/IndustryStandard/Pci22.h | 12 +- .../efi/IndustryStandard/UefiTcgPlatform.h | 57 +- src/include/ipxe/efi/Library/BaseLib.h | 1089 ++++++++++++++++- src/include/ipxe/efi/Protocol/HiiDatabase.h | 4 +- src/include/ipxe/efi/Protocol/SimpleTextIn.h | 1 - src/include/ipxe/efi/Uefi/UefiBaseType.h | 10 +- src/include/ipxe/efi/X64/ProcessorBind.h | 6 + src/include/ipxe/efi/efi.h | 3 + 14 files changed, 1233 insertions(+), 44 deletions(-) diff --git a/src/include/ipxe/efi/AArch64/ProcessorBind.h b/src/include/ipxe/efi/AArch64/ProcessorBind.h index 858aefe3a..909b5cde4 100644 --- a/src/include/ipxe/efi/AArch64/ProcessorBind.h +++ b/src/include/ipxe/efi/AArch64/ProcessorBind.h @@ -106,6 +106,12 @@ typedef INT64 INTN; /// #define CPU_STACK_ALIGNMENT 16 +/// +/// Page allocation granularity for AARCH64 +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x10000) + // // Modifier to ensure that all protocol member functions and EFI intrinsics // use the correct C calling convention. All protocol member functions and diff --git a/src/include/ipxe/efi/Arm/ProcessorBind.h b/src/include/ipxe/efi/Arm/ProcessorBind.h index fac183237..efe3bf174 100644 --- a/src/include/ipxe/efi/Arm/ProcessorBind.h +++ b/src/include/ipxe/efi/Arm/ProcessorBind.h @@ -112,6 +112,12 @@ typedef INT32 INTN; /// #define CPU_STACK_ALIGNMENT sizeof(UINT64) +/// +/// Page allocation granularity for ARM +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000) + // // Modifier to ensure that all protocol member functions and EFI intrinsics // use the correct C calling convention. All protocol member functions and diff --git a/src/include/ipxe/efi/Base.h b/src/include/ipxe/efi/Base.h index b241e6279..26c90c154 100644 --- a/src/include/ipxe/efi/Base.h +++ b/src/include/ipxe/efi/Base.h @@ -6,7 +6,7 @@ environment. There are a set of base libraries in the Mde Package that can be used to implement base modules. -Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -65,6 +65,29 @@ VERIFY_SIZE_OF (UINT64, 8); VERIFY_SIZE_OF (CHAR8, 1); VERIFY_SIZE_OF (CHAR16, 2); +// +// The following three enum types are used to verify that the compiler +// configuration for enum types is compliant with Section 2.3.1 of the +// UEFI 2.3 Specification. These enum types and enum values are not +// intended to be used. A prefix of '__' is used avoid conflicts with +// other types. +// +typedef enum { + __VerifyUint8EnumValue = 0xff +} __VERIFY_UINT8_ENUM_SIZE; + +typedef enum { + __VerifyUint16EnumValue = 0xffff +} __VERIFY_UINT16_ENUM_SIZE; + +typedef enum { + __VerifyUint32EnumValue = 0xffffffff +} __VERIFY_UINT32_ENUM_SIZE; + +VERIFY_SIZE_OF (__VERIFY_UINT8_ENUM_SIZE, 4); +VERIFY_SIZE_OF (__VERIFY_UINT16_ENUM_SIZE, 4); +VERIFY_SIZE_OF (__VERIFY_UINT32_ENUM_SIZE, 4); + // // The Microsoft* C compiler can removed references to unreferenced data items // if the /OPT:REF linker option is used. We defined a macro as this is a @@ -244,6 +267,20 @@ typedef struct { UINT8 Data4[8]; } GUID; +/// +/// 4-byte buffer. An IPv4 internet protocol address. +/// +typedef struct { + UINT8 Addr[4]; +} IPv4_ADDRESS; + +/// +/// 16-byte buffer. An IPv6 internet protocol address. +/// +typedef struct { + UINT8 Addr[16]; +} IPv6_ADDRESS; + // // 8-bytes unsigned value that represents a physical system address. // @@ -324,6 +361,11 @@ struct _LIST_ENTRY { /// #define NULL ((VOID *) 0) +// +// Null character +// +#define CHAR_NULL 0x0000 + /// /// Maximum values for common UEFI Data Types /// @@ -1213,5 +1255,18 @@ typedef UINTN RETURN_STATUS; #define RETURN_ADDRESS(L) ((VOID *) 0) #endif +/** + Return the number of elements in an array. + + @param Array An object of array type. Array is only used as an argument to + the sizeof operator, therefore Array is never evaluated. The + caller is responsible for ensuring that Array's type is not + incomplete; that is, Array must have known constant size. + + @return The number of elements in Array. The result has type UINTN. + +**/ +#define ARRAY_SIZE(Array) (sizeof (Array) / sizeof ((Array)[0])) + #endif diff --git a/src/include/ipxe/efi/Ia32/ProcessorBind.h b/src/include/ipxe/efi/Ia32/ProcessorBind.h index 82e1d46ac..2d6c4b4bb 100644 --- a/src/include/ipxe/efi/Ia32/ProcessorBind.h +++ b/src/include/ipxe/efi/Ia32/ProcessorBind.h @@ -259,6 +259,12 @@ typedef INT32 INTN; /// #define CPU_STACK_ALIGNMENT sizeof(UINTN) +/// +/// Page allocation granularity for IA-32. +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000) + // // Modifier to ensure that all protocol member functions and EFI intrinsics // use the correct C calling convention. All protocol member functions and diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi51.h b/src/include/ipxe/efi/IndustryStandard/Acpi51.h index b00613942..1ca114cad 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi51.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi51.h @@ -1,8 +1,8 @@ /** @file - ACPI 5.1 definitions from the ACPI Specification Revision 5.1 July, 2014. + ACPI 5.1 definitions from the ACPI Specification Revision 5.1 Errata B January, 2016. Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
- Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -539,8 +539,8 @@ typedef struct { /// /// GIC Version /// -#define EFI_ACPI_5_1_GIC_V2 0x01 -#define EFI_ACPI_5_1_GIC_V2m 0x02 +#define EFI_ACPI_5_1_GIC_V1 0x01 +#define EFI_ACPI_5_1_GIC_V2 0x02 #define EFI_ACPI_5_1_GIC_V3 0x03 #define EFI_ACPI_5_1_GIC_V4 0x04 diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi60.h b/src/include/ipxe/efi/IndustryStandard/Acpi60.h index 18eb5f7d9..c600735fd 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi60.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi60.h @@ -1,7 +1,7 @@ /** @file - ACPI 6.0 definitions from the ACPI Specification Revision 6.0 April, 2015. + ACPI 6.0 definitions from the ACPI Specification Revision 6.0 Errata A January, 2016. - Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -292,9 +292,9 @@ typedef struct { } EFI_ACPI_6_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER; /// -/// MADT Revision (as defined in ACPI 6.0 spec.) +/// MADT Revision (as defined in ACPI 6.0 Errata A spec.) /// -#define EFI_ACPI_6_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION 0x03 +#define EFI_ACPI_6_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION 0x04 /// /// Multiple APIC Flags @@ -2254,9 +2254,9 @@ typedef struct { #define EFI_ACPI_6_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE SIGNATURE_32('i', 'B', 'F', 'T') /// -/// "IORT" Interrupt Source Override +/// "IORT" I/O Remapping Table /// -#define EFI_ACPI_6_0_INTERRUPT_SOURCE_OVERRIDE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') +#define EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') /// /// "IVRS" I/O Virtualization Reporting Structure diff --git a/src/include/ipxe/efi/IndustryStandard/Pci22.h b/src/include/ipxe/efi/IndustryStandard/Pci22.h index d585e8e70..c14d4b4bd 100644 --- a/src/include/ipxe/efi/IndustryStandard/Pci22.h +++ b/src/include/ipxe/efi/IndustryStandard/Pci22.h @@ -7,7 +7,7 @@ PC Card Standard, 8.0 PCI Power Management Interface Specifiction, Revision 1.2 - Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
Copyright (c) 2014 - 2015, Hewlett-Packard Development Company, L.P.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -782,22 +782,12 @@ typedef struct { /// } EFI_PCI_CAPABILITY_HOTPLUG; -#define DEVICE_ID_NOCARE 0xFFFF - -#define PCI_ACPI_UNUSED 0 -#define PCI_BAR_NOCHANGE 0 -#define PCI_BAR_OLD_ALIGN 0xFFFFFFFFFFFFFFFFULL -#define PCI_BAR_EVEN_ALIGN 0xFFFFFFFFFFFFFFFEULL -#define PCI_BAR_SQUAD_ALIGN 0xFFFFFFFFFFFFFFFDULL -#define PCI_BAR_DQUAD_ALIGN 0xFFFFFFFFFFFFFFFCULL - #define PCI_BAR_IDX0 0x00 #define PCI_BAR_IDX1 0x01 #define PCI_BAR_IDX2 0x02 #define PCI_BAR_IDX3 0x03 #define PCI_BAR_IDX4 0x04 #define PCI_BAR_IDX5 0x05 -#define PCI_BAR_ALL 0xFF /// /// EFI PCI Option ROM definitions diff --git a/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h b/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h index df65be914..3394c7cbe 100644 --- a/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h +++ b/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h @@ -1,7 +1,7 @@ /** @file TCG EFI Platform Definition in TCG_EFI_Platform_1_20_Final - Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -153,6 +153,7 @@ typedef struct tdEFI_HANDOFF_TABLE_POINTERS { /// This structure serves as the header for measuring variables. The name of the /// variable (in Unicode format) should immediately follow, then the variable /// data. +/// This is defined in TCG EFI Platform Spec for TPM1.1 or 1.2 V1.22 /// typedef struct tdEFI_VARIABLE_DATA { EFI_GUID VariableName; @@ -162,6 +163,22 @@ typedef struct tdEFI_VARIABLE_DATA { INT8 VariableData[1]; ///< Driver or platform-specific data } EFI_VARIABLE_DATA; +/// +/// UEFI_VARIABLE_DATA +/// +/// This structure serves as the header for measuring variables. The name of the +/// variable (in Unicode format) should immediately follow, then the variable +/// data. +/// This is defined in TCG PC Client Firmware Profile Spec 00.21 +/// +typedef struct tdUEFI_VARIABLE_DATA { + EFI_GUID VariableName; + UINT64 UnicodeNameLength; + UINT64 VariableDataLength; + CHAR16 UnicodeName[1]; + INT8 VariableData[1]; ///< Driver or platform-specific data +} UEFI_VARIABLE_DATA; + // // For TrEE1.0 compatibility // @@ -190,6 +207,17 @@ typedef struct tdTCG_PCR_EVENT2 { UINT8 Event[1]; } TCG_PCR_EVENT2; +// +// TCG PCR Event2 Header +// Follow TCG EFI Protocol Spec 5.2 Crypto Agile Log Entry Format +// +typedef struct tdTCG_PCR_EVENT2_HDR{ + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; + TPML_DIGEST_VALUES Digests; + UINT32 EventSize; +} TCG_PCR_EVENT2_HDR; + // // Log Header Entry Data // @@ -270,6 +298,33 @@ typedef struct { //UINT8 vendorInfo[vendorInfoSize]; } TCG_EfiSpecIDEventStruct; + + +#define TCG_EfiStartupLocalityEvent_SIGNATURE "StartupLocality" + + +// +// PC Client PTP spec Table 8 Relationship between Locality and Locality Attribute +// +#define LOCALITY_0_INDICATOR 0x01 +#define LOCALITY_1_INDICATOR 0x02 +#define LOCALITY_2_INDICATOR 0x03 +#define LOCALITY_3_INDICATOR 0x04 +#define LOCALITY_4_INDICATOR 0x05 + + +// +// Startup Locality Event +// +typedef struct tdTCG_EfiStartupLocalityEvent{ + UINT8 Signature[16]; + // + // The Locality Indicator which sent the TPM2_Startup command + // + UINT8 StartupLocality; +} TCG_EfiStartupLocalityEvent; + + // // Restore original structure alignment // diff --git a/src/include/ipxe/efi/Library/BaseLib.h b/src/include/ipxe/efi/Library/BaseLib.h index a254ed2d0..a03ef30ef 100644 --- a/src/include/ipxe/efi/Library/BaseLib.h +++ b/src/include/ipxe/efi/Library/BaseLib.h @@ -2,7 +2,7 @@ Provides string functions, linked list functions, math functions, synchronization functions, file path functions, and CPU architecture-specific functions. -Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -209,6 +209,34 @@ StrnLenS ( IN UINTN MaxSize ); +/** + Returns the size of a Null-terminated Unicode string in bytes, including the + Null terminator. + + This function returns the size of the Null-terminated Unicode string + specified by String in bytes, including the Null terminator. + + If String is not aligned on a 16-bit boundary, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + @param MaxSize The maximum number of Destination Unicode + char, including the Null terminator. + + @retval 0 If String is NULL. + @retval (sizeof (CHAR16) * (MaxSize + 1)) + If there is no Null terminator in the first MaxSize characters of + String. + @return The size of the Null-terminated Unicode string in bytes, including + the Null terminator. + +**/ +UINTN +EFIAPI +StrnSizeS ( + IN CONST CHAR16 *String, + IN UINTN MaxSize + ); + /** Copies the string pointed to by Source (including the terminating null char) to the array pointed to by Destination. @@ -363,6 +391,240 @@ StrnCatS ( IN UINTN Length ); +/** + Convert a Null-terminated Unicode decimal string to a value of type UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Unicode string specified by String as a decimal number. The format of the + input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +StrDecimalToUintnS ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Unicode decimal string to a value of type UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Unicode string specified by String as a decimal number. The format of the + input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +StrDecimalToUint64S ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type + UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Unicode string specified by String as a hexadecimal number. The format of + the input Unicode string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab + characters, before [zeros], [x] or [hexadecimal digit]. The running zero + before [x] or [hexadecimal digit] will be ignored. Then, the decoding starts + after [x] or the first valid hexadecimal digit. Then, the function stops at + the first character that is a not a valid hexadecimal character or NULL, + whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +StrHexToUintnS ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type + UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Unicode string specified by String as a hexadecimal number. The format of + the input Unicode string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab + characters, before [zeros], [x] or [hexadecimal digit]. The running zero + before [x] or [hexadecimal digit] will be ignored. Then, the decoding starts + after [x] or the first valid hexadecimal digit. Then, the function stops at + the first character that is a not a valid hexadecimal character or NULL, + whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +StrHexToUint64S ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + /** Returns the length of a Null-terminated Ascii string. @@ -384,6 +646,32 @@ AsciiStrnLenS ( IN UINTN MaxSize ); +/** + Returns the size of a Null-terminated Ascii string in bytes, including the + Null terminator. + + This function returns the size of the Null-terminated Ascii string specified + by String in bytes, including the Null terminator. + + @param String A pointer to a Null-terminated Ascii string. + @param MaxSize The maximum number of Destination Ascii + char, including the Null terminator. + + @retval 0 If String is NULL. + @retval (sizeof (CHAR8) * (MaxSize + 1)) + If there is no Null terminator in the first MaxSize characters of + String. + @return The size of the Null-terminated Ascii string in bytes, including the + Null terminator. + +**/ +UINTN +EFIAPI +AsciiStrnSizeS ( + IN CONST CHAR8 *String, + IN UINTN MaxSize + ); + /** Copies the string pointed to by Source (including the terminating null char) to the array pointed to by Destination. @@ -530,6 +818,234 @@ AsciiStrnCatS ( IN UINTN Length ); +/** + Convert a Null-terminated Ascii decimal string to a value of type UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Ascii string specified by String as a decimal number. The format of the + input Ascii string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrDecimalToUintnS ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Ascii decimal string to a value of type UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Ascii string specified by String as a decimal number. The format of the + input Ascii string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrDecimalToUint64S ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + +/** + Convert a Null-terminated Ascii hexadecimal string to a value of type UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Ascii string specified by String as a hexadecimal number. The format of + the input Ascii string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. If + "x" appears in the input string, it must be prefixed with at least one 0. The + function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digits]. The running zero before [x] or + [hexadecimal digits] will be ignored. Then, the decoding starts after [x] or + the first valid hexadecimal digit. Then, the function stops at the first + character that is a not a valid hexadecimal character or Null-terminator, + whichever on comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrHexToUintnS ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Ascii hexadecimal string to a value of type UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Ascii string specified by String as a hexadecimal number. The format of + the input Ascii string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. If + "x" appears in the input string, it must be prefixed with at least one 0. The + function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digits]. The running zero before [x] or + [hexadecimal digits] will be ignored. Then, the decoding starts after [x] or + the first valid hexadecimal digit. Then, the function stops at the first + character that is a not a valid hexadecimal character or Null-terminator, + whichever on comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrHexToUint64S ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + #ifndef DISABLE_NEW_DEPRECATED_INTERFACES @@ -881,7 +1397,7 @@ StrStr ( If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according - to the range defined by UINTN, then ASSERT(). + to the range defined by UINTN, then MAX_UINTN is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including @@ -921,7 +1437,7 @@ StrDecimalToUintn ( If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according - to the range defined by UINT64, then ASSERT(). + to the range defined by UINT64, then MAX_UINT64 is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including @@ -963,7 +1479,7 @@ StrDecimalToUint64 ( If String has no leading pad spaces, leading zeros or valid hexadecimal digits, then zero is returned. If the number represented by String overflows according to the range defined by - UINTN, then ASSERT(). + UINTN, then MAX_UINTN is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, @@ -1005,7 +1521,7 @@ StrHexToUintn ( If String has no leading pad spaces, leading zeros or valid hexadecimal digits, then zero is returned. If the number represented by String overflows according to the range defined by - UINT64, then ASSERT(). + UINT64, then MAX_UINT64 is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, @@ -1022,6 +1538,237 @@ StrHexToUint64 ( IN CONST CHAR16 *String ); +/** + Convert a Null-terminated Unicode string to IPv6 address and prefix length. + + This function outputs a value of type IPv6_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the Unicode string specified + by String. The format of the input Unicode string String is as follows: + + X:X:X:X:X:X:X:X[/P] + + X contains one to four hexadecimal digit characters in the range [0-9], [a-f] and + [A-F]. X is converted to a value of type UINT16, whose low byte is stored in low + memory address and high byte is stored in high memory address. P contains decimal + digit characters in the range [0-9]. The running zero in the beginning of P will + be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid hexadecimal digit character after eight X's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + "::" can be used to compress one or more groups of X when X contains only 0. + The "::" can only appear once in the String. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If String is not aligned in a 16-bit boundary, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv6 address. + @param PrefixLength Pointer to the converted IPv6 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If X contains more than four hexadecimal + digit characters. + If String contains "::" and number of X + is not less than 8. + If P starts with character that is not a + valid decimal digit character. + If the decimal number converted from P + exceeds 128. + +**/ +RETURN_STATUS +EFIAPI +StrToIpv6Address ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT IPv6_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +/** + Convert a Null-terminated Unicode string to IPv4 address and prefix length. + + This function outputs a value of type IPv4_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the Unicode string specified + by String. The format of the input Unicode string String is as follows: + + D.D.D.D[/P] + + D and P are decimal digit characters in the range [0-9]. The running zero in + the beginning of D and P will be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid decimal digit character after four D's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If String is not aligned in a 16-bit boundary, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv4 address. + @param PrefixLength Pointer to the converted IPv4 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not in the correct format. + If any decimal number converted from D + exceeds 255. + If the decimal number converted from P + exceeds 32. + +**/ +RETURN_STATUS +EFIAPI +StrToIpv4Address ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT IPv4_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +#define GUID_STRING_LENGTH 36 + +/** + Convert a Null-terminated Unicode GUID string to a value of type + EFI_GUID. + + This function outputs a GUID value by interpreting the contents of + the Unicode string specified by String. The format of the input + Unicode string String consists of 36 characters, as follows: + + aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + + The pairs aa - pp are two characters in the range [0-9], [a-f] and + [A-F], with each pair representing a single byte hexadecimal value. + + The mapping between String and the EFI_GUID structure is as follows: + aa Data1[24:31] + bb Data1[16:23] + cc Data1[8:15] + dd Data1[0:7] + ee Data2[8:15] + ff Data2[0:7] + gg Data3[8:15] + hh Data3[0:7] + ii Data4[0:7] + jj Data4[8:15] + kk Data4[16:23] + ll Data4[24:31] + mm Data4[32:39] + nn Data4[40:47] + oo Data4[48:55] + pp Data4[56:63] + + If String is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + + @param String Pointer to a Null-terminated Unicode string. + @param Guid Pointer to the converted GUID. + + @retval RETURN_SUCCESS Guid is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not as the above format. + +**/ +RETURN_STATUS +EFIAPI +StrToGuid ( + IN CONST CHAR16 *String, + OUT GUID *Guid + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a byte array. + + This function outputs a byte array by interpreting the contents of + the Unicode string specified by String in hexadecimal format. The format of + the input Unicode string String is: + + [XX]* + + X is a hexadecimal digit character in the range [0-9], [a-f] and [A-F]. + The function decodes every two hexadecimal digit characters as one byte. The + decoding stops after Length of characters and outputs Buffer containing + (Length / 2) bytes. + + If String is not aligned in a 16-bit boundary, then ASSERT(). + + If String is NULL, then ASSERT(). + + If Buffer is NULL, then ASSERT(). + + If Length is not multiple of 2, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero and Length is greater than + PcdMaximumUnicodeStringLength, then ASSERT(). + + If MaxBufferSize is less than (Length / 2), then ASSERT(). + + @param String Pointer to a Null-terminated Unicode string. + @param Length The number of Unicode characters to decode. + @param Buffer Pointer to the converted bytes array. + @param MaxBufferSize The maximum size of Buffer. + + @retval RETURN_SUCCESS Buffer is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If Length is not multiple of 2. + If PcdMaximumUnicodeStringLength is not zero, + and Length is greater than + PcdMaximumUnicodeStringLength. + @retval RETURN_UNSUPPORTED If Length of characters from String contain + a character that is not valid hexadecimal + digit characters, or a Null-terminator. + @retval RETURN_BUFFER_TOO_SMALL If MaxBufferSize is less than (Length / 2). +**/ +RETURN_STATUS +EFIAPI +StrHexToBytes ( + IN CONST CHAR16 *String, + IN UINTN Length, + OUT UINT8 *Buffer, + IN UINTN MaxBufferSize + ); + #ifndef DISABLE_NEW_DEPRECATED_INTERFACES /** @@ -1117,6 +1864,60 @@ UnicodeStrToAsciiStrS ( IN UINTN DestMax ); +/** + Convert not more than Length successive characters from a Null-terminated + Unicode string to a Null-terminated Ascii string. If no null char is copied + from Source, then Destination[Length] is always set to null. + + This function converts not more than Length successive characters from the + Unicode string Source to the Ascii string Destination by copying the lower 8 + bits of each Unicode character. The function terminates the Ascii string + Destination by appending a Null-terminator character at the end. + + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((StrLen (Source) + 1) * sizeof (CHAR8)) in bytes. + + If any Unicode characters in Source contain non-zero value in the upper 8 + bits, then ASSERT(). + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then the Destination is unmodified. + + @param Source The pointer to a Null-terminated Unicode string. + @param Length The maximum number of Unicode characters to + convert. + @param Destination The pointer to a Null-terminated Ascii string. + @param DestMax The maximum number of Destination Ascii + char, including terminating null char. + @param DestinationLength The number of Unicode characters converted. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If DestinationLength is NULL. + If PcdMaximumAsciiStringLength is not zero, + and Length or DestMax is greater than + PcdMaximumAsciiStringLength. + If PcdMaximumUnicodeStringLength is not + zero, and Length or DestMax is greater than + PcdMaximumUnicodeStringLength. + If DestMax is 0. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than + MIN(StrLen(Source), Length). + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +UnicodeStrnToAsciiStrS ( + IN CONST CHAR16 *Source, + IN UINTN Length, + OUT CHAR8 *Destination, + IN UINTN DestMax, + OUT UINTN *DestinationLength + ); + #ifndef DISABLE_NEW_DEPRECATED_INTERFACES /** @@ -1483,7 +2284,7 @@ AsciiStrStr ( If String has only pad spaces, then 0 is returned. If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according to the range defined by - UINTN, then ASSERT(). + UINTN, then MAX_UINTN is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including the Null-terminator, @@ -1520,7 +2321,7 @@ AsciiStrDecimalToUintn ( If String has only pad spaces, then 0 is returned. If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according to the range defined by - UINT64, then ASSERT(). + UINT64, then MAX_UINT64 is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including the Null-terminator, @@ -1561,7 +2362,7 @@ AsciiStrDecimalToUint64 ( 0 is returned. If the number represented by String overflows according to the range defined by UINTN, - then ASSERT(). + then MAX_UINTN is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including @@ -1602,7 +2403,7 @@ AsciiStrHexToUintn ( 0 is returned. If the number represented by String overflows according to the range defined by UINT64, - then ASSERT(). + then MAX_UINT64 is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including @@ -1619,6 +2420,220 @@ AsciiStrHexToUint64 ( IN CONST CHAR8 *String ); +/** + Convert a Null-terminated ASCII string to IPv6 address and prefix length. + + This function outputs a value of type IPv6_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the ASCII string specified + by String. The format of the input ASCII string String is as follows: + + X:X:X:X:X:X:X:X[/P] + + X contains one to four hexadecimal digit characters in the range [0-9], [a-f] and + [A-F]. X is converted to a value of type UINT16, whose low byte is stored in low + memory address and high byte is stored in high memory address. P contains decimal + digit characters in the range [0-9]. The running zero in the beginning of P will + be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid hexadecimal digit character after eight X's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + "::" can be used to compress one or more groups of X when X contains only 0. + The "::" can only appear once in the String. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated ASCII string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv6 address. + @param PrefixLength Pointer to the converted IPv6 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If X contains more than four hexadecimal + digit characters. + If String contains "::" and number of X + is not less than 8. + If P starts with character that is not a + valid decimal digit character. + If the decimal number converted from P + exceeds 128. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToIpv6Address ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT IPv6_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +/** + Convert a Null-terminated ASCII string to IPv4 address and prefix length. + + This function outputs a value of type IPv4_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the ASCII string specified + by String. The format of the input ASCII string String is as follows: + + D.D.D.D[/P] + + D and P are decimal digit characters in the range [0-9]. The running zero in + the beginning of D and P will be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid decimal digit character after four D's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated ASCII string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv4 address. + @param PrefixLength Pointer to the converted IPv4 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not in the correct format. + If any decimal number converted from D + exceeds 255. + If the decimal number converted from P + exceeds 32. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToIpv4Address ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT IPv4_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +/** + Convert a Null-terminated ASCII GUID string to a value of type + EFI_GUID. + + This function outputs a GUID value by interpreting the contents of + the ASCII string specified by String. The format of the input + ASCII string String consists of 36 characters, as follows: + + aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + + The pairs aa - pp are two characters in the range [0-9], [a-f] and + [A-F], with each pair representing a single byte hexadecimal value. + + The mapping between String and the EFI_GUID structure is as follows: + aa Data1[24:31] + bb Data1[16:23] + cc Data1[8:15] + dd Data1[0:7] + ee Data2[8:15] + ff Data2[0:7] + gg Data3[8:15] + hh Data3[0:7] + ii Data4[0:7] + jj Data4[8:15] + kk Data4[16:23] + ll Data4[24:31] + mm Data4[32:39] + nn Data4[40:47] + oo Data4[48:55] + pp Data4[56:63] + + If String is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + + @param String Pointer to a Null-terminated ASCII string. + @param Guid Pointer to the converted GUID. + + @retval RETURN_SUCCESS Guid is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not as the above format. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToGuid ( + IN CONST CHAR8 *String, + OUT GUID *Guid + ); + +/** + Convert a Null-terminated ASCII hexadecimal string to a byte array. + + This function outputs a byte array by interpreting the contents of + the ASCII string specified by String in hexadecimal format. The format of + the input ASCII string String is: + + [XX]* + + X is a hexadecimal digit character in the range [0-9], [a-f] and [A-F]. + The function decodes every two hexadecimal digit characters as one byte. The + decoding stops after Length of characters and outputs Buffer containing + (Length / 2) bytes. + + If String is NULL, then ASSERT(). + + If Buffer is NULL, then ASSERT(). + + If Length is not multiple of 2, then ASSERT(). + + If PcdMaximumAsciiStringLength is not zero and Length is greater than + PcdMaximumAsciiStringLength, then ASSERT(). + + If MaxBufferSize is less than (Length / 2), then ASSERT(). + + @param String Pointer to a Null-terminated ASCII string. + @param Length The number of ASCII characters to decode. + @param Buffer Pointer to the converted bytes array. + @param MaxBufferSize The maximum size of Buffer. + + @retval RETURN_SUCCESS Buffer is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If Length is not multiple of 2. + If PcdMaximumAsciiStringLength is not zero, + and Length is greater than + PcdMaximumAsciiStringLength. + @retval RETURN_UNSUPPORTED If Length of characters from String contain + a character that is not valid hexadecimal + digit characters, or a Null-terminator. + @retval RETURN_BUFFER_TOO_SMALL If MaxBufferSize is less than (Length / 2). +**/ +RETURN_STATUS +EFIAPI +AsciiStrHexToBytes ( + IN CONST CHAR8 *String, + IN UINTN Length, + OUT UINT8 *Buffer, + IN UINTN MaxBufferSize + ); + #ifndef DISABLE_NEW_DEPRECATED_INTERFACES /** @@ -1704,6 +2719,59 @@ AsciiStrToUnicodeStrS ( IN UINTN DestMax ); +/** + Convert not more than Length successive characters from a Null-terminated + Ascii string to a Null-terminated Unicode string. If no null char is copied + from Source, then Destination[Length] is always set to null. + + This function converts not more than Length successive characters from the + Ascii string Source to the Unicode string Destination. The function + terminates the Unicode string Destination by appending a Null-terminator + character at the end. + + The caller is responsible to make sure Destination points to a buffer with + size not smaller than + ((MIN(AsciiStrLen(Source), Length) + 1) * sizeof (CHAR8)) in bytes. + + If Destination is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then Destination and DestinationLength are + unmodified. + + @param Source The pointer to a Null-terminated Ascii string. + @param Length The maximum number of Ascii characters to convert. + @param Destination The pointer to a Null-terminated Unicode string. + @param DestMax The maximum number of Destination Unicode char, + including terminating null char. + @param DestinationLength The number of Ascii characters converted. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If DestinationLength is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and Length or DestMax is greater than + PcdMaximumUnicodeStringLength. + If PcdMaximumAsciiStringLength is not zero, + and Length or DestMax is greater than + PcdMaximumAsciiStringLength. + If DestMax is 0. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than + MIN(AsciiStrLen(Source), Length). + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrnToUnicodeStrS ( + IN CONST CHAR8 *Source, + IN UINTN Length, + OUT CHAR16 *Destination, + IN UINTN DestMax, + OUT UINTN *DestinationLength + ); + /** Converts an 8-bit value to an 8-bit BCD value. @@ -1749,8 +2817,7 @@ BcdToDecimal8 ( // /** - Removes the last directory or file entry in a path by changing the last - L'\' to a CHAR_NULL. + Removes the last directory or file entry in a path. @param[in, out] Path The pointer to the path to modify. diff --git a/src/include/ipxe/efi/Protocol/HiiDatabase.h b/src/include/ipxe/efi/Protocol/HiiDatabase.h index cbc0108b0..e070d29de 100644 --- a/src/include/ipxe/efi/Protocol/HiiDatabase.h +++ b/src/include/ipxe/efi/Protocol/HiiDatabase.h @@ -216,7 +216,7 @@ EFI_STATUS @param Handle An array of EFI_HII_HANDLE instances returned. - @retval EFI_SUCCESS The matching handles are outputed successfully. + @retval EFI_SUCCESS The matching handles are outputted successfully. HandleBufferLength is updated with the actual length. @retval EFI_BUFFER_TOO_SMALL The HandleBufferLength parameter indicates that Handle is too @@ -275,7 +275,7 @@ EFI_STATUS @retval EFI_OUT_OF_RESOURCES BufferSize is too small to hold the package. - @retval EFI_NOT_FOUND The specifiecd Handle could not be found in the + @retval EFI_NOT_FOUND The specified Handle could not be found in the current database. @retval EFI_INVALID_PARAMETER BufferSize was NULL. diff --git a/src/include/ipxe/efi/Protocol/SimpleTextIn.h b/src/include/ipxe/efi/Protocol/SimpleTextIn.h index 571ecaf30..e6d0eb24d 100644 --- a/src/include/ipxe/efi/Protocol/SimpleTextIn.h +++ b/src/include/ipxe/efi/Protocol/SimpleTextIn.h @@ -48,7 +48,6 @@ typedef struct { // // Required unicode control chars // -#define CHAR_NULL 0x0000 #define CHAR_BACKSPACE 0x0008 #define CHAR_TAB 0x0009 #define CHAR_LINEFEED 0x000A diff --git a/src/include/ipxe/efi/Uefi/UefiBaseType.h b/src/include/ipxe/efi/Uefi/UefiBaseType.h index ef0ea671a..5bfcccf34 100644 --- a/src/include/ipxe/efi/Uefi/UefiBaseType.h +++ b/src/include/ipxe/efi/Uefi/UefiBaseType.h @@ -1,7 +1,7 @@ /** @file Defines data types and constants introduced in UEFI. -Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
Portions copyright (c) 2011 - 2016, ARM Ltd. All rights reserved.
This program and the accompanying materials are licensed and made available under @@ -89,16 +89,12 @@ typedef struct { /// /// 4-byte buffer. An IPv4 internet protocol address. /// -typedef struct { - UINT8 Addr[4]; -} EFI_IPv4_ADDRESS; +typedef IPv4_ADDRESS EFI_IPv4_ADDRESS; /// /// 16-byte buffer. An IPv6 internet protocol address. /// -typedef struct { - UINT8 Addr[16]; -} EFI_IPv6_ADDRESS; +typedef IPv6_ADDRESS EFI_IPv6_ADDRESS; /// /// 32-byte buffer containing a network Media Access Control address. diff --git a/src/include/ipxe/efi/X64/ProcessorBind.h b/src/include/ipxe/efi/X64/ProcessorBind.h index ad7ae81a5..9f02e0c5b 100644 --- a/src/include/ipxe/efi/X64/ProcessorBind.h +++ b/src/include/ipxe/efi/X64/ProcessorBind.h @@ -273,6 +273,12 @@ typedef INT64 INTN; /// #define CPU_STACK_ALIGNMENT 16 +/// +/// Page allocation granularity for x64 +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000) + // // Modifier to ensure that all protocol member functions and EFI intrinsics // use the correct C calling convention. All protocol member functions and diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 48ec0968a..c4529f0f8 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -26,6 +26,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* EFI headers rudely redefine NULL */ #undef NULL +/* EFI headers redefine ARRAY_SIZE */ +#undef ARRAY_SIZE + /* EFI headers expect ICC to define __GNUC__ */ #if defined ( __ICC ) && ! defined ( __GNUC__ ) #define __GNUC__ 1 From 553f4857346faa8c5f6ddf9eced4180924890bfc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Mar 2017 21:51:59 +0000 Subject: [PATCH 372/591] [efi] Add EFI_ACPI_TABLE_PROTOCOL header and GUID definition Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/AcpiTable.h | 129 ++++++++++++++++++++++ src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_debug.c | 2 + src/interface/efi/efi_guid.c | 5 + 4 files changed, 137 insertions(+) create mode 100644 src/include/ipxe/efi/Protocol/AcpiTable.h diff --git a/src/include/ipxe/efi/Protocol/AcpiTable.h b/src/include/ipxe/efi/Protocol/AcpiTable.h new file mode 100644 index 000000000..798b13dc3 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/AcpiTable.h @@ -0,0 +1,129 @@ +/** @file + The file provides the protocol to install or remove an ACPI + table from a platform. + + Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __ACPI_TABLE_H___ +#define __ACPI_TABLE_H___ + +FILE_LICENCE ( BSD3 ); + +#define EFI_ACPI_TABLE_PROTOCOL_GUID \ + { 0xffe06bdd, 0x6107, 0x46a6, { 0x7b, 0xb2, 0x5a, 0x9c, 0x7e, 0xc5, 0x27, 0x5c }} + + +typedef struct _EFI_ACPI_TABLE_PROTOCOL EFI_ACPI_TABLE_PROTOCOL; + +/** + + The InstallAcpiTable() function allows a caller to install an + ACPI table. When successful, the table will be linked by the + RSDT/XSDT. AcpiTableBuffer specifies the table to be installed. + InstallAcpiTable() will make a copy of the table and insert the + copy into the RSDT/XSDT. InstallAcpiTable() must insert the new + table at the end of the RSDT/XSDT. To prevent namespace + collision, ACPI tables may be created using UEFI ACPI table + format. If this protocol is used to install a table with a + signature already present in the system, the new table will not + replace the existing table. It is a platform implementation + decision to add a new table with a signature matching an + existing table or disallow duplicate table signatures and + return EFI_ACCESS_DENIED. On successful output, TableKey is + initialized with a unique key. Its value may be used in a + subsequent call to UninstallAcpiTable to remove an ACPI table. + If an EFI application is running at the time of this call, the + relevant EFI_CONFIGURATION_TABLE pointer to the RSDT is no + longer considered valid. + + + @param This A pointer to a EFI_ACPI_TABLE_PROTOCOL. + + @param AcpiTableBuffer A pointer to a buffer containing the + ACPI table to be installed. + + @param AcpiTableBufferSize Specifies the size, in bytes, of + the AcpiTableBuffer buffer. + + + @param TableKey Returns a key to refer to the ACPI table. + + @retval EFI_SUCCESS The table was successfully inserted + + @retval EFI_INVALID_PARAMETER Either AcpiTableBuffer is NULL, + TableKey is NULL, or + AcpiTableBufferSize and the size + field embedded in the ACPI table + pointed to by AcpiTableBuffer + are not in sync. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources exist to + complete the request. + @retval EFI_ACCESS_DENIED The table signature matches a table already + present in the system and platform policy + does not allow duplicate tables of this type. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ACPI_TABLE_INSTALL_ACPI_TABLE)( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN VOID *AcpiTableBuffer, + IN UINTN AcpiTableBufferSize, + OUT UINTN *TableKey +); + + +/** + + The UninstallAcpiTable() function allows a caller to remove an + ACPI table. The routine will remove its reference from the + RSDT/XSDT. A table is referenced by the TableKey parameter + returned from a prior call to InstallAcpiTable(). If an EFI + application is running at the time of this call, the relevant + EFI_CONFIGURATION_TABLE pointer to the RSDT is no longer + considered valid. + + @param This A pointer to a EFI_ACPI_TABLE_PROTOCOL. + + @param TableKey Specifies the table to uninstall. The key was + returned from InstallAcpiTable(). + + @retval EFI_SUCCESS The table was successfully inserted + + @retval EFI_NOT_FOUND TableKey does not refer to a valid key + for a table entry. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources exist to + complete the request. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE)( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN UINTN TableKey +); + +/// +/// The EFI_ACPI_TABLE_PROTOCOL provides the ability for a component +/// to install and uninstall ACPI tables from a platform. +/// +struct _EFI_ACPI_TABLE_PROTOCOL { + EFI_ACPI_TABLE_INSTALL_ACPI_TABLE InstallAcpiTable; + EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE UninstallAcpiTable; +}; + +extern EFI_GUID gEfiAcpiTableProtocolGuid; + +#endif + diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index c4529f0f8..669e5364a 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -157,6 +157,7 @@ struct efi_config_table { #define EEFI( efirc ) EPLATFORM ( EINFO_EPLATFORM, efirc ) extern EFI_GUID efi_absolute_pointer_protocol_guid; +extern EFI_GUID efi_acpi_table_protocol_guid; extern EFI_GUID efi_apple_net_boot_protocol_guid; extern EFI_GUID efi_arp_protocol_guid; extern EFI_GUID efi_arp_service_binding_protocol_guid; diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index dc9ed85c4..8ea0a822d 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -71,6 +71,8 @@ struct efi_well_known_guid { static struct efi_well_known_guid efi_well_known_guids[] = { { &efi_absolute_pointer_protocol_guid, "AbsolutePointer" }, + { &efi_acpi_table_protocol_guid, + "AcpiTable" }, { &efi_apple_net_boot_protocol_guid, "AppleNetBoot" }, { &efi_arp_protocol_guid, diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index cd81876fc..b0278cd10 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -86,6 +87,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); EFI_GUID efi_absolute_pointer_protocol_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; +/** ACPI table protocol GUID */ +EFI_GUID efi_acpi_table_protocl_guid + = EFI_ACPI_TABLE_PROTOCOL_GUID; + /** Apple NetBoot protocol GUID */ EFI_GUID efi_apple_net_boot_protocol_guid = EFI_APPLE_NET_BOOT_PROTOCOL_GUID; From fdcdc5203b230fef23fa721573fd30ad093679d1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 13 Mar 2017 12:18:46 +0000 Subject: [PATCH 373/591] [efi] Provide ACPI table description for SAN devices Provide a basic proof of concept ACPI table description (e.g. iBFT for iSCSI) for SAN devices in a UEFI environment, using a control flow that is functionally identical to that used in a BIOS environment. Originally-implemented-by: Vishvananda Ishaya Abrams Signed-off-by: Michael Brown --- src/interface/efi/efi_block.c | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 8eb300a1a..10504f6a8 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -46,15 +46,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include +#include #include #include #include #include #include +/** ACPI table protocol protocol */ +static EFI_ACPI_TABLE_PROTOCOL *acpi; +EFI_REQUEST_PROTOCOL ( EFI_ACPI_TABLE_PROTOCOL, &acpi ); + /** Boot filename */ static wchar_t efi_block_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; @@ -399,7 +405,17 @@ static void efi_block_unhook ( unsigned int drive ) { * @ret rc Return status code */ static int efi_block_describe ( unsigned int drive ) { + static union { + /** ACPI header */ + struct acpi_description_header acpi; + /** Padding */ + char pad[768]; + } xbftab; + static UINTN key; struct san_device *sandev; + size_t len; + EFI_STATUS efirc; + int rc; /* Find SAN device */ sandev = sandev_find ( drive ); @@ -408,6 +424,59 @@ static int efi_block_describe ( unsigned int drive ) { return -ENODEV; } + /* Sanity check */ + if ( ! acpi ) { + DBGC ( sandev, "EFIBLK %#02x has no ACPI table protocol\n", + sandev->drive ); + return -ENOTSUP; + } + + /* Remove existing table, if any */ + if ( key ) { + if ( ( efirc = acpi->UninstallAcpiTable ( acpi, key ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not uninstall ACPI " + "table: %s\n", sandev->drive, strerror ( rc ) ); + /* Continue anyway */ + } + key = 0; + } + + /* Reopen block device if necessary */ + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) + return rc; + + /* Clear table */ + memset ( &xbftab, 0, sizeof ( xbftab ) ); + + /* Fill in common parameters */ + strncpy ( xbftab.acpi.oem_id, "FENSYS", + sizeof ( xbftab.acpi.oem_id ) ); + strncpy ( xbftab.acpi.oem_table_id, "iPXE", + sizeof ( xbftab.acpi.oem_table_id ) ); + + /* Fill in remaining parameters */ + if ( ( rc = acpi_describe ( &sandev->block, &xbftab.acpi, + sizeof ( xbftab ) ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x could not create ACPI " + "description: %s\n", sandev->drive, strerror ( rc ) ); + return rc; + } + len = le32_to_cpu ( xbftab.acpi.length ); + + /* Fix up ACPI checksum */ + acpi_fix_checksum ( &xbftab.acpi ); + + /* Install table */ + if ( ( efirc = acpi->InstallAcpiTable ( acpi, &xbftab, len, + &key ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not install ACPI table: " + "%s\n", sandev->drive, strerror ( rc ) ); + return rc; + } + return 0; } From 6324227dcaa820436da4acd88ee174d3e26f0d54 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 19 Mar 2017 13:22:33 +0000 Subject: [PATCH 374/591] [efi] Skip cable detection at initialisation where possible We currently request cable detection in PXE_OPCODE_INITIALIZE to work around buggy Emulex drivers (see commit c0b61ba ("[efi] Work around bugs in Emulex NII driver")). This causes problems with some other NII drivers (e.g. Mellanox), which may time out if the underlying link is intrinsically slow to come up. Attempt to work around both problems simultaneously by requesting cable detection only if the underlying NII driver does not support link status reporting via PXE_OPCODE_GET_STATUS. (This is based on a potentially incorrect assumption that the buggy Emulex drivers do not claim to report link status via PXE_OPCODE_GET_STATUS.) Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 42 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index d68b36cc3..d76bfb898 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -632,22 +632,6 @@ static int nii_initialise ( struct nii_nic *nii ) { return nii_initialise_flags ( nii, flags ); } -/** - * Initialise UNDI and detect cable - * - * @v nii NII NIC - * @ret rc Return status code - */ -static int nii_initialise_and_detect ( struct nii_nic *nii ) { - unsigned int flags; - - /* Initialise UNDI and detect cable. This is required to work - * around bugs in some Emulex NII drivers. - */ - flags = PXE_OPFLAGS_INITIALIZE_DETECT_CABLE; - return nii_initialise_flags ( nii, flags ); -} - /** * Shut down UNDI * @@ -968,20 +952,32 @@ static void nii_poll ( struct net_device *netdev ) { */ static int nii_open ( struct net_device *netdev ) { struct nii_nic *nii = netdev->priv; + unsigned int flags; int rc; /* Initialise NIC + * + * We don't care about link state here, and would prefer to + * have the NIC initialise even if no cable is present, to + * match the behaviour of all other iPXE drivers. * * Some Emulex NII drivers have a bug which prevents packets * from being sent or received unless we specifically ask it - * to detect cable presence during initialisation. Work - * around these buggy drivers by requesting cable detection at - * this point, even though we don't care about link state here - * (and would prefer to have the NIC initialise even if no - * cable is present, to match the behaviour of all other iPXE - * drivers). + * to detect cable presence during initialisation. + * + * Unfortunately, some other NII drivers (e.g. Mellanox) may + * time out and report failure if asked to detect cable + * presence during initialisation on links that are physically + * slow to reach link-up. + * + * Attempt to work around both of these problems by requesting + * cable detection at this point if any only if the driver is + * not capable of reporting link status changes at runtime via + * PXE_OPCODE_GET_STATUS. */ - if ( ( rc = nii_initialise_and_detect ( nii ) ) != 0 ) + flags = ( nii->media ? PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE + : PXE_OPFLAGS_INITIALIZE_DETECT_CABLE ); + if ( ( rc = nii_initialise_flags ( nii, flags ) ) != 0 ) goto err_initialise; /* Attempt to set station address */ From 7692a8ff02c078ef32dac4f05451f0ffa5872e64 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 19 Mar 2017 15:57:24 +0000 Subject: [PATCH 375/591] [undi] Move PXE API caller back into UNDI driver As of commit 10d19bd ("[pxe] Always retrieve cached DHCPACK and apply to relevant network device"), the UNDI driver has been the only user of pxeparent_call(). Remove the unnecessary layer of abstraction by refactoring this code back into undinet.c, and fix the ability of undiisr.S to fall back to chaining to the original handler if we were unable to unhook our own ISR. This effectively reverts commit 337e1ed ("[pxe] Separate parent PXE API caller from UNDINET driver"). Signed-off-by: Michael Brown --- src/arch/x86/Makefile | 1 - src/arch/x86/drivers/net/undiisr.S | 4 +- src/arch/x86/drivers/net/undinet.c | 364 +++++++++++++++---- src/arch/x86/include/pxeparent.h | 11 - src/arch/x86/interface/pxeparent/pxeparent.c | 287 --------------- 5 files changed, 300 insertions(+), 367 deletions(-) delete mode 100644 src/arch/x86/include/pxeparent.h delete mode 100644 src/arch/x86/interface/pxeparent/pxeparent.c diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 368c29f6d..011260cac 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -13,7 +13,6 @@ SRCDIRS += arch/x86/core SRCDIRS += arch/x86/image SRCDIRS += arch/x86/interface/pcbios SRCDIRS += arch/x86/interface/pxe -SRCDIRS += arch/x86/interface/pxeparent SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/interface/vmware SRCDIRS += arch/x86/interface/syslinux diff --git a/src/arch/x86/drivers/net/undiisr.S b/src/arch/x86/drivers/net/undiisr.S index b27effe1d..2428d1f5d 100644 --- a/src/arch/x86/drivers/net/undiisr.S +++ b/src/arch/x86/drivers/net/undiisr.S @@ -31,7 +31,7 @@ undiisr: movw %ax, %ds /* Check that we have an UNDI entry point */ - cmpw $0, pxeparent_entry_point + cmpw $0, undinet_entry_point je chain /* Issue UNDI API call */ @@ -42,7 +42,7 @@ undiisr: pushw %es pushw %di pushw %bx - lcall *pxeparent_entry_point + lcall *undinet_entry_point cli /* Just in case */ addw $6, %sp cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index 2afffa251..781911814 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -36,7 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include /** @file * @@ -56,6 +55,12 @@ struct undi_nic { int hacks; }; +/* Disambiguate the various error causes */ +#define EINFO_EPXECALL \ + __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ + "External PXE API error" ) +#define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status ) + /** * @defgroup undi_hacks UNDI workarounds * @{ @@ -80,29 +85,268 @@ struct undi_nic { static void undinet_close ( struct net_device *netdev ); -/** Address of UNDI entry point */ -static SEGOFF16_t undinet_entry; +/** + * UNDI parameter block + * + * Used as the parameter block for all UNDI API calls. Resides in + * base memory. + */ +static union u_PXENV_ANY __bss16 ( undinet_params ); +#define undinet_params __use_data16 ( undinet_params ) -/** Transmit profiler */ -static struct profiler undinet_tx_profiler __profiler = - { .name = "undinet.tx" }; - -/** Transmit call profiler */ -static struct profiler undinet_tx_call_profiler __profiler = - { .name = "undinet.tx_call" }; +/** + * UNDI entry point + * + * Used as the indirection vector for all UNDI API calls. Resides in + * base memory. + */ +SEGOFF16_t __bss16 ( undinet_entry_point ); +#define undinet_entry_point __use_data16 ( undinet_entry_point ) /** IRQ profiler */ static struct profiler undinet_irq_profiler __profiler = { .name = "undinet.irq" }; -/** ISR call profiler */ -static struct profiler undinet_isr_call_profiler __profiler = - { .name = "undinet.isr_call" }; - /** Receive profiler */ static struct profiler undinet_rx_profiler __profiler = { .name = "undinet.rx" }; +/** A PXE API call breakdown profiler */ +struct undinet_profiler { + /** Total time spent performing REAL_CALL() */ + struct profiler total; + /** Time spent transitioning to real mode */ + struct profiler p2r; + /** Time spent in external code */ + struct profiler ext; + /** Time spent transitioning back to protected mode */ + struct profiler r2p; +}; + +/** PXENV_UNDI_TRANSMIT profiler */ +static struct undinet_profiler undinet_tx_profiler __profiler = { + { .name = "undinet.tx" }, + { .name = "undinet.tx_p2r" }, + { .name = "undinet.tx_ext" }, + { .name = "undinet.tx_r2p" }, +}; + +/** PXENV_UNDI_ISR profiler + * + * Note that this profiler will not see calls to + * PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do + * not go via undinet_call(). + */ +static struct undinet_profiler undinet_isr_profiler __profiler = { + { .name = "undinet.isr" }, + { .name = "undinet.isr_p2r" }, + { .name = "undinet.isr_ext" }, + { .name = "undinet.isr_r2p" }, +}; + +/** PXE unknown API call profiler + * + * This profiler can be used to measure the overhead of a dummy PXE + * API call. + */ +static struct undinet_profiler undinet_unknown_profiler __profiler = { + { .name = "undinet.unknown" }, + { .name = "undinet.unknown_p2r" }, + { .name = "undinet.unknown_ext" }, + { .name = "undinet.unknown_r2p" }, +}; + +/** Miscellaneous PXE API call profiler */ +static struct undinet_profiler undinet_misc_profiler __profiler = { + { .name = "undinet.misc" }, + { .name = "undinet.misc_p2r" }, + { .name = "undinet.misc_ext" }, + { .name = "undinet.misc_r2p" }, +}; + +/***************************************************************************** + * + * UNDI API call + * + ***************************************************************************** + */ + +/** + * Name PXE API call + * + * @v function API call number + * @ret name API call name + */ +static inline __attribute__ (( always_inline )) const char * +undinet_function_name ( unsigned int function ) { + switch ( function ) { + case PXENV_START_UNDI: + return "PXENV_START_UNDI"; + case PXENV_STOP_UNDI: + return "PXENV_STOP_UNDI"; + case PXENV_UNDI_STARTUP: + return "PXENV_UNDI_STARTUP"; + case PXENV_UNDI_CLEANUP: + return "PXENV_UNDI_CLEANUP"; + case PXENV_UNDI_INITIALIZE: + return "PXENV_UNDI_INITIALIZE"; + case PXENV_UNDI_RESET_ADAPTER: + return "PXENV_UNDI_RESET_ADAPTER"; + case PXENV_UNDI_SHUTDOWN: + return "PXENV_UNDI_SHUTDOWN"; + case PXENV_UNDI_OPEN: + return "PXENV_UNDI_OPEN"; + case PXENV_UNDI_CLOSE: + return "PXENV_UNDI_CLOSE"; + case PXENV_UNDI_TRANSMIT: + return "PXENV_UNDI_TRANSMIT"; + case PXENV_UNDI_SET_MCAST_ADDRESS: + return "PXENV_UNDI_SET_MCAST_ADDRESS"; + case PXENV_UNDI_SET_STATION_ADDRESS: + return "PXENV_UNDI_SET_STATION_ADDRESS"; + case PXENV_UNDI_SET_PACKET_FILTER: + return "PXENV_UNDI_SET_PACKET_FILTER"; + case PXENV_UNDI_GET_INFORMATION: + return "PXENV_UNDI_GET_INFORMATION"; + case PXENV_UNDI_GET_STATISTICS: + return "PXENV_UNDI_GET_STATISTICS"; + case PXENV_UNDI_CLEAR_STATISTICS: + return "PXENV_UNDI_CLEAR_STATISTICS"; + case PXENV_UNDI_INITIATE_DIAGS: + return "PXENV_UNDI_INITIATE_DIAGS"; + case PXENV_UNDI_FORCE_INTERRUPT: + return "PXENV_UNDI_FORCE_INTERRUPT"; + case PXENV_UNDI_GET_MCAST_ADDRESS: + return "PXENV_UNDI_GET_MCAST_ADDRESS"; + case PXENV_UNDI_GET_NIC_TYPE: + return "PXENV_UNDI_GET_NIC_TYPE"; + case PXENV_UNDI_GET_IFACE_INFO: + return "PXENV_UNDI_GET_IFACE_INFO"; + /* + * Duplicate case value; this is a bug in the PXE specification. + * + * case PXENV_UNDI_GET_STATE: + * return "PXENV_UNDI_GET_STATE"; + */ + case PXENV_UNDI_ISR: + return "PXENV_UNDI_ISR"; + case PXENV_GET_CACHED_INFO: + return "PXENV_GET_CACHED_INFO"; + default: + return "UNKNOWN API CALL"; + } +} + +/** + * Determine applicable profiler pair (for debugging) + * + * @v function API call number + * @ret profiler Profiler + */ +static struct undinet_profiler * undinet_profiler ( unsigned int function ) { + + /* Determine applicable profiler */ + switch ( function ) { + case PXENV_UNDI_TRANSMIT: + return &undinet_tx_profiler; + case PXENV_UNDI_ISR: + return &undinet_isr_profiler; + case PXENV_UNKNOWN: + return &undinet_unknown_profiler; + default: + return &undinet_misc_profiler; + } +} + +/** + * Issue UNDI API call + * + * @v undinic UNDI NIC + * @v function API call number + * @v params PXE parameter block + * @v params_len Length of PXE parameter block + * @ret rc Return status code + */ +static int undinet_call ( struct undi_nic *undinic, unsigned int function, + void *params, size_t params_len ) { + struct undinet_profiler *profiler = undinet_profiler ( function ); + PXENV_EXIT_t exit; + uint32_t before; + uint32_t started; + uint32_t stopped; + uint32_t after; + int discard_D; + int rc; + + /* Copy parameter block and entry point */ + assert ( params_len <= sizeof ( undinet_params ) ); + memcpy ( &undinet_params, params, params_len ); + + /* Call real-mode entry point. This calling convention will + * work with both the !PXE and the PXENV+ entry points. + */ + profile_start ( &profiler->total ); + __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ + "rdtsc\n\t" + "pushl %%eax\n\t" + "pushw %%es\n\t" + "pushw %%di\n\t" + "pushw %%bx\n\t" + "lcall *undinet_entry_point\n\t" + "movw %%ax, %%bx\n\t" + "rdtsc\n\t" + "addw $6, %%sp\n\t" + "popl %%edx\n\t" + "popl %%ebp\n\t" /* gcc bug */ ) + : "=a" ( stopped ), "=d" ( started ), + "=b" ( exit ), "=D" ( discard_D ) + : "b" ( function ), + "D" ( __from_data16 ( &undinet_params ) ) + : "ecx", "esi" ); + profile_stop ( &profiler->total ); + before = profile_started ( &profiler->total ); + after = profile_stopped ( &profiler->total ); + profile_start_at ( &profiler->p2r, before ); + profile_stop_at ( &profiler->p2r, started ); + profile_start_at ( &profiler->ext, started ); + profile_stop_at ( &profiler->ext, stopped ); + profile_start_at ( &profiler->r2p, stopped ); + profile_stop_at ( &profiler->r2p, after ); + + /* Determine return status code based on PXENV_EXIT and + * PXENV_STATUS + */ + rc = ( ( exit == PXENV_EXIT_SUCCESS ) ? + 0 : -EPXECALL ( undinet_params.Status ) ); + + /* If anything goes wrong, print as much debug information as + * it's possible to give. + */ + if ( rc != 0 ) { + SEGOFF16_t rm_params = { + .segment = rm_ds, + .offset = __from_data16 ( &undinet_params ), + }; + + DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic, + undinet_function_name ( function ), strerror ( rc ) ); + DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length " + "%#02zx, entry point at %04x:%04x\n", undinic, + rm_params.segment, rm_params.offset, params_len, + undinet_entry_point.segment, + undinet_entry_point.offset ); + DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic ); + DBGC_HDA ( undinic, rm_params, params, params_len ); + DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic ); + DBGC_HDA ( undinic, rm_params, &undinet_params, params_len ); + } + + /* Copy parameter block back */ + memcpy ( params, &undinet_params, params_len ); + + return rc; +} + /***************************************************************************** * * UNDI interrupt service routine @@ -216,9 +460,6 @@ static int undinet_transmit ( struct net_device *netdev, size_t len; int rc; - /* Start profiling */ - profile_start ( &undinet_tx_profiler ); - /* Technically, we ought to make sure that the previous * transmission has completed before we re-use the buffer. * However, many PXE stacks (including at least some Intel PXE @@ -281,16 +522,12 @@ static int undinet_transmit ( struct net_device *netdev, undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet ); /* Issue PXE API call */ - profile_start ( &undinet_tx_call_profiler ); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT, - &undi_transmit, - sizeof ( undi_transmit ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, &undi_transmit, + sizeof ( undi_transmit ) ) ) != 0 ) goto done; - profile_stop ( &undinet_tx_call_profiler ); /* Free I/O buffer */ netdev_tx_complete ( netdev, iobuf ); - profile_stop ( &undinet_tx_profiler ); done: return rc; } @@ -369,14 +606,11 @@ static void undinet_poll ( struct net_device *netdev ) { /* Run through the ISR loop */ while ( quota ) { - profile_start ( &undinet_isr_call_profiler ); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR, - &undi_isr, - sizeof ( undi_isr ) ) ) != 0 ) { + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr, + sizeof ( undi_isr ) ) ) != 0 ) { netdev_rx_err ( netdev, NULL, rc ); break; } - profile_stop ( &undinet_isr_call_profiler ); switch ( undi_isr.FuncFlag ) { case PXENV_UNDI_ISR_OUT_TRANSMIT: /* We don't care about transmit completions */ @@ -480,8 +714,8 @@ static int undinet_open ( struct net_device *netdev ) { */ memcpy ( undi_set_address.StationAddress, netdev->ll_addr, sizeof ( undi_set_address.StationAddress ) ); - pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS, - &undi_set_address, sizeof ( undi_set_address ) ); + undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS, + &undi_set_address, sizeof ( undi_set_address ) ); /* Open NIC. We ask for promiscuous operation, since it's the * only way to ask for all multicast addresses. On any @@ -490,8 +724,8 @@ static int undinet_open ( struct net_device *netdev ) { */ memset ( &undi_open, 0, sizeof ( undi_open ) ); undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS ); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN, - &undi_open, sizeof ( undi_open ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open, + sizeof ( undi_open ) ) ) != 0 ) goto err; DBGC ( undinic, "UNDINIC %p opened\n", undinic ); @@ -516,9 +750,8 @@ static void undinet_close ( struct net_device *netdev ) { /* Ensure ISR has exited cleanly */ while ( undinic->isr_processing ) { undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR, - &undi_isr, - sizeof ( undi_isr ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr, + sizeof ( undi_isr ) ) ) != 0 ) break; switch ( undi_isr.FuncFlag ) { case PXENV_UNDI_ISR_OUT_TRANSMIT: @@ -533,8 +766,8 @@ static void undinet_close ( struct net_device *netdev ) { } /* Close NIC */ - pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE, - &undi_close, sizeof ( undi_close ) ); + undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close, + sizeof ( undi_close ) ); /* Disable interrupt and unhook ISR if applicable */ if ( undinic->irq ) { @@ -647,7 +880,7 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { undi_set_drvdata ( undi, netdev ); netdev->dev = dev; memset ( undinic, 0, sizeof ( *undinic ) ); - undinet_entry = undi->entry; + undinet_entry_point = undi->entry; DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi ); /* Hook in UNDI stack */ @@ -658,9 +891,9 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { start_undi.DX = undi->isapnp_read_port; start_undi.ES = BIOS_SEG; start_undi.DI = find_pnp_bios(); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI, - &start_undi, - sizeof ( start_undi ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI, + &start_undi, + sizeof ( start_undi ) ) ) != 0 ) goto err_start_undi; } undi->flags |= UNDI_FL_STARTED; @@ -668,9 +901,9 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { /* Bring up UNDI stack */ if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) { memset ( &undi_startup, 0, sizeof ( undi_startup ) ); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP, - &undi_startup, - sizeof ( undi_startup ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP, + &undi_startup, + sizeof ( undi_startup ) ) ) != 0 ) goto err_undi_startup; /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail * due to a transient condition (e.g. media test @@ -680,10 +913,10 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { */ for ( retry = 0 ; ; ) { memset ( &undi_init, 0, sizeof ( undi_init ) ); - if ( ( rc = pxeparent_call ( undinet_entry, - PXENV_UNDI_INITIALIZE, - &undi_init, - sizeof ( undi_init ))) ==0) + if ( ( rc = undinet_call ( undinic, + PXENV_UNDI_INITIALIZE, + &undi_init, + sizeof ( undi_init ) ) ) ==0) break; if ( ++retry > UNDI_INITIALIZE_RETRY_MAX ) goto err_undi_initialize; @@ -698,8 +931,8 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { /* Get device information */ memset ( &undi_info, 0, sizeof ( undi_info ) ); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION, - &undi_info, sizeof ( undi_info ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION, + &undi_info, sizeof ( undi_info ) ) ) != 0 ) goto err_undi_get_information; memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN ); memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN ); @@ -715,9 +948,8 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { /* Get interface information */ memset ( &undi_iface, 0, sizeof ( undi_iface ) ); - if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO, - &undi_iface, - sizeof ( undi_iface ) ) ) != 0 ) + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO, + &undi_iface, sizeof ( undi_iface ) ) ) != 0 ) goto err_undi_get_iface_info; DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n", undinic, undi_iface.IfaceType, undi_iface.LinkSpeed, @@ -757,17 +989,17 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { err_undi_initialize: /* Shut down UNDI stack */ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) ); - pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown, - sizeof ( undi_shutdown ) ); + undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown, + sizeof ( undi_shutdown ) ); memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); - pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup, - sizeof ( undi_cleanup ) ); + undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup, + sizeof ( undi_cleanup ) ); undi->flags &= ~UNDI_FL_INITIALIZED; err_undi_startup: /* Unhook UNDI stack */ memset ( &stop_undi, 0, sizeof ( stop_undi ) ); - pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi, - sizeof ( stop_undi ) ); + undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, + sizeof ( stop_undi ) ); undi->flags &= ~UNDI_FL_STARTED; err_start_undi: netdev_nullify ( netdev ); @@ -798,22 +1030,22 @@ void undinet_remove ( struct undi_device *undi ) { /* Shut down UNDI stack */ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) ); - pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, - &undi_shutdown, sizeof ( undi_shutdown ) ); + undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, + &undi_shutdown, sizeof ( undi_shutdown ) ); memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); - pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, - &undi_cleanup, sizeof ( undi_cleanup ) ); + undinet_call ( undinic, PXENV_UNDI_CLEANUP, + &undi_cleanup, sizeof ( undi_cleanup ) ); undi->flags &= ~UNDI_FL_INITIALIZED; /* Unhook UNDI stack */ memset ( &stop_undi, 0, sizeof ( stop_undi ) ); - pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi, - sizeof ( stop_undi ) ); + undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, + sizeof ( stop_undi ) ); undi->flags &= ~UNDI_FL_STARTED; } /* Clear entry point */ - memset ( &undinet_entry, 0, sizeof ( undinet_entry ) ); + memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) ); /* Free network device */ netdev_nullify ( netdev ); diff --git a/src/arch/x86/include/pxeparent.h b/src/arch/x86/include/pxeparent.h deleted file mode 100644 index b31e24a76..000000000 --- a/src/arch/x86/include/pxeparent.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef PXEPARENT_H -#define PXEPARENT_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include - -extern int pxeparent_call ( SEGOFF16_t entry, unsigned int function, - void *params, size_t params_len ); - -#endif diff --git a/src/arch/x86/interface/pxeparent/pxeparent.c b/src/arch/x86/interface/pxeparent/pxeparent.c deleted file mode 100644 index cc6101c1f..000000000 --- a/src/arch/x86/interface/pxeparent/pxeparent.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include -#include -#include -#include - -/** @file - * - * Call interface to parent PXE stack - * - */ - -/* Disambiguate the various error causes */ -#define EINFO_EPXECALL \ - __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ - "External PXE API error" ) -#define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status ) - -/** A parent PXE API call profiler */ -struct pxeparent_profiler { - /** Total time spent performing REAL_CALL() */ - struct profiler total; - /** Time spent transitioning to real mode */ - struct profiler p2r; - /** Time spent in external code */ - struct profiler ext; - /** Time spent transitioning back to protected mode */ - struct profiler r2p; -}; - -/** PXENV_UNDI_TRANSMIT profiler */ -static struct pxeparent_profiler pxeparent_tx_profiler __profiler = { - { .name = "pxeparent.tx" }, - { .name = "pxeparent.tx_p2r" }, - { .name = "pxeparent.tx_ext" }, - { .name = "pxeparent.tx_r2p" }, -}; - -/** PXENV_UNDI_ISR profiler - * - * Note that this profiler will not see calls to - * PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do - * not go via pxeparent_call(). - */ -static struct pxeparent_profiler pxeparent_isr_profiler __profiler = { - { .name = "pxeparent.isr" }, - { .name = "pxeparent.isr_p2r" }, - { .name = "pxeparent.isr_ext" }, - { .name = "pxeparent.isr_r2p" }, -}; - -/** PXE unknown API call profiler - * - * This profiler can be used to measure the overhead of a dummy PXE - * API call. - */ -static struct pxeparent_profiler pxeparent_unknown_profiler __profiler = { - { .name = "pxeparent.unknown" }, - { .name = "pxeparent.unknown_p2r" }, - { .name = "pxeparent.unknown_ext" }, - { .name = "pxeparent.unknown_r2p" }, -}; - -/** Miscellaneous PXE API call profiler */ -static struct pxeparent_profiler pxeparent_misc_profiler __profiler = { - { .name = "pxeparent.misc" }, - { .name = "pxeparent.misc_p2r" }, - { .name = "pxeparent.misc_ext" }, - { .name = "pxeparent.misc_r2p" }, -}; - -/** - * Name PXE API call - * - * @v function API call number - * @ret name API call name - */ -static inline __attribute__ (( always_inline )) const char * -pxeparent_function_name ( unsigned int function ) { - switch ( function ) { - case PXENV_START_UNDI: - return "PXENV_START_UNDI"; - case PXENV_STOP_UNDI: - return "PXENV_STOP_UNDI"; - case PXENV_UNDI_STARTUP: - return "PXENV_UNDI_STARTUP"; - case PXENV_UNDI_CLEANUP: - return "PXENV_UNDI_CLEANUP"; - case PXENV_UNDI_INITIALIZE: - return "PXENV_UNDI_INITIALIZE"; - case PXENV_UNDI_RESET_ADAPTER: - return "PXENV_UNDI_RESET_ADAPTER"; - case PXENV_UNDI_SHUTDOWN: - return "PXENV_UNDI_SHUTDOWN"; - case PXENV_UNDI_OPEN: - return "PXENV_UNDI_OPEN"; - case PXENV_UNDI_CLOSE: - return "PXENV_UNDI_CLOSE"; - case PXENV_UNDI_TRANSMIT: - return "PXENV_UNDI_TRANSMIT"; - case PXENV_UNDI_SET_MCAST_ADDRESS: - return "PXENV_UNDI_SET_MCAST_ADDRESS"; - case PXENV_UNDI_SET_STATION_ADDRESS: - return "PXENV_UNDI_SET_STATION_ADDRESS"; - case PXENV_UNDI_SET_PACKET_FILTER: - return "PXENV_UNDI_SET_PACKET_FILTER"; - case PXENV_UNDI_GET_INFORMATION: - return "PXENV_UNDI_GET_INFORMATION"; - case PXENV_UNDI_GET_STATISTICS: - return "PXENV_UNDI_GET_STATISTICS"; - case PXENV_UNDI_CLEAR_STATISTICS: - return "PXENV_UNDI_CLEAR_STATISTICS"; - case PXENV_UNDI_INITIATE_DIAGS: - return "PXENV_UNDI_INITIATE_DIAGS"; - case PXENV_UNDI_FORCE_INTERRUPT: - return "PXENV_UNDI_FORCE_INTERRUPT"; - case PXENV_UNDI_GET_MCAST_ADDRESS: - return "PXENV_UNDI_GET_MCAST_ADDRESS"; - case PXENV_UNDI_GET_NIC_TYPE: - return "PXENV_UNDI_GET_NIC_TYPE"; - case PXENV_UNDI_GET_IFACE_INFO: - return "PXENV_UNDI_GET_IFACE_INFO"; - /* - * Duplicate case value; this is a bug in the PXE specification. - * - * case PXENV_UNDI_GET_STATE: - * return "PXENV_UNDI_GET_STATE"; - */ - case PXENV_UNDI_ISR: - return "PXENV_UNDI_ISR"; - case PXENV_GET_CACHED_INFO: - return "PXENV_GET_CACHED_INFO"; - default: - return "UNKNOWN API CALL"; - } -} - -/** - * Determine applicable profiler pair (for debugging) - * - * @v function API call number - * @ret profiler Profiler - */ -static struct pxeparent_profiler * pxeparent_profiler ( unsigned int function ){ - - /* Determine applicable profiler */ - switch ( function ) { - case PXENV_UNDI_TRANSMIT: - return &pxeparent_tx_profiler; - case PXENV_UNDI_ISR: - return &pxeparent_isr_profiler; - case PXENV_UNKNOWN: - return &pxeparent_unknown_profiler; - default: - return &pxeparent_misc_profiler; - } -} - -/** - * PXE parent parameter block - * - * Used as the parameter block for all parent PXE API calls. Resides - * in base memory. - */ -static union u_PXENV_ANY __bss16 ( pxeparent_params ); -#define pxeparent_params __use_data16 ( pxeparent_params ) - -/** PXE parent entry point - * - * Used as the indirection vector for all parent PXE API calls. Resides in - * base memory. - */ -SEGOFF16_t __bss16 ( pxeparent_entry_point ); -#define pxeparent_entry_point __use_data16 ( pxeparent_entry_point ) - -/** - * Issue parent PXE API call - * - * @v entry Parent PXE stack entry point - * @v function API call number - * @v params PXE parameter block - * @v params_len Length of PXE parameter block - * @ret rc Return status code - */ -int pxeparent_call ( SEGOFF16_t entry, unsigned int function, - void *params, size_t params_len ) { - struct pxeparent_profiler *profiler = pxeparent_profiler ( function ); - PXENV_EXIT_t exit; - uint32_t before; - uint32_t started; - uint32_t stopped; - uint32_t after; - int discard_D; - int rc; - - /* Copy parameter block and entry point */ - assert ( params_len <= sizeof ( pxeparent_params ) ); - memcpy ( &pxeparent_params, params, params_len ); - memcpy ( &pxeparent_entry_point, &entry, sizeof ( entry ) ); - - /* Call real-mode entry point. This calling convention will - * work with both the !PXE and the PXENV+ entry points. - */ - profile_start ( &profiler->total ); - __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ - "rdtsc\n\t" - "pushl %%eax\n\t" - "pushw %%es\n\t" - "pushw %%di\n\t" - "pushw %%bx\n\t" - "lcall *pxeparent_entry_point\n\t" - "movw %%ax, %%bx\n\t" - "rdtsc\n\t" - "addw $6, %%sp\n\t" - "popl %%edx\n\t" - "popl %%ebp\n\t" /* gcc bug */ ) - : "=a" ( stopped ), "=d" ( started ), - "=b" ( exit ), "=D" ( discard_D ) - : "b" ( function ), - "D" ( __from_data16 ( &pxeparent_params ) ) - : "ecx", "esi" ); - profile_stop ( &profiler->total ); - before = profile_started ( &profiler->total ); - after = profile_stopped ( &profiler->total ); - profile_start_at ( &profiler->p2r, before ); - profile_stop_at ( &profiler->p2r, started ); - profile_start_at ( &profiler->ext, started ); - profile_stop_at ( &profiler->ext, stopped ); - profile_start_at ( &profiler->r2p, stopped ); - profile_stop_at ( &profiler->r2p, after ); - - /* Determine return status code based on PXENV_EXIT and - * PXENV_STATUS - */ - rc = ( ( exit == PXENV_EXIT_SUCCESS ) ? - 0 : -EPXECALL ( pxeparent_params.Status ) ); - - /* If anything goes wrong, print as much debug information as - * it's possible to give. - */ - if ( rc != 0 ) { - SEGOFF16_t rm_params = { - .segment = rm_ds, - .offset = __from_data16 ( &pxeparent_params ), - }; - - DBG ( "PXEPARENT %s failed: %s\n", - pxeparent_function_name ( function ), strerror ( rc ) ); - DBG ( "PXEPARENT parameters at %04x:%04x length " - "%#02zx, entry point at %04x:%04x\n", - rm_params.segment, rm_params.offset, params_len, - pxeparent_entry_point.segment, - pxeparent_entry_point.offset ); - DBG ( "PXEPARENT parameters provided:\n" ); - DBG_HDA ( rm_params, params, params_len ); - DBG ( "PXEPARENT parameters returned:\n" ); - DBG_HDA ( rm_params, &pxeparent_params, params_len ); - } - - /* Copy parameter block back */ - memcpy ( params, &pxeparent_params, params_len ); - - return rc; -} - From de2c6fa240fc3b42c91c06775de6a26eb4aa3faf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 20 Mar 2017 13:58:59 +0200 Subject: [PATCH 376/591] [dhcp] Allow vendor class to be changed in DHCP requests Allow the DHCPv4 vendor class to be specified via the "vendor-class" setting. Signed-off-by: Michael Brown --- src/core/settings.c | 9 +++++++++ src/include/ipxe/settings.h | 2 ++ src/net/udp/dhcp.c | 34 +++++++++++++++++++++++----------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index e60a882a7..f5be5c4eb 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -2429,6 +2429,15 @@ const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA, .type = &setting_type_string, }; +/** DHCP vendor class setting */ +const struct setting vendor_class_setting __setting ( SETTING_HOST_EXTRA, + vendor-class ) = { + .name = "vendor-class", + .description = "DHCP vendor class", + .tag = DHCP_VENDOR_CLASS_ID, + .type = &setting_type_string, +}; + /****************************************************************************** * * Built-in settings block diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 341fc3c7f..521efa99d 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -468,6 +468,8 @@ busid_setting __setting ( SETTING_NETDEV, busid ); extern const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA, user-class ); extern const struct setting +vendor_class_setting __setting ( SETTING_HOST_EXTRA, vendor-class ); +extern const struct setting manufacturer_setting __setting ( SETTING_HOST_EXTRA, manufacturer ); extern const struct setting product_setting __setting ( SETTING_HOST_EXTRA, product ); diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 95f80821e..3a3666c9a 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -99,6 +99,12 @@ static uint8_t dhcp_request_options_data[] = { DHCP_END }; +/** Settings copied in to all DHCP requests */ +static const struct setting * dhcp_request_settings[] = { + &user_class_setting, + &vendor_class_setting, +}; + /** DHCP server address setting */ const struct setting dhcp_server_setting __setting ( SETTING_MISC, dhcp-server ) = { @@ -975,11 +981,13 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, struct dhcp_netdev_desc dhcp_desc; struct dhcp_client_id client_id; struct dhcp_client_uuid client_uuid; + const struct setting *setting; uint8_t *dhcp_features; size_t dhcp_features_len; size_t ll_addr_len; - void *user_class; + void *raw; ssize_t len; + unsigned int i; int rc; /* Create DHCP packet */ @@ -1047,19 +1055,23 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, } } - /* Add user class, if we have one. */ - if ( ( len = fetch_raw_setting_copy ( NULL, &user_class_setting, - &user_class ) ) >= 0 ) { - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID, - user_class, len ) ) != 0 ) { - DBG ( "DHCP could not set user class: %s\n", - strerror ( rc ) ); - goto err_store_user_class; + /* Add request settings, if applicable */ + for ( i = 0 ; i < ( sizeof ( dhcp_request_settings ) / + sizeof ( dhcp_request_settings[0] ) ) ; i++ ) { + setting = dhcp_request_settings[i]; + if ( ( len = fetch_raw_setting_copy ( NULL, setting, + &raw ) ) >= 0 ) { + rc = dhcppkt_store ( dhcppkt, setting->tag, raw, len ); + free ( raw ); + if ( rc != 0 ) { + DBG ( "DHCP could not set %s: %s\n", + setting->name, strerror ( rc ) ); + goto err_store_raw; + } } } - err_store_user_class: - free ( user_class ); + err_store_raw: err_store_client_uuid: err_store_client_id: err_store_busid: From 6ee62eb24220d8e364728c007f0d6cb8285e544a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 11:46:17 +0200 Subject: [PATCH 377/591] [hermon] Avoid potential integer overflow when calculating memory mappings When the area to be mapped straddles the 2GB boundary, the expression (high+size) will overflow on the first loop iteration. Fix by using (end-size), which cannot underflow. Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 79d606093..2199a9d98 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -2135,7 +2135,7 @@ static int hermon_map_vpm ( struct hermon *hermon, if ( ( low - size ) >= start ) { low -= size; pa = low; - } else if ( ( high + size ) <= end ) { + } else if ( high <= ( end - size ) ) { pa = high; high += size; } else { From a5affc832e2ae5fbbc88aafa452354fa418578b4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 11:46:17 +0200 Subject: [PATCH 378/591] [arbel] Avoid potential integer overflow when calculating memory mappings When the area to be mapped straddles the 2GB boundary, the expression (high+size) will overflow on the first loop iteration. Fix by using (end-size), which cannot underflow. Signed-off-by: Michael Brown --- src/drivers/infiniband/arbel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index 9671174c3..ea65d8b8d 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -1994,7 +1994,7 @@ static int arbel_map_vpm ( struct arbel *arbel, if ( ( low - size ) >= start ) { low -= size; pa = low; - } else if ( ( high + size ) <= end ) { + } else if ( high <= ( end - size ) ) { pa = high; high += size; } else { From 91372d6dab27b3471f89bbef20534fb0905a3a1a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 13:38:39 +0200 Subject: [PATCH 379/591] [xfer] Ensure va_end() is called on failure path Signed-off-by: Michael Brown --- src/core/xfer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/xfer.c b/src/core/xfer.c index 3a2f174d0..0faf3292a 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -306,11 +306,11 @@ int xfer_vprintf ( struct interface *intf, const char *format, /* Create temporary string */ va_copy ( args_tmp, args ); len = vasprintf ( &buf, format, args ); + va_end ( args_tmp ); if ( len < 0 ) { rc = len; goto err_asprintf; } - va_end ( args_tmp ); /* Transmit string */ if ( ( rc = xfer_deliver_raw ( intf, buf, len ) ) != 0 ) From e500e5dd07c17d8c8ff5177ddb0a883d89998918 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 13:45:17 +0200 Subject: [PATCH 380/591] [nfs] Fix double free bug on error path Signed-off-by: Michael Brown --- src/net/tcp/oncrpc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/net/tcp/oncrpc.c b/src/net/tcp/oncrpc.c index 6469867e9..cb66aeb85 100644 --- a/src/net/tcp/oncrpc.c +++ b/src/net/tcp/oncrpc.c @@ -128,7 +128,6 @@ void oncrpc_init_session ( struct oncrpc_session *session, int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, uint32_t proc_name, const struct oncrpc_field fields[] ) { - int rc; size_t frame_size; struct io_buffer *io_buf; @@ -161,11 +160,7 @@ int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, oncrpc_iob_add_fields ( io_buf, header ); oncrpc_iob_add_fields ( io_buf, fields ); - rc = xfer_deliver_iob ( intf, io_buf ); - if ( rc != 0 ) - free_iob ( io_buf ); - - return rc; + return xfer_deliver_iob ( intf, iob_disown ( io_buf ) ); } size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) { From 6ee15cbac358914fef8bf9d9a9e5ea8bee29c99d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 13:50:51 +0200 Subject: [PATCH 381/591] [linda] Use correct length for memset() Signed-off-by: Michael Brown --- src/drivers/infiniband/linda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/infiniband/linda.c b/src/drivers/infiniband/linda.c index 77d50d110..e8d61c865 100644 --- a/src/drivers/infiniband/linda.c +++ b/src/drivers/infiniband/linda.c @@ -537,7 +537,7 @@ static int linda_init_send ( struct linda *linda ) { rc = -ENOMEM; goto err_alloc_sendbufavail; } - memset ( linda->sendbufavail, 0, sizeof ( linda->sendbufavail ) ); + memset ( linda->sendbufavail, 0, sizeof ( *linda->sendbufavail ) ); /* Program SendBufAvailAddr into the hardware */ memset ( &sendbufavailaddr, 0, sizeof ( sendbufavailaddr ) ); From ae915aa5cc6eb788604f0a8033216f1b6fc472cd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 13:51:56 +0200 Subject: [PATCH 382/591] [qib7322] Use correct length for memset() Signed-off-by: Michael Brown --- src/drivers/infiniband/qib7322.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c index af7006e04..18011c19a 100644 --- a/src/drivers/infiniband/qib7322.c +++ b/src/drivers/infiniband/qib7322.c @@ -675,7 +675,7 @@ static int qib7322_init_send ( struct qib7322 *qib7322 ) { rc = -ENOMEM; goto err_alloc_sendbufavail; } - memset ( qib7322->sendbufavail, 0, sizeof ( qib7322->sendbufavail ) ); + memset ( qib7322->sendbufavail, 0, sizeof ( *qib7322->sendbufavail ) ); /* Program SendBufAvailAddr into the hardware */ memset ( &sendbufavailaddr, 0, sizeof ( sendbufavailaddr ) ); From eb6acabc8f85d4f4e3c73a56b0e6372da01b5bf5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 13:55:04 +0200 Subject: [PATCH 383/591] [sis900] Remove extraneous memset() with incorrect length Signed-off-by: Michael Brown --- src/drivers/net/sis900.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/drivers/net/sis900.c b/src/drivers/net/sis900.c index 724515672..8a3ac01bc 100644 --- a/src/drivers/net/sis900.c +++ b/src/drivers/net/sis900.c @@ -249,7 +249,7 @@ static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic * MAC address is read into @net_dev->dev_addr. */ -static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic) +static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic __unused) { #if 0 u8 reg; @@ -279,7 +279,6 @@ static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic #endif /* Does not work with current bus/device model */ - memset ( nic->node_addr, 0, sizeof ( nic->node_addr ) ); return 0; } From 9b581158b5f1aaddd70eec5c303a2ad94a244f40 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:01:08 +0200 Subject: [PATCH 384/591] [802.11] Remove redundant NULL pointer check after dereference Signed-off-by: Michael Brown --- src/net/80211/net80211.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c index 62912741f..482000102 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -1586,9 +1586,6 @@ struct list_head *net80211_probe_finish_all ( struct net80211_probe_ctx *ctx ) { struct list_head *beacons = ctx->beacons; - if ( ! ctx ) - return NULL; - net80211_keep_mgmt ( ctx->dev, ctx->old_keep_mgmt ); if ( ctx->probe ) From 36cffe054d2229ca21c093f28957e45fd09ba921 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:07:07 +0200 Subject: [PATCH 385/591] [crypto] Free correct pointer on the error path Signed-off-by: Michael Brown --- src/image/pem.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/image/pem.c b/src/image/pem.c index 721b11ec6..3f5d1f302 100644 --- a/src/image/pem.c +++ b/src/image/pem.c @@ -145,7 +145,7 @@ static int pem_asn1 ( struct image *image, size_t offset, *cursor = malloc ( sizeof ( **cursor ) + decoded_max_len ); if ( ! *cursor ) { rc = -ENOMEM; - goto err_alloc_decoded; + goto err_alloc_cursor; } decoded = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); @@ -172,8 +172,9 @@ static int pem_asn1 ( struct image *image, size_t offset, return offset; err_decode: - free ( decoded ); - err_alloc_decoded: + free ( *cursor ); + *cursor = NULL; + err_alloc_cursor: free ( encoded ); err_alloc_encoded: err_end: From d25e7daf47efbefa83bbf9c07e044cdf747c7fda Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:17:18 +0200 Subject: [PATCH 386/591] [librm] Fail gracefully if asked to ioremap() a zero length Signed-off-by: Michael Brown --- src/arch/x86/transitions/librm_mgmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c index 8776f2854..8144e7671 100644 --- a/src/arch/x86/transitions/librm_mgmt.c +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -197,7 +197,8 @@ static void * ioremap_pages ( unsigned long bus_addr, size_t len ) { DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len ); /* Sanity check */ - assert ( len != 0 ); + if ( ! len ) + return NULL; /* Round down start address to a page boundary */ start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) ); From 7b113bc744a4c15f25c2dfb4b8560da8be21bfbd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:21:54 +0200 Subject: [PATCH 387/591] [usb] Use correct length for memcpy() Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 880e3f08c..cad26c77b 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -1022,7 +1022,8 @@ static int usb_describe ( struct usb_device *usb, } /* Describe function */ - memcpy ( &desc->class, &interface->class, sizeof ( desc->class ) ); + memcpy ( &desc->class.class, &interface->class, + sizeof ( desc->class.class ) ); desc->count = 1; interfaces[0] = first; From d29e2d551c3778066ee94c868850ec1e8ab17c81 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:37:53 +0200 Subject: [PATCH 388/591] [mucurses] Attempt to fix test for empty string Signed-off-by: Michael Brown --- src/hci/mucurses/slk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c index 660eb65c0..da35c5675 100644 --- a/src/hci/mucurses/slk.c +++ b/src/hci/mucurses/slk.c @@ -83,7 +83,7 @@ static void _print_label ( struct _softlabel sl ) { space_ch = ' '; // protect against gaps in the soft label keys array - if ( sl.label == NULL ) { + if ( ! sl.label[0] ) { memset( str, space_ch, (size_t)(slks->max_label_len) ); } else { /* we need to pad the label with varying amounts of leading From 583d258b891902f85e623ca1d7b0ba7365673474 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:42:02 +0200 Subject: [PATCH 389/591] [mucurses] Attempt to fix keypress processing logic Signed-off-by: Michael Brown --- src/hci/mucurses/kb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index 8face14d8..a4b6ea871 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -103,7 +103,9 @@ int wgetnstr ( WINDOW *win, char *str, int n ) { _wcursback( win ); wdelch( win ); } else { - if ( c >= KEY_MIN ) { + if ( c >= 32 && c <= 126 ) { + *(_str++) = c; n--; + } else { switch(c) { case KEY_LEFT : case KEY_BACKSPACE : @@ -118,9 +120,6 @@ int wgetnstr ( WINDOW *win, char *str, int n ) { break; } } - if ( c >= 32 && c <= 126 ) { - *(_str++) = c; n--; - } } } From 2ae759219b4a0f461284bf6e386ece58307ad6f0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:46:19 +0200 Subject: [PATCH 390/591] [mucurses] Attempt to fix resource leaks Signed-off-by: Michael Brown --- src/hci/mucurses/windows.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hci/mucurses/windows.c b/src/hci/mucurses/windows.c index 5f5d1f4e2..090fcfd2e 100644 --- a/src/hci/mucurses/windows.c +++ b/src/hci/mucurses/windows.c @@ -48,11 +48,11 @@ int delwin ( WINDOW *win ) { WINDOW *derwin ( WINDOW *parent, int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *child; - if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) - return NULL; if ( ( (unsigned)ncols > parent->width ) || ( (unsigned)nlines > parent->height ) ) return NULL; + if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) + return NULL; child->ori_y = parent->ori_y + begin_y; child->ori_x = parent->ori_x + begin_x; child->height = nlines; @@ -113,11 +113,11 @@ int mvwin ( WINDOW *win, int y, int x ) { */ WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *win; - if ( ( win = malloc( sizeof(WINDOW) ) ) == NULL ) - return NULL; if ( ( (unsigned)( begin_y + nlines ) > stdscr->height ) && ( (unsigned)( begin_x + ncols ) > stdscr->width ) ) return NULL; + if ( ( win = malloc( sizeof(WINDOW) ) ) == NULL ) + return NULL; win->ori_y = begin_y; win->ori_x = begin_x; win->height = nlines; @@ -140,8 +140,6 @@ WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *subwin ( WINDOW *parent, int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *child; - if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) - return NULL; child = newwin( nlines, ncols, begin_y, begin_x ); child->parent = parent; child->scr = parent->scr; From 8963193cda877e087308d85c70486dee29dc8860 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:51:03 +0200 Subject: [PATCH 391/591] [hyperv] Fix resource leaks on error path Signed-off-by: Michael Brown --- src/interface/hyperv/vmbus.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index 286572ce0..7915ddfe0 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -448,28 +448,31 @@ int vmbus_open ( struct vmbus_device *vmdev, /* Post message */ if ( ( rc = vmbus_post_message ( hv, &open.header, sizeof ( open ) ) ) != 0 ) - return rc; + goto err_post_message; /* Wait for response */ if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_OPEN_CHANNEL_RESULT ) ) != 0) - return rc; + goto err_wait_for_message; /* Check response */ if ( opened->channel != cpu_to_le32 ( vmdev->channel ) ) { DBGC ( vmdev, "VMBUS %s unexpected opened channel %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->channel ) ); - return -EPROTO; + rc = -EPROTO; + goto err_check_response; } if ( opened->id != open_id /* Non-endian */ ) { DBGC ( vmdev, "VMBUS %s unexpected open ID %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->id ) ); - return -EPROTO; + rc = -EPROTO; + goto err_check_response; } if ( opened->status != 0 ) { DBGC ( vmdev, "VMBUS %s open failed: %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->status ) ); - return -EPROTO; + rc = -EPROTO; + goto err_check_response; } /* Store channel parameters */ @@ -488,6 +491,9 @@ int vmbus_open ( struct vmbus_device *vmdev, ( virt_to_phys ( vmdev->out ) + len ) ); return 0; + err_check_response: + err_wait_for_message: + err_post_message: vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ); err_establish: free_dma ( ring, len ); From 60561d0f3d3353b11c9b876ef8e098cf696969c3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:53:13 +0200 Subject: [PATCH 392/591] [slam] Fix resource leak on error path Signed-off-by: Michael Brown --- src/net/udp/slam.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/net/udp/slam.c b/src/net/udp/slam.c index 8fcc97632..61dd7d985 100644 --- a/src/net/udp/slam.c +++ b/src/net/udp/slam.c @@ -266,7 +266,8 @@ static int slam_tx_nack ( struct slam_request *slam ) { if ( ! iobuf ) { DBGC ( slam, "SLAM %p could not allocate I/O buffer\n", slam ); - return -ENOMEM; + rc = -ENOMEM; + goto err_alloc; } /* Construct NACK. We always request only a single packet; @@ -294,14 +295,19 @@ static int slam_tx_nack ( struct slam_request *slam ) { "0-%ld\n", slam, ( num_blocks - 1 ) ); } if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 ) - return rc; + goto err_put_value; if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 ) - return rc; + goto err_put_value; nul = iob_put ( iobuf, 1 ); *nul = 0; /* Transmit packet */ - return xfer_deliver_iob ( &slam->socket, iobuf ); + return xfer_deliver_iob ( &slam->socket, iob_disown ( iobuf ) ); + + err_put_value: + free_iob ( iobuf ); + err_alloc: + return rc; } /** From 64de7dc7fd06470424bb4c3ea537f542c46895c4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 14:57:36 +0200 Subject: [PATCH 393/591] [slam] Avoid NULL pointer dereference in slam_pull_value() Signed-off-by: Michael Brown --- src/net/udp/slam.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/net/udp/slam.c b/src/net/udp/slam.c index 61dd7d985..c165b4fb9 100644 --- a/src/net/udp/slam.c +++ b/src/net/udp/slam.c @@ -400,12 +400,16 @@ static int slam_pull_value ( struct slam_request *slam, return -EINVAL; } - /* Read value */ + /* Strip value */ iob_pull ( iobuf, len ); - *value = ( *data & 0x1f ); - while ( --len ) { - *value <<= 8; - *value |= *(++data); + + /* Read value, if applicable */ + if ( value ) { + *value = ( *data & 0x1f ); + while ( --len ) { + *value <<= 8; + *value |= *(++data); + } } return 0; From 1ec2a60614ea0f27843d553d3ee698f9e8d43d79 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 15:07:10 +0200 Subject: [PATCH 394/591] [eoib] Avoid passing a NULL I/O buffer to netdev_tx_complete_err() Report errors in eoib_duplicate() via netdev_tx_err() rather than netdev_tx_complete_err(), since netdev_tx_complete_err() accepts only valid I/O buffers that are currently in the network device's transmit queue. Signed-off-by: Michael Brown --- src/drivers/net/eoib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c index f58e74b7d..e82478372 100644 --- a/src/drivers/net/eoib.c +++ b/src/drivers/net/eoib.c @@ -870,8 +870,9 @@ static void eoib_duplicate ( struct eoib_device *eoib, err_post_send: err_path: + list_del ( ©->list ); err_alloc: - netdev_tx_complete_err ( netdev, copy, rc ); + netdev_tx_err ( netdev, copy, rc ); } /** From f17cf0ecd0363523dde781338fb1f3b5f5a7c6f7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 15:20:45 +0200 Subject: [PATCH 395/591] [http] Add missing check for memory allocation failure Signed-off-by: Michael Brown --- src/net/tcp/httpconn.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c index a2c01a418..2cfca9c94 100644 --- a/src/net/tcp/httpconn.c +++ b/src/net/tcp/httpconn.c @@ -277,6 +277,10 @@ int http_connect ( struct interface *xfer, struct uri *uri ) { /* Allocate and initialise structure */ conn = zalloc ( sizeof ( *conn ) ); + if ( ! conn ) { + rc = -ENOMEM; + goto err_alloc; + } ref_init ( &conn->refcnt, http_conn_free ); conn->uri = uri_get ( uri ); conn->scheme = scheme; @@ -310,5 +314,6 @@ int http_connect ( struct interface *xfer, struct uri *uri ) { conn->scheme->name, conn->uri->host, port, strerror ( rc ) ); http_conn_close ( conn, rc ); ref_put ( &conn->refcnt ); + err_alloc: return rc; } From 501fa53b2587642f1459fa79db71359e8eac75dc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 15:30:05 +0200 Subject: [PATCH 396/591] [mucurses] Attempt to fix use of uninitialised buffer with strcat() Signed-off-by: Michael Brown --- src/hci/mucurses/slk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c index da35c5675..b900c068a 100644 --- a/src/hci/mucurses/slk.c +++ b/src/hci/mucurses/slk.c @@ -81,6 +81,7 @@ static void _print_label ( struct _softlabel sl ) { assert ( slks->max_label_len <= SLK_MAX_LABEL_LEN ); space_ch = ' '; + memset ( str, 0, sizeof ( str ) ); // protect against gaps in the soft label keys array if ( ! sl.label[0] ) { From 6124c0ebfae22542e5cbcc837143133fdb1d814d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 21 Mar 2017 16:22:42 +0200 Subject: [PATCH 397/591] [xhci] Avoid accessing beyond end of endpoint context array Signed-off-by: Michael Brown --- src/drivers/usb/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 48ac6a307..825171a52 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -1558,7 +1558,7 @@ static void xhci_transfer ( struct xhci_device *xhci, } /* Identify endpoint */ - if ( ( trb->endpoint > XHCI_CTX_END ) || + if ( ( trb->endpoint >= XHCI_CTX_END ) || ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) { DBGC ( xhci, "XHCI %s slot %d transfer event invalid epid " "%d:\n", xhci->name, slot->id, trb->endpoint ); From e88e2a29657824963c4fdb948e5ac99dfe6472ee Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 08:19:33 +0200 Subject: [PATCH 398/591] [build] Avoid confusing sparse in single-argument DBG() macros For visual consistency with surrounding lines, the definitions of DBG_MORE(), DBG_PAUSE(), etc include an unnecessary ##__VA_ARGS__ argument which is always elided. This confuses sparse, which complains about DBG_MORE_IF() being called with more than one argument. Work around this problem by adding an unused variable argument list to the single-argument macros DBG_MORE_IF() and DBG_PAUSE_IF(). Signed-off-by: Michael Brown --- src/include/compiler.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/compiler.h b/src/include/compiler.h index 56a5f63d5..a936425de 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -403,7 +403,7 @@ char __debug_disable(OBJECT) = ( DBGLVL_MAX & ~DBGLVL_DFLT ); * * @v level Debug level */ -#define DBG_PAUSE_IF( level ) do { \ +#define DBG_PAUSE_IF( level, ... ) do { \ if ( DBG_ ## level ) { \ dbg_pause(); \ } \ @@ -414,7 +414,7 @@ char __debug_disable(OBJECT) = ( DBGLVL_MAX & ~DBGLVL_DFLT ); * * @v level Debug level */ -#define DBG_MORE_IF( level ) do { \ +#define DBG_MORE_IF( level, ... ) do { \ if ( DBG_ ## level ) { \ dbg_more(); \ } \ From 39ef530088859ccbbcf29bf6af2cf9f0307dc476 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 10:47:46 +0200 Subject: [PATCH 399/591] [infiniband] Return status code from ib_create_cq() and ib_create_qp() Any underlying errors arising during ib_create_cq() or ib_create_qp() are lost since the functions simply return NULL on error. This makes debugging harder, since a debug-enabled build is required to discover the root cause of the error. Fix by returning a status code from these functions, thereby allowing any underlying errors to be propagated. Signed-off-by: Michael Brown --- src/drivers/infiniband/flexboot_nodnic.c | 16 ++++----- src/drivers/infiniband/hermon.c | 20 +++++------ src/drivers/net/eoib.c | 19 +++++----- src/drivers/net/ipoib.c | 20 +++++------ src/include/ipxe/infiniband.h | 18 +++++----- src/net/infiniband.c | 45 ++++++++++++++---------- src/net/infiniband/ib_cmrc.c | 23 ++++++------ src/net/infiniband/ib_mi.c | 17 ++++----- 8 files changed, 88 insertions(+), 90 deletions(-) diff --git a/src/drivers/infiniband/flexboot_nodnic.c b/src/drivers/infiniband/flexboot_nodnic.c index 1ee10f54b..2108e78fc 100644 --- a/src/drivers/infiniband/flexboot_nodnic.c +++ b/src/drivers/infiniband/flexboot_nodnic.c @@ -860,6 +860,7 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { mlx_uint64 cq_size = 0; mlx_uint32 qpn = 0; nodnic_port_state state = nodnic_port_state_down; + int rc; if ( port->port_priv.port_state & NODNIC_PORT_OPENED ) { DBGC ( flexboot_nodnic, "%s: port %d is already opened\n", @@ -877,11 +878,11 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { } INIT_LIST_HEAD ( &dummy_cq->work_queues ); - port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH, - FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq, - FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq, - &flexboot_nodnic_eth_qp_op, netdev->name ); - if ( !port->eth_qp ) { + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_ETH, + FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq, + FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq, + &flexboot_nodnic_eth_qp_op, netdev->name, + &port->eth_qp ) ) != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create queue pair\n", flexboot_nodnic, ibdev->port ); status = MLX_OUT_OF_RESOURCES; @@ -894,9 +895,8 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { MLX_FATAL_CHECK_STATUS(status, get_cq_size_err, "nodnic_port_get_cq_size failed"); - port->eth_cq = ib_create_cq ( ibdev, cq_size, - &flexboot_nodnic_eth_cq_op ); - if ( !port->eth_cq ) { + if ( ( rc = ib_create_cq ( ibdev, cq_size, &flexboot_nodnic_eth_cq_op, + &port->eth_cq ) ) != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create completion queue\n", flexboot_nodnic, ibdev->port ); diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 2199a9d98..3797d96e8 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -3261,24 +3261,20 @@ static int hermon_eth_open ( struct net_device *netdev ) { goto err_open; /* Allocate completion queue */ - port->eth_cq = ib_create_cq ( ibdev, HERMON_ETH_NUM_CQES, - &hermon_eth_cq_op ); - if ( ! port->eth_cq ) { + if ( ( rc = ib_create_cq ( ibdev, HERMON_ETH_NUM_CQES, + &hermon_eth_cq_op, &port->eth_cq ) ) != 0 ) { DBGC ( hermon, "Hermon %p port %d could not create completion " - "queue\n", hermon, ibdev->port ); - rc = -ENOMEM; + "queue: %s\n", hermon, ibdev->port, strerror ( rc ) ); goto err_create_cq; } /* Allocate queue pair */ - port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH, - HERMON_ETH_NUM_SEND_WQES, port->eth_cq, - HERMON_ETH_NUM_RECV_WQES, port->eth_cq, - &hermon_eth_qp_op, netdev->name ); - if ( ! port->eth_qp ) { + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_ETH, HERMON_ETH_NUM_SEND_WQES, + port->eth_cq, HERMON_ETH_NUM_RECV_WQES, + port->eth_cq, &hermon_eth_qp_op, + netdev->name, &port->eth_qp ) ) != 0 ) { DBGC ( hermon, "Hermon %p port %d could not create queue " - "pair\n", hermon, ibdev->port ); - rc = -ENOMEM; + "pair: %s\n", hermon, ibdev->port, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( port->eth_qp, netdev ); diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c index e82478372..ba2912953 100644 --- a/src/drivers/net/eoib.c +++ b/src/drivers/net/eoib.c @@ -538,22 +538,19 @@ static int eoib_open ( struct net_device *netdev ) { } /* Allocate completion queue */ - eoib->cq = ib_create_cq ( ibdev, EOIB_NUM_CQES, &eoib_cq_op ); - if ( ! eoib->cq ) { - DBGC ( eoib, "EoIB %s could not allocate completion queue\n", - eoib->name ); - rc = -ENOMEM; + if ( ( rc = ib_create_cq ( ibdev, EOIB_NUM_CQES, &eoib_cq_op, + &eoib->cq ) ) != 0 ) { + DBGC ( eoib, "EoIB %s could not create completion queue: %s\n", + eoib->name, strerror ( rc ) ); goto err_create_cq; } /* Allocate queue pair */ - eoib->qp = ib_create_qp ( ibdev, IB_QPT_UD, EOIB_NUM_SEND_WQES, + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_UD, EOIB_NUM_SEND_WQES, eoib->cq, EOIB_NUM_RECV_WQES, eoib->cq, - &eoib_qp_op, netdev->name ); - if ( ! eoib->qp ) { - DBGC ( eoib, "EoIB %s could not allocate queue pair\n", - eoib->name ); - rc = -ENOMEM; + &eoib_qp_op, netdev->name, &eoib->qp ) )!=0){ + DBGC ( eoib, "EoIB %s could not create queue pair: %s\n", + eoib->name, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( eoib->qp, eoib ); diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 8a65c87ba..33c7ddccd 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -854,22 +854,20 @@ static int ipoib_open ( struct net_device *netdev ) { } /* Allocate completion queue */ - ipoib->cq = ib_create_cq ( ibdev, IPOIB_NUM_CQES, &ipoib_cq_op ); - if ( ! ipoib->cq ) { - DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n", - ipoib ); - rc = -ENOMEM; + if ( ( rc = ib_create_cq ( ibdev, IPOIB_NUM_CQES, &ipoib_cq_op, + &ipoib->cq ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not create completion queue: " + "%s\n", ipoib, strerror ( rc ) ); goto err_create_cq; } /* Allocate queue pair */ - ipoib->qp = ib_create_qp ( ibdev, IB_QPT_UD, IPOIB_NUM_SEND_WQES, + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_UD, IPOIB_NUM_SEND_WQES, ipoib->cq, IPOIB_NUM_RECV_WQES, ipoib->cq, - &ipoib_qp_op, netdev->name ); - if ( ! ipoib->qp ) { - DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n", - ipoib ); - rc = -ENOMEM; + &ipoib_qp_op, netdev->name, + &ipoib->qp ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not create queue pair: %s\n", + ipoib, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( ipoib->qp, ipoib ); diff --git a/src/include/ipxe/infiniband.h b/src/include/ipxe/infiniband.h index d7ecd1623..6f4951f17 100644 --- a/src/include/ipxe/infiniband.h +++ b/src/include/ipxe/infiniband.h @@ -493,18 +493,20 @@ struct ib_driver { /** Declare an Infiniband driver */ #define __ib_driver __table_entry ( IB_DRIVERS, 01 ) -extern struct ib_completion_queue * -ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, - struct ib_completion_queue_operations *op ); +extern int ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, + struct ib_completion_queue_operations *op, + struct ib_completion_queue **new_cq ); extern void ib_destroy_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ); extern void ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ); -extern struct ib_queue_pair * -ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, - unsigned int num_send_wqes, struct ib_completion_queue *send_cq, - unsigned int num_recv_wqes, struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op, const char *name ); +extern int ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, + unsigned int num_send_wqes, + struct ib_completion_queue *send_cq, + unsigned int num_recv_wqes, + struct ib_completion_queue *recv_cq, + struct ib_queue_pair_operations *op, + const char *name, struct ib_queue_pair **new_qp ); extern int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ); extern void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ); diff --git a/src/net/infiniband.c b/src/net/infiniband.c index 15ff0529b..fa45653ea 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -92,11 +92,12 @@ struct errortab infiniband_errors[] __errortab = { * @v ibdev Infiniband device * @v num_cqes Number of completion queue entries * @v op Completion queue operations - * @ret cq New completion queue + * @v new_cq New completion queue to fill in + * @ret rc Return status code */ -struct ib_completion_queue * -ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, - struct ib_completion_queue_operations *op ) { +int ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, + struct ib_completion_queue_operations *op, + struct ib_completion_queue **new_cq ) { struct ib_completion_queue *cq; int rc; @@ -104,8 +105,10 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, /* Allocate and initialise data structure */ cq = zalloc ( sizeof ( *cq ) ); - if ( ! cq ) + if ( ! cq ) { + rc = -ENOMEM; goto err_alloc_cq; + } cq->ibdev = ibdev; list_add_tail ( &cq->list, &ibdev->cqs ); cq->num_cqes = num_cqes; @@ -122,14 +125,15 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, DBGC ( ibdev, "IBDEV %s created %d-entry completion queue %p (%p) " "with CQN %#lx\n", ibdev->name, num_cqes, cq, ib_cq_get_drvdata ( cq ), cq->cqn ); - return cq; + *new_cq = cq; + return 0; ibdev->op->destroy_cq ( ibdev, cq ); err_dev_create_cq: list_del ( &cq->list ); free ( cq ); err_alloc_cq: - return NULL; + return rc; } /** @@ -186,19 +190,19 @@ void ib_poll_cq ( struct ib_device *ibdev, * @v recv_cq Receive completion queue * @v op Queue pair operations * @v name Queue pair name - * @ret qp Queue pair + * @v new_qp New queue pair to fill in + * @ret rc Return status code * * The queue pair will be left in the INIT state; you must call * ib_modify_qp() before it is ready to use for sending and receiving. */ -struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, - enum ib_queue_pair_type type, - unsigned int num_send_wqes, - struct ib_completion_queue *send_cq, - unsigned int num_recv_wqes, - struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op, - const char *name ) { +int ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, + unsigned int num_send_wqes, + struct ib_completion_queue *send_cq, + unsigned int num_recv_wqes, + struct ib_completion_queue *recv_cq, + struct ib_queue_pair_operations *op, const char *name, + struct ib_queue_pair **new_qp ) { struct ib_queue_pair *qp; size_t total_size; int rc; @@ -210,8 +214,10 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ) + ( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) ); qp = zalloc ( total_size ); - if ( ! qp ) + if ( ! qp ) { + rc = -ENOMEM; goto err_alloc_qp; + } qp->ibdev = ibdev; list_add_tail ( &qp->list, &ibdev->qps ); qp->type = type; @@ -265,7 +271,8 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, ibdev->name, qp->qpn, qp->ext_qpn ); } - return qp; + *new_qp = qp; + return 0; ibdev->op->destroy_qp ( ibdev, qp ); err_dev_create_qp: @@ -274,7 +281,7 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, list_del ( &qp->list ); free ( qp ); err_alloc_qp: - return NULL; + return rc; } /** diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 2cd49018f..b8f4bf36b 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -423,23 +423,20 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, } /* Create completion queue */ - cmrc->cq = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES, - &ib_cmrc_completion_ops ); - if ( ! cmrc->cq ) { - DBGC ( cmrc, "CMRC %s %s could not create completion queue\n", - ibdev->name, cmrc->name ); - rc = -ENOMEM; + if ( ( rc = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES, + &ib_cmrc_completion_ops, &cmrc->cq ) ) != 0){ + DBGC ( cmrc, "CMRC %s %s could not create completion queue: " + "%s\n", ibdev->name, cmrc->name, strerror ( rc ) ); goto err_create_cq; } /* Create queue pair */ - cmrc->qp = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES, - cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, - &ib_cmrc_queue_pair_ops, name ); - if ( ! cmrc->qp ) { - DBGC ( cmrc, "CMRC %s %s could not create queue pair\n", - ibdev->name, cmrc->name ); - rc = -ENOMEM; + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES, + cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, + &ib_cmrc_queue_pair_ops, name, + &cmrc->qp ) ) != 0 ) { + DBGC ( cmrc, "CMRC %s %s could not create queue pair: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( cmrc->qp, cmrc ); diff --git a/src/net/infiniband/ib_mi.c b/src/net/infiniband/ib_mi.c index 548a1c829..149c1e4dd 100644 --- a/src/net/infiniband/ib_mi.c +++ b/src/net/infiniband/ib_mi.c @@ -357,19 +357,20 @@ struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, INIT_LIST_HEAD ( &mi->madx ); /* Create completion queue */ - mi->cq = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops ); - if ( ! mi->cq ) { - DBGC ( mi, "MI %p could not allocate completion queue\n", mi ); + if ( ( rc = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops, + &mi->cq ) ) != 0 ) { + DBGC ( mi, "MI %p could not create completion queue: %s\n", + mi, strerror ( rc ) ); goto err_create_cq; } /* Create queue pair */ name = ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ); - mi->qp = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq, - IB_MI_NUM_RECV_WQES, mi->cq, - &ib_mi_queue_pair_ops, name ); - if ( ! mi->qp ) { - DBGC ( mi, "MI %p could not allocate queue pair\n", mi ); + if ( ( rc = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq, + IB_MI_NUM_RECV_WQES, mi->cq, + &ib_mi_queue_pair_ops, name, &mi->qp ) )!=0){ + DBGC ( mi, "MI %p could not create queue pair: %s\n", + mi, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( mi->qp, mi ); From c26c1fd07c51bb9c5e2c7829f77be4d6d0fcc806 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 10:57:06 +0200 Subject: [PATCH 400/591] [infiniband] Return status code from ib_create_mi() Signed-off-by: Michael Brown --- src/include/ipxe/ib_mi.h | 5 +++-- src/net/infiniband.c | 14 ++++++-------- src/net/infiniband/ib_mi.c | 16 ++++++++++------ 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/include/ipxe/ib_mi.h b/src/include/ipxe/ib_mi.h index c7c8143ba..bd087cd35 100644 --- a/src/include/ipxe/ib_mi.h +++ b/src/include/ipxe/ib_mi.h @@ -127,8 +127,9 @@ ib_create_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi, extern void ib_destroy_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi, struct ib_mad_transaction *madx ); -extern struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, - enum ib_queue_pair_type type ); +extern int ib_create_mi ( struct ib_device *ibdev, + enum ib_queue_pair_type type, + struct ib_mad_interface **new_mi ); extern void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ); diff --git a/src/net/infiniband.c b/src/net/infiniband.c index fa45653ea..3b79a660c 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -666,10 +666,9 @@ int ib_open ( struct ib_device *ibdev ) { } /* Create subnet management interface */ - ibdev->smi = ib_create_mi ( ibdev, IB_QPT_SMI ); - if ( ! ibdev->smi ) { - DBGC ( ibdev, "IBDEV %s could not create SMI\n", ibdev->name ); - rc = -ENOMEM; + if ( ( rc = ib_create_mi ( ibdev, IB_QPT_SMI, &ibdev->smi ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %s could not create SMI: %s\n", + ibdev->name, strerror ( rc ) ); goto err_create_smi; } @@ -681,10 +680,9 @@ int ib_open ( struct ib_device *ibdev ) { } /* Create general services interface */ - ibdev->gsi = ib_create_mi ( ibdev, IB_QPT_GSI ); - if ( ! ibdev->gsi ) { - DBGC ( ibdev, "IBDEV %s could not create GSI\n", ibdev->name ); - rc = -ENOMEM; + if ( ( rc = ib_create_mi ( ibdev, IB_QPT_GSI, &ibdev->gsi ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %s could not create GSI: %s\n", + ibdev->name, strerror ( rc ) ); goto err_create_gsi; } diff --git a/src/net/infiniband/ib_mi.c b/src/net/infiniband/ib_mi.c index 149c1e4dd..781a3e2ea 100644 --- a/src/net/infiniband/ib_mi.c +++ b/src/net/infiniband/ib_mi.c @@ -341,18 +341,21 @@ void ib_destroy_madx ( struct ib_device *ibdev __unused, * * @v ibdev Infiniband device * @v type Queue pair type - * @ret mi Management agent, or NULL + * @v new_mi New management interface to fill in + * @ret rc Return status code */ -struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, - enum ib_queue_pair_type type ) { +int ib_create_mi ( struct ib_device *ibdev, enum ib_queue_pair_type type, + struct ib_mad_interface **new_mi ) { struct ib_mad_interface *mi; const char *name; int rc; /* Allocate and initialise fields */ mi = zalloc ( sizeof ( *mi ) ); - if ( ! mi ) + if ( ! mi ) { + rc = -ENOMEM; goto err_alloc; + } mi->ibdev = ibdev; INIT_LIST_HEAD ( &mi->madx ); @@ -387,7 +390,8 @@ struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, /* Fill receive ring */ ib_refill_recv ( ibdev, mi->qp ); - return mi; + *new_mi = mi; + return 0; err_modify_qp: ib_destroy_qp ( ibdev, mi->qp ); @@ -396,7 +400,7 @@ struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, err_create_cq: free ( mi ); err_alloc: - return NULL; + return rc; } /** From e846bd22c35fbbb0567c97896627b33f9f5c717b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 11:45:28 +0200 Subject: [PATCH 401/591] [block] Quell spurious Coverity size mismatch warning Signed-off-by: Michael Brown --- src/core/sanboot.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 95fa5c4ee..88d254ff8 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -466,7 +466,10 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) { .type = ISO9660_TYPE_PRIMARY, .id = ISO9660_ID, }; - struct iso9660_primary_descriptor *primary; + union { + struct iso9660_primary_descriptor primary; + char bytes[ISO9660_BLKSIZE]; + } *scratch; unsigned int blksize; unsigned int blksize_shift; unsigned int lba; @@ -489,14 +492,14 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) { count = ( 1 << blksize_shift ); /* Allocate scratch area */ - primary = malloc ( ISO9660_BLKSIZE ); - if ( ! primary ) { + scratch = malloc ( ISO9660_BLKSIZE ); + if ( ! scratch ) { rc = -ENOMEM; goto err_alloc; } /* Read primary volume descriptor */ - if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( primary ), + if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( scratch ), block_read ) ) != 0 ) { DBGC ( sandev, "SAN %#02x could not read ISO9660 primary" "volume descriptor: %s\n", @@ -505,7 +508,8 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) { } /* Configure as CD-ROM if applicable */ - if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) == 0){ + if ( memcmp ( &scratch->primary.fixed, &primary_check, + sizeof ( primary_check ) ) == 0 ) { DBGC ( sandev, "SAN %#02x contains an ISO9660 filesystem; " "treating as CD-ROM\n", sandev->drive ); sandev->blksize_shift = blksize_shift; @@ -513,7 +517,7 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) { } err_rw: - free ( primary ); + free ( scratch ); err_alloc: invalid_blksize: return rc; From 45f2265bfcbbf2afd7fac24372ae26e453f2b52d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 11:52:09 +0200 Subject: [PATCH 402/591] [ath] Add missing break statements Signed-off-by: Michael Brown --- src/drivers/net/ath/ath5k/ath5k_desc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/drivers/net/ath/ath5k/ath5k_desc.c b/src/drivers/net/ath/ath5k/ath5k_desc.c index 30fe1c777..816d26ede 100644 --- a/src/drivers/net/ath/ath5k/ath5k_desc.c +++ b/src/drivers/net/ath/ath5k/ath5k_desc.c @@ -104,10 +104,13 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, case AR5K_PKT_TYPE_BEACON: case AR5K_PKT_TYPE_PROBE_RESP: frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY; + break; case AR5K_PKT_TYPE_PIFS: frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS; + break; default: frame_type = type /*<< 2 ?*/; + break; } tx_ctl->tx_control_0 |= From 966a960a8333a9d75da8103cbe5903d380ef7770 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 14:11:19 +0200 Subject: [PATCH 403/591] [pixbuf] Avoid potential division by zero Avoid potential division by zero when performing the check against multiplication overflow. (Note that if the width is zero then there can be no overflow anyway, so it is then safe to bypass the check.) Signed-off-by: Michael Brown --- src/core/pixbuf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/pixbuf.c b/src/core/pixbuf.c index 4742d285d..641a0fb53 100644 --- a/src/core/pixbuf.c +++ b/src/core/pixbuf.c @@ -68,8 +68,10 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { pixbuf->len = ( width * height * sizeof ( uint32_t ) ); /* Check for multiplication overflow */ - if ( ( ( pixbuf->len / sizeof ( uint32_t ) ) / width ) != height ) + if ( ( width != 0 ) && + ( ( pixbuf->len / sizeof ( uint32_t ) ) / width ) != height ) { goto err_overflow; + } /* Allocate pixel data buffer */ pixbuf->data = umalloc ( pixbuf->len ); From 21d8624da828f35f544b5bc7a764eb7f7edea90b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 14:14:57 +0200 Subject: [PATCH 404/591] [usb] Use correct length for memcpy() Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index cad26c77b..bd2a446be 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -1005,8 +1005,8 @@ static int usb_describe ( struct usb_device *usb, } /* Describe function */ - memcpy ( &desc->class, &association->class, - sizeof ( desc->class ) ); + memcpy ( &desc->class.class, &association->class, + sizeof ( desc->class.class ) ); desc->count = association->count; for ( i = 0 ; i < association->count ; i++ ) interfaces[i] = ( first + i ); From 01496a50282542fbac98b16ed40cbe52d7d6161d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 14:41:01 +0200 Subject: [PATCH 405/591] [xen] Use standard calling pattern for asprintf() Our asprintf() implementation guarantees that strp will be NULL on allocation failure, but this is not standard behaviour. Detect errors by checking for a negative return value instead of a NULL pointer. Signed-off-by: Michael Brown --- src/interface/xen/xenstore.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/interface/xen/xenstore.c b/src/interface/xen/xenstore.c index 23424a926..a14881fcd 100644 --- a/src/interface/xen/xenstore.c +++ b/src/interface/xen/xenstore.c @@ -538,8 +538,7 @@ void xenstore_dump ( struct xen_hypervisor *xen, const char *key ) { child += ( strlen ( child ) + 1 /* NUL */ ) ) { /* Construct child key */ - asprintf ( &child_key, "%s/%s", key, child ); - if ( ! child_key ) { + if ( asprintf ( &child_key, "%s/%s", key, child ) < 0 ){ DBGC ( xen, "XENSTORE could not allocate child " "key \"%s/%s\"\n", key, child ); rc = -ENOMEM; From 75bb948008be207599fbb6534e282d45528f267b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 15:11:05 +0200 Subject: [PATCH 406/591] [tcp] Use correct length for memset() Signed-off-by: Michael Brown --- src/net/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/tcp.c b/src/net/tcp.c index 37a202ef1..cb3b84edd 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -697,7 +697,7 @@ static void tcp_xmit_sack ( struct tcp_connection *tcp, uint32_t sack_seq ) { wsopt->wsopt.length = sizeof ( wsopt->wsopt ); wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE; spopt = iob_push ( iobuf, sizeof ( *spopt ) ); - memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt ) ); + memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt->nop ) ); spopt->spopt.kind = TCP_OPTION_SACK_PERMITTED; spopt->spopt.length = sizeof ( spopt->spopt ); } From 7495813792c13f8095053bffe7b362e09df99d83 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 15:13:06 +0200 Subject: [PATCH 407/591] [video_subr] Use memmove() for overlapping memory copy Signed-off-by: Michael Brown --- src/arch/x86/core/video_subr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/x86/core/video_subr.c b/src/arch/x86/core/video_subr.c index 3f701bd96..f5cc4cdd4 100644 --- a/src/arch/x86/core/video_subr.c +++ b/src/arch/x86/core/video_subr.c @@ -57,7 +57,7 @@ static void video_scroll(void) { int i; - memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2); + memmove(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2); for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2) vidmem[i] = ' '; } From 0ced99e97c59e193eb7b1313fbb4473c720c59dc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 15:18:54 +0200 Subject: [PATCH 408/591] [arbel] Assert that mapping length is non-zero An (impossible) mapping length of zero produces a negative bit shift, which is technically undefined. Signed-off-by: Michael Brown --- src/drivers/infiniband/arbel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index ea65d8b8d..98a2b6010 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -1972,6 +1972,7 @@ static int arbel_map_vpm ( struct arbel *arbel, assert ( ( va & ( ARBEL_PAGE_SIZE - 1 ) ) == 0 ); assert ( ( pa & ( ARBEL_PAGE_SIZE - 1 ) ) == 0 ); assert ( ( len & ( ARBEL_PAGE_SIZE - 1 ) ) == 0 ); + assert ( len != 0 ); /* Calculate starting points */ start = pa; From dea5b744750f691c071b018b1084fa916ed630a8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 15:18:54 +0200 Subject: [PATCH 409/591] [hermon] Assert that mapping length is non-zero An (impossible) mapping length of zero produces a negative bit shift, which is technically undefined. Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 3797d96e8..a1d2a3bd5 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -2113,6 +2113,7 @@ static int hermon_map_vpm ( struct hermon *hermon, assert ( ( va & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); + assert ( len != 0 ); /* Calculate starting points */ start = pa; From ad725fa7d9ad6dfa1167885394b5c6463820207f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 15:28:58 +0200 Subject: [PATCH 410/591] [tlan] Guard against failure to identify chip Signed-off-by: Michael Brown --- src/drivers/net/tlan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/drivers/net/tlan.c b/src/drivers/net/tlan.c index 7742f6d80..0e85b35b6 100644 --- a/src/drivers/net/tlan.c +++ b/src/drivers/net/tlan.c @@ -808,6 +808,8 @@ static int tlan_probe ( struct nic *nic, struct pci_device *pci ) { } i++; } + if (chip_idx == -1) + return 0; priv->vendor_id = pci->vendor; priv->dev_id = pci->device; From 99e1207a4d20da21b0953c7953122d2b59ba375a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 15:59:27 +0200 Subject: [PATCH 411/591] [w89c840] Avoid potential array overrun Signed-off-by: Michael Brown --- src/drivers/net/w89c840.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/w89c840.c b/src/drivers/net/w89c840.c index d8144a8ce..72ccf3a28 100644 --- a/src/drivers/net/w89c840.c +++ b/src/drivers/net/w89c840.c @@ -247,7 +247,7 @@ static struct winbond_private /* MII transceiver section. */ int mii_cnt; /* MII device addresses. */ u16 advertising; /* NWay media advertisement */ - unsigned char phys[2]; /* MII device addresses. */ + unsigned char phys[4]; /* MII device addresses. */ } w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES))); /* NIC specific static variables go here */ From 3870a7bde2aea95f2ec8e730b853166eb469f2c2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 16:12:56 +0200 Subject: [PATCH 412/591] [sis190] Avoid NULL pointer dereference Signed-off-by: Michael Brown --- src/drivers/net/sis190.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/sis190.c b/src/drivers/net/sis190.c index 81f3d9844..b92e95f2a 100644 --- a/src/drivers/net/sis190.c +++ b/src/drivers/net/sis190.c @@ -965,8 +965,8 @@ static int sis190_get_mac_addr_from_apc(struct pci_device *pdev, list_for_each_entry(d, &(pdev->dev.siblings), siblings) { unsigned int i; - isa_bridge = container_of(d, struct pci_device, dev); for(i = 0; i < sis190_isa_bridge_driver.id_count; i++) { + isa_bridge = container_of(d, struct pci_device, dev); if(isa_bridge->vendor == sis190_isa_bridge_driver.ids[i].vendor && isa_bridge->device == From f032556b15f13a7884d3fc0ccc4d54c2a390d4e9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 16:35:29 +0200 Subject: [PATCH 413/591] [mucurses] Ensure SLK labels are always terminated Signed-off-by: Michael Brown --- src/hci/mucurses/slk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c index b900c068a..169e0120b 100644 --- a/src/hci/mucurses/slk.c +++ b/src/hci/mucurses/slk.c @@ -359,7 +359,7 @@ int slk_set ( int labnum, const char *label, int fmt ) { return ERR; strncpy(slks->fkeys[labnum].label, label, - sizeof(slks->fkeys[labnum].label)); + (sizeof(slks->fkeys[labnum].label) - 1)); slks->fkeys[labnum].fmt = fmt; return OK; From 65a3518013ec9ae97c9eb824cb89452111f8a7c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 16:46:03 +0200 Subject: [PATCH 414/591] [coverity] Add Coverity user model Add a trivial model file to prevent Coverity from making various incorrect assumptions about functions where the iPXE behaviour diverges from POSIX or Linux norms. Signed-off-by: Michael Brown --- contrib/coverity/model.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 contrib/coverity/model.c diff --git a/contrib/coverity/model.c b/contrib/coverity/model.c new file mode 100644 index 000000000..15535d421 --- /dev/null +++ b/contrib/coverity/model.c @@ -0,0 +1,21 @@ +/* + * Coverity modelling file + * + */ + +typedef long off_t; +typedef void * userptr_t; +typedef long long time_t; +struct tm; + +/* Inhibit use of built-in models for functions where Coverity's + * assumptions about the modelled function are incorrect for iPXE. + */ +char * strerror ( int errno ) { +} +void copy_from_user ( void *dest, userptr_t src, off_t src_off, size_t len ) { +} +time_t mktime ( struct tm *tm ) { +} +int getchar ( void ) { +} From c90b4d82b7f98e8c470ed3199e6f59c0082c095a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 20:01:25 +0200 Subject: [PATCH 415/591] [malloc] Track maximum heap usage Track the current and maximum heap usage, and display the maximum during shutdown when DEBUG=malloc is enabled. Signed-off-by: Michael Brown --- src/core/malloc.c | 24 +++++++++++++++++++++--- src/include/ipxe/malloc.h | 2 ++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/core/malloc.c b/src/core/malloc.c index 32c203532..6ddc08b7a 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -93,6 +93,12 @@ static LIST_HEAD ( free_blocks ); /** Total amount of free memory */ size_t freemem; +/** Total amount of used memory */ +size_t usedmem; + +/** Maximum amount of used memory */ +size_t maxusedmem; + /** * Heap size * @@ -351,8 +357,11 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { VALGRIND_MAKE_MEM_NOACCESS ( pre, sizeof ( *pre ) ); } - /* Update total free memory */ + /* Update memory usage statistics */ freemem -= actual_size; + usedmem += actual_size; + if ( usedmem > maxusedmem ) + maxusedmem = usedmem; /* Return allocated block */ DBGC2 ( &heap, "Allocated [%p,%p)\n", block, ( ( ( void * ) block ) + size ) ); @@ -474,8 +483,9 @@ void free_memblock ( void *ptr, size_t size ) { VALGRIND_MAKE_MEM_NOACCESS ( block, sizeof ( *block ) ); } - /* Update free memory counter */ + /* Update memory usage statistics */ freemem += actual_size; + usedmem -= actual_size; check_blocks(); valgrind_make_blocks_noaccess(); @@ -629,10 +639,17 @@ void * zalloc ( size_t size ) { * @c start must be aligned to at least a multiple of sizeof(void*). */ void mpopulate ( void *start, size_t len ) { + /* Prevent free_memblock() from rounding up len beyond the end * of what we were actually given... */ - free_memblock ( start, ( len & ~( MIN_MEMBLOCK_SIZE - 1 ) ) ); + len &= ~( MIN_MEMBLOCK_SIZE - 1 ); + + /* Add to allocation pool */ + free_memblock ( start, len ); + + /* Fix up memory usage statistics */ + usedmem += len; } /** @@ -656,6 +673,7 @@ struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = { */ static void shutdown_cache ( int booting __unused ) { discard_all_cache(); + DBGC ( &heap, "Maximum heap usage %zdkB\n", ( maxusedmem >> 10 ) ); } /** Memory allocator shutdown function */ diff --git a/src/include/ipxe/malloc.h b/src/include/ipxe/malloc.h index dd158b8e6..1878978fd 100644 --- a/src/include/ipxe/malloc.h +++ b/src/include/ipxe/malloc.h @@ -22,6 +22,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include extern size_t freemem; +extern size_t usedmem; +extern size_t maxusedmem; extern void * __malloc alloc_memblock ( size_t size, size_t align, size_t offset ); From aa1f7b0f7f4b18412a09be9f5e7d8e1095637472 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 20:20:53 +0200 Subject: [PATCH 416/591] [travis] Add minimal .travis.yml file Allow for automated builds via Travis CI (https://travis-ci.org). Note that the bin-i386-linux build platform is deliberately omitted since the required linux-libc-dev:i386 package is not on the allowed packages list for the Travis 14.04 ("trusty") container environment. Signed-off-by: Michael Brown --- .travis.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..7e64c9390 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,45 @@ +dist: trusty + +sudo: false + +language: c + +cache: ccache + +compiler: + - gcc + +addons: + apt: + packages: + - binutils-dev + - liblzma-dev + - syslinux + - genisoimage + +env: + global: + - MAKEFLAGS="-j 4" + +script: + - make -C src bin/blib.a + - make -C src bin/ipxe.pxe + - make -C src bin/ipxe.usb + - make -C src bin/ipxe.iso + - make -C src bin/8086100e.mrom + - make -C src bin-x86_64-pcbios/blib.a + - make -C src bin-x86_64-pcbios/ipxe.pxe + - make -C src bin-x86_64-pcbios/ipxe.usb + - make -C src bin-x86_64-pcbios/ipxe.iso + - make -C src bin-x86_64-pcbios/8086100e.mrom + - make -C src bin-x86_64-efi/blib.a + - make -C src bin-x86_64-efi/ipxe.efi + - make -C src bin-x86_64-efi/intel.efidrv + - make -C src bin-x86_64-efi/intel.efirom + - make -C src bin-i386-efi/blib.a + - make -C src bin-i386-efi/ipxe.efi + - make -C src bin-i386-efi/intel.efidrv + - make -C src bin-i386-efi/intel.efirom + - make -C src bin-x86_64-linux/blib.a + - make -C src bin-x86_64-linux/tap.linux + - make -C src bin-x86_64-linux/af_packet.linux From 9ecad204fc8e55bc34ffb4b3ef8f19e57729308b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Mar 2017 22:22:25 +0200 Subject: [PATCH 417/591] [travis] Build and run the unit test suite Signed-off-by: Michael Brown --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7e64c9390..97fa3bae1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,3 +43,5 @@ script: - make -C src bin-x86_64-linux/blib.a - make -C src bin-x86_64-linux/tap.linux - make -C src bin-x86_64-linux/af_packet.linux + - make -C src bin-x86_64-linux/tests.linux + - ./src/bin-x86_64-linux/tests.linux From c24f772349947e7144203e23ebf7f7fd350844ad Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 23 Mar 2017 10:22:36 +0200 Subject: [PATCH 418/591] [travis] Integrate with Coverity Scan Signed-off-by: Michael Brown --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 97fa3bae1..0e75158e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,13 @@ addons: - liblzma-dev - syslinux - genisoimage + coverity_scan: + project: + name: "ipxe/ipxe" + version: $TRAVIS_COMMIT + build_command_prepend: "make -C src bin/deps" + build_command: "make -C src bin/blib.a" + branch_pattern: coverity_scan env: global: From ce240c8c2d5d51841e3b261951b181961215de6b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 23 Mar 2017 11:40:23 +0200 Subject: [PATCH 419/591] [rtl818x] Fix resource leak on error path Signed-off-by: Michael Brown --- src/drivers/net/rtl818x/rtl818x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/rtl818x/rtl818x.c b/src/drivers/net/rtl818x/rtl818x.c index 8b3c206d4..f5082084e 100644 --- a/src/drivers/net/rtl818x/rtl818x.c +++ b/src/drivers/net/rtl818x/rtl818x.c @@ -663,7 +663,8 @@ int rtl818x_probe(struct pci_device *pdev ) hwinfo = zalloc(sizeof(*hwinfo)); if (!hwinfo) { DBG("rtl818x: hwinfo alloc failed\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_alloc_hwinfo; } adjust_pci_device(pdev); @@ -671,7 +672,8 @@ int rtl818x_probe(struct pci_device *pdev ) dev = net80211_alloc(sizeof(*priv)); if (!dev) { DBG("rtl818x: net80211 alloc failed\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_alloc_dev; } priv = dev->priv; @@ -816,7 +818,9 @@ int rtl818x_probe(struct pci_device *pdev ) err_free_dev: pci_set_drvdata(pdev, NULL); net80211_free(dev); + err_alloc_dev: free(hwinfo); + err_alloc_hwinfo: return err; } From 1ff1eebcf7a93a237a1b91ea5d9dcc5b5f1a13bf Mon Sep 17 00:00:00 2001 From: Raed Salem Date: Thu, 23 Mar 2017 15:56:27 +0200 Subject: [PATCH 420/591] [golan] Bug fixes and improved paging allocation method Updates: - revert Support for clear interrupt via BAR Signed-off-by: Raed Salem Signed-off-by: Michael Brown --- src/Makefile | 1 + src/drivers/infiniband/flexboot_nodnic.c | 29 ++- src/drivers/infiniband/flexboot_nodnic.h | 1 + src/drivers/infiniband/golan.c | 214 ++++++------------ src/drivers/infiniband/golan.h | 16 ++ .../infiniband/mlx_nodnic/src/mlx_device.c | 8 +- .../mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c | 38 +++- .../mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h | 10 +- .../mlx_lib/mlx_nvconfig/mlx_nvconfig.c | 8 +- .../mlx_lib/mlx_nvconfig/mlx_nvconfig.h | 32 ++- .../mlx_nvconfig/mlx_nvconfig_defaults.c | 17 +- .../mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c | 145 ------------ .../mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h | 73 ------ .../mlx_lib/mlx_reg_access/mlx_reg_access.h | 5 - .../mlx_lib/mlx_wol_rol/mlx_wol_rol.c | 84 ------- .../mlx_lib/mlx_wol_rol/mlx_wol_rol.h | 61 ----- .../src/private/uefi/mlx_logging_impl.c | 9 - .../infiniband/mlx_utils/src/public/mlx_pci.c | 2 +- 18 files changed, 199 insertions(+), 554 deletions(-) delete mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c delete mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h delete mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c delete mode 100644 src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h delete mode 100644 src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c diff --git a/src/Makefile b/src/Makefile index 1a81e9152..e6900d4b6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -89,6 +89,7 @@ SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu SRCDIRS += drivers/infiniband/mlx_nodnic/src SRCDIRS += drivers/usb SRCDIRS += interface/pxe interface/efi interface/smbios diff --git a/src/drivers/infiniband/flexboot_nodnic.c b/src/drivers/infiniband/flexboot_nodnic.c index 2108e78fc..c13fcefc5 100644 --- a/src/drivers/infiniband/flexboot_nodnic.c +++ b/src/drivers/infiniband/flexboot_nodnic.c @@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" #include "mlx_utils/include/public/mlx_pci_gw.h" #include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h" +#include "mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h" /*************************************************************************** * @@ -823,6 +824,7 @@ static void flexboot_nodnic_eth_complete_recv ( struct ib_device *ibdev __unused netdev_rx_err ( netdev, iobuf, -ENOTTY ); return; } + netdev_rx ( netdev, iobuf ); } @@ -907,6 +909,7 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { list_del(&port->eth_qp->send.list); list_add ( &port->eth_qp->send.list, &port->eth_cq->work_queues ); port->eth_qp->recv.cq = port->eth_cq; + port->cmdsn = 0; list_del(&port->eth_qp->recv.list); list_add ( &port->eth_qp->recv.list, &port->eth_cq->work_queues ); @@ -1445,12 +1448,6 @@ static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) struct pci_device *pci = flexboot_nodnic->pci; nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar; - if ( ! flexboot_nodnic->device_priv.utils ) { - uar->virt = NULL; - DBGC ( flexboot_nodnic, "%s: mlx_utils is not initialized \n", __FUNCTION__ ); - return -EINVAL; - } - if ( ! flexboot_nodnic->device_priv.device_cap.support_uar_tx_db ) { DBGC ( flexboot_nodnic, "%s: tx db using uar is not supported \n", __FUNCTION__ ); return -ENOTSUP; @@ -1467,6 +1464,18 @@ static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) return status; } +static int flexboot_nodnic_dealloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) { + nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar; + + if ( uar->virt ) { + iounmap( uar->virt ); + uar->virt = NULL; + } + + return MLX_SUCCESS; +} + + int flexboot_nodnic_probe ( struct pci_device *pci, struct flexboot_nodnic_callbacks *callbacks, void *drv_priv __unused ) { @@ -1508,6 +1517,10 @@ int flexboot_nodnic_probe ( struct pci_device *pci, MLX_FATAL_CHECK_STATUS(status, get_cap_err, "nodnic_device_get_cap failed"); + if ( mlx_set_admin_mtu ( device_priv->utils, 1, EN_DEFAULT_ADMIN_MTU ) ) { + MLX_DEBUG_ERROR( device_priv->utils, "Failed to set admin mtu\n" ); + } + status = flexboot_nodnic_set_port_masking ( flexboot_nodnic_priv ); MLX_FATAL_CHECK_STATUS(status, err_set_masking, "flexboot_nodnic_set_port_masking failed"); @@ -1522,7 +1535,7 @@ int flexboot_nodnic_probe ( struct pci_device *pci, "flexboot_nodnic_thin_init_ports failed"); if ( ( status = flexboot_nodnic_alloc_uar ( flexboot_nodnic_priv ) ) ) { - DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_pci_init failed" + DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_alloc_uar failed" " ( status = %d )\n",__FUNCTION__, status ); } @@ -1550,6 +1563,7 @@ int flexboot_nodnic_probe ( struct pci_device *pci, flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); reg_err: err_set_ports_types: + flexboot_nodnic_dealloc_uar ( flexboot_nodnic_priv ); err_thin_init_ports: err_alloc_ibdev: err_set_masking: @@ -1568,6 +1582,7 @@ void flexboot_nodnic_remove ( struct pci_device *pci ) struct flexboot_nodnic *flexboot_nodnic_priv = pci_get_drvdata ( pci ); nodnic_device_priv *device_priv = & ( flexboot_nodnic_priv->device_priv ); + flexboot_nodnic_dealloc_uar ( flexboot_nodnic_priv ); flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); nodnic_device_teardown( device_priv ); free_mlx_utils ( & device_priv->utils ); diff --git a/src/drivers/infiniband/flexboot_nodnic.h b/src/drivers/infiniband/flexboot_nodnic.h index 3020f7455..84a6768c1 100644 --- a/src/drivers/infiniband/flexboot_nodnic.h +++ b/src/drivers/infiniband/flexboot_nodnic.h @@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define FLEXBOOT_NODNIC_PAGE_SHIFT 12 #define FLEXBOOT_NODNIC_PAGE_SIZE (1 << FLEXBOOT_NODNIC_PAGE_SHIFT) #define FLEXBOOT_NODNIC_PAGE_MASK (FLEXBOOT_NODNIC_PAGE_SIZE - 1) +#define EN_DEFAULT_ADMIN_MTU 1522 /* Port protocol */ enum flexboot_nodnic_protocol { diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index b704a939f..30eaabab2 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -42,80 +42,47 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "mlx_utils/include/public/mlx_bail.h" #include "mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h" + #define DEVICE_IS_CIB( device ) ( device == 0x1011 ) + /******************************************************************************/ /************* Very simple memory management for umalloced pages **************/ /******* Temporary solution until full memory management is implemented *******/ /******************************************************************************/ + struct golan_page { struct list_head list; userptr_t addr; }; -static void golan_free_pages ( struct list_head *head ) { - struct golan_page *page, *tmp; - list_for_each_entry_safe ( page, tmp, head, list ) { - list_del ( &page->list ); - ufree ( page->addr ); - free ( page ); - } -} +static void golan_free_fw_areas ( struct golan *golan ) { + int i; -static int golan_init_pages ( struct list_head *head ) { - int rc = 0; - - if ( !head ) { - rc = -EINVAL; - goto err_golan_init_pages_bad_param; - } - - INIT_LIST_HEAD ( head ); - return rc; - -err_golan_init_pages_bad_param: - return rc; -} - -static userptr_t golan_get_page ( struct list_head *head ) { - struct golan_page *page; - userptr_t addr; - - if ( list_empty ( head ) ) { - addr = umalloc ( GOLAN_PAGE_SIZE ); - if ( addr == UNULL ) { - goto err_golan_iget_page_alloc_page; + for (i = 0; i < GOLAN_FW_AREAS_NUM; i++) { + if ( golan->fw_areas[i].area ) { + ufree ( golan->fw_areas[i].area ); + golan->fw_areas[i].area = UNULL; } - } else { - page = list_first_entry ( head, struct golan_page, list ); - list_del ( &page->list ); - addr = page->addr; - free ( page ); } -err_golan_iget_page_alloc_page: - return addr; } -static int golan_return_page ( struct list_head *head, - userptr_t addr ) { - struct golan_page *new_entry; - int rc = 0; +static int golan_init_fw_areas ( struct golan *golan ) { + int rc = 0, i = 0; - if ( ! head ) { + if ( ! golan ) { rc = -EINVAL; - goto err_golan_return_page_bad_param; + goto err_golan_init_fw_areas_bad_param; } - new_entry = zalloc ( sizeof ( *new_entry ) ); - if ( new_entry == NULL ) { - rc = -ENOMEM; - goto err_golan_return_page_alloc_page; - } - new_entry->addr = addr; - list_add_tail( &new_entry->list, head ); -err_golan_return_page_alloc_page: -err_golan_return_page_bad_param: + for (i = 0; i < GOLAN_FW_AREAS_NUM; i++) + golan->fw_areas[i].area = UNULL; + + return rc; + + err_golan_init_fw_areas_bad_param: return rc; } + /******************************************************************************/ const char *golan_qp_state_as_string[] = { @@ -177,16 +144,6 @@ static inline u8 xor8_buf(void *buf, int len) return sum; } -static inline int verify_block_sig(struct golan_cmd_prot_block *block) -{ - if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff) - return -EINVAL; - - if (xor8_buf(block, sizeof(*block)) != 0xff) - return -EINVAL; - return 0; -} - static inline const char *cmd_status_str(u8 status) { switch (status) { @@ -258,24 +215,6 @@ static inline void golan_calc_sig(struct golan *golan, uint32_t cmd_idx, cmd->sig = ~xor8_buf(cmd, sizeof(*cmd)); } -/** - * Get Golan FW - */ -static int fw_ver_and_cmdif ( struct golan *golan ) { - DBGC (golan ,"\n[%x:%x]rev maj.min.submin = %x.%x.%x cmdif = %x\n", - golan->iseg->fw_rev, - golan->iseg->cmdif_rev_fw_sub, - fw_rev_maj ( golan ), fw_rev_min ( golan ), - fw_rev_sub ( golan ), cmdif_rev ( golan)); - - if (cmdif_rev ( golan) != PXE_CMDIF_REF) { - DBGC (golan ,"CMDIF %d not supported current is %d\n", - cmdif_rev ( golan ), PXE_CMDIF_REF); - return 1; - } - return 0; -} - static inline void show_out_status(uint32_t *out) { DBG("%x\n", be32_to_cpu(out[0])); @@ -466,10 +405,8 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 while ( pages > 0 ) { uint32_t pas_num = min(pages, MAX_PASE_MBOX); - unsigned i; struct golan_cmd_layout *cmd; struct golan_manage_pages_inbox *in; - struct golan_manage_pages_outbox_data *out; size_ibox = sizeof(struct golan_manage_pages_inbox) + (pas_num * GOLAN_PAS_SIZE); size_obox = sizeof(struct golan_manage_pages_outbox) + (pas_num * GOLAN_PAS_SIZE); @@ -485,11 +422,7 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 in->num_entries = cpu_to_be32(pas_num); if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) { - out = (struct golan_manage_pages_outbox_data *)GET_OUTBOX(golan, MEM_MBOX); out_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries); - for (i = 0; i < out_num_entries; ++i) { - golan_return_page ( &golan->pages, ( BE64_BUS_2_USR( out->pas[i] ) ) ); - } } else { if ( rc == -EBUSY ) { DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" ); @@ -506,17 +439,29 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 pages -= out_num_entries; } DBGC( golan , "%s Pages handled\n", __FUNCTION__); - return 0; + return rc; } -static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __be16 func_id ) { +static inline int golan_provide_pages ( struct golan *golan , uint32_t pages + , __be16 func_id,struct golan_firmware_area *fw_area) { struct mbox *mailbox; int size_ibox = 0; int size_obox = 0; int rc = 0; + userptr_t next_page_addr = UNULL; DBGC(golan, "%s\n", __FUNCTION__); - + if ( ! fw_area->area ) { + fw_area->area = umalloc ( GOLAN_PAGE_SIZE * pages ); + if ( fw_area->area == UNULL ) { + rc = -ENOMEM; + DBGC (golan ,"Failed to allocated %d pages \n",pages); + goto err_golan_alloc_fw_area; + } + fw_area->npages = pages; + } + assert ( fw_area->npages == pages ); + next_page_addr = fw_area->area; while ( pages > 0 ) { uint32_t pas_num = min(pages, MAX_PASE_MBOX); unsigned i, j; @@ -538,12 +483,9 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __ in->func_id = func_id; /* Already BE */ in->num_entries = cpu_to_be32(pas_num); - for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j ) { - if ( ! ( addr = golan_get_page ( & golan->pages ) ) ) { - rc = -ENOMEM; - DBGC (golan ,"Couldnt allocated page \n"); - goto malloc_dma_failed; - } + for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j, + next_page_addr += GOLAN_PAGE_SIZE ) { + addr = next_page_addr; if (GOLAN_PAGE_MASK & user_to_phys(addr, 0)) { DBGC (golan ,"Addr not Page alligned [%lx %lx]\n", user_to_phys(addr, 0), addr); } @@ -563,7 +505,6 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __ get_cmd( golan , MEM_CMD_IDX )->status_own, be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num); } - golan_return_page ( &golan->pages ,addr ); goto err_send_command; } } @@ -571,7 +512,7 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __ return 0; err_send_command: -malloc_dma_failed: +err_golan_alloc_fw_area: /* Go over In box and free pages */ /* Send Error to FW */ /* What is next - Disable HCA? */ @@ -609,7 +550,7 @@ static inline int golan_handle_pages(struct golan *golan, total_pages = (( pages >= 0 ) ? pages : ( pages * ( -1 ) )); if ( mode == GOLAN_PAGES_GIVE ) { - rc = golan_provide_pages(golan, total_pages, func_id); + rc = golan_provide_pages(golan, total_pages, func_id, & ( golan->fw_areas[qry-1] )); } else { rc = golan_take_pages(golan, golan->total_dma_pages, func_id); golan->total_dma_pages = 0; @@ -799,16 +740,14 @@ static int golan_create_eq(struct golan *golan) struct golan_cmd_layout *cmd; struct golan_create_eq_mbox_out *out; int rc, i; - userptr_t addr; eq->cons_index = 0; eq->size = GOLAN_NUM_EQES * sizeof(eq->eqes[0]); - addr = golan_get_page ( &golan->pages ); - if (!addr) { + eq->eqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE ); + if (!eq->eqes) { rc = -ENOMEM; goto err_create_eq_eqe_alloc; } - eq->eqes = (struct golan_eqe *)user_to_virt(addr, 0); /* Set EQEs ownership bit to HW ownership */ for (i = 0; i < GOLAN_NUM_EQES; ++i) { @@ -823,7 +762,7 @@ static int golan_create_eq(struct golan *golan) in = (struct golan_create_eq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); /* Fill the physical address of the page */ - in->pas[0] = USR_2_BE64_BUS(addr); + in->pas[0] = VIRT_2_BE64_BUS( eq->eqes ); in->ctx.log_sz_usr_page = cpu_to_be32((ilog2(GOLAN_NUM_EQES)) << 24 | golan->uar.index); DBGC( golan , "UAR idx %x (BE %x)\n", golan->uar.index, in->ctx.log_sz_usr_page); in->events_mask = cpu_to_be64(1 << GOLAN_EVENT_TYPE_PORT_CHANGE); @@ -842,7 +781,7 @@ static int golan_create_eq(struct golan *golan) return 0; err_create_eq_cmd: - golan_return_page ( & golan->pages, virt_to_user ( eq->eqes ) ); + free_dma ( eq->eqes , GOLAN_PAGE_SIZE ); err_create_eq_eqe_alloc: DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); return rc; @@ -867,7 +806,7 @@ static void golan_destory_eq(struct golan *golan) rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); GOLAN_PRINT_RC_AND_CMD_STATUS; - golan_return_page ( &golan->pages, virt_to_user ( golan->eq.eqes ) ); + free_dma ( golan->eq.eqes , GOLAN_PAGE_SIZE ); golan->eq.eqn = 0; DBGC( golan, "%s Event queue (0x%x) was destroyed\n", __FUNCTION__, eqn); @@ -1016,7 +955,6 @@ static int golan_create_cq(struct ib_device *ibdev, struct golan_create_cq_mbox_out *out; int rc; unsigned int i; - userptr_t addr; golan_cq = zalloc(sizeof(*golan_cq)); if (!golan_cq) { @@ -1031,12 +969,11 @@ static int golan_create_cq(struct ib_device *ibdev, goto err_create_cq_db_alloc; } - addr = golan_get_page ( &golan->pages ); - if (!addr) { + golan_cq->cqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE ); + if (!golan_cq->cqes) { rc = -ENOMEM; goto err_create_cq_cqe_alloc; } - golan_cq->cqes = (struct golan_cqe64 *)user_to_virt(addr, 0); /* Set CQEs ownership bit to HW ownership */ for (i = 0; i < cq->num_cqes; ++i) { @@ -1053,7 +990,7 @@ static int golan_create_cq(struct ib_device *ibdev, in = (struct golan_create_cq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); /* Fill the physical address of the page */ - in->pas[0] = USR_2_BE64_BUS(addr); + in->pas[0] = VIRT_2_BE64_BUS( golan_cq->cqes ); in->ctx.cqe_sz_flags = GOLAN_CQE_SIZE_64 << 5; in->ctx.log_sz_usr_page = cpu_to_be32(((ilog2(cq->num_cqes)) << 24) | golan->uar.index); in->ctx.c_eqn = cpu_to_be16(golan->eq.eqn); @@ -1071,7 +1008,7 @@ static int golan_create_cq(struct ib_device *ibdev, return 0; err_create_cq_cmd: - golan_return_page ( & golan->pages, virt_to_user ( golan_cq->cqes ) ); + free_dma( golan_cq->cqes , GOLAN_PAGE_SIZE ); err_create_cq_cqe_alloc: free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); err_create_cq_db_alloc: @@ -1108,7 +1045,7 @@ static void golan_destroy_cq(struct ib_device *ibdev, cq->cqn = 0; ib_cq_set_drvdata(cq, NULL); - golan_return_page ( & golan->pages, virt_to_user ( golan_cq->cqes ) ); + free_dma ( golan_cq->cqes , GOLAN_PAGE_SIZE ); free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); free(golan_cq); @@ -1154,7 +1091,6 @@ static int golan_create_qp_aux(struct ib_device *ibdev, struct golan_cmd_layout *cmd; struct golan_wqe_data_seg *data; struct golan_create_qp_mbox_out *out; - userptr_t addr; uint32_t wqe_size_in_bytes; uint32_t max_qp_size_in_wqes; unsigned int i; @@ -1202,12 +1138,11 @@ static int golan_create_qp_aux(struct ib_device *ibdev, golan_qp->size = golan_qp->sq.size + golan_qp->rq.size; /* allocate dma memory for WQEs (1 page is enough) - should change it */ - addr = golan_get_page ( &golan->pages ); - if (!addr) { + golan_qp->wqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE ); + if (!golan_qp->wqes) { rc = -ENOMEM; goto err_create_qp_wqe_alloc; } - golan_qp->wqes = user_to_virt(addr, 0); golan_qp->rq.wqes = golan_qp->wqes; golan_qp->sq.wqes = golan_qp->wqes + golan_qp->rq.size;//(union golan_send_wqe *)& //(((struct golan_recv_wqe_ud *)(golan_qp->wqes))[qp->recv.num_wqes]); @@ -1241,7 +1176,7 @@ static int golan_create_qp_aux(struct ib_device *ibdev, in = (struct golan_create_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); /* Fill the physical address of the page */ - in->pas[0] = USR_2_BE64_BUS(addr); + in->pas[0] = VIRT_2_BE64_BUS(golan_qp->wqes); in->ctx.qp_counter_set_usr_page = cpu_to_be32(golan->uar.index); in->ctx.flags_pd = cpu_to_be32(golan->pdn); @@ -1280,7 +1215,7 @@ static int golan_create_qp_aux(struct ib_device *ibdev, err_create_qp_cmd: free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); err_create_qp_db_alloc: - golan_return_page ( & golan->pages, ( userptr_t ) golan_qp->wqes ); + free_dma ( golan_qp->wqes, GOLAN_PAGE_SIZE ); err_create_qp_wqe_alloc: err_create_qp_sq_size: err_create_qp_sq_wqe_size: @@ -1488,7 +1423,7 @@ static void golan_destroy_qp(struct ib_device *ibdev, ib_qp_set_drvdata(qp, NULL); free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); - golan_return_page ( & golan->pages, ( userptr_t ) golan_qp->wqes ); + free_dma ( golan_qp->wqes, GOLAN_PAGE_SIZE ); free(golan_qp); DBGC( golan ,"%s QP 0x%lx was destroyed\n", __FUNCTION__, qpn); @@ -1526,7 +1461,6 @@ static int golan_post_send(struct ib_device *ibdev, unsigned long wqe_idx; struct golan_wqe_data_seg *data = NULL; struct golan_wqe_ctrl_seg *ctrl = NULL; -// static uint8_t toggle = 0; wqe_idx_mask = (qp->send.num_wqes - 1); @@ -1576,8 +1510,9 @@ static int golan_post_send(struct ib_device *ibdev, golan_qp->sq.next_idx = (golan_qp->sq.next_idx + GOLAN_WQEBBS_PER_SEND_UD_WQE); golan_qp->doorbell_record->send_db = cpu_to_be16(golan_qp->sq.next_idx); wmb(); - writeq(*((__be64 *)ctrl), golan->uar.virt + 0x800);// + -// ((toggle++ & 0x1) ? 0x100 : 0x0)); + writeq(*((__be64 *)ctrl), golan->uar.virt + + ( ( golan_qp->sq.next_idx & 0x1 ) ? DB_BUFFER0_EVEN_OFFSET + : DB_BUFFER0_ODD_OFFSET ) ); return 0; } @@ -1702,7 +1637,6 @@ err_query_vport_gid_cmd: static int golan_query_vport_pkey ( struct ib_device *ibdev ) { struct golan *golan = ib_get_drvdata ( ibdev ); struct golan_cmd_layout *cmd; - //struct golan_query_hca_vport_pkey_data *pkey_table; struct golan_query_hca_vport_pkey_inbox *in; int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size)); int rc; @@ -1719,8 +1653,6 @@ static int golan_query_vport_pkey ( struct ib_device *ibdev ) { rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_pkey_cmd ); - //pkey_table = (struct golan_query_hca_vport_pkey_data *)( GET_OUTBOX ( golan, GEN_MBOX ) ); - return 0; err_query_vport_pkey_cmd: DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); @@ -2100,10 +2032,15 @@ static void golan_poll_eq(struct ib_device *ibdev) cqn, eqe->data.cq_err.syndrome); // mlx5_cq_event(dev, cqn, eqe->type); break; + /* + * currently the driver do not support dynamic memory request + * during FW run, a follow up change will allocate FW pages once and + * never release them till driver shutdown, this change will not support + * this request as currently this request is not issued anyway. case GOLAN_EVENT_TYPE_PAGE_REQUEST: { - /* we should check if we get this event while we - * waiting for a command */ + // we should check if we get this event while we + // waiting for a command u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id); s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages); @@ -2112,6 +2049,7 @@ static void golan_poll_eq(struct ib_device *ibdev) golan_provide_pages(golan, npages, func_id); } break; + */ default: DBGC (golan ,"%s Unhandled event 0x%x on EQ 0x%x\n", __FUNCTION__, eqe->type, eq->eqn); @@ -2231,7 +2169,6 @@ static int golan_register_ibdev(struct golan_port *port) static inline void golan_bring_down(struct golan *golan) { - DBGC(golan, "%s: start\n", __FUNCTION__); if (~golan->flags & GOLAN_OPEN) { @@ -2413,7 +2350,8 @@ static int golan_probe_normal ( struct pci_device *pci ) { goto err_golan_alloc; } - if ( golan_init_pages( &golan->pages ) ) { + /* at POST stage some BIOSes have limited available dynamic memory */ + if ( golan_init_fw_areas ( golan ) ) { rc = -ENOMEM; goto err_golan_golan_init_pages; } @@ -2423,11 +2361,6 @@ static int golan_probe_normal ( struct pci_device *pci ) { golan->pci = pci; golan_pci_init( golan ); /* config command queues */ - if ( fw_ver_and_cmdif( golan ) ) { - rc = -1; - goto err_fw_ver_cmdif; - } - if ( golan_bring_up( golan ) ) { DBGC (golan ,"golan bringup failed\n"); rc = -1; @@ -2482,9 +2415,8 @@ err_golan_probe_alloc_ibdev: err_utils_init: golan_bring_down ( golan ); err_golan_bringup: -err_fw_ver_cmdif: iounmap( golan->iseg ); - golan_free_pages( &golan->pages ); + golan_free_fw_areas ( golan ); err_golan_golan_init_pages: free ( golan ); err_golan_alloc: @@ -2513,7 +2445,7 @@ static void golan_remove_normal ( struct pci_device *pci ) { free_mlx_utils ( & golan->utils ); } iounmap( golan->iseg ); - golan_free_pages( &golan->pages ); + golan_free_fw_areas ( golan ); free(golan); } @@ -2528,14 +2460,16 @@ static mlx_status shomron_tx_uar_send_db ( struct ib_device *ibdev, ( struct shomron_nodnic_eth_send_wqe * )wqbb; struct shomronprm_wqe_segment_ctrl_send *ctrl; - if ( ! ibdev || ! eth_wqe || ! flexboot_nodnic->device_priv.uar.virt ) { + if ( ! eth_wqe || ! flexboot_nodnic->device_priv.uar.virt ) { DBG("%s: Invalid parameters\n",__FUNCTION__); status = MLX_FAILED; goto err; } wmb(); ctrl = & eth_wqe->ctrl; - writeq(*((__be64 *)ctrl), flexboot_nodnic->device_priv.uar.virt + 0x800); + writeq(*((__be64 *)ctrl), flexboot_nodnic->device_priv.uar.virt + + ( ( MLX_GET ( ctrl, wqe_index ) & 0x1 ) ? DB_BUFFER0_ODD_OFFSET + : DB_BUFFER0_EVEN_OFFSET ) ); err: return status; } diff --git a/src/drivers/infiniband/golan.h b/src/drivers/infiniband/golan.h index c5227dd73..2fd06ecf0 100755 --- a/src/drivers/infiniband/golan.h +++ b/src/drivers/infiniband/golan.h @@ -111,6 +111,18 @@ struct golan_uar { unsigned long phys; }; + +struct golan_firmware_area { + /* length of area in pages */ + uint32_t npages; + /** Firmware area in external memory + * + * This is allocated when first needed, and freed only on + * final teardown, in order to avoid memory map changes at + * runtime. + */ + userptr_t area; +}; /* Queue Pair */ #define GOLAN_SEND_WQE_BB_SIZE 64 #define GOLAN_SEND_UD_WQE_SIZE sizeof(struct golan_send_wqe_ud) @@ -204,6 +216,8 @@ struct golan_completion_queue { #define GOLAN_EQE_SIZE sizeof(struct golan_eqe) #define GOLAN_NUM_EQES 8 #define GOLAN_EQ_DOORBELL_OFFSET 0x40 +#define DB_BUFFER0_EVEN_OFFSET 0x800 +#define DB_BUFFER0_ODD_OFFSET 0x900 #define GOLAN_EQ_MAP_ALL_EVENTS \ ((1 << GOLAN_EVENT_TYPE_PATH_MIG )| \ @@ -323,6 +337,8 @@ struct golan { mlx_utils *utils; struct golan_port ports[GOLAN_MAX_PORTS]; +#define GOLAN_FW_AREAS_NUM 2 + struct golan_firmware_area fw_areas[GOLAN_FW_AREAS_NUM]; }; #endif /* _GOLAN_H_*/ diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c index f6fdacdbf..65655457c 100644 --- a/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c @@ -169,13 +169,7 @@ nodnic_device_clear_int ( mlx_status status = MLX_SUCCESS; mlx_uint32 disable = 1; #ifndef DEVICE_CX3 -#define NODNIC_CLEAR_INT_BAR_OFFSET 0x100C - if ( device_priv->device_cap.support_bar_cq_ctrl ) { - status = mlx_pci_mem_write ( device_priv->utils, MlxPciWidthUint32, 0, - ( mlx_uint64 ) ( NODNIC_CLEAR_INT_BAR_OFFSET ), 1, &disable ); - } else { - status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable); - } + status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable); MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit"); #else mlx_utils *utils = device_priv->utils; diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c index 755730284..f0af1ecfa 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c @@ -20,8 +20,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "mlx_mtu.h" -#include "mlx_memory.h" -#include "mlx_bail.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" mlx_status mlx_get_max_mtu( @@ -58,3 +58,37 @@ reg_err: bad_param: return status; } + +mlx_status +mlx_set_admin_mtu( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN mlx_uint32 admin_mtu + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_mtu mtu; + mlx_uint32 reg_status; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &mtu, 0, sizeof(mtu)); + + mtu.local_port = port_num; + mtu.admin_mtu = admin_mtu; + + status = mlx_reg_access(utils, REG_ID_PMTU, REG_ACCESS_WRITE, &mtu, + sizeof(mtu), ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } +reg_err: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h index c6222625c..bd3ded3f1 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h @@ -22,8 +22,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); -#include "mlx_reg_access.h" -#include "mlx_utils.h" +#include "../../include/public/mlx_utils.h" +#include "../../mlx_lib/mlx_reg_access/mlx_reg_access.h" #define BYTE_TO_BIT 0x8 @@ -49,4 +49,10 @@ mlx_get_max_mtu( OUT mlx_uint32 *max_mtu ); +mlx_status +mlx_set_admin_mtu( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN mlx_uint32 admin_mtu + ); #endif /* MLX_MTU_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c index 1ea68dd8a..028ba5ce6 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c @@ -39,7 +39,6 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { TlvMappingEntry(0x2001, 0x195, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2010, 0x210, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2011, 0x211, NVRAM_TLV_CLASS_GLOBAL, FALSE), - TlvMappingEntry(0x2020, 0x2020, NVRAM_TLV_CLASS_PHYSICAL_PORT, FALSE), TlvMappingEntry(0x2021, 0x221, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2023, 0x223, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x2006, 0x206, NVRAM_TLV_CLASS_HOST, FALSE), @@ -67,6 +66,7 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { TlvMappingEntry(0x110, 0x110, NVRAM_TLV_CLASS_HOST, FALSE), TlvMappingEntry(0x192, 0x192, NVRAM_TLV_CLASS_GLOBAL, FALSE), TlvMappingEntry(0x101, 0x101, NVRAM_TLV_CLASS_GLOBAL, TRUE), + TlvMappingEntry(0x194, 0x194, NVRAM_TLV_CLASS_GLOBAL, FALSE), TlvMappingEntry(0, 0, 0, 0), }; @@ -239,6 +239,7 @@ nvconfig_nvdata_access( IN REG_ACCESS_OPT opt, IN mlx_size data_size, IN NV_DEFAULT_OPT def_en, + IN NVDA_WRITER_ID writer_id, IN OUT mlx_uint8 *version, IN OUT mlx_void *data ) @@ -263,10 +264,9 @@ nvconfig_nvdata_access( data_size_align_to_dword = ((data_size + 3) / sizeof(mlx_uint32)) * sizeof(mlx_uint32); mlx_memory_set(utils, &nvda, 0, sizeof(nvda)); nvda.nv_header.length = data_size_align_to_dword; - nvda.nv_header.rd_en = 0; - nvda.nv_header.def_en = def_en; - nvda.nv_header.over_en = 1; + nvda.nv_header.access_mode = def_en; nvda.nv_header.version = *version; + nvda.nv_header.writer_id = writer_id; nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nvda.nv_header.tlv_type); diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h index 0a99bb1b5..3058c781a 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h @@ -31,6 +31,17 @@ typedef enum { NVRAM_TLV_CLASS_HOST = 3, } NVRAM_CLASS_CODE; +typedef enum { + NVDA_NV_HEADER_WRITER_ID_UEFI_HII = 0x6, + NVDA_NV_HEADER_WRITER_ID_FLEXBOOT = 0x8, +} NVDA_WRITER_ID; + +typedef enum { + TLV_ACCESS_DEFAULT_DIS = 0, + TLV_ACCESS_CURRENT = 1, + TLV_ACCESS_DEFAULT_EN = 2, +} NV_DEFAULT_OPT; + struct nvconfig_tlv_type_per_port { mlx_uint32 param_idx :16; mlx_uint32 port :8; @@ -78,26 +89,24 @@ struct nvconfig_header { mlx_uint32 length :9; /*Size of configuration item data in bytes between 0..256 */ mlx_uint32 reserved0 :3; mlx_uint32 version :4; /* Configuration item version */ - mlx_uint32 reserved1 :7; + mlx_uint32 writer_id :5; + mlx_uint32 reserved1 :1; - mlx_uint32 def_en :1; /*Choose whether to access the default value or the user-defined value. - 0x0 Read or write the user-defined value. - 0x1 Read the default value (only valid for reads).*/ + mlx_uint32 access_mode :2; /*Defines which value of the Configuration Item will be accessed. + 0x0: NEXT - Next value to be applied + 0x1: CURRENT - Currently set values (only valid for Query operation) Supported only if NVGC.nvda_read_current_settings==1. + 0x2: FACTORY - Default factory values (only valid for Query operation). Supported only if NVGC.nvda_read_factory_settings==1.*/ - mlx_uint32 rd_en :1; /*enables reading the TLV by lower priorities - 0 - TLV can be read by the subsequent lifecycle priorities. - 1 - TLV cannot be read by the subsequent lifecycle priorities. */ - mlx_uint32 over_en :1; /*enables overwriting the TLV by lower priorities - 0 - Can only be overwritten by the current lifecycle priority - 1 - Allowed to be overwritten by subsequent lifecycle priorities */ + mlx_uint32 reserved2 :2; mlx_uint32 header_type :2; - mlx_uint32 priority :2; + mlx_uint32 reserved3 :2; mlx_uint32 valid :2; /* -------------- */ union nvconfig_tlv_type tlv_type;; /* -------------- */ mlx_uint32 crc :16; mlx_uint32 reserved :16; + }; #define NVCONFIG_MAX_TLV_SIZE 256 @@ -149,6 +158,7 @@ nvconfig_nvdata_access( IN REG_ACCESS_OPT opt, IN mlx_size data_size, IN NV_DEFAULT_OPT def_en, + IN NVDA_WRITER_ID writer_id, IN OUT mlx_uint8 *version, IN OUT mlx_void *data ); diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c index f5b2f155f..ca5a65914 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c @@ -386,7 +386,8 @@ nvconfig_nvdata_default_access( mlx_uint8 version = 0; status = nvconfig_nvdata_access(utils, port, tlv_type, REG_ACCESS_READ, - data_size, TLV_ACCESS_DEFAULT_EN, &version, data); + data_size, TLV_ACCESS_DEFAULT_EN, 0, + &version, data); MLX_CHECK_STATUS(NULL, status, nvdata_access_err, "nvconfig_nvdata_access failed "); for (index = 0; index * 4 < data_size; index++) { @@ -493,6 +494,8 @@ nvconfig_read_rom_ini_values( ) { mlx_status status = MLX_SUCCESS; + mlx_uint8 version = 0; + mlx_uint32 index; if (utils == NULL || rom_ini == NULL) { status = MLX_INVALID_PARAMETER; @@ -501,8 +504,16 @@ nvconfig_read_rom_ini_values( } mlx_memory_set(utils, rom_ini, 0, sizeof(*rom_ini)); - status = nvconfig_nvdata_default_access(utils, 0, GLOBAL_ROM_INI_TYPE, - sizeof(*rom_ini), rom_ini); + status = nvconfig_nvdata_access(utils, 0, GLOBAL_ROM_INI_TYPE, REG_ACCESS_READ, + sizeof(*rom_ini), TLV_ACCESS_DEFAULT_DIS, 0, + &version, rom_ini); + MLX_CHECK_STATUS(NULL, status, bad_param, + "nvconfig_nvdata_access failed "); + for (index = 0; index * 4 < sizeof(*rom_ini); index++) { + mlx_memory_be32_to_cpu(utils, (((mlx_uint32 *) rom_ini)[index]), + ((mlx_uint32 *) rom_ini) + index); + } + bad_param: return status; } diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c deleted file mode 100644 index 3852efbf1..000000000 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2015 Mellanox Technologies Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "mlx_ocbb.h" -#include "mlx_icmd.h" -#include "mlx_bail.h" - -mlx_status -mlx_ocbb_init ( - IN mlx_utils *utils, - IN mlx_uint64 address - ) -{ - mlx_status status = MLX_SUCCESS; - struct mlx_ocbb_init ocbb_init; - ocbb_init.address_hi = (mlx_uint32)(address >> 32); - ocbb_init.address_lo = (mlx_uint32)address; - - if (utils == NULL) { - status = MLX_INVALID_PARAMETER; - goto bad_param; - } - - status = mlx_icmd_send_command( - utils, - OCBB_INIT, - &ocbb_init, - sizeof(ocbb_init), - 0 - ); - MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); -icmd_err: -bad_param: - return status; -} - -mlx_status -mlx_ocbb_query_header_status ( - IN mlx_utils *utils, - OUT mlx_uint8 *ocbb_status - ) -{ - mlx_status status = MLX_SUCCESS; - struct mlx_ocbb_query_status ocbb_query_status; - - if (utils == NULL) { - status = MLX_INVALID_PARAMETER; - goto bad_param; - } - - status = mlx_icmd_send_command( - utils, - OCBB_QUERY_HEADER_STATUS, - &ocbb_query_status, - 0, - sizeof(ocbb_query_status) - ); - MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); - *ocbb_status = ocbb_query_status.status; -icmd_err: -bad_param: - return status; -} - -mlx_status -mlx_ocbb_query_etoc_status ( - IN mlx_utils *utils, - OUT mlx_uint8 *ocbb_status - ) -{ - mlx_status status = MLX_SUCCESS; - struct mlx_ocbb_query_status ocbb_query_status; - - if (utils == NULL) { - status = MLX_INVALID_PARAMETER; - goto bad_param; - } - - status = mlx_icmd_send_command( - utils, - OCBB_QUERY_ETOC_STATUS, - &ocbb_query_status, - 0, - sizeof(ocbb_query_status) - ); - MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); - *ocbb_status = ocbb_query_status.status; -icmd_err: -bad_param: - return status; -} - -mlx_status -mlx_ocbb_set_event ( - IN mlx_utils *utils, - IN mlx_uint64 event_data, - IN mlx_uint8 event_number, - IN mlx_uint8 event_length, - IN mlx_uint8 data_length, - IN mlx_uint8 data_start_offset - ) -{ - mlx_status status = MLX_SUCCESS; - struct mlx_ocbb_set_event ocbb_event; - - if (utils == NULL) { - status = MLX_INVALID_PARAMETER; - goto bad_param; - } - - ocbb_event.data_length = data_length; - ocbb_event.data_start_offset = data_start_offset; - ocbb_event.event_number = event_number; - ocbb_event.event_data = event_data; - ocbb_event.event_length = event_length; - status = mlx_icmd_send_command( - utils, - OCBB_QUERY_SET_EVENT, - &ocbb_event, - sizeof(ocbb_event), - 0 - ); - MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); -icmd_err: -bad_param: - return status; -} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h deleted file mode 100644 index 49312b98f..000000000 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_ocbb/mlx_ocbb.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef MLX_OCBB_H_ -#define MLX_OCBB_H_ - -/* - * Copyright (C) 2015 Mellanox Technologies Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "mlx_utils.h" - -#define MLX_OCBB_EVENT_DATA_SIZE 2 -struct mlx_ocbb_init { - mlx_uint32 address_hi; - mlx_uint32 address_lo; -}; - -struct mlx_ocbb_query_status { - mlx_uint32 reserved :24; - mlx_uint32 status :8; -}; - -struct mlx_ocbb_set_event { - mlx_uint64 event_data; - mlx_uint32 event_number :8; - mlx_uint32 event_length :8; - mlx_uint32 data_length :8; - mlx_uint32 data_start_offset :8; -}; - -mlx_status -mlx_ocbb_init ( - IN mlx_utils *utils, - IN mlx_uint64 address - ); - -mlx_status -mlx_ocbb_query_header_status ( - IN mlx_utils *utils, - OUT mlx_uint8 *ocbb_status - ); - -mlx_status -mlx_ocbb_query_etoc_status ( - IN mlx_utils *utils, - OUT mlx_uint8 *ocbb_status - ); - -mlx_status -mlx_ocbb_set_event ( - IN mlx_utils *utils, - IN mlx_uint64 EventData, - IN mlx_uint8 EventNumber, - IN mlx_uint8 EventLength, - IN mlx_uint8 DataLength, - IN mlx_uint8 DataStartOffset - ); -#endif /* MLX_OCBB_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h index 9fbf51631..ca7ca2f84 100644 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h @@ -31,11 +31,6 @@ typedef enum { REG_ACCESS_WRITE = 2, } REG_ACCESS_OPT; -typedef enum { - TLV_ACCESS_DEFAULT_DIS = 0, - TLV_ACCESS_DEFAULT_EN = 1, -} NV_DEFAULT_OPT; - #define REG_ID_NVDA 0x9024 #define REG_ID_NVDI 0x9025 #define REG_ID_NVIA 0x9029 diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c deleted file mode 100644 index a6c23c4a1..000000000 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2015 Mellanox Technologies Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "mlx_wol_rol.h" -#include "mlx_icmd.h" -#include "mlx_memory.h" -#include "mlx_bail.h" - -mlx_status -mlx_set_wol ( - IN mlx_utils *utils, - IN mlx_uint8 wol_mask - ) -{ - mlx_status status = MLX_SUCCESS; - struct mlx_wol_rol wol_rol; - - if (utils == NULL) { - status = MLX_INVALID_PARAMETER; - goto bad_param; - } - - mlx_memory_set(utils, &wol_rol, 0, sizeof(wol_rol)); - wol_rol.wol_mode_valid = TRUE; - wol_rol.wol_mode = wol_mask; - status = mlx_icmd_send_command( - utils, - SET_WOL_ROL, - &wol_rol, - sizeof(wol_rol), - 0 - ); - MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); -icmd_err: -bad_param: - return status; -} - -mlx_status -mlx_query_wol ( - IN mlx_utils *utils, - OUT mlx_uint8 *wol_mask - ) -{ - mlx_status status = MLX_SUCCESS; - struct mlx_wol_rol wol_rol; - - if (utils == NULL || wol_mask == NULL) { - status = MLX_INVALID_PARAMETER; - goto bad_param; - } - - mlx_memory_set(utils, &wol_rol, 0, sizeof(wol_rol)); - status = mlx_icmd_send_command( - utils, - QUERY_WOL_ROL, - &wol_rol, - 0, - sizeof(wol_rol) - ); - MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); - *wol_mask = wol_rol.wol_mode; -icmd_err: -bad_param: - return status; -} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h deleted file mode 100644 index 610419d5d..000000000 --- a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_wol_rol/mlx_wol_rol.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef MLX_WOL_ROL_H_ -#define MLX_WOL_ROL_H_ - -/* - * Copyright (C) 2015 Mellanox Technologies Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - - -#include "mlx_utils.h" - -typedef enum { - WOL_MODE_DISABLE = 0x0, - WOL_MODE_SECURE = 0x2, - WOL_MODE_MAGIC = 0x4, - WOL_MODE_ARP = 0x8, - WOL_MODE_BC = 0x10, - WOL_MODE_MC = 0x20, - WOL_MODE_UC = 0x40, - WOL_MODE_PHY = 0x80, -} WOL_MODE; - -struct mlx_wol_rol { - mlx_uint32 reserved0 :32; - mlx_uint32 reserved1 :32; - mlx_uint32 wol_mode :8; - mlx_uint32 rol_mode :8; - mlx_uint32 reserved3 :14; - mlx_uint32 wol_mode_valid :1; - mlx_uint32 rol_mode_valid :1; -}; - -mlx_status -mlx_set_wol ( - IN mlx_utils *utils, - IN mlx_uint8 wol_mask - ); - -mlx_status -mlx_query_wol ( - IN mlx_utils *utils, - OUT mlx_uint8 *wol_mask - ); - -#endif /* MLX_WOL_ROL_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c b/src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c deleted file mode 100644 index 4386ad9b9..000000000 --- a/src/drivers/infiniband/mlx_utils/src/private/uefi/mlx_logging_impl.c +++ /dev/null @@ -1,9 +0,0 @@ -MlxDebugLogImpl() - { - DBGC((DEBUG),""); - } -MlxInfoLogImpl() -{ - DBGC((INFO),""); - } -} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c index f9f9b2a12..d4ff1b9a1 100644 --- a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c @@ -107,7 +107,7 @@ mlx_pci_mem_read( status = MLX_INVALID_PARAMETER; goto bail; } - status = mlx_pci_mem_read_priv(utils, bar_index, width, offset, count, buffer); + status = mlx_pci_mem_read_priv(utils, width,bar_index, offset, count, buffer); bail: return status; } From 19d3e966d9ee535fc89e8fa26737cc133882ae1c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 23 Mar 2017 17:35:10 +0200 Subject: [PATCH 421/591] [pcnet32] Eliminate redundant register read The value of ( ( x & 0x0c00 ) | 0x0c00 ) is always 0x0c00 regardless of the value of x, and so the read_csr() is redundant. (There are no read side effects for this register, according to the datasheet.) This line of code originated in Linux kernel 2.3.19pre1 as a->write_csr(ioaddr, 80, a->read_csr(ioaddr, 80) | 0x0c00); and was modified in kernel 2.3.41pre4 to read a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); In the absence of commit messages, the intention of the code is unclear. However, the logic resulting in a fixed value of 0x0c00 has remained unaltered for over 17 years, and can probably be assumed to have the correct overall result. Signed-off-by: Michael Brown --- src/drivers/net/pcnet32.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c index 26633a248..2635aaca2 100644 --- a/src/drivers/net/pcnet32.c +++ b/src/drivers/net/pcnet32.c @@ -414,8 +414,7 @@ pcnet32_chip_detect ( struct pcnet32_private *priv ) if (fset) { a->write_bcr ( ioaddr, 18, ( a->read_bcr ( ioaddr, 18 ) | 0x0860 ) ); - a->write_csr ( ioaddr, 80, - ( a->read_csr ( ioaddr, 80 ) & 0x0C00) | 0x0C00 ); + a->write_csr ( ioaddr, 80, 0x0c00 ); } priv->full_duplex = fdx; From a317e9a310d6ea5288edd39d9933e6c2fb2358aa Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Thu, 23 Mar 2017 17:54:03 +0200 Subject: [PATCH 422/591] [sky2] Use 32-bit read to read Y2_VAUX_AVAIL B0_CTST is a 24bit register according to the vendor driver (sk98lin). A 16bit read on B0_CTST will always return 0 for Y2_VAUX_AVAIL (1<<16), so use a 32bit read when testing Y2_VAUX_AVAIL. [This patch is copied directly from the Linux kernel tree.] Signed-off-by: Mike McCormack Signed-off-by: Michael Brown --- src/drivers/net/sky2.c | 2 +- src/drivers/net/sky2.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/sky2.c b/src/drivers/net/sky2.c index 35ff66c6d..211f22466 100644 --- a/src/drivers/net/sky2.c +++ b/src/drivers/net/sky2.c @@ -246,7 +246,7 @@ static void sky2_power_aux(struct sky2_hw *hw) Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); /* switch power to VAUX */ - if (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL) + if (sky2_read32(hw, B0_CTST) & Y2_VAUX_AVAIL) sky2_write8(hw, B0_POWER_CTRL, (PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF)); diff --git a/src/drivers/net/sky2.h b/src/drivers/net/sky2.h index 9bb63010e..9c3313128 100644 --- a/src/drivers/net/sky2.h +++ b/src/drivers/net/sky2.h @@ -294,7 +294,7 @@ enum csr_regs { Y2_CFG_AER = 0x1d00, /* PCI Advanced Error Report region */ }; -/* B0_CTST 16 bit Control/Status register */ +/* B0_CTST 24 bit Control/Status register */ enum { Y2_VMAIN_AVAIL = 1<<17,/* VMAIN available (YUKON-2 only) */ Y2_VAUX_AVAIL = 1<<16,/* VAUX available (YUKON-2 only) */ From b340971852912616de01b51848b224de23024177 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 23 Mar 2017 18:15:24 +0200 Subject: [PATCH 423/591] [iobuf] Increase minimum I/O buffer size to 128 bytes The eIPoIB translation layer needs to translate outbound ARP packets from Ethernet to IPoIB. A 64-byte buffer (starting with the Ethernet header) does not provide enough tailroom to expand to hold the two 20-byte IPoIB MAC addresses. The result is that an UNDI API user will be unable to send ARP packets. We could potentially shuffle the packet contents to reuse the space occupied by the stripped Ethernet link-layer header, but this would add complexity. Instead, fix by increasing the minimum allocation size to 128 bytes. Signed-off-by: Michael Brown --- src/include/ipxe/iobuf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/ipxe/iobuf.h b/src/include/ipxe/iobuf.h index 27d285d44..b40ade350 100644 --- a/src/include/ipxe/iobuf.h +++ b/src/include/ipxe/iobuf.h @@ -20,7 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * necessary. This is used on behalf of hardware that is not capable * of auto-padding. */ -#define IOB_ZLEN 64 +#define IOB_ZLEN 128 /** * A persistent I/O buffer From c13bf525098949291cf930d5c87da5c51c812af4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 23 Mar 2017 21:10:25 +0200 Subject: [PATCH 424/591] [vxge] Fix use of stale I/O buffer on error path Signed-off-by: Michael Brown --- src/drivers/net/vxge/vxge_traffic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/vxge/vxge_traffic.c b/src/drivers/net/vxge/vxge_traffic.c index 0b1caf106..dbd799015 100644 --- a/src/drivers/net/vxge/vxge_traffic.c +++ b/src/drivers/net/vxge/vxge_traffic.c @@ -667,6 +667,8 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) vxge_debug(VXGE_INFO, "%s: rx frame received at offset %d\n", hldev->ndev->name, ring->rxd_offset); + iobuf = (struct io_buffer *)(intptr_t)rxd->host_control; + if (tcode != VXGE_HW_RING_T_CODE_OK) { netdev_rx_err(hldev->ndev, NULL, -EINVAL); vxge_debug(VXGE_ERR, "%s:%d, rx error tcode %d\n", @@ -675,8 +677,6 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) goto err1; } - iobuf = (struct io_buffer *)(intptr_t)rxd->host_control; - len = VXGE_HW_RING_RXD_1_BUFFER0_SIZE_GET(rxd->control_1); len -= ETH_FCS_LEN; From 6bc4a8ac918b2dc5a460b3d2032055d8f7a478ff Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 26 Mar 2017 11:21:14 +0300 Subject: [PATCH 425/591] [scsi] Avoid duplicate call to scsicmd_close() on TEST UNIT READY failure When the TEST UNIT READY command receives an error response, the shutdown of the command's block data interface will result in scsidev_ready() closing the SCSI device. This will subsequently result in a duplicate call to scsicmd_close(), leading to an assertion failure when list_del() is called for the second time. Fix by removing the command from the list of outstanding commands before shutting down the command's interfaces. Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index cb4bb94c4..d51b5cfa5 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -392,11 +392,13 @@ static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) { scsidev, scsicmd->tag, strerror ( rc ) ); } + /* Remove from list of commands */ + list_del ( &scsicmd->list ); + /* Shut down interfaces */ intfs_shutdown ( rc, &scsicmd->scsi, &scsicmd->block, NULL ); - /* Remove from list of commands and drop list's reference */ - list_del ( &scsicmd->list ); + /* Drop list's reference */ scsicmd_put ( scsicmd ); } From c212597336fd055de854043b83425cbdf1f42603 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 26 Mar 2017 15:42:52 +0300 Subject: [PATCH 426/591] [block] Add dummy SAN device Add a dummy SAN device which allows the "sanhook" command to be tested even when no SAN booting capability is present on the platform. This allows substantial portions of the SAN boot code to be run in Linux under Valgrind. Signed-off-by: Michael Brown --- src/core/dummy_sanboot.c | 115 +++++++++++++++++++++++++++++++ src/include/ipxe/dummy_sanboot.h | 18 +++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/sanboot.h | 1 + 4 files changed, 135 insertions(+) create mode 100644 src/core/dummy_sanboot.c create mode 100644 src/include/ipxe/dummy_sanboot.h diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c new file mode 100644 index 000000000..163477475 --- /dev/null +++ b/src/core/dummy_sanboot.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 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 + * + * Dummy SAN device + * + */ + +#include +#include + +/** + * Hook dummy SAN device + * + * @v uri URI + * @v drive Drive number + * @ret drive Drive number, or negative error + */ +static int dummy_san_hook ( struct uri *uri, unsigned int drive ) { + struct san_device *sandev; + int rc; + + /* Allocate SAN device */ + sandev = alloc_sandev ( uri, 0 ); + if ( ! sandev ) { + rc = -ENOMEM; + goto err_alloc; + } + sandev->drive = drive; + + /* Register SAN device */ + if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x could not register: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_register; + } + + return drive; + + unregister_sandev ( sandev ); + err_register: + sandev_put ( sandev ); + err_alloc: + return rc; +} + +/** + * Unhook dummy SAN device + * + * @v drive Drive number + */ +static void dummy_san_unhook ( unsigned int drive ) { + struct san_device *sandev; + + /* Find drive */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { + DBG ( "SAN %#02x does not exist\n", drive ); + return; + } + + /* Unregister SAN device */ + unregister_sandev ( sandev ); + + /* Drop reference to drive */ + sandev_put ( sandev ); +} + +/** + * Boot from dummy SAN device + * + * @v drive Drive number + */ +static int dummy_san_boot ( unsigned int drive __unused ) { + + return -EOPNOTSUPP; +} + +/** + * Describe dummy SAN device + * + * @v drive Drive number + */ +static int dummy_san_describe ( unsigned int drive __unused ) { + + return 0; +} + +PROVIDE_SANBOOT ( dummy, san_hook, dummy_san_hook ); +PROVIDE_SANBOOT ( dummy, san_unhook, dummy_san_unhook ); +PROVIDE_SANBOOT ( dummy, san_boot, dummy_san_boot ); +PROVIDE_SANBOOT ( dummy, san_describe, dummy_san_describe ); diff --git a/src/include/ipxe/dummy_sanboot.h b/src/include/ipxe/dummy_sanboot.h new file mode 100644 index 000000000..9c9d942aa --- /dev/null +++ b/src/include/ipxe/dummy_sanboot.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_DUMMY_SANBOOT_H +#define _IPXE_DUMMY_SANBOOT_H + +/** @file + * + * Dummy SAN device + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef SANBOOT_DUMMY +#define SANBOOT_PREFIX_dummy +#else +#define SANBOOT_PREFIX_dummy __dummy_ +#endif + +#endif /* _IPXE_DUMMY_SANBOOT_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index cd5c1959c..703f45652 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -73,6 +73,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_pixbuf ( ERRFILE_CORE | 0x00210000 ) #define ERRFILE_efi_block ( ERRFILE_CORE | 0x00220000 ) #define ERRFILE_sanboot ( ERRFILE_CORE | 0x00230000 ) +#define ERRFILE_dummy_sanboot ( ERRFILE_CORE | 0x00240000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 3e7ed1c80..c2e57f716 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -90,6 +90,7 @@ struct san_device { /* Include all architecture-independent sanboot API headers */ #include +#include #include /* Include all architecture-dependent sanboot API headers */ From bb5a54b79a414082d0b39d478a8b3332c56d68e5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 26 Mar 2017 15:12:11 +0300 Subject: [PATCH 427/591] [block] Add basic multipath support Add basic support for multipath block devices. The "sanboot" and "sanhook" commands now accept a list of SAN URIs. We open all URIs concurrently. The first connection to become available for issuing block device commands is marked as the active path and used for all subsequent commands; all other connections are then closed. Whenever the active path fails, we reopen all URIs and repeat the process. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 20 +- src/core/dummy_sanboot.c | 8 +- src/core/null_sanboot.c | 5 +- src/core/sanboot.c | 302 +++++++++++++++++++------- src/hci/commands/sanboot_cmd.c | 30 +-- src/include/ipxe/sanboot.h | 46 +++- src/include/usr/autoboot.h | 3 +- src/interface/efi/efi_block.c | 25 ++- src/usr/autoboot.c | 14 +- src/usr/pxemenu.c | 2 +- 10 files changed, 327 insertions(+), 128 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 23cfefca9..c322440e7 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -833,6 +833,7 @@ static int int13_extended_seek ( struct san_device *sandev, */ static int int13_device_path_info ( struct san_device *sandev, struct edd_device_path_information *dpi ) { + struct san_path *sanpath; struct device *device; struct device_description *desc; unsigned int i; @@ -843,9 +844,11 @@ static int int13_device_path_info ( struct san_device *sandev, if ( sandev_needs_reopen ( sandev ) && ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) return rc; + sanpath = sandev->active; + assert ( sanpath != NULL ); /* Get underlying hardware device */ - device = identify_device ( &sandev->block ); + device = identify_device ( &sanpath->block ); if ( ! device ) { DBGC ( sandev, "INT13 drive %02x cannot identify hardware " "device\n", sandev->drive ); @@ -869,7 +872,7 @@ static int int13_device_path_info ( struct san_device *sandev, } /* Get EDD block device description */ - if ( ( rc = edd_describe ( &sandev->block, &dpi->interface_type, + if ( ( rc = edd_describe ( &sanpath->block, &dpi->interface_type, &dpi->device_path ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x cannot identify block device: " "%s\n", sandev->drive, strerror ( rc ) ); @@ -1199,14 +1202,16 @@ static void int13_unhook_vector ( void ) { /** * Hook INT 13 SAN device * - * @v uri URI * @v drive Drive number + * @v uris List of URIs + * @v count Number of URIs * @ret drive Drive number, or negative error * * Registers the drive with the INT 13 emulation subsystem, and hooks * the INT 13 interrupt vector (if not already hooked). */ -static int int13_hook ( struct uri *uri, unsigned int drive ) { +static int int13_hook ( unsigned int drive, struct uri **uris, + unsigned int count ) { struct san_device *sandev; struct int13_data *int13; unsigned int natural_drive; @@ -1223,7 +1228,7 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { drive = natural_drive; /* Allocate SAN device */ - sandev = alloc_sandev ( uri, sizeof ( *int13 ) ); + sandev = alloc_sandev ( uris, count, sizeof ( *int13 ) ); if ( ! sandev ) { rc = -ENOMEM; goto err_alloc; @@ -1525,6 +1530,7 @@ static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) )); */ static int int13_describe ( unsigned int drive ) { struct san_device *sandev; + struct san_path *sanpath; struct segoff xbft_address; int rc; @@ -1539,6 +1545,8 @@ static int int13_describe ( unsigned int drive ) { if ( sandev_needs_reopen ( sandev ) && ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) return rc; + sanpath = sandev->active; + assert ( sanpath != NULL ); /* Clear table */ memset ( &xbftab, 0, sizeof ( xbftab ) ); @@ -1550,7 +1558,7 @@ static int int13_describe ( unsigned int drive ) { sizeof ( xbftab.acpi.oem_table_id ) ); /* Fill in remaining parameters */ - if ( ( rc = acpi_describe ( &sandev->block, &xbftab.acpi, + if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi, sizeof ( xbftab ) ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x could not create ACPI " "description: %s\n", sandev->drive, strerror ( rc ) ); diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c index 163477475..64d5206fe 100644 --- a/src/core/dummy_sanboot.c +++ b/src/core/dummy_sanboot.c @@ -35,16 +35,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Hook dummy SAN device * - * @v uri URI * @v drive Drive number + * @v uris List of URIs + * @v count Number of URIs * @ret drive Drive number, or negative error */ -static int dummy_san_hook ( struct uri *uri, unsigned int drive ) { +static int dummy_san_hook ( unsigned int drive, struct uri **uris, + unsigned int count ) { struct san_device *sandev; int rc; /* Allocate SAN device */ - sandev = alloc_sandev ( uri, 0 ); + sandev = alloc_sandev ( uris, count, 0 ); if ( ! sandev ) { rc = -ENOMEM; goto err_alloc; diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c index 31a8a56b0..42fb06824 100644 --- a/src/core/null_sanboot.c +++ b/src/core/null_sanboot.c @@ -26,8 +26,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -static int null_san_hook ( struct uri *uri __unused, - unsigned int drive __unused ) { +static int null_san_hook ( unsigned int drive __unused, + struct uri **uris __unused, + unsigned int count __unused ) { return -EOPNOTSUPP; } diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 88d254ff8..90bf763bd 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -101,9 +101,13 @@ struct san_device * sandev_find ( unsigned int drive ) { static void sandev_free ( struct refcnt *refcnt ) { struct san_device *sandev = container_of ( refcnt, struct san_device, refcnt ); + struct san_path *sanpath; assert ( ! timer_running ( &sandev->timer ) ); - uri_put ( sandev->uri ); + assert ( ! sandev->active ); + assert ( list_empty ( &sandev->opened ) ); + list_for_each_entry ( sanpath, &sandev->closed, list ) + uri_put ( sanpath->uri ); free ( sandev ); } @@ -162,6 +166,143 @@ static void sandev_command_expired ( struct retry_timer *timer, sandev_command_close ( sandev, -ETIMEDOUT ); } +/** + * Open SAN path + * + * @v sanpath SAN path + * @ret rc Return status code + */ +static int sanpath_open ( struct san_path *sanpath ) { + struct san_device *sandev = sanpath->sandev; + int rc; + + /* Sanity check */ + list_check_contains_entry ( sanpath, &sandev->closed, list ); + + /* Open interface */ + if ( ( rc = xfer_open_uri ( &sanpath->block, sanpath->uri ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x.%d could not (re)open URI: " + "%s\n", sandev->drive, sanpath->index, strerror ( rc ) ); + return rc; + } + + /* Start process */ + process_add ( &sanpath->process ); + + /* Mark as opened */ + list_del ( &sanpath->list ); + list_add_tail ( &sanpath->list, &sandev->opened ); + + /* Record as in progress */ + sanpath->path_rc = -EINPROGRESS; + + return 0; +} + +/** + * Close SAN path + * + * @v sanpath SAN path + * @v rc Reason for close + */ +static void sanpath_close ( struct san_path *sanpath, int rc ) { + struct san_device *sandev = sanpath->sandev; + + /* Record status */ + sanpath->path_rc = rc; + + /* Mark as closed */ + list_del ( &sanpath->list ); + list_add_tail ( &sanpath->list, &sandev->closed ); + + /* Stop process */ + process_del ( &sanpath->process ); + + /* Restart interfaces, avoiding potential loops */ + if ( sanpath == sandev->active ) { + intfs_restart ( rc, &sandev->command, &sanpath->block, NULL ); + sandev->active = NULL; + sandev_command_close ( sandev, rc ); + } else { + intf_restart ( &sanpath->block, rc ); + } +} + +/** + * Handle closure of underlying block device interface + * + * @v sanpath SAN path + * @v rc Reason for close + */ +static void sanpath_block_close ( struct san_path *sanpath, int rc ) { + struct san_device *sandev = sanpath->sandev; + + /* Any closure is an error from our point of view */ + if ( rc == 0 ) + rc = -ENOTCONN; + DBGC ( sandev, "SAN %#02x.%d closed: %s\n", + sandev->drive, sanpath->index, strerror ( rc ) ); + + /* Close path */ + sanpath_close ( sanpath, rc ); +} + +/** + * Check flow control window + * + * @v sanpath SAN path + */ +static size_t sanpath_block_window ( struct san_path *sanpath __unused ) { + + /* We are never ready to receive data via this interface. + * This prevents objects that support both block and stream + * interfaces from attempting to send us stream data. + */ + return 0; +} + +/** + * SAN path process + * + * @v sanpath SAN path + */ +static void sanpath_step ( struct san_path *sanpath ) { + struct san_device *sandev = sanpath->sandev; + + /* Wait until path has become available */ + if ( ! xfer_window ( &sanpath->block ) ) + return; + + /* Record status */ + sanpath->path_rc = 0; + + /* Mark as active path or close as applicable */ + if ( ! sandev->active ) { + DBGC ( sandev, "SAN %#02x.%d is active\n", + sandev->drive, sanpath->index ); + sandev->active = sanpath; + } else { + DBGC ( sandev, "SAN %#02x.%d is available\n", + sandev->drive, sanpath->index ); + sanpath_close ( sanpath, 0 ); + } +} + +/** SAN path block interface operations */ +static struct interface_operation sanpath_block_op[] = { + INTF_OP ( intf_close, struct san_path *, sanpath_block_close ), + INTF_OP ( xfer_window, struct san_path *, sanpath_block_window ), + INTF_OP ( xfer_window_changed, struct san_path *, sanpath_step ), +}; + +/** SAN path block interface descriptor */ +static struct interface_descriptor sanpath_block_desc = + INTF_DESC ( struct san_path, block, sanpath_block_op ); + +/** SAN path process descriptor */ +static struct process_descriptor sanpath_process_desc = + PROC_DESC_ONCE ( struct san_path, process, sanpath_step ); + /** * Restart SAN device interface * @@ -169,15 +310,19 @@ static void sandev_command_expired ( struct retry_timer *timer, * @v rc Reason for restart */ static void sandev_restart ( struct san_device *sandev, int rc ) { + struct san_path *sanpath; - /* Restart block device interface */ - intfs_restart ( rc, &sandev->command, &sandev->block, NULL ); + /* Restart all block device interfaces */ + while ( ( sanpath = list_first_entry ( &sandev->opened, + struct san_path, list ) ) ) { + sanpath_close ( sanpath, rc ); + } + + /* Clear active path */ + sandev->active = NULL; /* Close any outstanding command */ sandev_command_close ( sandev, rc ); - - /* Record device error */ - sandev->block_rc = rc; } /** @@ -189,77 +334,53 @@ static void sandev_restart ( struct san_device *sandev, int rc ) { * This function will block until the device is available. */ int sandev_reopen ( struct san_device *sandev ) { + struct san_path *sanpath; int rc; - /* Close any outstanding command and restart interface */ + /* Close any outstanding command and restart interfaces */ sandev_restart ( sandev, -ECONNRESET ); + assert ( sandev->active == NULL ); + assert ( list_empty ( &sandev->opened ) ); - /* Mark device as being not yet open */ - sandev->block_rc = -EINPROGRESS; - - /* Open block device interface */ - if ( ( rc = xfer_open_uri ( &sandev->block, sandev->uri ) ) != 0 ) { - DBGC ( sandev, "SAN %#02x could not (re)open URI: %s\n", - sandev->drive, strerror ( rc ) ); - return rc; + /* Open all paths */ + while ( ( sanpath = list_first_entry ( &sandev->closed, + struct san_path, list ) ) ) { + if ( ( rc = sanpath_open ( sanpath ) ) != 0 ) + goto err_open; } - /* Wait for device to become available */ - while ( sandev->block_rc == -EINPROGRESS ) { + /* Wait for any device to become available, or for all devices + * to fail. + */ + while ( sandev->active == NULL ) { step(); - if ( xfer_window ( &sandev->block ) != 0 ) { - sandev->block_rc = 0; - return 0; + if ( list_empty ( &sandev->opened ) ) { + /* Get status of the first device to be + * closed. Do this on the basis that earlier + * errors (e.g. "invalid IQN") are probably + * more interesting than later errors + * (e.g. "TCP timeout"). + */ + rc = -ENODEV; + list_for_each_entry ( sanpath, &sandev->closed, list ) { + rc = sanpath->path_rc; + break; + } + DBGC ( sandev, "SAN %#02x never became available: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_none; } } - DBGC ( sandev, "SAN %#02x never became available: %s\n", - sandev->drive, strerror ( sandev->block_rc ) ); - return sandev->block_rc; -} - -/** - * Handle closure of underlying block device interface - * - * @v sandev SAN device - * @ret rc Reason for close - */ -static void sandev_block_close ( struct san_device *sandev, int rc ) { - - /* Any closure is an error from our point of view */ - if ( rc == 0 ) - rc = -ENOTCONN; - DBGC ( sandev, "SAN %#02x went away: %s\n", - sandev->drive, strerror ( rc ) ); - - /* Close any outstanding command and restart interface */ - sandev_restart ( sandev, rc ); -} - -/** - * Check SAN device flow control window - * - * @v sandev SAN device - */ -static size_t sandev_block_window ( struct san_device *sandev __unused ) { - - /* We are never ready to receive data via this interface. - * This prevents objects that support both block and stream - * interfaces from attempting to send us stream data. - */ + assert ( ! list_empty ( &sandev->opened ) ); return 0; + + err_none: + err_open: + sandev_restart ( sandev, rc ); + return rc; } -/** SAN device block interface operations */ -static struct interface_operation sandev_block_op[] = { - INTF_OP ( intf_close, struct san_device *, sandev_block_close ), - INTF_OP ( xfer_window, struct san_device *, sandev_block_window ), -}; - -/** SAN device block interface descriptor */ -static struct interface_descriptor sandev_block_desc = - INTF_DESC ( struct san_device, block, sandev_block_op ); - /** SAN device read/write command parameters */ struct san_command_rw_params { /** SAN device read/write operation */ @@ -289,15 +410,19 @@ union san_command_params { */ static int sandev_command_rw ( struct san_device *sandev, const union san_command_params *params ) { + struct san_path *sanpath = sandev->active; size_t len = ( params->rw.count * sandev->capacity.blksize ); int rc; + /* Sanity check */ + assert ( sanpath != NULL ); + /* Initiate read/write command */ - if ( ( rc = params->rw.block_rw ( &sandev->block, &sandev->command, + if ( ( rc = params->rw.block_rw ( &sanpath->block, &sandev->command, params->rw.lba, params->rw.count, params->rw.buffer, len ) ) != 0 ) { - DBGC ( sandev, "SAN %#02x could not initiate read/write: " - "%s\n", sandev->drive, strerror ( rc ) ); + DBGC ( sandev, "SAN %#02x.%d could not initiate read/write: " + "%s\n", sandev->drive, sanpath->index, strerror ( rc ) ); return rc; } @@ -314,13 +439,17 @@ static int sandev_command_rw ( struct san_device *sandev, static int sandev_command_read_capacity ( struct san_device *sandev, const union san_command_params *params __unused){ + struct san_path *sanpath = sandev->active; int rc; + /* Sanity check */ + assert ( sanpath != NULL ); + /* Initiate read capacity command */ - if ( ( rc = block_read_capacity ( &sandev->block, + if ( ( rc = block_read_capacity ( &sanpath->block, &sandev->command ) ) != 0 ) { - DBGC ( sandev, "SAN %#02x could not initiate read capacity: " - "%s\n", sandev->drive, strerror ( rc ) ); + DBGC ( sandev, "SAN %#02x.%d could not initiate read capacity: " + "%s\n", sandev->drive, sanpath->index, strerror ( rc ) ); return rc; } @@ -526,22 +655,41 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) { /** * Allocate SAN device * + * @v uris List of URIs + * @v count Number of URIs + * @v priv_size Size of private data * @ret sandev SAN device, or NULL */ -struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size ) { +struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, + size_t priv_size ) { struct san_device *sandev; + struct san_path *sanpath; + size_t size; + unsigned int i; /* Allocate and initialise structure */ - sandev = zalloc ( sizeof ( *sandev ) + priv_size ); + size = ( sizeof ( *sandev ) + ( count * sizeof ( sandev->path[0] ) ) ); + sandev = zalloc ( size + priv_size ); if ( ! sandev ) return NULL; ref_init ( &sandev->refcnt, sandev_free ); - sandev->uri = uri_get ( uri ); - intf_init ( &sandev->block, &sandev_block_desc, &sandev->refcnt ); - sandev->block_rc = -EINPROGRESS; intf_init ( &sandev->command, &sandev_command_desc, &sandev->refcnt ); timer_init ( &sandev->timer, sandev_command_expired, &sandev->refcnt ); - sandev->priv = ( ( ( void * ) sandev ) + sizeof ( *sandev ) ); + sandev->priv = ( ( ( void * ) sandev ) + size ); + INIT_LIST_HEAD ( &sandev->opened ); + INIT_LIST_HEAD ( &sandev->closed ); + for ( i = 0 ; i < count ; i++ ) { + sanpath = &sandev->path[i]; + sanpath->sandev = sandev; + sanpath->index = i; + sanpath->uri = uri_get ( uris[i] ); + list_add_tail ( &sanpath->list, &sandev->closed ); + intf_init ( &sanpath->block, &sanpath_block_desc, + &sandev->refcnt ); + process_init_stopped ( &sanpath->process, &sanpath_process_desc, + &sandev->refcnt ); + sanpath->path_rc = -EINPROGRESS; + } return sandev; } @@ -588,7 +736,7 @@ void unregister_sandev ( struct san_device *sandev ) { assert ( ! timer_running ( &sandev->timer ) ); /* Shut down interfaces */ - intfs_shutdown ( 0, &sandev->block, &sandev->command, NULL ); + sandev_restart ( sandev, 0 ); /* Remove from list of SAN devices */ list_del ( &sandev->list ); diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 24ec8bc4e..9965ec151 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -71,12 +71,12 @@ static union { /** "sanhook" command descriptor */ static struct command_descriptor sanhook_cmd = - COMMAND_DESC ( struct sanboot_options, opts.sanhook, 1, 1, + COMMAND_DESC ( struct sanboot_options, opts.sanhook, 1, MAX_ARGUMENTS, "" ); /** "sanboot" command descriptor */ static struct command_descriptor sanboot_cmd = - COMMAND_DESC ( struct sanboot_options, opts.sanboot, 0, 1, + COMMAND_DESC ( struct sanboot_options, opts.sanboot, 0, MAX_ARGUMENTS, "[]" ); /** "sanunhook" command descriptor */ @@ -96,9 +96,10 @@ static int sanboot_core_exec ( int argc, char **argv, struct command_descriptor *cmd, int default_flags, int no_root_path_flags ) { struct sanboot_options opts; - const char *root_path; - struct uri *uri; + struct uri *uris[argc]; + int count; int flags; + int i; int rc; /* Initialise options */ @@ -109,17 +110,14 @@ static int sanboot_core_exec ( int argc, char **argv, if ( ( rc = reparse_options ( argc, argv, cmd, &opts ) ) != 0 ) goto err_parse_options; - /* Parse root path, if present */ - if ( argc > optind ) { - root_path = argv[optind]; - uri = parse_uri ( root_path ); - if ( ! uri ) { + /* Parse root paths, if present */ + count = ( argc - optind ); + for ( i = 0 ; i < count ; i++ ) { + uris[i] = parse_uri ( argv[ optind + i ] ); + if ( ! uris[i] ) { rc = -ENOMEM; goto err_parse_uri; } - } else { - root_path = NULL; - uri = NULL; } /* Construct flags */ @@ -128,16 +126,18 @@ static int sanboot_core_exec ( int argc, char **argv, flags |= URIBOOT_NO_SAN_DESCRIBE; if ( opts.keep ) flags |= URIBOOT_NO_SAN_UNHOOK; - if ( ! root_path ) + if ( ! count ) flags |= no_root_path_flags; /* Boot from root path */ - if ( ( rc = uriboot ( NULL, uri, opts.drive, flags ) ) != 0 ) + if ( ( rc = uriboot ( NULL, uris, count, opts.drive, flags ) ) != 0 ) goto err_uriboot; err_uriboot: - uri_put ( uri ); + i = count; err_parse_uri: + for ( i-- ; i >= 0 ; i-- ) + uri_put ( uris[i] ); err_parse_options: return rc; } diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index c2e57f716..a8b0291de 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -16,9 +16,29 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include +/** A SAN path */ +struct san_path { + /** Containing SAN device */ + struct san_device *sandev; + /** Path index */ + unsigned int index; + /** SAN device URI */ + struct uri *uri; + /** List of open/closed paths */ + struct list_head list; + + /** Underlying block device interface */ + struct interface block; + /** Process */ + struct process process; + /** Path status */ + int path_rc; +}; + /** A SAN device */ struct san_device { /** Reference count */ @@ -26,16 +46,9 @@ struct san_device { /** List of SAN devices */ struct list_head list; - /** SAN device URI */ - struct uri *uri; /** Drive number */ unsigned int drive; - /** Underlying block device interface */ - struct interface block; - /** Current device status */ - int block_rc; - /** Command interface */ struct interface command; /** Command timeout timer */ @@ -57,6 +70,15 @@ struct san_device { /** Driver private data */ void *priv; + + /** Current active path */ + struct san_path *active; + /** List of opened SAN paths */ + struct list_head opened; + /** List of closed SAN paths */ + struct list_head closed; + /** SAN paths */ + struct san_path path[0]; }; /** @@ -99,11 +121,12 @@ struct san_device { /** * Hook SAN device * - * @v uri URI * @v drive Drive number + * @v uris List of URIs + * @v count Number of URIs * @ret drive Drive number, or negative error */ -int san_hook ( struct uri *uri, unsigned int drive ); +int san_hook ( unsigned int drive, struct uri **uris, unsigned int count ); /** * Unhook SAN device @@ -191,7 +214,7 @@ static inline uint64_t sandev_capacity ( struct san_device *sandev ) { * @ret needs_reopen SAN device needs to be reopened */ static inline int sandev_needs_reopen ( struct san_device *sandev ) { - return ( sandev->block_rc != 0 ); + return ( sandev->active == NULL ); } extern struct san_device * sandev_find ( unsigned int drive ); @@ -203,7 +226,8 @@ extern int sandev_rw ( struct san_device *sandev, uint64_t lba, struct interface *data, uint64_t lba, unsigned int count, userptr_t buffer, size_t len ) ); -extern struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size ); +extern struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, + size_t priv_size ); extern int register_sandev ( struct san_device *sandev ); extern void unregister_sandev ( struct san_device *sandev ); extern unsigned int san_default_drive ( void ); diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index 4db226b9c..c62d06c6f 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -30,7 +30,8 @@ extern void set_autoboot_busloc ( unsigned int bus_type, unsigned int location ); extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len ); -extern int uriboot ( struct uri *filename, struct uri *root_path, int drive, +extern int uriboot ( struct uri *filename, struct uri **root_paths, + unsigned int root_path_count, int drive, unsigned int flags ); extern struct uri * fetch_next_server_and_filename ( struct settings *settings ); diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 10504f6a8..9679fc0da 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -251,11 +251,13 @@ static void efi_block_connect ( struct san_device *sandev ) { /** * Hook EFI block device * - * @v uri URI * @v drive Drive number + * @v uris List of URIs + * @v count Number of URIs * @ret drive Drive number, or negative error */ -static int efi_block_hook ( struct uri *uri, unsigned int drive ) { +static int efi_block_hook ( unsigned int drive, struct uri **uris, + unsigned int count ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_DEVICE_PATH_PROTOCOL *end; struct efi_block_vendor_path *vendor; @@ -270,6 +272,13 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { EFI_STATUS efirc; int rc; + /* Sanity check */ + if ( ! count ) { + DBG ( "EFIBLK has no URIs\n" ); + rc = -ENOTTY; + goto err_no_uris; + } + /* Find an appropriate parent device handle */ snpdev = last_opened_snpdev(); if ( ! snpdev ) { @@ -280,14 +289,14 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { /* Calculate length of private data */ prefix_len = efi_devpath_len ( snpdev->path ); - uri_len = format_uri ( uri, NULL, 0 ); + uri_len = format_uri ( uris[0], NULL, 0 ); vendor_len = ( sizeof ( *vendor ) + ( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) ); len = ( sizeof ( *block ) + uri_len + 1 /* NUL */ + prefix_len + vendor_len + sizeof ( *end ) ); /* Allocate and initialise structure */ - sandev = alloc_sandev ( uri, len ); + sandev = alloc_sandev ( uris, count, len ); if ( ! sandev ) { rc = -ENOMEM; goto err_alloc; @@ -315,7 +324,7 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { vendor->vendor.Header.Length[1] = ( vendor_len >> 8 ); memcpy ( &vendor->vendor.Guid, &ipxe_block_device_path_guid, sizeof ( vendor->vendor.Guid ) ); - format_uri ( uri, uri_buf, ( uri_len + 1 /* NUL */ ) ); + format_uri ( uris[0], uri_buf, ( uri_len + 1 /* NUL */ ) ); efi_snprintf ( vendor->uri, ( uri_len + 1 /* NUL */ ), "%s", uri_buf ); end = ( ( ( void * ) vendor ) + vendor_len ); end->Type = END_DEVICE_PATH_TYPE; @@ -364,6 +373,7 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { sandev_put ( sandev ); err_alloc: err_no_snpdev: + err_no_uris: return rc; } @@ -413,6 +423,7 @@ static int efi_block_describe ( unsigned int drive ) { } xbftab; static UINTN key; struct san_device *sandev; + struct san_path *sanpath; size_t len; EFI_STATUS efirc; int rc; @@ -446,6 +457,8 @@ static int efi_block_describe ( unsigned int drive ) { if ( sandev_needs_reopen ( sandev ) && ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) return rc; + sanpath = sandev->active; + assert ( sanpath != NULL ); /* Clear table */ memset ( &xbftab, 0, sizeof ( xbftab ) ); @@ -457,7 +470,7 @@ static int efi_block_describe ( unsigned int drive ) { sizeof ( xbftab.acpi.oem_table_id ) ); /* Fill in remaining parameters */ - if ( ( rc = acpi_describe ( &sandev->block, &xbftab.acpi, + if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi, sizeof ( xbftab ) ) ) != 0 ) { DBGC ( sandev, "EFIBLK %#02x could not create ACPI " "description: %s\n", sandev->drive, strerror ( rc ) ); diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 57bf96ef2..a0c793516 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -109,7 +109,8 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, * Boot from filename and root-path URIs * * @v filename Filename - * @v root_path Root path + * @v root_paths Root path(s) + * @v root_path_count Number of root paths * @v drive SAN drive (if applicable) * @v flags Boot action flags * @ret rc Return status code @@ -120,14 +121,14 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, * provide backwards compatibility for the "keep-san" and * "skip-san-boot" options. */ -int uriboot ( struct uri *filename, struct uri *root_path, int drive, - unsigned int flags ) { +int uriboot ( struct uri *filename, struct uri **root_paths, + unsigned int root_path_count, int drive, unsigned int flags ) { struct image *image; int rc; /* Hook SAN device, if applicable */ - if ( root_path ) { - drive = san_hook ( root_path, drive ); + if ( root_path_count ) { + drive = san_hook ( drive, root_paths, root_path_count ); if ( drive < 0 ) { rc = drive; printf ( "Could not open SAN device: %s\n", @@ -396,7 +397,8 @@ int netboot ( struct net_device *netdev ) { } /* Boot using next server, filename and root path */ - if ( ( rc = uriboot ( filename, root_path, san_default_drive(), + if ( ( rc = uriboot ( filename, &root_path, ( root_path ? 1 : 0 ), + san_default_drive(), ( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 ) goto err_uriboot; diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c index 2d05d3f51..391d698a9 100644 --- a/src/usr/pxemenu.c +++ b/src/usr/pxemenu.c @@ -378,7 +378,7 @@ int pxe_menu_boot ( struct net_device *netdev ) { return -ENOMEM; /* Attempt boot */ - rc = uriboot ( uri, NULL, 0, URIBOOT_NO_SAN ); + rc = uriboot ( uri, NULL, 0, 0, URIBOOT_NO_SAN ); uri_put ( uri ); return rc; } From ebceb8ad8a5c3ad58ecbe513174c3620592d5976 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 26 Mar 2017 21:03:50 +0300 Subject: [PATCH 428/591] [int13] Improve geometry guessing for unaligned partitions Some partition tables have partitions that are not aligned to a cylinder boundary, which confuses the current geometry guessing logic. Enhance the existing logic to ensure that we never reduce our guesses for the number of heads or sectors per track, and add extra logic to calculate the exact number of sectors per track if we find a partition that starts within cylinder zero. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 56 +++++++++++++++++++++------ 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index c322440e7..0bc123d78 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -219,14 +219,13 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch, struct master_boot_record *mbr = scratch; struct partition_table_entry *partition; unsigned int i; + unsigned int start_cylinder; + unsigned int start_head; + unsigned int start_sector; unsigned int end_head; unsigned int end_sector; int rc; - /* Default guess is xx/255/63 */ - *heads = 255; - *sectors = 63; - /* Read partition table */ if ( ( rc = sandev_rw ( sandev, 0, 1, virt_to_user ( mbr ), block_read ) ) != 0 ) { @@ -244,19 +243,54 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch, * heads and sectors_per_track if we find any used * partitions. */ + *heads = 0; + *sectors = 0; for ( i = 0 ; i < 4 ; i++ ) { + + /* Skip empty partitions */ partition = &mbr->partitions[i]; + if ( ! partition->type ) + continue; + + /* If partition starts on cylinder 0 then we can + * unambiguously determine the number of sectors. + */ + start_cylinder = PART_CYLINDER ( partition->chs_start ); + start_head = PART_HEAD ( partition->chs_start ); + start_sector = PART_SECTOR ( partition->chs_start ); + if ( ( start_cylinder == 0 ) && ( start_head != 0 ) ) { + *sectors = ( ( partition->start + 1 - start_sector ) / + start_head ); + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "xx/xx/%d based on partition %d\n", + sandev->drive, *sectors, ( i + 1 ) ); + } + + /* If partition ends on a higher head or sector number + * than our current guess, then increase the guess. + */ end_head = PART_HEAD ( partition->chs_end ); end_sector = PART_SECTOR ( partition->chs_end ); - if ( ! ( partition->type && end_head && end_sector ) ) - continue; - *heads = ( end_head + 1 ); - *sectors = end_sector; - DBGC ( sandev, "INT13 drive %02x guessing C/H/S xx/%d/%d based " - "on partition %d\n", - sandev->drive, *heads, *sectors, ( i + 1 ) ); + if ( ( end_head + 1 ) > *heads ) { + *heads = ( end_head + 1 ); + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "xx/%d/xx based on partition %d\n", + sandev->drive, *heads, ( i + 1 ) ); + } + if ( end_sector > *sectors ) { + *sectors = end_sector; + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "xx/xx/%d based on partition %d\n", + sandev->drive, *sectors, ( i + 1 ) ); + } } + /* Default guess is xx/255/63 */ + if ( ! *heads ) + *heads = 255; + if ( ! *sectors ) + *sectors = 63; + return 0; } From c73af29fe206dda55a72119b6b29c5628aa09ed1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 10:50:59 +0300 Subject: [PATCH 429/591] [int13con] Avoid overwriting random portions of SAN boot disks The INT13 console type (CONSOLE_INT13) autodetects at initialisation time a magic partition to be used for logging iPXE console output. If the INT13 drive number mapping is subsequently changed (e.g. because iPXE was used to perform a SAN boot), then the console logging output will be written to the incorrect disk. Fix by recording the INT13 vector at initialisation time, and using this original vector to emulate INT13 calls for all subsequent accesses. This should be robust against drive remapping performed either by ourselves or by another bootloader (e.g. a chainloaded undionly.kpxe which then performs a SAN boot). Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13con.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13con.c b/src/arch/x86/interface/pcbios/int13con.c index 2414c6909..8106cd153 100644 --- a/src/arch/x86/interface/pcbios/int13con.c +++ b/src/arch/x86/interface/pcbios/int13con.c @@ -62,6 +62,10 @@ struct int13con_header { /** Log partition magic signature */ #define INT13CON_MAGIC "iPXE LOG\n\n" +/** Original INT13 vector */ +static struct segoff __bss16 ( int13con_vector ); +#define int13con_vector __use_data16 ( int13con_vector ) + /** Sector buffer */ static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] ); #define int13con_buffer __use_data16 ( int13con_buffer ) @@ -101,8 +105,13 @@ static int int13con_rw ( unsigned int op, uint64_t lba ) { int13con_address.buffer.offset = __from_data16 ( int13con_buffer ); int13con_address.lba = lba; - /* Issue INT13 */ - __asm__ ( REAL_CODE ( "int $0x13\n\t" ) + /* Emulate INT13 via original vector. We do this since iPXE + * (or another subsequent bootloader) may hook INT13 and remap + * drive numbers. + */ + __asm__ ( REAL_CODE ( "pushfw\n\t" + "cli\n\t" + "lcall *int13con_vector\n\t" ) : "=a" ( error ) : "0" ( op << 8 ), "d" ( INT13CON_DRIVE ), "S" ( __from_data16 ( &int13con_address ) ) ); @@ -261,6 +270,12 @@ static void int13con_init ( void ) { return; } + /* Store original INT13 vector */ + copy_from_real ( &int13con_vector, 0, ( 0x13 * 4 ), + sizeof ( int13con_vector ) ); + DBG ( "INT13CON using original INT13 vector %04x:%04x\n", + int13con_vector.segment, int13con_vector.offset ); + /* Locate log partition */ if ( ( rc = int13con_find() ) != 0) return; From 6bd0060f265907dc4cd687926777c19fc3c7a683 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 15:31:42 +0300 Subject: [PATCH 430/591] [time] Add sleep_fixed() function to sleep without checking for Ctrl-C Signed-off-by: Michael Brown --- src/core/timer.c | 39 ++++++++++++++++++++++++++++++++++++--- src/include/ipxe/timer.h | 1 + 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/core/timer.c b/src/core/timer.c index 791cdcdbb..24745cef7 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -90,19 +90,21 @@ void mdelay ( unsigned long msecs ) { } /** - * Sleep (interruptibly) for a fixed number of seconds + * Sleep (possibly interruptibly) for a fixed number of seconds * * @v secs Number of seconds for which to delay + * @v interrupted Interrupt checking method, or NULL * @ret secs Number of seconds remaining, if interrupted */ -unsigned int sleep ( unsigned int secs ) { +static unsigned int sleep_interruptible ( unsigned int secs, + int ( * interrupted ) ( void ) ) { unsigned long start = currticks(); unsigned long now; for ( ; secs ; secs-- ) { while ( ( ( now = currticks() ) - start ) < TICKS_PER_SEC ) { step(); - if ( iskey() && ( getchar() == CTRL_C ) ) + if ( interrupted && interrupted() ) return secs; cpu_nap(); } @@ -112,6 +114,37 @@ unsigned int sleep ( unsigned int secs ) { return 0; } +/** + * Check if sleep has been interrupted by keypress + * + * @ret interrupted Sleep has been interrupted + */ +static int keypress_interrupted ( void ) { + + return ( iskey() && ( getchar() == CTRL_C ) ); +} + +/** + * Sleep (interruptibly) for a fixed number of seconds + * + * @v secs Number of seconds for which to delay + * @ret secs Number of seconds remaining, if interrupted + */ +unsigned int sleep ( unsigned int secs ) { + + return sleep_interruptible ( secs, keypress_interrupted ); +} + +/** + * Sleep (uninterruptibly) for a fixed number of seconds + * + * @v secs Number of seconds for which to delay + */ +void sleep_fixed ( unsigned int secs ) { + + sleep_interruptible ( secs, NULL ); +} + /** * Find a working timer * diff --git a/src/include/ipxe/timer.h b/src/include/ipxe/timer.h index e6b95172e..a6dffaf1c 100644 --- a/src/include/ipxe/timer.h +++ b/src/include/ipxe/timer.h @@ -75,5 +75,6 @@ extern void udelay ( unsigned long usecs ); extern void mdelay ( unsigned long msecs ); extern unsigned long currticks ( void ); extern unsigned int sleep ( unsigned int seconds ); +extern void sleep_fixed ( unsigned int secs ); #endif /* _IPXE_TIMER_H */ From 6b385c9da388e63a741bb1bb4ee59419c0a141c5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 13:06:16 +0300 Subject: [PATCH 431/591] [block] Allow SAN retry count to be reconfigured Allow the SAN retry count to be configured via the ${san-retry} setting, defaulting to the current value of 10 retries if not specified. Note that setting a retry count of zero is inadvisable, since iSCSI targets in particular will often report spurious errors such as "power on occurred" for the first few commands. Signed-off-by: Michael Brown --- src/core/sanboot.c | 69 ++++++++++++++++++++++++++++---------- src/include/ipxe/dhcp.h | 7 ++++ src/include/ipxe/sanboot.h | 2 ++ 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 90bf763bd..c7279ad36 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -65,18 +65,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SAN_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) /** - * Number of times to retry commands + * Default number of times to retry commands * * We may need to retry commands. For example, the underlying * connection may be closed by the SAN target due to an inactivity * timeout, or the SAN target may return pointless "error" messages * such as "SCSI power-on occurred". */ -#define SAN_COMMAND_MAX_RETRIES 10 +#define SAN_DEFAULT_RETRIES 10 /** List of SAN devices */ LIST_HEAD ( san_devices ); +/** Number of times to retry commands */ +static unsigned long san_retries = SAN_DEFAULT_RETRIES; + /** * Find SAN device by drive number * @@ -101,13 +104,13 @@ struct san_device * sandev_find ( unsigned int drive ) { static void sandev_free ( struct refcnt *refcnt ) { struct san_device *sandev = container_of ( refcnt, struct san_device, refcnt ); - struct san_path *sanpath; + unsigned int i; assert ( ! timer_running ( &sandev->timer ) ); assert ( ! sandev->active ); assert ( list_empty ( &sandev->opened ) ); - list_for_each_entry ( sanpath, &sandev->closed, list ) - uri_put ( sanpath->uri ); + for ( i = 0 ; i < sandev->paths ; i++ ) + uri_put ( sandev->path[i].uri ); free ( sandev ); } @@ -469,14 +472,14 @@ sandev_command ( struct san_device *sandev, int ( * command ) ( struct san_device *sandev, const union san_command_params *params ), const union san_command_params *params ) { - unsigned int retries; + unsigned int retries = 0; int rc; /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); /* (Re)try command */ - for ( retries = 0 ; retries < SAN_COMMAND_MAX_RETRIES ; retries++ ) { + do { /* Reopen block device if applicable */ if ( sandev_needs_reopen ( sandev ) && @@ -484,23 +487,24 @@ sandev_command ( struct san_device *sandev, continue; } + /* Initiate command */ + if ( ( rc = command ( sandev, params ) ) != 0 ) + continue; + /* Start expiry timer */ start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); - /* Initiate command */ - if ( ( rc = command ( sandev, params ) ) != 0 ) { - stop_timer ( &sandev->timer ); - continue; - } - /* Wait for command to complete */ while ( timer_running ( &sandev->timer ) ) step(); - /* Exit on success */ - if ( ( rc = sandev->command_rc ) == 0 ) - return 0; - } + /* Check command status */ + if ( ( rc = sandev->command_rc ) != 0 ) + continue; + + return 0; + + } while ( ++retries <= san_retries ); /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); @@ -676,6 +680,7 @@ struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, intf_init ( &sandev->command, &sandev_command_desc, &sandev->refcnt ); timer_init ( &sandev->timer, sandev_command_expired, &sandev->refcnt ); sandev->priv = ( ( ( void * ) sandev ) + size ); + sandev->paths = count; INIT_LIST_HEAD ( &sandev->opened ); INIT_LIST_HEAD ( &sandev->closed ); for ( i = 0 ; i < count ; i++ ) { @@ -767,3 +772,33 @@ unsigned int san_default_drive ( void ) { /* Otherwise, default to booting from first hard disk */ return SAN_DEFAULT_DRIVE; } + +/** The "san-retries" setting */ +const struct setting san_retries_setting __setting ( SETTING_SANBOOT_EXTRA, + san-retries ) = { + .name = "san-retries", + .description = "SAN retry count", + .tag = DHCP_EB_SAN_RETRY, + .type = &setting_type_int8, +}; + +/** + * Apply SAN boot settings + * + * @ret rc Return status code + */ +static int sandev_apply ( void ) { + + /* Apply "san-retries" setting */ + if ( fetch_uint_setting ( NULL, &san_retries_setting, + &san_retries ) < 0 ) { + san_retries = SAN_DEFAULT_RETRIES; + } + + return 0; +} + +/** Settings applicator */ +struct settings_applicator sandev_applicator __settings_applicator = { + .apply = sandev_apply, +}; diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index 23f16aaa7..0f662d633 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -433,6 +433,13 @@ struct dhcp_netdev_desc { /** Use cached network settings (obsolete; do not reuse this value) */ #define DHCP_EB_USE_CACHED DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb2 ) +/** SAN retry count + * + * This is the maximum number of times that SAN operations will be + * retried. + */ +#define DHCP_EB_SAN_RETRY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbb ) + /** SAN drive number * * This is the drive number for a SAN-hooked drive. For BIOS, 0x80 is diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index a8b0291de..64b665a52 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -71,6 +71,8 @@ struct san_device { /** Driver private data */ void *priv; + /** Number of paths */ + unsigned int paths; /** Current active path */ struct san_path *active; /** List of opened SAN paths */ From 164378fee60d02c0287bbaa9d390510fc9eab1fd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 15:32:29 +0300 Subject: [PATCH 432/591] [block] Add a small delay between attempts to reopen SAN targets When all SAN targets are completely unreachable, there will be a natural delay between reopening attempts due to the network connection timeout on the unreachable targets. However, some SAN targets may accept connections instantly and report a temporary unavailability by e.g. failing the TEST UNIT READY command. If all targets are behaving this way then there will be no natural delay, and we will attempt to saturate the network with connection attempts. Fix by introducing a small delay between attempts. Signed-off-by: Michael Brown --- src/core/sanboot.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index c7279ad36..efab3d02f 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -74,6 +74,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define SAN_DEFAULT_RETRIES 10 +/** + * Delay between reopening attempts + * + * Some SAN targets will always accept connections instantly and + * report a temporary unavailability by e.g. failing the TEST UNIT + * READY command. Avoid bombarding such targets by introducing a + * small delay between attempts. + */ +#define SAN_REOPEN_DELAY_SECS 5 + /** List of SAN devices */ LIST_HEAD ( san_devices ); @@ -484,6 +494,10 @@ sandev_command ( struct san_device *sandev, /* Reopen block device if applicable */ if ( sandev_needs_reopen ( sandev ) && ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) { + + /* Delay reopening attempts */ + sleep_fixed ( SAN_REOPEN_DELAY_SECS ); + continue; } From ee35b03583080f44ad63aebaadf1e9b4976f6605 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 13:18:14 +0300 Subject: [PATCH 433/591] [block] Retry reopening indefinitely for multipath devices For multipath SAN devices, verify that the device is capable of being opened (i.e. that all URIs are parseable and that at least one path is alive) and thereafter retry indefinitely to reopen the device as needed. Signed-off-by: Michael Brown --- src/core/sanboot.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index efab3d02f..8eb54b7cc 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -498,12 +498,18 @@ sandev_command ( struct san_device *sandev, /* Delay reopening attempts */ sleep_fixed ( SAN_REOPEN_DELAY_SECS ); + /* Retry opening indefinitely for multipath devices */ + if ( sandev->paths <= 1 ) + retries++; + continue; } /* Initiate command */ - if ( ( rc = command ( sandev, params ) ) != 0 ) + if ( ( rc = command ( sandev, params ) ) != 0 ) { + retries++; continue; + } /* Start expiry timer */ start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); @@ -513,12 +519,14 @@ sandev_command ( struct san_device *sandev, step(); /* Check command status */ - if ( ( rc = sandev->command_rc ) != 0 ) + if ( ( rc = sandev->command_rc ) != 0 ) { + retries++; continue; + } return 0; - } while ( ++retries <= san_retries ); + } while ( retries <= san_retries ); /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); @@ -728,6 +736,13 @@ int register_sandev ( struct san_device *sandev ) { return -EADDRINUSE; } + /* Check that device is capable of being opened (i.e. that all + * URIs are well-formed and that at least one path is + * working). + */ + if ( ( rc = sandev_reopen ( sandev ) ) != 0 ) + return rc; + /* Read device capacity */ if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity, NULL ) ) != 0 ) From 539088a27b0c60ef4bc413a7042f8b9f3ad60e97 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 16:56:36 +0300 Subject: [PATCH 434/591] [block] Gracefully close SAN device if registration fails Signed-off-by: Michael Brown --- src/core/sanboot.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 8eb54b7cc..7dbf03db2 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -733,7 +733,8 @@ int register_sandev ( struct san_device *sandev ) { /* Check that drive number is not in use */ if ( sandev_find ( sandev->drive ) != NULL ) { DBGC ( sandev, "SAN %#02x is already in use\n", sandev->drive ); - return -EADDRINUSE; + rc = -EADDRINUSE; + goto err_in_use; } /* Check that device is capable of being opened (i.e. that all @@ -741,22 +742,30 @@ int register_sandev ( struct san_device *sandev ) { * working). */ if ( ( rc = sandev_reopen ( sandev ) ) != 0 ) - return rc; + goto err_reopen; /* Read device capacity */ if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity, NULL ) ) != 0 ) - return rc; + goto err_capacity; /* Configure as a CD-ROM, if applicable */ if ( ( rc = sandev_parse_iso9660 ( sandev ) ) != 0 ) - return rc; + goto err_iso9660; /* Add to list of SAN devices */ list_add_tail ( &sandev->list, &san_devices ); DBGC ( sandev, "SAN %#02x registered\n", sandev->drive ); return 0; + + list_del ( &sandev->list ); + err_iso9660: + err_capacity: + err_reopen: + sandev_restart ( sandev, rc ); + err_in_use: + return rc; } /** @@ -769,11 +778,12 @@ void unregister_sandev ( struct san_device *sandev ) { /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); + /* Remove from list of SAN devices */ + list_del ( &sandev->list ); + /* Shut down interfaces */ sandev_restart ( sandev, 0 ); - /* Remove from list of SAN devices */ - list_del ( &sandev->list ); DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive ); } From fa879f9f52be070949377ddb85acb1d55e88af68 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Mar 2017 15:22:42 +0300 Subject: [PATCH 435/591] [linux] Use dummy SAN device Allow for easier testing of SAN code by using the dummy SAN device by default. Signed-off-by: Michael Brown --- src/config/defaults/linux.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index bc5ba7851..75fd617f9 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -15,7 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define UMALLOC_LINUX #define NAP_LINUX #define SMBIOS_LINUX -#define SANBOOT_NULL +#define SANBOOT_DUMMY #define ENTROPY_LINUX #define TIME_LINUX #define REBOOT_NULL @@ -25,4 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define IMAGE_SCRIPT +#define SANBOOT_PROTO_ISCSI +#define SANBOOT_PROTO_AOE +#define SANBOOT_PROTO_IB_SRP +#define SANBOOT_PROTO_FCP +#define SANBOOT_PROTO_HTTP + #endif /* CONFIG_DEFAULTS_LINUX_H */ From 414b4fc9c531ceb15607f7c6edd8a959e0bd3065 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Mar 2017 18:58:47 +0300 Subject: [PATCH 436/591] [block] Ignore redundant xfer_window_changed() messages For some block device protocols, the active path may continue to receive xfer_window_changed() notifications during normal use. These currently result in the active path being erroneously closed. Fix by ignoring any xfer_window_changed() messages if this path is already the active path. Signed-off-by: Michael Brown --- src/core/sanboot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 7dbf03db2..03beae799 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -282,6 +282,10 @@ static size_t sanpath_block_window ( struct san_path *sanpath __unused ) { static void sanpath_step ( struct san_path *sanpath ) { struct san_device *sandev = sanpath->sandev; + /* Ignore if we are already the active device */ + if ( sanpath == sandev->active ) + return; + /* Wait until path has become available */ if ( ! xfer_window ( &sanpath->block ) ) return; From 7cfdd769aac76d605aa31146c69ba518b194bea7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Mar 2017 18:20:34 +0300 Subject: [PATCH 437/591] [block] Describe all SAN devices via ACPI tables Describe all SAN devices via ACPI tables such as the iBFT. For tables that can describe only a single device (i.e. the aBFT and sBFT), one table is installed per device. For multi-device tables (i.e. the iBFT), all devices are described in a single table. An underlying SAN device connection may be closed at the time that we need to construct an ACPI table. We therefore introduce the concept of an "ACPI descriptor" which enables the SAN boot code to maintain an opaque pointer to the underlying object, and an "ACPI model" which can build tables from a list of such descriptors. This separates the lifecycles of ACPI descriptions from the lifecycles of the block device interfaces, and allows for construction of the ACPI tables even if the block device interface has been closed. For a multipath SAN device, iPXE will wait until sufficient information is available to describe all devices but will not wait for all paths to connect successfully. For example: with a multipath iSCSI boot iPXE will wait until at least one path has become available and name resolution has completed on all other paths. We do this since the iBFT has to include IP addresses rather than DNS names. We will commence booting without waiting for the inactive paths to either become available or close; this avoids unnecessary boot delays. Note that the Linux kernel will refuse to accept an iBFT with more than two NIC or target structures. We therefore describe only the NICs that are actually required in order to reach the described targets. Any iBFT with at most two targets is therefore guaranteed to describe at most two NICs. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 115 ++++---- src/core/acpi.c | 85 ++++-- src/core/dummy_sanboot.c | 25 +- src/core/null_sanboot.c | 5 +- src/core/sanboot.c | 101 ++++++- src/drivers/block/ibft.c | 400 +++++++++++++++++++------- src/drivers/block/srp.c | 68 ----- src/include/ipxe/acpi.h | 93 +++++- src/include/ipxe/aoe.h | 2 +- src/include/ipxe/ibft.h | 36 ++- src/include/ipxe/iscsi.h | 7 + src/include/ipxe/sanboot.h | 24 +- src/include/ipxe/srp.h | 2 +- src/interface/efi/efi_block.c | 148 +++++----- src/net/aoe.c | 90 ++++-- src/net/fcp.c | 20 -- src/net/infiniband/ib_srp.c | 141 ++++++--- src/net/tcp/httpblock.c | 18 -- src/net/tcp/httpcore.c | 17 -- src/net/tcp/iscsi.c | 14 +- src/usr/autoboot.c | 10 +- 21 files changed, 930 insertions(+), 491 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 0bc123d78..b793d730c 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -1239,13 +1239,14 @@ static void int13_unhook_vector ( void ) { * @v drive Drive number * @v uris List of URIs * @v count Number of URIs + * @v flags Flags * @ret drive Drive number, or negative error * * Registers the drive with the INT 13 emulation subsystem, and hooks * the INT 13 interrupt vector (if not already hooked). */ static int int13_hook ( unsigned int drive, struct uri **uris, - unsigned int count ) { + unsigned int count, unsigned int flags ) { struct san_device *sandev; struct int13_data *int13; unsigned int natural_drive; @@ -1267,14 +1268,13 @@ static int int13_hook ( unsigned int drive, struct uri **uris, rc = -ENOMEM; goto err_alloc; } - sandev->drive = drive; int13 = sandev->priv; int13->natural_drive = natural_drive; /* Register SAN device */ - if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x could not register: %s\n", - sandev->drive, strerror ( rc ) ); + drive, strerror ( rc ) ); goto err_register; } @@ -1544,70 +1544,83 @@ static int int13_boot ( unsigned int drive ) { return -ECANCELED; /* -EIMPOSSIBLE */ } -/** A boot firmware table generated by iPXE */ -union xbft_table { - /** ACPI header */ - struct acpi_description_header acpi; - /** Padding */ - char pad[768]; -}; +/** Maximum size of boot firmware table(s) */ +#define XBFTAB_SIZE 768 -/** The boot firmware table generated by iPXE */ -static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) )); +/** Alignment of boot firmware table entries */ +#define XBFTAB_ALIGN 16 + +/** The boot firmware table(s) generated by iPXE */ +static uint8_t __bss16_array ( xbftab, [XBFTAB_SIZE] ) + __attribute__ (( aligned ( XBFTAB_ALIGN ) )); #define xbftab __use_data16 ( xbftab ) +/** Total used length of boot firmware tables */ +static size_t xbftab_used; + /** - * Describe SAN device for SAN-booted operating system + * Install ACPI table * - * @v drive Drive number + * @v acpi ACPI description header * @ret rc Return status code */ -static int int13_describe ( unsigned int drive ) { - struct san_device *sandev; - struct san_path *sanpath; +static int int13_install ( struct acpi_header *acpi ) { struct segoff xbft_address; - int rc; + struct acpi_header *installed; + size_t len; - /* Find drive */ - sandev = sandev_find ( drive ); - if ( ! sandev ) { - DBG ( "INT13 cannot find drive %02x\n", drive ); - return -ENODEV; + /* Check length */ + len = acpi->length; + if ( len > ( sizeof ( xbftab ) - xbftab_used ) ) { + DBGC ( acpi, "INT13 out of space for %s table\n", + acpi_name ( acpi->signature ) ); + return -ENOSPC; } - /* Reopen block device if necessary */ - if ( sandev_needs_reopen ( sandev ) && - ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) - return rc; - sanpath = sandev->active; - assert ( sanpath != NULL ); - - /* Clear table */ - memset ( &xbftab, 0, sizeof ( xbftab ) ); + /* Install table */ + installed = ( ( ( void * ) xbftab ) + xbftab_used ); + memcpy ( installed, acpi, len ); + xbft_address.segment = rm_ds; + xbft_address.offset = __from_data16 ( installed ); /* Fill in common parameters */ - strncpy ( xbftab.acpi.oem_id, "FENSYS", - sizeof ( xbftab.acpi.oem_id ) ); - strncpy ( xbftab.acpi.oem_table_id, "iPXE", - sizeof ( xbftab.acpi.oem_table_id ) ); + strncpy ( installed->oem_id, "FENSYS", + sizeof ( installed->oem_id ) ); + strncpy ( installed->oem_table_id, "iPXE", + sizeof ( installed->oem_table_id ) ); - /* Fill in remaining parameters */ - if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi, - sizeof ( xbftab ) ) ) != 0 ) { - DBGC ( sandev, "INT13 drive %02x could not create ACPI " - "description: %s\n", sandev->drive, strerror ( rc ) ); + /* Fix checksum */ + acpi_fix_checksum ( installed ); + + /* Update used length */ + xbftab_used = ( ( xbftab_used + len + XBFTAB_ALIGN - 1 ) & + ~( XBFTAB_ALIGN - 1 ) ); + + DBGC ( acpi, "INT13 installed %s:\n", + acpi_name ( installed->signature ) ); + DBGC_HDA ( acpi, xbft_address, installed, len ); + return 0; +} + +/** + * Describe SAN devices for SAN-booted operating system + * + * @ret rc Return status code + */ +static int int13_describe ( void ) { + int rc; + + /* Clear tables */ + memset ( &xbftab, 0, sizeof ( xbftab ) ); + xbftab_used = 0; + + /* Install ACPI tables */ + if ( ( rc = acpi_install ( int13_install ) ) != 0 ) { + DBG ( "INT13 could not install ACPI tables: %s\n", + strerror ( rc ) ); return rc; } - /* Fix up ACPI checksum */ - acpi_fix_checksum ( &xbftab.acpi ); - xbft_address.segment = rm_ds; - xbft_address.offset = __from_data16 ( &xbftab ); - DBGC ( sandev, "INT13 drive %02x described using boot firmware " - "table:\n", sandev->drive ); - DBGC_HDA ( sandev, xbft_address, &xbftab, - le32_to_cpu ( xbftab.acpi.length ) ); - return 0; } diff --git a/src/core/acpi.c b/src/core/acpi.c index 955637e00..8ebe4b198 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -42,28 +42,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ -/** - * Transcribe ACPI table signature (for debugging) - * - * @v signature ACPI table signature - * @ret name ACPI table signature name - */ -static const char * acpi_name ( uint32_t signature ) { - static union { - uint32_t signature; - char name[5]; - } u; - - u.signature = cpu_to_le32 ( signature ); - return u.name; -} - /** * Fix up ACPI table checksum * * @v acpi ACPI table header */ -void acpi_fix_checksum ( struct acpi_description_header *acpi ) { +void acpi_fix_checksum ( struct acpi_header *acpi ) { unsigned int i = 0; uint8_t sum = 0; @@ -147,7 +131,7 @@ userptr_t acpi_find_rsdt ( userptr_t ebda ) { * @ret table Table, or UNULL if not found */ userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) { - struct acpi_description_header acpi; + struct acpi_header acpi; struct acpi_rsdt *rsdtab; typeof ( rsdtab->entry[0] ) entry; userptr_t table; @@ -227,7 +211,7 @@ userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) { * the ACPI specification itself. */ static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) { - struct acpi_description_header acpi; + struct acpi_header acpi; union { uint32_t dword; uint8_t byte[4]; @@ -331,34 +315,73 @@ int acpi_sx ( userptr_t rsdt, uint32_t signature ) { /****************************************************************************** * - * Interface methods + * Descriptors * ****************************************************************************** */ /** - * Describe object in an ACPI table + * Add ACPI descriptor + * + * @v desc ACPI descriptor + */ +void acpi_add ( struct acpi_descriptor *desc ) { + + /* Add to list of descriptors */ + ref_get ( desc->refcnt ); + list_add_tail ( &desc->list, &desc->model->descs ); +} + +/** + * Remove ACPI descriptor + * + * @v desc ACPI descriptor + */ +void acpi_del ( struct acpi_descriptor *desc ) { + + /* Remove from list of descriptors */ + list_check_contains_entry ( desc, &desc->model->descs, list ); + list_del ( &desc->list ); + ref_put ( desc->refcnt ); +} + +/** + * Get object's ACPI descriptor * * @v intf Interface - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code + * @ret desc ACPI descriptor, or NULL */ -int acpi_describe ( struct interface *intf, - struct acpi_description_header *acpi, size_t len ) { +struct acpi_descriptor * acpi_describe ( struct interface *intf ) { struct interface *dest; acpi_describe_TYPE ( void * ) *op = intf_get_dest_op ( intf, acpi_describe, &dest ); void *object = intf_object ( dest ); - int rc; + struct acpi_descriptor *desc; if ( op ) { - rc = op ( object, acpi, len ); + desc = op ( object ); } else { - /* Default is to fail to describe */ - rc = -EOPNOTSUPP; + desc = NULL; } intf_put ( dest ); - return rc; + return desc; +} + +/** + * Install ACPI tables + * + * @v install Table installation method + * @ret rc Return status code + */ +int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ){ + struct acpi_model *model; + int rc; + + for_each_table_entry ( model, ACPI_MODELS ) { + if ( ( rc = model->install ( install ) ) != 0 ) + return rc; + } + + return 0; } diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c index 64d5206fe..08180852c 100644 --- a/src/core/dummy_sanboot.c +++ b/src/core/dummy_sanboot.c @@ -38,10 +38,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v drive Drive number * @v uris List of URIs * @v count Number of URIs + * @v flags Flags * @ret drive Drive number, or negative error */ static int dummy_san_hook ( unsigned int drive, struct uri **uris, - unsigned int count ) { + unsigned int count, unsigned int flags ) { struct san_device *sandev; int rc; @@ -51,10 +52,9 @@ static int dummy_san_hook ( unsigned int drive, struct uri **uris, rc = -ENOMEM; goto err_alloc; } - sandev->drive = drive; /* Register SAN device */ - if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) { DBGC ( sandev, "SAN %#02x could not register: %s\n", sandev->drive, strerror ( rc ) ); goto err_register; @@ -101,14 +101,27 @@ static int dummy_san_boot ( unsigned int drive __unused ) { return -EOPNOTSUPP; } +/** + * Install ACPI table + * + * @v acpi ACPI description header + * @ret rc Return status code + */ +static int dummy_install ( struct acpi_header *acpi ) { + + DBGC ( acpi, "ACPI table %s:\n", acpi_name ( acpi->signature ) ); + DBGC_HDA ( acpi, 0, acpi, le32_to_cpu ( acpi->length ) ); + return 0; +} + /** * Describe dummy SAN device * - * @v drive Drive number + * @ret rc Return status code */ -static int dummy_san_describe ( unsigned int drive __unused ) { +static int dummy_san_describe ( void ) { - return 0; + return acpi_install ( dummy_install ); } PROVIDE_SANBOOT ( dummy, san_hook, dummy_san_hook ); diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c index 42fb06824..b09562e28 100644 --- a/src/core/null_sanboot.c +++ b/src/core/null_sanboot.c @@ -28,7 +28,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static int null_san_hook ( unsigned int drive __unused, struct uri **uris __unused, - unsigned int count __unused ) { + unsigned int count __unused, + unsigned int flags __unused ) { return -EOPNOTSUPP; } @@ -40,7 +41,7 @@ static int null_san_boot ( unsigned int drive __unused ) { return -EOPNOTSUPP; } -static int null_san_describe ( unsigned int drive __unused ) { +static int null_san_describe ( void ) { return -EOPNOTSUPP; } diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 03beae799..f134f76a3 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -119,8 +119,10 @@ static void sandev_free ( struct refcnt *refcnt ) { assert ( ! timer_running ( &sandev->timer ) ); assert ( ! sandev->active ); assert ( list_empty ( &sandev->opened ) ); - for ( i = 0 ; i < sandev->paths ; i++ ) + for ( i = 0 ; i < sandev->paths ; i++ ) { uri_put ( sandev->path[i].uri ); + assert ( sandev->path[i].desc == NULL ); + } free ( sandev ); } @@ -199,6 +201,15 @@ static int sanpath_open ( struct san_path *sanpath ) { return rc; } + /* Update ACPI descriptor, if applicable */ + if ( ! ( sandev->flags & SAN_NO_DESCRIBE ) ) { + if ( sanpath->desc ) + acpi_del ( sanpath->desc ); + sanpath->desc = acpi_describe ( &sanpath->block ); + if ( sanpath->desc ) + acpi_add ( sanpath->desc ); + } + /* Start process */ process_add ( &sanpath->process ); @@ -606,6 +617,72 @@ int sandev_rw ( struct san_device *sandev, uint64_t lba, return 0; } +/** + * Describe SAN device + * + * @v sandev SAN device + * @ret rc Return status code + * + * Allow connections to progress until all existent path descriptors + * are complete. + */ +static int sandev_describe ( struct san_device *sandev ) { + struct san_path *sanpath; + struct acpi_descriptor *desc; + int rc; + + /* Wait for all paths to be either described or closed */ + while ( 1 ) { + + /* Allow connections to progress */ + step(); + + /* Fail if any closed path has an incomplete descriptor */ + list_for_each_entry ( sanpath, &sandev->closed, list ) { + desc = sanpath->desc; + if ( ! desc ) + continue; + if ( ( rc = desc->model->complete ( desc ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x.%d could not be " + "described: %s\n", sandev->drive, + sanpath->index, strerror ( rc ) ); + return rc; + } + } + + /* Succeed if no paths have an incomplete descriptor */ + rc = 0; + list_for_each_entry ( sanpath, &sandev->opened, list ) { + desc = sanpath->desc; + if ( ! desc ) + continue; + if ( ( rc = desc->model->complete ( desc ) ) != 0 ) + break; + } + if ( rc == 0 ) + return 0; + } +} + +/** + * Remove SAN device descriptors + * + * @v sandev SAN device + */ +static void sandev_undescribe ( struct san_device *sandev ) { + struct san_path *sanpath; + unsigned int i; + + /* Remove all ACPI descriptors */ + for ( i = 0 ; i < sandev->paths ; i++ ) { + sanpath = &sandev->path[i]; + if ( sanpath->desc ) { + acpi_del ( sanpath->desc ); + sanpath->desc = NULL; + } + } +} + /** * Configure SAN device as a CD-ROM, if applicable * @@ -729,18 +806,25 @@ struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, * Register SAN device * * @v sandev SAN device + * @v drive Drive number + * @v flags Flags * @ret rc Return status code */ -int register_sandev ( struct san_device *sandev ) { +int register_sandev ( struct san_device *sandev, unsigned int drive, + unsigned int flags ) { int rc; /* Check that drive number is not in use */ - if ( sandev_find ( sandev->drive ) != NULL ) { - DBGC ( sandev, "SAN %#02x is already in use\n", sandev->drive ); + if ( sandev_find ( drive ) != NULL ) { + DBGC ( sandev, "SAN %#02x is already in use\n", drive ); rc = -EADDRINUSE; goto err_in_use; } + /* Record drive number and flags */ + sandev->drive = drive; + sandev->flags = flags; + /* Check that device is capable of being opened (i.e. that all * URIs are well-formed and that at least one path is * working). @@ -748,6 +832,10 @@ int register_sandev ( struct san_device *sandev ) { if ( ( rc = sandev_reopen ( sandev ) ) != 0 ) goto err_reopen; + /* Describe device */ + if ( ( rc = sandev_describe ( sandev ) ) != 0 ) + goto err_describe; + /* Read device capacity */ if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity, NULL ) ) != 0 ) @@ -766,8 +854,10 @@ int register_sandev ( struct san_device *sandev ) { list_del ( &sandev->list ); err_iso9660: err_capacity: + err_describe: err_reopen: sandev_restart ( sandev, rc ); + sandev_undescribe ( sandev ); err_in_use: return rc; } @@ -788,6 +878,9 @@ void unregister_sandev ( struct san_device *sandev ) { /* Shut down interfaces */ sandev_restart ( sandev, 0 ); + /* Remove ACPI descriptors */ + sandev_undescribe ( sandev ); + DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive ); } diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c index 91a808d85..a9d21f9ac 100644 --- a/src/drivers/block/ibft.c +++ b/src/drivers/block/ibft.c @@ -28,6 +28,7 @@ FILE_LICENCE ( BSD2 ); #include +#include #include #include #include @@ -38,6 +39,7 @@ FILE_LICENCE ( BSD2 ); #include #include #include +#include #include #include #include @@ -54,37 +56,31 @@ FILE_LICENCE ( BSD2 ); */ /** - * An iBFT created by iPXE - * - */ -struct ipxe_ibft { - /** The fixed section */ - struct ibft_table table; - /** The Initiator section */ - struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) )); - /** The NIC section */ - struct ibft_nic nic __attribute__ (( aligned ( 16 ) )); - /** The Target section */ - struct ibft_target target __attribute__ (( aligned ( 16 ) )); - /** Strings block */ - char strings[0]; -} __attribute__ (( packed, aligned ( 16 ) )); - -/** - * iSCSI string block descriptor + * iSCSI string buffer * * This is an internal structure that we use to keep track of the * allocation of string data. */ struct ibft_strings { - /** The iBFT containing these strings */ - struct ibft_table *table; - /** Offset of first free byte within iBFT */ - size_t offset; - /** Total length of the iBFT */ + /** Strings data */ + char *data; + /** Starting offset of strings */ + size_t start; + /** Total length */ size_t len; }; +/** + * Align structure within iBFT + * + * @v len Unaligned length (or offset) + * @ret len Aligned length (or offset) + */ +static inline size_t ibft_align ( size_t len ) { + + return ( ( len + IBFT_ALIGN - 1 ) & ~( IBFT_ALIGN - 1 ) ); +} + /** * Fill in an IP address field within iBFT * @@ -141,15 +137,29 @@ static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) { */ static char * ibft_alloc_string ( struct ibft_strings *strings, struct ibft_string *string, size_t len ) { + size_t new_len; + char *new_data; + char *dest; - if ( ( strings->offset + len ) >= strings->len ) + /* Extend string data buffer */ + new_len = ( strings->len + len + 1 /* NUL */ ); + new_data = realloc ( strings->data, new_len ); + if ( ! new_data ) return NULL; + strings->data = new_data; - string->offset = cpu_to_le16 ( strings->offset ); + /* Fill in string field */ + string->offset = cpu_to_le16 ( strings->start + strings->len ); string->len = cpu_to_le16 ( len ); - strings->offset += ( len + 1 ); - return ( ( ( char * ) strings->table ) + string->offset ); + /* Zero string */ + dest = ( strings->data + strings->len ); + memset ( dest, 0, ( len + 1 /* NUL */ ) ); + + /* Update allocated length */ + strings->len = new_len; + + return dest; } /** @@ -217,8 +227,28 @@ static int ibft_set_string_setting ( struct settings *settings, */ static const char * ibft_string ( struct ibft_strings *strings, struct ibft_string *string ) { - return ( string->offset ? - ( ( ( char * ) strings->table ) + string->offset ) : NULL ); + size_t offset = le16_to_cpu ( string->offset ); + + return ( offset ? ( strings->data + offset - strings->start ) : NULL ); +} + +/** + * Check if network device is required for the iBFT + * + * @v netdev Network device + * @ret is_required Network device is required + */ +static int ibft_netdev_is_required ( struct net_device *netdev ) { + struct iscsi_session *iscsi; + struct sockaddr_tcpip *st_target; + + list_for_each_entry ( iscsi, &ibft_model.descs, desc.list ) { + st_target = ( struct sockaddr_tcpip * ) &iscsi->target_sockaddr; + if ( tcpip_netdev ( st_target ) == netdev ) + return 1; + } + + return 0; } /** @@ -245,29 +275,33 @@ static int ibft_fill_nic ( struct ibft_nic *nic, nic->header.length = cpu_to_le16 ( sizeof ( *nic ) ); nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID | IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ); + DBG ( "iBFT NIC %d is %s\n", nic->header.index, netdev->name ); /* Determine origin of IP address */ fetch_setting ( parent, &ip_setting, &origin, NULL, NULL, 0 ); nic->origin = ( ( origin == parent ) ? IBFT_NIC_ORIGIN_MANUAL : IBFT_NIC_ORIGIN_DHCP ); - DBG ( "iBFT NIC origin = %d\n", nic->origin ); + DBG ( "iBFT NIC %d origin = %d\n", nic->header.index, nic->origin ); /* Extract values from configuration settings */ ibft_set_ipaddr_setting ( parent, &nic->ip_address, &ip_setting, 1 ); - DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) ); + DBG ( "iBFT NIC %d IP = %s\n", + nic->header.index, ibft_ipaddr ( &nic->ip_address ) ); ibft_set_ipaddr_setting ( parent, &nic->gateway, &gateway_setting, 1 ); - DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) ); + DBG ( "iBFT NIC %d gateway = %s\n", + nic->header.index, ibft_ipaddr ( &nic->gateway ) ); ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting, ( sizeof ( nic->dns ) / sizeof ( nic->dns[0] ) ) ); ibft_set_ipaddr_setting ( parent, &nic->dhcp, &dhcp_server_setting, 1 ); - DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) ); + DBG ( "iBFT NIC %d DNS = %s", + nic->header.index, ibft_ipaddr ( &nic->dns[0] ) ); DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) ); if ( ( rc = ibft_set_string_setting ( NULL, strings, &nic->hostname, &hostname_setting ) ) != 0 ) return rc; - DBG ( "iBFT NIC hostname = %s\n", - ibft_string ( strings, &nic->hostname ) ); + DBG ( "iBFT NIC %d hostname = %s\n", + nic->header.index, ibft_string ( strings, &nic->hostname ) ); /* Derive subnet mask prefix from subnet mask */ fetch_ipv4_setting ( parent, &netmask_setting, &netmask_addr ); @@ -277,19 +311,24 @@ static int ibft_fill_nic ( struct ibft_nic *nic, netmask_addr.s_addr >>= 1; } nic->subnet_mask_prefix = netmask_count; - DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix ); + DBG ( "iBFT NIC %d subnet = /%d\n", + nic->header.index, nic->subnet_mask_prefix ); /* Extract values from net-device configuration */ nic->vlan = cpu_to_le16 ( vlan_tag ( netdev ) ); - DBG ( "iBFT NIC VLAN = %02x\n", le16_to_cpu ( nic->vlan ) ); + DBG ( "iBFT NIC %d VLAN = %02x\n", + nic->header.index, le16_to_cpu ( nic->vlan ) ); if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr, nic->mac_address ) ) != 0 ) { - DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) ); + DBG ( "Could not determine %s MAC: %s\n", + netdev->name, strerror ( rc ) ); return rc; } - DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) ); + DBG ( "iBFT NIC %d MAC = %s\n", + nic->header.index, eth_ntoa ( nic->mac_address ) ); nic->pci_bus_dev_func = cpu_to_le16 ( netdev->dev->desc.location ); - DBG ( "iBFT NIC PCI = %04x\n", le16_to_cpu ( nic->pci_bus_dev_func ) ); + DBG ( "iBFT NIC %d PCI = %04x\n", + nic->header.index, le16_to_cpu ( nic->pci_bus_dev_func ) ); return 0; } @@ -299,12 +338,10 @@ static int ibft_fill_nic ( struct ibft_nic *nic, * * @v initiator Initiator portion of iBFT * @v strings iBFT string block descriptor - * @v iscsi iSCSI session * @ret rc Return status code */ static int ibft_fill_initiator ( struct ibft_initiator *initiator, - struct ibft_strings *strings, - struct iscsi_session *iscsi ) { + struct ibft_strings *strings ) { int rc; /* Fill in common header */ @@ -314,16 +351,57 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator, initiator->header.flags = ( IBFT_FL_INITIATOR_BLOCK_VALID | IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ); - /* Fill in hostname */ - if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name, - iscsi->initiator_iqn ) ) != 0 ) + /* Fill in initiator name */ + if ( ( rc = ibft_set_string_setting ( NULL, strings, + &initiator->initiator_name, + &initiator_iqn_setting ) ) != 0 ) return rc; - DBG ( "iBFT initiator hostname = %s\n", + DBG ( "iBFT initiator name = %s\n", ibft_string ( strings, &initiator->initiator_name ) ); return 0; } +/** + * Fill in Target NIC association + * + * @v target Target portion of iBFT + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target_nic_association ( struct ibft_target *target, + struct iscsi_session *iscsi ) { + struct sockaddr_tcpip *st_target = + ( struct sockaddr_tcpip * ) &iscsi->target_sockaddr; + struct net_device *associated; + struct net_device *netdev; + + /* Find network device used to reach target */ + associated = tcpip_netdev ( st_target ); + if ( ! associated ) { + DBG ( "iBFT target %d has no net device\n", + target->header.index ); + return -EHOSTUNREACH; + } + + /* Calculate association */ + for_each_netdev ( netdev ) { + if ( netdev == associated ) { + DBG ( "iBFT target %d uses NIC %d (%s)\n", + target->header.index, target->nic_association, + netdev->name ); + return 0; + } + if ( ! ibft_netdev_is_required ( netdev ) ) + continue; + target->nic_association++; + } + + DBG ( "iBFT target %d has impossible NIC %s\n", + target->header.index, netdev->name ); + return -EINVAL; +} + /** * Fill in Target CHAP portion of iBFT * @@ -347,12 +425,12 @@ static int ibft_fill_target_chap ( struct ibft_target *target, if ( ( rc = ibft_set_string ( strings, &target->chap_name, iscsi->initiator_username ) ) != 0 ) return rc; - DBG ( "iBFT target username = %s\n", + DBG ( "iBFT target %d username = %s\n", target->header.index, ibft_string ( strings, &target->chap_name ) ); if ( ( rc = ibft_set_string ( strings, &target->chap_secret, iscsi->initiator_password ) ) != 0 ) return rc; - DBG ( "iBFT target password = \n" ); + DBG ( "iBFT target %d password = \n", target->header.index ); return 0; } @@ -382,12 +460,13 @@ static int ibft_fill_target_reverse_chap ( struct ibft_target *target, if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name, iscsi->target_username ) ) != 0 ) return rc; - DBG ( "iBFT target reverse username = %s\n", + DBG ( "iBFT target %d reverse username = %s\n", target->header.index, ibft_string ( strings, &target->chap_name ) ); if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret, iscsi->target_password ) ) != 0 ) return rc; - DBG ( "iBFT target reverse password = \n" ); + DBG ( "iBFT target %d reverse password = \n", + target->header.index ); return 0; } @@ -403,6 +482,8 @@ static int ibft_fill_target_reverse_chap ( struct ibft_target *target, static int ibft_fill_target ( struct ibft_target *target, struct ibft_strings *strings, struct iscsi_session *iscsi ) { + struct sockaddr_tcpip *st_target = + ( struct sockaddr_tcpip * ) &iscsi->target_sockaddr; struct sockaddr_in *sin_target = ( struct sockaddr_in * ) &iscsi->target_sockaddr; int rc; @@ -416,17 +497,21 @@ static int ibft_fill_target ( struct ibft_target *target, /* Fill in Target values */ ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr ); - DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) ); - target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) ); - DBG ( "iBFT target port = %d\n", target->socket ); + DBG ( "iBFT target %d IP = %s\n", + target->header.index, ibft_ipaddr ( &target->ip_address ) ); + target->socket = cpu_to_le16 ( ntohs ( st_target->st_port ) ); + DBG ( "iBFT target %d port = %d\n", + target->header.index, target->socket ); memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) ); - DBG ( "iBFT target boot LUN = " SCSI_LUN_FORMAT "\n", - SCSI_LUN_DATA ( target->boot_lun ) ); + DBG ( "iBFT target %d boot LUN = " SCSI_LUN_FORMAT "\n", + target->header.index, SCSI_LUN_DATA ( target->boot_lun ) ); if ( ( rc = ibft_set_string ( strings, &target->target_name, iscsi->target_iqn ) ) != 0 ) return rc; - DBG ( "iBFT target name = %s\n", + DBG ( "iBFT target %d name = %s\n", target->header.index, ibft_string ( strings, &target->target_name ) ); + if ( ( rc = ibft_fill_target_nic_association ( target, iscsi ) ) != 0 ) + return rc; if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 ) return rc; if ( ( rc = ibft_fill_target_reverse_chap ( target, strings, @@ -437,62 +522,159 @@ static int ibft_fill_target ( struct ibft_target *target, } /** - * Fill in iBFT + * Check if iBFT descriptor is complete * - * @v iscsi iSCSI session - * @v acpi ACPI table - * @v len Length of ACPI table + * @v desc ACPI descriptor * @ret rc Return status code */ -int ibft_describe ( struct iscsi_session *iscsi, - struct acpi_description_header *acpi, - size_t len ) { - struct ipxe_ibft *ibft = - container_of ( acpi, struct ipxe_ibft, table.acpi ); - struct ibft_strings strings = { - .table = &ibft->table, - .offset = offsetof ( typeof ( *ibft ), strings ), - .len = len, - }; - struct net_device *netdev; - int rc; +static int ibft_complete ( struct acpi_descriptor *desc ) { + struct iscsi_session *iscsi = + container_of ( desc, struct iscsi_session, desc ); - /* Ugly hack. Now that we have a generic interface mechanism - * that can support ioctls, we can potentially eliminate this. - */ - netdev = last_opened_netdev(); - if ( ! netdev ) { - DBGC ( iscsi, "iSCSI %p cannot guess network device\n", - iscsi ); - return -ENODEV; - } - - /* Fill in ACPI header */ - ibft->table.acpi.signature = cpu_to_le32 ( IBFT_SIG ); - ibft->table.acpi.length = cpu_to_le32 ( len ); - ibft->table.acpi.revision = 1; - - /* Fill in Control block */ - ibft->table.control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL; - ibft->table.control.header.version = 1; - ibft->table.control.header.length = - cpu_to_le16 ( sizeof ( ibft->table.control ) ); - ibft->table.control.initiator = - cpu_to_le16 ( offsetof ( typeof ( *ibft ), initiator ) ); - ibft->table.control.nic_0 = - cpu_to_le16 ( offsetof ( typeof ( *ibft ), nic ) ); - ibft->table.control.target_0 = - cpu_to_le16 ( offsetof ( typeof ( *ibft ), target ) ); - - /* Fill in NIC, Initiator and Target blocks */ - if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings, netdev ) ) != 0 ) - return rc; - if ( ( rc = ibft_fill_initiator ( &ibft->initiator, &strings, - iscsi ) ) != 0 ) - return rc; - if ( ( rc = ibft_fill_target ( &ibft->target, &strings, - iscsi ) ) != 0 ) - return rc; + /* Fail if we do not yet have the target address */ + if ( ! iscsi->target_sockaddr.sa_family ) + return -EAGAIN; return 0; } + +/** + * Install iBFT + * + * @v install Installation method + * @ret rc Return status code + */ +static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { + struct net_device *netdev; + struct iscsi_session *iscsi; + struct ibft_table *table; + struct ibft_initiator *initiator; + struct ibft_nic *nic; + struct ibft_target *target; + struct ibft_strings strings; + struct acpi_header *acpi; + void *data; + unsigned int targets = 0; + unsigned int pairs = 0; + size_t offset = 0; + size_t table_len; + size_t control_len; + size_t initiator_offset; + size_t nic_offset; + size_t target_offset; + size_t strings_offset; + size_t len; + unsigned int i; + int rc; + + /* Calculate table sizes and offsets */ + list_for_each_entry ( iscsi, &ibft_model.descs, desc.list ) + targets++; + pairs = ( sizeof ( table->control.pair ) / + sizeof ( table->control.pair[0] ) ); + if ( pairs < targets ) + pairs = targets; + offset = offsetof ( typeof ( *table ), control.pair ); + offset += ( pairs * sizeof ( table->control.pair[0] ) ); + table_len = offset; + control_len = ( table_len - offsetof ( typeof ( *table ), control ) ); + offset = ibft_align ( offset ); + initiator_offset = offset; + offset += ibft_align ( sizeof ( *initiator ) ); + nic_offset = offset; + offset += ( pairs * ibft_align ( sizeof ( *nic ) ) ); + target_offset = offset; + offset += ( pairs * ibft_align ( sizeof ( *target ) ) ); + strings_offset = offset; + strings.data = NULL; + strings.start = strings_offset; + strings.len = 0; + len = offset; + + /* Allocate table */ + data = zalloc ( len ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Fill in Control block */ + table = data; + table->control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL; + table->control.header.version = 1; + table->control.header.length = cpu_to_le16 ( control_len ); + + /* Fill in Initiator block */ + initiator = ( data + initiator_offset ); + table->control.initiator = cpu_to_le16 ( initiator_offset ); + if ( ( rc = ibft_fill_initiator ( initiator, &strings ) ) != 0 ) + goto err_initiator; + + /* Fill in NIC blocks */ + i = 0; + for_each_netdev ( netdev ) { + if ( ! ibft_netdev_is_required ( netdev ) ) + continue; + assert ( i < pairs ); + table->control.pair[i].nic = nic_offset; + nic = ( data + nic_offset ); + nic->header.index = i; + if ( ( rc = ibft_fill_nic ( nic, &strings, netdev ) ) != 0 ) + goto err_nic; + i++; + nic_offset += ibft_align ( sizeof ( *nic ) ); + } + + /* Fill in Target blocks */ + i = 0; + list_for_each_entry ( iscsi, &ibft_model.descs, desc.list ) { + assert ( i < pairs ); + table->control.pair[i].target = target_offset; + target = ( data + target_offset ); + target->header.index = i; + if ( ( rc = ibft_fill_target ( target, &strings, iscsi ) ) != 0) + goto err_target; + i++; + target_offset += ibft_align ( sizeof ( *target ) ); + } + + /* Reallocate table to include space for strings */ + len += strings.len; + acpi = realloc ( data, len ); + if ( ! acpi ) + goto err_realloc; + data = NULL; + + /* Fill in ACPI header */ + acpi->signature = cpu_to_le32 ( IBFT_SIG ); + acpi->length = cpu_to_le32 ( len ); + acpi->revision = 1; + + /* Append strings */ + memcpy ( ( ( ( void * ) acpi ) + strings_offset ), strings.data, + strings.len ); + + /* Install ACPI table */ + if ( ( rc = install ( acpi ) ) != 0 ) { + DBG ( "iBFT could not install: %s\n", strerror ( rc ) ); + goto err_install; + } + + err_install: + free ( acpi ); + err_realloc: + err_target: + err_nic: + err_initiator: + free ( data ); + err_alloc: + free ( strings.data ); + return rc; +} + +/** iBFT model */ +struct acpi_model ibft_model __acpi_model = { + .descs = LIST_HEAD_INIT ( ibft_model.descs ), + .complete = ibft_complete, + .install = ibft_install, +}; diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c index 7edf69ace..ab4812519 100644 --- a/src/drivers/block/srp.c +++ b/src/drivers/block/srp.c @@ -113,13 +113,6 @@ struct srp_device { /** Login completed successfully */ int logged_in; - /** Initiator port ID (for boot firmware table) */ - union srp_port_id initiator; - /** Target port ID (for boot firmware table) */ - union srp_port_id target; - /** SCSI LUN (for boot firmware table) */ - struct scsi_lun lun; - /** List of active commands */ struct list_head commands; }; @@ -684,61 +677,6 @@ static size_t srpdev_window ( struct srp_device *srpdev ) { return ( srpdev->logged_in ? ~( ( size_t ) 0 ) : 0 ); } -/** - * A (transport-independent) sBFT created by iPXE - */ -struct ipxe_sbft { - /** The table header */ - struct sbft_table table; - /** The SCSI subtable */ - struct sbft_scsi_subtable scsi; - /** The SRP subtable */ - struct sbft_srp_subtable srp; -} __attribute__ (( packed, aligned ( 16 ) )); - -/** - * Describe SRP device in an ACPI table - * - * @v srpdev SRP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int srpdev_describe ( struct srp_device *srpdev, - struct acpi_description_header *acpi, - size_t len ) { - struct ipxe_sbft *sbft = - container_of ( acpi, struct ipxe_sbft, table.acpi ); - int rc; - - /* Sanity check */ - if ( len < sizeof ( *sbft ) ) - return -ENOBUFS; - - /* Populate table */ - sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG ); - sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) ); - sbft->table.acpi.revision = 1; - sbft->table.scsi_offset = - cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) ); - memcpy ( &sbft->scsi.lun, &srpdev->lun, sizeof ( sbft->scsi.lun ) ); - sbft->table.srp_offset = - cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) ); - memcpy ( &sbft->srp.initiator, &srpdev->initiator, - sizeof ( sbft->srp.initiator ) ); - memcpy ( &sbft->srp.target, &srpdev->target, - sizeof ( sbft->srp.target ) ); - - /* Ask transport layer to describe transport-specific portions */ - if ( ( rc = acpi_describe ( &srpdev->socket, acpi, len ) ) != 0 ) { - DBGC ( srpdev, "SRP %p cannot describe transport layer: %s\n", - srpdev, strerror ( rc ) ); - return rc; - } - - return 0; -} - /** SRP device socket interface operations */ static struct interface_operation srpdev_socket_op[] = { INTF_OP ( xfer_deliver, struct srp_device *, srpdev_deliver ), @@ -755,7 +693,6 @@ static struct interface_operation srpdev_scsi_op[] = { INTF_OP ( scsi_command, struct srp_device *, srpdev_scsi_command ), INTF_OP ( xfer_window, struct srp_device *, srpdev_window ), INTF_OP ( intf_close, struct srp_device *, srpdev_close ), - INTF_OP ( acpi_describe, struct srp_device *, srpdev_describe ), }; /** SRP device SCSI interface descriptor */ @@ -797,11 +734,6 @@ int srp_open ( struct interface *block, struct interface *socket, ntohl ( target->dwords[0] ), ntohl ( target->dwords[1] ), ntohl ( target->dwords[2] ), ntohl ( target->dwords[3] ) ); - /* Preserve parameters required for boot firmware table */ - memcpy ( &srpdev->initiator, initiator, sizeof ( srpdev->initiator ) ); - memcpy ( &srpdev->target, target, sizeof ( srpdev->target ) ); - memcpy ( &srpdev->lun, lun, sizeof ( srpdev->lun ) ); - /* Attach to socket interface and initiate login */ intf_plug_plug ( &srpdev->socket, socket ); tag = srp_new_tag ( srpdev ); diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index 17d29b9df..f87b8ae9a 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -10,8 +10,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include +#include #include #include +#include /** * An ACPI description header @@ -19,7 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * This is the structure common to the start of all ACPI system * description tables. */ -struct acpi_description_header { +struct acpi_header { /** ACPI signature (4 ASCII characters) */ uint32_t signature; /** Length of table, in bytes, including header */ @@ -40,6 +44,22 @@ struct acpi_description_header { uint32_t asl_compiler_revision; } __attribute__ (( packed )); +/** + * Transcribe ACPI table signature (for debugging) + * + * @v signature ACPI table signature + * @ret name ACPI table signature name + */ +static inline const char * acpi_name ( uint32_t signature ) { + static union { + uint32_t signature; + char name[5]; + } u; + + u.signature = cpu_to_le32 ( signature ); + return u.name; +} + /** * Build ACPI signature * @@ -87,7 +107,7 @@ struct acpi_rsdp { /** ACPI Root System Description Table (RSDT) */ struct acpi_rsdt { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** ACPI table entries */ uint32_t entry[0]; } __attribute__ (( packed )); @@ -98,7 +118,7 @@ struct acpi_rsdt { /** Fixed ACPI Description Table (FADT) */ struct acpi_fadt { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** Physical address of FACS */ uint32_t facs; /** Physical address of DSDT */ @@ -122,17 +142,70 @@ struct acpi_fadt { /** Secondary System Description Table (SSDT) signature */ #define SSDT_SIGNATURE ACPI_SIGNATURE ( 'S', 'S', 'D', 'T' ) -extern int acpi_describe ( struct interface *interface, - struct acpi_description_header *acpi, size_t len ); -#define acpi_describe_TYPE( object_type ) \ - typeof ( int ( object_type, \ - struct acpi_description_header *acpi, \ - size_t len ) ) +/** An ACPI descriptor (used to construct ACPI tables) */ +struct acpi_descriptor { + /** Reference count of containing object */ + struct refcnt *refcnt; + /** Table model */ + struct acpi_model *model; + /** List of ACPI descriptors for this model */ + struct list_head list; +}; -extern void acpi_fix_checksum ( struct acpi_description_header *acpi ); +/** + * Initialise ACPI descriptor + * + * @v desc ACPI descriptor + * @v model Table model + * @v refcnt Reference count + */ +static inline __attribute__ (( always_inline )) void +acpi_init ( struct acpi_descriptor *desc, struct acpi_model *model, + struct refcnt *refcnt ) { + + desc->refcnt = refcnt; + desc->model = model; + INIT_LIST_HEAD ( &desc->list ); +} + +/** An ACPI table model */ +struct acpi_model { + /** List of descriptors */ + struct list_head descs; + /** + * Check if ACPI descriptor is complete + * + * @v desc ACPI descriptor + * @ret rc Return status code + */ + int ( * complete ) ( struct acpi_descriptor *desc ); + /** + * Install ACPI tables + * + * @v install Installation method + * @ret rc Return status code + */ + int ( * install ) ( int ( * install ) ( struct acpi_header *acpi ) ); +}; + +/** ACPI models */ +#define ACPI_MODELS __table ( struct acpi_model, "acpi_models" ) + +/** Declare an ACPI model */ +#define __acpi_model __table_entry ( ACPI_MODELS, 01 ) + +extern struct acpi_descriptor * +acpi_describe ( struct interface *interface ); +#define acpi_describe_TYPE( object_type ) \ + typeof ( struct acpi_descriptor * ( object_type ) ) + +extern void acpi_fix_checksum ( struct acpi_header *acpi ); extern userptr_t acpi_find_rsdt ( userptr_t ebda ); extern userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ); extern int acpi_sx ( userptr_t rsdt, uint32_t signature ); +extern void acpi_add ( struct acpi_descriptor *desc ); +extern void acpi_del ( struct acpi_descriptor *desc ); +extern int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ); #endif /* _IPXE_ACPI_H */ diff --git a/src/include/ipxe/aoe.h b/src/include/ipxe/aoe.h index 0c656e7c2..a51044d15 100644 --- a/src/include/ipxe/aoe.h +++ b/src/include/ipxe/aoe.h @@ -117,7 +117,7 @@ struct aoehdr { */ struct abft_table { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** AoE shelf */ uint16_t shelf; /** AoE slot */ diff --git a/src/include/ipxe/ibft.h b/src/include/ipxe/ibft.h index 35f15105c..51ce781a6 100644 --- a/src/include/ipxe/ibft.h +++ b/src/include/ipxe/ibft.h @@ -49,6 +49,9 @@ FILE_LICENCE ( BSD2 ); /** iSCSI Boot Firmware Table signature */ #define IBFT_SIG ACPI_SIGNATURE ( 'i', 'B', 'F', 'T' ) +/** Alignment of structures within iBFT */ +#define IBFT_ALIGN 16 + /** An offset from the start of the iBFT */ typedef uint16_t ibft_off_t; @@ -97,6 +100,20 @@ struct ibft_header { uint8_t flags; } __attribute__ (( packed )); +/** + * iBFT NIC and Target offset pair + * + * There is no implicit relation between the NIC and the Target, but + * using this structure simplifies the table construction code while + * matching the expected table layout. + */ +struct ibft_offset_pair { + /** Offset to NIC structure */ + ibft_off_t nic; + /** Offset to Target structure */ + ibft_off_t target; +} __attribute__ (( packed )); + /** * iBFT Control structure * @@ -108,14 +125,8 @@ struct ibft_control { uint16_t extensions; /** Offset to Initiator structure */ ibft_off_t initiator; - /** Offset to NIC structure for NIC 0 */ - ibft_off_t nic_0; - /** Offset to Target structure for target 0 */ - ibft_off_t target_0; - /** Offset to NIC structure for NIC 1 */ - ibft_off_t nic_1; - /** Offset to Target structure for target 1 */ - ibft_off_t target_1; + /** Offsets to NIC and Target structures */ + struct ibft_offset_pair pair[2]; } __attribute__ (( packed )); /** Structure ID for Control section */ @@ -262,18 +273,13 @@ struct ibft_target { */ struct ibft_table { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** Reserved */ uint8_t reserved[12]; /** Control structure */ struct ibft_control control; } __attribute__ (( packed )); -struct iscsi_session; -struct net_device; - -extern int ibft_describe ( struct iscsi_session *iscsi, - struct acpi_description_header *acpi, - size_t len ); +extern struct acpi_model ibft_model __acpi_model; #endif /* _IPXE_IBFT_H */ diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h index c75ff4188..966cf52b6 100644 --- a/src/include/ipxe/iscsi.h +++ b/src/include/ipxe/iscsi.h @@ -16,6 +16,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** Default iSCSI port */ #define ISCSI_PORT 3260 @@ -647,6 +649,8 @@ struct iscsi_session { struct sockaddr target_sockaddr; /** SCSI LUN (for boot firmware table) */ struct scsi_lun lun; + /** ACPI descriptor */ + struct acpi_descriptor desc; }; /** iSCSI session is currently in the security negotiation phase */ @@ -697,4 +701,7 @@ struct iscsi_session { /** Default initiator IQN prefix */ #define ISCSI_DEFAULT_IQN_PREFIX "iqn.2010-04.org.ipxe" +extern const struct setting +initiator_iqn_setting __setting ( SETTING_SANBOOT_EXTRA, initiator-iqn ); + #endif /* _IPXE_ISCSI_H */ diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 64b665a52..8737bbc0b 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** A SAN path */ @@ -37,6 +38,9 @@ struct san_path { struct process process; /** Path status */ int path_rc; + + /** ACPI descriptor (if applicable) */ + struct acpi_descriptor *desc; }; /** A SAN device */ @@ -48,6 +52,8 @@ struct san_device { /** Drive number */ unsigned int drive; + /** Flags */ + unsigned int flags; /** Command interface */ struct interface command; @@ -83,6 +89,12 @@ struct san_device { struct san_path path[0]; }; +/** SAN device flags */ +enum san_device_flags { + /** Device should not be included in description tables */ + SAN_NO_DESCRIBE = 0x0001, +}; + /** * Calculate static inline sanboot API function name * @@ -126,9 +138,11 @@ struct san_device { * @v drive Drive number * @v uris List of URIs * @v count Number of URIs + * @v flags Flags * @ret drive Drive number, or negative error */ -int san_hook ( unsigned int drive, struct uri **uris, unsigned int count ); +int san_hook ( unsigned int drive, struct uri **uris, unsigned int count, + unsigned int flags ); /** * Unhook SAN device @@ -146,12 +160,11 @@ void san_unhook ( unsigned int drive ); int san_boot ( unsigned int drive ); /** - * Describe SAN device for SAN-booted operating system + * Describe SAN devices for SAN-booted operating system * - * @v drive Drive number * @ret rc Return status code */ -int san_describe ( unsigned int drive ); +int san_describe ( void ); extern struct list_head san_devices; @@ -230,7 +243,8 @@ extern int sandev_rw ( struct san_device *sandev, uint64_t lba, userptr_t buffer, size_t len ) ); extern struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, size_t priv_size ); -extern int register_sandev ( struct san_device *sandev ); +extern int register_sandev ( struct san_device *sandev, unsigned int drive, + unsigned int flags ); extern void unregister_sandev ( struct san_device *sandev ); extern unsigned int san_default_drive ( void ); diff --git a/src/include/ipxe/srp.h b/src/include/ipxe/srp.h index 8d7f799cd..3abb0995f 100644 --- a/src/include/ipxe/srp.h +++ b/src/include/ipxe/srp.h @@ -790,7 +790,7 @@ typedef uint16_t sbft_off_t; */ struct sbft_table { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** Offset to SCSI subtable */ sbft_off_t scsi_offset; /** Offset to SRP subtable */ diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 9679fc0da..fa2271b31 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -254,10 +254,11 @@ static void efi_block_connect ( struct san_device *sandev ) { * @v drive Drive number * @v uris List of URIs * @v count Number of URIs + * @v flags Flags * @ret drive Drive number, or negative error */ static int efi_block_hook ( unsigned int drive, struct uri **uris, - unsigned int count ) { + unsigned int count, unsigned int flags ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_DEVICE_PATH_PROTOCOL *end; struct efi_block_vendor_path *vendor; @@ -301,7 +302,6 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris, rc = -ENOMEM; goto err_alloc; } - sandev->drive = drive; block = sandev->priv; block->sandev = sandev; block->media.MediaPresent = 1; @@ -331,12 +331,12 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris, end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; end->Length[0] = sizeof ( *end ); DBGC ( sandev, "EFIBLK %#02x has device path %s\n", - sandev->drive, efi_devpath_text ( block->path ) ); + drive, efi_devpath_text ( block->path ) ); /* Register SAN device */ - if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) { DBGC ( sandev, "EFIBLK %#02x could not register: %s\n", - sandev->drive, strerror ( rc ) ); + drive, strerror ( rc ) ); goto err_register; } @@ -408,85 +408,105 @@ static void efi_block_unhook ( unsigned int drive ) { sandev_put ( sandev ); } +/** An installed ACPI table */ +struct efi_acpi_table { + /** List of installed tables */ + struct list_head list; + /** Table key */ + UINTN key; +}; + +/** List of installed ACPI tables */ +static LIST_HEAD ( efi_acpi_tables ); + /** - * Describe EFI block device + * Install ACPI table * - * @v drive Drive number + * @v hdr ACPI description header * @ret rc Return status code */ -static int efi_block_describe ( unsigned int drive ) { - static union { - /** ACPI header */ - struct acpi_description_header acpi; - /** Padding */ - char pad[768]; - } xbftab; - static UINTN key; - struct san_device *sandev; - struct san_path *sanpath; - size_t len; +static int efi_block_install ( struct acpi_header *hdr ) { + size_t len = le32_to_cpu ( hdr->length ); + struct efi_acpi_table *installed; EFI_STATUS efirc; int rc; - /* Find SAN device */ - sandev = sandev_find ( drive ); - if ( ! sandev ) { - DBG ( "EFIBLK cannot find drive %#02x\n", drive ); - return -ENODEV; + /* Allocate installed table record */ + installed = zalloc ( sizeof ( *installed ) ); + if ( ! installed ) { + rc = -ENOMEM; + goto err_alloc; } + /* Fill in common parameters */ + strncpy ( hdr->oem_id, "FENSYS", sizeof ( hdr->oem_id ) ); + strncpy ( hdr->oem_table_id, "iPXE", sizeof ( hdr->oem_table_id ) ); + + /* Fix up ACPI checksum */ + acpi_fix_checksum ( hdr ); + + /* Install table */ + if ( ( efirc = acpi->InstallAcpiTable ( acpi, hdr, len, + &installed->key ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( acpi, "EFIBLK could not install %s: %s\n", + acpi_name ( hdr->signature ), strerror ( rc ) ); + DBGC_HDA ( acpi, 0, hdr, len ); + goto err_install; + } + + /* Add to list of installed tables */ + list_add_tail ( &installed->list, &efi_acpi_tables ); + + DBGC ( acpi, "EFIBLK installed %s as ACPI table %#lx:\n", + acpi_name ( hdr->signature ), + ( ( unsigned long ) installed->key ) ); + DBGC_HDA ( acpi, 0, hdr, len ); + return 0; + + list_del ( &installed->list ); + err_install: + free ( installed ); + err_alloc: + return rc; +} + +/** + * Describe EFI block devices + * + * @ret rc Return status code + */ +static int efi_block_describe ( void ) { + struct efi_acpi_table *installed; + struct efi_acpi_table *tmp; + UINTN key; + EFI_STATUS efirc; + int rc; + /* Sanity check */ if ( ! acpi ) { - DBGC ( sandev, "EFIBLK %#02x has no ACPI table protocol\n", - sandev->drive ); + DBG ( "EFIBLK has no ACPI table protocol\n" ); return -ENOTSUP; } - /* Remove existing table, if any */ - if ( key ) { + /* Uninstall any existing ACPI tables */ + list_for_each_entry_safe ( installed, tmp, &efi_acpi_tables, list ) { + key = installed->key; if ( ( efirc = acpi->UninstallAcpiTable ( acpi, key ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( sandev, "EFIBLK %#02x could not uninstall ACPI " - "table: %s\n", sandev->drive, strerror ( rc ) ); + DBGC ( acpi, "EFIBLK could not uninstall ACPI table " + "%#lx: %s\n", ( ( unsigned long ) key ), + strerror ( rc ) ); /* Continue anyway */ } - key = 0; + list_del ( &installed->list ); + free ( installed ); } - /* Reopen block device if necessary */ - if ( sandev_needs_reopen ( sandev ) && - ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) - return rc; - sanpath = sandev->active; - assert ( sanpath != NULL ); - - /* Clear table */ - memset ( &xbftab, 0, sizeof ( xbftab ) ); - - /* Fill in common parameters */ - strncpy ( xbftab.acpi.oem_id, "FENSYS", - sizeof ( xbftab.acpi.oem_id ) ); - strncpy ( xbftab.acpi.oem_table_id, "iPXE", - sizeof ( xbftab.acpi.oem_table_id ) ); - - /* Fill in remaining parameters */ - if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi, - sizeof ( xbftab ) ) ) != 0 ) { - DBGC ( sandev, "EFIBLK %#02x could not create ACPI " - "description: %s\n", sandev->drive, strerror ( rc ) ); - return rc; - } - len = le32_to_cpu ( xbftab.acpi.length ); - - /* Fix up ACPI checksum */ - acpi_fix_checksum ( &xbftab.acpi ); - - /* Install table */ - if ( ( efirc = acpi->InstallAcpiTable ( acpi, &xbftab, len, - &key ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( sandev, "EFIBLK %#02x could not install ACPI table: " - "%s\n", sandev->drive, strerror ( rc ) ); + /* Install ACPI tables */ + if ( ( rc = acpi_install ( efi_block_install ) ) != 0 ) { + DBGC ( acpi, "EFIBLK could not install ACPI tables: %s\n", + strerror ( rc ) ); return rc; } diff --git a/src/net/aoe.c b/src/net/aoe.c index 2da8655b4..3a6611d04 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -53,6 +53,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 ); struct net_protocol aoe_protocol __net_protocol; +struct acpi_model abft_model __acpi_model; /****************************************************************************** * @@ -91,6 +92,9 @@ struct aoe_device { struct interface config; /** Device is configued */ int configured; + + /** ACPI descriptor */ + struct acpi_descriptor desc; }; /** An AoE command */ @@ -790,32 +794,13 @@ static struct device * aoedev_identify_device ( struct aoe_device *aoedev ) { } /** - * Describe AoE device in an ACPI table + * Get AoE ACPI descriptor * * @v aoedev AoE device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code + * @ret desc ACPI descriptor */ -static int aoedev_describe ( struct aoe_device *aoedev, - struct acpi_description_header *acpi, - size_t len ) { - struct abft_table *abft = - container_of ( acpi, struct abft_table, acpi ); - - /* Sanity check */ - if ( len < sizeof ( *abft ) ) - return -ENOBUFS; - - /* Populate table */ - abft->acpi.signature = cpu_to_le32 ( ABFT_SIG ); - abft->acpi.length = cpu_to_le32 ( sizeof ( *abft ) ); - abft->acpi.revision = 1; - abft->shelf = cpu_to_le16 ( aoedev->major ); - abft->slot = aoedev->minor; - memcpy ( abft->mac, aoedev->netdev->ll_addr, sizeof ( abft->mac ) ); - - return 0; +static struct acpi_descriptor * aoedev_describe ( struct aoe_device *aoedev ) { + return &aoedev->desc; } /** AoE device ATA interface operations */ @@ -869,6 +854,7 @@ static int aoedev_open ( struct interface *parent, struct net_device *netdev, aoedev->minor = minor; memcpy ( aoedev->target, netdev->ll_broadcast, netdev->ll_protocol->ll_addr_len ); + acpi_init ( &aoedev->desc, &abft_model, &aoedev->refcnt ); /* Initiate configuration */ if ( ( rc = aoedev_cfg_command ( aoedev, &aoedev->config ) ) < 0 ) { @@ -1059,3 +1045,61 @@ struct uri_opener aoe_uri_opener __uri_opener = { .scheme = "aoe", .open = aoe_open, }; + +/****************************************************************************** + * + * AoE boot firmware table (aBFT) + * + ****************************************************************************** + */ + +/** + * Check if AoE boot firmware table descriptor is complete + * + * @v desc ACPI descriptor + * @ret rc Return status code + */ +static int abft_complete ( struct acpi_descriptor *desc __unused ) { + return 0; +} + +/** + * Install AoE boot firmware table(s) + * + * @v install Installation method + * @ret rc Return status code + */ +static int abft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { + struct aoe_device *aoedev; + struct abft_table abft; + int rc; + + list_for_each_entry ( aoedev, &abft_model.descs, desc.list ) { + + /* Populate table */ + memset ( &abft, 0, sizeof ( abft ) ); + abft.acpi.signature = cpu_to_le32 ( ABFT_SIG ); + abft.acpi.length = cpu_to_le32 ( sizeof ( abft ) ); + abft.acpi.revision = 1; + abft.shelf = cpu_to_le16 ( aoedev->major ); + abft.slot = aoedev->minor; + memcpy ( abft.mac, aoedev->netdev->ll_addr, + sizeof ( abft.mac ) ); + + /* Install table */ + if ( ( rc = install ( &abft.acpi ) ) != 0 ) { + DBGC ( aoedev, "AoE %s could not install aBFT: %s\n", + aoedev_name ( aoedev ), strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** aBFT model */ +struct acpi_model abft_model __acpi_model = { + .descs = LIST_HEAD_INIT ( abft_model.descs ), + .complete = abft_complete, + .install = abft_install, +}; diff --git a/src/net/fcp.c b/src/net/fcp.c index 930bf7dd4..d92cfdcf3 100644 --- a/src/net/fcp.c +++ b/src/net/fcp.c @@ -843,25 +843,6 @@ static size_t fcpdev_window ( struct fcp_device *fcpdev ) { ~( ( size_t ) 0 ) : 0 ); } -/** - * Describe FCP device in an ACPI table - * - * @v fcpdev FCP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int fcpdev_acpi_describe ( struct fcp_device *fcpdev, - struct acpi_description_header *acpi, - size_t len ) { - - DBGC ( fcpdev, "FCP %p cannot yet describe device in an ACPI table\n", - fcpdev ); - ( void ) acpi; - ( void ) len; - return 0; -} - /** * Describe FCP device using EDD * @@ -917,7 +898,6 @@ static struct interface_operation fcpdev_scsi_op[] = { INTF_OP ( scsi_command, struct fcp_device *, fcpdev_scsi_command ), INTF_OP ( xfer_window, struct fcp_device *, fcpdev_window ), INTF_OP ( intf_close, struct fcp_device *, fcpdev_close ), - INTF_OP ( acpi_describe, struct fcp_device *, fcpdev_acpi_describe ), INTF_OP ( edd_describe, struct fcp_device *, fcpdev_edd_describe ), INTF_OP ( identify_device, struct fcp_device *, fcpdev_identify_device ), diff --git a/src/net/infiniband/ib_srp.c b/src/net/infiniband/ib_srp.c index 3b4914abf..cf1ef3bfd 100644 --- a/src/net/infiniband/ib_srp.c +++ b/src/net/infiniband/ib_srp.c @@ -60,6 +60,8 @@ FILE_LICENCE ( BSD2 ); #define EINFO_EINVAL_RP_TOO_SHORT __einfo_uniqify \ ( EINFO_EINVAL, 0x04, "Root path too short" ) +struct acpi_model ib_sbft_model __acpi_model; + /****************************************************************************** * * IB SRP devices @@ -67,6 +69,20 @@ FILE_LICENCE ( BSD2 ); ****************************************************************************** */ +/** + * An IB SRP sBFT created by iPXE + */ +struct ipxe_ib_sbft { + /** The table header */ + struct sbft_table table; + /** The SCSI subtable */ + struct sbft_scsi_subtable scsi; + /** The SRP subtable */ + struct sbft_srp_subtable srp; + /** The Infiniband subtable */ + struct sbft_ib_subtable ib; +}; + /** An Infiniband SRP device */ struct ib_srp_device { /** Reference count */ @@ -80,10 +96,10 @@ struct ib_srp_device { /** Infiniband device */ struct ib_device *ibdev; - /** Destination GID (for boot firmware table) */ - union ib_gid dgid; - /** Service ID (for boot firmware table) */ - union ib_guid service_id; + /** ACPI descriptor */ + struct acpi_descriptor desc; + /** Boot firmware table parameters */ + struct ipxe_ib_sbft sbft; }; /** @@ -113,43 +129,15 @@ static void ib_srp_close ( struct ib_srp_device *ib_srp, int rc ) { } /** - * Describe IB SRP device in an ACPI table + * Get IB SRP ACPI descriptor * - * @v srpdev SRP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code + * @v ib_srp IB SRP device + * @ret desc ACPI descriptor */ -static int ib_srp_describe ( struct ib_srp_device *ib_srp, - struct acpi_description_header *acpi, - size_t len ) { - struct ib_device *ibdev = ib_srp->ibdev; - struct sbft_table *sbft = - container_of ( acpi, struct sbft_table, acpi ); - struct sbft_ib_subtable *ib_sbft; - size_t used; +static struct acpi_descriptor * +ib_srp_describe ( struct ib_srp_device *ib_srp ) { - /* Sanity check */ - if ( acpi->signature != SBFT_SIG ) - return -EINVAL; - - /* Append IB subtable to existing table */ - used = le32_to_cpu ( sbft->acpi.length ); - sbft->ib_offset = cpu_to_le16 ( used ); - ib_sbft = ( ( ( void * ) sbft ) + used ); - used += sizeof ( *ib_sbft ); - if ( used > len ) - return -ENOBUFS; - sbft->acpi.length = cpu_to_le32 ( used ); - - /* Populate subtable */ - memcpy ( &ib_sbft->sgid, &ibdev->gid, sizeof ( ib_sbft->sgid ) ); - memcpy ( &ib_sbft->dgid, &ib_srp->dgid, sizeof ( ib_sbft->dgid ) ); - memcpy ( &ib_sbft->service_id, &ib_srp->service_id, - sizeof ( ib_sbft->service_id ) ); - ib_sbft->pkey = cpu_to_le16 ( ibdev->pkey ); - - return 0; + return &ib_srp->desc; } /** IB SRP CMRC interface operations */ @@ -188,6 +176,7 @@ static int ib_srp_open ( struct interface *block, struct ib_device *ibdev, union srp_port_id *initiator, union srp_port_id *target, struct scsi_lun *lun ) { struct ib_srp_device *ib_srp; + struct ipxe_ib_sbft *sbft; int rc; /* Allocate and initialise structure */ @@ -200,13 +189,19 @@ static int ib_srp_open ( struct interface *block, struct ib_device *ibdev, intf_init ( &ib_srp->srp, &ib_srp_srp_desc, &ib_srp->refcnt ); intf_init ( &ib_srp->cmrc, &ib_srp_cmrc_desc, &ib_srp->refcnt ); ib_srp->ibdev = ibdev_get ( ibdev ); + acpi_init ( &ib_srp->desc, &ib_sbft_model, &ib_srp->refcnt ); DBGC ( ib_srp, "IBSRP %p for " IB_GID_FMT " " IB_GUID_FMT "\n", ib_srp, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); /* Preserve parameters required for boot firmware table */ - memcpy ( &ib_srp->dgid, dgid, sizeof ( ib_srp->dgid ) ); - memcpy ( &ib_srp->service_id, service_id, - sizeof ( ib_srp->service_id ) ); + sbft = &ib_srp->sbft; + memcpy ( &sbft->scsi.lun, lun, sizeof ( sbft->scsi.lun ) ); + memcpy ( &sbft->srp.initiator, initiator, + sizeof ( sbft->srp.initiator ) ); + memcpy ( &sbft->srp.target, target, sizeof ( sbft->srp.target ) ); + memcpy ( &sbft->ib.dgid, dgid, sizeof ( sbft->ib.dgid ) ); + memcpy ( &sbft->ib.service_id, service_id, + sizeof ( sbft->ib.service_id ) ); /* Open CMRC socket */ if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid, @@ -579,3 +574,67 @@ struct uri_opener ib_srp_uri_opener __uri_opener = { .scheme = "ib_srp", .open = ib_srp_open_uri, }; + +/****************************************************************************** + * + * IB SRP boot firmware table (sBFT) + * + ****************************************************************************** + */ + +/** + * Check if IB SRP boot firmware table descriptor is complete + * + * @v desc ACPI descriptor + * @ret rc Return status code + */ +static int ib_sbft_complete ( struct acpi_descriptor *desc __unused ) { + return 0; +} + +/** + * Install IB SRP boot firmware table(s) + * + * @v install Installation method + * @ret rc Return status code + */ +static int ib_sbft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { + struct ib_srp_device *ib_srp; + struct ipxe_ib_sbft *sbft; + struct ib_device *ibdev; + int rc; + + list_for_each_entry ( ib_srp, &ib_sbft_model.descs, desc.list ) { + + /* Complete table */ + sbft = &ib_srp->sbft; + ibdev = ib_srp->ibdev; + sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG ); + sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) ); + sbft->table.acpi.revision = 1; + sbft->table.scsi_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) ); + sbft->table.srp_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) ); + sbft->table.ib_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), ib ) ); + memcpy ( &sbft->ib.sgid, &ibdev->gid, sizeof ( sbft->ib.sgid )); + sbft->ib.pkey = cpu_to_le16 ( ibdev->pkey ); + + /* Install table */ + if ( ( rc = install ( &sbft->table.acpi ) ) != 0 ) { + DBGC ( ib_srp, "IBSRP %p could not install sBFT: %s\n", + ib_srp, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** IB sBFT model */ +struct acpi_model ib_sbft_model __acpi_model = { + .descs = LIST_HEAD_INIT ( ib_sbft_model.descs ), + .complete = ib_sbft_complete, + .install = ib_sbft_install, +}; diff --git a/src/net/tcp/httpblock.c b/src/net/tcp/httpblock.c index e124ad2d6..1abd6b34d 100644 --- a/src/net/tcp/httpblock.c +++ b/src/net/tcp/httpblock.c @@ -114,21 +114,3 @@ int http_block_read_capacity ( struct http_transaction *http, err_open: return rc; } - -/** - * Describe device in ACPI table - * - * @v http HTTP transaction - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -int http_acpi_describe ( struct http_transaction *http, - struct acpi_description_header *acpi, size_t len ) { - - DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", - http ); - ( void ) acpi; - ( void ) len; - return 0; -} diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index ec527c64d..ce53a2127 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -509,28 +509,11 @@ __weak int http_block_read_capacity ( struct http_transaction *http __unused, return -ENOTSUP; } -/** - * Describe device in ACPI table (when HTTP block device support is not present) - * - * @v http HTTP transaction - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -__weak int http_acpi_describe ( struct http_transaction *http __unused, - struct acpi_description_header *acpi __unused, - size_t len __unused ) { - - return -ENOTSUP; -} - /** HTTP data transfer interface operations */ static struct interface_operation http_xfer_operations[] = { INTF_OP ( block_read, struct http_transaction *, http_block_read ), INTF_OP ( block_read_capacity, struct http_transaction *, http_block_read_capacity ), - INTF_OP ( acpi_describe, struct http_transaction *, - http_acpi_describe ), INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), INTF_OP ( intf_close, struct http_transaction *, http_close ), }; diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 1e6fd1f51..7de4a7bf6 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1810,12 +1810,23 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi, return iscsi->itt; } +/** + * Get iSCSI ACPI descriptor + * + * @v iscsi iSCSI session + * @ret desc ACPI descriptor + */ +static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) { + + return &iscsi->desc; +} + /** iSCSI SCSI command-issuing interface operations */ static struct interface_operation iscsi_control_op[] = { INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ), INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ), INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), - INTF_OP ( acpi_describe, struct iscsi_session *, ibft_describe ), + INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ), }; /** iSCSI SCSI command-issuing interface descriptor */ @@ -2064,6 +2075,7 @@ static int iscsi_open ( struct interface *parent, struct uri *uri ) { intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt ); process_init_stopped ( &iscsi->process, &iscsi_process_desc, &iscsi->refcnt ); + acpi_init ( &iscsi->desc, &ibft_model, &iscsi->refcnt ); /* Parse root path */ if ( ( rc = iscsi_parse_root_path ( iscsi, uri->opaque ) ) != 0 ) diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index a0c793516..10ccdc1d6 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -128,7 +128,9 @@ int uriboot ( struct uri *filename, struct uri **root_paths, /* Hook SAN device, if applicable */ if ( root_path_count ) { - drive = san_hook ( drive, root_paths, root_path_count ); + drive = san_hook ( drive, root_paths, root_path_count, + ( ( flags & URIBOOT_NO_SAN_DESCRIBE ) ? + SAN_NO_DESCRIBE : 0 ) ); if ( drive < 0 ) { rc = drive; printf ( "Could not open SAN device: %s\n", @@ -140,9 +142,9 @@ int uriboot ( struct uri *filename, struct uri **root_paths, /* Describe SAN device, if applicable */ if ( ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) { - if ( ( rc = san_describe ( drive ) ) != 0 ) { - printf ( "Could not describe SAN device %#02x: %s\n", - drive, strerror ( rc ) ); + if ( ( rc = san_describe() ) != 0 ) { + printf ( "Could not describe SAN devices: %s\n", + strerror ( rc ) ); goto err_san_describe; } } From 2ace5196e5580810ea3aa53352a4a7331f442e58 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Mar 2017 20:45:23 +0300 Subject: [PATCH 438/591] [iscsi] Do not install iBFT when no iSCSI targets exist Signed-off-by: Michael Brown --- src/drivers/block/ibft.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c index a9d21f9ac..bb7812b3b 100644 --- a/src/drivers/block/ibft.c +++ b/src/drivers/block/ibft.c @@ -591,6 +591,12 @@ static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { strings.len = 0; len = offset; + /* Do nothing if no targets exist */ + if ( ! targets ) { + rc = 0; + goto no_targets; + } + /* Allocate table */ data = zalloc ( len ); if ( ! data ) { @@ -668,6 +674,7 @@ static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { err_initiator: free ( data ); err_alloc: + no_targets: free ( strings.data ); return rc; } From c8cae7cc178776dbb42cbe1cdaecbac99ddc060d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Mar 2017 23:37:03 +0300 Subject: [PATCH 439/591] [http] Notify data transfer interface when underlying connection is ready HTTP implements xfer_window_changed() on the underlying server connection using http_step(), which does not propagate the window change notification to the data transfer interface. This breaks the multipath-capable SAN boot code, which relies on the window change notification to discover that the HTTP block device is ready for commands to be issued. Fix by sending xfer_window_changed() in http_step() once the underlying connection has been determined to be ready. Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index ce53a2127..4a1300cd7 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -352,6 +352,9 @@ static void http_step ( struct http_transaction *http ) { if ( ! xfer_window ( &http->conn ) ) return; + /* Notify data transfer interface that window may have changed */ + xfer_window_changed ( &http->xfer ); + /* Do nothing until data transfer interface is ready */ if ( ! xfer_window ( &http->xfer ) ) return; From 28e26dd2503e6006fabb26f8c33050ba93a99623 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Mar 2017 10:35:05 +0300 Subject: [PATCH 440/591] [mucurses] Fix erroneous __nonnull attribute Signed-off-by: Michael Brown --- src/include/curses.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/include/curses.h b/src/include/curses.h index 04060fe27..1f6fe029b 100644 --- a/src/include/curses.h +++ b/src/include/curses.h @@ -443,7 +443,8 @@ extern int wborder ( WINDOW *, chtype, chtype, chtype, chtype, chtype, chtype, extern int wclrtobot ( WINDOW * ) __nonnull; extern int wclrtoeol ( WINDOW * ) __nonnull; extern void wcursyncup ( WINDOW * ); -extern int wcolour_set ( WINDOW *, short, void * ) __nonnull; +extern int wcolour_set ( WINDOW *, short, void * ) + __attribute__ (( nonnull (1))); #define wcolor_set(w,s,v) wcolour_set((w),(s),(v)) extern int wdelch ( WINDOW * ) __nonnull; extern int wdeleteln ( WINDOW * ) __nonnull; From 5f85cbb9ee1c00cec81a848a9e871ad5d1e7f53f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Mar 2017 10:36:03 +0300 Subject: [PATCH 441/591] [build] Avoid implicit-fallthrough warnings on GCC 7 Reported-by: Vinson Lee Reported-by: Liang Yan Signed-off-by: Michael Brown --- src/arch/x86/image/bzimage.c | 2 ++ src/drivers/infiniband/golan.c | 1 + src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c | 2 ++ src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c | 1 + src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c | 1 + src/drivers/net/igbvf/igbvf_vf.c | 1 + src/drivers/net/tg3/tg3_hw.c | 12 ++++++++++++ src/tests/setjmp_test.c | 5 +++-- 8 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index e3c4cb83d..51498bf95 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -282,9 +282,11 @@ static int bzimage_parse_cmdline ( struct image *image, case 'G': case 'g': bzimg->mem_limit <<= 10; + /* Fall through */ case 'M': case 'm': bzimg->mem_limit <<= 10; + /* Fall through */ case 'K': case 'k': bzimg->mem_limit <<= 10; diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 30eaabab2..61331d4c1 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -1956,6 +1956,7 @@ static inline void golan_handle_port_event(struct golan *golan, struct golan_eqe case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG: case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE: golan_ib_update ( ibdev ); + /* Fall through */ case GOLAN_PORT_CHANGE_SUBTYPE_DOWN: case GOLAN_PORT_CHANGE_SUBTYPE_LID: case GOLAN_PORT_CHANGE_SUBTYPE_PKEY: diff --git a/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c b/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c index 2b6c133cb..a98e4bb66 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c @@ -640,12 +640,14 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah) case 0x5: REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); + /* Fall through */ case 0x3: if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) { REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); break; } + /* Fall through */ case 0x1: case 0x2: case 0x7: diff --git a/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c b/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c index 72203ba48..65cfad597 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c @@ -122,6 +122,7 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) aModeRefSel = 2; if (aModeRefSel) break; + /* Fall through */ case 1: default: aModeRefSel = 0; diff --git a/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c b/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c index 2244b775a..b66358b92 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c @@ -539,6 +539,7 @@ void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) case 0x5: REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); + /* Fall through */ case 0x3: case 0x1: case 0x2: diff --git a/src/drivers/net/igbvf/igbvf_vf.c b/src/drivers/net/igbvf/igbvf_vf.c index f2dac8be7..f841d5e3d 100644 --- a/src/drivers/net/igbvf/igbvf_vf.c +++ b/src/drivers/net/igbvf/igbvf_vf.c @@ -357,6 +357,7 @@ s32 igbvf_promisc_set_vf(struct e1000_hw *hw, enum e1000_promisc_type type) break; case e1000_promisc_enabled: msgbuf |= E1000_VF_SET_PROMISC_MULTICAST; + /* Fall through */ case e1000_promisc_unicast: msgbuf |= E1000_VF_SET_PROMISC_UNICAST; case e1000_promisc_disabled: diff --git a/src/drivers/net/tg3/tg3_hw.c b/src/drivers/net/tg3/tg3_hw.c index 50353cf36..798f8519f 100644 --- a/src/drivers/net/tg3/tg3_hw.c +++ b/src/drivers/net/tg3/tg3_hw.c @@ -2518,28 +2518,40 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) switch (limit) { case 16: tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0); + /* Fall through */ case 15: tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0); + /* Fall through */ case 14: tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0); + /* Fall through */ case 13: tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0); + /* Fall through */ case 12: tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0); + /* Fall through */ case 11: tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0); + /* Fall through */ case 10: tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0); + /* Fall through */ case 9: tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0); + /* Fall through */ case 8: tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0); + /* Fall through */ case 7: tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0); + /* Fall through */ case 6: tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0); + /* Fall through */ case 5: tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0); + /* Fall through */ case 4: /* tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); */ case 3: diff --git a/src/tests/setjmp_test.c b/src/tests/setjmp_test.c index 50ad13f3c..deafcee09 100644 --- a/src/tests/setjmp_test.c +++ b/src/tests/setjmp_test.c @@ -111,8 +111,9 @@ static void setjmp_return_ok ( struct setjmp_test *test, int value ) { * @v file Test code file * @v line Test code line */ -static void longjmp_okx ( struct setjmp_test *test, int value, - const char *file, unsigned int line ) { +static void __attribute__ (( noreturn )) +longjmp_okx ( struct setjmp_test *test, int value, + const char *file, unsigned int line ) { /* Record expected value. A zero passed to longjmp() should * result in setjmp() returning a value of one. From 2c056f02d09a19fb3ac91859c7bd8c558c8473cf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Mar 2017 10:58:17 +0300 Subject: [PATCH 442/591] [linux] Fix building with kernel 4.11 headers Signed-off-by: Michael Brown --- src/drivers/linux/af_packet.c | 1 + src/drivers/linux/tap.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/drivers/linux/af_packet.c b/src/drivers/linux/af_packet.c index 1622c8c0e..65aafc5b1 100644 --- a/src/drivers/linux/af_packet.c +++ b/src/drivers/linux/af_packet.c @@ -31,6 +31,7 @@ #include /* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ +#define _SYS_SOCKET_H #define __GLIBC__ 2 #include #include diff --git a/src/drivers/linux/tap.c b/src/drivers/linux/tap.c index 979436654..6fe76fd4d 100644 --- a/src/drivers/linux/tap.c +++ b/src/drivers/linux/tap.c @@ -31,6 +31,7 @@ #include /* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ +#define _SYS_SOCKET_H #define __GLIBC__ 2 #include #undef __GLIBC__ From a66ac07165855d8144f69a68d5c0ab0eb36e97e2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Mar 2017 12:29:44 +0300 Subject: [PATCH 443/591] [scsi] Retry TEST UNIT READY command The TEST UNIT READY command is issued automatically when the device is opened, and is not the result of a command being issued by the caller. This is required in order that a permanent TEST UNIT READY failure can be used to identify unusable paths in a multipath SAN device. Since the TEST UNIT READY command is not part of the caller's command issuing process, it is not covered by any external retry loops (such as the main retry loop in sandev_command()). We must therefore be prepared to retry the TEST UNIT READY command within the SCSI layer itself. We retry only the TEST UNIT READY command so as not to multiply the number of potential retries for normal commands (which are already retried by sandev_command()). Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 45 +++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index d51b5cfa5..f765c9762 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -40,6 +40,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** Maximum number of TEST UNIT READY retries */ +#define SCSI_READY_MAX_RETRIES 10 + /* Error numbers generated by SCSI sense data */ #define EIO_NO_SENSE __einfo_error ( EINFO_EIO_NO_SENSE ) #define EINFO_EIO_NO_SENSE \ @@ -240,6 +243,8 @@ struct scsi_device { struct interface ready; /** TEST UNIT READY process */ struct process process; + /** TEST UNIT READY retry count */ + unsigned int retries; /** List of commands */ struct list_head cmds; @@ -875,20 +880,40 @@ static struct interface_descriptor scsidev_block_desc = static void scsidev_ready ( struct scsi_device *scsidev, int rc ) { /* Shut down interface */ - intf_shutdown ( &scsidev->ready, rc ); + intf_restart ( &scsidev->ready, rc ); - /* Close device on failure */ - if ( rc != 0 ) { - DBGC ( scsidev, "SCSI %p not ready: %s\n", - scsidev, strerror ( rc ) ); - scsidev_close ( scsidev, rc ); + /* Mark device as ready, if applicable */ + if ( rc == 0 ) { + DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev ); + scsidev->flags |= SCSIDEV_UNIT_READY; + xfer_window_changed ( &scsidev->block ); + return; + } + DBGC ( scsidev, "SCSI %p not ready: %s\n", scsidev, strerror ( rc ) ); + + /* SCSI targets have an annoying habit of returning occasional + * pointless "error" messages such as "power-on occurred", so + * we have to be prepared to retry commands. + * + * For most commands, we rely on the caller (e.g. the generic + * SAN device layer) to retry commands as needed. However, a + * TEST UNIT READY failure is used as an indication that the + * whole SCSI device is unavailable and should be closed. We + * must therefore perform this retry loop within the SCSI + * layer. + */ + if ( scsidev->retries++ < SCSI_READY_MAX_RETRIES ) { + DBGC ( scsidev, "SCSI %p retrying (retry %d)\n", + scsidev, scsidev->retries ); + scsidev->flags &= ~SCSIDEV_UNIT_TESTED; + process_add ( &scsidev->process ); return; } - /* Mark device as ready */ - scsidev->flags |= SCSIDEV_UNIT_READY; - xfer_window_changed ( &scsidev->block ); - DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev ); + /* Close device */ + DBGC ( scsidev, "SCSI %p never became ready: %s\n", + scsidev, strerror ( rc ) ); + scsidev_close ( scsidev, rc ); } /** SCSI device TEST UNIT READY interface operations */ From fd6d1f4660a37d75acba1c64e2e5f137307bbc31 Mon Sep 17 00:00:00 2001 From: "Adamczyk, Konrad" Date: Thu, 30 Mar 2017 13:54:59 +0000 Subject: [PATCH 444/591] [thunderx] Use ThunderxConfigProtocol to obtain board configuration Following changes were introduced: - added GetBgxProp and GetLmacProp methods to ThunderxConfigProtocol - replaced direct BOARD_CFG access with usage of introduced methods - removed redundant BOARD_CFG - changed GUID of ThunderxConfigProtocol, as this is not compatible with previous version - changed UINTN* to UINT64* buffer type to fix issue on 32-bit platforms with MAC address This change allows us to avoid alignment of BOARD_CFG definitions every time it changes in UEFI. Signed-off-by: Konrad Adamczyk Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/thunderx.c | 24 +++--- src/drivers/net/thunderxcfg.h | 146 +++++++++++----------------------- 2 files changed, 60 insertions(+), 110 deletions(-) diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c index c9c246cb8..9ddb98ab8 100644 --- a/src/drivers/net/thunderx.c +++ b/src/drivers/net/thunderx.c @@ -1494,18 +1494,24 @@ static void txnic_bgx_init ( struct txnic_bgx *bgx, unsigned int type ) { */ static void txnic_bgx_mac ( struct txnic_lmac *lmac ) { struct txnic_bgx *bgx = lmac->bgx; - BOARD_CFG *boardcfg; - NODE_CFG *nodecfg; - BGX_CFG *bgxcfg; - LMAC_CFG *lmaccfg; + unsigned int lmac_idx = TXNIC_LMAC_IDX ( lmac->idx ); + uint64_t mac; + EFI_STATUS efirc; + int rc; /* Extract MAC from Board Configuration protocol, if available */ if ( txcfg ) { - boardcfg = txcfg->BoardConfig; - nodecfg = &boardcfg->Node[ bgx->node % MAX_NODES ]; - bgxcfg = &nodecfg->BgxCfg[ bgx->idx % BGX_PER_NODE_COUNT ]; - lmaccfg = &bgxcfg->Lmacs[ lmac->idx % LMAC_PER_BGX_COUNT ]; - lmac->mac.be64 = cpu_to_be64 ( lmaccfg->MacAddress ); + if ( ( efirc = txcfg->GetLmacProp ( txcfg, bgx->node, bgx->idx, + lmac_idx, MAC_ADDRESS, + sizeof ( mac ), + &mac ) ) == 0 ) { + lmac->mac.be64 = cpu_to_be64 ( mac ); + } else { + rc = -EEFI ( efirc ); + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d could not get " + "MAC address: %s\n", bgx->node, bgx->idx, + lmac->idx, strerror ( rc ) ); + } } else { DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no board " "configuration protocol\n", bgx->node, bgx->idx, diff --git a/src/drivers/net/thunderxcfg.h b/src/drivers/net/thunderxcfg.h index 235c54319..ffb34d36e 100644 --- a/src/drivers/net/thunderxcfg.h +++ b/src/drivers/net/thunderxcfg.h @@ -63,104 +63,6 @@ FILE_LICENCE ( BSD2 ); #define THUNDERX_CPU_ID(node, cluster, core) (((node) << 16) | ((cluster) << 8) | (core)) -//TODO: Put common type definitions in separate common include file -typedef enum { - BGX_MODE_SGMII, /* 1 lane, 1.250 Gbaud */ - BGX_MODE_XAUI, /* 4 lanes, 3.125 Gbaud */ - BGX_MODE_DXAUI, /* 4 lanes, 6.250 Gbaud */ - BGX_MODE_RXAUI, /* 2 lanes, 6.250 Gbaud */ - BGX_MODE_XFI, /* 1 lane, 10.3125 Gbaud */ - BGX_MODE_XLAUI, /* 4 lanes, 10.3125 Gbaud */ - BGX_MODE_10G_KR,/* 1 lane, 10.3125 Gbaud */ - BGX_MODE_40G_KR,/* 4 lanes, 10.3125 Gbaud */ - BGX_MODE_UNKNOWN -} BGX_MODE_T; - -typedef enum { - EBB8800, - EBB8804, - CRB_1S, - CRB_2S, - ASIANCAT, - GBT_MT60, - INVENTEC_P3E003, - BOARD_MAX -} BOARD_TYPE; - -typedef struct { - BOOLEAN Enabled; - UINT64 LaneToSds; - UINT64 MacAddress; -} LMAC_CFG; - -typedef struct { - BOOLEAN BgxEnabled; - BGX_MODE_T BgxMode; - UINTN LmacCount; //Maximum number of LMAcs - UINT64 BaseAddress; - UINT64 LmacType; - /* Bit mask of QLMs connected to this BGX */ - UINT64 QlmMask; - UINT64 QlmFreq; - BOOLEAN UseTraining; - LMAC_CFG Lmacs[LMAC_PER_BGX_COUNT]; -} BGX_CFG; - -typedef struct { - BOOLEAN PemUsable; -} PEM_CFG; - -typedef struct { - enum { NotPresent, Empty, Available } Status; - UINT8 Type; - UINT8 SubType; - UINT8 Rank; - UINT16 Mfg; - UINTN SizeMb; - UINTN Speed; - CHAR8 Serial[16]; - CHAR8 PartNo[24]; -} DIMM_CFG; - -typedef struct { - DIMM_CFG DimmCfg[DIMM_PER_LMC_COUNT]; -} LMC_CFG; - -typedef struct { - BOOLEAN Core[CORE_COUNT]; - BGX_CFG BgxCfg[BGX_PER_NODE_COUNT]; - PEM_CFG PemCfg[PEM_PER_NODE_COUNT]; - LMC_CFG LmcCfg[LMC_PER_NODE_COUNT]; - UINT64 RamStart; - UINT64 RamReserve; - UINT64 RamSize; - UINTN CPUSpeed; - UINTN CPUVersion; -} NODE_CFG; - -#define MAX_SERIAL 32 -#define MAX_REVISION 32 -typedef struct { - BOARD_TYPE BoardType; - CHAR8 Serial[MAX_SERIAL]; - CHAR8 Revision[MAX_REVISION]; - UINTN NumNodes; - UINTN BmcBootTwsiBus; - UINTN BmcBootTwsiAddr; - UINTN RtcTwsiBus; - UINTN RtcTwsiAddr; - /* IPMI support*/ - UINTN BmcIpmiTwsiBus; - UINTN BmcIpmiTwsiAddr; - NODE_CFG Node[MAX_NODES]; - UINT16 CpuClusterCount; - UINT16 CpuPerClusterCount; - UINT16 PcieSegmentCount; - UINT64 MacAddrRangeStart; - UINTN DdrSpeed; - UINT64 AcpiOemTableId; -} BOARD_CFG; - /****************************************************************************** * * From ThunderConfigProtocol.h @@ -183,13 +85,30 @@ typedef struct { */ #define EFI_THUNDER_CONFIG_PROTOCOL_GUID \ - {0xb75a0608, 0x99ff, 0x11e5, {0x9b, 0xeb, 0x00, 0x14, 0xd1, 0xfa, 0x23, 0x5c}} + {0xc12b1873, 0xac17, 0x4176, {0xac, 0x77, 0x7e, 0xcb, 0x4d, 0xef, 0xff, 0xec}} /// /// Forward declaration /// typedef struct _EFI_THUNDER_CONFIG_PROTOCOL EFI_THUNDER_CONFIG_PROTOCOL; +typedef enum { + BGX_ENABLED, + BGX_MODE, + LMAC_COUNT, + BASE_ADDRESS, + LMAC_TYPE_BGX, + QLM_MASK, + QLM_FREQ, + USE_TRAINING +} BGX_PROPERTY; + +typedef enum { + ENABLED, + LANE_TO_SDS, + MAC_ADDRESS +} LMAC_PROPERTY; + /// /// Function prototypes /// @@ -197,7 +116,30 @@ typedef EFI_STATUS (EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG)( IN EFI_THUNDER_CONFIG_PROTOCOL *This, - OUT BOARD_CFG** cfg + OUT VOID** cfg + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_BGX_PROP)( + IN EFI_THUNDER_CONFIG_PROTOCOL *This, + IN UINTN NodeId, + IN UINTN BgxId, + IN BGX_PROPERTY BgxProp, + IN UINT64 ValueSize, + OUT UINT64 *Value + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_LMAC_PROP)( + IN EFI_THUNDER_CONFIG_PROTOCOL *This, + IN UINTN NodeId, + IN UINTN BgxId, + IN UINTN LmacId, + IN LMAC_PROPERTY LmacProp, + IN UINT64 ValueSize, + OUT UINT64 *Value ); /// @@ -205,7 +147,9 @@ EFI_STATUS /// struct _EFI_THUNDER_CONFIG_PROTOCOL { EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG GetConfig; - BOARD_CFG* BoardConfig; + EFI_THUNDER_CONFIG_PROTOCOL_GET_BGX_PROP GetBgxProp; + EFI_THUNDER_CONFIG_PROTOCOL_GET_LMAC_PROP GetLmacProp; + VOID* BoardConfig; }; #endif /* _THUNDERXCFG_H */ From ffb5fe4ced74712393e9659c8271292f0698020f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Apr 2017 16:11:50 +0100 Subject: [PATCH 445/591] [libc] Add stdbool.h standard header Signed-off-by: Michael Brown --- src/include/curses.h | 2 +- src/include/stdbool.h | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/include/stdbool.h diff --git a/src/include/curses.h b/src/include/curses.h index 1f6fe029b..cf8cc53c9 100644 --- a/src/include/curses.h +++ b/src/include/curses.h @@ -2,6 +2,7 @@ #define CURSES_H #include +#include #include #include @@ -25,7 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef TRUE #define TRUE (1) -typedef int bool; typedef uint32_t chtype; typedef uint32_t attr_t; diff --git a/src/include/stdbool.h b/src/include/stdbool.h new file mode 100644 index 000000000..c49a7f192 --- /dev/null +++ b/src/include/stdbool.h @@ -0,0 +1,10 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define bool _Bool +#define true 1 +#define false 0 + +#endif /* _STDBOOL_H */ From f3788fa8376b449965c464ee0200f13e68045035 Mon Sep 17 00:00:00 2001 From: Martin Habets Date: Fri, 7 Apr 2017 10:46:15 +0100 Subject: [PATCH 446/591] [sfc] Add driver for Solarflare SFC8XXX adapters Signed-off-by: Martin Habets Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/Makefile | 1 + src/drivers/net/sfc/ef10_regs.h | 364 ++++ src/drivers/net/sfc/efx_bitfield.h | 555 +++++++ src/drivers/net/sfc/efx_common.c | 97 ++ src/drivers/net/sfc/efx_common.h | 232 +++ src/drivers/net/sfc/efx_hunt.c | 510 ++++++ src/drivers/net/sfc/efx_hunt.h | 75 + src/drivers/net/sfc/mc_driver_pcol.h | 2281 ++++++++++++++++++++++++++ src/drivers/net/sfc/mcdi.h | 164 ++ src/drivers/net/sfc/sfc_hunt.c | 1324 +++++++++++++++ src/include/ipxe/errfile.h | 2 + 11 files changed, 5605 insertions(+) create mode 100644 src/drivers/net/sfc/ef10_regs.h create mode 100644 src/drivers/net/sfc/efx_bitfield.h create mode 100644 src/drivers/net/sfc/efx_common.c create mode 100644 src/drivers/net/sfc/efx_common.h create mode 100644 src/drivers/net/sfc/efx_hunt.c create mode 100644 src/drivers/net/sfc/efx_hunt.h create mode 100644 src/drivers/net/sfc/mc_driver_pcol.h create mode 100644 src/drivers/net/sfc/mcdi.h create mode 100644 src/drivers/net/sfc/sfc_hunt.c diff --git a/src/Makefile b/src/Makefile index e6900d4b6..911d27ab5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,7 @@ SRCDIRS += drivers/net/ath/ath9k SRCDIRS += drivers/net/vxge SRCDIRS += drivers/net/efi SRCDIRS += drivers/net/tg3 +SRCDIRS += drivers/net/sfc SRCDIRS += drivers/block SRCDIRS += drivers/nvs SRCDIRS += drivers/bitbash diff --git a/src/drivers/net/sfc/ef10_regs.h b/src/drivers/net/sfc/ef10_regs.h new file mode 100644 index 000000000..0510e8fff --- /dev/null +++ b/src/drivers/net/sfc/ef10_regs.h @@ -0,0 +1,364 @@ +/**************************************************************************** + * + * Driver for Solarflare network controllers and boards + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ + +#ifndef EFX_EF10_REGS_H +#define EFX_EF10_REGS_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** \file ef10_regs.h + * EF10 hardware architecture definitions + * + * EF10 hardware architecture definitions have a name prefix following + * the format: + * + * E__ + * + * The following strings are used: + * + * MMIO register Host memory structure + * Address R + * Bitfield RF SF + * Enumerator FE SE + * + * is the first revision to which the definition applies: + * + * D: Huntington A0 + * + * If the definition has been changed or removed in later revisions + * then is the last revision to which the definition applies; + * otherwise it is "Z". + */ + +/************************************************************************** + * + * EF10 registers and descriptors + * + ************************************************************************** + */ + +/* BIU_HW_REV_ID_REG: */ +#define ER_DZ_BIU_HW_REV_ID 0x00000000 +#define ERF_DZ_HW_REV_ID_LBN 0 +#define ERF_DZ_HW_REV_ID_WIDTH 32 + +/* BIU_MC_SFT_STATUS_REG: */ +#define ER_DZ_BIU_MC_SFT_STATUS 0x00000010 +#define ER_DZ_BIU_MC_SFT_STATUS_STEP 4 +#define ER_DZ_BIU_MC_SFT_STATUS_ROWS 8 +#define ERF_DZ_MC_SFT_STATUS_LBN 0 +#define ERF_DZ_MC_SFT_STATUS_WIDTH 32 + +/* BIU_INT_ISR_REG: */ +#define ER_DZ_BIU_INT_ISR 0x00000090 +#define ERF_DZ_ISR_REG_LBN 0 +#define ERF_DZ_ISR_REG_WIDTH 32 + +/* MC_DB_LWRD_REG: */ +#define ER_DZ_MC_DB_LWRD 0x00000200 +#define ERF_DZ_MC_DOORBELL_L_LBN 0 +#define ERF_DZ_MC_DOORBELL_L_WIDTH 32 + +/* MC_DB_HWRD_REG: */ +#define ER_DZ_MC_DB_HWRD 0x00000204 +#define ERF_DZ_MC_DOORBELL_H_LBN 0 +#define ERF_DZ_MC_DOORBELL_H_WIDTH 32 + +/* EVQ_RPTR_REG: */ +#define ER_DZ_EVQ_RPTR 0x00000400 +#define ER_DZ_EVQ_RPTR_STEP 8192 +#define ER_DZ_EVQ_RPTR_ROWS 2048 +#define ERF_DZ_EVQ_RPTR_VLD_LBN 15 +#define ERF_DZ_EVQ_RPTR_VLD_WIDTH 1 +#define ERF_DZ_EVQ_RPTR_LBN 0 +#define ERF_DZ_EVQ_RPTR_WIDTH 15 + +/* EVQ_TMR_REG: */ +#define ER_DZ_EVQ_TMR 0x00000420 +#define ER_DZ_EVQ_TMR_STEP 8192 +#define ER_DZ_EVQ_TMR_ROWS 2048 +#define ERF_DZ_TC_TIMER_MODE_LBN 14 +#define ERF_DZ_TC_TIMER_MODE_WIDTH 2 +#define ERF_DZ_TC_TIMER_VAL_LBN 0 +#define ERF_DZ_TC_TIMER_VAL_WIDTH 14 + +/* RX_DESC_UPD_REG: */ +#define ER_DZ_RX_DESC_UPD 0x00000830 +#define ER_DZ_RX_DESC_UPD_STEP 8192 +#define ER_DZ_RX_DESC_UPD_ROWS 2048 +#define ERF_DZ_RX_DESC_WPTR_LBN 0 +#define ERF_DZ_RX_DESC_WPTR_WIDTH 12 + +/* TX_DESC_UPD_REG: */ +#define ER_DZ_TX_DESC_UPD 0x00000a10 +#define ER_DZ_TX_DESC_UPD_STEP 8192 +#define ER_DZ_TX_DESC_UPD_ROWS 2048 +#define ERF_DZ_RSVD_LBN 76 +#define ERF_DZ_RSVD_WIDTH 20 +#define ERF_DZ_TX_DESC_WPTR_LBN 64 +#define ERF_DZ_TX_DESC_WPTR_WIDTH 12 +#define ERF_DZ_TX_DESC_HWORD_LBN 32 +#define ERF_DZ_TX_DESC_HWORD_WIDTH 32 +#define ERF_DZ_TX_DESC_LWORD_LBN 0 +#define ERF_DZ_TX_DESC_LWORD_WIDTH 32 + +/* DRIVER_EV */ +#define ESF_DZ_DRV_CODE_LBN 60 +#define ESF_DZ_DRV_CODE_WIDTH 4 +#define ESF_DZ_DRV_SUB_CODE_LBN 56 +#define ESF_DZ_DRV_SUB_CODE_WIDTH 4 +#define ESE_DZ_DRV_TIMER_EV 3 +#define ESE_DZ_DRV_START_UP_EV 2 +#define ESE_DZ_DRV_WAKE_UP_EV 1 +#define ESF_DZ_DRV_SUB_DATA_LBN 0 +#define ESF_DZ_DRV_SUB_DATA_WIDTH 56 +#define ESF_DZ_DRV_EVQ_ID_LBN 0 +#define ESF_DZ_DRV_EVQ_ID_WIDTH 14 +#define ESF_DZ_DRV_TMR_ID_LBN 0 +#define ESF_DZ_DRV_TMR_ID_WIDTH 14 + +/* EVENT_ENTRY */ +#define ESF_DZ_EV_CODE_LBN 60 +#define ESF_DZ_EV_CODE_WIDTH 4 +#define ESE_DZ_EV_CODE_MCDI_EV 12 +#define ESE_DZ_EV_CODE_DRIVER_EV 5 +#define ESE_DZ_EV_CODE_TX_EV 2 +#define ESE_DZ_EV_CODE_RX_EV 0 +#define ESE_DZ_OTHER other +#define ESF_DZ_EV_DATA_LBN 0 +#define ESF_DZ_EV_DATA_WIDTH 60 + +/* MC_EVENT */ +#define ESF_DZ_MC_CODE_LBN 60 +#define ESF_DZ_MC_CODE_WIDTH 4 +#define ESF_DZ_MC_OVERRIDE_HOLDOFF_LBN 59 +#define ESF_DZ_MC_OVERRIDE_HOLDOFF_WIDTH 1 +#define ESF_DZ_MC_DROP_EVENT_LBN 58 +#define ESF_DZ_MC_DROP_EVENT_WIDTH 1 +#define ESF_DZ_MC_SOFT_LBN 0 +#define ESF_DZ_MC_SOFT_WIDTH 58 + +/* RX_EVENT */ +#define ESF_DZ_RX_CODE_LBN 60 +#define ESF_DZ_RX_CODE_WIDTH 4 +#define ESF_DZ_RX_OVERRIDE_HOLDOFF_LBN 59 +#define ESF_DZ_RX_OVERRIDE_HOLDOFF_WIDTH 1 +#define ESF_DZ_RX_DROP_EVENT_LBN 58 +#define ESF_DZ_RX_DROP_EVENT_WIDTH 1 +#define ESF_DZ_RX_EV_RSVD2_LBN 54 +#define ESF_DZ_RX_EV_RSVD2_WIDTH 4 +#define ESF_DZ_RX_EV_SOFT2_LBN 52 +#define ESF_DZ_RX_EV_SOFT2_WIDTH 2 +#define ESF_DZ_RX_DSC_PTR_LBITS_LBN 48 +#define ESF_DZ_RX_DSC_PTR_LBITS_WIDTH 4 +#define ESF_DZ_RX_L4_CLASS_LBN 45 +#define ESF_DZ_RX_L4_CLASS_WIDTH 3 +#define ESE_DZ_L4_CLASS_RSVD7 7 +#define ESE_DZ_L4_CLASS_RSVD6 6 +#define ESE_DZ_L4_CLASS_RSVD5 5 +#define ESE_DZ_L4_CLASS_RSVD4 4 +#define ESE_DZ_L4_CLASS_RSVD3 3 +#define ESE_DZ_L4_CLASS_UDP 2 +#define ESE_DZ_L4_CLASS_TCP 1 +#define ESE_DZ_L4_CLASS_UNKNOWN 0 +#define ESF_DZ_RX_L3_CLASS_LBN 42 +#define ESF_DZ_RX_L3_CLASS_WIDTH 3 +#define ESE_DZ_L3_CLASS_RSVD7 7 +#define ESE_DZ_L3_CLASS_IP6_FRAG 6 +#define ESE_DZ_L3_CLASS_ARP 5 +#define ESE_DZ_L3_CLASS_IP4_FRAG 4 +#define ESE_DZ_L3_CLASS_FCOE 3 +#define ESE_DZ_L3_CLASS_IP6 2 +#define ESE_DZ_L3_CLASS_IP4 1 +#define ESE_DZ_L3_CLASS_UNKNOWN 0 +#define ESF_DZ_RX_ETH_TAG_CLASS_LBN 39 +#define ESF_DZ_RX_ETH_TAG_CLASS_WIDTH 3 +#define ESE_DZ_ETH_TAG_CLASS_RSVD7 7 +#define ESE_DZ_ETH_TAG_CLASS_RSVD6 6 +#define ESE_DZ_ETH_TAG_CLASS_RSVD5 5 +#define ESE_DZ_ETH_TAG_CLASS_RSVD4 4 +#define ESE_DZ_ETH_TAG_CLASS_RSVD3 3 +#define ESE_DZ_ETH_TAG_CLASS_VLAN2 2 +#define ESE_DZ_ETH_TAG_CLASS_VLAN1 1 +#define ESE_DZ_ETH_TAG_CLASS_NONE 0 +#define ESF_DZ_RX_ETH_BASE_CLASS_LBN 36 +#define ESF_DZ_RX_ETH_BASE_CLASS_WIDTH 3 +#define ESE_DZ_ETH_BASE_CLASS_LLC_SNAP 2 +#define ESE_DZ_ETH_BASE_CLASS_LLC 1 +#define ESE_DZ_ETH_BASE_CLASS_ETH2 0 +#define ESF_DZ_RX_MAC_CLASS_LBN 35 +#define ESF_DZ_RX_MAC_CLASS_WIDTH 1 +#define ESE_DZ_MAC_CLASS_MCAST 1 +#define ESE_DZ_MAC_CLASS_UCAST 0 +#define ESF_DZ_RX_EV_SOFT1_LBN 32 +#define ESF_DZ_RX_EV_SOFT1_WIDTH 3 +#define ESF_DZ_RX_EV_RSVD1_LBN 31 +#define ESF_DZ_RX_EV_RSVD1_WIDTH 1 +#define ESF_DZ_RX_ABORT_LBN 30 +#define ESF_DZ_RX_ABORT_WIDTH 1 +#define ESF_DZ_RX_ECC_ERR_LBN 29 +#define ESF_DZ_RX_ECC_ERR_WIDTH 1 +#define ESF_DZ_RX_CRC1_ERR_LBN 28 +#define ESF_DZ_RX_CRC1_ERR_WIDTH 1 +#define ESF_DZ_RX_CRC0_ERR_LBN 27 +#define ESF_DZ_RX_CRC0_ERR_WIDTH 1 +#define ESF_DZ_RX_TCPUDP_CKSUM_ERR_LBN 26 +#define ESF_DZ_RX_TCPUDP_CKSUM_ERR_WIDTH 1 +#define ESF_DZ_RX_IPCKSUM_ERR_LBN 25 +#define ESF_DZ_RX_IPCKSUM_ERR_WIDTH 1 +#define ESF_DZ_RX_ECRC_ERR_LBN 24 +#define ESF_DZ_RX_ECRC_ERR_WIDTH 1 +#define ESF_DZ_RX_QLABEL_LBN 16 +#define ESF_DZ_RX_QLABEL_WIDTH 5 +#define ESF_DZ_RX_PARSE_INCOMPLETE_LBN 15 +#define ESF_DZ_RX_PARSE_INCOMPLETE_WIDTH 1 +#define ESF_DZ_RX_CONT_LBN 14 +#define ESF_DZ_RX_CONT_WIDTH 1 +#define ESF_DZ_RX_BYTES_LBN 0 +#define ESF_DZ_RX_BYTES_WIDTH 14 + +/* RX_KER_DESC */ +#define ESF_DZ_RX_KER_RESERVED_LBN 62 +#define ESF_DZ_RX_KER_RESERVED_WIDTH 2 +#define ESF_DZ_RX_KER_BYTE_CNT_LBN 48 +#define ESF_DZ_RX_KER_BYTE_CNT_WIDTH 14 +#define ESF_DZ_RX_KER_BUF_ADDR_LBN 0 +#define ESF_DZ_RX_KER_BUF_ADDR_WIDTH 48 + +/* TX_CSUM_TSTAMP_DESC */ +#define ESF_DZ_TX_DESC_IS_OPT_LBN 63 +#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1 +#define ESF_DZ_TX_OPTION_TYPE_LBN 60 +#define ESF_DZ_TX_OPTION_TYPE_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_TSO 7 +#define ESE_DZ_TX_OPTION_DESC_VLAN 6 +#define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TIMESTAMP_LBN 5 +#define ESF_DZ_TX_TIMESTAMP_WIDTH 1 +#define ESF_DZ_TX_OPTION_CRC_MODE_LBN 2 +#define ESF_DZ_TX_OPTION_CRC_MODE_WIDTH 3 +#define ESE_DZ_TX_OPTION_CRC_FCOIP_MPA 5 +#define ESE_DZ_TX_OPTION_CRC_FCOIP_FCOE 4 +#define ESE_DZ_TX_OPTION_CRC_ISCSI_HDR_AND_PYLD 3 +#define ESE_DZ_TX_OPTION_CRC_ISCSI_HDR 2 +#define ESE_DZ_TX_OPTION_CRC_FCOE 1 +#define ESE_DZ_TX_OPTION_CRC_OFF 0 +#define ESF_DZ_TX_OPTION_UDP_TCP_CSUM_LBN 1 +#define ESF_DZ_TX_OPTION_UDP_TCP_CSUM_WIDTH 1 +#define ESF_DZ_TX_OPTION_IP_CSUM_LBN 0 +#define ESF_DZ_TX_OPTION_IP_CSUM_WIDTH 1 + +/* TX_EVENT */ +#define ESF_DZ_TX_CODE_LBN 60 +#define ESF_DZ_TX_CODE_WIDTH 4 +#define ESF_DZ_TX_OVERRIDE_HOLDOFF_LBN 59 +#define ESF_DZ_TX_OVERRIDE_HOLDOFF_WIDTH 1 +#define ESF_DZ_TX_DROP_EVENT_LBN 58 +#define ESF_DZ_TX_DROP_EVENT_WIDTH 1 +#define ESF_DZ_TX_EV_RSVD_LBN 48 +#define ESF_DZ_TX_EV_RSVD_WIDTH 10 +#define ESF_DZ_TX_SOFT2_LBN 32 +#define ESF_DZ_TX_SOFT2_WIDTH 16 +#define ESF_DZ_TX_CAN_MERGE_LBN 31 +#define ESF_DZ_TX_CAN_MERGE_WIDTH 1 +#define ESF_DZ_TX_SOFT1_LBN 24 +#define ESF_DZ_TX_SOFT1_WIDTH 7 +#define ESF_DZ_TX_QLABEL_LBN 16 +#define ESF_DZ_TX_QLABEL_WIDTH 5 +#define ESF_DZ_TX_DESCR_INDX_LBN 0 +#define ESF_DZ_TX_DESCR_INDX_WIDTH 16 + +/* TX_KER_DESC */ +#define ESF_DZ_TX_KER_TYPE_LBN 63 +#define ESF_DZ_TX_KER_TYPE_WIDTH 1 +#define ESF_DZ_TX_KER_CONT_LBN 62 +#define ESF_DZ_TX_KER_CONT_WIDTH 1 +#define ESF_DZ_TX_KER_BYTE_CNT_LBN 48 +#define ESF_DZ_TX_KER_BYTE_CNT_WIDTH 14 +#define ESF_DZ_TX_KER_BUF_ADDR_LBN 0 +#define ESF_DZ_TX_KER_BUF_ADDR_WIDTH 48 + +/* TX_PIO_DESC */ +#define ESF_DZ_TX_PIO_TYPE_LBN 63 +#define ESF_DZ_TX_PIO_TYPE_WIDTH 1 +#define ESF_DZ_TX_PIO_OPT_LBN 60 +#define ESF_DZ_TX_PIO_OPT_WIDTH 3 +#define ESF_DZ_TX_PIO_CONT_LBN 59 +#define ESF_DZ_TX_PIO_CONT_WIDTH 1 +#define ESF_DZ_TX_PIO_BYTE_CNT_LBN 32 +#define ESF_DZ_TX_PIO_BYTE_CNT_WIDTH 12 +#define ESF_DZ_TX_PIO_BUF_ADDR_LBN 0 +#define ESF_DZ_TX_PIO_BUF_ADDR_WIDTH 12 + +/* TX_TSO_DESC */ +#define ESF_DZ_TX_DESC_IS_OPT_LBN 63 +#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1 +#define ESF_DZ_TX_OPTION_TYPE_LBN 60 +#define ESF_DZ_TX_OPTION_TYPE_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_TSO 7 +#define ESE_DZ_TX_OPTION_DESC_VLAN 6 +#define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TSO_TCP_FLAGS_LBN 48 +#define ESF_DZ_TX_TSO_TCP_FLAGS_WIDTH 8 +#define ESF_DZ_TX_TSO_IP_ID_LBN 32 +#define ESF_DZ_TX_TSO_IP_ID_WIDTH 16 +#define ESF_DZ_TX_TSO_TCP_SEQNO_LBN 0 +#define ESF_DZ_TX_TSO_TCP_SEQNO_WIDTH 32 + +/*************************************************************************/ + +/* TX_DESC_UPD_REG: Transmit descriptor update register. + * We may write just one dword of these registers. + */ +#define ER_DZ_TX_DESC_UPD_DWORD (ER_DZ_TX_DESC_UPD + 2 * 4) +#define ERF_DZ_TX_DESC_WPTR_DWORD_LBN (ERF_DZ_TX_DESC_WPTR_LBN - 2 * 32) +#define ERF_DZ_TX_DESC_WPTR_DWORD_WIDTH ERF_DZ_TX_DESC_WPTR_WIDTH + +/* The workaround for bug 35388 requires multiplexing writes through + * the TX_DESC_UPD_DWORD address. + * TX_DESC_UPD: 0ppppppppppp (bit 11 lost) + * EVQ_RPTR: 1000hhhhhhhh, 1001llllllll (split into high and low bits) + * EVQ_TMR: 11mmvvvvvvvv (bits 8:13 of value lost) + */ +#define ER_DD_EVQ_INDIRECT ER_DZ_TX_DESC_UPD_DWORD +#define ERF_DD_EVQ_IND_RPTR_FLAGS_LBN 8 +#define ERF_DD_EVQ_IND_RPTR_FLAGS_WIDTH 4 +#define EFE_DD_EVQ_IND_RPTR_FLAGS_HIGH 8 +#define EFE_DD_EVQ_IND_RPTR_FLAGS_LOW 9 +#define ERF_DD_EVQ_IND_RPTR_LBN 0 +#define ERF_DD_EVQ_IND_RPTR_WIDTH 8 +#define ERF_DD_EVQ_IND_TIMER_FLAGS_LBN 10 +#define ERF_DD_EVQ_IND_TIMER_FLAGS_WIDTH 2 +#define EFE_DD_EVQ_IND_TIMER_FLAGS 3 +#define ERF_DD_EVQ_IND_TIMER_MODE_LBN 8 +#define ERF_DD_EVQ_IND_TIMER_MODE_WIDTH 2 +#define ERF_DD_EVQ_IND_TIMER_VAL_LBN 0 +#define ERF_DD_EVQ_IND_TIMER_VAL_WIDTH 8 + +/* TX_PIOBUF + * PIO buffer aperture (paged) + */ +#define ER_DZ_TX_PIOBUF 4096 +#define ER_DZ_TX_PIOBUF_SIZE 2048 + +/* RX packet prefix */ +#define ES_DZ_RX_PREFIX_HASH_OFST 0 +#define ES_DZ_RX_PREFIX_VLAN1_OFST 4 +#define ES_DZ_RX_PREFIX_VLAN2_OFST 6 +#define ES_DZ_RX_PREFIX_PKTLEN_OFST 8 +#define ES_DZ_RX_PREFIX_TSTAMP_OFST 10 +#define ES_DZ_RX_PREFIX_SIZE 14 + +#endif /* EFX_EF10_REGS_H */ diff --git a/src/drivers/net/sfc/efx_bitfield.h b/src/drivers/net/sfc/efx_bitfield.h new file mode 100644 index 000000000..f1e9b932b --- /dev/null +++ b/src/drivers/net/sfc/efx_bitfield.h @@ -0,0 +1,555 @@ +/**************************************************************************** + * + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ + +#ifndef EFX_BITFIELD_H +#define EFX_BITFIELD_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** \file efx_bitfield.h + * Efx bitfield access + * + * Efx NICs make extensive use of bitfields up to 128 bits + * wide. Since there is no native 128-bit datatype on most systems, + * and since 64-bit datatypes are inefficient on 32-bit systems and + * vice versa, we wrap accesses in a way that uses the most efficient + * datatype. + * + * The NICs are PCI devices and therefore little-endian. Since most + * of the quantities that we deal with are DMAed to/from host memory, + * we define our datatypes (efx_oword_t, efx_qword_t and + * efx_dword_t) to be little-endian. + */ + +/* Lowest bit numbers and widths */ +#define EFX_DUMMY_FIELD_LBN 0 +#define EFX_DUMMY_FIELD_WIDTH 0 +#define EFX_WORD_0_LBN 0 +#define EFX_WORD_0_WIDTH 16 +#define EFX_WORD_1_LBN 16 +#define EFX_WORD_1_WIDTH 16 +#define EFX_DWORD_0_LBN 0 +#define EFX_DWORD_0_WIDTH 32 +#define EFX_DWORD_1_LBN 32 +#define EFX_DWORD_1_WIDTH 32 +#define EFX_DWORD_2_LBN 64 +#define EFX_DWORD_2_WIDTH 32 +#define EFX_DWORD_3_LBN 96 +#define EFX_DWORD_3_WIDTH 32 +#define EFX_QWORD_0_LBN 0 +#define EFX_QWORD_0_WIDTH 64 + +/* Specified attribute (e.g. LBN) of the specified field */ +#define EFX_VAL(field, attribute) field ## _ ## attribute +/* Low bit number of the specified field */ +#define EFX_LOW_BIT(field) EFX_VAL(field, LBN) +/* Bit width of the specified field */ +#define EFX_WIDTH(field) EFX_VAL(field, WIDTH) +/* High bit number of the specified field */ +#define EFX_HIGH_BIT(field) (EFX_LOW_BIT(field) + EFX_WIDTH(field) - 1) +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 64 bits. + */ +#define EFX_MASK64(width) \ + ((width) == 64 ? ~((u64) 0) : \ + (((((u64) 1) << (width))) - 1)) + +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 32 bits. Use + * EFX_MASK64 for higher width fields. + */ +#define EFX_MASK32(width) \ + ((width) == 32 ? ~((u32) 0) : \ + (((((u32) 1) << (width))) - 1)) + +/** A doubleword (4 byte) datatype - little-endian in HW */ +typedef union efx_dword { + __le32 u32[1]; +} efx_dword_t; + +/** A quadword (8 byte) datatype - little-endian in HW */ +typedef union efx_qword { + __le64 u64[1]; + __le32 u32[2]; + efx_dword_t dword[2]; +} efx_qword_t; + +/** An octword (eight-word, so 16 byte) datatype - little-endian in HW */ +typedef union efx_oword { + __le64 u64[2]; + efx_qword_t qword[2]; + __le32 u32[4]; + efx_dword_t dword[4]; +} efx_oword_t; + +/* Format string and value expanders for printk */ +#define EFX_DWORD_FMT "%08x" +#define EFX_QWORD_FMT "%08x:%08x" +#define EFX_OWORD_FMT "%08x:%08x:%08x:%08x" +#define EFX_DWORD_VAL(dword) \ + ((unsigned int) le32_to_cpu((dword).u32[0])) +#define EFX_QWORD_VAL(qword) \ + ((unsigned int) le32_to_cpu((qword).u32[1])), \ + ((unsigned int) le32_to_cpu((qword).u32[0])) +#define EFX_OWORD_VAL(oword) \ + ((unsigned int) le32_to_cpu((oword).u32[3])), \ + ((unsigned int) le32_to_cpu((oword).u32[2])), \ + ((unsigned int) le32_to_cpu((oword).u32[1])), \ + ((unsigned int) le32_to_cpu((oword).u32[0])) + +/* + * Extract bit field portion [low,high) from the native-endian element + * which contains bits [min,max). + * + * For example, suppose "element" represents the high 32 bits of a + * 64-bit value, and we wish to extract the bits belonging to the bit + * field occupying bits 28-45 of this 64-bit value. + * + * Then EFX_EXTRACT ( element, 32, 63, 28, 45 ) would give + * + * ( element ) << 4 + * + * The result will contain the relevant bits filled in in the range + * [0,high-low), with garbage in bits [high-low+1,...). + */ +#define EFX_EXTRACT_NATIVE(native_element, min, max, low, high) \ + ((low) > (max) || (high) < (min) ? 0 : \ + (low) > (min) ? \ + (native_element) >> ((low) - (min)) : \ + (native_element) << ((min) - (low))) + +/* + * Extract bit field portion [low,high) from the 64-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT64(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high) + +/* + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT32(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high) + +#define EFX_EXTRACT_OWORD64(oword, low, high) \ + ((EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ + EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) & \ + EFX_MASK64((high) + 1 - (low))) + +#define EFX_EXTRACT_QWORD64(qword, low, high) \ + (EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) & \ + EFX_MASK64((high) + 1 - (low))) + +#define EFX_EXTRACT_OWORD32(oword, low, high) \ + ((EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ + EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ + EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) & \ + EFX_MASK32((high) + 1 - (low))) + +#define EFX_EXTRACT_QWORD32(qword, low, high) \ + ((EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) & \ + EFX_MASK32((high) + 1 - (low))) + +#define EFX_EXTRACT_DWORD(dword, low, high) \ + (EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) & \ + EFX_MASK32((high) + 1 - (low))) + +#define EFX_OWORD_FIELD64(oword, field) \ + EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_QWORD_FIELD64(qword, field) \ + EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_OWORD_FIELD32(oword, field) \ + EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_QWORD_FIELD32(qword, field) \ + EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_DWORD_FIELD(dword, field) \ + EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_OWORD_IS_ZERO64(oword) \ + (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) + +#define EFX_QWORD_IS_ZERO64(qword) \ + (((qword).u64[0]) == (__force __le64) 0) + +#define EFX_OWORD_IS_ZERO32(oword) \ + (((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \ + == (__force __le32) 0) + +#define EFX_QWORD_IS_ZERO32(qword) \ + (((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0) + +#define EFX_DWORD_IS_ZERO(dword) \ + (((dword).u32[0]) == (__force __le32) 0) + +#define EFX_OWORD_IS_ALL_ONES64(oword) \ + (((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0)) + +#define EFX_QWORD_IS_ALL_ONES64(qword) \ + ((qword).u64[0] == ~((__force __le64) 0)) + +#define EFX_OWORD_IS_ALL_ONES32(oword) \ + (((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \ + == ~((__force __le32) 0)) + +#define EFX_QWORD_IS_ALL_ONES32(qword) \ + (((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0)) + +#define EFX_DWORD_IS_ALL_ONES(dword) \ + ((dword).u32[0] == ~((__force __le32) 0)) + +#if BITS_PER_LONG == 64 +#define EFX_OWORD_FIELD EFX_OWORD_FIELD64 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD64 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO64 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO64 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES64 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES64 +#else +#define EFX_OWORD_FIELD EFX_OWORD_FIELD32 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD32 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO32 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO32 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES32 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES32 +#endif + +/* + * Construct bit field portion + * + * Creates the portion of the bit field [low,high) that lies within + * the range [min,max). + */ +#define EFX_INSERT_NATIVE64(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u64) (value)) << (low - min)) : \ + (((u64) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE32(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u32) (value)) << (low - min)) : \ + (((u32) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE(min, max, low, high, value) \ + ((((max - min) >= 32) || ((high - low) >= 32)) ? \ + EFX_INSERT_NATIVE64(min, max, low, high, value) : \ + EFX_INSERT_NATIVE32(min, max, low, high, value)) + +/* + * Construct bit field portion + * + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define EFX_INSERT_FIELD_NATIVE(min, max, field, value) \ + EFX_INSERT_NATIVE(min, max, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +/* + * Construct bit field + * + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define EFX_INSERT_FIELDS_NATIVE(min, max, \ + field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7, \ + field8, value8, \ + field9, value9, \ + field10, value10) \ + (EFX_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field10, (value10))) + +#define EFX_INSERT_FIELDS64(...) \ + cpu_to_le64(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_INSERT_FIELDS32(...) \ + cpu_to_le32(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_POPULATE_OWORD64(oword, ...) do { \ + (oword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + (oword).u64[1] = EFX_INSERT_FIELDS64(64, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD64(qword, ...) do { \ + (qword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_OWORD32(oword, ...) do { \ + (oword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (oword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + (oword).u32[2] = EFX_INSERT_FIELDS32(64, 95, __VA_ARGS__); \ + (oword).u32[3] = EFX_INSERT_FIELDS32(96, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD32(qword, ...) do { \ + (qword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (qword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_DWORD(dword, ...) do { \ + (dword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + } while (0) + +#if BITS_PER_LONG == 64 +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD64 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD64 +#else +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD32 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD32 +#endif + +/* Populate an octword field with various numbers of arguments */ +#define EFX_POPULATE_OWORD_10 EFX_POPULATE_OWORD +#define EFX_POPULATE_OWORD_9(oword, ...) \ + EFX_POPULATE_OWORD_10(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_8(oword, ...) \ + EFX_POPULATE_OWORD_9(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_7(oword, ...) \ + EFX_POPULATE_OWORD_8(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_6(oword, ...) \ + EFX_POPULATE_OWORD_7(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_5(oword, ...) \ + EFX_POPULATE_OWORD_6(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_4(oword, ...) \ + EFX_POPULATE_OWORD_5(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_3(oword, ...) \ + EFX_POPULATE_OWORD_4(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_2(oword, ...) \ + EFX_POPULATE_OWORD_3(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_1(oword, ...) \ + EFX_POPULATE_OWORD_2(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_OWORD(oword) \ + EFX_POPULATE_OWORD_1(oword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_OWORD(oword) \ + EFX_POPULATE_OWORD_4(oword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff, \ + EFX_DWORD_2, 0xffffffff, \ + EFX_DWORD_3, 0xffffffff) + +/* Populate a quadword field with various numbers of arguments */ +#define EFX_POPULATE_QWORD_10 EFX_POPULATE_QWORD +#define EFX_POPULATE_QWORD_9(qword, ...) \ + EFX_POPULATE_QWORD_10(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_8(qword, ...) \ + EFX_POPULATE_QWORD_9(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_7(qword, ...) \ + EFX_POPULATE_QWORD_8(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_6(qword, ...) \ + EFX_POPULATE_QWORD_7(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_5(qword, ...) \ + EFX_POPULATE_QWORD_6(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_4(qword, ...) \ + EFX_POPULATE_QWORD_5(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_3(qword, ...) \ + EFX_POPULATE_QWORD_4(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_2(qword, ...) \ + EFX_POPULATE_QWORD_3(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_1(qword, ...) \ + EFX_POPULATE_QWORD_2(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_QWORD(qword) \ + EFX_POPULATE_QWORD_1(qword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_QWORD(qword) \ + EFX_POPULATE_QWORD_2(qword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff) + +/* Populate a dword field with various numbers of arguments */ +#define EFX_POPULATE_DWORD_10 EFX_POPULATE_DWORD +#define EFX_POPULATE_DWORD_9(dword, ...) \ + EFX_POPULATE_DWORD_10(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_8(dword, ...) \ + EFX_POPULATE_DWORD_9(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_7(dword, ...) \ + EFX_POPULATE_DWORD_8(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_6(dword, ...) \ + EFX_POPULATE_DWORD_7(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_5(dword, ...) \ + EFX_POPULATE_DWORD_6(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_4(dword, ...) \ + EFX_POPULATE_DWORD_5(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_3(dword, ...) \ + EFX_POPULATE_DWORD_4(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_2(dword, ...) \ + EFX_POPULATE_DWORD_3(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_1(dword, ...) \ + EFX_POPULATE_DWORD_2(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xffffffff) + +/* + * Modify a named field within an already-populated structure. Used + * for read-modify-write operations. + * + */ +#define EFX_INVERT_OWORD(oword) do { \ + (oword).u64[0] = ~((oword).u64[0]); \ + (oword).u64[1] = ~((oword).u64[1]); \ + } while (0) + +#define EFX_AND_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] & (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] & (mask).u64[1]; \ + } while (0) + +#define EFX_AND_QWORD(qword, from, mask) \ + (qword).u64[0] = (from).u64[0] & (mask).u64[0] + +#define EFX_OR_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] | (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] | (mask).u64[1]; \ + } while (0) + +#define EFX_INSERT64(min, max, low, high, value) \ + cpu_to_le64(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INSERT32(min, max, low, high, value) \ + cpu_to_le32(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INPLACE_MASK64(min, max, low, high) \ + EFX_INSERT64(min, max, low, high, EFX_MASK64((high) + 1 - (low))) + +#define EFX_INPLACE_MASK32(min, max, low, high) \ + EFX_INSERT32(min, max, low, high, EFX_MASK32((high) + 1 - (low))) + +#define EFX_SET_OWORD64(oword, low, high, value) do { \ + (oword).u64[0] = (((oword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ + (oword).u64[1] = (((oword).u64[1] \ + & ~EFX_INPLACE_MASK64(64, 127, low, high)) \ + | EFX_INSERT64(64, 127, low, high, value)); \ + } while (0) + +#define EFX_SET_QWORD64(qword, low, high, value) do { \ + (qword).u64[0] = (((qword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD32(oword, low, high, value) do { \ + (oword).u32[0] = (((oword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + (oword).u32[1] = (((oword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + (oword).u32[2] = (((oword).u32[2] \ + & ~EFX_INPLACE_MASK32(64, 95, low, high)) \ + | EFX_INSERT32(64, 95, low, high, value)); \ + (oword).u32[3] = (((oword).u32[3] \ + & ~EFX_INPLACE_MASK32(96, 127, low, high)) \ + | EFX_INSERT32(96, 127, low, high, value)); \ + } while (0) + +#define EFX_SET_QWORD32(qword, low, high, value) do { \ + (qword).u32[0] = (((qword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + (qword).u32[1] = (((qword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_DWORD32(dword, low, high, value) do { \ + (dword).u32[0] = (((dword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD_FIELD64(oword, field, value) \ + EFX_SET_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD64(qword, field, value) \ + EFX_SET_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_OWORD_FIELD32(oword, field, value) \ + EFX_SET_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD32(qword, field, value) \ + EFX_SET_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_DWORD_FIELD(dword, field, value) \ + EFX_SET_DWORD32(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + + + +#if BITS_PER_LONG == 64 +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64 +#else +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD32 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32 +#endif + +/* Used to avoid compiler warnings about shift range exceeding width + * of the data types when dma_addr_t is only 32 bits wide. + */ +#define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t)) +#define EFX_DMA_TYPE_WIDTH(width) \ + (((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH) + + +/* Static initialiser */ +#define EFX_OWORD32(a, b, c, d) \ + { .u32 = { cpu_to_le32(a), cpu_to_le32(b), \ + cpu_to_le32(c), cpu_to_le32(d) } } + +#endif /* EFX_BITFIELD_H */ diff --git a/src/drivers/net/sfc/efx_common.c b/src/drivers/net/sfc/efx_common.c new file mode 100644 index 000000000..79a994355 --- /dev/null +++ b/src/drivers/net/sfc/efx_common.c @@ -0,0 +1,97 @@ +/************************************************************************** + * + * Driver datapath common code for Solarflare network cards + * + * Written by Shradha Shah + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * Copyright 2006-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efx_common.h" +#include "efx_bitfield.h" +#include "mc_driver_pcol.h" + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/******************************************************************************* + * + * + * Low-level hardware access + * + * + ******************************************************************************/ + +void +efx_writel(struct efx_nic *efx, efx_dword_t *value, unsigned int reg) +{ + DBGCIO(efx, "Writing partial register %x with " EFX_DWORD_FMT "\n", + reg, EFX_DWORD_VAL(*value)); + _efx_writel(efx, value->u32[0], reg); +} + +void +efx_readl(struct efx_nic *efx, efx_dword_t *value, unsigned int reg) +{ + value->u32[0] = _efx_readl(efx, reg); + DBGCIO(efx, "Read from register %x, got " EFX_DWORD_FMT "\n", + reg, EFX_DWORD_VAL(*value)); +} + +/******************************************************************************* + * + * + * Inititialization and Close + * + * + ******************************************************************************/ +void efx_probe(struct net_device *netdev, enum efx_revision revision) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct pci_device *pci = container_of(netdev->dev, + struct pci_device, dev); + + efx->netdev = netdev; + efx->revision = revision; + + /* MMIO bar */ + efx->mmio_start = pci_bar_start(pci, PCI_BASE_ADDRESS_2); + efx->mmio_len = pci_bar_size(pci, PCI_BASE_ADDRESS_2); + efx->membase = ioremap(efx->mmio_start, efx->mmio_len); + + DBGCP(efx, "BAR of %lx bytes at phys %lx mapped at %p\n", + efx->mmio_len, efx->mmio_start, efx->membase); + + /* Enable PCI access */ + adjust_pci_device(pci); +} + +void efx_remove(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + + iounmap(efx->membase); + efx->membase = NULL; +} diff --git a/src/drivers/net/sfc/efx_common.h b/src/drivers/net/sfc/efx_common.h new file mode 100644 index 000000000..3487966ce --- /dev/null +++ b/src/drivers/net/sfc/efx_common.h @@ -0,0 +1,232 @@ +/************************************************************************** + * + * GPL common net driver for Solarflare network cards + * + * Written by Michael Brown + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * Copyright Solarflare Communications Inc. 2013-2017 + * + * 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. + * + * 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. + * + ***************************************************************************/ +#ifndef EFX_COMMON_H +#define EFX_COMMON_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define __packed __attribute__((__packed__)) +#define __force /*nothing*/ + +typedef uint16_t __le16; +typedef uint32_t __le32; +typedef uint64_t __le64; + +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct{int: -!!(e); })) +#define BUILD_BUG_ON(e) ((void)BUILD_BUG_ON_ZERO(e)) + +#include +#include +#include +#include "efx_bitfield.h" +#include "mcdi.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/************************************************************************** + * + * Hardware data structures and sizing + * + ***************************************************************************/ +typedef efx_qword_t efx_rx_desc_t; +typedef efx_qword_t efx_tx_desc_t; +typedef efx_qword_t efx_event_t; + +#define EFX_BUF_ALIGN 4096 +#define EFX_RXD_SIZE 512 +#define EFX_RXD_MASK (EFX_RXD_SIZE - 1) +#define EFX_TXD_SIZE 512 +#define EFX_TXD_MASK (EFX_TXD_SIZE - 1) +#define EFX_EVQ_SIZE 512 +#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) + +/* There is space for 512 rx descriptors available. This number can be + * anything between 1 and 512 in powers of 2. This value will affect the + * network performance. During a test we were able to push 239 descriptors + * before we ran out of space. + */ +#define EFX_NUM_RX_DESC 64 +#define EFX_NUM_RX_DESC_MASK (EFX_NUM_RX_DESC - 1) + +/* The packet size is usually 1500 bytes hence we choose 1600 as the buf size, + * which is (1500+metadata) + */ +#define EFX_RX_BUF_SIZE 1600 + +/* Settings for the state field in efx_nic. + */ +#define EFX_STATE_POLLING 1 + +typedef unsigned long long dma_addr_t; + +/** A buffer table allocation backing a tx dma, rx dma or eventq */ +struct efx_special_buffer { + dma_addr_t dma_addr; + int id; +}; + +/** A transmit queue */ +struct efx_tx_queue { + /* The hardware ring */ + efx_tx_desc_t *ring; + + /* The software ring storing io_buffers. */ + struct io_buffer *buf[EFX_TXD_SIZE]; + + /* The buffer table reservation pushed to hardware */ + struct efx_special_buffer entry; + + /* Software descriptor write ptr */ + unsigned int write_ptr; + + /* Hardware descriptor read ptr */ + unsigned int read_ptr; +}; + +/** A receive queue */ +struct efx_rx_queue { + /* The hardware ring */ + efx_rx_desc_t *ring; + + /* The software ring storing io_buffers */ + struct io_buffer *buf[EFX_NUM_RX_DESC]; + + /* The buffer table reservation pushed to hardware */ + struct efx_special_buffer entry; + + /* Descriptor write ptr, into both the hardware and software rings */ + unsigned int write_ptr; + + /* Hardware completion ptr */ + unsigned int read_ptr; + + /* The value of RX_CONT in the previous RX event */ + unsigned int rx_cont_prev; +}; + +/** An event queue */ +struct efx_ev_queue { + /* The hardware ring to push to hardware. + * Must be the first entry in the structure. + */ + efx_event_t *ring; + + /* The buffer table reservation pushed to hardware */ + struct efx_special_buffer entry; + + /* Pointers into the ring */ + unsigned int read_ptr; +}; + +/* Hardware revisions */ +enum efx_revision { + EFX_HUNTINGTON, +}; + +/** Hardware access */ +struct efx_nic { + struct net_device *netdev; + enum efx_revision revision; + const struct efx_nic_type *type; + + int port; + u32 state; + + /** Memory and IO base */ + void *membase; + unsigned long mmio_start; + unsigned long mmio_len; + + /* Buffer table allocation head */ + int buffer_head; + + /* Queues */ + struct efx_rx_queue rxq; + struct efx_tx_queue txq; + struct efx_ev_queue evq; + + unsigned int rx_prefix_size; + + /** INT_REG_KER */ + int int_en; + efx_oword_t int_ker __aligned; + + /* Set to true if firmware supports the workaround for bug35388 */ + bool workaround_35388; + +}; + + +/** Efx device type definition */ +struct efx_nic_type { + int (*mcdi_rpc)(struct efx_nic *efx, unsigned int cmd, + const efx_dword_t *inbuf, size_t inlen, + efx_dword_t *outbuf, size_t outlen, + size_t *outlen_actual, bool quiet); +}; + +extern const struct efx_nic_type hunt_nic_type; + +#define EFX_MAC_FRAME_LEN(_mtu) \ + (((_mtu) \ + + /* EtherII already included */ \ + + 4 /* FCS */ \ + /* No VLAN supported */ \ + + 16 /* bug16772 */ \ + + 7) & ~7) + +/******************************************************************************* + * + * + * Hardware API + * + * + ******************************************************************************/ +static inline void _efx_writel(struct efx_nic *efx, uint32_t value, + unsigned int reg) +{ + writel((value), (efx)->membase + (reg)); +} + +static inline uint32_t _efx_readl(struct efx_nic *efx, unsigned int reg) +{ + return readl((efx)->membase + (reg)); +} + +#define efx_writel_table(efx, value, index, reg) \ + efx_writel(efx, value, (reg) + ((index) * reg##_STEP)) + +#define efx_writel_page(efx, value, index, reg) \ + efx_writel(efx, value, (reg) + ((index) * 0x2000)) + +/* Hardware access */ +extern void efx_writel(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg); +extern void efx_readl(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg); + +/* Initialisation */ +extern void efx_probe(struct net_device *netdev, enum efx_revision rev); +extern void efx_remove(struct net_device *netdev); + +#endif /* EFX_COMMON_H */ diff --git a/src/drivers/net/sfc/efx_hunt.c b/src/drivers/net/sfc/efx_hunt.c new file mode 100644 index 000000000..07dd7dfea --- /dev/null +++ b/src/drivers/net/sfc/efx_hunt.c @@ -0,0 +1,510 @@ +/************************************************************************** + * + * Driver datapath for Solarflare network cards + * + * Written by Shradha Shah + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efx_hunt.h" +#include "efx_bitfield.h" +#include "ef10_regs.h" + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +void efx_hunt_free_special_buffer(void *buf, int bytes) +{ + free_dma(buf, bytes); +} + +static void *efx_hunt_alloc_special_buffer(int bytes, + struct efx_special_buffer *entry) +{ + void *buffer; + dma_addr_t dma_addr; + + /* Allocate the buffer, aligned on a buffer address boundary. This + * buffer will be passed into an MC_CMD_INIT_*Q command to setup the + * appropriate type of queue via MCDI. + */ + buffer = malloc_dma(bytes, EFX_BUF_ALIGN); + if (!buffer) + return NULL; + + entry->dma_addr = dma_addr = virt_to_bus(buffer); + assert((dma_addr & (EFX_BUF_ALIGN - 1)) == 0); + + /* Buffer table entries aren't allocated, so set id to zero */ + entry->id = 0; + DBGP("Allocated 0x%x bytes at %p\n", bytes, buffer); + + return buffer; +} + +/******************************************************************************* + * + * + * TX + * + * + ******************************************************************************/ +static void +efx_hunt_build_tx_desc(efx_tx_desc_t *txd, struct io_buffer *iob) +{ + dma_addr_t dma_addr; + + dma_addr = virt_to_bus(iob->data); + + EFX_POPULATE_QWORD_4(*txd, + ESF_DZ_TX_KER_TYPE, 0, + ESF_DZ_TX_KER_CONT, 0, + ESF_DZ_TX_KER_BYTE_CNT, iob_len(iob), + ESF_DZ_TX_KER_BUF_ADDR, dma_addr); +} + +static void +efx_hunt_notify_tx_desc(struct efx_nic *efx) +{ + struct efx_tx_queue *txq = &efx->txq; + int ptr = txq->write_ptr & EFX_TXD_MASK; + efx_dword_t reg; + + EFX_POPULATE_DWORD_1(reg, ERF_DZ_TX_DESC_WPTR_DWORD, ptr); + efx_writel_page(efx, ®, 0, ER_DZ_TX_DESC_UPD_DWORD); +} + +int +efx_hunt_transmit(struct net_device *netdev, struct io_buffer *iob) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_tx_queue *txq = &efx->txq; + int fill_level, space; + efx_tx_desc_t *txd; + int buf_id; + + fill_level = txq->write_ptr - txq->read_ptr; + space = EFX_TXD_SIZE - fill_level - 1; + if (space < 1) + return -ENOBUFS; + + /* Save the iobuffer for later completion */ + buf_id = txq->write_ptr & EFX_TXD_MASK; + assert(txq->buf[buf_id] == NULL); + txq->buf[buf_id] = iob; + + DBGCIO(efx, "tx_buf[%d] for iob %p data %p len %zd\n", + buf_id, iob, iob->data, iob_len(iob)); + + /* Form the descriptor, and push it to hardware */ + txd = txq->ring + buf_id; + efx_hunt_build_tx_desc(txd, iob); + ++txq->write_ptr; + efx_hunt_notify_tx_desc(efx); + + return 0; +} + +static void +efx_hunt_transmit_done(struct efx_nic *efx, int id) +{ + struct efx_tx_queue *txq = &efx->txq; + unsigned int read_ptr, stop; + + /* Complete all buffers from read_ptr up to and including id */ + read_ptr = txq->read_ptr & EFX_TXD_MASK; + stop = (id + 1) & EFX_TXD_MASK; + + while (read_ptr != stop) { + struct io_buffer *iob = txq->buf[read_ptr]; + + assert(iob); + /* Complete the tx buffer */ + if (iob) + netdev_tx_complete(efx->netdev, iob); + DBGCIO(efx, "tx_buf[%d] for iob %p done\n", read_ptr, iob); + txq->buf[read_ptr] = NULL; + + ++txq->read_ptr; + read_ptr = txq->read_ptr & EFX_TXD_MASK; + } +} + +int efx_hunt_tx_init(struct net_device *netdev, dma_addr_t *dma_addr) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_tx_queue *txq = &efx->txq; + size_t bytes; + + /* Allocate hardware transmit queue */ + bytes = sizeof(efx_tx_desc_t) * EFX_TXD_SIZE; + txq->ring = efx_hunt_alloc_special_buffer(bytes, &txq->entry); + if (!txq->ring) + return -ENOMEM; + + txq->read_ptr = txq->write_ptr = 0; + *dma_addr = txq->entry.dma_addr; + return 0; +} + +/******************************************************************************* + * + * + * RX + * + * + ******************************************************************************/ +static void +efx_hunt_build_rx_desc(efx_rx_desc_t *rxd, struct io_buffer *iob) +{ + dma_addr_t dma_addr = virt_to_bus(iob->data); + + EFX_POPULATE_QWORD_2(*rxd, + ESF_DZ_RX_KER_BYTE_CNT, EFX_RX_BUF_SIZE, + ESF_DZ_RX_KER_BUF_ADDR, dma_addr); +} + +static void +efx_hunt_notify_rx_desc(struct efx_nic *efx) +{ + struct efx_rx_queue *rxq = &efx->rxq; + int ptr = rxq->write_ptr & EFX_RXD_MASK; + efx_dword_t reg; + + EFX_POPULATE_DWORD_1(reg, ERF_DZ_RX_DESC_WPTR, ptr); + efx_writel_page(efx, ®, 0, ER_DZ_RX_DESC_UPD); +} + +static void +efx_hunt_rxq_fill(struct efx_nic *efx) +{ + struct efx_rx_queue *rxq = &efx->rxq; + int fill_level = rxq->write_ptr - rxq->read_ptr; + int space = EFX_NUM_RX_DESC - fill_level - 1; + int pushed = 0; + + while (space) { + int buf_id = rxq->write_ptr & (EFX_NUM_RX_DESC - 1); + int desc_id = rxq->write_ptr & EFX_RXD_MASK; + struct io_buffer *iob; + efx_rx_desc_t *rxd; + + assert(rxq->buf[buf_id] == NULL); + iob = alloc_iob(EFX_RX_BUF_SIZE); + if (!iob) + break; + + DBGCP(efx, "pushing rx_buf[%d] iob %p data %p\n", + buf_id, iob, iob->data); + + rxq->buf[buf_id] = iob; + rxd = rxq->ring + desc_id; + efx_hunt_build_rx_desc(rxd, iob); + ++rxq->write_ptr; + ++pushed; + --space; + } + + /* Push the ptr to hardware */ + if (pushed > 0) { + efx_hunt_notify_rx_desc(efx); + + DBGCP(efx, "pushed %d rx buffers to fill level %d\n", + pushed, rxq->write_ptr - rxq->read_ptr); + } +} + +static void +efx_hunt_receive(struct efx_nic *efx, unsigned int id, int len, int drop) +{ + struct efx_rx_queue *rxq = &efx->rxq; + unsigned int read_ptr = rxq->read_ptr & EFX_RXD_MASK; + unsigned int buf_ptr = rxq->read_ptr & EFX_NUM_RX_DESC_MASK; + struct io_buffer *iob; + + /* id is the lower 4 bits of the desc index + 1 in huntington*/ + /* hence anding with 15 */ + assert((id & 15) == ((read_ptr + (len != 0)) & 15)); + + /* Pop this rx buffer out of the software ring */ + iob = rxq->buf[buf_ptr]; + rxq->buf[buf_ptr] = NULL; + + DBGCIO(efx, "popping rx_buf[%d] iob %p data %p with %d bytes %s %x\n", + read_ptr, iob, iob->data, len, drop ? "bad" : "ok", drop); + + /* Pass the packet up if required */ + if (drop) + netdev_rx_err(efx->netdev, iob, EBADMSG); + else { + iob_put(iob, len); + iob_pull(iob, efx->rx_prefix_size); + netdev_rx(efx->netdev, iob); + } + + ++rxq->read_ptr; +} + +int efx_hunt_rx_init(struct net_device *netdev, dma_addr_t *dma_addr) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_rx_queue *rxq = &efx->rxq; + size_t bytes; + + /* Allocate hardware receive queue */ + bytes = sizeof(efx_rx_desc_t) * EFX_RXD_SIZE; + rxq->ring = efx_hunt_alloc_special_buffer(bytes, &rxq->entry); + if (rxq->ring == NULL) + return -ENOMEM; + + rxq->read_ptr = rxq->write_ptr = 0; + *dma_addr = rxq->entry.dma_addr; + return 0; +} + +/******************************************************************************* + * + * + * Event queues and interrupts + * + * + ******************************************************************************/ +int efx_hunt_ev_init(struct net_device *netdev, dma_addr_t *dma_addr) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_ev_queue *evq = &efx->evq; + size_t bytes; + + /* Allocate the hardware event queue */ + bytes = sizeof(efx_event_t) * EFX_EVQ_SIZE; + evq->ring = efx_hunt_alloc_special_buffer(bytes, &evq->entry); + if (evq->ring == NULL) + return -ENOMEM; + + memset(evq->ring, 0xff, bytes); + evq->read_ptr = 0; + *dma_addr = evq->entry.dma_addr; + return 0; +} + +static void +efx_hunt_clear_interrupts(struct efx_nic *efx) +{ + efx_dword_t reg; + /* read the ISR */ + efx_readl(efx, ®, ER_DZ_BIU_INT_ISR); +} + +/** + * See if an event is present + * + * @v event EFX event structure + * @ret True An event is pending + * @ret False No event is pending + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int +efx_hunt_event_present(efx_event_t *event) +{ + return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | + EFX_DWORD_IS_ALL_ONES(event->dword[1]))); +} + +static void +efx_hunt_evq_read_ack(struct efx_nic *efx) +{ + struct efx_ev_queue *evq = &efx->evq; + efx_dword_t reg; + + if (efx->workaround_35388) { + EFX_POPULATE_DWORD_2(reg, ERF_DD_EVQ_IND_RPTR_FLAGS, + EFE_DD_EVQ_IND_RPTR_FLAGS_HIGH, + ERF_DD_EVQ_IND_RPTR, + evq->read_ptr >> ERF_DD_EVQ_IND_RPTR_WIDTH); + efx_writel_page(efx, ®, 0, ER_DD_EVQ_INDIRECT); + EFX_POPULATE_DWORD_2(reg, ERF_DD_EVQ_IND_RPTR_FLAGS, + EFE_DD_EVQ_IND_RPTR_FLAGS_LOW, + ERF_DD_EVQ_IND_RPTR, evq->read_ptr & + ((1 << ERF_DD_EVQ_IND_RPTR_WIDTH) - 1)); + efx_writel_page(efx, ®, 0, ER_DD_EVQ_INDIRECT); + } else { + EFX_POPULATE_DWORD_1(reg, ERF_DZ_EVQ_RPTR, evq->read_ptr); + efx_writel_table(efx, ®, 0, ER_DZ_EVQ_RPTR); + } +} + +static unsigned int +efx_hunt_handle_event(struct efx_nic *efx, efx_event_t *evt) +{ + struct efx_rx_queue *rxq = &efx->rxq; + int ev_code, desc_ptr, len; + int next_ptr_lbits, packet_drop; + int rx_cont; + + /* Decode event */ + ev_code = EFX_QWORD_FIELD(*evt, ESF_DZ_EV_CODE); + + switch (ev_code) { + case ESE_DZ_EV_CODE_TX_EV: + desc_ptr = EFX_QWORD_FIELD(*evt, ESF_DZ_TX_DESCR_INDX); + efx_hunt_transmit_done(efx, desc_ptr); + break; + + case ESE_DZ_EV_CODE_RX_EV: + len = EFX_QWORD_FIELD(*evt, ESF_DZ_RX_BYTES); + next_ptr_lbits = EFX_QWORD_FIELD(*evt, ESF_DZ_RX_DSC_PTR_LBITS); + rx_cont = EFX_QWORD_FIELD(*evt, ESF_DZ_RX_CONT); + + /* We don't expect to receive scattered packets, so drop the + * packet if RX_CONT is set on the current or previous event, or + * if len is zero. + */ + packet_drop = (len == 0) | (rx_cont << 1) | + (rxq->rx_cont_prev << 2); + efx_hunt_receive(efx, next_ptr_lbits, len, packet_drop); + rxq->rx_cont_prev = rx_cont; + return 1; + + default: + DBGCP(efx, "Unknown event type %d\n", ev_code); + break; + } + return 0; +} + +void efx_hunt_poll(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_ev_queue *evq = &efx->evq; + efx_event_t *evt; + int budget = 10; + + /* Read the event queue by directly looking for events + * (we don't even bother to read the eventq write ptr) + */ + evt = evq->ring + evq->read_ptr; + while (efx_hunt_event_present(evt) && (budget > 0)) { + DBGCP(efx, "Event at index 0x%x address %p is " + EFX_QWORD_FMT "\n", evq->read_ptr, + evt, EFX_QWORD_VAL(*evt)); + + budget -= efx_hunt_handle_event(efx, evt); + + /* Clear the event */ + EFX_SET_QWORD(*evt); + + /* Move to the next event. We don't ack the event + * queue until the end + */ + evq->read_ptr = ((evq->read_ptr + 1) & EFX_EVQ_MASK); + evt = evq->ring + evq->read_ptr; + } + + /* Push more rx buffers if needed */ + efx_hunt_rxq_fill(efx); + + /* Clear any pending interrupts */ + efx_hunt_clear_interrupts(efx); + + /* Ack the event queue if interrupts are enabled */ + if (efx->int_en) + efx_hunt_evq_read_ack(efx); +} + +void efx_hunt_irq(struct net_device *netdev, int enable) +{ + struct efx_nic *efx = netdev_priv(netdev); + + efx->int_en = enable; + + /* If interrupts are enabled, prime the event queue. Otherwise ack any + * pending interrupts + */ + if (enable) + efx_hunt_evq_read_ack(efx); + else if (efx->netdev->state & NETDEV_OPEN) + efx_hunt_clear_interrupts(efx); +} + +/******************************************************************************* + * + * + * Initialization and Close + * + * + ******************************************************************************/ +int efx_hunt_open(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + efx_dword_t cmd; + + /* Set interrupt moderation to 0*/ + EFX_POPULATE_DWORD_2(cmd, + ERF_DZ_TC_TIMER_MODE, 0, + ERF_DZ_TC_TIMER_VAL, 0); + efx_writel_page(efx, &cmd, 0, ER_DZ_EVQ_TMR); + + /* Ack the eventq */ + if (efx->int_en) + efx_hunt_evq_read_ack(efx); + + /* Push receive buffers */ + efx_hunt_rxq_fill(efx); + + return 0; +} + +void efx_hunt_close(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_rx_queue *rxq = &efx->rxq; + struct efx_tx_queue *txq = &efx->txq; + int i; + + /* Complete outstanding descriptors */ + for (i = 0; i < EFX_NUM_RX_DESC; i++) { + if (rxq->buf[i]) { + free_iob(rxq->buf[i]); + rxq->buf[i] = NULL; + } + } + + for (i = 0; i < EFX_TXD_SIZE; i++) { + if (txq->buf[i]) { + netdev_tx_complete(efx->netdev, txq->buf[i]); + txq->buf[i] = NULL; + } + } + + /* Clear interrupts */ + efx_hunt_clear_interrupts(efx); +} diff --git a/src/drivers/net/sfc/efx_hunt.h b/src/drivers/net/sfc/efx_hunt.h new file mode 100644 index 000000000..b8377bf20 --- /dev/null +++ b/src/drivers/net/sfc/efx_hunt.h @@ -0,0 +1,75 @@ +/************************************************************************** + * + * GPL net driver for Solarflare network cards + * + * Written by Shradha Shah + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ + +#ifndef EFX_HUNT_H +#define EFX_HUNT_H + +#include "efx_common.h" + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/************************************************************************** + * + * Hardware data structures and sizing + * + ***************************************************************************/ + +#define EFX_EV_SIZE(_nevs) ((_nevs) * sizeof(efx_qword_t)) +#define EFX_EVQ_NBUFS(_nevs) (EFX_EV_SIZE(_nevs) / EFX_BUF_ALIGN) + +#define EFX_RXQ_SIZE(_ndescs) ((_ndescs) * sizeof(efx_qword_t)) +#define EFX_RXQ_NBUFS(_ndescs) (EFX_RXQ_SIZE(_ndescs) / EFX_BUF_ALIGN) + +#define EFX_TXQ_SIZE(_ndescs) ((_ndescs) * sizeof(efx_qword_t)) +#define EFX_TXQ_NBUFS(_ndescs) (EFX_TXQ_SIZE(_ndescs) / EFX_BUF_ALIGN) + +/** MCDI request structure */ +struct efx_mcdi_req_s { + unsigned int emr_cmd; + efx_dword_t *emr_in_buf; + size_t emr_in_length; + int emr_rc; + efx_dword_t *emr_out_buf; + size_t emr_out_length; + size_t emr_out_length_used; +}; + +/******************************************************************************* + * + * + * Hardware API + * + * + ******************************************************************************/ + +extern void efx_hunt_free_special_buffer(void *buf, int bytes); + +/* Data path entry points */ +extern int efx_hunt_transmit(struct net_device *netdev, struct io_buffer *iob); +extern void efx_hunt_poll(struct net_device *netdev); +extern void efx_hunt_irq(struct net_device *netdev, int enable); + +/* Initialisation */ +extern int efx_hunt_ev_init(struct net_device *netdev, dma_addr_t *dma_addr); +extern int efx_hunt_rx_init(struct net_device *netdev, dma_addr_t *dma_addr); +extern int efx_hunt_tx_init(struct net_device *netdev, dma_addr_t *dma_addr); +extern int efx_hunt_open(struct net_device *netdev); +extern void efx_hunt_close(struct net_device *netdev); + +#endif /* EFX_HUNT_H */ diff --git a/src/drivers/net/sfc/mc_driver_pcol.h b/src/drivers/net/sfc/mc_driver_pcol.h new file mode 100644 index 000000000..e1174bd77 --- /dev/null +++ b/src/drivers/net/sfc/mc_driver_pcol.h @@ -0,0 +1,2281 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ +#ifndef SFC_MCDI_PCOL_H +#define SFC_MCDI_PCOL_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** \file mc_driver_pcol.h + * This file is a subset of the MCDI headers generated from the yml files. + */ + +/* The current version of the MCDI protocol. + * + * Note that the ROM burnt into the card only talks V0, so at the very + * least every driver must support version 0 and MCDI_PCOL_VERSION + */ +#ifdef WITH_MCDI_V2 +#define MCDI_PCOL_VERSION 2 +#else +#define MCDI_PCOL_VERSION 1 +#endif + +/* Unused commands: 0x23, 0x27, 0x30, 0x31 */ + +/* MCDI version 1 + * + * Each MCDI request starts with an MCDI_HEADER, which is a 32bit + * structure, filled in by the client. + * + * 0 7 8 16 20 22 23 24 31 + * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS | + * | | | + * | | \--- Response + * | \------- Error + * \------------------------------ Resync (always set) + * + * The client writes it's request into MC shared memory, and rings the + * doorbell. Each request is completed by either by the MC writing + * back into shared memory, or by writing out an event. + * + * All MCDI commands support completion by shared memory response. Each + * request may also contain additional data (accounted for by HEADER.LEN), + * and some response's may also contain additional data (again, accounted + * for by HEADER.LEN). + * + * Some MCDI commands support completion by event, in which any associated + * response data is included in the event. + * + * The protocol requires one response to be delivered for every request, a + * request should not be sent unless the response for the previous request + * has been received (either by polling shared memory, or by receiving + * an event). + */ + +/** Request/Response structure */ +#define MCDI_HEADER_OFST 0 +#define MCDI_HEADER_CODE_LBN 0 +#define MCDI_HEADER_CODE_WIDTH 7 +#define MCDI_HEADER_RESYNC_LBN 7 +#define MCDI_HEADER_RESYNC_WIDTH 1 +#define MCDI_HEADER_DATALEN_LBN 8 +#define MCDI_HEADER_DATALEN_WIDTH 8 +#define MCDI_HEADER_SEQ_LBN 16 +#define MCDI_HEADER_SEQ_WIDTH 4 +#define MCDI_HEADER_RSVD_LBN 20 +#define MCDI_HEADER_RSVD_WIDTH 1 +#define MCDI_HEADER_NOT_EPOCH_LBN 21 +#define MCDI_HEADER_NOT_EPOCH_WIDTH 1 +#define MCDI_HEADER_ERROR_LBN 22 +#define MCDI_HEADER_ERROR_WIDTH 1 +#define MCDI_HEADER_RESPONSE_LBN 23 +#define MCDI_HEADER_RESPONSE_WIDTH 1 +#define MCDI_HEADER_XFLAGS_LBN 24 +#define MCDI_HEADER_XFLAGS_WIDTH 8 +/* Request response using event */ +#define MCDI_HEADER_XFLAGS_EVREQ 0x01 +/* Request (and signal) early doorbell return */ +#define MCDI_HEADER_XFLAGS_DBRET 0x02 + +/* Maximum number of payload bytes */ +#define MCDI_CTL_SDU_LEN_MAX_V1 0xfc +#define MCDI_CTL_SDU_LEN_MAX_V2 0x400 + +#ifdef WITH_MCDI_V2 +#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V2 +#else +#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V1 +#endif + + +/* The MC can generate events for two reasons: + * - To advance a shared memory request if XFLAGS_EVREQ was set + * - As a notification (link state, i2c event), controlled + * via MC_CMD_LOG_CTRL + * + * Both events share a common structure: + * + * 0 32 33 36 44 52 60 + * | Data | Cont | Level | Src | Code | Rsvd | + * | + * \ There is another event pending in this notification + * + * If Code==CMDDONE, then the fields are further interpreted as: + * + * - LEVEL==INFO Command succeeded + * - LEVEL==ERR Command failed + * + * 0 8 16 24 32 + * | Seq | Datalen | Errno | Rsvd | + * + * These fields are taken directly out of the standard MCDI header, i.e., + * LEVEL==ERR, Datalen == 0 => Reboot + * + * Events can be squirted out of the UART (using LOG_CTRL) without a + * MCDI header. An event can be distinguished from a MCDI response by + * examining the first byte which is 0xc0. This corresponds to the + * non-existent MCDI command MC_CMD_DEBUG_LOG. + * + * 0 7 8 + * | command | Resync | = 0xc0 + * + * Since the event is written in big-endian byte order, this works + * providing bits 56-63 of the event are 0xc0. + * + * 56 60 63 + * | Rsvd | Code | = 0xc0 + * + * Which means for convenience the event code is 0xc for all MC + * generated events. + */ +#define FSE_AZ_EV_CODE_MCDI_EVRESPONSE 0xc + + +/* Operation not permitted. */ +#define MC_CMD_ERR_EPERM 1 +/* Non-existent command target */ +#define MC_CMD_ERR_ENOENT 2 +/* assert() has killed the MC */ +#define MC_CMD_ERR_EINTR 4 +/* I/O failure */ +#define MC_CMD_ERR_EIO 5 +/* Already exists */ +#define MC_CMD_ERR_EEXIST 6 +/* Try again */ +#define MC_CMD_ERR_EAGAIN 11 +/* Out of memory */ +#define MC_CMD_ERR_ENOMEM 12 +/* Caller does not hold required locks */ +#define MC_CMD_ERR_EACCES 13 +/* Resource is currently unavailable (e.g. lock contention) */ +#define MC_CMD_ERR_EBUSY 16 +/* No such device */ +#define MC_CMD_ERR_ENODEV 19 +/* Invalid argument to target */ +#define MC_CMD_ERR_EINVAL 22 +/* Broken pipe */ +#define MC_CMD_ERR_EPIPE 32 +/* Read-only */ +#define MC_CMD_ERR_EROFS 30 +/* Out of range */ +#define MC_CMD_ERR_ERANGE 34 +/* Non-recursive resource is already acquired */ +#define MC_CMD_ERR_EDEADLK 35 +/* Operation not implemented */ +#define MC_CMD_ERR_ENOSYS 38 +/* Operation timed out */ +#define MC_CMD_ERR_ETIME 62 +/* Link has been severed */ +#define MC_CMD_ERR_ENOLINK 67 +/* Protocol error */ +#define MC_CMD_ERR_EPROTO 71 +/* Operation not supported */ +#define MC_CMD_ERR_ENOTSUP 95 +/* Address not available */ +#define MC_CMD_ERR_EADDRNOTAVAIL 99 +/* Not connected */ +#define MC_CMD_ERR_ENOTCONN 107 +/* Operation already in progress */ +#define MC_CMD_ERR_EALREADY 114 + +/* Resource allocation failed. */ +#define MC_CMD_ERR_ALLOC_FAIL 0x1000 +/* V-adaptor not found. */ +#define MC_CMD_ERR_NO_VADAPTOR 0x1001 +/* EVB port not found. */ +#define MC_CMD_ERR_NO_EVB_PORT 0x1002 +/* V-switch not found. */ +#define MC_CMD_ERR_NO_VSWITCH 0x1003 +/* Too many VLAN tags. */ +#define MC_CMD_ERR_VLAN_LIMIT 0x1004 +/* Bad PCI function number. */ +#define MC_CMD_ERR_BAD_PCI_FUNC 0x1005 +/* Invalid VLAN mode. */ +#define MC_CMD_ERR_BAD_VLAN_MODE 0x1006 +/* Invalid v-switch type. */ +#define MC_CMD_ERR_BAD_VSWITCH_TYPE 0x1007 +/* Invalid v-port type. */ +#define MC_CMD_ERR_BAD_VPORT_TYPE 0x1008 +/* MAC address exists. */ +#define MC_CMD_ERR_MAC_EXIST 0x1009 +/* Slave core not present */ +#define MC_CMD_ERR_SLAVE_NOT_PRESENT 0x100a +/* The datapath is disabled. */ +#define MC_CMD_ERR_DATAPATH_DISABLED 0x100b +/* The requesting client is not a function */ +#define MC_CMD_ERR_CLIENT_NOT_FN 0x100c +/* The requested operation might require the + * command to be passed between MCs, and the + * transport doesn't support that. Should + * only ever been seen over the UART. + */ +#define MC_CMD_ERR_TRANSPORT_NOPROXY 0x100d +/* VLAN tag(s) exists */ +#define MC_CMD_ERR_VLAN_EXIST 0x100e +/* No MAC address assigned to an EVB port */ +#define MC_CMD_ERR_NO_MAC_ADDR 0x100f +/* Notifies the driver that the request has been relayed + * to an admin function for authorization. The driver should + * wait for a PROXY_RESPONSE event and then resend its request. + * This error code is followed by a 32-bit handle that + * helps matching it with the respective PROXY_RESPONSE event. + */ +#define MC_CMD_ERR_PROXY_PENDING 0x1010 +#define MC_CMD_ERR_PROXY_PENDING_HANDLE_OFST 4 +/* The request cannot be passed for authorization because + * another request from the same function is currently being + * authorized. The drvier should try again later. + */ +#define MC_CMD_ERR_PROXY_INPROGRESS 0x1011 +/* Returned by MC_CMD_PROXY_COMPLETE if the caller is not the function + * that has enabled proxying or BLOCK_INDEX points to a function that + * doesn't await an authorization. + */ +#define MC_CMD_ERR_PROXY_UNEXPECTED 0x1012 +/* This code is currently only used internally in FW. Its meaning is that + * an operation failed due to lack of SR-IOV privilege. + * Normally it is translated to EPERM by send_cmd_err(), + * but it may also be used to trigger some special mechanism + * for handling such case, e.g. to relay the failed request + * to a designated admin function for authorization. + */ +#define MC_CMD_ERR_NO_PRIVILEGE 0x1013 +/* Workaround 26807 could not be turned on/off because some functions + * have already installed filters. See the comment at + * MC_CMD_WORKAROUND_BUG26807. + */ +#define MC_CMD_ERR_FILTERS_PRESENT 0x1014 +/* The clock whose frequency you've attempted to set set + * doesn't exist on this NIC + */ +#define MC_CMD_ERR_NO_CLOCK 0x1015 +/* Returned by MC_CMD_TESTASSERT if the action that should + * have caused an assertion failed to do so. + */ +#define MC_CMD_ERR_UNREACHABLE 0x1016 +/* This command needs to be processed in the background but there were no + * resources to do so. Send it again after a command has completed. + */ +#define MC_CMD_ERR_QUEUE_FULL 0x1017 + +#define MC_CMD_ERR_CODE_OFST 0 + + +#ifdef WITH_MCDI_V2 + +/* Version 2 adds an optional argument to error returns: the errno value + * may be followed by the (0-based) number of the first argument that + * could not be processed. + */ +#define MC_CMD_ERR_ARG_OFST 4 + +/* No space */ +#define MC_CMD_ERR_ENOSPC 28 + +#endif + +/* MCDI_EVENT structuredef */ +#define MCDI_EVENT_LEN 8 +#define MCDI_EVENT_CONT_LBN 32 +#define MCDI_EVENT_CONT_WIDTH 1 +#define MCDI_EVENT_LEVEL_LBN 33 +#define MCDI_EVENT_LEVEL_WIDTH 3 +/* enum: Info. */ +#define MCDI_EVENT_LEVEL_INFO 0x0 +/* enum: Warning. */ +#define MCDI_EVENT_LEVEL_WARN 0x1 +/* enum: Error. */ +#define MCDI_EVENT_LEVEL_ERR 0x2 +/* enum: Fatal. */ +#define MCDI_EVENT_LEVEL_FATAL 0x3 +#define MCDI_EVENT_DATA_OFST 0 +#define MCDI_EVENT_CMDDONE_SEQ_LBN 0 +#define MCDI_EVENT_CMDDONE_SEQ_WIDTH 8 +#define MCDI_EVENT_CMDDONE_DATALEN_LBN 8 +#define MCDI_EVENT_CMDDONE_DATALEN_WIDTH 8 +#define MCDI_EVENT_CMDDONE_ERRNO_LBN 16 +#define MCDI_EVENT_CMDDONE_ERRNO_WIDTH 8 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_LBN 0 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_WIDTH 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4 +/* enum: 100Mbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_100M 0x1 +/* enum: 1Gbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_1G 0x2 +/* enum: 10Gbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_10G 0x3 +/* enum: 40Gbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_40G 0x4 +#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20 +#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_MONITOR_LBN 0 +#define MCDI_EVENT_SENSOREVT_MONITOR_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_STATE_LBN 8 +#define MCDI_EVENT_SENSOREVT_STATE_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_VALUE_LBN 16 +#define MCDI_EVENT_SENSOREVT_VALUE_WIDTH 16 +#define MCDI_EVENT_FWALERT_DATA_LBN 8 +#define MCDI_EVENT_FWALERT_DATA_WIDTH 24 +#define MCDI_EVENT_FWALERT_REASON_LBN 0 +#define MCDI_EVENT_FWALERT_REASON_WIDTH 8 +/* enum: SRAM Access. */ +#define MCDI_EVENT_FWALERT_REASON_SRAM_ACCESS 0x1 +#define MCDI_EVENT_FLR_VF_LBN 0 +#define MCDI_EVENT_FLR_VF_WIDTH 8 +#define MCDI_EVENT_TX_ERR_TXQ_LBN 0 +#define MCDI_EVENT_TX_ERR_TXQ_WIDTH 12 +#define MCDI_EVENT_TX_ERR_TYPE_LBN 12 +#define MCDI_EVENT_TX_ERR_TYPE_WIDTH 4 +/* enum: Descriptor loader reported failure */ +#define MCDI_EVENT_TX_ERR_DL_FAIL 0x1 +/* enum: Descriptor ring empty and no EOP seen for packet */ +#define MCDI_EVENT_TX_ERR_NO_EOP 0x2 +/* enum: Overlength packet */ +#define MCDI_EVENT_TX_ERR_2BIG 0x3 +/* enum: Malformed option descriptor */ +#define MCDI_EVENT_TX_BAD_OPTDESC 0x5 +/* enum: Option descriptor part way through a packet */ +#define MCDI_EVENT_TX_OPT_IN_PKT 0x8 +/* enum: DMA or PIO data access error */ +#define MCDI_EVENT_TX_ERR_BAD_DMA_OR_PIO 0x9 +#define MCDI_EVENT_TX_ERR_INFO_LBN 16 +#define MCDI_EVENT_TX_ERR_INFO_WIDTH 16 +#define MCDI_EVENT_TX_FLUSH_TO_DRIVER_LBN 12 +#define MCDI_EVENT_TX_FLUSH_TO_DRIVER_WIDTH 1 +#define MCDI_EVENT_TX_FLUSH_TXQ_LBN 0 +#define MCDI_EVENT_TX_FLUSH_TXQ_WIDTH 12 +#define MCDI_EVENT_PTP_ERR_TYPE_LBN 0 +#define MCDI_EVENT_PTP_ERR_TYPE_WIDTH 8 +/* enum: PLL lost lock */ +#define MCDI_EVENT_PTP_ERR_PLL_LOST 0x1 +/* enum: Filter overflow (PDMA) */ +#define MCDI_EVENT_PTP_ERR_FILTER 0x2 +/* enum: FIFO overflow (FPGA) */ +#define MCDI_EVENT_PTP_ERR_FIFO 0x3 +/* enum: Merge queue overflow */ +#define MCDI_EVENT_PTP_ERR_QUEUE 0x4 +#define MCDI_EVENT_AOE_ERR_TYPE_LBN 0 +#define MCDI_EVENT_AOE_ERR_TYPE_WIDTH 8 +/* enum: AOE failed to load - no valid image? */ +#define MCDI_EVENT_AOE_NO_LOAD 0x1 +/* enum: AOE FC reported an exception */ +#define MCDI_EVENT_AOE_FC_ASSERT 0x2 +/* enum: AOE FC watchdogged */ +#define MCDI_EVENT_AOE_FC_WATCHDOG 0x3 +/* enum: AOE FC failed to start */ +#define MCDI_EVENT_AOE_FC_NO_START 0x4 +/* enum: Generic AOE fault - likely to have been reported via other means too + * but intended for use by aoex driver. + */ +#define MCDI_EVENT_AOE_FAULT 0x5 +/* enum: Results of reprogramming the CPLD (status in AOE_ERR_DATA) */ +#define MCDI_EVENT_AOE_CPLD_REPROGRAMMED 0x6 +/* enum: AOE loaded successfully */ +#define MCDI_EVENT_AOE_LOAD 0x7 +/* enum: AOE DMA operation completed (LSB of HOST_HANDLE in AOE_ERR_DATA) */ +#define MCDI_EVENT_AOE_DMA 0x8 +/* enum: AOE byteblaster connected/disconnected (Connection status in + * AOE_ERR_DATA) + */ +#define MCDI_EVENT_AOE_BYTEBLASTER 0x9 +/* enum: DDR ECC status update */ +#define MCDI_EVENT_AOE_DDR_ECC_STATUS 0xa +/* enum: PTP status update */ +#define MCDI_EVENT_AOE_PTP_STATUS 0xb +/* enum: FPGA header incorrect */ +#define MCDI_EVENT_AOE_FPGA_LOAD_HEADER_ERR 0xc +/* enum: FPGA Powered Off due to error in powering up FPGA */ +#define MCDI_EVENT_AOE_FPGA_POWER_OFF 0xd +/* enum: AOE FPGA load failed due to MC to MUM communication failure */ +#define MCDI_EVENT_AOE_FPGA_LOAD_FAILED 0xe +/* enum: Notify that invalid flash type detected */ +#define MCDI_EVENT_AOE_INVALID_FPGA_FLASH_TYPE 0xf +/* enum: Notify that the attempt to run FPGA Controller firmware timedout */ +#define MCDI_EVENT_AOE_FC_RUN_TIMEDOUT 0x10 +/* enum: Failure to probe one or more FPGA boot flash chips */ +#define MCDI_EVENT_AOE_FPGA_BOOT_FLASH_INVALID 0x11 +/* enum: FPGA boot-flash contains an invalid image header */ +#define MCDI_EVENT_AOE_FPGA_BOOT_FLASH_HDR_INVALID 0x12 +/* enum: Failed to program clocks required by the FPGA */ +#define MCDI_EVENT_AOE_FPGA_CLOCKS_PROGRAM_FAILED 0x13 +/* enum: Notify that FPGA Controller is alive to serve MCDI requests */ +#define MCDI_EVENT_AOE_FC_RUNNING 0x14 +#define MCDI_EVENT_AOE_ERR_DATA_LBN 8 +#define MCDI_EVENT_AOE_ERR_DATA_WIDTH 8 +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_INFO_LBN 8 +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_INFO_WIDTH 8 +/* enum: FC Assert happened, but the register information is not available */ +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_SEEN 0x0 +/* enum: The register information for FC Assert is ready for readinng by driver + */ +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_DATA_READY 0x1 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_HEADER_VERIFY_FAILED_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_HEADER_VERIFY_FAILED_WIDTH 8 +/* enum: Reading from NV failed */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_NV_READ_FAIL 0x0 +/* enum: Invalid Magic Number if FPGA header */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_MAGIC_FAIL 0x1 +/* enum: Invalid Silicon type detected in header */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_SILICON_TYPE 0x2 +/* enum: Unsupported VRatio */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_VRATIO 0x3 +/* enum: Unsupported DDR Type */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_TYPE 0x4 +/* enum: DDR Voltage out of supported range */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_VOLTAGE 0x5 +/* enum: Unsupported DDR speed */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_SPEED 0x6 +/* enum: Unsupported DDR size */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_SIZE 0x7 +/* enum: Unsupported DDR rank */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_RANK 0x8 +#define MCDI_EVENT_AOE_ERR_CODE_INVALID_FPGA_FLASH_TYPE_INFO_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_INVALID_FPGA_FLASH_TYPE_INFO_WIDTH 8 +/* enum: Primary boot flash */ +#define MCDI_EVENT_AOE_FLASH_TYPE_BOOT_PRIMARY 0x0 +/* enum: Secondary boot flash */ +#define MCDI_EVENT_AOE_FLASH_TYPE_BOOT_SECONDARY 0x1 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_POWER_OFF_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_POWER_OFF_WIDTH 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_LOAD_FAILED_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_LOAD_FAILED_WIDTH 8 +#define MCDI_EVENT_RX_ERR_RXQ_LBN 0 +#define MCDI_EVENT_RX_ERR_RXQ_WIDTH 12 +#define MCDI_EVENT_RX_ERR_TYPE_LBN 12 +#define MCDI_EVENT_RX_ERR_TYPE_WIDTH 4 +#define MCDI_EVENT_RX_ERR_INFO_LBN 16 +#define MCDI_EVENT_RX_ERR_INFO_WIDTH 16 +#define MCDI_EVENT_RX_FLUSH_TO_DRIVER_LBN 12 +#define MCDI_EVENT_RX_FLUSH_TO_DRIVER_WIDTH 1 +#define MCDI_EVENT_RX_FLUSH_RXQ_LBN 0 +#define MCDI_EVENT_RX_FLUSH_RXQ_WIDTH 12 +#define MCDI_EVENT_MC_REBOOT_COUNT_LBN 0 +#define MCDI_EVENT_MC_REBOOT_COUNT_WIDTH 16 +#define MCDI_EVENT_MUM_ERR_TYPE_LBN 0 +#define MCDI_EVENT_MUM_ERR_TYPE_WIDTH 8 +/* enum: MUM failed to load - no valid image? */ +#define MCDI_EVENT_MUM_NO_LOAD 0x1 +/* enum: MUM f/w reported an exception */ +#define MCDI_EVENT_MUM_ASSERT 0x2 +/* enum: MUM not kicking watchdog */ +#define MCDI_EVENT_MUM_WATCHDOG 0x3 +#define MCDI_EVENT_MUM_ERR_DATA_LBN 8 +#define MCDI_EVENT_MUM_ERR_DATA_WIDTH 8 +#define MCDI_EVENT_DATA_LBN 0 +#define MCDI_EVENT_DATA_WIDTH 32 +#define MCDI_EVENT_SRC_LBN 36 +#define MCDI_EVENT_SRC_WIDTH 8 +#define MCDI_EVENT_EV_CODE_LBN 60 +#define MCDI_EVENT_EV_CODE_WIDTH 4 +#define MCDI_EVENT_CODE_LBN 44 +#define MCDI_EVENT_CODE_WIDTH 8 +/* enum: Event generated by host software */ +#define MCDI_EVENT_SW_EVENT 0x0 +/* enum: Bad assert. */ +#define MCDI_EVENT_CODE_BADSSERT 0x1 +/* enum: PM Notice. */ +#define MCDI_EVENT_CODE_PMNOTICE 0x2 +/* enum: Command done. */ +#define MCDI_EVENT_CODE_CMDDONE 0x3 +/* enum: Link change. */ +#define MCDI_EVENT_CODE_LINKCHANGE 0x4 +/* enum: Sensor Event. */ +#define MCDI_EVENT_CODE_SENSOREVT 0x5 +/* enum: Schedule error. */ +#define MCDI_EVENT_CODE_SCHEDERR 0x6 +/* enum: Reboot. */ +#define MCDI_EVENT_CODE_REBOOT 0x7 +/* enum: Mac stats DMA. */ +#define MCDI_EVENT_CODE_MAC_STATS_DMA 0x8 +/* enum: Firmware alert. */ +#define MCDI_EVENT_CODE_FWALERT 0x9 +/* enum: Function level reset. */ +#define MCDI_EVENT_CODE_FLR 0xa +/* enum: Transmit error */ +#define MCDI_EVENT_CODE_TX_ERR 0xb +/* enum: Tx flush has completed */ +#define MCDI_EVENT_CODE_TX_FLUSH 0xc +/* enum: PTP packet received timestamp */ +#define MCDI_EVENT_CODE_PTP_RX 0xd +/* enum: PTP NIC failure */ +#define MCDI_EVENT_CODE_PTP_FAULT 0xe +/* enum: PTP PPS event */ +#define MCDI_EVENT_CODE_PTP_PPS 0xf +/* enum: Rx flush has completed */ +#define MCDI_EVENT_CODE_RX_FLUSH 0x10 +/* enum: Receive error */ +#define MCDI_EVENT_CODE_RX_ERR 0x11 +/* enum: AOE fault */ +#define MCDI_EVENT_CODE_AOE 0x12 +/* enum: Network port calibration failed (VCAL). */ +#define MCDI_EVENT_CODE_VCAL_FAIL 0x13 +/* enum: HW PPS event */ +#define MCDI_EVENT_CODE_HW_PPS 0x14 +/* enum: The MC has rebooted (huntington and later, siena uses CODE_REBOOT and + * a different format) + */ +#define MCDI_EVENT_CODE_MC_REBOOT 0x15 +/* enum: the MC has detected a parity error */ +#define MCDI_EVENT_CODE_PAR_ERR 0x16 +/* enum: the MC has detected a correctable error */ +#define MCDI_EVENT_CODE_ECC_CORR_ERR 0x17 +/* enum: the MC has detected an uncorrectable error */ +#define MCDI_EVENT_CODE_ECC_FATAL_ERR 0x18 +/* enum: The MC has entered offline BIST mode */ +#define MCDI_EVENT_CODE_MC_BIST 0x19 +/* enum: PTP tick event providing current NIC time */ +#define MCDI_EVENT_CODE_PTP_TIME 0x1a +/* enum: MUM fault */ +#define MCDI_EVENT_CODE_MUM 0x1b +/* enum: notify the designated PF of a new authorization request */ +#define MCDI_EVENT_CODE_PROXY_REQUEST 0x1c +/* enum: notify a function that awaits an authorization that its request has + * been processed and it may now resend the command + */ +#define MCDI_EVENT_CODE_PROXY_RESPONSE 0x1d +/* enum: Artificial event generated by host and posted via MC for test + * purposes. + */ +#define MCDI_EVENT_CODE_TESTGEN 0xfa +#define MCDI_EVENT_CMDDONE_DATA_OFST 0 +#define MCDI_EVENT_CMDDONE_DATA_LBN 0 +#define MCDI_EVENT_CMDDONE_DATA_WIDTH 32 +#define MCDI_EVENT_LINKCHANGE_DATA_OFST 0 +#define MCDI_EVENT_LINKCHANGE_DATA_LBN 0 +#define MCDI_EVENT_LINKCHANGE_DATA_WIDTH 32 +#define MCDI_EVENT_SENSOREVT_DATA_OFST 0 +#define MCDI_EVENT_SENSOREVT_DATA_LBN 0 +#define MCDI_EVENT_SENSOREVT_DATA_WIDTH 32 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_OFST 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_LBN 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_WIDTH 32 +#define MCDI_EVENT_TX_ERR_DATA_OFST 0 +#define MCDI_EVENT_TX_ERR_DATA_LBN 0 +#define MCDI_EVENT_TX_ERR_DATA_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the seconds field of + * timestamp + */ +#define MCDI_EVENT_PTP_SECONDS_OFST 0 +#define MCDI_EVENT_PTP_SECONDS_LBN 0 +#define MCDI_EVENT_PTP_SECONDS_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the major field of + * timestamp + */ +#define MCDI_EVENT_PTP_MAJOR_OFST 0 +#define MCDI_EVENT_PTP_MAJOR_LBN 0 +#define MCDI_EVENT_PTP_MAJOR_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the nanoseconds field + * of timestamp + */ +#define MCDI_EVENT_PTP_NANOSECONDS_OFST 0 +#define MCDI_EVENT_PTP_NANOSECONDS_LBN 0 +#define MCDI_EVENT_PTP_NANOSECONDS_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the minor field of + * timestamp + */ +#define MCDI_EVENT_PTP_MINOR_OFST 0 +#define MCDI_EVENT_PTP_MINOR_LBN 0 +#define MCDI_EVENT_PTP_MINOR_WIDTH 32 +/* For CODE_PTP_RX events, the lowest four bytes of sourceUUID from PTP packet + */ +#define MCDI_EVENT_PTP_UUID_OFST 0 +#define MCDI_EVENT_PTP_UUID_LBN 0 +#define MCDI_EVENT_PTP_UUID_WIDTH 32 +#define MCDI_EVENT_RX_ERR_DATA_OFST 0 +#define MCDI_EVENT_RX_ERR_DATA_LBN 0 +#define MCDI_EVENT_RX_ERR_DATA_WIDTH 32 +#define MCDI_EVENT_PAR_ERR_DATA_OFST 0 +#define MCDI_EVENT_PAR_ERR_DATA_LBN 0 +#define MCDI_EVENT_PAR_ERR_DATA_WIDTH 32 +#define MCDI_EVENT_ECC_CORR_ERR_DATA_OFST 0 +#define MCDI_EVENT_ECC_CORR_ERR_DATA_LBN 0 +#define MCDI_EVENT_ECC_CORR_ERR_DATA_WIDTH 32 +#define MCDI_EVENT_ECC_FATAL_ERR_DATA_OFST 0 +#define MCDI_EVENT_ECC_FATAL_ERR_DATA_LBN 0 +#define MCDI_EVENT_ECC_FATAL_ERR_DATA_WIDTH 32 +/* For CODE_PTP_TIME events, the major value of the PTP clock */ +#define MCDI_EVENT_PTP_TIME_MAJOR_OFST 0 +#define MCDI_EVENT_PTP_TIME_MAJOR_LBN 0 +#define MCDI_EVENT_PTP_TIME_MAJOR_WIDTH 32 +/* For CODE_PTP_TIME events, bits 19-26 of the minor value of the PTP clock */ +#define MCDI_EVENT_PTP_TIME_MINOR_26_19_LBN 36 +#define MCDI_EVENT_PTP_TIME_MINOR_26_19_WIDTH 8 +/* For CODE_PTP_TIME events where report sync status is enabled, indicates + * whether the NIC clock has ever been set + */ +#define MCDI_EVENT_PTP_TIME_NIC_CLOCK_VALID_LBN 36 +#define MCDI_EVENT_PTP_TIME_NIC_CLOCK_VALID_WIDTH 1 +/* For CODE_PTP_TIME events where report sync status is enabled, indicates + * whether the NIC and System clocks are in sync + */ +#define MCDI_EVENT_PTP_TIME_HOST_NIC_IN_SYNC_LBN 37 +#define MCDI_EVENT_PTP_TIME_HOST_NIC_IN_SYNC_WIDTH 1 +/* For CODE_PTP_TIME events where report sync status is enabled, bits 21-26 of + * the minor value of the PTP clock + */ +#define MCDI_EVENT_PTP_TIME_MINOR_26_21_LBN 38 +#define MCDI_EVENT_PTP_TIME_MINOR_26_21_WIDTH 6 +#define MCDI_EVENT_PROXY_REQUEST_BUFF_INDEX_OFST 0 +#define MCDI_EVENT_PROXY_REQUEST_BUFF_INDEX_LBN 0 +#define MCDI_EVENT_PROXY_REQUEST_BUFF_INDEX_WIDTH 32 +#define MCDI_EVENT_PROXY_RESPONSE_HANDLE_OFST 0 +#define MCDI_EVENT_PROXY_RESPONSE_HANDLE_LBN 0 +#define MCDI_EVENT_PROXY_RESPONSE_HANDLE_WIDTH 32 +/* Zero means that the request has been completed or authorized, and the driver + * should resend it. A non-zero value means that the authorization has been + * denied, and gives the reason. Typically it will be EPERM. + */ +#define MCDI_EVENT_PROXY_RESPONSE_RC_LBN 36 +#define MCDI_EVENT_PROXY_RESPONSE_RC_WIDTH 8 + +/* EVB_PORT_ID structuredef */ +#define EVB_PORT_ID_LEN 4 +#define EVB_PORT_ID_PORT_ID_OFST 0 +/* enum: An invalid port handle. */ +#define EVB_PORT_ID_NULL 0x0 +/* enum: The port assigned to this function.. */ +#define EVB_PORT_ID_ASSIGNED 0x1000000 +/* enum: External network port 0 */ +#define EVB_PORT_ID_MAC0 0x2000000 +/* enum: External network port 1 */ +#define EVB_PORT_ID_MAC1 0x2000001 +/* enum: External network port 2 */ +#define EVB_PORT_ID_MAC2 0x2000002 +/* enum: External network port 3 */ +#define EVB_PORT_ID_MAC3 0x2000003 +#define EVB_PORT_ID_PORT_ID_LBN 0 +#define EVB_PORT_ID_PORT_ID_WIDTH 32 + + +/***********************************/ +/* MC_CMD_DRV_ATTACH + * Inform MCPU that this port is managed on the host (i.e. driver active). For + * Huntington, also request the preferred datapath firmware to use if possible + * (it may not be possible for this request to be fulfilled; the driver must + * issue a subsequent MC_CMD_GET_CAPABILITIES command to determine which + * features are actually available). The FIRMWARE_ID field is ignored by older + * platforms. + */ +#define MC_CMD_DRV_ATTACH 0x1c +#undef MC_CMD_0x1c_PRIVILEGE_CTG + +#define MC_CMD_0x1c_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_DRV_ATTACH_IN msgrequest */ +#define MC_CMD_DRV_ATTACH_IN_LEN 12 +/* new state to set if UPDATE=1 */ +#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_OFST 0 +#define MC_CMD_DRV_ATTACH_LBN 0 +#define MC_CMD_DRV_ATTACH_WIDTH 1 +#define MC_CMD_DRV_PREBOOT_LBN 1 +#define MC_CMD_DRV_PREBOOT_WIDTH 1 +/* 1 to set new state, or 0 to just report the existing state */ +#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4 +/* preferred datapath firmware (for Huntington; ignored for Siena) */ +#define MC_CMD_DRV_ATTACH_IN_FIRMWARE_ID_OFST 8 +/* enum: Prefer to use full featured firmware */ +#define MC_CMD_FW_FULL_FEATURED 0x0 +/* enum: Prefer to use firmware with fewer features but lower latency */ +#define MC_CMD_FW_LOW_LATENCY 0x1 +/* enum: Prefer to use firmware for SolarCapture packed stream mode */ +#define MC_CMD_FW_PACKED_STREAM 0x2 +/* enum: Prefer to use firmware with fewer features and simpler TX event + * batching but higher TX packet rate + */ +#define MC_CMD_FW_HIGH_TX_RATE 0x3 +/* enum: Reserved value */ +#define MC_CMD_FW_PACKED_STREAM_HASH_MODE_1 0x4 +/* enum: Prefer to use firmware with additional "rules engine" filtering + * support + */ +#define MC_CMD_FW_RULES_ENGINE 0x5 +/* enum: Only this option is allowed for non-admin functions */ +#define MC_CMD_FW_DONT_CARE 0xffffffff + +/* MC_CMD_DRV_ATTACH_OUT msgresponse */ +#define MC_CMD_DRV_ATTACH_OUT_LEN 4 +/* previous or existing state, see the bitmask at NEW_STATE */ +#define MC_CMD_DRV_ATTACH_OUT_OLD_STATE_OFST 0 + +/* MC_CMD_DRV_ATTACH_EXT_OUT msgresponse */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_LEN 8 +/* previous or existing state, see the bitmask at NEW_STATE */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_OLD_STATE_OFST 0 +/* Flags associated with this function */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FUNC_FLAGS_OFST 4 +/* enum: Labels the lowest-numbered function visible to the OS */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY 0x0 +/* enum: The function can control the link state of the physical port it is + * bound to. + */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL 0x1 +/* enum: The function can perform privileged operations */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED 0x2 +/* enum: The function does not have an active port associated with it. The port + * refers to the Sorrento external FPGA port. + */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT 0x3 + + +/***********************************/ +/* MC_CMD_ENTITY_RESET + * Generic per-resource reset. There is no equivalent for per-board reset. + * Locks required: None; Return code: 0, ETIME. NOTE: This command is an + * extended version of the deprecated MC_CMD_PORT_RESET with added fields. + */ +#define MC_CMD_ENTITY_RESET 0x20 +/* MC_CMD_0x20_PRIVILEGE_CTG SRIOV_CTG_GENERAL */ + +/* MC_CMD_ENTITY_RESET_IN msgrequest */ +#define MC_CMD_ENTITY_RESET_IN_LEN 4 +/* Optional flags field. Omitting this will perform a "legacy" reset action + * (TBD). + */ +#define MC_CMD_ENTITY_RESET_IN_FLAG_OFST 0 +#define MC_CMD_ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET_LBN 0 +#define MC_CMD_ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET_WIDTH 1 + +/* MC_CMD_ENTITY_RESET_OUT msgresponse */ +#define MC_CMD_ENTITY_RESET_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_GET_PHY_CFG + * Report PHY configuration. This guarantees to succeed even if the PHY is in a + * 'zombie' state. Locks required: None + */ +#define MC_CMD_GET_PHY_CFG 0x24 +#undef MC_CMD_0x24_PRIVILEGE_CTG + +#define MC_CMD_0x24_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_PHY_CFG_IN msgrequest */ +#define MC_CMD_GET_PHY_CFG_IN_LEN 0 + +/* MC_CMD_GET_PHY_CFG_OUT msgresponse */ +#define MC_CMD_GET_PHY_CFG_OUT_LEN 72 +/* flags */ +#define MC_CMD_GET_PHY_CFG_OUT_FLAGS_OFST 0 +#define MC_CMD_GET_PHY_CFG_OUT_PRESENT_LBN 0 +#define MC_CMD_GET_PHY_CFG_OUT_PRESENT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN 2 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_LBN 3 +#define MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_POWEROFF_LBN 4 +#define MC_CMD_GET_PHY_CFG_OUT_POWEROFF_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_TXDIS_LBN 5 +#define MC_CMD_GET_PHY_CFG_OUT_TXDIS_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_LBN 6 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_WIDTH 1 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_TYPE_OFST 4 +/* Bitmask of supported capabilities */ +#define MC_CMD_GET_PHY_CFG_OUT_SUPPORTED_CAP_OFST 8 +#define MC_CMD_PHY_CAP_10HDX_LBN 1 +#define MC_CMD_PHY_CAP_10HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10FDX_LBN 2 +#define MC_CMD_PHY_CAP_10FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100HDX_LBN 3 +#define MC_CMD_PHY_CAP_100HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100FDX_LBN 4 +#define MC_CMD_PHY_CAP_100FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000HDX_LBN 5 +#define MC_CMD_PHY_CAP_1000HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000FDX_LBN 6 +#define MC_CMD_PHY_CAP_1000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10000FDX_LBN 7 +#define MC_CMD_PHY_CAP_10000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_PAUSE_LBN 8 +#define MC_CMD_PHY_CAP_PAUSE_WIDTH 1 +#define MC_CMD_PHY_CAP_ASYM_LBN 9 +#define MC_CMD_PHY_CAP_ASYM_WIDTH 1 +#define MC_CMD_PHY_CAP_AN_LBN 10 +#define MC_CMD_PHY_CAP_AN_WIDTH 1 +#define MC_CMD_PHY_CAP_40000FDX_LBN 11 +#define MC_CMD_PHY_CAP_40000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_DDM_LBN 12 +#define MC_CMD_PHY_CAP_DDM_WIDTH 1 +#define MC_CMD_PHY_CAP_100000FDX_LBN 13 +#define MC_CMD_PHY_CAP_100000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_25000FDX_LBN 14 +#define MC_CMD_PHY_CAP_25000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_50000FDX_LBN 15 +#define MC_CMD_PHY_CAP_50000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_BASER_FEC_LBN 16 +#define MC_CMD_PHY_CAP_BASER_FEC_WIDTH 1 +#define MC_CMD_PHY_CAP_BASER_FEC_REQ_LBN 17 +#define MC_CMD_PHY_CAP_BASER_FEC_REQ_WIDTH 1 +#define MC_CMD_PHY_CAP_RS_FEC_LBN 17 +#define MC_CMD_PHY_CAP_RS_FEC_WIDTH 1 +#define MC_CMD_PHY_CAP_RS_FEC_REQ_LBN 18 +#define MC_CMD_PHY_CAP_RS_FEC_REQ_WIDTH 1 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_CHANNEL_OFST 12 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_PRT_OFST 16 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_STATS_MASK_OFST 20 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_NAME_OFST 24 +#define MC_CMD_GET_PHY_CFG_OUT_NAME_LEN 20 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_MEDIA_TYPE_OFST 44 +/* enum: Xaui. */ +#define MC_CMD_MEDIA_XAUI 0x1 +/* enum: CX4. */ +#define MC_CMD_MEDIA_CX4 0x2 +/* enum: KX4. */ +#define MC_CMD_MEDIA_KX4 0x3 +/* enum: XFP Far. */ +#define MC_CMD_MEDIA_XFP 0x4 +/* enum: SFP+. */ +#define MC_CMD_MEDIA_SFP_PLUS 0x5 +/* enum: 10GBaseT. */ +#define MC_CMD_MEDIA_BASE_T 0x6 +/* enum: QSFP+. */ +#define MC_CMD_MEDIA_QSFP_PLUS 0x7 +#define MC_CMD_GET_PHY_CFG_OUT_MMD_MASK_OFST 48 +/* enum: Native clause 22 */ +#define MC_CMD_MMD_CLAUSE22 0x0 +#define MC_CMD_MMD_CLAUSE45_PMAPMD 0x1 /* enum */ +#define MC_CMD_MMD_CLAUSE45_WIS 0x2 /* enum */ +#define MC_CMD_MMD_CLAUSE45_PCS 0x3 /* enum */ +#define MC_CMD_MMD_CLAUSE45_PHYXS 0x4 /* enum */ +#define MC_CMD_MMD_CLAUSE45_DTEXS 0x5 /* enum */ +#define MC_CMD_MMD_CLAUSE45_TC 0x6 /* enum */ +#define MC_CMD_MMD_CLAUSE45_AN 0x7 /* enum */ +/* enum: Clause22 proxied over clause45 by PHY. */ +#define MC_CMD_MMD_CLAUSE45_C22EXT 0x1d +#define MC_CMD_MMD_CLAUSE45_VEND1 0x1e /* enum */ +#define MC_CMD_MMD_CLAUSE45_VEND2 0x1f /* enum */ +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_OFST 52 +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN 20 + + +/***********************************/ +/* MC_CMD_GET_LINK + * Read the unified MAC/PHY link state. Locks required: None Return code: 0, + * ETIME. + */ +#define MC_CMD_GET_LINK 0x29 +#undef MC_CMD_0x29_PRIVILEGE_CTG + +#define MC_CMD_0x29_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_LINK_IN msgrequest */ +#define MC_CMD_GET_LINK_IN_LEN 0 + +/* MC_CMD_GET_LINK_OUT msgresponse */ +#define MC_CMD_GET_LINK_OUT_LEN 28 +/* near-side advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_CAP_OFST 0 +/* link-partner advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4 +/* Autonegotiated speed in mbit/s. The link may still be down even if this + * reads non-zero. + */ +#define MC_CMD_GET_LINK_OUT_LINK_SPEED_OFST 8 +/* Current loopback setting. */ +#define MC_CMD_GET_LINK_OUT_LOOPBACK_MODE_OFST 12 +/* Enum values, see field(s): */ +/* MC_CMD_GET_LOOPBACK_MODES/MC_CMD_GET_LOOPBACK_MODES_OUT/100M */ +#define MC_CMD_GET_LINK_OUT_FLAGS_OFST 16 +#define MC_CMD_GET_LINK_OUT_LINK_UP_LBN 0 +#define MC_CMD_GET_LINK_OUT_LINK_UP_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN 1 +#define MC_CMD_GET_LINK_OUT_FULL_DUPLEX_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_BPX_LINK_LBN 2 +#define MC_CMD_GET_LINK_OUT_BPX_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_PHY_LINK_LBN 3 +#define MC_CMD_GET_LINK_OUT_PHY_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_RX_LBN 6 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_RX_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_TX_LBN 7 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_TX_WIDTH 1 +/* This returns the negotiated flow control value. */ +#define MC_CMD_GET_LINK_OUT_FCNTL_OFST 20 +/* Enum values, see field(s): */ +/* MC_CMD_SET_MAC/MC_CMD_SET_MAC_IN/FCNTL */ +#define MC_CMD_GET_LINK_OUT_MAC_FAULT_OFST 24 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_PENDING_RECONFIG_LBN 3 +#define MC_CMD_MAC_FAULT_PENDING_RECONFIG_WIDTH 1 + + +/***********************************/ +/* MC_CMD_SET_MAC + * Set MAC configuration. Locks required: None. Return code: 0, EINVAL + */ +#define MC_CMD_SET_MAC 0x2c +#undef MC_CMD_0x2c_PRIVILEGE_CTG + +#define MC_CMD_0x2c_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_SET_MAC_IN msgrequest */ +#define MC_CMD_SET_MAC_IN_LEN 28 +/* The MTU is the MTU programmed directly into the XMAC/GMAC (inclusive of + * EtherII, VLAN, bug16011 padding). + */ +#define MC_CMD_SET_MAC_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_IN_ADDR_LEN 8 +#define MC_CMD_SET_MAC_IN_ADDR_LO_OFST 8 +#define MC_CMD_SET_MAC_IN_ADDR_HI_OFST 12 +#define MC_CMD_SET_MAC_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_FCNTL_OFST 20 +/* enum: Flow control is off. */ +#define MC_CMD_FCNTL_OFF 0x0 +/* enum: Respond to flow control. */ +#define MC_CMD_FCNTL_RESPOND 0x1 +/* enum: Respond to and Issue flow control. */ +#define MC_CMD_FCNTL_BIDIR 0x2 +/* enum: Auto neg flow control. */ +#define MC_CMD_FCNTL_AUTO 0x3 +/* enum: Priority flow control (eftest builds only). */ +#define MC_CMD_FCNTL_QBB 0x4 +/* enum: Issue flow control. */ +#define MC_CMD_FCNTL_GENERATE 0x5 +#define MC_CMD_SET_MAC_IN_FLAGS_OFST 24 +#define MC_CMD_SET_MAC_IN_FLAG_INCLUDE_FCS_LBN 0 +#define MC_CMD_SET_MAC_IN_FLAG_INCLUDE_FCS_WIDTH 1 + +/* MC_CMD_SET_MAC_EXT_IN msgrequest */ +#define MC_CMD_SET_MAC_EXT_IN_LEN 32 +/* The MTU is the MTU programmed directly into the XMAC/GMAC (inclusive of + * EtherII, VLAN, bug16011 padding). + */ +#define MC_CMD_SET_MAC_EXT_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_EXT_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_LEN 8 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_LO_OFST 8 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_HI_OFST 12 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_FCNTL_OFST 20 +/* enum: Flow control is off. */ +/* MC_CMD_FCNTL_OFF 0x0 */ +/* enum: Respond to flow control. */ +/* MC_CMD_FCNTL_RESPOND 0x1 */ +/* enum: Respond to and Issue flow control. */ +/* MC_CMD_FCNTL_BIDIR 0x2 */ +/* enum: Auto neg flow control. */ +/* MC_CMD_FCNTL_AUTO 0x3 */ +/* enum: Priority flow control (eftest builds only). */ +/* MC_CMD_FCNTL_QBB 0x4 */ +/* enum: Issue flow control. */ +/* MC_CMD_FCNTL_GENERATE 0x5 */ +#define MC_CMD_SET_MAC_EXT_IN_FLAGS_OFST 24 +#define MC_CMD_SET_MAC_EXT_IN_FLAG_INCLUDE_FCS_LBN 0 +#define MC_CMD_SET_MAC_EXT_IN_FLAG_INCLUDE_FCS_WIDTH 1 +/* Select which parameters to configure. A parameter will only be modified if + * the corresponding control flag is set. If SET_MAC_ENHANCED is not set in + * capabilities then this field is ignored (and all flags are assumed to be + * set). + */ +#define MC_CMD_SET_MAC_EXT_IN_CONTROL_OFST 28 +#define MC_CMD_SET_MAC_EXT_IN_CFG_MTU_LBN 0 +#define MC_CMD_SET_MAC_EXT_IN_CFG_MTU_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_DRAIN_LBN 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_DRAIN_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_REJECT_LBN 2 +#define MC_CMD_SET_MAC_EXT_IN_CFG_REJECT_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCNTL_LBN 3 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCNTL_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCS_LBN 4 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCS_WIDTH 1 + +/* MC_CMD_SET_MAC_OUT msgresponse */ +#define MC_CMD_SET_MAC_OUT_LEN 0 + +/* MC_CMD_SET_MAC_V2_OUT msgresponse */ +#define MC_CMD_SET_MAC_V2_OUT_LEN 4 +/* MTU as configured after processing the request. See comment at + * MC_CMD_SET_MAC_IN/MTU. To query MTU without doing any changes, set CONTROL + * to 0. + */ +#define MC_CMD_SET_MAC_V2_OUT_MTU_OFST 0 + + +/***********************************/ +/* MC_CMD_REBOOT + * Reboot the MC. + * + * The AFTER_ASSERTION flag is intended to be used when the driver notices an + * assertion failure (at which point it is expected to perform a complete tear + * down and reinitialise), to allow both ports to reset the MC once in an + * atomic fashion. + * + * Production mc firmwares are generally compiled with REBOOT_ON_ASSERT=1, + * which means that they will automatically reboot out of the assertion + * handler, so this is in practise an optional operation. It is still + * recommended that drivers execute this to support custom firmwares with + * REBOOT_ON_ASSERT=0. + * + * Locks required: NONE Returns: Nothing. You get back a response with ERR=1, + * DATALEN=0 + */ +#define MC_CMD_REBOOT 0x3d +#undef MC_CMD_0x3d_PRIVILEGE_CTG + +#define MC_CMD_0x3d_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_REBOOT_IN msgrequest */ +#define MC_CMD_REBOOT_IN_LEN 4 +#define MC_CMD_REBOOT_IN_FLAGS_OFST 0 +#define MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION 0x1 /* enum */ + +/* MC_CMD_REBOOT_OUT msgresponse */ +#define MC_CMD_REBOOT_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_REBOOT_MODE + * Set the mode for the next MC reboot. Locks required: NONE. Sets the reboot + * mode to the specified value. Returns the old mode. + */ +#define MC_CMD_REBOOT_MODE 0x3f +#undef MC_CMD_0x3f_PRIVILEGE_CTG + +#define MC_CMD_0x3f_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_REBOOT_MODE_IN msgrequest */ +#define MC_CMD_REBOOT_MODE_IN_LEN 4 +#define MC_CMD_REBOOT_MODE_IN_VALUE_OFST 0 +/* enum: Normal. */ +#define MC_CMD_REBOOT_MODE_NORMAL 0x0 +/* enum: Power-on Reset. */ +#define MC_CMD_REBOOT_MODE_POR 0x2 +/* enum: Snapper. */ +#define MC_CMD_REBOOT_MODE_SNAPPER 0x3 +/* enum: snapper fake POR */ +#define MC_CMD_REBOOT_MODE_SNAPPER_POR 0x4 +#define MC_CMD_REBOOT_MODE_IN_FAKE_LBN 7 +#define MC_CMD_REBOOT_MODE_IN_FAKE_WIDTH 1 + +/* MC_CMD_REBOOT_MODE_OUT msgresponse */ +#define MC_CMD_REBOOT_MODE_OUT_LEN 4 +#define MC_CMD_REBOOT_MODE_OUT_VALUE_OFST 0 + + +/***********************************/ +/* MC_CMD_WORKAROUND + * Enable/Disable a given workaround. The mcfw will return EINVAL if it doesn't + * understand the given workaround number - which should not be treated as a + * hard error by client code. This op does not imply any semantics about each + * workaround, that's between the driver and the mcfw on a per-workaround + * basis. Locks required: None. Returns: 0, EINVAL . + */ +#define MC_CMD_WORKAROUND 0x4a +#undef MC_CMD_0x4a_PRIVILEGE_CTG + +#define MC_CMD_0x4a_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_WORKAROUND_IN msgrequest */ +#define MC_CMD_WORKAROUND_IN_LEN 8 +/* The enums here must correspond with those in MC_CMD_GET_WORKAROUND. */ +#define MC_CMD_WORKAROUND_IN_TYPE_OFST 0 +/* enum: Bug 17230 work around. */ +#define MC_CMD_WORKAROUND_BUG17230 0x1 +/* enum: Bug 35388 work around (unsafe EVQ writes). */ +#define MC_CMD_WORKAROUND_BUG35388 0x2 +/* enum: Bug35017 workaround (A64 tables must be identity map) */ +#define MC_CMD_WORKAROUND_BUG35017 0x3 +/* enum: Bug 41750 present (MC_CMD_TRIGGER_INTERRUPT won't work) */ +#define MC_CMD_WORKAROUND_BUG41750 0x4 +/* enum: Bug 42008 present (Interrupts can overtake associated events). Caution + * - before adding code that queries this workaround, remember that there's + * released Monza firmware that doesn't understand MC_CMD_WORKAROUND_BUG42008, + * and will hence (incorrectly) report that the bug doesn't exist. + */ +#define MC_CMD_WORKAROUND_BUG42008 0x5 +/* enum: Bug 26807 features present in firmware (multicast filter chaining) + * This feature cannot be turned on/off while there are any filters already + * present. The behaviour in such case depends on the acting client's privilege + * level. If the client has the admin privilege, then all functions that have + * filters installed will be FLRed and the FLR_DONE flag will be set. Otherwise + * the command will fail with MC_CMD_ERR_FILTERS_PRESENT. + */ +#define MC_CMD_WORKAROUND_BUG26807 0x6 +/* enum: Bug 61265 work around (broken EVQ TMR writes). */ +#define MC_CMD_WORKAROUND_BUG61265 0x7 +/* 0 = disable the workaround indicated by TYPE; any non-zero value = enable + * the workaround + */ +#define MC_CMD_WORKAROUND_IN_ENABLED_OFST 4 + +/* MC_CMD_WORKAROUND_OUT msgresponse */ +#define MC_CMD_WORKAROUND_OUT_LEN 0 + +/* MC_CMD_WORKAROUND_EXT_OUT msgresponse: This response format will be used + * when (TYPE == MC_CMD_WORKAROUND_BUG26807) + */ +#define MC_CMD_WORKAROUND_EXT_OUT_LEN 4 +#define MC_CMD_WORKAROUND_EXT_OUT_FLAGS_OFST 0 +#define MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_LBN 0 +#define MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_WIDTH 1 + + +/***********************************/ +/* MC_CMD_GET_MAC_ADDRESSES + * Returns the base MAC, count and stride for the requesting function + */ +#define MC_CMD_GET_MAC_ADDRESSES 0x55 +#undef MC_CMD_0x55_PRIVILEGE_CTG + +#define MC_CMD_0x55_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_MAC_ADDRESSES_IN msgrequest */ +#define MC_CMD_GET_MAC_ADDRESSES_IN_LEN 0 + +/* MC_CMD_GET_MAC_ADDRESSES_OUT msgresponse */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_LEN 16 +/* Base MAC address */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE_OFST 0 +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE_LEN 6 +/* Padding */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_RESERVED_OFST 6 +#define MC_CMD_GET_MAC_ADDRESSES_OUT_RESERVED_LEN 2 +/* Number of allocated MAC addresses */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_COUNT_OFST 8 +/* Spacing of allocated MAC addresses */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_STRIDE_OFST 12 + + +/***********************************/ +/* MC_CMD_GET_WORKAROUNDS + * Read the list of all implemented and all currently enabled workarounds. The + * enums here must correspond with those in MC_CMD_WORKAROUND. + */ +#define MC_CMD_GET_WORKAROUNDS 0x59 +#undef MC_CMD_0x59_PRIVILEGE_CTG + +#define MC_CMD_0x59_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_WORKAROUNDS_OUT msgresponse */ +#define MC_CMD_GET_WORKAROUNDS_OUT_LEN 8 +/* Each workaround is represented by a single bit according to the enums below. + */ +#define MC_CMD_GET_WORKAROUNDS_OUT_IMPLEMENTED_OFST 0 +#define MC_CMD_GET_WORKAROUNDS_OUT_ENABLED_OFST 4 +/* enum: Bug 17230 work around. */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG17230 0x2 +/* enum: Bug 35388 work around (unsafe EVQ writes). */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG35388 0x4 +/* enum: Bug35017 workaround (A64 tables must be identity map) */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG35017 0x8 +/* enum: Bug 41750 present (MC_CMD_TRIGGER_INTERRUPT won't work) */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG41750 0x10 +/* enum: Bug 42008 present (Interrupts can overtake associated events). Caution + * - before adding code that queries this workaround, remember that there's + * released Monza firmware that doesn't understand MC_CMD_WORKAROUND_BUG42008, + * and will hence (incorrectly) report that the bug doesn't exist. + */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG42008 0x20 +/* enum: Bug 26807 features present in firmware (multicast filter chaining) */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG26807 0x40 +/* enum: Bug 61265 work around (broken EVQ TMR writes). */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG61265 0x80 + + +/***********************************/ +/* MC_CMD_V2_EXTN + * Encapsulation for a v2 extended command + */ +#define MC_CMD_V2_EXTN 0x7f + +/* MC_CMD_V2_EXTN_IN msgrequest */ +#define MC_CMD_V2_EXTN_IN_LEN 4 +/* the extended command number */ +#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_LBN 0 +#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_WIDTH 15 +#define MC_CMD_V2_EXTN_IN_UNUSED_LBN 15 +#define MC_CMD_V2_EXTN_IN_UNUSED_WIDTH 1 +/* the actual length of the encapsulated command (which is not in the v1 + * header) + */ +#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_LBN 16 +#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_WIDTH 10 +#define MC_CMD_V2_EXTN_IN_UNUSED2_LBN 26 +#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 2 +/* Type of command/response */ +#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_LBN 28 +#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_WIDTH 4 +/* enum: MCDI command directed to or response originating from the MC. */ +#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_MC 0x0 +/* enum: MCDI command directed to a TSA controller. MCDI responses of this type + * are not defined. + */ +#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_TSA 0x1 + + +/***********************************/ +/* MC_CMD_INIT_EVQ + * Set up an event queue according to the supplied parameters. The IN arguments + * end with an address for each 4k of host memory required to back the EVQ. + */ +#define MC_CMD_INIT_EVQ 0x80 +#undef MC_CMD_0x80_PRIVILEGE_CTG + +#define MC_CMD_0x80_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_INIT_EVQ_IN msgrequest */ +#define MC_CMD_INIT_EVQ_IN_LENMIN 44 +#define MC_CMD_INIT_EVQ_IN_LENMAX 548 +#define MC_CMD_INIT_EVQ_IN_LEN(num) (36+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_EVQ_IN_SIZE_OFST 0 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_EVQ_IN_INSTANCE_OFST 4 +/* The initial timer value. The load value is ignored if the timer mode is DIS. + */ +#define MC_CMD_INIT_EVQ_IN_TMR_LOAD_OFST 8 +/* The reload value is ignored in one-shot modes */ +#define MC_CMD_INIT_EVQ_IN_TMR_RELOAD_OFST 12 +/* tbd */ +#define MC_CMD_INIT_EVQ_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_EVQ_IN_FLAG_INTERRUPTING_LBN 0 +#define MC_CMD_INIT_EVQ_IN_FLAG_INTERRUPTING_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_RPTR_DOS_LBN 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_RPTR_DOS_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_INT_ARMD_LBN 2 +#define MC_CMD_INIT_EVQ_IN_FLAG_INT_ARMD_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_CUT_THRU_LBN 3 +#define MC_CMD_INIT_EVQ_IN_FLAG_CUT_THRU_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_RX_MERGE_LBN 4 +#define MC_CMD_INIT_EVQ_IN_FLAG_RX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_TX_MERGE_LBN 5 +#define MC_CMD_INIT_EVQ_IN_FLAG_TX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_USE_TIMER_LBN 6 +#define MC_CMD_INIT_EVQ_IN_FLAG_USE_TIMER_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_TMR_MODE_OFST 20 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS 0x0 +/* enum: Immediate */ +#define MC_CMD_INIT_EVQ_IN_TMR_IMMED_START 0x1 +/* enum: Triggered */ +#define MC_CMD_INIT_EVQ_IN_TMR_TRIG_START 0x2 +/* enum: Hold-off */ +#define MC_CMD_INIT_EVQ_IN_TMR_INT_HLDOFF 0x3 +/* Target EVQ for wakeups if in wakeup mode. */ +#define MC_CMD_INIT_EVQ_IN_TARGET_EVQ_OFST 24 +/* Target interrupt if in interrupting mode (note union with target EVQ). Use + * MC_CMD_RESOURCE_INSTANCE_ANY unless a specific one required for test + * purposes. + */ +#define MC_CMD_INIT_EVQ_IN_IRQ_NUM_OFST 24 +/* Event Counter Mode. */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_OFST 28 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS 0x0 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_RX 0x1 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_TX 0x2 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_RXTX 0x3 +/* Event queue packet count threshold. */ +#define MC_CMD_INIT_EVQ_IN_COUNT_THRSHLD_OFST 32 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_OFST 36 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_LO_OFST 36 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_HI_OFST 40 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_MAXNUM 64 + +/* MC_CMD_INIT_EVQ_OUT msgresponse */ +#define MC_CMD_INIT_EVQ_OUT_LEN 4 +/* Only valid if INTRFLAG was true */ +#define MC_CMD_INIT_EVQ_OUT_IRQ_OFST 0 + +/* MC_CMD_INIT_EVQ_V2_IN msgrequest */ +#define MC_CMD_INIT_EVQ_V2_IN_LENMIN 44 +#define MC_CMD_INIT_EVQ_V2_IN_LENMAX 548 +#define MC_CMD_INIT_EVQ_V2_IN_LEN(num) (36+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_EVQ_V2_IN_SIZE_OFST 0 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_EVQ_V2_IN_INSTANCE_OFST 4 +/* The initial timer value. The load value is ignored if the timer mode is DIS. + */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_LOAD_OFST 8 +/* The reload value is ignored in one-shot modes */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_RELOAD_OFST 12 +/* tbd */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INTERRUPTING_LBN 0 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INTERRUPTING_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RPTR_DOS_LBN 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RPTR_DOS_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INT_ARMD_LBN 2 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INT_ARMD_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_CUT_THRU_LBN 3 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_CUT_THRU_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RX_MERGE_LBN 4 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TX_MERGE_LBN 5 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_USE_TIMER_LBN 6 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_USE_TIMER_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_LBN 7 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_WIDTH 4 +/* enum: All initialisation flags specified by host. */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_MANUAL 0x0 +/* enum: MEDFORD only. Certain initialisation flags specified by host may be + * over-ridden by firmware based on licenses and firmware variant in order to + * provide the lowest latency achievable. See + * MC_CMD_INIT_EVQ_V2/MC_CMD_INIT_EVQ_V2_OUT/FLAGS for list of affected flags. + */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_LOW_LATENCY 0x1 +/* enum: MEDFORD only. Certain initialisation flags specified by host may be + * over-ridden by firmware based on licenses and firmware variant in order to + * provide the best throughput achievable. See + * MC_CMD_INIT_EVQ_V2/MC_CMD_INIT_EVQ_V2_OUT/FLAGS for list of affected flags. + */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_THROUGHPUT 0x2 +/* enum: MEDFORD only. Certain initialisation flags may be over-ridden by + * firmware based on licenses and firmware variant. See + * MC_CMD_INIT_EVQ_V2/MC_CMD_INIT_EVQ_V2_OUT/FLAGS for list of affected flags. + */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO 0x3 +#define MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_OFST 20 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_DIS 0x0 +/* enum: Immediate */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_IMMED_START 0x1 +/* enum: Triggered */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_TRIG_START 0x2 +/* enum: Hold-off */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_INT_HLDOFF 0x3 +/* Target EVQ for wakeups if in wakeup mode. */ +#define MC_CMD_INIT_EVQ_V2_IN_TARGET_EVQ_OFST 24 +/* Target interrupt if in interrupting mode (note union with target EVQ). Use + * MC_CMD_RESOURCE_INSTANCE_ANY unless a specific one required for test + * purposes. + */ +#define MC_CMD_INIT_EVQ_V2_IN_IRQ_NUM_OFST 24 +/* Event Counter Mode. */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_OFST 28 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_DIS 0x0 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_RX 0x1 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_TX 0x2 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_RXTX 0x3 +/* Event queue packet count threshold. */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_THRSHLD_OFST 32 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_OFST 36 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_LO_OFST 36 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_HI_OFST 40 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_MAXNUM 64 + +/* MC_CMD_INIT_EVQ_V2_OUT msgresponse */ +#define MC_CMD_INIT_EVQ_V2_OUT_LEN 8 +/* Only valid if INTRFLAG was true */ +#define MC_CMD_INIT_EVQ_V2_OUT_IRQ_OFST 0 +/* Actual configuration applied on the card */ +#define MC_CMD_INIT_EVQ_V2_OUT_FLAGS_OFST 4 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_CUT_THRU_LBN 0 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_CUT_THRU_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RX_MERGE_LBN 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_TX_MERGE_LBN 2 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_TX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RXQ_FORCE_EV_MERGING_LBN 3 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RXQ_FORCE_EV_MERGING_WIDTH 1 + +/* QUEUE_CRC_MODE structuredef */ +#define QUEUE_CRC_MODE_LEN 1 +#define QUEUE_CRC_MODE_MODE_LBN 0 +#define QUEUE_CRC_MODE_MODE_WIDTH 4 +/* enum: No CRC. */ +#define QUEUE_CRC_MODE_NONE 0x0 +/* enum: CRC Fiber channel over ethernet. */ +#define QUEUE_CRC_MODE_FCOE 0x1 +/* enum: CRC (digest) iSCSI header only. */ +#define QUEUE_CRC_MODE_ISCSI_HDR 0x2 +/* enum: CRC (digest) iSCSI header and payload. */ +#define QUEUE_CRC_MODE_ISCSI 0x3 +/* enum: CRC Fiber channel over IP over ethernet. */ +#define QUEUE_CRC_MODE_FCOIPOE 0x4 +/* enum: CRC MPA. */ +#define QUEUE_CRC_MODE_MPA 0x5 +#define QUEUE_CRC_MODE_SPARE_LBN 4 +#define QUEUE_CRC_MODE_SPARE_WIDTH 4 + + +/***********************************/ +/* MC_CMD_INIT_RXQ + * set up a receive queue according to the supplied parameters. The IN + * arguments end with an address for each 4k of host memory required to back + * the RXQ. + */ +#define MC_CMD_INIT_RXQ 0x81 +#undef MC_CMD_0x81_PRIVILEGE_CTG + +#define MC_CMD_0x81_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_INIT_RXQ_IN msgrequest: Legacy RXQ_INIT request. Use extended version + * in new code. + */ +#define MC_CMD_INIT_RXQ_IN_LENMIN 36 +#define MC_CMD_INIT_RXQ_IN_LENMAX 252 +#define MC_CMD_INIT_RXQ_IN_LEN(num) (28+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_RXQ_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to INIT_EVQ + */ +#define MC_CMD_INIT_RXQ_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_RXQ_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_RXQ_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_RXQ_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_RXQ_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_RXQ_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_HDR_SPLIT_LBN 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_HDR_SPLIT_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_TIMESTAMP_LBN 2 +#define MC_CMD_INIT_RXQ_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_CRC_MODE_LBN 3 +#define MC_CMD_INIT_RXQ_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_RXQ_IN_FLAG_CHAIN_LBN 7 +#define MC_CMD_INIT_RXQ_IN_FLAG_CHAIN_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_PREFIX_LBN 8 +#define MC_CMD_INIT_RXQ_IN_FLAG_PREFIX_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_DISABLE_SCATTER_LBN 9 +#define MC_CMD_INIT_RXQ_IN_FLAG_DISABLE_SCATTER_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_UNUSED_LBN 10 +#define MC_CMD_INIT_RXQ_IN_UNUSED_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_RXQ_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_RXQ_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_MAXNUM 28 + +/* MC_CMD_INIT_RXQ_EXT_IN msgrequest: Extended RXQ_INIT with additional mode + * flags + */ +#define MC_CMD_INIT_RXQ_EXT_IN_LEN 544 +/* Size, in entries */ +#define MC_CMD_INIT_RXQ_EXT_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to INIT_EVQ + */ +#define MC_CMD_INIT_RXQ_EXT_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_RXQ_EXT_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_RXQ_EXT_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_RXQ_EXT_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_HDR_SPLIT_LBN 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_HDR_SPLIT_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_TIMESTAMP_LBN 2 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_CRC_MODE_LBN 3 +#define MC_CMD_INIT_RXQ_EXT_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_CHAIN_LBN 7 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_CHAIN_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_PREFIX_LBN 8 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_PREFIX_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_DISABLE_SCATTER_LBN 9 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_DISABLE_SCATTER_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_MODE_LBN 10 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_MODE_WIDTH 4 +/* enum: One packet per descriptor (for normal networking) */ +#define MC_CMD_INIT_RXQ_EXT_IN_SINGLE_PACKET 0x0 +/* enum: Pack multiple packets into large descriptors (for SolarCapture) */ +#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM 0x1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_SNAPSHOT_MODE_LBN 14 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_SNAPSHOT_MODE_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE_LBN 15 +#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE_WIDTH 3 +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_1M 0x0 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_512K 0x1 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_256K 0x2 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_128K 0x3 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_64K 0x4 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES_LBN 18 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_FORCE_EV_MERGING_LBN 19 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_FORCE_EV_MERGING_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_RXQ_EXT_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_RXQ_EXT_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_NUM 64 +/* Maximum length of packet to receive, if SNAPSHOT_MODE flag is set */ +#define MC_CMD_INIT_RXQ_EXT_IN_SNAPSHOT_LENGTH_OFST 540 + +/* MC_CMD_INIT_RXQ_OUT msgresponse */ +#define MC_CMD_INIT_RXQ_OUT_LEN 0 + +/* MC_CMD_INIT_RXQ_EXT_OUT msgresponse */ +#define MC_CMD_INIT_RXQ_EXT_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_INIT_TXQ + */ +#define MC_CMD_INIT_TXQ 0x82 +#undef MC_CMD_0x82_PRIVILEGE_CTG + +#define MC_CMD_0x82_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_INIT_TXQ_IN msgrequest: Legacy INIT_TXQ request. Use extended version + * in new code. + */ +#define MC_CMD_INIT_TXQ_IN_LENMIN 36 +#define MC_CMD_INIT_TXQ_IN_LENMAX 252 +#define MC_CMD_INIT_TXQ_IN_LEN(num) (28+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_TXQ_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to + * INIT_EVQ. + */ +#define MC_CMD_INIT_TXQ_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_TXQ_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_TXQ_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_TXQ_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_TXQ_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_TXQ_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_IP_CSUM_DIS_LBN 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_IP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_CSUM_DIS_LBN 2 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_UDP_ONLY_LBN 3 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_UDP_ONLY_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_CRC_MODE_LBN 4 +#define MC_CMD_INIT_TXQ_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_TXQ_IN_FLAG_TIMESTAMP_LBN 8 +#define MC_CMD_INIT_TXQ_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_PACER_BYPASS_LBN 9 +#define MC_CMD_INIT_TXQ_IN_FLAG_PACER_BYPASS_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_IP_CSUM_EN_LBN 10 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_IP_CSUM_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_TCP_CSUM_EN_LBN 11 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_TCP_CSUM_EN_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_TXQ_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_TXQ_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_MAXNUM 28 + +/* MC_CMD_INIT_TXQ_EXT_IN msgrequest: Extended INIT_TXQ with additional mode + * flags + */ +#define MC_CMD_INIT_TXQ_EXT_IN_LEN 544 +/* Size, in entries */ +#define MC_CMD_INIT_TXQ_EXT_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to + * INIT_EVQ. + */ +#define MC_CMD_INIT_TXQ_EXT_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_TXQ_EXT_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_TXQ_EXT_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_TXQ_EXT_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_IP_CSUM_DIS_LBN 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_IP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_CSUM_DIS_LBN 2 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_UDP_ONLY_LBN 3 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_UDP_ONLY_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_CRC_MODE_LBN 4 +#define MC_CMD_INIT_TXQ_EXT_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TIMESTAMP_LBN 8 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_PACER_BYPASS_LBN 9 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_PACER_BYPASS_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_IP_CSUM_EN_LBN 10 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_IP_CSUM_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_TCP_CSUM_EN_LBN 11 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_TCP_CSUM_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TSOV2_EN_LBN 12 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TSOV2_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_CTPIO_LBN 13 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_CTPIO_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_TXQ_EXT_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_TXQ_EXT_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_MAXNUM 64 +/* Flags related to Qbb flow control mode. */ +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_FLAGS_OFST 540 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_ENABLE_LBN 0 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_ENABLE_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_PRIORITY_LBN 1 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_PRIORITY_WIDTH 3 + +/* MC_CMD_INIT_TXQ_OUT msgresponse */ +#define MC_CMD_INIT_TXQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FINI_EVQ + * Teardown an EVQ. + * + * All DMAQs or EVQs that point to the EVQ to tear down must be torn down first + * or the operation will fail with EBUSY + */ +#define MC_CMD_FINI_EVQ 0x83 +#undef MC_CMD_0x83_PRIVILEGE_CTG + +#define MC_CMD_0x83_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FINI_EVQ_IN msgrequest */ +#define MC_CMD_FINI_EVQ_IN_LEN 4 +/* Instance of EVQ to destroy. Should be the same instance as that previously + * passed to INIT_EVQ + */ +#define MC_CMD_FINI_EVQ_IN_INSTANCE_OFST 0 + +/* MC_CMD_FINI_EVQ_OUT msgresponse */ +#define MC_CMD_FINI_EVQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FINI_RXQ + * Teardown a RXQ. + */ +#define MC_CMD_FINI_RXQ 0x84 +#undef MC_CMD_0x84_PRIVILEGE_CTG + +#define MC_CMD_0x84_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FINI_RXQ_IN msgrequest */ +#define MC_CMD_FINI_RXQ_IN_LEN 4 +/* Instance of RXQ to destroy */ +#define MC_CMD_FINI_RXQ_IN_INSTANCE_OFST 0 + +/* MC_CMD_FINI_RXQ_OUT msgresponse */ +#define MC_CMD_FINI_RXQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FINI_TXQ + * Teardown a TXQ. + */ +#define MC_CMD_FINI_TXQ 0x85 +#undef MC_CMD_0x85_PRIVILEGE_CTG + +#define MC_CMD_0x85_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FINI_TXQ_IN msgrequest */ +#define MC_CMD_FINI_TXQ_IN_LEN 4 +/* Instance of TXQ to destroy */ +#define MC_CMD_FINI_TXQ_IN_INSTANCE_OFST 0 + +/* MC_CMD_FINI_TXQ_OUT msgresponse */ +#define MC_CMD_FINI_TXQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FILTER_OP + * Multiplexed MCDI call for filter operations + */ +#define MC_CMD_FILTER_OP 0x8a +#undef MC_CMD_0x8a_PRIVILEGE_CTG + +#define MC_CMD_0x8a_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FILTER_OP_IN msgrequest */ +#define MC_CMD_FILTER_OP_IN_LEN 108 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_IN_OP_OFST 0 +/* enum: single-recipient filter insert */ +#define MC_CMD_FILTER_OP_IN_OP_INSERT 0x0 +/* enum: single-recipient filter remove */ +#define MC_CMD_FILTER_OP_IN_OP_REMOVE 0x1 +/* enum: multi-recipient filter subscribe */ +#define MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE 0x2 +/* enum: multi-recipient filter unsubscribe */ +#define MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE 0x3 +/* enum: replace one recipient with another (warning - the filter handle may + * change) + */ +#define MC_CMD_FILTER_OP_IN_OP_REPLACE 0x4 +/* filter handle (for remove / unsubscribe operations) */ +#define MC_CMD_FILTER_OP_IN_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_IN_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_IN_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_IN_HANDLE_HI_OFST 8 +/* The port ID associated with the v-adaptor which should contain this filter. + */ +#define MC_CMD_FILTER_OP_IN_PORT_ID_OFST 12 +/* fields to include in match criteria */ +#define MC_CMD_FILTER_OP_IN_MATCH_FIELDS_OFST 16 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_IP_LBN 0 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_IP_LBN 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_MAC_LBN 2 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_PORT_LBN 3 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_MAC_LBN 4 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_PORT_LBN 5 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_ETHER_TYPE_LBN 6 +#define MC_CMD_FILTER_OP_IN_MATCH_ETHER_TYPE_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_INNER_VLAN_LBN 7 +#define MC_CMD_FILTER_OP_IN_MATCH_INNER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_OUTER_VLAN_LBN 8 +#define MC_CMD_FILTER_OP_IN_MATCH_OUTER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_IP_PROTO_LBN 9 +#define MC_CMD_FILTER_OP_IN_MATCH_IP_PROTO_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF0_LBN 10 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF0_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF1_LBN 11 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF1_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN 30 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN 31 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_WIDTH 1 +/* receive destination */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_OFST 20 +/* enum: drop packets */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_DROP 0x0 +/* enum: receive to host */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_HOST 0x1 +/* enum: receive to MC */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_MC 0x2 +/* enum: loop back to TXDP 0 */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_TX0 0x3 +/* enum: loop back to TXDP 1 */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_TX1 0x4 +/* receive queue handle (for multiple queue modes, this is the base queue) */ +#define MC_CMD_FILTER_OP_IN_RX_QUEUE_OFST 24 +/* receive mode */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_OFST 28 +/* enum: receive to just the specified queue */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE 0x0 +/* enum: receive to multiple queues using RSS context */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_RSS 0x1 +/* enum: receive to multiple queues using .1p mapping */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_DOT1P_MAPPING 0x2 +/* enum: install a filter entry that will never match; for test purposes only + */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000 +/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for + * RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or + * MC_CMD_DOT1P_MAPPING_ALLOC. + */ +#define MC_CMD_FILTER_OP_IN_RX_CONTEXT_OFST 32 +/* transmit domain (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_IN_TX_DOMAIN_OFST 36 +/* transmit destination (either set the MAC and/or PM bits for explicit + * control, or set this field to TX_DEST_DEFAULT for sensible default + * behaviour) + */ +#define MC_CMD_FILTER_OP_IN_TX_DEST_OFST 40 +/* enum: request default behaviour (based on filter type) */ +#define MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT 0xffffffff +#define MC_CMD_FILTER_OP_IN_TX_DEST_MAC_LBN 0 +#define MC_CMD_FILTER_OP_IN_TX_DEST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_TX_DEST_PM_LBN 1 +#define MC_CMD_FILTER_OP_IN_TX_DEST_PM_WIDTH 1 +/* source MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_SRC_MAC_OFST 44 +#define MC_CMD_FILTER_OP_IN_SRC_MAC_LEN 6 +/* source port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_SRC_PORT_OFST 50 +#define MC_CMD_FILTER_OP_IN_SRC_PORT_LEN 2 +/* destination MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_DST_MAC_OFST 52 +#define MC_CMD_FILTER_OP_IN_DST_MAC_LEN 6 +/* destination port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_DST_PORT_OFST 58 +#define MC_CMD_FILTER_OP_IN_DST_PORT_LEN 2 +/* Ethernet type to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_ETHER_TYPE_OFST 60 +#define MC_CMD_FILTER_OP_IN_ETHER_TYPE_LEN 2 +/* Inner VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_INNER_VLAN_OFST 62 +#define MC_CMD_FILTER_OP_IN_INNER_VLAN_LEN 2 +/* Outer VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_OUTER_VLAN_OFST 64 +#define MC_CMD_FILTER_OP_IN_OUTER_VLAN_LEN 2 +/* IP protocol to match (in low byte; set high byte to 0) */ +#define MC_CMD_FILTER_OP_IN_IP_PROTO_OFST 66 +#define MC_CMD_FILTER_OP_IN_IP_PROTO_LEN 2 +/* Firmware defined register 0 to match (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_IN_FWDEF0_OFST 68 +/* Firmware defined register 1 to match (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_IN_FWDEF1_OFST 72 +/* source IP address to match (as bytes in network order; set last 12 bytes to + * 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_IN_SRC_IP_OFST 76 +#define MC_CMD_FILTER_OP_IN_SRC_IP_LEN 16 +/* destination IP address to match (as bytes in network order; set last 12 + * bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_IN_DST_IP_OFST 92 +#define MC_CMD_FILTER_OP_IN_DST_IP_LEN 16 + +/* MC_CMD_FILTER_OP_EXT_IN msgrequest: Extension to MC_CMD_FILTER_OP_IN to + * include handling of VXLAN/NVGRE encapsulated frame filtering (which is + * supported on Medford only). + */ +#define MC_CMD_FILTER_OP_EXT_IN_LEN 172 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_EXT_IN_OP_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_IN/OP */ +/* filter handle (for remove / unsubscribe operations) */ +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_HI_OFST 8 +/* The port ID associated with the v-adaptor which should contain this filter. + */ +#define MC_CMD_FILTER_OP_EXT_IN_PORT_ID_OFST 12 +/* fields to include in match criteria */ +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_FIELDS_OFST 16 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_IP_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_IP_LBN 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_MAC_LBN 2 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_PORT_LBN 3 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_MAC_LBN 4 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_PORT_LBN 5 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_ETHER_TYPE_LBN 6 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_ETHER_TYPE_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_INNER_VLAN_LBN 7 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_INNER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_OUTER_VLAN_LBN 8 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_OUTER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IP_PROTO_LBN 9 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IP_PROTO_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_FWDEF0_LBN 10 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_FWDEF0_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_VNI_OR_VSID_LBN 11 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_VNI_OR_VSID_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_IP_LBN 12 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_IP_LBN 13 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_MAC_LBN 14 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_PORT_LBN 15 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_MAC_LBN 16 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_PORT_LBN 17 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_ETHER_TYPE_LBN 18 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_ETHER_TYPE_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_INNER_VLAN_LBN 19 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_INNER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_OUTER_VLAN_LBN 20 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_OUTER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_IP_PROTO_LBN 21 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_IP_PROTO_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF0_LBN 22 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF0_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF1_LBN 23 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF1_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_LBN 24 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_LBN 25 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST_LBN 30 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST_LBN 31 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST_WIDTH 1 +/* receive destination */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_OFST 20 +/* enum: drop packets */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_DROP 0x0 +/* enum: receive to host */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_HOST 0x1 +/* enum: receive to MC */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_MC 0x2 +/* enum: loop back to TXDP 0 */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX0 0x3 +/* enum: loop back to TXDP 1 */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX1 0x4 +/* receive queue handle (for multiple queue modes, this is the base queue) */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_QUEUE_OFST 24 +/* receive mode */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_OFST 28 +/* enum: receive to just the specified queue */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_SIMPLE 0x0 +/* enum: receive to multiple queues using RSS context */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_RSS 0x1 +/* enum: receive to multiple queues using .1p mapping */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_DOT1P_MAPPING 0x2 +/* enum: install a filter entry that will never match; for test purposes only + */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000 +/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for + * RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or + * MC_CMD_DOT1P_MAPPING_ALLOC. + */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_CONTEXT_OFST 32 +/* transmit domain (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_EXT_IN_TX_DOMAIN_OFST 36 +/* transmit destination (either set the MAC and/or PM bits for explicit + * control, or set this field to TX_DEST_DEFAULT for sensible default + * behaviour) + */ +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_OFST 40 +/* enum: request default behaviour (based on filter type) */ +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_DEFAULT 0xffffffff +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_MAC_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_PM_LBN 1 +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_PM_WIDTH 1 +/* source MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_SRC_MAC_OFST 44 +#define MC_CMD_FILTER_OP_EXT_IN_SRC_MAC_LEN 6 +/* source port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_SRC_PORT_OFST 50 +#define MC_CMD_FILTER_OP_EXT_IN_SRC_PORT_LEN 2 +/* destination MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_DST_MAC_OFST 52 +#define MC_CMD_FILTER_OP_EXT_IN_DST_MAC_LEN 6 +/* destination port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_DST_PORT_OFST 58 +#define MC_CMD_FILTER_OP_EXT_IN_DST_PORT_LEN 2 +/* Ethernet type to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_ETHER_TYPE_OFST 60 +#define MC_CMD_FILTER_OP_EXT_IN_ETHER_TYPE_LEN 2 +/* Inner VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_INNER_VLAN_OFST 62 +#define MC_CMD_FILTER_OP_EXT_IN_INNER_VLAN_LEN 2 +/* Outer VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_OUTER_VLAN_OFST 64 +#define MC_CMD_FILTER_OP_EXT_IN_OUTER_VLAN_LEN 2 +/* IP protocol to match (in low byte; set high byte to 0) */ +#define MC_CMD_FILTER_OP_EXT_IN_IP_PROTO_OFST 66 +#define MC_CMD_FILTER_OP_EXT_IN_IP_PROTO_LEN 2 +/* Firmware defined register 0 to match (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_EXT_IN_FWDEF0_OFST 68 +/* VNI (for VXLAN/Geneve, when IP protocol is UDP) or VSID (for NVGRE, when IP + * protocol is GRE) to match (as bytes in network order; set last byte to 0 for + * VXLAN/NVGRE, or 1 for Geneve) + */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_OR_VSID_OFST 72 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_VALUE_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_VALUE_WIDTH 24 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_LBN 24 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_WIDTH 8 +/* enum: Match VXLAN traffic with this VNI */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_VXLAN 0x0 +/* enum: Match Geneve traffic with this VNI */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_GENEVE 0x1 +/* enum: Reserved for experimental development use */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_EXPERIMENTAL 0xfe +#define MC_CMD_FILTER_OP_EXT_IN_VSID_VALUE_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_VSID_VALUE_WIDTH 24 +#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_LBN 24 +#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_WIDTH 8 +/* enum: Match NVGRE traffic with this VSID */ +#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_NVGRE 0x0 +/* source IP address to match (as bytes in network order; set last 12 bytes to + * 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_SRC_IP_OFST 76 +#define MC_CMD_FILTER_OP_EXT_IN_SRC_IP_LEN 16 +/* destination IP address to match (as bytes in network order; set last 12 + * bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_DST_IP_OFST 92 +#define MC_CMD_FILTER_OP_EXT_IN_DST_IP_LEN 16 +/* VXLAN/NVGRE inner frame source MAC address to match (as bytes in network + * order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_MAC_OFST 108 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_MAC_LEN 6 +/* VXLAN/NVGRE inner frame source port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_PORT_OFST 114 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_PORT_LEN 2 +/* VXLAN/NVGRE inner frame destination MAC address to match (as bytes in + * network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_MAC_OFST 116 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_MAC_LEN 6 +/* VXLAN/NVGRE inner frame destination port to match (as bytes in network + * order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_PORT_OFST 122 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_PORT_LEN 2 +/* VXLAN/NVGRE inner frame Ethernet type to match (as bytes in network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_ETHER_TYPE_OFST 124 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_ETHER_TYPE_LEN 2 +/* VXLAN/NVGRE inner frame Inner VLAN tag to match (as bytes in network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_INNER_VLAN_OFST 126 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_INNER_VLAN_LEN 2 +/* VXLAN/NVGRE inner frame Outer VLAN tag to match (as bytes in network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_OUTER_VLAN_OFST 128 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_OUTER_VLAN_LEN 2 +/* VXLAN/NVGRE inner frame IP protocol to match (in low byte; set high byte to + * 0) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_IP_PROTO_OFST 130 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_IP_PROTO_LEN 2 +/* VXLAN/NVGRE inner frame Firmware defined register 0 to match (reserved; set + * to 0) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_FWDEF0_OFST 132 +/* VXLAN/NVGRE inner frame Firmware defined register 1 to match (reserved; set + * to 0) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_FWDEF1_OFST 136 +/* VXLAN/NVGRE inner frame source IP address to match (as bytes in network + * order; set last 12 bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_IP_OFST 140 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_IP_LEN 16 +/* VXLAN/NVGRE inner frame destination IP address to match (as bytes in network + * order; set last 12 bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_IP_OFST 156 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_IP_LEN 16 + +/* MC_CMD_FILTER_OP_OUT msgresponse */ +#define MC_CMD_FILTER_OP_OUT_LEN 12 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_OUT_OP_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_IN/OP */ +/* Returned filter handle (for insert / subscribe operations). Note that these + * handles should be considered opaque to the host, although a value of + * 0xFFFFFFFF_FFFFFFFF is guaranteed never to be a valid handle. + */ +#define MC_CMD_FILTER_OP_OUT_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_OUT_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_OFST 8 +/* enum: guaranteed invalid filter handle (low 32 bits) */ +#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_INVALID 0xffffffff +/* enum: guaranteed invalid filter handle (high 32 bits) */ +#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_INVALID 0xffffffff + +/* MC_CMD_FILTER_OP_EXT_OUT msgresponse */ +#define MC_CMD_FILTER_OP_EXT_OUT_LEN 12 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_EXT_OUT_OP_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_EXT_IN/OP */ +/* Returned filter handle (for insert / subscribe operations). Note that these + * handles should be considered opaque to the host, although a value of + * 0xFFFFFFFF_FFFFFFFF is guaranteed never to be a valid handle. + */ +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_HI_OFST 8 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_OUT/HANDLE */ + + +/***********************************/ +/* MC_CMD_ALLOC_VIS + * Allocate VIs for current PCI function. + */ +#define MC_CMD_ALLOC_VIS 0x8b +#undef MC_CMD_0x8b_PRIVILEGE_CTG + +#define MC_CMD_0x8b_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_ALLOC_VIS_IN msgrequest */ +#define MC_CMD_ALLOC_VIS_IN_LEN 8 +/* The minimum number of VIs that is acceptable */ +#define MC_CMD_ALLOC_VIS_IN_MIN_VI_COUNT_OFST 0 +/* The maximum number of VIs that would be useful */ +#define MC_CMD_ALLOC_VIS_IN_MAX_VI_COUNT_OFST 4 + +/* MC_CMD_ALLOC_VIS_OUT msgresponse: Huntington-compatible VI_ALLOC request. + * Use extended version in new code. + */ +#define MC_CMD_ALLOC_VIS_OUT_LEN 8 +/* The number of VIs allocated on this function */ +#define MC_CMD_ALLOC_VIS_OUT_VI_COUNT_OFST 0 +/* The base absolute VI number allocated to this function. Required to + * correctly interpret wakeup events. + */ +#define MC_CMD_ALLOC_VIS_OUT_VI_BASE_OFST 4 + +/* MC_CMD_ALLOC_VIS_EXT_OUT msgresponse */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_LEN 12 +/* The number of VIs allocated on this function */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_VI_COUNT_OFST 0 +/* The base absolute VI number allocated to this function. Required to + * correctly interpret wakeup events. + */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_VI_BASE_OFST 4 +/* Function's port vi_shift value (always 0 on Huntington) */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_VI_SHIFT_OFST 8 + + +/***********************************/ +/* MC_CMD_FREE_VIS + * Free VIs for current PCI function. Any linked PIO buffers will be unlinked, + * but not freed. + */ +#define MC_CMD_FREE_VIS 0x8c +#undef MC_CMD_0x8c_PRIVILEGE_CTG + +#define MC_CMD_0x8c_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FREE_VIS_IN msgrequest */ +#define MC_CMD_FREE_VIS_IN_LEN 0 + +/* MC_CMD_FREE_VIS_OUT msgresponse */ +#define MC_CMD_FREE_VIS_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_GET_PORT_ASSIGNMENT + * Get port assignment for current PCI function. + */ +#define MC_CMD_GET_PORT_ASSIGNMENT 0xb8 +#undef MC_CMD_0xb8_PRIVILEGE_CTG + +#define MC_CMD_0xb8_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_PORT_ASSIGNMENT_IN msgrequest */ +#define MC_CMD_GET_PORT_ASSIGNMENT_IN_LEN 0 + +/* MC_CMD_GET_PORT_ASSIGNMENT_OUT msgresponse */ +#define MC_CMD_GET_PORT_ASSIGNMENT_OUT_LEN 4 +/* Identifies the port assignment for this function. */ +#define MC_CMD_GET_PORT_ASSIGNMENT_OUT_PORT_OFST 0 + + +/***********************************/ +/* MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS + * Configure UDP ports for tunnel encapsulation hardware acceleration. The + * parser-dispatcher will attempt to parse traffic on these ports as tunnel + * encapsulation PDUs and filter them using the tunnel encapsulation filter + * chain rather than the standard filter chain. Note that this command can + * cause all functions to see a reset. (Available on Medford only.) + */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS 0x117 +#undef MC_CMD_0x117_PRIVILEGE_CTG + +#define MC_CMD_0x117_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN msgrequest */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMIN 4 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX 68 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LEN(num) (4+4*(num)) +/* Flags */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS_OFST 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS_LEN 2 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING_LBN 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING_WIDTH 1 +/* The number of entries in the ENTRIES array */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES_OFST 2 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES_LEN 2 +/* Entries defining the UDP port to protocol mapping, each laid out as a + * TUNNEL_ENCAP_UDP_PORT_ENTRY + */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_OFST 4 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_LEN 4 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MINNUM 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM 16 + +/* MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT msgresponse */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN 2 +/* Flags */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS_OFST 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS_LEN 2 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_LBN 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_WIDTH 1 + + +#endif /* SFC_MCDI_PCOL_H */ diff --git a/src/drivers/net/sfc/mcdi.h b/src/drivers/net/sfc/mcdi.h new file mode 100644 index 000000000..19c62021a --- /dev/null +++ b/src/drivers/net/sfc/mcdi.h @@ -0,0 +1,164 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * + * Written by Martin Habets + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ +#ifndef SFC_MCDI_H +#define SFC_MCDI_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +#define MCDI_SEQ_MASK 0xf + +/* We expect that 16- and 32-bit fields in MCDI requests and responses + * are appropriately aligned, but 64-bit fields are only + * 32-bit-aligned. Also, on Siena we must copy to the MC shared + * memory strictly 32 bits at a time, so add any necessary padding. + */ +#define MCDI_DECLARE_BUF(_name, _len) \ + efx_dword_t _name[DIV_ROUND_UP(_len, 4)] +#define MCDI_DECLARE_BUF_OUT_OR_ERR(_name, _len) \ + MCDI_DECLARE_BUF(_name, max_t(size_t, _len, 8)) +#define _MCDI_PTR(_buf, _offset) \ + ((u8 *)(_buf) + (_offset)) +#define MCDI_PTR(_buf, _field) \ + _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST) +#define _MCDI_CHECK_ALIGN(_ofst, _align) \ + ((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1))) +#define _MCDI_DWORD(_buf, _field) \ + ((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2)) + +#define MCDI_WORD(_buf, _field) \ + ((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ + le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) +#define MCDI_SET_DWORD(_buf, _field, _value) \ + EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value) +#define MCDI_DWORD(_buf, _field) \ + EFX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0) +#define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1) \ + EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1) +#define MCDI_POPULATE_DWORD_2(_buf, _field, _name1, _value1, \ + _name2, _value2) \ + EFX_POPULATE_DWORD_2(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2) +#define MCDI_POPULATE_DWORD_3(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3) \ + EFX_POPULATE_DWORD_3(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3) +#define MCDI_POPULATE_DWORD_4(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4) \ + EFX_POPULATE_DWORD_4(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4) +#define MCDI_POPULATE_DWORD_5(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4, _name5, _value5) \ + EFX_POPULATE_DWORD_5(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4, \ + MC_CMD_ ## _name5, _value5) +#define MCDI_POPULATE_DWORD_6(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4, _name5, _value5, \ + _name6, _value6) \ + EFX_POPULATE_DWORD_6(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4, \ + MC_CMD_ ## _name5, _value5, \ + MC_CMD_ ## _name6, _value6) +#define MCDI_POPULATE_DWORD_7(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4, _name5, _value5, \ + _name6, _value6, _name7, _value7) \ + EFX_POPULATE_DWORD_7(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4, \ + MC_CMD_ ## _name5, _value5, \ + MC_CMD_ ## _name6, _value6, \ + MC_CMD_ ## _name7, _value7) +#define MCDI_SET_QWORD(_buf, _field, _value) \ + do { \ + EFX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[0], \ + EFX_DWORD_0, (u32)(_value)); \ + EFX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[1], \ + EFX_DWORD_0, (u64)(_value) >> 32); \ + } while (0) +#define MCDI_QWORD(_buf, _field) \ + (EFX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[0], EFX_DWORD_0) | \ + (u64)EFX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[1], EFX_DWORD_0) << 32) +#define MCDI_FIELD(_ptr, _type, _field) \ + EFX_EXTRACT_DWORD( \ + *(efx_dword_t *) \ + _MCDI_PTR(_ptr, MC_CMD_ ## _type ## _ ## _field ## _OFST & ~3),\ + MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f, \ + (MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f) + \ + MC_CMD_ ## _type ## _ ## _field ## _WIDTH - 1) + +#define _MCDI_ARRAY_PTR(_buf, _field, _index, _align) \ + (_MCDI_PTR(_buf, _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, _align))\ + + (_index) * _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _LEN, _align)) +#define MCDI_DECLARE_STRUCT_PTR(_name) \ + efx_dword_t *_name +#define MCDI_ARRAY_STRUCT_PTR(_buf, _field, _index) \ + ((efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4)) +#define MCDI_VAR_ARRAY_LEN(_len, _field) \ + min_t(size_t, MC_CMD_ ## _field ## _MAXNUM, \ + ((_len) - MC_CMD_ ## _field ## _OFST) / MC_CMD_ ## _field ## _LEN) +#define MCDI_ARRAY_WORD(_buf, _field, _index) \ + (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ + le16_to_cpu(*(__force const __le16 *) \ + _MCDI_ARRAY_PTR(_buf, _field, _index, 2))) +#define _MCDI_ARRAY_DWORD(_buf, _field, _index) \ + (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 4) + \ + (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4)) +#define MCDI_SET_ARRAY_DWORD(_buf, _field, _index, _value) \ + EFX_SET_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), \ + EFX_DWORD_0, _value) +#define MCDI_ARRAY_DWORD(_buf, _field, _index) \ + EFX_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), EFX_DWORD_0) +#define _MCDI_ARRAY_QWORD(_buf, _field, _index) \ + (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 8) + \ + (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4)) +#define MCDI_SET_ARRAY_QWORD(_buf, _field, _index, _value) \ + do { \ + EFX_SET_DWORD_FIELD(_MCDI_ARRAY_QWORD(_buf, _field, _index)[0],\ + EFX_DWORD_0, (u32)(_value)); \ + EFX_SET_DWORD_FIELD(_MCDI_ARRAY_QWORD(_buf, _field, _index)[1],\ + EFX_DWORD_0, (u64)(_value) >> 32); \ + } while (0) +#define MCDI_ARRAY_FIELD(_buf, _field1, _type, _index, _field2) \ + MCDI_FIELD(MCDI_ARRAY_STRUCT_PTR(_buf, _field1, _index), \ + _type ## _TYPEDEF, _field2) + +#define MCDI_EVENT_FIELD(_ev, _field) \ + EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) + +#endif diff --git a/src/drivers/net/sfc/sfc_hunt.c b/src/drivers/net/sfc/sfc_hunt.c new file mode 100644 index 000000000..25780ffd8 --- /dev/null +++ b/src/drivers/net/sfc/sfc_hunt.c @@ -0,0 +1,1324 @@ +/************************************************************************** + * + * Device driver for Solarflare Communications EF10 devices + * + * Written by Shradha Shah + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efx_hunt.h" +#include "efx_bitfield.h" +#include "ef10_regs.h" +#include "mc_driver_pcol.h" +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define HUNTINGTON_NVRAM_CHUNK 0x80 +#define HUNTINGTON_NVS_MAX_LENGTH 0x1000 + +#define EMCDI_IO(code) EUNIQ(EINFO_EIO, (code)) + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#endif + +struct hunt_nic *primary_nics = NULL; + +struct hunt_nic { + struct efx_nic efx; + + /* PHY information */ + unsigned int phy_cap_mask; + unsigned int phy_cap; + unsigned long link_poll_timer; + + /* resource housekeeping */ + uint64_t uc_filter_id; + uint64_t mc_filter_id; + u8 mac[ETH_ALEN]; + + struct { + /* Common payload for all MCDI requests */ + unsigned int seqno; + + size_t resp_hdr_len; + size_t resp_data_len; + + struct io_buffer *iob; + uint64_t dma_addr; + } mcdi; + + struct hunt_nic *primary; + struct hunt_nic *next_primary; + u32 flags; +}; + +static int hunt_nic_is_primary(struct hunt_nic *hunt) +{ + return (hunt->flags & (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY)); +} + +/******************************************************************************* + * + * + * MCDI transport + * + * This has been based on the implementation of MCDI in the common code driver. + * + * + ******************************************************************************/ + +static int hunt_mcdi_init(struct hunt_nic *hunt) +{ + size_t max_msg_size; + int rc; + + /* The MCDI message has two 32-bit headers (the MCDI header and the + * MCDI v2 extended command) and then up to MCDI_CTL_SDU_LEN_MAX_V2 + * bytes of payload + */ + max_msg_size = 2 * sizeof(efx_dword_t) + MCDI_CTL_SDU_LEN_MAX_V2; + + hunt->mcdi.iob = alloc_iob(max_msg_size); + if (!hunt->mcdi.iob) { + rc = -ENOMEM; + return rc; + } + return 0; +} + +static void hunt_mcdi_copyin(struct hunt_nic *hunt, + unsigned int cmd, + uint8_t *inbuf, + size_t inlen) +{ + efx_dword_t hdr[2]; + uint32_t seqno; + unsigned int xflags; + size_t hdr_len; + u8 *pdu = hunt->mcdi.iob->data; + + seqno = hunt->mcdi.seqno & MCDI_SEQ_MASK; + + xflags = 0; + + EFX_POPULATE_DWORD_7(hdr[0], + MCDI_HEADER_CODE, MC_CMD_V2_EXTN, + MCDI_HEADER_RESYNC, 1, + MCDI_HEADER_DATALEN, 0, + MCDI_HEADER_SEQ, seqno, + MCDI_HEADER_ERROR, 0, + MCDI_HEADER_RESPONSE, 0, + MCDI_HEADER_XFLAGS, xflags); + EFX_POPULATE_DWORD_2(hdr[1], + MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd, + MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen); + + hdr_len = sizeof(hdr); + + memcpy(pdu, &hdr, hdr_len); + memcpy(pdu + hdr_len, inbuf, inlen); + + wmb(); /* Sync the data before ringing the doorbell */ + + /* Ring the doorbell to post the command DMA address to the MC */ + hunt->mcdi.dma_addr = virt_to_bus(hunt->mcdi.iob->data); + + assert((hunt->mcdi.dma_addr & 0xFF) == 0); + + _efx_writel(&hunt->efx, + cpu_to_le32((u64)hunt->mcdi.dma_addr >> 32), + ER_DZ_MC_DB_LWRD); + + _efx_writel(&hunt->efx, + cpu_to_le32((u32)hunt->mcdi.dma_addr), + ER_DZ_MC_DB_HWRD); +} + +static void hunt_mcdi_copyout(struct hunt_nic *hunt, + uint8_t *outbuf, size_t outlen) +{ + size_t offset; + const u8 *pdu = hunt->mcdi.iob->data; + + offset = hunt->mcdi.resp_hdr_len; + + if (outlen > 0) + memcpy(outbuf, pdu+offset, outlen); +} + +static int hunt_mcdi_request_poll(struct hunt_nic *hunt, bool quiet) +{ + unsigned int resplen, respseq, error; + unsigned long finish; + efx_dword_t errdword; + efx_qword_t qword; + const efx_dword_t *pdu = hunt->mcdi.iob->data; + const u8 *pdu1 = hunt->mcdi.iob->data; + int delay, rc; + + /* Spin for up to 5s, polling at intervals of 10us, 20us, ... ~100ms */ + finish = currticks() + (5 * TICKS_PER_SEC); + delay = 10; + while (1) { + udelay(delay); + + /* Check for an MCDI response */ + if (EFX_DWORD_FIELD(*pdu, MCDI_HEADER_RESPONSE)) + break; + + if (currticks() >= finish) + return -ETIMEDOUT; + + if (delay < 100000) + delay *= 2; + } + + memcpy(&qword, pdu1, 8); + + /* qword.dword[0] is the MCDI header; qword.dword[1] is the MCDI v2 + * extended command + */ + respseq = EFX_DWORD_FIELD(qword.dword[0], MCDI_HEADER_SEQ); + error = EFX_DWORD_FIELD(qword.dword[0], MCDI_HEADER_ERROR); + resplen = EFX_DWORD_FIELD(qword.dword[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN); + + if (error && resplen == 0) { + if (!quiet) + DBGC(hunt, "MC rebooted\n"); + return -EIO; + } else if ((respseq ^ hunt->mcdi.seqno) & MCDI_SEQ_MASK) { + if (!quiet) + DBGC(hunt, "MC response mismatch rxseq 0x%x txseq " + "0x%x\n", respseq, hunt->mcdi.seqno); + return -EIO; + } else if (error) { + memcpy(&errdword, pdu1 + 8, 4); + rc = EFX_DWORD_FIELD(errdword, EFX_DWORD_0); + switch (rc) { + case MC_CMD_ERR_ENOENT: + return -ENOENT; + case MC_CMD_ERR_EINTR: + return -EINTR; + case MC_CMD_ERR_EACCES: + return -EACCES; + case MC_CMD_ERR_EBUSY: + return -EBUSY; + case MC_CMD_ERR_EINVAL: + return -EINVAL; + case MC_CMD_ERR_EDEADLK: + return -EDEADLK; + case MC_CMD_ERR_ENOSYS: + return -ENOSYS; + case MC_CMD_ERR_ETIME: + return -ETIME; + case MC_CMD_ERR_EPERM: + return -EPERM; + default: + /* Return the MC error in an I/O error. */ + return EMCDI_IO(rc & 0xff); + } + } + hunt->mcdi.resp_hdr_len = 8; + hunt->mcdi.resp_data_len = resplen; + + return 0; +} + +static void hunt_mcdi_fini(struct hunt_nic *hunt) +{ + free_iob(hunt->mcdi.iob); +} + +int _hunt_mcdi(struct efx_nic *efx, unsigned int cmd, + const efx_dword_t *inbuf, size_t inlen, + efx_dword_t *outbuf, size_t outlen, + size_t *outlen_actual, bool quiet) +{ + int rc; + struct hunt_nic *hunt = (struct hunt_nic *) efx; + size_t local_outlen_actual; + + if (outlen_actual == NULL) + outlen_actual = &local_outlen_actual; + + ++hunt->mcdi.seqno; + hunt_mcdi_copyin(hunt, cmd, (uint8_t *) inbuf, inlen); + + rc = hunt_mcdi_request_poll(hunt, quiet); + if (rc != 0) { + if (!quiet) + DBGC(hunt, "MC response to cmd 0x%x: %s\n", + cmd, strerror(rc)); + return rc; + } + + *outlen_actual = hunt->mcdi.resp_data_len; + + hunt_mcdi_copyout(hunt, (uint8_t *) outbuf, outlen); + + return 0; +} + +static int hunt_mcdi(struct hunt_nic *hunt, struct efx_mcdi_req_s *req) +{ + return _hunt_mcdi(&hunt->efx, req->emr_cmd, + (const efx_dword_t *) req->emr_in_buf, + req->emr_in_length, + (efx_dword_t *) req->emr_out_buf, req->emr_out_length, + &req->emr_out_length_used, false); +} + +static int hunt_mcdi_quiet(struct hunt_nic *hunt, struct efx_mcdi_req_s *req) +{ + return _hunt_mcdi(&hunt->efx, req->emr_cmd, + (const efx_dword_t *) req->emr_in_buf, + req->emr_in_length, + (efx_dword_t *) req->emr_out_buf, req->emr_out_length, + &req->emr_out_length_used, true); +} + +/******************************************************************************* + * + * + * Hardware initialization + * + * + ******************************************************************************/ +static int hunt_get_workarounds(struct hunt_nic *hunt, uint32_t *implemented, + uint32_t *enabled) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_WORKAROUNDS_OUT_LEN); + int rc; + + *implemented = *enabled = 0; + + req.emr_cmd = MC_CMD_GET_WORKAROUNDS; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + + if (rc) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_WORKAROUNDS_OUT_LEN) + return -EMSGSIZE; + + *implemented = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_IMPLEMENTED); + *enabled = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_ENABLED); + return 0; +} + +static int hunt_enable_workaround_35388(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN); + + req.emr_cmd = MC_CMD_WORKAROUND; + req.emr_in_buf = payload; + req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, WORKAROUND_IN_TYPE, + MC_CMD_WORKAROUND_BUG35388); + MCDI_SET_DWORD(req.emr_in_buf, WORKAROUND_IN_ENABLED, 1); + + /* If the firmware doesn't support this workaround, hunt_mcdi() will + * return -EINVAL from hunt_mcdi_request_poll(). + */ + return hunt_mcdi(hunt, &req); +} + +static int hunt_workaround_35388(struct hunt_nic *hunt) +{ + uint32_t implemented, enabled; + int rc = hunt_get_workarounds(hunt, &implemented, &enabled); + + if (rc < 0) + return 0; + if (!(implemented & MC_CMD_GET_WORKAROUNDS_OUT_BUG35388)) + return 0; + if (enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG35388) + return 1; + + rc = hunt_enable_workaround_35388(hunt); + if (rc == 0) + return 1; /* Workaround is enabled */ + else + return 0; +} + +static int hunt_get_port_assignment(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PORT_ASSIGNMENT_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_PORT_ASSIGNMENT; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + hunt->efx.port = MCDI_DWORD(req.emr_out_buf, + GET_PORT_ASSIGNMENT_OUT_PORT); + return 0; +} + +static int hunt_mac_addr(struct hunt_nic *hunt, uint8_t *ll_addr) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_MAC_ADDRESSES; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = MC_CMD_GET_MAC_ADDRESSES_OUT_LEN; + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_MAC_ADDRESSES_OUT_LEN) + return -EMSGSIZE; + + memcpy(ll_addr, + MCDI_PTR(req.emr_out_buf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE), + ETH_ALEN); + + return 0; +} + +static int hunt_get_phy_cfg(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_CFG_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_PHY_CFG; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) + return -EMSGSIZE; + + hunt->phy_cap_mask = hunt->phy_cap = + MCDI_DWORD(req.emr_out_buf, GET_PHY_CFG_OUT_SUPPORTED_CAP); + DBGC2(hunt, "GET_PHY_CFG: flags=%x, caps=%x\n", rc, hunt->phy_cap); + return 0; +} + +static int hunt_driver_attach(struct hunt_nic *hunt, int attach) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_DRV_ATTACH_IN_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_EXT_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_DRV_ATTACH; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + /* Set the PREBOOT flag to indicate later instances of attach should + * force an ENTITY RESET + */ + if (attach) + attach |= 1 << MC_CMD_DRV_PREBOOT_LBN; + + MCDI_SET_DWORD(req.emr_in_buf, DRV_ATTACH_IN_NEW_STATE, attach); + MCDI_SET_DWORD(req.emr_in_buf, DRV_ATTACH_IN_UPDATE, 1); + MCDI_SET_DWORD(req.emr_in_buf, DRV_ATTACH_IN_FIRMWARE_ID, + MC_CMD_FW_DONT_CARE); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) + return -EMSGSIZE; + + hunt->flags = MCDI_DWORD(outbuf, DRV_ATTACH_EXT_OUT_FUNC_FLAGS); + + return 0; +} + +static int hunt_reset(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_ENTITY_RESET_IN_LEN); + + req.emr_cmd = MC_CMD_ENTITY_RESET; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_POPULATE_DWORD_1(req.emr_in_buf, ENTITY_RESET_IN_FLAG, + ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET, 1); + return hunt_mcdi(hunt, &req); +} + +static void hunt_clear_udp_tunnel_ports(struct hunt_nic *hunt) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX); + MCDI_DECLARE_BUF(outbuf, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN); + struct efx_mcdi_req_s req; + int rc; + + memset(inbuf, 0, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX); + MCDI_SET_DWORD(inbuf, SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS, + (1 << MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING_LBN)); + + req.emr_cmd = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi_quiet(hunt, &req); + if (rc) + return; + + if (MCDI_DWORD(outbuf, SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS) & + (1 << MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_LBN)) { + DBGC(hunt, + "Rebooting MC due to clearing UDP tunnel port list\n"); + /* Delay for the MC reboot to complete. */ + mdelay(100); + } +} + +static int hunt_set_mac(struct hunt_nic *hunt) +{ + struct net_device *netdev = hunt->efx.netdev; + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(payload, MC_CMD_SET_MAC_IN_LEN); + unsigned int fcntl; + int rc; + + req.emr_cmd = MC_CMD_SET_MAC; + req.emr_in_buf = payload; + req.emr_in_length = MC_CMD_SET_MAC_IN_LEN; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_MTU, + EFX_MAC_FRAME_LEN(ETH_FRAME_LEN)); + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_DRAIN, 0); + memcpy(MCDI_PTR(req.emr_in_buf, SET_MAC_IN_ADDR), + netdev->ll_addr, ETH_ALEN); + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_REJECT, 0); + + /* If the PHY supports autnegotiation, then configure the MAC to match + * the negotiated settings. Otherwise force the MAC to TX and RX flow + * control. + */ + if (hunt->phy_cap_mask & (1 << MC_CMD_PHY_CAP_AN_LBN)) + fcntl = MC_CMD_FCNTL_AUTO; + else + fcntl = MC_CMD_FCNTL_BIDIR; + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_FCNTL, fcntl); + + rc = hunt_mcdi(hunt, &req); + /* Ignore failure for permissions reasons */ + if (rc == -EPERM) + rc = 0; + return rc; +} + +static int hunt_alloc_vis(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_ALLOC_VIS_IN_LEN); + + req.emr_cmd = MC_CMD_ALLOC_VIS; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, ALLOC_VIS_IN_MIN_VI_COUNT, 1); + MCDI_SET_DWORD(req.emr_in_buf, ALLOC_VIS_IN_MAX_VI_COUNT, 1); + + return hunt_mcdi(hunt, &req); +} + +static void hunt_free_vis(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + int rc; + + req.emr_cmd = MC_CMD_FREE_VIS; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FREE_VIS Failed\n"); +} + +/******************************************************************************* + * + * + * Link state handling + * + * + ******************************************************************************/ +static int hunt_check_link(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN); + unsigned int flags, speed; + bool up; + int rc; + static bool link_state = false; + + req.emr_cmd = MC_CMD_GET_LINK; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_LEN) + return -EMSGSIZE; + + flags = MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_FLAGS); + up = !!(flags & (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN)); + speed = MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_LINK_SPEED); + + /* Set netdev_link_*() based on the link status from the MC */ + if (up && speed) + netdev_link_up(hunt->efx.netdev); + else + netdev_link_down(hunt->efx.netdev); + + if (up != link_state) { + DBGC(hunt, "Link %s, flags=%x, our caps=%x, lpa=%x, speed=%d, fcntl=%x, mac_fault=%x\n", + (up? "up": "down"), flags, + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_CAP), + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_LP_CAP), + speed, + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_FCNTL), + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_MAC_FAULT)); + link_state = up; + } + + return 0; +} + +#define MCDI_PORT_SPEED_CAPS ((1 << MC_CMD_PHY_CAP_10HDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_10FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_100HDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_100FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_1000HDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_1000FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_10000FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_40000FDX_LBN)) + +/******************************************************************************* + * + * + * TX + * + * + ******************************************************************************/ +static int +hunt_tx_init(struct net_device *netdev, struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + dma_addr_t dma_addr; + efx_qword_t *addr; + MCDI_DECLARE_BUF(inbuf, + MC_CMD_INIT_TXQ_IN_LEN(EFX_TXQ_NBUFS(EFX_TXD_SIZE))); + int rc, npages; + + rc = efx_hunt_tx_init(netdev, &dma_addr); + if (rc != 0) + return rc; + + npages = EFX_TXQ_NBUFS(EFX_TXD_SIZE); + + req.emr_cmd = MC_CMD_INIT_TXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_INIT_TXQ_IN_LEN(npages); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_SIZE, EFX_TXD_SIZE); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_TARGET_EVQ, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_LABEL, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_INSTANCE, 0); + + MCDI_POPULATE_DWORD_6(req.emr_in_buf, INIT_TXQ_IN_FLAGS, + INIT_TXQ_IN_FLAG_BUFF_MODE, 0, + INIT_TXQ_IN_FLAG_IP_CSUM_DIS, 1, + INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, 1, + INIT_TXQ_IN_FLAG_TCP_UDP_ONLY, 0, + INIT_TXQ_IN_CRC_MODE, 0, + INIT_TXQ_IN_FLAG_TIMESTAMP, 0); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_OWNER_ID, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_PORT_ID, + EVB_PORT_ID_ASSIGNED); + + addr = (efx_qword_t *) MCDI_PTR(req.emr_in_buf, INIT_TXQ_IN_DMA_ADDR); + + EFX_POPULATE_QWORD_2(*addr, + EFX_DWORD_1, (uint32_t)(dma_addr >> 32), + EFX_DWORD_0, (uint32_t)(dma_addr & 0xffffffff)); + + return hunt_mcdi(hunt, &req); +} + +static void hunt_tx_fini(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_TXQ_IN_LEN); + struct efx_nic *efx = &hunt->efx; + struct efx_tx_queue *txq = &efx->txq; + int rc; + + req.emr_cmd = MC_CMD_FINI_TXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FINI_TXQ_IN_INSTANCE, 0); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FINI_TXQ Failed\n"); + + efx_hunt_free_special_buffer(txq->ring, + sizeof(efx_tx_desc_t) * EFX_TXD_SIZE); + txq->ring = NULL; +} + +/******************************************************************************* + * + * + * RX + * + * + ******************************************************************************/ +static int hunt_rx_filter_insert(struct net_device *netdev, + struct hunt_nic *hunt, + int multicast) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_IN_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_FILTER_OP_OUT_LEN); + int rc; + uint64_t filter_id; + (void) netdev; + + req.emr_cmd = MC_CMD_FILTER_OP; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_OP, + multicast ? MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE + : MC_CMD_FILTER_OP_IN_OP_INSERT); + MCDI_POPULATE_DWORD_1(req.emr_in_buf, FILTER_OP_IN_MATCH_FIELDS, + FILTER_OP_IN_MATCH_DST_MAC, 1); + if (multicast) + memset(MCDI_PTR(req.emr_in_buf, FILTER_OP_IN_DST_MAC), + 0xff, ETH_ALEN); + else + memcpy(MCDI_PTR(req.emr_in_buf, FILTER_OP_IN_DST_MAC), + hunt->mac, ETH_ALEN); + + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_PORT_ID, + EVB_PORT_ID_ASSIGNED); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_RX_DEST, + MC_CMD_FILTER_OP_IN_RX_DEST_HOST); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_RX_QUEUE, 0); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_RX_MODE, 0); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_TX_DEST, + MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_FILTER_OP_OUT_LEN) + return -EIO; + + filter_id = MCDI_QWORD(req.emr_out_buf, FILTER_OP_OUT_HANDLE); + if (multicast) + hunt->mc_filter_id = filter_id; + else + hunt->uc_filter_id = filter_id; + + return 0; +} + +static int hunt_rx_filter_remove(struct hunt_nic *hunt, + int multicast) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_IN_LEN); + + req.emr_cmd = MC_CMD_FILTER_OP; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_OP, + multicast ? MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE : + MC_CMD_FILTER_OP_IN_OP_REMOVE); + MCDI_SET_QWORD(req.emr_in_buf, FILTER_OP_IN_HANDLE, + multicast ? hunt->mc_filter_id : + hunt->uc_filter_id); + return hunt_mcdi(hunt, &req); +} + +static int hunt_get_mac(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_MAC_ADDRESSES; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_INIT_EVQ_OUT_LEN) + return -EMSGSIZE; + + memcpy(hunt->mac, MCDI_PTR(outbuf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE), + ETH_ALEN); + return 0; +} + +static int hunt_rx_filter_init(struct net_device *netdev, + struct hunt_nic *hunt) +{ + int rc = hunt_get_mac(hunt); + + if (rc != 0) + return rc; + + rc = hunt_rx_filter_insert(netdev, hunt, 0); + if (rc != 0) + return rc; + + rc = hunt_rx_filter_insert(netdev, hunt, 1); + if (rc != 0) + hunt_rx_filter_remove(hunt, 0); + + return rc; +} + +static int +hunt_rx_init(struct net_device *netdev, + struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + dma_addr_t dma_addr; + efx_qword_t *addr; + MCDI_DECLARE_BUF(inbuf, + MC_CMD_INIT_RXQ_IN_LEN(EFX_RXQ_NBUFS(EFX_RXD_SIZE))); + int rc, npages; + + rc = efx_hunt_rx_init(netdev, &dma_addr); + if (rc != 0) + return rc; + + npages = EFX_RXQ_NBUFS(EFX_RXD_SIZE); + + req.emr_cmd = MC_CMD_INIT_RXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_INIT_RXQ_IN_LEN(npages); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_SIZE, EFX_RXD_SIZE); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_TARGET_EVQ, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_LABEL, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_INSTANCE, 0); + MCDI_POPULATE_DWORD_5(req.emr_in_buf, INIT_RXQ_IN_FLAGS, + INIT_RXQ_IN_FLAG_BUFF_MODE, 0, + INIT_RXQ_IN_FLAG_HDR_SPLIT, 0, + INIT_RXQ_IN_FLAG_TIMESTAMP, 0, + INIT_RXQ_IN_CRC_MODE, 0, + INIT_RXQ_IN_FLAG_PREFIX, 1); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_OWNER_ID, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_PORT_ID, + EVB_PORT_ID_ASSIGNED); + + addr = (efx_qword_t *) MCDI_PTR(req.emr_in_buf, INIT_RXQ_IN_DMA_ADDR); + + EFX_POPULATE_QWORD_2(*addr, + EFX_DWORD_1, (uint32_t)(dma_addr >> 32), + EFX_DWORD_0, (uint32_t)(dma_addr & 0xffffffff)); + return hunt_mcdi(hunt, &req); +} + +static void hunt_rx_filter_fini(struct hunt_nic *hunt) +{ + hunt_rx_filter_remove(hunt, 0); + hunt_rx_filter_remove(hunt, 1); +} + +static void hunt_rx_fini(struct hunt_nic *hunt) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_RXQ_IN_LEN); + struct efx_mcdi_req_s req; + struct efx_nic *efx = &hunt->efx; + struct efx_rx_queue *rxq = &efx->rxq; + int rc; + + req.emr_cmd = MC_CMD_FINI_RXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_FINI_RXQ_IN_LEN; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FINI_RXQ_IN_INSTANCE, 0); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FINI_RXQ Failed\n"); + + efx_hunt_free_special_buffer(rxq->ring, + sizeof(efx_rx_desc_t) * EFX_RXD_SIZE); + rxq->ring = NULL; +} + +/******************************************************************************* + * + * + * Event queues and interrupts + * + * + ******************************************************************************/ +static int +hunt_ev_init(struct net_device *netdev, + struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + dma_addr_t dma_addr; + efx_qword_t *addr; + MCDI_DECLARE_BUF(inbuf, + MC_CMD_INIT_EVQ_IN_LEN(EFX_EVQ_NBUFS(EFX_EVQ_SIZE))); + MCDI_DECLARE_BUF(outbuf, MC_CMD_INIT_EVQ_OUT_LEN); + int rc, npages; + + rc = efx_hunt_ev_init(netdev, &dma_addr); + if (rc != 0) + return rc; + + npages = EFX_EVQ_NBUFS(EFX_EVQ_SIZE); + + req.emr_cmd = MC_CMD_INIT_EVQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_INIT_EVQ_IN_LEN(npages); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_SIZE, EFX_EVQ_SIZE); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_INSTANCE, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_IRQ_NUM, 0); + + MCDI_POPULATE_DWORD_6(req.emr_in_buf, INIT_EVQ_IN_FLAGS, + INIT_EVQ_IN_FLAG_INTERRUPTING, 1, + INIT_EVQ_IN_FLAG_RPTR_DOS, 0, + INIT_EVQ_IN_FLAG_INT_ARMD, 0, + INIT_EVQ_IN_FLAG_CUT_THRU, 0, + INIT_EVQ_IN_FLAG_RX_MERGE, 0, + INIT_EVQ_IN_FLAG_TX_MERGE, 0); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_TMR_MODE, + MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_TMR_LOAD, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_TMR_RELOAD, 0); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_COUNT_MODE, + MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_COUNT_THRSHLD, 0); + + addr = (efx_qword_t *) MCDI_PTR(req.emr_in_buf, INIT_EVQ_IN_DMA_ADDR); + + EFX_POPULATE_QWORD_2(*addr, + EFX_DWORD_1, (uint32_t)(dma_addr >> 32), + EFX_DWORD_0, (uint32_t)(dma_addr & 0xffffffff)); + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_INIT_EVQ_OUT_LEN) + return -EMSGSIZE; + + return 0; +} + +static void hunt_ev_fini(struct hunt_nic *hunt) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_EVQ_IN_LEN); + struct efx_mcdi_req_s req; + struct efx_nic *efx = &hunt->efx; + struct efx_ev_queue *evq = &efx->evq; + int rc; + + req.emr_cmd = MC_CMD_FINI_EVQ; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FINI_EVQ_IN_INSTANCE, 0); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FINI_EVQ Failed\n"); + + efx_hunt_free_special_buffer(evq->ring, + sizeof(efx_event_t) * EFX_EVQ_SIZE); + evq->ring = NULL; +} + +static void +hunt_poll(struct net_device *netdev) +{ + struct hunt_nic *hunt = netdev_priv(netdev); + + /* If called while already polling, return immediately */ + if (hunt->efx.state & EFX_STATE_POLLING) + return; + hunt->efx.state |= EFX_STATE_POLLING; + + /* Poll link state */ + if (hunt->link_poll_timer + TICKS_PER_SEC < currticks()) { + hunt->link_poll_timer = currticks(); + hunt_check_link(hunt); + } + + /* Poll data path */ + efx_hunt_poll(netdev); + + hunt->efx.state &= ~EFX_STATE_POLLING; +} + +/******************************************************************************* + * + * + * Netdevice operations + * + * + ******************************************************************************/ +static int hunt_open(struct net_device *netdev) +{ + struct hunt_nic *hunt = netdev_priv(netdev); + int rc; + + /* Allocate VIs */ + rc = hunt_alloc_vis(hunt); + if (rc != 0) + goto fail2; + + /* Initialize data path */ + rc = hunt_ev_init(netdev, hunt); + if (rc != 0) + goto fail3; + + rc = hunt_rx_init(netdev, hunt); + if (rc != 0) + goto fail4; + + rc = hunt_rx_filter_init(netdev, hunt); + if (rc != 0) + goto fail5; + + rc = hunt_tx_init(netdev, hunt); + if (rc != 0) + goto fail6; + + rc = efx_hunt_open(netdev); + if (rc) + goto fail7; + + rc = hunt_set_mac(hunt); + if (rc) + goto fail8; + + /* Mark the link as down before checking the link state because the + * latter might fail. + */ + netdev_link_down(netdev); + hunt_check_link(hunt); + + DBGC2(hunt, "%s: open ok\n", netdev->name); + return 0; + +fail8: + efx_hunt_close(netdev); +fail7: + hunt_tx_fini(hunt); +fail6: + hunt_rx_filter_fini(hunt); +fail5: + hunt_rx_fini(hunt); +fail4: + hunt_ev_fini(hunt); +fail3: + hunt_free_vis(hunt); +fail2: + DBGC2(hunt, "%s: %s\n", netdev->name, strerror(rc)); + return rc; +} + + +static void hunt_close(struct net_device *netdev) +{ + struct hunt_nic *hunt = netdev_priv(netdev); + + /* Stop datapath */ + efx_hunt_close(netdev); + + hunt_tx_fini(hunt); + hunt_rx_fini(hunt); + hunt_rx_filter_fini(hunt); + hunt_ev_fini(hunt); + + hunt_free_vis(hunt); + + /* Reset hardware and detach */ + hunt_reset(hunt); +} + + +/******************************************************************************* + * + * + * Public operations + * + * + ******************************************************************************/ + +static struct net_device_operations hunt_operations = { + .open = hunt_open, + .close = hunt_close, + .transmit = efx_hunt_transmit, + .poll = hunt_poll, + .irq = efx_hunt_irq, +}; + +static int +hunt_probe(struct pci_device *pci) +{ + struct net_device *netdev; + struct hunt_nic *hunt; + struct efx_nic *efx; + int rc = 0; + + /* Create the network adapter */ + netdev = alloc_etherdev(sizeof(struct hunt_nic)); + if (!netdev) { + rc = -ENOMEM; + goto fail1; + } + + /* Initialise the network adapter, and initialise private storage */ + netdev_init(netdev, &hunt_operations); + pci_set_drvdata(pci, netdev); + netdev->dev = &pci->dev; + netdev->state |= NETDEV_IRQ_UNSUPPORTED; + + hunt = netdev_priv(netdev); + memset(hunt, 0, sizeof(*hunt)); + efx = &hunt->efx; + + efx->type = &hunt_nic_type; + + /* Initialise efx datapath */ + efx_probe(netdev, EFX_HUNTINGTON); + + /* Initialise MCDI. In case we are recovering from a crash, first + * cancel any outstanding request by sending a special message using the + * least significant bits of the 'high' (doorbell) register. + */ + _efx_writel(efx, cpu_to_le32(1), ER_DZ_MC_DB_HWRD); + rc = hunt_mcdi_init(hunt); + if (rc != 0) + goto fail2; + + /* Reset (most) configuration for this function */ + rc = hunt_reset(hunt); + if (rc != 0) + goto fail3; + + /* Medford has a list of UDP tunnel ports that is populated by the + * driver. Avoid dropping any unencapsulated packets. This may cause + * an MC reboot. + */ + hunt_clear_udp_tunnel_ports(hunt); + + /* Enable the workaround for bug35388, if supported */ + efx->workaround_35388 = hunt_workaround_35388(hunt); + + /* Set the RX packet prefix size */ + efx->rx_prefix_size = ES_DZ_RX_PREFIX_SIZE; + + rc = hunt_get_port_assignment(hunt); + if (rc != 0) + goto fail3; + + rc = hunt_mac_addr(hunt, netdev->ll_addr); + if (rc != 0) + goto fail4; + + rc = hunt_get_phy_cfg(hunt); + if (rc != 0) + goto fail5; + + rc = hunt_driver_attach(hunt, 1); + if (rc != 0) + goto fail5; + + /* If not exposing this network device, return successfully here */ + if (hunt->flags & (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT)) + return 0; + + if (hunt_nic_is_primary(hunt)) { + hunt->next_primary = primary_nics; + primary_nics = hunt; + hunt->primary = hunt; + } else { + struct hunt_nic *other_hunt = primary_nics; + + while (other_hunt && !hunt->primary) { + struct pci_device *other_pci = (struct pci_device *) + other_hunt->efx.netdev->dev; + /* Check if the seg:bus:dev parts match. */ + if (PCI_FIRST_FUNC(other_pci->busdevfn) == + PCI_FIRST_FUNC(pci->busdevfn)) + hunt->primary = other_hunt; + + other_hunt = other_hunt->next_primary; + } + if (!hunt->primary) { + rc = -EIO; + goto fail6; + } + } + + rc = register_netdev(netdev); + if (rc != 0) + goto fail8; + + DBG2("%s " PCI_FMT " ok\n", __func__, PCI_ARGS(pci)); + return 0; + +fail8: +fail6: + (void) hunt_driver_attach(hunt, 0); +fail5: +fail4: +fail3: + hunt_mcdi_fini(hunt); +fail2: + efx_remove(netdev); + netdev_put(netdev); +fail1: + DBG2("%s " PCI_FMT " rc=%d\n", __func__, PCI_ARGS(pci), rc); + return rc; +} + +static void hunt_remove(struct pci_device *pci) +{ + struct net_device *netdev = pci_get_drvdata(pci); + struct hunt_nic *hunt = netdev_priv(netdev); + + if (!(hunt->flags & + (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT))) { + /* The netdevice might still be open, so unregister it now + * before ripping stuff out from underneath. + */ + unregister_netdev(netdev); + } + + (void)hunt_driver_attach(hunt, 0); + hunt_mcdi_fini(hunt); + + /* Destroy data path */ + efx_remove(netdev); + + netdev_nullify(netdev); + netdev_put(netdev); +} + +const struct efx_nic_type hunt_nic_type = { + .mcdi_rpc = _hunt_mcdi, +}; + +static struct pci_device_id hunt_nics[] = { + PCI_ROM(0x1924, 0x0a03, "SFC9220", "Solarflare SFN8xxx Adapter", 0), +}; + +struct pci_driver hunt_driver __pci_driver = { + .ids = hunt_nics, + .id_count = ARRAY_SIZE(hunt_nics), + .probe = hunt_probe, + .remove = hunt_remove, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 703f45652..cd12ebf3c 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -197,6 +197,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_axge ( ERRFILE_DRIVER | 0x00c10000 ) #define ERRFILE_thunderx ( ERRFILE_DRIVER | 0x00c20000 ) #define ERRFILE_af_packet ( ERRFILE_DRIVER | 0x00c30000 ) +#define ERRFILE_sfc_hunt ( ERRFILE_DRIVER | 0x00c40000 ) +#define ERRFILE_efx_hunt ( ERRFILE_DRIVER | 0x00c50000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 6c7487d00d4659aa4eec6943dfcdec1af6b5d1b9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Apr 2017 16:46:11 +0100 Subject: [PATCH 447/591] [efi] Fix typo in efi_acpi_table_protocol_guid Signed-off-by: Michael Brown --- src/interface/efi/efi_guid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index b0278cd10..663585dc2 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -88,7 +88,7 @@ EFI_GUID efi_absolute_pointer_protocol_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; /** ACPI table protocol GUID */ -EFI_GUID efi_acpi_table_protocl_guid +EFI_GUID efi_acpi_table_protocol_guid = EFI_ACPI_TABLE_PROTOCOL_GUID; /** Apple NetBoot protocol GUID */ From a82f93748545427cd7f104f107be1663c55a35fd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 Apr 2017 15:01:56 +0100 Subject: [PATCH 448/591] [efi] Add efi_sprintf() and efi_vsprintf() Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_strings.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/include/ipxe/efi/efi_strings.h b/src/include/ipxe/efi/efi_strings.h index 2f241537e..a8ace45e7 100644 --- a/src/include/ipxe/efi/efi_strings.h +++ b/src/include/ipxe/efi/efi_strings.h @@ -20,4 +20,27 @@ extern int efi_vssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, extern int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ); +/** + * Write a formatted string to a wide-character buffer + * + * @v wbuf Buffer into which to write the string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret wlen Length of formatted string (in wide characters) + */ +static inline int efi_vsprintf ( wchar_t *buf, const char *fmt, va_list args ) { + return efi_vsnprintf ( buf, ~( ( size_t ) 0 ), fmt, args ); +} + +/** + * Write a formatted string to a buffer + * + * @v wbuf Buffer into which to write the string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret wlen Length of formatted string (in wide characters) + */ +#define efi_sprintf( buf, fmt, ... ) \ + efi_snprintf ( (buf), ~( ( size_t ) 0 ), (fmt), ## __VA_ARGS__ ) + #endif /* _IPXE_EFI_STRINGS_H */ From 84d406ccf48c8808b0bb30d526b758e4b58cacc8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 Apr 2017 15:03:25 +0100 Subject: [PATCH 449/591] [block] Allow use of a non-default EFI SAN boot filename Some older operating systems (e.g. RHEL6) use a non-default filename on the root disk and rely on setting an EFI variable to point to the bootloader. This does not work when performing a SAN boot on a machine where the EFI variable is not present. Fix by allowing a non-default filename to be specified via the "sanboot --filename" option or the "san-filename" setting. For example: sanboot --filename \efi\redhat\grub.efi \ iscsi:192.168.0.1::::iqn.2010-04.org.ipxe.demo:rhel6 or option ipxe.san-filename code 188 = string; option ipxe.san-filename "\\efi\\redhat\\grub.efi"; option root-path "iscsi:192.168.0.1::::iqn.2010-04.org.ipxe.demo:rhel6"; Originally-implemented-by: Vishvananda Ishaya Abrams Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 3 +- src/core/dummy_sanboot.c | 5 ++- src/core/null_sanboot.c | 3 +- src/core/settings.c | 9 +++++ src/hci/commands/sanboot_cmd.c | 11 ++++-- src/include/ipxe/dhcp.h | 6 ++++ src/include/ipxe/sanboot.h | 3 +- src/include/ipxe/settings.h | 2 ++ src/include/usr/autoboot.h | 2 +- src/interface/efi/efi_block.c | 22 ++++++++---- src/usr/autoboot.c | 50 +++++++++++++++++++++++---- src/usr/pxemenu.c | 2 +- 12 files changed, 97 insertions(+), 21 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index b793d730c..8ac14e58c 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -1507,6 +1507,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { * Attempt to boot from an INT 13 drive * * @v drive Drive number + * @v filename Filename (or NULL to use default) * @ret rc Return status code * * This boots from the specified INT 13 drive by loading the Master @@ -1516,7 +1517,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { * * Note that this function can never return success, by definition. */ -static int int13_boot ( unsigned int drive ) { +static int int13_boot ( unsigned int drive, const char *filename __unused ) { struct memory_map memmap; struct segoff address; int rc; diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c index 08180852c..e6293099a 100644 --- a/src/core/dummy_sanboot.c +++ b/src/core/dummy_sanboot.c @@ -95,8 +95,11 @@ static void dummy_san_unhook ( unsigned int drive ) { * Boot from dummy SAN device * * @v drive Drive number + * @v filename Filename (or NULL to use default) + * @ret rc Return status code */ -static int dummy_san_boot ( unsigned int drive __unused ) { +static int dummy_san_boot ( unsigned int drive __unused, + const char *filename __unused ) { return -EOPNOTSUPP; } diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c index b09562e28..7c0680f58 100644 --- a/src/core/null_sanboot.c +++ b/src/core/null_sanboot.c @@ -37,7 +37,8 @@ static void null_san_unhook ( unsigned int drive __unused ) { /* Do nothing */ } -static int null_san_boot ( unsigned int drive __unused ) { +static int null_san_boot ( unsigned int drive __unused, + const char *filename __unused ) { return -EOPNOTSUPP; } diff --git a/src/core/settings.c b/src/core/settings.c index f5be5c4eb..879057224 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -2396,6 +2396,15 @@ const struct setting root_path_setting __setting ( SETTING_SANBOOT, root-path)={ .type = &setting_type_string, }; +/** SAN filename setting */ +const struct setting san_filename_setting __setting ( SETTING_SANBOOT, + san-filename ) = { + .name = "san-filename", + .description = "SAN filename", + .tag = DHCP_EB_SAN_FILENAME, + .type = &setting_type_string, +}; + /** Username setting */ const struct setting username_setting __setting ( SETTING_AUTH, username ) = { .name = "username", diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 9965ec151..3907276a0 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -47,12 +47,14 @@ struct sanboot_options { int no_describe; /** Keep SAN device */ int keep; + /** Filename */ + char *filename; }; /** "sanboot" option list */ static union { - /* "sanboot" takes all three options */ - struct option_descriptor sanboot[3]; + /* "sanboot" takes all four options */ + struct option_descriptor sanboot[4]; /* "sanhook" takes only --drive and --no-describe */ struct option_descriptor sanhook[2]; /* "sanunhook" takes only --drive */ @@ -65,6 +67,8 @@ static union { struct sanboot_options, no_describe, parse_flag ), OPTION_DESC ( "keep", 'k', no_argument, struct sanboot_options, keep, parse_flag ), + OPTION_DESC ( "filename", 'f', required_argument, + struct sanboot_options, filename, parse_string ), }, }; @@ -130,7 +134,8 @@ static int sanboot_core_exec ( int argc, char **argv, flags |= no_root_path_flags; /* Boot from root path */ - if ( ( rc = uriboot ( NULL, uris, count, opts.drive, flags ) ) != 0 ) + if ( ( rc = uriboot ( NULL, uris, count, opts.drive, opts.filename, + flags ) ) != 0 ) goto err_uriboot; err_uriboot: diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index 0f662d633..b7a5f004b 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -440,6 +440,12 @@ struct dhcp_netdev_desc { */ #define DHCP_EB_SAN_RETRY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbb ) +/** SAN filename + * + * This is the path of the bootloader within the SAN device. + */ +#define DHCP_EB_SAN_FILENAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbc ) + /** SAN drive number * * This is the drive number for a SAN-hooked drive. For BIOS, 0x80 is diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 8737bbc0b..8b3e2b282 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -155,9 +155,10 @@ void san_unhook ( unsigned int drive ); * Attempt to boot from a SAN device * * @v drive Drive number + * @v filename Filename (or NULL to use default) * @ret rc Return status code */ -int san_boot ( unsigned int drive ); +int san_boot ( unsigned int drive, const char *filename ); /** * Describe SAN devices for SAN-booted operating system diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 521efa99d..36b4c2410 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -452,6 +452,8 @@ filename_setting __setting ( SETTING_BOOT, filename ); extern const struct setting root_path_setting __setting ( SETTING_SANBOOT, root-path ); extern const struct setting +san_filename_setting __setting ( SETTING_SANBOOT, san-filename ); +extern const struct setting username_setting __setting ( SETTING_AUTH, username ); extern const struct setting password_setting __setting ( SETTING_AUTH, password ); diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index c62d06c6f..f88b8494f 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -32,7 +32,7 @@ extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len ); extern int uriboot ( struct uri *filename, struct uri **root_paths, unsigned int root_path_count, int drive, - unsigned int flags ); + const char *san_filename, unsigned int flags ); extern struct uri * fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index fa2271b31..70a87dcf1 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -518,10 +518,12 @@ static int efi_block_describe ( void ) { * * @v sandev SAN device * @v handle EFI handle + * @v filename Filename (or NULL to use default) + * @v image Image handle to fill in * @ret rc Return status code */ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, - EFI_HANDLE *image ) { + const char *filename, EFI_HANDLE *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_block_data *block = sandev->priv; union { @@ -563,7 +565,10 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, end = efi_devpath_end ( path.path ); prefix_len = ( ( ( void * ) end ) - ( ( void * ) path.path ) ); filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH + - sizeof ( efi_block_boot_filename ) ); + ( filename ? + ( ( strlen ( filename ) + 1 /* NUL */ ) * + sizeof ( filepath->PathName[0] ) ) : + sizeof ( efi_block_boot_filename ) ) ); boot_path_len = ( prefix_len + filepath_len + sizeof ( *end ) ); boot_path = zalloc ( boot_path_len ); if ( ! boot_path ) { @@ -576,8 +581,12 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, filepath->Header.SubType = MEDIA_FILEPATH_DP; filepath->Header.Length[0] = ( filepath_len & 0xff ); filepath->Header.Length[1] = ( filepath_len >> 8 ); - memcpy ( filepath->PathName, efi_block_boot_filename, - sizeof ( efi_block_boot_filename ) ); + if ( filename ) { + efi_sprintf ( filepath->PathName, "%s", filename ); + } else { + memcpy ( filepath->PathName, efi_block_boot_filename, + sizeof ( efi_block_boot_filename ) ); + } end = ( ( ( void * ) filepath ) + filepath_len ); end->Type = END_DEVICE_PATH_TYPE; end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -609,9 +618,10 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, * Boot from EFI block device * * @v drive Drive number + * @v filename Filename (or NULL to use default) * @ret rc Return status code */ -static int efi_block_boot ( unsigned int drive ) { +static int efi_block_boot ( unsigned int drive, const char *filename ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct san_device *sandev; EFI_HANDLE *handles; @@ -649,7 +659,7 @@ static int efi_block_boot ( unsigned int drive ) { */ rc = -ENOENT; for ( i = 0 ; i < count ; i++ ) { - if ( ( rc = efi_block_boot_image ( sandev, handles[i], + if ( ( rc = efi_block_boot_image ( sandev, handles[i], filename, &image ) ) != 0 ) continue; DBGC ( sandev, "EFIBLK %#02x found boot image\n", diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 10ccdc1d6..106e0f879 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -112,6 +112,7 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, * @v root_paths Root path(s) * @v root_path_count Number of root paths * @v drive SAN drive (if applicable) + * @v san_filename SAN filename (or NULL to use default) * @v flags Boot action flags * @ret rc Return status code * @@ -122,7 +123,8 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, * "skip-san-boot" options. */ int uriboot ( struct uri *filename, struct uri **root_paths, - unsigned int root_path_count, int drive, unsigned int flags ) { + unsigned int root_path_count, int drive, + const char *san_filename, unsigned int flags ) { struct image *image; int rc; @@ -177,8 +179,10 @@ int uriboot ( struct uri *filename, struct uri **root_paths, /* Attempt SAN boot if applicable */ if ( ! ( flags & URIBOOT_NO_SAN_BOOT ) ) { if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) { - printf ( "Booting from SAN device %#02x\n", drive ); - rc = san_boot ( drive ); + printf ( "Booting%s%s from SAN device %#02x\n", + ( san_filename ? " " : "" ), + ( san_filename ? san_filename : "" ), drive ); + rc = san_boot ( drive, san_filename ); printf ( "Boot from SAN device %#02x failed: %s\n", drive, strerror ( rc ) ); } else { @@ -300,10 +304,10 @@ static struct uri * fetch_root_path ( struct settings *settings ) { root_path = expand_settings ( raw_root_path ); if ( ! root_path ) goto err_expand; - - /* Parse root path */ if ( root_path[0] ) printf ( "Root path: %s\n", root_path ); + + /* Parse root path */ uri = parse_uri ( root_path ); if ( ! uri ) goto err_parse; @@ -316,6 +320,35 @@ static struct uri * fetch_root_path ( struct settings *settings ) { return uri; } +/** + * Fetch san-filename setting + * + * @v settings Settings block + * @ret san_filename SAN filename, or NULL on failure + */ +static char * fetch_san_filename ( struct settings *settings ) { + char *raw_san_filename; + char *san_filename = NULL; + + /* Fetch san-filename setting */ + fetch_string_setting_copy ( settings, &san_filename_setting, + &raw_san_filename ); + if ( ! raw_san_filename ) + goto err_fetch; + + /* Expand san-filename setting */ + san_filename = expand_settings ( raw_san_filename ); + if ( ! san_filename ) + goto err_expand; + if ( san_filename[0] ) + printf ( "SAN filename: %s\n", san_filename ); + + err_expand: + free ( raw_san_filename ); + err_fetch: + return san_filename; +} + /** * Check whether or not we have a usable PXE menu * @@ -351,6 +384,7 @@ static int have_pxe_menu ( void ) { int netboot ( struct net_device *netdev ) { struct uri *filename; struct uri *root_path; + char *san_filename; int rc; /* Close all other network devices */ @@ -379,6 +413,9 @@ int netboot ( struct net_device *netdev ) { /* Fetch root path (if any) */ root_path = fetch_root_path ( NULL ); + /* Fetch SAN filename (if any) */ + san_filename = fetch_san_filename ( NULL ); + /* If we have both a filename and a root path, ignore an * unsupported or missing URI scheme in the root path, since * it may represent an NFS root. @@ -400,12 +437,13 @@ int netboot ( struct net_device *netdev ) { /* Boot using next server, filename and root path */ if ( ( rc = uriboot ( filename, &root_path, ( root_path ? 1 : 0 ), - san_default_drive(), + san_default_drive(), san_filename, ( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 ) goto err_uriboot; err_uriboot: err_no_boot: + free ( san_filename ); uri_put ( root_path ); uri_put ( filename ); err_pxe_menu_boot: diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c index 391d698a9..5e497f990 100644 --- a/src/usr/pxemenu.c +++ b/src/usr/pxemenu.c @@ -378,7 +378,7 @@ int pxe_menu_boot ( struct net_device *netdev ) { return -ENOMEM; /* Attempt boot */ - rc = uriboot ( uri, NULL, 0, 0, URIBOOT_NO_SAN ); + rc = uriboot ( uri, NULL, 0, 0, NULL, URIBOOT_NO_SAN ); uri_put ( uri ); return rc; } From e6616da8b8647fe928b633e39c9e2e656aed5249 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 14 Apr 2017 10:09:57 +0100 Subject: [PATCH 450/591] [intel] Show original CTRL and STATUS values in debugging output In situations where iPXE fails to reach link-up as expected, it is useful to know the original values of the CTRL and STATUS registers prior to our reset attempt. Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 534252ba3..548bf90a7 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -268,6 +268,12 @@ static int intel_reset ( struct intel_nic *intel ) { uint32_t pba; uint32_t ctrl; uint32_t status; + uint32_t orig_ctrl; + uint32_t orig_status; + + /* Record initial control and status register values */ + orig_ctrl = ctrl = readl ( intel->regs + INTEL_CTRL ); + orig_status = readl ( intel->regs + INTEL_STATUS ); /* Force RX and TX packet buffer allocation, to work around an * errata in ICH devices. @@ -285,7 +291,6 @@ static int intel_reset ( struct intel_nic *intel ) { } /* Always reset MAC. Required to reset the TX and RX rings. */ - ctrl = readl ( intel->regs + INTEL_CTRL ); writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); @@ -309,9 +314,10 @@ static int intel_reset ( struct intel_nic *intel ) { status = readl ( intel->regs + INTEL_STATUS ); if ( ( intel->flags & INTEL_NO_PHY_RST ) || ( status & INTEL_STATUS_LU ) ) { - DBGC ( intel, "INTEL %p %sMAC reset (ctrl %08x)\n", intel, + DBGC ( intel, "INTEL %p %sMAC reset (%08x/%08x was " + "%08x/%08x)\n", intel, ( ( intel->flags & INTEL_NO_PHY_RST ) ? "forced " : "" ), - ctrl ); + ctrl, status, orig_ctrl, orig_status ); return 0; } @@ -323,8 +329,10 @@ static int intel_reset ( struct intel_nic *intel ) { /* PHY reset is not self-clearing on all models */ writel ( ctrl, intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); + status = readl ( intel->regs + INTEL_STATUS ); - DBGC ( intel, "INTEL %p MAC+PHY reset (ctrl %08x)\n", intel, ctrl ); + DBGC ( intel, "INTEL %p MAC+PHY reset (%08x/%08x was %08x/%08x)\n", + intel, ctrl, status, orig_ctrl, orig_status ); return 0; } From 2d79b20f2a3eba199012de3bd11f8aef0a5b9dbf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 16 Apr 2017 21:26:13 +0100 Subject: [PATCH 451/591] [intel] Do not enable ASDE on i350 backplane NIC On most Intel NICs, Auto-Speed Detection Enable (ASDE) can be used to automatically detect the correct link speed by sampling the link using the internal PHY. This feature is automatically inhibited when not appropriate for the physical link (e.g. when using internal SerDes mode on the 8254x). On the i350 datasheet ASDE is a reserved bit, but the relevant auto-speed detection hardware appears still to be present. However, enabling ASDE on the i350 1000BASE-KX backplane NIC seems to cause an immediate link failure. It is possible that the auto-speed detection hardware is still present, is not connected to a physical link, and is not inhibited from being applied in this mode. Work around this problem by adding an INTEL_NO_ASDE flag bit (analogous to INTEL_NO_PHY_RST), and applying this for the i350 backplane NIC. Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 6 ++++-- src/drivers/net/intel.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 548bf90a7..1bca87bca 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -295,7 +295,9 @@ static int intel_reset ( struct intel_nic *intel ) { mdelay ( INTEL_RESET_DELAY_MS ); /* Set a sensible default configuration */ - ctrl |= ( INTEL_CTRL_SLU | INTEL_CTRL_ASDE ); + if ( ! ( intel->flags & INTEL_NO_ASDE ) ) + ctrl |= INTEL_CTRL_ASDE; + ctrl |= INTEL_CTRL_SLU; ctrl &= ~( INTEL_CTRL_LRST | INTEL_CTRL_FRCSPD | INTEL_CTRL_FRCDPLX ); writel ( ctrl, intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); @@ -1110,7 +1112,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1518, "82576ns", "82576NS SerDes", 0 ), PCI_ROM ( 0x8086, 0x1521, "i350", "I350", 0 ), PCI_ROM ( 0x8086, 0x1522, "i350-f", "I350 Fiber", 0 ), - PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", 0 ), + PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x1524, "i350-2", "I350", 0 ), PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ), PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ), diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index 630eaf84c..14877687a 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -306,6 +306,8 @@ enum intel_flags { INTEL_VMWARE = 0x0002, /** PHY reset is broken */ INTEL_NO_PHY_RST = 0x0004, + /** ASDE is broken */ + INTEL_NO_ASDE = 0x0008, }; /** From dd976cb50d842b9ba30a5b6413134fdefa9e864d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 26 Apr 2017 20:00:10 +0100 Subject: [PATCH 452/591] [block] Provide sandev_read() and sandev_write() as global symbols Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 50 +++++++++++------------- src/core/sanboot.c | 56 +++++++++++++++++++++++---- src/include/ipxe/sanboot.h | 10 ++--- src/interface/efi/efi_block.c | 17 ++++---- 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 8ac14e58c..ca789a0d1 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -181,8 +181,8 @@ static int int13_parse_eltorito ( struct san_device *sandev, void *scratch ) { int rc; /* Read boot record volume descriptor */ - if ( ( rc = sandev_rw ( sandev, ELTORITO_LBA, 1, - virt_to_user ( boot ), block_read ) ) != 0 ) { + if ( ( rc = sandev_read ( sandev, ELTORITO_LBA, 1, + virt_to_user ( boot ) ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x could not read El Torito boot " "record volume descriptor: %s\n", sandev->drive, strerror ( rc ) ); @@ -227,8 +227,7 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch, int rc; /* Read partition table */ - if ( ( rc = sandev_rw ( sandev, 0, 1, virt_to_user ( mbr ), - block_read ) ) != 0 ) { + if ( ( rc = sandev_read ( sandev, 0, 1, virt_to_user ( mbr ) ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x could not read " "partition table to guess geometry: %s\n", sandev->drive, strerror ( rc ) ); @@ -506,18 +505,16 @@ static int int13_get_last_status ( struct san_device *sandev, * @v cl (bits 5:0) Sector number * @v dh Head number * @v es:bx Data buffer - * @v block_rw Block read/write method + * @v sandev_rw SAN device read/write method * @ret status Status code * @ret al Number of sectors read or written */ static int int13_rw_sectors ( struct san_device *sandev, struct i386_all_regs *ix86, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, - unsigned int count, - userptr_t buffer, - size_t len ) ) { + int ( * sandev_rw ) ( struct san_device *sandev, + uint64_t lba, + unsigned int count, + userptr_t buffer ) ) { struct int13_data *int13 = sandev->priv; unsigned int cylinder, head, sector; unsigned long lba; @@ -555,7 +552,7 @@ static int int13_rw_sectors ( struct san_device *sandev, count ); /* Read from / write to block device */ - if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_rw ) ) != 0 ){ + if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ){ DBGC ( sandev, "INT13 drive %02x I/O failed: %s\n", sandev->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; @@ -581,7 +578,7 @@ static int int13_read_sectors ( struct san_device *sandev, struct i386_all_regs *ix86 ) { DBGC2 ( sandev, "Read: " ); - return int13_rw_sectors ( sandev, ix86, block_read ); + return int13_rw_sectors ( sandev, ix86, sandev_read ); } /** @@ -601,7 +598,7 @@ static int int13_write_sectors ( struct san_device *sandev, struct i386_all_regs *ix86 ) { DBGC2 ( sandev, "Write: " ); - return int13_rw_sectors ( sandev, ix86, block_write ); + return int13_rw_sectors ( sandev, ix86, sandev_write ); } /** @@ -701,17 +698,15 @@ static int int13_extension_check ( struct san_device *sandev __unused, * * @v sandev SAN device * @v ds:si Disk address packet - * @v block_rw Block read/write method + * @v sandev_rw SAN device read/write method * @ret status Status code */ static int int13_extended_rw ( struct san_device *sandev, struct i386_all_regs *ix86, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, - unsigned int count, - userptr_t buffer, - size_t len ) ) { + int ( * sandev_rw ) ( struct san_device *sandev, + uint64_t lba, + unsigned int count, + userptr_t buffer ) ) { struct int13_disk_address addr; uint8_t bufsize; uint64_t lba; @@ -762,7 +757,7 @@ static int int13_extended_rw ( struct san_device *sandev, DBGC2 ( sandev, " (count %ld)\n", count ); /* Read from / write to block device */ - if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_rw ) ) != 0 ){ + if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x extended I/O failed: %s\n", sandev->drive, strerror ( rc ) ); /* Record that no blocks were transferred successfully */ @@ -787,7 +782,7 @@ static int int13_extended_read ( struct san_device *sandev, struct i386_all_regs *ix86 ) { DBGC2 ( sandev, "Extended read: " ); - return int13_extended_rw ( sandev, ix86, block_read ); + return int13_extended_rw ( sandev, ix86, sandev_read ); } /** @@ -801,7 +796,7 @@ static int int13_extended_write ( struct san_device *sandev, struct i386_all_regs *ix86 ) { DBGC2 ( sandev, "Extended write: " ); - return int13_extended_rw ( sandev, ix86, block_write ); + return int13_extended_rw ( sandev, ix86, sandev_write ); } /** @@ -1038,6 +1033,7 @@ static int int13_cdrom_read_boot_catalog ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_data *int13 = sandev->priv; struct int13_cdrom_boot_catalog_command command; + unsigned int start; int rc; /* Read parameters from command packet */ @@ -1051,11 +1047,11 @@ static int int13_cdrom_read_boot_catalog ( struct san_device *sandev, sandev->drive ); return -INT13_STATUS_INVALID; } + start = ( int13->boot_catalog + command.start ); /* Read from boot catalog */ - if ( ( rc = sandev_rw ( sandev, ( int13->boot_catalog + command.start ), - command.count, phys_to_user ( command.buffer ), - block_read ) ) != 0 ) { + if ( ( rc = sandev_read ( sandev, start, command.count, + phys_to_user ( command.buffer ) ) ) != 0 ) { DBGC ( sandev, "INT13 drive %02x could not read boot catalog: " "%s\n", sandev->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; diff --git a/src/core/sanboot.c b/src/core/sanboot.c index f134f76a3..1fd634904 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -577,12 +577,12 @@ int sandev_reset ( struct san_device *sandev ) { * @v block_rw Block read/write method * @ret rc Return status code */ -int sandev_rw ( struct san_device *sandev, uint64_t lba, - unsigned int count, userptr_t buffer, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len ) ) { +static int sandev_rw ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer, + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) ) { union san_command_params params; unsigned int remaining; size_t frag_len; @@ -617,6 +617,46 @@ int sandev_rw ( struct san_device *sandev, uint64_t lba, return 0; } +/** + * Read from SAN device + * + * @v sandev SAN device + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @ret rc Return status code + */ +int sandev_read ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer ) { + int rc; + + /* Read from device */ + if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_read ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Write to SAN device + * + * @v sandev SAN device + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @ret rc Return status code + */ +int sandev_write ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer ) { + int rc; + + /* Write to device */ + if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_write ) ) != 0 ) + return rc; + + return 0; +} + /** * Describe SAN device * @@ -735,8 +775,8 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) { } /* Read primary volume descriptor */ - if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( scratch ), - block_read ) ) != 0 ) { + if ( ( rc = sandev_read ( sandev, lba, count, + virt_to_user ( scratch ) ) ) != 0 ) { DBGC ( sandev, "SAN %#02x could not read ISO9660 primary" "volume descriptor: %s\n", sandev->drive, strerror ( rc ) ); diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 8b3e2b282..b163a94b8 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -236,12 +236,10 @@ static inline int sandev_needs_reopen ( struct san_device *sandev ) { extern struct san_device * sandev_find ( unsigned int drive ); extern int sandev_reopen ( struct san_device *sandev ); extern int sandev_reset ( struct san_device *sandev ); -extern int sandev_rw ( struct san_device *sandev, uint64_t lba, - unsigned int count, userptr_t buffer, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len ) ); +extern int sandev_read ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer ); +extern int sandev_write ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer ); extern struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, size_t priv_size ); extern int register_sandev ( struct san_device *sandev, unsigned int drive, diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 70a87dcf1..c6445ab6c 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -102,15 +102,14 @@ struct efi_block_data { * @v lba Starting LBA * @v data Data buffer * @v len Size of buffer - * @v block_rw Block read/write method + * @v sandev_rw SAN device read/write method * @ret rc Return status code */ static int efi_block_rw ( struct san_device *sandev, uint64_t lba, void *data, size_t len, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len ) ){ + int ( * sandev_rw ) ( struct san_device *sandev, + uint64_t lba, unsigned int count, + userptr_t buffer ) ) { struct efi_block_data *block = sandev->priv; unsigned int count; int rc; @@ -124,8 +123,8 @@ static int efi_block_rw ( struct san_device *sandev, uint64_t lba, } /* Read from / write to block device */ - if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( data ), - block_rw ) ) != 0 ) { + if ( ( rc = sandev_rw ( sandev, lba, count, + virt_to_user ( data ) ) ) != 0 ) { DBGC ( sandev, "EFIBLK %#02x I/O failed: %s\n", sandev->drive, strerror ( rc ) ); return rc; @@ -176,7 +175,7 @@ efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, DBGC2 ( sandev, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", sandev->drive, lba, data, ( ( size_t ) len ) ); efi_snp_claim(); - rc = efi_block_rw ( sandev, lba, data, len, block_read ); + rc = efi_block_rw ( sandev, lba, data, len, sandev_read ); efi_snp_release(); return EFIRC ( rc ); } @@ -202,7 +201,7 @@ efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, DBGC2 ( sandev, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", sandev->drive, lba, data, ( ( size_t ) len ) ); efi_snp_claim(); - rc = efi_block_rw ( sandev, lba, data, len, block_write ); + rc = efi_block_rw ( sandev, lba, data, len, sandev_write ); efi_snp_release(); return EFIRC ( rc ); } From 648657b7762a9439879691e610f77afee4c56bcb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 26 Apr 2017 20:02:23 +0100 Subject: [PATCH 453/591] [block] Provide abstraction to allow system to be quiesced When performing a SAN boot via INT 13, there is no way for the operating system to indicate that it has finished using the INT 13 SAN device. We therefore have no opportunity to clean up state before the loaded operating system's native drivers take over. This can cause problems when booting Windows, which tends not to be forgiving of unexpected system state. Windows will typically write a flag to the SAN device as the last action before transferring control to the native drivers. We can use this as a heuristic to bring the system to a quiescent state (without performing a full shutdown); this provides us an opportunity to temporarily clean up state that could otherwise prevent a successful Windows boot. Signed-off-by: Michael Brown --- src/core/quiesce.c | 53 ++++++++++++++++++++++++++++++++++++++ src/core/sanboot.c | 15 +++++++++++ src/include/ipxe/quiesce.h | 31 ++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/core/quiesce.c create mode 100644 src/include/ipxe/quiesce.h diff --git a/src/core/quiesce.c b/src/core/quiesce.c new file mode 100644 index 000000000..5d2a919d0 --- /dev/null +++ b/src/core/quiesce.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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 + * + * Quiesce system + * + */ + +#include + +/** Quiesce system */ +void quiesce ( void ) { + struct quiescer *quiescer; + + /* Call all quiescers */ + for_each_table_entry ( quiescer, QUIESCERS ) { + quiescer->quiesce(); + } +} + +/** Unquiesce system */ +void unquiesce ( void ) { + struct quiescer *quiescer; + + /* Call all quiescers */ + for_each_table_entry ( quiescer, QUIESCERS ) { + quiescer->unquiesce(); + } +} diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 1fd634904..cabc48430 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -41,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @@ -365,6 +366,9 @@ int sandev_reopen ( struct san_device *sandev ) { struct san_path *sanpath; int rc; + /* Unquiesce system */ + unquiesce(); + /* Close any outstanding command and restart interfaces */ sandev_restart ( sandev, -ECONNRESET ); assert ( sandev->active == NULL ); @@ -503,6 +507,9 @@ sandev_command ( struct san_device *sandev, /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); + /* Unquiesce system */ + unquiesce(); + /* (Re)try command */ do { @@ -654,6 +661,14 @@ int sandev_write ( struct san_device *sandev, uint64_t lba, if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_write ) ) != 0 ) return rc; + /* Quiesce system. This is a heuristic designed to ensure + * that the system is quiesced before Windows starts up, since + * a Windows SAN boot will typically write a status flag to + * the disk as its last action before transferring control to + * the native drivers. + */ + quiesce(); + return 0; } diff --git a/src/include/ipxe/quiesce.h b/src/include/ipxe/quiesce.h new file mode 100644 index 000000000..00b530b83 --- /dev/null +++ b/src/include/ipxe/quiesce.h @@ -0,0 +1,31 @@ +#ifndef _IPXE_QUIESCE_H +#define _IPXE_QUIESCE_H + +/** @file + * + * Quiesce system + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A quiescer */ +struct quiescer { + /** Quiesce system */ + void ( * quiesce ) ( void ); + /** Unquiesce system */ + void ( * unquiesce ) ( void ); +}; + +/** Quiescer table */ +#define QUIESCERS __table ( struct quiescer, "quiescers" ) + +/** Declare a quiescer */ +#define __quiescer __table_entry ( QUIESCERS, 01 ) + +extern void quiesce ( void ); +extern void unquiesce ( void ); + +#endif /* _IPXE_QUIESCE_H */ From a0f6e75532c68f49b3e1c73ca88151d9663f5269 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Apr 2017 13:33:52 +0100 Subject: [PATCH 454/591] [hyperv] Do not fail if guest OS ID MSR is already set Signed-off-by: Michael Brown --- src/arch/x86/drivers/hyperv/hyperv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index cc6e38688..8298f2097 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -245,9 +245,8 @@ static int hv_map_hypercall ( struct hv_hypervisor *hv ) { /* Report guest OS identity */ guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID ); if ( guest_os_id != 0 ) { - DBGC ( hv, "HV %p guest OS ID MSR already set to %#08llx\n", + DBGC ( hv, "HV %p guest OS ID MSR was %#08llx\n", hv, guest_os_id ); - return -EBUSY; } guest_os_id = HV_GUEST_OS_ID_IPXE; DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id ); From 276d618ca9588198acde376e28e49bd5a93b20d6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Apr 2017 16:18:58 +0100 Subject: [PATCH 455/591] [hyperv] Remove redundant return status code from mapping functions Signed-off-by: Michael Brown --- src/arch/x86/drivers/hyperv/hyperv.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index 8298f2097..b90937df3 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -223,9 +223,8 @@ static int hv_check_features ( struct hv_hypervisor *hv ) { * Map hypercall page * * @v hv Hyper-V hypervisor - * @ret rc Return status code */ -static int hv_map_hypercall ( struct hv_hypervisor *hv ) { +static void hv_map_hypercall ( struct hv_hypervisor *hv ) { union { struct { uint32_t ebx; @@ -267,8 +266,6 @@ static int hv_map_hypercall ( struct hv_hypervisor *hv ) { hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE ); DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall ); wrmsr ( HV_X64_MSR_HYPERCALL, hypercall ); - - return 0; } /** @@ -296,9 +293,8 @@ static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) { * Map synthetic interrupt controller * * @v hv Hyper-V hypervisor - * @ret rc Return status code */ -static int hv_map_synic ( struct hv_hypervisor *hv ) { +static void hv_map_synic ( struct hv_hypervisor *hv ) { uint64_t simp; uint64_t siefp; uint64_t scontrol; @@ -322,8 +318,6 @@ static int hv_map_synic ( struct hv_hypervisor *hv ) { scontrol |= HV_SCONTROL_ENABLE; DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); - - return 0; } /** @@ -552,12 +546,10 @@ static int hv_probe ( struct root_device *rootdev ) { goto err_alloc_message; /* Map hypercall page */ - if ( ( rc = hv_map_hypercall ( hv ) ) != 0 ) - goto err_map_hypercall; + hv_map_hypercall ( hv ); /* Map synthetic interrupt controller */ - if ( ( rc = hv_map_synic ( hv ) ) != 0 ) - goto err_map_synic; + hv_map_synic ( hv ); /* Probe Hyper-V devices */ if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 ) @@ -569,9 +561,7 @@ static int hv_probe ( struct root_device *rootdev ) { vmbus_remove ( hv, &rootdev->dev ); err_vmbus_probe: hv_unmap_synic ( hv ); - err_map_synic: hv_unmap_hypercall ( hv ); - err_map_hypercall: hv_free_message ( hv ); err_alloc_message: hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, From b91cc983da48b2791a672431551f7859e33126ec Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Apr 2017 14:13:22 +0100 Subject: [PATCH 456/591] [hyperv] Cope with Windows Server 2016 enlightenments An "enlightened" external bootloader (such as Windows Server 2016's winload.exe) may take ownership of the Hyper-V connection before all INT 13 operations have been completed. When this happens, all VMBus devices are implicitly closed and we are left with a non-functional network connection. Detect when our Hyper-V connection has been lost (by checking the SynIC message page MSR). Reclaim ownership of the Hyper-V connection and reestablish any VMBus devices, without disrupting any existing iPXE state (such as IPv4 settings attached to the network device). Windows Server 2016 will not cleanly take ownership of an active Hyper-V connection. Experimentation shows that we can quiesce by resetting only the SynIC message page MSR; this results in a successful SAN boot (on a Windows 2012 R2 physical host). Choose to quiesce by resetting (almost) all MSRs, in the hope that this will be more robust against corner cases such as a stray synthetic interrupt occurring during the handover. Signed-off-by: Michael Brown --- src/arch/x86/drivers/hyperv/hyperv.c | 135 +++++++++++++++++++++++++-- src/drivers/net/netvsc.c | 47 ++++++++++ src/drivers/net/netvsc.h | 15 +++ src/include/ipxe/hyperv.h | 3 + src/include/ipxe/vmbus.h | 26 ++++++ src/interface/hyperv/vmbus.c | 133 +++++++++++++++++++++++++- 6 files changed, 345 insertions(+), 14 deletions(-) diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index b90937df3..98c2b30c0 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -299,6 +300,10 @@ static void hv_map_synic ( struct hv_hypervisor *hv ) { uint64_t siefp; uint64_t scontrol; + /* Zero SynIC message and event pages */ + memset ( hv->synic.message, 0, PAGE_SIZE ); + memset ( hv->synic.event, 0, PAGE_SIZE ); + /* Map SynIC message page */ simp = rdmsr ( HV_X64_MSR_SIMP ); simp &= ( PAGE_SIZE - 1 ); @@ -321,21 +326,14 @@ static void hv_map_synic ( struct hv_hypervisor *hv ) { } /** - * Unmap synthetic interrupt controller + * Unmap synthetic interrupt controller, leaving SCONTROL untouched * * @v hv Hyper-V hypervisor */ -static void hv_unmap_synic ( struct hv_hypervisor *hv ) { - uint64_t scontrol; +static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor *hv ) { uint64_t siefp; uint64_t simp; - /* Disable SynIC */ - scontrol = rdmsr ( HV_X64_MSR_SCONTROL ); - scontrol &= ~HV_SCONTROL_ENABLE; - DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); - wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); - /* Unmap SynIC event page */ siefp = rdmsr ( HV_X64_MSR_SIEFP ); siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE ); @@ -349,6 +347,24 @@ static void hv_unmap_synic ( struct hv_hypervisor *hv ) { wrmsr ( HV_X64_MSR_SIMP, simp ); } +/** + * Unmap synthetic interrupt controller + * + * @v hv Hyper-V hypervisor + */ +static void hv_unmap_synic ( struct hv_hypervisor *hv ) { + uint64_t scontrol; + + /* Disable SynIC */ + scontrol = rdmsr ( HV_X64_MSR_SCONTROL ); + scontrol &= ~HV_SCONTROL_ENABLE; + DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); + wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); + + /* Unmap SynIC event and message pages */ + hv_unmap_synic_no_scontrol ( hv ); +} + /** * Enable synthetic interrupt * @@ -385,8 +401,12 @@ void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) { unsigned long msr = HV_X64_MSR_SINT ( sintx ); uint64_t sint; - /* Disable synthetic interrupt */ + /* Do nothing if interrupt is already disabled */ sint = rdmsr ( msr ); + if ( sint & HV_SINT_MASKED ) + return; + + /* Disable synthetic interrupt */ sint &= ~HV_SINT_AUTO_EOI; sint |= HV_SINT_MASKED; DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint ); @@ -589,6 +609,7 @@ static void hv_remove ( struct root_device *rootdev ) { hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, NULL ); free ( hv ); + rootdev_set_drvdata ( rootdev, NULL ); } /** Hyper-V root device driver */ @@ -603,6 +624,100 @@ struct root_device hv_root_device __root_device = { .driver = &hv_root_driver, }; +/** + * Quiesce system + * + */ +static void hv_quiesce ( void ) { + struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device ); + unsigned int i; + + /* Do nothing if we are not running in Hyper-V */ + if ( ! hv ) + return; + + /* The "enlightened" portions of the Windows Server 2016 boot + * process will not cleanly take ownership of an active + * Hyper-V connection. Experimentation shows that the minimum + * requirement is that we disable the SynIC message page + * (i.e. zero the SIMP MSR). + * + * We cannot perform a full shutdown of the Hyper-V + * connection. Experimentation shows that if we disable the + * SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016 + * will enter an indefinite wait loop. + * + * Attempt to create a safe handover environment by resetting + * all MSRs except for SCONTROL. + * + * Note that we do not shut down our VMBus devices, since we + * may need to unquiesce the system and continue operation. + */ + + /* Disable all synthetic interrupts */ + for ( i = 0 ; i <= HV_SINT_MAX ; i++ ) + hv_disable_sint ( hv, i ); + + /* Unmap synthetic interrupt controller, leaving SCONTROL + * enabled (see above). + */ + hv_unmap_synic_no_scontrol ( hv ); + + /* Unmap hypercall page */ + hv_unmap_hypercall ( hv ); + + DBGC ( hv, "HV %p quiesced\n", hv ); +} + +/** + * Unquiesce system + * + */ +static void hv_unquiesce ( void ) { + struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device ); + uint64_t simp; + int rc; + + /* Do nothing if we are not running in Hyper-V */ + if ( ! hv ) + return; + + /* Experimentation shows that the "enlightened" portions of + * Windows Server 2016 will break our Hyper-V connection at + * some point during a SAN boot. Surprisingly it does not + * change the guest OS ID MSR, but it does leave the SynIC + * message page disabled. + * + * Our own explicit quiescing procedure will also disable the + * SynIC message page. We can therefore use the SynIC message + * page enable bit as a heuristic to determine when we need to + * reestablish our Hyper-V connection. + */ + simp = rdmsr ( HV_X64_MSR_SIMP ); + if ( simp & HV_SIMP_ENABLE ) + return; + + /* Remap hypercall page */ + hv_map_hypercall ( hv ); + + /* Remap synthetic interrupt controller */ + hv_map_synic ( hv ); + + /* Reset Hyper-V devices */ + if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) { + DBGC ( hv, "HV %p could not unquiesce: %s\n", + hv, strerror ( rc ) ); + /* Nothing we can do */ + return; + } +} + +/** Hyper-V quiescer */ +struct quiescer hv_quiescer __quiescer = { + .quiesce = hv_quiesce, + .unquiesce = hv_unquiesce, +}; + /** * Probe timer * diff --git a/src/drivers/net/netvsc.c b/src/drivers/net/netvsc.c index d269cd63e..5be52fb8e 100644 --- a/src/drivers/net/netvsc.c +++ b/src/drivers/net/netvsc.c @@ -259,6 +259,15 @@ static int netvsc_revoke_buffer ( struct netvsc_device *netvsc, struct netvsc_revoke_buffer_message msg; int rc; + /* If the buffer's GPADL is obsolete (i.e. was created before + * the most recent Hyper-V reset), then we will never receive + * a response to the revoke message. Since the GPADL is + * already destroyed as far as the hypervisor is concerned, no + * further action is required. + */ + if ( netvsc_is_obsolete ( netvsc ) ) + return 0; + /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( buffer->revoke_type ); @@ -474,6 +483,14 @@ static int netvsc_transmit ( struct rndis_device *rndis, uint64_t xid; int rc; + /* If the device is obsolete (i.e. was opened before the most + * recent Hyper-V reset), then we will never receive transmit + * completions. Fail transmissions immediately to minimise + * the delay in closing and reopening the device. + */ + if ( netvsc_is_obsolete ( netvsc ) ) + return -EPIPE; + /* Sanity check */ assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); @@ -823,6 +840,35 @@ static int netvsc_probe ( struct vmbus_device *vmdev ) { return rc; } +/** + * Reset device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ +static int netvsc_reset ( struct vmbus_device *vmdev ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + struct net_device *netdev = rndis->netdev; + int rc; + + /* A closed device holds no NetVSC (or RNDIS) state, so there + * is nothing to reset. + */ + if ( ! netdev_is_open ( netdev ) ) + return 0; + + /* Close and reopen device to reset any stale state */ + netdev_close ( netdev ); + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not reopen: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + /** * Remove device * @@ -844,5 +890,6 @@ struct vmbus_driver netvsc_driver __vmbus_driver = { .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ), .probe = netvsc_probe, + .reset = netvsc_reset, .remove = netvsc_remove, }; diff --git a/src/drivers/net/netvsc.h b/src/drivers/net/netvsc.h index 39eeb891c..93192357f 100644 --- a/src/drivers/net/netvsc.h +++ b/src/drivers/net/netvsc.h @@ -362,4 +362,19 @@ struct netvsc_device { int wait_rc; }; +/** + * Check if NetVSC device is obsolete + * + * @v netvsc NetVSC device + * @v is_obsolete NetVSC device is obsolete + * + * Check if NetVSC device is obsolete (i.e. was opened before the most + * recent Hyper-V reset). + */ +static inline __attribute__ (( always_inline )) int +netvsc_is_obsolete ( struct netvsc_device *netvsc ) { + + return vmbus_gpadl_is_obsolete ( netvsc->rx.gpadl ); +} + #endif /* _NETVSC_H */ diff --git a/src/include/ipxe/hyperv.h b/src/include/ipxe/hyperv.h index c61e2a083..9194a9766 100644 --- a/src/include/ipxe/hyperv.h +++ b/src/include/ipxe/hyperv.h @@ -61,6 +61,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Synthetic interrupt vector mask */ #define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff ) +/** Maximum synthetic interrupt number */ +#define HV_SINT_MAX 15 + /** Post message */ #define HV_POST_MESSAGE 0x005c diff --git a/src/include/ipxe/vmbus.h b/src/include/ipxe/vmbus.h index 26fc578c6..682441857 100644 --- a/src/include/ipxe/vmbus.h +++ b/src/include/ipxe/vmbus.h @@ -479,6 +479,8 @@ struct vmbus_device { /** Hyper-V hypervisor */ struct hv_hypervisor *hv; + /** Channel instance */ + union uuid instance; /** Channel ID */ unsigned int channel; /** Monitor ID */ @@ -527,6 +529,12 @@ struct vmbus_driver { * @ret rc Return status code */ int ( * probe ) ( struct vmbus_device *vmdev ); + /** Reset device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ + int ( * reset ) ( struct vmbus_device *vmdev ); /** Remove device * * @v vmdev VMBus device @@ -609,6 +617,23 @@ vmbus_unregister_pages ( struct vmbus_device *vmdev, list_del ( &pages->list ); } +extern unsigned int vmbus_obsolete_gpadl; + +/** + * Check if GPADL is obsolete + * + * @v gpadl GPADL ID + * @v is_obsolete GPADL ID is obsolete + * + * Check if GPADL is obsolete (i.e. was created before the most recent + * Hyper-V reset). + */ +static inline __attribute__ (( always_inline )) int +vmbus_gpadl_is_obsolete ( unsigned int gpadl ) { + + return ( gpadl <= vmbus_obsolete_gpadl ); +} + extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, size_t len ); extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, @@ -629,6 +654,7 @@ extern int vmbus_poll ( struct vmbus_device *vmdev ); extern void vmbus_dump_channel ( struct vmbus_device *vmdev ); extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ); +extern int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ); extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ); #endif /* _IPXE_VMBUS_H */ diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index 7915ddfe0..45a7caec4 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -50,6 +50,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define VMBUS_GPADL_MAGIC 0x18ae0000 +/** Current (i.e. most recently issued) GPADL ID */ +static unsigned int vmbus_gpadl = VMBUS_GPADL_MAGIC; + +/** Obsolete GPADL ID threshold + * + * When the Hyper-V connection is reset, any previous GPADLs are + * automatically rendered obsolete. + */ +unsigned int vmbus_obsolete_gpadl; + /** * Post message * @@ -281,12 +291,12 @@ int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, uint64_t pfn[pfn_count]; } __attribute__ (( packed )) gpadlhdr; const struct vmbus_gpadl_created *created = &vmbus->message->created; - static unsigned int gpadl = VMBUS_GPADL_MAGIC; + unsigned int gpadl; unsigned int i; int rc; /* Allocate GPADL ID */ - gpadl++; + gpadl = ++vmbus_gpadl; /* Construct message */ memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) ); @@ -347,6 +357,15 @@ int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) { const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown; int rc; + /* If GPADL is obsolete (i.e. was created before the most + * recent Hyper-V reset), then we will never receive a + * response to the teardown message. Since the GPADL is + * already destroyed as far as the hypervisor is concerned, no + * further action is required. + */ + if ( vmbus_gpadl_is_obsolete ( gpadl ) ) + return 0; + /* Construct message */ memset ( &teardown, 0, sizeof ( teardown ) ); teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN ); @@ -530,8 +549,7 @@ void vmbus_close ( struct vmbus_device *vmdev ) { } /* Tear down GPADL */ - if ( ( rc = vmbus_gpadl_teardown ( vmdev, - vmdev->gpadl ) ) != 0 ) { + if ( ( rc = vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ) ) != 0 ) { DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: " "%s\n", vmdev->dev.name, strerror ( rc ) ); /* We can't prevent the remote VM from continuing to @@ -1187,6 +1205,8 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, &parent->children ); vmdev->dev.parent = parent; vmdev->hv = hv; + memcpy ( &vmdev->instance, &offer->instance, + sizeof ( vmdev->instance ) ); vmdev->channel = channel; vmdev->monitor = offer->monitor; vmdev->signal = ( offer->monitored ? @@ -1201,6 +1221,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, } else if ( header->type == cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) { + /* End of offer list */ break; } else { @@ -1244,6 +1265,77 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, return rc; } + +/** + * Reset channels + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +static int vmbus_reset_channels ( struct hv_hypervisor *hv, + struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + const struct vmbus_offer_channel *offer = &vmbus->message->offer; + const union uuid *type; + struct vmbus_device *vmdev; + unsigned int channel; + int rc; + + /* Post message */ + if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0) + return rc; + + /* Collect responses */ + while ( 1 ) { + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 ) + return rc; + + /* Handle response */ + if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) { + + /* Parse offer */ + type = &offer->type; + channel = le32_to_cpu ( offer->channel ); + DBGC2 ( vmbus, "VMBUS %p offer %d type %s", + vmbus, channel, uuid_ntoa ( type ) ); + if ( offer->monitored ) + DBGC2 ( vmbus, " monitor %d", offer->monitor ); + DBGC2 ( vmbus, "\n" ); + + /* Do nothing with the offer; we already have all + * of the relevant state from the initial probe. + */ + + } else if ( header->type == + cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) { + + /* End of offer list */ + break; + + } else { + DBGC ( vmbus, "VMBUS %p unexpected offer response type " + "%d\n", vmbus, le32_to_cpu ( header->type ) ); + return -EPROTO; + } + } + + /* Reset all devices */ + list_for_each_entry ( vmdev, &parent->children, dev.siblings ) { + if ( ( rc = vmdev->driver->reset ( vmdev ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not reset: %s\n", + vmdev->dev.name, strerror ( rc ) ); + /* Continue attempting to reset other devices */ + continue; + } + } + + return 0; +} + /** * Remove channels * @@ -1330,6 +1422,39 @@ int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) { return rc; } +/** + * Reset Hyper-V virtual machine bus + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + int rc; + + /* Mark all existent GPADLs as obsolete */ + vmbus_obsolete_gpadl = vmbus_gpadl; + + /* Clear interrupt and monitor pages */ + memset ( vmbus->intr, 0, PAGE_SIZE ); + memset ( vmbus->monitor_in, 0, PAGE_SIZE ); + memset ( vmbus->monitor_out, 0, PAGE_SIZE ); + + /* Enable message interrupt */ + hv_enable_sint ( hv, VMBUS_MESSAGE_SINT ); + + /* Renegotiate protocol version */ + if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 ) + return rc; + + /* Reenumerate channels */ + if ( ( rc = vmbus_reset_channels ( hv, parent ) ) != 0 ) + return rc; + + return 0; +} + /** * Remove Hyper-V virtual machine bus * From 17887f87b78fe5f0ac52f23a4eb0584794462d84 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 1 May 2017 14:01:54 +0100 Subject: [PATCH 457/591] [efi] Standardise PCI debug messages Use the PCI bus:dev.fn address in debug messages, falling back to the EFI handle name only if we do not yet have enough information to determine the bus:dev.fn address. Include the vendor and device IDs in debug messages when no suitable driver is found, to match the diagnostics available in a BIOS environment. Signed-off-by: Michael Brown --- src/interface/efi/efi_pci.c | 60 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 9f0851a56..c1f451c99 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -87,8 +87,8 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, &efi_pci_root_bridge_io_protocol_guid, NULL, &num_handles, &handles ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( pci, "EFIPCI cannot locate root bridges: %s\n", - strerror ( rc ) ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot locate root bridges: " + "%s\n", PCI_ARGS ( pci ), strerror ( rc ) ); goto err_locate; } @@ -100,8 +100,9 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, &u.interface, efi_image_handle, *handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( pci, "EFIPCI cannot open %s: %s\n", - efi_handle_name ( *handle ), strerror ( rc ) ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n", + PCI_ARGS ( pci ), efi_handle_name ( *handle ), + strerror ( rc ) ); continue; } if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) { @@ -113,7 +114,7 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, &efi_pci_root_bridge_io_protocol_guid, efi_image_handle, *handle ); } - DBGC ( pci, "EFIPCI found no root bridge for " PCI_FMT "\n", + DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n", PCI_ARGS ( pci ) ); rc = -ENOENT; @@ -163,9 +164,9 @@ int efipci_read ( struct pci_device *pci, unsigned long location, efipci_address ( pci, location ), 1, value ) ) != 0 ) { rc = -EEFI ( efirc ); - DBG ( "EFIPCI config read from " PCI_FMT " offset %02lx " - "failed: %s\n", PCI_ARGS ( pci ), - EFIPCI_OFFSET ( location ), strerror ( rc ) ); + DBGC ( pci, "EFIPCI " PCI_FMT " config read from offset %02lx " + "failed: %s\n", PCI_ARGS ( pci ), + EFIPCI_OFFSET ( location ), strerror ( rc ) ); goto err_read; } @@ -201,9 +202,9 @@ int efipci_write ( struct pci_device *pci, unsigned long location, efipci_address ( pci, location ), 1, &value ) ) != 0 ) { rc = -EEFI ( efirc ); - DBG ( "EFIPCI config write to " PCI_FMT " offset %02lx " - "failed: %s\n", PCI_ARGS ( pci ), - EFIPCI_OFFSET ( location ), strerror ( rc ) ); + DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx " + "failed: %s\n", PCI_ARGS ( pci ), + EFIPCI_OFFSET ( location ), strerror ( rc ) ); goto err_write; } @@ -268,10 +269,10 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, efi_handle_name ( device ), strerror ( rc ) ); goto err_get_location; } - DBGC2 ( device, "EFIPCI %s is PCI %04lx:%02lx:%02lx.%lx\n", - efi_handle_name ( device ), ( ( unsigned long ) pci_segment ), - ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), - ( ( unsigned long ) pci_fn ) ); + busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); + pci_init ( pci, busdevfn ); + DBGCP ( device, "EFIPCI " PCI_FMT " is %s\n", + PCI_ARGS ( pci ), efi_handle_name ( device ) ); /* Try to enable I/O cycles, memory cycles, and bus mastering. * Some platforms will 'helpfully' report errors if these bits @@ -290,11 +291,10 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ - busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); - pci_init ( pci, busdevfn ); if ( ( rc = pci_read_config ( pci ) ) != 0 ) { - DBGC ( device, "EFIPCI %s cannot read PCI configuration: %s\n", - efi_handle_name ( device ), strerror ( rc ) ); + DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI " + "configuration: %s\n", + PCI_ARGS ( pci ), strerror ( rc ) ); goto err_pci_read_config; } @@ -364,12 +364,14 @@ static int efipci_supported ( EFI_HANDLE device ) { /* Look for a driver */ if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { - DBGCP ( device, "EFIPCI %s has no driver\n", - efi_handle_name ( device ) ); + DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " + "has no driver\n", PCI_ARGS ( &pci ), pci.vendor, + pci.device, pci.class ); return rc; } - DBGC ( device, "EFIPCI %s has driver \"%s\"\n", - efi_handle_name ( device ), pci.id->name ); + DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) has driver " + "\"%s\"\n", PCI_ARGS ( &pci ), pci.vendor, pci.device, + pci.class, pci.id->name ); return 0; } @@ -404,8 +406,8 @@ static int efipci_start ( struct efi_device *efidev ) { /* Find driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { - DBGC ( device, "EFIPCI %s has no driver\n", - efi_handle_name ( device ) ); + DBGC ( device, "EFIPCI " PCI_FMT " has no driver\n", + PCI_ARGS ( pci ) ); goto err_find_driver; } @@ -415,13 +417,13 @@ static int efipci_start ( struct efi_device *efidev ) { /* Probe driver */ if ( ( rc = pci_probe ( pci ) ) != 0 ) { - DBGC ( device, "EFIPCI %s could not probe driver \"%s\": %s\n", - efi_handle_name ( device ), pci->id->name, + DBGC ( device, "EFIPCI " PCI_FMT " could not probe driver " + "\"%s\": %s\n", PCI_ARGS ( pci ), pci->id->name, strerror ( rc ) ); goto err_probe; } - DBGC ( device, "EFIPCI %s using driver \"%s\"\n", - efi_handle_name ( device ), pci->id->name ); + DBGC ( device, "EFIPCI " PCI_FMT " using driver \"%s\"\n", + PCI_ARGS ( pci ), pci->id->name ); efidev_set_drvdata ( efidev, pci ); return 0; From 785389c2ba84870e003c23304ca88aa4ec1f144d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 3 May 2017 13:01:11 +0100 Subject: [PATCH 458/591] [iscsi] Always send FirstBurstLength parameter As of kernel 4.11, the LIO target will propose a value for FirstBurstLength if the initiator did not do so. This is entirely redundant in our case, since FirstBurstLength is defined by RFC 3720 to be "Irrelevant when: ( InitialR2T=Yes and ImmediateData=No )" and we already enforce both InitialR2T=Yes and ImmediateData=No in our initial proposal. However, LIO (arguably correctly) complains when we do not respond to its redundant proposal of an already-irrelevant value. Fix by always proposing the default value for FirstBurstLength. Debugged-by: Patrick Seeburger Tested-by: Patrick Seeburger Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 7de4a7bf6..789ac86e6 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -644,12 +644,12 @@ static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, * * HeaderDigest=None * DataDigest=None - * MaxConnections is irrelevant; we make only one connection anyway [4] + * MaxConnections=1 (irrelevant; we make only one connection anyway) [4] * InitialR2T=Yes [1] - * ImmediateData is irrelevant; we never send immediate data [4] + * ImmediateData=No (irrelevant; we never send immediate data) [4] * MaxRecvDataSegmentLength=8192 (default; we don't care) [3] * MaxBurstLength=262144 (default; we don't care) [3] - * FirstBurstLength=262144 (default; we don't care) + * FirstBurstLength=65536 (irrelevant due to other settings) [5] * DefaultTime2Wait=0 [2] * DefaultTime2Retain=0 [2] * MaxOutstandingR2T=1 @@ -674,6 +674,11 @@ static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, * these parameters, but some targets (notably a QNAP TS-639Pro) fail * unless they are supplied, so we explicitly specify the default * values. + * + * [5] FirstBurstLength is defined to be irrelevant since we already + * force InitialR2T=Yes and ImmediateData=No, but some targets + * (notably LIO as of kernel 4.11) fail unless it is specified, so we + * explicitly specify the default value. */ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, void *data, size_t len ) { @@ -732,13 +737,14 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, "ImmediateData=No%c" "MaxRecvDataSegmentLength=8192%c" "MaxBurstLength=262144%c" + "FirstBurstLength=65536%c" "DefaultTime2Wait=0%c" "DefaultTime2Retain=0%c" "MaxOutstandingR2T=1%c" "DataPDUInOrder=Yes%c" "DataSequenceInOrder=Yes%c" "ErrorRecoveryLevel=0%c", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); } return used; From a19ac24971214e6b14b9c50b377c24079523dbb5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 10 May 2017 15:30:51 +0100 Subject: [PATCH 459/591] [iscsi] Fix iBFT when no explicit initiator name setting exists Commit 7cfdd76 ("[block] Describe all SAN devices via ACPI tables") changed the definition of the iSCSI initiator IQN in the iBFT to represent a common initiator IQN used for all iSCSI sessions, and attempted to calculate this common initiator IQN by fetching the common ${initiator-iqn} setting. This fails when no explicit ${initiator-iqn} has been specified (i.e. when an initiator IQN has instead been constructed from either the hostname or system UUID), and results in an empty initiator IQN in the iBFT. Fix by using the initiator IQN of an arbitrary iSCSI session present in the iBFT. Debugged-by: Tal Aloni Signed-off-by: Michael Brown --- src/drivers/block/ibft.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c index bb7812b3b..f9918363a 100644 --- a/src/drivers/block/ibft.c +++ b/src/drivers/block/ibft.c @@ -338,10 +338,12 @@ static int ibft_fill_nic ( struct ibft_nic *nic, * * @v initiator Initiator portion of iBFT * @v strings iBFT string block descriptor + * @v initiator_iqn Initiator IQN * @ret rc Return status code */ static int ibft_fill_initiator ( struct ibft_initiator *initiator, - struct ibft_strings *strings ) { + struct ibft_strings *strings, + const char *initiator_iqn ) { int rc; /* Fill in common header */ @@ -352,9 +354,8 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator, IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ); /* Fill in initiator name */ - if ( ( rc = ibft_set_string_setting ( NULL, strings, - &initiator->initiator_name, - &initiator_iqn_setting ) ) != 0 ) + if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name, + initiator_iqn ) ) != 0 ) return rc; DBG ( "iBFT initiator name = %s\n", ibft_string ( strings, &initiator->initiator_name ) ); @@ -613,7 +614,10 @@ static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { /* Fill in Initiator block */ initiator = ( data + initiator_offset ); table->control.initiator = cpu_to_le16 ( initiator_offset ); - if ( ( rc = ibft_fill_initiator ( initiator, &strings ) ) != 0 ) + iscsi = list_first_entry ( &ibft_model.descs, struct iscsi_session, + desc.list ); + if ( ( rc = ibft_fill_initiator ( initiator, &strings, + iscsi->initiator_iqn ) ) != 0 ) goto err_initiator; /* Fill in NIC blocks */ From 7457bfc5b29cee677ab9a49efcea4a476a648a1a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 May 2017 02:50:21 +0100 Subject: [PATCH 460/591] [xen] Provide 18 4kB receive buffers to work around xen-netback bug The Xen network backend (xen-netback) suffered from a regression between upstream Linux kernels 3.18 and 4.2 inclusive, which would cause packet reception to fail unless at least 18 receive buffers were available. This bug was fixed in kernel commit 1d5d485 ("xen-netback: require fewer guest Rx slots when not using GSO"). Work around this bug in affected versions of xen-netback by providing the requisite 18 receive buffers. Reported-by: Taylor Schneider Tested-by: Taylor Schneider Signed-off-by: Michael Brown --- src/drivers/net/netfront.c | 19 ++++++++++--------- src/drivers/net/netfront.h | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 2f4bbf2a0..b6205542b 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -511,15 +511,12 @@ static void netfront_refill_rx ( struct net_device *netdev ) { struct xen_device *xendev = netfront->xendev; struct io_buffer *iobuf; struct netif_rx_request *request; + unsigned int refilled = 0; int notify; int rc; - /* Do nothing if ring is already full */ - if ( netfront_ring_is_full ( &netfront->rx ) ) - return; - /* Refill ring */ - do { + while ( netfront_ring_fill ( &netfront->rx ) < NETFRONT_RX_FILL ) { /* Allocate I/O buffer */ iobuf = alloc_iob ( PAGE_SIZE ); @@ -543,13 +540,17 @@ static void netfront_refill_rx ( struct net_device *netdev ) { /* Move to next descriptor */ netfront->rx_fring.req_prod_pvt++; + refilled++; - } while ( ! netfront_ring_is_full ( &netfront->rx ) ); + } /* Push new descriptors and notify backend if applicable */ - RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, notify ); - if ( notify ) - netfront_send_event ( netfront ); + if ( refilled ) { + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, + notify ); + if ( notify ) + netfront_send_event ( netfront ); + } } /** diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h index 38fd0a77e..c95ed2645 100644 --- a/src/drivers/net/netfront.h +++ b/src/drivers/net/netfront.h @@ -16,7 +16,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define NETFRONT_NUM_TX_DESC 16 /** Number of receive ring entries */ -#define NETFRONT_NUM_RX_DESC 8 +#define NETFRONT_NUM_RX_DESC 32 + +/** Receive ring fill level + * + * The xen-netback driver from kernels 3.18 to 4.2 inclusive have a + * bug (CA-163395) which prevents packet reception if fewer than 18 + * receive descriptors are available. This was fixed in upstream + * kernel commit d5d4852 ("xen-netback: require fewer guest Rx slots + * when not using GSO"). + * + * We provide 18 receive descriptors to avoid unpleasant silent + * failures on these kernel versions. + */ +#define NETFRONT_RX_FILL 18 /** Grant reference indices */ enum netfront_ref_index { @@ -88,6 +101,21 @@ netfront_init_ring ( struct netfront_ring *ring, const char *ref_key, ring->ids = ids; } +/** + * Calculate descriptor ring fill level + * + * @v ring Descriptor ring + * @v fill Fill level + */ +static inline __attribute__ (( always_inline )) unsigned int +netfront_ring_fill ( struct netfront_ring *ring ) { + unsigned int fill_level; + + fill_level = ( ring->id_prod - ring->id_cons ); + assert ( fill_level <= ring->count ); + return fill_level; +} + /** * Check whether or not descriptor ring is full * @@ -96,11 +124,8 @@ netfront_init_ring ( struct netfront_ring *ring, const char *ref_key, */ static inline __attribute__ (( always_inline )) int netfront_ring_is_full ( struct netfront_ring *ring ) { - unsigned int fill_level; - fill_level = ( ring->id_prod - ring->id_cons ); - assert ( fill_level <= ring->count ); - return ( fill_level >= ring->count ); + return ( netfront_ring_fill ( ring ) >= ring->count ); } /** @@ -112,7 +137,7 @@ netfront_ring_is_full ( struct netfront_ring *ring ) { static inline __attribute__ (( always_inline )) int netfront_ring_is_empty ( struct netfront_ring *ring ) { - return ( ring->id_prod == ring->id_cons ); + return ( netfront_ring_fill ( ring ) == 0 ); } /** A netfront NIC */ From de37652044d51a73e8c65353e8b74a3544c54183 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 May 2017 12:21:18 +0100 Subject: [PATCH 461/591] [efi] Prevent EFI code from being linked in to non-EFI builds Ensure that efi_systab is an undefined symbol in non-EFI builds. In particular, this prevents users from incorrectly enabling IMAGE_EFI in a BIOS build of iPXE. Signed-off-by: Michael Brown --- src/interface/efi/efi_init.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index cfaff606e..ed9707f2c 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -32,8 +32,14 @@ EFI_HANDLE efi_image_handle; /** Loaded image protocol for this image */ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; -/** System table passed to entry point */ -EFI_SYSTEM_TABLE *efi_systab; +/** System table passed to entry point + * + * We construct the symbol name efi_systab via the PLATFORM macro. + * This ensures that the symbol is defined only in EFI builds, and so + * prevents EFI code from being incorrectly linked in to a non-EFI + * build. + */ +EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab ); /** EFI shutdown is in progress */ int efi_shutdown_in_progress; From 2f126904555f3890a5c05868bb615d2fd62f8b0c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 May 2017 13:17:23 +0100 Subject: [PATCH 462/591] [tls] Keep cipherstream window open until TLS negotiation is complete When performing a SAN boot, the plainstream window size will be zero (since this is the mechanism used internally to indicate that no data should be fetched via the initial request). This zero value currently propagates to the advertised TCP window size, which prevents the TLS negotiation from completing. Fix by ensuring that the cipherstream window is held open until TLS negotiation is complete, and only then falling back to passing through the plainstream window size. Reported-by: John Wigley Tested-by: John Wigley Signed-off-by: Michael Brown --- src/net/tls.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/net/tls.c b/src/net/tls.c index 90f9f9767..2b809a628 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -2328,6 +2328,21 @@ static int tls_newdata_process_data ( struct tls_session *tls ) { return 0; } +/** + * Check flow control window + * + * @v tls TLS session + * @ret len Length of window + */ +static size_t tls_cipherstream_window ( struct tls_session *tls ) { + + /* Open window until we are ready to accept data */ + if ( ! tls_ready ( tls ) ) + return -1UL; + + return xfer_window ( &tls->plainstream ); +} + /** * Receive new ciphertext * @@ -2390,6 +2405,7 @@ static int tls_cipherstream_deliver ( struct tls_session *tls, static struct interface_operation tls_cipherstream_ops[] = { INTF_OP ( xfer_deliver, struct tls_session *, tls_cipherstream_deliver ), + INTF_OP ( xfer_window, struct tls_session *, tls_cipherstream_window ), INTF_OP ( xfer_window_changed, struct tls_session *, tls_tx_resume ), INTF_OP ( intf_close, struct tls_session *, tls_close ), }; From ee9897fe641acab11ab99c5635a97fce0cb10a17 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 May 2017 16:58:01 +0100 Subject: [PATCH 463/591] [settings] Extend numerical setting tags to 64 bits Signed-off-by: Michael Brown --- src/core/memmap_settings.c | 38 ++++++++++++++++++++++--------------- src/core/settings.c | 4 ++-- src/include/ipxe/settings.h | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c index 1098bd756..c620a0343 100644 --- a/src/core/memmap_settings.c +++ b/src/core/memmap_settings.c @@ -142,28 +142,36 @@ static int memmap_settings_fetch ( struct settings *settings, struct memory_map memmap; struct memory_region *region; uint64_t result = 0; - unsigned int i; + unsigned int start; unsigned int count; + unsigned int scale; + int include_start; + int include_length; + int ignore_nonexistent; + unsigned int i; - DBGC ( settings, "MEMMAP start %ld count %ld %s%s%s%s scale %ld\n", - MEMMAP_START ( setting->tag ), MEMMAP_COUNT ( setting->tag ), - ( MEMMAP_INCLUDE_START ( setting->tag ) ? "start" : "" ), - ( ( MEMMAP_INCLUDE_START ( setting->tag ) && - MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) ? "+" : "" ), - ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ? "length" : "" ), - ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ? " ignore" : "" ), - MEMMAP_SCALE ( setting->tag ) ); + /* Parse settings tag */ + start = MEMMAP_START ( setting->tag ); + count = MEMMAP_COUNT ( setting->tag ); + scale = MEMMAP_SCALE ( setting->tag ); + include_start = MEMMAP_INCLUDE_START ( setting->tag ); + include_length = MEMMAP_INCLUDE_LENGTH ( setting->tag ); + ignore_nonexistent = MEMMAP_IGNORE_NONEXISTENT ( setting->tag ); + DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n", + start, count, ( include_start ? "start" : "" ), + ( ( include_start && include_length ) ? "+" : "" ), + ( include_length ? "length" : "" ), + ( ignore_nonexistent ? " ignore" : "" ), scale ); /* Fetch memory map */ get_memmap ( &memmap ); /* Extract results from memory map */ - count = MEMMAP_COUNT ( setting->tag ); - for ( i = MEMMAP_START ( setting->tag ) ; count-- ; i++ ) { + for ( i = start ; count-- ; i++ ) { /* Check that region exists */ if ( i >= memmap.count ) { - if ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ) { + if ( ignore_nonexistent ) { continue; } else { DBGC ( settings, "MEMMAP region %d does not " @@ -174,12 +182,12 @@ static int memmap_settings_fetch ( struct settings *settings, /* Extract results from this region */ region = &memmap.regions[i]; - if ( MEMMAP_INCLUDE_START ( setting->tag ) ) { + if ( include_start ) { result += region->start; DBGC ( settings, "MEMMAP %d start %08llx\n", i, region->start ); } - if ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) { + if ( include_length ) { result += ( region->end - region->start ); DBGC ( settings, "MEMMAP %d length %08llx\n", i, ( region->end - region->start ) ); @@ -187,7 +195,7 @@ static int memmap_settings_fetch ( struct settings *settings, } /* Scale result */ - result >>= MEMMAP_SCALE ( setting->tag ); + result >>= scale; /* Return result */ result = cpu_to_be64 ( result ); diff --git a/src/core/settings.c b/src/core/settings.c index 879057224..3e5d416e7 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1478,9 +1478,9 @@ struct setting * find_setting ( const char *name ) { * @v name Name * @ret tag Tag number, or 0 if not a valid number */ -static unsigned long parse_setting_tag ( const char *name ) { +static uint64_t parse_setting_tag ( const char *name ) { char *tmp = ( ( char * ) name ); - unsigned long tag = 0; + uint64_t tag = 0; while ( 1 ) { tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) ); diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 36b4c2410..f463e6674 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -40,7 +40,7 @@ struct setting { * (such as a DHCP option number, or an SMBIOS structure and * field number). */ - unsigned long tag; + uint64_t tag; /** Setting scope (or NULL) * * For historic reasons, a NULL scope with a non-zero tag From 933e6dadc0b415b26c7c0752423e8284164945e0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 May 2017 19:27:30 +0100 Subject: [PATCH 464/591] [acpi] Make acpi_find_rsdt() a per-platform method Signed-off-by: Michael Brown --- src/arch/arm/include/bits/acpi.h | 12 +++ src/arch/x86/include/bits/acpi.h | 14 +++ src/arch/x86/include/ipxe/rsdp.h | 18 ++++ src/arch/x86/interface/pcbios/acpipwr.c | 18 +--- src/arch/x86/interface/pcbios/rsdp.c | 125 ++++++++++++++++++++++++ src/config/defaults/efi.h | 1 + src/config/defaults/pcbios.h | 1 + src/core/acpi.c | 91 ++++------------- src/core/null_acpi.c | 3 + src/include/ipxe/acpi.h | 62 +++++++++--- src/include/ipxe/null_acpi.h | 23 +++++ 11 files changed, 265 insertions(+), 103 deletions(-) create mode 100644 src/arch/arm/include/bits/acpi.h create mode 100644 src/arch/x86/include/bits/acpi.h create mode 100644 src/arch/x86/include/ipxe/rsdp.h create mode 100644 src/arch/x86/interface/pcbios/rsdp.c create mode 100644 src/core/null_acpi.c create mode 100644 src/include/ipxe/null_acpi.h diff --git a/src/arch/arm/include/bits/acpi.h b/src/arch/arm/include/bits/acpi.h new file mode 100644 index 000000000..f9f2f00e7 --- /dev/null +++ b/src/arch/arm/include/bits/acpi.h @@ -0,0 +1,12 @@ +#ifndef _BITS_ACPI_H +#define _BITS_ACPI_H + +/** @file + * + * ARM-specific ACPI API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_ACPI_H */ diff --git a/src/arch/x86/include/bits/acpi.h b/src/arch/x86/include/bits/acpi.h new file mode 100644 index 000000000..a6ff90804 --- /dev/null +++ b/src/arch/x86/include/bits/acpi.h @@ -0,0 +1,14 @@ +#ifndef _BITS_ACPI_H +#define _BITS_ACPI_H + +/** @file + * + * x86-specific ACPI API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_ACPI_H */ diff --git a/src/arch/x86/include/ipxe/rsdp.h b/src/arch/x86/include/ipxe/rsdp.h new file mode 100644 index 000000000..7e32c0011 --- /dev/null +++ b/src/arch/x86/include/ipxe/rsdp.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_RSDP_H +#define _IPXE_RSDP_H + +/** @file + * + * Standard PC-BIOS ACPI RSDP interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef ACPI_RSDP +#define ACPI_PREFIX_rsdp +#else +#define ACPI_PREFIX_rsdp __rsdp_ +#endif + +#endif /* _IPXE_RSDP_H */ diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c index d19f972d5..dc164c7d5 100644 --- a/src/arch/x86/interface/pcbios/acpipwr.c +++ b/src/arch/x86/interface/pcbios/acpipwr.c @@ -26,8 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include -#include #include #include #include @@ -51,8 +49,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int acpi_poweroff ( void ) { struct acpi_fadt fadtab; - uint16_t ebda; - userptr_t rsdt; userptr_t fadt; unsigned int pm1a_cnt_blk; unsigned int pm1b_cnt_blk; @@ -63,18 +59,8 @@ int acpi_poweroff ( void ) { int s5; int rc; - /* Locate EBDA */ - get_real ( ebda, BDA_SEG, BDA_EBDA ); - - /* Locate RSDT */ - rsdt = acpi_find_rsdt ( real_to_user ( ebda, 0 ) ); - if ( ! rsdt ) { - DBGC ( colour, "ACPI could not find RSDT (EBDA %04x)\n", ebda ); - return -ENOENT; - } - /* Locate FADT */ - fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 ); + fadt = acpi_find ( FADT_SIGNATURE, 0 ); if ( ! fadt ) { DBGC ( colour, "ACPI could not find FADT\n" ); return -ENOENT; @@ -88,7 +74,7 @@ int acpi_poweroff ( void ) { pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT ); /* Extract \_S5 from DSDT or any SSDT */ - s5 = acpi_sx ( rsdt, S5_SIGNATURE ); + s5 = acpi_sx ( S5_SIGNATURE ); if ( s5 < 0 ) { rc = s5; DBGC ( colour, "ACPI could not extract \\_S5: %s\n", diff --git a/src/arch/x86/interface/pcbios/rsdp.c b/src/arch/x86/interface/pcbios/rsdp.c new file mode 100644 index 000000000..8da0b5588 --- /dev/null +++ b/src/arch/x86/interface/pcbios/rsdp.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 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 + * + * ACPI Root System Description Pointer + * + */ + +#include +#include +#include +#include +#include + +/** EBDA RSDP maximum segment */ +#define RSDP_EBDA_END_SEG 0xa000 + +/** Fixed BIOS area RSDP start address */ +#define RSDP_BIOS_START 0xe0000 + +/** Fixed BIOS area RSDP length */ +#define RSDP_BIOS_LEN 0x20000 + +/** Stride at which to search for RSDP */ +#define RSDP_STRIDE 16 + +/** + * Locate ACPI root system description table within a memory range + * + * @v start Start address to search + * @v len Length to search + * @ret rsdt ACPI root system description table, or UNULL + */ +static userptr_t rsdp_find_rsdt_range ( userptr_t start, size_t len ) { + static const char signature[8] = RSDP_SIGNATURE; + struct acpi_rsdp rsdp; + userptr_t rsdt; + size_t offset; + uint8_t sum; + unsigned int i; + + /* Search for RSDP */ + for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ; + offset += RSDP_STRIDE ) { + + /* Check signature and checksum */ + copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) ); + if ( memcmp ( rsdp.signature, signature, + sizeof ( signature ) ) != 0 ) + continue; + for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ ) + sum += *( ( ( uint8_t * ) &rsdp ) + i ); + if ( sum != 0 ) + continue; + + /* Extract RSDT */ + rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) ); + DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n", + user_to_phys ( rsdt, 0 ), + user_to_phys ( start, offset ) ); + return rsdt; + } + + return UNULL; +} + +/** + * Locate ACPI root system description table + * + * @ret rsdt ACPI root system description table, or UNULL + */ +static userptr_t rsdp_find_rsdt ( void ) { + static userptr_t rsdt; + uint16_t ebda_seg; + userptr_t ebda; + size_t ebda_len; + + /* Return existing RSDT if already found */ + if ( rsdt ) + return rsdt; + + /* Search EBDA */ + get_real ( ebda_seg, BDA_SEG, BDA_EBDA ); + if ( ebda_seg < RSDP_EBDA_END_SEG ) { + ebda = real_to_user ( ebda_seg, 0 ); + ebda_len = ( ( RSDP_EBDA_END_SEG - ebda_seg ) * 16 ); + rsdt = rsdp_find_rsdt_range ( ebda, ebda_len ); + if ( rsdt ) + return rsdt; + } + + /* Search fixed BIOS area */ + rsdt = rsdp_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ), + RSDP_BIOS_LEN ); + if ( rsdt ) + return rsdt; + + return UNULL; +} + +PROVIDE_ACPI ( rsdp, acpi_find_rsdt, rsdp_find_rsdt ); diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index a4f70a1d5..5e1eed79a 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ENTROPY_EFI #define TIME_EFI #define REBOOT_EFI +#define ACPI_NULL #define DOWNLOAD_PROTO_FILE /* Local filesystem access */ diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index e1915054c..21821c95c 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ENTROPY_RTC #define TIME_RTC #define REBOOT_PCBIOS +#define ACPI_RSDP #ifdef __x86_64__ #define IOMAP_PAGES diff --git a/src/core/acpi.c b/src/core/acpi.c index 8ebe4b198..ff8875e1c 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -57,88 +57,30 @@ void acpi_fix_checksum ( struct acpi_header *acpi ) { acpi->checksum -= sum; } -/** - * Locate ACPI root system description table within a memory range - * - * @v start Start address to search - * @v len Length to search - * @ret rsdt ACPI root system description table, or UNULL - */ -static userptr_t acpi_find_rsdt_range ( userptr_t start, size_t len ) { - static const char signature[8] = RSDP_SIGNATURE; - struct acpi_rsdp rsdp; - userptr_t rsdt; - size_t offset; - uint8_t sum; - unsigned int i; - - /* Search for RSDP */ - for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ; - offset += RSDP_STRIDE ) { - - /* Check signature and checksum */ - copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) ); - if ( memcmp ( rsdp.signature, signature, - sizeof ( signature ) ) != 0 ) - continue; - for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ ) - sum += *( ( ( uint8_t * ) &rsdp ) + i ); - if ( sum != 0 ) - continue; - - /* Extract RSDT */ - rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) ); - DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n", - user_to_phys ( rsdt, 0 ), - user_to_phys ( start, offset ) ); - return rsdt; - } - - return UNULL; -} - -/** - * Locate ACPI root system description table - * - * @v ebda Extended BIOS data area, or UNULL - * @ret rsdt ACPI root system description table, or UNULL - */ -userptr_t acpi_find_rsdt ( userptr_t ebda ) { - userptr_t rsdt; - - /* Search EBDA, if applicable */ - if ( ebda ) { - rsdt = acpi_find_rsdt_range ( ebda, RSDP_EBDA_LEN ); - if ( rsdt ) - return rsdt; - } - - /* Search fixed BIOS area */ - rsdt = acpi_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ), - RSDP_BIOS_LEN ); - if ( rsdt ) - return rsdt; - - return UNULL; -} - /** * Locate ACPI table * - * @v rsdt ACPI root system description table * @v signature Requested table signature * @v index Requested index of table with this signature * @ret table Table, or UNULL if not found */ -userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) { +userptr_t acpi_find ( uint32_t signature, unsigned int index ) { struct acpi_header acpi; struct acpi_rsdt *rsdtab; typeof ( rsdtab->entry[0] ) entry; + userptr_t rsdt; userptr_t table; size_t len; unsigned int count; unsigned int i; + /* Locate RSDT */ + rsdt = acpi_find_rsdt(); + if ( ! rsdt ) { + DBG ( "RSDT not found\n" ); + return UNULL; + } + /* Read RSDT header */ copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) ); if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) { @@ -278,20 +220,27 @@ static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) { /** * Extract \_Sx value from DSDT/SSDT * - * @v rsdt ACPI root system description table * @v signature Signature (e.g. "_S5_") * @ret sx \_Sx value, or negative error */ -int acpi_sx ( userptr_t rsdt, uint32_t signature ) { +int acpi_sx ( uint32_t signature ) { struct acpi_fadt fadtab; + userptr_t rsdt; userptr_t fadt; userptr_t dsdt; userptr_t ssdt; unsigned int i; int sx; + /* Locate RSDT */ + rsdt = acpi_find_rsdt(); + if ( ! rsdt ) { + DBG ( "RSDT not found\n" ); + return -ENOENT; + } + /* Try DSDT first */ - fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 ); + fadt = acpi_find ( FADT_SIGNATURE, 0 ); if ( fadt ) { copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); dsdt = phys_to_user ( fadtab.dsdt ); @@ -301,7 +250,7 @@ int acpi_sx ( userptr_t rsdt, uint32_t signature ) { /* Try all SSDTs */ for ( i = 0 ; ; i++ ) { - ssdt = acpi_find ( rsdt, SSDT_SIGNATURE, i ); + ssdt = acpi_find ( SSDT_SIGNATURE, i ); if ( ! ssdt ) break; if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 ) diff --git a/src/core/null_acpi.c b/src/core/null_acpi.c new file mode 100644 index 000000000..90c784855 --- /dev/null +++ b/src/core/null_acpi.c @@ -0,0 +1,3 @@ +#include + +PROVIDE_ACPI_INLINE ( null, acpi_find_rsdt ); diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index f87b8ae9a..e7ca35da9 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -16,6 +16,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** * An ACPI description header @@ -89,18 +91,6 @@ struct acpi_rsdp { uint32_t rsdt; } __attribute__ (( packed )); -/** EBDA RSDP length */ -#define RSDP_EBDA_LEN 0x400 - -/** Fixed BIOS area RSDP start address */ -#define RSDP_BIOS_START 0xe0000 - -/** Fixed BIOS area RSDP length */ -#define RSDP_BIOS_LEN 0x20000 - -/** Stride at which to search for RSDP */ -#define RSDP_STRIDE 16 - /** Root System Description Table (RSDT) signature */ #define RSDT_SIGNATURE ACPI_SIGNATURE ( 'R', 'S', 'D', 'T' ) @@ -194,16 +184,56 @@ struct acpi_model { /** Declare an ACPI model */ #define __acpi_model __table_entry ( ACPI_MODELS, 01 ) +/** + * Calculate static inline ACPI API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define ACPI_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( ACPI_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an ACPI API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_ACPI( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( ACPI_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline ACPI API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_ACPI_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( ACPI_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent ACPI API headers */ +#include + +/* Include all architecture-dependent ACPI API headers */ +#include + +/** + * Locate ACPI root system description table + * + * @ret rsdt ACPI root system description table, or UNULL + */ +userptr_t acpi_find_rsdt ( void ); + extern struct acpi_descriptor * acpi_describe ( struct interface *interface ); #define acpi_describe_TYPE( object_type ) \ typeof ( struct acpi_descriptor * ( object_type ) ) extern void acpi_fix_checksum ( struct acpi_header *acpi ); -extern userptr_t acpi_find_rsdt ( userptr_t ebda ); -extern userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, - unsigned int index ); -extern int acpi_sx ( userptr_t rsdt, uint32_t signature ); +extern userptr_t acpi_find ( uint32_t signature, unsigned int index ); +extern int acpi_sx ( uint32_t signature ); extern void acpi_add ( struct acpi_descriptor *desc ); extern void acpi_del ( struct acpi_descriptor *desc ); extern int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ); diff --git a/src/include/ipxe/null_acpi.h b/src/include/ipxe/null_acpi.h new file mode 100644 index 000000000..1e469e33d --- /dev/null +++ b/src/include/ipxe/null_acpi.h @@ -0,0 +1,23 @@ +#ifndef _IPXE_NULL_ACPI_H +#define _IPXE_NULL_ACPI_H + +/** @file + * + * Standard do-nothing ACPI interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef ACPI_NULL +#define ACPI_PREFIX_null +#else +#define ACPI_PREFIX_null __null_ +#endif + +static inline __always_inline userptr_t +ACPI_INLINE ( null, acpi_find_rsdt ) ( void ) { + return UNULL; +} + +#endif /* _IPXE_NULL_ACPI_H */ From 993fd2b45140d071d7837ff502df771e87f14127 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 23 May 2017 18:32:31 +0100 Subject: [PATCH 465/591] [efi] Provide access to ACPI tables Signed-off-by: Michael Brown --- src/config/defaults/efi.h | 2 +- src/include/ipxe/acpi.h | 1 + src/include/ipxe/efi/Guid/Acpi.h | 48 +++++++++++++++++++++++++++ src/include/ipxe/efi/efi_acpi.h | 18 ++++++++++ src/interface/efi/efi_acpi.c | 56 ++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/include/ipxe/efi/Guid/Acpi.h create mode 100644 src/include/ipxe/efi/efi_acpi.h create mode 100644 src/interface/efi/efi_acpi.c diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 5e1eed79a..74effa425 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -21,7 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ENTROPY_EFI #define TIME_EFI #define REBOOT_EFI -#define ACPI_NULL +#define ACPI_EFI #define DOWNLOAD_PROTO_FILE /* Local filesystem access */ diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index e7ca35da9..68131e73a 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -215,6 +215,7 @@ struct acpi_model { /* Include all architecture-independent ACPI API headers */ #include +#include /* Include all architecture-dependent ACPI API headers */ #include diff --git a/src/include/ipxe/efi/Guid/Acpi.h b/src/include/ipxe/efi/Guid/Acpi.h new file mode 100644 index 000000000..c4169c5ff --- /dev/null +++ b/src/include/ipxe/efi/Guid/Acpi.h @@ -0,0 +1,48 @@ +/** @file + GUIDs used for ACPI entries in the EFI system table + + These GUIDs point the ACPI tables as defined in the ACPI specifications. + ACPI 2.0 specification defines the ACPI 2.0 GUID. UEFI 2.0 defines the + ACPI 2.0 Table GUID and ACPI Table GUID. + + Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + GUIDs defined in UEFI 2.0 spec. + +**/ + +#ifndef __ACPI_GUID_H__ +#define __ACPI_GUID_H__ + +FILE_LICENCE ( BSD3 ); + +#define ACPI_TABLE_GUID \ + { \ + 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define EFI_ACPI_TABLE_GUID \ + { \ + 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \ + } + +#define ACPI_10_TABLE_GUID ACPI_TABLE_GUID + +// +// ACPI 2.0 or newer tables should use EFI_ACPI_TABLE_GUID. +// +#define EFI_ACPI_20_TABLE_GUID EFI_ACPI_TABLE_GUID + +extern EFI_GUID gEfiAcpiTableGuid; +extern EFI_GUID gEfiAcpi10TableGuid; +extern EFI_GUID gEfiAcpi20TableGuid; + +#endif diff --git a/src/include/ipxe/efi/efi_acpi.h b/src/include/ipxe/efi/efi_acpi.h new file mode 100644 index 000000000..01456f137 --- /dev/null +++ b/src/include/ipxe/efi/efi_acpi.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFI_ACPI_H +#define _IPXE_EFI_ACPI_H + +/** @file + * + * iPXE ACPI API for EFI + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef ACPI_EFI +#define ACPI_PREFIX_efi +#else +#define ACPI_PREFIX_efi __efi_ +#endif + +#endif /* _IPXE_EFI_ACPI_H */ diff --git a/src/interface/efi/efi_acpi.c b/src/interface/efi/efi_acpi.c new file mode 100644 index 000000000..a347eaf3a --- /dev/null +++ b/src/interface/efi/efi_acpi.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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 + * + * iPXE ACPI API for EFI + * + */ + +#include +#include +#include +#include + +/** ACPI configuration table */ +static EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *rsdp; +EFI_USE_TABLE ( ACPI_10_TABLE, &rsdp, 0 ); + +/** + * Locate ACPI root system description table + * + * @ret rsdt ACPI root system description table, or UNULL + */ +static userptr_t efi_find_rsdt ( void ) { + + /* Locate RSDT via ACPI configuration table, if available */ + if ( rsdp ) + return phys_to_user ( rsdp->RsdtAddress ); + + return UNULL; +} + +PROVIDE_ACPI ( efi, acpi_find_rsdt, efi_find_rsdt ); From 356f6c1b64d7a97746d1816cef8ca22bdd8d0b5d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 23 May 2017 15:44:22 +0100 Subject: [PATCH 466/591] [acpi] Expose ACPI tables via settings mechanism Allow values to be read from ACPI tables using the syntax ${acpi/..0..} where is the ACPI table signature as a 32-bit hexadecimal number (e.g. 0x41504093 for the 'APIC' signature on the MADT), is the index into the array of tables matching this signature, is the byte offset within the table, and is the field length in bytes. Numeric values are returned in reverse byte order, since ACPI numeric values are usually little-endian. For example: ${acpi/0x41504943.0.0.0.0} - entire MADT table in raw hex ${acpi/0x41504943.0.0.0x0a.6:string} - MADT table OEM ID ${acpi/0x41504943.0.0.0x24.4:uint32} - local APIC address Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/settings.h | 1 + src/core/acpi_settings.c | 161 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 4 files changed, 166 insertions(+) create mode 100644 src/core/acpi_settings.c diff --git a/src/config/config.c b/src/config/config.c index faf9aefa8..8adb1ed3f 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -337,6 +337,9 @@ REQUIRE_OBJECT ( memmap_settings ); #ifdef VRAM_SETTINGS REQUIRE_OBJECT ( vram_settings ); #endif +#ifdef ACPI_SETTINGS +REQUIRE_OBJECT ( acpi_settings ); +#endif /* * Drag in selected keyboard map diff --git a/src/config/settings.h b/src/config/settings.h index 01feaaa87..d9c86a384 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define MEMMAP_SETTINGS /* Memory map settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ //#define VRAM_SETTINGS /* Video RAM dump settings */ +//#define ACPI_SETTINGS /* ACPI settings */ #include #include NAMED_CONFIG(settings.h) diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c new file mode 100644 index 000000000..7ba2e979f --- /dev/null +++ b/src/core/acpi_settings.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 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 + * + * ACPI settings + * + */ + +#include +#include +#include +#include +#include + +/** ACPI settings scope */ +static const struct settings_scope acpi_settings_scope; + +/** + * Check applicability of ACPI setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int acpi_settings_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &acpi_settings_scope ); +} + +/** + * Fetch value of ACPI setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int acpi_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct acpi_header acpi; + uint32_t tag_high; + uint32_t tag_low; + uint32_t tag_signature; + unsigned int tag_index; + size_t tag_offset; + size_t tag_len; + userptr_t table; + size_t offset; + size_t max_len; + int delta; + unsigned int i; + + /* Parse settings tag */ + tag_high = ( setting->tag >> 32 ); + tag_low = setting->tag; + tag_signature = bswap_32 ( tag_high ); + tag_index = ( ( tag_low >> 24 ) & 0xff ); + tag_offset = ( ( tag_low >> 8 ) & 0xffff ); + tag_len = ( ( tag_low >> 0 ) & 0xff ); + DBGC ( settings, "ACPI %s.%d offset %#zx length %#zx\n", + acpi_name ( tag_signature ), tag_index, tag_offset, tag_len ); + + /* Locate ACPI table */ + table = acpi_find ( tag_signature, tag_index ); + if ( ! table ) + return -ENOENT; + + /* Read table header */ + copy_from_user ( &acpi, table, 0, sizeof ( acpi ) ); + + /* Calculate starting offset and maximum available length */ + max_len = le32_to_cpu ( acpi.length ); + if ( tag_offset > max_len ) + return -ENOENT; + offset = tag_offset; + max_len -= offset; + + /* Restrict to requested length, if specified */ + if ( tag_len && ( tag_len < max_len ) ) + max_len = tag_len; + + /* Invert endianness for numeric settings */ + if ( setting->type && setting->type->numerate ) { + offset += ( max_len - 1 ); + delta = -1; + } else { + delta = +1; + } + + /* Read data */ + for ( i = 0 ; ( ( i < max_len ) && ( i < len ) ) ; i++ ) { + copy_from_user ( data, table, offset, 1 ); + data++; + offset += delta; + } + + /* Set type if not already specified */ + if ( ! setting->type ) + setting->type = &setting_type_hexraw; + + return max_len; +} + +/** ACPI settings operations */ +static struct settings_operations acpi_settings_operations = { + .applies = acpi_settings_applies, + .fetch = acpi_settings_fetch, +}; + +/** ACPI settings */ +static struct settings acpi_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( acpi_settings.siblings ), + .children = LIST_HEAD_INIT ( acpi_settings.children ), + .op = &acpi_settings_operations, + .default_scope = &acpi_settings_scope, +}; + +/** Initialise ACPI settings */ +static void acpi_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &acpi_settings, NULL, + "acpi" ) ) != 0 ) { + DBG ( "ACPI could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** ACPI settings initialiser */ +struct init_fn acpi_settings_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = acpi_settings_init, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index cd12ebf3c..faa1e77f5 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -366,6 +366,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) #define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) #define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) +#define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) /** @} */ From 1fdf4dddbd6fa2329138e4374c46bbca1ad264c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Jun 2017 11:54:41 +0100 Subject: [PATCH 467/591] [syslog] Handle backspace characters Signed-off-by: Michael Brown --- src/core/lineconsole.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/lineconsole.c b/src/core/lineconsole.c index bb3bfafc9..0a72d1434 100644 --- a/src/core/lineconsole.c +++ b/src/core/lineconsole.c @@ -47,6 +47,13 @@ size_t line_putchar ( struct line_console *line, int character ) { if ( character < 0 ) return 0; + /* Handle backspace characters */ + if ( character == '\b' ) { + if ( line->index ) + line->index--; + return 0; + } + /* Ignore carriage return */ if ( character == '\r' ) return 0; From 84e25513b1ce6aeb4f0d6c9d20cd687c00591de8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Jun 2017 13:16:26 +0100 Subject: [PATCH 468/591] [hdprefix] Avoid attempts to read beyond the end of the disk When booting from a hard disk image (e.g. bin/ipxe.usb) within an emulator such as QEMU, the disk may not exist beyond the end of the image. Limit all reads to the length of the image to avoid spurious errors when loading the iPXE image. Signed-off-by: Michael Brown --- src/arch/x86/prefix/hdprefix.S | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/arch/x86/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S index 24f5d3850..28c8a532d 100644 --- a/src/arch/x86/prefix/hdprefix.S +++ b/src/arch/x86/prefix/hdprefix.S @@ -27,14 +27,18 @@ load_image: popw %es popal -1: /* Read to end of current track */ +1: /* Read to end of current track (or end of image) */ movb %cl, %al negb %al addb max_sector, %al incb %al andb $0x3f, %al movzbl %al, %eax - call *read_sectors + movl load_length, %ebx + cmpl %eax, %ebx + ja 2f + movl %ebx, %eax +2: call *read_sectors jc load_failed /* Update %es */ @@ -53,12 +57,12 @@ load_image: orb $0x01, %cl incb %dh cmpb max_head, %dh - jbe 2f + jbe 3f xorb %dh, %dh incb %ch - jnc 2f + jnc 3f addb $0xc0, %cl -2: +3: /* Loop until whole image is read */ subl %eax, load_length ja 1b From 63113f591fc8d36f3cd2c90c2b145480d9ab1926 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 May 2017 15:18:25 +0100 Subject: [PATCH 469/591] [usb] Allow for USB network devices with no interrupt endpoint Signed-off-by: Michael Brown --- src/drivers/usb/usbnet.c | 34 +++++++++++++++++++++------------- src/include/ipxe/usbnet.h | 14 +++++++++++++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/drivers/usb/usbnet.c b/src/drivers/usb/usbnet.c index d18d81772..0fac00b56 100644 --- a/src/drivers/usb/usbnet.c +++ b/src/drivers/usb/usbnet.c @@ -35,11 +35,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * USB network devices use a variety of packet formats and interface * descriptors, but tend to have several features in common: * - * - a single interrupt endpoint using the generic refill mechanism + * - a single bulk OUT endpoint * * - a single bulk IN endpoint using the generic refill mechanism * - * - a single bulk OUT endpoint + * - an optional interrupt endpoint using the generic refill mechanism * * - optional use of an alternate setting to enable the data interface * @@ -55,15 +55,17 @@ int usbnet_open ( struct usbnet_device *usbnet ) { struct usb_device *usb = usbnet->func->usb; int rc; - /* Open interrupt endpoint */ - if ( ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { + /* Open interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { DBGC ( usbnet, "USBNET %s could not open interrupt: %s\n", usbnet->func->name, strerror ( rc ) ); goto err_open_intr; } - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { + /* Refill interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { DBGC ( usbnet, "USBNET %s could not refill interrupt: %s\n", usbnet->func->name, strerror ( rc ) ); goto err_refill_intr; @@ -111,7 +113,8 @@ int usbnet_open ( struct usbnet_device *usbnet ) { usb_set_interface ( usb, usbnet->data, 0 ); err_set_interface: err_refill_intr: - usb_endpoint_close ( &usbnet->intr ); + if ( usbnet_has_intr ( usbnet ) ) + usb_endpoint_close ( &usbnet->intr ); err_open_intr: return rc; } @@ -134,8 +137,9 @@ void usbnet_close ( struct usbnet_device *usbnet ) { if ( usbnet->alternate ) usb_set_interface ( usb, usbnet->data, 0 ); - /* Close interrupt endpoint */ - usb_endpoint_close ( &usbnet->intr ); + /* Close interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) ) + usb_endpoint_close ( &usbnet->intr ); } /** @@ -151,9 +155,11 @@ int usbnet_refill ( struct usbnet_device *usbnet ) { if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) return rc; - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) + /* Refill interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { return rc; + } return 0; } @@ -272,9 +278,11 @@ int usbnet_describe ( struct usbnet_device *usbnet, struct usb_configuration_descriptor *config ) { int rc; - /* Describe communications interface */ - if ( ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) + /* Describe communications interface, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) { return rc; + } /* Describe data interface */ if ( ( rc = usbnet_data_describe ( usbnet, config ) ) != 0 ) diff --git a/src/include/ipxe/usbnet.h b/src/include/ipxe/usbnet.h index 33a8f3f58..a7276eba5 100644 --- a/src/include/ipxe/usbnet.h +++ b/src/include/ipxe/usbnet.h @@ -36,7 +36,7 @@ struct usbnet_device { * * @v usbnet USB network device * @v func USB function - * @v intr Interrupt endpoint operations + * @v intr Interrupt endpoint operations, or NULL * @v in Bulk IN endpoint operations * @v out Bulk OUT endpoint operations */ @@ -53,6 +53,18 @@ usbnet_init ( struct usbnet_device *usbnet, struct usb_function *func, usb_endpoint_init ( &usbnet->out, usb, out ); } +/** + * Check if USB network device has an interrupt endpoint + * + * @v usbnet USB network device + * @ret has_intr Device has an interrupt endpoint + */ +static inline __attribute__ (( always_inline )) int +usbnet_has_intr ( struct usbnet_device *usbnet ) { + + return ( usbnet->intr.driver != NULL ); +} + extern int usbnet_open ( struct usbnet_device *usbnet ); extern void usbnet_close ( struct usbnet_device *usbnet ); extern int usbnet_refill ( struct usbnet_device *usbnet ); From 62573b99ccbcbbcaa39cfbd451e6162a7ec1cb1e Mon Sep 17 00:00:00 2001 From: Mika Tiainen Date: Tue, 30 May 2017 20:23:17 +0300 Subject: [PATCH 470/591] [intel] Add INTEL_NO_PHY_RST for I219-V Fix booting on HP EliteBook 820 G3. Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 1bca87bca..d09978ad9 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1124,7 +1124,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", 0 ), - PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", 0 ), + PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), From 7c395b0e21806b946fe944a27fc273407f357ea1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 14 Jun 2017 12:33:16 +0100 Subject: [PATCH 471/591] [build] Use -no-pie on newer versions of gcc Some distributions patch gcc to generate position independent executables by default. We currently include a workaround to check for this and to add -fno-PIE -nopie to CFLAGS if required. Newer patched versions of gcc require -fno-PIE -no-pie instead. Check for both variants. Reported-by: Nathan Rennie-Waldock Originally-fixed-by: Markos Chandras Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index fe3adc9ce..b7c2792d9 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -74,10 +74,15 @@ CFLAGS += -Ui386 # recognise an option that starts with "no", so we have to test for # output on stderr instead of checking the exit status. # +# Current versions of gcc require -no-pie; older versions require +# -nopie. We therefore test for both. +# ifeq ($(CCTYPE),gcc) -PIE_TEST = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ] -PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie') -WORKAROUND_CFLAGS += $(PIE_FLAGS) +PIE_TEST = [ -z "`$(CC) -fno-PIE -no-pie -x c -c /dev/null -o /dev/null 2>&1`" ] +PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -no-pie') +PIE_TEST2 = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ] +PIE_FLAGS2 := $(shell $(PIE_TEST2) && $(ECHO) '-fno-PIE -nopie') +WORKAROUND_CFLAGS += $(PIE_FLAGS) $(PIE_FLAGS2) endif # i386-specific directories containing source files From c8cb867d655227d779ce8ce9e392e853997b01dd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 14 Jun 2017 17:40:05 +0100 Subject: [PATCH 472/591] [ecm] Display invalid MAC address strings in debug messages Signed-off-by: Michael Brown --- src/drivers/net/ecm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index f2d9161c1..847a45b85 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -101,13 +101,18 @@ int ecm_fetch_mac ( struct usb_device *usb, } /* Sanity check */ - if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) + if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) { + DBGC ( usb, "USB %s has invalid ECM MAC \"%s\"\n", + usb->name, buf ); return -EINVAL; + } /* Decode MAC address */ len = base16_decode ( buf, hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; + DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n", + usb->name, buf, strerror ( rc ) ); return rc; } From a6a5825f8d658834610de31b7b9be2e7d0a8804e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Jun 2017 14:50:20 +0100 Subject: [PATCH 473/591] [cpuid] Allow input %ecx value to be specified For some CPUID leaves (e.g. %eax=0x00000004), the result depends on the input value of %ecx. Allow this subfunction number to be specified as a parameter to the cpuid() wrapper. The subfunction number is exposed via the ${cpuid/...} settings mechanism using the syntax ${cpuid/.0x40..} e.g. ${cpuid/0.0x40.0.0x0000000b} ${cpuid/1.0x40.0.0x0000000b} to retrieve the CPU topology information. Signed-off-by: Michael Brown --- src/arch/x86/core/cpuid.c | 6 +-- src/arch/x86/core/cpuid_settings.c | 69 ++++++++++++++++------------ src/arch/x86/core/rdtsc_timer.c | 2 +- src/arch/x86/drivers/hyperv/hyperv.c | 10 ++-- src/arch/x86/drivers/xen/hvm.c | 6 +-- src/arch/x86/include/ipxe/cpuid.h | 9 ++-- 6 files changed, 57 insertions(+), 45 deletions(-) diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c index 864c1035d..1a7c93e83 100644 --- a/src/arch/x86/core/cpuid.c +++ b/src/arch/x86/core/cpuid.c @@ -84,7 +84,7 @@ int cpuid_supported ( uint32_t function ) { return rc; /* Find highest supported function number within this family */ - cpuid ( ( function & CPUID_EXTENDED ), &max_function, &discard_b, + cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b, &discard_c, &discard_d ); /* Fail if maximum function number is meaningless (e.g. if we @@ -125,7 +125,7 @@ static void x86_intel_features ( struct x86_features *features ) { } /* Get features */ - cpuid ( CPUID_FEATURES, &discard_a, &discard_b, + cpuid ( CPUID_FEATURES, 0, &discard_a, &discard_b, &features->intel.ecx, &features->intel.edx ); DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n", features->intel.ecx, features->intel.edx ); @@ -149,7 +149,7 @@ static void x86_amd_features ( struct x86_features *features ) { } /* Get features */ - cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b, + cpuid ( CPUID_AMD_FEATURES, 0, &discard_a, &discard_b, &features->amd.ecx, &features->amd.edx ); DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n", features->amd.ecx, features->amd.edx ); diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c index 306dbefb8..0b67ee91d 100644 --- a/src/arch/x86/core/cpuid_settings.c +++ b/src/arch/x86/core/cpuid_settings.c @@ -37,10 +37,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * CPUID settings are numerically encoded as: * * Bit 31 Extended function - * Bits 30-28 Unused - * Bits 27-24 Number of consecutive functions to call, minus one + * Bits 30-24 (bit 22 = 1) Subfunction number + * (bit 22 = 0) Number of consecutive functions to call, minus one * Bit 23 Return result as little-endian (used for strings) - * Bits 22-18 Unused + * Bit 22 Interpret bits 30-24 as a subfunction number + * Bits 21-18 Unused * Bits 17-16 Number of registers in register array, minus one * Bits 15-8 Array of register indices. First entry in array is in * bits 9-8. Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx. @@ -50,6 +51,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * extracting a single register from a single function to be encoded * using "cpuid/.", e.g. "cpuid/2.0x80000001" to * retrieve the value of %ecx from calling CPUID with %eax=0x80000001. + * + * A subfunction (i.e. an input value for %ecx) may be specified using + * "cpuid/.0x40..". This slightly + * cumbersome syntax is required in order to maintain backwards + * compatibility with older scripts. */ /** CPUID setting tag register indices */ @@ -60,12 +66,18 @@ enum cpuid_registers { CPUID_EDX = 3, }; +/** CPUID setting tag flags */ +enum cpuid_flags { + CPUID_LITTLE_ENDIAN = 0x00800000UL, + CPUID_USE_SUBFUNCTION = 0x00400000UL, +}; + /** * Construct CPUID setting tag * * @v function Starting function number - * @v num_functions Number of consecutive functions - * @v little_endian Return result as little-endian + * @v subfunction Subfunction, or number of consecutive functions minus 1 + * @v flags Flags * @v num_registers Number of registers in register array * @v register1 First register in register array (or zero, if empty) * @v register2 Second register in register array (or zero, if empty) @@ -73,21 +85,13 @@ enum cpuid_registers { * @v register4 Fourth register in register array (or zero, if empty) * @ret tag Setting tag */ -#define CPUID_TAG( function, num_functions, little_endian, num_registers, \ - register1, register2, register3, register4 ) \ - ( (function) | ( ( (num_functions) - 1 ) << 24 ) | \ - ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) | \ - ( (register1) << 8 ) | ( (register2) << 10 ) | \ +#define CPUID_TAG( function, subfunction, flags, num_registers, \ + register1, register2, register3, register4 ) \ + ( (function) | ( (subfunction) << 24 ) | (flags) | \ + ( ( (num_registers) - 1 ) << 16 ) | \ + ( (register1) << 8 ) | ( (register2) << 10 ) | \ ( (register3) << 12 ) | ( (register4) << 14 ) ) -/** - * Extract endianness from CPUID setting tag - * - * @v tag Setting tag - * @ret little_endian Result should be returned as little-endian - */ -#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL ) - /** * Extract starting function number from CPUID setting tag * @@ -97,12 +101,12 @@ enum cpuid_registers { #define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL ) /** - * Extract number of consecutive functions from CPUID setting tag + * Extract subfunction number from CPUID setting tag * * @v tag Setting tag - * @ret num_functions Number of consecutive functions + * @ret subfunction Subfunction number */ -#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 ) +#define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f ) /** * Extract register array from CPUID setting tag @@ -149,6 +153,7 @@ static int cpuid_settings_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { uint32_t function; + uint32_t subfunction; uint32_t num_functions; uint32_t registers; uint32_t num_registers; @@ -160,7 +165,13 @@ static int cpuid_settings_fetch ( struct settings *settings, /* Call each function in turn */ function = CPUID_FUNCTION ( setting->tag ); - num_functions = CPUID_NUM_FUNCTIONS ( setting->tag ); + subfunction = CPUID_SUBFUNCTION ( setting->tag ); + if ( setting->tag & CPUID_USE_SUBFUNCTION ) { + num_functions = 1; + } else { + num_functions = ( subfunction + 1 ); + subfunction = 0; + } for ( ; num_functions-- ; function++ ) { /* Fail if this function is not supported */ @@ -171,17 +182,17 @@ static int cpuid_settings_fetch ( struct settings *settings, } /* Issue CPUID */ - cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX], - &buf[CPUID_ECX], &buf[CPUID_EDX] ); - DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n", - function, buf[0], buf[1], buf[2], buf[3] ); + cpuid ( function, subfunction, &buf[CPUID_EAX], + &buf[CPUID_EBX], &buf[CPUID_ECX], &buf[CPUID_EDX] ); + DBGC ( settings, "CPUID %#08x:%x => %#08x:%#08x:%#08x:%#08x\n", + function, subfunction, buf[0], buf[1], buf[2], buf[3] ); /* Copy results to buffer */ registers = CPUID_REGISTERS ( setting->tag ); num_registers = CPUID_NUM_REGISTERS ( setting->tag ); for ( ; num_registers-- ; registers >>= 2 ) { output = buf[ registers & 0x3 ]; - if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) ) + if ( ! ( setting->tag & CPUID_LITTLE_ENDIAN ) ) output = cpu_to_be32 ( output ); frag_len = sizeof ( output ); if ( frag_len > len ) @@ -237,7 +248,7 @@ const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA, cpuvendor ) = { .name = "cpuvendor", .description = "CPU vendor", - .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3, + .tag = CPUID_TAG ( CPUID_VENDOR_ID, 0, CPUID_LITTLE_ENDIAN, 3, CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ), .type = &setting_type_string, .scope = &cpuid_settings_scope, @@ -248,7 +259,7 @@ const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA, cpumodel ) = { .name = "cpumodel", .description = "CPU model", - .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4, + .tag = CPUID_TAG ( CPUID_MODEL, 2, CPUID_LITTLE_ENDIAN, 4, CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ), .type = &setting_type_string, .scope = &cpuid_settings_scope, diff --git a/src/arch/x86/core/rdtsc_timer.c b/src/arch/x86/core/rdtsc_timer.c index ed5151503..bee5f1ca4 100644 --- a/src/arch/x86/core/rdtsc_timer.c +++ b/src/arch/x86/core/rdtsc_timer.c @@ -132,7 +132,7 @@ static int rdtsc_probe ( void ) { strerror ( rc ) ); return rc; } - cpuid ( CPUID_APM, &discard_a, &discard_b, &discard_c, &apm ); + cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm ); if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) { DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n", apm ); diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index 98c2b30c0..4e6876878 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -170,7 +170,7 @@ static int hv_check_hv ( void ) { } /* Check that hypervisor is Hyper-V */ - cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx, + cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx, &discard_ecx, &discard_edx ); if ( interface_id != HV_INTERFACE_ID ) { DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface " @@ -194,7 +194,7 @@ static int hv_check_features ( struct hv_hypervisor *hv ) { uint32_t discard_edx; /* Check that required features and privileges are available */ - cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx, + cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx, &discard_edx ); if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) { DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n", @@ -253,10 +253,10 @@ static void hv_map_hypercall ( struct hv_hypervisor *hv ) { wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id ); /* Get hypervisor system identity (for debugging) */ - cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx, + cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx, &vendor_id.ecx, &vendor_id.edx ); vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0'; - cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx, + cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx, &discard_edx ); DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv, vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build ); @@ -735,7 +735,7 @@ static int hv_timer_probe ( void ) { return rc; /* Check for available reference counter */ - cpuid ( HV_CPUID_FEATURES, &available, &discard_ebx, &discard_ecx, + cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx, &discard_edx ); if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) { DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" ); diff --git a/src/arch/x86/drivers/xen/hvm.c b/src/arch/x86/drivers/xen/hvm.c index 7ac32d54c..57196f555 100644 --- a/src/arch/x86/drivers/xen/hvm.c +++ b/src/arch/x86/drivers/xen/hvm.c @@ -66,12 +66,12 @@ static int hvm_cpuid_base ( struct hvm_device *hvm ) { /* Scan for magic signature */ for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ; base += HVM_CPUID_STEP ) { - cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx, + cpuid ( base, 0, &discard_eax, &signature.ebx, &signature.ecx, &signature.edx ); if ( memcmp ( &signature, HVM_CPUID_MAGIC, sizeof ( signature ) ) == 0 ) { hvm->cpuid_base = base; - cpuid ( ( base + HVM_CPUID_VERSION ), &version, + cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version, &discard_ebx, &discard_ecx, &discard_edx ); DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n", base, ( version >> 16 ), ( version & 0xffff ) ); @@ -101,7 +101,7 @@ static int hvm_map_hypercall ( struct hvm_device *hvm ) { int rc; /* Get number of hypercall pages and MSR to use */ - cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr, + cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), 0, &pages, &msr, &discard_ecx, &discard_edx ); /* Allocate pages */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index a9df9f0de..0ae572da4 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -66,19 +66,20 @@ struct x86_features { /** * Issue CPUID instruction * - * @v function CPUID function + * @v function CPUID function (input via %eax) + * @v subfunction CPUID subfunction (input via %ecx) * @v eax Output via %eax * @v ebx Output via %ebx * @v ecx Output via %ecx * @v edx Output via %edx */ static inline __attribute__ (( always_inline )) void -cpuid ( uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, - uint32_t *edx ) { +cpuid ( uint32_t function, uint32_t subfunction, uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx ) { __asm__ ( "cpuid" : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) - : "0" ( function ) ); + : "0" ( function ), "2" ( subfunction ) ); } extern int cpuid_supported ( uint32_t function ); From 5b608bbfe00c352f425ebaebad7f1ba2dd41334c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Jun 2017 12:09:52 +0100 Subject: [PATCH 474/591] [crypto] Expose RSA_CTX_SIZE constant Signed-off-by: Michael Brown --- src/crypto/rsa.c | 2 +- src/include/ipxe/rsa.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/crypto/rsa.c b/src/crypto/rsa.c index 36109280d..7ac0bca59 100644 --- a/src/crypto/rsa.c +++ b/src/crypto/rsa.c @@ -625,7 +625,7 @@ static int rsa_match ( const void *private_key, size_t private_key_len, /** RSA public-key algorithm */ struct pubkey_algorithm rsa_algorithm = { .name = "rsa", - .ctxsize = sizeof ( struct rsa_context ), + .ctxsize = RSA_CTX_SIZE, .init = rsa_init, .max_len = rsa_max_len, .encrypt = rsa_encrypt, diff --git a/src/include/ipxe/rsa.h b/src/include/ipxe/rsa.h index d947eec73..a1b5e0c03 100644 --- a/src/include/ipxe/rsa.h +++ b/src/include/ipxe/rsa.h @@ -77,6 +77,9 @@ struct rsa_context { void *tmp; }; +/** RSA context size */ +#define RSA_CTX_SIZE sizeof ( struct rsa_context ) + extern struct pubkey_algorithm rsa_algorithm; #endif /* _IPXE_RSA_H */ From e5bfa107bad45ea9e0304a74f07cc0c7fc9860ae Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Jun 2017 12:11:40 +0100 Subject: [PATCH 475/591] [crypto] Expose asn1_grow() Signed-off-by: Michael Brown --- src/crypto/asn1.c | 2 +- src/include/ipxe/asn1.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index ff56e1f3b..549ee4d86 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -739,7 +739,7 @@ static size_t asn1_header ( struct asn1_builder_header *header, * @v extra Extra space to prepend * @ret rc Return status code */ -static int asn1_grow ( struct asn1_builder *builder, size_t extra ) { +int asn1_grow ( struct asn1_builder *builder, size_t extra ) { size_t new_len; void *new; diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index b0a82c00c..5a6c0d9c3 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -365,6 +365,7 @@ extern int asn1_signature_algorithm ( const struct asn1_cursor *cursor, struct asn1_algorithm **algorithm ); extern int asn1_generalized_time ( const struct asn1_cursor *cursor, time_t *time ); +extern int asn1_grow ( struct asn1_builder *builder, size_t extra ); extern int asn1_prepend_raw ( struct asn1_builder *builder, const void *data, size_t len ); extern int asn1_prepend ( struct asn1_builder *builder, unsigned int type, From b506528c1e4ce70dc24da87c0f7cd16ddac60701 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Jun 2017 12:12:11 +0100 Subject: [PATCH 476/591] [crypto] Provide asn1_built() to construct a cursor from a builder Our ASN.1 parsing code uses a struct asn1_cursor, while the object construction code uses a struct asn1_builder. These structures are identical apart from the const modifier applied to the data pointer in struct asn1_cursor. Provide asn1_built() to safely typecast a struct asn1_builder to a struct asn1_cursor, allowing constructed objects to be passed to functions expecting a struct asn1_cursor. Signed-off-by: Michael Brown --- src/include/ipxe/asn1.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 5a6c0d9c3..847d845bb 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -9,7 +9,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include +#include #include #include @@ -337,6 +339,28 @@ asn1_type ( const struct asn1_cursor *cursor ) { return ( ( cursor->len >= sizeof ( *type ) ) ? *type : ASN1_END ); } +/** + * Get cursor for built object + * + * @v builder ASN.1 object builder + * @ret cursor ASN.1 object cursor + */ +static inline __attribute__ (( always_inline )) struct asn1_cursor * +asn1_built ( struct asn1_builder *builder ) { + union { + struct asn1_builder builder; + struct asn1_cursor cursor; + } *u = container_of ( builder, typeof ( *u ), builder ); + + /* Sanity check */ + linker_assert ( ( ( const void * ) &u->builder.data ) == + &u->cursor.data, asn1_builder_cursor_data_mismatch ); + linker_assert ( &u->builder.len == &u->cursor.len, + asn1_builder_cursor_len_mismatch ); + + return &u->cursor; +} + extern int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ); extern int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ); From 14e3b4b29a96eda6230b1c2dac6cc5e793235d5a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 29 May 2017 17:29:20 +0100 Subject: [PATCH 477/591] [crypto] Expose pem_asn1() for use with non-image data Signed-off-by: Michael Brown --- src/image/pem.c | 115 ++++++++++++++++++++++++++--------------- src/include/ipxe/pem.h | 8 ++- 2 files changed, 81 insertions(+), 42 deletions(-) diff --git a/src/image/pem.c b/src/image/pem.c index 3f5d1f302..2dcc36442 100644 --- a/src/image/pem.c +++ b/src/image/pem.c @@ -27,10 +27,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include #include +#include /** @file * @@ -41,95 +41,98 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Locate next line * - * @v image PEM image + * @v data PEM data + * @v len Length of PEM data * @v offset Starting offset * @ret next Offset to next line */ -static size_t pem_next ( struct image *image, size_t offset ) { +static size_t pem_next ( userptr_t data, size_t len, size_t offset ) { off_t eol; /* Find and skip next newline character, if any */ - eol = memchr_user ( image->data, offset, '\n', ( image->len - offset )); + eol = memchr_user ( data, offset, '\n', ( len - offset ) ); if ( eol < 0 ) - return image->len; + return len; return ( eol + 1 ); } /** * Locate boundary marker line * - * @v image PEM image + * @v data PEM data + * @v len Length of PEM data * @v offset Starting offset * @v marker Boundary marker * @ret offset Offset to boundary marker line, or negative error */ -static int pem_marker ( struct image *image, size_t offset, +static int pem_marker ( userptr_t data, size_t len, size_t offset, const char *marker ) { char buf[ strlen ( marker ) ]; /* Sanity check */ - assert ( offset <= image->len ); + assert ( offset <= len ); /* Scan for marker at start of line */ - while ( offset < image->len ) { + while ( offset < len ) { /* Check for marker */ - if ( ( image->len - offset ) < sizeof ( buf ) ) + if ( ( len - offset ) < sizeof ( buf ) ) break; - copy_from_user ( buf, image->data, offset, sizeof ( buf ) ); + copy_from_user ( buf, data, offset, sizeof ( buf ) ); if ( memcmp ( buf, marker, sizeof ( buf ) ) == 0 ) return offset; /* Move to next line */ - offset = pem_next ( image, offset ); - assert ( offset <= image->len ); + offset = pem_next ( data, len, offset ); + assert ( offset <= len ); } return -ENOENT; } /** - * Extract ASN.1 object from image + * Extract ASN.1 object from PEM data * - * @v image PEM image - * @v offset Offset within image + * @v data PEM data + * @v len Length of PEM data + * @v offset Offset within data * @v cursor ASN.1 cursor to fill in - * @ret next Offset to next image, or negative error + * @ret next Offset to next object, or negative error * * The caller is responsible for eventually calling free() on the * allocated ASN.1 cursor. */ -static int pem_asn1 ( struct image *image, size_t offset, - struct asn1_cursor **cursor ) { +int pem_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ) { size_t encoded_len; size_t decoded_max_len; char *encoded; void *decoded; + int decoded_len; int begin; int end; - int len; int rc; /* Locate and skip BEGIN marker */ - begin = pem_marker ( image, offset, PEM_BEGIN ); + begin = pem_marker ( data, len, offset, PEM_BEGIN ); if ( begin < 0 ) { rc = begin; - DBGC ( image, "PEM %s [%#zx,%#zx) missing BEGIN marker: %s\n", - image->name, offset, image->len, strerror ( rc ) ); + DBGC ( data, "PEM [%#zx,%#zx) missing BEGIN marker: %s\n", + offset, len, strerror ( rc ) ); goto err_begin; } - begin = pem_next ( image, begin ); + begin = pem_next ( data, len, begin ); /* Locate and skip END marker */ - end = pem_marker ( image, begin, PEM_END ); + end = pem_marker ( data, len, begin, PEM_END ); if ( end < 0 ) { rc = end; - DBGC ( image, "PEM %s [%#zx,%#zx) missing END marker: %s\n", - image->name, offset, image->len, strerror ( rc ) ); + DBGC ( data, "PEM [%#zx,%#zx) missing END marker: %s\n", + offset, len, strerror ( rc ) ); goto err_end; } encoded_len = ( end - begin ); - end = pem_next ( image, end ); + end = pem_next ( data, len, end ); /* Extract Base64-encoded data */ encoded = malloc ( encoded_len + 1 /* NUL */ ); @@ -137,7 +140,7 @@ static int pem_asn1 ( struct image *image, size_t offset, rc = -ENOMEM; goto err_alloc_encoded; } - copy_from_user ( encoded, image->data, begin, encoded_len ); + copy_from_user ( encoded, data, begin, encoded_len ); encoded[encoded_len] = '\0'; /* Allocate cursor and data buffer */ @@ -150,15 +153,14 @@ static int pem_asn1 ( struct image *image, size_t offset, decoded = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); /* Decode Base64-encoded data */ - len = base64_decode ( encoded, decoded, decoded_max_len ); - if ( len < 0 ) { - rc = len; - DBGC ( image, "PEM %s could not decode: %s\n", - image->name, strerror ( rc ) ); + decoded_len = base64_decode ( encoded, decoded, decoded_max_len ); + if ( decoded_len < 0 ) { + rc = decoded_len; + DBGC ( data, "PEM could not decode: %s\n", strerror ( rc ) ); goto err_decode; } (*cursor)->data = decoded; - (*cursor)->len = len; + (*cursor)->len = decoded_len; assert ( (*cursor)->len <= decoded_max_len ); /* Free Base64-encoded data */ @@ -166,8 +168,8 @@ static int pem_asn1 ( struct image *image, size_t offset, /* Update offset and skip any unencapsulated trailer */ offset = end; - if ( pem_marker ( image, offset, PEM_BEGIN ) < 0 ) - offset = image->len; + if ( pem_marker ( data, len, offset, PEM_BEGIN ) < 0 ) + offset = len; return offset; @@ -188,11 +190,14 @@ static int pem_asn1 ( struct image *image, size_t offset, * @v image PEM image * @ret rc Return status code */ -static int pem_probe ( struct image *image ) { +static int pem_image_probe ( struct image *image ) { + int offset; int rc; /* Check that image contains a BEGIN marker */ - if ( ( rc = pem_marker ( image, 0, PEM_BEGIN ) ) < 0 ) { + if ( ( offset = pem_marker ( image->data, image->len, 0, + PEM_BEGIN ) ) < 0 ) { + rc = offset; DBGC ( image, "PEM %s has no BEGIN marker: %s\n", image->name, strerror ( rc ) ); return rc; @@ -201,9 +206,37 @@ static int pem_probe ( struct image *image ) { return 0; } +/** + * Extract ASN.1 object from image + * + * @v image PEM image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int pem_image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Extract ASN.1 object */ + if ( ( next = pem_asn1 ( image->data, image->len, offset, + cursor ) ) < 0 ) { + rc = next; + DBGC ( image, "PEM %s could not extract ASN.1: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} + /** PEM image type */ struct image_type pem_image_type __image_type ( PROBE_NORMAL ) = { .name = "PEM", - .probe = pem_probe, - .asn1 = pem_asn1, + .probe = pem_image_probe, + .asn1 = pem_image_asn1, }; diff --git a/src/include/ipxe/pem.h b/src/include/ipxe/pem.h index 1276f94ad..d88ec5b6f 100644 --- a/src/include/ipxe/pem.h +++ b/src/include/ipxe/pem.h @@ -3,12 +3,15 @@ /** @file * - * PEM image format + * PEM-encoded ASN.1 data * */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include +#include +#include #include /** Pre-encapsulation boundary marker */ @@ -17,6 +20,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Post-encapsulation boundary marker */ #define PEM_END "-----END" +extern int pem_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ); + extern struct image_type pem_image_type __image_type ( PROBE_NORMAL ); #endif /* _IPXE_PEM_H */ From 1e5c5a216351ffc18004e3e190f09fc04a110b52 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Jun 2017 12:10:14 +0100 Subject: [PATCH 478/591] [exanic] Add driver for Exablaze ExaNIC cards Signed-off-by: Michael Brown --- src/drivers/net/exanic.c | 911 +++++++++++++++++++++++++++++++++++++ src/drivers/net/exanic.h | 257 +++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 1169 insertions(+) create mode 100644 src/drivers/net/exanic.c create mode 100644 src/drivers/net/exanic.h diff --git a/src/drivers/net/exanic.c b/src/drivers/net/exanic.c new file mode 100644 index 000000000..62296927a --- /dev/null +++ b/src/drivers/net/exanic.c @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2017 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 +#include +#include +#include "exanic.h" + +/** @file + * + * Exablaze ExaNIC driver + * + */ + +/* Disambiguate the various error causes */ +#define EIO_ABORTED __einfo_error ( EINFO_EIO_ABORTED ) +#define EINFO_EIO_ABORTED \ + __einfo_uniqify ( EINFO_EIO, 0x01, "Frame aborted" ) +#define EIO_CORRUPT __einfo_error ( EINFO_EIO_CORRUPT ) +#define EINFO_EIO_CORRUPT \ + __einfo_uniqify ( EINFO_EIO, 0x02, "CRC incorrect" ) +#define EIO_HWOVFL __einfo_error ( EINFO_EIO_HWOVFL ) +#define EINFO_EIO_HWOVFL \ + __einfo_uniqify ( EINFO_EIO, 0x03, "Hardware overflow" ) +#define EIO_STATUS( status ) \ + EUNIQ ( EINFO_EIO, ( (status) & EXANIC_STATUS_ERROR_MASK ), \ + EIO_ABORTED, EIO_CORRUPT, EIO_HWOVFL ) + +/** + * Write DMA base address register + * + * @v addr DMA base address + * @v reg Register + */ +static void exanic_write_base ( physaddr_t addr, void *reg ) { + uint32_t lo; + uint32_t hi; + + /* Write high and low registers, setting flags as appropriate */ + lo = addr; + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + /* 64-bit build; may be a 32-bit or 64-bit address */ + hi = ( ( ( uint64_t ) addr ) >> 32 ); + if ( ! hi ) + lo |= EXANIC_DMA_32_BIT; + } else { + /* 32-bit build; always a 32-bit address */ + hi = 0; + lo |= EXANIC_DMA_32_BIT; + } + writel ( hi, ( reg + 0 ) ); + writel ( lo, ( reg + 4 ) ); +} + +/** + * Clear DMA base address register + * + * @v reg Register + */ +static inline void exanic_clear_base ( void *reg ) { + + /* Clear both high and low registers */ + writel ( 0, ( reg + 0 ) ); + writel ( 0, ( reg + 4 ) ); +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v exanic ExaNIC device + */ +static void exanic_reset ( struct exanic *exanic ) { + void *port_regs; + unsigned int i; + + /* Disable all possible ports */ + for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) { + port_regs = ( exanic->regs + EXANIC_PORT_REGS ( i ) ); + writel ( 0, ( port_regs + EXANIC_PORT_ENABLE ) ); + writel ( 0, ( port_regs + EXANIC_PORT_IRQ ) ); + exanic_clear_base ( port_regs + EXANIC_PORT_RX_BASE ); + } + + /* Disable transmit feedback */ + exanic_clear_base ( exanic->regs + EXANIC_TXF_BASE ); +} + +/****************************************************************************** + * + * MAC address + * + ****************************************************************************** + */ + +/** + * Read I2C line status + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @ret zero Input is a logic 0 + * @ret non-zero Input is a logic 1 + */ +static int exanic_i2c_read_bit ( struct bit_basher *basher, + unsigned int bit_id ) { + struct exanic *exanic = + container_of ( basher, struct exanic, basher.basher ); + unsigned int shift; + uint32_t i2c; + + /* Identify bit */ + assert ( bit_id == I2C_BIT_SDA ); + shift = exanic->i2cfg.getsda; + + /* Read I2C register */ + DBG_DISABLE ( DBGLVL_IO ); + i2c = readl ( exanic->regs + EXANIC_I2C ); + DBG_ENABLE ( DBGLVL_IO ); + return ( ( i2c >> shift ) & 1 ); +} + +/** + * Write I2C line status + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + */ +static void exanic_i2c_write_bit ( struct bit_basher *basher, + unsigned int bit_id, unsigned long data ) { + struct exanic *exanic = + container_of ( basher, struct exanic, basher.basher ); + unsigned int shift; + uint32_t mask; + uint32_t i2c; + + /* Identify shift */ + assert ( ( bit_id == I2C_BIT_SCL ) || ( bit_id == I2C_BIT_SDA ) ); + shift = ( ( bit_id == I2C_BIT_SCL ) ? + exanic->i2cfg.setscl : exanic->i2cfg.setsda ); + mask = ( 1UL << shift ); + + /* Modify I2C register */ + DBG_DISABLE ( DBGLVL_IO ); + i2c = readl ( exanic->regs + EXANIC_I2C ); + i2c &= ~mask; + if ( ! data ) + i2c |= mask; + writel ( i2c, ( exanic->regs + EXANIC_I2C ) ); + DBG_ENABLE ( DBGLVL_IO ); +} + +/** I2C bit-bashing interface operations */ +static struct bit_basher_operations exanic_i2c_basher_ops = { + .read = exanic_i2c_read_bit, + .write = exanic_i2c_write_bit, +}; + +/** Possible I2C bus configurations */ +static struct exanic_i2c_config exanic_i2cfgs[] = { + /* X2/X10 */ + { .setscl = 7, .setsda = 4, .getsda = 12 }, + /* X4 */ + { .setscl = 7, .setsda = 5, .getsda = 13 }, +}; + +/** + * Initialise EEPROM + * + * @v exanic ExaNIC device + * @v i2cfg I2C bus configuration + * @ret rc Return status code + */ +static int exanic_try_init_eeprom ( struct exanic *exanic, + struct exanic_i2c_config *i2cfg ) { + int rc; + + /* Configure I2C bus */ + memcpy ( &exanic->i2cfg, i2cfg, sizeof ( exanic->i2cfg ) ); + + /* Initialise I2C bus */ + if ( ( rc = init_i2c_bit_basher ( &exanic->basher, + &exanic_i2c_basher_ops ) ) != 0 ) { + DBGC2 ( exanic, "EXANIC %p found no I2C bus via %d/%d/%d\n", + exanic, exanic->i2cfg.setscl, + exanic->i2cfg.setsda, exanic->i2cfg.getsda ); + return rc; + } + + /* Check for EEPROM presence */ + init_i2c_eeprom ( &exanic->eeprom, EXANIC_EEPROM_ADDRESS ); + if ( ( rc = i2c_check_presence ( &exanic->basher.i2c, + &exanic->eeprom ) ) != 0 ) { + DBGC2 ( exanic, "EXANIC %p found no EEPROM via %d/%d/%d\n", + exanic, exanic->i2cfg.setscl, + exanic->i2cfg.setsda, exanic->i2cfg.getsda ); + return rc; + } + + DBGC ( exanic, "EXANIC %p found EEPROM via %d/%d/%d\n", + exanic, exanic->i2cfg.setscl, + exanic->i2cfg.setsda, exanic->i2cfg.getsda ); + return 0; +} + +/** + * Initialise EEPROM + * + * @v exanic ExaNIC device + * @ret rc Return status code + */ +static int exanic_init_eeprom ( struct exanic *exanic ) { + struct exanic_i2c_config *i2cfg; + unsigned int i; + int rc; + + /* Try all possible bus configurations */ + for ( i = 0 ; i < ( sizeof ( exanic_i2cfgs ) / + sizeof ( exanic_i2cfgs[0] ) ) ; i++ ) { + i2cfg = &exanic_i2cfgs[i]; + if ( ( rc = exanic_try_init_eeprom ( exanic, i2cfg ) ) == 0 ) + return 0; + } + + DBGC ( exanic, "EXANIC %p found no EEPROM\n", exanic ); + return -ENODEV; +} + +/** + * Fetch base MAC address + * + * @v exanic ExaNIC device + * @ret rc Return status code + */ +static int exanic_fetch_mac ( struct exanic *exanic ) { + struct i2c_interface *i2c = &exanic->basher.i2c; + int rc; + + /* Initialise EEPROM */ + if ( ( rc = exanic_init_eeprom ( exanic ) ) != 0 ) + return rc; + + /* Fetch base MAC address */ + if ( ( rc = i2c->read ( i2c, &exanic->eeprom, 0, exanic->mac, + sizeof ( exanic->mac ) ) ) != 0 ) { + DBGC ( exanic, "EXANIC %p could not read MAC address: %s\n", + exanic, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void exanic_check_link ( struct net_device *netdev ) { + struct exanic_port *port = netdev->priv; + uint32_t status; + uint32_t speed; + + /* Report port status changes */ + status = readl ( port->regs + EXANIC_PORT_STATUS ); + speed = readl ( port->regs + EXANIC_PORT_SPEED ); + if ( status != port->status ) { + DBGC ( port, "EXANIC %s port status %#08x speed %dMbps\n", + netdev->name, status, speed ); + if ( status & EXANIC_PORT_STATUS_LINK ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + port->status = status; + } +} + +/** + * Check link state periodically + * + * @v retry Link state check timer + * @v over Failure indicator + */ +static void exanic_expired ( struct retry_timer *timer, int over __unused ) { + struct exanic_port *port = + container_of ( timer, struct exanic_port, timer ); + struct net_device *netdev = port->netdev; + static const uint32_t speeds[] = { + 100, 1000, 10000, 40000, 100000, + }; + unsigned int index; + + /* Restart timer */ + start_timer_fixed ( timer, EXANIC_LINK_INTERVAL ); + + /* Check link state */ + exanic_check_link ( netdev ); + + /* Do nothing further if link is already up */ + if ( netdev_link_ok ( netdev ) ) + return; + + /* Do nothing further unless we have a valid list of supported speeds */ + if ( ! port->speeds ) + return; + + /* Autonegotiation is not supported; try manually selecting + * the next supported link speed. + */ + do { + if ( ! port->speed ) + port->speed = ( 8 * sizeof ( port->speeds ) ); + port->speed--; + } while ( ! ( ( 1UL << port->speed ) & port->speeds ) ); + index = ( port->speed - ( ffs ( EXANIC_CAPS_SPEED_MASK ) - 1 ) ); + assert ( index < ( sizeof ( speeds ) / sizeof ( speeds[0] ) ) ); + + /* Attempt the selected speed */ + DBGC ( netdev, "EXANIC %s attempting %dMbps\n", + netdev->name, speeds[index] ); + writel ( speeds[index], ( port->regs + EXANIC_PORT_SPEED ) ); +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int exanic_open ( struct net_device *netdev ) { + struct exanic_port *port = netdev->priv; + struct exanic_tx_chunk *tx; + unsigned int i; + + /* Reset transmit region contents */ + for ( i = 0 ; i < port->tx_count ; i++ ) { + tx = ( port->tx + ( i * sizeof ( *tx ) ) ); + writew ( port->txf_slot, &tx->desc.txf_slot ); + writeb ( EXANIC_TYPE_RAW, &tx->desc.type ); + writeb ( 0, &tx->desc.flags ); + writew ( 0, &tx->pad ); + } + + /* Reset receive region contents */ + memset_user ( port->rx, 0, 0xff, EXANIC_RX_LEN ); + + /* Reset transmit feedback region */ + *(port->txf) = 0; + + /* Reset counters */ + port->tx_prod = 0; + port->tx_cons = 0; + port->rx_cons = 0; + + /* Map receive region */ + exanic_write_base ( phys_to_bus ( user_to_phys ( port->rx, 0 ) ), + ( port->regs + EXANIC_PORT_RX_BASE ) ); + + /* Enable promiscuous mode */ + writel ( EXANIC_PORT_FLAGS_PROMISC, + ( port->regs + EXANIC_PORT_FLAGS ) ); + + /* Reset to default speed and clear cached status */ + writel ( port->default_speed, ( port->regs + EXANIC_PORT_SPEED ) ); + port->speed = 0; + port->status = 0; + + /* Enable port */ + wmb(); + writel ( EXANIC_PORT_ENABLE_ENABLED, + ( port->regs + EXANIC_PORT_ENABLE ) ); + + /* Start link state timer */ + start_timer_fixed ( &port->timer, EXANIC_LINK_INTERVAL ); + + return 0; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void exanic_close ( struct net_device *netdev ) { + struct exanic_port *port = netdev->priv; + + /* Stop link state timer */ + stop_timer ( &port->timer ); + + /* Disable port */ + writel ( 0, ( port->regs + EXANIC_PORT_ENABLE ) ); + wmb(); + + /* Clear receive region */ + exanic_clear_base ( port->regs + EXANIC_PORT_RX_BASE ); + + /* Discard any in-progress receive */ + if ( port->rx_iobuf ) { + netdev_rx_err ( netdev, port->rx_iobuf, -ECANCELED ); + port->rx_iobuf = NULL; + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int exanic_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct exanic_port *port = netdev->priv; + struct exanic_tx_chunk *tx; + unsigned int tx_fill; + unsigned int tx_index; + size_t offset; + size_t len; + uint8_t *src; + uint8_t *dst; + + /* Sanity check */ + len = iob_len ( iobuf ); + if ( len > sizeof ( tx->data ) ) { + DBGC ( port, "EXANIC %s transmit too large\n", netdev->name ); + return -ENOTSUP; + } + + /* Get next transmit descriptor */ + tx_fill = ( port->tx_prod - port->tx_cons ); + if ( tx_fill >= port->tx_count ) { + DBGC ( port, "EXANIC %s out of transmit descriptors\n", + netdev->name ); + return -ENOBUFS; + } + tx_index = ( port->tx_prod & ( port->tx_count - 1 ) ); + offset = ( tx_index * sizeof ( *tx ) ); + tx = ( port->tx + offset ); + DBGC2 ( port, "EXANIC %s TX %04x at [%05zx,%05zx)\n", + netdev->name, port->tx_prod, ( port->tx_offset + offset ), + ( port->tx_offset + offset + + offsetof ( typeof ( *tx ), data ) + len ) ); + port->tx_prod++; + + /* Populate transmit descriptor */ + writew ( port->tx_prod, &tx->desc.txf_id ); + writew ( ( sizeof ( tx->pad ) + len ), &tx->desc.len ); + + /* Copy data to transmit region. There is no DMA on the + * transmit data path. + */ + src = iobuf->data; + dst = tx->data; + while ( len-- ) + writeb ( *(src++), dst++ ); + + /* Send transmit command */ + wmb(); + writel ( ( port->tx_offset + offset ), + ( port->regs + EXANIC_PORT_TX_COMMAND ) ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void exanic_poll_tx ( struct net_device *netdev ) { + struct exanic_port *port = netdev->priv; + + /* Report any completed packets */ + while ( port->tx_cons != *(port->txf) ) { + DBGC2 ( port, "EXANIC %s TX %04x complete\n", + netdev->name, port->tx_cons ); + netdev_tx_complete_next ( netdev ); + port->tx_cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void exanic_poll_rx ( struct net_device *netdev ) { + struct exanic_port *port = netdev->priv; + struct exanic_rx_chunk *rx; + struct exanic_rx_descriptor desc; + uint8_t current; + uint8_t previous; + size_t offset; + size_t len; + + for ( ; ; port->rx_cons++ ) { + + /* Fetch descriptor */ + offset = ( ( port->rx_cons * sizeof ( *rx ) ) % EXANIC_RX_LEN ); + copy_from_user ( &desc, port->rx, + ( offset + offsetof ( typeof ( *rx ), desc ) ), + sizeof ( desc ) ); + + /* Calculate generation */ + current = ( port->rx_cons / ( EXANIC_RX_LEN / sizeof ( *rx ) )); + previous = ( current - 1 ); + + /* Do nothing if no chunk is ready */ + if ( desc.generation == previous ) + break; + + /* Allocate I/O buffer if needed */ + if ( ! port->rx_iobuf ) { + port->rx_iobuf = alloc_iob ( EXANIC_MAX_RX_LEN ); + if ( ! port->rx_iobuf ) { + /* Wait for next poll */ + break; + } + port->rx_rc = 0; + } + + /* Calculate chunk length */ + len = ( desc.len ? desc.len : sizeof ( rx->data ) ); + + /* Append data to I/O buffer */ + if ( len <= iob_tailroom ( port->rx_iobuf ) ) { + copy_from_user ( iob_put ( port->rx_iobuf, len ), + port->rx, + ( offset + offsetof ( typeof ( *rx ), + data ) ), len ); + } else { + DBGC ( port, "EXANIC %s RX too large\n", + netdev->name ); + port->rx_rc = -ERANGE; + } + + /* Check for overrun */ + rmb(); + copy_from_user ( &desc.generation, port->rx, + ( offset + offsetof ( typeof ( *rx ), + desc.generation ) ), + sizeof ( desc.generation ) ); + if ( desc.generation != current ) { + DBGC ( port, "EXANIC %s RX overrun\n", netdev->name ); + port->rx_rc = -ENOBUFS; + continue; + } + + /* Wait for end of packet */ + if ( ! desc.len ) + continue; + + /* Check for receive errors */ + if ( desc.status & EXANIC_STATUS_ERROR_MASK ) { + port->rx_rc = -EIO_STATUS ( desc.status ); + DBGC ( port, "EXANIC %s RX %04x error: %s\n", + netdev->name, port->rx_cons, + strerror ( port->rx_rc ) ); + } else { + DBGC2 ( port, "EXANIC %s RX %04x\n", + netdev->name, port->rx_cons ); + } + + /* Hand off to network stack */ + if ( port->rx_rc ) { + netdev_rx_err ( netdev, port->rx_iobuf, port->rx_rc ); + } else { + iob_unput ( port->rx_iobuf, 4 /* strip CRC */ ); + netdev_rx ( netdev, port->rx_iobuf ); + } + port->rx_iobuf = NULL; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void exanic_poll ( struct net_device *netdev ) { + + /* Poll for completed packets */ + exanic_poll_tx ( netdev ); + + /* Poll for received packets */ + exanic_poll_rx ( netdev ); +} + +/** ExaNIC network device operations */ +static struct net_device_operations exanic_operations = { + .open = exanic_open, + .close = exanic_close, + .transmit = exanic_transmit, + .poll = exanic_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe port + * + * @v exanic ExaNIC device + * @v dev Parent device + * @v index Port number + * @ret rc Return status code + */ +static int exanic_probe_port ( struct exanic *exanic, struct device *dev, + unsigned int index ) { + struct net_device *netdev; + struct exanic_port *port; + void *port_regs; + uint32_t status; + size_t tx_len; + int rc; + + /* Do nothing if port is not physically present */ + port_regs = ( exanic->regs + EXANIC_PORT_REGS ( index ) ); + status = readl ( port_regs + EXANIC_PORT_STATUS ); + tx_len = readl ( port_regs + EXANIC_PORT_TX_LEN ); + if ( ( status & EXANIC_PORT_STATUS_ABSENT ) || ( tx_len == 0 ) ) { + rc = 0; + goto absent; + } + + /* Allocate network device */ + netdev = alloc_etherdev ( sizeof ( *port ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc_netdev; + } + netdev_init ( netdev, &exanic_operations ); + netdev->dev = dev; + port = netdev->priv; + memset ( port, 0, sizeof ( *port ) ); + exanic->port[index] = port; + port->netdev = netdev; + port->regs = port_regs; + timer_init ( &port->timer, exanic_expired, &netdev->refcnt ); + + /* Identify transmit region */ + port->tx_offset = readl ( port->regs + EXANIC_PORT_TX_OFFSET ); + if ( tx_len > EXANIC_MAX_TX_LEN ) + tx_len = EXANIC_MAX_TX_LEN; + assert ( ! ( tx_len & ( tx_len - 1 ) ) ); + port->tx = ( exanic->tx + port->tx_offset ); + port->tx_count = ( tx_len / sizeof ( struct exanic_tx_chunk ) ); + + /* Identify transmit feedback region */ + port->txf_slot = EXANIC_TXF_SLOT ( index ); + port->txf = ( exanic->txf + + ( port->txf_slot * sizeof ( *(port->txf) ) ) ); + + /* Allocate receive region (via umalloc()) */ + port->rx = umalloc ( EXANIC_RX_LEN ); + if ( ! port->rx ) { + rc = -ENOMEM; + goto err_alloc_rx; + } + + /* Set MAC address */ + memcpy ( netdev->hw_addr, exanic->mac, ETH_ALEN ); + netdev->hw_addr[ ETH_ALEN - 1 ] += index; + + /* Record default link speed and supported speeds */ + port->default_speed = readl ( port->regs + EXANIC_PORT_SPEED ); + port->speeds = ( exanic->caps & EXANIC_CAPS_SPEED_MASK ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + DBGC ( port, "EXANIC %s port %d TX [%#05zx,%#05zx) TXF %#02x RX " + "[%#lx,%#lx)\n", netdev->name, index, port->tx_offset, + ( port->tx_offset + tx_len ), port->txf_slot, + user_to_phys ( port->rx, 0 ), + user_to_phys ( port->rx, EXANIC_RX_LEN ) ); + + /* Set initial link state */ + exanic_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + ufree ( port->rx ); + err_alloc_rx: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc_netdev: + absent: + return rc; +} + +/** + * Probe port + * + * @v exanic ExaNIC device + * @v index Port number + */ +static void exanic_remove_port ( struct exanic *exanic, unsigned int index ) { + struct exanic_port *port; + + /* Do nothing if port is not physically present */ + port = exanic->port[index]; + if ( ! port ) + return; + + /* Unregister network device */ + unregister_netdev ( port->netdev ); + + /* Free receive region */ + ufree ( port->rx ); + + /* Free network device */ + netdev_nullify ( port->netdev ); + netdev_put ( port->netdev ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int exanic_probe ( struct pci_device *pci ) { + struct exanic *exanic; + unsigned long regs_bar_start; + unsigned long tx_bar_start; + size_t tx_bar_len; + int i; + int rc; + + /* Allocate and initialise structure */ + exanic = zalloc ( sizeof ( *exanic ) ); + if ( ! exanic ) { + rc = -ENOMEM; + goto err_alloc; + } + pci_set_drvdata ( pci, exanic ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + regs_bar_start = pci_bar_start ( pci, EXANIC_REGS_BAR ); + exanic->regs = ioremap ( regs_bar_start, EXANIC_REGS_LEN ); + if ( ! exanic->regs ) { + rc = -ENODEV; + goto err_ioremap_regs; + } + + /* Reset device */ + exanic_reset ( exanic ); + + /* Read capabilities */ + exanic->caps = readl ( exanic->regs + EXANIC_CAPS ); + + /* Fetch base MAC address */ + if ( ( rc = exanic_fetch_mac ( exanic ) ) != 0 ) + goto err_fetch_mac; + DBGC ( exanic, "EXANIC %p capabilities %#08x base MAC %s\n", + exanic, exanic->caps, eth_ntoa ( exanic->mac ) ); + + /* Map transmit region */ + tx_bar_start = pci_bar_start ( pci, EXANIC_TX_BAR ); + tx_bar_len = pci_bar_size ( pci, EXANIC_TX_BAR ); + exanic->tx = ioremap ( tx_bar_start, tx_bar_len ); + if ( ! exanic->tx ) { + rc = -ENODEV; + goto err_ioremap_tx; + } + + /* Allocate transmit feedback region (shared between all ports) */ + exanic->txf = malloc_dma ( EXANIC_TXF_LEN, EXANIC_ALIGN ); + if ( ! exanic->txf ) { + rc = -ENOMEM; + goto err_alloc_txf; + } + memset ( exanic->txf, 0, EXANIC_TXF_LEN ); + exanic_write_base ( virt_to_bus ( exanic->txf ), + ( exanic->regs + EXANIC_TXF_BASE ) ); + + /* Allocate and initialise per-port network devices */ + for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) { + if ( ( rc = exanic_probe_port ( exanic, &pci->dev, i ) ) != 0 ) + goto err_probe_port; + } + + return 0; + + i = EXANIC_MAX_PORTS; + err_probe_port: + for ( i-- ; i >= 0 ; i-- ) + exanic_remove_port ( exanic, i ); + exanic_reset ( exanic ); + free_dma ( exanic->txf, EXANIC_TXF_LEN ); + err_alloc_txf: + iounmap ( exanic->tx ); + err_ioremap_tx: + iounmap ( exanic->regs ); + err_fetch_mac: + err_ioremap_regs: + free ( exanic ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void exanic_remove ( struct pci_device *pci ) { + struct exanic *exanic = pci_get_drvdata ( pci ); + unsigned int i; + + /* Remove all ports */ + for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) + exanic_remove_port ( exanic, i ); + + /* Reset device */ + exanic_reset ( exanic ); + + /* Free transmit feedback region */ + free_dma ( exanic->txf, EXANIC_TXF_LEN ); + + /* Unmap transmit region */ + iounmap ( exanic->tx ); + + /* Unmap registers */ + iounmap ( exanic->regs ); + + /* Free device */ + free ( exanic ); +} + +/** ExaNIC PCI device IDs */ +static struct pci_device_id exanic_ids[] = { + PCI_ROM ( 0x10ee, 0x2b00, "exanic-old", "ExaNIC (old)", 0 ), + PCI_ROM ( 0x1ce4, 0x0001, "exanic-x4", "ExaNIC X4", 0 ), + PCI_ROM ( 0x1ce4, 0x0002, "exanic-x2", "ExaNIC X2", 0 ), + PCI_ROM ( 0x1ce4, 0x0003, "exanic-x10", "ExaNIC X10", 0 ), + PCI_ROM ( 0x1ce4, 0x0004, "exanic-x10gm", "ExaNIC X10 GM", 0 ), + PCI_ROM ( 0x1ce4, 0x0005, "exanic-x40", "ExaNIC X40", 0 ), + PCI_ROM ( 0x1ce4, 0x0006, "exanic-x10hpt", "ExaNIC X10 HPT", 0 ), +}; + +/** ExaNIC PCI driver */ +struct pci_driver exanic_driver __pci_driver = { + .ids = exanic_ids, + .id_count = ( sizeof ( exanic_ids ) / sizeof ( exanic_ids[0] ) ), + .probe = exanic_probe, + .remove = exanic_remove, +}; diff --git a/src/drivers/net/exanic.h b/src/drivers/net/exanic.h new file mode 100644 index 000000000..fd9f5b8ce --- /dev/null +++ b/src/drivers/net/exanic.h @@ -0,0 +1,257 @@ +#ifndef _EXANIC_H +#define _EXANIC_H + +/** @file + * + * Exablaze ExaNIC driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/** Maximum number of ports */ +#define EXANIC_MAX_PORTS 8 + +/** Register BAR */ +#define EXANIC_REGS_BAR PCI_BASE_ADDRESS_0 + +/** Transmit region BAR */ +#define EXANIC_TX_BAR PCI_BASE_ADDRESS_2 + +/** Alignment for DMA regions */ +#define EXANIC_ALIGN 0x1000 + +/** Flag for 32-bit DMA addresses */ +#define EXANIC_DMA_32_BIT 0x00000001UL + +/** Register set length */ +#define EXANIC_REGS_LEN 0x2000 + +/** Transmit feedback region length */ +#define EXANIC_TXF_LEN 0x1000 + +/** Transmit feedback slot + * + * This is a policy decision. + */ +#define EXANIC_TXF_SLOT( index ) ( 0x40 * (index) ) + +/** Receive region length */ +#define EXANIC_RX_LEN 0x200000 + +/** Transmit feedback base address register */ +#define EXANIC_TXF_BASE 0x0014 + +/** Capabilities register */ +#define EXANIC_CAPS 0x0038 +#define EXANIC_CAPS_100M 0x01000000UL /**< 100Mbps supported */ +#define EXANIC_CAPS_1G 0x02000000UL /**< 1Gbps supported */ +#define EXANIC_CAPS_10G 0x04000000UL /**< 10Gbps supported */ +#define EXANIC_CAPS_40G 0x08000000UL /**< 40Gbps supported */ +#define EXANIC_CAPS_100G 0x10000000UL /**< 100Gbps supported */ +#define EXANIC_CAPS_SPEED_MASK 0x1f000000UL /**< Supported speeds mask */ + +/** I2C GPIO register */ +#define EXANIC_I2C 0x012c + +/** Port register offset */ +#define EXANIC_PORT_REGS( index ) ( 0x0200 + ( 0x40 * (index) ) ) + +/** Port enable register */ +#define EXANIC_PORT_ENABLE 0x0000 +#define EXANIC_PORT_ENABLE_ENABLED 0x00000001UL /**< Port is enabled */ + +/** Port speed register */ +#define EXANIC_PORT_SPEED 0x0004 + +/** Port status register */ +#define EXANIC_PORT_STATUS 0x0008 +#define EXANIC_PORT_STATUS_LINK 0x00000008UL /**< Link is up */ +#define EXANIC_PORT_STATUS_ABSENT 0x80000000UL /**< Port is not present */ + +/** Port MAC address (second half) register */ +#define EXANIC_PORT_MAC 0x000c + +/** Port flags register */ +#define EXANIC_PORT_FLAGS 0x0010 +#define EXANIC_PORT_FLAGS_PROMISC 0x00000001UL /**< Promiscuous mode */ + +/** Port receive chunk base address register */ +#define EXANIC_PORT_RX_BASE 0x0014 + +/** Port transmit command register */ +#define EXANIC_PORT_TX_COMMAND 0x0020 + +/** Port transmit region offset register */ +#define EXANIC_PORT_TX_OFFSET 0x0024 + +/** Port transmit region length register */ +#define EXANIC_PORT_TX_LEN 0x0028 + +/** Port MAC address (first half) register */ +#define EXANIC_PORT_OUI 0x0030 + +/** Port interrupt configuration register */ +#define EXANIC_PORT_IRQ 0x0034 + +/** An ExaNIC transmit chunk descriptor */ +struct exanic_tx_descriptor { + /** Feedback ID */ + uint16_t txf_id; + /** Feedback slot */ + uint16_t txf_slot; + /** Payload length (including padding */ + uint16_t len; + /** Payload type */ + uint8_t type; + /** Flags */ + uint8_t flags; +} __attribute__ (( packed )); + +/** An ExaNIC transmit chunk */ +struct exanic_tx_chunk { + /** Descriptor */ + struct exanic_tx_descriptor desc; + /** Padding */ + uint8_t pad[2]; + /** Payload data */ + uint8_t data[2038]; +} __attribute__ (( packed )); + +/** Raw Ethernet frame type */ +#define EXANIC_TYPE_RAW 0x01 + +/** An ExaNIC receive chunk descriptor */ +struct exanic_rx_descriptor { + /** Timestamp */ + uint32_t timestamp; + /** Status (valid only on final chunk) */ + uint8_t status; + /** Length (zero except on the final chunk) */ + uint8_t len; + /** Filter number */ + uint8_t filter; + /** Generation */ + uint8_t generation; +} __attribute__ (( packed )); + +/** An ExaNIC receive chunk */ +struct exanic_rx_chunk { + /** Payload data */ + uint8_t data[120]; + /** Descriptor */ + struct exanic_rx_descriptor desc; +} __attribute__ (( packed )); + +/** Receive status error mask */ +#define EXANIC_STATUS_ERROR_MASK 0x0f + +/** An ExaNIC I2C bus configuration */ +struct exanic_i2c_config { + /** GPIO bit for pulling SCL low */ + uint8_t setscl; + /** GPIO bit for pulling SDA low */ + uint8_t setsda; + /** GPIO bit for reading SDA */ + uint8_t getsda; +}; + +/** EEPROM address */ +#define EXANIC_EEPROM_ADDRESS 0x50 + +/** An ExaNIC port */ +struct exanic_port { + /** Network device */ + struct net_device *netdev; + /** Port registers */ + void *regs; + + /** Transmit region offset */ + size_t tx_offset; + /** Transmit region */ + void *tx; + /** Number of transmit descriptors */ + uint16_t tx_count; + /** Transmit producer counter */ + uint16_t tx_prod; + /** Transmit consumer counter */ + uint16_t tx_cons; + /** Transmit feedback slot */ + uint16_t txf_slot; + /** Transmit feedback region */ + uint16_t *txf; + + /** Receive region */ + userptr_t rx; + /** Receive consumer counter */ + unsigned int rx_cons; + /** Receive I/O buffer (if any) */ + struct io_buffer *rx_iobuf; + /** Receive status */ + int rx_rc; + + /** Port status */ + uint32_t status; + /** Default link speed (as raw register value) */ + uint32_t default_speed; + /** Speed capability bitmask */ + uint32_t speeds; + /** Current attempted link speed (as a capability bit index) */ + unsigned int speed; + /** Port status check timer */ + struct retry_timer timer; +}; + +/** An ExaNIC */ +struct exanic { + /** Registers */ + void *regs; + /** Transmit region */ + void *tx; + /** Transmit feedback region */ + void *txf; + + /** I2C bus configuration */ + struct exanic_i2c_config i2cfg; + /** I2C bit-bashing interface */ + struct i2c_bit_basher basher; + /** I2C serial EEPROM */ + struct i2c_device eeprom; + + /** Capabilities */ + uint32_t caps; + /** Base MAC address */ + uint8_t mac[ETH_ALEN]; + + /** Ports */ + struct exanic_port *port[EXANIC_MAX_PORTS]; +}; + +/** Maximum used length of transmit region + * + * This is a policy decision to avoid overflowing the 16-bit transmit + * producer and consumer counters. + */ +#define EXANIC_MAX_TX_LEN ( 256 * sizeof ( struct exanic_tx_chunk ) ) + +/** Maximum length of received packet + * + * This is a policy decision. + */ +#define EXANIC_MAX_RX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) + +/** Interval between link state checks + * + * This is a policy decision. + */ +#define EXANIC_LINK_INTERVAL ( 1 * TICKS_PER_SEC ) + +#endif /* _EXANIC_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index faa1e77f5..dc3de0518 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -199,6 +199,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_af_packet ( ERRFILE_DRIVER | 0x00c30000 ) #define ERRFILE_sfc_hunt ( ERRFILE_DRIVER | 0x00c40000 ) #define ERRFILE_efx_hunt ( ERRFILE_DRIVER | 0x00c50000 ) +#define ERRFILE_exanic ( ERRFILE_DRIVER | 0x00c60000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 8e48d0df6b5a249e4f9f0759cc55a45af186b564 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 3 Jul 2017 13:38:55 +0100 Subject: [PATCH 479/591] [usb] Use non-zero language ID to retrieve strings We currently use a zero language ID to retrieve strings such as the ECM/NCM MAC address. This works on most hardware devices, but is known to fail on some software emulated CDC-NCM devices. Fix by using the first supported language ID, falling back to English (0x0409) if any error occurs when fetching the list of supported languages. This matches the behaviour of the Linux kernel. Signed-off-by: Michael Brown --- src/drivers/bus/usb.c | 37 ++++++++++++++++++++++++++++++++++++- src/include/ipxe/usb.h | 6 ++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index bd2a446be..d8db3849a 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -843,12 +843,40 @@ int usb_control ( struct usb_device *usb, unsigned int request, return rc; } +/** + * Get default language ID + * + * @v usb USB device + * @ret language Language ID + */ +static unsigned int usb_get_default_language ( struct usb_device *usb ) { + struct { + struct usb_descriptor_header header; + uint16_t language[1]; + } __attribute__ (( packed )) desc; + unsigned int language; + int rc; + + /* Get descriptor */ + if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, 0, 0, + &desc.header, sizeof ( desc ) ) ) !=0){ + DBGC ( usb, "USB %s has no default language: %s\n", + usb->name, strerror ( rc ) ); + return USB_LANG_ENGLISH; + } + + /* Use first language ID */ + language = le16_to_cpu ( desc.language[0] ); + DBGC2 ( usb, "USB %s default language %#04x\n", usb->name, language ); + return language; +} + /** * Get USB string descriptor * * @v usb USB device * @v index String index - * @v language Language ID + * @v language Language ID, or 0 to use default * @v buf Data buffer * @v len Length of buffer * @ret len String length (excluding NUL), or negative error @@ -864,6 +892,13 @@ int usb_get_string_descriptor ( struct usb_device *usb, unsigned int index, unsigned int i; int rc; + /* Use default language ID, if applicable */ + if ( ( language == 0 ) && ( index != 0 ) ) { + if ( ! usb->language ) + usb->language = usb_get_default_language ( usb ); + language = usb->language; + } + /* Allocate buffer for string */ desc = malloc ( sizeof ( *desc ) ); if ( ! desc ) { diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index e7909d300..68289d26d 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -223,6 +223,9 @@ struct usb_string_descriptor { /** A USB string descriptor */ #define USB_STRING_DESCRIPTOR 3 +/** Language ID for English */ +#define USB_LANG_ENGLISH 0x0409 + /** A USB interface descriptor */ struct usb_interface_descriptor { /** Descriptor header */ @@ -728,6 +731,9 @@ struct usb_device { struct usb_endpoint control; /** Completed control transfers */ struct list_head complete; + + /** Default language ID (if known) */ + unsigned int language; }; /** USB device host controller operations */ From c20da4fc5c3c3a3fd7a08b695e1624939bd6a911 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 4 Jul 2017 12:51:53 +0100 Subject: [PATCH 480/591] [mucurses] Avoid potential division by zero Signed-off-by: Michael Brown --- src/hci/mucurses/slk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c index 169e0120b..2a57b1dee 100644 --- a/src/hci/mucurses/slk.c +++ b/src/hci/mucurses/slk.c @@ -269,8 +269,7 @@ int slk_init ( int fmt ) { slks->spaces[0] = 3; slks->spaces[1] = 7; break; default: - nblocks = 0; nmaj = 0; nmin = 0; - break; + return ERR; } // determine maximum label length and major space size From 1e4a3f5babdf46c8c47b6bbaf8c197b4c94d6964 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 4 Jul 2017 12:51:29 +0100 Subject: [PATCH 481/591] [tls] Support RFC5746 secure renegotiation Support renegotiation with servers supporting RFC5746. This allows for the use of per-directory client certificates. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 15 +++ src/net/tls.c | 207 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 203 insertions(+), 19 deletions(-) diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 7d982c326..7345fbee4 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -108,6 +108,17 @@ struct tls_header { /* TLS signature algorithms extension */ #define TLS_SIGNATURE_ALGORITHMS 13 +/* TLS renegotiation information extension */ +#define TLS_RENEGOTIATION_INFO 0xff01 + +/** TLS verification data */ +struct tls_verify_data { + /** Client verification data */ + uint8_t client[12]; + /** Server verification data */ + uint8_t server[12]; +} __attribute__ (( packed )); + /** TLS RX state machine state */ enum tls_rx_state { TLS_RX_HEADER = 0, @@ -271,6 +282,10 @@ struct tls_session { uint8_t *handshake_ctx; /** Client certificate (if used) */ struct x509_certificate *cert; + /** Secure renegotiation flag */ + int secure_renegotiation; + /** Verification data */ + struct tls_verify_data verify; /** Server certificate chain */ struct x509_chain *chain; diff --git a/src/net/tls.c b/src/net/tls.c index 2b809a628..b197c111f 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -162,6 +162,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPERM_CLIENT_CERT \ __einfo_uniqify ( EINFO_EPERM, 0x03, \ "No suitable client certificate available" ) +#define EPERM_RENEG_INSECURE __einfo_error ( EINFO_EPERM_RENEG_INSECURE ) +#define EINFO_EPERM_RENEG_INSECURE \ + __einfo_uniqify ( EINFO_EPERM, 0x04, \ + "Secure renegotiation not supported" ) +#define EPERM_RENEG_VERIFY __einfo_error ( EINFO_EPERM_RENEG_VERIFY ) +#define EINFO_EPERM_RENEG_VERIFY \ + __einfo_uniqify ( EINFO_EPERM, 0x05, \ + "Secure renegotiation verification failed" ) #define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION ) #define EINFO_EPROTO_VERSION \ __einfo_uniqify ( EINFO_EPROTO, 0x01, \ @@ -887,6 +895,30 @@ static void tls_verify_handshake ( struct tls_session *tls, void *out ) { ****************************************************************************** */ +/** + * Restart negotiation + * + * @v tls TLS session + */ +static void tls_restart ( struct tls_session *tls ) { + + /* Sanity check */ + assert ( ! tls->tx_pending ); + assert ( ! is_pending ( &tls->client_negotiation ) ); + assert ( ! is_pending ( &tls->server_negotiation ) ); + + /* (Re)initialise handshake context */ + digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); + digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx ); + tls->handshake_digest = &sha256_algorithm; + tls->handshake_ctx = tls->handshake_sha256_ctx; + + /* (Re)start negotiation */ + tls->tx_pending = TLS_TX_CLIENT_HELLO; + pending_get ( &tls->client_negotiation ); + pending_get ( &tls->server_negotiation ); +} + /** * Resume TX state machine * @@ -954,6 +986,13 @@ static int tls_send_client_hello ( struct tls_session *tls ) { struct tls_signature_hash_id code[TLS_NUM_SIG_HASH_ALGORITHMS]; } __attribute__ (( packed )) signature_algorithms; + uint16_t renegotiation_info_type; + uint16_t renegotiation_info_len; + struct { + uint8_t len; + uint8_t data[ tls->secure_renegotiation ? + sizeof ( tls->verify.client ) :0]; + } __attribute__ (( packed )) renegotiation_info; } __attribute__ (( packed )) extensions; } __attribute__ (( packed )) hello; struct tls_cipher_suite *suite; @@ -995,6 +1034,14 @@ static int tls_send_client_hello ( struct tls_session *tls ) { = htons ( sizeof ( hello.extensions.signature_algorithms.code)); i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS ) hello.extensions.signature_algorithms.code[i++] = sighash->code; + hello.extensions.renegotiation_info_type + = htons ( TLS_RENEGOTIATION_INFO ); + hello.extensions.renegotiation_info_len + = htons ( sizeof ( hello.extensions.renegotiation_info ) ); + hello.extensions.renegotiation_info.len + = sizeof ( hello.extensions.renegotiation_info.data ); + memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client, + sizeof ( hello.extensions.renegotiation_info.data ) ); return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); } @@ -1201,20 +1248,24 @@ static int tls_send_finished ( struct tls_session *tls ) { struct digest_algorithm *digest = tls->handshake_digest; struct { uint32_t type_length; - uint8_t verify_data[12]; + uint8_t verify_data[ sizeof ( tls->verify.client ) ]; } __attribute__ (( packed )) finished; uint8_t digest_out[ digest->digestsize ]; int rc; + /* Construct client verification data */ + tls_verify_handshake ( tls, digest_out ); + tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), + tls->verify.client, sizeof ( tls->verify.client ), + "client finished", digest_out, sizeof ( digest_out ) ); + /* Construct record */ memset ( &finished, 0, sizeof ( finished ) ); finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) | htonl ( sizeof ( finished ) - sizeof ( finished.type_length ) ) ); - tls_verify_handshake ( tls, digest_out ); - tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - finished.verify_data, sizeof ( finished.verify_data ), - "client finished", digest_out, sizeof ( digest_out ) ); + memcpy ( finished.verify_data, tls->verify.client, + sizeof ( finished.verify_data ) ); /* Transmit record */ if ( ( rc = tls_send_handshake ( tls, &finished, @@ -1295,6 +1346,37 @@ static int tls_new_alert ( struct tls_session *tls, const void *data, } } +/** + * Receive new Hello Request handshake record + * + * @v tls TLS session + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_hello_request ( struct tls_session *tls, + const void *data __unused, + size_t len __unused ) { + + /* Ignore if a handshake is in progress */ + if ( ! tls_ready ( tls ) ) { + DBGC ( tls, "TLS %p ignoring Hello Request\n", tls ); + return 0; + } + + /* Fail unless server supports secure renegotiation */ + if ( ! tls->secure_renegotiation ) { + DBGC ( tls, "TLS %p refusing to renegotiate insecurely\n", + tls ); + return -EPERM_RENEG_INSECURE; + } + + /* Restart negotiation */ + tls_restart ( tls ); + + return 0; +} + /** * Receive new Server Hello handshake record * @@ -1317,7 +1399,23 @@ static int tls_new_server_hello ( struct tls_session *tls, uint8_t compression_method; char next[0]; } __attribute__ (( packed )) *hello_b; + const struct { + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *exts; + const struct { + uint16_t type; + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *ext; + const struct { + uint8_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *reneg = NULL; uint16_t version; + size_t exts_len; + size_t ext_len; + size_t remaining; int rc; /* Parse header */ @@ -1332,6 +1430,56 @@ static int tls_new_server_hello ( struct tls_session *tls, session_id = hello_a->session_id; hello_b = ( ( void * ) ( session_id + hello_a->session_id_len ) ); + /* Parse extensions, if present */ + remaining = ( len - sizeof ( *hello_a ) - hello_a->session_id_len - + sizeof ( *hello_b ) ); + if ( remaining ) { + + /* Parse extensions length */ + exts = ( ( void * ) hello_b->next ); + if ( ( sizeof ( *exts ) > remaining ) || + ( ( exts_len = ntohs ( exts->len ) ) > + ( remaining - sizeof ( *exts ) ) ) ) { + DBGC ( tls, "TLS %p received underlength extensions\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_HELLO; + } + + /* Parse extensions */ + for ( ext = ( ( void * ) exts->data ), remaining = exts_len ; + remaining ; + ext = ( ( ( void * ) ext ) + sizeof ( *ext ) + ext_len ), + remaining -= ( sizeof ( *ext ) + ext_len ) ) { + + /* Parse extension length */ + if ( ( sizeof ( *ext ) > remaining ) || + ( ( ext_len = ntohs ( ext->len ) ) > + ( remaining - sizeof ( *ext ) ) ) ) { + DBGC ( tls, "TLS %p received underlength " + "extension\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_HELLO; + } + + /* Record known extensions */ + switch ( ext->type ) { + case htons ( TLS_RENEGOTIATION_INFO ) : + reneg = ( ( void * ) ext->data ); + if ( ( sizeof ( *reneg ) > ext_len ) || + ( reneg->len > + ( ext_len - sizeof ( *reneg ) ) ) ) { + DBGC ( tls, "TLS %p received " + "underlength renegotiation " + "info\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_HELLO; + } + break; + } + } + } + /* Check and store protocol version */ version = ntohs ( hello_a->version ); if ( version < TLS_VERSION_TLS_1_0 ) { @@ -1370,6 +1518,30 @@ static int tls_new_server_hello ( struct tls_session *tls, if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) return rc; + /* Handle secure renegotiation */ + if ( tls->secure_renegotiation ) { + + /* Secure renegotiation is expected; verify data */ + if ( ( reneg == NULL ) || + ( reneg->len != sizeof ( tls->verify ) ) || + ( memcmp ( reneg->data, &tls->verify, + sizeof ( tls->verify ) ) != 0 ) ) { + DBGC ( tls, "TLS %p server failed secure " + "renegotiation\n", tls ); + return -EPERM_RENEG_VERIFY; + } + + } else if ( reneg != NULL ) { + + /* Secure renegotiation is being enabled */ + if ( reneg->len != 0 ) { + DBGC ( tls, "TLS %p server provided non-empty initial " + "renegotiation\n", tls ); + return -EPERM_RENEG_VERIFY; + } + tls->secure_renegotiation = 1; + } + return 0; } @@ -1569,11 +1741,10 @@ static int tls_new_finished ( struct tls_session *tls, const void *data, size_t len ) { struct digest_algorithm *digest = tls->handshake_digest; const struct { - uint8_t verify_data[12]; + uint8_t verify_data[ sizeof ( tls->verify.server ) ]; char next[0]; } __attribute__ (( packed )) *finished = data; uint8_t digest_out[ digest->digestsize ]; - uint8_t verify_data[ sizeof ( finished->verify_data ) ]; /* Sanity check */ if ( sizeof ( *finished ) != len ) { @@ -1585,10 +1756,10 @@ static int tls_new_finished ( struct tls_session *tls, /* Verify data */ tls_verify_handshake ( tls, digest_out ); tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - verify_data, sizeof ( verify_data ), "server finished", - digest_out, sizeof ( digest_out ) ); - if ( memcmp ( verify_data, finished->verify_data, - sizeof ( verify_data ) ) != 0 ) { + tls->verify.server, sizeof ( tls->verify.server ), + "server finished", digest_out, sizeof ( digest_out ) ); + if ( memcmp ( tls->verify.server, finished->verify_data, + sizeof ( tls->verify.server ) ) != 0 ) { DBGC ( tls, "TLS %p verification failed\n", tls ); return -EPERM_VERIFY; } @@ -1644,6 +1815,10 @@ static int tls_new_handshake ( struct tls_session *tls, /* Handle payload */ switch ( handshake->type ) { + case TLS_HELLO_REQUEST: + rc = tls_new_hello_request ( tls, payload, + payload_len ); + break; case TLS_SERVER_HELLO: rc = tls_new_server_hello ( tls, payload, payload_len ); break; @@ -2622,18 +2797,12 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { goto err_random; } - digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); - digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx ); - tls->handshake_digest = &sha256_algorithm; - tls->handshake_ctx = tls->handshake_sha256_ctx; - tls->tx_pending = TLS_TX_CLIENT_HELLO; iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, sizeof ( tls->rx_header ) ); INIT_LIST_HEAD ( &tls->rx_data ); - /* Add pending operations for server and client Finished messages */ - pending_get ( &tls->client_negotiation ); - pending_get ( &tls->server_negotiation ); + /* Start negotiation */ + tls_restart ( tls ); /* Attach to parent interface, mortalise self, and return */ intf_plug_plug ( &tls->plainstream, xfer ); From 1015a350f7952ffeaaab85b569e10744b0f2c1ee Mon Sep 17 00:00:00 2001 From: Jerone Young Date: Sun, 2 Jul 2017 17:23:17 -0500 Subject: [PATCH 482/591] [intel] Add support for I219-V in 7th Gen Intel NUC Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index d09978ad9..f66de7bf3 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1132,6 +1132,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", 0 ), + PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; From 5a7558447ae9a795215468e5e7ad2511dc4a02fd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 Jul 2017 16:58:22 +0100 Subject: [PATCH 483/591] [smscusb] Abstract out common SMSC USB device functionality The smsc75xx and smsc95xx drivers include a substantial amount of identical functionality, varying only in the base address of register sets. Abstract out this common functionality to allow code to be shared between the drivers. Signed-off-by: Michael Brown --- src/drivers/net/smscusb.c | 523 +++++++++++++++++++++++++++++++++++++ src/drivers/net/smscusb.h | 302 +++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 826 insertions(+) create mode 100644 src/drivers/net/smscusb.c create mode 100644 src/drivers/net/smscusb.h diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c new file mode 100644 index 000000000..d4f3af034 --- /dev/null +++ b/src/drivers/net/smscusb.c @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2017 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 "smscusb.h" + +/** @file + * + * SMSC USB Ethernet drivers + * + */ + +/** Interrupt completion profiler */ +static struct profiler smscusb_intr_profiler __profiler = + { .name = "smscusb.intr" }; + +/****************************************************************************** + * + * EEPROM access + * + ****************************************************************************** + */ + +/** + * Wait for EEPROM to become idle + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @ret rc Return status code + */ +static int smscusb_eeprom_wait ( struct smscusb_device *smscusb, + unsigned int e2p_base ) { + uint32_t e2p_cmd; + unsigned int i; + int rc; + + /* Wait for EPC_BSY to become clear */ + for ( i = 0 ; i < SMSCUSB_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* Read E2P_CMD and check EPC_BSY */ + if ( ( rc = smscusb_readl ( smscusb, + ( e2p_base + SMSCUSB_E2P_CMD ), + &e2p_cmd ) ) != 0 ) + return rc; + if ( ! ( e2p_cmd & SMSCUSB_E2P_CMD_EPC_BSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for EEPROM\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Read byte from EEPROM + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @v address EEPROM address + * @ret byte Byte read, or negative error + */ +static int smscusb_eeprom_read_byte ( struct smscusb_device *smscusb, + unsigned int e2p_base, + unsigned int address ) { + uint32_t e2p_cmd; + uint32_t e2p_data; + int rc; + + /* Wait for EEPROM to become idle */ + if ( ( rc = smscusb_eeprom_wait ( smscusb, e2p_base ) ) != 0 ) + return rc; + + /* Initiate read command */ + e2p_cmd = ( SMSCUSB_E2P_CMD_EPC_BSY | SMSCUSB_E2P_CMD_EPC_CMD_READ | + SMSCUSB_E2P_CMD_EPC_ADDR ( address ) ); + if ( ( rc = smscusb_writel ( smscusb, ( e2p_base + SMSCUSB_E2P_CMD ), + e2p_cmd ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_eeprom_wait ( smscusb, e2p_base ) ) != 0 ) + return rc; + + /* Read EEPROM data */ + if ( ( rc = smscusb_readl ( smscusb, ( e2p_base + SMSCUSB_E2P_DATA ), + &e2p_data ) ) != 0 ) + return rc; + + return SMSCUSB_E2P_DATA_GET ( e2p_data ); +} + +/** + * Read data from EEPROM + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @v address EEPROM address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smscusb_eeprom_read ( struct smscusb_device *smscusb, + unsigned int e2p_base, unsigned int address, + void *data, size_t len ) { + uint8_t *bytes; + int byte; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smscusb_eeprom_read_byte ( smscusb, e2p_base, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/** + * Fetch MAC address from EEPROM + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @ret rc Return status code + */ +int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, + unsigned int e2p_base ) { + struct net_device *netdev = smscusb->netdev; + int rc; + + /* Read MAC address from EEPROM */ + if ( ( rc = smscusb_eeprom_read ( smscusb, e2p_base, SMSCUSB_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + return rc; + + /* Check that EEPROM is physically present */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + DBGC ( smscusb, "SMSCUSB %p has no EEPROM (%s)\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return -ENODEV; + } + + DBGC ( smscusb, "SMSCUSB %p using EEPROM MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/****************************************************************************** + * + * MII access + * + ****************************************************************************** + */ + +/** + * Wait for MII to become idle + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smscusb_mii_wait ( struct smscusb_device *smscusb ) { + unsigned int base = smscusb->mii_base; + uint32_t mii_access; + unsigned int i; + int rc; + + /* Wait for MIIBZY to become clear */ + for ( i = 0 ; i < SMSCUSB_MII_MAX_WAIT_MS ; i++ ) { + + /* Read MII_ACCESS and check MIIBZY */ + if ( ( rc = smscusb_readl ( smscusb, + ( base + SMSCUSB_MII_ACCESS ), + &mii_access ) ) != 0 ) + return rc; + if ( ! ( mii_access & SMSCUSB_MII_ACCESS_MIIBZY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for MII\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int smscusb_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct smscusb_device *smscusb = + container_of ( mii, struct smscusb_device, mii ); + unsigned int base = smscusb->mii_base; + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + /* Initiate read command */ + mii_access = ( SMSCUSB_MII_ACCESS_PHY_ADDRESS | + SMSCUSB_MII_ACCESS_MIIRINDA ( reg ) | + SMSCUSB_MII_ACCESS_MIIBZY ); + if ( ( rc = smscusb_writel ( smscusb, ( base + SMSCUSB_MII_ACCESS ), + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + /* Read MII data */ + if ( ( rc = smscusb_readl ( smscusb, ( base + SMSCUSB_MII_DATA ), + &mii_data ) ) != 0 ) + return rc; + + return SMSCUSB_MII_DATA_GET ( mii_data ); +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int smscusb_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data ) { + struct smscusb_device *smscusb = + container_of ( mii, struct smscusb_device, mii ); + unsigned int base = smscusb->mii_base; + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + /* Write MII data */ + mii_data = SMSCUSB_MII_DATA_SET ( data ); + if ( ( rc = smscusb_writel ( smscusb, ( base + SMSCUSB_MII_DATA ), + mii_data ) ) != 0 ) + return rc; + + /* Initiate write command */ + mii_access = ( SMSCUSB_MII_ACCESS_PHY_ADDRESS | + SMSCUSB_MII_ACCESS_MIIRINDA ( reg ) | + SMSCUSB_MII_ACCESS_MIIWNR | + SMSCUSB_MII_ACCESS_MIIBZY ); + if ( ( rc = smscusb_writel ( smscusb, ( base + SMSCUSB_MII_ACCESS ), + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + return 0; +} + +/** MII operations */ +struct mii_operations smscusb_mii_operations = { + .read = smscusb_mii_read, + .write = smscusb_mii_write, +}; + +/** + * Check link status + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +int smscusb_mii_check_link ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + int intr; + int rc; + + /* Read PHY interrupt source */ + intr = mii_read ( &smscusb->mii, SMSCUSB_MII_PHY_INTR_SOURCE ); + if ( intr < 0 ) { + rc = intr; + DBGC ( smscusb, "SMSCUSB %p could not get PHY interrupt " + "source: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Acknowledge PHY interrupt */ + if ( ( rc = mii_write ( &smscusb->mii, SMSCUSB_MII_PHY_INTR_SOURCE, + intr ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not acknowledge PHY " + "interrupt: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Check link status */ + if ( ( rc = mii_check_link ( &smscusb->mii, netdev ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not check link: %s\n", + smscusb, strerror ( rc ) ); + return rc; + } + + DBGC ( smscusb, "SMSCUSB %p link %s (intr %#04x)\n", + smscusb, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); + return 0; +} + +/** + * Enable PHY interrupts and update link status + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +int smscusb_mii_open ( struct smscusb_device *smscusb ) { + int rc; + + /* Enable PHY interrupts */ + if ( ( rc = mii_write ( &smscusb->mii, SMSCUSB_MII_PHY_INTR_MASK, + ( SMSCUSB_PHY_INTR_ANEG_DONE | + SMSCUSB_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not set PHY interrupt " + "mask: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + smscusb_mii_check_link ( smscusb ); + + return 0; +} + +/****************************************************************************** + * + * Receive filtering + * + ****************************************************************************** + */ + +/** + * Set receive address + * + * @v smscusb SMSC USB device + * @v addr_base Receive address register base + * @ret rc Return status code + */ +int smscusb_set_address ( struct smscusb_device *smscusb, + unsigned int addr_base ) { + struct net_device *netdev = smscusb->netdev; + union smscusb_mac mac; + int rc; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + + /* Write MAC address high register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( addr_base + SMSCUSB_RX_ADDRH ), + mac.addr.h ) ) != 0 ) + return rc; + + /* Write MAC address low register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( addr_base + SMSCUSB_RX_ADDRL ), + mac.addr.l ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Set receive filter + * + * @v smscusb SMSC USB device + * @v filt_base Receive filter register base + * @ret rc Return status code + */ +int smscusb_set_filter ( struct smscusb_device *smscusb, + unsigned int filt_base ) { + struct net_device *netdev = smscusb->netdev; + union smscusb_mac mac; + int rc; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + mac.addr.h |= cpu_to_le32 ( SMSCUSB_ADDR_FILTH_VALID ); + + /* Write MAC address perfect filter high register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( filt_base + SMSCUSB_ADDR_FILTH(0) ), + mac.addr.h ) ) != 0 ) + return rc; + + /* Write MAC address perfect filter low register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( filt_base + SMSCUSB_ADDR_FILTL(0) ), + mac.addr.l ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smscusb_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.intr ); + struct net_device *netdev = smscusb->netdev; + struct smscusb_interrupt *intr; + + /* Profile completions */ + profile_start ( &smscusb_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smscusb, "SMSCUSB %p interrupt failed: %s\n", + smscusb, strerror ( rc ) ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract interrupt data */ + if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { + DBGC ( smscusb, "SMSCUSB %p malformed interrupt\n", + smscusb ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + intr = iobuf->data; + + /* Record interrupt status */ + smscusb->int_sts = le32_to_cpu ( intr->int_sts ); + profile_stop ( &smscusb_intr_profiler ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +struct usb_endpoint_driver_operations smscusb_intr_operations = { + .complete = smscusb_intr_complete, +}; + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smscusb_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.out ); + struct net_device *netdev = smscusb->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +struct usb_endpoint_driver_operations smscusb_out_operations = { + .complete = smscusb_out_complete, +}; diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h new file mode 100644 index 000000000..152338184 --- /dev/null +++ b/src/drivers/net/smscusb.h @@ -0,0 +1,302 @@ +#ifndef _SMSCUSB_H +#define _SMSCUSB_H + +/** @file + * + * SMSC USB Ethernet drivers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** Register write command */ +#define SMSCUSB_REGISTER_WRITE \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa0 ) ) + +/** Register read command */ +#define SMSCUSB_REGISTER_READ \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa1 ) ) + +/** Get statistics command */ +#define SMSCUSB_GET_STATISTICS \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa2 ) ) + +/** EEPROM command register offset */ +#define SMSCUSB_E2P_CMD 0x000 +#define SMSCUSB_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ +#define SMSCUSB_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ +#define SMSCUSB_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ + +/** EEPROM data register offset */ +#define SMSCUSB_E2P_DATA 0x004 +#define SMSCUSB_E2P_DATA_GET(e2p_data) \ + ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ + +/** MAC address EEPROM address */ +#define SMSCUSB_EEPROM_MAC 0x01 + +/** Maximum time to wait for EEPROM (in milliseconds) */ +#define SMSCUSB_EEPROM_MAX_WAIT_MS 100 + +/** MII access register offset */ +#define SMSCUSB_MII_ACCESS 0x000 +#define SMSCUSB_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ +#define SMSCUSB_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ +#define SMSCUSB_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ +#define SMSCUSB_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ + +/** MII data register offset */ +#define SMSCUSB_MII_DATA 0x004 +#define SMSCUSB_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ +#define SMSCUSB_MII_DATA_GET(mii_data) \ + ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ + +/** PHY interrupt source MII register */ +#define SMSCUSB_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSCUSB_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSCUSB_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSCUSB_PHY_INTR_LINK_DOWN 0x0010 + +/** Maximum time to wait for MII (in milliseconds) */ +#define SMSCUSB_MII_MAX_WAIT_MS 100 + +/** MAC address */ +union smscusb_mac { + /** MAC receive address registers */ + struct { + /** MAC receive address low register */ + uint32_t l; + /** MAC receive address high register */ + uint32_t h; + } __attribute__ (( packed )) addr; + /** Raw MAC address */ + uint8_t raw[ETH_ALEN]; +}; + +/** MAC receive address high register offset */ +#define SMSCUSB_RX_ADDRH 0x000 + +/** MAC receive address low register offset */ +#define SMSCUSB_RX_ADDRL 0x004 + +/** MAC address perfect filter N high register offset */ +#define SMSCUSB_ADDR_FILTH(n) ( 0x000 + ( 8 * (n) ) ) +#define SMSCUSB_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ + +/** MAC address perfect filter N low register offset */ +#define SMSCUSB_ADDR_FILTL(n) ( 0x004 + ( 8 * (n) ) ) + +/** Interrupt packet format */ +struct smscusb_interrupt { + /** Current value of INT_STS register */ + uint32_t int_sts; +} __attribute__ (( packed )); + +/** An SMSC USB device */ +struct smscusb_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + /** MII interface */ + struct mii_interface mii; + /** MII register base */ + uint16_t mii_base; + /** Interrupt status */ + uint32_t int_sts; +}; + +/** + * Write register (without byte-swapping) + * + * @v smscusb Smscusb device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static int smscusb_raw_writel ( struct smscusb_device *smscusb, + unsigned int address, uint32_t value ) { + int rc; + + /* Write register */ + DBGCIO ( smscusb, "SMSCUSB %p [%03x] <= %08x\n", + smscusb, address, le32_to_cpu ( value ) ); + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_WRITE, 0, + address, &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not write %03x: %s\n", + smscusb, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write register + * + * @v smscusb SMSC USB device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smscusb_writel ( struct smscusb_device *smscusb, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = smscusb_raw_writel ( smscusb, address, + cpu_to_le32 ( value ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read register (without byte-swapping) + * + * @v smscusb SMSC USB device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static int smscusb_raw_readl ( struct smscusb_device *smscusb, + unsigned int address, uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_READ, 0, + address, value, sizeof ( *value ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not read %03x: %s\n", + smscusb, address, strerror ( rc ) ); + return rc; + } + DBGCIO ( smscusb, "SMSCUSB %p [%03x] => %08x\n", + smscusb, address, le32_to_cpu ( *value ) ); + + return 0; +} + +/** + * Read register + * + * @v smscusb SMSC USB device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smscusb_readl ( struct smscusb_device *smscusb, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = smscusb_raw_readl ( smscusb, address, value ) ) != 0 ) + return rc; + le32_to_cpus ( value ); + + return 0; +} + +/** + * Get statistics + * + * @v smscusb SMSC USB device + * @v index Statistics set index + * @v data Statistics data to fill in + * @v len Length of statistics data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smscusb_get_statistics ( struct smscusb_device *smscusb, unsigned int index, + void *data, size_t len ) { + int rc; + + /* Read statistics */ + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_GET_STATISTICS, 0, + index, data, len ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not get statistics set %d: " + "%s\n", smscusb, index, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define SMSCUSB_INTR_MAX_FILL 2 + +extern struct usb_endpoint_driver_operations smscusb_intr_operations; +extern struct usb_endpoint_driver_operations smscusb_out_operations; +extern struct mii_operations smscusb_mii_operations; + +/** + * Initialise SMSC USB device + * + * @v smscusb SMSC USB device + * @v netdev Network device + * @v func USB function + * @v in Bulk IN endpoint operations + */ +static inline __attribute__ (( always_inline )) void +smscusb_init ( struct smscusb_device *smscusb, struct net_device *netdev, + struct usb_function *func, + struct usb_endpoint_driver_operations *in ) { + struct usb_device *usb = func->usb; + + smscusb->usb = usb; + smscusb->bus = usb->port->hub->bus; + smscusb->netdev = netdev; + usbnet_init ( &smscusb->usbnet, func, &smscusb_intr_operations, in, + &smscusb_out_operations ); + usb_refill_init ( &smscusb->usbnet.intr, 0, 0, SMSCUSB_INTR_MAX_FILL ); +} + +/** + * Initialise SMSC USB device MII interface + * + * @v smscusb SMSC USB device + * @v mii_base MII register base + */ +static inline __attribute__ (( always_inline )) void +smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base ) { + + mii_init ( &smscusb->mii, &smscusb_mii_operations ); + smscusb->mii_base = mii_base; +} + +extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, + unsigned int e2p_base ); +extern int smscusb_mii_check_link ( struct smscusb_device *smscusb ); +extern int smscusb_mii_open ( struct smscusb_device *smscusb ); +extern int smscusb_set_address ( struct smscusb_device *smscusb, + unsigned int addr_base ); +extern int smscusb_set_filter ( struct smscusb_device *smscusb, + unsigned int filt_base ); + +#endif /* _SMSCUSB_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index dc3de0518..6da1a4505 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -200,6 +200,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_sfc_hunt ( ERRFILE_DRIVER | 0x00c40000 ) #define ERRFILE_efx_hunt ( ERRFILE_DRIVER | 0x00c50000 ) #define ERRFILE_exanic ( ERRFILE_DRIVER | 0x00c60000 ) +#define ERRFILE_smscusb ( ERRFILE_DRIVER | 0x00c70000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 550e0d8353ec3d4ba50bcb0eb3c5e6f5cf999806 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 Jul 2017 17:25:14 +0100 Subject: [PATCH 484/591] [smsc95xx] Use common SMSC USB device functionality Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 804 +++++++------------------------------ src/drivers/net/smsc95xx.h | 115 +----- 2 files changed, 141 insertions(+), 778 deletions(-) diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index 3d9c0f1aa..e56cf5b45 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -41,10 +41,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Interrupt completion profiler */ -static struct profiler smsc95xx_intr_profiler __profiler = - { .name = "smsc95xx.intr" }; - /** Bulk IN completion profiler */ static struct profiler smsc95xx_in_profiler __profiler = { .name = "smsc95xx.in" }; @@ -53,204 +49,6 @@ static struct profiler smsc95xx_in_profiler __profiler = static struct profiler smsc95xx_out_profiler __profiler = { .name = "smsc95xx.out" }; -/****************************************************************************** - * - * Register access - * - ****************************************************************************** - */ - -/** - * Write register (without byte-swapping) - * - * @v smsc95xx SMSC95xx device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static int smsc95xx_raw_writel ( struct smsc95xx_device *smsc95xx, - unsigned int address, uint32_t value ) { - int rc; - - /* Write register */ - DBGCIO ( smsc95xx, "SMSC95XX %p [%03x] <= %08x\n", - smsc95xx, address, le32_to_cpu ( value ) ); - if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_REGISTER_WRITE, 0, - address, &value, sizeof ( value ) ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not write %03x: %s\n", - smsc95xx, address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Write register - * - * @v smsc95xx SMSC95xx device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -smsc95xx_writel ( struct smsc95xx_device *smsc95xx, unsigned int address, - uint32_t value ) { - int rc; - - /* Write register */ - if ( ( rc = smsc95xx_raw_writel ( smsc95xx, address, - cpu_to_le32 ( value ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Read register (without byte-swapping) - * - * @v smsc95xx SMSC95xx device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static int smsc95xx_raw_readl ( struct smsc95xx_device *smsc95xx, - unsigned int address, uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_REGISTER_READ, 0, - address, value, sizeof ( *value ) ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not read %03x: %s\n", - smsc95xx, address, strerror ( rc ) ); - return rc; - } - DBGCIO ( smsc95xx, "SMSC95XX %p [%03x] => %08x\n", - smsc95xx, address, le32_to_cpu ( *value ) ); - - return 0; -} - -/** - * Read register - * - * @v smsc95xx SMSC95xx device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -smsc95xx_readl ( struct smsc95xx_device *smsc95xx, unsigned int address, - uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = smsc95xx_raw_readl ( smsc95xx, address, value ) ) != 0 ) - return rc; - le32_to_cpus ( value ); - - return 0; -} - -/****************************************************************************** - * - * EEPROM access - * - ****************************************************************************** - */ - -/** - * Wait for EEPROM to become idle - * - * @v smsc95xx SMSC95xx device - * @ret rc Return status code - */ -static int smsc95xx_eeprom_wait ( struct smsc95xx_device *smsc95xx ) { - uint32_t e2p_cmd; - unsigned int i; - int rc; - - /* Wait for EPC_BSY to become clear */ - for ( i = 0 ; i < SMSC95XX_EEPROM_MAX_WAIT_MS ; i++ ) { - - /* Read E2P_CMD and check EPC_BSY */ - if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_E2P_CMD, - &e2p_cmd ) ) != 0 ) - return rc; - if ( ! ( e2p_cmd & SMSC95XX_E2P_CMD_EPC_BSY ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( smsc95xx, "SMSC95XX %p timed out waiting for EEPROM\n", - smsc95xx ); - return -ETIMEDOUT; -} - -/** - * Read byte from EEPROM - * - * @v smsc95xx SMSC95xx device - * @v address EEPROM address - * @ret byte Byte read, or negative error - */ -static int smsc95xx_eeprom_read_byte ( struct smsc95xx_device *smsc95xx, - unsigned int address ) { - uint32_t e2p_cmd; - uint32_t e2p_data; - int rc; - - /* Wait for EEPROM to become idle */ - if ( ( rc = smsc95xx_eeprom_wait ( smsc95xx ) ) != 0 ) - return rc; - - /* Initiate read command */ - e2p_cmd = ( SMSC95XX_E2P_CMD_EPC_BSY | SMSC95XX_E2P_CMD_EPC_CMD_READ | - SMSC95XX_E2P_CMD_EPC_ADDR ( address ) ); - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_E2P_CMD, - e2p_cmd ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc95xx_eeprom_wait ( smsc95xx ) ) != 0 ) - return rc; - - /* Read EEPROM data */ - if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_E2P_DATA, - &e2p_data ) ) != 0 ) - return rc; - - return SMSC95XX_E2P_DATA_GET ( e2p_data ); -} - -/** - * Read data from EEPROM - * - * @v smsc95xx SMSC95xx device - * @v address EEPROM address - * @v data Data buffer - * @v len Length of data - * @ret rc Return status code - */ -static int smsc95xx_eeprom_read ( struct smsc95xx_device *smsc95xx, - unsigned int address, void *data, - size_t len ) { - uint8_t *bytes; - int byte; - - /* Read bytes */ - for ( bytes = data ; len-- ; address++, bytes++ ) { - byte = smsc95xx_eeprom_read_byte ( smsc95xx, address ); - if ( byte < 0 ) - return byte; - *bytes = byte; - } - - return 0; -} - /****************************************************************************** * * MAC address @@ -258,43 +56,14 @@ static int smsc95xx_eeprom_read ( struct smsc95xx_device *smsc95xx, ****************************************************************************** */ -/** - * Fetch MAC address from EEPROM - * - * @v smsc95xx SMSC95xx device - * @v hw_addr Hardware address to fill in - * @ret rc Return status code - */ -static int smsc95xx_fetch_mac_eeprom ( struct smsc95xx_device *smsc95xx, - uint8_t *hw_addr ) { - int rc; - - /* Read MAC address from EEPROM */ - if ( ( rc = smsc95xx_eeprom_read ( smsc95xx, SMSC95XX_EEPROM_MAC, - hw_addr, ETH_ALEN ) ) != 0 ) - return rc; - - /* Check that EEPROM is physically present */ - if ( ! is_valid_ether_addr ( hw_addr ) ) { - DBGC ( smsc95xx, "SMSC95XX %p has no EEPROM (%s)\n", - smsc95xx, eth_ntoa ( hw_addr ) ); - return -ENODEV; - } - - DBGC ( smsc95xx, "SMSC95XX %p using EEPROM MAC %s\n", - smsc95xx, eth_ntoa ( hw_addr ) ); - return 0; -} - /** * Construct MAC address for Honeywell VM3 * - * @v smsc95xx SMSC95xx device - * @v hw_addr Hardware address to fill in + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, - uint8_t *hw_addr ) { +static int smsc95xx_vm3_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; struct smbios_structure structure; struct smbios_system_information system; struct { @@ -308,16 +77,16 @@ static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, /* Find system information */ if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_SYSTEM_INFORMATION, 0, &structure ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not find system " - "information: %s\n", smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p could not find system " + "information: %s\n", smscusb, strerror ( rc ) ); return rc; } /* Read system information */ if ( ( rc = read_smbios_structure ( &structure, &system, sizeof ( system ) ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not read system " - "information: %s\n", smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p could not read system " + "information: %s\n", smscusb, strerror ( rc ) ); return rc; } @@ -330,8 +99,8 @@ static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, ( sizeof ( strings.manufacturer ) - 1 ) ); if ( len < 0 ) { rc = len; - DBGC ( smsc95xx, "SMSC95XX %p could not read manufacturer " - "name: %s\n", smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p could not read manufacturer " + "name: %s\n", smscusb, strerror ( rc ) ); return rc; } @@ -340,8 +109,8 @@ static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, ( sizeof ( strings.product ) - 1 ) ); if ( len < 0 ) { rc = len; - DBGC ( smsc95xx, "SMSC95XX %p could not read product name: " - "%s\n", smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p could not read product name: " + "%s\n", smscusb, strerror ( rc ) ); return rc; } @@ -353,8 +122,8 @@ static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, /* Find OEM strings */ if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_OEM_STRINGS, 0, &structure ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not find OEM strings: %s\n", - smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p could not find OEM strings: %s\n", + smscusb, strerror ( rc ) ); return rc; } @@ -363,218 +132,55 @@ static int smsc95xx_fetch_mac_vm3 ( struct smsc95xx_device *smsc95xx, strings.mac, ( sizeof ( strings.mac ) - 1 )); if ( len < 0 ) { rc = len; - DBGC ( smsc95xx, "SMSC95XX %p could not read OEM string: %s\n", - smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p could not read OEM string: %s\n", + smscusb, strerror ( rc ) ); return rc; } /* Sanity check */ if ( len != ( ( int ) ( sizeof ( strings.mac ) - 1 ) ) ) { - DBGC ( smsc95xx, "SMSC95XX %p invalid MAC address \"%s\"\n", - smsc95xx, strings.mac ); + DBGC ( smscusb, "SMSC95XX %p invalid MAC address \"%s\"\n", + smscusb, strings.mac ); return -EINVAL; } /* Decode MAC address */ - len = base16_decode ( strings.mac, hw_addr, ETH_ALEN ); + len = base16_decode ( strings.mac, netdev->hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; - DBGC ( smsc95xx, "SMSC95XX %p invalid MAC address \"%s\"\n", - smsc95xx, strings.mac ); + DBGC ( smscusb, "SMSC95XX %p invalid MAC address \"%s\"\n", + smscusb, strings.mac ); return rc; } - DBGC ( smsc95xx, "SMSC95XX %p using VM3 MAC %s\n", - smsc95xx, eth_ntoa ( hw_addr ) ); + DBGC ( smscusb, "SMSC95XX %p using VM3 MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); return 0; } /** * Fetch MAC address * - * @v smsc95xx SMSC95xx device - * @v hw_addr Hardware address to fill in + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc95xx_fetch_mac ( struct smsc95xx_device *smsc95xx, - uint8_t *hw_addr ) { +static int smsc95xx_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; int rc; /* Read MAC address from EEPROM, if present */ - if ( ( rc = smsc95xx_fetch_mac_eeprom ( smsc95xx, hw_addr ) ) == 0 ) + if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb, + SMSC95XX_E2P_BASE ) ) == 0 ) return 0; /* Construct MAC address for Honeywell VM3, if applicable */ - if ( ( rc = smsc95xx_fetch_mac_vm3 ( smsc95xx, hw_addr ) ) == 0 ) + if ( ( rc = smsc95xx_vm3_fetch_mac ( smscusb ) ) == 0 ) return 0; /* Otherwise, generate a random MAC address */ - eth_random_addr ( hw_addr ); - DBGC ( smsc95xx, "SMSC95XX %p using random MAC %s\n", - smsc95xx, eth_ntoa ( hw_addr ) ); - return 0; -} - -/****************************************************************************** - * - * MII access - * - ****************************************************************************** - */ - -/** - * Wait for MII to become idle - * - * @v smsc95xx SMSC95xx device - * @ret rc Return status code - */ -static int smsc95xx_mii_wait ( struct smsc95xx_device *smsc95xx ) { - uint32_t mii_access; - unsigned int i; - int rc; - - /* Wait for MIIBZY to become clear */ - for ( i = 0 ; i < SMSC95XX_MII_MAX_WAIT_MS ; i++ ) { - - /* Read MII_ACCESS and check MIIBZY */ - if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_MII_ACCESS, - &mii_access ) ) != 0 ) - return rc; - if ( ! ( mii_access & SMSC95XX_MII_ACCESS_MIIBZY ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( smsc95xx, "SMSC95XX %p timed out waiting for MII\n", - smsc95xx ); - return -ETIMEDOUT; -} - -/** - * Read from MII register - * - * @v mii MII interface - * @v reg Register address - * @ret value Data read, or negative error - */ -static int smsc95xx_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct smsc95xx_device *smsc95xx = - container_of ( mii, struct smsc95xx_device, mii ); - uint32_t mii_access; - uint32_t mii_data; - int rc; - - /* Wait for MII to become idle */ - if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) - return rc; - - /* Initiate read command */ - mii_access = ( SMSC95XX_MII_ACCESS_PHY_ADDRESS | - SMSC95XX_MII_ACCESS_MIIRINDA ( reg ) | - SMSC95XX_MII_ACCESS_MIIBZY ); - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MII_ACCESS, - mii_access ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) - return rc; - - /* Read MII data */ - if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_MII_DATA, - &mii_data ) ) != 0 ) - return rc; - - return SMSC95XX_MII_DATA_GET ( mii_data ); -} - -/** - * Write to MII register - * - * @v mii MII interface - * @v reg Register address - * @v data Data to write - * @ret rc Return status code - */ -static int smsc95xx_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data ) { - struct smsc95xx_device *smsc95xx = - container_of ( mii, struct smsc95xx_device, mii ); - uint32_t mii_access; - uint32_t mii_data; - int rc; - - /* Wait for MII to become idle */ - if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) - return rc; - - /* Write MII data */ - mii_data = SMSC95XX_MII_DATA_SET ( data ); - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MII_DATA, - mii_data ) ) != 0 ) - return rc; - - /* Initiate write command */ - mii_access = ( SMSC95XX_MII_ACCESS_PHY_ADDRESS | - SMSC95XX_MII_ACCESS_MIIRINDA ( reg ) | - SMSC95XX_MII_ACCESS_MIIWNR | - SMSC95XX_MII_ACCESS_MIIBZY ); - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MII_ACCESS, - mii_access ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc95xx_mii_wait ( smsc95xx ) ) != 0 ) - return rc; - - return 0; -} - -/** MII operations */ -static struct mii_operations smsc95xx_mii_operations = { - .read = smsc95xx_mii_read, - .write = smsc95xx_mii_write, -}; - -/** - * Check link status - * - * @v smsc95xx SMSC95xx device - * @ret rc Return status code - */ -static int smsc95xx_check_link ( struct smsc95xx_device *smsc95xx ) { - struct net_device *netdev = smsc95xx->netdev; - int intr; - int rc; - - /* Read PHY interrupt source */ - intr = mii_read ( &smsc95xx->mii, SMSC95XX_MII_PHY_INTR_SOURCE ); - if ( intr < 0 ) { - rc = intr; - DBGC ( smsc95xx, "SMSC95XX %p could not get PHY interrupt " - "source: %s\n", smsc95xx, strerror ( rc ) ); - return rc; - } - - /* Acknowledge PHY interrupt */ - if ( ( rc = mii_write ( &smsc95xx->mii, SMSC95XX_MII_PHY_INTR_SOURCE, - intr ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not acknowledge PHY " - "interrupt: %s\n", smsc95xx, strerror ( rc ) ); - return rc; - } - - /* Check link status */ - if ( ( rc = mii_check_link ( &smsc95xx->mii, netdev ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not check link: %s\n", - smsc95xx, strerror ( rc ) ); - return rc; - } - - DBGC ( smsc95xx, "SMSC95XX %p link %s (intr %#04x)\n", - smsc95xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); + eth_random_addr ( netdev->hw_addr ); + DBGC ( smscusb, "SMSC95XX %p using random MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); return 0; } @@ -585,59 +191,13 @@ static int smsc95xx_check_link ( struct smsc95xx_device *smsc95xx ) { ****************************************************************************** */ -/** - * Get RX statistics - * - * @v smsc95xx SMSC95xx device - * @v stats Statistics to fill in - * @ret rc Return status code - */ -static int smsc95xx_get_rx_statistics ( struct smsc95xx_device *smsc95xx, - struct smsc95xx_rx_statistics *stats ) { - int rc; - - /* Get statistics */ - if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_GET_STATISTICS, 0, - SMSC95XX_RX_STATISTICS, stats, - sizeof ( *stats ) ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not get RX statistics: " - "%s\n", smsc95xx, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Get TX statistics - * - * @v smsc95xx SMSC95xx device - * @v stats Statistics to fill in - * @ret rc Return status code - */ -static int smsc95xx_get_tx_statistics ( struct smsc95xx_device *smsc95xx, - struct smsc95xx_tx_statistics *stats ) { - int rc; - - /* Get statistics */ - if ( ( rc = usb_control ( smsc95xx->usb, SMSC95XX_GET_STATISTICS, 0, - SMSC95XX_TX_STATISTICS, stats, - sizeof ( *stats ) ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not get TX statistics: " - "%s\n", smsc95xx, strerror ( rc ) ); - return rc; - } - - return 0; -} - /** * Dump statistics (for debugging) * - * @v smsc95xx SMSC95xx device + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc95xx_dump_statistics ( struct smsc95xx_device *smsc95xx ) { +static int smsc95xx_dump_statistics ( struct smscusb_device *smscusb ) { struct smsc95xx_rx_statistics rx; struct smsc95xx_tx_statistics tx; int rc; @@ -647,22 +207,30 @@ static int smsc95xx_dump_statistics ( struct smsc95xx_device *smsc95xx ) { return 0; /* Get RX statistics */ - if ( ( rc = smsc95xx_get_rx_statistics ( smsc95xx, &rx ) ) != 0 ) + if ( ( rc = smscusb_get_statistics ( smscusb, SMSC95XX_RX_STATISTICS, + &rx, sizeof ( rx ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not get RX statistics: " + "%s\n", smscusb, strerror ( rc ) ); return rc; + } /* Get TX statistics */ - if ( ( rc = smsc95xx_get_tx_statistics ( smsc95xx, &tx ) ) != 0 ) + if ( ( rc = smscusb_get_statistics ( smscusb, SMSC95XX_TX_STATISTICS, + &tx, sizeof ( tx ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not get TX statistics: " + "%s\n", smscusb, strerror ( rc ) ); return rc; + } /* Dump statistics */ - DBGC ( smsc95xx, "SMSC95XX %p RX good %d bad %d crc %d und %d aln %d " - "ovr %d lat %d drp %d\n", smsc95xx, le32_to_cpu ( rx.good ), + DBGC ( smscusb, "SMSC95XX %p RX good %d bad %d crc %d und %d aln %d " + "ovr %d lat %d drp %d\n", smscusb, le32_to_cpu ( rx.good ), le32_to_cpu ( rx.bad ), le32_to_cpu ( rx.crc ), le32_to_cpu ( rx.undersize ), le32_to_cpu ( rx.alignment ), le32_to_cpu ( rx.oversize ), le32_to_cpu ( rx.late ), le32_to_cpu ( rx.dropped ) ); - DBGC ( smsc95xx, "SMSC95XX %p TX good %d bad %d pau %d sgl %d mul %d " - "exc %d lat %d und %d def %d car %d\n", smsc95xx, + DBGC ( smscusb, "SMSC95XX %p TX good %d bad %d pau %d sgl %d mul %d " + "exc %d lat %d und %d def %d car %d\n", smscusb, le32_to_cpu ( tx.good ), le32_to_cpu ( tx.bad ), le32_to_cpu ( tx.pause ), le32_to_cpu ( tx.single ), le32_to_cpu ( tx.multiple ), le32_to_cpu ( tx.excessive ), @@ -682,28 +250,27 @@ static int smsc95xx_dump_statistics ( struct smsc95xx_device *smsc95xx ) { /** * Reset device * - * @v smsc95xx SMSC95xx device + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc95xx_reset ( struct smsc95xx_device *smsc95xx ) { +static int smsc95xx_reset ( struct smscusb_device *smscusb ) { uint32_t hw_cfg; uint32_t led_gpio_cfg; int rc; /* Reset device */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_HW_CFG, - SMSC95XX_HW_CFG_LRST ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_HW_CFG, + SMSC95XX_HW_CFG_LRST ) ) != 0 ) return rc; /* Wait for reset to complete */ udelay ( SMSC95XX_RESET_DELAY_US ); /* Check that reset has completed */ - if ( ( rc = smsc95xx_readl ( smsc95xx, SMSC95XX_HW_CFG, - &hw_cfg ) ) != 0 ) + if ( ( rc = smscusb_readl ( smscusb, SMSC95XX_HW_CFG, &hw_cfg ) ) != 0 ) return rc; if ( hw_cfg & SMSC95XX_HW_CFG_LRST ) { - DBGC ( smsc95xx, "SMSC95XX %p failed to reset\n", smsc95xx ); + DBGC ( smscusb, "SMSC95XX %p failed to reset\n", smscusb ); return -ETIMEDOUT; } @@ -711,10 +278,10 @@ static int smsc95xx_reset ( struct smsc95xx_device *smsc95xx ) { led_gpio_cfg = ( SMSC95XX_LED_GPIO_CFG_GPCTL2_NSPD_LED | SMSC95XX_LED_GPIO_CFG_GPCTL1_NLNKA_LED | SMSC95XX_LED_GPIO_CFG_GPCTL0_NFDX_LED ); - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_LED_GPIO_CFG, - led_gpio_cfg ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not configure LEDs: %s\n", - smsc95xx, strerror ( rc ) ); + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_LED_GPIO_CFG, + led_gpio_cfg ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not configure LEDs: %s\n", + smscusb, strerror ( rc ) ); /* Ignore error and continue */ } @@ -728,60 +295,6 @@ static int smsc95xx_reset ( struct smsc95xx_device *smsc95xx ) { ****************************************************************************** */ -/** - * Complete interrupt transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void smsc95xx_intr_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct smsc95xx_device *smsc95xx = - container_of ( ep, struct smsc95xx_device, usbnet.intr ); - struct net_device *netdev = smsc95xx->netdev; - struct smsc95xx_interrupt *intr; - - /* Profile completions */ - profile_start ( &smsc95xx_intr_profiler ); - - /* Ignore packets cancelled when the endpoint closes */ - if ( ! ep->open ) - goto done; - - /* Record USB errors against the network device */ - if ( rc != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p interrupt failed: %s\n", - smsc95xx, strerror ( rc ) ); - DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); - netdev_rx_err ( netdev, NULL, rc ); - goto done; - } - - /* Extract interrupt data */ - if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { - DBGC ( smsc95xx, "SMSC95XX %p malformed interrupt\n", - smsc95xx ); - DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); - netdev_rx_err ( netdev, NULL, rc ); - goto done; - } - intr = iobuf->data; - - /* Record interrupt status */ - smsc95xx->int_sts = le32_to_cpu ( intr->int_sts ); - profile_stop ( &smsc95xx_intr_profiler ); - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); -} - -/** Interrupt endpoint operations */ -static struct usb_endpoint_driver_operations smsc95xx_intr_operations = { - .complete = smsc95xx_intr_complete, -}; - /** * Complete bulk IN transfer * @@ -791,9 +304,9 @@ static struct usb_endpoint_driver_operations smsc95xx_intr_operations = { */ static void smsc95xx_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { - struct smsc95xx_device *smsc95xx = - container_of ( ep, struct smsc95xx_device, usbnet.in ); - struct net_device *netdev = smsc95xx->netdev; + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.in ); + struct net_device *netdev = smscusb->netdev; struct smsc95xx_rx_header *header; /* Profile completions */ @@ -807,16 +320,16 @@ static void smsc95xx_in_complete ( struct usb_endpoint *ep, /* Record USB errors against the network device */ if ( rc != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p bulk IN failed: %s\n", - smsc95xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC95XX %p bulk IN failed: %s\n", + smscusb, strerror ( rc ) ); goto err; } /* Sanity check */ if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { - DBGC ( smsc95xx, "SMSC95XX %p underlength bulk IN\n", - smsc95xx ); - DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( smscusb, "SMSC95XX %p underlength bulk IN\n", + smscusb ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EINVAL; goto err; } @@ -830,9 +343,9 @@ static void smsc95xx_in_complete ( struct usb_endpoint *ep, if ( header->command & cpu_to_le32 ( SMSC95XX_RX_RUNT | SMSC95XX_RX_LATE | SMSC95XX_RX_CRC ) ) { - DBGC ( smsc95xx, "SMSC95XX %p receive error (%08x):\n", - smsc95xx, le32_to_cpu ( header->command ) ); - DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( smscusb, "SMSC95XX %p receive error (%08x):\n", + smscusb, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EIO; goto err; } @@ -856,11 +369,11 @@ static struct usb_endpoint_driver_operations smsc95xx_in_operations = { /** * Transmit packet * - * @v smsc95xx SMSC95xx device + * @v smscusb SMSC USB device * @v iobuf I/O buffer * @ret rc Return status code */ -static int smsc95xx_out_transmit ( struct smsc95xx_device *smsc95xx, +static int smsc95xx_out_transmit ( struct smscusb_device *smscusb, struct io_buffer *iobuf ) { struct smsc95xx_tx_header *header; size_t len = iob_len ( iobuf ); @@ -878,35 +391,13 @@ static int smsc95xx_out_transmit ( struct smsc95xx_device *smsc95xx, header->len = cpu_to_le32 ( len ); /* Enqueue I/O buffer */ - if ( ( rc = usb_stream ( &smsc95xx->usbnet.out, iobuf, 0 ) ) != 0 ) + if ( ( rc = usb_stream ( &smscusb->usbnet.out, iobuf, 0 ) ) != 0 ) return rc; profile_stop ( &smsc95xx_out_profiler ); return 0; } -/** - * Complete bulk OUT transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void smsc95xx_out_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct smsc95xx_device *smsc95xx = - container_of ( ep, struct smsc95xx_device, usbnet.out ); - struct net_device *netdev = smsc95xx->netdev; - - /* Report TX completion */ - netdev_tx_complete_err ( netdev, iobuf, rc ); -} - -/** Bulk OUT endpoint operations */ -static struct usb_endpoint_driver_operations smsc95xx_out_operations = { - .complete = smsc95xx_out_complete, -}; - /****************************************************************************** * * Network device interface @@ -921,91 +412,71 @@ static struct usb_endpoint_driver_operations smsc95xx_out_operations = { * @ret rc Return status code */ static int smsc95xx_open ( struct net_device *netdev ) { - struct smsc95xx_device *smsc95xx = netdev->priv; - union smsc95xx_mac mac; + struct smscusb_device *smscusb = netdev->priv; int rc; /* Clear stored interrupt status */ - smsc95xx->int_sts = 0; - - /* Copy MAC address */ - memset ( &mac, 0, sizeof ( mac ) ); - memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + smscusb->int_sts = 0; /* Configure bulk IN empty response */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_HW_CFG, - SMSC95XX_HW_CFG_BIR ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_HW_CFG, + SMSC95XX_HW_CFG_BIR ) ) != 0 ) goto err_hw_cfg; /* Open USB network device */ - if ( ( rc = usbnet_open ( &smsc95xx->usbnet ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not open: %s\n", - smsc95xx, strerror ( rc ) ); + if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not open: %s\n", + smscusb, strerror ( rc ) ); goto err_open; } /* Configure interrupt endpoint */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_INT_EP_CTL, - ( SMSC95XX_INT_EP_CTL_RXDF_EN | - SMSC95XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_INT_EP_CTL, + ( SMSC95XX_INT_EP_CTL_RXDF_EN | + SMSC95XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) goto err_int_ep_ctl; /* Configure bulk IN delay */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_BULK_IN_DLY, - SMSC95XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_BULK_IN_DLY, + SMSC95XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) goto err_bulk_in_dly; /* Configure MAC */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_MAC_CR, - ( SMSC95XX_MAC_CR_RXALL | - SMSC95XX_MAC_CR_FDPX | - SMSC95XX_MAC_CR_MCPAS | - SMSC95XX_MAC_CR_PRMS | - SMSC95XX_MAC_CR_PASSBAD | - SMSC95XX_MAC_CR_TXEN | - SMSC95XX_MAC_CR_RXEN ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_MAC_CR, + ( SMSC95XX_MAC_CR_RXALL | + SMSC95XX_MAC_CR_FDPX | + SMSC95XX_MAC_CR_MCPAS | + SMSC95XX_MAC_CR_PRMS | + SMSC95XX_MAC_CR_PASSBAD | + SMSC95XX_MAC_CR_TXEN | + SMSC95XX_MAC_CR_RXEN ) ) ) != 0 ) goto err_mac_cr; /* Configure transmit datapath */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_TX_CFG, - SMSC95XX_TX_CFG_ON ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_TX_CFG, + SMSC95XX_TX_CFG_ON ) ) != 0 ) goto err_tx_cfg; - /* Write MAC address high register */ - if ( ( rc = smsc95xx_raw_writel ( smsc95xx, SMSC95XX_ADDRH, - mac.addr.h ) ) != 0 ) - goto err_addrh; + /* Set MAC address */ + if ( ( rc = smscusb_set_address ( smscusb, SMSC95XX_ADDR_BASE ) ) != 0 ) + goto err_set_address; - /* Write MAC address low register */ - if ( ( rc = smsc95xx_raw_writel ( smsc95xx, SMSC95XX_ADDRL, - mac.addr.l ) ) != 0 ) - goto err_addrl; - - /* Enable PHY interrupts */ - if ( ( rc = mii_write ( &smsc95xx->mii, SMSC95XX_MII_PHY_INTR_MASK, - ( SMSC95XX_PHY_INTR_ANEG_DONE | - SMSC95XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not set PHY interrupt " - "mask: %s\n", smsc95xx, strerror ( rc ) ); - goto err_phy_intr_mask; - } - - /* Update link status */ - smsc95xx_check_link ( smsc95xx ); + /* Enable PHY interrupts and update link status */ + if ( ( rc = smscusb_mii_open ( smscusb ) ) != 0 ) + goto err_mii_open; return 0; - err_phy_intr_mask: - err_addrl: - err_addrh: + err_mii_open: + err_set_address: err_tx_cfg: err_mac_cr: err_bulk_in_dly: err_int_ep_ctl: - usbnet_close ( &smsc95xx->usbnet ); + usbnet_close ( &smscusb->usbnet ); err_open: err_hw_cfg: - smsc95xx_reset ( smsc95xx ); + smsc95xx_reset ( smscusb ); return rc; } @@ -1015,16 +486,16 @@ static int smsc95xx_open ( struct net_device *netdev ) { * @v netdev Network device */ static void smsc95xx_close ( struct net_device *netdev ) { - struct smsc95xx_device *smsc95xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; /* Close USB network device */ - usbnet_close ( &smsc95xx->usbnet ); + usbnet_close ( &smscusb->usbnet ); /* Dump statistics (for debugging) */ - smsc95xx_dump_statistics ( smsc95xx ); + smsc95xx_dump_statistics ( smscusb ); /* Reset device */ - smsc95xx_reset ( smsc95xx ); + smsc95xx_reset ( smscusb ); } /** @@ -1036,11 +507,11 @@ static void smsc95xx_close ( struct net_device *netdev ) { */ static int smsc95xx_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { - struct smsc95xx_device *smsc95xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; int rc; /* Transmit packet */ - if ( ( rc = smsc95xx_out_transmit ( smsc95xx, iobuf ) ) != 0 ) + if ( ( rc = smsc95xx_out_transmit ( smscusb, iobuf ) ) != 0 ) return rc; return 0; @@ -1052,48 +523,48 @@ static int smsc95xx_transmit ( struct net_device *netdev, * @v netdev Network device */ static void smsc95xx_poll ( struct net_device *netdev ) { - struct smsc95xx_device *smsc95xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; uint32_t int_sts; int rc; /* Poll USB bus */ - usb_poll ( smsc95xx->bus ); + usb_poll ( smscusb->bus ); /* Refill endpoints */ - if ( ( rc = usbnet_refill ( &smsc95xx->usbnet ) ) != 0 ) + if ( ( rc = usbnet_refill ( &smscusb->usbnet ) ) != 0 ) netdev_rx_err ( netdev, NULL, rc ); /* Do nothing more unless there are interrupts to handle */ - int_sts = smsc95xx->int_sts; + int_sts = smscusb->int_sts; if ( ! int_sts ) return; /* Check link status if applicable */ if ( int_sts & SMSC95XX_INT_STS_PHY_INT ) { - smsc95xx_check_link ( smsc95xx ); + smscusb_mii_check_link ( smscusb ); int_sts &= ~SMSC95XX_INT_STS_PHY_INT; } /* Record RX FIFO overflow if applicable */ if ( int_sts & SMSC95XX_INT_STS_RXDF_INT ) { - DBGC2 ( smsc95xx, "SMSC95XX %p RX FIFO overflowed\n", - smsc95xx ); + DBGC2 ( smscusb, "SMSC95XX %p RX FIFO overflowed\n", + smscusb ); netdev_rx_err ( netdev, NULL, -ENOBUFS ); int_sts &= ~SMSC95XX_INT_STS_RXDF_INT; } /* Check for unexpected interrupts */ if ( int_sts ) { - DBGC ( smsc95xx, "SMSC95XX %p unexpected interrupt %#08x\n", - smsc95xx, int_sts ); + DBGC ( smscusb, "SMSC95XX %p unexpected interrupt %#08x\n", + smscusb, int_sts ); netdev_rx_err ( netdev, NULL, -ENOTTY ); } /* Clear interrupts */ - if ( ( rc = smsc95xx_writel ( smsc95xx, SMSC95XX_INT_STS, - smsc95xx->int_sts ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_INT_STS, + smscusb->int_sts ) ) != 0 ) netdev_rx_err ( netdev, NULL, rc ); - smsc95xx->int_sts = 0; + smscusb->int_sts = 0; } /** SMSC95xx network device operations */ @@ -1120,48 +591,41 @@ static struct net_device_operations smsc95xx_operations = { */ static int smsc95xx_probe ( struct usb_function *func, struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; struct net_device *netdev; - struct smsc95xx_device *smsc95xx; + struct smscusb_device *smscusb; int rc; /* Allocate and initialise structure */ - netdev = alloc_etherdev ( sizeof ( *smsc95xx ) ); + netdev = alloc_etherdev ( sizeof ( *smscusb ) ); if ( ! netdev ) { rc = -ENOMEM; goto err_alloc; } netdev_init ( netdev, &smsc95xx_operations ); netdev->dev = &func->dev; - smsc95xx = netdev->priv; - memset ( smsc95xx, 0, sizeof ( *smsc95xx ) ); - smsc95xx->usb = usb; - smsc95xx->bus = usb->port->hub->bus; - smsc95xx->netdev = netdev; - usbnet_init ( &smsc95xx->usbnet, func, &smsc95xx_intr_operations, - &smsc95xx_in_operations, &smsc95xx_out_operations ); - usb_refill_init ( &smsc95xx->usbnet.intr, 0, 0, - SMSC95XX_INTR_MAX_FILL ); - usb_refill_init ( &smsc95xx->usbnet.in, + smscusb = netdev->priv; + memset ( smscusb, 0, sizeof ( *smscusb ) ); + smscusb_init ( smscusb, netdev, func, &smsc95xx_in_operations ); + smscusb_mii_init ( smscusb, SMSC95XX_MII_BASE ); + usb_refill_init ( &smscusb->usbnet.in, ( sizeof ( struct smsc95xx_tx_header ) - sizeof ( struct smsc95xx_rx_header ) ), SMSC95XX_IN_MTU, SMSC95XX_IN_MAX_FILL ); - mii_init ( &smsc95xx->mii, &smsc95xx_mii_operations ); - DBGC ( smsc95xx, "SMSC95XX %p on %s\n", smsc95xx, func->name ); + DBGC ( smscusb, "SMSC95XX %p on %s\n", smscusb, func->name ); /* Describe USB network device */ - if ( ( rc = usbnet_describe ( &smsc95xx->usbnet, config ) ) != 0 ) { - DBGC ( smsc95xx, "SMSC95XX %p could not describe: %s\n", - smsc95xx, strerror ( rc ) ); + if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not describe: %s\n", + smscusb, strerror ( rc ) ); goto err_describe; } /* Reset device */ - if ( ( rc = smsc95xx_reset ( smsc95xx ) ) != 0 ) + if ( ( rc = smsc95xx_reset ( smscusb ) ) != 0 ) goto err_reset; /* Read MAC address */ - if ( ( rc = smsc95xx_fetch_mac ( smsc95xx, netdev->hw_addr ) ) != 0 ) + if ( ( rc = smsc95xx_fetch_mac ( smscusb ) ) != 0 ) goto err_fetch_mac; /* Register network device */ diff --git a/src/drivers/net/smsc95xx.h b/src/drivers/net/smsc95xx.h index c2512e0ee..b0ecf6ee2 100644 --- a/src/drivers/net/smsc95xx.h +++ b/src/drivers/net/smsc95xx.h @@ -9,25 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include -#include -#include -#include - -/** Register write command */ -#define SMSC95XX_REGISTER_WRITE \ - ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa0 ) ) - -/** Register read command */ -#define SMSC95XX_REGISTER_READ \ - ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa1 ) ) - -/** Get statistics command */ -#define SMSC95XX_GET_STATISTICS \ - ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa2 ) ) +#include "smscusb.h" /** Interrupt status register */ #define SMSC95XX_INT_STS 0x008 @@ -55,19 +37,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC95XX_LED_GPIO_CFG_GPCTL0_NFDX_LED \ SMSC95XX_LED_GPIO_CFG_GPCTL0 ( 1 ) /**< Full-duplex LED */ -/** EEPROM command register */ -#define SMSC95XX_E2P_CMD 0x030 -#define SMSC95XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ -#define SMSC95XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ -#define SMSC95XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ - -/** EEPROM data register */ -#define SMSC95XX_E2P_DATA 0x034 -#define SMSC95XX_E2P_DATA_GET(e2p_data) \ - ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ - -/** MAC address EEPROM address */ -#define SMSC95XX_EEPROM_MAC 0x01 +/** EEPROM register base */ +#define SMSC95XX_E2P_BASE 0x030 /** Interrupt endpoint control register */ #define SMSC95XX_INT_EP_CTL 0x068 @@ -88,49 +59,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC95XX_MAC_CR_TXEN 0x00000008UL /**< TX enabled */ #define SMSC95XX_MAC_CR_RXEN 0x00000004UL /**< RX enabled */ -/** MAC address high register */ -#define SMSC95XX_ADDRH 0x104 +/** MAC address register base */ +#define SMSC95XX_ADDR_BASE 0x104 -/** MAC address low register */ -#define SMSC95XX_ADDRL 0x108 - -/** MII access register */ -#define SMSC95XX_MII_ACCESS 0x114 -#define SMSC95XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ -#define SMSC95XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ -#define SMSC95XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ -#define SMSC95XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ - -/** MII data register */ -#define SMSC95XX_MII_DATA 0x118 -#define SMSC95XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ -#define SMSC95XX_MII_DATA_GET(mii_data) \ - ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ - -/** PHY interrupt source MII register */ -#define SMSC95XX_MII_PHY_INTR_SOURCE 29 - -/** PHY interrupt mask MII register */ -#define SMSC95XX_MII_PHY_INTR_MASK 30 - -/** PHY interrupt: auto-negotiation complete */ -#define SMSC95XX_PHY_INTR_ANEG_DONE 0x0040 - -/** PHY interrupt: link down */ -#define SMSC95XX_PHY_INTR_LINK_DOWN 0x0010 - -/** MAC address */ -union smsc95xx_mac { - /** MAC receive address registers */ - struct { - /** MAC receive address low register */ - uint32_t l; - /** MAC receive address high register */ - uint32_t h; - } __attribute__ (( packed )) addr; - /** Raw MAC address */ - uint8_t raw[ETH_ALEN]; -}; +/** MII register base */ +#define SMSC95XX_MII_BASE 0x0114 /** Receive packet header */ struct smsc95xx_rx_header { @@ -164,12 +97,6 @@ struct smsc95xx_tx_header { /** Buffer size */ #define SMSC95XX_TX_LEN(len) ( (len) << 0 ) -/** Interrupt packet format */ -struct smsc95xx_interrupt { - /** Current value of INT_STS register */ - uint32_t int_sts; -} __attribute__ (( packed )); - /** Receive statistics */ struct smsc95xx_rx_statistics { /** Good frames */ @@ -220,37 +147,9 @@ struct smsc95xx_tx_statistics { /** Transmit statistics */ #define SMSC95XX_TX_STATISTICS 1 -/** A SMSC95xx network device */ -struct smsc95xx_device { - /** USB device */ - struct usb_device *usb; - /** USB bus */ - struct usb_bus *bus; - /** Network device */ - struct net_device *netdev; - /** USB network device */ - struct usbnet_device usbnet; - /** MII interface */ - struct mii_interface mii; - /** Interrupt status */ - uint32_t int_sts; -}; - /** Reset delay (in microseconds) */ #define SMSC95XX_RESET_DELAY_US 2 -/** Maximum time to wait for EEPROM (in milliseconds) */ -#define SMSC95XX_EEPROM_MAX_WAIT_MS 100 - -/** Maximum time to wait for MII (in milliseconds) */ -#define SMSC95XX_MII_MAX_WAIT_MS 100 - -/** Interrupt maximum fill level - * - * This is a policy decision. - */ -#define SMSC95XX_INTR_MAX_FILL 2 - /** Bulk IN maximum fill level * * This is a policy decision. From b1df34d7bd872b2259a77b5dfc46edfe1a8fe20f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 Jul 2017 17:10:31 +0100 Subject: [PATCH 485/591] [smsc75xx] Use common SMSC USB device functionality Signed-off-by: Michael Brown --- src/drivers/net/smsc75xx.c | 723 ++++++------------------------------- src/drivers/net/smsc75xx.h | 123 +------ 2 files changed, 126 insertions(+), 720 deletions(-) diff --git a/src/drivers/net/smsc75xx.c b/src/drivers/net/smsc75xx.c index 4ce98ac80..c04dc1a7f 100644 --- a/src/drivers/net/smsc75xx.c +++ b/src/drivers/net/smsc75xx.c @@ -39,10 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Interrupt completion profiler */ -static struct profiler smsc75xx_intr_profiler __profiler = - { .name = "smsc75xx.intr" }; - /** Bulk IN completion profiler */ static struct profiler smsc75xx_in_profiler __profiler = { .name = "smsc75xx.in" }; @@ -51,363 +47,6 @@ static struct profiler smsc75xx_in_profiler __profiler = static struct profiler smsc75xx_out_profiler __profiler = { .name = "smsc75xx.out" }; -/****************************************************************************** - * - * Register access - * - ****************************************************************************** - */ - -/** - * Write register (without byte-swapping) - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static int smsc75xx_raw_writel ( struct smsc75xx_device *smsc75xx, - unsigned int address, uint32_t value ) { - int rc; - - /* Write register */ - if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_WRITE, 0, - address, &value, sizeof ( value ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not write %03x: %s\n", - smsc75xx, address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Write register - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -smsc75xx_writel ( struct smsc75xx_device *smsc75xx, unsigned int address, - uint32_t value ) { - int rc; - - /* Write register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, address, - cpu_to_le32 ( value ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Read register (without byte-swapping) - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static int smsc75xx_raw_readl ( struct smsc75xx_device *smsc75xx, - unsigned int address, uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_READ, 0, - address, value, sizeof ( *value ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not read %03x: %s\n", - smsc75xx, address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Read register - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -smsc75xx_readl ( struct smsc75xx_device *smsc75xx, unsigned int address, - uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = smsc75xx_raw_readl ( smsc75xx, address, value ) ) != 0 ) - return rc; - le32_to_cpus ( value ); - - return 0; -} - -/****************************************************************************** - * - * EEPROM access - * - ****************************************************************************** - */ - -/** - * Wait for EEPROM to become idle - * - * @v smsc75xx SMSC75xx device - * @ret rc Return status code - */ -static int smsc75xx_eeprom_wait ( struct smsc75xx_device *smsc75xx ) { - uint32_t e2p_cmd; - unsigned int i; - int rc; - - /* Wait for EPC_BSY to become clear */ - for ( i = 0 ; i < SMSC75XX_EEPROM_MAX_WAIT_MS ; i++ ) { - - /* Read E2P_CMD and check EPC_BSY */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_CMD, - &e2p_cmd ) ) != 0 ) - return rc; - if ( ! ( e2p_cmd & SMSC75XX_E2P_CMD_EPC_BSY ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for EEPROM\n", - smsc75xx ); - return -ETIMEDOUT; -} - -/** - * Read byte from EEPROM - * - * @v smsc75xx SMSC75xx device - * @v address EEPROM address - * @ret byte Byte read, or negative error - */ -static int smsc75xx_eeprom_read_byte ( struct smsc75xx_device *smsc75xx, - unsigned int address ) { - uint32_t e2p_cmd; - uint32_t e2p_data; - int rc; - - /* Wait for EEPROM to become idle */ - if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Initiate read command */ - e2p_cmd = ( SMSC75XX_E2P_CMD_EPC_BSY | SMSC75XX_E2P_CMD_EPC_CMD_READ | - SMSC75XX_E2P_CMD_EPC_ADDR ( address ) ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_E2P_CMD, - e2p_cmd ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Read EEPROM data */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_DATA, - &e2p_data ) ) != 0 ) - return rc; - - return SMSC75XX_E2P_DATA_GET ( e2p_data ); -} - -/** - * Read data from EEPROM - * - * @v smsc75xx SMSC75xx device - * @v address EEPROM address - * @v data Data buffer - * @v len Length of data - * @ret rc Return status code - */ -static int smsc75xx_eeprom_read ( struct smsc75xx_device *smsc75xx, - unsigned int address, void *data, - size_t len ) { - uint8_t *bytes; - int byte; - - /* Read bytes */ - for ( bytes = data ; len-- ; address++, bytes++ ) { - byte = smsc75xx_eeprom_read_byte ( smsc75xx, address ); - if ( byte < 0 ) - return byte; - *bytes = byte; - } - - return 0; -} - -/****************************************************************************** - * - * MII access - * - ****************************************************************************** - */ - -/** - * Wait for MII to become idle - * - * @v smsc75xx SMSC75xx device - * @ret rc Return status code - */ -static int smsc75xx_mii_wait ( struct smsc75xx_device *smsc75xx ) { - uint32_t mii_access; - unsigned int i; - int rc; - - /* Wait for MIIBZY to become clear */ - for ( i = 0 ; i < SMSC75XX_MII_MAX_WAIT_MS ; i++ ) { - - /* Read MII_ACCESS and check MIIBZY */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_ACCESS, - &mii_access ) ) != 0 ) - return rc; - if ( ! ( mii_access & SMSC75XX_MII_ACCESS_MIIBZY ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for MII\n", - smsc75xx ); - return -ETIMEDOUT; -} - -/** - * Read from MII register - * - * @v mii MII interface - * @v reg Register address - * @ret value Data read, or negative error - */ -static int smsc75xx_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct smsc75xx_device *smsc75xx = - container_of ( mii, struct smsc75xx_device, mii ); - uint32_t mii_access; - uint32_t mii_data; - int rc; - - /* Wait for MII to become idle */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Initiate read command */ - mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | - SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | - SMSC75XX_MII_ACCESS_MIIBZY ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, - mii_access ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Read MII data */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_DATA, - &mii_data ) ) != 0 ) - return rc; - - return SMSC75XX_MII_DATA_GET ( mii_data ); -} - -/** - * Write to MII register - * - * @v mii MII interface - * @v reg Register address - * @v data Data to write - * @ret rc Return status code - */ -static int smsc75xx_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data ) { - struct smsc75xx_device *smsc75xx = - container_of ( mii, struct smsc75xx_device, mii ); - uint32_t mii_access; - uint32_t mii_data; - int rc; - - /* Wait for MII to become idle */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Write MII data */ - mii_data = SMSC75XX_MII_DATA_SET ( data ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_DATA, - mii_data ) ) != 0 ) - return rc; - - /* Initiate write command */ - mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | - SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | - SMSC75XX_MII_ACCESS_MIIWNR | - SMSC75XX_MII_ACCESS_MIIBZY ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, - mii_access ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - return 0; -} - -/** MII operations */ -static struct mii_operations smsc75xx_mii_operations = { - .read = smsc75xx_mii_read, - .write = smsc75xx_mii_write, -}; - -/** - * Check link status - * - * @v smsc75xx SMSC75xx device - * @ret rc Return status code - */ -static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) { - struct net_device *netdev = smsc75xx->netdev; - int intr; - int rc; - - /* Read PHY interrupt source */ - intr = mii_read ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE ); - if ( intr < 0 ) { - rc = intr; - DBGC ( smsc75xx, "SMSC75XX %p could not get PHY interrupt " - "source: %s\n", smsc75xx, strerror ( rc ) ); - return rc; - } - - /* Acknowledge PHY interrupt */ - if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE, - intr ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not acknowledge PHY " - "interrupt: %s\n", smsc75xx, strerror ( rc ) ); - return rc; - } - - /* Check link status */ - if ( ( rc = mii_check_link ( &smsc75xx->mii, netdev ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not check link: %s\n", - smsc75xx, strerror ( rc ) ); - return rc; - } - - DBGC ( smsc75xx, "SMSC75XX %p link %s (intr %#04x)\n", - smsc75xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); - return 0; -} - /****************************************************************************** * * Statistics (for debugging) @@ -415,35 +54,13 @@ static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) { ****************************************************************************** */ -/** - * Get statistics - * - * @v smsc75xx SMSC75xx device - * @v stats Statistics to fill in - * @ret rc Return status code - */ -static int smsc75xx_get_statistics ( struct smsc75xx_device *smsc75xx, - struct smsc75xx_statistics *stats ) { - int rc; - - /* Get statistics */ - if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_GET_STATISTICS, 0, 0, - stats, sizeof ( *stats ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not get statistics: %s\n", - smsc75xx, strerror ( rc ) ); - return rc; - } - - return 0; -} - /** * Dump statistics (for debugging) * - * @v smsc75xx SMSC75xx device + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { +static int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ) { struct smsc75xx_statistics stats; int rc; @@ -452,29 +69,33 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { return 0; /* Get statistics */ - if ( ( rc = smsc75xx_get_statistics ( smsc75xx, &stats ) ) != 0 ) + if ( ( rc = smscusb_get_statistics ( smscusb, 0, &stats, + sizeof ( stats ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC75XX %p could not get statistics: " + "%s\n", smscusb, strerror ( rc ) ); return rc; + } /* Dump statistics */ - DBGC ( smsc75xx, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d " - "ovr %d drp %d\n", smsc75xx, le32_to_cpu ( stats.rx.err.fcs ), + DBGC ( smscusb, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d " + "ovr %d drp %d\n", smscusb, le32_to_cpu ( stats.rx.err.fcs ), le32_to_cpu ( stats.rx.err.alignment ), le32_to_cpu ( stats.rx.err.fragment ), le32_to_cpu ( stats.rx.err.jabber ), le32_to_cpu ( stats.rx.err.undersize ), le32_to_cpu ( stats.rx.err.oversize ), le32_to_cpu ( stats.rx.err.dropped ) ); - DBGC ( smsc75xx, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n", - smsc75xx, le32_to_cpu ( stats.rx.byte.unicast ), + DBGC ( smscusb, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n", + smscusb, le32_to_cpu ( stats.rx.byte.unicast ), le32_to_cpu ( stats.rx.byte.broadcast ), le32_to_cpu ( stats.rx.byte.multicast ) ); - DBGC ( smsc75xx, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause " - "%d\n", smsc75xx, le32_to_cpu ( stats.rx.frame.unicast ), + DBGC ( smscusb, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause " + "%d\n", smscusb, le32_to_cpu ( stats.rx.frame.unicast ), le32_to_cpu ( stats.rx.frame.broadcast ), le32_to_cpu ( stats.rx.frame.multicast ), le32_to_cpu ( stats.rx.frame.pause ) ); - DBGC ( smsc75xx, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d " - "mul %d exc %d lat %d\n", smsc75xx, + DBGC ( smscusb, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d " + "mul %d exc %d lat %d\n", smscusb, le32_to_cpu ( stats.tx.err.fcs ), le32_to_cpu ( stats.tx.err.deferral ), le32_to_cpu ( stats.tx.err.carrier ), @@ -483,12 +104,12 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { le32_to_cpu ( stats.tx.err.multiple ), le32_to_cpu ( stats.tx.err.excessive ), le32_to_cpu ( stats.tx.err.late ) ); - DBGC ( smsc75xx, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n", - smsc75xx, le32_to_cpu ( stats.tx.byte.unicast ), + DBGC ( smscusb, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n", + smscusb, le32_to_cpu ( stats.tx.byte.unicast ), le32_to_cpu ( stats.tx.byte.broadcast ), le32_to_cpu ( stats.tx.byte.multicast ) ); - DBGC ( smsc75xx, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause " - "%d\n", smsc75xx, le32_to_cpu ( stats.tx.frame.unicast ), + DBGC ( smscusb, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause " + "%d\n", smscusb, le32_to_cpu ( stats.tx.frame.unicast ), le32_to_cpu ( stats.tx.frame.broadcast ), le32_to_cpu ( stats.tx.frame.multicast ), le32_to_cpu ( stats.tx.frame.pause ) ); @@ -506,25 +127,25 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { /** * Reset device * - * @v smsc75xx SMSC75xx device + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { +static int smsc75xx_reset ( struct smscusb_device *smscusb ) { uint32_t hw_cfg; unsigned int i; int rc; /* Reset device */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, - SMSC75XX_HW_CFG_LRST ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_LRST ) ) != 0 ) return rc; /* Wait for reset to complete */ for ( i = 0 ; i < SMSC75XX_RESET_MAX_WAIT_MS ; i++ ) { /* Check if reset has completed */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG, - &hw_cfg ) ) != 0 ) + if ( ( rc = smscusb_readl ( smscusb, SMSC75XX_HW_CFG, + &hw_cfg ) ) != 0 ) return rc; if ( ! ( hw_cfg & SMSC75XX_HW_CFG_LRST ) ) return 0; @@ -533,8 +154,8 @@ static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { mdelay ( 1 ); } - DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for reset\n", - smsc75xx ); + DBGC ( smscusb, "SMSC75XX %p timed out waiting for reset\n", + smscusb ); return -ETIMEDOUT; } @@ -545,60 +166,6 @@ static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { ****************************************************************************** */ -/** - * Complete interrupt transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void smsc75xx_intr_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct smsc75xx_device *smsc75xx = - container_of ( ep, struct smsc75xx_device, usbnet.intr ); - struct net_device *netdev = smsc75xx->netdev; - struct smsc75xx_interrupt *intr; - - /* Profile completions */ - profile_start ( &smsc75xx_intr_profiler ); - - /* Ignore packets cancelled when the endpoint closes */ - if ( ! ep->open ) - goto done; - - /* Record USB errors against the network device */ - if ( rc != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p interrupt failed: %s\n", - smsc75xx, strerror ( rc ) ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); - netdev_rx_err ( netdev, NULL, rc ); - goto done; - } - - /* Extract interrupt data */ - if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { - DBGC ( smsc75xx, "SMSC75XX %p malformed interrupt\n", - smsc75xx ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); - netdev_rx_err ( netdev, NULL, rc ); - goto done; - } - intr = iobuf->data; - - /* Record interrupt status */ - smsc75xx->int_sts = le32_to_cpu ( intr->int_sts ); - profile_stop ( &smsc75xx_intr_profiler ); - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); -} - -/** Interrupt endpoint operations */ -static struct usb_endpoint_driver_operations smsc75xx_intr_operations = { - .complete = smsc75xx_intr_complete, -}; - /** * Complete bulk IN transfer * @@ -608,9 +175,9 @@ static struct usb_endpoint_driver_operations smsc75xx_intr_operations = { */ static void smsc75xx_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { - struct smsc75xx_device *smsc75xx = - container_of ( ep, struct smsc75xx_device, usbnet.in ); - struct net_device *netdev = smsc75xx->netdev; + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.in ); + struct net_device *netdev = smscusb->netdev; struct smsc75xx_rx_header *header; /* Profile completions */ @@ -624,16 +191,16 @@ static void smsc75xx_in_complete ( struct usb_endpoint *ep, /* Record USB errors against the network device */ if ( rc != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p bulk IN failed: %s\n", - smsc75xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC75XX %p bulk IN failed: %s\n", + smscusb, strerror ( rc ) ); goto err; } /* Sanity check */ if ( iob_len ( iobuf ) < ( sizeof ( *header ) ) ) { - DBGC ( smsc75xx, "SMSC75XX %p underlength bulk IN\n", - smsc75xx ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( smscusb, "SMSC75XX %p underlength bulk IN\n", + smscusb ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EINVAL; goto err; } @@ -644,9 +211,9 @@ static void smsc75xx_in_complete ( struct usb_endpoint *ep, /* Check for errors */ if ( header->command & cpu_to_le32 ( SMSC75XX_RX_RED ) ) { - DBGC ( smsc75xx, "SMSC75XX %p receive error (%08x):\n", - smsc75xx, le32_to_cpu ( header->command ) ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( smscusb, "SMSC75XX %p receive error (%08x):\n", + smscusb, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EIO; goto err; } @@ -670,11 +237,11 @@ static struct usb_endpoint_driver_operations smsc75xx_in_operations = { /** * Transmit packet * - * @v smsc75xx SMSC75xx device + * @v smscusb SMSC USB device * @v iobuf I/O buffer * @ret rc Return status code */ -static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx, +static int smsc75xx_out_transmit ( struct smscusb_device *smscusb, struct io_buffer *iobuf ) { struct smsc75xx_tx_header *header; size_t len = iob_len ( iobuf ); @@ -692,35 +259,13 @@ static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx, header->mss = 0; /* Enqueue I/O buffer */ - if ( ( rc = usb_stream ( &smsc75xx->usbnet.out, iobuf, 0 ) ) != 0 ) + if ( ( rc = usb_stream ( &smscusb->usbnet.out, iobuf, 0 ) ) != 0 ) return rc; profile_stop ( &smsc75xx_out_profiler ); return 0; } -/** - * Complete bulk OUT transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void smsc75xx_out_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct smsc75xx_device *smsc75xx = - container_of ( ep, struct smsc75xx_device, usbnet.out ); - struct net_device *netdev = smsc75xx->netdev; - - /* Report TX completion */ - netdev_tx_complete_err ( netdev, iobuf, rc ); -} - -/** Bulk OUT endpoint operations */ -static struct usb_endpoint_driver_operations smsc75xx_out_operations = { - .complete = smsc75xx_out_complete, -}; - /****************************************************************************** * * Network device interface @@ -735,110 +280,84 @@ static struct usb_endpoint_driver_operations smsc75xx_out_operations = { * @ret rc Return status code */ static int smsc75xx_open ( struct net_device *netdev ) { - struct smsc75xx_device *smsc75xx = netdev->priv; - union smsc75xx_mac mac; + struct smscusb_device *smscusb = netdev->priv; int rc; /* Clear stored interrupt status */ - smsc75xx->int_sts = 0; - - /* Copy MAC address */ - memset ( &mac, 0, sizeof ( mac ) ); - memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + smscusb->int_sts = 0; /* Configure bulk IN empty response */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, - SMSC75XX_HW_CFG_BIR ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_BIR ) ) != 0 ) goto err_hw_cfg; /* Open USB network device */ - if ( ( rc = usbnet_open ( &smsc75xx->usbnet ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not open: %s\n", - smsc75xx, strerror ( rc ) ); + if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) { + DBGC ( smscusb, "SMSC75XX %p could not open: %s\n", + smscusb, strerror ( rc ) ); goto err_open; } /* Configure interrupt endpoint */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_EP_CTL, - ( SMSC75XX_INT_EP_CTL_RDFO_EN | - SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_INT_EP_CTL, + ( SMSC75XX_INT_EP_CTL_RDFO_EN | + SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) goto err_int_ep_ctl; /* Configure bulk IN delay */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_BULK_IN_DLY, - SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_BULK_IN_DLY, + SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) goto err_bulk_in_dly; /* Configure receive filters */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_RFE_CTL, - ( SMSC75XX_RFE_CTL_AB | - SMSC75XX_RFE_CTL_AM | - SMSC75XX_RFE_CTL_AU ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_RFE_CTL, + ( SMSC75XX_RFE_CTL_AB | + SMSC75XX_RFE_CTL_AM | + SMSC75XX_RFE_CTL_AU ) ) ) != 0 ) goto err_rfe_ctl; /* Configure receive FIFO */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_RX_CTL, - ( SMSC75XX_FCT_RX_CTL_EN | - SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_FCT_RX_CTL, + ( SMSC75XX_FCT_RX_CTL_EN | + SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 ) goto err_fct_rx_ctl; /* Configure transmit FIFO */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_TX_CTL, - SMSC75XX_FCT_TX_CTL_EN ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_FCT_TX_CTL, + SMSC75XX_FCT_TX_CTL_EN ) ) != 0 ) goto err_fct_tx_ctl; /* Configure receive datapath */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_RX, - ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT | - SMSC75XX_MAC_RX_FCS | - SMSC75XX_MAC_RX_EN ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_MAC_RX, + ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT | + SMSC75XX_MAC_RX_FCS | + SMSC75XX_MAC_RX_EN ) ) ) != 0 ) goto err_mac_rx; /* Configure transmit datapath */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_TX, - SMSC75XX_MAC_TX_EN ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_MAC_TX, + SMSC75XX_MAC_TX_EN ) ) != 0 ) goto err_mac_tx; - /* Write MAC address high register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRH, - mac.addr.h ) ) != 0 ) - goto err_rx_addrh; + /* Set MAC address */ + if ( ( rc = smscusb_set_address ( smscusb, + SMSC75XX_RX_ADDR_BASE ) ) != 0 ) + goto err_set_address; - /* Write MAC address low register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRL, - mac.addr.l ) ) != 0 ) - goto err_rx_addrl; + /* Set MAC address perfect filter */ + if ( ( rc = smscusb_set_filter ( smscusb, + SMSC75XX_ADDR_FILT_BASE ) ) != 0 ) + goto err_set_filter; - /* Write MAC address perfect filter high register */ - mac.addr.h |= cpu_to_le32 ( SMSC75XX_ADDR_FILTH_VALID ); - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTH ( 0 ), - mac.addr.h ) ) != 0 ) - goto err_addr_filth; - - /* Write MAC address perfect filter low register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTL ( 0 ), - mac.addr.l ) ) != 0 ) - goto err_addr_filtl; - - /* Enable PHY interrupts */ - if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_MASK, - ( SMSC75XX_PHY_INTR_ANEG_DONE | - SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not set PHY interrupt " - "mask: %s\n", smsc75xx, strerror ( rc ) ); - goto err_phy_intr_mask; - } - - /* Update link status */ - smsc75xx_check_link ( smsc75xx ); + /* Enable PHY interrupts and update link status */ + if ( ( rc = smscusb_mii_open ( smscusb ) ) != 0 ) + goto err_mii_open; return 0; - err_phy_intr_mask: - err_addr_filtl: - err_addr_filth: - err_rx_addrl: - err_rx_addrh: + err_mii_open: + err_set_filter: + err_set_address: err_mac_tx: err_mac_rx: err_fct_tx_ctl: @@ -846,10 +365,10 @@ static int smsc75xx_open ( struct net_device *netdev ) { err_rfe_ctl: err_bulk_in_dly: err_int_ep_ctl: - usbnet_close ( &smsc75xx->usbnet ); + usbnet_close ( &smscusb->usbnet ); err_open: err_hw_cfg: - smsc75xx_reset ( smsc75xx ); + smsc75xx_reset ( smscusb ); return rc; } @@ -859,16 +378,16 @@ static int smsc75xx_open ( struct net_device *netdev ) { * @v netdev Network device */ static void smsc75xx_close ( struct net_device *netdev ) { - struct smsc75xx_device *smsc75xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; /* Close USB network device */ - usbnet_close ( &smsc75xx->usbnet ); + usbnet_close ( &smscusb->usbnet ); /* Dump statistics (for debugging) */ - smsc75xx_dump_statistics ( smsc75xx ); + smsc75xx_dump_statistics ( smscusb ); /* Reset device */ - smsc75xx_reset ( smsc75xx ); + smsc75xx_reset ( smscusb ); } /** @@ -880,11 +399,11 @@ static void smsc75xx_close ( struct net_device *netdev ) { */ static int smsc75xx_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { - struct smsc75xx_device *smsc75xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; int rc; /* Transmit packet */ - if ( ( rc = smsc75xx_out_transmit ( smsc75xx, iobuf ) ) != 0 ) + if ( ( rc = smsc75xx_out_transmit ( smscusb, iobuf ) ) != 0 ) return rc; return 0; @@ -896,48 +415,47 @@ static int smsc75xx_transmit ( struct net_device *netdev, * @v netdev Network device */ static void smsc75xx_poll ( struct net_device *netdev ) { - struct smsc75xx_device *smsc75xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; uint32_t int_sts; int rc; /* Poll USB bus */ - usb_poll ( smsc75xx->bus ); + usb_poll ( smscusb->bus ); /* Refill endpoints */ - if ( ( rc = usbnet_refill ( &smsc75xx->usbnet ) ) != 0 ) + if ( ( rc = usbnet_refill ( &smscusb->usbnet ) ) != 0 ) netdev_rx_err ( netdev, NULL, rc ); /* Do nothing more unless there are interrupts to handle */ - int_sts = smsc75xx->int_sts; + int_sts = smscusb->int_sts; if ( ! int_sts ) return; /* Check link status if applicable */ if ( int_sts & SMSC75XX_INT_STS_PHY_INT ) { - smsc75xx_check_link ( smsc75xx ); + smscusb_mii_check_link ( smscusb ); int_sts &= ~SMSC75XX_INT_STS_PHY_INT; } /* Record RX FIFO overflow if applicable */ if ( int_sts & SMSC75XX_INT_STS_RDFO_INT ) { - DBGC2 ( smsc75xx, "SMSC75XX %p RX FIFO overflowed\n", - smsc75xx ); + DBGC2 ( smscusb, "SMSC75XX %p RX FIFO overflowed\n", smscusb ); netdev_rx_err ( netdev, NULL, -ENOBUFS ); int_sts &= ~SMSC75XX_INT_STS_RDFO_INT; } /* Check for unexpected interrupts */ if ( int_sts ) { - DBGC ( smsc75xx, "SMSC75XX %p unexpected interrupt %#08x\n", - smsc75xx, int_sts ); + DBGC ( smscusb, "SMSC75XX %p unexpected interrupt %#08x\n", + smscusb, int_sts ); netdev_rx_err ( netdev, NULL, -ENOTTY ); } /* Clear interrupts */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_STS, - smsc75xx->int_sts ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_INT_STS, + smscusb->int_sts ) ) != 0 ) netdev_rx_err ( netdev, NULL, rc ); - smsc75xx->int_sts = 0; + smscusb->int_sts = 0; } /** SMSC75xx network device operations */ @@ -964,48 +482,41 @@ static struct net_device_operations smsc75xx_operations = { */ static int smsc75xx_probe ( struct usb_function *func, struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; struct net_device *netdev; - struct smsc75xx_device *smsc75xx; + struct smscusb_device *smscusb; int rc; /* Allocate and initialise structure */ - netdev = alloc_etherdev ( sizeof ( *smsc75xx ) ); + netdev = alloc_etherdev ( sizeof ( *smscusb ) ); if ( ! netdev ) { rc = -ENOMEM; goto err_alloc; } netdev_init ( netdev, &smsc75xx_operations ); netdev->dev = &func->dev; - smsc75xx = netdev->priv; - memset ( smsc75xx, 0, sizeof ( *smsc75xx ) ); - smsc75xx->usb = usb; - smsc75xx->bus = usb->port->hub->bus; - smsc75xx->netdev = netdev; - usbnet_init ( &smsc75xx->usbnet, func, &smsc75xx_intr_operations, - &smsc75xx_in_operations, &smsc75xx_out_operations ); - usb_refill_init ( &smsc75xx->usbnet.intr, 0, 0, - SMSC75XX_INTR_MAX_FILL ); - usb_refill_init ( &smsc75xx->usbnet.in, 0, SMSC75XX_IN_MTU, + smscusb = netdev->priv; + memset ( smscusb, 0, sizeof ( *smscusb ) ); + smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations ); + smscusb_mii_init ( smscusb, SMSC75XX_MII_BASE ); + usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU, SMSC75XX_IN_MAX_FILL ); - mii_init ( &smsc75xx->mii, &smsc75xx_mii_operations ); - DBGC ( smsc75xx, "SMSC75XX %p on %s\n", smsc75xx, func->name ); + DBGC ( smscusb, "SMSC75XX %p on %s\n", smscusb, func->name ); /* Describe USB network device */ - if ( ( rc = usbnet_describe ( &smsc75xx->usbnet, config ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not describe: %s\n", - smsc75xx, strerror ( rc ) ); + if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) { + DBGC ( smscusb, "SMSC75XX %p could not describe: %s\n", + smscusb, strerror ( rc ) ); goto err_describe; } /* Reset device */ - if ( ( rc = smsc75xx_reset ( smsc75xx ) ) != 0 ) + if ( ( rc = smsc75xx_reset ( smscusb ) ) != 0 ) goto err_reset; /* Read MAC address */ - if ( ( rc = smsc75xx_eeprom_read ( smsc75xx, SMSC75XX_EEPROM_MAC, - netdev->hw_addr, ETH_ALEN ) ) != 0 ) - goto err_eeprom_read; + if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb, + SMSC75XX_E2P_BASE ) ) != 0 ) + goto err_fetch_mac; /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) @@ -1016,7 +527,7 @@ static int smsc75xx_probe ( struct usb_function *func, unregister_netdev ( netdev ); err_register: - err_eeprom_read: + err_fetch_mac: err_reset: err_describe: netdev_nullify ( netdev ); diff --git a/src/drivers/net/smsc75xx.h b/src/drivers/net/smsc75xx.h index ae81fc168..0a330fd9f 100644 --- a/src/drivers/net/smsc75xx.h +++ b/src/drivers/net/smsc75xx.h @@ -9,25 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include -#include -#include -#include - -/** Register write command */ -#define SMSC75XX_REGISTER_WRITE \ - ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa0 ) ) - -/** Register read command */ -#define SMSC75XX_REGISTER_READ \ - ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa1 ) ) - -/** Get statistics command */ -#define SMSC75XX_GET_STATISTICS \ - ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa2 ) ) +#include "smscusb.h" /** Interrupt status register */ #define SMSC75XX_INT_STS 0x00c @@ -48,19 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC75XX_BULK_IN_DLY 0x03c #define SMSC75XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ -/** EEPROM command register */ -#define SMSC75XX_E2P_CMD 0x040 -#define SMSC75XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ -#define SMSC75XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ -#define SMSC75XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ - -/** EEPROM data register */ -#define SMSC75XX_E2P_DATA 0x044 -#define SMSC75XX_E2P_DATA_GET(e2p_data) \ - ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ - -/** MAC address EEPROM address */ -#define SMSC75XX_EEPROM_MAC 0x01 +/** EEPROM register base */ +#define SMSC75XX_E2P_BASE 0x040 /** Receive filtering engine control register */ #define SMSC75XX_RFE_CTL 0x060 @@ -89,56 +60,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC75XX_MAC_TX 0x108 #define SMSC75XX_MAC_TX_EN 0x00000001UL /**< TX enable */ -/** MAC receive address high register */ -#define SMSC75XX_RX_ADDRH 0x118 +/** MAC receive address register base */ +#define SMSC75XX_RX_ADDR_BASE 0x118 -/** MAC receive address low register */ -#define SMSC75XX_RX_ADDRL 0x11c +/** MII register base */ +#define SMSC75XX_MII_BASE 0x120 -/** MII access register */ -#define SMSC75XX_MII_ACCESS 0x120 -#define SMSC75XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ -#define SMSC75XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ -#define SMSC75XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ -#define SMSC75XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ - -/** MII data register */ -#define SMSC75XX_MII_DATA 0x124 -#define SMSC75XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ -#define SMSC75XX_MII_DATA_GET(mii_data) \ - ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ - -/** PHY interrupt source MII register */ -#define SMSC75XX_MII_PHY_INTR_SOURCE 29 - -/** PHY interrupt mask MII register */ -#define SMSC75XX_MII_PHY_INTR_MASK 30 - -/** PHY interrupt: auto-negotiation complete */ -#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040 - -/** PHY interrupt: link down */ -#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010 - -/** MAC address perfect filter N high register */ -#define SMSC75XX_ADDR_FILTH(n) ( 0x300 + ( 8 * (n) ) ) -#define SMSC75XX_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ - -/** MAC address perfect filter N low register */ -#define SMSC75XX_ADDR_FILTL(n) ( 0x304 + ( 8 * (n) ) ) - -/** MAC address */ -union smsc75xx_mac { - /** MAC receive address registers */ - struct { - /** MAC receive address low register */ - uint32_t l; - /** MAC receive address high register */ - uint32_t h; - } __attribute__ (( packed )) addr; - /** Raw MAC address */ - uint8_t raw[ETH_ALEN]; -}; +/** MAC address perfect filter register base */ +#define SMSC75XX_ADDR_FILT_BASE 0x300 /** Receive packet header */ struct smsc75xx_rx_header { @@ -168,12 +97,6 @@ struct smsc75xx_tx_header { /** Insert frame checksum and pad */ #define SMSC75XX_TX_FCS 0x00400000UL -/** Interrupt packet format */ -struct smsc75xx_interrupt { - /** Current value of INT_STS register */ - uint32_t int_sts; -} __attribute__ (( packed )); - /** Byte count statistics */ struct smsc75xx_byte_statistics { /** Unicast byte count */ @@ -264,37 +187,9 @@ struct smsc75xx_statistics { struct smsc75xx_tx_statistics tx; } __attribute__ (( packed )); -/** A SMSC75xx network device */ -struct smsc75xx_device { - /** USB device */ - struct usb_device *usb; - /** USB bus */ - struct usb_bus *bus; - /** Network device */ - struct net_device *netdev; - /** USB network device */ - struct usbnet_device usbnet; - /** MII interface */ - struct mii_interface mii; - /** Interrupt status */ - uint32_t int_sts; -}; - /** Maximum time to wait for reset (in milliseconds) */ #define SMSC75XX_RESET_MAX_WAIT_MS 100 -/** Maximum time to wait for EEPROM (in milliseconds) */ -#define SMSC75XX_EEPROM_MAX_WAIT_MS 100 - -/** Maximum time to wait for MII (in milliseconds) */ -#define SMSC75XX_MII_MAX_WAIT_MS 100 - -/** Interrupt maximum fill level - * - * This is a policy decision. - */ -#define SMSC75XX_INTR_MAX_FILL 2 - /** Bulk IN maximum fill level * * This is a policy decision. From d4df9f573f3466164cae22972d47632152c278d4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 7 Jul 2017 19:01:25 +0100 Subject: [PATCH 486/591] [smscusb] Add ability to read MAC address from OTP Signed-off-by: Michael Brown --- src/drivers/net/smscusb.c | 208 +++++++++++++++++++++++++++++++++++++- src/drivers/net/smscusb.h | 44 ++++++++ 2 files changed, 251 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c index d4f3af034..32e24418b 100644 --- a/src/drivers/net/smscusb.c +++ b/src/drivers/net/smscusb.c @@ -166,7 +166,7 @@ int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, /* Check that EEPROM is physically present */ if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { - DBGC ( smscusb, "SMSCUSB %p has no EEPROM (%s)\n", + DBGC ( smscusb, "SMSCUSB %p has no EEPROM MAC (%s)\n", smscusb, eth_ntoa ( netdev->hw_addr ) ); return -ENODEV; } @@ -176,6 +176,212 @@ int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, return 0; } +/****************************************************************************** + * + * OTP access + * + ****************************************************************************** + */ + +/** + * Power up OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @ret rc Return status code + */ +static int smscusb_otp_power_up ( struct smscusb_device *smscusb, + unsigned int otp_base ) { + uint32_t otp_power; + unsigned int i; + int rc; + + /* Power up OTP */ + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_POWER ), + 0 ) ) != 0 ) + return rc; + + /* Wait for OTP_POWER_DOWN to become clear */ + for ( i = 0 ; i < SMSCUSB_OTP_MAX_WAIT_MS ; i++ ) { + + /* Read OTP_POWER and check OTP_POWER_DOWN */ + if ( ( rc = smscusb_readl ( smscusb, + ( otp_base + SMSCUSB_OTP_POWER ), + &otp_power ) ) != 0 ) + return rc; + if ( ! ( otp_power & SMSCUSB_OTP_POWER_DOWN ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for OTP power up\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Wait for OTP to become idle + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @ret rc Return status code + */ +static int smscusb_otp_wait ( struct smscusb_device *smscusb, + unsigned int otp_base ) { + uint32_t otp_status; + unsigned int i; + int rc; + + /* Wait for OTP_STATUS_BUSY to become clear */ + for ( i = 0 ; i < SMSCUSB_OTP_MAX_WAIT_MS ; i++ ) { + + /* Read OTP_STATUS and check OTP_STATUS_BUSY */ + if ( ( rc = smscusb_readl ( smscusb, + ( otp_base + SMSCUSB_OTP_STATUS ), + &otp_status ) ) != 0 ) + return rc; + if ( ! ( otp_status & SMSCUSB_OTP_STATUS_BUSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for OTP\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Read byte from OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @v address OTP address + * @ret byte Byte read, or negative error + */ +static int smscusb_otp_read_byte ( struct smscusb_device *smscusb, + unsigned int otp_base, + unsigned int address ) { + uint8_t addrh = ( address >> 8 ); + uint8_t addrl = ( address >> 0 ); + uint32_t otp_data; + int rc; + + /* Wait for OTP to become idle */ + if ( ( rc = smscusb_otp_wait ( smscusb, otp_base ) ) != 0 ) + return rc; + + /* Initiate read command */ + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_ADDRH ), + addrh ) ) != 0 ) + return rc; + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_ADDRL ), + addrl ) ) != 0 ) + return rc; + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_CMD ), + SMSCUSB_OTP_CMD_READ ) ) != 0 ) + return rc; + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_GO ), + SMSCUSB_OTP_GO_GO ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_otp_wait ( smscusb, otp_base ) ) != 0 ) + return rc; + + /* Read OTP data */ + if ( ( rc = smscusb_readl ( smscusb, ( otp_base + SMSCUSB_OTP_DATA ), + &otp_data ) ) != 0 ) + return rc; + + return SMSCUSB_OTP_DATA_GET ( otp_data ); +} + +/** + * Read data from OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @v address OTP address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smscusb_otp_read ( struct smscusb_device *smscusb, + unsigned int otp_base, unsigned int address, + void *data, size_t len ) { + uint8_t *bytes; + int byte; + int rc; + + /* Power up OTP */ + if ( ( rc = smscusb_otp_power_up ( smscusb, otp_base ) ) != 0 ) + return rc; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smscusb_otp_read_byte ( smscusb, otp_base, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/** + * Fetch MAC address from OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @ret rc Return status code + */ +int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, + unsigned int otp_base ) { + struct net_device *netdev = smscusb->netdev; + uint8_t signature; + unsigned int address; + int rc; + + /* Read OTP signature byte */ + if ( ( rc = smscusb_otp_read ( smscusb, otp_base, 0, &signature, + sizeof ( signature ) ) ) != 0 ) + return rc; + + /* Determine location of MAC address */ + switch ( signature ) { + case SMSCUSB_OTP_1_SIG: + address = SMSCUSB_OTP_1_MAC; + break; + case SMSCUSB_OTP_2_SIG: + address = SMSCUSB_OTP_2_MAC; + break; + default: + DBGC ( smscusb, "SMSCUSB %p unknown OTP signature %#02x\n", + smscusb, signature ); + return -ENOTSUP; + } + + /* Read MAC address from OTP */ + if ( ( rc = smscusb_otp_read ( smscusb, otp_base, address, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + return rc; + + /* Check that OTP is valid */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + DBGC ( smscusb, "SMSCUSB %p has no layout %#02x OTP MAC (%s)\n", + smscusb, signature, eth_ntoa ( netdev->hw_addr ) ); + return -ENODEV; + } + + DBGC ( smscusb, "SMSCUSB %p using layout %#02x OTP MAC %s\n", + smscusb, signature, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + /****************************************************************************** * * MII access diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h index 152338184..8c602dc54 100644 --- a/src/drivers/net/smscusb.h +++ b/src/drivers/net/smscusb.h @@ -50,6 +50,48 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum time to wait for EEPROM (in milliseconds) */ #define SMSCUSB_EEPROM_MAX_WAIT_MS 100 +/** OTP power register offset */ +#define SMSCUSB_OTP_POWER 0x000 +#define SMSCUSB_OTP_POWER_DOWN 0x00000001UL /**< OTP power down */ + +/** OTP address high byte register offset */ +#define SMSCUSB_OTP_ADDRH 0x004 + +/** OTP address low byte register offset */ +#define SMSCUSB_OTP_ADDRL 0x008 + +/** OTP data register offset */ +#define SMSCUSB_OTP_DATA 0x018 +#define SMSCUSB_OTP_DATA_GET(otp_data) \ + ( ( (otp_data) >> 0 ) & 0xff ) /**< OTP data */ + +/** OTP command selection register offset */ +#define SMSCUSB_OTP_CMD 0x020 +#define SMSCUSB_OTP_CMD_READ 0x00000001UL /**< Read command */ + +/** OTP command initiation register offset */ +#define SMSCUSB_OTP_GO 0x028 +#define SMSCUSB_OTP_GO_GO 0x00000001UL /**< Initiate command */ + +/** OTP status register offset */ +#define SMSCUSB_OTP_STATUS 0x030 +#define SMSCUSB_OTP_STATUS_BUSY 0x00000001UL /**< OTP busy */ + +/** Maximum time to wait for OTP (in milliseconds) */ +#define SMSCUSB_OTP_MAX_WAIT_MS 100 + +/** OTP layout 1 signature */ +#define SMSCUSB_OTP_1_SIG 0xf3 + +/** OTP layout 1 MAC address offset */ +#define SMSCUSB_OTP_1_MAC 0x001 + +/** OTP layout 2 signature */ +#define SMSCUSB_OTP_2_SIG 0xf7 + +/** OTP layout 2 MAC address offset */ +#define SMSCUSB_OTP_2_MAC 0x101 + /** MII access register offset */ #define SMSCUSB_MII_ACCESS 0x000 #define SMSCUSB_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ @@ -292,6 +334,8 @@ smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base ) { extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, unsigned int e2p_base ); +extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, + unsigned int otp_base ); extern int smscusb_mii_check_link ( struct smscusb_device *smscusb ); extern int smscusb_mii_open ( struct smscusb_device *smscusb ); extern int smscusb_set_address ( struct smscusb_device *smscusb, From 6a258d8d5523c9b529ae2607d7f49e48eea8beeb Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Mon, 10 Jul 2017 17:10:41 +0800 Subject: [PATCH 487/591] [virtio] Support VIRTIO_NET_F_IOMMU_PLATFORM Since we don't enable IOMMU at all, we can then simply enable the IOMMU support by claiming the support of VIRITO_F_IOMMU_PLATFORM. This fixes booting failure when iommu_platform is set from qemu cli. Signed-off-by: Jason Wang Signed-off-by: Michael Brown --- src/drivers/net/virtio-net.c | 3 ++- src/include/ipxe/virtio-ring.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 4151532e6..fe79a92c4 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -259,7 +259,8 @@ static int virtnet_open_modern ( struct net_device *netdev ) { ( 1ULL << VIRTIO_NET_F_MAC ) | ( 1ULL << VIRTIO_NET_F_MTU ) | ( 1ULL << VIRTIO_F_VERSION_1 ) | - ( 1ULL << VIRTIO_F_ANY_LAYOUT ) ) ); + ( 1ULL << VIRTIO_F_ANY_LAYOUT ) | + ( 1ULL << VIRTIO_F_IOMMU_PLATFORM ) ) ); vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FEATURES_OK ); status = vpm_get_status ( &virtnet->vdev ); diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index e608e624f..852769f29 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -20,6 +20,7 @@ #define VIRTIO_F_ANY_LAYOUT 27 /* v1.0 compliant. */ #define VIRTIO_F_VERSION_1 32 +#define VIRTIO_F_IOMMU_PLATFORM 33 #define MAX_QUEUE_NUM (256) From 340f03392d37e1f8552b3bd3021705744caecf25 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Jul 2017 11:54:24 +0100 Subject: [PATCH 488/591] [smscusb] Move non-inline register access functions to smscusb.c Signed-off-by: Michael Brown --- src/drivers/net/smscusb.c | 57 +++++++++++++++++++++++++++++++++++++++ src/drivers/net/smscusb.h | 53 +++--------------------------------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c index 32e24418b..19a679c46 100644 --- a/src/drivers/net/smscusb.c +++ b/src/drivers/net/smscusb.c @@ -42,6 +42,63 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static struct profiler smscusb_intr_profiler __profiler = { .name = "smscusb.intr" }; +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Write register (without byte-swapping) + * + * @v smscusb Smscusb device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +int smscusb_raw_writel ( struct smscusb_device *smscusb, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + DBGCIO ( smscusb, "SMSCUSB %p [%03x] <= %08x\n", + smscusb, address, le32_to_cpu ( value ) ); + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_WRITE, 0, + address, &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not write %03x: %s\n", + smscusb, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Read register (without byte-swapping) + * + * @v smscusb SMSC USB device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +int smscusb_raw_readl ( struct smscusb_device *smscusb, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_READ, 0, + address, value, sizeof ( *value ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not read %03x: %s\n", + smscusb, address, strerror ( rc ) ); + return rc; + } + DBGCIO ( smscusb, "SMSCUSB %p [%03x] => %08x\n", + smscusb, address, le32_to_cpu ( *value ) ); + + return 0; +} + /****************************************************************************** * * EEPROM access diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h index 8c602dc54..d7216d9a8 100644 --- a/src/drivers/net/smscusb.h +++ b/src/drivers/net/smscusb.h @@ -170,30 +170,10 @@ struct smscusb_device { uint32_t int_sts; }; -/** - * Write register (without byte-swapping) - * - * @v smscusb Smscusb device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static int smscusb_raw_writel ( struct smscusb_device *smscusb, - unsigned int address, uint32_t value ) { - int rc; - - /* Write register */ - DBGCIO ( smscusb, "SMSCUSB %p [%03x] <= %08x\n", - smscusb, address, le32_to_cpu ( value ) ); - if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_WRITE, 0, - address, &value, sizeof ( value ) ) ) != 0 ) { - DBGC ( smscusb, "SMSCUSB %p could not write %03x: %s\n", - smscusb, address, strerror ( rc ) ); - return rc; - } - - return 0; -} +extern int smscusb_raw_writel ( struct smscusb_device *smscusb, + unsigned int address, uint32_t value ); +extern int smscusb_raw_readl ( struct smscusb_device *smscusb, + unsigned int address, uint32_t *value ); /** * Write register @@ -216,31 +196,6 @@ smscusb_writel ( struct smscusb_device *smscusb, unsigned int address, return 0; } -/** - * Read register (without byte-swapping) - * - * @v smscusb SMSC USB device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static int smscusb_raw_readl ( struct smscusb_device *smscusb, - unsigned int address, uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_READ, 0, - address, value, sizeof ( *value ) ) ) != 0 ) { - DBGC ( smscusb, "SMSCUSB %p could not read %03x: %s\n", - smscusb, address, strerror ( rc ) ); - return rc; - } - DBGCIO ( smscusb, "SMSCUSB %p [%03x] => %08x\n", - smscusb, address, le32_to_cpu ( *value ) ); - - return 0; -} - /** * Read register * From 74f934a14e8d0bead8086433596745e7c2b67990 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Jul 2017 12:38:39 +0100 Subject: [PATCH 489/591] [smscusb] Allow for alternative PHY register layouts The LAN78xx PHY interrupt source and mask registers do not match those used by the SMSC75xx and SMSC95xx. Signed-off-by: Michael Brown --- src/drivers/net/smsc75xx.c | 7 +++++-- src/drivers/net/smsc75xx.h | 12 ++++++++++++ src/drivers/net/smsc95xx.c | 7 +++++-- src/drivers/net/smsc95xx.h | 12 ++++++++++++ src/drivers/net/smscusb.c | 13 +++++++------ src/drivers/net/smscusb.h | 22 ++++++++-------------- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/drivers/net/smsc75xx.c b/src/drivers/net/smsc75xx.c index c04dc1a7f..0da255c20 100644 --- a/src/drivers/net/smsc75xx.c +++ b/src/drivers/net/smsc75xx.c @@ -350,7 +350,9 @@ static int smsc75xx_open ( struct net_device *netdev ) { goto err_set_filter; /* Enable PHY interrupts and update link status */ - if ( ( rc = smscusb_mii_open ( smscusb ) ) != 0 ) + if ( ( rc = smscusb_mii_open ( smscusb, SMSC75XX_MII_PHY_INTR_MASK, + ( SMSC75XX_PHY_INTR_ANEG_DONE | + SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0) goto err_mii_open; return 0; @@ -497,7 +499,8 @@ static int smsc75xx_probe ( struct usb_function *func, smscusb = netdev->priv; memset ( smscusb, 0, sizeof ( *smscusb ) ); smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations ); - smscusb_mii_init ( smscusb, SMSC75XX_MII_BASE ); + smscusb_mii_init ( smscusb, SMSC75XX_MII_BASE, + SMSC75XX_MII_PHY_INTR_SOURCE ); usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU, SMSC75XX_IN_MAX_FILL ); DBGC ( smscusb, "SMSC75XX %p on %s\n", smscusb, func->name ); diff --git a/src/drivers/net/smsc75xx.h b/src/drivers/net/smsc75xx.h index 0a330fd9f..f8bcefb78 100644 --- a/src/drivers/net/smsc75xx.h +++ b/src/drivers/net/smsc75xx.h @@ -66,6 +66,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** MII register base */ #define SMSC75XX_MII_BASE 0x120 +/** PHY interrupt source MII register */ +#define SMSC75XX_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSC75XX_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010 + /** MAC address perfect filter register base */ #define SMSC75XX_ADDR_FILT_BASE 0x300 diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index e56cf5b45..9b09657db 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -462,7 +462,9 @@ static int smsc95xx_open ( struct net_device *netdev ) { goto err_set_address; /* Enable PHY interrupts and update link status */ - if ( ( rc = smscusb_mii_open ( smscusb ) ) != 0 ) + if ( ( rc = smscusb_mii_open ( smscusb, SMSC95XX_MII_PHY_INTR_MASK, + ( SMSC95XX_PHY_INTR_ANEG_DONE | + SMSC95XX_PHY_INTR_LINK_DOWN ) ) ) != 0) goto err_mii_open; return 0; @@ -606,7 +608,8 @@ static int smsc95xx_probe ( struct usb_function *func, smscusb = netdev->priv; memset ( smscusb, 0, sizeof ( *smscusb ) ); smscusb_init ( smscusb, netdev, func, &smsc95xx_in_operations ); - smscusb_mii_init ( smscusb, SMSC95XX_MII_BASE ); + smscusb_mii_init ( smscusb, SMSC95XX_MII_BASE, + SMSC95XX_MII_PHY_INTR_SOURCE ); usb_refill_init ( &smscusb->usbnet.in, ( sizeof ( struct smsc95xx_tx_header ) - sizeof ( struct smsc95xx_rx_header ) ), diff --git a/src/drivers/net/smsc95xx.h b/src/drivers/net/smsc95xx.h index b0ecf6ee2..0cdf38248 100644 --- a/src/drivers/net/smsc95xx.h +++ b/src/drivers/net/smsc95xx.h @@ -65,6 +65,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** MII register base */ #define SMSC95XX_MII_BASE 0x0114 +/** PHY interrupt source MII register */ +#define SMSC95XX_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSC95XX_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSC95XX_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSC95XX_PHY_INTR_LINK_DOWN 0x0010 + /** Receive packet header */ struct smsc95xx_rx_header { /** Command word */ diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c index 19a679c46..60390ce31 100644 --- a/src/drivers/net/smscusb.c +++ b/src/drivers/net/smscusb.c @@ -578,7 +578,7 @@ int smscusb_mii_check_link ( struct smscusb_device *smscusb ) { int rc; /* Read PHY interrupt source */ - intr = mii_read ( &smscusb->mii, SMSCUSB_MII_PHY_INTR_SOURCE ); + intr = mii_read ( &smscusb->mii, smscusb->phy_source ); if ( intr < 0 ) { rc = intr; DBGC ( smscusb, "SMSCUSB %p could not get PHY interrupt " @@ -587,7 +587,7 @@ int smscusb_mii_check_link ( struct smscusb_device *smscusb ) { } /* Acknowledge PHY interrupt */ - if ( ( rc = mii_write ( &smscusb->mii, SMSCUSB_MII_PHY_INTR_SOURCE, + if ( ( rc = mii_write ( &smscusb->mii, smscusb->phy_source, intr ) ) != 0 ) { DBGC ( smscusb, "SMSCUSB %p could not acknowledge PHY " "interrupt: %s\n", smscusb, strerror ( rc ) ); @@ -610,15 +610,16 @@ int smscusb_mii_check_link ( struct smscusb_device *smscusb ) { * Enable PHY interrupts and update link status * * @v smscusb SMSC USB device + * @v phy_mask PHY interrupt mask register + * @v intrs PHY interrupts to enable * @ret rc Return status code */ -int smscusb_mii_open ( struct smscusb_device *smscusb ) { +int smscusb_mii_open ( struct smscusb_device *smscusb, + unsigned int phy_mask, unsigned int intrs ) { int rc; /* Enable PHY interrupts */ - if ( ( rc = mii_write ( &smscusb->mii, SMSCUSB_MII_PHY_INTR_MASK, - ( SMSCUSB_PHY_INTR_ANEG_DONE | - SMSCUSB_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { + if ( ( rc = mii_write ( &smscusb->mii, phy_mask, intrs ) ) != 0 ) { DBGC ( smscusb, "SMSCUSB %p could not set PHY interrupt " "mask: %s\n", smscusb, strerror ( rc ) ); return rc; diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h index d7216d9a8..5e4440ea6 100644 --- a/src/drivers/net/smscusb.h +++ b/src/drivers/net/smscusb.h @@ -105,18 +105,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSCUSB_MII_DATA_GET(mii_data) \ ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ -/** PHY interrupt source MII register */ -#define SMSCUSB_MII_PHY_INTR_SOURCE 29 - -/** PHY interrupt mask MII register */ -#define SMSCUSB_MII_PHY_INTR_MASK 30 - -/** PHY interrupt: auto-negotiation complete */ -#define SMSCUSB_PHY_INTR_ANEG_DONE 0x0040 - -/** PHY interrupt: link down */ -#define SMSCUSB_PHY_INTR_LINK_DOWN 0x0010 - /** Maximum time to wait for MII (in milliseconds) */ #define SMSCUSB_MII_MAX_WAIT_MS 100 @@ -166,6 +154,8 @@ struct smscusb_device { struct mii_interface mii; /** MII register base */ uint16_t mii_base; + /** PHY interrupt source register */ + uint16_t phy_source; /** Interrupt status */ uint32_t int_sts; }; @@ -279,12 +269,15 @@ smscusb_init ( struct smscusb_device *smscusb, struct net_device *netdev, * * @v smscusb SMSC USB device * @v mii_base MII register base + * @v phy_source Interrupt source PHY register */ static inline __attribute__ (( always_inline )) void -smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base ) { +smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base, + unsigned int phy_source ) { mii_init ( &smscusb->mii, &smscusb_mii_operations ); smscusb->mii_base = mii_base; + smscusb->phy_source = phy_source; } extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, @@ -292,7 +285,8 @@ extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, unsigned int otp_base ); extern int smscusb_mii_check_link ( struct smscusb_device *smscusb ); -extern int smscusb_mii_open ( struct smscusb_device *smscusb ); +extern int smscusb_mii_open ( struct smscusb_device *smscusb, + unsigned int phy_mask, unsigned int intrs ); extern int smscusb_set_address ( struct smscusb_device *smscusb, unsigned int addr_base ); extern int smscusb_set_filter ( struct smscusb_device *smscusb, From 1ee7f4e03652951670cff6e2f49171230ea2bcd7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 7 Jul 2017 17:25:37 +0100 Subject: [PATCH 490/591] [smsc75xx] Expose functionality shared with LAN78xx devices The LAN78xx datapath is essentially identical to that of the SMSC75xx. Expose the transmit, poll, and bulk IN endpoint operations to allow for reuse by the LAN78xx driver. Signed-off-by: Michael Brown --- src/drivers/net/smsc75xx.c | 12 ++++++------ src/drivers/net/smsc75xx.h | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/drivers/net/smsc75xx.c b/src/drivers/net/smsc75xx.c index 0da255c20..861669edf 100644 --- a/src/drivers/net/smsc75xx.c +++ b/src/drivers/net/smsc75xx.c @@ -60,7 +60,7 @@ static struct profiler smsc75xx_out_profiler __profiler = * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ) { +int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ) { struct smsc75xx_statistics stats; int rc; @@ -230,7 +230,7 @@ static void smsc75xx_in_complete ( struct usb_endpoint *ep, } /** Bulk IN endpoint operations */ -static struct usb_endpoint_driver_operations smsc75xx_in_operations = { +struct usb_endpoint_driver_operations smsc75xx_in_operations = { .complete = smsc75xx_in_complete, }; @@ -386,7 +386,8 @@ static void smsc75xx_close ( struct net_device *netdev ) { usbnet_close ( &smscusb->usbnet ); /* Dump statistics (for debugging) */ - smsc75xx_dump_statistics ( smscusb ); + if ( DBG_LOG ) + smsc75xx_dump_statistics ( smscusb ); /* Reset device */ smsc75xx_reset ( smscusb ); @@ -399,8 +400,7 @@ static void smsc75xx_close ( struct net_device *netdev ) { * @v iobuf I/O buffer * @ret rc Return status code */ -static int smsc75xx_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ) { +int smsc75xx_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct smscusb_device *smscusb = netdev->priv; int rc; @@ -416,7 +416,7 @@ static int smsc75xx_transmit ( struct net_device *netdev, * * @v netdev Network device */ -static void smsc75xx_poll ( struct net_device *netdev ) { +void smsc75xx_poll ( struct net_device *netdev ) { struct smscusb_device *smscusb = netdev->priv; uint32_t int_sts; int rc; diff --git a/src/drivers/net/smsc75xx.h b/src/drivers/net/smsc75xx.h index f8bcefb78..72339df03 100644 --- a/src/drivers/net/smsc75xx.h +++ b/src/drivers/net/smsc75xx.h @@ -213,4 +213,11 @@ struct smsc75xx_statistics { ( sizeof ( struct smsc75xx_rx_header ) + \ ETH_FRAME_LEN + 4 /* possible VLAN header */ ) +extern struct usb_endpoint_driver_operations smsc75xx_in_operations; + +extern int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ); +extern int smsc75xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ); +extern void smsc75xx_poll ( struct net_device *netdev ); + #endif /* _SMSC75XX_H */ From 0600d3ae94f93efd10fc6b3c7420a9557a3a1670 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 16 Jun 2016 15:06:25 +0100 Subject: [PATCH 491/591] [lan78xx] Add driver for Microchip LAN78xx USB Ethernet NICs Originally-implemented-by: Ravi Hegde Signed-off-by: Michael Brown --- src/drivers/net/lan78xx.c | 397 +++++++++++++++++++++++++++++++++++++ src/drivers/net/lan78xx.h | 97 +++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 495 insertions(+) create mode 100644 src/drivers/net/lan78xx.c create mode 100644 src/drivers/net/lan78xx.h diff --git a/src/drivers/net/lan78xx.c b/src/drivers/net/lan78xx.c new file mode 100644 index 000000000..3f705203e --- /dev/null +++ b/src/drivers/net/lan78xx.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2017 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 "lan78xx.h" + +/** @file + * + * Microchip LAN78xx USB Ethernet driver + * + */ + +/****************************************************************************** + * + * MAC address + * + ****************************************************************************** + */ + +/** + * Fetch MAC address from EEPROM + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int lan78xx_eeprom_fetch_mac ( struct smscusb_device *smscusb ) { + uint32_t hw_cfg; + uint32_t orig_hw_cfg; + int rc; + + /* Read original HW_CFG value */ + if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG, &hw_cfg ) ) != 0 ) + goto err_read_hw_cfg; + orig_hw_cfg = hw_cfg; + + /* Temporarily disable LED0 and LED1 (which share physical + * pins with EEDO and EECLK respectively). + */ + hw_cfg &= ~( LAN78XX_HW_CFG_LED0_EN | LAN78XX_HW_CFG_LED1_EN ); + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG, hw_cfg ) ) != 0 ) + goto err_write_hw_cfg; + + /* Fetch MAC address from EEPROM */ + if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb, + LAN78XX_E2P_BASE ) ) != 0 ) + goto err_fetch_mac; + + err_fetch_mac: + smscusb_writel ( smscusb, LAN78XX_HW_CFG, orig_hw_cfg ); + err_write_hw_cfg: + err_read_hw_cfg: + return rc; +} + +/** + * Fetch MAC address + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + int rc; + + /* Read MAC address from EEPROM, if present */ + if ( ( rc = lan78xx_eeprom_fetch_mac ( smscusb ) ) == 0 ) + return 0; + + /* Read MAC address from OTP, if present */ + if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 ) + return 0; + + /* Otherwise, generate a random MAC address */ + eth_random_addr ( netdev->hw_addr ); + DBGC ( smscusb, "LAN78XX %p using random MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int lan78xx_reset ( struct smscusb_device *smscusb ) { + uint32_t hw_cfg; + unsigned int i; + int rc; + + /* Reset device */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG, + LAN78XX_HW_CFG_LRST ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + for ( i = 0 ; i < LAN78XX_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset has completed */ + if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( ! ( hw_cfg & LAN78XX_HW_CFG_LRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "LAN78XX %p timed out waiting for reset\n", + smscusb ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int lan78xx_open ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + uint32_t usb_cfg0; + int rc; + + /* Clear stored interrupt status */ + smscusb->int_sts = 0; + + /* Configure bulk IN empty response */ + if ( ( rc = smscusb_readl ( smscusb, LAN78XX_USB_CFG0, + &usb_cfg0 ) ) != 0 ) + goto err_usb_cfg0_read; + usb_cfg0 |= LAN78XX_USB_CFG0_BIR; + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_USB_CFG0, + usb_cfg0 ) ) != 0 ) + goto err_usb_cfg0_write; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) { + DBGC ( smscusb, "LAN78XX %p could not open: %s\n", + smscusb, strerror ( rc ) ); + goto err_open; + } + + /* Configure interrupt endpoint */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_INT_EP_CTL, + ( LAN78XX_INT_EP_CTL_RDFO_EN | + LAN78XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + goto err_int_ep_ctl; + + /* Configure bulk IN delay */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_BULK_IN_DLY, + LAN78XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + goto err_bulk_in_dly; + + /* Configure receive filters */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_RFE_CTL, + ( LAN78XX_RFE_CTL_AB | + LAN78XX_RFE_CTL_AM | + LAN78XX_RFE_CTL_AU ) ) ) != 0 ) + goto err_rfe_ctl; + + /* Configure receive FIFO */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_RX_CTL, + ( LAN78XX_FCT_RX_CTL_EN | + LAN78XX_FCT_RX_CTL_BAD ) ) ) != 0 ) + goto err_fct_rx_ctl; + + /* Configure transmit FIFO */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_TX_CTL, + LAN78XX_FCT_TX_CTL_EN ) ) != 0 ) + goto err_fct_tx_ctl; + + /* Configure receive datapath */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_RX, + ( LAN78XX_MAC_RX_MAX_SIZE_DEFAULT | + LAN78XX_MAC_RX_FCS | + LAN78XX_MAC_RX_EN ) ) ) != 0 ) + goto err_mac_rx; + + /* Configure transmit datapath */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_TX, + LAN78XX_MAC_TX_EN ) ) != 0 ) + goto err_mac_tx; + + /* Set MAC address */ + if ( ( rc = smscusb_set_address ( smscusb, + LAN78XX_RX_ADDR_BASE ) ) != 0 ) + goto err_set_address; + + /* Set MAC address perfect filter */ + if ( ( rc = smscusb_set_filter ( smscusb, + LAN78XX_ADDR_FILT_BASE ) ) != 0 ) + goto err_set_filter; + + /* Enable PHY interrupts and update link status */ + if ( ( rc = smscusb_mii_open ( smscusb, LAN78XX_MII_PHY_INTR_MASK, + ( LAN78XX_PHY_INTR_ENABLE | + LAN78XX_PHY_INTR_LINK | + LAN78XX_PHY_INTR_ANEG_ERR | + LAN78XX_PHY_INTR_ANEG_DONE ) ) ) != 0 ) + goto err_mii_open; + + return 0; + + err_mii_open: + err_set_filter: + err_set_address: + err_mac_tx: + err_mac_rx: + err_fct_tx_ctl: + err_fct_rx_ctl: + err_rfe_ctl: + err_bulk_in_dly: + err_int_ep_ctl: + usbnet_close ( &smscusb->usbnet ); + err_open: + err_usb_cfg0_write: + err_usb_cfg0_read: + lan78xx_reset ( smscusb ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void lan78xx_close ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &smscusb->usbnet ); + + /* Dump statistics (for debugging) */ + if ( DBG_LOG ) + smsc75xx_dump_statistics ( smscusb ); + + /* Reset device */ + lan78xx_reset ( smscusb ); +} + +/** LAN78xx network device operations */ +static struct net_device_operations lan78xx_operations = { + .open = lan78xx_open, + .close = lan78xx_close, + .transmit = smsc75xx_transmit, + .poll = smsc75xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int lan78xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct net_device *netdev; + struct smscusb_device *smscusb; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *smscusb ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &lan78xx_operations ); + netdev->dev = &func->dev; + smscusb = netdev->priv; + memset ( smscusb, 0, sizeof ( *smscusb ) ); + smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations ); + smscusb_mii_init ( smscusb, LAN78XX_MII_BASE, + LAN78XX_MII_PHY_INTR_SOURCE ); + usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU, + SMSC75XX_IN_MAX_FILL ); + DBGC ( smscusb, "LAN78XX %p on %s\n", smscusb, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) { + DBGC ( smscusb, "LAN78XX %p could not describe: %s\n", + smscusb, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = lan78xx_reset ( smscusb ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = lan78xx_fetch_mac ( smscusb ) ) != 0 ) + goto err_fetch_mac; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_fetch_mac: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void lan78xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** LAN78xx device IDs */ +static struct usb_device_id lan78xx_ids[] = { + { + .name = "lan7800", + .vendor = 0x0424, + .product = 0x7800, + }, + { + .name = "lan7850", + .vendor = 0x0424, + .product = 0x7850, + }, +}; + +/** LAN78xx driver */ +struct usb_driver lan78xx_driver __usb_driver = { + .ids = lan78xx_ids, + .id_count = ( sizeof ( lan78xx_ids ) / sizeof ( lan78xx_ids[0] ) ), + .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ), + .score = USB_SCORE_NORMAL, + .probe = lan78xx_probe, + .remove = lan78xx_remove, +}; diff --git a/src/drivers/net/lan78xx.h b/src/drivers/net/lan78xx.h new file mode 100644 index 000000000..6ae17238e --- /dev/null +++ b/src/drivers/net/lan78xx.h @@ -0,0 +1,97 @@ +#ifndef _LAN78XX_H +#define _LAN78XX_H + +/** @file + * + * Microchip LAN78xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "smscusb.h" +#include "smsc75xx.h" + +/** Hardware configuration register */ +#define LAN78XX_HW_CFG 0x0010 +#define LAN78XX_HW_CFG_LED1_EN 0x00200000UL /**< LED1 enable */ +#define LAN78XX_HW_CFG_LED0_EN 0x00100000UL /**< LED1 enable */ +#define LAN78XX_HW_CFG_LRST 0x00000002UL /**< Soft lite reset */ + +/** Interrupt endpoint control register */ +#define LAN78XX_INT_EP_CTL 0x0098 +#define LAN78XX_INT_EP_CTL_RDFO_EN 0x00400000UL /**< RX FIFO overflow */ +#define LAN78XX_INT_EP_CTL_PHY_EN 0x00020000UL /**< PHY interrupt */ + +/** Bulk IN delay register */ +#define LAN78XX_BULK_IN_DLY 0x0094 +#define LAN78XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ + +/** EEPROM register base */ +#define LAN78XX_E2P_BASE 0x0040 + +/** USB configuration register 0 */ +#define LAN78XX_USB_CFG0 0x0080 +#define LAN78XX_USB_CFG0_BIR 0x00000040UL /**< Bulk IN use NAK */ + +/** Receive filtering engine control register */ +#define LAN78XX_RFE_CTL 0x00b0 +#define LAN78XX_RFE_CTL_AB 0x00000400UL /**< Accept broadcast */ +#define LAN78XX_RFE_CTL_AM 0x00000200UL /**< Accept multicast */ +#define LAN78XX_RFE_CTL_AU 0x00000100UL /**< Accept unicast */ + +/** FIFO controller RX FIFO control register */ +#define LAN78XX_FCT_RX_CTL 0x00c0 +#define LAN78XX_FCT_RX_CTL_EN 0x80000000UL /**< FCT RX enable */ +#define LAN78XX_FCT_RX_CTL_BAD 0x02000000UL /**< Store bad frames */ + +/** FIFO controller TX FIFO control register */ +#define LAN78XX_FCT_TX_CTL 0x00c4 +#define LAN78XX_FCT_TX_CTL_EN 0x80000000UL /**< FCT TX enable */ + +/** MAC receive register */ +#define LAN78XX_MAC_RX 0x0104 +#define LAN78XX_MAC_RX_MAX_SIZE(mtu) ( (mtu) << 16 ) /**< Max frame size */ +#define LAN78XX_MAC_RX_MAX_SIZE_DEFAULT \ + LAN78XX_MAC_RX_MAX_SIZE ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) +#define LAN78XX_MAC_RX_FCS 0x00000010UL /**< FCS stripping */ +#define LAN78XX_MAC_RX_EN 0x00000001UL /**< RX enable */ + +/** MAC transmit register */ +#define LAN78XX_MAC_TX 0x0108 +#define LAN78XX_MAC_TX_EN 0x00000001UL /**< TX enable */ + +/** MAC receive address register base */ +#define LAN78XX_RX_ADDR_BASE 0x0118 + +/** MII register base */ +#define LAN78XX_MII_BASE 0x0120 + +/** PHY interrupt mask MII register */ +#define LAN78XX_MII_PHY_INTR_MASK 25 + +/** PHY interrupt source MII register */ +#define LAN78XX_MII_PHY_INTR_SOURCE 26 + +/** PHY interrupt: global enable */ +#define LAN78XX_PHY_INTR_ENABLE 0x8000 + +/** PHY interrupt: link state change */ +#define LAN78XX_PHY_INTR_LINK 0x2000 + +/** PHY interrupt: auto-negotiation failure */ +#define LAN78XX_PHY_INTR_ANEG_ERR 0x0800 + +/** PHY interrupt: auto-negotiation complete */ +#define LAN78XX_PHY_INTR_ANEG_DONE 0x0400 + +/** MAC address perfect filter register base */ +#define LAN78XX_ADDR_FILT_BASE 0x0400 + +/** OTP register base */ +#define LAN78XX_OTP_BASE 0x1000 + +/** Maximum time to wait for reset (in milliseconds) */ +#define LAN78XX_RESET_MAX_WAIT_MS 100 + +#endif /* _LAN78XX_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 6da1a4505..2dc182dd4 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -201,6 +201,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efx_hunt ( ERRFILE_DRIVER | 0x00c50000 ) #define ERRFILE_exanic ( ERRFILE_DRIVER | 0x00c60000 ) #define ERRFILE_smscusb ( ERRFILE_DRIVER | 0x00c70000 ) +#define ERRFILE_lan78xx ( ERRFILE_DRIVER | 0x00c80000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 9ccd8fe569c75265dfa834d325046696e18616de Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 21 Jul 2017 14:48:27 +0100 Subject: [PATCH 492/591] [efi] Enumerate PCI BARs in same order as SnpDxe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UEFI specification has an implicit and demonstrably incorrect requirement (in the Mem_IO() calling convention) that any UNDI network device has at most one memory BAR and one I/O BAR. Some UEFI platforms have been observed to report the existence of non-existent additional I/O BARs, causing iPXE to select the wrong BAR. This problem does not affect the SnpDxe driver, since that driver will always choose the lowest numbered existent BAR of each type. Adjust iPXE's behaviour to match that of SnpDxe, i.e. to always select the lowest numbered BAR(s). Debugged-by: Andreas Hammarskjöld Debugged-by: Adklei Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index d76bfb898..1c6980e69 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -202,7 +202,7 @@ static int nii_pci_open ( struct nii_nic *nii ) { EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *acpi; void *resource; } desc; - unsigned int bar; + int bar; EFI_STATUS efirc; int rc; @@ -230,7 +230,7 @@ static int nii_pci_open ( struct nii_nic *nii ) { /* Identify memory and I/O BARs */ nii->mem_bar = PCI_MAX_BAR; nii->io_bar = PCI_MAX_BAR; - for ( bar = 0 ; bar < PCI_MAX_BAR ; bar++ ) { + for ( bar = ( PCI_MAX_BAR - 1 ) ; bar >= 0 ; bar-- ) { efirc = nii->pci_io->GetBarAttributes ( nii->pci_io, bar, NULL, &desc.resource ); if ( efirc == EFI_UNSUPPORTED ) { From b6fc8be2c43c4e1b068613abadc48c1d86caee2b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jul 2017 12:47:06 +0100 Subject: [PATCH 493/591] [build] Conditionalise use of -mabi=lp64 for ARM64 builds The -mabi option was added in GCC 4.9. Test for the existence of this option to allow for building with earlier versions of GCC. Reported-by: Benjamin S. Allen Signed-off-by: Michael Brown --- src/arch/arm64/Makefile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/arch/arm64/Makefile b/src/arch/arm64/Makefile index d121871f7..9b9dd5ec8 100644 --- a/src/arch/arm64/Makefile +++ b/src/arch/arm64/Makefile @@ -4,10 +4,21 @@ SRCDIRS += arch/arm64/core # ARM64-specific flags # -CFLAGS += -mabi=lp64 -mlittle-endian -mcmodel=small +CFLAGS += -mlittle-endian -mcmodel=small CFLAGS += -fomit-frame-pointer ASFLAGS += -mabi=lp64 -EL +# We want to specify the LP64 model. There is an explicit -mabi=lp64 +# on GCC 4.9 and later, and no guarantee as to which is the default +# model. In earlier versions of GCC, there is no -mabi option and the +# default appears to be LP64 anyway. +# +ifeq ($(CCTYPE),gcc) +LP64_TEST = $(CC) -mabi=lp64 -x c -c /dev/null -o /dev/null >/dev/null 2>&1 +LP64_FLAGS := $(shell $(LP64_TEST) && $(ECHO) '-mabi=lp64') +WORKAROUND_CFLAGS += $(LP64_FLAGS) +endif + # EFI requires -fshort-wchar, and nothing else currently uses wchar_t # CFLAGS += -fshort-wchar From 1a7746603bca1022b63c406c9459525312a2b2b6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jul 2017 13:50:35 +0100 Subject: [PATCH 494/591] [build] Fix use of inline assembly on GCC 4.8 ARM64 builds The inline assembly used in include/errno.h to generate the einfo blocks requires the ability to generate an immediate constant with no immediate-value prefix (such as the dollar sign for x86 assembly). We currently achieve this via the undocumented "%c0" form of operand. This causes an "invalid operand prefix" error on GCC 4.8 for ARM64 builds. Fix by switching to the equally undocumented "%a0" form of operand, which appears to work correctly on all tested versions of GCC. Reported-by: Benjamin S. Allen Signed-off-by: Michael Brown --- src/include/errno.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/errno.h b/src/include/errno.h index 342384fa4..e80bf9ca5 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -262,10 +262,10 @@ static inline void eplatform_discard ( int dummy __unused, ... ) {} ".align 8\n\t" \ "\n1:\n\t" \ ".long ( 4f - 1b )\n\t" \ - ".long %c0\n\t" \ + ".long %a0\n\t" \ ".long ( 2f - 1b )\n\t" \ ".long ( 3f - 1b )\n\t" \ - ".long %c1\n\t" \ + ".long %a1\n\t" \ "\n2:\t.asciz \"" __einfo_desc ( einfo ) "\"\n\t" \ "\n3:\t.asciz \"" __FILE__ "\"\n\t" \ ".align 8\n\t" \ From 8866c919f86ad2f00ff63192fa23cd19f749f90f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jul 2017 15:40:44 +0100 Subject: [PATCH 495/591] [build] Fix ARM32 EFI builds with current EDK2 headers EDK2 commit 6440385 ("MdePkg/Include: Add enumeration size checks to Base.h") enforced the UEFI specification mandate that enums should always be 32 bits. This revealed a latent bug in iPXE, which does not build with -fno-short-enums. Fix by adding -fno-short-enums to CFLAGS for ARM32 EFI builds. Reported-by: Benjamin S. Allen Signed-off-by: Michael Brown --- src/arch/arm32/Makefile.efi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arch/arm32/Makefile.efi b/src/arch/arm32/Makefile.efi index a06354f1d..e139a055a 100644 --- a/src/arch/arm32/Makefile.efi +++ b/src/arch/arm32/Makefile.efi @@ -1,5 +1,9 @@ # -*- makefile -*- : Force emacs to use Makefile mode +# UEFI requires that enums are always 32 bits +# +CFLAGS += -fno-short-enums + # Specify EFI image builder # ELF2EFI = $(ELF2EFI32) From 041d362423bb8414b0bd1baf93023bac14e52f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Gourv=C3=A9nec?= Date: Thu, 27 Jul 2017 16:04:35 +0200 Subject: [PATCH 496/591] [acpi] Compute and check checksum for ACPI tables Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/core/acpi.c | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/core/acpi.c b/src/core/acpi.c index ff8875e1c..07679153f 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -42,19 +42,41 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ +/** + * Compute ACPI table checksum + * + * @v table Any ACPI table + * @ret checksum 0 if checksum is good + */ +static uint8_t acpi_checksum ( userptr_t table ) { + struct acpi_header acpi; + uint8_t sum = 0; + uint8_t data; + unsigned int i; + + /* Read table length */ + copy_from_user ( &acpi.length, table, + offsetof ( typeof ( acpi ), length ), + sizeof ( acpi.length ) ); + + /* Compute checksum */ + for ( i = 0 ; i < le32_to_cpu ( acpi.length ) ; i++ ) { + copy_from_user ( &data, table, i, sizeof ( data ) ); + sum += data; + } + + return sum; +} + /** * Fix up ACPI table checksum * * @v acpi ACPI table header */ void acpi_fix_checksum ( struct acpi_header *acpi ) { - unsigned int i = 0; - uint8_t sum = 0; - for ( i = 0 ; i < acpi->length ; i++ ) { - sum += *( ( ( uint8_t * ) acpi ) + i ); - } - acpi->checksum -= sum; + /* Update checksum */ + acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) ); } /** @@ -123,6 +145,15 @@ userptr_t acpi_find ( uint32_t signature, unsigned int index ) { if ( index-- ) continue; + /* Check table integrity */ + if ( acpi_checksum ( table ) != 0 ) { + DBGC ( rsdt, "RSDT %#08lx found %s with bad checksum " + "at %08lx\n", user_to_phys ( rsdt, 0 ), + acpi_name ( signature ), + user_to_phys ( table, 0 ) ); + break; + } + DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n", user_to_phys ( rsdt, 0 ), acpi_name ( signature ), user_to_phys ( table, 0 ) ); From 51a79731f6a94e48cfe355165c330ce7a6892e9a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jul 2017 20:19:31 +0100 Subject: [PATCH 497/591] [acpi] Fix spurious uninitialised-variable warning on some gcc versions Reported-by: Christian Nilsson Signed-off-by: Michael Brown --- src/core/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/acpi.c b/src/core/acpi.c index 07679153f..e6912afa2 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -51,7 +51,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static uint8_t acpi_checksum ( userptr_t table ) { struct acpi_header acpi; uint8_t sum = 0; - uint8_t data; + uint8_t data = 0; unsigned int i; /* Read table length */ From 936657832f2262ad04bdf16b9229ce0b1d1c174f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jul 2017 21:19:45 +0100 Subject: [PATCH 498/591] [hyperv] Do not steal ownership from the Gen 2 UEFI firmware We must not steal ownership from the Gen 2 UEFI firmware, since doing so will cause an immediate system crash (most likely in the form of a reboot). This problem was masked before commit a0f6e75 ("[hyperv] Do not fail if guest OS ID MSR is already set"), since prior to that commit we would always fail if we found any non-zero guest OS identity. We now accept a non-zero previous guest OS identity in order to allow for situations such as chainloading from iPXE to another iPXE, and as a prerequisite for commit b91cc98 ("[hyperv] Cope with Windows Server 2016 enlightenments"). A proper fix would be to reverse engineer the UEFI protocols exposed within the Hyper-V Gen 2 firmware and use these to bind to the VMBus device representing the network connection, (with the native Hyper-V driver moved to become a BIOS-only feature). As an interim solution, fail to initialise the native Hyper-V driver if we detect the guest OS identity known to be used by the Gen 2 UEFI firmware. This will cause the standard all-drivers build (ipxe.efi) to fall back to using the SNP driver. Signed-off-by: Michael Brown --- src/arch/x86/drivers/hyperv/hyperv.c | 28 ++++++++++++++++++++++++++++ src/include/ipxe/hyperv.h | 7 +++++++ 2 files changed, 35 insertions(+) diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index 4e6876878..1903d1db2 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -220,6 +220,29 @@ static int hv_check_features ( struct hv_hypervisor *hv ) { return 0; } +/** + * Check that Gen 2 UEFI firmware is not running + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + * + * We must not steal ownership from the Gen 2 UEFI firmware, since + * doing so will cause an immediate crash. Avoid this by checking for + * the guest OS identity known to be used by the Gen 2 UEFI firmware. + */ +static int hv_check_uefi ( struct hv_hypervisor *hv ) { + uint64_t guest_os_id; + + /* Check for UEFI firmware's guest OS identity */ + guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID ); + if ( guest_os_id == HV_GUEST_OS_ID_UEFI ) { + DBGC ( hv, "HV %p is owned by UEFI firmware\n", hv ); + return -ENOTSUP; + } + + return 0; +} + /** * Map hypercall page * @@ -556,6 +579,10 @@ static int hv_probe ( struct root_device *rootdev ) { if ( ( rc = hv_check_features ( hv ) ) != 0 ) goto err_check_features; + /* Check that Gen 2 UEFI firmware is not running */ + if ( ( rc = hv_check_uefi ( hv ) ) != 0 ) + goto err_check_uefi; + /* Allocate pages */ if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message, &hv->synic.event, NULL ) ) != 0 ) @@ -587,6 +614,7 @@ static int hv_probe ( struct root_device *rootdev ) { hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, NULL ); err_alloc_pages: + err_check_uefi: err_check_features: free ( hv ); err_alloc: diff --git a/src/include/ipxe/hyperv.h b/src/include/ipxe/hyperv.h index 9194a9766..9b7e54a51 100644 --- a/src/include/ipxe/hyperv.h +++ b/src/include/ipxe/hyperv.h @@ -37,6 +37,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define HV_GUEST_OS_ID_IPXE ( ( 1ULL << 63 ) | ( 0x18aeULL << 48 ) ) +/** Guest OS identity for Gen 2 UEFI firmware + * + * This does not conform to the documented structure for guest OS + * identities. + */ +#define HV_GUEST_OS_ID_UEFI ( 1ULL << 40 ) + /** Enable hypercall page */ #define HV_HYPERCALL_ENABLE 0x00000001UL From 7054468d56aead705e20cdaf9f93819d60007fde Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 1 Aug 2017 20:25:28 +0100 Subject: [PATCH 499/591] [shell] Enable "shell" command even when BANNER_TIMEOUT is zero Setting BANNER_TIMEOUT to zero removes the only symbol reference to shell.o, causing the "shell" command to become unavailable. Add SHELL_CMD in config/general.h (enabled by default) which will explicitly drag in shell.o regardless of the value of BANNER_TIMEOUT. Reported-by: Julian Brost Signed-off-by: Michael Brown --- src/config/config.c | 3 +++ src/config/general.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/config/config.c b/src/config/config.c index 8adb1ed3f..2ca05dff7 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -248,6 +248,9 @@ REQUIRE_OBJECT ( cpuid_cmd ); #ifdef SYNC_CMD REQUIRE_OBJECT ( sync_cmd ); #endif +#ifdef SHELL_CMD +REQUIRE_OBJECT ( shell ); +#endif #ifdef NSLOOKUP_CMD REQUIRE_OBJECT ( nslookup_cmd ); #endif diff --git a/src/config/general.h b/src/config/general.h index fb1ac93f4..e06db5252 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -134,6 +134,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define MENU_CMD /* Menu commands */ #define LOGIN_CMD /* Login command */ #define SYNC_CMD /* Sync command */ +#define SHELL_CMD /* Shell command */ //#define NSLOOKUP_CMD /* DNS resolving command */ //#define TIME_CMD /* Time commands */ //#define DIGEST_CMD /* Image crypto digest commands */ From 8b104d881abbeb8f9592708a490c16916b0b1ecc Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Tue, 22 Aug 2017 10:38:42 +0100 Subject: [PATCH 500/591] [intel] Add various PCI device IDs Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index f66de7bf3..dc511b8a4 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1132,7 +1132,11 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", 0 ), + PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; From 75acb3c775544b6ecc4bfb1bba633717c8ec9394 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 30 Aug 2017 10:15:25 +0100 Subject: [PATCH 501/591] [romprefix] Avoid unaligned accesses within ROM headers Ensure that all headers (PCI, UNDI, PnP, iPXE) are aligned to at least four bytes, so that all accesses to header fields will be correctly aligned even when reading directly from the expansion ROM BAR. Reported-by: Peter von Konigsmark Signed-off-by: Michael Brown --- src/arch/x86/prefix/mromprefix.S | 1 + src/arch/x86/prefix/romprefix.S | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/arch/x86/prefix/mromprefix.S b/src/arch/x86/prefix/mromprefix.S index 568d1c006..73a869d90 100644 --- a/src/arch/x86/prefix/mromprefix.S +++ b/src/arch/x86/prefix/mromprefix.S @@ -492,6 +492,7 @@ mromheader: .word 0 .size mromheader, . - mromheader + .align 4 mpciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S index f4ca20677..978b07b57 100644 --- a/src/arch/x86/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -88,6 +88,7 @@ checksum: .previous .ifeqs BUSTYPE, "PCIR" + .align 4 pciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ @@ -183,6 +184,7 @@ prodstr_pci_id: .globl undiheader .weak undiloader + .align 4 undiheader: .ascii "UNDI" /* Signature */ .byte undiheader_len /* Length of structure */ @@ -197,6 +199,7 @@ undiheader: .equ undiheader_len, . - undiheader .size undiheader, . - undiheader + .align 4 ipxeheader: .ascii "iPXE" /* Signature */ .byte ipxeheader_len /* Length of structure */ From 42eedb04c1ad71548043c33ef1996dfe8e524996 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Sep 2017 14:00:32 +0100 Subject: [PATCH 502/591] [malloc] Avoid false positive warnings from valgrind Calling discard_cache() is likely to result in a call to free_memblock(), which will call valgrind_make_blocks_noaccess() before returning. This causes valgrind to report an invalid read on the next iteration through the loop in alloc_memblock(). Fix by explicitly calling valgrind_make_blocks_defined() after discard_cache() returns. Also call valgrind_make_blocks_noaccess() before calling discard_cache(), to guard against free list corruption while executing cache discarders. Signed-off-by: Michael Brown --- src/core/malloc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/malloc.c b/src/core/malloc.c index 6ddc08b7a..91c8e4d35 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -284,6 +284,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { size_t post_size; struct memory_block *pre; struct memory_block *post; + unsigned int discarded; void *ptr; /* Sanity checks */ @@ -371,7 +372,13 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { } /* Try discarding some cached data to free up memory */ - if ( ! discard_cache() ) { + DBGC ( &heap, "Attempting discard for %#zx (aligned %#zx+%zx), " + "used %zdkB\n", size, align, offset, ( usedmem >> 10 ) ); + valgrind_make_blocks_noaccess(); + discarded = discard_cache(); + valgrind_make_blocks_defined(); + check_blocks(); + if ( ! discarded ) { /* Nothing available to discard */ DBGC ( &heap, "Failed to allocate %#zx (aligned " "%#zx)\n", size, align ); From 306465bef3fbbe14e6270fecf3e2d0c427f4bbc7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Sep 2017 18:00:34 +0100 Subject: [PATCH 503/591] [linux] Impose receive quota on tap driver The tap driver can retrieve a potentially unlimited number of packets in a single poll. This can lead to heap exhaustion under heavy load. Fix by imposing an artificial receive quota (as already used in other drivers without natural receive limits). Signed-off-by: Michael Brown --- src/drivers/linux/tap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/drivers/linux/tap.c b/src/drivers/linux/tap.c index 6fe76fd4d..db3b7955b 100644 --- a/src/drivers/linux/tap.c +++ b/src/drivers/linux/tap.c @@ -40,6 +40,7 @@ #include #define RX_BUF_SIZE 1536 +#define RX_QUOTA 4 /** @file * @@ -127,6 +128,7 @@ static void tap_poll(struct net_device *netdev) struct tap_nic * nic = netdev->priv; struct pollfd pfd; struct io_buffer * iobuf; + unsigned int quota = RX_QUOTA; int r; pfd.fd = nic->fd; @@ -144,7 +146,8 @@ static void tap_poll(struct net_device *netdev) if (! iobuf) goto allocfail; - while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) { + while (quota-- && + ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0)) { DBGC2(nic, "tap %p read %d bytes\n", nic, r); iob_put(iobuf, r); From 3ae70be5bab1f898efcac859b1b8e3418e0f4a4b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 10:48:41 +0100 Subject: [PATCH 504/591] [efi] Raise TPL when calling UNDI entry point The SnpDxe driver raises the task priority level to TPL_CALLBACK when calling the UNDI entry point. This does not appear to be a documented requirement, but we should probably match the behaviour of SnpDxe to minimise surprises to third party code. Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index 1c6980e69..1700b4bd8 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -402,7 +402,9 @@ static EFIAPI VOID nii_block ( UINT64 unique_id, UINT32 acquire ) { */ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, size_t cpb_len, void *db, size_t db_len ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; PXE_CDB cdb; + UINTN tpl; /* Prepare command descriptor block */ memset ( &cdb, 0, sizeof ( cdb ) ); @@ -414,6 +416,9 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, cdb.DBsize = db_len; cdb.IFnum = nii->nii->IfNum; + /* Raise task priority level */ + tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Issue command */ DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n", nii->dev.name, cdb.OpCode, cdb.OpFlags, cdb.IFnum, @@ -424,6 +429,9 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, DBGC2_HD ( nii, db, db_len ); nii->issue ( ( intptr_t ) &cdb ); + /* Restore task priority level */ + bs->RestoreTPL ( tpl ); + /* Check completion status */ if ( cdb.StatCode != PXE_STATCODE_SUCCESS ) return -cdb.StatCode; From 97f0f56a34e32e705d3eee18526222f43fc88e6e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 12:21:11 +0100 Subject: [PATCH 505/591] [netdevice] Cancel all pending transmissions on any transmit error Some external code (such as the UEFI UNDI driver for the Realtek USB NIC on a Microsoft Surface Book) will block during transmission attempts and can take several seconds to report a transmit error. If there is a large queue of pending transmissions, then the accumulated time from a series of such failures can easily exceed the EFI watchdog timeout, resulting in what appears to be a system lockup followed by a reboot. Work around this problem by immediately cancelling any pending transmissions as soon as any transmit error occurs. The only expected transmit error under normal operation is ENOBUFS arising when the hardware transmit queue is full. By definition, this can happen only for drivers that do not utilise deferred transmissions, and so this new behaviour will not affect these drivers. Signed-off-by: Michael Brown --- src/net/netdevice.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 41ece77f0..4c211d707 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -402,11 +402,24 @@ void netdev_tx_complete_err ( struct net_device *netdev, list_del ( &iobuf->list ); netdev_tx_err ( netdev, iobuf, rc ); - /* Transmit first pending packet, if any */ - if ( ( iobuf = list_first_entry ( &netdev->tx_deferred, - struct io_buffer, list ) ) != NULL ) { + /* Handle pending transmit queue */ + while ( ( iobuf = list_first_entry ( &netdev->tx_deferred, + struct io_buffer, list ) ) ) { + + /* Remove from pending transmit queue */ list_del ( &iobuf->list ); + + /* When any transmit completion fails, cancel all + * pending transmissions. + */ + if ( rc != 0 ) { + netdev_tx_err ( netdev, iobuf, -ECANCELED ); + continue; + } + + /* Otherwise, attempt to transmit the first pending packet */ netdev_tx ( netdev, iobuf ); + break; } } From 7e6b367b7ef5994916459bcac5d772a0a574c1d3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 23:21:34 +0100 Subject: [PATCH 506/591] [monojob] Check for job progress only once per timer tick Checking for job progress is essentially a user interface activity, and can safely be performed only once per timer tick (as is already done with checking for keypresses). Signed-off-by: Michael Brown --- src/core/monojob.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/core/monojob.c b/src/core/monojob.c index 817f21b2c..d426235d6 100644 --- a/src/core/monojob.c +++ b/src/core/monojob.c @@ -64,7 +64,7 @@ struct interface monojob = INTF_INIT ( monojob_intf_desc ); */ int monojob_wait ( const char *string, unsigned long timeout ) { struct job_progress progress; - unsigned long last_keycheck; + unsigned long last_check; unsigned long last_progress; unsigned long last_display; unsigned long now; @@ -81,26 +81,28 @@ int monojob_wait ( const char *string, unsigned long timeout ) { if ( string ) printf ( "%s...", string ); monojob_rc = -EINPROGRESS; - last_keycheck = last_progress = last_display = currticks(); + last_check = last_progress = last_display = currticks(); while ( monojob_rc == -EINPROGRESS ) { /* Allow job to progress */ step(); now = currticks(); - /* Check for keypresses. This can be time-consuming, - * so check only once per clock tick. + /* Continue until a timer tick occurs (to minimise + * time wasted checking for progress and keypresses). */ - elapsed = ( now - last_keycheck ); - if ( elapsed ) { - if ( iskey() ) { - key = getchar(); - if ( key == CTRL_C ) { - monojob_rc = -ECANCELED; - break; - } + elapsed = ( now - last_check ); + if ( ! elapsed ) + continue; + last_check = now; + + /* Check for keypresses */ + if ( iskey() ) { + key = getchar(); + if ( key == CTRL_C ) { + monojob_rc = -ECANCELED; + break; } - last_keycheck = now; } /* Monitor progress */ From e30cc5e9e5fc0aed7c30245daab1e922a90460c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 22:52:35 +0100 Subject: [PATCH 507/591] [job] Allow jobs to report an arbitrary status message Signed-off-by: Michael Brown --- src/include/ipxe/job.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/include/ipxe/job.h b/src/include/ipxe/job.h index 7e1bd8109..c01bd1740 100644 --- a/src/include/ipxe/job.h +++ b/src/include/ipxe/job.h @@ -28,6 +28,8 @@ struct job_progress { * account before calculating @c completed/total. */ unsigned long total; + /** Message (optional) */ + char message[32]; }; extern int job_progress ( struct interface *intf, From a258b0897b0560cdf123c79687e84141af9d3ff5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 22:53:49 +0100 Subject: [PATCH 508/591] [downloader] Allow underlying downloads to provide detailed job progress Signed-off-by: Michael Brown --- src/core/downloader.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/downloader.c b/src/core/downloader.c index 35b5b0ac6..33737bfac 100644 --- a/src/core/downloader.c +++ b/src/core/downloader.c @@ -111,13 +111,20 @@ static void downloader_finished ( struct downloader *downloader, int rc ) { */ static int downloader_progress ( struct downloader *downloader, struct job_progress *progress ) { + int rc; + + /* Allow data transfer to provide an accurate description */ + if ( ( rc = job_progress ( &downloader->xfer, progress ) ) != 0 ) + return rc; /* This is not entirely accurate, since downloaded data may * arrive out of order (e.g. with multicast protocols), but * it's a reasonable first approximation. */ - progress->completed = downloader->buffer.pos; - progress->total = downloader->buffer.len; + if ( ! progress->total ) { + progress->completed = downloader->buffer.pos; + progress->total = downloader->buffer.len; + } return 0; } From 4674df25ef94d089f60e9991541d6f586a474de7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 22:53:10 +0100 Subject: [PATCH 509/591] [monojob] Display job status message, if present Signed-off-by: Michael Brown --- src/core/monojob.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/core/monojob.c b/src/core/monojob.c index d426235d6..2f066331c 100644 --- a/src/core/monojob.c +++ b/src/core/monojob.c @@ -55,6 +55,22 @@ static struct interface_descriptor monojob_intf_desc = struct interface monojob = INTF_INIT ( monojob_intf_desc ); +/** + * Clear previously displayed message + * + * @v len Length of previously displayed message + */ +static void monojob_clear ( size_t len ) { + unsigned int i; + + for ( i = 0 ; i < len ; i++ ) + putchar ( '\b' ); + for ( i = 0 ; i < len ; i++ ) + putchar ( ' ' ); + for ( i = 0 ; i < len ; i++ ) + putchar ( '\b' ); +} + /** * Wait for single foreground job to complete * @@ -73,7 +89,7 @@ int monojob_wait ( const char *string, unsigned long timeout ) { unsigned long scaled_completed; unsigned long scaled_total; unsigned int percentage; - int shown_percentage = 0; + size_t clear_len = 0; int ongoing_rc; int key; int rc; @@ -123,19 +139,21 @@ int monojob_wait ( const char *string, unsigned long timeout ) { /* Display progress, if applicable */ elapsed = ( now - last_display ); if ( string && ( elapsed >= TICKS_PER_SEC ) ) { - if ( shown_percentage ) - printf ( "\b\b\b\b \b\b\b\b" ); + monojob_clear ( clear_len ); /* Normalise progress figures to avoid overflow */ scaled_completed = ( progress.completed / 128 ); scaled_total = ( progress.total / 128 ); if ( scaled_total ) { percentage = ( ( 100 * scaled_completed ) / scaled_total ); - printf ( "%3d%%", percentage ); - shown_percentage = 1; + clear_len = printf ( "%3d%%", percentage ); } else { printf ( "." ); - shown_percentage = 0; + clear_len = 0; + } + if ( progress.message[0] ) { + clear_len += printf ( " [%s]", + progress.message ); } last_display = now; } @@ -143,9 +161,7 @@ int monojob_wait ( const char *string, unsigned long timeout ) { rc = monojob_rc; monojob_close ( &monojob, rc ); - if ( shown_percentage ) - printf ( "\b\b\b\b \b\b\b\b" ); - + monojob_clear ( clear_len ); if ( string ) { if ( rc ) { printf ( " %s\n", strerror ( rc ) ); From 7e673a6b67be1594e16a8cc5ab4a0d6c17799547 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Sep 2017 22:55:05 +0100 Subject: [PATCH 510/591] [peerdist] Gather and report peer statistics during download Record and report the number of peers (calculated as the maximum number of peers discovered for a block's segment at the time that the block download is complete), and the percentage of blocks retrieved from peers rather than from the origin server. Signed-off-by: Michael Brown --- src/include/ipxe/peerdisc.h | 6 ++++ src/include/ipxe/peermux.h | 13 +++++++++ src/net/peerblk.c | 8 ++++++ src/net/peerdisc.c | 30 +++++++++++++++++++ src/net/peermux.c | 57 +++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+) diff --git a/src/include/ipxe/peerdisc.h b/src/include/ipxe/peerdisc.h index f08ccaae2..45d592e76 100644 --- a/src/include/ipxe/peerdisc.h +++ b/src/include/ipxe/peerdisc.h @@ -109,6 +109,12 @@ peerdisc_init ( struct peerdisc_client *peerdisc, extern unsigned int peerdisc_timeout_secs; +extern void peerdisc_stat ( struct interface *intf, struct peerdisc_peer *peer, + struct list_head *peers ); +#define peerdisc_stat_TYPE( object_type ) \ + typeof ( void ( object_type, struct peerdisc_peer *peer, \ + struct list_head *peers ) ) + extern int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, size_t len ); extern void peerdisc_close ( struct peerdisc_client *peerdisc ); diff --git a/src/include/ipxe/peermux.h b/src/include/ipxe/peermux.h index 44cbdb9d6..54acbfec9 100644 --- a/src/include/ipxe/peermux.h +++ b/src/include/ipxe/peermux.h @@ -41,6 +41,16 @@ struct peerdist_multiplexed_block { struct interface xfer; }; +/** PeerDist statistics */ +struct peerdist_statistics { + /** Maximum observed number of peers */ + unsigned int peers; + /** Number of blocks downloaded in total */ + unsigned int total; + /** Number of blocks downloaded from peers */ + unsigned int local; +}; + /** A PeerDist download multiplexer */ struct peerdist_multiplexer { /** Reference count */ @@ -65,6 +75,9 @@ struct peerdist_multiplexer { struct list_head idle; /** Block downloads */ struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS]; + + /** Statistics */ + struct peerdist_statistics stats; }; extern int peermux_filter ( struct interface *xfer, struct interface *info, diff --git a/src/net/peerblk.c b/src/net/peerblk.c index 9fd52b736..78888d2db 100644 --- a/src/net/peerblk.c +++ b/src/net/peerblk.c @@ -270,6 +270,9 @@ static int peerblk_deliver ( struct peerdist_block *peerblk, */ static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { struct digest_algorithm *digest = peerblk->digest; + struct peerdisc_segment *segment = peerblk->discovery.segment; + struct peerdisc_peer *head; + struct peerdisc_peer *peer; uint8_t hash[digest->digestsize]; unsigned long now = peerblk_timestamp(); @@ -296,6 +299,11 @@ static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { profile_custom ( &peerblk_attempt_success_profiler, ( now - peerblk->attempted ) ); + /* Report peer statistics */ + head = list_entry ( &segment->peers, struct peerdisc_peer, list ); + peer = ( ( peerblk->peer == head ) ? NULL : peerblk->peer ); + peerdisc_stat ( &peerblk->xfer, peer, &segment->peers ); + /* Close download */ peerblk_close ( peerblk, 0 ); return; diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index 4c3cd2ea5..20ac2427b 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -76,6 +76,36 @@ static struct peerdisc_segment * peerdisc_find ( const char *id ); static int peerdisc_discovered ( struct peerdisc_segment *segment, const char *location ); +/****************************************************************************** + * + * Statistics reporting + * + ****************************************************************************** + */ + +/** + * Report peer discovery statistics + * + * @v intf Interface + * @v peer Selected peer (or NULL) + * @v peers List of available peers + */ +void peerdisc_stat ( struct interface *intf, struct peerdisc_peer *peer, + struct list_head *peers ) { + struct interface *dest; + peerdisc_stat_TYPE ( void * ) *op = + intf_get_dest_op ( intf, peerdisc_stat, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object, peer, peers ); + } else { + /* Default is to do nothing */ + } + + intf_put ( dest ); +} + /****************************************************************************** * * Discovery sockets diff --git a/src/net/peermux.c b/src/net/peermux.c index 634c69992..a391ed373 100644 --- a/src/net/peermux.c +++ b/src/net/peermux.c @@ -24,9 +24,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include #include +#include #include #include @@ -74,6 +76,28 @@ static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) { intf_shutdown ( &peermux->info, rc ); } +/** + * Report progress of PeerDist download + * + * @v peermux PeerDist download multiplexer + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int peermux_progress ( struct peerdist_multiplexer *peermux, + struct job_progress *progress ) { + struct peerdist_statistics *stats = &peermux->stats; + unsigned int percentage; + + /* Construct PeerDist status message */ + if ( stats->total ) { + percentage = ( ( 100 * stats->local ) / stats->total ); + snprintf ( progress->message, sizeof ( progress->message ), + "%3d%% from %d peers", percentage, stats->peers ); + } + + return 0; +} + /** * Receive content information * @@ -274,6 +298,35 @@ peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) { return xfer_buffer ( &peermux->xfer ); } +/** + * Record peer discovery statistics + * + * @v peermblk PeerDist multiplexed block download + * @v peer Selected peer (or NULL) + * @v peers List of available peers + */ +static void peermux_block_stat ( struct peerdist_multiplexed_block *peermblk, + struct peerdisc_peer *peer, + struct list_head *peers ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + struct peerdist_statistics *stats = &peermux->stats; + struct peerdisc_peer *tmp; + unsigned int count = 0; + + /* Record maximum number of available peers */ + list_for_each_entry ( tmp, peers, list ) + count++; + if ( count > stats->peers ) + stats->peers = count; + + /* Update block counts */ + if ( peer ) + stats->local++; + stats->total++; + DBGC2 ( peermux, "PEERMUX %p downloaded %d/%d from %d peers\n", + peermux, stats->local, stats->total, stats->peers ); +} + /** * Close multiplexed block download * @@ -303,6 +356,8 @@ static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk, /** Data transfer interface operations */ static struct interface_operation peermux_xfer_operations[] = { + INTF_OP ( job_progress, struct peerdist_multiplexer *, + peermux_progress ), INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ), }; @@ -330,6 +385,8 @@ static struct interface_operation peermux_block_operations[] = { peermux_block_deliver ), INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *, peermux_block_buffer ), + INTF_OP ( peerdisc_stat, struct peerdist_multiplexed_block *, + peermux_block_stat ), INTF_OP ( intf_close, struct peerdist_multiplexed_block *, peermux_block_close ), }; From 8047baf7c66601f4e3aa14a3dcb60f3679f03a79 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Sep 2017 10:49:22 +0100 Subject: [PATCH 511/591] [netdevice] Add "hwaddr" setting Expose the underlying hardware address as a setting. For IPoIB devices, this provides scripts with access to the Infiniband GUID. Requested-by: Allen, Benjamin S. Signed-off-by: Michael Brown --- src/net/netdev_settings.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index c54288d4f..cc2e10354 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -45,6 +45,11 @@ const struct setting mac_setting __setting ( SETTING_NETDEV, mac ) = { .description = "MAC address", .type = &setting_type_hex, }; +const struct setting hwaddr_setting __setting ( SETTING_NETDEV, hwaddr ) = { + .name = "hwaddr", + .description = "Hardware address", + .type = &setting_type_hex, +}; const struct setting bustype_setting __setting ( SETTING_NETDEV, bustype ) = { .name = "bustype", .description = "Bus type", @@ -78,7 +83,7 @@ const struct setting mtu_setting __setting ( SETTING_NETDEV, mtu ) = { }; /** - * Store MAC address setting + * Store link-layer address setting * * @v netdev Network device * @v data Setting data, or NULL to clear setting @@ -103,7 +108,7 @@ static int netdev_store_mac ( struct net_device *netdev, } /** - * Fetch MAC address setting + * Fetch link-layer address setting * * @v netdev Network device * @v data Buffer to fill with setting data @@ -112,11 +117,30 @@ static int netdev_store_mac ( struct net_device *netdev, */ static int netdev_fetch_mac ( struct net_device *netdev, void *data, size_t len ) { + size_t max_len = netdev->ll_protocol->ll_addr_len; - if ( len > netdev->ll_protocol->ll_addr_len ) - len = netdev->ll_protocol->ll_addr_len; + if ( len > max_len ) + len = max_len; memcpy ( data, netdev->ll_addr, len ); - return netdev->ll_protocol->ll_addr_len; + return max_len; +} + +/** + * Fetch hardware address setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_hwaddr ( struct net_device *netdev, void *data, + size_t len ) { + size_t max_len = netdev->ll_protocol->hw_addr_len; + + if ( len > max_len ) + len = max_len; + memcpy ( data, netdev->hw_addr, len ); + return max_len; } /** @@ -253,6 +277,7 @@ struct netdev_setting_operation { /** Network device settings */ static struct netdev_setting_operation netdev_setting_operations[] = { { &mac_setting, netdev_store_mac, netdev_fetch_mac }, + { &hwaddr_setting, NULL, netdev_fetch_hwaddr }, { &bustype_setting, NULL, netdev_fetch_bustype }, { &busloc_setting, NULL, netdev_fetch_busloc }, { &busid_setting, NULL, netdev_fetch_busid }, From 53f273af90df0b88a406d5eadf41b4203d516a4a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Sep 2017 11:43:22 +0100 Subject: [PATCH 512/591] [resolv] Use pass-through interfaces for name resolution multiplexer Signed-off-by: Michael Brown --- src/core/resolv.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/core/resolv.c b/src/core/resolv.c index 1e3182b0b..fab8def4b 100644 --- a/src/core/resolv.c +++ b/src/core/resolv.c @@ -180,19 +180,16 @@ static int resmux_try ( struct resolv_mux *mux ) { } /** - * Child resolved name + * Close name resolution multiplexer * * @v mux Name resolution multiplexer - * @v sa Completed socket address + * @v rc Reason for close */ -static void resmux_child_resolv_done ( struct resolv_mux *mux, - struct sockaddr *sa ) { +static void resmux_close ( struct resolv_mux *mux, int rc ) { - DBGC ( mux, "RESOLV %p resolved \"%s\" to %s using method %s\n", - mux, mux->name, sock_ntoa ( sa ), mux->resolver->name ); - - /* Pass resolution to parent */ - resolv_done ( &mux->parent, sa ); + /* Shut down all interfaces */ + intf_shutdown ( &mux->child, rc ); + intf_shutdown ( &mux->parent, rc ); } /** @@ -226,18 +223,28 @@ static void resmux_child_close ( struct resolv_mux *mux, int rc ) { return; finished: - intf_shutdown ( &mux->parent, rc ); + resmux_close ( mux, rc ); } /** Name resolution multiplexer child interface operations */ static struct interface_operation resmux_child_op[] = { - INTF_OP ( resolv_done, struct resolv_mux *, resmux_child_resolv_done ), INTF_OP ( intf_close, struct resolv_mux *, resmux_child_close ), }; /** Name resolution multiplexer child interface descriptor */ static struct interface_descriptor resmux_child_desc = - INTF_DESC ( struct resolv_mux, child, resmux_child_op ); + INTF_DESC_PASSTHRU ( struct resolv_mux, child, resmux_child_op, + parent ); + +/** Name resolution multiplexer parent interface operations */ +static struct interface_operation resmux_parent_op[] = { + INTF_OP ( intf_close, struct resolv_mux *, resmux_close ), +}; + +/** Name resolution multiplexer parent interface descriptor */ +static struct interface_descriptor resmux_parent_desc = + INTF_DESC_PASSTHRU ( struct resolv_mux, parent, resmux_parent_op, + child ); /** * Start name resolution @@ -258,7 +265,7 @@ int resolv ( struct interface *resolv, const char *name, if ( ! mux ) return -ENOMEM; ref_init ( &mux->refcnt, NULL ); - intf_init ( &mux->parent, &null_intf_desc, &mux->refcnt ); + intf_init ( &mux->parent, &resmux_parent_desc, &mux->refcnt ); intf_init ( &mux->child, &resmux_child_desc, &mux->refcnt ); mux->resolver = table_start ( RESOLVERS ); if ( sa ) @@ -338,7 +345,8 @@ static struct interface_operation named_xfer_ops[] = { /** Named socket opener data transfer interface descriptor */ static struct interface_descriptor named_xfer_desc = - INTF_DESC ( struct named_socket, xfer, named_xfer_ops ); + INTF_DESC_PASSTHRU ( struct named_socket, xfer, named_xfer_ops, + resolv ); /** * Name resolved @@ -379,7 +387,8 @@ static struct interface_operation named_resolv_op[] = { /** Named socket opener resolver interface descriptor */ static struct interface_descriptor named_resolv_desc = - INTF_DESC ( struct named_socket, resolv, named_resolv_op ); + INTF_DESC_PASSTHRU ( struct named_socket, resolv, named_resolv_op, + xfer ); /** * Open named socket From 9faf069126b37cef87a47209b661de434d9e71cf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Sep 2017 11:46:13 +0100 Subject: [PATCH 513/591] [dns] Report current DNS query as job progress status message Signed-off-by: Michael Brown --- src/net/udp/dns.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index e849472de..897e0f857 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -867,6 +868,22 @@ static void dns_xfer_close ( struct dns_request *dns, int rc ) { dns_done ( dns, rc ); } +/** + * Report job progress + * + * @v dns DNS request + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int dns_progress ( struct dns_request *dns, + struct job_progress *progress ) { + + /* Show current question as progress message */ + dns_decode ( &dns->name, progress->message, + sizeof ( progress->message ) ); + return 0; +} + /** DNS socket interface operations */ static struct interface_operation dns_socket_operations[] = { INTF_OP ( xfer_deliver, struct dns_request *, dns_xfer_deliver ), @@ -879,6 +896,7 @@ static struct interface_descriptor dns_socket_desc = /** DNS resolver interface operations */ static struct interface_operation dns_resolv_op[] = { + INTF_OP ( job_progress, struct dns_request *, dns_progress ), INTF_OP ( intf_close, struct dns_request *, dns_done ), }; From 3f429bdcfe0c3698f886e93a1f603656ccaa0f28 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Sep 2017 23:18:29 +0100 Subject: [PATCH 514/591] [efi] Check buffer length for packets retrieved via our SNP protocol We do not currently check the length of the caller's buffer for received packets. This creates a potential buffer overrun when iPXE is being used via the SNP or UNDI protocols. Fix by checking the buffer length and correctly returning the required length and an EFI_BUFFER_TOO_SMALL error. Reported-by: Paul McMillan Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index e6388bf67..5c3592e52 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -710,6 +710,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, const void *iob_ll_src; uint16_t iob_net_proto; unsigned int iob_flags; + size_t max_len; int rc; DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, @@ -722,19 +723,28 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, /* Poll the network device */ efi_snp_poll ( snpdev ); - /* Dequeue a packet, if one is available */ + /* Check for an available packet */ iobuf = list_first_entry ( &snpdev->rx, struct io_buffer, list ); if ( ! iobuf ) { DBGC2 ( snpdev, "\n" ); rc = -EAGAIN; goto out_no_packet; } - list_del ( &iobuf->list ); DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); + /* Check buffer length */ + max_len = *len; + *len = iob_len ( iobuf ); + if ( *len > max_len ) { + rc = -ERANGE; + goto out_too_long; + } + + /* Dequeue packet */ + list_del ( &iobuf->list ); + /* Return packet to caller */ memcpy ( data, iobuf->data, iob_len ( iobuf ) ); - *len = iob_len ( iobuf ); /* Attempt to decode link-layer header */ if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest, @@ -759,6 +769,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, out_bad_ll_header: free_iob ( iobuf ); + out_too_long: out_no_packet: return EFIRC ( rc ); } From e8f30571a375777fc83c14602f9ed32ac26eedb4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Sep 2017 23:56:22 +0100 Subject: [PATCH 515/591] [efi] Match behaviour of SnpDxe for truncated received packets The UEFI specification does not state whether or not a return value of EFI_BUFFER_TOO_SMALL from the SNP Receive() method should follow the usual EFI API behaviour of allowing the caller to retry the request with an increased buffer size. Examination of the SnpDxe driver in EDK2 suggests that Receive() will just return the truncated packet (complete with any requested link-layer header fields), so match this behaviour. Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 5c3592e52..263a25ac6 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -710,7 +710,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, const void *iob_ll_src; uint16_t iob_net_proto; unsigned int iob_flags; - size_t max_len; + size_t copy_len; int rc; DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, @@ -732,19 +732,15 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, } DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); - /* Check buffer length */ - max_len = *len; - *len = iob_len ( iobuf ); - if ( *len > max_len ) { - rc = -ERANGE; - goto out_too_long; - } - /* Dequeue packet */ list_del ( &iobuf->list ); - /* Return packet to caller */ - memcpy ( data, iobuf->data, iob_len ( iobuf ) ); + /* Return packet to caller, truncating to buffer length */ + copy_len = iob_len ( iobuf ); + if ( copy_len > *len ) + copy_len = *len; + memcpy ( data, iobuf->data, copy_len ); + *len = iob_len ( iobuf ); /* Attempt to decode link-layer header */ if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest, @@ -765,11 +761,11 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, if ( net_proto ) *net_proto = ntohs ( iob_net_proto ); - rc = 0; + /* Check buffer length */ + rc = ( ( copy_len == *len ) ? 0 : -ERANGE ); out_bad_ll_header: free_iob ( iobuf ); - out_too_long: out_no_packet: return EFIRC ( rc ); } From af02a8d07193e8b8900a37a8f42aefcdc332e68c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 7 Sep 2017 12:17:18 +0100 Subject: [PATCH 516/591] [dns] Ensure DNS names are NUL-terminated when used as diagnostic strings Signed-off-by: Michael Brown --- src/net/udp/dns.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index 897e0f857..f412f7109 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -417,7 +417,7 @@ static const char * dns_name ( struct dns_name *name ) { static char buf[256]; int len; - len = dns_decode ( name, buf, sizeof ( buf ) ); + len = dns_decode ( name, buf, ( sizeof ( buf ) - 1 /* NUL */ ) ); return ( ( len < 0 ) ? "" : buf ); } @@ -877,10 +877,16 @@ static void dns_xfer_close ( struct dns_request *dns, int rc ) { */ static int dns_progress ( struct dns_request *dns, struct job_progress *progress ) { + int len; /* Show current question as progress message */ - dns_decode ( &dns->name, progress->message, - sizeof ( progress->message ) ); + len = dns_decode ( &dns->name, progress->message, + ( sizeof ( progress->message ) - 1 /* NUL */ ) ); + if ( len < 0 ) { + /* Ignore undecodable names */ + progress->message[0] = '\0'; + } + return 0; } From f756fd78f7bbdb9476f88d8d92fbe47bde123636 Mon Sep 17 00:00:00 2001 From: Peter von Konigsmark Date: Wed, 6 Sep 2017 00:06:42 +0100 Subject: [PATCH 517/591] [exanic] Power up optical PHYs (if present) Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/exanic.c | 3 +++ src/drivers/net/exanic.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/drivers/net/exanic.c b/src/drivers/net/exanic.c index 62296927a..64a47b454 100644 --- a/src/drivers/net/exanic.c +++ b/src/drivers/net/exanic.c @@ -812,6 +812,9 @@ static int exanic_probe ( struct pci_device *pci ) { /* Read capabilities */ exanic->caps = readl ( exanic->regs + EXANIC_CAPS ); + /* Power up PHYs */ + writel ( EXANIC_POWER_ON, ( exanic->regs + EXANIC_POWER ) ); + /* Fetch base MAC address */ if ( ( rc = exanic_fetch_mac ( exanic ) ) != 0 ) goto err_fetch_mac; diff --git a/src/drivers/net/exanic.h b/src/drivers/net/exanic.h index fd9f5b8ce..041b9e21a 100644 --- a/src/drivers/net/exanic.h +++ b/src/drivers/net/exanic.h @@ -62,6 +62,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** I2C GPIO register */ #define EXANIC_I2C 0x012c +/** Power control register */ +#define EXANIC_POWER 0x0138 +#define EXANIC_POWER_ON 0x000000f0UL /**< Power on PHYs */ + /** Port register offset */ #define EXANIC_PORT_REGS( index ) ( 0x0200 + ( 0x40 * (index) ) ) From 9720f8396fc354d04ba70ae292c5c2b8f7fe21b8 Mon Sep 17 00:00:00 2001 From: Peter von Konigsmark Date: Thu, 7 Sep 2017 12:37:09 +0100 Subject: [PATCH 518/591] [exanic] Add PCI device ID for another X40 variant Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/exanic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/exanic.c b/src/drivers/net/exanic.c index 64a47b454..287e14e8d 100644 --- a/src/drivers/net/exanic.c +++ b/src/drivers/net/exanic.c @@ -903,6 +903,7 @@ static struct pci_device_id exanic_ids[] = { PCI_ROM ( 0x1ce4, 0x0004, "exanic-x10gm", "ExaNIC X10 GM", 0 ), PCI_ROM ( 0x1ce4, 0x0005, "exanic-x40", "ExaNIC X40", 0 ), PCI_ROM ( 0x1ce4, 0x0006, "exanic-x10hpt", "ExaNIC X10 HPT", 0 ), + PCI_ROM ( 0x1ce4, 0x0007, "exanic-x40g", "ExaNIC X40", 0 ), }; /** ExaNIC PCI driver */ From d46c53cfc6fe98fbb51afc2560dac26703e3d178 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 13 Sep 2017 10:07:55 +0300 Subject: [PATCH 519/591] [efi] Continue to connect remaining handles after connection errors Some UEFI BIOSes will deliberately break the implementation of ConnectController() to return errors for devices that have been "disabled" via the BIOS setup screen. (As an added bonus, such BIOSes may return garbage EFI_STATUS values such as 0xff.) Work around these broken UEFI BIOSes by ignoring failures and continuing to attempt to connect any remaining handles. Signed-off-by: Michael Brown --- src/interface/efi/efi_driver.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 22aa3ee72..2cefed2fd 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -497,14 +497,16 @@ static int efi_driver_handles ( int ( * method ) ( EFI_HANDLE handle ) ) { /* Connect/disconnect driver from all handles */ for ( i = 0 ; i < num_handles ; i++ ) { - if ( ( rc = method ( handles[i] ) ) != 0 ) - goto err_method; + if ( ( rc = method ( handles[i] ) ) != 0 ) { + /* Ignore errors and continue to process + * remaining handles. + */ + } } /* Success */ rc = 0; - err_method: bs->FreePool ( handles ); err_locate: return rc; From 7428ab7258e03ddef79ba774432b7b8c11cc2730 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 18 Sep 2017 13:32:39 +0100 Subject: [PATCH 520/591] [build] Exclude selected directories from Secure Boot builds When submitting binaries for UEFI Secure Boot signing, certain known-dubious subsystems (such as 802.11 and NFS) must be excluded from the build. Mark the directories containing these subsystems as insecure, and allow the build target to include an explicit "security flag" (a literal "-sb" appended to the build platform) to exclude these source directories from the build process. For example: make bin-x86_64-efi-sb/ipxe.efi will build iPXE with all code from the 802.11 and NFS subsystems excluded from the build. Signed-off-by: Michael Brown --- src/Makefile | 16 +++++++++++----- src/Makefile.housekeeping | 33 +++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/Makefile b/src/Makefile index 911d27ab5..d74565d13 100644 --- a/src/Makefile +++ b/src/Makefile @@ -62,7 +62,7 @@ QEMUIMG := qemu-img SRCDIRS := SRCDIRS += libgcc SRCDIRS += core -SRCDIRS += net net/oncrpc net/tcp net/udp net/infiniband net/80211 +SRCDIRS += net net/tcp net/udp net/infiniband SRCDIRS += image SRCDIRS += drivers/bus SRCDIRS += drivers/net @@ -71,10 +71,6 @@ SRCDIRS += drivers/net/e1000e SRCDIRS += drivers/net/igb SRCDIRS += drivers/net/igbvf SRCDIRS += drivers/net/phantom -SRCDIRS += drivers/net/rtl818x -SRCDIRS += drivers/net/ath -SRCDIRS += drivers/net/ath/ath5k -SRCDIRS += drivers/net/ath/ath9k SRCDIRS += drivers/net/vxge SRCDIRS += drivers/net/efi SRCDIRS += drivers/net/tg3 @@ -105,6 +101,16 @@ SRCDIRS += hci/keymap SRCDIRS += usr SRCDIRS += config +# These directories contain code that is not eligible for UEFI Secure +# Boot signing. +# +SRCDIRS_INSEC += net/oncrpc +SRCDIRS_INSEC += net/80211 +SRCDIRS_INSEC += drivers/net/rtl818x +SRCDIRS_INSEC += drivers/net/ath +SRCDIRS_INSEC += drivers/net/ath/ath5k +SRCDIRS_INSEC += drivers/net/ath/ath9k + # NON_AUTO_SRCS lists files that are excluded from the normal # automatic build system. # diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index f09db3728..00b079263 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -299,7 +299,7 @@ endif # # Select build architecture and platform based on $(BIN) # -# BIN has the form bin[-[arch-]platform] +# BIN has the form bin[-[-][-sb]] ARCHS := $(patsubst arch/%,%,$(wildcard arch/*)) PLATFORMS := $(patsubst config/defaults/%.h,%,\ @@ -312,17 +312,18 @@ platforms : ifdef BIN -# Determine architecture portion of $(BIN), if present -BIN_ARCH := $(strip $(foreach A,$(ARCHS),\ - $(patsubst bin-$(A)-%,$(A),\ - $(filter bin-$(A)-%,$(BIN))))) - -# Determine platform portion of $(BIN), if present -ifeq ($(BIN_ARCH),) -BIN_PLATFORM := $(patsubst bin-%,%,$(filter bin-%,$(BIN))) +# Split $(BIN) into architecture, platform, and security flag (where present) +BIN_ELEMENTS := $(subst -,$(SPACE),$(BIN)) +BIN_APS := $(wordlist 2,4,$(BIN_ELEMENTS)) +ifeq ($(lastword $(BIN_APS)),sb) +BIN_AP := $(wordlist 2,$(words $(BIN_APS)),discard $(BIN_APS)) +BIN_SECUREBOOT := 1 else -BIN_PLATFORM := $(patsubst bin-$(BIN_ARCH)-%,%,$(BIN)) +BIN_AP := $(BIN_APS) +BIN_SECUREBOOT := 0 endif +BIN_PLATFORM := $(lastword $(BIN_AP)) +BIN_ARCH := $(wordlist 2,$(words $(BIN_AP)),discard $(BIN_AP)) # Determine build architecture DEFAULT_ARCH := i386 @@ -339,6 +340,13 @@ CFLAGS += -DPLATFORM=$(PLATFORM) platform : @$(ECHO) $(PLATFORM) +# Determine security flag +DEFAULT_SECUREBOOT := 0 +SECUREBOOT := $(firstword $(BIN_SECUREBOOT) $(DEFAULT_SECUREBOOT)) +CFLAGS += -DSECUREBOOT=$(SECUREBOOT) +secureboot : + @$(ECHO) $(SECUREBOOT) + endif # defined(BIN) # Include architecture-specific Makefile @@ -357,6 +365,11 @@ endif # # Source file handling +# Exclude known-insecure files from Secure Boot builds +ifeq ($(SECUREBOOT),0) +SRCDIRS += $(SRCDIRS_INSEC) +endif + # SRCDIRS lists all directories containing source files. srcdirs : @$(ECHO) $(SRCDIRS) From 74d90b33f8490adcee2026ece18d8411d93b6a39 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 22 Sep 2017 14:02:40 +0100 Subject: [PATCH 521/591] [efi] Inhibit our driver Start() method during disconnection attempts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some HP BIOSes (observed with a Z840) seem to attempt to connect our drivers in the middle of our call to DisconnectController(). The precise chain of events is unclear, but the symptom is that we see several calls to our Supported() and Start() methods, followed by a system lock-up. Work around this dubious BIOS behaviour by explicitly failing calls to our Start() method while we are in the middle of attempting to disconnect drivers. Reported-by: Jordan Wright Debugged-by: Adrian Lucrèce Céleste Debugged-by: Christian Nilsson Tested-by: Jordan Wright Signed-off-by: Michael Brown --- src/interface/efi/efi_driver.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 2cefed2fd..de0f61f92 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -44,6 +44,9 @@ static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding; /** List of controlled EFI devices */ static LIST_HEAD ( efi_devices ); +/** We are currently disconnecting drivers */ +static int efi_driver_disconnecting; + /** * Find EFI device * @@ -159,6 +162,14 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, goto err_already_started; } + /* Do nothing if we are currently disconnecting drivers */ + if ( efi_driver_disconnecting ) { + DBGC ( device, "EFIDRV %s refusing to start during " + "disconnection\n", efi_handle_name ( device ) ); + efirc = EFI_NOT_READY; + goto err_disconnecting; + } + /* Open device path */ if ( ( efirc = bs->OpenProtocol ( device, &efi_device_path_protocol_guid, @@ -220,6 +231,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, efi_image_handle, device ); } err_open_path: + err_disconnecting: err_already_started: return efirc; } @@ -411,6 +423,7 @@ static int efi_driver_connect ( EFI_HANDLE device ) { DBGC2_EFI_PROTOCOLS ( device, device ); DBGC ( device, "EFIDRV %s disconnecting existing drivers\n", efi_handle_name ( device ) ); + efi_driver_disconnecting = 1; if ( ( efirc = bs->DisconnectController ( device, NULL, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); @@ -419,6 +432,7 @@ static int efi_driver_connect ( EFI_HANDLE device ) { strerror ( rc ) ); /* Ignore the error and attempt to connect our drivers */ } + efi_driver_disconnecting = 0; DBGC2 ( device, "EFIDRV %s after disconnecting:\n", efi_handle_name ( device ) ); DBGC2_EFI_PROTOCOLS ( device, device ); @@ -450,9 +464,11 @@ static int efi_driver_disconnect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; /* Disconnect our driver */ + efi_driver_disconnecting = 1; bs->DisconnectController ( device, efi_driver_binding.DriverBindingHandle, NULL ); + efi_driver_disconnecting = 0; return 0; } From 0631a46a94fdf86992f18b50921c42e42a822bab Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 20 Sep 2017 11:52:16 +0200 Subject: [PATCH 522/591] [crypto] Fail fast if cross-certificate source is empty In fully self-contained deployments it may be desirable to build iPXE with an empty CROSSCERT source to avoid talking to external services. Add an explicit check for this case and make validator_start_download fail immediately if the base URI is empty. Signed-off-by: Ladi Prosek Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/net/validator.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/net/validator.c b/src/net/validator.c index 52845b6ed..68abe1b55 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -239,6 +239,10 @@ static int validator_start_download ( struct validator *validator, /* Determine cross-signed certificate source */ fetch_string_setting_copy ( NULL, &crosscert_setting, &crosscert_copy ); crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default ); + if ( ! crosscert[0] ) { + rc = -EINVAL; + goto err_check_uri_string; + } /* Allocate URI string */ uri_string_len = ( strlen ( crosscert ) + 22 /* "/%08x.der?subject=" */ @@ -277,6 +281,7 @@ static int validator_start_download ( struct validator *validator, err_open_uri_string: free ( uri_string ); err_alloc_uri_string: + err_check_uri_string: free ( crosscert_copy ); return rc; } From c4ce92599da7804268906fad2ba5d3564b1e9b83 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 10 Sep 2017 17:12:16 +0200 Subject: [PATCH 523/591] [efi] Accept (and ignore) R_ARM_V4BX relocations Relocation type R_ARM_V4BX requires no computation. It marks the location of an ARMv4 branch exchange instruction. Signed-off-by: Heinrich Schuchardt Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/util/elf2efi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 27f37d98a..84122032c 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -630,6 +630,7 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, case ELF_MREL ( EM_ARM, R_ARM_CALL ) : case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) : case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) : + case ELF_MREL ( EM_ARM, R_ARM_V4BX ): case ELF_MREL ( EM_X86_64, R_X86_64_PC32 ) : case ELF_MREL ( EM_AARCH64, R_AARCH64_CALL26 ) : case ELF_MREL ( EM_AARCH64, R_AARCH64_JUMP26 ) : From 1b67a0564657b6fcef18b1588ea6491ca1b1996d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 24 Sep 2017 19:26:58 +0100 Subject: [PATCH 524/591] [efi] Allow for building with older versions of elf.h system header Signed-off-by: Michael Brown --- src/util/elf2efi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 84122032c..e8e6c523e 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -93,6 +93,9 @@ #ifndef R_ARM_THM_JUMP24 #define R_ARM_THM_JUMP24 30 #endif +#ifndef R_ARM_V4BX +#define R_ARM_V4BX 40 +#endif /* Seems to be missing from elf.h */ #ifndef R_AARCH64_NULL From fb6b66ce13af689b0b622b0898670dece645a8ab Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 11 Nov 2017 23:44:50 +0000 Subject: [PATCH 525/591] [crypto] Fix endianness typo in comment Signed-off-by: Michael Brown --- src/crypto/md5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/md5.c b/src/crypto/md5.c index f9738b0ac..493591f3c 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -213,7 +213,7 @@ static void md5_digest ( struct md5_context *context ) { i, *a, *b, *c, *d ); } - /* Add chunk to hash and convert back to big-endian */ + /* Add chunk to hash and convert back to little-endian */ for ( i = 0 ; i < 4 ; i++ ) { context->ddd.dd.digest.h[i] = cpu_to_le32 ( context->ddd.dd.digest.h[i] + From 32d54691e97258b6fa3eb79a0ba11a400d89c331 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 12 Nov 2017 18:30:08 +0000 Subject: [PATCH 526/591] [crypto] Eliminate repetitions in MD5 round constant table Signed-off-by: Michael Brown --- src/crypto/md5.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/crypto/md5.c b/src/crypto/md5.c index 493591f3c..185a61f35 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -66,11 +66,11 @@ static const uint32_t k[64] = { }; /** MD5 shift amounts */ -static const uint8_t r[64] = { - 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 +static const uint8_t r[4][4] = { + { 7, 12, 17, 22 }, + { 5, 9, 14, 20 }, + { 4, 11, 16, 23 }, + { 6, 10, 15, 21 }, }; /** @@ -174,6 +174,7 @@ static void md5_digest ( struct md5_context *context ) { uint32_t g; uint32_t temp; struct md5_step *step; + unsigned int round; unsigned int i; /* Sanity checks */ @@ -201,13 +202,15 @@ static void md5_digest ( struct md5_context *context ) { /* Main loop */ for ( i = 0 ; i < 64 ; i++ ) { - step = &md5_steps[ i / 16 ]; + round = ( i / 16 ); + step = &md5_steps[round]; f = step->f ( &u.v ); g = ( ( ( step->coefficient * i ) + step->constant ) % 16 ); temp = *d; *d = *c; *c = *b; - *b = ( *b + rol32 ( ( *a + f + k[i] + w[g] ), r[i] ) ); + *b = ( *b + rol32 ( ( *a + f + k[i] + w[g] ), + r[round][ i % 4 ] ) ); *a = temp; DBGC2 ( context, "%2d : %08x %08x %08x %08x\n", i, *a, *b, *c, *d ); From 0077b0933dea01113336f1b0fb5e0cfecbfc212c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 7 Nov 2017 23:20:10 +0000 Subject: [PATCH 527/591] [crypto] Add MD4 message digest algorithm Signed-off-by: Michael Brown --- src/crypto/md4.c | 280 ++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/asn1.h | 6 + src/include/ipxe/md4.h | 73 +++++++++++ src/tests/md4_test.c | 76 +++++++++++ src/tests/tests.c | 1 + 5 files changed, 436 insertions(+) create mode 100644 src/crypto/md4.c create mode 100644 src/include/ipxe/md4.h create mode 100644 src/tests/md4_test.c diff --git a/src/crypto/md4.c b/src/crypto/md4.c new file mode 100644 index 000000000..f4a8d78df --- /dev/null +++ b/src/crypto/md4.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2017 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 + * + * MD4 algorithm + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** MD4 variables */ +struct md4_variables { + /* This layout matches that of struct md4_digest_data, + * allowing for efficient endianness-conversion, + */ + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t w[16]; +} __attribute__ (( packed )); + +/** MD4 shift amounts */ +static const uint8_t r[3][4] = { + { 3, 7, 11, 19 }, + { 3, 5, 9, 13 }, + { 3, 9, 11, 15 }, +}; + +/** + * f(b,c,d,w) for steps 0 to 15 + * + * @v v MD4 variables + * @v i Index within round + * @ret f f(b,c,d,w) + */ +static uint32_t md4_f_0_15 ( struct md4_variables *v, unsigned int i ) { + return ( ( ( v->b & v->c ) | ( ~v->b & v->d ) ) + v->w[i] ); +} + +/** + * f(b,c,d,w) for steps 16 to 31 + * + * @v v MD4 variables + * @v i Index within round + * @ret f f(b,c,d,w) + */ +static uint32_t md4_f_16_31 ( struct md4_variables *v, unsigned int i ) { + return ( ( ( v->b & v->c ) | ( v->b & v->d ) | ( v->c & v->d ) ) + + v->w[ ( ( i << 2 ) | ( i >> 2 ) ) % 16 ] ); +} + +/** + * f(b,c,d,w) for steps 32 to 47 + * + * @v v MD4 variables + * @v i Index within round + * @ret f f(b,c,d,w) + */ +static uint32_t md4_f_32_47 ( struct md4_variables *v, unsigned int i ) { + static const uint8_t reverse[16] = { + 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 + }; + return ( ( v->b ^ v->c ^ v->d ) + v->w[reverse[i]] ); +} + +/** An MD4 step function */ +struct md4_step { + /** + * Calculate f(b,c,d,w) + * + * @v v MD4 variables + * @v i Index within round + * @ret f f(b,c,d,w) + */ + uint32_t ( * f ) ( struct md4_variables *v, unsigned int i ); + /** Constant */ + uint32_t constant; +}; + +/** MD4 steps */ +static struct md4_step md4_steps[4] = { + /** 0 to 15 */ + { .f = md4_f_0_15, .constant = 0x00000000UL }, + /** 16 to 31 */ + { .f = md4_f_16_31, .constant = 0x5a827999UL }, + /** 32 to 47 */ + { .f = md4_f_32_47, .constant = 0x6ed9eba1UL }, +}; + +/** + * Initialise MD4 algorithm + * + * @v ctx MD4 context + */ +static void md4_init ( void *ctx ) { + struct md4_context *context = ctx; + + context->ddd.dd.digest.h[0] = cpu_to_le32 ( 0x67452301 ); + context->ddd.dd.digest.h[1] = cpu_to_le32 ( 0xefcdab89 ); + context->ddd.dd.digest.h[2] = cpu_to_le32 ( 0x98badcfe ); + context->ddd.dd.digest.h[3] = cpu_to_le32 ( 0x10325476 ); + context->len = 0; +} + +/** + * Calculate MD4 digest of accumulated data + * + * @v context MD4 context + */ +static void md4_digest ( struct md4_context *context ) { + union { + union md4_digest_data_dwords ddd; + struct md4_variables v; + } u; + uint32_t *a = &u.v.a; + uint32_t *b = &u.v.b; + uint32_t *c = &u.v.c; + uint32_t *d = &u.v.d; + uint32_t *w = u.v.w; + uint32_t f; + uint32_t temp; + struct md4_step *step; + unsigned int round; + unsigned int i; + + /* Sanity checks */ + assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); + linker_assert ( &u.ddd.dd.digest.h[0] == a, md4_bad_layout ); + linker_assert ( &u.ddd.dd.digest.h[1] == b, md4_bad_layout ); + linker_assert ( &u.ddd.dd.digest.h[2] == c, md4_bad_layout ); + linker_assert ( &u.ddd.dd.digest.h[3] == d, md4_bad_layout ); + linker_assert ( &u.ddd.dd.data.dword[0] == w, md4_bad_layout ); + + DBGC ( context, "MD4 digesting:\n" ); + DBGC_HDA ( context, 0, &context->ddd.dd.digest, + sizeof ( context->ddd.dd.digest ) ); + DBGC_HDA ( context, context->len, &context->ddd.dd.data, + sizeof ( context->ddd.dd.data ) ); + + /* Convert h[0..3] to host-endian, and initialise a, b, c, d, + * and x[0..15] + */ + for ( i = 0 ; i < ( sizeof ( u.ddd.dword ) / + sizeof ( u.ddd.dword[0] ) ) ; i++ ) { + le32_to_cpus ( &context->ddd.dword[i] ); + u.ddd.dword[i] = context->ddd.dword[i]; + } + + /* Main loop */ + for ( i = 0 ; i < 48 ; i++ ) { + round = ( i / 16 ); + step = &md4_steps[round]; + f = step->f ( &u.v, ( i % 16 ) ); + temp = *d; + *d = *c; + *c = *b; + *b = rol32 ( ( *a + f + step->constant ), r[round][ i % 4 ] ); + *a = temp; + DBGC2 ( context, "%2d : %08x %08x %08x %08x\n", + i, *a, *b, *c, *d ); + } + + /* Add chunk to hash and convert back to little-endian */ + for ( i = 0 ; i < 4 ; i++ ) { + context->ddd.dd.digest.h[i] = + cpu_to_le32 ( context->ddd.dd.digest.h[i] + + u.ddd.dd.digest.h[i] ); + } + + DBGC ( context, "MD4 digested:\n" ); + DBGC_HDA ( context, 0, &context->ddd.dd.digest, + sizeof ( context->ddd.dd.digest ) ); +} + +/** + * Accumulate data with MD4 algorithm + * + * @v ctx MD4 context + * @v data Data + * @v len Length of data + */ +static void md4_update ( void *ctx, const void *data, size_t len ) { + struct md4_context *context = ctx; + const uint8_t *byte = data; + size_t offset; + + /* Accumulate data a byte at a time, performing the digest + * whenever we fill the data buffer + */ + while ( len-- ) { + offset = ( context->len % sizeof ( context->ddd.dd.data ) ); + context->ddd.dd.data.byte[offset] = *(byte++); + context->len++; + if ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ) + md4_digest ( context ); + } +} + +/** + * Generate MD4 digest + * + * @v ctx MD4 context + * @v out Output buffer + */ +static void md4_final ( void *ctx, void *out ) { + struct md4_context *context = ctx; + uint64_t len_bits; + uint8_t pad; + + /* Record length before pre-processing */ + len_bits = cpu_to_le64 ( ( ( uint64_t ) context->len ) * 8 ); + + /* Pad with a single "1" bit followed by as many "0" bits as required */ + pad = 0x80; + do { + md4_update ( ctx, &pad, sizeof ( pad ) ); + pad = 0x00; + } while ( ( context->len % sizeof ( context->ddd.dd.data ) ) != + offsetof ( typeof ( context->ddd.dd.data ), final.len ) ); + + /* Append length (in bits) */ + md4_update ( ctx, &len_bits, sizeof ( len_bits ) ); + assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); + + /* Copy out final digest */ + memcpy ( out, &context->ddd.dd.digest, + sizeof ( context->ddd.dd.digest ) ); +} + +/** MD4 algorithm */ +struct digest_algorithm md4_algorithm = { + .name = "md4", + .ctxsize = sizeof ( struct md4_context ), + .blocksize = sizeof ( union md4_block ), + .digestsize = sizeof ( struct md4_digest ), + .init = md4_init, + .update = md4_update, + .final = md4_final, +}; + +/** "md4" object identifier */ +static uint8_t oid_md4[] = { ASN1_OID_MD4 }; + +/** "md4" OID-identified algorithm */ +struct asn1_algorithm oid_md4_algorithm __asn1_algorithm = { + .name = "md4", + .digest = &md4_algorithm, + .oid = ASN1_OID_CURSOR ( oid_md4 ), +}; diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 847d845bb..24caecdc5 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -161,6 +161,12 @@ struct asn1_builder_header { ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 14 ) +/** ASN.1 OID for id-md4 (1.2.840.113549.2.4) */ +#define ASN1_OID_MD4 \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 2 ), \ + ASN1_OID_SINGLE ( 4 ) + /** ASN.1 OID for id-md5 (1.2.840.113549.2.5) */ #define ASN1_OID_MD5 \ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ diff --git a/src/include/ipxe/md4.h b/src/include/ipxe/md4.h new file mode 100644 index 000000000..8f172e626 --- /dev/null +++ b/src/include/ipxe/md4.h @@ -0,0 +1,73 @@ +#ifndef _IPXE_MD4_H +#define _IPXE_MD4_H + +/** @file + * + * MD4 algorithm + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** An MD4 digest */ +struct md4_digest { + /** Hash output */ + uint32_t h[4]; +}; + +/** An MD4 data block */ +union md4_block { + /** Raw bytes */ + uint8_t byte[64]; + /** Raw dwords */ + uint32_t dword[16]; + /** Final block structure */ + struct { + /** Padding */ + uint8_t pad[56]; + /** Length in bits */ + uint64_t len; + } final; +}; + +/** MD4 digest and data block + * + * The order of fields within this structure is designed to minimise + * code size. + */ +struct md4_digest_data { + /** Digest of data already processed */ + struct md4_digest digest; + /** Accumulated data */ + union md4_block data; +} __attribute__ (( packed )); + +/** MD4 digest and data block */ +union md4_digest_data_dwords { + /** Digest and data block */ + struct md4_digest_data dd; + /** Raw dwords */ + uint32_t dword[ sizeof ( struct md4_digest_data ) / + sizeof ( uint32_t ) ]; +}; + +/** An MD4 context */ +struct md4_context { + /** Amount of accumulated data */ + size_t len; + /** Digest and accumulated data */ + union md4_digest_data_dwords ddd; +} __attribute__ (( packed )); + +/** MD4 context size */ +#define MD4_CTX_SIZE sizeof ( struct md4_context ) + +/** MD4 digest size */ +#define MD4_DIGEST_SIZE sizeof ( struct md4_digest ) + +extern struct digest_algorithm md4_algorithm; + +#endif /* _IPXE_MD4_H */ diff --git a/src/tests/md4_test.c b/src/tests/md4_test.c new file mode 100644 index 000000000..b6528c6ec --- /dev/null +++ b/src/tests/md4_test.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 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 + * + * MD4 tests + * + * Test inputs borrowed from NIST SHA-1 tests, with results calculated + * using "openssl dgst -md4" + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include "digest_test.h" + +/* Empty test vector */ +DIGEST_TEST ( md4_empty, &md4_algorithm, DIGEST_EMPTY, + DIGEST ( 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, 0xb7, + 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( md4_nist_abc, &md4_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52, 0x5f, + 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d ) ); + +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( md4_nist_abc_opq, &md4_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x46, 0x91, 0xa9, 0xec, 0x81, 0xb1, 0xa6, 0xbd, 0x1a, + 0xb8, 0x55, 0x72, 0x40, 0xb2, 0x45, 0xc5 ) ); + +/** + * Perform MD4 self-test + * + */ +static void md4_test_exec ( void ) { + + /* Correctness tests */ + digest_ok ( &md4_empty ); + digest_ok ( &md4_nist_abc ); + digest_ok ( &md4_nist_abc_opq ); + + /* Speed tests */ + DBG ( "MD4 required %ld cycles per byte\n", + digest_cost ( &md4_algorithm ) ); +} + +/** MD4 self-test */ +struct self_test md4_test __self_test = { + .name = "md4", + .exec = md4_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 39c5136ea..a0c4ff5d7 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -46,6 +46,7 @@ REQUIRE_OBJECT ( tcpip_test ); REQUIRE_OBJECT ( ipv4_test ); REQUIRE_OBJECT ( ipv6_test ); REQUIRE_OBJECT ( crc32_test ); +REQUIRE_OBJECT ( md4_test ); REQUIRE_OBJECT ( md5_test ); REQUIRE_OBJECT ( sha1_test ); REQUIRE_OBJECT ( sha256_test ); From fc2f0dd93013d0037e2dd442eb0b71174ad8412d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 8 Nov 2017 19:08:54 +0000 Subject: [PATCH 528/591] [ntlm] Add support for NTLM authentication mechanism Signed-off-by: Michael Brown --- src/crypto/ntlm.c | 334 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/ntlm.h | 199 ++++++++++++++++++++++ src/tests/ntlm_test.c | 312 ++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 5 files changed, 847 insertions(+) create mode 100644 src/crypto/ntlm.c create mode 100644 src/include/ipxe/ntlm.h create mode 100644 src/tests/ntlm_test.c diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c new file mode 100644 index 000000000..870af2132 --- /dev/null +++ b/src/crypto/ntlm.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2017 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 + * + * NT LAN Manager (NTLM) authentication + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Negotiate message + * + * This message content is fixed since there is no need to specify the + * calling workstation name or domain name, and the set of flags is + * mandated by the MS-NLMP specification. + */ +const struct ntlm_negotiate ntlm_negotiate = { + .header = { + .magic = NTLM_MAGIC, + .type = cpu_to_le32 ( NTLM_NEGOTIATE ), + }, + .flags = cpu_to_le32 ( NTLM_NEGOTIATE_EXTENDED_SESSIONSECURITY | + NTLM_NEGOTIATE_ALWAYS_SIGN | + NTLM_NEGOTIATE_NTLM | + NTLM_REQUEST_TARGET | + NTLM_NEGOTIATE_UNICODE ), +}; + +/** + * Parse NTLM Challenge + * + * @v challenge Challenge message + * @v len Length of Challenge message + * @v info Challenge information to fill in + * @ret rc Return status code + */ +int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len, + struct ntlm_challenge_info *info ) { + size_t offset; + + DBGC ( challenge, "NTLM challenge message:\n" ); + DBGC_HDA ( challenge, 0, challenge, len ); + + /* Sanity checks */ + if ( len < sizeof ( *challenge ) ) { + DBGC ( challenge, "NTLM underlength challenge (%zd bytes)\n", + len ); + return -EINVAL; + } + + /* Extract nonce */ + info->nonce = &challenge->nonce; + DBGC ( challenge, "NTLM challenge nonce:\n" ); + DBGC_HDA ( challenge, 0, info->nonce, sizeof ( *info->nonce ) ); + + /* Extract target information */ + info->len = le16_to_cpu ( challenge->info.len ); + offset = le32_to_cpu ( challenge->info.offset ); + if ( ( offset > len ) || + ( info->len > ( len - offset ) ) ) { + DBGC ( challenge, "NTLM target information outside " + "challenge\n" ); + DBGC_HDA ( challenge, 0, challenge, len ); + return -EINVAL; + } + info->target = ( ( ( void * ) challenge ) + offset ); + DBGC ( challenge, "NTLM challenge target information:\n" ); + DBGC_HDA ( challenge, 0, info->target, info->len ); + + return 0; +} + +/** + * Calculate NTLM verification key + * + * @v domain Domain name (or NULL) + * @v username User name (or NULL) + * @v password Password (or NULL) + * @v key Key to fill in + * + * This is the NTOWFv2() function as defined in MS-NLMP. + */ +void ntlm_key ( const char *domain, const char *username, + const char *password, struct ntlm_key *key ) { + struct digest_algorithm *md4 = &md4_algorithm; + struct digest_algorithm *md5 = &md5_algorithm; + union { + uint8_t md4[MD4_CTX_SIZE]; + uint8_t md5[MD5_CTX_SIZE]; + } ctx; + uint8_t digest[MD4_DIGEST_SIZE]; + size_t digest_len; + uint8_t c; + uint16_t wc; + + /* Use empty usernames/passwords if not specified */ + if ( ! domain ) + domain = ""; + if ( ! username ) + username = ""; + if ( ! password ) + password = ""; + + /* Construct MD4 digest of (Unicode) password */ + digest_init ( md4, ctx.md4 ); + while ( ( c = *(password++) ) ) { + wc = cpu_to_le16 ( c ); + digest_update ( md4, ctx.md4, &wc, sizeof ( wc ) ); + } + 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 ); + while ( ( c = *(username++) ) ) { + wc = cpu_to_le16 ( toupper ( c ) ); + hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); + } + while ( ( c = *(domain++) ) ) { + wc = cpu_to_le16 ( c ); + hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); + } + hmac_final ( md5, ctx.md5, digest, &digest_len, key->raw ); + DBGC ( key, "NTLM key:\n" ); + DBGC_HDA ( key, 0, key, sizeof ( *key ) ); +} + +/** + * Construct NTLM responses + * + * @v info Challenge information + * @v key Verification key + * @v nonce Nonce, or NULL to use a random nonce + * @v lm LAN Manager response to fill in + * @v nt NT response to fill in + */ +void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, + struct ntlm_nonce *nonce, struct ntlm_lm_response *lm, + 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 ); + unsigned int i; + + /* Generate random nonce, if needed */ + if ( ! nonce ) { + for ( i = 0 ; i < sizeof ( tmp_nonce ) ; i++ ) + tmp_nonce.raw[i] = random(); + nonce = &tmp_nonce; + } + + /* Construct LAN Manager response */ + memcpy ( &lm->nonce, nonce, sizeof ( lm->nonce ) ); + hmac_init ( md5, ctx, key->raw, &key_len ); + 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 ); + DBGC ( key, "NTLM LAN Manager response:\n" ); + DBGC_HDA ( key, 0, lm, sizeof ( *lm ) ); + + /* Construct NT response */ + memset ( nt, 0, sizeof ( *nt ) ); + 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_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 ); + DBGC ( key, "NTLM NT response prefix:\n" ); + DBGC_HDA ( key, 0, nt, sizeof ( *nt ) ); +} + +/** + * Append data to NTLM message + * + * @v header Message header, or NULL to only calculate next payload + * @v data Data descriptor + * @v payload Data payload + * @v len Length of data + * @ret payload Next data payload + */ +static void * ntlm_append ( struct ntlm_header *header, struct ntlm_data *data, + void *payload, size_t len ) { + + /* Populate data descriptor */ + if ( header ) { + data->offset = cpu_to_le32 ( payload - ( ( void * ) header ) ); + data->len = data->max_len = cpu_to_le16 ( len ); + } + + return ( payload + len ); +} + +/** + * Append Unicode string data to NTLM message + * + * @v header Message header, or NULL to only calculate next payload + * @v data Data descriptor + * @v payload Data payload + * @v string String to append, or NULL + * @ret payload Next data payload + */ +static void * ntlm_append_string ( struct ntlm_header *header, + struct ntlm_data *data, void *payload, + const char *string ) { + uint16_t *tmp = payload; + uint8_t c; + + /* Convert string to Unicode */ + for ( tmp = payload ; ( string && ( c = *(string++) ) ) ; tmp++ ) { + if ( header ) + *tmp = cpu_to_le16 ( c ); + } + + /* Append string data */ + return ntlm_append ( header, data, payload, + ( ( ( void * ) tmp ) - payload ) ); +} + +/** + * Construct NTLM Authenticate message + * + * @v info Challenge information + * @v domain Domain name, or NULL + * @v username User name, or NULL + * @v workstation Workstation name, or NULL + * @v lm LAN Manager response + * @v nt NT response + * @v auth Message to fill in, or NULL to only calculate length + * @ret len Length of message + */ +size_t ntlm_authenticate ( struct ntlm_challenge_info *info, const char *domain, + const char *username, const char *workstation, + struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt, + struct ntlm_authenticate *auth ) { + void *tmp; + size_t nt_len; + size_t len; + + /* Construct response header */ + if ( auth ) { + memset ( auth, 0, sizeof ( *auth ) ); + memcpy ( auth->header.magic, ntlm_negotiate.header.magic, + sizeof ( auth->header.magic ) ); + auth->header.type = cpu_to_le32 ( NTLM_AUTHENTICATE ); + auth->flags = ntlm_negotiate.flags; + } + tmp = ( ( ( void * ) auth ) + sizeof ( *auth ) ); + + /* Construct LAN Manager response */ + if ( auth ) + memcpy ( tmp, lm, sizeof ( *lm ) ); + tmp = ntlm_append ( &auth->header, &auth->lm, tmp, sizeof ( *lm ) ); + + /* Construct NT response */ + nt_len = ( sizeof ( *nt ) + info->len + sizeof ( nt->zero ) ); + if ( auth ) { + memcpy ( tmp, nt, sizeof ( *nt ) ); + memcpy ( ( tmp + sizeof ( *nt ) ), info->target, info->len ); + memset ( ( tmp + sizeof ( *nt ) + info->len ), 0, + sizeof ( nt->zero ) ); + } + tmp = ntlm_append ( &auth->header, &auth->nt, tmp, nt_len ); + + /* Populate domain, user, and workstation names */ + tmp = ntlm_append_string ( &auth->header, &auth->domain, tmp, domain ); + tmp = ntlm_append_string ( &auth->header, &auth->user, tmp, username ); + tmp = ntlm_append_string ( &auth->header, &auth->workstation, tmp, + workstation ); + + /* Calculate length */ + len = ( tmp - ( ( void * ) auth ) ); + if ( auth ) { + DBGC ( auth, "NTLM authenticate message:\n" ); + DBGC_HDA ( auth, 0, auth, len ); + } + + return len; +} + +/** + * Calculate NTLM Authenticate message length + * + * @v info Challenge information + * @v domain Domain name, or NULL + * @v username User name, or NULL + * @v workstation Workstation name, or NULL + * @ret len Length of Authenticate message + */ +size_t ntlm_authenticate_len ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation ) { + + return ntlm_authenticate ( info, domain, username, workstation, + NULL, NULL, NULL ); +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2dc182dd4..e0d0dbc83 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -370,6 +370,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) #define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) #define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) +#define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 ) /** @} */ diff --git a/src/include/ipxe/ntlm.h b/src/include/ipxe/ntlm.h new file mode 100644 index 000000000..b0436c9ac --- /dev/null +++ b/src/include/ipxe/ntlm.h @@ -0,0 +1,199 @@ +#ifndef _IPXE_NTLM_H +#define _IPXE_NTLM_H + +/** @file + * + * NT LAN Manager (NTLM) authentication + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** A message header */ +struct ntlm_header { + /** Magic signature */ + uint8_t magic[8]; + /** Message type */ + uint32_t type; +} __attribute__ (( packed )); + +/** Magic signature */ +#define NTLM_MAGIC { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' } + +/** Message types */ +enum ntlm_type { + /** Negotiate message type */ + NTLM_NEGOTIATE = 0x00000001UL, + /** Challenge message type */ + NTLM_CHALLENGE = 0x00000002UL, + /** Authenticate message */ + NTLM_AUTHENTICATE = 0x00000003UL, +}; + +/** Negotiation flags */ +enum ntlm_flags { + /** Negotiate key exchange */ + NTLM_NEGOTIATE_KEY_EXCH = 0x20000000UL, + /** Negotiate extended security */ + NTLM_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000UL, + /** Negotiate always sign */ + NTLM_NEGOTIATE_ALWAYS_SIGN = 0x00008000UL, + /** Negotiate NTLM key */ + NTLM_NEGOTIATE_NTLM = 0x00000200UL, + /** Request target name and information */ + NTLM_REQUEST_TARGET = 0x00000004UL, + /** Negotiate Unicode character encoding */ + NTLM_NEGOTIATE_UNICODE = 0x00000001UL, +}; + +/** A version descriptor */ +struct ntlm_version { + /** Product major version */ + uint8_t major; + /** Product minor version */ + uint8_t minor; + /** Product build number */ + uint16_t build; + /** Reserved */ + uint8_t reserved[3]; + /** NTLMSSP revision */ + uint8_t revision; +} __attribute__ (( packed )); + +/** A nonce */ +struct ntlm_nonce { + /** Raw bytes */ + uint8_t raw[8]; +} __attribute__ (( packed )); + +/** A variable-length data descriptor */ +struct ntlm_data { + /** Length (in bytes) */ + uint16_t len; + /** Maximum length (in bytes) + * + * Should always be set equal to the length; this field is + * entirely superfluous. + */ + uint16_t max_len; + /** Offset from start of message header */ + uint32_t offset; +} __attribute__ (( packed )); + +/** A Negotiate message */ +struct ntlm_negotiate { + /** Message header */ + struct ntlm_header header; + /** Negotiation flags */ + uint32_t flags; + /** Domain name */ + struct ntlm_data domain; + /** Workstation name */ + struct ntlm_data workstation; +} __attribute__ (( packed )); + +/** A Challenge message */ +struct ntlm_challenge { + /** Message header */ + struct ntlm_header header; + /** Target name */ + struct ntlm_data name; + /** Negotiation flags */ + uint32_t flags; + /** Server nonce */ + struct ntlm_nonce nonce; + /** Reserved */ + uint8_t reserved[8]; + /** Target information */ + struct ntlm_data info; +} __attribute__ (( packed )); + +/** An Authenticate message */ +struct ntlm_authenticate { + /** Message header */ + struct ntlm_header header; + /** LAN Manager response */ + struct ntlm_data lm; + /** NT response */ + struct ntlm_data nt; + /** Domain name */ + struct ntlm_data domain; + /** User name */ + struct ntlm_data user; + /** Workstation name */ + struct ntlm_data workstation; + /** Session key */ + struct ntlm_data session; + /** Negotiation flags */ + uint32_t flags; +} __attribute__ (( packed )); + +/** A LAN Manager response */ +struct ntlm_lm_response { + /** HMAC-MD5 digest */ + uint8_t digest[MD5_DIGEST_SIZE]; + /** Client nonce */ + struct ntlm_nonce nonce; +} __attribute__ (( packed )); + +/** An NT response */ +struct ntlm_nt_response { + /** HMAC-MD5 digest */ + uint8_t digest[MD5_DIGEST_SIZE]; + /** Response version */ + uint8_t version; + /** Highest response version */ + uint8_t high; + /** Reserved */ + uint8_t reserved_a[6]; + /** Current time */ + uint64_t time; + /** Client nonce */ + struct ntlm_nonce nonce; + /** Must be zero */ + uint32_t zero; +} __attribute__ (( packed )); + +/** NTLM version */ +#define NTLM_VERSION_NTLMV2 0x01 + +/** NTLM challenge information */ +struct ntlm_challenge_info { + /** Server nonce */ + struct ntlm_nonce *nonce; + /** Target information */ + void *target; + /** Length of target information */ + size_t len; +}; + +/** An NTLM verification key */ +struct ntlm_key { + /** Raw bytes */ + uint8_t raw[MD5_DIGEST_SIZE]; +}; + +extern const struct ntlm_negotiate ntlm_negotiate; +extern int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len, + struct ntlm_challenge_info *info ); +extern void ntlm_key ( const char *domain, const char *username, + const char *password, struct ntlm_key *key ); +extern void ntlm_response ( struct ntlm_challenge_info *info, + struct ntlm_key *key, struct ntlm_nonce *nonce, + struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt ); +extern size_t ntlm_authenticate ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation, + struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt, + struct ntlm_authenticate *auth ); +extern size_t ntlm_authenticate_len ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation ); + +#endif /* _IPXE_NTLM_H */ diff --git a/src/tests/ntlm_test.c b/src/tests/ntlm_test.c new file mode 100644 index 000000000..65a8b8c67 --- /dev/null +++ b/src/tests/ntlm_test.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2017 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 + * + * NTLM authentication self-tests + * + * The test vectors are taken from the MS-NLMP specification document. + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include + +/** A key generation test */ +struct ntlm_key_test { + /** Domain name (or NULL) */ + const char *domain; + /** User name (or NULL) */ + const char *username; + /** Password (or NULL) */ + const char *password; + /** Expected key */ + struct ntlm_key expected; +}; + +/** An authentication test */ +struct ntlm_authenticate_test { + /** Domain name (or NULL) */ + const char *domain; + /** User name (or NULL) */ + const char *username; + /** Password (or NULL) */ + const char *password; + /** Workstation (or NULL) */ + const char *workstation; + /** Nonce */ + struct ntlm_nonce nonce; + /** Challenge message */ + struct ntlm_challenge *challenge; + /** Length of Challenge message */ + size_t challenge_len; + /** Expected Authenticate message */ + struct ntlm_authenticate *expected; + /** Expected length of Authenticate message */ + size_t expected_len; +}; + +/** Define inline message data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a key generation digest test */ +#define KEY_TEST( name, DOMAIN, USERNAME, PASSWORD, EXPECTED ) \ + static struct ntlm_key_test name = { \ + .domain = DOMAIN, \ + .username = USERNAME, \ + .password = PASSWORD, \ + .expected = { \ + .raw = EXPECTED, \ + }, \ + }; + +/** Define an authentication test */ +#define AUTHENTICATE_TEST( name, DOMAIN, USERNAME, PASSWORD, \ + WORKSTATION, NONCE, CHALLENGE, EXPECTED ) \ + static const uint8_t name ## _challenge[] = CHALLENGE; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct ntlm_authenticate_test name = { \ + .domain = DOMAIN, \ + .username = USERNAME, \ + .password = PASSWORD, \ + .workstation = WORKSTATION, \ + .nonce = { \ + .raw = NONCE, \ + }, \ + .challenge = ( ( void * ) name ## _challenge ), \ + .challenge_len = sizeof ( name ## _challenge ), \ + .expected = ( ( void * ) name ## _expected ), \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** NTOWFv2() test from MS-NLMP specification */ +KEY_TEST ( msnlmp_ntowfv2, "Domain", "User", "Password", + DATA ( 0x0c, 0x86, 0x8a, 0x40, 0x3b, 0xfd, 0x7a, 0x93, 0xa3, 0x00, + 0x1e, 0xf2, 0x2e, 0xf0, 0x2e, 0x3f ) ); + +/** Authentication test from MS-NLMP specification */ +AUTHENTICATE_TEST ( msnlmp_authenticate, + "Domain", "User", "Password", "COMPUTER", + DATA ( 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa ), + DATA ( 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x33, 0x82, 0x8a, 0xe2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x24, 0x00, 0x44, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00 ), + DATA ( 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x54, 0x00, 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x5c, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xd8, 0x00, 0x00, 0x00, + 0x35, 0x82, 0x88, 0xe2, 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, + 0x00, 0x0f, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x50, 0x00, + 0x55, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x86, 0xc3, + 0x50, 0x97, 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, + 0x57, 0xcc, 0xcc, 0x19, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, + 0xaa, 0xbc, 0x92, 0x7b, 0xeb, 0xef, 0x6a, 0x1c, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, + 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xda, 0xd2, 0x54, + 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, + 0xd0, 0x3e ) ); + +/** + * Report key generation test result + * + * @v test Key generation test + * @v file Test code file + * @v line Test code line + */ +static void ntlm_key_okx ( struct ntlm_key_test *test, + const char *file, unsigned int line ) { + struct ntlm_key key; + + ntlm_key ( test->domain, test->username, test->password, &key ); + okx ( memcmp ( &key, &test->expected, sizeof ( key ) ) == 0, + file, line ); +} +#define ntlm_key_ok( test ) \ + ntlm_key_okx ( test, __FILE__, __LINE__ ) + +/** + * Report NTLM variable-length data test result + * + * @v msg Message header + * @v msg_len Length of message + * @v data Variable-length data descriptor + * @v expected Expected message header + * @v expected_data Expected variable-length data descriptor + * @v field Field name + * @v file Test code file + * @v line Test code line + */ +static void ntlm_data_okx ( struct ntlm_header *msg, size_t msg_len, + struct ntlm_data *data, + struct ntlm_header *expected, + struct ntlm_data *expected_data, + const char *field, const char *file, + unsigned int line ) { + size_t offset; + size_t len; + void *raw; + void *expected_raw; + + /* Verify data lies within message */ + okx ( data->len == data->max_len, file, line ); + offset = le32_to_cpu ( data->offset ); + len = le16_to_cpu ( data->len ); + okx ( offset <= msg_len, file, line ); + okx ( len <= ( msg_len - offset ), file, line ); + + /* Verify content matches expected content */ + raw = ( ( ( void * ) msg ) + offset ); + expected_raw = ( ( ( void * ) expected ) + + le32_to_cpu ( expected_data->offset ) ); + DBGC ( msg, "NTLM %s expected:\n", field ); + DBGC_HDA ( msg, 0, expected_raw, le16_to_cpu ( expected_data->len ) ); + DBGC ( msg, "NTLM %s actual:\n", field ); + DBGC_HDA ( msg, 0, raw, len ); + okx ( data->len == expected_data->len, file, line ); + okx ( memcmp ( raw, expected_raw, len ) == 0, file, line ); +} +#define ntlm_data_ok( msg, msg_len, data, expected, expected_data ) \ + ntlm_data_okx ( msg, msg_len, data, expected, expected_data, \ + __FILE__, __LINE__ ) + +/** + * Report NTLM authentication test result + * + * @v test Authentication test + * @v file Test code file + * @v line Test code line + */ +static void ntlm_authenticate_okx ( struct ntlm_authenticate_test *test, + const char *file, unsigned int line ) { + struct ntlm_authenticate *expected = test->expected; + struct ntlm_challenge_info info; + struct ntlm_authenticate *auth; + struct ntlm_key key; + struct ntlm_lm_response lm; + struct ntlm_nt_response nt; + size_t len; + + /* Parse Challenge message */ + okx ( ntlm_challenge ( test->challenge, test->challenge_len, + &info ) == 0, file, line ); + + /* Generate key */ + ntlm_key ( test->domain, test->username, test->password, &key ); + + /* Generate responses */ + ntlm_response ( &info, &key, &test->nonce, &lm, &nt ); + + /* Allocate buffer for Authenticate message */ + len = ntlm_authenticate_len ( &info, test->domain, test->username, + test->workstation ); + okx ( len >= sizeof ( *auth ), file, line ); + auth = malloc ( len ); + okx ( auth != NULL, file, line ); + + /* Construct Authenticate message */ + okx ( ntlm_authenticate ( &info, test->domain, test->username, + test->workstation, &lm, &nt, auth ) == len, + file, line ); + + /* Verify header */ + okx ( memcmp ( &auth->header, &expected->header, + sizeof ( auth->header ) ) == 0, file, line ); + + /* Verify LAN Manager response */ + ntlm_data_okx ( &auth->header, len, &auth->lm, &expected->header, + &expected->lm, "LM", file, line ); + + /* Verify NT response */ + ntlm_data_okx ( &auth->header, len, &auth->nt, &expected->header, + &expected->nt, "NT", file, line ); + + /* Verify domain name */ + ntlm_data_okx ( &auth->header, len, &auth->domain, &expected->header, + &expected->domain, "domain", file, line ); + + /* Verify user name */ + ntlm_data_okx ( &auth->header, len, &auth->user, &expected->header, + &expected->user, "user", file, line ); + + /* Verify workstation name */ + ntlm_data_okx ( &auth->header, len, &auth->workstation, + &expected->header, &expected->workstation, + "workstation",file, line ); + + /* Verify session key */ + if ( auth->flags & NTLM_NEGOTIATE_KEY_EXCH ) { + ntlm_data_okx ( &auth->header, len, &auth->session, + &expected->header, &expected->session, + "session", file, line ); + } + + /* Free Authenticate message */ + free ( auth ); +} +#define ntlm_authenticate_ok( test ) \ + ntlm_authenticate_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform NTLM self-test + * + */ +static void ntlm_test_exec ( void ) { + + /* Verify key generation */ + ntlm_key_ok ( &msnlmp_ntowfv2 ); + + /* Verify authentication response */ + ntlm_authenticate_ok ( &msnlmp_authenticate ); +} + +/** NTLM self-test */ +struct self_test ntlm_test __self_test = { + .name = "ntlm", + .exec = ntlm_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index a0c4ff5d7..2e812d6ff 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -72,3 +72,4 @@ REQUIRE_OBJECT ( iobuf_test ); REQUIRE_OBJECT ( bitops_test ); REQUIRE_OBJECT ( der_test ); REQUIRE_OBJECT ( pem_test ); +REQUIRE_OBJECT ( ntlm_test ); From c49acbb4d2d84c6cb2faacd18fa21ed5d12ed450 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 7 Nov 2017 11:33:13 +0000 Subject: [PATCH 529/591] [http] Gracefully handle offers of multiple authentication schemes Servers may provide multiple WWW-Authenticate headers, each offering a different authentication scheme. We currently fail the request as soon as we encounter an unrecognised scheme, which prevents subsequent offers from succeeding. Fix by silently ignoring headers for schemes that we do not recognise. If no schemes are recognised then the request will eventually fail anyway due to the 401 response code. If multiple schemes are supported, arbitrarily choose the scheme appearing first within the response headers. Signed-off-by: Michael Brown --- src/net/tcp/httpauth.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/net/tcp/httpauth.c b/src/net/tcp/httpauth.c index fb6dcd035..bb327244d 100644 --- a/src/net/tcp/httpauth.c +++ b/src/net/tcp/httpauth.c @@ -104,6 +104,7 @@ static struct http_www_authenticate_field http_www_auth_fields[] = { static int http_parse_www_authenticate ( struct http_transaction *http, char *line ) { struct http_www_authenticate_field *field; + struct http_authentication *auth; char *name; char *key; char *value; @@ -118,13 +119,19 @@ static int http_parse_www_authenticate ( struct http_transaction *http, } /* Identify scheme */ - http->response.auth.auth = http_authentication ( name ); - if ( ! http->response.auth.auth ) { + auth = http_authentication ( name ); + if ( ! auth ) { DBGC ( http, "HTTP %p unrecognised authentication scheme " "\"%s\"\n", http, name ); - return -ENOTSUP; + /* Ignore; the server may offer other schemes */ + return 0; } + /* Use first supported scheme */ + if ( http->response.auth.auth ) + return 0; + http->response.auth.auth = auth; + /* Process fields */ while ( ( key = http_token ( &line, &value ) ) ) { for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / From 96bd872c0342fcc290e9162154d07371405cf384 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 11 Nov 2017 22:05:53 +0000 Subject: [PATCH 530/591] [http] Handle parsing of WWW-Authenticate header within authentication scheme Allow individual authentication schemes to parse WWW-Authenticate headers that do not comply with RFC2617. Signed-off-by: Michael Brown --- src/include/ipxe/http.h | 55 +++++++++++++++++++++---- src/net/tcp/httpauth.c | 66 ++++-------------------------- src/net/tcp/httpbasic.c | 24 ++++++++++- src/net/tcp/httpdigest.c | 87 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 157 insertions(+), 75 deletions(-) diff --git a/src/include/ipxe/http.h b/src/include/ipxe/http.h index a0dff7d00..0f42a22e4 100644 --- a/src/include/ipxe/http.h +++ b/src/include/ipxe/http.h @@ -150,14 +150,18 @@ struct http_request_content { size_t len; }; -/** HTTP request authentication descriptor */ -struct http_request_auth { - /** Authentication scheme (if any) */ - struct http_authentication *auth; +/** HTTP request Basic authentication descriptor */ +struct http_request_auth_basic { /** Username */ const char *username; /** Password */ const char *password; +}; + +/** HTTP request Digest authentication descriptor */ +struct http_request_auth_digest { + /** Username */ + const char *username; /** Quality of protection */ const char *qop; /** Algorithm */ @@ -168,6 +172,19 @@ struct http_request_auth { char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; }; +/** HTTP request authentication descriptor */ +struct http_request_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Per-scheme information */ + union { + /** Basic authentication descriptor */ + struct http_request_auth_basic basic; + /** Digest authentication descriptor */ + struct http_request_auth_digest digest; + }; +}; + /** An HTTP request * * This represents a single request to be sent to a server, including @@ -235,10 +252,12 @@ struct http_response_content { struct http_content_encoding *encoding; }; -/** HTTP response authorization descriptor */ -struct http_response_auth { - /** Authentication scheme (if any) */ - struct http_authentication *auth; +/** HTTP response Basic authorization descriptor */ +struct http_response_auth_basic { +}; + +/** HTTP response Digest authorization descriptor */ +struct http_response_auth_digest { /** Realm */ const char *realm; /** Quality of protection */ @@ -251,6 +270,19 @@ struct http_response_auth { const char *opaque; }; +/** HTTP response authorization descriptor */ +struct http_response_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Per-scheme information */ + union { + /** Basic authorization descriptor */ + struct http_response_auth_basic basic; + /** Digest authorization descriptor */ + struct http_response_auth_digest digest; + }; +}; + /** An HTTP response * * This represents a single response received from the server, @@ -461,6 +493,13 @@ struct http_content_encoding { struct http_authentication { /** Name (e.g. "Digest") */ const char *name; + /** Parse remaining "WWW-Authenticate" header line + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ + int ( * parse ) ( struct http_transaction *http, char *line ); /** Perform authentication * * @v http HTTP transaction diff --git a/src/net/tcp/httpauth.c b/src/net/tcp/httpauth.c index bb327244d..2c57e3d48 100644 --- a/src/net/tcp/httpauth.c +++ b/src/net/tcp/httpauth.c @@ -54,46 +54,6 @@ static struct http_authentication * http_authentication ( const char *name ) { return NULL; } -/** An HTTP "WWW-Authenticate" response field */ -struct http_www_authenticate_field { - /** Name */ - const char *name; - /** Offset */ - size_t offset; -}; - -/** Define an HTTP "WWW-Authenticate" response field */ -#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) { \ - .name = #_name, \ - .offset = offsetof ( struct http_transaction, \ - response.auth._name ), \ - } - -/** - * Set HTTP "WWW-Authenticate" response field value - * - * @v http HTTP transaction - * @v field Response field - * @v value Field value - */ -static inline void -http_www_auth_field ( struct http_transaction *http, - struct http_www_authenticate_field *field, char *value ) { - char **ptr; - - ptr = ( ( ( void * ) http ) + field->offset ); - *ptr = value; -} - -/** HTTP "WWW-Authenticate" fields */ -static struct http_www_authenticate_field http_www_auth_fields[] = { - HTTP_WWW_AUTHENTICATE_FIELD ( realm ), - HTTP_WWW_AUTHENTICATE_FIELD ( qop ), - HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ), - HTTP_WWW_AUTHENTICATE_FIELD ( nonce ), - HTTP_WWW_AUTHENTICATE_FIELD ( opaque ), -}; - /** * Parse HTTP "WWW-Authenticate" header * @@ -103,18 +63,15 @@ static struct http_www_authenticate_field http_www_auth_fields[] = { */ static int http_parse_www_authenticate ( struct http_transaction *http, char *line ) { - struct http_www_authenticate_field *field; struct http_authentication *auth; char *name; - char *key; - char *value; - unsigned int i; + int rc; /* Get scheme name */ name = http_token ( &line, NULL ); if ( ! name ) { DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n", - http, value ); + http, line ); return -EPROTO; } @@ -132,22 +89,13 @@ static int http_parse_www_authenticate ( struct http_transaction *http, return 0; http->response.auth.auth = auth; - /* Process fields */ - while ( ( key = http_token ( &line, &value ) ) ) { - for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / - sizeof ( http_www_auth_fields[0] ) ) ; i++){ - field = &http_www_auth_fields[i]; - if ( strcasecmp ( key, field->name ) == 0 ) - http_www_auth_field ( http, field, value ); - } + /* Parse remaining header line */ + if ( ( rc = auth->parse ( http, line ) ) != 0 ) { + DBGC ( http, "HTTP %p could not parse %s WWW-Authenticate " + "\"%s\": %s\n", http, name, line, strerror ( rc ) ); + return rc; } - /* Allow HTTP request to be retried if the request had not - * already tried authentication. - */ - if ( ! http->request.auth.auth ) - http->response.flags |= HTTP_RESPONSE_RETRY; - return 0; } diff --git a/src/net/tcp/httpbasic.c b/src/net/tcp/httpbasic.c index 7ed7de9e7..52a67063d 100644 --- a/src/net/tcp/httpbasic.c +++ b/src/net/tcp/httpbasic.c @@ -42,6 +42,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x01, \ "No username available for Basic authentication" ) +/** + * Parse HTTP "WWW-Authenticate" header for Basic authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_basic_auth ( struct http_transaction *http, + char *line __unused ) { + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + /** * Perform HTTP Basic authentication * @@ -49,7 +68,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret rc Return status code */ static int http_basic_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; + struct http_request_auth_basic *req = &http->request.auth.basic; /* Record username and password */ if ( ! http->uri->user ) { @@ -73,7 +92,7 @@ static int http_basic_authenticate ( struct http_transaction *http ) { */ static int http_format_basic_auth ( struct http_transaction *http, char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; + struct http_request_auth_basic *req = &http->request.auth.basic; size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ + strlen ( req->password ) ); char user_pw[ user_pw_len + 1 /* NUL */ ]; @@ -93,6 +112,7 @@ static int http_format_basic_auth ( struct http_transaction *http, /** HTTP Basic authentication scheme */ struct http_authentication http_basic_auth __http_authentication = { .name = "Basic", + .parse = http_parse_basic_auth, .authenticate = http_basic_authenticate, .format = http_format_basic_auth, }; diff --git a/src/net/tcp/httpdigest.c b/src/net/tcp/httpdigest.c index 626dd7e9d..4074078c7 100644 --- a/src/net/tcp/httpdigest.c +++ b/src/net/tcp/httpdigest.c @@ -45,6 +45,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x01, \ "No username available for Digest authentication" ) +/** An HTTP Digest "WWW-Authenticate" response field */ +struct http_digest_field { + /** Name */ + const char *name; + /** Offset */ + size_t offset; +}; + +/** Define an HTTP Digest "WWW-Authenticate" response field */ +#define HTTP_DIGEST_FIELD( _name ) { \ + .name = #_name, \ + .offset = offsetof ( struct http_transaction, \ + response.auth.digest._name ), \ + } + +/** + * Set HTTP Digest "WWW-Authenticate" response field value + * + * @v http HTTP transaction + * @v field Response field + * @v value Field value + */ +static inline void +http_digest_field ( struct http_transaction *http, + struct http_digest_field *field, char *value ) { + char **ptr; + + ptr = ( ( ( void * ) http ) + field->offset ); + *ptr = value; +} + +/** HTTP Digest "WWW-Authenticate" fields */ +static struct http_digest_field http_digest_fields[] = { + HTTP_DIGEST_FIELD ( realm ), + HTTP_DIGEST_FIELD ( qop ), + HTTP_DIGEST_FIELD ( algorithm ), + HTTP_DIGEST_FIELD ( nonce ), + HTTP_DIGEST_FIELD ( opaque ), +}; + +/** + * Parse HTTP "WWW-Authenticate" header for Digest authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_digest_auth ( struct http_transaction *http, + char *line ) { + struct http_digest_field *field; + char *key; + char *value; + unsigned int i; + + /* Process fields */ + while ( ( key = http_token ( &line, &value ) ) ) { + for ( i = 0 ; i < ( sizeof ( http_digest_fields ) / + sizeof ( http_digest_fields[0] ) ) ; i++){ + field = &http_digest_fields[i]; + if ( strcasecmp ( key, field->name ) == 0 ) + http_digest_field ( http, field, value ); + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + /** * Initialise HTTP Digest * @@ -95,13 +168,14 @@ static void http_digest_final ( struct md5_context *ctx, char *out, * @ret rc Return status code */ static int http_digest_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; + struct http_request_auth_digest *req = &http->request.auth.digest; + struct http_response_auth_digest *rsp = &http->response.auth.digest; char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; static const char md5sess[] = "MD5-sess"; static const char md5[] = "MD5"; struct md5_context ctx; + const char *password; /* Check for required response parameters */ if ( ! rsp->realm ) { @@ -122,7 +196,7 @@ static int http_digest_authenticate ( struct http_transaction *http ) { return -EACCES_USERNAME; } req->username = http->uri->user; - req->password = ( http->uri->password ? http->uri->password : "" ); + password = ( http->uri->password ? http->uri->password : "" ); /* Handle quality of protection */ if ( rsp->qop ) { @@ -146,7 +220,7 @@ static int http_digest_authenticate ( struct http_transaction *http ) { http_digest_init ( &ctx ); http_digest_update ( &ctx, req->username ); http_digest_update ( &ctx, rsp->realm ); - http_digest_update ( &ctx, req->password ); + http_digest_update ( &ctx, password ); http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); if ( req->algorithm == md5sess ) { http_digest_init ( &ctx ); @@ -187,8 +261,8 @@ static int http_digest_authenticate ( struct http_transaction *http ) { */ static int http_format_digest_auth ( struct http_transaction *http, char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; + struct http_request_auth_digest *req = &http->request.auth.digest; + struct http_response_auth_digest *rsp = &http->response.auth.digest; size_t used = 0; /* Sanity checks */ @@ -225,6 +299,7 @@ static int http_format_digest_auth ( struct http_transaction *http, /** HTTP Digest authentication scheme */ struct http_authentication http_digest_auth __http_authentication = { .name = "Digest", + .parse = http_parse_digest_auth, .authenticate = http_digest_authenticate, .format = http_format_digest_auth, }; From b5e0b5072317f11b27d2d813bd1c93788584a9f2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 11 Nov 2017 22:43:03 +0000 Subject: [PATCH 531/591] [http] Add support for NTLM authentication Signed-off-by: Michael Brown --- src/config/config_http.c | 3 + src/config/general.h | 1 + src/include/ipxe/errfile.h | 1 + src/include/ipxe/http.h | 25 +++++ src/net/tcp/httpntlm.c | 201 +++++++++++++++++++++++++++++++++++++ 5 files changed, 231 insertions(+) create mode 100644 src/net/tcp/httpntlm.c diff --git a/src/config/config_http.c b/src/config/config_http.c index 3c0e78028..4373ea2c0 100644 --- a/src/config/config_http.c +++ b/src/config/config_http.c @@ -40,6 +40,9 @@ REQUIRE_OBJECT ( httpbasic ); #ifdef HTTP_AUTH_DIGEST REQUIRE_OBJECT ( httpdigest ); #endif +#ifdef HTTP_AUTH_NTLM +REQUIRE_OBJECT ( httpntlm ); +#endif #ifdef HTTP_ENC_PEERDIST REQUIRE_OBJECT ( peerdist ); #endif diff --git a/src/config/general.h b/src/config/general.h index e06db5252..3c14a2cd0 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define HTTP_AUTH_BASIC /* Basic authentication */ #define HTTP_AUTH_DIGEST /* Digest authentication */ +//#define HTTP_AUTH_NTLM /* NTLM authentication */ //#define HTTP_ENC_PEERDIST /* PeerDist content encoding */ //#define HTTP_HACK_GCE /* Google Compute Engine hacks */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e0d0dbc83..1a98599ec 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -277,6 +277,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) #define ERRFILE_xsigo ( ERRFILE_NET | 0x00480000 ) #define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 ) +#define ERRFILE_httpntlm ( ERRFILE_NET | 0x004a0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/http.h b/src/include/ipxe/http.h index 0f42a22e4..0893c9537 100644 --- a/src/include/ipxe/http.h +++ b/src/include/ipxe/http.h @@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include struct http_transaction; @@ -172,6 +173,18 @@ struct http_request_auth_digest { char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; }; +/** HTTP request NTLM authentication descriptor */ +struct http_request_auth_ntlm { + /** Username */ + const char *username; + /** LAN Manager response */ + struct ntlm_lm_response lm; + /** NT response */ + struct ntlm_nt_response nt; + /** Authenticate message length */ + size_t len; +}; + /** HTTP request authentication descriptor */ struct http_request_auth { /** Authentication scheme (if any) */ @@ -182,6 +195,8 @@ struct http_request_auth { struct http_request_auth_basic basic; /** Digest authentication descriptor */ struct http_request_auth_digest digest; + /** NTLM authentication descriptor */ + struct http_request_auth_ntlm ntlm; }; }; @@ -270,6 +285,14 @@ struct http_response_auth_digest { const char *opaque; }; +/** HTTP response NTLM authorization descriptor */ +struct http_response_auth_ntlm { + /** Challenge message */ + struct ntlm_challenge *challenge; + /** Challenge information */ + struct ntlm_challenge_info info; +}; + /** HTTP response authorization descriptor */ struct http_response_auth { /** Authentication scheme (if any) */ @@ -280,6 +303,8 @@ struct http_response_auth { struct http_response_auth_basic basic; /** Digest authorization descriptor */ struct http_response_auth_digest digest; + /** NTLM authorization descriptor */ + struct http_response_auth_ntlm ntlm; }; }; diff --git a/src/net/tcp/httpntlm.c b/src/net/tcp/httpntlm.c new file mode 100644 index 000000000..00238e96c --- /dev/null +++ b/src/net/tcp/httpntlm.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 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 + * + * Hyper Text Transfer Protocol (HTTP) NTLM authentication + * + */ + +#include +#include +#include +#include +#include +#include + +struct http_authentication http_ntlm_auth __http_authentication; + +/** Workstation name used for NTLM authentication */ +static const char http_ntlm_workstation[] = "iPXE"; + +/** + * Parse HTTP "WWW-Authenticate" header for NTLM authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_ntlm_auth ( struct http_transaction *http, char *line ) { + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + char *copy; + int len; + int rc; + + /* Create temporary copy of Base64-encoded challenge message */ + copy = strdup ( line ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Decode challenge message, overwriting the original */ + len = base64_decode ( copy, line, strlen ( line ) ); + if ( len < 0 ) { + rc = len; + DBGC ( http, "HTTP %p could not decode NTLM challenge " + "\"%s\": %s\n", http, copy, strerror ( rc ) ); + goto err_decode; + } + + /* Parse challenge, if present */ + if ( len ) { + rsp->challenge = ( ( void * ) line ); + if ( ( rc = ntlm_challenge ( rsp->challenge, len, + &rsp->info ) ) != 0 ) { + DBGC ( http, "HTTP %p could not parse NTLM challenge: " + "%s\n", http, strerror ( rc ) ); + goto err_challenge; + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. Note that NTLM requires an + * additional round trip to obtain the challenge message, + * which is not present in the initial WWW-Authenticate. + */ + if ( ( http->request.auth.auth == NULL ) || + ( ( http->request.auth.auth == &http_ntlm_auth ) && + ( http->request.auth.ntlm.len == 0 ) && len ) ) { + http->response.flags |= HTTP_RESPONSE_RETRY; + } + + /* Success */ + rc = 0; + + err_challenge: + err_decode: + free ( copy ); + err_alloc: + return rc; +} + +/** + * Perform HTTP NTLM authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_ntlm_authenticate ( struct http_transaction *http ) { + struct http_request_auth_ntlm *req = &http->request.auth.ntlm; + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + struct ntlm_key key; + const char *password; + + /* If we have no challenge yet, then just send a Negotiate message */ + if ( ! rsp->challenge ) { + DBGC ( http, "HTTP %p sending NTLM Negotiate\n", http ); + return 0; + } + + /* Record username */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for NTLM " + "authentication\n", http ); + return -EACCES; + } + req->username = http->uri->user; + password = ( http->uri->password ? http->uri->password : "" ); + + /* Generate key */ + ntlm_key ( NULL, req->username, password, &key ); + + /* Generate responses */ + ntlm_response ( &rsp->info, &key, NULL, &req->lm, &req->nt ); + + /* Calculate Authenticate message length */ + req->len = ntlm_authenticate_len ( &rsp->info, NULL, req->username, + http_ntlm_workstation ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for NTLM authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_ntlm_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth_ntlm *req = &http->request.auth.ntlm; + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + struct ntlm_authenticate *auth; + size_t check; + + /* If we have no challenge yet, then just send a Negotiate message */ + if ( ! rsp->challenge ) { + return base64_encode ( &ntlm_negotiate, + sizeof ( ntlm_negotiate ), buf, len ); + } + + /* Skip allocation if just calculating length */ + if ( ! len ) + return base64_encoded_len ( req->len ); + + /* Allocate temporary buffer for Authenticate message */ + auth = malloc ( req->len ); + if ( ! auth ) + return -ENOMEM; + + /* Construct raw Authenticate message */ + check = ntlm_authenticate ( &rsp->info, NULL, req->username, + http_ntlm_workstation, &req->lm, + &req->nt, auth ); + assert ( check == req->len ); + + /* Base64-encode Authenticate message */ + len = base64_encode ( auth, req->len, buf, len ); + + /* Free raw Authenticate message */ + free ( auth ); + + return len; +} + +/** HTTP NTLM authentication scheme */ +struct http_authentication http_ntlm_auth __http_authentication = { + .name = "NTLM", + .parse = http_parse_ntlm_auth, + .authenticate = http_ntlm_authenticate, + .format = http_format_ntlm_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_ntlm_auth ); +REQUIRE_OBJECT ( httpauth ); From aeffcce44fdf3ebe704b98ef3da8a74d4ec8a521 Mon Sep 17 00:00:00 2001 From: Janos Mattyasovszky Date: Wed, 20 Dec 2017 12:20:43 +0000 Subject: [PATCH 532/591] [intel] Add PCI device ID for X550-T2 Signed-off-by: Michael Brown --- src/drivers/net/intelx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index 982b74f12..0d5fb256c 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -473,6 +473,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ), PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ), + PCI_ROM ( 0x8086, 0x1563, "x550t2", "X550-T2", 0 ), }; /** PCI driver */ From e4461f65d8e1aef44e9d437e4c5f7a89cb88f20b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Dec 2017 12:09:27 +0000 Subject: [PATCH 533/591] [xen] Skip probing of any unsupported device types Xen 4.4 includes the device "device/suspend/event-channel" which does not have a "backend" key. This currently causes the entire XenBus device tree probe to fail. Fix by skipping probe attempts for device types for which there is no iPXE driver. Debugged-by: Eytan Heidingsfeld Signed-off-by: Michael Brown --- src/interface/xen/xenbus.c | 39 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/interface/xen/xenbus.c b/src/interface/xen/xenbus.c index c328af443..5dd01dfa3 100644 --- a/src/interface/xen/xenbus.c +++ b/src/interface/xen/xenbus.c @@ -206,13 +206,14 @@ static struct xen_driver * xenbus_find_driver ( const char *type ) { * * @v xen Xen hypervisor * @v parent Parent device - * @v type Device type * @v instance Device instance + * @v driver Device driver * @ret rc Return status code */ static int xenbus_probe_device ( struct xen_hypervisor *xen, - struct device *parent, const char *type, - const char *instance ) { + struct device *parent, const char *instance, + struct xen_driver *driver ) { + const char *type = driver->type; struct xen_device *xendev; size_t key_len; int rc; @@ -234,6 +235,10 @@ static int xenbus_probe_device ( struct xen_hypervisor *xen, xendev->xen = xen; xendev->key = ( ( void * ) ( xendev + 1 ) ); snprintf ( xendev->key, key_len, "device/%s/%s", type, instance ); + xendev->driver = driver; + xendev->dev.driver_name = driver->name; + DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key, + xendev->driver->name ); /* Read backend key */ if ( ( rc = xenstore_read ( xen, &xendev->backend, xendev->key, @@ -253,18 +258,6 @@ static int xenbus_probe_device ( struct xen_hypervisor *xen, DBGC ( xendev, "XENBUS %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); - /* Look for a driver */ - xendev->driver = xenbus_find_driver ( type ); - if ( ! xendev->driver ) { - DBGC ( xendev, "XENBUS %s has no driver\n", xendev->key ); - /* Not a fatal error */ - rc = 0; - goto err_no_driver; - } - xendev->dev.driver_name = xendev->driver->name; - DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key, - xendev->driver->name ); - /* Probe driver */ if ( ( rc = xendev->driver->probe ( xendev ) ) != 0 ) { DBGC ( xendev, "XENBUS could not probe %s: %s\n", @@ -276,7 +269,6 @@ static int xenbus_probe_device ( struct xen_hypervisor *xen, xendev->driver->remove ( xendev ); err_probe: - err_no_driver: err_read_backend_id: free ( xendev->backend ); err_read_backend: @@ -310,11 +302,21 @@ static void xenbus_remove_device ( struct xen_device *xendev ) { */ static int xenbus_probe_type ( struct xen_hypervisor *xen, struct device *parent, const char *type ) { + struct xen_driver *driver; char *children; char *child; size_t len; int rc; + /* Look for a driver */ + driver = xenbus_find_driver ( type ); + if ( ! driver ) { + DBGC ( xen, "XENBUS has no driver for \"%s\" devices\n", type ); + /* Not a fatal error */ + rc = 0; + goto err_no_driver; + } + /* Get children of this key */ if ( ( rc = xenstore_directory ( xen, &children, &len, "device", type, NULL ) ) != 0 ) { @@ -326,8 +328,8 @@ static int xenbus_probe_type ( struct xen_hypervisor *xen, /* Probe each child */ for ( child = children ; child < ( children + len ) ; child += ( strlen ( child ) + 1 /* NUL */ ) ) { - if ( ( rc = xenbus_probe_device ( xen, parent, type, - child ) ) != 0 ) + if ( ( rc = xenbus_probe_device ( xen, parent, child, + driver ) ) != 0 ) goto err_probe_device; } @@ -337,6 +339,7 @@ static int xenbus_probe_type ( struct xen_hypervisor *xen, err_probe_device: free ( children ); err_directory: + err_no_driver: return rc; } From ea29122a70c6dd589530db72b82c011c7ecc5bb6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Dec 2017 12:34:07 +0000 Subject: [PATCH 534/591] [http] Include error messages for 4xx and 5xx response codes Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index 4a1300cd7..01143a1a7 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -55,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /* Disambiguate the various error causes */ @@ -110,6 +111,12 @@ static struct profiler http_rx_profiler __profiler = { .name = "http.rx" }; /** Data transfer profiler */ static struct profiler http_xfer_profiler __profiler = { .name = "http.xfer" }; +/** Human-readable error messages */ +struct errortab http_errors[] __errortab = { + __einfo_errortab ( EINFO_EIO_4XX ), + __einfo_errortab ( EINFO_EIO_5XX ), +}; + static struct http_state http_request; static struct http_state http_headers; static struct http_state http_trailers; From 659c484efcb04c434cc562162eb0231be41cb816 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Dec 2017 13:04:59 +0000 Subject: [PATCH 535/591] [http] Report unsuccessful response status lines at DBGVL_LOG The precise HTTP response status code is currently visible only at DBGLVL_EXTRA. Allow for easier debugging by reporting the whole status line at DBGLVL_LOG for any unsuccessful responses. Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index 01143a1a7..b3c9a00e6 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -1163,6 +1163,8 @@ static int http_parse_status ( struct http_transaction *http, char *line ) { response_rc = -EIO_OTHER; } http->response.rc = response_rc; + if ( response_rc ) + DBGC ( http, "HTTP %p status %s\n", http, status ); return 0; } From be9ed2848dde771307d9089dbfd4481ed16e2607 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Dec 2017 13:38:50 +0000 Subject: [PATCH 536/591] [image] Omit URI query string and fragment from download progress messages The URIs printed as part of download progress messages are intended to provide a quick visual progress indication to the user. Very long query strings can render this visual indication useless in practice, since the most important information (generally the URI host and path) is drowned out by multiple lines of human-illegible URI-encoded data. Omit the query string entirely from the download progress message. For consistency and brevity, also omit the URI fragment along with the username and password (which was previously redacted anyway). Signed-off-by: Michael Brown --- src/usr/imgmgmt.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/usr/imgmgmt.c b/src/usr/imgmgmt.c index 352dd0242..a01d6e291 100644 --- a/src/usr/imgmgmt.c +++ b/src/usr/imgmgmt.c @@ -50,16 +50,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int imgdownload ( struct uri *uri, unsigned long timeout, struct image **image ) { - const char *password; + struct uri uri_redacted; char *uri_string_redacted; int rc; /* Construct redacted URI */ - password = uri->password; - if ( password ) - uri->password = "***"; - uri_string_redacted = format_uri_alloc ( uri ); - uri->password = password; + memcpy ( &uri_redacted, uri, sizeof ( uri_redacted ) ); + uri_redacted.user = NULL; + uri_redacted.password = NULL; + uri_redacted.query = NULL; + uri_redacted.fragment = NULL; + uri_string_redacted = format_uri_alloc ( &uri_redacted ); if ( ! uri_string_redacted ) { rc = -ENOMEM; goto err_uri_string; From ff648c339d25589a343a284286137058d19c17c2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Dec 2017 11:53:46 +0000 Subject: [PATCH 537/591] [legal] Add missing FILE_LICENCE declarations Add missing FILE_LICENCE declarations to EFI headers based on the corresponding source file. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_file.h | 2 ++ src/include/ipxe/efi/efi_snp.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/include/ipxe/efi/efi_file.h b/src/include/ipxe/efi/efi_file.h index e4db0305a..79c073cf1 100644 --- a/src/include/ipxe/efi/efi_file.h +++ b/src/include/ipxe/efi/efi_file.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + extern int efi_file_install ( EFI_HANDLE handle ); extern void efi_file_uninstall ( EFI_HANDLE handle ); diff --git a/src/include/ipxe/efi/efi_snp.h b/src/include/ipxe/efi/efi_snp.h index a9f67cfcb..9076f1d56 100644 --- a/src/include/ipxe/efi/efi_snp.h +++ b/src/include/ipxe/efi/efi_snp.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include #include #include From 00c5b958c59ec45736469fce674c5307cabbfd91 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Dec 2017 11:57:00 +0000 Subject: [PATCH 538/591] [legal] Add missing FILE_LICENCE declarations Add missing FILE_LICENCE declarations to x86_64 headers based on the corresponding i386 headers (from which the x86_64 headers were originally derived). Signed-off-by: Michael Brown --- src/arch/x86_64/include/bits/compiler.h | 2 ++ src/arch/x86_64/include/bits/stdint.h | 2 ++ src/arch/x86_64/include/limits.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/arch/x86_64/include/bits/compiler.h b/src/arch/x86_64/include/bits/compiler.h index 98c560e7d..46985da3e 100644 --- a/src/arch/x86_64/include/bits/compiler.h +++ b/src/arch/x86_64/include/bits/compiler.h @@ -1,6 +1,8 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** Dummy relocation type */ #define RELOC_TYPE_NONE R_X86_64_NONE diff --git a/src/arch/x86_64/include/bits/stdint.h b/src/arch/x86_64/include/bits/stdint.h index 9eb72e9c4..fe1f9946a 100644 --- a/src/arch/x86_64/include/bits/stdint.h +++ b/src/arch/x86_64/include/bits/stdint.h @@ -1,6 +1,8 @@ #ifndef _BITS_STDINT_H #define _BITS_STDINT_H +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + typedef __SIZE_TYPE__ size_t; typedef signed long ssize_t; typedef signed long off_t; diff --git a/src/arch/x86_64/include/limits.h b/src/arch/x86_64/include/limits.h index 8cf87b471..a1374a17f 100644 --- a/src/arch/x86_64/include/limits.h +++ b/src/arch/x86_64/include/limits.h @@ -1,6 +1,8 @@ #ifndef LIMITS_H #define LIMITS_H 1 +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /* Number of bits in a `char' */ #define CHAR_BIT 8 From 2bb4ec1f54af11f51f6f064c0086b6b80be2dcd2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 2 Jan 2018 21:26:40 +0100 Subject: [PATCH 539/591] [build] Avoid use of "ld --oformat binary" Using "ld --oformat binary" for mbr.bin and usbdisk.bin seems to cause segmentation faults on some versions of binutils (observed on Fedora 27). Work around this problem by using ld to create an intermediate ELF object, followed by objcopy (via the existing %.tmp -> %.bin rule) to create the final binary. Note that we cannot simply use a single-stage "objcopy -O binary" since this will not process the relocation records for x86_64: see commit 1afcccd ("[build] Do not use "objcopy -O binary" for objects with relocation records"). Reported-by: Brent S Signed-off-by: Michael Brown --- src/arch/x86/Makefile.pcbios | 8 ++++---- src/arch/x86/prefix/mbr.S | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/arch/x86/Makefile.pcbios b/src/arch/x86/Makefile.pcbios index df08e0182..c44eefc1f 100644 --- a/src/arch/x86/Makefile.pcbios +++ b/src/arch/x86/Makefile.pcbios @@ -106,14 +106,14 @@ NON_AUTO_MEDIA += fd0 $(Q)sync # Special target for building Master Boot Record binary -$(BIN)/mbr.bin : $(BIN)/mbr.o +$(BIN)/mbr.tmp : $(BIN)/mbr.o $(QM)$(ECHO) " [LD] $@" - $(Q)$(LD) $(LDFLAGS) -o $@ --oformat binary -e 0 $< + $(Q)$(LD) $(LDFLAGS) -o $@ -e mbr $< # rule to make a USB disk image -$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o +$(BIN)/usbdisk.tmp : $(BIN)/usbdisk.o $(QM)$(ECHO) " [LD] $@" - $(Q)$(LD) $(LDFLAGS) -o $@ --oformat binary -e 0 $< + $(Q)$(LD) $(LDFLAGS) -o $@ -e mbr $< NON_AUTO_MEDIA += usb %usb: $(BIN)/usbdisk.bin %hd diff --git a/src/arch/x86/prefix/mbr.S b/src/arch/x86/prefix/mbr.S index a1e237de8..032c0e775 100644 --- a/src/arch/x86/prefix/mbr.S +++ b/src/arch/x86/prefix/mbr.S @@ -6,6 +6,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .code16 .org 0 + .globl mbr mbr: movw $exec_sector, %bp jmp find_active_partition From 2fb70e8b32244e25ca3f43518b580fe0a54536d2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Jan 2018 13:21:40 +0000 Subject: [PATCH 540/591] [ena] Add driver for Amazon ENA virtual function NIC Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 1039 ++++++++++++++++++++++++++++++++++++ src/drivers/net/ena.h | 588 ++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 1628 insertions(+) create mode 100644 src/drivers/net/ena.c create mode 100644 src/drivers/net/ena.h diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c new file mode 100644 index 000000000..b6d8bc6f1 --- /dev/null +++ b/src/drivers/net/ena.c @@ -0,0 +1,1039 @@ +/* + * Copyright (C) 2018 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 +#include "ena.h" + +/** @file + * + * Amazon ENA network driver + * + */ + +/** + * Get direction name (for debugging) + * + * @v direction Direction + * @ret name Direction name + */ +static const char * ena_direction ( unsigned int direction ) { + + switch ( direction ) { + case ENA_SQ_TX: return "TX"; + case ENA_SQ_RX: return "RX"; + default: return ""; + } +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_reset ( struct ena_nic *ena ) { + uint32_t stat; + unsigned int i; + + /* Trigger reset */ + writel ( ENA_CTRL_RESET, ( ena->regs + ENA_CTRL ) ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < ENA_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if device is ready */ + stat = readl ( ena->regs + ENA_STAT ); + if ( stat & ENA_STAT_READY ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ena, "ENA %p timed out waiting for reset (status %#08x)\n", + ena, stat ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** + * Set queue base address + * + * @v ena ENA device + * @v offset Register offset + * @v address Base address + */ +static inline void ena_set_base ( struct ena_nic *ena, unsigned int offset, + void *base ) { + physaddr_t phys = virt_to_bus ( base ); + + /* Program base address registers */ + writel ( ( phys & 0xffffffffUL ), + ( ena->regs + offset + ENA_BASE_LO ) ); + if ( sizeof ( phys ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) phys ) >> 32 ), + ( ena->regs + offset + ENA_BASE_HI ) ); + } else { + writel ( 0, ( ena->regs + offset + ENA_BASE_HI ) ); + } +} + +/** + * Set queue capabilities + * + * @v ena ENA device + * @v offset Register offset + * @v count Number of entries + * @v size Size of each entry + */ +static inline __attribute__ (( always_inline )) void +ena_set_caps ( struct ena_nic *ena, unsigned int offset, unsigned int count, + size_t size ) { + + /* Program capabilities register */ + writel ( ENA_CAPS ( count, size ), ( ena->regs + offset ) ); +} + +/** + * Clear queue capabilities + * + * @v ena ENA device + * @v offset Register offset + */ +static inline __attribute__ (( always_inline )) void +ena_clear_caps ( struct ena_nic *ena, unsigned int offset ) { + + /* Clear capabilities register */ + writel ( 0, ( ena->regs + offset ) ); +} + +/** + * Create admin queues + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_create_admin ( struct ena_nic *ena ) { + size_t aq_len = ( ENA_AQ_COUNT * sizeof ( ena->aq.req[0] ) ); + size_t acq_len = ( ENA_ACQ_COUNT * sizeof ( ena->acq.rsp[0] ) ); + int rc; + + /* Allocate admin completion queue */ + ena->acq.rsp = malloc_dma ( acq_len, acq_len ); + if ( ! ena->acq.rsp ) { + rc = -ENOMEM; + goto err_alloc_acq; + } + memset ( ena->acq.rsp, 0, acq_len ); + + /* Allocate admin queue */ + ena->aq.req = malloc_dma ( aq_len, aq_len ); + if ( ! ena->aq.req ) { + rc = -ENOMEM; + goto err_alloc_aq; + } + memset ( ena->aq.req, 0, aq_len ); + + /* Program queue addresses and capabilities */ + ena_set_base ( ena, ENA_ACQ_BASE, ena->acq.rsp ); + ena_set_caps ( ena, ENA_ACQ_CAPS, ENA_ACQ_COUNT, + sizeof ( ena->acq.rsp[0] ) ); + ena_set_base ( ena, ENA_AQ_BASE, ena->aq.req ); + ena_set_caps ( ena, ENA_AQ_CAPS, ENA_AQ_COUNT, + sizeof ( ena->aq.req[0] ) ); + + DBGC ( ena, "ENA %p AQ [%08lx,%08lx) ACQ [%08lx,%08lx)\n", + ena, virt_to_phys ( ena->aq.req ), + ( virt_to_phys ( ena->aq.req ) + aq_len ), + virt_to_phys ( ena->acq.rsp ), + ( virt_to_phys ( ena->acq.rsp ) + acq_len ) ); + return 0; + + ena_clear_caps ( ena, ENA_AQ_CAPS ); + ena_clear_caps ( ena, ENA_ACQ_CAPS ); + free_dma ( ena->aq.req, aq_len ); + err_alloc_aq: + free_dma ( ena->acq.rsp, acq_len ); + err_alloc_acq: + return rc; +} + +/** + * Destroy admin queues + * + * @v ena ENA device + */ +static void ena_destroy_admin ( struct ena_nic *ena ) { + size_t aq_len = ( ENA_AQ_COUNT * sizeof ( ena->aq.req[0] ) ); + size_t acq_len = ( ENA_ACQ_COUNT * sizeof ( ena->acq.rsp[0] ) ); + + /* Clear queue capabilities */ + ena_clear_caps ( ena, ENA_AQ_CAPS ); + ena_clear_caps ( ena, ENA_ACQ_CAPS ); + wmb(); + + /* Free queues */ + free_dma ( ena->aq.req, aq_len ); + free_dma ( ena->acq.rsp, acq_len ); + DBGC ( ena, "ENA %p AQ and ACQ destroyed\n", ena ); +} + +/** + * Get next available admin queue request + * + * @v ena ENA device + * @ret req Admin queue request + */ +static union ena_aq_req * ena_admin_req ( struct ena_nic *ena ) { + union ena_aq_req *req; + unsigned int index; + + /* Get next request */ + index = ( ena->aq.prod % ENA_AQ_COUNT ); + req = &ena->aq.req[index]; + + /* Initialise request */ + memset ( ( ( ( void * ) req ) + sizeof ( req->header ) ), 0, + ( sizeof ( *req ) - sizeof ( req->header ) ) ); + req->header.id = ena->aq.prod; + + /* Increment producer counter */ + ena->aq.prod++; + + return req; +} + +/** + * Issue admin queue request + * + * @v ena ENA device + * @v req Admin queue request + * @v rsp Admin queue response to fill in + * @ret rc Return status code + */ +static int ena_admin ( struct ena_nic *ena, union ena_aq_req *req, + union ena_acq_rsp **rsp ) { + unsigned int index; + unsigned int i; + int rc; + + /* Locate response */ + index = ( ena->acq.cons % ENA_ACQ_COUNT ); + *rsp = &ena->acq.rsp[index]; + + /* Mark request as ready */ + req->header.flags ^= ENA_AQ_PHASE; + wmb(); + DBGC2 ( ena, "ENA %p admin request %#x:\n", + ena, le16_to_cpu ( req->header.id ) ); + DBGC2_HDA ( ena, virt_to_phys ( req ), req, sizeof ( *req ) ); + + /* Ring doorbell */ + writel ( ena->aq.prod, ( ena->regs + ENA_AQ_DB ) ); + + /* Wait for response */ + for ( i = 0 ; i < ENA_ADMIN_MAX_WAIT_MS ; i++ ) { + + /* Check for response */ + if ( ( (*rsp)->header.flags ^ ena->acq.phase ) & ENA_ACQ_PHASE){ + mdelay ( 1 ); + continue; + } + DBGC2 ( ena, "ENA %p admin response %#x:\n", + ena, le16_to_cpu ( (*rsp)->header.id ) ); + DBGC2_HDA ( ena, virt_to_phys ( *rsp ), *rsp, sizeof ( **rsp )); + + /* Increment consumer counter */ + ena->acq.cons++; + if ( ( ena->acq.cons % ENA_ACQ_COUNT ) == 0 ) + ena->acq.phase ^= ENA_ACQ_PHASE; + + /* Check command identifier */ + if ( (*rsp)->header.id != req->header.id ) { + DBGC ( ena, "ENA %p admin response %#x mismatch:\n", + ena, le16_to_cpu ( (*rsp)->header.id ) ); + rc = -EILSEQ; + goto err; + } + + /* Check status */ + if ( (*rsp)->header.status != 0 ) { + DBGC ( ena, "ENA %p admin response %#x status %d:\n", + ena, le16_to_cpu ( (*rsp)->header.id ), + (*rsp)->header.status ); + rc = -EIO; + goto err; + } + + /* Success */ + return 0; + } + + rc = -ETIMEDOUT; + DBGC ( ena, "ENA %p timed out waiting for admin request %#x:\n", + ena, le16_to_cpu ( req->header.id ) ); + err: + DBGC_HDA ( ena, virt_to_phys ( req ), req, sizeof ( *req ) ); + DBGC_HDA ( ena, virt_to_phys ( *rsp ), *rsp, sizeof ( **rsp ) ); + return rc; +} + +/** + * Create submission queue + * + * @v ena ENA device + * @v sq Submission queue + * @v cq Corresponding completion queue + * @ret rc Return status code + */ +static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, + struct ena_cq *cq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Allocate submission queue entries */ + sq->sqe.raw = malloc_dma ( sq->len, ENA_ALIGN ); + if ( ! sq->sqe.raw ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( sq->sqe.raw, 0, sq->len ); + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_CREATE_SQ; + req->create_sq.direction = sq->direction; + req->create_sq.policy = cpu_to_le16 ( ENA_SQ_HOST_MEMORY | + ENA_SQ_CONTIGUOUS ); + req->create_sq.cq_id = cpu_to_le16 ( cq->id ); + req->create_sq.count = cpu_to_le16 ( sq->count ); + req->create_sq.address = cpu_to_le64 ( virt_to_bus ( sq->sqe.raw ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + goto err_admin; + + /* Parse response */ + sq->id = le16_to_cpu ( rsp->create_sq.id ); + sq->doorbell = le32_to_cpu ( rsp->create_sq.doorbell ); + + /* Reset producer counter and phase */ + sq->prod = 0; + sq->phase = ENA_SQE_PHASE; + + DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) db +%04x CQ%d\n", + ena, ena_direction ( sq->direction ), sq->id, + virt_to_phys ( sq->sqe.raw ), + ( virt_to_phys ( sq->sqe.raw ) + sq->len ), + sq->doorbell, cq->id ); + return 0; + + err_admin: + free_dma ( sq->sqe.raw, sq->len ); + err_alloc: + return rc; +} + +/** + * Destroy submission queue + * + * @v ena ENA device + * @v sq Submission queue + * @ret rc Return status code + */ +static int ena_destroy_sq ( struct ena_nic *ena, struct ena_sq *sq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_DESTROY_SQ; + req->destroy_sq.id = cpu_to_le16 ( sq->id ); + req->destroy_sq.direction = sq->direction; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Free submission queue entries */ + free_dma ( sq->sqe.raw, sq->len ); + + DBGC ( ena, "ENA %p %s SQ%d destroyed\n", + ena, ena_direction ( sq->direction ), sq->id ); + return 0; +} + +/** + * Create completion queue + * + * @v ena ENA device + * @v cq Completion queue + * @ret rc Return status code + */ +static int ena_create_cq ( struct ena_nic *ena, struct ena_cq *cq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Allocate completion queue entries */ + cq->cqe.raw = malloc_dma ( cq->len, ENA_ALIGN ); + if ( ! cq->cqe.raw ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( cq->cqe.raw, 0, cq->len ); + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_CREATE_CQ; + req->create_cq.size = cq->size; + req->create_cq.count = cpu_to_le16 ( cq->requested ); + req->create_cq.address = cpu_to_le64 ( virt_to_bus ( cq->cqe.raw ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + goto err_admin; + + /* Parse response */ + cq->id = le16_to_cpu ( rsp->create_cq.id ); + cq->actual = le16_to_cpu ( rsp->create_cq.count ); + cq->doorbell = le32_to_cpu ( rsp->create_cq.doorbell ); + cq->mask = ( cq->actual - 1 ); + if ( cq->actual != cq->requested ) { + DBGC ( ena, "ENA %p CQ%d requested %d actual %d\n", + ena, cq->id, cq->requested, cq->actual ); + } + + /* Reset consumer counter and phase */ + cq->cons = 0; + cq->phase = ENA_CQE_PHASE; + + DBGC ( ena, "ENA %p CQ%d at [%08lx,%08lx) db +%04x\n", + ena, cq->id, virt_to_phys ( cq->cqe.raw ), + ( virt_to_phys ( cq->cqe.raw ) + cq->len ), cq->doorbell ); + return 0; + + err_admin: + free_dma ( cq->cqe.raw, cq->len ); + err_alloc: + return rc; +} + +/** + * Destroy completion queue + * + * @v ena ENA device + * @v cq Completion queue + * @ret rc Return status code + */ +static int ena_destroy_cq ( struct ena_nic *ena, struct ena_cq *cq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_DESTROY_CQ; + req->destroy_cq.id = cpu_to_le16 ( cq->id ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Free completion queue entries */ + free_dma ( cq->cqe.raw, cq->len ); + + DBGC ( ena, "ENA %p CQ%d destroyed\n", ena, cq->id ); + return 0; +} + +/** + * Create queue pair + * + * @v ena ENA device + * @v qp Queue pair + * @ret rc Return status code + */ +static int ena_create_qp ( struct ena_nic *ena, struct ena_qp *qp ) { + int rc; + + /* Create completion queue */ + if ( ( rc = ena_create_cq ( ena, &qp->cq ) ) != 0 ) + goto err_create_cq; + + /* Create submission queue */ + if ( ( rc = ena_create_sq ( ena, &qp->sq, &qp->cq ) ) != 0 ) + goto err_create_sq; + + return 0; + + ena_destroy_sq ( ena, &qp->sq ); + err_create_sq: + ena_destroy_cq ( ena, &qp->cq ); + err_create_cq: + return rc; +} + +/** + * Destroy queue pair + * + * @v ena ENA device + * @v qp Queue pair + * @ret rc Return status code + */ +static int ena_destroy_qp ( struct ena_nic *ena, struct ena_qp *qp ) { + + /* Destroy submission queue */ + ena_destroy_sq ( ena, &qp->sq ); + + /* Destroy completion queue */ + ena_destroy_cq ( ena, &qp->cq ); + + return 0; +} + +/** + * Get feature + * + * @v ena ENA device + * @v id Feature identifier + * @v feature Feature to fill in + * @ret rc Return status code + */ +static int ena_get_feature ( struct ena_nic *ena, unsigned int id, + union ena_feature **feature ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_GET_FEATURE; + req->get_feature.id = id; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Parse response */ + *feature = &rsp->get_feature.feature; + + return 0; +} + +/** + * Get device attributes + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ena_get_device_attributes ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + union ena_feature *feature; + int rc; + + /* Get device attributes */ + if ( ( rc = ena_get_feature ( ena, ENA_DEVICE_ATTRIBUTES, + &feature ) ) != 0 ) + return rc; + + /* Extract MAC address */ + memcpy ( netdev->hw_addr, feature->device.mac, ETH_ALEN ); + + /* Extract MTU */ + netdev->max_pkt_len = le32_to_cpu ( feature->device.mtu ); + + DBGC ( ena, "ENA %p MAC %s MTU %zd\n", + ena, eth_ntoa ( netdev->hw_addr ), netdev->max_pkt_len ); + return 0; +} + +/** + * Get statistics (for debugging) + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_get_stats ( struct ena_nic *ena ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + struct ena_get_stats_rsp *stats; + int rc; + + /* Do nothing unless debug messages are enabled */ + if ( ! DBG_LOG ) + return 0; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_GET_STATS; + req->get_stats.type = ENA_STATS_TYPE_BASIC; + req->get_stats.scope = ENA_STATS_SCOPE_ETH; + req->get_stats.device = ENA_DEVICE_MINE; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Parse response */ + stats = &rsp->get_stats; + DBGC ( ena, "ENA %p TX bytes %#llx packets %#llx\n", ena, + ( ( unsigned long long ) le64_to_cpu ( stats->tx_bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( stats->tx_packets ) ) ); + DBGC ( ena, "ENA %p RX bytes %#llx packets %#llx drops %#llx\n", ena, + ( ( unsigned long long ) le64_to_cpu ( stats->rx_bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( stats->rx_packets ) ), + ( ( unsigned long long ) le64_to_cpu ( stats->rx_drops ) ) ); + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Refill receive queue + * + * @v netdev Network device + */ +static void ena_refill_rx ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + struct io_buffer *iobuf; + struct ena_rx_sqe *sqe; + unsigned int index; + physaddr_t address; + size_t len = netdev->max_pkt_len; + unsigned int refilled = 0; + + /* Refill queue */ + while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ENA_RX_COUNT ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next submission queue entry */ + index = ( ena->rx.sq.prod % ENA_RX_COUNT ); + sqe = &ena->rx.sq.sqe.rx[index]; + + /* Construct submission queue entry */ + address = virt_to_bus ( iobuf->data ); + sqe->len = cpu_to_le16 ( len ); + sqe->id = cpu_to_le16 ( ena->rx.sq.prod ); + sqe->address = cpu_to_le64 ( address ); + wmb(); + sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | + ena->rx.sq.phase ); + + /* Increment producer counter */ + ena->rx.sq.prod++; + if ( ( ena->rx.sq.prod % ENA_RX_COUNT ) == 0 ) + ena->rx.sq.phase ^= ENA_SQE_PHASE; + + /* Record I/O buffer */ + assert ( ena->rx_iobuf[index] == NULL ); + ena->rx_iobuf[index] = iobuf; + + DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, sqe->id, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + refilled++; + } + + /* Ring doorbell, if applicable */ + if ( refilled ) { + wmb(); + writel ( ena->rx.sq.prod, ( ena->regs + ena->rx.sq.doorbell ) ); + } +} + +/** + * Discard unused receive I/O buffers + * + * @v ena ENA device + */ +static void ena_empty_rx ( struct ena_nic *ena ) { + unsigned int i; + + for ( i = 0 ; i < ENA_RX_COUNT ; i++ ) { + if ( ena->rx_iobuf[i] ) + free_iob ( ena->rx_iobuf[i] ); + ena->rx_iobuf[i] = NULL; + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ena_open ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + int rc; + + /* Create transmit queue pair */ + if ( ( rc = ena_create_qp ( ena, &ena->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive queue pair */ + if ( ( rc = ena_create_qp ( ena, &ena->rx ) ) != 0 ) + goto err_create_rx; + + /* Refill receive queue */ + ena_refill_rx ( netdev ); + + return 0; + + ena_destroy_qp ( ena, &ena->rx ); + err_create_rx: + ena_destroy_qp ( ena, &ena->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ena_close ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + + /* Dump statistics (for debugging) */ + ena_get_stats ( ena ); + + /* Destroy receive queue pair */ + ena_destroy_qp ( ena, &ena->rx ); + + /* Discard any unused receive buffers */ + ena_empty_rx ( ena ); + + /* Destroy transmit queue pair */ + ena_destroy_qp ( ena, &ena->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct ena_nic *ena = netdev->priv; + struct ena_tx_sqe *sqe; + unsigned int index; + physaddr_t address; + size_t len; + + /* Get next submission queue entry */ + if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ENA_TX_COUNT ) { + DBGC ( ena, "ENA %p out of transmit descriptors\n", ena ); + return -ENOBUFS; + } + index = ( ena->tx.sq.prod % ENA_TX_COUNT ); + sqe = &ena->tx.sq.sqe.tx[index]; + + /* Construct submission queue entry */ + address = virt_to_bus ( iobuf->data ); + len = iob_len ( iobuf ); + sqe->len = cpu_to_le16 ( len ); + sqe->id = ena->tx.sq.prod; + sqe->address = cpu_to_le64 ( address ); + wmb(); + sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | + ena->tx.sq.phase ); + wmb(); + + /* Increment producer counter */ + ena->tx.sq.prod++; + if ( ( ena->tx.sq.prod % ENA_TX_COUNT ) == 0 ) + ena->tx.sq.phase ^= ENA_SQE_PHASE; + + /* Ring doorbell */ + writel ( ena->tx.sq.prod, ( ena->regs + ena->tx.sq.doorbell ) ); + + DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, sqe->id, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + return 0; +} + +/** + * Poll for completed transmissions + * + * @v netdev Network device + */ +static void ena_poll_tx ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + struct ena_tx_cqe *cqe; + unsigned int index; + + /* Check for completed packets */ + while ( ena->tx.cq.cons != ena->tx.sq.prod ) { + + /* Get next completion queue entry */ + index = ( ena->tx.cq.cons & ena->tx.cq.mask ); + cqe = &ena->tx.cq.cqe.tx[index]; + + /* Stop if completion queue entry is empty */ + if ( ( cqe->flags ^ ena->tx.cq.phase ) & ENA_CQE_PHASE ) + return; + DBGC2 ( ena, "ENA %p TX %d complete\n", ena, + ( le16_to_cpu ( cqe->id ) >> 2 /* Don't ask */ ) ); + + /* Increment consumer counter */ + ena->tx.cq.cons++; + if ( ! ( ena->tx.cq.cons & ena->tx.cq.mask ) ) + ena->tx.cq.phase ^= ENA_CQE_PHASE; + + /* Complete transmit */ + netdev_tx_complete_next ( netdev ); + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void ena_poll_rx ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + struct ena_rx_cqe *cqe; + struct io_buffer *iobuf; + unsigned int index; + size_t len; + + /* Check for received packets */ + while ( ena->rx.cq.cons != ena->rx.sq.prod ) { + + /* Get next completion queue entry */ + index = ( ena->rx.cq.cons % ENA_RX_COUNT ); + cqe = &ena->rx.cq.cqe.rx[index]; + + /* Stop if completion queue entry is empty */ + if ( ( cqe->flags ^ ena->rx.cq.phase ) & ENA_CQE_PHASE ) + return; + + /* Increment consumer counter */ + ena->rx.cq.cons++; + if ( ! ( ena->rx.cq.cons & ena->rx.cq.mask ) ) + ena->rx.cq.phase ^= ENA_CQE_PHASE; + + /* Populate I/O buffer */ + iobuf = ena->rx_iobuf[index]; + ena->rx_iobuf[index] = NULL; + len = le16_to_cpu ( cqe->len ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + DBGC2 ( ena, "ENA %p RX %d complete (length %zd)\n", + ena, le16_to_cpu ( cqe->id ), len ); + netdev_rx ( netdev, iobuf ); + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void ena_poll ( struct net_device *netdev ) { + + /* Poll for transmit completions */ + ena_poll_tx ( netdev ); + + /* Poll for receive completions */ + ena_poll_rx ( netdev ); + + /* Refill receive ring */ + ena_refill_rx ( netdev ); +} + +/** ENA network device operations */ +static struct net_device_operations ena_operations = { + .open = ena_open, + .close = ena_close, + .transmit = ena_transmit, + .poll = ena_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ena_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct ena_nic *ena; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *ena ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ena_operations ); + ena = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( ena, 0, sizeof ( *ena ) ); + ena->acq.phase = ENA_ACQ_PHASE; + ena_cq_init ( &ena->tx.cq, ENA_TX_COUNT, + sizeof ( ena->tx.cq.cqe.tx[0] ) ); + ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, + sizeof ( ena->tx.sq.sqe.tx[0] ) ); + ena_cq_init ( &ena->rx.cq, ENA_RX_COUNT, + sizeof ( ena->rx.cq.cqe.rx[0] ) ); + ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, + sizeof ( ena->rx.sq.sqe.rx[0] ) ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + ena->regs = ioremap ( pci->membase, ENA_BAR_SIZE ); + if ( ! ena->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the NIC */ + if ( ( rc = ena_reset ( ena ) ) != 0 ) + goto err_reset; + + /* Create admin queues */ + if ( ( rc = ena_create_admin ( ena ) ) != 0 ) + goto err_create_admin; + + /* Fetch MAC address */ + if ( ( rc = ena_get_device_attributes ( netdev ) ) != 0 ) + goto err_get_device_attributes; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Mark as link up, since we have no way to test link state on + * this hardware. + */ + netdev_link_up ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_get_device_attributes: + ena_destroy_admin ( ena ); + err_create_admin: + ena_reset ( ena ); + err_reset: + iounmap ( ena->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void ena_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct ena_nic *ena = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Destroy admin queues */ + ena_destroy_admin ( ena ); + + /* Reset card */ + ena_reset ( ena ); + + /* Free network device */ + iounmap ( ena->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** ENA PCI device IDs */ +static struct pci_device_id ena_nics[] = { + PCI_ROM ( 0x1d0f, 0xec20, "ena-vf", "ENA VF", 0 ), + PCI_ROM ( 0x1d0f, 0xec21, "ena-vf-llq", "ENA VF (LLQ)", 0 ), +}; + +/** ENA PCI driver */ +struct pci_driver ena_driver __pci_driver = { + .ids = ena_nics, + .id_count = ( sizeof ( ena_nics ) / sizeof ( ena_nics[0] ) ), + .probe = ena_probe, + .remove = ena_remove, +}; diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h new file mode 100644 index 000000000..0496fc6bd --- /dev/null +++ b/src/drivers/net/ena.h @@ -0,0 +1,588 @@ +#ifndef _ENA_H +#define _ENA_H + +/** @file + * + * Amazon ENA network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** BAR size */ +#define ENA_BAR_SIZE 16384 + +/** Queue alignment */ +#define ENA_ALIGN 4096 + +/** Number of admin queue entries */ +#define ENA_AQ_COUNT 2 + +/** Number of admin completion queue entries */ +#define ENA_ACQ_COUNT 2 + +/** Number of transmit queue entries */ +#define ENA_TX_COUNT 16 + +/** Number of receive queue entries */ +#define ENA_RX_COUNT 16 + +/** Base address low register offset */ +#define ENA_BASE_LO 0x0 + +/** Base address high register offset */ +#define ENA_BASE_HI 0x4 + +/** Capability register value */ +#define ENA_CAPS( count, size ) ( ( (size) << 16 ) | ( (count) << 0 ) ) + +/** Admin queue base address register */ +#define ENA_AQ_BASE 0x10 + +/** Admin queue capabilities register */ +#define ENA_AQ_CAPS 0x18 + +/** Admin completion queue base address register */ +#define ENA_ACQ_BASE 0x20 + +/** Admin completion queue capabilities register */ +#define ENA_ACQ_CAPS 0x28 + +/** Admin queue doorbell register */ +#define ENA_AQ_DB 0x2c + +/** Maximum time to wait for admin requests */ +#define ENA_ADMIN_MAX_WAIT_MS 5000 + +/** Device control register */ +#define ENA_CTRL 0x54 +#define ENA_CTRL_RESET 0x00000001UL /**< Reset */ + +/** Maximum time to wait for reset */ +#define ENA_RESET_MAX_WAIT_MS 1000 + +/** Device status register */ +#define ENA_STAT 0x58 +#define ENA_STAT_READY 0x00000001UL /**< Ready */ + +/** Admin queue entry header */ +struct ena_aq_header { + /** Request identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved; + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; +} __attribute__ (( packed )); + +/** Admin queue ownership phase flag */ +#define ENA_AQ_PHASE 0x01 + +/** Admin completion queue entry header */ +struct ena_acq_header { + /** Request identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Extended status */ + uint16_t ext; + /** Consumer index */ + uint16_t cons; +} __attribute__ (( packed )); + +/** Admin completion queue ownership phase flag */ +#define ENA_ACQ_PHASE 0x01 + +/** Device attributes */ +#define ENA_DEVICE_ATTRIBUTES 1 + +/** Device attributes */ +struct ena_device_attributes { + /** Implementation */ + uint32_t implementation; + /** Device version */ + uint32_t version; + /** Supported features */ + uint32_t features; + /** Reserved */ + uint8_t reserved_a[4]; + /** Physical address width */ + uint32_t physical; + /** Virtual address width */ + uint32_t virtual; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Reserved */ + uint8_t reserved_b[2]; + /** Maximum MTU */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Feature */ +union ena_feature { + /** Device attributes */ + struct ena_device_attributes device; +}; + +/** Submission queue direction */ +enum ena_sq_direction { + /** Transmit */ + ENA_SQ_TX = 0x20, + /** Receive */ + ENA_SQ_RX = 0x40, +}; + +/** Create submission queue */ +#define ENA_CREATE_SQ 1 + +/** Create submission queue request */ +struct ena_create_sq_req { + /** Header */ + struct ena_aq_header header; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved_a; + /** Policy */ + uint16_t policy; + /** Completion queue identifier */ + uint16_t cq_id; + /** Number of entries */ + uint16_t count; + /** Base address */ + uint64_t address; + /** Writeback address */ + uint64_t writeback; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Submission queue policy */ +enum ena_sq_policy { + /** Use host memory */ + ENA_SQ_HOST_MEMORY = 0x0001, + /** Memory is contiguous */ + ENA_SQ_CONTIGUOUS = 0x0100, +}; + +/** Create submission queue response */ +struct ena_create_sq_rsp { + /** Header */ + struct ena_acq_header header; + /** Submission queue identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved[2]; + /** Doorbell register offset */ + uint32_t doorbell; + /** LLQ descriptor ring offset */ + uint32_t llq_desc; + /** LLQ header offset */ + uint32_t llq_data; +} __attribute__ (( packed )); + +/** Destroy submission queue */ +#define ENA_DESTROY_SQ 2 + +/** Destroy submission queue request */ +struct ena_destroy_sq_req { + /** Header */ + struct ena_aq_header header; + /** Submission queue identifier */ + uint16_t id; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** Destroy submission queue response */ +struct ena_destroy_sq_rsp { + /** Header */ + struct ena_acq_header header; +} __attribute__ (( packed )); + +/** Create completion queue */ +#define ENA_CREATE_CQ 3 + +/** Create completion queue request */ +struct ena_create_cq_req { + /** Header */ + struct ena_aq_header header; + /** Interrupts enabled */ + uint8_t intr; + /** Entry size (in 32-bit words) */ + uint8_t size; + /** Number of entries */ + uint16_t count; + /** MSI-X vector */ + uint32_t vector; + /** Base address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Create completion queue response */ +struct ena_create_cq_rsp { + /** Header */ + struct ena_acq_header header; + /** Completion queue identifier */ + uint16_t id; + /** Actual number of entries */ + uint16_t count; + /** NUMA node register offset */ + uint32_t node; + /** Doorbell register offset */ + uint32_t doorbell; + /** Interrupt unmask register offset */ + uint32_t intr; +} __attribute__ (( packed )); + +/** Destroy completion queue */ +#define ENA_DESTROY_CQ 4 + +/** Destroy completion queue request */ +struct ena_destroy_cq_req { + /** Header */ + struct ena_aq_header header; + /** Completion queue identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved[2]; +} __attribute__ (( packed )); + +/** Destroy completion queue response */ +struct ena_destroy_cq_rsp { + /** Header */ + struct ena_acq_header header; +} __attribute__ (( packed )); + +/** Get feature */ +#define ENA_GET_FEATURE 8 + +/** Get feature request */ +struct ena_get_feature_req { + /** Header */ + struct ena_aq_header header; + /** Length */ + uint32_t len; + /** Address */ + uint64_t address; + /** Flags */ + uint8_t flags; + /** Feature identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved[2]; +} __attribute__ (( packed )); + +/** Get feature response */ +struct ena_get_feature_rsp { + /** Header */ + struct ena_acq_header header; + /** Feature */ + union ena_feature feature; +} __attribute__ (( packed )); + +/** Get statistics */ +#define ENA_GET_STATS 11 + +/** Get statistics request */ +struct ena_get_stats_req { + /** Header */ + struct ena_aq_header header; + /** Reserved */ + uint8_t reserved_a[12]; + /** Type */ + uint8_t type; + /** Scope */ + uint8_t scope; + /** Reserved */ + uint8_t reserved_b[2]; + /** Queue ID */ + uint16_t queue; + /** Device ID */ + uint16_t device; +} __attribute__ (( packed )); + +/** Basic statistics */ +#define ENA_STATS_TYPE_BASIC 0 + +/** Ethernet statistics */ +#define ENA_STATS_SCOPE_ETH 1 + +/** My device */ +#define ENA_DEVICE_MINE 0xffff + +/** Get statistics response */ +struct ena_get_stats_rsp { + /** Header */ + struct ena_acq_header header; + /** Transmit byte count */ + uint64_t tx_bytes; + /** Transmit packet count */ + uint64_t tx_packets; + /** Receive byte count */ + uint64_t rx_bytes; + /** Receive packet count */ + uint64_t rx_packets; + /** Receive drop count */ + uint64_t rx_drops; +} __attribute__ (( packed )); + +/** Admin queue request */ +union ena_aq_req { + /** Header */ + struct ena_aq_header header; + /** Create submission queue */ + struct ena_create_sq_req create_sq; + /** Destroy submission queue */ + struct ena_destroy_sq_req destroy_sq; + /** Create completion queue */ + struct ena_create_cq_req create_cq; + /** Destroy completion queue */ + struct ena_destroy_cq_req destroy_cq; + /** Get feature */ + struct ena_get_feature_req get_feature; + /** Get statistics */ + struct ena_get_stats_req get_stats; + /** Padding */ + uint8_t pad[64]; +}; + +/** Admin completion queue response */ +union ena_acq_rsp { + /** Header */ + struct ena_acq_header header; + /** Create submission queue */ + struct ena_create_sq_rsp create_sq; + /** Destroy submission queue */ + struct ena_destroy_sq_rsp destroy_sq; + /** Create completion queue */ + struct ena_create_cq_rsp create_cq; + /** Destroy completion queue */ + struct ena_destroy_cq_rsp destroy_cq; + /** Get feature */ + struct ena_get_feature_rsp get_feature; + /** Get statistics */ + struct ena_get_stats_rsp get_stats; + /** Padding */ + uint8_t pad[64]; +}; + +/** Admin queue */ +struct ena_aq { + /** Requests */ + union ena_aq_req *req; + /** Producer counter */ + unsigned int prod; +}; + +/** Admin completion queue */ +struct ena_acq { + /** Responses */ + union ena_acq_rsp *rsp; + /** Consumer counter */ + unsigned int cons; + /** Phase */ + unsigned int phase; +}; + +/** Transmit submission queue entry */ +struct ena_tx_sqe { + /** Length */ + uint16_t len; + /** Reserved */ + uint8_t reserved_a; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[3]; + /** Request identifier */ + uint8_t id; + /** Address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Receive submission queue entry */ +struct ena_rx_sqe { + /** Length */ + uint16_t len; + /** Reserved */ + uint8_t reserved_a; + /** Flags */ + uint8_t flags; + /** Request identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[2]; + /** Address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Submission queue ownership phase flag */ +#define ENA_SQE_PHASE 0x01 + +/** This is the first descriptor */ +#define ENA_SQE_FIRST 0x04 + +/** This is the last descriptor */ +#define ENA_SQE_LAST 0x08 + +/** Request completion */ +#define ENA_SQE_CPL 0x10 + +/** Transmit completion queue entry */ +struct ena_tx_cqe { + /** Request identifier */ + uint16_t id; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[2]; + /** Consumer index */ + uint16_t cons; +} __attribute__ (( packed )); + +/** Receive completion queue entry */ +struct ena_rx_cqe { + /** Reserved */ + uint8_t reserved_a[3]; + /** Flags */ + uint8_t flags; + /** Length */ + uint16_t len; + /** Request identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Completion queue ownership phase flag */ +#define ENA_CQE_PHASE 0x01 + +/** Submission queue */ +struct ena_sq { + /** Entries */ + union { + /** Transmit submission queue entries */ + struct ena_tx_sqe *tx; + /** Receive submission queue entries */ + struct ena_rx_sqe *rx; + /** Raw data */ + void *raw; + } sqe; + /** Doorbell register offset */ + unsigned int doorbell; + /** Total length of entries */ + size_t len; + /** Producer counter */ + unsigned int prod; + /** Phase */ + unsigned int phase; + /** Submission queue identifier */ + uint16_t id; + /** Direction */ + uint8_t direction; + /** Number of entries */ + uint8_t count; +}; + +/** + * Initialise submission queue + * + * @v sq Submission queue + * @v direction Direction + * @v count Number of entries + * @v size Size of each entry + */ +static inline __attribute__ (( always_inline )) void +ena_sq_init ( struct ena_sq *sq, unsigned int direction, unsigned int count, + size_t size ) { + + sq->len = ( count * size ); + sq->direction = direction; + sq->count = count; +} + +/** Completion queue */ +struct ena_cq { + /** Entries */ + union { + /** Transmit completion queue entries */ + struct ena_tx_cqe *tx; + /** Receive completion queue entries */ + struct ena_rx_cqe *rx; + /** Raw data */ + void *raw; + } cqe; + /** Doorbell register offset */ + unsigned int doorbell; + /** Total length of entries */ + size_t len; + /** Consumer counter */ + unsigned int cons; + /** Phase */ + unsigned int phase; + /** Completion queue identifier */ + uint16_t id; + /** Entry size (in 32-bit words) */ + uint8_t size; + /** Requested number of entries */ + uint8_t requested; + /** Actual number of entries */ + uint8_t actual; + /** Actual number of entries minus one */ + uint8_t mask; +}; + +/** + * Initialise completion queue + * + * @v cq Completion queue + * @v count Number of entries + * @v size Size of each entry + */ +static inline __attribute__ (( always_inline )) void +ena_cq_init ( struct ena_cq *cq, unsigned int count, size_t size ) { + + cq->len = ( count * size ); + cq->size = ( size / sizeof ( uint32_t ) ); + cq->requested = count; +} + +/** Queue pair */ +struct ena_qp { + /** Submission queue */ + struct ena_sq sq; + /** Completion queue */ + struct ena_cq cq; +}; + +/** An ENA network card */ +struct ena_nic { + /** Registers */ + void *regs; + /** Admin queue */ + struct ena_aq aq; + /** Admin completion queue */ + struct ena_acq acq; + /** Transmit queue */ + struct ena_qp tx; + /** Receive queue */ + struct ena_qp rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[ENA_RX_COUNT]; +}; + +#endif /* _ENA_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 1a98599ec..e465d188b 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -202,6 +202,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_exanic ( ERRFILE_DRIVER | 0x00c60000 ) #define ERRFILE_smscusb ( ERRFILE_DRIVER | 0x00c70000 ) #define ERRFILE_lan78xx ( ERRFILE_DRIVER | 0x00c80000 ) +#define ERRFILE_ena ( ERRFILE_DRIVER | 0x00c90000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) From 329202691fa38558e234480571184321f69139eb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 14 Jan 2018 21:16:36 +0000 Subject: [PATCH 541/591] [skel] Remove MII interface Most drivers do not utilise an MII interface, since the link state is typically available directly from a memory-mapped register. Signed-off-by: Michael Brown --- src/drivers/net/skeleton.c | 58 -------------------------------------- src/drivers/net/skeleton.h | 2 -- 2 files changed, 60 deletions(-) diff --git a/src/drivers/net/skeleton.c b/src/drivers/net/skeleton.c index 0435b9d0e..0bae3089c 100644 --- a/src/drivers/net/skeleton.c +++ b/src/drivers/net/skeleton.c @@ -34,7 +34,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include "skeleton.h" /** @file @@ -43,54 +42,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/****************************************************************************** - * - * MII interface - * - ****************************************************************************** - */ - -/** - * Read from MII register - * - * @v mii MII interface - * @v reg Register address - * @ret value Data read, or negative error - */ -static int skeleton_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct skeleton_nic *skel = - container_of ( mii, struct skeleton_nic, mii ); - - DBGC ( skel, "SKELETON %p does not yet support MII read\n", skel ); - ( void ) reg; - return -ENOTSUP; -} - -/** - * Write to MII register - * - * @v mii MII interface - * @v reg Register address - * @v data Data to write - * @ret rc Return status code - */ -static int skeleton_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data) { - struct skeleton_nic *skel = - container_of ( mii, struct skeleton_nic, mii ); - - DBGC ( skel, "SKELETON %p does not yet support MII write\n", skel ); - ( void ) reg; - ( void ) data; - return -ENOTSUP; -} - -/** Skeleton MII operations */ -static struct mii_operations skeleton_mii_operations = { - .read = skeleton_mii_read, - .write = skeleton_mii_write, -}; - /****************************************************************************** * * Device reset @@ -254,14 +205,6 @@ static int skeleton_probe ( struct pci_device *pci ) { if ( ( rc = skeleton_reset ( skel ) ) != 0 ) goto err_reset; - /* Initialise and reset MII interface */ - mii_init ( &skel->mii, &skeleton_mii_operations ); - if ( ( rc = mii_reset ( &skel->mii ) ) != 0 ) { - DBGC ( skel, "SKELETON %p could not reset MII: %s\n", - skel, strerror ( rc ) ); - goto err_mii_reset; - } - /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; @@ -273,7 +216,6 @@ static int skeleton_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: - err_mii_reset: skeleton_reset ( skel ); err_reset: iounmap ( skel->regs ); diff --git a/src/drivers/net/skeleton.h b/src/drivers/net/skeleton.h index 2ab01bd56..030922808 100644 --- a/src/drivers/net/skeleton.h +++ b/src/drivers/net/skeleton.h @@ -16,8 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct skeleton_nic { /** Registers */ void *regs; - /** MII interface */ - struct mii_interface mii; }; #endif /* _SKELETON_H */ From 08db2fd55c80157d0c5329560f57b125ceb8adf8 Mon Sep 17 00:00:00 2001 From: Joseph Wong Date: Sun, 14 Jan 2018 21:26:36 +0000 Subject: [PATCH 542/591] [tg3] Add support for SerDes PHY initialization Signed-off-by: Michael Brown --- src/drivers/net/tg3/tg3.h | 56 ++ src/drivers/net/tg3/tg3_phy.c | 969 +++++++++++++++++++++++++++++++++- 2 files changed, 1021 insertions(+), 4 deletions(-) diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index 05d516344..fa809c041 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -34,6 +34,13 @@ #define ADVERTISED_Autoneg (1 << 6) /* */ +#ifndef ADVERTISED_Pause +#define ADVERTISED_Pause (1 << 13) +#endif +#ifndef ADVERTISED_Asym_Pause +#define ADVERTISED_Asym_Pause (1 << 14) +#endif + /* mdio.h: */ #define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ @@ -139,9 +146,15 @@ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 +#ifndef SPEED_UNKNOWN +#define SPEED_UNKNOWN -1 +#endif #define DUPLEX_HALF 0x00 #define DUPLEX_FULL 0x01 +#ifndef DUPLEX_UNKNOWN +#define DUPLEX_UNKNOWN 0xff +#endif #define TG3_64BIT_REG_HIGH 0x00UL #define TG3_64BIT_REG_LOW 0x04UL @@ -2425,6 +2438,14 @@ #define MII_TG3_FET_SHDW_AUXSTAT2 0x1b #define MII_TG3_FET_SHDW_AUXSTAT2_APD 0x0020 +/* Serdes PHY Register Definitions */ +#define SERDES_TG3_1000X_STATUS 0x14 +#define SERDES_TG3_SGMII_MODE 0x0001 +#define SERDES_TG3_LINK_UP 0x0002 +#define SERDES_TG3_FULL_DUPLEX 0x0004 +#define SERDES_TG3_SPEED_100 0x0008 +#define SERDES_TG3_SPEED_1000 0x0010 + /* APE registers. Accessible through BAR1 */ #define TG3_APE_EVENT 0x000c @@ -2815,6 +2836,7 @@ struct tg3_link_config { #define DUPLEX_INVALID 0xff #define AUTONEG_INVALID 0xff u16 active_speed; + u32 rmt_adv; /* When we go in and out of low power mode we need * to swap with this state. @@ -3282,6 +3304,7 @@ struct tg3 { u16 subsystem_vendor; u16 subsystem_device; + int link_up; }; #define TG3_TX_RING_SIZE 512 @@ -3410,6 +3433,39 @@ static inline u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv) return cap; } +static inline u32 mii_adv_to_ethtool_adv_x(u32 adv) +{ + u32 result = 0; + + if (adv & ADVERTISE_1000XHALF) + result |= ADVERTISED_1000baseT_Half; + if (adv & ADVERTISE_1000XFULL) + result |= ADVERTISED_1000baseT_Full; + if (adv & ADVERTISE_1000XPAUSE) + result |= ADVERTISED_Pause; + if (adv & ADVERTISE_1000XPSE_ASYM) + result |= ADVERTISED_Asym_Pause; + + return result; +} + +static inline u32 ethtool_adv_to_mii_adv_x(u32 ethadv) +{ + u32 result = 0; + + if (ethadv & ADVERTISED_1000baseT_Half) + result |= ADVERTISE_1000XHALF; + if (ethadv & ADVERTISED_1000baseT_Full) + result |= ADVERTISE_1000XFULL; + if (ethadv & ADVERTISED_Pause) + result |= ADVERTISE_1000XPAUSE; + if (ethadv & ADVERTISED_Asym_Pause) + result |= ADVERTISE_1000XPSE_ASYM; + + return result; +} + + #define ETH_FCS_LEN 4 #endif /* !(_T3_H) */ diff --git a/src/drivers/net/tg3/tg3_phy.c b/src/drivers/net/tg3/tg3_phy.c index 65dea7e66..e88b0be0f 100644 --- a/src/drivers/net/tg3/tg3_phy.c +++ b/src/drivers/net/tg3/tg3_phy.c @@ -1287,6 +1287,970 @@ static void tg3_link_report(struct tg3 *tp) } #endif +struct tg3_fiber_aneginfo { + int state; +#define ANEG_STATE_UNKNOWN 0 +#define ANEG_STATE_AN_ENABLE 1 +#define ANEG_STATE_RESTART_INIT 2 +#define ANEG_STATE_RESTART 3 +#define ANEG_STATE_DISABLE_LINK_OK 4 +#define ANEG_STATE_ABILITY_DETECT_INIT 5 +#define ANEG_STATE_ABILITY_DETECT 6 +#define ANEG_STATE_ACK_DETECT_INIT 7 +#define ANEG_STATE_ACK_DETECT 8 +#define ANEG_STATE_COMPLETE_ACK_INIT 9 +#define ANEG_STATE_COMPLETE_ACK 10 +#define ANEG_STATE_IDLE_DETECT_INIT 11 +#define ANEG_STATE_IDLE_DETECT 12 +#define ANEG_STATE_LINK_OK 13 +#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14 +#define ANEG_STATE_NEXT_PAGE_WAIT 15 + + u32 flags; +#define MR_AN_ENABLE 0x00000001 +#define MR_RESTART_AN 0x00000002 +#define MR_AN_COMPLETE 0x00000004 +#define MR_PAGE_RX 0x00000008 +#define MR_NP_LOADED 0x00000010 +#define MR_TOGGLE_TX 0x00000020 +#define MR_LP_ADV_FULL_DUPLEX 0x00000040 +#define MR_LP_ADV_HALF_DUPLEX 0x00000080 +#define MR_LP_ADV_SYM_PAUSE 0x00000100 +#define MR_LP_ADV_ASYM_PAUSE 0x00000200 +#define MR_LP_ADV_REMOTE_FAULT1 0x00000400 +#define MR_LP_ADV_REMOTE_FAULT2 0x00000800 +#define MR_LP_ADV_NEXT_PAGE 0x00001000 +#define MR_TOGGLE_RX 0x00002000 +#define MR_NP_RX 0x00004000 + +#define MR_LINK_OK 0x80000000 + + unsigned long link_time, cur_time; + + u32 ability_match_cfg; + int ability_match_count; + + char ability_match, idle_match, ack_match; + + u32 txconfig, rxconfig; +#define ANEG_CFG_NP 0x00000080 +#define ANEG_CFG_ACK 0x00000040 +#define ANEG_CFG_RF2 0x00000020 +#define ANEG_CFG_RF1 0x00000010 +#define ANEG_CFG_PS2 0x00000001 +#define ANEG_CFG_PS1 0x00008000 +#define ANEG_CFG_HD 0x00004000 +#define ANEG_CFG_FD 0x00002000 +#define ANEG_CFG_INVAL 0x00001f06 + +}; +#define ANEG_OK 0 +#define ANEG_DONE 1 +#define ANEG_TIMER_ENAB 2 +#define ANEG_FAILED -1 + +#define ANEG_STATE_SETTLE_TIME 10000 + +static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl) +{ + u16 miireg; + + if ((flow_ctrl & FLOW_CTRL_TX) && (flow_ctrl & FLOW_CTRL_RX)) + miireg = ADVERTISE_1000XPAUSE; + else if (flow_ctrl & FLOW_CTRL_TX) + miireg = ADVERTISE_1000XPSE_ASYM; + else if (flow_ctrl & FLOW_CTRL_RX) + miireg = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; + else + miireg = 0; + + return miireg; +} + +static void tg3_init_bcm8002(struct tg3 *tp) +{ + u32 mac_status = tr32(MAC_STATUS); + int i; + + /* Reset when initting first time or we have a link. */ + if (tg3_flag(tp, INIT_COMPLETE) && + !(mac_status & MAC_STATUS_PCS_SYNCED)) + return; + + /* Set PLL lock range. */ + tg3_writephy(tp, 0x16, 0x8007); + + /* SW reset */ + tg3_writephy(tp, MII_BMCR, BMCR_RESET); + + /* Wait for reset to complete. */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 500; i++) + udelay(10); + + /* Config mode; select PMA/Ch 1 regs. */ + tg3_writephy(tp, 0x10, 0x8411); + + /* Enable auto-lock and comdet, select txclk for tx. */ + tg3_writephy(tp, 0x11, 0x0a10); + + tg3_writephy(tp, 0x18, 0x00a0); + tg3_writephy(tp, 0x16, 0x41ff); + + /* Assert and deassert POR. */ + tg3_writephy(tp, 0x13, 0x0400); + udelay(40); + tg3_writephy(tp, 0x13, 0x0000); + + tg3_writephy(tp, 0x11, 0x0a50); + udelay(40); + tg3_writephy(tp, 0x11, 0x0a10); + + /* Wait for signal to stabilize */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 15000; i++) + udelay(10); + + /* Deselect the channel register so we can read the PHYID + * later. + */ + tg3_writephy(tp, 0x10, 0x8011); +} + +static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) +{ + u16 flowctrl; + int current_link_up; + u32 sg_dig_ctrl, sg_dig_status; + u32 serdes_cfg, expected_sg_dig_ctrl; + int workaround, port_a; + + serdes_cfg = 0; + expected_sg_dig_ctrl = 0; + workaround = 0; + port_a = 1; + current_link_up = 0; + + if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && + tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { + workaround = 1; + if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) + port_a = 0; + + /* preserve bits 0-11,13,14 for signal pre-emphasis */ + /* preserve bits 20-23 for voltage regulator */ + serdes_cfg = tr32(MAC_SERDES_CFG) & 0x00f06fff; + } + + sg_dig_ctrl = tr32(SG_DIG_CTRL); + + if (tp->link_config.autoneg != AUTONEG_ENABLE) { + if (sg_dig_ctrl & SG_DIG_USING_HW_AUTONEG) { + if (workaround) { + u32 val = serdes_cfg; + + if (port_a) + val |= 0xc010000; + else + val |= 0x4010000; + tw32_f(MAC_SERDES_CFG, val); + } + + tw32_f(SG_DIG_CTRL, SG_DIG_COMMON_SETUP); + } + if (mac_status & MAC_STATUS_PCS_SYNCED) { + tg3_setup_flow_control(tp, 0, 0); + current_link_up = 1; + } + goto out; + } + + /* Want auto-negotiation. */ + expected_sg_dig_ctrl = SG_DIG_USING_HW_AUTONEG | SG_DIG_COMMON_SETUP; + + flowctrl = tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); + if (flowctrl & ADVERTISE_1000XPAUSE) + expected_sg_dig_ctrl |= SG_DIG_PAUSE_CAP; + if (flowctrl & ADVERTISE_1000XPSE_ASYM) + expected_sg_dig_ctrl |= SG_DIG_ASYM_PAUSE; + + if (sg_dig_ctrl != expected_sg_dig_ctrl) { + if ((tp->phy_flags & TG3_PHYFLG_PARALLEL_DETECT) && + tp->serdes_counter && + ((mac_status & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_RCVD_CFG)) == + MAC_STATUS_PCS_SYNCED)) { + tp->serdes_counter--; + current_link_up = 1; + goto out; + } +restart_autoneg: + if (workaround) + tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000); + tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | SG_DIG_SOFT_RESET); + udelay(5); + tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); + + tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } else if (mac_status & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET)) { + sg_dig_status = tr32(SG_DIG_STATUS); + mac_status = tr32(MAC_STATUS); + + if ((sg_dig_status & SG_DIG_AUTONEG_COMPLETE) && + (mac_status & MAC_STATUS_PCS_SYNCED)) { + u32 local_adv = 0, remote_adv = 0; + + if (sg_dig_ctrl & SG_DIG_PAUSE_CAP) + local_adv |= ADVERTISE_1000XPAUSE; + if (sg_dig_ctrl & SG_DIG_ASYM_PAUSE) + local_adv |= ADVERTISE_1000XPSE_ASYM; + + if (sg_dig_status & SG_DIG_PARTNER_PAUSE_CAPABLE) + remote_adv |= LPA_1000XPAUSE; + if (sg_dig_status & SG_DIG_PARTNER_ASYM_PAUSE) + remote_adv |= LPA_1000XPAUSE_ASYM; + + tp->link_config.rmt_adv = + mii_adv_to_ethtool_adv_x(remote_adv); + + tg3_setup_flow_control(tp, local_adv, remote_adv); + current_link_up = 1; + tp->serdes_counter = 0; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } else if (!(sg_dig_status & SG_DIG_AUTONEG_COMPLETE)) { + if (tp->serdes_counter) + tp->serdes_counter--; + else { + if (workaround) { + u32 val = serdes_cfg; + + if (port_a) + val |= 0xc010000; + else + val |= 0x4010000; + + tw32_f(MAC_SERDES_CFG, val); + } + + tw32_f(SG_DIG_CTRL, SG_DIG_COMMON_SETUP); + udelay(40); + + /* Link parallel detection - link is up */ + /* only if we have PCS_SYNC and not */ + /* receiving config code words */ + mac_status = tr32(MAC_STATUS); + if ((mac_status & MAC_STATUS_PCS_SYNCED) && + !(mac_status & MAC_STATUS_RCVD_CFG)) { + tg3_setup_flow_control(tp, 0, 0); + current_link_up = 1; + tp->phy_flags |= + TG3_PHYFLG_PARALLEL_DETECT; + tp->serdes_counter = + SERDES_PARALLEL_DET_TIMEOUT; + } else + goto restart_autoneg; + } + } + } else { + tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } + +out: + return current_link_up; +} + +static int tg3_fiber_aneg_smachine(struct tg3 *tp, + struct tg3_fiber_aneginfo *ap) +{ + u16 flowctrl; + unsigned long delta; + u32 rx_cfg_reg; + int ret; + + if (ap->state == ANEG_STATE_UNKNOWN) { + ap->rxconfig = 0; + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + } + ap->cur_time++; + + if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { + rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); + + if (rx_cfg_reg != ap->ability_match_cfg) { + ap->ability_match_cfg = rx_cfg_reg; + ap->ability_match = 0; + ap->ability_match_count = 0; + } else { + if (++ap->ability_match_count > 1) { + ap->ability_match = 1; + ap->ability_match_cfg = rx_cfg_reg; + } + } + if (rx_cfg_reg & ANEG_CFG_ACK) + ap->ack_match = 1; + else + ap->ack_match = 0; + + ap->idle_match = 0; + } else { + ap->idle_match = 1; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->ack_match = 0; + + rx_cfg_reg = 0; + } + + ap->rxconfig = rx_cfg_reg; + ret = ANEG_OK; + + switch (ap->state) { + case ANEG_STATE_UNKNOWN: + if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) + ap->state = ANEG_STATE_AN_ENABLE; + + /* fallthru */ + case ANEG_STATE_AN_ENABLE: + ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); + if (ap->flags & MR_AN_ENABLE) { + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + + ap->state = ANEG_STATE_RESTART_INIT; + } else { + ap->state = ANEG_STATE_DISABLE_LINK_OK; + } + break; + + case ANEG_STATE_RESTART_INIT: + ap->link_time = ap->cur_time; + ap->flags &= ~(MR_NP_LOADED); + ap->txconfig = 0; + tw32(MAC_TX_AUTO_NEG, 0); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ret = ANEG_TIMER_ENAB; + ap->state = ANEG_STATE_RESTART; + + /* fallthru */ + case ANEG_STATE_RESTART: + delta = ap->cur_time - ap->link_time; + if (delta > ANEG_STATE_SETTLE_TIME) + ap->state = ANEG_STATE_ABILITY_DETECT_INIT; + else + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_DISABLE_LINK_OK: + ret = ANEG_DONE; + break; + + case ANEG_STATE_ABILITY_DETECT_INIT: + ap->flags &= ~(MR_TOGGLE_TX); + ap->txconfig = ANEG_CFG_FD; + flowctrl = tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); + if (flowctrl & ADVERTISE_1000XPAUSE) + ap->txconfig |= ANEG_CFG_PS1; + if (flowctrl & ADVERTISE_1000XPSE_ASYM) + ap->txconfig |= ANEG_CFG_PS2; + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ap->state = ANEG_STATE_ABILITY_DETECT; + break; + + case ANEG_STATE_ABILITY_DETECT: + if (ap->ability_match != 0 && ap->rxconfig != 0) + ap->state = ANEG_STATE_ACK_DETECT_INIT; + break; + + case ANEG_STATE_ACK_DETECT_INIT: + ap->txconfig |= ANEG_CFG_ACK; + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ap->state = ANEG_STATE_ACK_DETECT; + + /* fallthru */ + case ANEG_STATE_ACK_DETECT: + if (ap->ack_match != 0) { + if ((ap->rxconfig & ~ANEG_CFG_ACK) == + (ap->ability_match_cfg & ~ANEG_CFG_ACK)) { + ap->state = ANEG_STATE_COMPLETE_ACK_INIT; + } else { + ap->state = ANEG_STATE_AN_ENABLE; + } + } else if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + } + break; + + case ANEG_STATE_COMPLETE_ACK_INIT: + if (ap->rxconfig & ANEG_CFG_INVAL) { + ret = ANEG_FAILED; + break; + } + ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | + MR_LP_ADV_HALF_DUPLEX | + MR_LP_ADV_SYM_PAUSE | + MR_LP_ADV_ASYM_PAUSE | + MR_LP_ADV_REMOTE_FAULT1 | + MR_LP_ADV_REMOTE_FAULT2 | + MR_LP_ADV_NEXT_PAGE | + MR_TOGGLE_RX | + MR_NP_RX); + if (ap->rxconfig & ANEG_CFG_FD) + ap->flags |= MR_LP_ADV_FULL_DUPLEX; + if (ap->rxconfig & ANEG_CFG_HD) + ap->flags |= MR_LP_ADV_HALF_DUPLEX; + if (ap->rxconfig & ANEG_CFG_PS1) + ap->flags |= MR_LP_ADV_SYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_PS2) + ap->flags |= MR_LP_ADV_ASYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_RF1) + ap->flags |= MR_LP_ADV_REMOTE_FAULT1; + if (ap->rxconfig & ANEG_CFG_RF2) + ap->flags |= MR_LP_ADV_REMOTE_FAULT2; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_LP_ADV_NEXT_PAGE; + + ap->link_time = ap->cur_time; + + ap->flags ^= (MR_TOGGLE_TX); + if (ap->rxconfig & 0x0008) + ap->flags |= MR_TOGGLE_RX; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_NP_RX; + ap->flags |= MR_PAGE_RX; + + ap->state = ANEG_STATE_COMPLETE_ACK; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_COMPLETE_ACK: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > ANEG_STATE_SETTLE_TIME) { + if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + } else { + if ((ap->txconfig & ANEG_CFG_NP) == 0 && + !(ap->flags & MR_NP_RX)) { + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + } else { + ret = ANEG_FAILED; + } + } + } + break; + + case ANEG_STATE_IDLE_DETECT_INIT: + ap->link_time = ap->cur_time; + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ap->state = ANEG_STATE_IDLE_DETECT; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_IDLE_DETECT: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > ANEG_STATE_SETTLE_TIME) { + /* XXX another gem from the Broadcom driver :( */ + ap->state = ANEG_STATE_LINK_OK; + } + break; + + case ANEG_STATE_LINK_OK: + ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); + ret = ANEG_DONE; + break; + + case ANEG_STATE_NEXT_PAGE_WAIT_INIT: + /* ??? unimplemented */ + break; + + case ANEG_STATE_NEXT_PAGE_WAIT: + /* ??? unimplemented */ + break; + + default: + ret = ANEG_FAILED; + break; + } + + return ret; +} + +static int fiber_autoneg(struct tg3 *tp, u32 *txflags, u32 *rxflags) +{ + int res = 0; + struct tg3_fiber_aneginfo aninfo; + int status = ANEG_FAILED; + unsigned int tick; + u32 tmp; + + tw32_f(MAC_TX_AUTO_NEG, 0); + + tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; + tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); + udelay(40); + + tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); + udelay(40); + + memset(&aninfo, 0, sizeof(aninfo)); + aninfo.flags |= MR_AN_ENABLE; + aninfo.state = ANEG_STATE_UNKNOWN; + aninfo.cur_time = 0; + tick = 0; + while (++tick < 195000) { + status = tg3_fiber_aneg_smachine(tp, &aninfo); + if (status == ANEG_DONE || status == ANEG_FAILED) + break; + + udelay(1); + } + + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + *txflags = aninfo.txconfig; + *rxflags = aninfo.flags; + + if (status == ANEG_DONE && + (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | + MR_LP_ADV_FULL_DUPLEX))) + res = 1; + + return res; +} + +static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status) +{ + int current_link_up = 0; + + if (!(mac_status & MAC_STATUS_PCS_SYNCED)) + goto out; + + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + u32 txflags, rxflags; + int i; + + if (fiber_autoneg(tp, &txflags, &rxflags)) { + u32 local_adv = 0, remote_adv = 0; + + if (txflags & ANEG_CFG_PS1) + local_adv |= ADVERTISE_1000XPAUSE; + if (txflags & ANEG_CFG_PS2) + local_adv |= ADVERTISE_1000XPSE_ASYM; + + if (rxflags & MR_LP_ADV_SYM_PAUSE) + remote_adv |= LPA_1000XPAUSE; + if (rxflags & MR_LP_ADV_ASYM_PAUSE) + remote_adv |= LPA_1000XPAUSE_ASYM; + + tp->link_config.rmt_adv = + mii_adv_to_ethtool_adv_x(remote_adv); + + tg3_setup_flow_control(tp, local_adv, remote_adv); + + current_link_up = 1; + } + for (i = 0; i < 30; i++) { + udelay(20); + tw32_f(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(40); + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + + mac_status = tr32(MAC_STATUS); + if (!current_link_up && + (mac_status & MAC_STATUS_PCS_SYNCED) && + !(mac_status & MAC_STATUS_RCVD_CFG)) + current_link_up = 1; + } else { + tg3_setup_flow_control(tp, 0, 0); + + /* Forcing 1000FD link up. */ + current_link_up = 1; + + tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); + udelay(40); + + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + } + +out: + return current_link_up; +} + +static int tg3_test_and_report_link_chg(struct tg3 *tp, int curr_link_up) +{ + if (curr_link_up != tp->link_up) { + if (curr_link_up) { + netdev_link_up(tp->dev); + } else { + netdev_link_down(tp->dev); + if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } + + tg3_link_report(tp); + return 1; + } + + return 0; +} + +static void tg3_clear_mac_status(struct tg3 *tp) +{ + tw32(MAC_EVENT, 0); + + tw32_f(MAC_STATUS, + MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_MI_COMPLETION | + MAC_STATUS_LNKSTATE_CHANGED); + udelay(40); +} + +static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) +{ + u32 orig_pause_cfg; + u16 orig_active_speed; + u8 orig_active_duplex; + u32 mac_status; + int current_link_up = force_reset; + int i; + + orig_pause_cfg = tp->link_config.active_flowctrl; + orig_active_speed = tp->link_config.active_speed; + orig_active_duplex = tp->link_config.active_duplex; + + if (!tg3_flag(tp, HW_AUTONEG) && + tp->link_up && + tg3_flag(tp, INIT_COMPLETE)) { + mac_status = tr32(MAC_STATUS); + mac_status &= (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_RCVD_CFG); + if (mac_status == (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET)) { + tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + return 0; + } + } + + tw32_f(MAC_TX_AUTO_NEG, 0); + + tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); + tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + if (tp->phy_id == TG3_PHY_ID_BCM8002) + tg3_init_bcm8002(tp); + + /* Enable link change event even when serdes polling. */ + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + udelay(40); + + current_link_up = 0; + tp->link_config.rmt_adv = 0; + mac_status = tr32(MAC_STATUS); + + if (tg3_flag(tp, HW_AUTONEG)) + current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); + else + current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); + + tp->hw_status->status = + (SD_STATUS_UPDATED | + (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); + + for (i = 0; i < 100; i++) { + tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(5); + if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_LNKSTATE_CHANGED)) == 0) + break; + } + + mac_status = tr32(MAC_STATUS); + if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { + current_link_up = 0; + if (tp->link_config.autoneg == AUTONEG_ENABLE && + tp->serdes_counter == 0) { + tw32_f(MAC_MODE, (tp->mac_mode | + MAC_MODE_SEND_CONFIGS)); + udelay(1); + tw32_f(MAC_MODE, tp->mac_mode); + } + } + + if (current_link_up) { + tp->link_config.active_speed = SPEED_1000; + tp->link_config.active_duplex = DUPLEX_FULL; + tw32(MAC_LED_CTRL, (tp->led_ctrl | + LED_CTRL_LNKLED_OVERRIDE | + LED_CTRL_1000MBPS_ON)); + } else { + tp->link_config.active_speed = SPEED_UNKNOWN; + tp->link_config.active_duplex = DUPLEX_UNKNOWN; + tw32(MAC_LED_CTRL, (tp->led_ctrl | + LED_CTRL_LNKLED_OVERRIDE | + LED_CTRL_TRAFFIC_OVERRIDE)); + } + + if (!tg3_test_and_report_link_chg(tp, current_link_up)) { + u32 now_pause_cfg = tp->link_config.active_flowctrl; + if (orig_pause_cfg != now_pause_cfg || + orig_active_speed != tp->link_config.active_speed || + orig_active_duplex != tp->link_config.active_duplex) + tg3_link_report(tp); + } + + return 0; +} + +static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) +{ + int err = 0; + u32 bmsr, bmcr; + u16 current_speed = SPEED_UNKNOWN; + u8 current_duplex = DUPLEX_UNKNOWN; + int current_link_up = 0; + u32 local_adv, remote_adv, sgsr; + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) && + !tg3_readphy(tp, SERDES_TG3_1000X_STATUS, &sgsr) && + (sgsr & SERDES_TG3_SGMII_MODE)) { + + if (force_reset) + tg3_phy_reset(tp); + + tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; + + if (!(sgsr & SERDES_TG3_LINK_UP)) { + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + } else { + current_link_up = 1; + if (sgsr & SERDES_TG3_SPEED_1000) { + current_speed = SPEED_1000; + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + } else if (sgsr & SERDES_TG3_SPEED_100) { + current_speed = SPEED_100; + tp->mac_mode |= MAC_MODE_PORT_MODE_MII; + } else { + current_speed = SPEED_10; + tp->mac_mode |= MAC_MODE_PORT_MODE_MII; + } + + if (sgsr & SERDES_TG3_FULL_DUPLEX) + current_duplex = DUPLEX_FULL; + else + current_duplex = DUPLEX_HALF; + } + + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + tg3_clear_mac_status(tp); + + goto fiber_setup_done; + } + + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + tg3_clear_mac_status(tp); + + if (force_reset) + tg3_phy_reset(tp); + + tp->link_config.rmt_adv = 0; + + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { + if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) + bmsr |= BMSR_LSTATUS; + else + bmsr &= ~BMSR_LSTATUS; + } + + err |= tg3_readphy(tp, MII_BMCR, &bmcr); + + if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && + (tp->phy_flags & TG3_PHYFLG_PARALLEL_DETECT)) { + /* do nothing, just check for link up at the end */ + } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { + u32 adv, newadv; + + err |= tg3_readphy(tp, MII_ADVERTISE, &adv); + newadv = adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | + ADVERTISE_1000XPAUSE | + ADVERTISE_1000XPSE_ASYM | + ADVERTISE_SLCT); + + newadv |= tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); + newadv |= ethtool_adv_to_mii_adv_x(tp->link_config.advertising); + + if ((newadv != adv) || !(bmcr & BMCR_ANENABLE)) { + tg3_writephy(tp, MII_ADVERTISE, newadv); + bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; + tg3_writephy(tp, MII_BMCR, bmcr); + + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + tp->serdes_counter = SERDES_AN_TIMEOUT_5714S; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + + return err; + } + } else { + u32 new_bmcr; + + bmcr &= ~BMCR_SPEED1000; + new_bmcr = bmcr & ~(BMCR_ANENABLE | BMCR_FULLDPLX); + + if (tp->link_config.duplex == DUPLEX_FULL) + new_bmcr |= BMCR_FULLDPLX; + + if (new_bmcr != bmcr) { + /* BMCR_SPEED1000 is a reserved bit that needs + * to be set on write. + */ + new_bmcr |= BMCR_SPEED1000; + + /* Force a linkdown */ + if (tp->link_up) { + u32 adv; + + err |= tg3_readphy(tp, MII_ADVERTISE, &adv); + adv &= ~(ADVERTISE_1000XFULL | + ADVERTISE_1000XHALF | + ADVERTISE_SLCT); + tg3_writephy(tp, MII_ADVERTISE, adv); + tg3_writephy(tp, MII_BMCR, bmcr | + BMCR_ANRESTART | + BMCR_ANENABLE); + udelay(10); + netdev_link_down(tp->dev); + } + tg3_writephy(tp, MII_BMCR, new_bmcr); + bmcr = new_bmcr; + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { + if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) + bmsr |= BMSR_LSTATUS; + else + bmsr &= ~BMSR_LSTATUS; + } + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } + } + + if (bmsr & BMSR_LSTATUS) { + current_speed = SPEED_1000; + current_link_up = 1; + if (bmcr & BMCR_FULLDPLX) + current_duplex = DUPLEX_FULL; + else + current_duplex = DUPLEX_HALF; + + local_adv = 0; + remote_adv = 0; + + if (bmcr & BMCR_ANENABLE) { + u32 common; + + err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); + err |= tg3_readphy(tp, MII_LPA, &remote_adv); + common = local_adv & remote_adv; + if (common & (ADVERTISE_1000XHALF | + ADVERTISE_1000XFULL)) { + if (common & ADVERTISE_1000XFULL) + current_duplex = DUPLEX_FULL; + else + current_duplex = DUPLEX_HALF; + + tp->link_config.rmt_adv = + mii_adv_to_ethtool_adv_x(remote_adv); + } else if (!tg3_flag(tp, 5780_CLASS)) { + /* Link is up via parallel detect */ + } else { + current_link_up = 0; + } + } + } + +fiber_setup_done: + if (current_link_up && current_duplex == DUPLEX_FULL) + tg3_setup_flow_control(tp, local_adv, remote_adv); + + tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; + if (tp->link_config.active_duplex == DUPLEX_HALF) + tp->mac_mode |= MAC_MODE_HALF_DUPLEX; + + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + + tp->link_config.active_speed = current_speed; + tp->link_config.active_duplex = current_duplex; + + tg3_test_and_report_link_chg(tp, current_link_up); + return err; +} + static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) { DBGP("%s\n", __func__); @@ -1559,15 +2523,12 @@ int tg3_setup_phy(struct tg3 *tp, int force_reset) u32 val; int err; -#if 0 if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) err = tg3_setup_fiber_phy(tp, force_reset); else if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) err = tg3_setup_fiber_mii_phy(tp, force_reset); else -#endif - /* FIXME: add only copper phy variants for now */ - err = tg3_setup_copper_phy(tp, force_reset); + err = tg3_setup_copper_phy(tp, force_reset); val = (2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT); From 70189a8e4702016f654310e68d9ec4ec425e7344 Mon Sep 17 00:00:00 2001 From: Martin Habets Date: Wed, 10 Jan 2018 15:32:34 +0000 Subject: [PATCH 543/591] [netdevice] Make netdev_irq_enabled() independent of netdev_irq_supported() The UNDI layer uses the NETDEV_IRQ_ENABLED flag to choose whether to return PXENV_UNDI_ISR_OUT_OURS or PXENV_UNDI_ISR_OUT_NOT_OURS for a given interrupt. For a network device that does not support interrupts, the flag will never be set and so pxenv_undi_isr() will always return PXENV_UNDI_ISR_OUT_NOT_OURS. This causes some NBPs (such as lpxelinux.0) to hang. Redefine NETDEV_IRQ_ENABLED as a simple administrative flag which can be set even on network devices that do not support interrupts. This allows pxenv_undi_isr() (which is the sole user of NETDEV_IRQ_ENABLED) to function as expected by lpxelinux.0. Signed-off-by: Martin Habets Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/net/netdevice.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 4c211d707..71a37eccc 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -874,12 +874,9 @@ void unregister_netdev ( struct net_device *netdev ) { */ void netdev_irq ( struct net_device *netdev, int enable ) { - /* Do nothing if device does not support interrupts */ - if ( ! netdev_irq_supported ( netdev ) ) - return; - - /* Enable or disable device interrupts */ - netdev->op->irq ( netdev, enable ); + /* Enable or disable device interrupts, if applicable */ + if ( netdev_irq_supported ( netdev ) ) + netdev->op->irq ( netdev, enable ); /* Record interrupt enabled state */ netdev->state &= ~NETDEV_IRQ_ENABLED; From fbe8c52d0d9cdb3d6f5fe8be8edab54618becc1f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 17 Jan 2018 14:09:56 +0000 Subject: [PATCH 544/591] [ena] Fix spurious uninitialised variable warning on older versions of gcc Some older versions of gcc (observed with gcc 4.7.2) report a spurious uninitialised variable warning in ena_get_device_attributes(). Work around this warning by manually inlining the relevant code (which has only a single call site). Reported-by: xbgmsharp Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 46 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index b6d8bc6f1..8d29979bb 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -539,35 +539,6 @@ static int ena_destroy_qp ( struct ena_nic *ena, struct ena_qp *qp ) { return 0; } -/** - * Get feature - * - * @v ena ENA device - * @v id Feature identifier - * @v feature Feature to fill in - * @ret rc Return status code - */ -static int ena_get_feature ( struct ena_nic *ena, unsigned int id, - union ena_feature **feature ) { - union ena_aq_req *req; - union ena_acq_rsp *rsp; - int rc; - - /* Construct request */ - req = ena_admin_req ( ena ); - req->header.opcode = ENA_GET_FEATURE; - req->get_feature.id = id; - - /* Issue request */ - if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) - return rc; - - /* Parse response */ - *feature = &rsp->get_feature.feature; - - return 0; -} - /** * Get device attributes * @@ -576,18 +547,23 @@ static int ena_get_feature ( struct ena_nic *ena, unsigned int id, */ static int ena_get_device_attributes ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; + union ena_aq_req *req; + union ena_acq_rsp *rsp; union ena_feature *feature; int rc; - /* Get device attributes */ - if ( ( rc = ena_get_feature ( ena, ENA_DEVICE_ATTRIBUTES, - &feature ) ) != 0 ) + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_GET_FEATURE; + req->get_feature.id = ENA_DEVICE_ATTRIBUTES; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) return rc; - /* Extract MAC address */ + /* Parse response */ + feature = &rsp->get_feature.feature; memcpy ( netdev->hw_addr, feature->device.mac, ETH_ALEN ); - - /* Extract MTU */ netdev->max_pkt_len = le32_to_cpu ( feature->device.mtu ); DBGC ( ena, "ENA %p MAC %s MTU %zd\n", From c900751fa65c35f0975e103fce3c17d8c40aa270 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 29 Jan 2018 21:25:11 +0000 Subject: [PATCH 545/591] [xhci] Assume an invalid PSI table if any invalid PSI value is observed Invalid protocol speed ID tables appear to be increasingly common in the wild, to the point that it is infeasible to apply an explicit XHCI_BAD_PSIV flag for each offending PCI device ID. Fix by assuming an invalid PSI table as soon as any invalid value is reported by the hardware. Signed-off-by: Michael Brown --- src/drivers/usb/xhci.c | 53 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 825171a52..8bf3ca776 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -801,34 +801,41 @@ static int xhci_port_speed ( struct xhci_device *xhci, unsigned int port, ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); - /* Use the default mappings if applicable */ - if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { - switch ( psiv ) { - case XHCI_SPEED_LOW : return USB_SPEED_LOW; - case XHCI_SPEED_FULL : return USB_SPEED_FULL; - case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; - case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; - default: - DBGC ( xhci, "XHCI %s-%d non-standard PSI value %d\n", + /* Use protocol speed ID table unless device is known to be faulty */ + if ( ! ( xhci->quirks & XHCI_BAD_PSIV ) ) { + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + + XHCI_SUPPORTED_PSI ( i ) ); + if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + speed = USB_SPEED ( mantissa, exponent ); + return speed; + } + } + + /* Record device as faulty if no match is found */ + if ( psic != 0 ) { + DBGC ( xhci, "XHCI %s-%d spurious PSI value %d: " + "assuming PSI table is invalid\n", xhci->name, port, psiv ); - return -ENOTSUP; + xhci->quirks |= XHCI_BAD_PSIV; } } - /* Iterate over PSI dwords looking for a match */ - for ( i = 0 ; i < psic ; i++ ) { - psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); - if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { - mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); - exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); - speed = USB_SPEED ( mantissa, exponent ); - return speed; - } + /* Use the default mappings */ + switch ( psiv ) { + case XHCI_SPEED_LOW : return USB_SPEED_LOW; + case XHCI_SPEED_FULL : return USB_SPEED_FULL; + case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; + case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; + default: + DBGC ( xhci, "XHCI %s-%d unrecognised PSI value %d\n", + xhci->name, port, psiv ); + return -ENOTSUP; } - - DBGC ( xhci, "XHCI %s-%d spurious PSI value %d\n", - xhci->name, port, psiv ); - return -ENOENT; } /** From 546dd51de8459d4d09958891f426fa2c73ff090d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 3 Feb 2018 19:21:54 +0000 Subject: [PATCH 546/591] [intel] Work around broken reset mechanism in i219 devices The i219 appears to have a seriously broken reset mechanism. After any transmit or receive activity, resetting the card will break both the transmit and receive datapaths until the next PCI bus reset. The Linux and BSD drivers include a convoluted workaround authored by Intel which involves setting a bit in the undocumented FEXTNVM11 register, then transmitting a dummy 512-byte packet containing garbage data, then reconfiguring the receive descriptor prefetch thresholds and temporarily reenabling the receive datapath. The comments in the Intel fix do not even remotely match what the code actually does, and the code accidentally leaves the transmitter enabled after use. Experimentation suggests that an equivalent fix is to simply set the undocumented bit in FEXTNVM11 before enabling the transmit or receive descriptor rings. Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 31 ++++++++++++++++++++++--------- src/drivers/net/intel.h | 9 +++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index dc511b8a4..4cb8a4238 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -635,10 +635,23 @@ void intel_empty_rx ( struct intel_nic *intel ) { static int intel_open ( struct net_device *netdev ) { struct intel_nic *intel = netdev->priv; union intel_receive_address mac; + uint32_t fextnvm11; uint32_t tctl; uint32_t rctl; int rc; + /* Set undocumented bit in FEXTNVM11 to work around an errata + * in i219 devices that will otherwise cause a complete + * datapath hang at the next device reset. + */ + if ( intel->flags & INTEL_RST_HANG ) { + DBGC ( intel, "INTEL %p WARNING: applying reset hang " + "workaround\n", intel ); + fextnvm11 = readl ( intel->regs + INTEL_FEXTNVM11 ); + fextnvm11 |= INTEL_FEXTNVM11_WTF; + writel ( fextnvm11, intel->regs + INTEL_FEXTNVM11 ); + } + /* Create transmit descriptor ring */ if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 ) goto err_create_tx; @@ -1123,20 +1136,20 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), - PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", 0 ), - PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ), PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", 0 ), - PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_NO_PHY_RST ), - PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index 14877687a..9d740efc3 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -195,6 +195,10 @@ struct intel_descriptor { #define INTEL_RAH0 0x05404UL #define INTEL_RAH0_AV 0x80000000UL /**< Address valid */ +/** Future Extended NVM register 11 */ +#define INTEL_FEXTNVM11 0x05bbcUL +#define INTEL_FEXTNVM11_WTF 0x00002000UL /**< Don't ask */ + /** Receive address */ union intel_receive_address { struct { @@ -308,8 +312,13 @@ enum intel_flags { INTEL_NO_PHY_RST = 0x0004, /** ASDE is broken */ INTEL_NO_ASDE = 0x0008, + /** Reset may cause a complete device hang */ + INTEL_RST_HANG = 0x0010, }; +/** The i219 has a seriously broken reset mechanism */ +#define INTEL_I219 ( INTEL_NO_PHY_RST | INTEL_RST_HANG ) + /** * Dump diagnostic information * From 6737a8795f20c21bb48d410c2d9266f8c9c11bbc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 19 Feb 2018 11:58:28 +0000 Subject: [PATCH 547/591] [http] Allow for domain names within NTLM user names Allow a NetBIOS domain name to be specified within a URL using a syntax such as: http://domain%5Cusername:password@server/path Signed-off-by: Michael Brown --- src/core/netbios.c | 60 ++++++++++++++++++++++++++++++++++++++ src/include/ipxe/netbios.h | 30 +++++++++++++++++++ src/net/tcp/httpntlm.c | 25 ++++++++++++++-- 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 src/core/netbios.c create mode 100644 src/include/ipxe/netbios.h diff --git a/src/core/netbios.c b/src/core/netbios.c new file mode 100644 index 000000000..0d4e2086f --- /dev/null +++ b/src/core/netbios.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 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 + * + * NetBIOS user names + * + */ + +#include +#include +#include + +/** + * Split NetBIOS [domain\]username into separate domain and username fields + * + * @v username NetBIOS [domain\]username string + * @ret domain Domain portion of string, or NULL if no domain present + * + * This function modifies the original string by removing the + * separator. The caller may restore the string using + * netbios_domain_undo(). + */ +const char * netbios_domain ( char **username ) { + char *domain_username = *username; + char *sep; + + /* Find separator, if present */ + sep = strchr ( domain_username, '\\' ); + if ( ! sep ) + return NULL; + + /* Overwrite separator with NUL terminator and update username string */ + *sep = '\0'; + *username = ( sep + 1 ); + + return domain_username; +} diff --git a/src/include/ipxe/netbios.h b/src/include/ipxe/netbios.h new file mode 100644 index 000000000..c11552556 --- /dev/null +++ b/src/include/ipxe/netbios.h @@ -0,0 +1,30 @@ +#ifndef _IPXE_NETBIOS_H +#define _IPXE_NETBIOS_H + +/** @file + * + * NetBIOS user names + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern const char * netbios_domain ( char **username ); + +/** + * Restore NetBIOS [domain\]username + * + * @v domain NetBIOS domain name + * @v username NetBIOS user name + * + * Restore the separator in a NetBIOS [domain\]username as split by + * netbios_domain(). + */ +static inline void netbios_domain_undo ( const char *domain, char *username ) { + + /* Restore separator, if applicable */ + if ( domain ) + username[-1] = '\\'; +} + +#endif /* _IPXE_NETBIOS_H */ diff --git a/src/net/tcp/httpntlm.c b/src/net/tcp/httpntlm.c index 00238e96c..25187bd19 100644 --- a/src/net/tcp/httpntlm.c +++ b/src/net/tcp/httpntlm.c @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include struct http_authentication http_ntlm_auth __http_authentication; @@ -113,6 +114,8 @@ static int http_ntlm_authenticate ( struct http_transaction *http ) { struct http_request_auth_ntlm *req = &http->request.auth.ntlm; struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; struct ntlm_key key; + const char *domain; + char *username; const char *password; /* If we have no challenge yet, then just send a Negotiate message */ @@ -130,16 +133,23 @@ static int http_ntlm_authenticate ( struct http_transaction *http ) { req->username = http->uri->user; password = ( http->uri->password ? http->uri->password : "" ); + /* Split NetBIOS [domain\]username */ + username = ( ( char * ) req->username ); + domain = netbios_domain ( &username ); + /* Generate key */ - ntlm_key ( NULL, req->username, password, &key ); + ntlm_key ( domain, username, password, &key ); /* Generate responses */ ntlm_response ( &rsp->info, &key, NULL, &req->lm, &req->nt ); /* Calculate Authenticate message length */ - req->len = ntlm_authenticate_len ( &rsp->info, NULL, req->username, + req->len = ntlm_authenticate_len ( &rsp->info, domain, username, http_ntlm_workstation ); + /* Restore NetBIOS [domain\]username */ + netbios_domain_undo ( domain, username ); + return 0; } @@ -156,6 +166,8 @@ static int http_format_ntlm_auth ( struct http_transaction *http, struct http_request_auth_ntlm *req = &http->request.auth.ntlm; struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; struct ntlm_authenticate *auth; + const char *domain; + char *username; size_t check; /* If we have no challenge yet, then just send a Negotiate message */ @@ -173,12 +185,19 @@ static int http_format_ntlm_auth ( struct http_transaction *http, if ( ! auth ) return -ENOMEM; + /* Split NetBIOS [domain\]username */ + username = ( ( char * ) req->username ); + domain = netbios_domain ( &username ); + /* Construct raw Authenticate message */ - check = ntlm_authenticate ( &rsp->info, NULL, req->username, + check = ntlm_authenticate ( &rsp->info, domain, username, http_ntlm_workstation, &req->lm, &req->nt, auth ); assert ( check == req->len ); + /* Restore NetBIOS [domain\]username */ + netbios_domain_undo ( domain, username ); + /* Base64-encode Authenticate message */ len = base64_encode ( auth, req->len, buf, len ); From 8dbb73a779e5b11ee2b65f9d2af6dd9bd8998608 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 19 Feb 2018 18:59:45 +0000 Subject: [PATCH 548/591] [xhci] Consume event TRB before reporting completion to USB core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reporting a completion via usb_complete() will pass control outside the scope of xhci.c, and could potentially result in a further call to xhci_event_poll() before returning from usb_complete(). Since we currently update the event consumer counter only after calling usb_complete(), this can result in duplicate completions and consequent corruption of the submission TRB ring structures. Fix by updating the event ring consumer counter before passing control to usb_complete(). Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/drivers/usb/xhci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 8bf3ca776..ecf8bf4d5 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -1711,6 +1711,9 @@ static void xhci_event_poll ( struct xhci_device *xhci ) { ( event->cons >> shift ) ) & XHCI_TRB_C ) ) break; + /* Consume this TRB */ + event->cons++; + /* Handle TRB */ type = ( trb->common.type & XHCI_TRB_TYPE_MASK ); switch ( type ) { @@ -1733,14 +1736,11 @@ static void xhci_event_poll ( struct xhci_device *xhci ) { default: DBGC ( xhci, "XHCI %s unrecognised event %#x\n:", - xhci->name, event->cons ); + xhci->name, ( event->cons - 1 ) ); DBGC_HDA ( xhci, virt_to_phys ( trb ), trb, sizeof ( *trb ) ); break; } - - /* Consume this TRB */ - event->cons++; } /* Update dequeue pointer if applicable */ From c89a446cf09f30a121ae21d91f4a1aa071044084 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Feb 2018 10:56:31 +0000 Subject: [PATCH 549/591] [efi] Run at TPL_CALLBACK to protect against UEFI timers As noted in the comments, UEFI manages to combines the all of the worst aspects of both a polling design (inefficiency and inability to sleep until something interesting happens) and of an interrupt-driven design (the complexity of code that could be preempted at any time, thanks to UEFI timers). This causes problems in particular for UEFI USB keyboards: the keyboard driver calls UsbAsyncInterruptTransfer() to set up a periodic timer which is used to poll the USB bus. This poll may interrupt a critical section within iPXE, typically resulting in list corruption and either a hang or reboot. Work around this problem by mirroring the BIOS design, in which we run with interrupts disabled almost all of the time. Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 12 +++++++++ src/interface/efi/efi_timer.c | 41 +++++++++++++++++++++++++----- src/interface/efi/efi_usb.c | 48 ++--------------------------------- 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 263a25ac6..88d999bf0 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -45,6 +45,9 @@ static LIST_HEAD ( efi_snp_devices ); /** Network devices are currently claimed for use by iPXE */ static int efi_snp_claimed; +/** TPL prior to network devices being claimed */ +static EFI_TPL efi_snp_old_tpl; + /* Downgrade user experience if configured to do so * * The default UEFI user experience for network boot is somewhat @@ -1895,8 +1898,13 @@ struct efi_snp_device * last_opened_snpdev ( void ) { * @v delta Claim count change */ void efi_snp_add_claim ( int delta ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; + /* Raise TPL if we are about to claim devices */ + if ( ! efi_snp_claimed ) + efi_snp_old_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Claim SNP devices */ efi_snp_claimed += delta; assert ( efi_snp_claimed >= 0 ); @@ -1904,4 +1912,8 @@ void efi_snp_add_claim ( int delta ) { /* Update SNP mode state for each interface */ list_for_each_entry ( snpdev, &efi_snp_devices, list ) efi_snp_set_state ( snpdev ); + + /* Restore TPL if we have released devices */ + if ( ! efi_snp_claimed ) + bs->RestoreTPL ( efi_snp_old_tpl ); } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 0ffe2a1a0..8fe307ceb 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -76,11 +76,36 @@ static void efi_udelay ( unsigned long usecs ) { * @ret ticks Current time, in ticks */ static unsigned long efi_currticks ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - /* EFI provides no clean way for device drivers to shut down - * in preparation for handover to a booted operating system. - * The platform firmware simply doesn't bother to call the - * drivers' Stop() methods. Instead, drivers must register an + /* UEFI manages to ingeniously combine the worst aspects of + * both polling and interrupt-driven designs. There is no way + * to support proper interrupt-driven operation, since there + * is no way to hook in an interrupt service routine. A + * mockery of interrupts is provided by UEFI timers, which + * trigger at a preset rate and can fire at any time. + * + * We therefore have all of the downsides of a polling design + * (inefficiency and inability to sleep until something + * interesting happens) combined with all of the downsides of + * an interrupt-driven design (the complexity of code that + * could be preempted at any time). + * + * The UEFI specification expects us to litter the entire + * codebase with calls to RaiseTPL() as needed for sections of + * code that are not reentrant. Since this doesn't actually + * gain us any substantive benefits (since even with such + * calls we would still be suffering from the limitations of a + * polling design), we instead choose to run at TPL_CALLBACK + * almost all of the time, dropping to TPL_APPLICATION to + * allow timer ticks to occur. + * + * + * For added excitement, UEFI provides no clean way for device + * drivers to shut down in preparation for handover to a + * booted operating system. The platform firmware simply + * doesn't bother to call the drivers' Stop() methods. + * Instead, all non-trivial drivers must register an * EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when * ExitBootServices() is called, and clean up without any * reference to the EFI driver model. @@ -97,10 +122,14 @@ static unsigned long efi_currticks ( void ) { * the API lazily assumes that the host system continues to * travel through time in the usual direction. Work around * EFI's violation of this assumption by falling back to a - * simple free-running monotonic counter. + * simple free-running monotonic counter during shutdown. */ - if ( efi_shutdown_in_progress ) + if ( efi_shutdown_in_progress ) { efi_jiffies++; + } else { + bs->RestoreTPL ( TPL_APPLICATION ); + bs->RaiseTPL ( TPL_CALLBACK ); + } return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) ); } diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index db8c3d348..f9c91d09b 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -64,50 +64,6 @@ static const char * efi_usb_direction_name ( EFI_USB_DATA_DIRECTION direction ){ ****************************************************************************** */ -/** - * Poll USB bus - * - * @v usbdev EFI USB device - */ -static void efi_usb_poll ( struct efi_usb_device *usbdev ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct usb_bus *bus = usbdev->usb->port->hub->bus; - EFI_TPL tpl; - - /* UEFI manages to ingeniously combine the worst aspects of - * both polling and interrupt-driven designs. There is no way - * to support proper interrupt-driven operation, since there - * is no way to hook in an interrupt service routine. A - * mockery of interrupts is provided by UEFI timers, which - * trigger at a preset rate and can fire at any time. - * - * We therefore have all of the downsides of a polling design - * (inefficiency and inability to sleep until something - * interesting happens) combined with all of the downsides of - * an interrupt-driven design (the complexity of code that - * could be preempted at any time). - * - * The UEFI specification expects us to litter the entire - * codebase with calls to RaiseTPL() as needed for sections of - * code that are not reentrant. Since this doesn't actually - * gain us any substantive benefits (since even with such - * calls we would still be suffering from the limitations of a - * polling design), we instead choose to wrap only calls to - * usb_poll(). This should be sufficient for most practical - * purposes. - * - * A "proper" solution would involve rearchitecting the whole - * codebase to support interrupt-driven operation. - */ - tpl = bs->RaiseTPL ( TPL_NOTIFY ); - - /* Poll bus */ - usb_poll ( bus ); - - /* Restore task priority level */ - bs->RestoreTPL ( tpl ); -} - /** * Poll USB bus (from endpoint event timer) * @@ -216,7 +172,7 @@ static int efi_usb_open ( struct efi_usb_interface *usbintf, /* Create event */ if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ), - TPL_NOTIFY, efi_usb_timer, usbep, + TPL_CALLBACK, efi_usb_timer, usbep, &usbep->event ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( usbdev, "USBDEV %s %s could not create event: %s\n", @@ -363,7 +319,7 @@ static int efi_usb_sync_transfer ( struct efi_usb_interface *usbintf, for ( i = 0 ; ( ( timeout == 0 ) || ( i < timeout ) ) ; i++ ) { /* Poll bus */ - efi_usb_poll ( usbdev ); + usb_poll ( usbdev->usb->port->hub->bus ); /* Check for completion */ if ( usbep->rc != -EINPROGRESS ) { From a272b7ce5789ac9a467f37288c03f317d1a46517 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Feb 2018 11:08:25 +0000 Subject: [PATCH 550/591] [efi] Raise TPL within EFI_SIMPLE_NETWORK_PROTOCOL entry points Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 93 ++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 88d999bf0..3f95a8961 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -233,8 +233,10 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { static EFI_STATUS EFIAPI efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; int rc; DBGC ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n", @@ -242,17 +244,26 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, ( ( unsigned long ) extra_tx_bufsize ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Open network device */ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n", snpdev, snpdev->netdev->name, strerror ( rc ) ); - return EFIRC ( rc ); + goto err_open; } efi_snp_set_state ( snpdev ); - return 0; + err_open: + bs->RestoreTPL ( saved_tpl ); + err_claimed: + return EFIRC ( rc ); } /** @@ -264,29 +275,41 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, */ static EFI_STATUS EFIAPI efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; int rc; DBGC ( snpdev, "SNPDEV %p RESET (%s extended verification)\n", snpdev, ( ext_verify ? "with" : "without" ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Close network device */ netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); efi_snp_flush ( snpdev ); + /* Reopen network device */ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", snpdev, snpdev->netdev->name, strerror ( rc ) ); - return EFIRC ( rc ); + goto err_open; } efi_snp_set_state ( snpdev ); - return 0; + err_open: + bs->RestoreTPL ( saved_tpl ); + err_claimed: + return EFIRC ( rc ); } /** @@ -297,8 +320,10 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { */ static EFI_STATUS EFIAPI efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; DBGC ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); @@ -306,10 +331,17 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { if ( efi_snp_claimed ) return EFI_NOT_READY; + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Close network device */ netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); efi_snp_flush ( snpdev ); + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + return 0; } @@ -512,8 +544,10 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, static EFI_STATUS EFIAPI efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 *interrupts, VOID **txbuf ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); @@ -523,6 +557,9 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, return EFI_NOT_READY; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -545,6 +582,9 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, " TX:%p", *txbuf ); } + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + DBGC2 ( snpdev, "\n" ); return 0; } @@ -566,12 +606,14 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN ll_header_len, UINTN len, VOID *data, EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, UINT16 *net_proto ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; struct io_buffer *iobuf; size_t payload_len; unsigned int tx_fill; + EFI_TPL saved_tpl; int rc; DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, @@ -592,8 +634,13 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, "\n" ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); /* Sanity checks */ if ( ll_header_len ) { @@ -677,6 +724,9 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, snpdev->tx[ snpdev->tx_prod++ % EFI_SNP_NUM_TX ] = data; snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + return 0; err_ring_full: @@ -685,6 +735,8 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, free_iob ( iobuf ); err_alloc_iob: err_sanity: + bs->RestoreTPL ( saved_tpl ); + err_claimed: return EFIRC ( rc ); } @@ -705,6 +757,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN *ll_header_len, UINTN *len, VOID *data, EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, UINT16 *net_proto ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; @@ -714,14 +767,20 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, uint16_t iob_net_proto; unsigned int iob_flags; size_t copy_len; + EFI_TPL saved_tpl; int rc; DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, ( ( unsigned long ) *len ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -770,6 +829,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, out_bad_ll_header: free_iob ( iobuf ); out_no_packet: + bs->RestoreTPL ( saved_tpl ); + err_claimed: return EFIRC ( rc ); } @@ -781,7 +842,9 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, */ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, VOID *context ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = context; + EFI_TPL saved_tpl; DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); @@ -793,8 +856,14 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, if ( efi_snp_claimed ) return; + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Poll the network device */ efi_snp_poll ( snpdev ); + + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); } /** SNP interface */ From f672a27b34220865b403df519593f382859559e0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Feb 2018 11:19:39 +0000 Subject: [PATCH 551/591] [efi] Raise TPL within EFI_USB_IO_PROTOCOL entry points Signed-off-by: Michael Brown --- src/interface/efi/efi_usb.c | 47 ++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index f9c91d09b..48274f1d6 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -505,6 +505,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, EFI_USB_DATA_DIRECTION direction, UINT32 timeout, VOID *data, UINTN len, UINT32 *status ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; @@ -512,6 +513,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, USB_REQUEST_TYPE ( packet->Request ) ); unsigned int value = le16_to_cpu ( packet->Value ); unsigned int index = le16_to_cpu ( packet->Index ); + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s control %04x:%04x:%04x:%04x %s %dms " @@ -520,6 +522,9 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, efi_usb_direction_name ( direction ), timeout, data, ( ( size_t ) len ) ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Clear status */ *status = 0; @@ -563,6 +568,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, err_control: err_change_config: + bs->RestoreTPL ( saved_tpl ); return EFIRC ( rc ); } @@ -580,16 +586,21 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, static EFI_STATUS EFIAPI efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, UINTN *len, UINTN timeout, UINT32 *status ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; size_t actual = *len; + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s bulk %s %p+%zx %dms\n", usbintf->name, ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data, ( ( size_t ) *len ), ( ( unsigned int ) timeout ) ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Clear status */ *status = 0; @@ -599,10 +610,12 @@ efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, data, &actual ) ) != 0 ) { /* Assume that any error represents a timeout */ *status = EFI_USB_ERR_TIMEOUT; - return rc; + goto err_transfer; } - return 0; + err_transfer: + bs->RestoreTPL ( saved_tpl ); + return EFIRC ( rc ); } /** @@ -620,16 +633,21 @@ static EFI_STATUS EFIAPI efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, UINTN *len, UINTN timeout, UINT32 *status ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; size_t actual = *len; + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s sync intr %s %p+%zx %dms\n", usbintf->name, ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data, ( ( size_t ) *len ), ( ( unsigned int ) timeout ) ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Clear status */ *status = 0; @@ -639,10 +657,12 @@ efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, timeout, data, &actual ) ) != 0 ) { /* Assume that any error represents a timeout */ *status = EFI_USB_ERR_TIMEOUT; - return rc; + goto err_transfer; } - return 0; + err_transfer: + bs->RestoreTPL ( saved_tpl ); + return EFIRC ( rc ); } /** @@ -662,9 +682,11 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, BOOLEAN start, UINTN interval, UINTN len, EFI_ASYNC_USB_TRANSFER_CALLBACK callback, VOID *context ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s async intr %s len %#zx int %d %p/%p\n", @@ -673,6 +695,9 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, ( ( size_t ) len ), ( ( unsigned int ) interval ), callback, context ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Start/stop transfer as applicable */ if ( start ) { @@ -687,11 +712,13 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, /* Stop transfer */ efi_usb_async_stop ( usbintf, endpoint ); + /* Success */ + rc = 0; + } - return 0; - err_start: + bs->RestoreTPL ( saved_tpl ); return EFIRC ( rc ); } @@ -889,12 +916,16 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, struct usb_descriptor_header header; VOID *buffer; size_t len; + EFI_TPL saved_tpl; EFI_STATUS efirc; int rc; DBGC2 ( usbdev, "USBDEV %s get string %d:%d descriptor\n", usbintf->name, language, index ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Read descriptor header */ if ( ( rc = usb_get_descriptor ( usbdev->usb, 0, USB_STRING_DESCRIPTOR, index, language, &header, @@ -928,6 +959,9 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, ( len - sizeof ( header ) ) ); memset ( ( buffer + len - sizeof ( header ) ), 0, sizeof ( **string ) ); + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + /* Return allocated string */ *string = buffer; return 0; @@ -936,6 +970,7 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, bs->FreePool ( buffer ); err_alloc: err_get_header: + bs->RestoreTPL ( saved_tpl ); return EFIRC ( rc ); } From 47849be3a900c546cf92066849be0806f4e611d9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Feb 2018 18:02:25 +0000 Subject: [PATCH 552/591] [process] Include process name in debug messages Signed-off-by: Michael Brown --- src/include/ipxe/process.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/include/ipxe/process.h b/src/include/ipxe/process.h index d600508e7..0d059f8a1 100644 --- a/src/include/ipxe/process.h +++ b/src/include/ipxe/process.h @@ -29,6 +29,8 @@ struct process { /** A process descriptor */ struct process_descriptor { + /** Process name */ + const char *name; /** Offset of process within containing object */ size_t offset; /** @@ -78,6 +80,7 @@ struct process_descriptor { * @ret desc Object interface descriptor */ #define PROC_DESC( object_type, process, _step ) { \ + .name = #_step, \ .offset = process_offset ( object_type, process ), \ .step = PROC_STEP ( object_type, _step ), \ .reschedule = 1, \ @@ -92,6 +95,7 @@ struct process_descriptor { * @ret desc Object interface descriptor */ #define PROC_DESC_ONCE( object_type, process, _step ) { \ + .name = #_step, \ .offset = process_offset ( object_type, process ), \ .step = PROC_STEP ( object_type, _step ), \ .reschedule = 0, \ @@ -106,6 +110,7 @@ struct process_descriptor { * @ret desc Object interface descriptor */ #define PROC_DESC_PURE( _step ) { \ + .name = #_step, \ .offset = 0, \ .step = PROC_STEP ( struct process, _step ), \ .reschedule = 1, \ @@ -192,7 +197,7 @@ struct process name __permanent_process = { \ #define PROC_COL( process ) process_object ( process ) /** printf() format string for PROC_DBG() */ -#define PROC_FMT "%p+%zx" +#define PROC_FMT "%p %s()" /** * printf() arguments for representing a process @@ -200,6 +205,6 @@ struct process name __permanent_process = { \ * @v process Process * @ret args printf() argument list corresponding to PROC_FMT */ -#define PROC_DBG( process ) process_object ( process ), (process)->desc->offset +#define PROC_DBG( process ) process_object ( process ), (process)->desc->name #endif /* _IPXE_PROCESS_H */ From c84f9d67272beaed98f98bf308471df16340a3be Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 1 Mar 2018 13:30:41 +0000 Subject: [PATCH 553/591] [iscsi] Parse IPv6 address in root path The iSCSI root path may contain a literal IPv6 address. Update the parser to handle this address format correctly. Signed-off-by: Hannes Reinecke Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 789ac86e6..f8379b285 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1921,6 +1921,7 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi, char rp_copy[ strlen ( root_path ) + 1 ]; char *rp_comp[NUM_RP_COMPONENTS]; char *rp = rp_copy; + int skip = 0; int i = 0; int rc; @@ -1930,11 +1931,15 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi, rp_comp[i++] = rp; if ( i == NUM_RP_COMPONENTS ) break; - for ( ; *rp != ':' ; rp++ ) { + for ( ; ( ( *rp != ':' ) || skip ) ; rp++ ) { if ( ! *rp ) { DBGC ( iscsi, "iSCSI %p root path \"%s\" " "too short\n", iscsi, root_path ); return -EINVAL_ROOT_PATH_TOO_SHORT; + } else if ( *rp == '[' ) { + skip = 1; + } else if ( *rp == ']' ) { + skip = 0; } } *(rp++) = '\0'; From d8c500b7945e57023dde5bd0be2b0e40963315d9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 12 Mar 2018 10:55:28 +0000 Subject: [PATCH 554/591] [efi] Drop to TPL_APPLICATION when gathering entropy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit c89a446 ("[efi] Run at TPL_CALLBACK to protect against UEFI timers") introduced a regression in the EFI entropy gathering code. When the EFI_RNG_PROTOCOL is not present, we fall back to using timer interrupts (as for the BIOS build). Since timer interrupts are disabled at TPL_CALLBACK, WaitForEvent() fails and no entropy can be gathered. Fix by dropping to TPL_APPLICATION while entropy gathering is enabled. Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/interface/efi/efi_entropy.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index 881c4c9a2..2a2fc9054 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -79,6 +79,9 @@ static int efi_entropy_enable ( void ) { DBGC ( &tick, "ENTROPY %s RNG protocol\n", ( efirng ? "has" : "has no" ) ); + /* Drop to TPL_APPLICATION to allow timer tick event to take place */ + bs->RestoreTPL ( TPL_APPLICATION ); + /* Create timer tick event */ if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL, &tick ) ) != 0 ) { @@ -100,6 +103,9 @@ static void efi_entropy_disable ( void ) { /* Close timer tick event */ bs->CloseEvent ( tick ); + + /* Return to TPL_CALLBACK */ + bs->RaiseTPL ( TPL_CALLBACK ); } /** From 10d083ffa93e2cc5c69c8f963720ce4d48c5cf6f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 14 Mar 2018 23:55:28 +0000 Subject: [PATCH 555/591] [efi] Raise TPL within EFI_DRIVER_BINDING_PROTOCOL entry points Debugged-by: Rob Taglang Signed-off-by: Michael Brown --- src/interface/efi/efi_driver.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index de0f61f92..fe73630fd 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -95,7 +95,9 @@ struct efi_device * efidev_parent ( struct device *dev ) { static EFI_STATUS EFIAPI efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_driver *efidrv; + EFI_TPL saved_tpl; int rc; DBGCP ( device, "EFIDRV %s DRIVER_SUPPORTED", @@ -111,17 +113,22 @@ efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, return EFI_ALREADY_STARTED; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Look for a driver claiming to support this device */ for_each_table_entry ( efidrv, EFI_DRIVERS ) { if ( ( rc = efidrv->supported ( device ) ) == 0 ) { DBGC ( device, "EFIDRV %s has driver \"%s\"\n", efi_handle_name ( device ), efidrv->name ); + bs->RestoreTPL ( saved_tpl ); return 0; } } DBGCP ( device, "EFIDRV %s has no driver\n", efi_handle_name ( device ) ); + bs->RestoreTPL ( saved_tpl ); return EFI_UNSUPPORTED; } @@ -145,6 +152,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } path; EFI_DEVICE_PATH_PROTOCOL *path_end; size_t path_len; + EFI_TPL saved_tpl; EFI_STATUS efirc; int rc; @@ -162,6 +170,9 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, goto err_already_started; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Do nothing if we are currently disconnecting drivers */ if ( efi_driver_disconnecting ) { DBGC ( device, "EFIDRV %s refusing to start during " @@ -215,6 +226,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, DBGC ( device, "EFIDRV %s using driver \"%s\"\n", efi_handle_name ( device ), efidev->driver->name ); + bs->RestoreTPL ( saved_tpl ); return 0; } DBGC ( device, "EFIDRV %s could not start driver \"%s\": %s\n", @@ -232,6 +244,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } err_open_path: err_disconnecting: + bs->RestoreTPL ( saved_tpl ); err_already_started: return efirc; } @@ -250,8 +263,10 @@ static EFI_STATUS EFIAPI efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_HANDLE device, UINTN num_children, EFI_HANDLE *children ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_driver *efidrv; struct efi_device *efidev; + EFI_TPL saved_tpl; UINTN i; DBGC ( device, "EFIDRV %s DRIVER_STOP", efi_handle_name ( device ) ); @@ -269,6 +284,9 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, return EFI_DEVICE_ERROR; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Stop this device */ efidrv = efidev->driver; assert ( efidrv != NULL ); @@ -276,6 +294,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, list_del ( &efidev->dev.siblings ); free ( efidev ); + bs->RestoreTPL ( saved_tpl ); return 0; } From 0c43bb934ab531f86a850a930e9cf599650f582d Mon Sep 17 00:00:00 2001 From: Rob Taglang Date: Wed, 14 Mar 2018 16:09:20 -0400 Subject: [PATCH 556/591] [intel] Add PCI_ROM entry for Intel i354 NIC Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 4cb8a4238..a2e6d535b 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1150,6 +1150,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; From 89e31f8491895e6196d4ba56aee3809261aa6b89 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 14:54:57 +0200 Subject: [PATCH 557/591] [librm] Add facility to provide register and stack dump for CPU exceptions When DEBUG=librm_mgmt is enabled, intercept CPU exceptions and provide a register and stack dump, then drop to an emergency shell. Exiting from the shell will almost certainly not work, but this provides an opportunity to view the register and stack dump and carry out some basic debugging. Note that we can intercept only the first 8 CPU exceptions, since a PXE ROM is not permitted to rebase the PIC. Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 44 +++++++++++++ src/arch/x86/transitions/librm.S | 33 ++++++++-- src/arch/x86/transitions/librm_mgmt.c | 92 ++++++++++++++++++++++++++- 3 files changed, 162 insertions(+), 7 deletions(-) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 311748bec..597c65e6a 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -376,6 +376,50 @@ struct interrupt_vector { /** "jmp" instruction */ #define JMP_INSN 0xe9 +/** 32-bit interrupt wrapper stack frame */ +struct interrupt_frame32 { + uint32_t esp; + uint32_t ss; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t ebp; + uint32_t edi; + uint32_t esi; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +} __attribute__ (( packed )); + +/** 64-bit interrupt wrapper stack frame */ +struct interrupt_frame64 { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rbp; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} __attribute__ (( packed )); + extern void set_interrupt_vector ( unsigned int intr, void *vector ); /** A page table */ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index c31daad84..9d3eff954 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -1363,20 +1363,19 @@ flatten_dummy: .code32 .globl interrupt_wrapper interrupt_wrapper: - /* Preserve registers (excluding already-saved %eax and - * otherwise unused registers which are callee-save for both - * 32-bit and 64-bit ABIs). - */ + /* Preserve registers (excluding already-saved %eax) */ pushl %ebx pushl %ecx pushl %edx pushl %esi pushl %edi + pushl %ebp /* Expand IRQ number to whole %eax register */ movzbl %al, %eax .if64 ; /* Skip transition to long mode, if applicable */ + xorl %edx, %edx movw %cs, %bx cmpw $LONG_CS, %bx je 1f @@ -1391,24 +1390,45 @@ interrupt_wrapper: /* Switch to virtual addressing */ call intr_to_prot + + /* Pass 32-bit interrupt frame pointer in %edx */ + movl %esp, %edx + xorl %ecx, %ecx .if64 /* Switch to long mode */ call prot_to_long .code64 -1: /* Preserve long-mode caller-save registers */ +1: /* Preserve long-mode registers */ pushq %r8 pushq %r9 pushq %r10 pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 /* Expand IRQ number to whole %rdi register */ movl %eax, %edi + + /* Pass 32-bit interrupt frame pointer (if applicable) in %rsi */ + testl %edx, %edx + je 1f + movl %edx, %esi + addl virt_offset, %esi +1: + /* Pass 64-bit interrupt frame pointer in %rdx */ + movq %rsp, %rdx .endif /* Call interrupt handler */ call interrupt .if64 - /* Restore long-mode caller-save registers */ + /* Restore long-mode registers */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 popq %r11 popq %r10 popq %r9 @@ -1432,6 +1452,7 @@ interrupt_wrapper: popl %ds 1: /* Restore registers */ + popl %ebp popl %edi popl %esi popl %edx diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c index 8144e7671..f9e1d261a 100644 --- a/src/arch/x86/transitions/librm_mgmt.c +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /* * This file provides functions for managing librm. @@ -43,6 +44,9 @@ struct idtr64 idtr64 = { .limit = ( sizeof ( idt64 ) - 1 ), }; +/** Length of stack dump */ +#define STACK_DUMP_LEN 128 + /** Timer interrupt profiler */ static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" }; @@ -159,15 +163,101 @@ static struct profiler * interrupt_profiler ( int intr ) { } } +/** + * Display interrupt stack dump (for debugging) + * + * @v intr Interrupt number + * @v frame32 32-bit interrupt wrapper stack frame (or NULL) + * @v frame64 64-bit interrupt wrapper stack frame (or NULL) + */ +static __attribute__ (( unused )) void +interrupt_dump ( int intr, struct interrupt_frame32 *frame32, + struct interrupt_frame64 *frame64 ) { + unsigned long sp; + void *stack; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Print register dump */ + if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) { + sp = ( frame32->esp + sizeof ( *frame32 ) - + offsetof ( typeof ( *frame32 ), esp ) ); + DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n", + intr, frame32->cs, frame32->eip, frame32->ss, sp ); + DBGC ( &intr, "cs = %04x ds = %04x es = %04x fs = %04x " + "gs = %04x ss = %04x\n", frame32->cs, frame32->ds, + frame32->es, frame32->fs, frame32->gs, frame32->ss ); + DBGC ( &intr, "eax = %08x ebx = %08x ecx = %08x " + "edx = %08x flg = %08x\n", frame32->eax, frame32->ebx, + frame32->ecx, frame32->edx, frame32->eflags ); + DBGC ( &intr, "esi = %08x edi = %08x ebp = %08x " + "esp = %08lx eip = %08x\n", frame32->esi, frame32->edi, + frame32->ebp, sp, frame32->eip ); + stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) ); + } else { + DBGC ( &intr, "INT%d at %04llx:%016llx (stack " + "%04llx:%016llx):\n", intr, + ( ( unsigned long long ) frame64->cs ), + ( ( unsigned long long ) frame64->rip ), + ( ( unsigned long long ) frame64->ss ), + ( ( unsigned long long ) frame64->rsp ) ); + DBGC ( &intr, "rax = %016llx rbx = %016llx rcx = %016llx\n", + ( ( unsigned long long ) frame64->rax ), + ( ( unsigned long long ) frame64->rbx ), + ( ( unsigned long long ) frame64->rcx ) ); + DBGC ( &intr, "rdx = %016llx rsi = %016llx rdi = %016llx\n", + ( ( unsigned long long ) frame64->rdx ), + ( ( unsigned long long ) frame64->rsi ), + ( ( unsigned long long ) frame64->rdi ) ); + DBGC ( &intr, "rbp = %016llx rsp = %016llx flg = %016llx\n", + ( ( unsigned long long ) frame64->rbp ), + ( ( unsigned long long ) frame64->rsp ), + ( ( unsigned long long ) frame64->rflags ) ); + DBGC ( &intr, "r8 = %016llx r9 = %016llx r10 = %016llx\n", + ( ( unsigned long long ) frame64->r8 ), + ( ( unsigned long long ) frame64->r9 ), + ( ( unsigned long long ) frame64->r10 ) ); + DBGC ( &intr, "r11 = %016llx r12 = %016llx r13 = %016llx\n", + ( ( unsigned long long ) frame64->r11 ), + ( ( unsigned long long ) frame64->r12 ), + ( ( unsigned long long ) frame64->r13 ) ); + DBGC ( &intr, "r14 = %016llx r15 = %016llx\n", + ( ( unsigned long long ) frame64->r14 ), + ( ( unsigned long long ) frame64->r15 ) ); + sp = frame64->rsp; + stack = phys_to_virt ( sp ); + } + + /* Print stack dump */ + DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN ); +} + /** * Interrupt handler * * @v intr Interrupt number + * @v frame32 32-bit interrupt wrapper stack frame (or NULL) + * @v frame64 64-bit interrupt wrapper stack frame (or NULL) + * @v frame Interrupt wrapper stack frame */ -void __attribute__ (( regparm ( 1 ) )) interrupt ( int intr ) { +void __attribute__ (( regparm ( 3 ) )) +interrupt ( int intr, struct interrupt_frame32 *frame32, + struct interrupt_frame64 *frame64 ) { struct profiler *profiler = interrupt_profiler ( intr ); uint32_t discard_eax; + /* Trap CPU exceptions if debugging is enabled. Note that we + * cannot treat INT8+ as exceptions, since we are not + * permitted to rebase the PIC. + */ + if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) { + interrupt_dump ( intr, frame32, frame64 ); + DBG ( "CPU exception: dropping to emergency shell\n" ); + shell(); + } + /* Reissue interrupt in real mode */ profile_start ( profiler ); __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t" From 0778418e29ea16fc897fc5b6e497054f5ba86ebd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 15:40:23 +0200 Subject: [PATCH 558/591] [golan] Do not assume all devices are identical Remove the global variable shomron_nodnic_supported, since it may have different values for different PCI devices. Originally-fixed-by: Mohammed Taha Signed-off-by: Michael Brown --- src/drivers/infiniband/golan.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 61331d4c1..1858da7cf 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -2586,8 +2586,6 @@ struct flexboot_nodnic_callbacks shomron_nodnic_callbacks = { .tx_uar_send_doorbell_fn = shomron_tx_uar_send_db, }; -static int shomron_nodnic_supported = 0; - static int shomron_nodnic_is_supported ( struct pci_device *pci ) { if ( DEVICE_IS_CIB ( pci->device ) ) return 0; @@ -2607,8 +2605,7 @@ static int golan_probe ( struct pci_device *pci ) { goto probe_done; } - shomron_nodnic_supported = shomron_nodnic_is_supported ( pci ); - if ( shomron_nodnic_supported ) { + if ( shomron_nodnic_is_supported ( pci ) ) { DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ ); rc = flexboot_nodnic_probe ( pci, &shomron_nodnic_callbacks, NULL ); } else { @@ -2624,7 +2621,7 @@ probe_done: static void golan_remove ( struct pci_device *pci ) { DBG ( "%s: start\n", __FUNCTION__ ); - if ( ! shomron_nodnic_supported ) { + if ( ! shomron_nodnic_is_supported ( pci ) ) { DBG ( "%s: Using normal driver remove\n", __FUNCTION__ ); golan_remove_normal ( pci ); return; From 33d79d5d2b6cfd57fbe0733f66784fac658cb360 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 17:11:16 +0200 Subject: [PATCH 559/591] [lacp] Mark link as blocked if partner is not yet up and running Mark the link as blocked if the LACP partner is not reporting itself as being in sync, collecting, and distributing. This matches the behaviour for STP: we mark the link as blocked if we detect that the switch is actively blocking traffic, in order to extend the DHCP discovery period and so prevent boot failures on switches that take an excessively long time to enable ports. Signed-off-by: Michael Brown --- src/include/ipxe/eth_slow.h | 6 ++++++ src/net/eth_slow.c | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/include/ipxe/eth_slow.h b/src/include/ipxe/eth_slow.h index f6d731b3b..754ea6e1f 100644 --- a/src/include/ipxe/eth_slow.h +++ b/src/include/ipxe/eth_slow.h @@ -190,6 +190,12 @@ struct eth_slow_lacp_entity_tlv { */ #define LACP_STATE_EXPIRED 0x80 +/** LACP fast interval (1 second) */ +#define LACP_INTERVAL_FAST 1 + +/** LACP slow interval (30 seconds) */ +#define LACP_INTERVAL_SLOW 30 + /** LACP collector TLV */ struct eth_slow_lacp_collector_tlv { /** TLV header */ diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c index 049c26cb3..41ce79fe0 100644 --- a/src/net/eth_slow.c +++ b/src/net/eth_slow.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -148,9 +149,30 @@ static int eth_slow_lacp_rx ( struct io_buffer *iobuf, struct net_device *netdev ) { union eth_slow_packet *eth_slow = iobuf->data; struct eth_slow_lacp *lacp = ð_slow->lacp; + unsigned int interval; eth_slow_lacp_dump ( iobuf, netdev, "RX" ); + /* If partner is not in sync, collecting, and distributing, + * then block the link until after the next expected LACP + * packet. + */ + if ( ~lacp->partner.state & ( LACP_STATE_IN_SYNC | + LACP_STATE_COLLECTING | + LACP_STATE_DISTRIBUTING ) ) { + DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name ); + interval = ( ( lacp->partner.state & LACP_STATE_FAST ) ? + ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) : + ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) ); + netdev_link_block ( netdev, interval ); + } else { + if ( netdev_link_blocked ( netdev ) ) { + DBGC ( netdev, "SLOW %s LACP partner is up\n", + netdev->name ); + } + netdev_link_unblock ( netdev ); + } + /* Build response */ memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) ); memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) ); From c160c9dfc0d27af6989fb450937866d9f2d5b34c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 17:20:04 +0200 Subject: [PATCH 560/591] [lacp] Fix debug message to match documentation Signed-off-by: Michael Brown --- src/net/eth_slow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c index 41ce79fe0..cbc22aef4 100644 --- a/src/net/eth_slow.c +++ b/src/net/eth_slow.c @@ -92,7 +92,7 @@ eth_slow_marker_tlv_name ( uint8_t type ) { * @ret name LACP state name */ static const char * eth_slow_lacp_state_name ( uint8_t state ) { - static char state_chars[] = "AFGSRTLX"; + static char state_chars[] = "AFGSCDLX"; unsigned int i; for ( i = 0 ; i < 8 ; i++ ) { From b11ae1d91b32c7d0d52a0fc35a37b5e2f57a0f79 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 17:43:11 +0200 Subject: [PATCH 561/591] [tftp] Prevent potential division by zero Signed-off-by: Michael Brown --- src/net/udp/tftp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index 4255472ee..6ce27497c 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -279,6 +279,8 @@ static int tftp_presize ( struct tftp_request *tftp, size_t filesize ) { * length is an exact multiple of the blocksize will have a * trailing zero-length block, which must be included. */ + if ( tftp->blksize == 0 ) + return -EINVAL; num_blocks = ( ( filesize / tftp->blksize ) + 1 ); if ( ( rc = bitmap_resize ( &tftp->bitmap, num_blocks ) ) != 0 ) { DBGC ( tftp, "TFTP %p could not resize bitmap to %d blocks: " From ae930644962a886d3845e5d8836c5cf136c82df0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 18:36:58 +0200 Subject: [PATCH 562/591] [profile] Prevent potential division by zero Limit the profile sample count to INT_MAX to avoid both signed overflow and a potential division by zero when updating the stored mean value. Signed-off-by: Michael Brown --- src/core/profile.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/profile.c b/src/core/profile.c index 1075047b9..3655108ea 100644 --- a/src/core/profile.c +++ b/src/core/profile.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -122,8 +123,9 @@ void profile_update ( struct profiler *profiler, unsigned long sample ) { */ assert ( ( ( signed ) sample ) >= 0 ); - /* Update sample count */ - profiler->count++; + /* Update sample count, limiting to avoid signed overflow */ + if ( profiler->count < INT_MAX ) + profiler->count++; /* Adjust mean sample value scale if necessary. Skip if * sample is zero (in which case flsl(sample)-1 would From a0021a30dd8db832714e327bbbc65d3589f528ab Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 22:21:49 +0200 Subject: [PATCH 563/591] [ocsp] Centralise test for whether or not an OCSP check is required Signed-off-by: Michael Brown --- src/crypto/x509.c | 4 ++-- src/include/ipxe/ocsp.h | 15 +++++++++++++++ src/net/validator.c | 3 +-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 76ace0313..feb7e4a0a 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -1362,8 +1363,7 @@ int x509_validate ( struct x509_certificate *cert, } /* Fail if OCSP is required */ - if ( cert->extensions.auth_info.ocsp.uri.len && - ( ! cert->extensions.auth_info.ocsp.good ) ) { + if ( ocsp_required ( cert ) ) { DBGC ( cert, "X509 %p \"%s\" requires an OCSP check\n", cert, x509_name ( cert ) ); return -EACCES_OCSP_REQUIRED; diff --git a/src/include/ipxe/ocsp.h b/src/include/ipxe/ocsp.h index 71fa41dc9..9a6b3fe67 100644 --- a/src/include/ipxe/ocsp.h +++ b/src/include/ipxe/ocsp.h @@ -111,6 +111,21 @@ ocsp_put ( struct ocsp_check *ocsp ) { ref_put ( &ocsp->refcnt ); } +/** + * Check if X.509 certificate requires an OCSP check + * + * @v cert X.509 certificate + * @ret ocsp_required An OCSP check is required + */ +static inline int ocsp_required ( struct x509_certificate *cert ) { + + /* An OCSP check is required if an OCSP URI exists but the + * OCSP status is not (yet) good. + */ + return ( cert->extensions.auth_info.ocsp.uri.len && + ( ! cert->extensions.auth_info.ocsp.good ) ); +} + extern int ocsp_check ( struct x509_certificate *cert, struct x509_certificate *issuer, struct ocsp_check **ocsp ); diff --git a/src/net/validator.c b/src/net/validator.c index 68abe1b55..40f778c7d 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -488,8 +488,7 @@ static void validator_step ( struct validator *validator ) { /* The issuer is valid, but this certificate is not * yet valid. If OCSP is applicable, start it. */ - if ( cert->extensions.auth_info.ocsp.uri.len && - ( ! cert->extensions.auth_info.ocsp.good ) ) { + if ( ocsp_required ( cert ) ) { /* Start OCSP */ if ( ( rc = validator_start_ocsp ( validator, cert, issuer ) ) != 0 ) { From 9759860ec0c30685b53568b10caa5a91428bc7bf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Mar 2018 22:27:49 +0200 Subject: [PATCH 564/591] [ocsp] Allow OCSP checks to be disabled Some CAs provide non-functional OCSP servers, and some clients are forced to operate on networks without access to the OCSP servers. Allow the user to explicitly disable the use of OCSP checks by undefining OCSP_CHECK in config/crypto.h. Signed-off-by: Michael Brown --- src/config/crypto.h | 8 ++++++++ src/include/ipxe/ocsp.h | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/config/crypto.h b/src/config/crypto.h index 8f885c554..1edcdce45 100644 --- a/src/config/crypto.h +++ b/src/config/crypto.h @@ -58,6 +58,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define CROSSCERT "http://ca.ipxe.org/auto" +/** Perform OCSP checks when applicable + * + * Some CAs provide non-functional OCSP servers, and some clients are + * forced to operate on networks without access to the OCSP servers. + * Allow the user to explicitly disable the use of OCSP checks. + */ +#define OCSP_CHECK + #include #include NAMED_CONFIG(crypto.h) #include diff --git a/src/include/ipxe/ocsp.h b/src/include/ipxe/ocsp.h index 9a6b3fe67..be0bddc50 100644 --- a/src/include/ipxe/ocsp.h +++ b/src/include/ipxe/ocsp.h @@ -14,6 +14,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include + +/* Allow OCSP to be disabled completely */ +#ifdef OCSP_CHECK +#define OCSP_ENABLED 1 +#else +#define OCSP_ENABLED 0 +#endif /** OCSP algorithm identifier */ #define OCSP_ALGORITHM_IDENTIFIER( ... ) \ @@ -119,6 +127,10 @@ ocsp_put ( struct ocsp_check *ocsp ) { */ static inline int ocsp_required ( struct x509_certificate *cert ) { + /* An OCSP check is never required if OCSP checks are disabled */ + if ( ! OCSP_ENABLED ) + return 0; + /* An OCSP check is required if an OCSP URI exists but the * OCSP status is not (yet) good. */ From 342ff967cc109f8e21e743d841434b08737d9e6c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 19 Mar 2018 15:47:39 +0200 Subject: [PATCH 565/591] [lacp] Check the partner's own state when checking for blocked links The blocked link test in eth_slow_lacp_rx() is performed before the actor TLV is copied to the partner TLV, and so must test the actor state field rather than the partner state field. Signed-off-by: Michael Brown --- src/net/eth_slow.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c index cbc22aef4..baa51dbc1 100644 --- a/src/net/eth_slow.c +++ b/src/net/eth_slow.c @@ -157,11 +157,11 @@ static int eth_slow_lacp_rx ( struct io_buffer *iobuf, * then block the link until after the next expected LACP * packet. */ - if ( ~lacp->partner.state & ( LACP_STATE_IN_SYNC | - LACP_STATE_COLLECTING | - LACP_STATE_DISTRIBUTING ) ) { + if ( ~lacp->actor.state & ( LACP_STATE_IN_SYNC | + LACP_STATE_COLLECTING | + LACP_STATE_DISTRIBUTING ) ) { DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name ); - interval = ( ( lacp->partner.state & LACP_STATE_FAST ) ? + interval = ( ( lacp->actor.state & LACP_STATE_FAST ) ? ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) : ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) ); netdev_link_block ( netdev, interval ); From e8e9ca36130bed2ed241a38fa57e9a35c8ac61f3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Mar 2018 13:22:30 +0200 Subject: [PATCH 566/591] [efi] Provide Map_Mem() and associated UNDI callbacks Some drivers are known to call the optional Map_Mem() callback without first checking that the callback exists. Provide a usable basic implementation of Map_Mem() along with the other callbacks that become mandatory if Map_Mem() is provided. Note that in theory the PCI I/O protocol is allowed to require multiple calls to Map(), with each call handling only a subset of the overall mapped range. However, the reference implementation in EDK2 assumes that a single Map() will always suffice, so we can probably make the same simplifying assumption here. Tested with the Intel E3522X2.EFI driver (which, incidentally, fails to cleanly remove one of its mappings). Originally-implemented-by: Maor Dickman Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 163 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index 1700b4bd8..2d87e0c63 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -137,6 +138,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define PCI_MAX_BAR 6 +/** An NII memory mapping */ +struct nii_mapping { + /** List of mappings */ + struct list_head list; + /** Mapped address */ + UINT64 addr; + /** Mapping cookie created by PCI I/O protocol */ + VOID *mapping; +}; + /** An NII NIC */ struct nii_nic { /** EFI device */ @@ -179,6 +190,9 @@ struct nii_nic { struct io_buffer *txbuf; /** Current receive buffer */ struct io_buffer *rxbuf; + + /** Mapping list */ + struct list_head mappings; }; /** Maximum number of received packets per poll */ @@ -280,7 +294,19 @@ static int nii_pci_open ( struct nii_nic *nii ) { */ static void nii_pci_close ( struct nii_nic *nii ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct nii_mapping *map; + struct nii_mapping *tmp; + /* Remove any stale mappings */ + list_for_each_entry_safe ( map, tmp, &nii->mappings, list ) { + DBGC ( nii, "NII %s removing stale mapping %#llx\n", + nii->dev.name, ( ( unsigned long long ) map->addr ) ); + nii->pci_io->Unmap ( nii->pci_io, map->mapping ); + list_del ( &map->list ); + free ( map ); + } + + /* Close protocols */ bs->CloseProtocol ( nii->pci_device, &efi_pci_io_protocol_guid, efi_image_handle, nii->efidev->device ); } @@ -331,6 +357,139 @@ static EFIAPI VOID nii_io ( UINT64 unique_id, UINT8 op, UINT8 len, UINT64 addr, } } +/** + * Map callback + * + * @v unique_id NII NIC + * @v addr Address of memory to be mapped + * @v len Length of memory to be mapped + * @v dir Direction of data flow + * @v mapped Device mapped address to fill in + */ +static EFIAPI VOID nii_map ( UINT64 unique_id, UINT64 addr, UINT32 len, + UINT32 dir, UINT64 mapped ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + EFI_PHYSICAL_ADDRESS *phys = ( ( void * ) ( intptr_t ) mapped ); + EFI_PCI_IO_PROTOCOL_OPERATION op; + struct nii_mapping *map; + UINTN count = len; + EFI_STATUS efirc; + int rc; + + /* Return a zero mapped address on failure */ + *phys = 0; + + /* Determine PCI mapping operation */ + switch ( dir ) { + case TO_AND_FROM_DEVICE: + op = EfiPciIoOperationBusMasterCommonBuffer; + break; + case FROM_DEVICE: + op = EfiPciIoOperationBusMasterWrite; + break; + case TO_DEVICE: + op = EfiPciIoOperationBusMasterRead; + break; + default: + DBGC ( nii, "NII %s unsupported mapping direction %d\n", + nii->dev.name, dir ); + goto err_dir; + } + + /* Allocate a mapping record */ + map = zalloc ( sizeof ( *map ) ); + if ( ! map ) + goto err_alloc; + map->addr = addr; + + /* Create map */ + if ( ( efirc = nii->pci_io->Map ( nii->pci_io, op, + ( ( void * ) ( intptr_t ) addr ), + &count, phys, &map->mapping ) ) != 0){ + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s map operation failed: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_map; + } + + /* Add to list of mappings */ + list_add ( &map->list, &nii->mappings ); + DBGC2 ( nii, "NII %s mapped %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) *phys ) ); + return; + + list_del ( &map->list ); + err_map: + free ( map ); + err_alloc: + err_dir: + return; +} + +/** + * Unmap callback + * + * @v unique_id NII NIC + * @v addr Address of mapped memory + * @v len Length of mapped memory + * @v dir Direction of data flow + * @v mapped Device mapped address + */ +static EFIAPI VOID nii_unmap ( UINT64 unique_id, UINT64 addr, UINT32 len, + UINT32 dir __unused, UINT64 mapped ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + struct nii_mapping *map; + + /* Locate mapping record */ + list_for_each_entry ( map, &nii->mappings, list ) { + if ( map->addr == addr ) { + nii->pci_io->Unmap ( nii->pci_io, map->mapping ); + list_del ( &map->list ); + free ( map ); + DBGC2 ( nii, "NII %s unmapped %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) mapped ) ); + return; + } + } + + DBGC ( nii, "NII %s non-existent mapping %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) mapped ) ); +} + +/** + * Sync callback + * + * @v unique_id NII NIC + * @v addr Address of mapped memory + * @v len Length of mapped memory + * @v dir Direction of data flow + * @v mapped Device mapped address + */ +static EFIAPI VOID nii_sync ( UINT64 unique_id __unused, UINT64 addr, + UINT32 len, UINT32 dir, UINT64 mapped ) { + const void *src; + void *dst; + + /* Do nothing if this is an identity mapping */ + if ( addr == mapped ) + return; + + /* Determine direction */ + if ( dir == FROM_DEVICE ) { + src = ( ( void * ) ( intptr_t ) mapped ); + dst = ( ( void * ) ( intptr_t ) addr ); + } else { + src = ( ( void * ) ( intptr_t ) addr ); + dst = ( ( void * ) ( intptr_t ) mapped ); + } + + /* Copy data */ + memcpy ( dst, src, len ); +} + /** * Delay callback * @@ -499,6 +658,9 @@ static int nii_start_undi ( struct nii_nic *nii ) { cpb.Delay = ( ( intptr_t ) nii_delay ); cpb.Block = ( ( intptr_t ) nii_block ); cpb.Mem_IO = ( ( intptr_t ) nii_io ); + cpb.Map_Mem = ( ( intptr_t ) nii_map ); + cpb.UnMap_Mem = ( ( intptr_t ) nii_unmap ); + cpb.Sync_Mem = ( ( intptr_t ) nii_sync ); cpb.Unique_ID = ( ( intptr_t ) nii ); /* Issue command */ @@ -1063,6 +1225,7 @@ int nii_start ( struct efi_device *efidev ) { netdev_init ( netdev, &nii_operations ); nii = netdev->priv; nii->efidev = efidev; + INIT_LIST_HEAD ( &nii->mappings ); netdev->ll_broadcast = nii->broadcast; efidev_set_drvdata ( efidev, netdev ); From 3ec2079ce2078ff67c1f857eaf29293586bb5497 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Mar 2018 17:26:49 +0200 Subject: [PATCH 567/591] [time] Add support for the ACPI power management timer Allow the ACPI power management timer to be used if enabled via TIMER_ACPI in config/timer.h. This provides an alternative timer on systems where the standard 8254 PIT is unavailable or unreliable. Signed-off-by: Michael Brown --- src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86/interface/pcbios/acpi_timer.c | 136 +++++++++++++++++++++ src/config/config_timer.c | 3 + src/include/ipxe/acpi.h | 7 ++ 4 files changed, 147 insertions(+) create mode 100644 src/arch/x86/interface/pcbios/acpi_timer.c diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 9d1fed7f6..b0ae1abc2 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 ) #define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 ) #define ERRFILE_rdtsc_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 ) +#define ERRFILE_acpi_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00130000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/arch/x86/interface/pcbios/acpi_timer.c b/src/arch/x86/interface/pcbios/acpi_timer.c new file mode 100644 index 000000000..82e85a034 --- /dev/null +++ b/src/arch/x86/interface/pcbios/acpi_timer.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * ACPI power management timer + * + */ + +/** ACPI timer frequency (fixed 3.579545MHz) */ +#define ACPI_TIMER_HZ 3579545 + +/** ACPI timer mask + * + * Timers may be implemented as either 24-bit or 32-bit counters. We + * simplify the code by pessimistically assuming that the timer has + * only 24 bits. + */ +#define ACPI_TIMER_MASK 0x00ffffffUL + +/** Power management timer register address */ +static unsigned int pm_tmr; + +struct timer acpi_timer __timer ( TIMER_PREFERRED ); + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long acpi_currticks ( void ) { + static unsigned long offset; + static uint32_t prev; + uint32_t now; + + /* Read timer and account for wraparound */ + now = ( inl ( pm_tmr ) & ACPI_TIMER_MASK ); + if ( now < prev ) { + offset += ( ( ACPI_TIMER_MASK + 1 ) / + ( ACPI_TIMER_HZ / TICKS_PER_SEC ) ); + } + prev = now; + + /* Convert to timer ticks */ + return ( offset + ( now / ( ACPI_TIMER_HZ / TICKS_PER_SEC ) ) ); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void acpi_udelay ( unsigned long usecs ) { + uint32_t start; + uint32_t elapsed; + uint32_t threshold; + + /* Delay until a suitable number of ticks have elapsed. We do + * not need to allow for multiple wraparound, since the + * wraparound period for a 24-bit timer at 3.579545MHz is + * around 4700000us. + */ + start = inl ( pm_tmr ); + threshold = ( ( usecs * ACPI_TIMER_HZ ) / 1000000 ); + do { + elapsed = ( ( inl ( pm_tmr ) - start ) & ACPI_TIMER_MASK ); + } while ( elapsed < threshold ); +} + +/** + * Probe ACPI power management timer + * + * @ret rc Return status code + */ +static int acpi_timer_probe ( void ) { + struct acpi_fadt fadtab; + userptr_t fadt; + unsigned int pm_tmr_blk; + + /* Locate FADT */ + fadt = acpi_find ( FADT_SIGNATURE, 0 ); + if ( ! fadt ) { + DBGC ( &acpi_timer, "ACPI could not find FADT\n" ); + return -ENOENT; + } + + /* Read FADT */ + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + pm_tmr_blk = le32_to_cpu ( fadtab.pm_tmr_blk ); + if ( ! pm_tmr_blk ) { + DBGC ( &acpi_timer, "ACPI has no timer\n" ); + return -ENOENT; + } + + /* Record power management timer register address */ + pm_tmr = ( pm_tmr_blk + ACPI_PM_TMR ); + + return 0; +} + +/** ACPI timer */ +struct timer acpi_timer __timer ( TIMER_PREFERRED ) = { + .name = "acpi", + .probe = acpi_timer_probe, + .currticks = acpi_currticks, + .udelay = acpi_udelay, +}; diff --git a/src/config/config_timer.c b/src/config/config_timer.c index a462970c2..d53c39939 100644 --- a/src/config/config_timer.c +++ b/src/config/config_timer.c @@ -46,3 +46,6 @@ REQUIRE_OBJECT ( efi_timer ); #ifdef TIMER_LINUX REQUIRE_OBJECT ( linux_timer ); #endif +#ifdef TIMER_ACPI +REQUIRE_OBJECT ( acpi_timer ); +#endif diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index 68131e73a..78f402530 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -119,6 +119,10 @@ struct acpi_fadt { uint32_t pm1a_cnt_blk; /** PM1b Control Register Block */ uint32_t pm1b_cnt_blk; + /** PM2 Control Register Block */ + uint32_t pm2_cnt_blk; + /** PM Timer Control Register Block */ + uint32_t pm_tmr_blk; } __attribute__ (( packed )); /** ACPI PM1 Control Register (within PM1a_CNT_BLK or PM1A_CNT_BLK) */ @@ -126,6 +130,9 @@ struct acpi_fadt { #define ACPI_PM1_CNT_SLP_TYP(x) ( (x) << 10 ) /**< Sleep type */ #define ACPI_PM1_CNT_SLP_EN ( 1 << 13 ) /**< Sleep enable */ +/** ACPI PM Timer Register (within PM_TMR_BLK) */ +#define ACPI_PM_TMR 0 + /** Differentiated System Description Table (DSDT) signature */ #define DSDT_SIGNATURE ACPI_SIGNATURE ( 'D', 'S', 'D', 'T' ) From d5d4bf8870ccb68b3b897d2ee88079dc06e9dd48 Mon Sep 17 00:00:00 2001 From: Ameer Mahagneh Date: Tue, 20 Mar 2018 17:55:04 +0200 Subject: [PATCH 568/591] [golan] Set log_max_qp to 1 This is required to work around a bug in some firmware versions. Signed-off-by: Ameer Mahagneh Signed-off-by: Michael Brown --- src/drivers/infiniband/CIB_PRM.h | 5 ++--- src/drivers/infiniband/golan.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/drivers/infiniband/CIB_PRM.h b/src/drivers/infiniband/CIB_PRM.h index 6d07c0151..d578f9b04 100755 --- a/src/drivers/infiniband/CIB_PRM.h +++ b/src/drivers/infiniband/CIB_PRM.h @@ -33,7 +33,7 @@ typedef uint16_t __be16; #define GOLAN_PCI_CMD_XPORT 7 #define CMD_OWNER_HW 0x1 - +#define GOLAN_LOG_MAX_QP 0x1 #define IB_NUM_PKEYS 0x20 struct health_buffer { @@ -229,8 +229,7 @@ struct golan_hca_cap { u8 rsvd1[16]; u8 log_max_srq_sz; u8 log_max_qp_sz; - u8 rsvd2; - u8 log_max_qp; + __be16 log_max_qp; u8 log_max_strq_sz; u8 log_max_srqs; u8 rsvd4[2]; diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 1858da7cf..18ebfb1e9 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -363,7 +363,7 @@ static inline int golan_set_hca_cap(struct golan *golan) DBGC( golan , "%s caps.log_pg_sz = %d\n", __FUNCTION__, golan->caps.log_pg_sz); DBGC( golan , "%s caps.log_uar_sz = %d\n", __FUNCTION__, be32_to_cpu(golan->caps.uar_page_sz)); golan->caps.uar_page_sz = 0; - + golan->caps.log_max_qp = GOLAN_LOG_MAX_QP; memcpy(((struct golan_hca_cap *)GET_INBOX(golan, GEN_MBOX)), &(golan->caps), From 0d35411f88dd37dda755d52b4549337f8299c698 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Mar 2018 20:42:39 +0200 Subject: [PATCH 569/591] [rng] Use fixed-point calculations for min-entropy quantities We currently perform various min-entropy calculations using build-time floating-point arithmetic. No floating-point code ends up in the final binary, since the results are eventually converted to integers and asserted to be compile-time constants. Though this mechanism is undoubtedly cute, it inhibits us from using "-mno-sse" to prevent the use of SSE registers by the compiler. Fix by using fixed-point arithmetic instead. Signed-off-by: Michael Brown --- src/arch/x86/include/ipxe/rtc_entropy.h | 4 ++-- src/crypto/entropy.c | 5 +++-- src/include/ipxe/efi/efi_entropy.h | 4 ++-- src/include/ipxe/entropy.h | 26 ++++++++++++++++++++++--- src/include/ipxe/linux/linux_entropy.h | 4 ++-- src/include/ipxe/null_entropy.h | 4 ++-- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/arch/x86/include/ipxe/rtc_entropy.h b/src/arch/x86/include/ipxe/rtc_entropy.h index e214745d0..581abcd3e 100644 --- a/src/arch/x86/include/ipxe/rtc_entropy.h +++ b/src/arch/x86/include/ipxe/rtc_entropy.h @@ -22,7 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @ret min_entropy min-entropy of each sample */ -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) { /* The min-entropy has been measured on several platforms @@ -38,7 +38,7 @@ ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) { * safety margin to allow for some potential non-independence * of samples. */ - return 1.3; + return MIN_ENTROPY ( 1.3 ); } extern uint8_t rtc_sample ( void ); diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c index 5acbc0258..ced6fd921 100644 --- a/src/crypto/entropy.c +++ b/src/crypto/entropy.c @@ -70,7 +70,8 @@ repetition_count_cutoff ( void ) { * where W is set at 2^(-30) (in ANS X9.82 Part 2 (October * 2011 Draft) Section 8.5.2.1.3.1). */ - max_repetitions = ( 1 + ( 30 / min_entropy_per_sample() ) ); + max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) / + min_entropy_per_sample() ) ); /* Round up to a whole number of repetitions. We don't have * the ceil() function available, so do the rounding by hand. @@ -237,7 +238,7 @@ adaptive_proportion_cutoff ( void ) { /* Look up cutoff value in cutoff table */ n = ADAPTIVE_PROPORTION_WINDOW_SIZE; - h = min_entropy_per_sample(); + h = ( min_entropy_per_sample() / MIN_ENTROPY_SCALE ); cutoff = adaptive_proportion_cutoff_lookup ( n, h ); /* Fail unless cutoff value is a build-time constant */ diff --git a/src/include/ipxe/efi/efi_entropy.h b/src/include/ipxe/efi/efi_entropy.h index 39a667355..5b16fd7f9 100644 --- a/src/include/ipxe/efi/efi_entropy.h +++ b/src/include/ipxe/efi/efi_entropy.h @@ -22,14 +22,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @ret min_entropy min-entropy of each sample */ -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) { /* We use essentially the same mechanism as for the BIOS * RTC-based entropy source, and so assume the same * min-entropy per sample. */ - return 1.3; + return MIN_ENTROPY ( 1.3 ); } #endif /* _IPXE_EFI_ENTROPY_H */ diff --git a/src/include/ipxe/entropy.h b/src/include/ipxe/entropy.h index beeb3abfa..d2e3ce501 100644 --- a/src/include/ipxe/entropy.h +++ b/src/include/ipxe/entropy.h @@ -52,6 +52,25 @@ typedef uint8_t noise_sample_t; /** An entropy sample */ typedef uint8_t entropy_sample_t; +/** An amount of min-entropy + * + * Expressed as a fixed-point quantity in order to avoid floating + * point calculations. + */ +typedef unsigned int min_entropy_t; + +/** Fixed-point scale for min-entropy amounts */ +#define MIN_ENTROPY_SCALE ( 1 << 16 ) + +/** + * Construct a min-entropy fixed-point value + * + * @v bits min-entropy in bits + * @ret min_entropy min-entropy as a fixed-point value + */ +#define MIN_ENTROPY( bits ) \ + ( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) ) + /* Include all architecture-independent entropy API headers */ #include #include @@ -87,7 +106,7 @@ void entropy_disable ( void ); * * This must be a compile-time constant. */ -double min_entropy_per_sample ( void ); +min_entropy_t min_entropy_per_sample ( void ); /** * Get noise sample @@ -142,7 +161,7 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, /* Sanity checks */ linker_assert ( ( min_entropy_per_sample() <= - ( 8 * sizeof ( noise_sample_t ) ) ), + MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ), min_entropy_per_sample_is_impossibly_high ); linker_assert ( ( min_entropy_bits <= ( 8 * max_len ) ), entropy_buffer_too_small ); @@ -151,7 +170,8 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 ); /* Calculate number of samples required to contain sufficient entropy */ - min_samples = ( ( min_entropy_bits * 1.0 ) / min_entropy_per_sample() ); + min_samples = ( MIN_ENTROPY ( min_entropy_bits ) / + min_entropy_per_sample() ); /* Round up to a whole number of samples. We don't have the * ceil() function available, so do the rounding by hand. diff --git a/src/include/ipxe/linux/linux_entropy.h b/src/include/ipxe/linux/linux_entropy.h index afef6fe19..ea8c1f16c 100644 --- a/src/include/ipxe/linux/linux_entropy.h +++ b/src/include/ipxe/linux/linux_entropy.h @@ -20,7 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @ret min_entropy min-entropy of each sample */ -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { /* linux_get_noise() reads a single byte from /dev/random, @@ -28,7 +28,7 @@ ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { * entropy is available. We therefore assume that each sample * contains exactly 8 bits of entropy. */ - return 8.0; + return MIN_ENTROPY ( 8.0 ); } #endif /* _IPXE_LINUX_ENTROPY_H */ diff --git a/src/include/ipxe/null_entropy.h b/src/include/ipxe/null_entropy.h index 91adefa69..5a6bb6218 100644 --- a/src/include/ipxe/null_entropy.h +++ b/src/include/ipxe/null_entropy.h @@ -30,14 +30,14 @@ ENTROPY_INLINE ( null, entropy_disable ) ( void ) { /* Do nothing */ } -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( null, min_entropy_per_sample ) ( void ) { /* Actual amount of min-entropy is zero. To avoid * division-by-zero errors and to allow compilation of * entropy-consuming code, pretend to have 1 bit of entropy in * each sample. */ - return 1.0; + return MIN_ENTROPY ( 1.0 ); } static inline __always_inline int From 1df3b53051e2baa99abd00b2fe71ccfa6672331e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Mar 2018 21:34:46 +0200 Subject: [PATCH 570/591] [build] Prevent use of MMX and SSE registers The existence of MMX and SSE is required by the System V x86_64 ABI and so is assumed by gcc, but these registers are not preserved by our own interrupt handlers and are unlikely to be preserved by other context switch handlers in a boot firmware environment. Explicitly prevent gcc from using MMX or SSE registers to avoid potential problems due to silent register corruption. We must remove the %xmm0-%xmm5 clobbers from the x86_64 version of hv_call() since otherwise gcc will complain about unknown register names. Theoretically, we should probably add code to explicitly preserve the %xmm0-%xmm5 registers across a hypercall, in order to guarantee to external code that these registers remain unchanged. In practice this is difficult since SSE registers are disabled by default: for background information see commits 71560d1 ("[librm] Preserve FPU, MMX and SSE state across calls to virt_call()") and dd9a14d ("[librm] Conditionalize the workaround for the Tivoli VMM's SSE garbling"). Signed-off-by: Michael Brown --- src/arch/x86_64/Makefile | 4 ++++ src/arch/x86_64/include/bits/hyperv.h | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/Makefile b/src/arch/x86_64/Makefile index 246905cdb..b3064b752 100644 --- a/src/arch/x86_64/Makefile +++ b/src/arch/x86_64/Makefile @@ -13,6 +13,10 @@ CFLAGS += -m64 ASFLAGS += --64 LDFLAGS += -m elf_x86_64 +# Prevent use of MMX and SSE registers +# +CFLAGS += -mno-mmx -mno-sse + # EFI requires -fshort-wchar, and nothing else currently uses wchar_t # CFLAGS += -fshort-wchar diff --git a/src/arch/x86_64/include/bits/hyperv.h b/src/arch/x86_64/include/bits/hyperv.h index 975b1eee0..fa8bb3f93 100644 --- a/src/arch/x86_64/include/bits/hyperv.h +++ b/src/arch/x86_64/include/bits/hyperv.h @@ -44,8 +44,7 @@ hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, : "=a" ( result ), "+r" ( rcx ), "+r" ( rdx ), "+r" ( r8 ) : "m" ( hypercall ) - : "r9", "r10", "r11", "xmm0", "xmm1", "xmm2", - "xmm3", "xmm4", "xmm5" ); + : "r9", "r10", "r11" ); return result; } From 0600ffeb3051a3ccbd56a4d6999d3eb55bc886df Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 21 Mar 2018 10:28:05 +0200 Subject: [PATCH 571/591] [undi] Treat invalid IRQ numbers as non-fatal errors If the underlying PXE stack reports an invalid IRQ number (above IRQ_MAX), treat this as equivalent to an empty IRQ number and fall back to using polling mode. Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undinet.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index 781911814..e8ec772ef 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -938,10 +938,9 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN ); undinic->irq = undi_info.IntNumber; if ( undinic->irq > IRQ_MAX ) { - DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n", + DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n", undinic, undinic->irq ); - rc = -EINVAL; - goto err_bad_irq; + undinic->irq = 0; } DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n", undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq ); @@ -984,7 +983,6 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) { err_register: err_undi_get_iface_info: - err_bad_irq: err_undi_get_information: err_undi_initialize: /* Shut down UNDI stack */ From 6149e0af3ca21d8ea2a903dd555e6e5c4b6a630a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 21 Mar 2018 14:39:18 +0200 Subject: [PATCH 572/591] [librm] Provide symbols for inline code placed into other sections Provide symbols constructed from the object name and line number for code fragments placed into alternative sections, such as inline REAL_CODE() assembly placed into .text16. This simplifies the debugging task of finding the source code corresponding to a given instruction pointer. Note that we cannot use __FUNCTION__ since it is not a preprocessor macro and so cannot be concatenated with string literals. Signed-off-by: Michael Brown --- src/arch/x86/include/librm.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 597c65e6a..6bad9c42b 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -254,9 +254,13 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); #define CODE_DEFAULT ".code32" #endif +/* LINE_SYMBOL: declare a symbol for the current source code line */ +#define LINE_SYMBOL _S2 ( OBJECT ) "__line_" _S2 ( __LINE__ ) ":" + /* TEXT16_CODE: declare a fragment of code that resides in .text16 */ #define TEXT16_CODE( asm_code_str ) \ ".section \".text16\", \"ax\", @progbits\n\t" \ + "\n" LINE_SYMBOL "\n\t" \ ".code16\n\t" \ asm_code_str "\n\t" \ CODE_DEFAULT "\n\t" \ @@ -276,6 +280,7 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); "push $1f\n\t" \ "call phys_call\n\t" \ ".section \".text.phys\", \"ax\", @progbits\n\t"\ + "\n" LINE_SYMBOL "\n\t" \ ".code32\n\t" \ "\n1:\n\t" \ asm_code_str \ From bc85368cdd311fe68ffcf251e7e8e90c14f8a9dc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 21 Mar 2018 16:47:33 +0200 Subject: [PATCH 573/591] [librm] Ensure that inline code symbols are unique Commit 6149e0a ("[librm] Provide symbols for inline code placed into other sections") may cause build failures due to duplicate label names if the compiler chooses to duplicate inline assembly code. Fix by using the "%=" special format string to include a guaranteed-unique number within the label name. The "%=" will be expanded only if constraints exist for the inline assembly. This fix therefore requires that all REAL_CODE() fragments use a (possibly empty) constraint list. Signed-off-by: Michael Brown --- src/arch/x86/core/dumpregs.c | 2 +- src/arch/x86/include/librm.h | 2 +- src/arch/x86/interface/pcbios/bios_console.c | 4 ++-- src/arch/x86/interface/pcbios/bios_reboot.c | 2 +- src/arch/x86/interface/syslinux/comboot_call.c | 6 +++--- src/arch/x86/transitions/librm_test.c | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/arch/x86/core/dumpregs.c b/src/arch/x86/core/dumpregs.c index 37d62a7b6..a5108ea14 100644 --- a/src/arch/x86/core/dumpregs.c +++ b/src/arch/x86/core/dumpregs.c @@ -7,7 +7,7 @@ void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { TEXT16_CODE ( ".globl dump_regs\n\t" "\ndump_regs:\n\t" VIRT_CALL ( _dump_regs ) - "ret\n\t" ) ); + "ret\n\t" ) : ); printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 6bad9c42b..5196d390f 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -255,7 +255,7 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); #endif /* LINE_SYMBOL: declare a symbol for the current source code line */ -#define LINE_SYMBOL _S2 ( OBJECT ) "__line_" _S2 ( __LINE__ ) ":" +#define LINE_SYMBOL _S2 ( OBJECT ) "__line_" _S2 ( __LINE__ ) "__%=:" /* TEXT16_CODE: declare a fragment of code that resides in .text16 */ #define TEXT16_CODE( asm_code_str ) \ diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 81e3a7d7e..08fa6d036 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -521,12 +521,12 @@ static void bios_inject_startup ( void ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint16_wrapper:\n\t" "pushfw\n\t" - "cmpb $0, %cs:bios_inject_lock\n\t" + "cmpb $0, %%cs:bios_inject_lock\n\t" "jnz 1f\n\t" VIRT_CALL ( bios_inject ) "\n1:\n\t" "popfw\n\t" - "ljmp *%cs:int16_vector\n\t" ) ); + "ljmp *%%cs:int16_vector\n\t" ) : ); /* Hook INT 16 */ hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ), diff --git a/src/arch/x86/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c index c6c5a5a91..071173f19 100644 --- a/src/arch/x86/interface/pcbios/bios_reboot.c +++ b/src/arch/x86/interface/pcbios/bios_reboot.c @@ -48,7 +48,7 @@ static void bios_reboot ( int warm ) { put_real ( flag, BDA_SEG, BDA_REBOOT ); /* Jump to system reset vector */ - __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) ); + __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : ); } /** diff --git a/src/arch/x86/interface/syslinux/comboot_call.c b/src/arch/x86/interface/syslinux/comboot_call.c index 2f5c252c1..e70f200e3 100644 --- a/src/arch/x86/interface/syslinux/comboot_call.c +++ b/src/arch/x86/interface/syslinux/comboot_call.c @@ -663,7 +663,7 @@ void hook_comboot_interrupts ( ) { VIRT_CALL ( int20 ) "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) ); + "iret\n\t" ) : ); hook_bios_interrupt ( 0x20, ( intptr_t ) int20_wrapper, &int20_vector ); @@ -672,7 +672,7 @@ void hook_comboot_interrupts ( ) { VIRT_CALL ( int21 ) "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) ); + "iret\n\t" ) : ); hook_bios_interrupt ( 0x21, ( intptr_t ) int21_wrapper, &int21_vector ); @@ -681,7 +681,7 @@ void hook_comboot_interrupts ( ) { VIRT_CALL ( int22 ) "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) ); + "iret\n\t" ) : ); hook_bios_interrupt ( 0x22, ( intptr_t ) int22_wrapper, &int22_vector ); } diff --git a/src/arch/x86/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c index ba4254fe4..77cf8022c 100644 --- a/src/arch/x86/transitions/librm_test.c +++ b/src/arch/x86/transitions/librm_test.c @@ -97,7 +97,7 @@ static void librm_test_exec ( void ) { /* Profile complete real-mode call cycle */ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { profile_start ( &real_call_profiler ); - __asm__ __volatile__ ( REAL_CODE ( "" ) ); + __asm__ __volatile__ ( REAL_CODE ( "" ) : ); profile_stop ( &real_call_profiler ); } From eda9f4db612401e3c7e700d00336cc293376ab56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Smidsr=C3=B8d?= Date: Wed, 21 Mar 2018 17:08:58 +0200 Subject: [PATCH 574/591] [util] Support reversed sort ordering when generating NIC list Signed-off-by: Michael Brown --- src/util/niclist.pl | 50 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/util/niclist.pl b/src/util/niclist.pl index 0600c8232..2668a1c03 100755 --- a/src/util/niclist.pl +++ b/src/util/niclist.pl @@ -19,7 +19,7 @@ use Getopt::Long qw(GetOptions); GetOptions( 'help' => \( my $help = 0 ), 'format=s' => \( my $format = 'text' ), - 'sort=s' => \( my $sort = 'bus,ipxe_driver,ipxe_name' ), + 'sort=s' => \( my $sort = 'bus-,ipxe_driver,ipxe_name' ), 'columns=s' => \( my $columns = 'bus,vendor_id,device_id,' . 'vendor_name,device_name,ipxe_driver,' . 'ipxe_name,ipxe_description,file,legacy_api' @@ -47,26 +47,26 @@ Output formats: Column names (default order): bus, vendor_id, device_id, vendor_name, device_name, ipxe_driver, ipxe_name, ipxe_description, file, legacy_api + +Default sort order (minus at the end means reverse sort): + bus-, ipxe_driver, ipxe_name EOM # Only load runtime requirements if actually in use -given($format) { - when( /csv/ ) { - eval { require Text::CSV; }; - die("Please install Text::CSV CPAN module to use this feature.\n") - if $@; - } - when( /json/ ) { - eval { require JSON; }; - die("Please install JSON CPAN module to use this feature.\n") - if $@; - } - when( /html/ ) { - eval { require HTML::Entities; }; - die("Please install HTML::Entities CPAN module to use this feature.\n") - if $@; - } - default { } +if ( $format =~ /csv/ ) { + eval { require Text::CSV; }; + die("Please install Text::CSV CPAN module to use this feature.\n") + if $@; +} +if ( $format =~ /json/ ) { + eval { require JSON; }; + die("Please install JSON CPAN module to use this feature.\n") + if $@; +} +if ( $format =~ /html/ ) { + eval { require HTML::Entities; }; + die("Please install HTML::Entities CPAN module to use this feature.\n") + if $@; } # Scan source dir and build NIC list @@ -339,8 +339,16 @@ sub sort_ipxe_nic_list { my @sorted_list = @{ $ipxe_nic_list }; while(@sort_column_names) { my $column_name = pop @sort_column_names; - @sorted_list = sort { ( $a->{$column_name} || "" ) cmp ( $b->{$column_name} || "" ) } - @sorted_list; + my $reverse = substr($column_name, -1) eq '-' ? 1 : 0; # use reverse order if last character is minus + $column_name = substr($column_name, 0, -1) if $reverse; # chop of the minus + if ( $reverse ) { + @sorted_list = sort { ( $b->{$column_name} || "" ) cmp ( $a->{$column_name} || "" ) } + @sorted_list; + } + else { + @sorted_list = sort { ( $a->{$column_name} || "" ) cmp ( $b->{$column_name} || "" ) } + @sorted_list; + } } return \@sorted_list; } @@ -359,7 +367,7 @@ sub parse_columns_param { sub is_valid_column { my ($name) = @_; my $valid_column_map = { - map { $_ => 1 } + map { $_ => 1, $_ . "-" => 1 } # also supports keyword with a - suffix qw( bus file legacy_api ipxe_driver ipxe_name ipxe_description From ac4fbd47aea136185e17975bd68c268bf0cc081e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 23 Mar 2018 11:07:29 +0000 Subject: [PATCH 575/591] [tls] Ensure received data list is initialised before calling tls_free() A failure in tls_generate_random() will result in a call to ref_put() before the received data list has been initialised, which will cause free_tls() to attempt to traverse an uninitialised list. Fix by ensuring that all fields referenced by free_tls() are initialised before any of the potential failure paths. Signed-off-by: Michael Brown --- src/net/tls.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index b197c111f..329c6fe00 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -2788,6 +2788,9 @@ int add_tls ( struct interface *xfer, const char *name, tls_clear_cipher ( tls, &tls->rx_cipherspec ); tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); tls->client_random.gmt_unix_time = time ( NULL ); + iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, + sizeof ( tls->rx_header ) ); + INIT_LIST_HEAD ( &tls->rx_data ); if ( ( rc = tls_generate_random ( tls, &tls->client_random.random, ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) { goto err_random; @@ -2797,9 +2800,6 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { goto err_random; } - iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, - sizeof ( tls->rx_header ) ); - INIT_LIST_HEAD ( &tls->rx_data ); /* Start negotiation */ tls_restart ( tls ); From 6be010d9199df96deb3c120c19a5bd4591d9c596 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 24 Mar 2018 21:26:19 +0000 Subject: [PATCH 576/591] [list] Add list_is_first_entry() and list_is_last_entry() Signed-off-by: Michael Brown --- src/include/ipxe/list.h | 22 ++++++++++++++++++++++ src/tests/list_test.c | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/include/ipxe/list.h b/src/include/ipxe/list.h index 274fb64c6..8de254984 100644 --- a/src/include/ipxe/list.h +++ b/src/include/ipxe/list.h @@ -376,6 +376,28 @@ extern void extern_list_splice_tail_init ( struct list_head *list, member ); \ ( ( &prev->member == (head) ) ? NULL : prev ); } ) +/** + * Test if entry is first in a list + * + * @v entry List entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret is_first Entry is first in the list + */ +#define list_is_first_entry( entry, head, member ) \ + ( (head)->next == &(entry)->member ) + +/** + * Test if entry is last in a list + * + * @v entry List entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret is_last Entry is last in the list + */ +#define list_is_last_entry( entry, head, member ) \ + ( (head)->prev == &(entry)->member ) + /** * Iterate over a list * diff --git a/src/tests/list_test.c b/src/tests/list_test.c index f016a32eb..d5b5c65db 100644 --- a/src/tests/list_test.c +++ b/src/tests/list_test.c @@ -419,6 +419,27 @@ static void list_test_exec ( void ) { ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[5] ); ok ( list_next_entry ( &list_tests[1], list, list ) == NULL ); + /* Test list_is_first_entry() and list_is_last_entry() */ + INIT_LIST_HEAD ( list ); + list_add_tail ( &list_tests[4].list, list ); + list_add_tail ( &list_tests[8].list, list ); + list_add_tail ( &list_tests[3].list, list ); + list_add_tail ( &list_tests[6].list, list ); + ok ( list_is_first_entry ( &list_tests[4], list, list ) ); + ok ( ! list_is_first_entry ( &list_tests[8], list, list ) ); + ok ( ! list_is_first_entry ( &list_tests[3], list, list ) ); + ok ( ! list_is_first_entry ( &list_tests[6], list, list ) ); + ok ( ! list_is_last_entry ( &list_tests[4], list, list ) ); + ok ( ! list_is_last_entry ( &list_tests[8], list, list ) ); + ok ( ! list_is_last_entry ( &list_tests[3], list, list ) ); + ok ( list_is_last_entry ( &list_tests[6], list, list ) ); + list_del ( &list_tests[4].list ); + ok ( list_is_first_entry ( &list_tests[8], list, list ) ); + list_del ( &list_tests[8].list ); + list_del ( &list_tests[6].list ); + ok ( list_is_first_entry ( &list_tests[3], list, list ) ); + ok ( list_is_last_entry ( &list_tests[3], list, list ) ); + /* Test list_for_each() */ INIT_LIST_HEAD ( list ); list_add_tail ( &list_tests[6].list, list ); From 4152aff10300408d9b1b932e5e269001cc38cebf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 22 Mar 2018 18:10:46 +0200 Subject: [PATCH 577/591] [tls] Rename tls_session to tls_connection In TLS terminology a session conceptually spans multiple individual connections, and essentially represents the stored cryptographic state (master secret and cipher suite) required to establish communication without going through the certificate and key exchange handshakes. Rename tls_session to tls_connection in order to make the name tls_session available to represent the session state. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 4 +- src/net/tls.c | 242 +++++++++++++++++++++-------------------- 2 files changed, 125 insertions(+), 121 deletions(-) diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 7345fbee4..b1e702e18 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -242,8 +242,8 @@ struct md5_sha1_digest { /** MD5+SHA1 digest size */ #define MD5_SHA1_DIGEST_SIZE sizeof ( struct md5_sha1_digest ) -/** A TLS session */ -struct tls_session { +/** A TLS connection */ +struct tls_connection { /** Reference counter */ struct refcnt refcnt; diff --git a/src/net/tls.c b/src/net/tls.c index 329c6fe00..d28daa43c 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -175,9 +175,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); __einfo_uniqify ( EINFO_EPROTO, 0x01, \ "Illegal protocol version upgrade" ) -static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, +static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ); -static void tls_clear_cipher ( struct tls_session *tls, +static void tls_clear_cipher ( struct tls_connection *tls, struct tls_cipherspec *cipherspec ); /****************************************************************************** @@ -225,12 +225,12 @@ static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) { } /** - * Determine if TLS session is ready for application data + * Determine if TLS connection is ready for application data * - * @v tls TLS session - * @ret is_ready TLS session is ready + * @v tls TLS connection + * @ret is_ready TLS connection is ready */ -static int tls_ready ( struct tls_session *tls ) { +static int tls_ready ( struct tls_connection *tls ) { return ( ( ! is_pending ( &tls->client_negotiation ) ) && ( ! is_pending ( &tls->server_negotiation ) ) ); } @@ -308,13 +308,13 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { */ /** - * Free TLS session + * Free TLS connection * * @v refcnt Reference counter */ static void free_tls ( struct refcnt *refcnt ) { - struct tls_session *tls = - container_of ( refcnt, struct tls_session, refcnt ); + struct tls_connection *tls = + container_of ( refcnt, struct tls_connection, refcnt ); struct io_buffer *iobuf; struct io_buffer *tmp; @@ -335,12 +335,12 @@ static void free_tls ( struct refcnt *refcnt ) { } /** - * Finish with TLS session + * Finish with TLS connection * - * @v tls TLS session + * @v tls TLS connection * @v rc Status code */ -static void tls_close ( struct tls_session *tls, int rc ) { +static void tls_close ( struct tls_connection *tls, int rc ) { /* Remove pending operations, if applicable */ pending_put ( &tls->client_negotiation ); @@ -365,12 +365,12 @@ static void tls_close ( struct tls_session *tls, int rc ) { /** * Generate random data * - * @v tls TLS session + * @v tls TLS connection * @v data Buffer to fill * @v len Length of buffer * @ret rc Return status code */ -static int tls_generate_random ( struct tls_session *tls, +static int tls_generate_random ( struct tls_connection *tls, void *data, size_t len ) { int rc; @@ -407,7 +407,7 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest, /** * Generate secure pseudo-random data using a single hash function * - * @v tls TLS session + * @v tls TLS connection * @v digest Hash function to use * @v secret Secret * @v secret_len Length of secret @@ -415,7 +415,7 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest, * @v out_len Length of output buffer * @v seeds ( data, len ) pairs of seed data, terminated by NULL */ -static void tls_p_hash_va ( struct tls_session *tls, +static void tls_p_hash_va ( struct tls_connection *tls, struct digest_algorithm *digest, void *secret, size_t secret_len, void *out, size_t out_len, @@ -476,15 +476,15 @@ static void tls_p_hash_va ( struct tls_session *tls, /** * Generate secure pseudo-random data * - * @v tls TLS session + * @v tls TLS connection * @v secret Secret * @v secret_len Length of secret * @v out Output buffer * @v out_len Length of output buffer * @v ... ( data, len ) pairs of seed data, terminated by NULL */ -static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len, - void *out, size_t out_len, ... ) { +static void tls_prf ( struct tls_connection *tls, void *secret, + size_t secret_len, void *out, size_t out_len, ... ) { va_list seeds; va_list tmp; size_t subsecret_len; @@ -553,12 +553,12 @@ static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len, /** * Generate master secret * - * @v tls TLS session + * @v tls TLS connection * * The pre-master secret and the client and server random values must * already be known. */ -static void tls_generate_master_secret ( struct tls_session *tls ) { +static void tls_generate_master_secret ( struct tls_connection *tls ) { DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); DBGC_HD ( tls, &tls->pre_master_secret, sizeof ( tls->pre_master_secret ) ); @@ -581,11 +581,11 @@ static void tls_generate_master_secret ( struct tls_session *tls ) { /** * Generate key material * - * @v tls TLS session + * @v tls TLS connection * * The master secret must already be known. */ -static int tls_generate_keys ( struct tls_session *tls ) { +static int tls_generate_keys ( struct tls_connection *tls ) { struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending; struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; size_t hash_size = tx_cipherspec->suite->digest->digestsize; @@ -701,7 +701,7 @@ tls_find_cipher_suite ( unsigned int cipher_suite ) { * * @v cipherspec TLS cipher specification */ -static void tls_clear_cipher ( struct tls_session *tls __unused, +static void tls_clear_cipher ( struct tls_connection *tls __unused, struct tls_cipherspec *cipherspec ) { if ( cipherspec->suite ) { @@ -716,12 +716,12 @@ static void tls_clear_cipher ( struct tls_session *tls __unused, /** * Set cipher suite * - * @v tls TLS session + * @v tls TLS connection * @v cipherspec TLS cipher specification * @v suite Cipher suite * @ret rc Return status code */ -static int tls_set_cipher ( struct tls_session *tls, +static int tls_set_cipher ( struct tls_connection *tls, struct tls_cipherspec *cipherspec, struct tls_cipher_suite *suite ) { struct pubkey_algorithm *pubkey = suite->pubkey; @@ -759,11 +759,11 @@ static int tls_set_cipher ( struct tls_session *tls, /** * Select next cipher suite * - * @v tls TLS session + * @v tls TLS connection * @v cipher_suite Cipher suite specification * @ret rc Return status code */ -static int tls_select_cipher ( struct tls_session *tls, +static int tls_select_cipher ( struct tls_connection *tls, unsigned int cipher_suite ) { struct tls_cipher_suite *suite; int rc; @@ -794,12 +794,12 @@ static int tls_select_cipher ( struct tls_session *tls, /** * Activate next cipher suite * - * @v tls TLS session + * @v tls TLS connection * @v pending Pending cipher specification * @v active Active cipher specification to replace * @ret rc Return status code */ -static int tls_change_cipher ( struct tls_session *tls, +static int tls_change_cipher ( struct tls_connection *tls, struct tls_cipherspec *pending, struct tls_cipherspec *active ) { @@ -858,11 +858,11 @@ tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, /** * Add handshake record to verification hash * - * @v tls TLS session + * @v tls TLS connection * @v data Handshake record * @v len Length of handshake record */ -static void tls_add_handshake ( struct tls_session *tls, +static void tls_add_handshake ( struct tls_connection *tls, const void *data, size_t len ) { digest_update ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx, @@ -874,13 +874,13 @@ static void tls_add_handshake ( struct tls_session *tls, /** * Calculate handshake verification hash * - * @v tls TLS session + * @v tls TLS connection * @v out Output buffer * * Calculates the MD5+SHA1 or SHA256 digest over all handshake * messages seen so far. */ -static void tls_verify_handshake ( struct tls_session *tls, void *out ) { +static void tls_verify_handshake ( struct tls_connection *tls, void *out ) { struct digest_algorithm *digest = tls->handshake_digest; uint8_t ctx[ digest->ctxsize ]; @@ -898,9 +898,9 @@ static void tls_verify_handshake ( struct tls_session *tls, void *out ) { /** * Restart negotiation * - * @v tls TLS session + * @v tls TLS connection */ -static void tls_restart ( struct tls_session *tls ) { +static void tls_restart ( struct tls_connection *tls ) { /* Sanity check */ assert ( ! tls->tx_pending ); @@ -922,21 +922,21 @@ static void tls_restart ( struct tls_session *tls ) { /** * Resume TX state machine * - * @v tls TLS session + * @v tls TLS connection */ -static void tls_tx_resume ( struct tls_session *tls ) { +static void tls_tx_resume ( struct tls_connection *tls ) { process_add ( &tls->process ); } /** * Transmit Handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_send_handshake ( struct tls_session *tls, +static int tls_send_handshake ( struct tls_connection *tls, void *data, size_t len ) { /* Add to handshake digest */ @@ -949,10 +949,10 @@ static int tls_send_handshake ( struct tls_session *tls, /** * Transmit Client Hello record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_client_hello ( struct tls_session *tls ) { +static int tls_send_client_hello ( struct tls_connection *tls ) { struct { uint32_t type_length; uint16_t version; @@ -1049,10 +1049,10 @@ static int tls_send_client_hello ( struct tls_session *tls ) { /** * Transmit Certificate record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_certificate ( struct tls_session *tls ) { +static int tls_send_certificate ( struct tls_connection *tls ) { struct { uint32_t type_length; tls24_t length; @@ -1095,10 +1095,10 @@ static int tls_send_certificate ( struct tls_session *tls ) { /** * Transmit Client Key Exchange record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_client_key_exchange ( struct tls_session *tls ) { +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 ); @@ -1139,10 +1139,10 @@ static int tls_send_client_key_exchange ( struct tls_session *tls ) { /** * Transmit Certificate Verify record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_certificate_verify ( struct tls_session *tls ) { +static int tls_send_certificate_verify ( struct tls_connection *tls ) { struct digest_algorithm *digest = tls->handshake_digest; struct x509_certificate *cert = tls->cert; struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey; @@ -1229,10 +1229,10 @@ static int tls_send_certificate_verify ( struct tls_session *tls ) { /** * Transmit Change Cipher record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_change_cipher ( struct tls_session *tls ) { +static int tls_send_change_cipher ( struct tls_connection *tls ) { static const uint8_t change_cipher[1] = { 1 }; return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER, change_cipher, sizeof ( change_cipher ) ); @@ -1241,10 +1241,10 @@ static int tls_send_change_cipher ( struct tls_session *tls ) { /** * Transmit Finished record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_finished ( struct tls_session *tls ) { +static int tls_send_finished ( struct tls_connection *tls ) { struct digest_algorithm *digest = tls->handshake_digest; struct { uint32_t type_length; @@ -1281,12 +1281,12 @@ static int tls_send_finished ( struct tls_session *tls ) { /** * Receive new Change Cipher record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_new_change_cipher ( struct tls_session *tls, +static int tls_new_change_cipher ( struct tls_connection *tls, const void *data, size_t len ) { int rc; @@ -1310,12 +1310,12 @@ static int tls_new_change_cipher ( struct tls_session *tls, /** * Receive new Alert record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_new_alert ( struct tls_session *tls, const void *data, +static int tls_new_alert ( struct tls_connection *tls, const void *data, size_t len ) { const struct { uint8_t level; @@ -1349,12 +1349,12 @@ static int tls_new_alert ( struct tls_session *tls, const void *data, /** * Receive new Hello Request handshake record * - * @v tls TLS session + * @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_hello_request ( struct tls_session *tls, +static int tls_new_hello_request ( struct tls_connection *tls, const void *data __unused, size_t len __unused ) { @@ -1380,12 +1380,12 @@ static int tls_new_hello_request ( struct tls_session *tls, /** * Receive new Server Hello handshake record * - * @v tls TLS session + * @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_hello ( struct tls_session *tls, +static int tls_new_server_hello ( struct tls_connection *tls, const void *data, size_t len ) { const struct { uint16_t version; @@ -1548,12 +1548,12 @@ static int tls_new_server_hello ( struct tls_session *tls, /** * Parse certificate chain * - * @v tls TLS session + * @v tls TLS connection * @v data Certificate chain * @v len Length of certificate chain * @ret rc Return status code */ -static int tls_parse_chain ( struct tls_session *tls, +static int tls_parse_chain ( struct tls_connection *tls, const void *data, size_t len ) { size_t remaining = len; int rc; @@ -1626,12 +1626,12 @@ static int tls_parse_chain ( struct tls_session *tls, /** * Receive new Certificate handshake record * - * @v tls TLS session + * @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_certificate ( struct tls_session *tls, +static int tls_new_certificate ( struct tls_connection *tls, const void *data, size_t len ) { const struct { tls24_t length; @@ -1666,12 +1666,12 @@ static int tls_new_certificate ( struct tls_session *tls, /** * Receive new Certificate Request handshake record * - * @v tls TLS session + * @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_certificate_request ( struct tls_session *tls, +static int tls_new_certificate_request ( struct tls_connection *tls, const void *data __unused, size_t len __unused ) { @@ -1699,12 +1699,12 @@ static int tls_new_certificate_request ( struct tls_session *tls, /** * Receive new Server Hello Done handshake record * - * @v tls TLS session + * @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_hello_done ( struct tls_session *tls, +static int tls_new_server_hello_done ( struct tls_connection *tls, const void *data, size_t len ) { const struct { char next[0]; @@ -1732,12 +1732,12 @@ static int tls_new_server_hello_done ( struct tls_session *tls, /** * Receive new Finished handshake record * - * @v tls TLS session + * @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_finished ( struct tls_session *tls, +static int tls_new_finished ( struct tls_connection *tls, const void *data, size_t len ) { struct digest_algorithm *digest = tls->handshake_digest; const struct { @@ -1776,12 +1776,12 @@ static int tls_new_finished ( struct tls_session *tls, /** * Receive new Handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_new_handshake ( struct tls_session *tls, +static int tls_new_handshake ( struct tls_connection *tls, const void *data, size_t len ) { size_t remaining = len; int rc; @@ -1864,15 +1864,15 @@ static int tls_new_handshake ( struct tls_session *tls, /** * Receive new record * - * @v tls TLS session + * @v tls TLS connection * @v type Record type * @v rx_data List of received data buffers * @ret rc Return status code */ -static int tls_new_record ( struct tls_session *tls, unsigned int type, +static int tls_new_record ( struct tls_connection *tls, unsigned int type, struct list_head *rx_data ) { struct io_buffer *iobuf; - int ( * handler ) ( struct tls_session *tls, const void *data, + int ( * handler ) ( struct tls_connection *tls, const void *data, size_t len ); int rc; @@ -2010,16 +2010,16 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, /** * Allocate and assemble stream-ciphered record from data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @ret data Data * @ret len Length of data * @ret digest MAC digest * @ret plaintext_len Length of plaintext record * @ret plaintext Allocated plaintext record */ -static void * __malloc tls_assemble_stream ( struct tls_session *tls, - const void *data, size_t len, - void *digest, size_t *plaintext_len ) { +static void * __malloc +tls_assemble_stream ( struct tls_connection *tls, const void *data, size_t len, + void *digest, size_t *plaintext_len ) { size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize; void *plaintext; void *content; @@ -2045,14 +2045,14 @@ static void * __malloc tls_assemble_stream ( struct tls_session *tls, /** * Allocate and assemble block-ciphered record from data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @ret data Data * @ret len Length of data * @ret digest MAC digest * @ret plaintext_len Length of plaintext record * @ret plaintext Allocated plaintext record */ -static void * tls_assemble_block ( struct tls_session *tls, +static void * tls_assemble_block ( struct tls_connection *tls, const void *data, size_t len, void *digest, size_t *plaintext_len ) { size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize; @@ -2093,13 +2093,13 @@ static void * tls_assemble_block ( struct tls_session *tls, /** * Send plaintext record * - * @v tls TLS session + * @v tls TLS connection * @v type Record type * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, +static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ) { struct tls_header plaintext_tlshdr; struct tls_header *tlshdr; @@ -2185,12 +2185,12 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, /** * Split stream-ciphered record into data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @v rx_data List of received data buffers * @v mac MAC to fill in * @ret rc Return status code */ -static int tls_split_stream ( struct tls_session *tls, +static int tls_split_stream ( struct tls_connection *tls, struct list_head *rx_data, void **mac ) { size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; struct io_buffer *iobuf; @@ -2212,12 +2212,12 @@ static int tls_split_stream ( struct tls_session *tls, /** * Split block-ciphered record into data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @v rx_data List of received data buffers * @v mac MAC to fill in * @ret rc Return status code */ -static int tls_split_block ( struct tls_session *tls, +static int tls_split_block ( struct tls_connection *tls, struct list_head *rx_data, void **mac ) { size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; struct io_buffer *iobuf; @@ -2270,12 +2270,12 @@ static int tls_split_block ( struct tls_session *tls, /** * Receive new ciphertext record * - * @v tls TLS session + * @v tls TLS connection * @v tlshdr Record header * @v rx_data List of received data buffers * @ret rc Return status code */ -static int tls_new_ciphertext ( struct tls_session *tls, +static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_header *tlshdr, struct list_head *rx_data ) { struct tls_header plaintext_tlshdr; @@ -2343,10 +2343,10 @@ static int tls_new_ciphertext ( struct tls_session *tls, /** * Check flow control window * - * @v tls TLS session + * @v tls TLS connection * @ret len Length of window */ -static size_t tls_plainstream_window ( struct tls_session *tls ) { +static size_t tls_plainstream_window ( struct tls_connection *tls ) { /* Block window unless we are ready to accept data */ if ( ! tls_ready ( tls ) ) @@ -2358,12 +2358,12 @@ static size_t tls_plainstream_window ( struct tls_session *tls ) { /** * Deliver datagram as raw data * - * @v tls TLS session + * @v tls TLS connection * @v iobuf I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ -static int tls_plainstream_deliver ( struct tls_session *tls, +static int tls_plainstream_deliver ( struct tls_connection *tls, struct io_buffer *iobuf, struct xfer_metadata *meta __unused ) { int rc; @@ -2385,14 +2385,16 @@ static int tls_plainstream_deliver ( struct tls_session *tls, /** TLS plaintext stream interface operations */ static struct interface_operation tls_plainstream_ops[] = { - INTF_OP ( xfer_deliver, struct tls_session *, tls_plainstream_deliver ), - INTF_OP ( xfer_window, struct tls_session *, tls_plainstream_window ), - INTF_OP ( intf_close, struct tls_session *, tls_close ), + INTF_OP ( xfer_deliver, struct tls_connection *, + tls_plainstream_deliver ), + INTF_OP ( xfer_window, struct tls_connection *, + tls_plainstream_window ), + INTF_OP ( intf_close, struct tls_connection *, tls_close ), }; /** TLS plaintext stream interface descriptor */ static struct interface_descriptor tls_plainstream_desc = - INTF_DESC_PASSTHRU ( struct tls_session, plainstream, + INTF_DESC_PASSTHRU ( struct tls_connection, plainstream, tls_plainstream_ops, cipherstream ); /****************************************************************************** @@ -2405,10 +2407,10 @@ static struct interface_descriptor tls_plainstream_desc = /** * Handle received TLS header * - * @v tls TLS session + * @v tls TLS connection * @ret rc Returned status code */ -static int tls_newdata_process_header ( struct tls_session *tls ) { +static int tls_newdata_process_header ( struct tls_connection *tls ) { size_t data_len = ntohs ( tls->rx_header.length ); size_t remaining = data_len; size_t frag_len; @@ -2470,10 +2472,10 @@ static int tls_newdata_process_header ( struct tls_session *tls ) { /** * Handle received TLS data payload * - * @v tls TLS session + * @v tls TLS connection * @ret rc Returned status code */ -static int tls_newdata_process_data ( struct tls_session *tls ) { +static int tls_newdata_process_data ( struct tls_connection *tls ) { struct io_buffer *iobuf; int rc; @@ -2506,10 +2508,10 @@ static int tls_newdata_process_data ( struct tls_session *tls ) { /** * Check flow control window * - * @v tls TLS session + * @v tls TLS connection * @ret len Length of window */ -static size_t tls_cipherstream_window ( struct tls_session *tls ) { +static size_t tls_cipherstream_window ( struct tls_connection *tls ) { /* Open window until we are ready to accept data */ if ( ! tls_ready ( tls ) ) @@ -2521,16 +2523,16 @@ static size_t tls_cipherstream_window ( struct tls_session *tls ) { /** * Receive new ciphertext * - * @v tls TLS session + * @v tls TLS connection * @v iobuf I/O buffer * @v meta Data transfer metadat * @ret rc Return status code */ -static int tls_cipherstream_deliver ( struct tls_session *tls, +static int tls_cipherstream_deliver ( struct tls_connection *tls, struct io_buffer *iobuf, struct xfer_metadata *xfer __unused ) { size_t frag_len; - int ( * process ) ( struct tls_session *tls ); + int ( * process ) ( struct tls_connection *tls ); struct io_buffer *dest; int rc; @@ -2578,16 +2580,18 @@ static int tls_cipherstream_deliver ( struct tls_session *tls, /** TLS ciphertext stream interface operations */ static struct interface_operation tls_cipherstream_ops[] = { - INTF_OP ( xfer_deliver, struct tls_session *, + INTF_OP ( xfer_deliver, struct tls_connection *, tls_cipherstream_deliver ), - INTF_OP ( xfer_window, struct tls_session *, tls_cipherstream_window ), - INTF_OP ( xfer_window_changed, struct tls_session *, tls_tx_resume ), - INTF_OP ( intf_close, struct tls_session *, tls_close ), + INTF_OP ( xfer_window, struct tls_connection *, + tls_cipherstream_window ), + INTF_OP ( xfer_window_changed, struct tls_connection *, + tls_tx_resume ), + INTF_OP ( intf_close, struct tls_connection *, tls_close ), }; /** TLS ciphertext stream interface descriptor */ static struct interface_descriptor tls_cipherstream_desc = - INTF_DESC_PASSTHRU ( struct tls_session, cipherstream, + INTF_DESC_PASSTHRU ( struct tls_connection, cipherstream, tls_cipherstream_ops, plainstream ); /****************************************************************************** @@ -2600,10 +2604,10 @@ static struct interface_descriptor tls_cipherstream_desc = /** * Handle certificate validation completion * - * @v tls TLS session + * @v tls TLS connection * @v rc Reason for completion */ -static void tls_validator_done ( struct tls_session *tls, int rc ) { +static void tls_validator_done ( struct tls_connection *tls, int rc ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; @@ -2658,12 +2662,12 @@ static void tls_validator_done ( struct tls_session *tls, int rc ) { /** TLS certificate validator interface operations */ static struct interface_operation tls_validator_ops[] = { - INTF_OP ( intf_close, struct tls_session *, tls_validator_done ), + INTF_OP ( intf_close, struct tls_connection *, tls_validator_done ), }; /** TLS certificate validator interface descriptor */ static struct interface_descriptor tls_validator_desc = - INTF_DESC ( struct tls_session, validator, tls_validator_ops ); + INTF_DESC ( struct tls_connection, validator, tls_validator_ops ); /****************************************************************************** * @@ -2675,9 +2679,9 @@ static struct interface_descriptor tls_validator_desc = /** * TLS TX state machine * - * @v tls TLS session + * @v tls TLS connection */ -static void tls_tx_step ( struct tls_session *tls ) { +static void tls_tx_step ( struct tls_connection *tls ) { int rc; /* Wait for cipherstream to become ready */ @@ -2755,7 +2759,7 @@ static void tls_tx_step ( struct tls_session *tls ) { /** TLS TX process descriptor */ static struct process_descriptor tls_process_desc = - PROC_DESC_ONCE ( struct tls_session, process, tls_tx_step ); + PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step ); /****************************************************************************** * @@ -2766,7 +2770,7 @@ static struct process_descriptor tls_process_desc = int add_tls ( struct interface *xfer, const char *name, struct interface **next ) { - struct tls_session *tls; + struct tls_connection *tls; int rc; /* Allocate and initialise TLS structure */ From baaf50017d1a5e7a5a029a00e1f90ecfcb4336f5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 24 Mar 2018 21:44:09 +0000 Subject: [PATCH 578/591] [tls] Ensure that window change is propagated to plainstream interface The cipherstream xfer_window_changed() message is used to retrigger the TLS transmit state machine. If the transmit state machine is idle, then the window change message will not be propagated to the plainstream interface. This can potentially cause the plainstream interface peer (e.g. httpcore) to block waiting for a window change message that will never arrive. Fix by ensuring that the window change message is propagated to the plainstream interface if the transmit state machine is idle. (If the transmit state machine is not idle then the plainstream window will be zero anyway.) Signed-off-by: Michael Brown --- src/net/tls.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index d28daa43c..9d994cd77 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -2747,9 +2747,14 @@ static void tls_tx_step ( struct tls_connection *tls ) { tls->tx_pending &= ~TLS_TX_FINISHED; } - /* Reschedule process if pending transmissions remain */ - if ( tls->tx_pending ) + /* Reschedule process if pending transmissions remain, + * otherwise send notification of a window change. + */ + if ( tls->tx_pending ) { tls_tx_resume ( tls ); + } else { + xfer_window_changed ( &tls->plainstream ); + } return; From 331ac451e7bcbe49f8fad16f5b6ea5330c9c169b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 26 Mar 2018 11:31:41 +0100 Subject: [PATCH 579/591] [efi] Release SNP devices before starting SAN boot image Release SNP devices to allow the SAN booted image to use our EFI_SIMPLE_NETWORK_PROTOCOL instance, and to ensure that the image is started at TPL_APPLICATION. Signed-off-by: Michael Brown --- src/interface/efi/efi_block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index c6445ab6c..91f830a11 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -638,6 +638,9 @@ static int efi_block_boot ( unsigned int drive, const char *filename ) { goto err_sandev_find; } + /* Release SNP devices */ + efi_snp_release(); + /* Connect all possible protocols */ efi_block_connect ( sandev ); @@ -673,6 +676,7 @@ static int efi_block_boot ( unsigned int drive, const char *filename ) { bs->FreePool ( handles ); err_locate_file_systems: + efi_snp_claim(); err_sandev_find: return rc; } From 4f362a032b21ae201a58f59b5885e7613e326db3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 26 Mar 2018 12:10:09 +0100 Subject: [PATCH 580/591] [efi] Do not raise TPL within EFI_DRIVER_BINDING_PROTOCOL.Supported() When booting some versions of the UEFI shell, our driver binding protocol's Supported() entry point is called at TPL_NOTIFY for no discernible reason. Attempting to raise to TPL_CALLBACK triggers an immediate assertion failure in the firmware. Since our Supported() method can run at any TPL, fix by simply not attempting to raise the TPL within this method. Signed-off-by: Michael Brown --- src/interface/efi/efi_driver.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index fe73630fd..04796414a 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -95,9 +95,7 @@ struct efi_device * efidev_parent ( struct device *dev ) { static EFI_STATUS EFIAPI efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_driver *efidrv; - EFI_TPL saved_tpl; int rc; DBGCP ( device, "EFIDRV %s DRIVER_SUPPORTED", @@ -113,22 +111,17 @@ efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, return EFI_ALREADY_STARTED; } - /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); - /* Look for a driver claiming to support this device */ for_each_table_entry ( efidrv, EFI_DRIVERS ) { if ( ( rc = efidrv->supported ( device ) ) == 0 ) { DBGC ( device, "EFIDRV %s has driver \"%s\"\n", efi_handle_name ( device ), efidrv->name ); - bs->RestoreTPL ( saved_tpl ); return 0; } } DBGCP ( device, "EFIDRV %s has no driver\n", efi_handle_name ( device ) ); - bs->RestoreTPL ( saved_tpl ); return EFI_UNSUPPORTED; } From 8c17ee115d140ceb509f97158b581cd7a19ee586 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 27 Mar 2018 19:07:38 +0200 Subject: [PATCH 581/591] [efi] Add support for R_ARM_REL32 relocations The relocation type R_ARM_REL32 is generated when building bin-arm32-efi/snp.efi using gcc 6.3 and ld 2.28. R_ARM_REL32 is a program counter (PC) relative 32 bit relocation so we can ignore it like all other PC relative relocations. Signed-off-by: Heinrich Schuchardt Signed-off-by: Michael Brown --- src/util/elf2efi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index e8e6c523e..6718df777 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -631,6 +631,7 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, break; case ELF_MREL ( EM_386, R_386_PC32 ) : case ELF_MREL ( EM_ARM, R_ARM_CALL ) : + case ELF_MREL ( EM_ARM, R_ARM_REL32 ) : case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) : case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) : case ELF_MREL ( EM_ARM, R_ARM_V4BX ): From 2eef77ecc0b3a30a2244961564c17c15920424f6 Mon Sep 17 00:00:00 2001 From: Rob Taglang Date: Thu, 5 Apr 2018 14:47:23 -0400 Subject: [PATCH 582/591] [intelx] Add PCI_ROM entry for Intel X553 NIC Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/intelx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index 0d5fb256c..afd744eda 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -474,6 +474,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ), PCI_ROM ( 0x8086, 0x1563, "x550t2", "X550-T2", 0 ), + PCI_ROM ( 0x8086, 0x15e5, "x553", "X553", 0 ), }; /** PCI driver */ From d6f02c72c984aaeab11741d54f9d9e81d9ce79f2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 18 Apr 2018 16:38:36 +0100 Subject: [PATCH 583/591] [undi] Include subsystem IDs in broken interrupt device check Allow the subsystem IDs to be used when checking for PXE stacks with broken interrupt support. Suggested-by: Levi Hsieh Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undinet.c | 33 ++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index e8ec772ef..9b7d6d849 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -807,6 +808,10 @@ struct undinet_irq_broken { uint16_t pci_vendor; /** PCI device ID */ uint16_t pci_device; + /** PCI subsystem vendor ID */ + uint16_t pci_subsys_vendor; + /** PCI subsystem ID */ + uint16_t pci_subsys; }; /** @@ -822,10 +827,10 @@ struct undinet_irq_broken { */ static const struct undinet_irq_broken undinet_irq_broken_list[] = { /* HP XX70x laptops */ - { .pci_vendor = 0x8086, .pci_device = 0x1502 }, - { .pci_vendor = 0x8086, .pci_device = 0x1503 }, + { 0x8086, 0x1502, PCI_ANY_ID, PCI_ANY_ID }, + { 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID }, /* HP 745 G3 laptop */ - { .pci_vendor = 0x14e4, .pci_device = 0x1687 }, + { 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID }, }; /** @@ -836,14 +841,30 @@ static const struct undinet_irq_broken undinet_irq_broken_list[] = { */ static int undinet_irq_is_broken ( struct device_description *desc ) { const struct undinet_irq_broken *broken; + struct pci_device pci; + uint16_t subsys_vendor; + uint16_t subsys; unsigned int i; + /* Ignore non-PCI devices */ + if ( desc->bus_type != BUS_TYPE_PCI ) + return 0; + + /* Read subsystem IDs */ + pci_init ( &pci, desc->location ); + pci_read_config_word ( &pci, PCI_SUBSYSTEM_VENDOR_ID, &subsys_vendor ); + pci_read_config_word ( &pci, PCI_SUBSYSTEM_ID, &subsys ); + + /* Check for a match against the broken device list */ for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) / sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) { broken = &undinet_irq_broken_list[i]; - if ( ( desc->bus_type == BUS_TYPE_PCI ) && - ( desc->vendor == broken->pci_vendor ) && - ( desc->device == broken->pci_device ) ) { + if ( ( broken->pci_vendor == desc->vendor ) && + ( broken->pci_device == desc->device ) && + ( ( broken->pci_subsys_vendor == subsys_vendor ) || + ( broken->pci_subsys_vendor == PCI_ANY_ID ) ) && + ( ( broken->pci_subsys == subsys ) || + ( broken->pci_subsys == PCI_ANY_ID ) ) ) { return 1; } } From f71ba143c72b625aad273c2dfd5db21b5946d238 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 19 Apr 2018 12:34:08 +0100 Subject: [PATCH 584/591] [rhine] Fix usage of mii_read() Signed-off-by: Michael Brown --- src/drivers/net/rhine.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/drivers/net/rhine.c b/src/drivers/net/rhine.c index 42bc124e0..3d8f696db 100644 --- a/src/drivers/net/rhine.c +++ b/src/drivers/net/rhine.c @@ -726,8 +726,7 @@ static int rhine_probe ( struct pci_device *pci ) { goto err_mii_reset; } DBGC ( rhn, "RHINE PHY vendor %04x device %04x\n", - rhine_mii_read ( &rhn->mii, 0x02 ), - rhine_mii_read ( &rhn->mii, 0x03 ) ); + mii_read ( &rhn->mii, 0x02 ), mii_read ( &rhn->mii, 0x03 ) ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) From 285e3e5287358c2f904ad74fabac9fefcdb46150 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 19 Apr 2018 12:36:16 +0100 Subject: [PATCH 585/591] [velocity] Fix usage of mii_read() and mii_write() Signed-off-by: Michael Brown --- src/drivers/net/velocity.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/drivers/net/velocity.c b/src/drivers/net/velocity.c index 6d5185209..129fc136a 100644 --- a/src/drivers/net/velocity.c +++ b/src/drivers/net/velocity.c @@ -194,14 +194,14 @@ static void velocity_set_link ( struct velocity_nic *vlc ) { int tmp; /* Advertise 1000MBit */ - tmp = velocity_mii_read ( &vlc->mii, MII_CTRL1000 ); + tmp = mii_read ( &vlc->mii, MII_CTRL1000 ); tmp |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; - velocity_mii_write ( &vlc->mii, MII_CTRL1000, tmp ); + mii_write ( &vlc->mii, MII_CTRL1000, tmp ); /* Enable GBit operation in MII Control Register */ - tmp = velocity_mii_read ( &vlc->mii, MII_BMCR ); + tmp = mii_read ( &vlc->mii, MII_BMCR ); tmp |= BMCR_SPEED1000; - velocity_mii_write ( &vlc->mii, MII_BMCR, tmp ); + mii_write ( &vlc->mii, MII_BMCR, tmp ); } /****************************************************************************** From 6804a8c89b8c31c3ef4e7e8ab03b82ebee41dd45 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 19 Apr 2018 12:38:55 +0100 Subject: [PATCH 586/591] [mii] Separate concepts of MII interface and MII device We currently have no generic concept of a PHY address, since all existing implementations simply hardcode the PHY address within the MII access methods. A bit-bashing MII interface will need to be provided with an explicit PHY address in order to generate the correct waveform. Allow for this by separating out the concept of a MII device (i.e. a specific PHY address attached to a particular MII interface). Signed-off-by: Michael Brown --- src/drivers/net/mii.c | 14 ++++---- src/drivers/net/realtek.c | 23 ++++++++----- src/drivers/net/realtek.h | 4 ++- src/drivers/net/rhine.c | 19 +++++++---- src/drivers/net/rhine.h | 4 ++- src/drivers/net/smscusb.c | 16 +++++---- src/drivers/net/smscusb.h | 7 ++-- src/drivers/net/velocity.c | 19 +++++++---- src/drivers/net/velocity.h | 4 ++- src/include/ipxe/mii.h | 67 +++++++++++++++++++++++++++----------- 10 files changed, 118 insertions(+), 59 deletions(-) diff --git a/src/drivers/net/mii.c b/src/drivers/net/mii.c index 9b297029a..f6db30740 100644 --- a/src/drivers/net/mii.c +++ b/src/drivers/net/mii.c @@ -37,10 +37,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Restart autonegotiation * - * @v mii MII interface + * @v mii MII device * @ret rc Return status code */ -int mii_restart ( struct mii_interface *mii ) { +int mii_restart ( struct mii_device *mii ) { int bmcr; int rc; @@ -66,12 +66,12 @@ int mii_restart ( struct mii_interface *mii ) { } /** - * Reset MII interface + * Reset MII device * - * @v mii MII interface + * @v mii MII device * @ret rc Return status code */ -int mii_reset ( struct mii_interface *mii ) { +int mii_reset ( struct mii_device *mii ) { unsigned int i; int bmcr; int rc; @@ -119,11 +119,11 @@ int mii_reset ( struct mii_interface *mii ) { /** * Update link status via MII * - * @v mii MII interface + * @v mii MII device * @v netdev Network device * @ret rc Return status code */ -int mii_check_link ( struct mii_interface *mii, struct net_device *netdev ) { +int mii_check_link ( struct mii_device *mii, struct net_device *netdev ) { int bmsr; int link; int rc; diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index 022b59324..310b9f96a 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -242,12 +242,15 @@ static int realtek_init_eeprom ( struct net_device *netdev ) { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int realtek_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct realtek_nic *rtl = container_of ( mii, struct realtek_nic, mii ); +static int realtek_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { + struct realtek_nic *rtl = + container_of ( mdio, struct realtek_nic, mdio ); unsigned int i; uint32_t value; @@ -279,14 +282,17 @@ static int realtek_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int realtek_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data) { - struct realtek_nic *rtl = container_of ( mii, struct realtek_nic, mii ); +static int realtek_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, + unsigned int data ) { + struct realtek_nic *rtl = + container_of ( mdio, struct realtek_nic, mdio ); unsigned int i; /* Fail if PHYAR register is not present */ @@ -1158,7 +1164,8 @@ static int realtek_probe ( struct pci_device *pci ) { } /* Initialise and reset MII interface */ - mii_init ( &rtl->mii, &realtek_mii_operations ); + mdio_init ( &rtl->mdio, &realtek_mii_operations ); + mii_init ( &rtl->mii, &rtl->mdio, 0 ); if ( ( rc = realtek_phy_reset ( rtl ) ) != 0 ) goto err_phy_reset; diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h index b1ce7f98f..4d13784c4 100644 --- a/src/drivers/net/realtek.h +++ b/src/drivers/net/realtek.h @@ -283,7 +283,9 @@ struct realtek_nic { /** Non-volatile options */ struct nvo_block nvo; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** Legacy datapath mode */ int legacy; diff --git a/src/drivers/net/rhine.c b/src/drivers/net/rhine.c index 3d8f696db..a1dc58725 100644 --- a/src/drivers/net/rhine.c +++ b/src/drivers/net/rhine.c @@ -49,12 +49,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int rhine_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); +static int rhine_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { + struct rhine_nic *rhn = container_of ( mdio, struct rhine_nic, mdio ); unsigned int timeout = RHINE_TIMEOUT_US; uint8_t cr; @@ -80,14 +82,16 @@ static int rhine_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int rhine_mii_write ( struct mii_interface *mii, unsigned int reg, +static int rhine_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, unsigned int data ) { - struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); + struct rhine_nic *rhn = container_of ( mdio, struct rhine_nic, mdio ); unsigned int timeout = RHINE_TIMEOUT_US; uint8_t cr; @@ -719,7 +723,8 @@ static int rhine_probe ( struct pci_device *pci ) { netdev->hw_addr[i] = readb ( rhn->regs + RHINE_MAC + i ); /* Initialise and reset MII interface */ - mii_init ( &rhn->mii, &rhine_mii_operations ); + mdio_init ( &rhn->mdio, &rhine_mii_operations ); + mii_init ( &rhn->mii, &rhn->mdio, 0 ); if ( ( rc = mii_reset ( &rhn->mii ) ) != 0 ) { DBGC ( rhn, "RHINE %p could not reset MII: %s\n", rhn, strerror ( rc ) ); diff --git a/src/drivers/net/rhine.h b/src/drivers/net/rhine.h index b26f9ae78..eef49ec98 100644 --- a/src/drivers/net/rhine.h +++ b/src/drivers/net/rhine.h @@ -237,7 +237,9 @@ struct rhine_nic { uint8_t cr1; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** Transmit descriptor ring */ struct rhine_ring tx; diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c index 60390ce31..538d338c4 100644 --- a/src/drivers/net/smscusb.c +++ b/src/drivers/net/smscusb.c @@ -481,13 +481,15 @@ static int smscusb_mii_wait ( struct smscusb_device *smscusb ) { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int smscusb_mii_read ( struct mii_interface *mii, unsigned int reg ) { +static int smscusb_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { struct smscusb_device *smscusb = - container_of ( mii, struct smscusb_device, mii ); + container_of ( mdio, struct smscusb_device, mdio ); unsigned int base = smscusb->mii_base; uint32_t mii_access; uint32_t mii_data; @@ -520,15 +522,17 @@ static int smscusb_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int smscusb_mii_write ( struct mii_interface *mii, unsigned int reg, +static int smscusb_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, unsigned int data ) { struct smscusb_device *smscusb = - container_of ( mii, struct smscusb_device, mii ); + container_of ( mdio, struct smscusb_device, mdio ); unsigned int base = smscusb->mii_base; uint32_t mii_access; uint32_t mii_data; diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h index 5e4440ea6..b5d9ad3fc 100644 --- a/src/drivers/net/smscusb.h +++ b/src/drivers/net/smscusb.h @@ -151,7 +151,9 @@ struct smscusb_device { /** USB network device */ struct usbnet_device usbnet; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** MII register base */ uint16_t mii_base; /** PHY interrupt source register */ @@ -275,7 +277,8 @@ static inline __attribute__ (( always_inline )) void smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base, unsigned int phy_source ) { - mii_init ( &smscusb->mii, &smscusb_mii_operations ); + mdio_init ( &smscusb->mdio, &smscusb_mii_operations ); + mii_init ( &smscusb->mii, &smscusb->mdio, 0 ); smscusb->mii_base = mii_base; smscusb->phy_source = phy_source; } diff --git a/src/drivers/net/velocity.c b/src/drivers/net/velocity.c index 129fc136a..0a2a3ac10 100644 --- a/src/drivers/net/velocity.c +++ b/src/drivers/net/velocity.c @@ -100,13 +100,15 @@ static int velocity_autopoll_start ( struct velocity_nic *vlc ) { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int velocity_mii_read ( struct mii_interface *mii, unsigned int reg ) { +static int velocity_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { struct velocity_nic *vlc = - container_of ( mii, struct velocity_nic, mii ); + container_of ( mdio, struct velocity_nic, mdio ); int timeout = VELOCITY_TIMEOUT_US; int result; @@ -140,15 +142,17 @@ static int velocity_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int velocity_mii_write ( struct mii_interface *mii, unsigned int reg, +static int velocity_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, unsigned int data) { struct velocity_nic *vlc = - container_of ( mii, struct velocity_nic, mii ); + container_of ( mdio, struct velocity_nic, mdio ); int timeout = VELOCITY_TIMEOUT_US; DBGC2 ( vlc, "VELOCITY %p MII write reg %d data 0x%04x\n", @@ -747,7 +751,8 @@ static int velocity_probe ( struct pci_device *pci ) { netdev->hw_addr[5] = readb ( vlc->regs + VELOCITY_MAC5 ); /* Initialise and reset MII interface */ - mii_init ( &vlc->mii, &velocity_mii_operations ); + mdio_init ( &vlc->mdio, &velocity_mii_operations ); + mii_init ( &vlc->mii, &vlc->mdio, 0 ); if ( ( rc = mii_reset ( &vlc->mii ) ) != 0 ) { DBGC ( vlc, "VELOCITY %p could not reset MII: %s\n", vlc, strerror ( rc ) ); diff --git a/src/drivers/net/velocity.h b/src/drivers/net/velocity.h index 04e6a1460..84817d1bb 100644 --- a/src/drivers/net/velocity.h +++ b/src/drivers/net/velocity.h @@ -326,7 +326,9 @@ struct velocity_nic { /** Registers */ void *regs; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** Netdev */ struct net_device *netdev; diff --git a/src/include/ipxe/mii.h b/src/include/ipxe/mii.h index c2245b49e..154f69cf7 100644 --- a/src/include/ipxe/mii.h +++ b/src/include/ipxe/mii.h @@ -19,21 +19,24 @@ struct mii_operations { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret data Data read, or negative error */ - int ( * read ) ( struct mii_interface *mii, unsigned int reg ); + int ( * read ) ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg ); /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ - int ( * write ) ( struct mii_interface *mii, unsigned int reg, - unsigned int data ); + int ( * write ) ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg, unsigned int data ); }; /** An MII interface */ @@ -42,49 +45,75 @@ struct mii_interface { struct mii_operations *op; }; +/** An MII device */ +struct mii_device { + /** MII interface */ + struct mii_interface *mdio; + /** PHY address */ + unsigned int address; +}; + /** * Initialise MII interface * - * @v mii MII interface + * @v mdio MII interface * @v op MII interface operations */ static inline __attribute__ (( always_inline )) void -mii_init ( struct mii_interface *mii, struct mii_operations *op ) { - mii->op = op; +mdio_init ( struct mii_interface *mdio, struct mii_operations *op ) { + mdio->op = op; +} + +/** + * Initialise MII device + * + * @v mii MII device + * @v mii MII interface + * @v address PHY address + */ +static inline __attribute__ (( always_inline )) void +mii_init ( struct mii_device *mii, struct mii_interface *mdio, + unsigned int address ) { + mii->mdio = mdio; + mii->address = address; } /** * Read from MII register * - * @v mii MII interface + * @v mii MII device * @v reg Register address * @ret data Data read, or negative error */ static inline __attribute__ (( always_inline )) int -mii_read ( struct mii_interface *mii, unsigned int reg ) { - return mii->op->read ( mii, reg ); +mii_read ( struct mii_device *mii, unsigned int reg ) { + struct mii_interface *mdio = mii->mdio; + + return mdio->op->read ( mdio, mii->address, reg ); } /** * Write to MII register * - * @v mii MII interface + * @v mii MII device * @v reg Register address * @v data Data to write * @ret rc Return status code */ static inline __attribute__ (( always_inline )) int -mii_write ( struct mii_interface *mii, unsigned int reg, unsigned int data ) { - return mii->op->write ( mii, reg, data ); +mii_write ( struct mii_device *mii, unsigned int reg, unsigned int data ) { + struct mii_interface *mdio = mii->mdio; + + return mdio->op->write ( mdio, mii->address, reg, data ); } /** * Dump MII registers (for debugging) * - * @v mii MII interface + * @v mii MII device */ static inline void -mii_dump ( struct mii_interface *mii ) { +mii_dump ( struct mii_device *mii ) { unsigned int i; int data; @@ -112,9 +141,9 @@ mii_dump ( struct mii_interface *mii ) { /** Maximum time to wait for a reset, in milliseconds */ #define MII_RESET_MAX_WAIT_MS 500 -extern int mii_restart ( struct mii_interface *mii ); -extern int mii_reset ( struct mii_interface *mii ); -extern int mii_check_link ( struct mii_interface *mii, +extern int mii_restart ( struct mii_device *mii ); +extern int mii_reset ( struct mii_device *mii ); +extern int mii_check_link ( struct mii_device *mii, struct net_device *netdev ); #endif /* _IPXE_MII_H */ From e901e6b73b6b0f0c6ec251963da412eaa2121db9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 19 Apr 2018 17:32:07 +0100 Subject: [PATCH 587/591] [tcp] Add missing packed attribute on struct tcp_header Debugged-by: Mark Rutland Debugged-by: Heinrich Schuchardt Signed-off-by: Michael Brown --- src/include/ipxe/tcp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h index 21be3ca8a..f5508fe2b 100644 --- a/src/include/ipxe/tcp.h +++ b/src/include/ipxe/tcp.h @@ -26,7 +26,7 @@ struct tcp_header { uint16_t win; /* Advertised window */ uint16_t csum; /* Checksum */ uint16_t urg; /* Urgent pointer */ -}; +} __attribute__ (( packed )); /** @defgroup tcpopts TCP options * @{ From 6047b7ca7a8e58120078b2fb216eee280a99ce16 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 20 Apr 2018 13:25:46 +0100 Subject: [PATCH 588/591] [mii] Fix typo in parameter name Signed-off-by: Michael Brown --- src/include/ipxe/mii.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/ipxe/mii.h b/src/include/ipxe/mii.h index 154f69cf7..aaeae3db5 100644 --- a/src/include/ipxe/mii.h +++ b/src/include/ipxe/mii.h @@ -68,7 +68,7 @@ mdio_init ( struct mii_interface *mdio, struct mii_operations *op ) { * Initialise MII device * * @v mii MII device - * @v mii MII interface + * @v mdio MII interface * @v address PHY address */ static inline __attribute__ (( always_inline )) void From 7ed1dc98c325f5d5934fb9806307e0638735304a Mon Sep 17 00:00:00 2001 From: Sylvie Barlow Date: Fri, 20 Apr 2018 13:37:20 +0100 Subject: [PATCH 589/591] [mii] Add mii_find() Add the function mii_find() in order to locate the PHY address. Signed-off-by: Sylvie Barlow Signed-off-by: Michael Brown --- src/drivers/net/mii.c | 25 +++++++++++++++++++++++++ src/include/ipxe/mii.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/src/drivers/net/mii.c b/src/drivers/net/mii.c index f6db30740..87605f0cb 100644 --- a/src/drivers/net/mii.c +++ b/src/drivers/net/mii.c @@ -147,3 +147,28 @@ int mii_check_link ( struct mii_device *mii, struct net_device *netdev ) { return 0; } + +/** + * Find PHY address + * + * @v mii MII device + * @ret rc Return status code + */ +int mii_find ( struct mii_device *mii ) { + unsigned int address; + int id; + + /* Try all possible PHY addresses */ + for ( address = 0 ; address <= MII_MAX_PHY_ADDRESS ; address++ ) { + mii->address = address; + id = mii_read ( mii, MII_PHYSID1 ); + if ( ( id > 0x0000 ) && ( id < 0xffff ) ) { + DBGC ( mii, "MII %p found PHY at address %d\n", + mii, address ); + return 0; + } + } + + DBGC ( mii, "MII %p failed to find an address\n", mii ); + return -ENOENT; +} diff --git a/src/include/ipxe/mii.h b/src/include/ipxe/mii.h index aaeae3db5..89fc92a4a 100644 --- a/src/include/ipxe/mii.h +++ b/src/include/ipxe/mii.h @@ -141,9 +141,13 @@ mii_dump ( struct mii_device *mii ) { /** Maximum time to wait for a reset, in milliseconds */ #define MII_RESET_MAX_WAIT_MS 500 +/** Maximum PHY address */ +#define MII_MAX_PHY_ADDRESS 31 + extern int mii_restart ( struct mii_device *mii ); extern int mii_reset ( struct mii_device *mii ); extern int mii_check_link ( struct mii_device *mii, struct net_device *netdev ); +extern int mii_find ( struct mii_device *mii ); #endif /* _IPXE_MII_H */ From c239f0bff2496cefa5e9d3ef6b232f5d28a04d70 Mon Sep 17 00:00:00 2001 From: Sylvie Barlow Date: Fri, 20 Apr 2018 13:58:40 +0100 Subject: [PATCH 590/591] [mii] Add bit-bashing interface Signed-off-by: Sylvie Barlow Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/bitbash/mii_bit.c | 162 ++++++++++++++++++++++++++++++++++ src/include/ipxe/mii_bit.h | 55 ++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 src/drivers/bitbash/mii_bit.c create mode 100644 src/include/ipxe/mii_bit.h diff --git a/src/drivers/bitbash/mii_bit.c b/src/drivers/bitbash/mii_bit.c new file mode 100644 index 000000000..5f0ec04aa --- /dev/null +++ b/src/drivers/bitbash/mii_bit.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 Sylvie Barlow . + * + * 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 ); + +#include +#include +#include +#include + +/** + * Transfer bits over MII bit-bashing interface + * + * @v basher Bit basher + * @v mask Mask + * @v write Data to write + * @ret read Data read + */ +static uint32_t mii_bit_xfer ( struct bit_basher *basher, + uint32_t mask, uint32_t write ) { + uint32_t read = 0; + int bit; + + for ( ; mask ; mask >>= 1 ) { + + /* Delay */ + udelay ( 1 ); + + /* Write bit to basher */ + write_bit ( basher, MII_BIT_MDIO, ( write & mask ) ); + + /* Read bit from basher */ + bit = read_bit ( basher, MII_BIT_MDIO ); + read <<= 1; + read |= ( bit & 1 ); + + /* Set clock high */ + write_bit ( basher, MII_BIT_MDC, 1 ); + + /* Delay */ + udelay ( 1 ); + + /* Set clock low */ + write_bit ( basher, MII_BIT_MDC, 0 ); + } + return read; +} + +/** + * Read or write via MII bit-bashing interface + * + * @v basher Bit basher + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @v cmd Command + * @ret data Data read + */ +static unsigned int mii_bit_rw ( struct bit_basher *basher, + unsigned int phy, unsigned int reg, + unsigned int data, unsigned int cmd ) { + + /* Initiate drive for write */ + write_bit ( basher, MII_BIT_DRIVE, 1 ); + + /* Write start */ + mii_bit_xfer ( basher, MII_BIT_START_MASK, MII_BIT_START ); + + /* Write command */ + mii_bit_xfer ( basher, MII_BIT_CMD_MASK, cmd ); + + /* Write PHY address */ + mii_bit_xfer ( basher, MII_BIT_PHY_MASK, phy ); + + /* Write register address */ + mii_bit_xfer ( basher, MII_BIT_REG_MASK, reg ); + + /* Switch drive to read if applicable */ + write_bit ( basher, MII_BIT_DRIVE, ( cmd & MII_BIT_CMD_RW ) ); + + /* Allow space for turnaround */ + mii_bit_xfer ( basher, MII_BIT_SWITCH_MASK, MII_BIT_SWITCH ); + + /* Read or write data */ + data = mii_bit_xfer (basher, MII_BIT_DATA_MASK, data ); + + /* Initiate drive for read */ + write_bit ( basher, MII_BIT_DRIVE, 0 ); + + return data; +} + +/** + * Read from MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @ret data Data read, or negative error + */ +static int mii_bit_read ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg ) { + struct mii_bit_basher *miibit = + container_of ( mdio, struct mii_bit_basher, mdio ); + struct bit_basher *basher = &miibit->basher; + + return mii_bit_rw ( basher, phy, reg, 0, MII_BIT_CMD_READ ); +} + +/** + * Write to MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int mii_bit_write ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg, unsigned int data ) { + struct mii_bit_basher *miibit = + container_of ( mdio, struct mii_bit_basher, mdio ); + struct bit_basher *basher = &miibit->basher; + + mii_bit_rw ( basher, phy, reg, data, MII_BIT_CMD_WRITE ); + return 0; +} + +/** MII bit basher operations */ +static struct mii_operations mii_bit_op = { + .read = mii_bit_read, + .write = mii_bit_write, +}; + +/** + * Initialise bit-bashing interface + * + * @v miibit MII bit basher + */ +void init_mii_bit_basher ( struct mii_bit_basher *miibit ) { + mdio_init ( &miibit->mdio, &mii_bit_op ); +}; diff --git a/src/include/ipxe/mii_bit.h b/src/include/ipxe/mii_bit.h new file mode 100644 index 000000000..0f797e913 --- /dev/null +++ b/src/include/ipxe/mii_bit.h @@ -0,0 +1,55 @@ +#ifndef _IPXE_MII_BIT_H +#define _IPXE_MII_BIT_H + +/** @file + * + * MII bit-bashing interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +#define MII_BIT_START 0xffffffff /**< Start */ +#define MII_BIT_START_MASK 0x80000000 /**< Start mask */ + +#define MII_BIT_CMD_MASK 0x00000008 /**< Command mask */ +#define MII_BIT_CMD_READ 0x00000006 /**< Command read */ +#define MII_BIT_CMD_WRITE 0x00000005 /**< Command write */ +#define MII_BIT_CMD_RW 0x00000001 /**< Command read or write */ + +#define MII_BIT_PHY_MASK 0x00000010 /**< PHY mask */ + +#define MII_BIT_REG_MASK 0x00000010 /**< Register mask */ + +#define MII_BIT_SWITCH 0x00000002 /**< Switch */ +#define MII_BIT_SWITCH_MASK 0x00000002 /**< Switch mask */ + +#define MII_BIT_DATA_MASK 0x00008000 /**< Data mask */ + +/** A bit-bashing MII interface */ +struct mii_bit_basher { + /** MII interface */ + struct mii_interface mdio; + /** Bit-bashing interface */ + struct bit_basher basher; +}; + +/** Bit indices used for MII bit-bashing interface */ +enum { + /** MII clock */ + MII_BIT_MDC = 0, + /** MII data */ + MII_BIT_MDIO, + /** MII data direction */ + MII_BIT_DRIVE, +}; + +/** Delay between MDC transitions */ +#define MII_BIT_UDELAY 1 + +extern void init_mii_bit_basher ( struct mii_bit_basher *miibit ); + +#endif /* _IPXE_MII_BIT_H */ From 960d1e36b09803b291803ab336412f7d95349568 Mon Sep 17 00:00:00 2001 From: Sylvie Barlow Date: Fri, 20 Apr 2018 14:10:26 +0100 Subject: [PATCH 591/591] [icplus] Add driver for IC+ network card Signed-off-by: Sylvie Barlow Signed-off-by: Michael Brown --- src/drivers/net/icplus.c | 809 +++++++++++++++++++++++++++++++++++++ src/drivers/net/icplus.h | 206 ++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 1016 insertions(+) create mode 100644 src/drivers/net/icplus.c create mode 100644 src/drivers/net/icplus.h diff --git a/src/drivers/net/icplus.c b/src/drivers/net/icplus.c new file mode 100644 index 000000000..4bed92427 --- /dev/null +++ b/src/drivers/net/icplus.c @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2018 Sylvie Barlow . + * + * 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 +#include "icplus.h" + +/** @file + * + * IC+ network driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v icp IC+ device + * @ret rc Return status code + */ +static int icplus_reset ( struct icplus_nic *icp ) { + uint32_t asicctrl; + unsigned int i; + + /* Trigger reset */ + writel ( ( ICP_ASICCTRL_GLOBALRESET | ICP_ASICCTRL_DMA | + ICP_ASICCTRL_FIFO | ICP_ASICCTRL_NETWORK | ICP_ASICCTRL_HOST | + ICP_ASICCTRL_AUTOINIT ), ( icp->regs + ICP_ASICCTRL ) ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < ICP_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if device is ready */ + asicctrl = readl ( icp->regs + ICP_ASICCTRL ); + if ( ! ( asicctrl & ICP_ASICCTRL_RESETBUSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( icp, "ICPLUS %p timed out waiting for reset (asicctrl %#08x)\n", + icp, asicctrl ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * EEPROM interface + * + ****************************************************************************** + */ + +/** + * Read data from EEPROM + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int icplus_read_eeprom ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ) { + struct icplus_nic *icp = + container_of ( nvs, struct icplus_nic, eeprom ); + unsigned int i; + uint16_t eepromctrl; + uint16_t *data_word = data; + + /* Sanity check. We advertise a blocksize of one word, so + * should only ever receive single-word requests. + */ + assert ( len == sizeof ( *data_word ) ); + + /* Initiate read */ + writew ( ( ICP_EEPROMCTRL_OPCODE_READ | + ICP_EEPROMCTRL_ADDRESS ( address ) ), + ( icp->regs + ICP_EEPROMCTRL ) ); + + /* Wait for read to complete */ + for ( i = 0 ; i < ICP_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* If read is not complete, delay 1ms and retry */ + eepromctrl = readw ( icp->regs + ICP_EEPROMCTRL ); + if ( eepromctrl & ICP_EEPROMCTRL_BUSY ) { + mdelay ( 1 ); + continue; + } + + /* Extract data */ + *data_word = cpu_to_le16 ( readw ( icp->regs + ICP_EEPROMDATA )); + return 0; + } + + DBGC ( icp, "ICPLUS %p timed out waiting for EEPROM read\n", icp ); + return -ETIMEDOUT; +} + +/** + * Write data to EEPROM + * + * @v nvs NVS device + * @v address Address to which to write + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int icplus_write_eeprom ( struct nvs_device *nvs, + unsigned int address __unused, + const void *data __unused, + size_t len __unused ) { + struct icplus_nic *icp = + container_of ( nvs, struct icplus_nic, eeprom ); + + DBGC ( icp, "ICPLUS %p EEPROM write not supported\n", icp ); + return -ENOTSUP; +} + +/** + * Initialise EEPROM + * + * @v icp IC+ device + */ +static void icplus_init_eeprom ( struct icplus_nic *icp ) { + + /* The hardware supports only single-word reads */ + icp->eeprom.word_len_log2 = ICP_EEPROM_WORD_LEN_LOG2; + icp->eeprom.size = ICP_EEPROM_MIN_SIZE_WORDS; + icp->eeprom.block_size = 1; + icp->eeprom.read = icplus_read_eeprom; + icp->eeprom.write = icplus_write_eeprom; +} + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** Pin mapping for MII bit-bashing interface */ +static const uint8_t icplus_mii_bits[] = { + [MII_BIT_MDC] = ICP_PHYCTRL_MGMTCLK, + [MII_BIT_MDIO] = ICP_PHYCTRL_MGMTDATA, + [MII_BIT_DRIVE] = ICP_PHYCTRL_MGMTDIR, +}; + +/** + * Read input bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @ret zero Input is a logic 0 + * @ret non-zero Input is a logic 1 + */ +static int icplus_mii_read_bit ( struct bit_basher *basher, + unsigned int bit_id ) { + struct icplus_nic *icp = container_of ( basher, struct icplus_nic, + miibit.basher ); + uint8_t mask = icplus_mii_bits[bit_id]; + uint8_t reg; + + DBG_DISABLE ( DBGLVL_IO ); + reg = readb ( icp->regs + ICP_PHYCTRL ); + DBG_ENABLE ( DBGLVL_IO ); + return ( reg & mask ); +} + +/** + * Set/clear output bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + */ +static void icplus_mii_write_bit ( struct bit_basher *basher, + unsigned int bit_id, unsigned long data ) { + struct icplus_nic *icp = container_of ( basher, struct icplus_nic, + miibit.basher ); + uint8_t mask = icplus_mii_bits[bit_id]; + uint8_t reg; + + DBG_DISABLE ( DBGLVL_IO ); + reg = readb ( icp->regs + ICP_PHYCTRL ); + reg &= ~mask; + reg |= ( data & mask ); + writeb ( reg, icp->regs + ICP_PHYCTRL ); + readb ( icp->regs + ICP_PHYCTRL ); /* Ensure write reaches chip */ + DBG_ENABLE ( DBGLVL_IO ); +} + +/** MII bit-bashing interface */ +static struct bit_basher_operations icplus_basher_ops = { + .read = icplus_mii_read_bit, + .write = icplus_mii_write_bit, +}; + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Configure PHY + * + * @v icp IC+ device + * @ret rc Return status code + */ +static int icplus_init_phy ( struct icplus_nic *icp ) { + uint32_t asicctrl; + int rc; + + /* Find PHY address */ + if ( ( rc = mii_find ( &icp->mii ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not find PHY address: %s\n", + icp, strerror ( rc ) ); + return rc; + } + + /* Configure PHY to advertise 1000Mbps if applicable */ + asicctrl = readl ( icp->regs + ICP_ASICCTRL ); + if ( asicctrl & ICP_ASICCTRL_PHYSPEED1000 ) { + if ( ( rc = mii_write ( &icp->mii, MII_CTRL1000, + ADVERTISE_1000FULL ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not advertise 1000Mbps: " + "%s\n", icp, strerror ( rc ) ); + return rc; + } + } + + /* Reset PHY */ + if ( ( rc = mii_reset ( &icp->mii ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not reset PHY: %s\n", + icp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Check link state + * + * @v netdev Network device + */ +static void icplus_check_link ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + uint8_t phyctrl; + + /* Read link status */ + phyctrl = readb ( icp->regs + ICP_PHYCTRL ); + DBGC ( icp, "ICPLUS %p PHY control is %02x\n", icp, phyctrl ); + + /* Update network device */ + if ( phyctrl & ICP_PHYCTRL_LINKSPEED ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Set descriptor ring base address + * + * @v icp IC+ device + * @v offset Register offset + * @v address Base address + */ +static inline void icplus_set_base ( struct icplus_nic *icp, unsigned int offset, + void *base ) { + physaddr_t phys = virt_to_bus ( base ); + + /* Program base address registers */ + writel ( ( phys & 0xffffffffUL ), + ( icp->regs + offset + ICP_BASE_LO ) ); + if ( sizeof ( phys ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) phys ) >> 32 ), + ( icp->regs + offset + ICP_BASE_HI ) ); + } else { + writel ( 0, ( icp->regs + offset + ICP_BASE_HI ) ); + } +} + +/** + * Create descriptor ring + * + * @v icp IC+ device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int icplus_create_ring ( struct icplus_nic *icp, struct icplus_ring *ring ) { + size_t len = ( sizeof ( ring->entry[0] ) * ICP_NUM_DESC ); + int rc; + unsigned int i; + struct icplus_descriptor *desc; + struct icplus_descriptor *next; + + /* Allocate descriptor ring */ + ring->entry = malloc_dma ( len, ICP_ALIGN ); + if ( ! ring->entry ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Initialise descriptor ring */ + memset ( ring->entry, 0, len ); + for ( i = 0 ; i < ICP_NUM_DESC ; i++ ) { + desc = &ring->entry[i]; + next = &ring->entry[ ( i + 1 ) % ICP_NUM_DESC ]; + desc->next = cpu_to_le64 ( virt_to_bus ( next ) ); + desc->flags = ( ICP_TX_UNALIGN | ICP_TX_INDICATE ); + desc->control = ( ICP_TX_SOLE_FRAG | ICP_DONE ); + } + + /* Reset transmit producer & consumer counters */ + ring->prod = 0; + ring->cons = 0; + + DBGC ( icp, "ICP %p %s ring at [%#08lx,%#08lx)\n", + icp, ( ( ring->listptr == ICP_TFDLISTPTR ) ? "TX" : "RX" ), + virt_to_bus ( ring->entry ), + ( virt_to_bus ( ring->entry ) + len ) ); + return 0; + + free_dma ( ring->entry, len ); + ring->entry = NULL; + err_alloc: + return rc; +} + +/** + * Destroy descriptor ring + * + * @v icp IC+ device + * @v ring Descriptor ring + */ +static void icplus_destroy_ring ( struct icplus_nic *icp __unused, + struct icplus_ring *ring ) { + size_t len = ( sizeof ( ring->entry[0] ) * ICP_NUM_DESC ); + + /* Free descriptor ring */ + free_dma ( ring->entry, len ); + ring->entry = NULL; +} + +/** + * Refill receive descriptor ring + * + * @v icp IC+ device + */ +void icplus_refill_rx ( struct icplus_nic *icp ) { + struct icplus_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + physaddr_t address; + unsigned int refilled = 0; + + /* Refill ring */ + while ( ( icp->rx.prod - icp->rx.cons ) < ICP_NUM_DESC ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( ICP_RX_MAX_LEN ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rx_idx = ( icp->rx.prod++ % ICP_NUM_DESC ); + desc = &icp->rx.entry[rx_idx]; + + /* Populate receive descriptor */ + address = virt_to_bus ( iobuf->data ); + desc->data.address = cpu_to_le64 ( address ); + desc->data.len = cpu_to_le16 ( ICP_RX_MAX_LEN ); + wmb(); + desc->control = 0; + + /* Record I/O buffer */ + assert ( icp->rx_iobuf[rx_idx] == NULL ); + icp->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( icp, "ICP %p RX %d is [%llx,%llx)\n", icp, rx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + ICP_RX_MAX_LEN ) ); + refilled++; + } + + /* Push descriptors to card, if applicable */ + if ( refilled ) { + wmb(); + writew ( ICP_DMACTRL_RXPOLLNOW, icp->regs + ICP_DMACTRL ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int icplus_open ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + int rc; + + /* Create transmit descriptor ring */ + if ( ( rc = icplus_create_ring ( icp, &icp->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = icplus_create_ring ( icp, &icp->rx ) ) != 0 ) + goto err_create_rx; + + /* Program descriptor base address */ + icplus_set_base ( icp, icp->tx.listptr, icp->tx.entry ); + icplus_set_base ( icp, icp->rx.listptr, icp->rx.entry ); + + /* Enable receive mode */ + writew ( ( ICP_RXMODE_UNICAST | ICP_RXMODE_MULTICAST | + ICP_RXMODE_BROADCAST | ICP_RXMODE_ALLFRAMES ), + icp->regs + ICP_RXMODE ); + + /* Enable transmitter and receiver */ + writel ( ( ICP_MACCTRL_TXENABLE | ICP_MACCTRL_RXENABLE | + ICP_MACCTRL_DUPLEX ), icp->regs + ICP_MACCTRL ); + + /* Fill receive ring */ + icplus_refill_rx ( icp ); + + /* Check link state */ + icplus_check_link ( netdev ); + + return 0; + + icplus_reset ( icp ); + icplus_destroy_ring ( icp, &icp->rx ); + err_create_rx: + icplus_destroy_ring ( icp, &icp->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void icplus_close ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + unsigned int i; + + /* Perform global reset */ + icplus_reset ( icp ); + + /* Destroy receive descriptor ring */ + icplus_destroy_ring ( icp, &icp->rx ); + + /* Destroy transmit descriptor ring */ + icplus_destroy_ring ( icp, &icp->tx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < ICP_NUM_DESC ; i++ ) { + if ( icp->rx_iobuf[i] ) + free_iob ( icp->rx_iobuf[i] ); + icp->rx_iobuf[i] = NULL; + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int icplus_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct icplus_nic *icp = netdev->priv; + struct icplus_descriptor *desc; + unsigned int tx_idx; + physaddr_t address; + + /* Check if ring is full */ + if ( ( icp->tx.prod - icp->tx.cons ) >= ICP_NUM_DESC ) { + DBGC ( icp, "ICP %p out of transmit descriptors\n", icp ); + return -ENOBUFS; + } + + /* Find TX descriptor entry to use */ + tx_idx = ( icp->tx.prod++ % ICP_NUM_DESC ); + desc = &icp->tx.entry[tx_idx]; + + /* Fill in TX descriptor */ + address = virt_to_bus ( iobuf->data ); + desc->data.address = cpu_to_le64 ( address ); + desc->data.len = cpu_to_le16 ( iob_len ( iobuf ) ); + wmb(); + desc->control = ICP_TX_SOLE_FRAG; + wmb(); + + /* Ring doorbell */ + writew ( ICP_DMACTRL_TXPOLLNOW, icp->regs + ICP_DMACTRL ); + + DBGC2 ( icp, "ICP %p TX %d is [%llx,%llx)\n", icp, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + DBGC2_HDA ( icp, virt_to_phys ( desc ), desc, sizeof ( *desc ) ); + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void icplus_poll_tx ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + struct icplus_descriptor *desc; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( icp->tx.cons != icp->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( icp->tx.cons % ICP_NUM_DESC ); + desc = &icp->tx.entry[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( ! ( desc->control & ICP_DONE ) ) + return; + + /* Complete TX descriptor */ + DBGC2 ( icp, "ICP %p TX %d complete\n", icp, tx_idx ); + netdev_tx_complete_next ( netdev ); + icp->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void icplus_poll_rx ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + struct icplus_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( icp->rx.cons != icp->rx.prod ) { + + /* Get next transmit descriptor */ + rx_idx = ( icp->rx.cons % ICP_NUM_DESC ); + desc = &icp->rx.entry[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( ! ( desc->control & ICP_DONE ) ) + return; + + /* Populate I/O buffer */ + iobuf = icp->rx_iobuf[rx_idx]; + icp->rx_iobuf[rx_idx] = NULL; + len = le16_to_cpu ( desc->len ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( desc->flags & ( ICP_RX_ERR_OVERRUN | ICP_RX_ERR_RUNT | + ICP_RX_ERR_ALIGN | ICP_RX_ERR_FCS | + ICP_RX_ERR_OVERSIZED | ICP_RX_ERR_LEN ) ) { + DBGC ( icp, "ICP %p RX %d error (length %zd, " + "flags %02x)\n", icp, rx_idx, len, desc->flags ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } else { + DBGC2 ( icp, "ICP %p RX %d complete (length " + "%zd)\n", icp, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } + icp->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void icplus_poll ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + uint16_t intstatus; + uint32_t txstatus; + + /* Check for interrupts */ + intstatus = readw ( icp->regs + ICP_INTSTATUS ); + + /* Poll for TX completions, if applicable */ + if ( intstatus & ICP_INTSTATUS_TXCOMPLETE ) { + txstatus = readl ( icp->regs + ICP_TXSTATUS ); + if ( txstatus & ICP_TXSTATUS_ERROR ) + DBGC ( icp, "ICP %p TX error: %08x\n", icp, txstatus ); + icplus_poll_tx ( netdev ); + } + + /* Poll for RX completions, if applicable */ + if ( intstatus & ICP_INTSTATUS_RXDMACOMPLETE ) { + writew ( ICP_INTSTATUS_RXDMACOMPLETE, icp->regs + ICP_INTSTATUS ); + icplus_poll_rx ( netdev ); + } + + /* Check link state, if applicable */ + if ( intstatus & ICP_INTSTATUS_LINKEVENT ) { + writew ( ICP_INTSTATUS_LINKEVENT, icp->regs + ICP_INTSTATUS ); + icplus_check_link ( netdev ); + } + + /* Refill receive ring */ + icplus_refill_rx ( icp ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void icplus_irq ( struct net_device *netdev, int enable ) { + struct icplus_nic *icp = netdev->priv; + + DBGC ( icp, "ICPLUS %p does not yet support interrupts\n", icp ); + ( void ) enable; +} + +/** IC+ network device operations */ +static struct net_device_operations icplus_operations = { + .open = icplus_open, + .close = icplus_close, + .transmit = icplus_transmit, + .poll = icplus_poll, + .irq = icplus_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int icplus_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct icplus_nic *icp; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *icp ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &icplus_operations ); + icp = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( icp, 0, sizeof ( *icp ) ); + icp->miibit.basher.op = &icplus_basher_ops; + init_mii_bit_basher ( &icp->miibit ); + mii_init ( &icp->mii, &icp->miibit.mdio, 0 ); + icp->tx.listptr = ICP_TFDLISTPTR; + icp->rx.listptr = ICP_RFDLISTPTR; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + icp->regs = ioremap ( pci->membase, ICP_BAR_SIZE ); + if ( ! icp->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the NIC */ + if ( ( rc = icplus_reset ( icp ) ) != 0 ) + goto err_reset; + + /* Initialise EEPROM */ + icplus_init_eeprom ( icp ); + + /* Read EEPROM MAC address */ + if ( ( rc = nvs_read ( &icp->eeprom, ICP_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not read EEPROM MAC address: %s\n", + icp, strerror ( rc ) ); + goto err_eeprom; + } + + /* Configure PHY */ + if ( ( rc = icplus_init_phy ( icp ) ) != 0 ) + goto err_phy; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + icplus_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_phy: + err_eeprom: + icplus_reset ( icp ); + err_reset: + iounmap ( icp->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void icplus_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct icplus_nic *icp = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + icplus_reset ( icp ); + + /* Free network device */ + iounmap ( icp->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** IC+ PCI device IDs */ +static struct pci_device_id icplus_nics[] = { + PCI_ROM ( 0x13f0, 0x1023, "ip1000a", "IP1000A", 0 ), +}; + +/** IC+ PCI driver */ +struct pci_driver icplus_driver __pci_driver = { + .ids = icplus_nics, + .id_count = ( sizeof ( icplus_nics ) / sizeof ( icplus_nics[0] ) ), + .probe = icplus_probe, + .remove = icplus_remove, +}; diff --git a/src/drivers/net/icplus.h b/src/drivers/net/icplus.h new file mode 100644 index 000000000..35fa422ad --- /dev/null +++ b/src/drivers/net/icplus.h @@ -0,0 +1,206 @@ +#ifndef _ICPLUS_H +#define _ICPLUS_H + +/** @file + * + * IC+ network driver + * + */ + +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** BAR size */ +#define ICP_BAR_SIZE 0x200 + +/** Alignment requirement */ +#define ICP_ALIGN 0x8 + +/** Base address low register offset */ +#define ICP_BASE_LO 0x0 + +/** Base address high register offset */ +#define ICP_BASE_HI 0x4 + +/** ASIC control register (double word) */ +#define ICP_ASICCTRL 0x30 +#define ICP_ASICCTRL_PHYSPEED1000 0x00000040UL /**< PHY speed 1000 */ +#define ICP_ASICCTRL_GLOBALRESET 0x00010000UL /**< Global reset */ +#define ICP_ASICCTRL_DMA 0x00080000UL /**< DMA */ +#define ICP_ASICCTRL_FIFO 0x00100000UL /**< FIFO */ +#define ICP_ASICCTRL_NETWORK 0x00200000UL /**< Network */ +#define ICP_ASICCTRL_HOST 0x00400000UL /**< Host */ +#define ICP_ASICCTRL_AUTOINIT 0x00800000UL /**< Auto init */ +#define ICP_ASICCTRL_RESETBUSY 0x04000000UL /**< Reset busy */ + +/** Maximum time to wait for reset */ +#define ICP_RESET_MAX_WAIT_MS 1000 + +/** DMA control register (word/double word) */ +#define ICP_DMACTRL 0x00 +#define ICP_DMACTRL_RXPOLLNOW 0x0010 /**< Receive poll now */ +#define ICP_DMACTRL_TXPOLLNOW 0x1000 /**< Transmit poll now */ + +/** EEPROM control register (word) */ +#define ICP_EEPROMCTRL 0x4a +#define ICP_EEPROMCTRL_ADDRESS( x ) ( (x) << 0 ) /**< Address */ +#define ICP_EEPROMCTRL_OPCODE( x ) ( (x) << 8 ) /**< Opcode */ +#define ICP_EEPROMCTRL_OPCODE_READ \ + ICP_EEPROMCTRL_OPCODE ( 2 ) /**< Read register */ +#define ICP_EEPROMCTRL_BUSY 0x8000 /**< EEPROM busy */ + +/** Maximum time to wait for reading EEPROM */ +#define ICP_EEPROM_MAX_WAIT_MS 1000 + +/** EEPROM word length */ +#define ICP_EEPROM_WORD_LEN_LOG2 1 + +/** Minimum EEPROM size, in words */ +#define ICP_EEPROM_MIN_SIZE_WORDS 0x20 + +/** Address of MAC address within EEPROM */ +#define ICP_EEPROM_MAC 0x10 + +/** EEPROM data register (word) */ +#define ICP_EEPROMDATA 0x48 + +/** Interupt status register (word) */ +#define ICP_INTSTATUS 0x5e +#define ICP_INTSTATUS_TXCOMPLETE 0x0004 /**< TX complete */ +#define ICP_INTSTATUS_LINKEVENT 0x0100 /**< Link event */ +#define ICP_INTSTATUS_RXDMACOMPLETE 0x0400 /**< RX DMA complete */ + +/** MAC control register (double word) */ +#define ICP_MACCTRL 0x6c +#define ICP_MACCTRL_DUPLEX 0x00000020UL /**< Duplex select */ +#define ICP_MACCTRL_TXENABLE 0x01000000UL /**< TX enable */ +#define ICP_MACCTRL_TXDISABLE 0x02000000UL /**< TX disable */ +#define ICP_MACCTRL_RXENABLE 0x08000000UL /**< RX enable */ +#define ICP_MACCTRL_RXDISABLE 0x10000000UL /**< RX disable */ + +/** PHY control register (byte) */ +#define ICP_PHYCTRL 0x76 +#define ICP_PHYCTRL_MGMTCLK 0x01 /**< Management clock */ +#define ICP_PHYCTRL_MGMTDATA 0x02 /**< Management data */ +#define ICP_PHYCTRL_MGMTDIR 0x04 /**< Management direction */ +#define ICP_PHYCTRL_LINKSPEED 0xc0 /**< Link speed */ + +/** Receive mode register (word) */ +#define ICP_RXMODE 0x88 +#define ICP_RXMODE_UNICAST 0x0001 /**< Receive unicast */ +#define ICP_RXMODE_MULTICAST 0x0002 /**< Receice multicast */ +#define ICP_RXMODE_BROADCAST 0x0004 /**< Receive broadcast */ +#define ICP_RXMODE_ALLFRAMES 0x0008 /**< Receive all frames */ + +/** List pointer receive register */ +#define ICP_RFDLISTPTR 0x1c + +/** List pointer transmit register */ +#define ICP_TFDLISTPTR 0x10 + +/** Transmit status register */ +#define ICP_TXSTATUS 0x60 +#define ICP_TXSTATUS_ERROR 0x00000001UL /**< TX error */ + +/** Data fragment */ +union icplus_fragment { + /** Address of data */ + uint64_t address; + /** Length */ + struct { + /** Reserved */ + uint8_t reserved[6]; + /** Length of data */ + uint16_t len; + }; +}; + +/** Transmit or receive descriptor */ +struct icplus_descriptor { + /** Address of next descriptor */ + uint64_t next; + /** Actual length */ + uint16_t len; + /** Flags */ + uint8_t flags; + /** Control */ + uint8_t control; + /** VLAN */ + uint16_t vlan; + /** Reserved */ + uint16_t reserved_a; + /** Data buffer */ + union icplus_fragment data; + /** Reserved */ + uint8_t reserved_b[8]; +}; + +/** Descriptor complete */ +#define ICP_DONE 0x80 + +/** Transmit alignment disabled */ +#define ICP_TX_UNALIGN 0x01 + +/** Request transmit completion */ +#define ICP_TX_INDICATE 0x40 + +/** Sole transmit fragment */ +#define ICP_TX_SOLE_FRAG 0x01 + +/** Recieve frame overrun error */ +#define ICP_RX_ERR_OVERRUN 0x01 + +/** Receive runt frame error */ +#define ICP_RX_ERR_RUNT 0x02 + +/** Receive alignment error */ +#define ICP_RX_ERR_ALIGN 0x04 + +/** Receive FCS error */ +#define ICP_RX_ERR_FCS 0x08 + +/** Receive oversized frame error */ +#define ICP_RX_ERR_OVERSIZED 0x10 + +/** Recieve length error */ +#define ICP_RX_ERR_LEN 0x20 + +/** Descriptor ring */ +struct icplus_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Ring entries */ + struct icplus_descriptor *entry; + /* List pointer register */ + unsigned int listptr; +}; + +/** Number of descriptors */ +#define ICP_NUM_DESC 4 + +/** Maximum receive packet length */ +#define ICP_RX_MAX_LEN ETH_FRAME_LEN + +/** An IC+ network card */ +struct icplus_nic { + /** Registers */ + void *regs; + /** EEPROM */ + struct nvs_device eeprom; + /** MII bit bashing interface */ + struct mii_bit_basher miibit; + /** MII device */ + struct mii_device mii; + /** Transmit descriptor ring */ + struct icplus_ring tx; + /** Receive descriptor ring */ + struct icplus_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[ICP_NUM_DESC]; +}; + +#endif /* _ICPLUS_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e465d188b..8e989e42d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -203,6 +203,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_smscusb ( ERRFILE_DRIVER | 0x00c70000 ) #define ERRFILE_lan78xx ( ERRFILE_DRIVER | 0x00c80000 ) #define ERRFILE_ena ( ERRFILE_DRIVER | 0x00c90000 ) +#define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )