summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Brown2015-05-16 15:35:13 +0200
committerMichael Brown2015-05-16 15:54:37 +0200
commitbb1e1048f681c7b3cb0daf04a210ced3222089ee (patch)
treefffd1a0d0e471ba3b4d2050c5d8d21d68360dca0 /src
parent[intel] Allow for the use of advanced TX descriptors (diff)
downloadipxe-bb1e1048f681c7b3cb0daf04a210ced3222089ee.tar.gz
ipxe-bb1e1048f681c7b3cb0daf04a210ced3222089ee.tar.xz
ipxe-bb1e1048f681c7b3cb0daf04a210ced3222089ee.zip
[intel] Add support for mailbox used by virtual functions
Virtual functions use a mailbox to communicate with the physical function driver: this covers functionality such as obtaining the MAC address. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src')
-rw-r--r--src/drivers/net/intel.h26
-rw-r--r--src/drivers/net/intelvf.c303
-rw-r--r--src/drivers/net/intelvf.h84
-rw-r--r--src/include/ipxe/errfile.h1
4 files changed, 414 insertions, 0 deletions
diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h
index 2656cda5..ce9e3f46 100644
--- a/src/drivers/net/intel.h
+++ b/src/drivers/net/intel.h
@@ -243,6 +243,29 @@ intel_init_ring ( struct intel_ring *ring, unsigned int count, unsigned int reg,
ring->describe = describe;
}
+/** An Intel virtual function mailbox */
+struct intel_mailbox {
+ /** Mailbox control register */
+ unsigned int ctrl;
+ /** Mailbox memory base */
+ unsigned int mem;
+};
+
+/**
+ * Initialise mailbox
+ *
+ * @v mbox Mailbox
+ * @v ctrl Mailbox control register
+ * @v mem Mailbox memory register base
+ */
+static inline __attribute__ (( always_inline )) void
+intel_init_mbox ( struct intel_mailbox *mbox, unsigned int ctrl,
+ unsigned int mem ) {
+
+ mbox->ctrl = ctrl;
+ mbox->mem = mem;
+}
+
/** An Intel network card */
struct intel_nic {
/** Registers */
@@ -261,6 +284,9 @@ struct intel_nic {
/** EEPROM address shift */
unsigned int eerd_addr_shift;
+ /** Mailbox */
+ struct intel_mailbox mbox;
+
/** Transmit descriptor ring */
struct intel_ring tx;
/** Receive descriptor ring */
diff --git a/src/drivers/net/intelvf.c b/src/drivers/net/intelvf.c
new file mode 100644
index 00000000..c8d3a4d2
--- /dev/null
+++ b/src/drivers/net/intelvf.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2015 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 <ipxe/io.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include "intelvf.h"
+
+/** @file
+ *
+ * Intel 10/100/1000 virtual function network card driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Mailbox messages
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Write message to mailbox
+ *
+ * @v intel Intel device
+ * @v msg Message
+ */
+static void intelvf_mbox_write ( struct intel_nic *intel,
+ const union intelvf_msg *msg ) {
+ unsigned int i;
+
+ /* Write message */
+ DBGC2 ( intel, "INTEL %p sending message", intel );
+ for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){
+ DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] );
+ writel ( msg->dword[i], ( intel->regs + intel->mbox.mem +
+ ( i * sizeof ( msg->dword[0] ) ) ) );
+ }
+ DBGC2 ( intel, "\n" );
+}
+
+/**
+ * Read message from mailbox
+ *
+ * @v intel Intel device
+ * @v msg Message
+ */
+static void intelvf_mbox_read ( struct intel_nic *intel,
+ union intelvf_msg *msg ) {
+ unsigned int i;
+
+ /* Read message */
+ DBGC2 ( intel, "INTEL %p received message", intel );
+ for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){
+ msg->dword[i] = readl ( intel->regs + intel->mbox.mem +
+ ( i * sizeof ( msg->dword[0] ) ) );
+ DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] );
+ }
+ DBGC2 ( intel, "\n" );
+}
+
+/**
+ * Poll mailbox
+ *
+ * @v intel Intel device
+ * @ret rc Return status code
+ *
+ * Note that polling the mailbox may fail if the underlying PF is
+ * reset.
+ */
+int intelvf_mbox_poll ( struct intel_nic *intel ) {
+ struct intel_mailbox *mbox = &intel->mbox;
+ union intelvf_msg msg;
+ uint32_t ctrl;
+
+ /* Get mailbox status */
+ ctrl = readl ( intel->regs + mbox->ctrl );
+
+ /* Fail if a reset is in progress */
+ if ( ctrl & INTELVF_MBCTRL_RSTI )
+ return -EPIPE;
+
+ /* Acknowledge (and ignore) any received messages */
+ if ( ctrl & INTELVF_MBCTRL_PFSTS ) {
+ intelvf_mbox_read ( intel, &msg );
+ writel ( INTELVF_MBCTRL_ACK, intel->regs + mbox->ctrl );
+ }
+
+ return 0;
+}
+
+/**
+ * Wait for PF reset to complete
+ *
+ * @v intel Intel device
+ * @ret rc Return status code
+ */
+int intelvf_mbox_wait ( struct intel_nic *intel ) {
+ unsigned int i;
+ int rc;
+
+ /* Wait until a poll completes successfully */
+ for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) {
+
+ /* Check for successful poll */
+ if ( ( rc = intelvf_mbox_poll ( intel ) ) == 0 )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intel, "INTEL %p timed out waiting for reset\n", intel );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Send/receive mailbox message
+ *
+ * @v intel Intel device
+ * @v msg Message buffer
+ * @ret rc Return status code
+ */
+static int intelvf_mbox_msg ( struct intel_nic *intel,
+ union intelvf_msg *msg ) {
+ struct intel_mailbox *mbox = &intel->mbox;
+ uint32_t ctrl;
+ uint32_t seen = 0;
+ unsigned int i;
+
+ /* Sanity check */
+ assert ( ! ( msg->hdr & INTELVF_MSG_RESPONSE ) );
+
+ /* Handle mailbox */
+ for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) {
+
+ /* Attempt to claim mailbox, if we have not yet sent
+ * our message.
+ */
+ if ( ! ( seen & INTELVF_MBCTRL_VFU ) )
+ writel ( INTELVF_MBCTRL_VFU, intel->regs + mbox->ctrl );
+
+ /* Get mailbox status and record observed flags */
+ ctrl = readl ( intel->regs + mbox->ctrl );
+ seen |= ctrl;
+
+ /* If a reset is in progress, clear VFU and abort */
+ if ( ctrl & INTELVF_MBCTRL_RSTI ) {
+ writel ( 0, intel->regs + mbox->ctrl );
+ return -EPIPE;
+ }
+
+ /* Write message to mailbox, if applicable. This
+ * potentially overwrites a message sent by the PF (if
+ * the PF has simultaneously released PFU (thus
+ * allowing our VFU) and asserted PFSTS), but that
+ * doesn't really matter since there are no
+ * unsolicited PF->VF messages that require the actual
+ * message content to be observed.
+ */
+ if ( ctrl & INTELVF_MBCTRL_VFU )
+ intelvf_mbox_write ( intel, msg );
+
+ /* Read message from mailbox, if applicable. */
+ if ( ( seen & INTELVF_MBCTRL_VFU ) &&
+ ( seen & INTELVF_MBCTRL_PFACK ) &&
+ ( ctrl & INTELVF_MBCTRL_PFSTS ) )
+ intelvf_mbox_read ( intel, msg );
+
+ /* Acknowledge received message (if applicable),
+ * release VFU lock, and send message (if applicable).
+ */
+ ctrl = ( ( ( ctrl & INTELVF_MBCTRL_PFSTS ) ?
+ INTELVF_MBCTRL_ACK : 0 ) |
+ ( ( ctrl & INTELVF_MBCTRL_VFU ) ?
+ INTELVF_MBCTRL_REQ : 0 ) );
+ writel ( ctrl, intel->regs + mbox->ctrl );
+
+ /* Exit successfully if we have received a response */
+ if ( msg->hdr & INTELVF_MSG_RESPONSE ) {
+
+ /* Sanity check */
+ assert ( seen & INTELVF_MBCTRL_VFU );
+ assert ( seen & INTELVF_MBCTRL_PFACK );
+ assert ( seen & INTELVF_MBCTRL_PFSTS );
+
+ return 0;
+ }
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intel, "INTEL %p timed out waiting for mailbox (seen %08x)\n",
+ intel, seen );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Send reset message and get initial MAC address
+ *
+ * @v intel Intel device
+ * @v hw_addr Hardware address to fill in, or NULL
+ * @ret rc Return status code
+ */
+int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr ) {
+ union intelvf_msg msg;
+ int rc;
+
+ /* Send reset message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.hdr = INTELVF_MSG_TYPE_RESET;
+ if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p reset failed: %s\n",
+ intel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check response */
+ if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_RESET ) {
+ DBGC ( intel, "INTEL %p reset unexpected response:\n", intel );
+ DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+ return -EPROTO;
+ }
+
+ /* Fill in MAC address, if applicable */
+ if ( hw_addr ) {
+ if ( msg.hdr & INTELVF_MSG_ACK ) {
+ memcpy ( hw_addr, msg.mac.mac, sizeof ( msg.mac.mac ) );
+ DBGC ( intel, "INTEL %p reset assigned MAC address "
+ "%s\n", intel, eth_ntoa ( hw_addr ) );
+ } else {
+ eth_random_addr ( hw_addr );
+ DBGC ( intel, "INTEL %p reset generated MAC address "
+ "%s\n", intel, eth_ntoa ( hw_addr ) );
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Send set MAC address message
+ *
+ * @v intel Intel device
+ * @v ll_addr Link-layer address
+ * @ret rc Return status code
+ */
+int intelvf_mbox_set_mac ( struct intel_nic *intel, const uint8_t *ll_addr ) {
+ union intelvf_msg msg;
+ int rc;
+
+ /* Send set MAC address message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.hdr = INTELVF_MSG_TYPE_SET_MAC;
+ memcpy ( msg.mac.mac, ll_addr, sizeof ( msg.mac.mac ) );
+ if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p set MAC address failed: %s\n",
+ intel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check response */
+ if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MAC ) {
+ DBGC ( intel, "INTEL %p set MAC address unexpected response:\n",
+ intel );
+ DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+ return -EPROTO;
+ }
+
+ /* Check that we were allowed to set the MAC address */
+ if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
+ DBGC ( intel, "INTEL %p set MAC address refused\n", intel );
+ return -EPERM;
+ }
+
+ return 0;
+}
diff --git a/src/drivers/net/intelvf.h b/src/drivers/net/intelvf.h
new file mode 100644
index 00000000..d03a7f17
--- /dev/null
+++ b/src/drivers/net/intelvf.h
@@ -0,0 +1,84 @@
+#ifndef _INTELVF_H
+#define _INTELVF_H
+
+/** @file
+ *
+ * Intel 10/100/1000 virtual function network card driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "intel.h"
+
+/** Intel VF BAR size */
+#define INTELVF_BAR_SIZE ( 16 * 1024 )
+
+/** Mailbox Control Register */
+#define INTELVF_MBCTRL 0x0c40UL
+#define INTELVF_MBCTRL_REQ 0x00000001UL /**< Request for PF ready */
+#define INTELVF_MBCTRL_ACK 0x00000002UL /**< PF message received */
+#define INTELVF_MBCTRL_VFU 0x00000004UL /**< Buffer taken by VF */
+#define INTELVF_MBCTRL_PFU 0x00000008UL /**< Buffer taken to PF */
+#define INTELVF_MBCTRL_PFSTS 0x00000010UL /**< PF wrote a message */
+#define INTELVF_MBCTRL_PFACK 0x00000020UL /**< PF acknowledged message */
+#define INTELVF_MBCTRL_RSTI 0x00000040UL /**< PF reset in progress */
+#define INTELVF_MBCTRL_RSTD 0x00000080UL /**< PF reset complete */
+
+/** Mailbox Memory Register Base */
+#define INTELVF_MBMEM 0x0800UL
+
+/** Reset mailbox message */
+#define INTELVF_MSG_TYPE_RESET 0x00000001UL
+
+/** Set MAC address mailbox message */
+#define INTELVF_MSG_TYPE_SET_MAC 0x00000002UL
+
+/** Control ("ping") mailbox message */
+#define INTELVF_MSG_TYPE_CONTROL 0x00000100UL
+
+/** Message type mask */
+#define INTELVF_MSG_TYPE_MASK 0x0000ffffUL
+
+/** Message NACK flag */
+#define INTELVF_MSG_NACK 0x40000000UL
+
+/** Message ACK flag */
+#define INTELVF_MSG_ACK 0x80000000UL
+
+/** Message is a response */
+#define INTELVF_MSG_RESPONSE ( INTELVF_MSG_ACK | INTELVF_MSG_NACK )
+
+/** MAC address mailbox message */
+struct intelvf_msg_mac {
+ /** Message header */
+ uint32_t hdr;
+ /** MAC address */
+ uint8_t mac[ETH_ALEN];
+ /** Alignment padding */
+ uint8_t reserved[ (-ETH_ALEN) & 0x3 ];
+} __attribute__ (( packed ));
+
+/** Mailbox message */
+union intelvf_msg {
+ /** Message header */
+ uint32_t hdr;
+ /** MAC address message */
+ struct intelvf_msg_mac mac;
+ /** Raw dwords */
+ uint32_t dword[0];
+};
+
+/** Maximum time to wait for mailbox message
+ *
+ * This is a policy decision.
+ */
+#define INTELVF_MBOX_MAX_WAIT_MS 500
+
+extern int intelvf_mbox_poll ( struct intel_nic *intel );
+extern int intelvf_mbox_wait ( struct intel_nic *intel );
+extern int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr );
+extern int intelvf_mbox_set_mac ( struct intel_nic *intel,
+ const uint8_t *ll_addr );
+
+#endif /* _INTELVF_H */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index eca9bbd4..d1af68e8 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -177,6 +177,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_srp ( ERRFILE_DRIVER | 0x00750000 )
#define ERRFILE_qib7322 ( ERRFILE_DRIVER | 0x00760000 )
#define ERRFILE_smsc75xx ( ERRFILE_DRIVER | 0x00770000 )
+#define ERRFILE_intelvf ( ERRFILE_DRIVER | 0x00780000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )