mirror of https://github.com/ipxe/ipxe.git
[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
parent
9e2121be0d
commit
bb1e1048f6
|
@ -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 */
|
||||
|
|
|
@ -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,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 */
|
|
@ -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 )
|
||||
|
|
Loading…
Reference in New Issue