/** @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; }