]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[intel] Add support for mailbox used by virtual functions
authorMichael Brown <mcb30@ipxe.org>
Sat, 16 May 2015 13:35:13 +0000 (14:35 +0100)
committerMichael Brown <mcb30@ipxe.org>
Sat, 16 May 2015 13:54:37 +0000 (14:54 +0100)
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>
src/drivers/net/intel.h
src/drivers/net/intelvf.c [new file with mode: 0644]
src/drivers/net/intelvf.h [new file with mode: 0644]
src/include/ipxe/errfile.h

index 2656cda52775c25e3be6930ed622a32565e6d953..ce9e3f467d414d0131eab872df546df42043f739 100644 (file)
@@ -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 (file)
index 0000000..c8d3a4d
--- /dev/null
@@ -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 (file)
index 0000000..d03a7f1
--- /dev/null
@@ -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 */
index eca9bbd4d112ec39befe51dfed7f18e07f9a82d1..d1af68e8c1d688ca910362abda87dd86da317533 100644 (file)
@@ -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 )