/** @file
Copyright (c) 2016 Socionext Inc. All rights reserved.
Copyright (c) 2017, Linaro, Ltd. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "NetsecDxe.h"
EFI_CPU_ARCH_PROTOCOL *mCpu;
STATIC
VOID
GetCurrentMacAddress (
IN UINT64 EepromBase,
OUT UINT8 *Mac)
{
Mac[0] = MmioRead8 (EepromBase + MAC_ADDRESS + 3);
Mac[1] = MmioRead8 (EepromBase + MAC_ADDRESS + 2);
Mac[2] = MmioRead8 (EepromBase + MAC_ADDRESS + 1);
Mac[3] = MmioRead8 (EepromBase + MAC_ADDRESS + 0);
Mac[4] = MmioRead8 (EepromBase + MAC_ADDRESS + 7);
Mac[5] = MmioRead8 (EepromBase + MAC_ADDRESS + 6);
}
/*
* Probe()
*/
STATIC
EFI_STATUS
Probe (
IN EFI_HANDLE Handle,
IN NETSEC_DRIVER *LanDriver
)
{
ogma_param_t Param;
ogma_err_t ogma_err;
UINT64 dmac_hm_cmd_base, dmac_mh_cmd_base, core_cmd_base;
UINT32 dmac_hm_cmd_size, dmac_mh_cmd_size, core_cmd_size;
UINT64 EepromBase;
SetMem (&Param, sizeof (Param), 0);
Param.use_gmac_flag = OGMA_TRUE;
Param.use_jumbo_pkt_flag = FixedPcdGet8 (PcdJumboPacket);
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_TX].valid_flag = OGMA_TRUE;
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_TX].little_endian_flag = OGMA_TRUE;
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_TX].tmr_mode_flag = OGMA_FALSE;
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_TX].entry_num = FixedPcdGet16 (PcdEncTxDescNum);
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_RX].valid_flag = OGMA_TRUE;
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_RX].little_endian_flag = OGMA_TRUE;
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_RX].tmr_mode_flag = OGMA_FALSE;
Param.desc_ring_param[OGMA_DESC_RING_ID_NRM_RX].entry_num = FixedPcdGet16 (PcdDecRxDescNum);
// phy-interface
Param.gmac_config.phy_interface = OGMA_PHY_INTERFACE_RGMII;
// Read and save the Permanent MAC Address
EepromBase = LanDriver->Dev->Resources[1].AddrRangeMin;
GetCurrentMacAddress (EepromBase, LanDriver->SnpMode.PermanentAddress.Addr);
LanDriver->SnpMode.CurrentAddress = LanDriver->SnpMode.PermanentAddress;
DEBUG ((DEBUG_NET | DEBUG_INFO,
"Netsec: HW MAC Address: %02x-%02x-%02x-%02x-%02x-%02x\n",
LanDriver->SnpMode.PermanentAddress.Addr[0],
LanDriver->SnpMode.PermanentAddress.Addr[1],
LanDriver->SnpMode.PermanentAddress.Addr[2],
LanDriver->SnpMode.PermanentAddress.Addr[3],
LanDriver->SnpMode.PermanentAddress.Addr[4],
LanDriver->SnpMode.PermanentAddress.Addr[5]));
//
// The NETSEC microcode for the core engine and the TX and RX engines
// is loaded from a separate memory mapped ROM.
//
// Get hm microcode's physical addresses
dmac_hm_cmd_base = MmioRead32 (EepromBase + HM_ME_ADDRESS_H);
dmac_hm_cmd_base <<= 32;
dmac_hm_cmd_base |= MmioRead32 (EepromBase + HM_ME_ADDRESS_L);
dmac_hm_cmd_size = MmioRead32 (EepromBase + HM_ME_SIZE);
// Get mh microcode's physical addresses
dmac_mh_cmd_base = MmioRead32 (EepromBase + MH_ME_ADDRESS_H);
dmac_mh_cmd_base <<= 32;
dmac_mh_cmd_base |= MmioRead32 (EepromBase + MH_ME_ADDRESS_L);
dmac_mh_cmd_size = MmioRead32 (EepromBase + MH_ME_SIZE);
// Get core microcode's physical addresses
core_cmd_base = MmioRead32 (EepromBase + PACKET_ME_ADDRESS);
core_cmd_size = MmioRead32 (EepromBase + PACKET_ME_SIZE);
ogma_err = ogma_init (
(VOID *)(UINTN)LanDriver->Dev->Resources[0].AddrRangeMin,
Handle, &Param,
(VOID *)dmac_hm_cmd_base, dmac_hm_cmd_size,
(VOID *)dmac_mh_cmd_base, dmac_mh_cmd_size,
(VOID *)core_cmd_base, core_cmd_size,
&LanDriver->Handle);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR, "NETSEC: ogma_init() failed with error code %d\n",
ogma_err));
return EFI_DEVICE_ERROR;
}
LanDriver->PhyAddress = LanDriver->Dev->Resources[2].AddrRangeMin;
ogma_enable_top_irq (LanDriver->Handle,
OGMA_TOP_IRQ_REG_NRM_RX | OGMA_TOP_IRQ_REG_NRM_TX);
return EFI_SUCCESS;
}
/*
* UEFI Stop() function
*/
STATIC
EFI_STATUS
EFIAPI
SnpStop (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
)
{
NETSEC_DRIVER *LanDriver;
EFI_TPL SavedTpl;
EFI_STATUS Status;
// Check Snp Instance
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
// Check state of the driver
switch (Snp->Mode->State) {
case EfiSimpleNetworkStarted:
case EfiSimpleNetworkInitialized:
break;
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not started\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
gBS->CloseEvent (LanDriver->ExitBootEvent);
// Change the state
Snp->Mode->State = EfiSimpleNetworkStopped;
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
/*
* UEFI Initialize() function
*/
STATIC
EFI_STATUS
EFIAPI
SnpInitialize (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
IN UINTN RxBufferSize OPTIONAL,
IN UINTN TxBufferSize OPTIONAL
)
{
NETSEC_DRIVER *LanDriver;
EFI_TPL SavedTpl;
EFI_STATUS Status;
ogma_phy_link_status_t phy_link_status;
ogma_err_t ogma_err;
ogma_gmac_mode_t ogma_gmac_mode;
// Check Snp Instance
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
// Check that driver was started but not initialised
switch (Snp->Mode->State) {
case EfiSimpleNetworkStarted:
break;
case EfiSimpleNetworkInitialized:
DEBUG ((DEBUG_WARN, "NETSEC: Driver already initialized\n"));
ReturnUnlock (EFI_SUCCESS);
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not started\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Find the LanDriver structure
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
// Clean all descriptors on the RX ring.
ogma_err = ogma_clean_rx_desc_ring (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_RX);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_clean_rx_desc_ring() failed with error code %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_err = ogma_clean_tx_desc_ring (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_clean_tx_desc_ring() failed with error code %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_clear_desc_ring_irq_status (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_TX,
OGMA_CH_IRQ_REG_EMPTY);
// Start the RX queue
ogma_err = ogma_start_desc_ring (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_RX);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_start_desc_ring(ring_id=%d) failed with error code %d\n",
OGMA_DESC_RING_ID_NRM_RX,
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_err = ogma_set_irq_coalesce_param (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_RX,
RXINT_PKTCNT, OGMA_FALSE,
RXINT_TMR_CNT_US);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_set_irq_coalesce_param() failed with error code %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_err = ogma_start_desc_ring (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_TX);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_start_desc_ring(ring_id=%d) failed with error code %d\n",
OGMA_DESC_RING_ID_NRM_TX,
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_disable_desc_ring_irq (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_TX,
OGMA_CH_IRQ_REG_EMPTY);
// Stop and restart the physical link
ogma_err = ogma_stop_gmac (LanDriver->Handle, OGMA_TRUE, OGMA_TRUE);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_stop_gmac() failed with error status %d\n",
ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_err = ogma_get_phy_link_status (LanDriver->Handle, LanDriver->PhyAddress,
&phy_link_status);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_get_phy_link_status() failed error code %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
SetMem (&ogma_gmac_mode, sizeof (ogma_gmac_mode_t), 0);
ogma_gmac_mode.link_speed = phy_link_status.link_speed;
ogma_gmac_mode.half_duplex_flag = (ogma_bool)phy_link_status.half_duplex_flag;
if ((!phy_link_status.half_duplex_flag) && FixedPcdGet8 (PcdFlowCtrl)) {
ogma_gmac_mode.flow_ctrl_enable_flag = FixedPcdGet8 (PcdFlowCtrl);
ogma_gmac_mode.flow_ctrl_start_threshold = FixedPcdGet16 (PcdFlowCtrlStartThreshold);
ogma_gmac_mode.flow_ctrl_stop_threshold = FixedPcdGet16 (PcdFlowCtrlStopThreshold);
ogma_gmac_mode.pause_time = FixedPcdGet16 (PcdPauseTime);
}
ogma_err = ogma_set_gmac_mode (LanDriver->Handle, &ogma_gmac_mode);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_set_gmac() failed with error status %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_err = ogma_start_gmac (LanDriver->Handle, OGMA_TRUE, OGMA_TRUE);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_start_gmac() failed with error status %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Declare the driver as initialized
Snp->Mode->State = EfiSimpleNetworkInitialized;
Status = EFI_SUCCESS;
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "NETSEC: Driver started\n"));
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
/*
* UEFI Shutdown () function
*/
STATIC
EFI_STATUS
EFIAPI
SnpShutdown (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
)
{
NETSEC_DRIVER *LanDriver;
EFI_TPL SavedTpl;
EFI_STATUS Status;
// Check Snp Instance
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
// First check that driver has already been initialized
switch (Snp->Mode->State) {
case EfiSimpleNetworkInitialized:
break;
case EfiSimpleNetworkStarted:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not yet initialized\n"));
ReturnUnlock (EFI_DEVICE_ERROR);
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver in stopped state\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Find the LanDriver structure
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
ogma_stop_gmac (LanDriver->Handle, OGMA_TRUE, OGMA_TRUE);
ogma_stop_desc_ring (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_RX);
ogma_stop_desc_ring (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_TX);
Snp->Mode->State = EfiSimpleNetworkStarted;
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
STATIC
VOID
EFIAPI
NotifyExitBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
EFI_STATUS Status;
Snp = Context;
if (Snp->Mode != EfiSimpleNetworkStopped) {
Status = SnpShutdown (Snp);
if (!EFI_ERROR (Status)) {
SnpStop (Snp);
}
}
gBS->CloseEvent (Event);
}
/*
* UEFI Start() function
*/
STATIC
EFI_STATUS
EFIAPI
SnpStart (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
)
{
EFI_SIMPLE_NETWORK_MODE *Mode;
EFI_TPL SavedTpl;
EFI_STATUS Status;
NETSEC_DRIVER *LanDriver;
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
// Check Snp instance
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
Mode = Snp->Mode;
// Check state of the driver
switch (Mode->State) {
case EfiSimpleNetworkStopped:
break;
case EfiSimpleNetworkStarted:
case EfiSimpleNetworkInitialized:
DEBUG ((DEBUG_WARN, "NETSEC: Driver already started\n"));
ReturnUnlock (EFI_ALREADY_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
NotifyExitBoot, Snp, &LanDriver->ExitBootEvent);
ASSERT_EFI_ERROR (Status);
// Change state
Mode->State = EfiSimpleNetworkStarted;
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
/*
* UEFI ReceiveFilters() function
*/
STATIC
EFI_STATUS
EFIAPI
SnpReceiveFilters (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
IN UINT32 Enable,
IN UINT32 Disable,
IN BOOLEAN Reset,
IN UINTN NumMfilter OPTIONAL,
IN EFI_MAC_ADDRESS *Mfilter OPTIONAL
)
{
EFI_TPL SavedTpl;
EFI_STATUS Status;
// Check Snp Instance
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
// First check that driver has already been initialized
switch (Snp->Mode->State) {
case EfiSimpleNetworkInitialized:
break;
case EfiSimpleNetworkStarted:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not yet initialized\n"));
ReturnUnlock (EFI_DEVICE_ERROR);
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not started\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
/*
* UEFI GetStatus () function
*/
STATIC
EFI_STATUS
EFIAPI
SnpGetStatus (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
OUT UINT32 *IrqStat OPTIONAL,
OUT VOID **TxBuff OPTIONAL
)
{
NETSEC_DRIVER *LanDriver;
EFI_TPL SavedTpl;
EFI_STATUS Status;
pfdep_pkt_handle_t pkt_handle;
LIST_ENTRY *Link;
ogma_phy_link_status_t phy_link_status;
ogma_err_t ogma_err;
// Check preliminaries
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
// Check that driver was started and initialised
switch (Snp->Mode->State) {
case EfiSimpleNetworkInitialized:
break;
case EfiSimpleNetworkStarted:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not yet initialized\n"));
ReturnUnlock (EFI_DEVICE_ERROR);
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not started\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Find the LanDriver structure
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
// Update the media status
ogma_err = ogma_get_phy_link_status (LanDriver->Handle, LanDriver->PhyAddress,
&phy_link_status);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_get_phy_link_status failed with error code: %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
Snp->Mode->MediaPresent = phy_link_status.up_flag;
ogma_err = ogma_clean_tx_desc_ring (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX);
if (TxBuff != NULL) {
*TxBuff = NULL;
//
// Find a buffer in the list that has been released
//
for (Link = GetFirstNode (&LanDriver->TxBufferList);
!IsNull (&LanDriver->TxBufferList, Link);
Link = GetNextNode (&LanDriver->TxBufferList, Link)) {
pkt_handle = BASE_CR (Link, PACKET_HANDLE, Link);
if (pkt_handle->Released) {
*TxBuff = pkt_handle->Buffer;
RemoveEntryList (Link);
FreePool (pkt_handle);
break;
}
}
}
if (IrqStat != 0) {
*IrqStat = 0;
}
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
/*
* UEFI Transmit() function
*/
STATIC
EFI_STATUS
EFIAPI
SnpTransmit (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
IN UINTN HdrSize,
IN UINTN BufSize,
IN VOID *BufAddr,
IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
IN EFI_MAC_ADDRESS *DstAddr OPTIONAL,
IN UINT16 *Protocol OPTIONAL
)
{
NETSEC_DRIVER *LanDriver;
EFI_TPL SavedTpl;
EFI_STATUS Status;
ogma_tx_pkt_ctrl_t tx_pkt_ctrl;
ogma_frag_info_t scat_info;
ogma_uint16 tx_avail_num;
ogma_err_t ogma_err;
UINT16 Proto;
pfdep_pkt_handle_t pkt_handle;
// Check preliminaries
if ((Snp == NULL) || (BufAddr == NULL)) {
DEBUG ((DEBUG_ERROR,
"NETSEC: SnpTransmit(): NULL Snp (%p) or BufAddr (%p)\n", Snp, BufAddr));
return EFI_DEVICE_ERROR;
}
pkt_handle = AllocateZeroPool (sizeof (*pkt_handle));
if (pkt_handle == NULL) {
return EFI_OUT_OF_RESOURCES;
}
pkt_handle->Buffer = BufAddr;
pkt_handle->RecycleForTx = TRUE;
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
// Check that driver was started and initialised
switch (Snp->Mode->State) {
case EfiSimpleNetworkInitialized:
break;
case EfiSimpleNetworkStarted:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not yet initialized\n"));
ReturnUnlock (EFI_DEVICE_ERROR);
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not started\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Find the LanDriver structure
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
ogma_err = ogma_clear_desc_ring_irq_status (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX,
OGMA_CH_IRQ_REG_EMPTY);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_clear_desc_ring_irq_status failed with error code: %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
ogma_err = ogma_clean_tx_desc_ring (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_clean_tx_desc_ring failed with error code: %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Ensure header is correct size if non-zero
if (HdrSize) {
if (HdrSize != Snp->Mode->MediaHeaderSize) {
DEBUG ((DEBUG_ERROR, "NETSEC: SnpTransmit(): Invalid HdrSize %d\n",
HdrSize));
ReturnUnlock (EFI_INVALID_PARAMETER);
}
if ((DstAddr == NULL) || (Protocol == NULL)) {
DEBUG ((DEBUG_ERROR,
"NETSEC: SnpTransmit(): NULL DstAddr %p or Protocol %p\n",
DstAddr, Protocol));
ReturnUnlock (EFI_INVALID_PARAMETER);
}
// Copy destination address
CopyMem (BufAddr, (VOID *)DstAddr, NET_ETHER_ADDR_LEN);
// Copy source address
CopyMem (BufAddr + NET_ETHER_ADDR_LEN, (VOID *)SrcAddr, NET_ETHER_ADDR_LEN);
// Copy protocol
Proto = HTONS (*Protocol);
CopyMem (BufAddr + (NET_ETHER_ADDR_LEN * 2), (VOID *)&Proto,
sizeof (UINT16));
}
Status = DmaMap (MapOperationBusMasterRead, BufAddr, &BufSize,
&scat_info.phys_addr, &pkt_handle->Mapping);
if (EFI_ERROR (Status)) {
goto ExitUnlock;
}
scat_info.addr = BufAddr;
scat_info.len = BufSize;
SetMem (&tx_pkt_ctrl, sizeof (ogma_tx_pkt_ctrl_t), 0);
tx_pkt_ctrl.pass_through_flag = OGMA_TRUE;
tx_pkt_ctrl.target_desc_ring_id = OGMA_DESC_RING_ID_GMAC;
// check empty slot
do {
tx_avail_num = ogma_get_tx_avail_num (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX);
} while (tx_avail_num < SCAT_NUM);
// send
ogma_err = ogma_set_tx_pkt_data (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX,
&tx_pkt_ctrl,
SCAT_NUM,
&scat_info,
pkt_handle);
if (ogma_err != OGMA_ERR_OK) {
DmaUnmap (pkt_handle->Mapping);
FreePool (pkt_handle);
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_set_tx_pkt_data failed with error code: %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
//
// Queue the descriptor so we can release the buffer once it has been
// consumed by the hardware.
//
InsertTailList (&LanDriver->TxBufferList, &pkt_handle->Link);
gBS->RestoreTPL (SavedTpl);
return EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
FreePool (pkt_handle);
gBS->RestoreTPL (SavedTpl);
return Status;
}
/*
* UEFI Receive() function
*/
EFI_STATUS
EFIAPI
SnpReceive (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
OUT UINTN *HdrSize OPTIONAL,
IN OUT UINTN *BuffSize,
OUT VOID *Data,
OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
OUT EFI_MAC_ADDRESS *DstAddr OPTIONAL,
OUT UINT16 *Protocol OPTIONAL
)
{
EFI_TPL SavedTpl;
EFI_STATUS Status;
NETSEC_DRIVER *LanDriver;
ogma_err_t ogma_err;
ogma_rx_pkt_info_t rx_pkt_info;
ogma_frag_info_t rx_data;
ogma_uint16 len;
pfdep_pkt_handle_t pkt_handle;
// Check preliminaries
if ((Snp == NULL) || (Data == NULL)) {
return EFI_INVALID_PARAMETER;
}
// Serialize access to data and registers
SavedTpl = gBS->RaiseTPL (TPL_CALLBACK);
// Check that driver was started and initialised
switch (Snp->Mode->State) {
case EfiSimpleNetworkInitialized:
break;
case EfiSimpleNetworkStarted:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not yet initialized\n"));
ReturnUnlock (EFI_DEVICE_ERROR);
case EfiSimpleNetworkStopped:
DEBUG ((DEBUG_WARN, "NETSEC: Driver not started\n"));
ReturnUnlock (EFI_NOT_STARTED);
default:
DEBUG ((DEBUG_ERROR, "NETSEC: Driver in an invalid state: %u\n",
(UINTN)Snp->Mode->State));
ReturnUnlock (EFI_DEVICE_ERROR);
}
// Find the LanDriver structure
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
if (ogma_get_rx_num (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_RX) > 0) {
ogma_err = ogma_get_rx_pkt_data (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_RX,
&rx_pkt_info, &rx_data, &len, &pkt_handle);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_get_rx_pkt_data failed with error code: %d\n",
(INT32)ogma_err));
ReturnUnlock (EFI_DEVICE_ERROR);
}
DmaUnmap (pkt_handle->Mapping);
pkt_handle->Mapping = NULL;
CopyMem (Data, (VOID *)rx_data.addr, len);
*BuffSize = len;
pfdep_free_pkt_buf (LanDriver->Handle, rx_data.len, rx_data.addr,
rx_data.phys_addr, PFDEP_TRUE, pkt_handle);
} else {
// not received any packets
ReturnUnlock (EFI_NOT_READY);
}
if (HdrSize != NULL) {
*HdrSize = LanDriver->SnpMode.MediaHeaderSize;
}
ogma_clear_desc_ring_irq_status (LanDriver->Handle,
OGMA_DESC_RING_ID_NRM_TX,
OGMA_CH_IRQ_REG_EMPTY);
ogma_clean_tx_desc_ring (LanDriver->Handle, OGMA_DESC_RING_ID_NRM_TX);
ogma_enable_top_irq (LanDriver->Handle,
OGMA_TOP_IRQ_REG_NRM_TX | OGMA_TOP_IRQ_REG_NRM_RX);
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
EFI_STATUS
NetsecInit (
IN EFI_HANDLE DriverBindingHandle,
IN EFI_HANDLE ControllerHandle
)
{
EFI_STATUS Status;
NETSEC_DRIVER *LanDriver;
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
EFI_SIMPLE_NETWORK_MODE *SnpMode;
// Allocate Resources
LanDriver = AllocateZeroPool (sizeof (NETSEC_DRIVER));
if (LanDriver == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gBS->OpenProtocol (ControllerHandle,
&gEdkiiNonDiscoverableDeviceProtocolGuid,
(VOID **)&LanDriver->Dev,
DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR (Status)) {
goto FreeDevice;
}
// Initialize pointers
Snp = &(LanDriver->Snp);
SnpMode = &(LanDriver->SnpMode);
Snp->Mode = SnpMode;
// Set the signature of the LAN Driver structure
LanDriver->Signature = NETSEC_SIGNATURE;
// Probe the device
Status = Probe (DriverBindingHandle, LanDriver);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"NETSEC:%a(): Probe failed with status %d\n", __FUNCTION__, Status));
goto CloseDeviceProtocol;
}
// Assign fields and func pointers
Snp->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
Snp->WaitForPacket = NULL;
Snp->Initialize = SnpInitialize;
Snp->Start = SnpStart;
Snp->Stop = SnpStop;
Snp->Reset = NULL;
Snp->Shutdown = SnpShutdown;
Snp->ReceiveFilters = SnpReceiveFilters;
Snp->StationAddress = NULL;
Snp->Statistics = NULL;
Snp->MCastIpToMac = NULL;
Snp->NvData = NULL;
Snp->GetStatus = SnpGetStatus;
Snp->Transmit = SnpTransmit;
Snp->Receive = SnpReceive;
// Fill in simple network mode structure
SnpMode->State = EfiSimpleNetworkStopped;
SnpMode->HwAddressSize = NET_ETHER_ADDR_LEN;
SnpMode->MediaHeaderSize = sizeof (ETHER_HEAD);
SnpMode->MaxPacketSize = EFI_PAGE_SIZE;
// Supported receive filters
SnpMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS |
EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
// Initially-enabled receive filters
SnpMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
// Netsec has 64bit hash table. We can filter an infinite MACs, but
// higher-level software must filter out any hash collisions.
SnpMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
SnpMode->MCastFilterCount = 0;
ZeroMem (&SnpMode->MCastFilter,
MAX_MCAST_FILTER_CNT * sizeof (EFI_MAC_ADDRESS));
// Set the interface type (1: Ethernet or 6: IEEE 802 Networks)
SnpMode->IfType = NET_IFTYPE_ETHERNET;
// Mac address is changeable
SnpMode->MacAddressChangeable = TRUE;
// We can only transmit one packet at a time
SnpMode->MultipleTxSupported = FALSE;
// MediaPresent checks for cable connection and partner link
SnpMode->MediaPresentSupported = TRUE;
SnpMode->MediaPresent = FALSE;
// Set broadcast address
SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);
InitializeListHead (&LanDriver->TxBufferList);
LanDriver->DevicePath.Netsec.Header.Type = MESSAGING_DEVICE_PATH;
LanDriver->DevicePath.Netsec.Header.SubType = MSG_MAC_ADDR_DP;
SetDevicePathNodeLength (&LanDriver->DevicePath.Netsec.Header,
sizeof (LanDriver->DevicePath.Netsec));
CopyMem (&LanDriver->DevicePath.Netsec.MacAddress,
&SnpMode->PermanentAddress, PXE_HWADDR_LEN_ETHER);
LanDriver->DevicePath.Netsec.IfType = SnpMode->IfType;
SetDevicePathEndNode (&LanDriver->DevicePath.End);
// Initialise the protocol
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiSimpleNetworkProtocolGuid, Snp,
&gEfiDevicePathProtocolGuid, &LanDriver->DevicePath,
NULL);
LanDriver->ControllerHandle = ControllerHandle;
// Say what the status of loading the protocol structure is
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces failed - %r\n",
__FUNCTION__, Status));
ogma_terminate (LanDriver->Handle);
goto CloseDeviceProtocol;
}
return EFI_SUCCESS;
CloseDeviceProtocol:
gBS->CloseProtocol (ControllerHandle,
&gEdkiiNonDiscoverableDeviceProtocolGuid, DriverBindingHandle,
ControllerHandle);
FreeDevice:
FreePool (LanDriver);
return Status;
}
EFI_STATUS
NetsecRelease (
IN EFI_HANDLE DriverBindingHandle,
IN EFI_HANDLE ControllerHandle
)
{
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
NETSEC_DRIVER *LanDriver;
EFI_STATUS Status;
Status = gBS->HandleProtocol (ControllerHandle,
&gEfiSimpleNetworkProtocolGuid,
(VOID **)&Snp);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
Status = gBS->UninstallMultipleProtocolInterfaces (ControllerHandle,
&gEfiSimpleNetworkProtocolGuid, Snp,
&gEfiDevicePathProtocolGuid, &LanDriver->DevicePath,
NULL);
if (EFI_ERROR (Status)) {
return Status;
}
if (Snp->Mode->State == EfiSimpleNetworkInitialized) {
SnpShutdown (Snp);
}
ogma_terminate (LanDriver->Handle);
gBS->CloseEvent (LanDriver->ExitBootEvent);
Status = gBS->CloseProtocol (ControllerHandle,
&gEdkiiNonDiscoverableDeviceProtocolGuid,
DriverBindingHandle,
ControllerHandle);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->FreePool (LanDriver);
return EFI_SUCCESS;
}