summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Rettberg2019-08-02 10:04:22 +0200
committerSimon Rettberg2019-08-02 10:04:22 +0200
commitbebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333 (patch)
tree6a7538ee3a4e5daf2d98d76b47e95b859c5aa265 /src
parent[efi] Add very ugly hack to use HW RNG on EFI (diff)
parent[build] Do not apply WORKAROUND_CFLAGS for host compiler (diff)
downloadipxe-bebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333.tar.gz
ipxe-bebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333.tar.xz
ipxe-bebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.housekeeping11
-rw-r--r--src/arch/arm/include/ipxe/arm_io.h77
-rw-r--r--src/config/config_fdt.c41
-rw-r--r--src/config/defaults/efi.h1
-rw-r--r--src/config/fdt.h16
-rw-r--r--src/config/rpi/colour.h0
-rw-r--r--src/config/rpi/console.h0
-rw-r--r--src/config/rpi/crypto.h0
-rw-r--r--src/config/rpi/general.h0
-rw-r--r--src/config/rpi/serial.h0
-rw-r--r--src/config/rpi/settings.h0
-rw-r--r--src/config/rpi/sideband.h0
-rw-r--r--src/config/rpi/usb.h13
-rw-r--r--src/core/fdt.c486
-rw-r--r--src/crypto/ocsp.c42
-rw-r--r--src/drivers/bus/pcimsix.c251
-rwxr-xr-xsrc/drivers/infiniband/golan.c3
-rw-r--r--src/drivers/infiniband/hermon.c18
-rw-r--r--src/drivers/net/intel.c8
-rw-r--r--src/drivers/net/intelxl.c768
-rw-r--r--src/drivers/net/intelxl.h373
-rw-r--r--src/drivers/net/intelxlvf.c719
-rw-r--r--src/drivers/net/intelxlvf.h86
-rw-r--r--src/drivers/net/lan78xx.c4
-rw-r--r--src/drivers/net/smsc95xx.c4
-rw-r--r--src/drivers/net/smscusb.c34
-rw-r--r--src/drivers/net/smscusb.h1
-rw-r--r--src/include/errno.h4
-rw-r--r--src/include/ipxe/efi/efi_blacklist.h13
-rw-r--r--src/include/ipxe/errfile.h4
-rw-r--r--src/include/ipxe/fdt.h102
-rw-r--r--src/include/ipxe/ocsp.h4
-rw-r--r--src/include/ipxe/pci.h11
-rw-r--r--src/include/ipxe/pcimsix.h77
-rw-r--r--src/include/ipxe/tls.h45
-rw-r--r--src/include/ipxe/vlan.h8
-rw-r--r--src/interface/efi/efi_blacklist.c237
-rw-r--r--src/interface/efi/efi_debug.c3
-rw-r--r--src/interface/efi/efi_driver.c9
-rw-r--r--src/interface/efi/efi_fdt.c70
-rw-r--r--src/interface/efi/efiprefix.c6
-rw-r--r--src/net/netdevice.c34
-rw-r--r--src/net/tcp.c21
-rw-r--r--src/net/tls.c318
-rw-r--r--src/net/validator.c175
-rw-r--r--src/net/vlan.c47
-rw-r--r--src/util/elf2efi.c6
47 files changed, 3763 insertions, 387 deletions
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping
index f8334921b..1b175b950 100644
--- a/src/Makefile.housekeeping
+++ b/src/Makefile.housekeeping
@@ -185,6 +185,15 @@ WNST_TEST = $(CC) -Wstringop-truncation -x c -c /dev/null -o /dev/null \
>/dev/null 2>&1
WNST_FLAGS := $(shell $(WNST_TEST) && $(ECHO) '-Wno-stringop-truncation')
WORKAROUND_CFLAGS += $(WNST_FLAGS)
+
+# gcc 9.1 generates warnings for taking address of packed member which
+# may result in an unaligned pointer value. Inhibit the warnings.
+#
+WNAPM_TEST = $(CC) -Wno-address-of-packed-member -x c -c /dev/null \
+ -o /dev/null >/dev/null 2>&1
+WNAPM_FLAGS := $(shell $(WNAPM_TEST) && \
+ $(ECHO) '-Wno-address-of-packed-member')
+WORKAROUND_CFLAGS += $(WNAPM_FLAGS)
endif
# Some versions of gas choke on division operators, treating them as
@@ -445,7 +454,7 @@ endif
CFLAGS += $(WORKAROUND_CFLAGS) $(EXTRA_CFLAGS)
ASFLAGS += $(WORKAROUND_ASFLAGS) $(EXTRA_ASFLAGS)
LDFLAGS += $(WORKAROUND_LDFLAGS) $(EXTRA_LDFLAGS)
-HOST_CFLAGS += $(WORKAROUND_CFLAGS) -O2 -g
+HOST_CFLAGS += -O2 -g
# Inhibit -Werror if NO_WERROR is specified on make command line
#
diff --git a/src/arch/arm/include/ipxe/arm_io.h b/src/arch/arm/include/ipxe/arm_io.h
index f8765af75..105f22bfb 100644
--- a/src/arch/arm/include/ipxe/arm_io.h
+++ b/src/arch/arm/include/ipxe/arm_io.h
@@ -43,43 +43,84 @@ IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) {
*
*/
-#define ARM_READX( _api_func, _type, _insn_suffix, _reg_prefix ) \
+#define ARM_READX( _suffix, _type, _insn_suffix, _reg_prefix ) \
static inline __always_inline _type \
-IOAPI_INLINE ( arm, _api_func ) ( volatile _type *io_addr ) { \
+IOAPI_INLINE ( arm, read ## _suffix ) ( volatile _type *io_addr ) { \
_type data; \
__asm__ __volatile__ ( "ldr" _insn_suffix " %" _reg_prefix "0, %1" \
: "=r" ( data ) : "Qo" ( *io_addr ) ); \
return data; \
}
#ifdef __aarch64__
-ARM_READX ( readb, uint8_t, "b", "w" );
-ARM_READX ( readw, uint16_t, "h", "w" );
-ARM_READX ( readl, uint32_t, "", "w" );
-ARM_READX ( readq, uint64_t, "", "" );
+ARM_READX ( b, uint8_t, "b", "w" );
+ARM_READX ( w, uint16_t, "h", "w" );
+ARM_READX ( l, uint32_t, "", "w" );
+ARM_READX ( q, uint64_t, "", "" );
#else
-ARM_READX ( readb, uint8_t, "b", "" );
-ARM_READX ( readw, uint16_t, "h", "" );
-ARM_READX ( readl, uint32_t, "", "" );
+ARM_READX ( b, uint8_t, "b", "" );
+ARM_READX ( w, uint16_t, "h", "" );
+ARM_READX ( l, uint32_t, "", "" );
#endif
-#define ARM_WRITEX( _api_func, _type, _insn_suffix, _reg_prefix ) \
+#define ARM_WRITEX( _suffix, _type, _insn_suffix, _reg_prefix ) \
static inline __always_inline void \
-IOAPI_INLINE ( arm, _api_func ) ( _type data, volatile _type *io_addr ) { \
+IOAPI_INLINE ( arm, write ## _suffix ) ( _type data, \
+ volatile _type *io_addr ) { \
__asm__ __volatile__ ( "str" _insn_suffix " %" _reg_prefix "0, %1" \
: : "r" ( data ), "Qo" ( *io_addr ) ); \
}
#ifdef __aarch64__
-ARM_WRITEX ( writeb, uint8_t, "b", "w" );
-ARM_WRITEX ( writew, uint16_t, "h", "w" );
-ARM_WRITEX ( writel, uint32_t, "", "w" );
-ARM_WRITEX ( writeq, uint64_t, "", "" );
+ARM_WRITEX ( b, uint8_t, "b", "w" );
+ARM_WRITEX ( w, uint16_t, "h", "w" );
+ARM_WRITEX ( l, uint32_t, "", "w" );
+ARM_WRITEX ( q, uint64_t, "", "" );
#else
-ARM_WRITEX ( writeb, uint8_t, "b", "" );
-ARM_WRITEX ( writew, uint16_t, "h", "" );
-ARM_WRITEX ( writel, uint32_t, "", "" );
+ARM_WRITEX ( b, uint8_t, "b", "" );
+ARM_WRITEX ( w, uint16_t, "h", "" );
+ARM_WRITEX ( l, uint32_t, "", "" );
#endif
/*
+ * Dummy PIO reads and writes up to 32 bits
+ *
+ * There is no common standard for I/O-space access for ARM, and
+ * non-MMIO peripherals are vanishingly rare. Provide dummy
+ * implementations that will allow code to link and should cause
+ * drivers to simply fail to detect hardware at runtime.
+ *
+ */
+
+#define ARM_INX( _suffix, _type ) \
+static inline __always_inline _type \
+IOAPI_INLINE ( arm, in ## _suffix ) ( volatile _type *io_addr __unused) { \
+ return ~( (_type) 0 ); \
+} \
+static inline __always_inline void \
+IOAPI_INLINE ( arm, ins ## _suffix ) ( volatile _type *io_addr __unused, \
+ _type *data, unsigned int count ) { \
+ memset ( data, 0xff, count * sizeof ( *data ) ); \
+}
+ARM_INX ( b, uint8_t );
+ARM_INX ( w, uint16_t );
+ARM_INX ( l, uint32_t );
+
+#define ARM_OUTX( _suffix, _type ) \
+static inline __always_inline void \
+IOAPI_INLINE ( arm, out ## _suffix ) ( _type data __unused, \
+ volatile _type *io_addr __unused ) { \
+ /* Do nothing */ \
+} \
+static inline __always_inline void \
+IOAPI_INLINE ( arm, outs ## _suffix ) ( volatile _type *io_addr __unused, \
+ const _type *data __unused, \
+ unsigned int count __unused ) { \
+ /* Do nothing */ \
+}
+ARM_OUTX ( b, uint8_t );
+ARM_OUTX ( w, uint16_t );
+ARM_OUTX ( l, uint32_t );
+
+/*
* Slow down I/O
*
*/
diff --git a/src/config/config_fdt.c b/src/config/config_fdt.c
new file mode 100644
index 000000000..e8d425933
--- /dev/null
+++ b/src/config/config_fdt.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <config/fdt.h>
+
+/** @file
+ *
+ * Flattened Device Tree configuration options
+ *
+ */
+
+PROVIDE_REQUIRING_SYMBOL();
+
+/*
+ * Drag in devicetree sources
+ */
+#ifdef FDT_EFI
+REQUIRE_OBJECT ( efi_fdt );
+#endif
diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h
index 74effa425..53a7a7b4b 100644
--- a/src/config/defaults/efi.h
+++ b/src/config/defaults/efi.h
@@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define TIME_EFI
#define REBOOT_EFI
#define ACPI_EFI
+#define FDT_EFI
#define DOWNLOAD_PROTO_FILE /* Local filesystem access */
diff --git a/src/config/fdt.h b/src/config/fdt.h
new file mode 100644
index 000000000..4d13e0535
--- /dev/null
+++ b/src/config/fdt.h
@@ -0,0 +1,16 @@
+#ifndef CONFIG_FDT_H
+#define CONFIG_FDT_H
+
+/** @file
+ *
+ * Flattened Device Tree configuration
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <config/defaults.h>
+
+#include <config/local/fdt.h>
+
+#endif /* CONFIG_FDT_H */
diff --git a/src/config/rpi/colour.h b/src/config/rpi/colour.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/colour.h
diff --git a/src/config/rpi/console.h b/src/config/rpi/console.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/console.h
diff --git a/src/config/rpi/crypto.h b/src/config/rpi/crypto.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/crypto.h
diff --git a/src/config/rpi/general.h b/src/config/rpi/general.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/general.h
diff --git a/src/config/rpi/serial.h b/src/config/rpi/serial.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/serial.h
diff --git a/src/config/rpi/settings.h b/src/config/rpi/settings.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/settings.h
diff --git a/src/config/rpi/sideband.h b/src/config/rpi/sideband.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/config/rpi/sideband.h
diff --git a/src/config/rpi/usb.h b/src/config/rpi/usb.h
new file mode 100644
index 000000000..f17ea0de3
--- /dev/null
+++ b/src/config/rpi/usb.h
@@ -0,0 +1,13 @@
+/*
+ * Use EFI_USB_IO_PROTOCOL
+ *
+ * The Raspberry Pi uses an embedded DesignWare USB controller for
+ * which we do not have a native driver. Use via the
+ * EFI_USB_IO_PROTOCOL driver instead.
+ *
+ */
+#undef USB_HCD_XHCI
+#undef USB_HCD_EHCI
+#undef USB_HCD_UHCI
+#define USB_HCD_USBIO
+#undef USB_EFI
diff --git a/src/core/fdt.c b/src/core/fdt.c
new file mode 100644
index 000000000..f439422cf
--- /dev/null
+++ b/src/core/fdt.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/fdt.h>
+
+/** @file
+ *
+ * Flattened Device Tree
+ *
+ */
+
+/** The system flattened device tree (if present) */
+static struct fdt fdt;
+
+/** A position within a device tree */
+struct fdt_cursor {
+ /** Offset within structure block */
+ unsigned int offset;
+ /** Tree depth */
+ int depth;
+};
+
+/** A lexical descriptor */
+struct fdt_descriptor {
+ /** Node or property name (if applicable) */
+ const char *name;
+ /** Property data (if applicable) */
+ const void *data;
+ /** Length of property data (if applicable) */
+ size_t len;
+};
+
+/**
+ * Check if device tree exists
+ *
+ * @v has_fdt Device tree exists
+ */
+static inline __attribute__ (( always_inline )) int fdt_exists ( void ) {
+
+ return ( fdt.hdr != NULL );
+}
+
+/**
+ * Traverse device tree
+ *
+ * @v pos Position within device tree
+ * @v desc Lexical descriptor to fill in
+ * @ret rc Return status code
+ */
+static int fdt_traverse ( struct fdt_cursor *pos,
+ struct fdt_descriptor *desc ) {
+ const fdt_token_t *token;
+ const void *data;
+ const struct fdt_prop *prop;
+ unsigned int name_off;
+ size_t remaining;
+ size_t len;
+
+ /* Sanity checks */
+ assert ( pos->offset < fdt.len );
+ assert ( ( pos->offset & ( FDT_STRUCTURE_ALIGN - 1 ) ) == 0 );
+
+ /* Clear descriptor */
+ memset ( desc, 0, sizeof ( *desc ) );
+
+ /* Locate token and calculate remaining space */
+ token = ( fdt.raw + fdt.structure + pos->offset );
+ remaining = ( fdt.len - pos->offset );
+ if ( remaining < sizeof ( *token ) ) {
+ DBGC ( &fdt, "FDT truncated tree at +%#04x\n", pos->offset );
+ return -EINVAL;
+ }
+ remaining -= sizeof ( *token );
+ data = ( ( ( const void * ) token ) + sizeof ( *token ) );
+ len = 0;
+
+ /* Handle token */
+ switch ( *token ) {
+
+ case cpu_to_be32 ( FDT_BEGIN_NODE ):
+
+ /* Start of node */
+ desc->name = data;
+ len = ( strnlen ( desc->name, remaining ) + 1 /* NUL */ );
+ if ( remaining < len ) {
+ DBGC ( &fdt, "FDT unterminated node name at +%#04x\n",
+ pos->offset );
+ return -EINVAL;
+ }
+ pos->depth++;
+ break;
+
+ case cpu_to_be32 ( FDT_END_NODE ):
+
+ /* End of node */
+ if ( pos->depth < 0 ) {
+ DBGC ( &fdt, "FDT spurious node end at +%#04x\n",
+ pos->offset );
+ return -EINVAL;
+ }
+ pos->depth--;
+ if ( pos->depth < 0 ) {
+ /* End of (sub)tree */
+ return -ENOENT;
+ }
+ break;
+
+ case cpu_to_be32 ( FDT_PROP ):
+
+ /* Property */
+ prop = data;
+ if ( remaining < sizeof ( *prop ) ) {
+ DBGC ( &fdt, "FDT truncated property at +%#04x\n",
+ pos->offset );
+ return -EINVAL;
+ }
+ desc->data = ( ( ( const void * ) prop ) + sizeof ( *prop ) );
+ desc->len = be32_to_cpu ( prop->len );
+ len = ( sizeof ( *prop ) + desc->len );
+ if ( remaining < len ) {
+ DBGC ( &fdt, "FDT overlength property at +%#04x\n",
+ pos->offset );
+ return -EINVAL;
+ }
+ name_off = be32_to_cpu ( prop->name_off );
+ if ( name_off > fdt.strings_len ) {
+ DBGC ( &fdt, "FDT property name outside strings "
+ "block at +%#04x\n", pos->offset );
+ return -EINVAL;
+ }
+ desc->name = ( fdt.raw + fdt.strings + name_off );
+ break;
+
+ case cpu_to_be32 ( FDT_NOP ):
+
+ /* Do nothing */
+ break;
+
+ default:
+
+ /* Unrecognised or unexpected token */
+ DBGC ( &fdt, "FDT unexpected token %#08x at +%#04x\n",
+ be32_to_cpu ( *token ), pos->offset );
+ return -EINVAL;
+ }
+
+ /* Update cursor */
+ len = ( ( len + FDT_STRUCTURE_ALIGN - 1 ) &
+ ~( FDT_STRUCTURE_ALIGN - 1 ) );
+ pos->offset += ( sizeof ( *token ) + len );
+
+ /* Sanity checks */
+ assert ( pos->offset <= fdt.len );
+
+ return 0;
+}
+
+/**
+ * Find child node
+ *
+ * @v offset Starting node offset
+ * @v name Node name
+ * @v child Child node offset to fill in
+ * @ret rc Return status code
+ */
+static int fdt_child ( unsigned int offset, const char *name,
+ unsigned int *child ) {
+ struct fdt_cursor pos;
+ struct fdt_descriptor desc;
+ unsigned int orig_offset;
+ int rc;
+
+ /* Record original offset (for debugging) */
+ orig_offset = offset;
+
+ /* Initialise cursor */
+ pos.offset = offset;
+ pos.depth = -1;
+
+ /* Find child node */
+ while ( 1 ) {
+
+ /* Record current offset */
+ *child = pos.offset;
+
+ /* Traverse tree */
+ if ( ( rc = fdt_traverse ( &pos, &desc ) ) != 0 ) {
+ DBGC ( &fdt, "FDT +%#04x has no child node \"%s\": "
+ "%s\n", orig_offset, name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check for matching immediate child node */
+ if ( ( pos.depth == 1 ) && desc.name && ( ! desc.data ) ) {
+ DBGC2 ( &fdt, "FDT +%#04x has child node \"%s\"\n",
+ orig_offset, desc.name );
+ if ( strcmp ( name, desc.name ) == 0 ) {
+ DBGC2 ( &fdt, "FDT +%#04x found child node "
+ "\"%s\" at +%#04x\n", orig_offset,
+ desc.name, *child );
+ return 0;
+ }
+ }
+ }
+}
+
+/**
+ * Find node by path
+ *
+ * @v path Node path
+ * @v offset Offset to fill in
+ * @ret rc Return status code
+ */
+int fdt_path ( const char *path, unsigned int *offset ) {
+ char *tmp = ( ( char * ) path );
+ char *del;
+ int rc;
+
+ /* Initialise offset */
+ *offset = 0;
+
+ /* Traverse tree one path segment at a time */
+ while ( *tmp ) {
+
+ /* Skip any leading '/' */
+ while ( *tmp == '/' )
+ tmp++;
+
+ /* Find next '/' delimiter and convert to NUL */
+ del = strchr ( tmp, '/' );
+ if ( del )
+ *del = '\0';
+
+ /* Find child and restore delimiter */
+ rc = fdt_child ( *offset, tmp, offset );
+ if ( del )
+ *del = '/';
+ if ( rc != 0 )
+ return rc;
+
+ /* Move to next path component, if any */
+ while ( *tmp && ( *tmp != '/' ) )
+ tmp++;
+ }
+
+ DBGC2 ( &fdt, "FDT found path \"%s\" at +%#04x\n", path, *offset );
+ return 0;
+}
+
+/**
+ * Find node by alias
+ *
+ * @v name Alias name
+ * @v offset Offset to fill in
+ * @ret rc Return status code
+ */
+int fdt_alias ( const char *name, unsigned int *offset ) {
+ const char *alias;
+ int rc;
+
+ /* Locate "/aliases" node */
+ if ( ( rc = fdt_child ( 0, "aliases", offset ) ) != 0 )
+ return rc;
+
+ /* Locate alias property */
+ if ( ( alias = fdt_string ( *offset, name ) ) == NULL )
+ return -ENOENT;
+ DBGC ( &fdt, "FDT alias \"%s\" is \"%s\"\n", name, alias );
+
+ /* Locate aliased node */
+ if ( ( rc = fdt_path ( alias, offset ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Find property
+ *
+ * @v offset Starting node offset
+ * @v name Property name
+ * @v desc Lexical descriptor to fill in
+ * @ret rc Return status code
+ */
+static int fdt_property ( unsigned int offset, const char *name,
+ struct fdt_descriptor *desc ) {
+ struct fdt_cursor pos;
+ int rc;
+
+ /* Initialise cursor */
+ pos.offset = offset;
+ pos.depth = -1;
+
+ /* Find property */
+ while ( 1 ) {
+
+ /* Traverse tree */
+ if ( ( rc = fdt_traverse ( &pos, desc ) ) != 0 ) {
+ DBGC ( &fdt, "FDT +%#04x has no property \"%s\": %s\n",
+ offset, name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check for matching immediate child property */
+ if ( ( pos.depth == 0 ) && desc->data ) {
+ DBGC2 ( &fdt, "FDT +%#04x has property \"%s\" len "
+ "%#zx\n", offset, desc->name, desc->len );
+ if ( strcmp ( name, desc->name ) == 0 ) {
+ DBGC2 ( &fdt, "FDT +%#04x found property "
+ "\"%s\"\n", offset, desc->name );
+ DBGC2_HDA ( &fdt, 0, desc->data, desc->len );
+ return 0;
+ }
+ }
+ }
+}
+
+/**
+ * Find string property
+ *
+ * @v offset Starting node offset
+ * @v name Property name
+ * @ret string String property, or NULL on error
+ */
+const char * fdt_string ( unsigned int offset, const char *name ) {
+ struct fdt_descriptor desc;
+ int rc;
+
+ /* Find property */
+ if ( ( rc = fdt_property ( offset, name, &desc ) ) != 0 )
+ return NULL;
+
+ /* Check NUL termination */
+ if ( strnlen ( desc.data, desc.len ) == desc.len ) {
+ DBGC ( &fdt, "FDT unterminated string property \"%s\"\n",
+ name );
+ return NULL;
+ }
+
+ return desc.data;
+}
+
+/**
+ * Get MAC address from property
+ *
+ * @v offset Starting node offset
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+int fdt_mac ( unsigned int offset, struct net_device *netdev ) {
+ struct fdt_descriptor desc;
+ size_t len;
+ int rc;
+
+ /* Find applicable MAC address property */
+ if ( ( ( rc = fdt_property ( offset, "mac-address", &desc ) ) != 0 ) &&
+ ( ( rc = fdt_property ( offset, "local-mac-address",
+ &desc ) ) != 0 ) ) {
+ return rc;
+ }
+
+ /* Check length */
+ len = netdev->ll_protocol->hw_addr_len;
+ if ( len != desc.len ) {
+ DBGC ( &fdt, "FDT malformed MAC address \"%s\":\n",
+ desc.name );
+ DBGC_HDA ( &fdt, 0, desc.data, desc.len );
+ return -ERANGE;
+ }
+
+ /* Fill in MAC address */
+ memcpy ( netdev->hw_addr, desc.data, len );
+
+ return 0;
+}
+
+/**
+ * Register device tree
+ *
+ * @v fdt Device tree header
+ * @ret rc Return status code
+ */
+int register_fdt ( const struct fdt_header *hdr ) {
+ const uint8_t *end;
+
+ /* Record device tree location */
+ fdt.hdr = hdr;
+ fdt.len = be32_to_cpu ( hdr->totalsize );
+ DBGC ( &fdt, "FDT version %d at %p+%#04zx\n",
+ be32_to_cpu ( hdr->version ), fdt.hdr, fdt.len );
+
+ /* Check signature */
+ if ( hdr->magic != cpu_to_be32 ( FDT_MAGIC ) ) {
+ DBGC ( &fdt, "FDT has invalid magic value %#08x\n",
+ be32_to_cpu ( hdr->magic ) );
+ goto err;
+ }
+
+ /* Check version */
+ if ( hdr->last_comp_version != cpu_to_be32 ( FDT_VERSION ) ) {
+ DBGC ( &fdt, "FDT unsupported version %d\n",
+ be32_to_cpu ( hdr->last_comp_version ) );
+ goto err;
+ }
+
+ /* Record structure block location */
+ fdt.structure = be32_to_cpu ( hdr->off_dt_struct );
+ fdt.structure_len = be32_to_cpu ( hdr->size_dt_struct );
+ DBGC ( &fdt, "FDT structure block at +[%#04x,%#04zx)\n",
+ fdt.structure, ( fdt.structure + fdt.structure_len ) );
+ if ( ( fdt.structure > fdt.len ) ||
+ ( fdt.structure_len > ( fdt.len - fdt.structure ) ) ) {
+ DBGC ( &fdt, "FDT structure block exceeds table\n" );
+ goto err;
+ }
+ if ( ( fdt.structure | fdt.structure_len ) &
+ ( FDT_STRUCTURE_ALIGN - 1 ) ) {
+ DBGC ( &fdt, "FDT structure block is misaligned\n" );
+ goto err;
+ }
+
+ /* Record strings block location */
+ fdt.strings = be32_to_cpu ( hdr->off_dt_strings );
+ fdt.strings_len = be32_to_cpu ( hdr->size_dt_strings );
+ DBGC ( &fdt, "FDT strings block at +[%#04x,%#04zx)\n",
+ fdt.strings, ( fdt.strings + fdt.strings_len ) );
+ if ( ( fdt.strings > fdt.len ) ||
+ ( fdt.strings_len > ( fdt.len - fdt.strings ) ) ) {
+ DBGC ( &fdt, "FDT strings block exceeds table\n" );
+ goto err;
+ }
+
+ /* Shrink strings block to ensure NUL termination safety */
+ end = ( fdt.raw + fdt.strings + fdt.strings_len );
+ for ( ; fdt.strings_len ; fdt.strings_len-- ) {
+ if ( *(--end) == '\0' )
+ break;
+ }
+ if ( fdt.strings_len != be32_to_cpu ( hdr->size_dt_strings ) ) {
+ DBGC ( &fdt, "FDT strings block shrunk to +[%#04x,%#04zx)\n",
+ fdt.strings, ( fdt.strings + fdt.strings_len ) );
+ }
+
+ /* Print model name (for debugging) */
+ DBGC ( &fdt, "FDT model is \"%s\"\n", fdt_string ( 0, "model" ) );
+
+ return 0;
+
+ err:
+ DBGC_HDA ( &fdt, 0, hdr, sizeof ( *hdr ) );
+ fdt.hdr = NULL;
+ return -EINVAL;
+}
+
+/* Drag in objects via register_fdt */
+REQUIRING_SYMBOL ( register_fdt );
+
+/* Drag in device tree configuration */
+REQUIRE_OBJECT ( config_fdt );
diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c
index b83f4c035..2c747fb39 100644
--- a/src/crypto/ocsp.c
+++ b/src/crypto/ocsp.c
@@ -145,7 +145,7 @@ static void ocsp_free ( struct refcnt *refcnt ) {
static int ocsp_request ( struct ocsp_check *ocsp ) {
struct digest_algorithm *digest = &ocsp_digest_algorithm;
struct asn1_builder *builder = &ocsp->request.builder;
- struct asn1_cursor *cert_id = &ocsp->request.cert_id;
+ struct asn1_cursor *cert_id_tail = &ocsp->request.cert_id_tail;
uint8_t digest_ctx[digest->ctxsize];
uint8_t name_digest[digest->digestsize];
uint8_t pubkey_digest[digest->digestsize];
@@ -186,12 +186,14 @@ static int ocsp_request ( struct ocsp_check *ocsp ) {
DBGC2_HDA ( ocsp, 0, builder->data, builder->len );
/* Parse certificate ID for comparison with response */
- cert_id->data = builder->data;
- cert_id->len = builder->len;
- if ( ( rc = ( asn1_enter ( cert_id, ASN1_SEQUENCE ),
- asn1_enter ( cert_id, ASN1_SEQUENCE ),
- asn1_enter ( cert_id, ASN1_SEQUENCE ),
- asn1_enter ( cert_id, ASN1_SEQUENCE ) ) ) != 0 ) {
+ cert_id_tail->data = builder->data;
+ cert_id_tail->len = builder->len;
+ if ( ( rc = ( asn1_enter ( cert_id_tail, ASN1_SEQUENCE ),
+ asn1_enter ( cert_id_tail, ASN1_SEQUENCE ),
+ asn1_enter ( cert_id_tail, ASN1_SEQUENCE ),
+ asn1_enter ( cert_id_tail, ASN1_SEQUENCE ),
+ asn1_enter ( cert_id_tail, ASN1_SEQUENCE ),
+ asn1_skip ( cert_id_tail, ASN1_SEQUENCE ) ) ) != 0 ) {
DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n",
ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
return rc;
@@ -475,15 +477,31 @@ static int ocsp_parse_responder_id ( struct ocsp_check *ocsp,
static int ocsp_parse_cert_id ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
+ struct asn1_algorithm *algorithm;
+ int rc;
- /* Check certID matches request */
+ /* Check certID algorithm */
memcpy ( &cursor, raw, sizeof ( cursor ) );
- asn1_shrink_any ( &cursor );
- if ( asn1_compare ( &cursor, &ocsp->request.cert_id ) != 0 ) {
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+ if ( ( rc = asn1_digest_algorithm ( &cursor, &algorithm ) ) != 0 ) {
+ DBGC ( ocsp, "OCSP %p \"%s\" certID unknown algorithm: %s\n",
+ ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
+ return rc;
+ }
+ if ( algorithm->digest != &ocsp_digest_algorithm ) {
+ DBGC ( ocsp, "OCSP %p \"%s\" certID wrong algorithm %s\n",
+ ocsp, x509_name ( ocsp->cert ),
+ algorithm->digest->name );
+ return -EACCES_CERT_MISMATCH;
+ }
+
+ /* Check remaining certID fields */
+ asn1_skip ( &cursor, ASN1_SEQUENCE );
+ if ( asn1_compare ( &cursor, &ocsp->request.cert_id_tail ) != 0 ) {
DBGC ( ocsp, "OCSP %p \"%s\" certID mismatch:\n",
ocsp, x509_name ( ocsp->cert ) );
- DBGC_HDA ( ocsp, 0, ocsp->request.cert_id.data,
- ocsp->request.cert_id.len );
+ DBGC_HDA ( ocsp, 0, ocsp->request.cert_id_tail.data,
+ ocsp->request.cert_id_tail.len );
DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
return -EACCES_CERT_MISMATCH;
}
diff --git a/src/drivers/bus/pcimsix.c b/src/drivers/bus/pcimsix.c
new file mode 100644
index 000000000..80893c418
--- /dev/null
+++ b/src/drivers/bus/pcimsix.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/pci.h>
+#include <ipxe/pcimsix.h>
+
+/** @file
+ *
+ * PCI MSI-X interrupts
+ *
+ */
+
+/**
+ * Get MSI-X descriptor name (for debugging)
+ *
+ * @v cfg Configuration space offset
+ * @ret name Descriptor name
+ */
+static const char * pci_msix_name ( unsigned int cfg ) {
+
+ switch ( cfg ) {
+ case PCI_MSIX_DESC_TABLE: return "table";
+ case PCI_MSIX_DESC_PBA: return "PBA";
+ default: return "<UNKNOWN>";
+ }
+}
+
+/**
+ * Map MSI-X BAR portion
+ *
+ * @v pci PCI device
+ * @v msix MSI-X capability
+ * @v cfg Configuration space offset
+ * @ret io I/O address
+ */
+static void * pci_msix_ioremap ( struct pci_device *pci, struct pci_msix *msix,
+ unsigned int cfg ) {
+ uint32_t desc;
+ unsigned int bar;
+ unsigned long start;
+ unsigned long offset;
+ unsigned long base;
+ void *io;
+
+ /* Read descriptor */
+ pci_read_config_dword ( pci, ( msix->cap + cfg ), &desc );
+
+ /* Get BAR */
+ bar = PCI_MSIX_DESC_BIR ( desc );
+ offset = PCI_MSIX_DESC_OFFSET ( desc );
+ start = pci_bar_start ( pci, PCI_BASE_ADDRESS ( bar ) );
+ if ( ! start ) {
+ DBGC ( msix, "MSI-X %p %s could not find BAR%d\n",
+ msix, pci_msix_name ( cfg ), bar );
+ return NULL;
+ }
+ base = ( start + offset );
+ DBGC ( msix, "MSI-X %p %s at %#08lx (BAR%d+%#lx)\n",
+ msix, pci_msix_name ( cfg ), base, bar, offset );
+
+ /* Map BAR portion */
+ io = ioremap ( ( start + offset ), PCI_MSIX_LEN );
+ if ( ! io ) {
+ DBGC ( msix, "MSI-X %p %s could not map %#08lx\n",
+ msix, pci_msix_name ( cfg ), base );
+ return NULL;
+ }
+
+ return io;
+}
+
+/**
+ * Enable MSI-X interrupts
+ *
+ * @v pci PCI device
+ * @v msix MSI-X capability
+ * @ret rc Return status code
+ */
+int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix ) {
+ uint16_t ctrl;
+ int rc;
+
+ /* Locate capability */
+ msix->cap = pci_find_capability ( pci, PCI_CAP_ID_MSIX );
+ if ( ! msix->cap ) {
+ DBGC ( msix, "MSI-X %p found no MSI-X capability in "
+ PCI_FMT "\n", msix, PCI_ARGS ( pci ) );
+ rc = -ENOENT;
+ goto err_cap;
+ }
+
+ /* Extract interrupt count */
+ pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
+ msix->count = ( PCI_MSIX_CTRL_SIZE ( ctrl ) + 1 );
+ DBGC ( msix, "MSI-X %p has %d vectors for " PCI_FMT "\n",
+ msix, msix->count, PCI_ARGS ( pci ) );
+
+ /* Map MSI-X table */
+ msix->table = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_TABLE );
+ if ( ! msix->table ) {
+ rc = -ENOENT;
+ goto err_table;
+ }
+
+ /* Map pending bit array */
+ msix->pba = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_PBA );
+ if ( ! msix->pba ) {
+ rc = -ENOENT;
+ goto err_pba;
+ }
+
+ /* Enable MSI-X */
+ ctrl &= ~PCI_MSIX_CTRL_MASK;
+ ctrl |= PCI_MSIX_CTRL_ENABLE;
+ pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
+
+ return 0;
+
+ iounmap ( msix->pba );
+ err_pba:
+ iounmap ( msix->table );
+ err_table:
+ err_cap:
+ return rc;
+}
+
+/**
+ * Disable MSI-X interrupts
+ *
+ * @v pci PCI device
+ * @v msix MSI-X capability
+ */
+void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix ) {
+ uint16_t ctrl;
+
+ /* Disable MSI-X */
+ pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
+ ctrl &= ~PCI_MSIX_CTRL_ENABLE;
+ pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
+
+ /* Unmap pending bit array */
+ iounmap ( msix->pba );
+
+ /* Unmap MSI-X table */
+ iounmap ( msix->table );
+}
+
+/**
+ * Map MSI-X interrupt vector
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ * @v address Message address
+ * @v data Message data
+ */
+void pci_msix_map ( struct pci_msix *msix, unsigned int vector,
+ physaddr_t address, uint32_t data ) {
+ void *base;
+
+ /* Sanity check */
+ assert ( vector < msix->count );
+
+ /* Map interrupt vector */
+ base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
+ writel ( ( address & 0xffffffffUL ), ( base + PCI_MSIX_ADDRESS_LO ) );
+ if ( sizeof ( address ) > sizeof ( uint32_t ) ) {
+ writel ( ( ( ( uint64_t ) address ) >> 32 ),
+ ( base + PCI_MSIX_ADDRESS_HI ) );
+ } else {
+ writel ( 0, ( base + PCI_MSIX_ADDRESS_HI ) );
+ }
+ writel ( data, ( base + PCI_MSIX_DATA ) );
+}
+
+/**
+ * Control MSI-X interrupt vector
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ * @v mask Control mask
+ */
+void pci_msix_control ( struct pci_msix *msix, unsigned int vector,
+ uint32_t mask ) {
+ void *base;
+ uint32_t ctrl;
+
+ /* Mask/unmask interrupt vector */
+ base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
+ ctrl = readl ( base + PCI_MSIX_CONTROL );
+ ctrl &= ~PCI_MSIX_CONTROL_MASK;
+ ctrl |= mask;
+ writel ( ctrl, ( base + PCI_MSIX_CONTROL ) );
+}
+
+/**
+ * Dump MSI-X interrupt state (for debugging)
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ */
+void pci_msix_dump ( struct pci_msix *msix, unsigned int vector ) {
+ void *base;
+ uint32_t address_hi;
+ uint32_t address_lo;
+ physaddr_t address;
+ uint32_t data;
+ uint32_t ctrl;
+ uint32_t pba;
+
+ /* Do nothing in non-debug builds */
+ if ( ! DBG_LOG )
+ return;
+
+ /* Mask/unmask interrupt vector */
+ base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
+ address_hi = readl ( base + PCI_MSIX_ADDRESS_HI );
+ address_lo = readl ( base + PCI_MSIX_ADDRESS_LO );
+ data = readl ( base + PCI_MSIX_DATA );
+ ctrl = readl ( base + PCI_MSIX_CONTROL );
+ pba = readl ( msix->pba );
+ address = ( ( ( ( uint64_t ) address_hi ) << 32 ) | address_lo );
+ DBGC ( msix, "MSI-X %p vector %d %#08x => %#08lx%s%s\n",
+ msix, vector, data, address,
+ ( ( ctrl & PCI_MSIX_CONTROL_MASK ) ? " (masked)" : "" ),
+ ( ( pba & ( 1 << vector ) ) ? " (pending)" : "" ) );
+}
diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c
index 18ebfb1e9..e96ba2698 100755
--- a/src/drivers/infiniband/golan.c
+++ b/src/drivers/infiniband/golan.c
@@ -2640,6 +2640,9 @@ static struct pci_device_id golan_nics[] = {
PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ),
PCI_ROM ( 0x15b3, 0x1017, "ConnectX-5", "ConnectX-5 HCA driver, DevID 4119", 0 ),
PCI_ROM ( 0x15b3, 0x1019, "ConnectX-5EX", "ConnectX-5EX HCA driver, DevID 4121", 0 ),
+ PCI_ROM ( 0x15b3, 0x101b, "ConnectX-6", "ConnectX-6 HCA driver, DevID 4123", 0 ),
+ PCI_ROM ( 0x15b3, 0x101d, "ConnectX-6DX", "ConnectX-6DX HCA driver, DevID 4125", 0 ),
+ PCI_ROM ( 0x15b3, 0xa2d2, "BlueField", "BlueField integrated ConnectX-5 network controller HCA driver, DevID 41682", 0 ),
};
struct pci_driver golan_driver __pci_driver = {
diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c
index a1d2a3bd5..9675c156b 100644
--- a/src/drivers/infiniband/hermon.c
+++ b/src/drivers/infiniband/hermon.c
@@ -3207,22 +3207,16 @@ static void hermon_eth_complete_recv ( struct ib_device *ibdev __unused,
struct ib_address_vector *source,
struct io_buffer *iobuf, int rc ) {
struct net_device *netdev = ib_qp_get_ownerdata ( qp );
- struct net_device *vlan;
-
- /* Find VLAN device, if applicable */
- if ( source->vlan_present ) {
- if ( ( vlan = vlan_find ( netdev, source->vlan ) ) != NULL ) {
- netdev = vlan;
- } else if ( rc == 0 ) {
- rc = -ENODEV;
- }
- }
+ unsigned int tag;
+
+ /* Identify VLAN tag, if applicable */
+ tag = ( source->vlan_present ? source->vlan : 0 );
/* Hand off to network layer */
if ( rc == 0 ) {
- netdev_rx ( netdev, iobuf );
+ vlan_netdev_rx ( netdev, tag, iobuf );
} else {
- netdev_rx_err ( netdev, iobuf, rc );
+ vlan_netdev_rx_err ( netdev, tag, iobuf, rc );
}
}
diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c
index a2e6d535b..bb0b673b9 100644
--- a/src/drivers/net/intel.c
+++ b/src/drivers/net/intel.c
@@ -1146,9 +1146,17 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15bb, "i219lm-7", "I219-LM (7)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15bc, "i219v-7", "I219-V (7)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15bd, "i219lm-6", "I219-LM (6)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15be, "i219v-6", "I219-V (6)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15df, "i219lm-8", "I219-LM (8)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15e0, "i219v-8", "I219-V (8)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ),
PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ),
diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c
index 3e40fa4ae..c98ba265c 100644
--- a/src/drivers/net/intelxl.c
+++ b/src/drivers/net/intelxl.c
@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
+#include <ipxe/vlan.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/pci.h>
@@ -117,98 +118,239 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl,
/******************************************************************************
*
+ * MSI-X interrupts
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Enable MSI-X dummy interrupt
+ *
+ * @v intelxl Intel device
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+int intelxl_msix_enable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+ int rc;
+
+ /* Enable MSI-X capability */
+ if ( ( rc = pci_msix_enable ( pci, &intelxl->msix ) ) != 0 ) {
+ DBGC ( intelxl, "INTELXL %p could not enable MSI-X: %s\n",
+ intelxl, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Configure interrupt zero to write to dummy location */
+ pci_msix_map ( &intelxl->msix, 0, virt_to_bus ( &intelxl->msg ), 0 );
+
+ /* Enable dummy interrupt zero */
+ pci_msix_unmask ( &intelxl->msix, 0 );
+
+ return 0;
+}
+
+/**
+ * Disable MSI-X dummy interrupt
+ *
+ * @v intelxl Intel device
+ * @v pci PCI device
+ */
+void intelxl_msix_disable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+
+ /* Disable dummy interrupt zero */
+ pci_msix_mask ( &intelxl->msix, 0 );
+
+ /* Disable MSI-X capability */
+ pci_msix_disable ( pci, &intelxl->msix );
+}
+
+/******************************************************************************
+ *
* Admin queue
*
******************************************************************************
*/
+/** Admin queue register offsets */
+static const struct intelxl_admin_offsets intelxl_admin_offsets = {
+ .bal = INTELXL_ADMIN_BAL,
+ .bah = INTELXL_ADMIN_BAH,
+ .len = INTELXL_ADMIN_LEN,
+ .head = INTELXL_ADMIN_HEAD,
+ .tail = INTELXL_ADMIN_TAIL,
+};
+
/**
- * Create admin queue
+ * Allocate admin queue
*
* @v intelxl Intel device
* @v admin Admin queue
* @ret rc Return status code
*/
-static int intelxl_create_admin ( struct intelxl_nic *intelxl,
- struct intelxl_admin *admin ) {
+static int intelxl_alloc_admin ( struct intelxl_nic *intelxl,
+ struct intelxl_admin *admin ) {
+ size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC );
size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
- void *admin_regs = ( intelxl->regs + admin->reg );
- physaddr_t address;
/* Allocate admin queue */
- admin->desc = malloc_dma ( ( len + sizeof ( *admin->buffer ) ),
- INTELXL_ALIGN );
- if ( ! admin->desc )
+ admin->buf = malloc_dma ( ( buf_len + len ), INTELXL_ALIGN );
+ if ( ! admin->buf )
return -ENOMEM;
- admin->buffer = ( ( ( void * ) admin->desc ) + len );
+ admin->desc = ( ( ( void * ) admin->buf ) + buf_len );
+
+ DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf "
+ "[%08llx,%08llx)\n", intelxl,
+ ( ( admin == &intelxl->command ) ? 'T' : 'R' ),
+ ( ( unsigned long long ) virt_to_bus ( admin->desc ) ),
+ ( ( unsigned long long ) ( virt_to_bus ( admin->desc ) + len ) ),
+ ( ( unsigned long long ) virt_to_bus ( admin->buf ) ),
+ ( ( unsigned long long ) ( virt_to_bus ( admin->buf ) +
+ buf_len ) ) );
+ return 0;
+}
+
+/**
+ * Enable admin queue
+ *
+ * @v intelxl Intel device
+ * @v admin Admin queue
+ */
+static void intelxl_enable_admin ( struct intelxl_nic *intelxl,
+ struct intelxl_admin *admin ) {
+ size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
+ physaddr_t address;
/* Initialise admin queue */
memset ( admin->desc, 0, len );
/* Reset head and tail registers */
- writel ( 0, admin_regs + INTELXL_ADMIN_HEAD );
- writel ( 0, admin_regs + INTELXL_ADMIN_TAIL );
+ writel ( 0, admin_regs + regs->head );
+ writel ( 0, admin_regs + regs->tail );
/* Reset queue index */
admin->index = 0;
/* Program queue address */
address = virt_to_bus ( admin->desc );
- writel ( ( address & 0xffffffffUL ), admin_regs + INTELXL_ADMIN_BAL );
+ writel ( ( address & 0xffffffffUL ), admin_regs + regs->bal );
if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
writel ( ( ( ( uint64_t ) address ) >> 32 ),
- admin_regs + INTELXL_ADMIN_BAH );
+ admin_regs + regs->bah );
} else {
- writel ( 0, admin_regs + INTELXL_ADMIN_BAH );
+ writel ( 0, admin_regs + regs->bah );
}
/* Program queue length and enable queue */
writel ( ( INTELXL_ADMIN_LEN_LEN ( INTELXL_ADMIN_NUM_DESC ) |
INTELXL_ADMIN_LEN_ENABLE ),
- admin_regs + INTELXL_ADMIN_LEN );
-
- DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf "
- "[%08llx,%08llx)\n", intelxl,
- ( ( admin->reg == INTELXL_ADMIN_CMD ) ? 'T' : 'R' ),
- ( ( unsigned long long ) address ),
- ( ( unsigned long long ) address + len ),
- ( ( unsigned long long ) virt_to_bus ( admin->buffer ) ),
- ( ( unsigned long long ) ( virt_to_bus ( admin->buffer ) +
- sizeof ( admin->buffer[0] ) ) ) );
- return 0;
+ admin_regs + regs->len );
}
/**
- * Destroy admin queue
+ * Disable admin queue
*
* @v intelxl Intel device
* @v admin Admin queue
*/
-static void intelxl_destroy_admin ( struct intelxl_nic *intelxl,
+static void intelxl_disable_admin ( struct intelxl_nic *intelxl,
struct intelxl_admin *admin ) {
- size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
- void *admin_regs = ( intelxl->regs + admin->reg );
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
/* Disable queue */
- writel ( 0, admin_regs + INTELXL_ADMIN_LEN );
+ writel ( 0, admin_regs + regs->len );
+}
+
+/**
+ * Free admin queue
+ *
+ * @v intelxl Intel device
+ * @v admin Admin queue
+ */
+static void intelxl_free_admin ( struct intelxl_nic *intelxl __unused,
+ struct intelxl_admin *admin ) {
+ size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC );
+ size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
/* Free queue */
- free_dma ( admin->desc, ( len + sizeof ( *admin->buffer ) ) );
+ free_dma ( admin->buf, ( buf_len + len ) );
+}
+
+/**
+ * Get next admin command queue descriptor
+ *
+ * @v intelxl Intel device
+ * @ret cmd Command descriptor
+ */
+struct intelxl_admin_descriptor *
+intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin *admin = &intelxl->command;
+ struct intelxl_admin_descriptor *cmd;
+
+ /* Get and initialise next descriptor */
+ cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ memset ( cmd, 0, sizeof ( *cmd ) );
+ return cmd;
+}
+
+/**
+ * Get next admin command queue data buffer
+ *
+ * @v intelxl Intel device
+ * @ret buf Data buffer
+ */
+union intelxl_admin_buffer *
+intelxl_admin_command_buffer ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin *admin = &intelxl->command;
+ union intelxl_admin_buffer *buf;
+
+ /* Get next data buffer */
+ buf = &admin->buf[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ memset ( buf, 0, sizeof ( *buf ) );
+ return buf;
+}
+
+/**
+ * Initialise admin event queue descriptor
+ *
+ * @v intelxl Intel device
+ * @v index Event queue index
+ */
+static void intelxl_admin_event_init ( struct intelxl_nic *intelxl,
+ unsigned int index ) {
+ struct intelxl_admin *admin = &intelxl->event;
+ struct intelxl_admin_descriptor *evt;
+ union intelxl_admin_buffer *buf;
+ uint64_t address;
+
+ /* Initialise descriptor */
+ evt = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ];
+ buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ];
+ address = virt_to_bus ( buf );
+ evt->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
+ evt->len = cpu_to_le16 ( sizeof ( *buf ) );
+ evt->params.buffer.high = cpu_to_le32 ( address >> 32 );
+ evt->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL );
}
/**
* Issue admin queue command
*
* @v intelxl Intel device
- * @v cmd Command descriptor
* @ret rc Return status code
*/
-static int intelxl_admin_command ( struct intelxl_nic *intelxl,
- struct intelxl_admin_descriptor *cmd ) {
+int intelxl_admin_command ( struct intelxl_nic *intelxl ) {
struct intelxl_admin *admin = &intelxl->command;
- void *admin_regs = ( intelxl->regs + admin->reg );
- struct intelxl_admin_descriptor *desc;
- uint64_t buffer;
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ uint64_t address;
+ uint32_t cookie;
unsigned int index;
unsigned int tail;
unsigned int i;
@@ -217,69 +359,74 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl,
/* Get next queue entry */
index = admin->index++;
tail = ( admin->index % INTELXL_ADMIN_NUM_DESC );
- desc = &admin->desc[index % INTELXL_ADMIN_NUM_DESC];
-
- /* Clear must-be-zero flags */
- cmd->flags &= ~cpu_to_le16 ( INTELXL_ADMIN_FL_DD |
- INTELXL_ADMIN_FL_CMP |
- INTELXL_ADMIN_FL_ERR );
-
- /* Clear return value */
- cmd->ret = 0;
-
- /* Populate cookie */
- cmd->cookie = cpu_to_le32 ( index );
+ cmd = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ];
+ buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ];
+ DBGC2 ( intelxl, "INTELXL %p admin command %#x opcode %#04x",
+ intelxl, index, le16_to_cpu ( cmd->opcode ) );
+ if ( cmd->vopcode )
+ DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->vopcode ) );
+ DBGC2 ( intelxl, ":\n" );
+
+ /* Sanity checks */
+ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_DD ) ) );
+ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_CMP ) ) );
+ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_ERR ) ) );
+ assert ( cmd->ret == 0 );
/* Populate data buffer address if applicable */
if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) {
- buffer = virt_to_bus ( admin->buffer );
- cmd->params.buffer.high = cpu_to_le32 ( buffer >> 32 );
- cmd->params.buffer.low = cpu_to_le32 ( buffer & 0xffffffffUL );
+ address = virt_to_bus ( buf );
+ cmd->params.buffer.high = cpu_to_le32 ( address >> 32 );
+ cmd->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL );
}
- /* Copy command descriptor to queue entry */
- memcpy ( desc, cmd, sizeof ( *desc ) );
- DBGC2 ( intelxl, "INTELXL %p admin command %#x:\n", intelxl, index );
- DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) );
+ /* Populate cookie, if not being (ab)used for VF opcode */
+ if ( ! cmd->vopcode )
+ cmd->cookie = cpu_to_le32 ( index );
+
+ /* Record cookie */
+ cookie = cmd->cookie;
/* Post command descriptor */
+ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) );
+ if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) {
+ DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf,
+ le16_to_cpu ( cmd->len ) );
+ }
wmb();
- writel ( tail, admin_regs + INTELXL_ADMIN_TAIL );
+ writel ( tail, admin_regs + regs->tail );
/* Wait for completion */
for ( i = 0 ; i < INTELXL_ADMIN_MAX_WAIT_MS ; i++ ) {
/* If response is not complete, delay 1ms and retry */
- if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) ) {
+ if ( ! ( cmd->flags & INTELXL_ADMIN_FL_DD ) ) {
mdelay ( 1 );
continue;
}
DBGC2 ( intelxl, "INTELXL %p admin command %#x response:\n",
intelxl, index );
- DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc,
- sizeof ( *desc ) );
+ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd,
+ sizeof ( *cmd ) );
/* Check for cookie mismatch */
- if ( desc->cookie != cmd->cookie ) {
+ if ( cmd->cookie != cookie ) {
DBGC ( intelxl, "INTELXL %p admin command %#x bad "
"cookie %#x\n", intelxl, index,
- le32_to_cpu ( desc->cookie ) );
+ le32_to_cpu ( cmd->cookie ) );
rc = -EPROTO;
goto err;
}
/* Check for errors */
- if ( desc->ret != 0 ) {
+ if ( cmd->ret != 0 ) {
DBGC ( intelxl, "INTELXL %p admin command %#x error "
"%d\n", intelxl, index,
- le16_to_cpu ( desc->ret ) );
+ le16_to_cpu ( cmd->ret ) );
rc = -EIO;
goto err;
}
- /* Copy response back to command descriptor */
- memcpy ( cmd, desc, sizeof ( *cmd ) );
-
/* Success */
return 0;
}
@@ -288,8 +435,7 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl,
DBGC ( intelxl, "INTELXL %p timed out waiting for admin command %#x:\n",
intelxl, index );
err:
- DBGC_HDA ( intelxl, virt_to_phys ( desc ), cmd, sizeof ( *cmd ) );
- DBGC_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) );
+ DBGC_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) );
return rc;
}
@@ -300,17 +446,18 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl,
* @ret rc Return status code
*/
static int intelxl_admin_version ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_version_params *version = &cmd.params.version;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_version_params *version;
unsigned int api;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION );
+ version = &cmd->params.version;
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
api = le16_to_cpu ( version->api.major );
DBGC ( intelxl, "INTELXL %p firmware v%d.%d API v%d.%d\n",
@@ -335,24 +482,25 @@ static int intelxl_admin_version ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_driver_params *driver = &cmd.params.driver;
- struct intelxl_admin_driver_buffer *buf =
- &intelxl->command.buffer->driver;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_driver_params *driver;
+ union intelxl_admin_buffer *buf;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER );
- cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
- cmd.len = cpu_to_le16 ( sizeof ( *buf ) );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->driver ) );
+ driver = &cmd->params.driver;
driver->major = product_major_version;
driver->minor = product_minor_version;
- snprintf ( buf->name, sizeof ( buf->name ), "%s",
+ buf = intelxl_admin_command_buffer ( intelxl );
+ snprintf ( buf->driver.name, sizeof ( buf->driver.name ), "%s",
( product_name[0] ? product_name : product_short_name ) );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -365,17 +513,51 @@ static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_shutdown_params *shutdown = &cmd.params.shutdown;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_shutdown_params *shutdown;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN );
+ shutdown = &cmd->params.shutdown;
shutdown->unloading = INTELXL_ADMIN_SHUTDOWN_UNLOADING;
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Clear PXE mode
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_clear_pxe_params *pxe;
+ uint32_t gllan_rctl_0;
+ int rc;
+
+ /* Do nothing if device is already out of PXE mode */
+ gllan_rctl_0 = readl ( intelxl->regs + INTELXL_GLLAN_RCTL_0 );
+ if ( ! ( gllan_rctl_0 & INTELXL_GLLAN_RCTL_0_PXE_MODE ) ) {
+ DBGC2 ( intelxl, "INTELXL %p already in non-PXE mode\n",
+ intelxl );
+ return 0;
+ }
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_CLEAR_PXE );
+ pxe = &cmd->params.pxe;
+ pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC;
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -388,36 +570,38 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_switch_params *sw = &cmd.params.sw;
- struct intelxl_admin_switch_buffer *buf = &intelxl->command.buffer->sw;
- struct intelxl_admin_switch_config *cfg = &buf->cfg;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_switch_params *sw;
+ union intelxl_admin_buffer *buf;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH );
- cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
- cmd.len = cpu_to_le16 ( sizeof ( *buf ) );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->sw ) );
+ sw = &cmd->params.sw;
+ buf = intelxl_admin_command_buffer ( intelxl );
/* Get each configuration in turn */
do {
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
/* Dump raw configuration */
DBGC2 ( intelxl, "INTELXL %p SEID %#04x:\n",
- intelxl, le16_to_cpu ( cfg->seid ) );
- DBGC2_HDA ( intelxl, 0, cfg, sizeof ( *cfg ) );
+ intelxl, le16_to_cpu ( buf->sw.cfg.seid ) );
+ DBGC2_HDA ( intelxl, 0, &buf->sw.cfg, sizeof ( buf->sw.cfg ) );
/* Parse response */
- if ( cfg->type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) {
- intelxl->vsi = le16_to_cpu ( cfg->seid );
+ if ( buf->sw.cfg.type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) {
+ intelxl->vsi = le16_to_cpu ( buf->sw.cfg.seid );
DBGC ( intelxl, "INTELXL %p VSI %#04x uplink %#04x "
"downlink %#04x conn %#02x\n", intelxl,
- intelxl->vsi, le16_to_cpu ( cfg->uplink ),
- le16_to_cpu ( cfg->downlink ), cfg->connection );
+ intelxl->vsi, le16_to_cpu ( buf->sw.cfg.uplink ),
+ le16_to_cpu ( buf->sw.cfg.downlink ),
+ buf->sw.cfg.connection );
}
} while ( sw->next );
@@ -438,25 +622,27 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_vsi_params *vsi = &cmd.params.vsi;
- struct intelxl_admin_vsi_buffer *buf = &intelxl->command.buffer->vsi;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_vsi_params *vsi;
+ union intelxl_admin_buffer *buf;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI );
- cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
- cmd.len = cpu_to_le16 ( sizeof ( *buf ) );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->vsi ) );
+ vsi = &cmd->params.vsi;
vsi->vsi = cpu_to_le16 ( intelxl->vsi );
+ buf = intelxl_admin_command_buffer ( intelxl );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
/* Parse response */
- intelxl->queue = le16_to_cpu ( buf->queue[0] );
- intelxl->qset = le16_to_cpu ( buf->qset[0] );
+ intelxl->queue = le16_to_cpu ( buf->vsi.queue[0] );
+ intelxl->qset = le16_to_cpu ( buf->vsi.qset[0] );
DBGC ( intelxl, "INTELXL %p VSI %#04x queue %#04x qset %#04x\n",
intelxl, intelxl->vsi, intelxl->queue, intelxl->qset );
@@ -470,24 +656,25 @@ static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_promisc_params *promisc = &cmd.params.promisc;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_promisc_params *promisc;
uint16_t flags;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC );
flags = ( INTELXL_ADMIN_PROMISC_FL_UNICAST |
INTELXL_ADMIN_PROMISC_FL_MULTICAST |
INTELXL_ADMIN_PROMISC_FL_BROADCAST |
INTELXL_ADMIN_PROMISC_FL_VLAN );
+ promisc = &cmd->params.promisc;
promisc->flags = cpu_to_le16 ( flags );
promisc->valid = cpu_to_le16 ( flags );
promisc->vsi = cpu_to_le16 ( intelxl->vsi );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -500,18 +687,19 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_autoneg_params *autoneg = &cmd.params.autoneg;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_autoneg_params *autoneg;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG );
+ autoneg = &cmd->params.autoneg;
autoneg->flags = ( INTELXL_ADMIN_AUTONEG_FL_RESTART |
INTELXL_ADMIN_AUTONEG_FL_ENABLE );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -525,17 +713,18 @@ static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) {
*/
static int intelxl_admin_link ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_link_params *link = &cmd.params.link;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_link_params *link;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK );
+ link = &cmd->params.link;
link->notify = INTELXL_ADMIN_LINK_NOTIFY;
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
DBGC ( intelxl, "INTELXL %p PHY %#02x speed %#02x status %#02x\n",
intelxl, link->phy, link->speed, link->status );
@@ -551,19 +740,36 @@ static int intelxl_admin_link ( struct net_device *netdev ) {
}
/**
+ * Handle virtual function event (when VF driver is not present)
+ *
+ * @v netdev Network device
+ * @v evt Admin queue event descriptor
+ * @v buf Admin queue event data buffer
+ */
+__weak void
+intelxlvf_admin_event ( struct net_device *netdev __unused,
+ struct intelxl_admin_descriptor *evt __unused,
+ union intelxl_admin_buffer *buf __unused ) {
+
+ /* Nothing to do */
+}
+
+/**
* Refill admin event queue
*
* @v intelxl Intel device
*/
static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) {
struct intelxl_admin *admin = &intelxl->event;
- void *admin_regs = ( intelxl->regs + admin->reg );
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
unsigned int tail;
/* Update tail pointer */
tail = ( ( admin->index + INTELXL_ADMIN_NUM_DESC - 1 ) %
INTELXL_ADMIN_NUM_DESC );
- writel ( tail, admin_regs + INTELXL_ADMIN_TAIL );
+ wmb();
+ writel ( tail, admin_regs + regs->tail );
}
/**
@@ -571,42 +777,48 @@ static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) {
*
* @v netdev Network device
*/
-static void intelxl_poll_admin ( struct net_device *netdev ) {
+void intelxl_poll_admin ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxl_admin *admin = &intelxl->event;
- struct intelxl_admin_descriptor *desc;
+ struct intelxl_admin_descriptor *evt;
+ union intelxl_admin_buffer *buf;
/* Check for events */
while ( 1 ) {
- /* Get next event descriptor */
- desc = &admin->desc[admin->index % INTELXL_ADMIN_NUM_DESC];
+ /* Get next event descriptor and data buffer */
+ evt = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ buf = &admin->buf[ admin->index % INTELXL_ADMIN_NUM_DESC ];
/* Stop if descriptor is not yet completed */
- if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) )
+ if ( ! ( evt->flags & INTELXL_ADMIN_FL_DD ) )
return;
DBGC2 ( intelxl, "INTELXL %p admin event %#x:\n",
intelxl, admin->index );
- DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc,
- sizeof ( *desc ) );
+ DBGC2_HDA ( intelxl, virt_to_phys ( evt ), evt,
+ sizeof ( *evt ) );
+ if ( evt->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) {
+ DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf,
+ le16_to_cpu ( evt->len ) );
+ }
/* Handle event */
- switch ( desc->opcode ) {
+ switch ( evt->opcode ) {
case cpu_to_le16 ( INTELXL_ADMIN_LINK ):
intelxl_admin_link ( netdev );
break;
+ case cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ):
+ intelxlvf_admin_event ( netdev, evt, buf );
+ break;
default:
DBGC ( intelxl, "INTELXL %p admin event %#x "
"unrecognised opcode %#04x\n", intelxl,
- admin->index, le16_to_cpu ( desc->opcode ) );
+ admin->index, le16_to_cpu ( evt->opcode ) );
break;
}
- /* Clear event completion flag */
- desc->flags = 0;
- wmb();
-
- /* Update index and refill queue */
+ /* Reset descriptor and refill queue */
+ intelxl_admin_event_init ( intelxl, admin->index );
admin->index++;
intelxl_refill_admin ( intelxl );
}
@@ -618,19 +830,19 @@ static void intelxl_poll_admin ( struct net_device *netdev ) {
* @v intelxl Intel device
* @ret rc Return status code
*/
-static int intelxl_open_admin ( struct intelxl_nic *intelxl ) {
+int intelxl_open_admin ( struct intelxl_nic *intelxl ) {
int rc;
- /* Create admin event queue */
- if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->event ) ) != 0 )
- goto err_create_event;
+ /* Allocate admin event queue */
+ if ( ( rc = intelxl_alloc_admin ( intelxl, &intelxl->event ) ) != 0 )
+ goto err_alloc_event;
- /* Create admin command queue */
- if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->command ) ) != 0 )
- goto err_create_command;
+ /* Allocate admin command queue */
+ if ( ( rc = intelxl_alloc_admin ( intelxl, &intelxl->command ) ) != 0 )
+ goto err_alloc_command;
- /* Post all descriptors to event queue */
- intelxl_refill_admin ( intelxl );
+ /* (Re)open admin queues */
+ intelxl_reopen_admin ( intelxl );
/* Get firmware version */
if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 )
@@ -644,28 +856,54 @@ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) {
err_driver:
err_version:
- intelxl_destroy_admin ( intelxl, &intelxl->command );
- err_create_command:
- intelxl_destroy_admin ( intelxl, &intelxl->event );
- err_create_event:
+ intelxl_disable_admin ( intelxl, &intelxl->command );
+ intelxl_disable_admin ( intelxl, &intelxl->event );
+ intelxl_free_admin ( intelxl, &intelxl->command );
+ err_alloc_command:
+ intelxl_free_admin ( intelxl, &intelxl->event );
+ err_alloc_event:
return rc;
}
/**
+ * Reopen admin queues (after virtual function reset)
+ *
+ * @v intelxl Intel device
+ */
+void intelxl_reopen_admin ( struct intelxl_nic *intelxl ) {
+ unsigned int i;
+
+ /* Enable admin event queue */
+ intelxl_enable_admin ( intelxl, &intelxl->event );
+
+ /* Enable admin command queue */
+ intelxl_enable_admin ( intelxl, &intelxl->command );
+
+ /* Initialise all admin event queue descriptors */
+ for ( i = 0 ; i < INTELXL_ADMIN_NUM_DESC ; i++ )
+ intelxl_admin_event_init ( intelxl, i );
+
+ /* Post all descriptors to event queue */
+ intelxl_refill_admin ( intelxl );
+}
+
+/**
* Close admin queues
*
* @v intelxl Intel device
*/
-static void intelxl_close_admin ( struct intelxl_nic *intelxl ) {
+void intelxl_close_admin ( struct intelxl_nic *intelxl ) {
/* Shut down admin queues */
intelxl_admin_shutdown ( intelxl );
- /* Destroy admin command queue */
- intelxl_destroy_admin ( intelxl, &intelxl->command );
+ /* Disable admin queues */
+ intelxl_disable_admin ( intelxl, &intelxl->command );
+ intelxl_disable_admin ( intelxl, &intelxl->event );
- /* Destroy admin event queue */
- intelxl_destroy_admin ( intelxl, &intelxl->event );
+ /* Free admin queues */
+ intelxl_free_admin ( intelxl, &intelxl->command );
+ intelxl_free_admin ( intelxl, &intelxl->event );
}
/******************************************************************************
@@ -676,6 +914,62 @@ static void intelxl_close_admin ( struct intelxl_nic *intelxl ) {
*/
/**
+ * Allocate descriptor ring
+ *
+ * @v intelxl Intel device
+ * @v ring Descriptor ring
+ * @ret rc Return status code
+ */
+int intelxl_alloc_ring ( struct intelxl_nic *intelxl,
+ struct intelxl_ring *ring ) {
+ physaddr_t address;
+ int rc;
+
+ /* Allocate descriptor ring */
+ ring->desc.raw = malloc_dma ( ring->len, INTELXL_ALIGN );
+ if ( ! ring->desc.raw ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ address = virt_to_bus ( ring->desc.raw );
+
+ /* Initialise descriptor ring */
+ memset ( ring->desc.raw, 0, ring->len );
+
+ /* Reset tail pointer */
+ writel ( 0, ( intelxl->regs + ring->tail ) );
+
+ /* Reset counters */
+ ring->prod = 0;
+ ring->cons = 0;
+
+ DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n",
+ intelxl, ( ring->reg + ring->tail ),
+ ( ( unsigned long long ) address ),
+ ( ( unsigned long long ) address + ring->len ) );
+
+ return 0;
+
+ free_dma ( ring->desc.raw, ring->len );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Free descriptor ring
+ *
+ * @v intelxl Intel device
+ * @v ring Descriptor ring
+ */
+void intelxl_free_ring ( struct intelxl_nic *intelxl __unused,
+ struct intelxl_ring *ring ) {
+
+ /* Free descriptor ring */
+ free_dma ( ring->desc.raw, ring->len );
+ ring->desc.raw = NULL;
+}
+
+/**
* Dump queue context (for debugging)
*
* @v intelxl Intel device
@@ -861,7 +1155,7 @@ static int intelxl_context_rx ( struct intelxl_nic *intelxl,
base_count = INTELXL_CTX_RX_BASE_COUNT ( address, INTELXL_RX_NUM_DESC );
ctx.rx.base_count = cpu_to_le64 ( base_count );
ctx.rx.len = cpu_to_le16 ( INTELXL_CTX_RX_LEN ( intelxl->mfs ) );
- ctx.rx.flags = INTELXL_CTX_RX_FL_CRCSTRIP;
+ ctx.rx.flags = ( INTELXL_CTX_RX_FL_DSIZE | INTELXL_CTX_RX_FL_CRCSTRIP );
ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) );
/* Program context */
@@ -939,25 +1233,15 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl,
*/
static int intelxl_create_ring ( struct intelxl_nic *intelxl,
struct intelxl_ring *ring ) {
- void *ring_regs = ( intelxl->regs + ring->reg );
physaddr_t address;
int rc;
/* Allocate descriptor ring */
- ring->desc = malloc_dma ( ring->len, INTELXL_ALIGN );
- if ( ! ring->desc ) {
- rc = -ENOMEM;
+ if ( ( rc = intelxl_alloc_ring ( intelxl, ring ) ) != 0 )
goto err_alloc;
- }
-
- /* Initialise descriptor ring */
- memset ( ring->desc, 0, ring->len );
-
- /* Reset tail pointer */
- writel ( 0, ( ring_regs + INTELXL_QXX_TAIL ) );
/* Program queue context */
- address = virt_to_bus ( ring->desc );
+ address = virt_to_bus ( ring->desc.raw );
if ( ( rc = ring->context ( intelxl, address ) ) != 0 )
goto err_context;
@@ -965,20 +1249,12 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl,
if ( ( rc = intelxl_enable_ring ( intelxl, ring ) ) != 0 )
goto err_enable;
- /* Reset counters */
- ring->prod = 0;
- ring->cons = 0;
-
- DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n",
- intelxl, ring->reg, ( ( unsigned long long ) address ),
- ( ( unsigned long long ) address + ring->len ) );
-
return 0;
intelxl_disable_ring ( intelxl, ring );
err_enable:
err_context:
- free_dma ( ring->desc, ring->len );
+ intelxl_free_ring ( intelxl, ring );
err_alloc:
return rc;
}
@@ -1000,8 +1276,7 @@ static void intelxl_destroy_ring ( struct intelxl_nic *intelxl,
}
/* Free descriptor ring */
- free_dma ( ring->desc, ring->len );
- ring->desc = NULL;
+ intelxl_free_ring ( intelxl, ring );
}
/**
@@ -1029,7 +1304,7 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) {
/* Get next receive descriptor */
rx_idx = ( intelxl->rx.prod++ % INTELXL_RX_NUM_DESC );
- rx = &intelxl->rx.desc[rx_idx].rx;
+ rx = &intelxl->rx.desc.rx[rx_idx].data;
/* Populate receive descriptor */
address = virt_to_bus ( iobuf->data );
@@ -1050,8 +1325,23 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) {
if ( refilled ) {
wmb();
rx_tail = ( intelxl->rx.prod % INTELXL_RX_NUM_DESC );
- writel ( rx_tail,
- ( intelxl->regs + intelxl->rx.reg + INTELXL_QXX_TAIL));
+ writel ( rx_tail, ( intelxl->regs + intelxl->rx.tail ) );
+ }
+}
+
+/**
+ * Discard unused receive I/O buffers
+ *
+ * @v intelxl Intel device
+ */
+void intelxl_empty_rx ( struct intelxl_nic *intelxl ) {
+ unsigned int i;
+
+ /* Discard any unused receive buffers */
+ for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) {
+ if ( intelxl->rx_iobuf[i] )
+ free_iob ( intelxl->rx_iobuf[i] );
+ intelxl->rx_iobuf[i] = NULL;
}
}
@@ -1141,7 +1431,6 @@ static int intelxl_open ( struct net_device *netdev ) {
static void intelxl_close ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
unsigned int queue;
- unsigned int i;
/* Dump contexts (for debugging) */
intelxl_context_dump ( intelxl, INTELXL_PFCM_LANCTXCTL_TYPE_TX,
@@ -1163,11 +1452,7 @@ static void intelxl_close ( struct net_device *netdev ) {
intelxl_destroy_ring ( intelxl, &intelxl->rx );
/* Discard any unused receive buffers */
- for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) {
- if ( intelxl->rx_iobuf[i] )
- free_iob ( intelxl->rx_iobuf[i] );
- intelxl->rx_iobuf[i] = NULL;
- }
+ intelxl_empty_rx ( intelxl );
}
/**
@@ -1177,8 +1462,7 @@ static void intelxl_close ( struct net_device *netdev ) {
* @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int intelxl_transmit ( struct net_device *netdev,
- struct io_buffer *iobuf ) {
+int intelxl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxl_tx_data_descriptor *tx;
unsigned int tx_idx;
@@ -1194,7 +1478,7 @@ static int intelxl_transmit ( struct net_device *netdev,
}
tx_idx = ( intelxl->tx.prod++ % INTELXL_TX_NUM_DESC );
tx_tail = ( intelxl->tx.prod % INTELXL_TX_NUM_DESC );
- tx = &intelxl->tx.desc[tx_idx].tx;
+ tx = &intelxl->tx.desc.tx[tx_idx].data;
/* Populate transmit descriptor */
address = virt_to_bus ( iobuf->data );
@@ -1206,8 +1490,7 @@ static int intelxl_transmit ( struct net_device *netdev,
wmb();
/* Notify card that there are packets ready to transmit */
- writel ( tx_tail,
- ( intelxl->regs + intelxl->tx.reg + INTELXL_QXX_TAIL ) );
+ writel ( tx_tail, ( intelxl->regs + intelxl->tx.tail ) );
DBGC2 ( intelxl, "INTELXL %p TX %d is [%llx,%llx)\n", intelxl, tx_idx,
( ( unsigned long long ) address ),
@@ -1230,7 +1513,7 @@ static void intelxl_poll_tx ( struct net_device *netdev ) {
/* Get next transmit descriptor */
tx_idx = ( intelxl->tx.cons % INTELXL_TX_NUM_DESC );
- tx_wb = &intelxl->tx.desc[tx_idx].tx_wb;
+ tx_wb = &intelxl->tx.desc.tx[tx_idx].wb;
/* Stop if descriptor is still in use */
if ( ! ( tx_wb->flags & INTELXL_TX_WB_FL_DD ) )
@@ -1254,6 +1537,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
struct intelxl_rx_writeback_descriptor *rx_wb;
struct io_buffer *iobuf;
unsigned int rx_idx;
+ unsigned int tag;
size_t len;
/* Check for received packets */
@@ -1261,7 +1545,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
/* Get next receive descriptor */
rx_idx = ( intelxl->rx.cons % INTELXL_RX_NUM_DESC );
- rx_wb = &intelxl->rx.desc[rx_idx].rx_wb;
+ rx_wb = &intelxl->rx.desc.rx[rx_idx].wb;
/* Stop if descriptor is still in use */
if ( ! ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_DD ) ) )
@@ -1273,16 +1557,23 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
len = INTELXL_RX_WB_LEN ( le32_to_cpu ( rx_wb->len ) );
iob_put ( iobuf, len );
+ /* Find VLAN device, if applicable */
+ if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_VLAN ) ) {
+ tag = VLAN_TAG ( le16_to_cpu ( rx_wb->vlan ) );
+ } else {
+ tag = 0;
+ }
+
/* Hand off to network stack */
if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_RXE ) ) {
DBGC ( intelxl, "INTELXL %p RX %d error (length %zd, "
"flags %08x)\n", intelxl, rx_idx, len,
le32_to_cpu ( rx_wb->flags ) );
- netdev_rx_err ( netdev, iobuf, -EIO );
+ vlan_netdev_rx_err ( netdev, tag, iobuf, -EIO );
} else {
DBGC2 ( intelxl, "INTELXL %p RX %d complete (length "
"%zd)\n", intelxl, rx_idx, len );
- netdev_rx ( netdev, iobuf );
+ vlan_netdev_rx ( netdev, tag, iobuf );
}
intelxl->rx.cons++;
}
@@ -1293,16 +1584,9 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
*
* @v netdev Network device
*/
-static void intelxl_poll ( struct net_device *netdev ) {
+void intelxl_poll ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
- /* Acknowledge interrupts, if applicable */
- if ( netdev_irq_enabled ( netdev ) ) {
- writel ( ( INTELXL_PFINT_DYN_CTL0_CLEARPBA |
- INTELXL_PFINT_DYN_CTL0_INTENA_MASK ),
- intelxl->regs + INTELXL_PFINT_DYN_CTL0 );
- }
-
/* Poll for completed packets */
intelxl_poll_tx ( netdev );
@@ -1314,23 +1598,23 @@ static void intelxl_poll ( struct net_device *netdev ) {
/* Refill RX ring */
intelxl_refill_rx ( intelxl );
-}
-
-/**
- * Enable or disable interrupts
- *
- * @v netdev Network device
- * @v enable Interrupts should be enabled
- */
-static void intelxl_irq ( struct net_device *netdev, int enable ) {
- struct intelxl_nic *intelxl = netdev->priv;
- if ( enable ) {
- writel ( INTELXL_PFINT_DYN_CTL0_INTENA,
- intelxl->regs + INTELXL_PFINT_DYN_CTL0 );
- } else {
- writel ( 0, intelxl->regs + INTELXL_PFINT_DYN_CTL0 );
- }
+ /* Rearm interrupt, since otherwise receive descriptors will
+ * be written back only after a complete cacheline (four
+ * packets) have been received.
+ *
+ * There is unfortunately no efficient way to determine
+ * whether or not rearming the interrupt is necessary. If we
+ * are running inside a hypervisor (e.g. using a VF or PF as a
+ * passed-through PCI device), then the MSI-X write is
+ * redirected by the hypervisor to the real host APIC and the
+ * host ISR then raises an interrupt within the guest. We
+ * therefore cannot poll the nominal MSI-X target location to
+ * watch for the value being written. We could read from the
+ * INT_DYN_CTL register, but this is even less efficient than
+ * just unconditionally rearming the interrupt.
+ */
+ writel ( INTELXL_INT_DYN_CTL_INTENA, intelxl->regs + intelxl->intr );
}
/** Network device operations */
@@ -1339,7 +1623,6 @@ static struct net_device_operations intelxl_operations = {
.close = intelxl_close,
.transmit = intelxl_transmit,
.poll = intelxl_poll,
- .irq = intelxl_irq,
};
/******************************************************************************
@@ -1374,11 +1657,16 @@ static int intelxl_probe ( struct pci_device *pci ) {
netdev->dev = &pci->dev;
memset ( intelxl, 0, sizeof ( *intelxl ) );
intelxl->pf = PCI_FUNC ( pci->busdevfn );
- intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD );
- intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT );
+ intelxl->intr = INTELXL_PFINT_DYN_CTL0;
+ intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD,
+ &intelxl_admin_offsets );
+ intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT,
+ &intelxl_admin_offsets );
intelxl_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC,
+ sizeof ( intelxl->tx.desc.tx[0] ),
intelxl_context_tx );
intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC,
+ sizeof ( intelxl->rx.desc.rx[0] ),
intelxl_context_rx );
/* Fix up PCI device */
@@ -1408,10 +1696,18 @@ static int intelxl_probe ( struct pci_device *pci ) {
if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 )
goto err_fetch_mac;
+ /* Enable MSI-X dummy interrupt */
+ if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
+ goto err_msix;
+
/* Open admin queues */
if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
goto err_open_admin;
+ /* Clear PXE mode */
+ if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 )
+ goto err_admin_clear_pxe;
+
/* Get switch configuration */
if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 )
goto err_admin_switch;
@@ -1426,7 +1722,9 @@ static int intelxl_probe ( struct pci_device *pci ) {
/* Configure queue register addresses */
intelxl->tx.reg = INTELXL_QTX ( intelxl->queue );
+ intelxl->tx.tail = ( intelxl->tx.reg + INTELXL_QXX_TAIL );
intelxl->rx.reg = INTELXL_QRX ( intelxl->queue );
+ intelxl->rx.tail = ( intelxl->rx.reg + INTELXL_QXX_TAIL );
/* Configure interrupt causes */
writel ( ( INTELXL_QINT_TQCTL_NEXTQ_INDX_NONE |
@@ -1456,8 +1754,11 @@ static int intelxl_probe ( struct pci_device *pci ) {
err_admin_promisc:
err_admin_vsi:
err_admin_switch:
+ err_admin_clear_pxe:
intelxl_close_admin ( intelxl );
err_open_admin:
+ intelxl_msix_disable ( intelxl, pci );
+ err_msix:
err_fetch_mac:
intelxl_reset ( intelxl );
err_reset:
@@ -1484,6 +1785,9 @@ static void intelxl_remove ( struct pci_device *pci ) {
/* Close admin queues */
intelxl_close_admin ( intelxl );
+ /* Disable MSI-X dummy interrupt */
+ intelxl_msix_disable ( intelxl, pci );
+
/* Reset the NIC */
intelxl_reset ( intelxl );
diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h
index 02d9b98a2..80586cef0 100644
--- a/src/drivers/net/intelxl.h
+++ b/src/drivers/net/intelxl.h
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/if_ether.h>
+#include <ipxe/pcimsix.h>
struct intelxl_nic;
@@ -19,9 +20,9 @@ struct intelxl_nic;
/** Alignment
*
- * No data structure requires greater than 128 byte alignment.
+ * No data structure requires greater than 256 byte alignment.
*/
-#define INTELXL_ALIGN 128
+#define INTELXL_ALIGN 256
/******************************************************************************
*
@@ -53,6 +54,24 @@ struct intelxl_nic;
/** Admin Queue Tail Register (offset) */
#define INTELXL_ADMIN_TAIL 0x400
+/** Admin queue register offsets
+ *
+ * The physical and virtual function register maps have no discernible
+ * relationship.
+ */
+struct intelxl_admin_offsets {
+ /** Base Address Low Register offset */
+ unsigned int bal;
+ /** Base Address High Register offset */
+ unsigned int bah;
+ /** Length Register offset */
+ unsigned int len;
+ /** Head Register offset */
+ unsigned int head;
+ /** Tail Register offset */
+ unsigned int tail;
+};
+
/** Admin queue data buffer command parameters */
struct intelxl_admin_buffer_params {
/** Reserved */
@@ -125,6 +144,20 @@ struct intelxl_admin_shutdown_params {
/** Driver is unloading */
#define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01
+/** Admin queue Clear PXE Mode command */
+#define INTELXL_ADMIN_CLEAR_PXE 0x0110
+
+/** Admin queue Clear PXE Mode command parameters */
+struct intelxl_admin_clear_pxe_params {
+ /** Magic value */
+ uint8_t magic;
+ /** Reserved */
+ uint8_t reserved[15];
+} __attribute__ (( packed ));
+
+/** Clear PXE Mode magic value */
+#define INTELXL_ADMIN_CLEAR_PXE_MAGIC 0x02
+
/** Admin queue Get Switch Configuration command */
#define INTELXL_ADMIN_SWITCH 0x0200
@@ -271,6 +304,172 @@ struct intelxl_admin_link_params {
/** Link is up */
#define INTELXL_ADMIN_LINK_UP 0x01
+/** Admin queue Send Message to PF command */
+#define INTELXL_ADMIN_SEND_TO_PF 0x0801
+
+/** Admin queue Send Message to VF command */
+#define INTELXL_ADMIN_SEND_TO_VF 0x0802
+
+/** Admin Queue VF Reset opcode */
+#define INTELXL_ADMIN_VF_RESET 0x00000002
+
+/** Admin Queue VF Get Resources opcode */
+#define INTELXL_ADMIN_VF_GET_RESOURCES 0x00000003
+
+/** Admin Queue VF Get Resources data buffer */
+struct intelxl_admin_vf_get_resources_buffer {
+ /** Reserved */
+ uint8_t reserved_a[20];
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Reserved */
+ uint8_t reserved_b[8];
+ /** MAC address */
+ uint8_t mac[ETH_ALEN];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Status Change Event opcode */
+#define INTELXL_ADMIN_VF_STATUS 0x00000011
+
+/** Link status change event type */
+#define INTELXL_ADMIN_VF_STATUS_LINK 0x00000001
+
+/** Link status change event data */
+struct intelxl_admin_vf_status_link {
+ /** Link speed */
+ uint32_t speed;
+ /** Link status */
+ uint8_t status;
+ /** Reserved */
+ uint8_t reserved[3];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Status Change Event data buffer */
+struct intelxl_admin_vf_status_buffer {
+ /** Event type */
+ uint32_t event;
+ /** Event data */
+ union {
+ /** Link change event data */
+ struct intelxl_admin_vf_status_link link;
+ } data;
+ /** Reserved */
+ uint8_t reserved[4];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Configure Queues opcode */
+#define INTELXL_ADMIN_VF_CONFIGURE 0x00000006
+
+/** Admin Queue VF Configure Queues data buffer */
+struct intelxl_admin_vf_configure_buffer {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Number of queue pairs */
+ uint16_t count;
+ /** Reserved */
+ uint8_t reserved_a[4];
+ /** Transmit queue */
+ struct {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Queue ID */
+ uint16_t id;
+ /** Queue count */
+ uint16_t count;
+ /** Reserved */
+ uint8_t reserved_a[2];
+ /** Base address */
+ uint64_t base;
+ /** Reserved */
+ uint8_t reserved_b[8];
+ } __attribute__ (( packed )) tx;
+ /** Receive queue */
+ struct {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Queue ID */
+ uint16_t id;
+ /** Queue count */
+ uint32_t count;
+ /** Reserved */
+ uint8_t reserved_a[4];
+ /** Data buffer length */
+ uint32_t len;
+ /** Maximum frame size */
+ uint32_t mfs;
+ /** Reserved */
+ uint8_t reserved_b[4];
+ /** Base address */
+ uint64_t base;
+ /** Reserved */
+ uint8_t reserved_c[8];
+ } __attribute__ (( packed )) rx;
+ /** Reserved
+ *
+ * This field exists only due to a bug in the PF driver's
+ * message validation logic, which causes it to miscalculate
+ * the expected message length.
+ */
+ uint8_t reserved_b[64];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF IRQ Map opcode */
+#define INTELXL_ADMIN_VF_IRQ_MAP 0x00000007
+
+/** Admin Queue VF IRQ Map data buffer */
+struct intelxl_admin_vf_irq_map_buffer {
+ /** Number of interrupt vectors */
+ uint16_t count;
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Interrupt vector ID */
+ uint16_t vec;
+ /** Receive queue bitmap */
+ uint16_t rxmap;
+ /** Transmit queue bitmap */
+ uint16_t txmap;
+ /** Receive interrupt throttling index */
+ uint16_t rxitr;
+ /** Transmit interrupt throttling index */
+ uint16_t txitr;
+ /** Reserved
+ *
+ * This field exists only due to a bug in the PF driver's
+ * message validation logic, which causes it to miscalculate
+ * the expected message length.
+ */
+ uint8_t reserved[12];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Enable Queues opcode */
+#define INTELXL_ADMIN_VF_ENABLE 0x00000008
+
+/** Admin Queue VF Disable Queues opcode */
+#define INTELXL_ADMIN_VF_DISABLE 0x00000009
+
+/** Admin Queue VF Enable/Disable Queues data buffer */
+struct intelxl_admin_vf_queues_buffer {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Reserved */
+ uint8_t reserved[2];
+ /** Receive queue bitmask */
+ uint32_t rx;
+ /** Transmit queue bitmask */
+ uint32_t tx;
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Configure Promiscuous Mode opcode */
+#define INTELXL_ADMIN_VF_PROMISC 0x0000000e
+
+/** Admin Queue VF Configure Promiscuous Mode data buffer */
+struct intelxl_admin_vf_promisc_buffer {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Flags */
+ uint16_t flags;
+} __attribute__ (( packed ));
+
/** Admin queue command parameters */
union intelxl_admin_params {
/** Additional data buffer command parameters */
@@ -281,6 +480,8 @@ union intelxl_admin_params {
struct intelxl_admin_driver_params driver;
/** Shutdown command parameters */
struct intelxl_admin_shutdown_params shutdown;
+ /** Clear PXE Mode command parameters */
+ struct intelxl_admin_clear_pxe_params pxe;
/** Get Switch Configuration command parameters */
struct intelxl_admin_switch_params sw;
/** Get VSI Parameters command parameters */
@@ -301,6 +502,20 @@ union intelxl_admin_buffer {
struct intelxl_admin_switch_buffer sw;
/** Get VSI Parameters data buffer */
struct intelxl_admin_vsi_buffer vsi;
+ /** VF Get Resources data buffer */
+ struct intelxl_admin_vf_get_resources_buffer res;
+ /** VF Status Change Event data buffer */
+ struct intelxl_admin_vf_status_buffer stat;
+ /** VF Configure Queues data buffer */
+ struct intelxl_admin_vf_configure_buffer cfg;
+ /** VF Enable/Disable Queues data buffer */
+ struct intelxl_admin_vf_queues_buffer queues;
+ /** VF Configure Promiscuous Mode data buffer */
+ struct intelxl_admin_vf_promisc_buffer promisc;
+ /*** VF IRQ Map data buffer */
+ struct intelxl_admin_vf_irq_map_buffer irq;
+ /** Alignment padding */
+ uint8_t pad[INTELXL_ALIGN];
} __attribute__ (( packed ));
/** Admin queue descriptor */
@@ -313,10 +528,15 @@ struct intelxl_admin_descriptor {
uint16_t len;
/** Return value */
uint16_t ret;
- /** Cookie */
- uint32_t cookie;
- /** Reserved */
- uint32_t reserved;
+ /** Opaque cookie / VF opcode */
+ union {
+ /** Cookie */
+ uint32_t cookie;
+ /** VF opcode */
+ uint32_t vopcode;
+ };
+ /** VF return value */
+ int32_t vret;
/** Parameters */
union intelxl_admin_params params;
} __attribute__ (( packed ));
@@ -340,25 +560,30 @@ struct intelxl_admin_descriptor {
struct intelxl_admin {
/** Descriptors */
struct intelxl_admin_descriptor *desc;
+ /** Data buffers */
+ union intelxl_admin_buffer *buf;
/** Queue index */
unsigned int index;
- /** Register block */
- unsigned int reg;
- /** Data buffer */
- union intelxl_admin_buffer *buffer;
+ /** Register block base */
+ unsigned int base;
+ /** Register offsets */
+ const struct intelxl_admin_offsets *regs;
};
/**
* Initialise admin queue
*
* @v admin Admin queue
- * @v reg Register block
+ * @v base Register block base
+ * @v regs Register offsets
*/
static inline __attribute__ (( always_inline )) void
-intelxl_init_admin ( struct intelxl_admin *admin, unsigned int reg ) {
+intelxl_init_admin ( struct intelxl_admin *admin, unsigned int base,
+ const struct intelxl_admin_offsets *regs ) {
- admin->reg = reg;
+ admin->base = base;
+ admin->regs = regs;
}
/** Number of admin queue descriptors */
@@ -466,6 +691,9 @@ struct intelxl_context_rx {
/** Receive queue data buffer length */
#define INTELXL_CTX_RX_LEN( len ) ( (len) >> 1 )
+/** Use 32-byte receive descriptors */
+#define INTELXL_CTX_RX_FL_DSIZE 0x10
+
/** Strip CRC from received packets */
#define INTELXL_CTX_RX_FL_CRCSTRIP 0x20
@@ -524,6 +752,10 @@ struct intelxl_context_rx {
/** Queue Tail Pointer Register (offset) */
#define INTELXL_QXX_TAIL 0x8000
+/** Global RLAN Control 0 register */
+#define INTELXL_GLLAN_RCTL_0 0x12a500
+#define INTELXL_GLLAN_RCTL_0_PXE_MODE 0x00000001UL /**< PXE mode */
+
/** Transmit data descriptor */
struct intelxl_tx_data_descriptor {
/** Buffer address */
@@ -569,6 +801,14 @@ struct intelxl_tx_writeback_descriptor {
/** Transmit writeback descriptor complete */
#define INTELXL_TX_WB_FL_DD 0x01
+/** Transmit descriptor */
+union intelxl_tx_descriptor {
+ /** Transmit data descriptor */
+ struct intelxl_tx_data_descriptor data;
+ /** Transmit writeback descriptor */
+ struct intelxl_tx_writeback_descriptor wb;
+};
+
/** Receive data descriptor */
struct intelxl_rx_data_descriptor {
/** Buffer address */
@@ -576,22 +816,31 @@ struct intelxl_rx_data_descriptor {
/** Flags */
uint32_t flags;
/** Reserved */
- uint8_t reserved[4];
+ uint8_t reserved[20];
} __attribute__ (( packed ));
/** Receive writeback descriptor */
struct intelxl_rx_writeback_descriptor {
/** Reserved */
- uint8_t reserved[8];
+ uint8_t reserved_a[2];
+ /** VLAN tag */
+ uint16_t vlan;
+ /** Reserved */
+ uint8_t reserved_b[4];
/** Flags */
uint32_t flags;
/** Length */
uint32_t len;
+ /** Reserved */
+ uint8_t reserved_c[16];
} __attribute__ (( packed ));
/** Receive writeback descriptor complete */
#define INTELXL_RX_WB_FL_DD 0x00000001UL
+/** Receive writeback descriptor VLAN tag present */
+#define INTELXL_RX_WB_FL_VLAN 0x00000004UL
+
/** Receive writeback descriptor error */
#define INTELXL_RX_WB_FL_RXE 0x00080000UL
@@ -599,21 +848,24 @@ struct intelxl_rx_writeback_descriptor {
#define INTELXL_RX_WB_LEN(len) ( ( (len) >> 6 ) & 0x3fff )
/** Packet descriptor */
-union intelxl_descriptor {
- /** Transmit data descriptor */
- struct intelxl_tx_data_descriptor tx;
- /** Transmit writeback descriptor */
- struct intelxl_tx_writeback_descriptor tx_wb;
+union intelxl_rx_descriptor {
/** Receive data descriptor */
- struct intelxl_rx_data_descriptor rx;
+ struct intelxl_rx_data_descriptor data;
/** Receive writeback descriptor */
- struct intelxl_rx_writeback_descriptor rx_wb;
+ struct intelxl_rx_writeback_descriptor wb;
};
/** Descriptor ring */
struct intelxl_ring {
/** Descriptors */
- union intelxl_descriptor *desc;
+ union {
+ /** Transmit descriptors */
+ union intelxl_tx_descriptor *tx;
+ /** Receive descriptors */
+ union intelxl_rx_descriptor *rx;
+ /** Raw data */
+ void *raw;
+ } desc;
/** Producer index */
unsigned int prod;
/** Consumer index */
@@ -621,6 +873,8 @@ struct intelxl_ring {
/** Register block */
unsigned int reg;
+ /** Tail register */
+ unsigned int tail;
/** Length (in bytes) */
size_t len;
/** Program queue context
@@ -636,33 +890,39 @@ struct intelxl_ring {
*
* @v ring Descriptor ring
* @v count Number of descriptors
+ * @v len Length of a single descriptor
* @v context Method to program queue context
*/
static inline __attribute__ (( always_inline)) void
-intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count,
+intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len,
int ( * context ) ( struct intelxl_nic *intelxl,
physaddr_t address ) ) {
- ring->len = ( count * sizeof ( ring->desc[0] ) );
+ ring->len = ( count * len );
ring->context = context;
}
-/** Number of transmit descriptors */
-#define INTELXL_TX_NUM_DESC 16
+/** Number of transmit descriptors
+ *
+ * Chosen to exceed the receive ring fill level, in order to avoid
+ * running out of transmit descriptors when sending TCP ACKs.
+ */
+#define INTELXL_TX_NUM_DESC 64
/** Transmit descriptor ring maximum fill level */
#define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 )
/** Number of receive descriptors
*
- * In PXE mode (i.e. able to post single receive descriptors), 8
- * descriptors is the only permitted value covering all possible
- * numbers of PFs.
+ * Must be a multiple of 32.
*/
-#define INTELXL_RX_NUM_DESC 8
+#define INTELXL_RX_NUM_DESC 32
-/** Receive descriptor ring fill level */
-#define INTELXL_RX_FILL ( INTELXL_RX_NUM_DESC - 1 )
+/** Receive descriptor ring fill level
+ *
+ * Must be a multiple of 8 and greater than 8.
+ */
+#define INTELXL_RX_FILL 16
/******************************************************************************
*
@@ -673,9 +933,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count,
/** PF Interrupt Zero Dynamic Control Register */
#define INTELXL_PFINT_DYN_CTL0 0x038480
-#define INTELXL_PFINT_DYN_CTL0_INTENA 0x00000001UL /**< Enable */
-#define INTELXL_PFINT_DYN_CTL0_CLEARPBA 0x00000002UL /**< Acknowledge */
-#define INTELXL_PFINT_DYN_CTL0_INTENA_MASK 0x80000000UL /**< Ignore enable */
+#define INTELXL_INT_DYN_CTL_INTENA 0x00000001UL /**< Enable */
+#define INTELXL_INT_DYN_CTL_CLEARPBA 0x00000002UL /**< Acknowledge */
+#define INTELXL_INT_DYN_CTL_INTENA_MASK 0x80000000UL /**< Ignore enable */
/** PF Interrupt Zero Linked List Register */
#define INTELXL_PFINT_LNKLST0 0x038500
@@ -773,12 +1033,27 @@ struct intelxl_nic {
unsigned int vsi;
/** Queue set handle */
unsigned int qset;
+ /** Interrupt control register */
+ unsigned int intr;
+ /** MSI-X capability */
+ struct pci_msix msix;
+ /** MSI-X dummy interrupt target */
+ uint32_t msg;
+ /** PCI Express capability offset */
+ unsigned int exp;
/** Admin command queue */
struct intelxl_admin command;
/** Admin event queue */
struct intelxl_admin event;
+ /** Current VF opcode */
+ unsigned int vopcode;
+ /** Current VF return value */
+ int vret;
+ /** Current VF event data buffer */
+ union intelxl_admin_buffer vbuf;
+
/** Transmit descriptor ring */
struct intelxl_ring tx;
/** Receive descriptor ring */
@@ -787,4 +1062,30 @@ struct intelxl_nic {
struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC];
};
+extern int intelxl_msix_enable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci );
+extern void intelxl_msix_disable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci );
+extern struct intelxl_admin_descriptor *
+intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl );
+extern union intelxl_admin_buffer *
+intelxl_admin_command_buffer ( struct intelxl_nic *intelxl );
+extern int intelxl_admin_command ( struct intelxl_nic *intelxl );
+extern void intelxl_poll_admin ( struct net_device *netdev );
+extern int intelxl_open_admin ( struct intelxl_nic *intelxl );
+extern void intelxl_reopen_admin ( struct intelxl_nic *intelxl );
+extern void intelxl_close_admin ( struct intelxl_nic *intelxl );
+extern int intelxl_alloc_ring ( struct intelxl_nic *intelxl,
+ struct intelxl_ring *ring );
+extern void intelxl_free_ring ( struct intelxl_nic *intelxl,
+ struct intelxl_ring *ring );
+extern void intelxl_empty_rx ( struct intelxl_nic *intelxl );
+extern int intelxl_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf );
+extern void intelxl_poll ( struct net_device *netdev );
+
+extern void intelxlvf_admin_event ( struct net_device *netdev,
+ struct intelxl_admin_descriptor *evt,
+ union intelxl_admin_buffer *buf );
+
#endif /* _INTELXL_H */
diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c
new file mode 100644
index 000000000..8f76daf3d
--- /dev/null
+++ b/src/drivers/net/intelxlvf.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/pci.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include "intelxlvf.h"
+
+/** @file
+ *
+ * Intel 40 Gigabit Ethernet virtual function network card driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Device reset
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Reset hardware via PCIe function-level reset
+ *
+ * @v intelxl Intel device
+ */
+static void intelxlvf_reset_flr ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+ uint16_t control;
+
+ /* Perform a PCIe function-level reset */
+ pci_read_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ),
+ &control );
+ pci_write_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ),
+ ( control | PCI_EXP_DEVCTL_FLR ) );
+ mdelay ( INTELXL_RESET_DELAY_MS );
+}
+
+/**
+ * Wait for admin event queue to be torn down
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxlvf_reset_wait_teardown ( struct intelxl_nic *intelxl ) {
+ uint32_t admin_evt_len;
+ unsigned int i;
+
+ /* Wait for admin event queue to be torn down */
+ for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
+
+ /* Check admin event queue length register */
+ admin_evt_len = readl ( intelxl->regs + INTELXLVF_ADMIN +
+ INTELXLVF_ADMIN_EVT_LEN );
+ if ( ! ( admin_evt_len & INTELXL_ADMIN_LEN_ENABLE ) )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intelxl, "INTELXL %p timed out waiting for teardown (%#08x)\n",
+ intelxl, admin_evt_len );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Wait for virtual function to be marked as active
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) {
+ uint32_t vfgen_rstat;
+ unsigned int vfr_state;
+ unsigned int i;
+
+ /* Wait for virtual function to be marked as active */
+ for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
+
+ /* Check status as written by physical function driver */
+ vfgen_rstat = readl ( intelxl->regs + INTELXLVF_VFGEN_RSTAT );
+ vfr_state = INTELXLVF_VFGEN_RSTAT_VFR_STATE ( vfgen_rstat );
+ if ( vfr_state == INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intelxl, "INTELXL %p timed out waiting for activation "
+ "(%#08x)\n", intelxl, vfgen_rstat );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Reset hardware via admin queue
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin_descriptor *cmd;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_RESET );
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ goto err_command;
+
+ /* Wait for minimum reset time */
+ mdelay ( INTELXL_RESET_DELAY_MS );
+
+ /* Wait for reset to take effect */
+ if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 )
+ goto err_teardown;
+
+ /* Wait for virtual function to become active */
+ if ( ( rc = intelxlvf_reset_wait_active ( intelxl ) ) != 0 )
+ goto err_active;
+
+ err_active:
+ err_teardown:
+ intelxl_reopen_admin ( intelxl );
+ err_command:
+ return rc;
+}
+
+/******************************************************************************
+ *
+ * Admin queue
+ *
+ ******************************************************************************
+ */
+
+/** Admin command queue register offsets */
+static const struct intelxl_admin_offsets intelxlvf_admin_command_offsets = {
+ .bal = INTELXLVF_ADMIN_CMD_BAL,
+ .bah = INTELXLVF_ADMIN_CMD_BAH,
+ .len = INTELXLVF_ADMIN_CMD_LEN,
+ .head = INTELXLVF_ADMIN_CMD_HEAD,
+ .tail = INTELXLVF_ADMIN_CMD_TAIL,
+};
+
+/** Admin event queue register offsets */
+static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = {
+ .bal = INTELXLVF_ADMIN_EVT_BAL,
+ .bah = INTELXLVF_ADMIN_EVT_BAH,
+ .len = INTELXLVF_ADMIN_EVT_LEN,
+ .head = INTELXLVF_ADMIN_EVT_HEAD,
+ .tail = INTELXLVF_ADMIN_EVT_TAIL,
+};
+
+/**
+ * Issue admin queue virtual function command
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_command ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin *admin = &intelxl->command;
+ struct intelxl_admin_descriptor *cmd;
+ unsigned int i;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF );
+
+ /* Record opcode */
+ intelxl->vopcode = le32_to_cpu ( cmd->vopcode );
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ goto err_command;
+
+ /* Wait for response */
+ for ( i = 0 ; i < INTELXLVF_ADMIN_MAX_WAIT_MS ; i++ ) {
+
+ /* Poll admin event queue */
+ intelxl_poll_admin ( netdev );
+
+ /* If response has not arrived, delay 1ms and retry */
+ if ( intelxl->vopcode ) {
+ mdelay ( 1 );
+ continue;
+ }
+
+ /* Check for errors */
+ if ( intelxl->vret != 0 )
+ return -EIO;
+
+ return 0;
+ }
+
+ rc = -ETIMEDOUT;
+ DBGC ( intelxl, "INTELXL %p timed out waiting for admin VF command "
+ "%#x\n", intelxl, intelxl->vopcode );
+ err_command:
+ intelxl->vopcode = 0;
+ return rc;
+}
+
+/**
+ * Handle link status event
+ *
+ * @v netdev Network device
+ * @v link Link status
+ */
+static void intelxlvf_admin_link ( struct net_device *netdev,
+ struct intelxl_admin_vf_status_link *link ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+
+ DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl,
+ link->status, link->speed );
+
+ /* Update network device */
+ if ( link->status ) {
+ netdev_link_up ( netdev );
+ } else {
+ netdev_link_down ( netdev );
+ }
+}
+
+/**
+ * Handle status change event
+ *
+ * @v netdev Network device
+ * @v stat Status change event
+ */
+static void
+intelxlvf_admin_status ( struct net_device *netdev,
+ struct intelxl_admin_vf_status_buffer *stat ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+
+ /* Handle event */
+ switch ( stat->event ) {
+ case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ):
+ intelxlvf_admin_link ( netdev, &stat->data.link );
+ break;
+ default:
+ DBGC ( intelxl, "INTELXL %p unrecognised status change "
+ "event %#x:\n", intelxl, le32_to_cpu ( stat->event ) );
+ DBGC_HDA ( intelxl, 0, stat, sizeof ( *stat ) );
+ break;
+ }
+}
+
+/**
+ * Handle virtual function event
+ *
+ * @v netdev Network device
+ * @v evt Admin queue event descriptor
+ * @v buf Admin queue event data buffer
+ */
+void intelxlvf_admin_event ( struct net_device *netdev,
+ struct intelxl_admin_descriptor *evt,
+ union intelxl_admin_buffer *buf ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ unsigned int vopcode = le32_to_cpu ( evt->vopcode );
+
+ /* Record command response if applicable */
+ if ( vopcode == intelxl->vopcode ) {
+ memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) );
+ intelxl->vopcode = 0;
+ intelxl->vret = le32_to_cpu ( evt->vret );
+ if ( intelxl->vret != 0 ) {
+ DBGC ( intelxl, "INTELXL %p admin VF command %#x "
+ "error %d\n", intelxl, vopcode, intelxl->vret );
+ DBGC_HDA ( intelxl, virt_to_bus ( evt ), evt,
+ sizeof ( *evt ) );
+ DBGC_HDA ( intelxl, virt_to_bus ( buf ), buf,
+ le16_to_cpu ( evt->len ) );
+ }
+ return;
+ }
+
+ /* Handle unsolicited events */
+ switch ( vopcode ) {
+ case INTELXL_ADMIN_VF_STATUS:
+ intelxlvf_admin_status ( netdev, &buf->stat );
+ break;
+ default:
+ DBGC ( intelxl, "INTELXL %p unrecognised VF event %#x:\n",
+ intelxl, vopcode );
+ DBGC_HDA ( intelxl, 0, evt, sizeof ( *evt ) );
+ DBGC_HDA ( intelxl, 0, buf, le16_to_cpu ( evt->len ) );
+ break;
+ }
+}
+
+/**
+ * Get resources
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_get_resources ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_vf_get_resources_buffer *res;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ /* Parse response */
+ res = &intelxl->vbuf.res;
+ intelxl->vsi = le16_to_cpu ( res->vsi );
+ memcpy ( netdev->hw_addr, res->mac, ETH_ALEN );
+ DBGC ( intelxl, "INTELXL %p VSI %#04x\n", intelxl, intelxl->vsi );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Configure queues
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_configure ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->cfg ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->cfg.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->cfg.count = cpu_to_le16 ( 1 );
+ buf->cfg.tx.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->cfg.tx.count = cpu_to_le16 ( INTELXL_TX_NUM_DESC );
+ buf->cfg.tx.base = cpu_to_le64 ( virt_to_bus ( intelxl->tx.desc.raw ) );
+ buf->cfg.rx.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->cfg.rx.count = cpu_to_le32 ( INTELXL_RX_NUM_DESC );
+ buf->cfg.rx.len = cpu_to_le32 ( intelxl->mfs );
+ buf->cfg.rx.mfs = cpu_to_le32 ( intelxl->mfs );
+ buf->cfg.rx.base = cpu_to_le64 ( virt_to_bus ( intelxl->rx.desc.raw ) );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Configure IRQ mapping
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_irq_map ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->irq ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->irq.count = cpu_to_le16 ( 1 );
+ buf->irq.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->irq.rxmap = cpu_to_le16 ( 0x0001 );
+ buf->irq.txmap = cpu_to_le16 ( 0x0001 );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Enable/disable queues
+ *
+ * @v netdev Network device
+ * @v enable Enable queues
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXL_ADMIN_VF_ENABLE ) :
+ cpu_to_le32 ( INTELXL_ADMIN_VF_DISABLE ) );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->queues.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->queues.rx = cpu_to_le32 ( 1 );
+ buf->queues.tx = cpu_to_le32 ( 1 );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Configure promiscuous mode
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_promisc ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->promisc ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->promisc.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->promisc.flags = cpu_to_le16 ( INTELXL_ADMIN_PROMISC_FL_UNICAST |
+ INTELXL_ADMIN_PROMISC_FL_MULTICAST );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_open ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ int rc;
+
+ /* Calculate maximum frame size */
+ intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ +
+ INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) );
+
+ /* Allocate transmit descriptor ring */
+ if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->tx ) ) != 0 )
+ goto err_alloc_tx;
+
+ /* Allocate receive descriptor ring */
+ if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->rx ) ) != 0 )
+ goto err_alloc_rx;
+
+ /* Configure queues */
+ if ( ( rc = intelxlvf_admin_configure ( netdev ) ) != 0 )
+ goto err_configure;
+
+ /* Configure IRQ map */
+ if ( ( rc = intelxlvf_admin_irq_map ( netdev ) ) != 0 )
+ goto err_irq_map;
+
+ /* Enable queues */
+ if ( ( rc = intelxlvf_admin_queues ( netdev, 1 ) ) != 0 )
+ goto err_enable;
+
+ /* Configure promiscuous mode */
+ if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 )
+ goto err_promisc;
+
+ return 0;
+
+ err_promisc:
+ intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE );
+ err_enable:
+ err_irq_map:
+ err_configure:
+ intelxl_free_ring ( intelxl, &intelxl->rx );
+ err_alloc_rx:
+ intelxl_free_ring ( intelxl, &intelxl->tx );
+ err_alloc_tx:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void intelxlvf_close ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ int rc;
+
+ /* Disable queues */
+ if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) {
+ /* Leak memory; there's nothing else we can do */
+ return;
+ }
+
+ /* Free receive descriptor ring */
+ intelxl_free_ring ( intelxl, &intelxl->rx );
+
+ /* Free transmit descriptor ring */
+ intelxl_free_ring ( intelxl, &intelxl->tx );
+
+ /* Discard any unused receive buffers */
+ intelxl_empty_rx ( intelxl );
+}
+
+/** Network device operations */
+static struct net_device_operations intelxlvf_operations = {
+ .open = intelxlvf_open,
+ .close = intelxlvf_close,
+ .transmit = intelxl_transmit,
+ .poll = intelxl_poll,
+};
+
+/******************************************************************************
+ *
+ * PCI interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+static int intelxlvf_probe ( struct pci_device *pci ) {
+ struct net_device *netdev;
+ struct intelxl_nic *intelxl;
+ int rc;
+
+ /* Allocate and initialise net device */
+ netdev = alloc_etherdev ( sizeof ( *intelxl ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &intelxlvf_operations );
+ intelxl = netdev->priv;
+ pci_set_drvdata ( pci, netdev );
+ netdev->dev = &pci->dev;
+ memset ( intelxl, 0, sizeof ( *intelxl ) );
+ intelxl->intr = INTELXLVF_VFINT_DYN_CTL0;
+ intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN,
+ &intelxlvf_admin_command_offsets );
+ intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN,
+ &intelxlvf_admin_event_offsets );
+ intelxlvf_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC,
+ sizeof ( intelxl->tx.desc.tx[0] ),
+ INTELXLVF_QTX_TAIL );
+ intelxlvf_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC,
+ sizeof ( intelxl->rx.desc.rx[0] ),
+ INTELXLVF_QRX_TAIL );
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Map registers */
+ intelxl->regs = ioremap ( pci->membase, INTELXLVF_BAR_SIZE );
+ if ( ! intelxl->regs ) {
+ rc = -ENODEV;
+ goto err_ioremap;
+ }
+
+ /* Locate PCI Express capability */
+ intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP );
+ if ( ! intelxl->exp ) {
+ DBGC ( intelxl, "INTELXL %p missing PCIe capability\n",
+ intelxl );
+ rc = -ENXIO;
+ goto err_exp;
+ }
+
+ /* Reset the function via PCIe FLR */
+ intelxlvf_reset_flr ( intelxl, pci );
+
+ /* Enable MSI-X dummy interrupt */
+ if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
+ goto err_msix;
+
+ /* Open admin queues */
+ if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
+ goto err_open_admin;
+
+ /* Reset the function via admin queue */
+ if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 )
+ goto err_reset_admin;
+
+ /* Get MAC address */
+ if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 )
+ goto err_get_resources;
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register_netdev;
+
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register_netdev:
+ err_get_resources:
+ err_reset_admin:
+ intelxl_close_admin ( intelxl );
+ err_open_admin:
+ intelxl_msix_disable ( intelxl, pci );
+ err_msix:
+ intelxlvf_reset_flr ( intelxl, pci );
+ err_exp:
+ iounmap ( intelxl->regs );
+ err_ioremap:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void intelxlvf_remove ( struct pci_device *pci ) {
+ struct net_device *netdev = pci_get_drvdata ( pci );
+ struct intelxl_nic *intelxl = netdev->priv;
+
+ /* Unregister network device */
+ unregister_netdev ( netdev );
+
+ /* Reset the function via admin queue */
+ intelxlvf_reset_admin ( intelxl );
+
+ /* Close admin queues */
+ intelxl_close_admin ( intelxl );
+
+ /* Disable MSI-X dummy interrupt */
+ intelxl_msix_disable ( intelxl, pci );
+
+ /* Reset the function via PCIe FLR */
+ intelxlvf_reset_flr ( intelxl, pci );
+
+ /* Free network device */
+ iounmap ( intelxl->regs );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** PCI device IDs */
+static struct pci_device_id intelxlvf_nics[] = {
+ PCI_ROM ( 0x8086, 0x154c, "xl710-vf", "XL710 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x1571, "xl710-vf-hv", "XL710 VF (Hyper-V)", 0 ),
+ PCI_ROM ( 0x8086, 0x1889, "xl710-vf-ad", "XL710 VF (adaptive)", 0 ),
+ PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ),
+};
+
+/** PCI driver */
+struct pci_driver intelxlvf_driver __pci_driver = {
+ .ids = intelxlvf_nics,
+ .id_count = ( sizeof ( intelxlvf_nics ) /
+ sizeof ( intelxlvf_nics[0] ) ),
+ .probe = intelxlvf_probe,
+ .remove = intelxlvf_remove,
+};
diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h
new file mode 100644
index 000000000..ffcae5674
--- /dev/null
+++ b/src/drivers/net/intelxlvf.h
@@ -0,0 +1,86 @@
+#ifndef _INTELXLVF_H
+#define _INTELXLVF_H
+
+/** @file
+ *
+ * Intel 40 Gigabit Ethernet virtual function network card driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "intelxl.h"
+
+/** BAR size */
+#define INTELXLVF_BAR_SIZE 0x10000
+
+/** Transmit Queue Tail Register */
+#define INTELXLVF_QTX_TAIL 0x00000
+
+/** Receive Queue Tail Register */
+#define INTELXLVF_QRX_TAIL 0x02000
+
+/** VF Interrupt Zero Dynamic Control Register */
+#define INTELXLVF_VFINT_DYN_CTL0 0x5c00
+
+/** VF Admin Queue register block */
+#define INTELXLVF_ADMIN 0x6000
+
+/** Admin Command Queue Base Address Low Register (offset) */
+#define INTELXLVF_ADMIN_CMD_BAL 0x1c00
+
+/** Admin Command Queue Base Address High Register (offset) */
+#define INTELXLVF_ADMIN_CMD_BAH 0x1800
+
+/** Admin Command Queue Length Register (offset) */
+#define INTELXLVF_ADMIN_CMD_LEN 0x0800
+
+/** Admin Command Queue Head Register (offset) */
+#define INTELXLVF_ADMIN_CMD_HEAD 0x0400
+
+/** Admin Command Queue Tail Register (offset) */
+#define INTELXLVF_ADMIN_CMD_TAIL 0x2400
+
+/** Admin Event Queue Base Address Low Register (offset) */
+#define INTELXLVF_ADMIN_EVT_BAL 0x0c00
+
+/** Admin Event Queue Base Address High Register (offset) */
+#define INTELXLVF_ADMIN_EVT_BAH 0x0000
+
+/** Admin Event Queue Length Register (offset) */
+#define INTELXLVF_ADMIN_EVT_LEN 0x2000
+
+/** Admin Event Queue Head Register (offset) */
+#define INTELXLVF_ADMIN_EVT_HEAD 0x1400
+
+/** Admin Event Queue Tail Register (offset) */
+#define INTELXLVF_ADMIN_EVT_TAIL 0x1000
+
+/** Maximum time to wait for a VF admin request to complete */
+#define INTELXLVF_ADMIN_MAX_WAIT_MS 2000
+
+/** VF Reset Status Register */
+#define INTELXLVF_VFGEN_RSTAT 0x8800
+#define INTELXLVF_VFGEN_RSTAT_VFR_STATE(x) ( (x) & 0x3 )
+#define INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE 0x2
+
+/** Maximum time to wait for reset to complete */
+#define INTELXLVF_RESET_MAX_WAIT_MS 1000
+
+/**
+ * Initialise descriptor ring
+ *
+ * @v ring Descriptor ring
+ * @v count Number of descriptors
+ * @v len Length of a single descriptor
+ * @v tail Tail register offset
+ */
+static inline __attribute__ (( always_inline)) void
+intelxlvf_init_ring ( struct intelxl_ring *ring, unsigned int count,
+ size_t len, unsigned int tail ) {
+
+ ring->len = ( count * len );
+ ring->tail = tail;
+}
+
+#endif /* _INTELXLVF_H */
diff --git a/src/drivers/net/lan78xx.c b/src/drivers/net/lan78xx.c
index 3f705203e..a8cf57408 100644
--- a/src/drivers/net/lan78xx.c
+++ b/src/drivers/net/lan78xx.c
@@ -97,6 +97,10 @@ static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) {
if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 )
return 0;
+ /* Read MAC address from device tree, if present */
+ if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 )
+ return 0;
+
/* Otherwise, generate a random MAC address */
eth_random_addr ( netdev->hw_addr );
DBGC ( smscusb, "LAN78XX %p using random MAC %s\n",
diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c
index 9b09657db..3ec49584d 100644
--- a/src/drivers/net/smsc95xx.c
+++ b/src/drivers/net/smsc95xx.c
@@ -173,6 +173,10 @@ static int smsc95xx_fetch_mac ( struct smscusb_device *smscusb ) {
SMSC95XX_E2P_BASE ) ) == 0 )
return 0;
+ /* Read MAC address from device tree, if present */
+ if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 )
+ return 0;
+
/* Construct MAC address for Honeywell VM3, if applicable */
if ( ( rc = smsc95xx_vm3_fetch_mac ( smscusb ) ) == 0 )
return 0;
diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c
index 538d338c4..c639c58c1 100644
--- a/src/drivers/net/smscusb.c
+++ b/src/drivers/net/smscusb.c
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/usbnet.h>
#include <ipxe/ethernet.h>
#include <ipxe/profile.h>
+#include <ipxe/fdt.h>
#include "smscusb.h"
/** @file
@@ -441,6 +442,39 @@ int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb,
/******************************************************************************
*
+ * Device tree
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Fetch MAC address from device tree
+ *
+ * @v smscusb SMSC USB device
+ * @ret rc Return status code
+ */
+int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb ) {
+ struct net_device *netdev = smscusb->netdev;
+ unsigned int offset;
+ int rc;
+
+ /* Look for "ethernet[0]" alias */
+ if ( ( rc = fdt_alias ( "ethernet", &offset ) != 0 ) &&
+ ( rc = fdt_alias ( "ethernet0", &offset ) != 0 ) ) {
+ return rc;
+ }
+
+ /* Fetch MAC address */
+ if ( ( rc = fdt_mac ( offset, netdev ) ) != 0 )
+ return rc;
+
+ DBGC ( smscusb, "SMSCUSB %p using FDT MAC %s\n",
+ smscusb, eth_ntoa ( netdev->hw_addr ) );
+ return 0;
+}
+
+/******************************************************************************
+ *
* MII access
*
******************************************************************************
diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h
index b5d9ad3fc..e866bb747 100644
--- a/src/drivers/net/smscusb.h
+++ b/src/drivers/net/smscusb.h
@@ -287,6 +287,7 @@ extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb,
unsigned int e2p_base );
extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb,
unsigned int otp_base );
+extern int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb );
extern int smscusb_mii_check_link ( struct smscusb_device *smscusb );
extern int smscusb_mii_open ( struct smscusb_device *smscusb,
unsigned int phy_mask, unsigned int intrs );
diff --git a/src/include/errno.h b/src/include/errno.h
index e80bf9ca5..342384fa4 100644
--- a/src/include/errno.h
+++ b/src/include/errno.h
@@ -262,10 +262,10 @@ static inline void eplatform_discard ( int dummy __unused, ... ) {}
".align 8\n\t" \
"\n1:\n\t" \
".long ( 4f - 1b )\n\t" \
- ".long %a0\n\t" \
+ ".long %c0\n\t" \
".long ( 2f - 1b )\n\t" \
".long ( 3f - 1b )\n\t" \
- ".long %a1\n\t" \
+ ".long %c1\n\t" \
"\n2:\t.asciz \"" __einfo_desc ( einfo ) "\"\n\t" \
"\n3:\t.asciz \"" __FILE__ "\"\n\t" \
".align 8\n\t" \
diff --git a/src/include/ipxe/efi/efi_blacklist.h b/src/include/ipxe/efi/efi_blacklist.h
new file mode 100644
index 000000000..c5a5a61dc
--- /dev/null
+++ b/src/include/ipxe/efi/efi_blacklist.h
@@ -0,0 +1,13 @@
+#ifndef _IPXE_EFI_BLACKLIST_H
+#define _IPXE_EFI_BLACKLIST_H
+
+/** @file
+ *
+ * EFI driver blacklist
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern void efi_unload_blacklist ( void );
+
+#endif /* _IPXE_EFI_BLACKLIST_H */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 596491a1f..242f91f82 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -74,6 +74,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_efi_block ( ERRFILE_CORE | 0x00220000 )
#define ERRFILE_sanboot ( ERRFILE_CORE | 0x00230000 )
#define ERRFILE_dummy_sanboot ( ERRFILE_CORE | 0x00240000 )
+#define ERRFILE_fdt ( ERRFILE_CORE | 0x00250000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
@@ -205,6 +206,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_ena ( ERRFILE_DRIVER | 0x00c90000 )
#define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 )
#define ERRFILE_intelxl ( ERRFILE_DRIVER | 0x00cb0000 )
+#define ERRFILE_pcimsix ( ERRFILE_DRIVER | 0x00cc0000 )
+#define ERRFILE_intelxlvf ( ERRFILE_DRIVER | 0x00cd0000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )
@@ -375,6 +378,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 )
#define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 )
#define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 )
+#define ERRFILE_efi_blacklist ( ERRFILE_OTHER | 0x00520000 )
/** @} */
diff --git a/src/include/ipxe/fdt.h b/src/include/ipxe/fdt.h
new file mode 100644
index 000000000..97efa100c
--- /dev/null
+++ b/src/include/ipxe/fdt.h
@@ -0,0 +1,102 @@
+#ifndef _IPXE_FDT_H
+#define _IPXE_FDT_H
+
+/** @file
+ *
+ * Flattened Device Tree
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+struct net_device;
+
+/** Device tree header */
+struct fdt_header {
+ /** Magic signature */
+ uint32_t magic;
+ /** Total size of device tree */
+ uint32_t totalsize;
+ /** Offset to structure block */
+ uint32_t off_dt_struct;
+ /** Offset to strings block */
+ uint32_t off_dt_strings;
+ /** Offset to memory reservation block */
+ uint32_t off_mem_rsvmap;
+ /** Version of this data structure */
+ uint32_t version;
+ /** Lowest version to which this structure is compatible */
+ uint32_t last_comp_version;
+ /** Physical ID of the boot CPU */
+ uint32_t boot_cpuid_phys;
+ /** Length of string block */
+ uint32_t size_dt_strings;
+ /** Length of structure block */
+ uint32_t size_dt_struct;
+} __attribute__ (( packed ));
+
+/** Magic signature */
+#define FDT_MAGIC 0xd00dfeed
+
+/** Expected device tree version */
+#define FDT_VERSION 16
+
+/** Device tree token */
+typedef uint32_t fdt_token_t;
+
+/** Begin node token */
+#define FDT_BEGIN_NODE 0x00000001
+
+/** End node token */
+#define FDT_END_NODE 0x00000002
+
+/** Property token */
+#define FDT_PROP 0x00000003
+
+/** Property fragment */
+struct fdt_prop {
+ /** Data length */
+ uint32_t len;
+ /** Name offset */
+ uint32_t name_off;
+} __attribute__ (( packed ));
+
+/** NOP token */
+#define FDT_NOP 0x00000004
+
+/** End of structure block */
+#define FDT_END 0x00000009
+
+/** Alignment of structure block */
+#define FDT_STRUCTURE_ALIGN ( sizeof ( fdt_token_t ) )
+
+/** A device tree */
+struct fdt {
+ /** Tree data */
+ union {
+ /** Tree header */
+ const struct fdt_header *hdr;
+ /** Raw data */
+ const void *raw;
+ };
+ /** Length of tree */
+ size_t len;
+ /** Offset to structure block */
+ unsigned int structure;
+ /** Length of structure block */
+ size_t structure_len;
+ /** Offset to strings block */
+ unsigned int strings;
+ /** Length of strings block */
+ size_t strings_len;
+};
+
+extern int fdt_path ( const char *path, unsigned int *offset );
+extern int fdt_alias ( const char *name, unsigned int *offset );
+extern const char * fdt_string ( unsigned int offset, const char *name );
+extern int fdt_mac ( unsigned int offset, struct net_device *netdev );
+extern int register_fdt ( const struct fdt_header *hdr );
+
+#endif /* _IPXE_FDT_H */
diff --git a/src/include/ipxe/ocsp.h b/src/include/ipxe/ocsp.h
index be0bddc50..9eb70b2cc 100644
--- a/src/include/ipxe/ocsp.h
+++ b/src/include/ipxe/ocsp.h
@@ -42,8 +42,8 @@ struct ocsp_check;
struct ocsp_request {
/** Request builder */
struct asn1_builder builder;
- /** Certificate ID */
- struct asn1_cursor cert_id;
+ /** Certificate ID (excluding hashAlgorithm) */
+ struct asn1_cursor cert_id_tail;
};
/** An OCSP responder */
diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h
index ddd8c8d1e..272c4c06f 100644
--- a/src/include/ipxe/pci.h
+++ b/src/include/ipxe/pci.h
@@ -94,6 +94,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define PCI_CAP_ID_VPD 0x03 /**< Vital product data */
#define PCI_CAP_ID_VNDR 0x09 /**< Vendor-specific */
#define PCI_CAP_ID_EXP 0x10 /**< PCI Express */
+#define PCI_CAP_ID_MSIX 0x11 /**< MSI-X */
#define PCI_CAP_ID_EA 0x14 /**< Enhanced Allocation */
/** Next capability */
@@ -109,6 +110,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define PCI_EXP_DEVCTL 0x08
#define PCI_EXP_DEVCTL_FLR 0x8000 /**< Function level reset */
+/** MSI-X interrupts */
+#define PCI_MSIX_CTRL 0x02
+#define PCI_MSIX_CTRL_ENABLE 0x8000 /**< Enable MSI-X */
+#define PCI_MSIX_CTRL_MASK 0x4000 /**< Mask all interrupts */
+#define PCI_MSIX_CTRL_SIZE(x) ( (x) & 0x07ff ) /**< Table size */
+#define PCI_MSIX_DESC_TABLE 0x04
+#define PCI_MSIX_DESC_PBA 0x08
+#define PCI_MSIX_DESC_BIR(x) ( (x) & 0x00000007 ) /**< BAR index */
+#define PCI_MSIX_DESC_OFFSET(x) ( (x) & 0xfffffff8 ) /**< BAR offset */
+
/** Uncorrectable error status */
#define PCI_ERR_UNCOR_STATUS 0x04
diff --git a/src/include/ipxe/pcimsix.h b/src/include/ipxe/pcimsix.h
new file mode 100644
index 000000000..aa2aaf017
--- /dev/null
+++ b/src/include/ipxe/pcimsix.h
@@ -0,0 +1,77 @@
+#ifndef _IPXE_PCIMSIX_H
+#define _IPXE_PCIMSIX_H
+
+/** @file
+ *
+ * PCI MSI-X interrupts
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/pci.h>
+
+/** MSI-X BAR mapped length */
+#define PCI_MSIX_LEN 0x1000
+
+/** MSI-X vector offset */
+#define PCI_MSIX_VECTOR(n) ( (n) * 0x10 )
+
+/** MSI-X vector address low 32 bits */
+#define PCI_MSIX_ADDRESS_LO 0x0
+
+/** MSI-X vector address high 32 bits */
+#define PCI_MSIX_ADDRESS_HI 0x4
+
+/** MSI-X vector data */
+#define PCI_MSIX_DATA 0x8
+
+/** MSI-X vector control */
+#define PCI_MSIX_CONTROL 0xc
+#define PCI_MSIX_CONTROL_MASK 0x00000001 /**< Vector is masked */
+
+/** PCI MSI-X capability */
+struct pci_msix {
+ /** Capability offset */
+ unsigned int cap;
+ /** Number of vectors */
+ unsigned int count;
+ /** MSI-X table */
+ void *table;
+ /** Pending bit array */
+ void *pba;
+};
+
+extern int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix );
+extern void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix );
+extern void pci_msix_map ( struct pci_msix *msix, unsigned int vector,
+ physaddr_t address, uint32_t data );
+extern void pci_msix_control ( struct pci_msix *msix, unsigned int vector,
+ uint32_t mask );
+extern void pci_msix_dump ( struct pci_msix *msix, unsigned int vector );
+
+/**
+ * Mask MSI-X interrupt vector
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ */
+static inline __attribute__ (( always_inline )) void
+pci_msix_mask ( struct pci_msix *msix, unsigned int vector ) {
+
+ pci_msix_control ( msix, vector, PCI_MSIX_CONTROL_MASK );
+}
+
+/**
+ * Unmask MSI-X interrupt vector
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ */
+static inline __attribute__ (( always_inline )) void
+pci_msix_unmask ( struct pci_msix *msix, unsigned int vector ) {
+
+ pci_msix_control ( msix, vector, 0 );
+}
+
+#endif /* _IPXE_PCIMSIX_H */
diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h
index b1e702e18..febbdc589 100644
--- a/src/include/ipxe/tls.h
+++ b/src/include/ipxe/tls.h
@@ -63,6 +63,7 @@ struct tls_header {
#define TLS_HELLO_REQUEST 0
#define TLS_CLIENT_HELLO 1
#define TLS_SERVER_HELLO 2
+#define TLS_NEW_SESSION_TICKET 4
#define TLS_CERTIFICATE 11
#define TLS_SERVER_KEY_EXCHANGE 12
#define TLS_CERTIFICATE_REQUEST 13
@@ -108,6 +109,9 @@ struct tls_header {
/* TLS signature algorithms extension */
#define TLS_SIGNATURE_ALGORITHMS 13
+/* TLS session ticket extension */
+#define TLS_SESSION_TICKET 35
+
/* TLS renegotiation information extension */
#define TLS_RENEGOTIATION_INFO 0xff01
@@ -242,13 +246,48 @@ struct md5_sha1_digest {
/** MD5+SHA1 digest size */
#define MD5_SHA1_DIGEST_SIZE sizeof ( struct md5_sha1_digest )
-/** A TLS connection */
-struct tls_connection {
+/** A TLS session */
+struct tls_session {
/** Reference counter */
struct refcnt refcnt;
+ /** List of sessions */
+ struct list_head list;
/** Server name */
const char *name;
+ /** Session ID */
+ uint8_t id[32];
+ /** Length of session ID */
+ size_t id_len;
+ /** Session ticket */
+ void *ticket;
+ /** Length of session ticket */
+ size_t ticket_len;
+ /** Master secret */
+ uint8_t master_secret[48];
+
+ /** List of connections */
+ struct list_head conn;
+};
+
+/** A TLS connection */
+struct tls_connection {
+ /** Reference counter */
+ struct refcnt refcnt;
+
+ /** Session */
+ struct tls_session *session;
+ /** List of connections within the same session */
+ struct list_head list;
+ /** Session ID */
+ uint8_t session_id[32];
+ /** Length of session ID */
+ size_t session_id_len;
+ /** New session ticket */
+ void *new_session_ticket;
+ /** Length of new session ticket */
+ size_t new_session_ticket_len;
+
/** Plaintext stream */
struct interface plainstream;
/** Ciphertext stream */
@@ -296,6 +335,8 @@ struct tls_connection {
struct pending_operation client_negotiation;
/** Server security negotiation pending operation */
struct pending_operation server_negotiation;
+ /** Certificate validation pending operation */
+ struct pending_operation validation;
/** TX sequence number */
uint64_t tx_seq;
diff --git a/src/include/ipxe/vlan.h b/src/include/ipxe/vlan.h
index 439e0c16d..7f93439b3 100644
--- a/src/include/ipxe/vlan.h
+++ b/src/include/ipxe/vlan.h
@@ -10,6 +10,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <ipxe/netdevice.h>
+
/** A VLAN header */
struct vlan_header {
/** Tag control information */
@@ -59,12 +61,14 @@ struct vlan_header {
*/
#define VLAN_PRIORITY_IS_VALID( priority ) ( (priority) <= 7 )
-extern struct net_device * vlan_find ( struct net_device *trunk,
- unsigned int tag );
extern unsigned int vlan_tag ( struct net_device *netdev );
extern int vlan_can_be_trunk ( struct net_device *trunk );
extern int vlan_create ( struct net_device *trunk, unsigned int tag,
unsigned int priority );
extern int vlan_destroy ( struct net_device *netdev );
+extern void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag,
+ struct io_buffer *iobuf );
+extern void vlan_netdev_rx_err ( struct net_device *netdev, unsigned int tag,
+ struct io_buffer *iobuf, int rc );
#endif /* _IPXE_VLAN_H */
diff --git a/src/interface/efi/efi_blacklist.c b/src/interface/efi/efi_blacklist.c
new file mode 100644
index 000000000..292b28e8c
--- /dev/null
+++ b/src/interface/efi/efi_blacklist.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/settings.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/DriverBinding.h>
+#include <ipxe/efi/Protocol/LoadedImage.h>
+#include <ipxe/efi/Protocol/ComponentName.h>
+#include <ipxe/efi/efi_blacklist.h>
+
+/** @file
+ *
+ * EFI driver blacklist
+ *
+ */
+
+/** A blacklisted driver */
+struct efi_blacklist {
+ /** Name */
+ const char *name;
+ /**
+ * Check if driver is blacklisted
+ *
+ * @v binding Driver binding protocol
+ * @v loaded Loaded image protocol
+ * @v wtf Component name protocol, if present
+ * @ret blacklisted Driver is the blacklisted driver
+ */
+ int ( * blacklist ) ( EFI_DRIVER_BINDING_PROTOCOL *binding,
+ EFI_LOADED_IMAGE_PROTOCOL *loaded,
+ EFI_COMPONENT_NAME_PROTOCOL *wtf );
+};
+
+/**
+ * Blacklist Dell Ip4ConfigDxe driver
+ *
+ * @v binding Driver binding protocol
+ * @v loaded Loaded image protocol
+ * @v wtf Component name protocol, if present
+ * @ret blacklisted Driver is the blacklisted driver
+ */
+static int
+efi_blacklist_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused,
+ EFI_LOADED_IMAGE_PROTOCOL *loaded __unused,
+ EFI_COMPONENT_NAME_PROTOCOL *wtf ) {
+ static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver";
+ static const char dell[] = "Dell Inc.";
+ char manufacturer[ sizeof ( dell ) ];
+ CHAR16 *name;
+
+ /* Check driver name */
+ if ( ! wtf )
+ return 0;
+ if ( wtf->GetDriverName ( wtf, "eng", &name ) != 0 )
+ return 0;
+ if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 )
+ return 0;
+
+ /* Check manufacturer */
+ fetch_string_setting ( NULL, &manufacturer_setting, manufacturer,
+ sizeof ( manufacturer ) );
+ if ( strcmp ( manufacturer, dell ) != 0 )
+ return 0;
+
+ return 1;
+}
+
+/** Blacklisted drivers */
+static struct efi_blacklist efi_blacklists[] = {
+ {
+ .name = "Dell Ip4Config",
+ .blacklist = efi_blacklist_dell_ip4config,
+ },
+};
+
+/**
+ * Find driver blacklisting, if any
+ *
+ * @v driver Driver binding handle
+ * @ret blacklist Driver blacklisting, or NULL
+ * @ret rc Return status code
+ */
+static int efi_blacklist ( EFI_HANDLE driver,
+ struct efi_blacklist **blacklist ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_DRIVER_BINDING_PROTOCOL *binding;
+ void *interface;
+ } binding;
+ union {
+ EFI_LOADED_IMAGE_PROTOCOL *loaded;
+ void *interface;
+ } loaded;
+ union {
+ EFI_COMPONENT_NAME_PROTOCOL *wtf;
+ void *interface;
+ } wtf;
+ unsigned int i;
+ EFI_HANDLE image;
+ EFI_STATUS efirc;
+ int rc;
+
+ DBGC2 ( &efi_blacklists, "EFIBL checking %s\n",
+ efi_handle_name ( driver ) );
+
+ /* Mark as not blacklisted */
+ *blacklist = NULL;
+
+ /* Open driver binding protocol */
+ if ( ( efirc = bs->OpenProtocol (
+ driver, &efi_driver_binding_protocol_guid,
+ &binding.interface, efi_image_handle, driver,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( driver, "EFIBL %s could not open driver binding "
+ "protocol: %s\n", efi_handle_name ( driver ),
+ strerror ( rc ) );
+ goto err_binding;
+ }
+ image = binding.binding->ImageHandle;
+
+ /* Open loaded image protocol */
+ if ( ( efirc = bs->OpenProtocol (
+ image, &efi_loaded_image_protocol_guid,
+ &loaded.interface, efi_image_handle, image,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( driver, "EFIBL %s could not open",
+ efi_handle_name ( driver ) );
+ DBGC ( driver, " %s loaded image protocol: %s\n",
+ efi_handle_name ( image ), strerror ( rc ) );
+ goto err_loaded;
+ }
+
+ /* Open component name protocol, if present*/
+ if ( ( efirc = bs->OpenProtocol (
+ driver, &efi_component_name_protocol_guid,
+ &wtf.interface, efi_image_handle, driver,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+ /* Ignore failure; is not required to be present */
+ wtf.interface = NULL;
+ }
+
+ /* Check blacklistings */
+ for ( i = 0 ; i < ( sizeof ( efi_blacklists ) /
+ sizeof ( efi_blacklists[0] ) ) ; i++ ) {
+ if ( efi_blacklists[i].blacklist ( binding.binding,
+ loaded.loaded, wtf.wtf ) ) {
+ *blacklist = &efi_blacklists[i];
+ break;
+ }
+ }
+
+ /* Success */
+ rc = 0;
+
+ /* Close protocols */
+ if ( wtf.wtf ) {
+ bs->CloseProtocol ( driver, &efi_component_name_protocol_guid,
+ efi_image_handle, driver );
+ }
+ bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid,
+ efi_image_handle, image );
+ err_loaded:
+ bs->CloseProtocol ( driver, &efi_driver_binding_protocol_guid,
+ efi_image_handle, driver );
+ err_binding:
+ return rc;
+}
+
+/**
+ * Unload any blacklisted drivers
+ *
+ */
+void efi_unload_blacklist ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_blacklist *blacklist;
+ EFI_HANDLE *drivers;
+ EFI_HANDLE driver;
+ UINTN num_drivers;
+ unsigned int i;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Locate all driver binding protocol handles */
+ if ( ( efirc = bs->LocateHandleBuffer (
+ ByProtocol, &efi_driver_binding_protocol_guid,
+ NULL, &num_drivers, &drivers ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( &efi_blacklists, "EFIBL could not list all drivers: "
+ "%s\n", strerror ( rc ) );
+ return;
+ }
+
+ /* Unload any blacklisted drivers */
+ for ( i = 0 ; i < num_drivers ; i++ ) {
+ driver = drivers[i];
+ if ( ( rc = efi_blacklist ( driver, &blacklist ) ) != 0 ) {
+ DBGC ( driver, "EFIBL could not determine "
+ "blacklisting for %s: %s\n",
+ efi_handle_name ( driver ), strerror ( rc ) );
+ continue;
+ }
+ if ( ! blacklist )
+ continue;
+ DBGC ( driver, "EFIBL unloading %s (%s)\n",
+ efi_handle_name ( driver ), blacklist->name );
+ if ( ( efirc = bs->UnloadImage ( driver ) ) != 0 ) {
+ DBGC ( driver, "EFIBL could not unload %s: %s\n",
+ efi_handle_name ( driver ), strerror ( rc ) );
+ }
+ }
+
+ /* Free handle list */
+ bs->FreePool ( drivers );
+}
diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c
index 8ea0a822d..de9b1af55 100644
--- a/src/interface/efi/efi_debug.c
+++ b/src/interface/efi/efi_debug.c
@@ -331,8 +331,7 @@ void dbg_efi_protocols ( EFI_HANDLE handle ) {
/* Sanity check */
if ( ! handle ) {
- printf ( "HANDLE %s could not retrieve protocols\n",
- efi_handle_name ( handle ) );
+ printf ( "HANDLE %p could not retrieve protocols\n", handle );
return;
}
diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c
index 04796414a..7be2e585d 100644
--- a/src/interface/efi/efi_driver.c
+++ b/src/interface/efi/efi_driver.c
@@ -73,11 +73,14 @@ static struct efi_device * efidev_find ( EFI_HANDLE device ) {
*/
struct efi_device * efidev_parent ( struct device *dev ) {
struct device *parent;
+ struct efi_device *efidev;
- /* Walk upwards until we find an EFI device */
+ /* Walk upwards until we find a registered EFI device */
while ( ( parent = dev->parent ) ) {
- if ( parent->desc.bus_type == BUS_TYPE_EFI )
- return container_of ( parent, struct efi_device, dev );
+ list_for_each_entry ( efidev, &efi_devices, dev.siblings ) {
+ if ( parent == &efidev->dev )
+ return efidev;
+ }
dev = parent;
}
diff --git a/src/interface/efi/efi_fdt.c b/src/interface/efi/efi_fdt.c
new file mode 100644
index 000000000..cd3f109df
--- /dev/null
+++ b/src/interface/efi/efi_fdt.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <ipxe/fdt.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/init.h>
+
+/** @file
+ *
+ * EFI Flattened Device Tree
+ *
+ */
+
+#define DEVICE_TREE_TABLE_GUID \
+ { 0xb1b621d5, 0xf19c, 0x41a5, \
+ { 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } }
+
+/** EFI Flattened Device Tree configuration table */
+static struct fdt_header *efi_fdt;
+EFI_USE_TABLE ( DEVICE_TREE_TABLE, &efi_fdt, 0 );
+
+/**
+ * Initialise EFI Flattened Device Tree
+ *
+ */
+static void efi_fdt_init ( void ) {
+ int rc;
+
+ /* Do nothing if no configuration table is present */
+ if ( ! efi_fdt ) {
+ DBGC ( &efi_fdt, "EFIFDT has no configuration table\n" );
+ return;
+ }
+ DBGC ( &efi_fdt, "EFIFDT configuration table at %p\n", efi_fdt );
+
+ /* Register device tree */
+ if ( ( rc = register_fdt ( efi_fdt ) ) != 0 ) {
+ DBGC ( &efi_fdt, "EFIFDT could not register: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+/** EFI Flattened Device Tree initialisation function */
+struct init_fn efi_fdt_init_fn __init_fn ( INIT_EARLY ) = {
+ .initialise = efi_fdt_init,
+};
diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c
index 18b931e68..de3572c75 100644
--- a/src/interface/efi/efiprefix.c
+++ b/src/interface/efi/efiprefix.c
@@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi_snp.h>
#include <ipxe/efi/efi_autoboot.h>
#include <ipxe/efi/efi_watchdog.h>
+#include <ipxe/efi/efi_blacklist.h>
/**
* EFI entry point
@@ -75,6 +76,10 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
*/
static int efi_probe ( struct root_device *rootdev __unused ) {
+ /* Unloaded any blacklisted drivers */
+ efi_unload_blacklist();
+
+ /* Connect our drivers */
return efi_driver_connect_all();
}
@@ -85,6 +90,7 @@ static int efi_probe ( struct root_device *rootdev __unused ) {
*/
static void efi_remove ( struct root_device *rootdev __unused ) {
+ /* Disconnect our drivers */
efi_driver_disconnect_all();
}
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index 71a37eccc..3b02e64bd 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -1126,15 +1126,35 @@ __weak unsigned int vlan_tag ( struct net_device *netdev __unused ) {
}
/**
- * Identify VLAN device (when VLAN support is not present)
+ * Add VLAN tag-stripped packet to queue (when VLAN support is not present)
*
- * @v trunk Trunk network device
- * @v tag VLAN tag
- * @ret netdev VLAN device, if any
+ * @v netdev Network device
+ * @v tag VLAN tag, or zero
+ * @v iobuf I/O buffer
*/
-__weak struct net_device * vlan_find ( struct net_device *trunk __unused,
- unsigned int tag __unused ) {
- return NULL;
+__weak void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag,
+ struct io_buffer *iobuf ) {
+
+ if ( tag == 0 ) {
+ netdev_rx ( netdev, iobuf );
+ } else {
+ netdev_rx_err ( netdev, iobuf, -ENODEV );
+ }
+}
+
+/**
+ * Discard received VLAN tag-stripped packet (when VLAN support is not present)
+ *
+ * @v netdev Network device
+ * @v tag VLAN tag, or zero
+ * @v iobuf I/O buffer, or NULL
+ * @v rc Packet status code
+ */
+__weak void vlan_netdev_rx_err ( struct net_device *netdev,
+ unsigned int tag __unused,
+ struct io_buffer *iobuf, int rc ) {
+
+ netdev_rx_err ( netdev, iobuf, rc );
}
/** Networking stack process */
diff --git a/src/net/tcp.c b/src/net/tcp.c
index c445100ad..6bba44282 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -17,6 +17,7 @@
#include <ipxe/netdevice.h>
#include <ipxe/profile.h>
#include <ipxe/process.h>
+#include <ipxe/job.h>
#include <ipxe/tcpip.h>
#include <ipxe/tcp.h>
@@ -1704,10 +1705,30 @@ static int tcp_xfer_deliver ( struct tcp_connection *tcp,
return 0;
}
+/**
+ * Report job progress
+ *
+ * @v tcp TCP connection
+ * @v progress Progress report to fill in
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int tcp_progress ( struct tcp_connection *tcp,
+ struct job_progress *progress ) {
+
+ /* Report connection in progress if applicable */
+ if ( ! TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) {
+ snprintf ( progress->message, sizeof ( progress->message ),
+ "connecting" );
+ }
+
+ return 0;
+}
+
/** TCP data transfer interface operations */
static struct interface_operation tcp_xfer_operations[] = {
INTF_OP ( xfer_deliver, struct tcp_connection *, tcp_xfer_deliver ),
INTF_OP ( xfer_window, struct tcp_connection *, tcp_xfer_window ),
+ INTF_OP ( job_progress, struct tcp_connection *, tcp_progress ),
INTF_OP ( intf_close, struct tcp_connection *, tcp_xfer_close ),
};
diff --git a/src/net/tls.c b/src/net/tls.c
index 9d994cd77..746274d61 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -47,6 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/certstore.h>
#include <ipxe/rbg.h>
#include <ipxe/validator.h>
+#include <ipxe/job.h>
#include <ipxe/tls.h>
/* Disambiguate the various error causes */
@@ -102,9 +103,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EINVAL_MAC \
__einfo_uniqify ( EINFO_EINVAL, 0x0d, \
"Invalid MAC" )
+#define EINVAL_TICKET __einfo_error ( EINFO_EINVAL_TICKET )
+#define EINFO_EINVAL_TICKET \
+ __einfo_uniqify ( EINFO_EINVAL, 0x0e, \
+ "Invalid New Session Ticket record")
#define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT )
#define EINFO_EIO_ALERT \
- __einfo_uniqify ( EINFO_EINVAL, 0x01, \
+ __einfo_uniqify ( EINFO_EIO, 0x01, \
"Unknown alert level" )
#define ENOMEM_CONTEXT __einfo_error ( EINFO_ENOMEM_CONTEXT )
#define EINFO_ENOMEM_CONTEXT \
@@ -175,6 +180,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
__einfo_uniqify ( EINFO_EPROTO, 0x01, \
"Illegal protocol version upgrade" )
+/** List of TLS session */
+static LIST_HEAD ( tls_sessions );
+
+static void tls_tx_resume_all ( struct tls_session *session );
static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
const void *data, size_t len );
static void tls_clear_cipher ( struct tls_connection *tls,
@@ -308,6 +317,28 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = {
*/
/**
+ * Free TLS session
+ *
+ * @v refcnt Reference counter
+ */
+static void free_tls_session ( struct refcnt *refcnt ) {
+ struct tls_session *session =
+ container_of ( refcnt, struct tls_session, refcnt );
+
+ /* Sanity check */
+ assert ( list_empty ( &session->conn ) );
+
+ /* Remove from list of sessions */
+ list_del ( &session->list );
+
+ /* Free session ticket */
+ free ( session->ticket );
+
+ /* Free session */
+ free ( session );
+}
+
+/**
* Free TLS connection
*
* @v refcnt Reference counter
@@ -315,10 +346,12 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = {
static void free_tls ( struct refcnt *refcnt ) {
struct tls_connection *tls =
container_of ( refcnt, struct tls_connection, refcnt );
+ struct tls_session *session = tls->session;
struct io_buffer *iobuf;
struct io_buffer *tmp;
/* Free dynamically-allocated resources */
+ free ( tls->new_session_ticket );
tls_clear_cipher ( tls, &tls->tx_cipherspec );
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
tls_clear_cipher ( tls, &tls->rx_cipherspec );
@@ -330,8 +363,12 @@ static void free_tls ( struct refcnt *refcnt ) {
x509_put ( tls->cert );
x509_chain_put ( tls->chain );
+ /* Drop reference to session */
+ assert ( list_empty ( &tls->list ) );
+ ref_put ( &session->refcnt );
+
/* Free TLS structure itself */
- free ( tls );
+ free ( tls );
}
/**
@@ -345,6 +382,7 @@ static void tls_close ( struct tls_connection *tls, int rc ) {
/* Remove pending operations, if applicable */
pending_put ( &tls->client_negotiation );
pending_put ( &tls->server_negotiation );
+ pending_put ( &tls->validation );
/* Remove process */
process_del ( &tls->process );
@@ -353,6 +391,13 @@ static void tls_close ( struct tls_connection *tls, int rc ) {
intf_shutdown ( &tls->cipherstream, rc );
intf_shutdown ( &tls->plainstream, rc );
intf_shutdown ( &tls->validator, rc );
+
+ /* Remove from session */
+ list_del ( &tls->list );
+ INIT_LIST_HEAD ( &tls->list );
+
+ /* Resume all other connections, in case we were the lead connection */
+ tls_tx_resume_all ( tls->session );
}
/******************************************************************************
@@ -906,6 +951,7 @@ static void tls_restart ( struct tls_connection *tls ) {
assert ( ! tls->tx_pending );
assert ( ! is_pending ( &tls->client_negotiation ) );
assert ( ! is_pending ( &tls->server_negotiation ) );
+ assert ( ! is_pending ( &tls->validation ) );
/* (Re)initialise handshake context */
digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx );
@@ -929,6 +975,18 @@ static void tls_tx_resume ( struct tls_connection *tls ) {
}
/**
+ * Resume TX state machine for all connections within a session
+ *
+ * @v session TLS session
+ */
+static void tls_tx_resume_all ( struct tls_session *session ) {
+ struct tls_connection *tls;
+
+ list_for_each_entry ( tls, &session->conn, list )
+ tls_tx_resume ( tls );
+}
+
+/**
* Transmit Handshake record
*
* @v tls TLS connection
@@ -953,11 +1011,14 @@ static int tls_send_handshake ( struct tls_connection *tls,
* @ret rc Return status code
*/
static int tls_send_client_hello ( struct tls_connection *tls ) {
+ struct tls_session *session = tls->session;
+ size_t name_len = strlen ( session->name );
struct {
uint32_t type_length;
uint16_t version;
uint8_t random[32];
uint8_t session_id_len;
+ uint8_t session_id[tls->session_id_len];
uint16_t cipher_suite_len;
uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
uint8_t compression_methods_len;
@@ -971,7 +1032,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
struct {
uint8_t type;
uint16_t len;
- uint8_t name[ strlen ( tls->name ) ];
+ uint8_t name[name_len];
} __attribute__ (( packed )) list[1];
} __attribute__ (( packed )) server_name;
uint16_t max_fragment_length_type;
@@ -993,18 +1054,27 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
uint8_t data[ tls->secure_renegotiation ?
sizeof ( tls->verify.client ) :0];
} __attribute__ (( packed )) renegotiation_info;
+ uint16_t session_ticket_type;
+ uint16_t session_ticket_len;
+ struct {
+ uint8_t data[session->ticket_len];
+ } __attribute__ (( packed )) session_ticket;
} __attribute__ (( packed )) extensions;
} __attribute__ (( packed )) hello;
struct tls_cipher_suite *suite;
struct tls_signature_hash_algorithm *sighash;
unsigned int i;
+ /* Construct record */
memset ( &hello, 0, sizeof ( hello ) );
hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
htonl ( sizeof ( hello ) -
sizeof ( hello.type_length ) ) );
hello.version = htons ( tls->version );
memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) );
+ hello.session_id_len = tls->session_id_len;
+ memcpy ( hello.session_id, tls->session_id,
+ sizeof ( hello.session_id ) );
hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES )
hello.cipher_suites[i++] = suite->code;
@@ -1018,7 +1088,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
hello.extensions.server_name.list[0].type = TLS_SERVER_NAME_HOST_NAME;
hello.extensions.server_name.list[0].len
= htons ( sizeof ( hello.extensions.server_name.list[0].name ));
- memcpy ( hello.extensions.server_name.list[0].name, tls->name,
+ memcpy ( hello.extensions.server_name.list[0].name, session->name,
sizeof ( hello.extensions.server_name.list[0].name ) );
hello.extensions.max_fragment_length_type
= htons ( TLS_MAX_FRAGMENT_LENGTH );
@@ -1042,6 +1112,11 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
= sizeof ( hello.extensions.renegotiation_info.data );
memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client,
sizeof ( hello.extensions.renegotiation_info.data ) );
+ hello.extensions.session_ticket_type = htons ( TLS_SESSION_TICKET );
+ hello.extensions.session_ticket_len
+ = htons ( sizeof ( hello.extensions.session_ticket ) );
+ memcpy ( hello.extensions.session_ticket.data, session->ticket,
+ sizeof ( hello.extensions.session_ticket.data ) );
return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
}
@@ -1513,8 +1588,34 @@ static int tls_new_server_hello ( struct tls_connection *tls,
if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 )
return rc;
- /* Generate secrets */
- tls_generate_master_secret ( tls );
+ /* Reuse or generate master secret */
+ if ( hello_a->session_id_len &&
+ ( hello_a->session_id_len == tls->session_id_len ) &&
+ ( memcmp ( session_id, tls->session_id,
+ tls->session_id_len ) == 0 ) ) {
+
+ /* Session ID match: reuse master secret */
+ DBGC ( tls, "TLS %p resuming session ID:\n", tls );
+ DBGC_HDA ( tls, 0, tls->session_id, tls->session_id_len );
+
+ } else {
+
+ /* Generate new master secret */
+ tls_generate_master_secret ( tls );
+
+ /* Record new session ID, if present */
+ if ( hello_a->session_id_len &&
+ ( hello_a->session_id_len <= sizeof ( tls->session_id ))){
+ tls->session_id_len = hello_a->session_id_len;
+ memcpy ( tls->session_id, session_id,
+ tls->session_id_len );
+ DBGC ( tls, "TLS %p new session ID:\n", tls );
+ DBGC_HDA ( tls, 0, tls->session_id,
+ tls->session_id_len );
+ }
+ }
+
+ /* Generate keys */
if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
return rc;
@@ -1546,6 +1647,57 @@ static int tls_new_server_hello ( struct tls_connection *tls,
}
/**
+ * Receive New Session Ticket handshake record
+ *
+ * @v tls TLS connection
+ * @v data Plaintext handshake record
+ * @v len Length of plaintext handshake record
+ * @ret rc Return status code
+ */
+static int tls_new_session_ticket ( struct tls_connection *tls,
+ const void *data, size_t len ) {
+ const struct {
+ uint32_t lifetime;
+ uint16_t len;
+ uint8_t ticket[0];
+ } __attribute__ (( packed )) *new_session_ticket = data;
+ size_t ticket_len;
+
+ /* Parse header */
+ if ( sizeof ( *new_session_ticket ) > len ) {
+ DBGC ( tls, "TLS %p received underlength New Session Ticket\n",
+ tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL_TICKET;
+ }
+ ticket_len = ntohs ( new_session_ticket->len );
+ if ( ticket_len > ( len - sizeof ( *new_session_ticket ) ) ) {
+ DBGC ( tls, "TLS %p received overlength New Session Ticket\n",
+ tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL_TICKET;
+ }
+
+ /* Free any unapplied new session ticket */
+ free ( tls->new_session_ticket );
+ tls->new_session_ticket = NULL;
+ tls->new_session_ticket_len = 0;
+
+ /* Record ticket */
+ tls->new_session_ticket = malloc ( ticket_len );
+ if ( ! tls->new_session_ticket )
+ return -ENOMEM;
+ memcpy ( tls->new_session_ticket, new_session_ticket->ticket,
+ ticket_len );
+ tls->new_session_ticket_len = ticket_len;
+ DBGC ( tls, "TLS %p new session ticket:\n", tls );
+ DBGC_HDA ( tls, 0, tls->new_session_ticket,
+ tls->new_session_ticket_len );
+
+ return 0;
+}
+
+/**
* Parse certificate chain
*
* @v tls TLS connection
@@ -1725,6 +1877,7 @@ static int tls_new_server_hello_done ( struct tls_connection *tls,
"%s\n", tls, strerror ( rc ) );
return rc;
}
+ pending_get ( &tls->validation );
return 0;
}
@@ -1739,6 +1892,7 @@ static int tls_new_server_hello_done ( struct tls_connection *tls,
*/
static int tls_new_finished ( struct tls_connection *tls,
const void *data, size_t len ) {
+ struct tls_session *session = tls->session;
struct digest_algorithm *digest = tls->handshake_digest;
const struct {
uint8_t verify_data[ sizeof ( tls->verify.server ) ];
@@ -1767,6 +1921,39 @@ static int tls_new_finished ( struct tls_connection *tls,
/* Mark server as finished */
pending_put ( &tls->server_negotiation );
+ /* If we are resuming a session (i.e. if the server Finished
+ * arrives before the client Finished is sent), then schedule
+ * transmission of Change Cipher and Finished.
+ */
+ if ( is_pending ( &tls->client_negotiation ) ) {
+ tls->tx_pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED );
+ tls_tx_resume ( tls );
+ }
+
+ /* Record session ID, ticket, and master secret, if applicable */
+ if ( tls->session_id_len || tls->new_session_ticket_len ) {
+ memcpy ( session->master_secret, tls->master_secret,
+ sizeof ( session->master_secret ) );
+ }
+ if ( tls->session_id_len ) {
+ session->id_len = tls->session_id_len;
+ memcpy ( session->id, tls->session_id, sizeof ( session->id ) );
+ }
+ if ( tls->new_session_ticket_len ) {
+ free ( session->ticket );
+ session->ticket = tls->new_session_ticket;
+ session->ticket_len = tls->new_session_ticket_len;
+ tls->new_session_ticket = NULL;
+ tls->new_session_ticket_len = 0;
+ }
+
+ /* Move to end of session's connection list and allow other
+ * connections to start making progress.
+ */
+ list_del ( &tls->list );
+ list_add_tail ( &tls->list, &session->conn );
+ tls_tx_resume_all ( session );
+
/* Send notification of a window change */
xfer_window_changed ( &tls->plainstream );
@@ -1822,6 +2009,10 @@ static int tls_new_handshake ( struct tls_connection *tls,
case TLS_SERVER_HELLO:
rc = tls_new_server_hello ( tls, payload, payload_len );
break;
+ case TLS_NEW_SESSION_TICKET:
+ rc = tls_new_session_ticket ( tls, payload,
+ payload_len );
+ break;
case TLS_CERTIFICATE:
rc = tls_new_certificate ( tls, payload, payload_len );
break;
@@ -2383,12 +2574,31 @@ static int tls_plainstream_deliver ( struct tls_connection *tls,
return rc;
}
+/**
+ * Report job progress
+ *
+ * @v tls TLS connection
+ * @v progress Progress report to fill in
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int tls_progress ( struct tls_connection *tls,
+ struct job_progress *progress ) {
+
+ /* Return cipherstream or validator progress as applicable */
+ if ( is_pending ( &tls->validation ) ) {
+ return job_progress ( &tls->validator, progress );
+ } else {
+ return job_progress ( &tls->cipherstream, progress );
+ }
+}
+
/** TLS plaintext stream interface operations */
static struct interface_operation tls_plainstream_ops[] = {
INTF_OP ( xfer_deliver, struct tls_connection *,
tls_plainstream_deliver ),
INTF_OP ( xfer_window, struct tls_connection *,
tls_plainstream_window ),
+ INTF_OP ( job_progress, struct tls_connection *, tls_progress ),
INTF_OP ( intf_close, struct tls_connection *, tls_close ),
};
@@ -2608,10 +2818,14 @@ static struct interface_descriptor tls_cipherstream_desc =
* @v rc Reason for completion
*/
static void tls_validator_done ( struct tls_connection *tls, int rc ) {
+ struct tls_session *session = tls->session;
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
struct x509_certificate *cert;
+ /* Mark validation as complete */
+ pending_put ( &tls->validation );
+
/* Close validator interface */
intf_restart ( &tls->validator, rc );
@@ -2628,9 +2842,9 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) {
assert ( cert != NULL );
/* Verify server name */
- if ( ( rc = x509_check_name ( cert, tls->name ) ) != 0 ) {
+ if ( ( rc = x509_check_name ( cert, session->name ) ) != 0 ) {
DBGC ( tls, "TLS %p server certificate does not match %s: %s\n",
- tls, tls->name, strerror ( rc ) );
+ tls, session->name, strerror ( rc ) );
goto err;
}
@@ -2682,6 +2896,8 @@ static struct interface_descriptor tls_validator_desc =
* @v tls TLS connection
*/
static void tls_tx_step ( struct tls_connection *tls ) {
+ struct tls_session *session = tls->session;
+ struct tls_connection *conn;
int rc;
/* Wait for cipherstream to become ready */
@@ -2690,6 +2906,32 @@ static void tls_tx_step ( struct tls_connection *tls ) {
/* Send first pending transmission */
if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) {
+ /* Serialise server negotiations within a session, to
+ * provide a consistent view of session IDs and
+ * session tickets.
+ */
+ list_for_each_entry ( conn, &session->conn, list ) {
+ if ( conn == tls )
+ break;
+ if ( is_pending ( &conn->server_negotiation ) )
+ return;
+ }
+ /* Record or generate session ID and associated master secret */
+ if ( session->id_len ) {
+ /* Attempt to resume an existing session */
+ memcpy ( tls->session_id, session->id,
+ sizeof ( tls->session_id ) );
+ tls->session_id_len = session->id_len;
+ memcpy ( tls->master_secret, session->master_secret,
+ sizeof ( tls->master_secret ) );
+ } else {
+ /* No existing session: use a random session ID */
+ assert ( sizeof ( tls->session_id ) ==
+ sizeof ( tls->client_random ) );
+ memcpy ( tls->session_id, &tls->client_random,
+ sizeof ( tls->session_id ) );
+ tls->session_id_len = sizeof ( tls->session_id );
+ }
/* Send Client Hello */
if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Client Hello: %s\n",
@@ -2768,6 +3010,60 @@ static struct process_descriptor tls_process_desc =
/******************************************************************************
*
+ * Session management
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Find or create session for TLS connection
+ *
+ * @v tls TLS connection
+ * @v name Server name
+ * @ret rc Return status code
+ */
+static int tls_session ( struct tls_connection *tls, const char *name ) {
+ struct tls_session *session;
+ char *name_copy;
+ int rc;
+
+ /* Find existing matching session, if any */
+ list_for_each_entry ( session, &tls_sessions, list ) {
+ if ( strcmp ( name, session->name ) == 0 ) {
+ ref_get ( &session->refcnt );
+ tls->session = session;
+ DBGC ( tls, "TLS %p joining session %s\n", tls, name );
+ return 0;
+ }
+ }
+
+ /* Create new session */
+ session = zalloc ( sizeof ( *session ) + strlen ( name )
+ + 1 /* NUL */ );
+ if ( ! session ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ ref_init ( &session->refcnt, free_tls_session );
+ name_copy = ( ( ( void * ) session ) + sizeof ( *session ) );
+ strcpy ( name_copy, name );
+ session->name = name_copy;
+ INIT_LIST_HEAD ( &session->conn );
+ list_add ( &session->list, &tls_sessions );
+
+ /* Record session */
+ tls->session = session;
+
+ DBGC ( tls, "TLS %p created session %s\n", tls, name );
+ return 0;
+
+ ref_put ( &session->refcnt );
+ err_alloc:
+ return rc;
+}
+
+/******************************************************************************
+ *
* Instantiator
*
******************************************************************************
@@ -2786,7 +3082,7 @@ int add_tls ( struct interface *xfer, const char *name,
}
memset ( tls, 0, sizeof ( *tls ) );
ref_init ( &tls->refcnt, free_tls );
- tls->name = name;
+ INIT_LIST_HEAD ( &tls->list );
intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt );
intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt );
intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
@@ -2809,6 +3105,9 @@ int add_tls ( struct interface *xfer, const char *name,
( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) {
goto err_random;
}
+ if ( ( rc = tls_session ( tls, name ) ) != 0 )
+ goto err_session;
+ list_add_tail ( &tls->list, &tls->session->conn );
/* Start negotiation */
tls_restart ( tls );
@@ -2819,6 +3118,7 @@ int add_tls ( struct interface *xfer, const char *name,
ref_put ( &tls->refcnt );
return 0;
+ err_session:
err_random:
ref_put ( &tls->refcnt );
err_alloc:
diff --git a/src/net/validator.c b/src/net/validator.c
index 40f778c7d..f6b03ff41 100644
--- a/src/net/validator.c
+++ b/src/net/validator.c
@@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/base64.h>
#include <ipxe/crc32.h>
#include <ipxe/ocsp.h>
+#include <ipxe/job.h>
#include <ipxe/validator.h>
#include <config/crypto.h>
@@ -49,6 +50,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+struct validator;
+
+/** A certificate validator action */
+struct validator_action {
+ /** Name */
+ const char *name;
+ /** Action to take upon completed transfer */
+ int ( * done ) ( struct validator *validator, const void *data,
+ size_t len );
+};
+
/** A certificate validator */
struct validator {
/** Reference count */
@@ -67,12 +79,31 @@ struct validator {
struct ocsp_check *ocsp;
/** Data buffer */
struct xfer_buffer buffer;
- /** Action to take upon completed transfer */
- int ( * done ) ( struct validator *validator, const void *data,
- size_t len );
+
+ /** Current action */
+ const struct validator_action *action;
+ /** Current certificate
+ *
+ * This will always be present within the certificate chain
+ * and so this pointer does not hold a reference to the
+ * certificate.
+ */
+ struct x509_certificate *cert;
};
/**
+ * Get validator name (for debug messages)
+ *
+ * @v validator Certificate validator
+ * @ret name Validator name
+ */
+static const char * validator_name ( struct validator *validator ) {
+
+ /* Use name of first certificate in chain */
+ return x509_name ( x509_first ( validator->chain ) );
+}
+
+/**
* Free certificate validator
*
* @v refcnt Reference count
@@ -81,7 +112,8 @@ static void validator_free ( struct refcnt *refcnt ) {
struct validator *validator =
container_of ( refcnt, struct validator, refcnt );
- DBGC2 ( validator, "VALIDATOR %p freed\n", validator );
+ DBGC2 ( validator, "VALIDATOR %p \"%s\" freed\n",
+ validator, validator_name ( validator ) );
x509_chain_put ( validator->chain );
ocsp_put ( validator->ocsp );
xferbuf_free ( &validator->buffer );
@@ -110,8 +142,29 @@ static void validator_finished ( struct validator *validator, int rc ) {
*
*/
+/**
+ * Report job progress
+ *
+ * @v validator Certificate validator
+ * @v progress Progress report to fill in
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int validator_progress ( struct validator *validator,
+ struct job_progress *progress ) {
+
+ /* Report current action, if applicable */
+ if ( validator->action ) {
+ snprintf ( progress->message, sizeof ( progress->message ),
+ "%s %s", validator->action->name,
+ x509_name ( validator->cert ) );
+ }
+
+ return 0;
+}
+
/** Certificate validator job control interface operations */
static struct interface_operation validator_job_operations[] = {
+ INTF_OP ( job_progress, struct validator *, validator_progress ),
INTF_OP ( intf_close, struct validator *, validator_finished ),
};
@@ -165,8 +218,9 @@ static int validator_append ( struct validator *validator,
/* Enter certificateSet */
if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not enter "
- "certificateSet: %s\n", validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not enter "
+ "certificateSet: %s\n", validator,
+ validator_name ( validator ), strerror ( rc ) );
goto err_certificateset;
}
@@ -176,15 +230,16 @@ static int validator_append ( struct validator *validator,
/* Add certificate to chain */
if ( ( rc = x509_append_raw ( certs, cursor.data,
cursor.len ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not append "
- "certificate: %s\n",
- validator, strerror ( rc) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not "
+ "append certificate: %s\n", validator,
+ validator_name ( validator ), strerror ( rc) );
DBGC_HDA ( validator, 0, cursor.data, cursor.len );
return rc;
}
cert = x509_last ( certs );
- DBGC ( validator, "VALIDATOR %p found certificate %s\n",
- validator, x509_name ( cert ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" found certificate ",
+ validator, validator_name ( validator ) );
+ DBGC ( validator, "%s\n", x509_name ( cert ) );
/* Move to next certificate */
asn1_skip_any ( &cursor );
@@ -193,15 +248,17 @@ static int validator_append ( struct validator *validator,
/* Append certificates to chain */
last = x509_last ( validator->chain );
if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not append "
- "certificates: %s\n", validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not append "
+ "certificates: %s\n", validator,
+ validator_name ( validator ), strerror ( rc ) );
goto err_auto_append;
}
/* Check that at least one certificate has been added */
if ( last == x509_last ( validator->chain ) ) {
- DBGC ( validator, "VALIDATOR %p failed to append any "
- "applicable certificates\n", validator );
+ DBGC ( validator, "VALIDATOR %p \"%s\" failed to append any "
+ "applicable certificates\n", validator,
+ validator_name ( validator ) );
rc = -EACCES;
goto err_no_progress;
}
@@ -219,15 +276,22 @@ static int validator_append ( struct validator *validator,
return rc;
}
+/** Cross-signing certificate download validator action */
+static const struct validator_action validator_crosscert = {
+ .name = "XCRT",
+ .done = validator_append,
+};
+
/**
* Start download of cross-signing certificate
*
* @v validator Certificate validator
- * @v issuer Required issuer
+ * @v cert X.509 certificate
* @ret rc Return status code
*/
static int validator_start_download ( struct validator *validator,
- const struct asn1_cursor *issuer ) {
+ struct x509_certificate *cert ) {
+ const struct asn1_cursor *issuer = &cert->issuer.raw;
const char *crosscert;
char *crosscert_copy;
char *uri_string;
@@ -261,17 +325,21 @@ static int validator_start_download ( struct validator *validator,
crosscert, crc );
base64_encode ( issuer->data, issuer->len, ( uri_string + len ),
( uri_string_len - len ) );
- DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate "
- "from %s\n", validator, uri_string );
+ DBGC ( validator, "VALIDATOR %p \"%s\" downloading ",
+ validator, validator_name ( validator ) );
+ DBGC ( validator, "\"%s\" cross-signature from %s\n",
+ x509_name ( cert ), uri_string );
/* Set completion handler */
- validator->done = validator_append;
+ validator->action = &validator_crosscert;
+ validator->cert = cert;
/* Open URI */
if ( ( rc = xfer_open_uri_string ( &validator->xfer,
uri_string ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not open %s: %s\n",
- validator, uri_string, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: "
+ "%s\n", validator, validator_name ( validator ),
+ uri_string, strerror ( rc ) );
goto err_open_uri_string;
}
@@ -307,16 +375,18 @@ static int validator_ocsp_validate ( struct validator *validator,
/* Record OCSP response */
if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not record OCSP "
- "response: %s\n", validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not record OCSP "
+ "response: %s\n", validator,
+ validator_name ( validator ),strerror ( rc ) );
return rc;
}
/* Validate OCSP response */
now = time ( NULL );
if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not validate OCSP "
- "response: %s\n", validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not validate "
+ "OCSP response: %s\n", validator,
+ validator_name ( validator ), strerror ( rc ) );
return rc;
}
@@ -327,6 +397,12 @@ static int validator_ocsp_validate ( struct validator *validator,
return 0;
}
+/** OCSP validator action */
+static const struct validator_action validator_ocsp = {
+ .name = "OCSP",
+ .done = validator_ocsp_validate,
+};
+
/**
* Start OCSP check
*
@@ -344,22 +420,27 @@ static int validator_start_ocsp ( struct validator *validator,
/* Create OCSP check */
assert ( validator->ocsp == NULL );
if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not create OCSP check: "
- "%s\n", validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not create OCSP "
+ "check: %s\n", validator, validator_name ( validator ),
+ strerror ( rc ) );
return rc;
}
/* Set completion handler */
- validator->done = validator_ocsp_validate;
+ validator->action = &validator_ocsp;
+ validator->cert = cert;
/* Open URI */
uri_string = validator->ocsp->uri_string;
- DBGC ( validator, "VALIDATOR %p performing OCSP check at %s\n",
- validator, uri_string );
+ DBGC ( validator, "VALIDATOR %p \"%s\" checking ",
+ validator, validator_name ( validator ) );
+ DBGC ( validator, "\"%s\" via %s\n",
+ x509_name ( cert ), uri_string );
if ( ( rc = xfer_open_uri_string ( &validator->xfer,
uri_string ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not open %s: %s\n",
- validator, uri_string, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: "
+ "%s\n", validator, validator_name ( validator ),
+ uri_string, strerror ( rc ) );
return rc;
}
@@ -385,16 +466,18 @@ static void validator_xfer_close ( struct validator *validator, int rc ) {
/* Check for errors */
if ( rc != 0 ) {
- DBGC ( validator, "VALIDATOR %p transfer failed: %s\n",
- validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" transfer failed: %s\n",
+ validator, validator_name ( validator ),
+ strerror ( rc ) );
goto err_transfer;
}
- DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator );
+ DBGC2 ( validator, "VALIDATOR %p \"%s\" transfer complete\n",
+ validator, validator_name ( validator ) );
/* Process completed download */
- assert ( validator->done != NULL );
- if ( ( rc = validator->done ( validator, validator->buffer.data,
- validator->buffer.len ) ) != 0 )
+ assert ( validator->action != NULL );
+ if ( ( rc = validator->action->done ( validator, validator->buffer.data,
+ validator->buffer.len ) ) != 0 )
goto err_append;
/* Free downloaded data */
@@ -426,8 +509,9 @@ static int validator_xfer_deliver ( struct validator *validator,
/* Add data to buffer */
if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ),
meta ) ) != 0 ) {
- DBGC ( validator, "VALIDATOR %p could not receive data: %s\n",
- validator, strerror ( rc ) );
+ DBGC ( validator, "VALIDATOR %p \"%s\" could not receive "
+ "data: %s\n", validator, validator_name ( validator ),
+ strerror ( rc ) );
validator_finished ( validator, rc );
return rc;
}
@@ -471,6 +555,8 @@ static void validator_step ( struct validator *validator ) {
now = time ( NULL );
if ( ( rc = x509_validate_chain ( validator->chain, now, NULL,
NULL ) ) == 0 ) {
+ DBGC ( validator, "VALIDATOR %p \"%s\" validated\n",
+ validator, validator_name ( validator ) );
validator_finished ( validator, 0 );
return;
}
@@ -514,8 +600,7 @@ static void validator_step ( struct validator *validator ) {
/* Otherwise, try to download a suitable cross-signing
* certificate.
*/
- if ( ( rc = validator_start_download ( validator,
- &last->issuer.raw ) ) != 0 ) {
+ if ( ( rc = validator_start_download ( validator, last ) ) != 0 ) {
validator_finished ( validator, rc );
return;
}
@@ -567,8 +652,8 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) {
/* Attach parent interface, mortalise self, and return */
intf_plug_plug ( &validator->job, job );
ref_put ( &validator->refcnt );
- DBGC2 ( validator, "VALIDATOR %p validating X509 chain %p\n",
- validator, validator->chain );
+ DBGC2 ( validator, "VALIDATOR %p \"%s\" validating X509 chain %p\n",
+ validator, validator_name ( validator ), validator->chain );
return 0;
validator_finished ( validator, rc );
diff --git a/src/net/vlan.c b/src/net/vlan.c
index f515c2dc9..90f2934de 100644
--- a/src/net/vlan.c
+++ b/src/net/vlan.c
@@ -199,7 +199,8 @@ static void vlan_sync ( struct net_device *netdev ) {
* @v tag VLAN tag
* @ret netdev VLAN device, if any
*/
-struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) {
+static struct net_device * vlan_find ( struct net_device *trunk,
+ unsigned int tag ) {
struct net_device *netdev;
struct vlan_device *vlan;
@@ -506,3 +507,47 @@ struct net_driver vlan_driver __net_driver = {
.notify = vlan_notify,
.remove = vlan_remove,
};
+
+/**
+ * Add VLAN tag-stripped packet to receive queue
+ *
+ * @v netdev Network device
+ * @v tag VLAN tag, or zero
+ * @v iobuf I/O buffer
+ */
+void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag,
+ struct io_buffer *iobuf ) {
+ struct net_device *vlan;
+
+ /* Identify VLAN device, if applicable */
+ if ( tag ) {
+ if ( ( vlan = vlan_find ( netdev, tag ) ) == NULL ) {
+ netdev_rx_err ( netdev, iobuf, -ENODEV );
+ return;
+ }
+ netdev = vlan;
+ }
+
+ /* Hand off to network device */
+ netdev_rx ( netdev, iobuf );
+}
+
+/**
+ * Discard received VLAN tag-stripped packet
+ *
+ * @v netdev Network device
+ * @v tag VLAN tag, or zero
+ * @v iobuf I/O buffer, or NULL
+ * @v rc Packet status code
+ */
+void vlan_netdev_rx_err ( struct net_device *netdev, unsigned int tag,
+ struct io_buffer *iobuf, int rc ) {
+ struct net_device *vlan;
+
+ /* Identify VLAN device, if applicable */
+ if ( tag && ( ( vlan = vlan_find ( netdev, tag ) ) != NULL ) )
+ netdev = vlan;
+
+ /* Hand off to network device */
+ netdev_rx_err ( netdev, iobuf, rc );
+}
diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c
index 2c5b9df8a..bcd53c9af 100644
--- a/src/util/elf2efi.c
+++ b/src/util/elf2efi.c
@@ -458,6 +458,7 @@ static struct pe_section * process_section ( struct elf_file *elf,
struct pe_header *pe_header ) {
struct pe_section *new;
const char *name;
+ size_t name_len;
size_t section_memsz;
size_t section_filesz;
unsigned long code_start;
@@ -494,7 +495,10 @@ static struct pe_section * process_section ( struct elf_file *elf,
memset ( new, 0, sizeof ( *new ) + section_filesz );
/* Fill in section header details */
- strncpy ( ( char * ) new->hdr.Name, name, sizeof ( new->hdr.Name ) );
+ name_len = strlen ( name );
+ if ( name_len > sizeof ( new->hdr.Name ) )
+ name_len = sizeof ( new->hdr.Name );
+ memcpy ( new->hdr.Name, name, name_len );
new->hdr.Misc.VirtualSize = section_memsz;
new->hdr.VirtualAddress = shdr->sh_addr;
new->hdr.SizeOfRawData = section_filesz;