mirror of https://github.com/ipxe/ipxe.git
343 lines
9.0 KiB
C
343 lines
9.0 KiB
C
/*
|
|
* 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 ) {
|
|
const struct intelvf_msg_raw *raw = &msg->raw;
|
|
unsigned int i;
|
|
|
|
/* Write message */
|
|
DBGC2 ( intel, "INTEL %p sending message", intel );
|
|
for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( raw->dword[0] ) ) ; i++){
|
|
DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), raw->dword[i] );
|
|
writel ( raw->dword[i], ( intel->regs + intel->mbox.mem +
|
|
( i * sizeof ( raw->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 ) {
|
|
struct intelvf_msg_raw *raw = &msg->raw;
|
|
unsigned int i;
|
|
|
|
/* Read message */
|
|
DBGC2 ( intel, "INTEL %p received message", intel );
|
|
for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( raw->dword[0] ) ) ; i++){
|
|
raw->dword[i] = readl ( intel->regs + intel->mbox.mem +
|
|
( i * sizeof ( raw->dword[0] ) ) );
|
|
DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), raw->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
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Send set MTU message
|
|
*
|
|
* @v intel Intel device
|
|
* @v mtu Maximum packet size
|
|
* @ret rc Return status code
|
|
*/
|
|
int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) {
|
|
union intelvf_msg msg;
|
|
int rc;
|
|
|
|
/* Send set MTU message */
|
|
memset ( &msg, 0, sizeof ( msg ) );
|
|
msg.hdr = INTELVF_MSG_TYPE_SET_MTU;
|
|
msg.mtu.mtu = mtu;
|
|
if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
|
|
DBGC ( intel, "INTEL %p set MTU failed: %s\n",
|
|
intel, strerror ( rc ) );
|
|
return rc;
|
|
}
|
|
|
|
/* Check response */
|
|
if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MTU ) {
|
|
DBGC ( intel, "INTEL %p set MTU unexpected response:\n",
|
|
intel );
|
|
DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
|
|
return -EPROTO;
|
|
}
|
|
|
|
/* Check that we were allowed to set the MTU */
|
|
if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
|
|
DBGC ( intel, "INTEL %p set MTU refused\n", intel );
|
|
return -EPERM;
|
|
}
|
|
|
|
return 0;
|
|
}
|