Skip to content

Commit

Permalink
[intel] Add support for mailbox used by virtual functions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mcb30 committed May 16, 2015
1 parent 9e2121b commit bb1e104
Show file tree
Hide file tree
Showing 4 changed files with 414 additions and 0 deletions.
26 changes: 26 additions & 0 deletions src/drivers/net/intel.h
Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down
303 changes: 303 additions & 0 deletions 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;
}

0 comments on commit bb1e104

Please sign in to comment.