/** @file
Copyright (c) 2016 Socionext Inc. All rights reserved.
Copyright (c) 2017, Linaro, Ltd. All rights reserved.
Copyright (c) 2020, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#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;
Param.phy_addr = LanDriver->Dev->Resources[2].AddrRangeMin;
// 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 *)(UINTN)dmac_hm_cmd_base, dmac_hm_cmd_size,
(VOID *)(UINTN)dmac_mh_cmd_base, dmac_mh_cmd_size,
(VOID *)(UINTN)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;
}
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);
gBS->SetTimer (LanDriver->PhyStatusEvent, TimerCancel, 0);
gBS->CloseEvent (LanDriver->PhyStatusEvent);
LanDriver->PhyStatusEvent = NULL;
// Change the state
Snp->Mode->State = EfiSimpleNetworkStopped;
Status = EFI_SUCCESS;
// Restore TPL and return
ExitUnlock:
gBS->RestoreTPL (SavedTpl);
return Status;
}
EFI_STATUS
EFIAPI
NetsecUpdateLink (
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
)
{
NETSEC_DRIVER *LanDriver;
ogma_phy_link_status_t phy_link_status;
ogma_gmac_mode_t ogma_gmac_mode;
ogma_err_t ogma_err;
BOOLEAN ValidFlag;
ogma_gmac_mode_t GmacMode;
BOOLEAN RxRunningFlag;
BOOLEAN TxRunningFlag;
EFI_STATUS ErrorStatus;
LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
// Update the media status
ogma_err = ogma_get_phy_link_status (LanDriver->Handle,
&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));
ErrorStatus = EFI_DEVICE_ERROR;
goto Fail;
}
// Update the GMAC status
ogma_err = ogma_get_gmac_status (LanDriver->Handle, &ValidFlag, &GmacMode,
&RxRunningFlag, &TxRunningFlag);
if (ogma_err != OGMA_ERR_OK) {
DEBUG ((DEBUG_ERROR,
"NETSEC: ogma_get_gmac_status failed with error code: %d\n",
(INT32)ogma_err));
ErrorStatus = EFI_DEVICE_ERROR;
goto Fail;
}
// Stop GMAC when GMAC is running and physical link is down
if (RxRunningFlag && TxRunningFlag && !phy_link_status.up_flag) {
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));
ErrorStatus = EFI_DEVICE_ERROR;
goto Fail;
}
}
// Start GMAC when GMAC is stopped and physical link is up
if (!RxRunningFlag && !TxRunningFlag && phy_link_status.up_flag) {
ZeroMem (&ogma_gmac_mode, sizeof (ogma_gmac_mode_t));
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));
ErrorStatus = EFI_DEVICE_ERROR;
goto Fail;
}
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));
ErrorStatus = EFI_DEVICE_ERROR;
goto Fail;
}
}
/* Updating link status for external guery */
Snp->Mode->MediaPresent = phy_link_status.up_flag;
return EFI_SUCCESS;
Fail:
Snp->Mode->MediaPresent = FALSE;
return ErrorStatus;
}
/*
* 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_err_t ogma_err;
// 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);
// 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);
}
/**
Polling function to check the physical link status with GMAC
@param[in] Timer Event
@param[in] Context Pointer to the Snp structure
**/
STATIC
VOID
EFIAPI
NetsecPollPhyStatus (
IN EFI_EVENT Timer,
IN VOID *Context
)
{
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
Snp = (EFI_SIMPLE_NETWORK_PROTOCOL *)Context;
if (Snp == NULL) {
DEBUG((DEBUG_ERROR, "NETSEC: PollPhyStatus() invalid Snp\n"));
return;
}
NetsecUpdateLink (Snp);
}
/*
* 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);
Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
NetsecPollPhyStatus, Snp, &LanDriver->PhyStatusEvent);
ASSERT_EFI_ERROR (Status);
Status = gBS->SetTimer (
LanDriver->PhyStatusEvent,
TimerPeriodic,
NETSEC_PHY_STATUS_POLL_INTERVAL
);
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_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);
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 (
(VOID*)((UINTN)BufAddr + NET_ETHER_ADDR_LEN),
(VOID*)SrcAddr,
NET_ETHER_ADDR_LEN
);
// Copy protocol
Proto = HTONS (*Protocol);
CopyMem (
(VOID*)((UINTN)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;
}
STATIC
EFI_STATUS
EFIAPI
NetsecAipGetInformation (
IN EFI_ADAPTER_INFORMATION_PROTOCOL *This,
IN EFI_GUID *InformationType,
OUT VOID **InformationBlock,
OUT UINTN *InformationBlockSize
)
{
EFI_ADAPTER_INFO_MEDIA_STATE *AdapterInfo;
NETSEC_DRIVER *LanDriver;
if (This == NULL || InformationBlock == NULL ||
InformationBlockSize == NULL) {
return EFI_INVALID_PARAMETER;
}
if (!CompareGuid (InformationType, &gEfiAdapterInfoMediaStateGuid)) {
return EFI_UNSUPPORTED;
}
AdapterInfo = AllocateZeroPool (sizeof (EFI_ADAPTER_INFO_MEDIA_STATE));
if (AdapterInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
*InformationBlock = AdapterInfo;
*InformationBlockSize = sizeof (EFI_ADAPTER_INFO_MEDIA_STATE);
LanDriver = INSTANCE_FROM_AIP_THIS (This);
if (LanDriver->Snp.Mode->MediaPresent) {
AdapterInfo->MediaState = EFI_SUCCESS;
} else {
AdapterInfo->MediaState = EFI_NOT_READY;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
NetsecAipSetInformation (
IN EFI_ADAPTER_INFORMATION_PROTOCOL *This,
IN EFI_GUID *InformationType,
IN VOID *InformationBlock,
IN UINTN InformationBlockSize
)
{
if (This == NULL || InformationBlock == NULL) {
return EFI_INVALID_PARAMETER;
}
if (CompareGuid (InformationType, &gEfiAdapterInfoMediaStateGuid)) {
return EFI_WRITE_PROTECTED;
}
return EFI_UNSUPPORTED;
}
STATIC
EFI_STATUS
EFIAPI
NetsecAipGetSupportedTypes (
IN EFI_ADAPTER_INFORMATION_PROTOCOL *This,
OUT EFI_GUID **InfoTypesBuffer,
OUT UINTN *InfoTypesBufferCount
)
{
EFI_GUID *Guid;
if (This == NULL || InfoTypesBuffer == NULL ||
InfoTypesBufferCount == NULL) {
return EFI_INVALID_PARAMETER;
}
Guid = AllocatePool (sizeof *Guid);
if (Guid == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyGuid (Guid, &gEfiAdapterInfoMediaStateGuid);
*InfoTypesBuffer = Guid;
*InfoTypesBufferCount = 1;
return EFI_SUCCESS;
}
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;
LanDriver->Aip.GetInformation = NetsecAipGetInformation;
LanDriver->Aip.SetInformation = NetsecAipSetInformation;
LanDriver->Aip.GetSupportedTypes = NetsecAipGetSupportedTypes;
// Set broadcast address
SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);
InitializeListHead (&LanDriver->TxBufferList);
// Initialise the protocol
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiSimpleNetworkProtocolGuid, Snp,
&gEfiAdapterInformationProtocolGuid, &LanDriver->Aip,
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,
&gEfiAdapterInformationProtocolGuid, &LanDriver->Aip,
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;
}