[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>
pull/36/head
Michael Brown 2015-05-16 14:35:13 +01:00
parent 9e2121be0d
commit bb1e1048f6
4 changed files with 414 additions and 0 deletions

View 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 */

View File

@ -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;
}

View File

@ -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 */

View 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 )