| /* | |
| * 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 ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #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; | |
| } |