Date   

[edk2-platforms][PATCH v1 1/5] IntelSiliconPkg/Feature/PeiSmmAccessLibSmramc: Implement chipset support

Benjamin Doron
 

SMRAM must be opened to retrieve the lockbox for S3, and SMM
communication depends on this PPI. For security purposes, SMRAM
lock must be performed before EndOfPei (although FSP notify performs
lockdown too).

It seems to me that this library is generic and applicable to all Intel
platforms in the tree using the MCH SMRAMC register.

Cc: Nate DeSimone <nathaniel.l.desimone@...>
Cc: Ankit Sinha <ankit.sinha@...>
Cc: Ray Ni <ray.ni@...>
Cc: Rangasai V Chaganty <rangasai.v.chaganty@...>
Cc: Isaac Oram <isaac.w.oram@...>
Signed-off-by: Benjamin Doron <benjamin.doron00@...>
---
.../PeiSmmAccessLibSmramc/PeiSmmAccessLib.c | 430 ++++++++++++++++++
.../PeiSmmAccessLibSmramc/PeiSmmAccessLib.inf | 41 ++
2 files changed, 471 insertions(+)
create mode 100644 Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library=
/PeiSmmAccessLibSmramc/PeiSmmAccessLib.c
create mode 100644 Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library=
/PeiSmmAccessLibSmramc/PeiSmmAccessLib.inf

diff --git a/Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library/PeiSmm=
AccessLibSmramc/PeiSmmAccessLib.c b/Silicon/Intel/IntelSiliconPkg/Feature/S=
mmAccess/Library/PeiSmmAccessLibSmramc/PeiSmmAccessLib.c
new file mode 100644
index 000000000000..5b472bf86abf
--- /dev/null
+++ b/Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library/PeiSmmAccessL=
ibSmramc/PeiSmmAccessLib.c
@@ -0,0 +1,430 @@
+/** @file=0D
+ This is to publish the SMM Access Ppi instance.=0D
+=0D
+ Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+#include <Library/BaseMemoryLib.h>=0D
+#include <Library/DebugLib.h>=0D
+#include <Library/MemoryAllocationLib.h>=0D
+#include <Library/PciSegmentLib.h>=0D
+#include <Library/PeiServicesLib.h>=0D
+#include <Library/HobLib.h>=0D
+#include <Uefi/UefiBaseType.h>=0D
+#include <Guid/SmramMemoryReserve.h>=0D
+=0D
+#include <Ppi/MmAccess.h>=0D
+#include <IndustryStandard/Pci22.h>=0D
+=0D
+#define SMM_ACCESS_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('4', '5', 's', 'a'=
)=0D
+=0D
+///=0D
+/// Private data=0D
+///=0D
+typedef struct {=0D
+ UINTN Signature;=0D
+ EFI_HANDLE Handle;=0D
+ EFI_PEI_MM_ACCESS_PPI SmmAccess;=0D
+ //=0D
+ // Local Data for SMM Access interface goes here=0D
+ //=0D
+ UINTN NumberRegions;=0D
+ EFI_SMRAM_DESCRIPTOR *SmramDesc;=0D
+} SMM_ACCESS_PRIVATE_DATA;=0D
+=0D
+#define SMM_ACCESS_PRIVATE_DATA_FROM_THIS(a) \=0D
+ CR (a, \=0D
+ SMM_ACCESS_PRIVATE_DATA, \=0D
+ SmmAccess, \=0D
+ SMM_ACCESS_PRIVATE_DATA_SIGNATURE \=0D
+ )=0D
+=0D
+//=0D
+// Common registers:=0D
+//=0D
+// DEVICE 0 (Memory Controller Hub)=0D
+//=0D
+#define SA_MC_BUS 0x00=0D
+#define SA_MC_DEV 0x00=0D
+#define SA_MC_FUN 0x00=0D
+///=0D
+/// Description:=0D
+/// The SMRAMC register controls how accesses to Compatible SMRAM spaces =
are treated. The Open, Close and Lock bits function only when G_SMRAME bit=
is set to 1. Also, the Open bit must be reset before the Lock bit is set.=
=0D
+///=0D
+#define R_SA_SMRAMC (0x88)=0D
+#define B_SA_SMRAMC_D_LCK_MASK (0x10)=0D
+#define B_SA_SMRAMC_D_CLS_MASK (0x20)=0D
+#define B_SA_SMRAMC_D_OPEN_MASK (0x40)=0D
+=0D
+/**=0D
+ This routine accepts a request to "open" a region of SMRAM. The=0D
+ region could be legacy ABSEG, HSEG, or TSEG near top of physical memory.=
=0D
+ The use of "open" means that the memory is visible from all PEIM=0D
+ and SMM agents.=0D
+=0D
+ @param[in] PeiServices - General purpose services available to =
every PEIM.=0D
+ @param[in] This - Pointer to the SMM Access Interface.=0D
+ @param[in] DescriptorIndex - Region of SMRAM to Open.=0D
+=0D
+ @retval EFI_SUCCESS - The region was successfully opened.=0D
+ @retval EFI_DEVICE_ERROR - The region could not be opened because=
locked by=0D
+ chipset.=0D
+ @retval EFI_INVALID_PARAMETER - The descriptor index was out of bounds=
.=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+Open (=0D
+ IN EFI_PEI_SERVICES **PeiServices,=0D
+ IN EFI_PEI_MM_ACCESS_PPI *This,=0D
+ IN UINTN DescriptorIndex=0D
+ )=0D
+{=0D
+ SMM_ACCESS_PRIVATE_DATA *SmmAccess;=0D
+ UINT8 Index;=0D
+ UINT64 Address;=0D
+ UINT8 SmramControl;=0D
+=0D
+ SmmAccess =3D SMM_ACCESS_PRIVATE_DATA_FROM_THIS (This);=0D
+ if (DescriptorIndex >=3D SmmAccess->NumberRegions) {=0D
+ DEBUG ((DEBUG_WARN, "SMRAM region out of range\n"));=0D
+=0D
+ return EFI_INVALID_PARAMETER;=0D
+ } else if (SmmAccess->SmramDesc[DescriptorIndex].RegionState & EFI_SMRAM=
_LOCKED) {=0D
+ //=0D
+ // Cannot open a "locked" region=0D
+ //=0D
+ DEBUG ((DEBUG_WARN, "Cannot open a locked SMRAM region\n"));=0D
+=0D
+ return EFI_DEVICE_ERROR;=0D
+ }=0D
+=0D
+ ///=0D
+ /// BEGIN CHIPSET CODE=0D
+ ///=0D
+ ///=0D
+ /// SMRAM register is PCI 0:0:0:88, SMRAMC (8 bit)=0D
+ ///=0D
+ Address =3D PCI_SEGMENT_LIB_ADDRESS (0, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN,=
R_SA_SMRAMC);=0D
+ SmramControl =3D PciSegmentRead8 (Address);=0D
+ ///=0D
+ /// Is SMRAM locked?=0D
+ ///=0D
+ if ((SmramControl & B_SA_SMRAMC_D_LCK_MASK) !=3D 0) {=0D
+ ///=0D
+ /// Cannot Open a locked region=0D
+ ///=0D
+ for (Index =3D 0; Index < SmmAccess->NumberRegions; Index++) {=0D
+ SmmAccess->SmramDesc[Index].RegionState |=3D EFI_SMRAM_LOCKED;=0D
+ }=0D
+ DEBUG ((DEBUG_WARN, "Cannot open a locked SMRAM region\n"));=0D
+ return EFI_DEVICE_ERROR;=0D
+ }=0D
+ ///=0D
+ /// Open SMRAM region=0D
+ ///=0D
+ SmramControl |=3D B_SA_SMRAMC_D_OPEN_MASK;=0D
+ SmramControl &=3D ~(B_SA_SMRAMC_D_CLS_MASK);=0D
+=0D
+ PciSegmentWrite8 (Address, SmramControl);=0D
+ ///=0D
+ /// END CHIPSET CODE=0D
+ ///=0D
+=0D
+ SmmAccess->SmramDesc[DescriptorIndex].RegionState &=3D (UINT64) ~(EFI_SM=
RAM_CLOSED | EFI_ALLOCATED);=0D
+ SmmAccess->SmramDesc[DescriptorIndex].RegionState |=3D (UINT64) EFI_SMRA=
M_OPEN;=0D
+ SmmAccess->SmmAccess.OpenState =3D TRUE;=0D
+ return EFI_SUCCESS;=0D
+}=0D
+=0D
+/**=0D
+ This routine accepts a request to "close" a region of SMRAM. This is va=
lid for=0D
+ compatible SMRAM region.=0D
+=0D
+ @param[in] PeiServices - General purpose services available to =
every PEIM.=0D
+ @param[in] This - Pointer to the SMM Access Interface.=0D
+ @param[in] DescriptorIndex - Region of SMRAM to Close.=0D
+=0D
+ @retval EFI_SUCCESS - The region was successfully closed.=0D
+ @retval EFI_DEVICE_ERROR - The region could not be closed because=
locked by=0D
+ chipset.=0D
+ @retval EFI_INVALID_PARAMETER - The descriptor index was out of bounds=
.=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+Close (=0D
+ IN EFI_PEI_SERVICES **PeiServices,=0D
+ IN EFI_PEI_MM_ACCESS_PPI *This,=0D
+ IN UINTN DescriptorIndex=0D
+ )=0D
+{=0D
+ SMM_ACCESS_PRIVATE_DATA *SmmAccess;=0D
+ BOOLEAN OpenState;=0D
+ UINT8 Index;=0D
+ UINT64 Address;=0D
+ UINT8 SmramControl;=0D
+=0D
+ SmmAccess =3D SMM_ACCESS_PRIVATE_DATA_FROM_THIS (This);=0D
+ if (DescriptorIndex >=3D SmmAccess->NumberRegions) {=0D
+ DEBUG ((DEBUG_WARN, "SMRAM region out of range\n"));=0D
+=0D
+ return EFI_INVALID_PARAMETER;=0D
+ } else if (SmmAccess->SmramDesc[DescriptorIndex].RegionState & EFI_SMRAM=
_LOCKED) {=0D
+ //=0D
+ // Cannot close a "locked" region=0D
+ //=0D
+ DEBUG ((DEBUG_WARN, "Cannot close a locked SMRAM region\n"));=0D
+=0D
+ return EFI_DEVICE_ERROR;=0D
+ }=0D
+=0D
+ if (SmmAccess->SmramDesc[DescriptorIndex].RegionState & EFI_SMRAM_CLOSED=
) {=0D
+ return EFI_DEVICE_ERROR;=0D
+ }=0D
+=0D
+ ///=0D
+ /// BEGIN CHIPSET CODE=0D
+ ///=0D
+ ///=0D
+ /// SMRAM register is PCI 0:0:0:88, SMRAMC (8 bit)=0D
+ ///=0D
+ Address =3D PCI_SEGMENT_LIB_ADDRESS (0, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN,=
R_SA_SMRAMC);=0D
+ SmramControl =3D PciSegmentRead8 (Address);=0D
+ ///=0D
+ /// Is SMRAM locked?=0D
+ ///=0D
+ if ((SmramControl & B_SA_SMRAMC_D_LCK_MASK) !=3D 0) {=0D
+ ///=0D
+ /// Cannot Close a locked region=0D
+ ///=0D
+ for (Index =3D 0; Index < SmmAccess->NumberRegions; Index++) {=0D
+ SmmAccess->SmramDesc[Index].RegionState |=3D EFI_SMRAM_LOCKED;=0D
+ }=0D
+ DEBUG ((DEBUG_WARN, "Cannot close a locked SMRAM region\n"));=0D
+ return EFI_DEVICE_ERROR;=0D
+ }=0D
+ ///=0D
+ /// Close SMRAM region=0D
+ ///=0D
+ SmramControl &=3D ~(B_SA_SMRAMC_D_OPEN_MASK);=0D
+=0D
+ PciSegmentWrite8 (Address, SmramControl);=0D
+ ///=0D
+ /// END CHIPSET CODE=0D
+ ///=0D
+=0D
+ SmmAccess->SmramDesc[DescriptorIndex].RegionState &=3D (UINT64) ~EFI_SMR=
AM_OPEN;=0D
+ SmmAccess->SmramDesc[DescriptorIndex].RegionState |=3D (UINT64) (EFI_SMR=
AM_CLOSED | EFI_ALLOCATED);=0D
+=0D
+ //=0D
+ // Find out if any regions are still open=0D
+ //=0D
+ OpenState =3D FALSE;=0D
+ for (Index =3D 0; Index < SmmAccess->NumberRegions; Index++) {=0D
+ if ((SmmAccess->SmramDesc[Index].RegionState & EFI_SMRAM_OPEN) =3D=3D =
EFI_SMRAM_OPEN) {=0D
+ OpenState =3D TRUE;=0D
+ }=0D
+ }=0D
+=0D
+ SmmAccess->SmmAccess.OpenState =3D OpenState;=0D
+ return EFI_SUCCESS;=0D
+}=0D
+=0D
+/**=0D
+ This routine accepts a request to "lock" SMRAM. The=0D
+ region could be legacy AB or TSEG near top of physical memory.=0D
+ The use of "lock" means that the memory can no longer be opened=0D
+ to PEIM.=0D
+=0D
+ @param[in] PeiServices - General purpose services available to e=
very PEIM.=0D
+ @param[in] This - Pointer to the SMM Access Interface.=0D
+ @param[in] DescriptorIndex - Region of SMRAM to Lock.=0D
+=0D
+ @retval EFI_SUCCESS - The region was successfully locked.=0D
+ @retval EFI_DEVICE_ERROR - The region could not be locked because=
at least=0D
+ one range is still open.=0D
+ @retval EFI_INVALID_PARAMETER - The descriptor index was out of bounds=
.=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+Lock (=0D
+ IN EFI_PEI_SERVICES **PeiServices,=0D
+ IN EFI_PEI_MM_ACCESS_PPI *This,=0D
+ IN UINTN DescriptorIndex=0D
+ )=0D
+{=0D
+ SMM_ACCESS_PRIVATE_DATA *SmmAccess;=0D
+ UINT64 Address;=0D
+ UINT8 SmramControl;=0D
+=0D
+ SmmAccess =3D SMM_ACCESS_PRIVATE_DATA_FROM_THIS (This);=0D
+ if (DescriptorIndex >=3D SmmAccess->NumberRegions) {=0D
+ DEBUG ((DEBUG_WARN, "SMRAM region out of range\n"));=0D
+=0D
+ return EFI_INVALID_PARAMETER;=0D
+ } else if (SmmAccess->SmmAccess.OpenState) {=0D
+ DEBUG ((DEBUG_WARN, "Cannot lock SMRAM when SMRAM regions are still op=
en\n"));=0D
+=0D
+ return EFI_DEVICE_ERROR;=0D
+ }=0D
+=0D
+ SmmAccess->SmramDesc[DescriptorIndex].RegionState |=3D (UINT64) EFI_SMRA=
M_LOCKED;=0D
+ SmmAccess->SmmAccess.LockState =3D TRUE;=0D
+=0D
+ ///=0D
+ /// BEGIN CHIPSET CODE=0D
+ ///=0D
+ ///=0D
+ /// SMRAM register is PCI 0:0:0:88, SMRAMC (8 bit)=0D
+ ///=0D
+ Address =3D PCI_SEGMENT_LIB_ADDRESS (0, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN,=
R_SA_SMRAMC);=0D
+ SmramControl =3D PciSegmentRead8 (Address);=0D
+=0D
+ ///=0D
+ /// Lock the SMRAM=0D
+ ///=0D
+ SmramControl |=3D B_SA_SMRAMC_D_LCK_MASK;=0D
+=0D
+ PciSegmentWrite8 (Address, SmramControl);=0D
+ ///=0D
+ /// END CHIPSET CODE=0D
+ ///=0D
+=0D
+ return EFI_SUCCESS;=0D
+}=0D
+=0D
+/**=0D
+ This routine services a user request to discover the SMRAM=0D
+ capabilities of this platform. This will report the possible=0D
+ ranges that are possible for SMRAM access, based upon the=0D
+ memory controller capabilities.=0D
+=0D
+ @param[in] PeiServices - General purpose services available to ev=
ery PEIM.=0D
+ @param[in] This - Pointer to the SMRAM Access Interface.=
=0D
+ @param[in, out] SmramMapSize - Pointer to the variable containing size=
of the=0D
+ buffer to contain the description infor=
mation.=0D
+ @param[in, out] SmramMap - Buffer containing the data describing t=
he Smram=0D
+ region descriptors.=0D
+=0D
+ @retval EFI_BUFFER_TOO_SMALL - The user did not provide a sufficient b=
uffer.=0D
+ @retval EFI_SUCCESS - The user provided a sufficiently-sized =
buffer.=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+GetCapabilities (=0D
+ IN EFI_PEI_SERVICES **PeiServices,=0D
+ IN EFI_PEI_MM_ACCESS_PPI *This,=0D
+ IN OUT UINTN *SmramMapSize,=0D
+ IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ SMM_ACCESS_PRIVATE_DATA *SmmAccess;=0D
+ UINTN NecessaryBufferSize;=0D
+=0D
+ SmmAccess =3D SMM_ACCESS_PRIVATE_DATA_FROM_THIS (This);=0D
+ NecessaryBufferSize =3D SmmAccess->NumberRegions * sizeof (EFI_SMRAM_DES=
CRIPTOR);=0D
+ if (*SmramMapSize < NecessaryBufferSize) {=0D
+ DEBUG ((DEBUG_WARN, "SMRAM Map Buffer too small\n"));=0D
+=0D
+ Status =3D EFI_BUFFER_TOO_SMALL;=0D
+ } else {=0D
+ CopyMem (SmramMap, SmmAccess->SmramDesc, NecessaryBufferSize);=0D
+ Status =3D EFI_SUCCESS;=0D
+ }=0D
+=0D
+ *SmramMapSize =3D NecessaryBufferSize;=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ This function is to install an SMM Access PPI=0D
+ - <b>Introduction</b> \n=0D
+ An API to install an instance of EFI_PEI_MM_ACCESS_PPI. This PPI is co=
mmonly used to control SMM mode memory access for S3 resume.=0D
+=0D
+ @retval EFI_SUCCESS - Ppi successfully started and installed=
.=0D
+ @retval EFI_NOT_FOUND - Ppi can't be found.=0D
+ @retval EFI_OUT_OF_RESOURCES - Ppi does not have enough resources to =
initialize the driver.=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+PeiInstallSmmAccessPpi (=0D
+ VOID=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ UINTN Index;=0D
+ EFI_PEI_PPI_DESCRIPTOR *PpiList;=0D
+ EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *DescriptorBlock;=0D
+ SMM_ACCESS_PRIVATE_DATA *SmmAccessPrivate;=0D
+ VOID *HobList;=0D
+=0D
+ //=0D
+ // Initialize private data=0D
+ //=0D
+ SmmAccessPrivate =3D AllocateZeroPool (sizeof (*SmmAccessPrivate));=0D
+ ASSERT (SmmAccessPrivate !=3D NULL);=0D
+ if (SmmAccessPrivate =3D=3D NULL) {=0D
+ return EFI_OUT_OF_RESOURCES;=0D
+ }=0D
+ PpiList =3D AllocateZeroPool (sizeof (*PpiList));=0D
+ ASSERT (PpiList !=3D NULL);=0D
+ if (PpiList =3D=3D NULL) {=0D
+ return EFI_OUT_OF_RESOURCES;=0D
+ }=0D
+=0D
+ SmmAccessPrivate->Signature =3D SMM_ACCESS_PRIVATE_DATA_SIGNATURE;=0D
+ SmmAccessPrivate->Handle =3D NULL;=0D
+=0D
+ //=0D
+ // Get Hob list=0D
+ //=0D
+ HobList =3D GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);=0D
+ if (HobList =3D=3D NULL) {=0D
+ DEBUG ((DEBUG_WARN, "SmramMemoryReserve HOB not found\n"));=0D
+ return EFI_NOT_FOUND;=0D
+ }=0D
+=0D
+ DescriptorBlock =3D (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *) ((UINT8 *) HobLis=
t + sizeof (EFI_HOB_GUID_TYPE));=0D
+=0D
+ //=0D
+ // Alloc space for SmmAccessPrivate->SmramDesc=0D
+ //=0D
+ SmmAccessPrivate->SmramDesc =3D AllocateZeroPool ((DescriptorBlock->Numb=
erOfSmmReservedRegions) * sizeof (EFI_SMRAM_DESCRIPTOR));=0D
+ if (SmmAccessPrivate->SmramDesc =3D=3D NULL) {=0D
+ DEBUG ((DEBUG_WARN, "Alloc SmmAccessPrivate->SmramDesc fail.\n"));=0D
+ return EFI_OUT_OF_RESOURCES;=0D
+ }=0D
+=0D
+ DEBUG ((DEBUG_INFO, "Alloc SmmAccessPrivate->SmramDesc success.\n"));=0D
+=0D
+ //=0D
+ // use the hob to publish SMRAM capabilities=0D
+ //=0D
+ for (Index =3D 0; Index < DescriptorBlock->NumberOfSmmReservedRegions; I=
ndex++) {=0D
+ SmmAccessPrivate->SmramDesc[Index].PhysicalStart =3D DescriptorBlock-=
Descriptor[Index].PhysicalStart;=0D
+ SmmAccessPrivate->SmramDesc[Index].CpuStart =3D DescriptorBlock-=
Descriptor[Index].CpuStart;=0D
+ SmmAccessPrivate->SmramDesc[Index].PhysicalSize =3D DescriptorBlock-=
Descriptor[Index].PhysicalSize;=0D
+ SmmAccessPrivate->SmramDesc[Index].RegionState =3D DescriptorBlock-=
Descriptor[Index].RegionState;=0D
+ }=0D
+=0D
+ SmmAccessPrivate->NumberRegions =3D Index;=0D
+ SmmAccessPrivate->SmmAccess.Open =3D Open;=0D
+ SmmAccessPrivate->SmmAccess.Close =3D Close;=0D
+ SmmAccessPrivate->SmmAccess.Lock =3D Lock;=0D
+ SmmAccessPrivate->SmmAccess.GetCapabilities =3D GetCapabilities;=0D
+ SmmAccessPrivate->SmmAccess.LockState =3D FALSE;=0D
+ SmmAccessPrivate->SmmAccess.OpenState =3D FALSE;=0D
+=0D
+ //=0D
+ // Install PPI=0D
+ //=0D
+ PpiList->Flags =3D (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR=
_TERMINATE_LIST);=0D
+ PpiList->Guid =3D &gEfiPeiMmAccessPpiGuid;=0D
+ PpiList->Ppi =3D &SmmAccessPrivate->SmmAccess;=0D
+=0D
+ Status =3D PeiServicesInstallPpi (PpiList);=0D
+ ASSERT_EFI_ERROR (Status);=0D
+=0D
+ return EFI_SUCCESS;=0D
+}=0D
diff --git a/Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library/PeiSmm=
AccessLibSmramc/PeiSmmAccessLib.inf b/Silicon/Intel/IntelSiliconPkg/Feature=
/SmmAccess/Library/PeiSmmAccessLibSmramc/PeiSmmAccessLib.inf
new file mode 100644
index 000000000000..916346aacff3
--- /dev/null
+++ b/Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library/PeiSmmAccessL=
ibSmramc/PeiSmmAccessLib.inf
@@ -0,0 +1,41 @@
+## @file=0D
+# Library description file for the SmmAccess PPI=0D
+#=0D
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>=0D
+# SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+#=0D
+##=0D
+=0D
+[Defines]=0D
+INF_VERSION =3D 0x00010017=0D
+BASE_NAME =3D PeiSmmAccessLibSmramc=0D
+FILE_GUID =3D 3D28FD4B-F46F-4E24-88AA-9DA09C51BE87=0D
+VERSION_STRING =3D 1.0=0D
+MODULE_TYPE =3D PEIM=0D
+LIBRARY_CLASS =3D SmmAccessLib=0D
+=0D
+=0D
+[LibraryClasses]=0D
+BaseMemoryLib=0D
+MemoryAllocationLib=0D
+DebugLib=0D
+HobLib=0D
+PciSegmentLib=0D
+PeiServicesLib=0D
+=0D
+=0D
+[Packages]=0D
+MdePkg/MdePkg.dec=0D
+IntelSiliconPkg/IntelSiliconPkg.dec=0D
+=0D
+=0D
+[Sources]=0D
+PeiSmmAccessLib.c=0D
+=0D
+=0D
+[Ppis]=0D
+gEfiPeiMmAccessPpiGuid ## PRODUCES=0D
+=0D
+=0D
+[Guids]=0D
+gEfiSmmSmramMemoryGuid=0D
--=20
2.37.2


[edk2-platforms][PATCH v1 0/5] Implement S3 resume

Benjamin Doron
 

MinPlatform is an open-source EDK2 firmware project that can boot some
mainstream boards. However, it lacked working support for S3 resume, an
important feature for mobile platforms, which means that its
applicability as-is to mainstream use is limited. Therefore, I have now
implemented working S3 resume support on MinPlatform. This patch series
comprises a majority of one of my work products for GSoC 2022.

The BootScript-related modules are EDK2 open-source and fairly
straightforward to include. However, the partial dependency
PiSmmCommunicationPei (for signalling SMM) creates a dependency on both
the SmmAccess and SmmControl PPIs, so these are implemented here as
libraries, ported from the DXE drivers. As the register definitions are
generic, one library shim is implemented for compatibility, so the
library can be generic too. SmmAccess shall be required regardless of
SMM signalling, for the LockBox.

Like all boots, S3 resume will require working DRAM. To my
understanding, we do not need to parse the memory map HOBs again, so
some memory is allocated in DXE phase (to reserve it), the address
stashed in a variable and consumed on S3 resume flows. Some
optimisation can be performed here, regarding how much is necessary.
Stashing in a variable is imperfect, but the details must be available
without DRAM. As the FSP HOBs are published later, SmmAccess cannot be
used to retrieve from the LockBox.

Per my suspicions, notes from my mentors, Nate and Ankit, and the
coreboot code, the PAMs are opened for access to the AP wake vectors.
Presently, all are opened, more research can be performed here.

As noted, either FSP or PiSmmCpuDxeSmm can apply boot CPU structures
(GDT, IDT, MTRRs, etc). Per my research and findings by my mentors, the
closed-source module CpuInitDxe would be required, so this
implementation includes CpuS3DataDxe and open-source code will perform
this task instead.

Unfortunately, board-specific code has some tasks to perform too. I've
implemented these for Kabylake, the platform I can test. This includes
detecting the boot mode, policy (memory overwrite is contraindicated,
do not pass a VBT so FSP does not initialise graphics again) and
ensuring a special provision, if desired, for debugging
BootScriptExecutorDxe.

One major bug that blocked progress was simply specific to debugging.
DebugLibReportStatusCode should not be used for that module, because
RSC has uninstalled the serial port handler at end-of-BS. Furthermore,
it's obvious that the boot services are now unavailable. So,
DebugLibSerialPort should be used. As an aside, due to AutoGen ordering,
gBS cannot be used in SerialPortInitialize(). What really makes this
module a unique case is the fact that it's behaviour is very like
runtime drivers. For the integrity of the platform's security, the
module is copied to the LockBox at DxeSmmReadyToLock. This caused one
major bug that was blocking progress: libraries cannot attempt to
modify globals (the data section) with end-of-BS events; they will
never reach the true copy. So, rather than using flags to control code
flow, it must be coded this way instead. For instance, there is now a
special variant of I2cHdmiDebugSerialPortLib that does not use gBS in
SerialPortWrite().

Previous revisions of this patch-set tested on my Aspire VN7-572G
(Skylake). It's my belief and intention that this implementation be
ready for other platforms too (some Intel-specific assumptions made),
with a minimum of porting effort, though readying for some debugging is
recommended.

Some potential bugs include:
- Power failure is being set (PMC PWR_FLR), so BootMode == 0x0. This bit
is RW/1C, so mitigate it. Until finalised and I close the laptop
chassis, I don't know if this is a bug in Kabylake's PchPmcLib.
- Very early in testing I saw a memory init error, which means that
self-refresh failed. A BaseMemoryTest() predictably failed too,
inserted before the PEI core installs memory. Either this was fixed
in the code as I finished the implementation, or it's a bug. A major
difference in build options is SerialPortSpiFlash ->
I2cHdmiDebugSerialPortLib, but this seemed irrelevant. If it's simply
the finalised implementation, I think this isn't worth a diff against
the reflog.

Benjamin Doron (5):
IntelSiliconPkg/Feature/PeiSmmAccessLibSmramc: Implement chipset
support
Silicon/Intel: Port SmmControl protocol to PPI for S3
S3FeaturePkg: Implement working S3 resume
MinPlatformPkg: Implement working S3 resume
KabylakeOpenBoardPkg: Example of board S3

.../S3FeaturePkg/Include/PostMemory.fdf | 15 +
.../S3FeaturePkg/Include/PreMemory.fdf | 8 +-
.../S3FeaturePkg/Include/S3Feature.dsc | 42 +-
.../S3FeaturePkg/S3Dxe/S3Dxe.c | 156 +++++++
.../S3FeaturePkg/S3Dxe/S3Dxe.inf | 49 ++
.../S3FeaturePkg/S3Pei/S3Pei.c | 83 +++-
.../S3FeaturePkg/S3Pei/S3Pei.inf | 8 +-
.../PeiFspMiscUpdUpdateLib.c | 12 +-
.../PeiSaPolicyUpdate.c | 12 +-
.../PeiAspireVn7Dash572GInitPreMemLib.c | 61 ++-
.../BoardInitLib/PeiBoardInitPreMemLib.inf | 3 +
.../AspireVn7Dash572G/OpenBoardPkg.dsc | 18 +
.../AspireVn7Dash572G/OpenBoardPkgPcd.dsc | 7 +-
.../PeiSiliconPolicyUpdateLib.c | 11 +-
.../PeiSiliconPolicyUpdateLib.inf | 1 +
.../PeiFspMiscUpdUpdateLib.c | 11 +-
.../PeiSaPolicyUpdate.c | 12 +-
.../BoardInitLib/PeiBoardInitPreMemLib.inf | 1 +
.../BoardInitLib/PeiGalagoPro3InitPreMemLib.c | 27 +-
.../PeiMultiBoardInitPreMemLib.inf | 1 +
.../GalagoPro3/OpenBoardPkg.dsc | 16 +
.../GalagoPro3/OpenBoardPkgPcd.dsc | 2 +-
.../PeiFspMiscUpdUpdateLib.c | 12 +-
.../PeiSaPolicyUpdate.c | 12 +-
.../BoardInitLib/PeiBoardInitPreMemLib.inf | 1 +
.../PeiKabylakeRvp3InitPreMemLib.c | 27 +-
.../PeiMultiBoardInitPreMemLib.inf | 1 +
.../KabylakeRvp3/OpenBoardPkg.dsc | 13 +
.../KabylakeRvp3/OpenBoardPkgPcd.dsc | 2 +-
.../PeiSiliconPolicyUpdateLib.c | 11 +-
.../PeiSiliconPolicyUpdateLib.inf | 1 +
.../FspWrapperHobProcessLib.c | 70 ++-
.../PeiFspWrapperHobProcessLib.inf | 2 +
.../Include/AcpiS3MemoryNvData.h | 22 +
.../Include/Dsc/CorePeiInclude.dsc | 4 +
.../Include/Fdf/CorePostMemoryInclude.fdf | 4 +
.../BaseIntelCompatShimLibCfl.c | 24 +
.../BaseIntelCompatShimLibCfl.inf | 24 +
.../PeiSmmAccessLibSmramc/PeiSmmAccessLib.c | 430 ++++++++++++++++++
.../PeiSmmAccessLibSmramc/PeiSmmAccessLib.inf | 41 ++
.../PeiSmmControlLib/PeiSmmControlLib.c | 309 +++++++++++++
.../PeiSmmControlLib/PeiSmmControlLib.inf | 36 ++
.../Include/Library/IntelCompatShimLib.h | 20 +
.../Include/Library/SmmControlLib.h | 26 ++
.../Intel/IntelSiliconPkg/IntelSiliconPkg.dec | 4 +
.../BaseIntelCompatShimLibKbl.c | 29 ++
.../BaseIntelCompatShimLibKbl.inf | 24 +
47 files changed, 1663 insertions(+), 42 deletions(-)
create mode 100644 Features/Intel/PowerManagement/S3FeaturePkg/S3Dxe/S3Dxe.c
create mode 100644 Features/Intel/PowerManagement/S3FeaturePkg/S3Dxe/S3Dxe.inf
create mode 100644 Platform/Intel/MinPlatformPkg/Include/AcpiS3MemoryNvData.h
create mode 100644 Silicon/Intel/CoffeelakeSiliconPkg/Library/BaseIntelCompatShimLibCfl/BaseIntelCompatShimLibCfl.c
create mode 100644 Silicon/Intel/CoffeelakeSiliconPkg/Library/BaseIntelCompatShimLibCfl/BaseIntelCompatShimLibCfl.inf
create mode 100644 Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library/PeiSmmAccessLibSmramc/PeiSmmAccessLib.c
create mode 100644 Silicon/Intel/IntelSiliconPkg/Feature/SmmAccess/Library/PeiSmmAccessLibSmramc/PeiSmmAccessLib.inf
create mode 100644 Silicon/Intel/IntelSiliconPkg/Feature/SmmControl/Library/PeiSmmControlLib/PeiSmmControlLib.c
create mode 100644 Silicon/Intel/IntelSiliconPkg/Feature/SmmControl/Library/PeiSmmControlLib/PeiSmmControlLib.inf
create mode 100644 Silicon/Intel/IntelSiliconPkg/Include/Library/IntelCompatShimLib.h
create mode 100644 Silicon/Intel/IntelSiliconPkg/Include/Library/SmmControlLib.h
create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Library/BaseIntelCompatShimLibKbl/BaseIntelCompatShimLibKbl.c
create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Library/BaseIntelCompatShimLibKbl/BaseIntelCompatShimLibKbl.inf

--
2.37.2


Re: [edk2-platforms][PATCH v1 00/02] Add QemuOpenBoardPkg

Isaac Oram
 

Series Reviewed-by: Isaac Oram <isaac.w.oram@...>

-----Original Message-----
From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Théo Jehl
Sent: Friday, August 26, 2022 5:02 PM
To: devel@edk2.groups.io
Cc: Leif Lindholm <quic_llindhol@...>; Kinney, Michael D <michael.d.kinney@...>; Oram, Isaac W <isaac.w.oram@...>; Pedro Falcato <pedro.falcato@...>; Gerd Hoffmann <kraxel@...>; Stefan Hajnoczi <stefanha@...>
Subject: [edk2-devel] [edk2-platforms][PATCH v1 00/02] Add QemuOpenBoardPkg

QemuOpenBoardPkg adds a MinPlatform port to QEMU x86_64.
This package can boot UEFI Linux and Windows.
This port brings a starting place for understanding the MinPlatform, and board porting.
QemuOpenBoardPkg is my project for Google Summer of Code 2022.
I proposed this project to better understand UEFI inner workings, and to get started with platform level code.

CCing Gerd Hoffmann and Stefan Hajnoczi for their feedback, because of their work on OVMF and QEMU.


Cc: Leif Lindholm <quic_llindhol@...>
Cc: Michael D Kinney <michael.d.kinney@...>
Cc: Isaac Oram <isaac.w.oram@...>
Cc: Pedro Falcato <pedro.falcato@...>
Cc: Gerd Hoffmann <kraxel@...>
Cc: Stefan Hajnoczi <stefanha@...>

Signed-off-by: Théo Jehl <theojehl76@...>

Théo Jehl (2):
QemuOpenBoardPkg: Add QemuOpenBoardPkg
Add maintainers and reviewers for MinPlatform Arch QEMU board port.

--
2.32.1 (Apple Git-133)


[PATCH 2/2] MdeModulePkg: Add new Application/MpServicesTest application

Rebecca Cran <quic_rcran@...>
 

The MpServicesTest application exercises the EFI_MP_SERVICES_PROTOCOL.

usage:
MpServicesTest -A [-O]
MpServicesTest -T <Timeout>
MpServicesTest -S <Processor #>
MpServicesTest -P
MpServicesTest -U <Processor #>
MpServicesTest -W
MpServicesTest -E <Processor #>
MpServicesTest -D <Processor #>
MpServicesTest -h

Parameter:
-A: Run all APs.
-O: Run APs sequentially (use with -A).
-T: Specify timeout in milliseconds. Default is to wait forever.
-S: Specify the single AP to run.
-P: Print processor information.
-U: Set the specified AP to the Unhealthy status (use with -E/-D).
-W: Run WhoAmI and print index of BSP.
-E: Enable the specified AP.
-D: Disable the specified AP.
-h: Print this help page.

Signed-off-by: Rebecca Cran <rebecca@...>
---
MdeModulePkg/MdeModulePkg.dsc | 9 +
MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf | 41 ++
MdeModulePkg/Application/MpServicesTest/Options.h | 39 ++
MdeModulePkg/Application/MpServicesTest/MpServicesTest.c | 558 ++++++++++++++++++++
MdeModulePkg/Application/MpServicesTest/Options.c | 215 ++++++++
5 files changed, 862 insertions(+)

diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index 45a8ec84ad69..2444cd2927a2 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -166,6 +166,7 @@ [LibraryClasses.common.UEFI_APPLICATION]
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf
FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+ ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf

[LibraryClasses.common.MM_STANDALONE]
HobLib|MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf
@@ -512,9 +513,17 @@ [Components.IA32, Components.X64]
MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
MdeModulePkg/Universal/SmmCommunicationBufferDxe/SmmCommunicationBufferDxe.inf
MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
+ MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf

[Components.X64]
MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf

+[Components.AARCH64]
+ MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf {
+ <LibraryClasses>
+ CacheMaintenanceLib|ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf
+ ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
+ }
+
[BuildOptions]

diff --git a/MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf b/MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf
new file mode 100644
index 000000000000..25d5666a5fec
--- /dev/null
+++ b/MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf
@@ -0,0 +1,41 @@
+## @file
+# UEFI Application to exercise EFI_MP_SERVICES_PROTOCOL.
+#
+# Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = MpServicesTest
+ FILE_GUID = 43e9defa-7209-4b0d-b136-cc4ca02cb469
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 0.1
+ ENTRY_POINT = UefiMain
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 AARCH64
+#
+
+[Sources]
+ MpServicesTest.c
+ Options.c
+ Options.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ CacheMaintenanceLib
+ ShellLib
+ UefiApplicationEntryPoint
+ UefiLib
+
+[Protocols]
+ gEfiMpServiceProtocolGuid ## CONSUMES
+
diff --git a/MdeModulePkg/Application/MpServicesTest/Options.h b/MdeModulePkg/Application/MpServicesTest/Options.h
new file mode 100644
index 000000000000..cb28230ab095
--- /dev/null
+++ b/MdeModulePkg/Application/MpServicesTest/Options.h
@@ -0,0 +1,39 @@
+/** @file
+ Options handling code.
+
+ Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef MPSERVICESTEST_OPTIONS_H_
+#define MPSERVICESTEST_OPTIONS_H_
+
+#define INFINITE_TIMEOUT 0
+
+typedef struct {
+ UINTN Timeout;
+ UINTN ProcessorIndex;
+ BOOLEAN RunAllAPs;
+ BOOLEAN RunSingleAP;
+ BOOLEAN DisableProcessor;
+ BOOLEAN EnableProcessor;
+ BOOLEAN SetProcessorHealthy;
+ BOOLEAN SetProcessorUnhealthy;
+ BOOLEAN PrintProcessorInformation;
+ BOOLEAN PrintBspProcessorIndex;
+ BOOLEAN RunAPsSequentially;
+} MP_SERVICES_TEST_OPTIONS;
+
+/**
+ Parses any arguments provided on the command line.
+
+ @param Options The arguments structure.
+
+ @return EFI_SUCCESS on success, or an error code.
+**/
+EFI_STATUS
+ParseArguments (
+ MP_SERVICES_TEST_OPTIONS *Options
+ );
+
+#endif /* MPSERVICESTEST_OPTIONS_H_ */
diff --git a/MdeModulePkg/Application/MpServicesTest/MpServicesTest.c b/MdeModulePkg/Application/MpServicesTest/MpServicesTest.c
new file mode 100644
index 000000000000..82dbac700884
--- /dev/null
+++ b/MdeModulePkg/Application/MpServicesTest/MpServicesTest.c
@@ -0,0 +1,558 @@
+/** @file
+ UEFI Application to exercise EFI_MP_SERVICES_PROTOCOL.
+
+ Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Pi/PiMultiPhase.h>
+#include <Protocol/MpService.h>
+
+#include "Options.h"
+
+#define APFUNC_BUFFER_LEN 256
+
+typedef struct {
+ EFI_MP_SERVICES_PROTOCOL *Mp;
+ CHAR16 **Buffer;
+} APFUNC_ARG;
+
+/** The procedure to run with the MP Services interface.
+
+ @param Arg The procedure argument.
+
+**/
+STATIC
+VOID
+EFIAPI
+ApFunction (
+ IN OUT VOID *Arg
+ )
+{
+ APFUNC_ARG *Param;
+ UINTN ProcessorId;
+
+ if (Arg != NULL) {
+ Param = Arg;
+
+ Param->Mp->WhoAmI (Param->Mp, &ProcessorId);
+ UnicodeSPrint (Param->Buffer[ProcessorId], APFUNC_BUFFER_LEN, L"Hello from CPU %ld\n", ProcessorId);
+ }
+}
+
+/**
+ Fetches the number of processors and which processor is the BSP.
+
+ @param Mp MP Services Protocol.
+ @param NumProcessors Number of processors.
+ @param BspIndex The index of the BSP.
+**/
+STATIC
+EFI_STATUS
+GetProcessorInformation (
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ OUT UINTN *NumProcessors,
+ OUT UINTN *BspIndex
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumEnabledProcessors;
+
+ Status = Mp->GetNumberOfProcessors (Mp, NumProcessors, &NumEnabledProcessors);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mp->WhoAmI (Mp, BspIndex);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Displays information returned from MP Services Protocol.
+
+ @param Mp The MP Services Protocol
+ @param BspIndex On return, contains the index of the BSP.
+
+ @return The number of CPUs in the system.
+
+**/
+STATIC
+UINTN
+PrintProcessorInformation (
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ OUT UINTN *BspIndex
+ )
+{
+ EFI_STATUS Status;
+ EFI_PROCESSOR_INFORMATION CpuInfo;
+ UINTN Index;
+ UINTN NumCpu;
+ UINTN NumEnabledCpu;
+
+ Status = Mp->GetNumberOfProcessors (Mp, &NumCpu, &NumEnabledCpu);
+ if (EFI_ERROR (Status)) {
+ Print (L"GetNumberOfProcessors failed: %r\n", Status);
+ } else {
+ Print (L"Number of CPUs: %ld, Enabled: %d\n", NumCpu, NumEnabledCpu);
+ }
+
+ for (Index = 0; Index < NumCpu; Index++) {
+ Status = Mp->GetProcessorInfo (Mp, CPU_V2_EXTENDED_TOPOLOGY | Index, &CpuInfo);
+ if (EFI_ERROR (Status)) {
+ Print (L"GetProcessorInfo for Processor %d failed: %r\n", Index, Status);
+ } else {
+ Print (
+ L"Processor %d:\n"
+ L"\tID: %016lx\n"
+ L"\tStatus: %s | ",
+ Index,
+ CpuInfo.ProcessorId,
+ (CpuInfo.StatusFlag & PROCESSOR_AS_BSP_BIT) ? L"BSP" : L"AP"
+ );
+
+ if ((CpuInfo.StatusFlag & PROCESSOR_AS_BSP_BIT) && (BspIndex != NULL)) {
+ *BspIndex = Index;
+ }
+
+ Print (L"%s | ", (CpuInfo.StatusFlag & PROCESSOR_ENABLED_BIT) ? L"Enabled" : L"Disabled");
+ Print (L"%s\n", (CpuInfo.StatusFlag & PROCESSOR_HEALTH_STATUS_BIT) ? L"Healthy" : L"Faulted");
+
+ Print (
+ L"\tLocation: Package %d, Core %d, Thread %d\n"
+ L"\tExtended Information: Package %d, Module %d, Tile %d, Die %d, Core %d, Thread %d\n\n",
+ CpuInfo.Location.Package,
+ CpuInfo.Location.Core,
+ CpuInfo.Location.Thread,
+ CpuInfo.ExtendedInformation.Location2.Package,
+ CpuInfo.ExtendedInformation.Location2.Module,
+ CpuInfo.ExtendedInformation.Location2.Tile,
+ CpuInfo.ExtendedInformation.Location2.Die,
+ CpuInfo.ExtendedInformation.Location2.Core,
+ CpuInfo.ExtendedInformation.Location2.Thread
+ );
+ }
+ }
+
+ return NumCpu;
+}
+
+/** Allocates memory in ApArg for the single AP specified.
+
+ @param ApArg Pointer to the AP argument structure.
+ @param Mp The MP Services Protocol.
+ @param ProcessorIndex The index of the AP.
+
+ @retval EFI_SUCCESS Memory was successfully allocated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+STATIC
+EFI_STATUS
+AllocateApFuncBufferSingleAP (
+ IN APFUNC_ARG *ApArg,
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ IN UINTN ProcessorIndex
+ )
+{
+ ApArg->Mp = Mp;
+
+ ApArg->Buffer = AllocateZeroPool ((ProcessorIndex + 1) * sizeof (VOID *));
+ if (ApArg->Buffer == NULL) {
+ Print (L"Failed to allocate buffer for AP buffer\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ApArg->Buffer[ProcessorIndex] = AllocateZeroPool (APFUNC_BUFFER_LEN);
+ if (ApArg->Buffer[ProcessorIndex] == NULL) {
+ Print (L"Failed to allocate buffer for AP buffer\n");
+ FreePool (ApArg->Buffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Allocates memory in ApArg for all APs.
+
+ @param ApArg Pointer to the AP argument structure.
+ @param Mp The MP Services Protocol.
+ @param NumCpus The number of CPUs.
+
+ @retval EFI_SUCCESS Memory was successfully allocated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+STATIC
+EFI_STATUS
+AllocateApFuncBufferAllAPs (
+ IN APFUNC_ARG *ApArg,
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ IN UINTN NumCpus
+ )
+{
+ INT32 Index;
+
+ ApArg->Mp = Mp;
+
+ ApArg->Buffer = AllocateZeroPool (NumCpus * sizeof (VOID *));
+ if (ApArg->Buffer == NULL) {
+ Print (L"Failed to allocate buffer for AP message\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < NumCpus; Index++) {
+ ApArg->Buffer[Index] = AllocateZeroPool (APFUNC_BUFFER_LEN);
+ if (ApArg->Buffer[Index] == NULL) {
+ Print (L"Failed to allocate buffer for AP message\n");
+ for (--Index; Index >= 0; Index++) {
+ FreePool (ApArg->Buffer[Index]);
+ }
+
+ FreePool (ApArg->Buffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Frees memory in ApArg for all APs.
+
+ @param ApArg Pointer to the AP argument structure.
+ @param NumCpus The number of CPUs.
+
+**/
+STATIC
+VOID
+FreeApFuncBuffer (
+ APFUNC_ARG *ApArg,
+ UINTN NumCpus
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < NumCpus; Index++) {
+ if (ApArg->Buffer[Index] != NULL) {
+ FreePool (ApArg->Buffer[Index]);
+ }
+ }
+
+ FreePool (ApArg->Buffer);
+}
+
+/** Runs a specified AP.
+
+ @param Mp The MP Services Protocol.
+ @param ProcessorIndex The processor index.
+ @param Timeout Timeout in milliseconds.
+
+ @return EFI_SUCCESS on success, or an error code.
+
+**/
+STATIC
+EFI_STATUS
+StartupThisAP (
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ IN UINTN ProcessorIndex,
+ IN UINTN Timeout
+ )
+{
+ EFI_STATUS Status;
+ APFUNC_ARG ApArg;
+
+ Status = AllocateApFuncBufferSingleAP (&ApArg, Mp, ProcessorIndex);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = AllocateApFuncBufferSingleAP (&ApArg, Mp, ProcessorIndex);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Print (
+ L"StartupThisAP on Processor %d with %d%s timeout...",
+ ProcessorIndex,
+ Timeout,
+ (Timeout == INFINITE_TIMEOUT) ? " (infinite)" : "ms"
+ );
+ Status = Mp->StartupThisAP (
+ Mp,
+ ApFunction,
+ ProcessorIndex,
+ NULL,
+ Timeout * 1000,
+ &ApArg,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Print (L"failed: %r\n", Status);
+ return Status;
+ } else {
+ Print (L"done.\n");
+ Print (ApArg.Buffer[ProcessorIndex]);
+ }
+
+ FreeApFuncBuffer (&ApArg, ProcessorIndex + 1);
+
+ return EFI_SUCCESS;
+}
+
+/** Runs all APs.
+
+ @param Mp The MP Services Protocol.
+ @param NumCpus The number of CPUs in the system.
+ @param Timeout Timeout in milliseconds.
+ @param RunAPsSequentially Run APs sequentially (FALSE: run simultaneously)
+
+ @return EFI_SUCCESS on success, or an error code.
+
+**/
+STATIC
+EFI_STATUS
+StartupAllAPs (
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ IN UINTN NumCpus,
+ IN UINTN Timeout,
+ IN BOOLEAN RunAPsSequentially
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ APFUNC_ARG ApArg;
+
+ Status = AllocateApFuncBufferAllAPs (&ApArg, Mp, NumCpus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Print (
+ L"Running with SingleThread TRUE, %dms timeout...",
+ Timeout,
+ (Timeout == INFINITE_TIMEOUT) ? " (infinite)" : "ms"
+ );
+ Status = Mp->StartupAllAPs (
+ Mp,
+ ApFunction,
+ RunAPsSequentially,
+ NULL,
+ Timeout * 1000,
+ &ApArg,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Print (L"failed: %r\n", Status);
+ return Status;
+ } else {
+ Print (L"done.\n");
+ for (Index = 0; Index < NumCpus; Index++) {
+ Print (ApArg.Buffer[Index]);
+ }
+ }
+
+ FreeApFuncBuffer (&ApArg, NumCpus);
+ return EFI_SUCCESS;
+}
+
+/**
+ Enables the specified AP.
+
+ @param Mp The MP Services Protocol.
+ @param ProcessorIndex The processor to enable.
+ @param ProcessorHealthy The health status of the processor.
+
+ @return EFI_SUCCESS on success, or an error code.
+**/
+STATIC
+EFI_STATUS
+EnableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ UINTN ProcessorIndex,
+ BOOLEAN ProcessorHealthy
+ )
+{
+ EFI_STATUS Status;
+ UINT32 HealthFlag;
+
+ if (ProcessorHealthy) {
+ Print (L"Enabling Processor %d with HealthFlag healthy...", ProcessorIndex);
+ HealthFlag = PROCESSOR_HEALTH_STATUS_BIT;
+ } else {
+ Print (L"Enabling Processor %d with HealthFlag faulted...", ProcessorIndex);
+ HealthFlag = 0;
+ }
+
+ Status = Mp->EnableDisableAP (Mp, ProcessorIndex, TRUE, &HealthFlag);
+ if (EFI_ERROR (Status)) {
+ Print (L"failed: %r\n", Status);
+ return Status;
+ } else {
+ Print (L"done.\n");
+ }
+
+ return Status;
+}
+
+/**
+ Disables the specified AP.
+
+ @param Mp The MP Services Protocol.
+ @param ProcessorIndex The processor to disable.
+ @param ProcessorHealthy The health status of the processor.
+
+ @return EFI_SUCCESS on success, or an error code.
+**/
+STATIC
+EFI_STATUS
+DisableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *Mp,
+ UINTN ProcessorIndex,
+ BOOLEAN ProcessorHealthy
+ )
+{
+ EFI_STATUS Status;
+ UINT32 HealthFlag;
+
+ if (ProcessorHealthy) {
+ Print (L"Disabling Processor %d with HealthFlag healthy...", ProcessorIndex);
+ HealthFlag = PROCESSOR_HEALTH_STATUS_BIT;
+ } else {
+ Print (L"Disabling Processor %d with HealthFlag faulted...", ProcessorIndex);
+ HealthFlag = 0;
+ }
+
+ Status = Mp->EnableDisableAP (Mp, ProcessorIndex, FALSE, &HealthFlag);
+ if (EFI_ERROR (Status)) {
+ Print (L"failed: %r\n", Status);
+ return Status;
+ } else {
+ Print (L"done.\n");
+ }
+
+ return Status;
+}
+
+/**
+ The user Entry Point for Application. The user code starts with this function
+ as the real entry point for the application.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+UefiMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_MP_SERVICES_PROTOCOL *Mp;
+ UINTN BspIndex;
+ UINTN CpuIndex;
+ UINTN NumCpus;
+ BOOLEAN ProcessorHealthy;
+ MP_SERVICES_TEST_OPTIONS Options;
+
+ WriteBackDataCacheRange ((VOID *)&ApFunction, 32);
+
+ BspIndex = 0;
+
+ Status = gBS->LocateProtocol (
+ &gEfiMpServiceProtocolGuid,
+ NULL,
+ (VOID **)&Mp
+ );
+ if (EFI_ERROR (Status)) {
+ Print (L"Failed to locate EFI_MP_SERVICES_PROTOCOL (%r). Not installed on platform?\n", Status);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = ParseArguments (&Options);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProcessorInformation (Mp, &NumCpus, &BspIndex);
+ if (EFI_ERROR (Status)) {
+ Print (L"Error: Failed to fetch processor information.\n");
+ return Status;
+ }
+
+ if (Options.PrintBspProcessorIndex) {
+ Status = Mp->WhoAmI (Mp, &CpuIndex);
+ if (EFI_ERROR (Status)) {
+ Print (L"WhoAmI failed: %r\n", Status);
+ return Status;
+ } else {
+ Print (L"BSP: %016lx\n", CpuIndex);
+ }
+ }
+
+ if (Options.PrintProcessorInformation) {
+ NumCpus = PrintProcessorInformation (Mp, &BspIndex);
+ if (NumCpus < 2) {
+ Print (L"Error: Uniprocessor system found.\n");
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (Options.RunSingleAP) {
+ Status = StartupThisAP (
+ Mp,
+ Options.ProcessorIndex,
+ Options.Timeout
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (Options.RunAllAPs) {
+ Status = StartupAllAPs (Mp, NumCpus, Options.Timeout, Options.RunAPsSequentially);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (Options.EnableProcessor) {
+ ProcessorHealthy = TRUE;
+ if (Options.SetProcessorUnhealthy) {
+ ProcessorHealthy = FALSE;
+ }
+
+ Status = EnableAP (Mp, Options.ProcessorIndex, ProcessorHealthy);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (Options.DisableProcessor) {
+ ProcessorHealthy = TRUE;
+ if (Options.SetProcessorUnhealthy) {
+ ProcessorHealthy = FALSE;
+ }
+
+ Status = DisableAP (Mp, Options.ProcessorIndex, ProcessorHealthy);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Application/MpServicesTest/Options.c b/MdeModulePkg/Application/MpServicesTest/Options.c
new file mode 100644
index 000000000000..7380ca44daf7
--- /dev/null
+++ b/MdeModulePkg/Application/MpServicesTest/Options.c
@@ -0,0 +1,215 @@
+/** @file
+ Options handling code.
+
+ Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+ Copyright (c) 2010-2019 Finnbarr P. Murphy. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Protocol/ShellParameters.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "Options.h"
+
+STATIC UINTN Argc;
+STATIC CHAR16 **Argv;
+
+/**
+
+ This function provides argc and argv.
+
+ @return Status
+**/
+EFI_STATUS
+GetArg (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters;
+
+ Status = gBS->HandleProtocol (
+ gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID **)&ShellParameters
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Argc = ShellParameters->Argc;
+ Argv = ShellParameters->Argv;
+ return EFI_SUCCESS;
+}
+
+/**
+ Checks if the character is a decimal digit.
+
+ @param Char The character to check.
+
+ @return TRUE if the character is a decimal digit.
+**/
+BOOLEAN
+IsUnicodeDecimalDigit (
+ CHAR16 Char
+ )
+{
+ return ((BOOLEAN)(Char >= L'0' && Char <= L'9'));
+}
+
+/**
+ Converts the string to an integer.
+
+ @param String The input string.
+ @param Value The converted number.
+
+ @return EFI_SUCCESS on success, or an error code.
+**/
+EFI_STATUS
+UnicodeStringToInteger (
+ CHAR16 *String,
+ UINTN *Value
+ )
+{
+ UINTN Result;
+
+ Result = 0;
+
+ if ((String == NULL) || (StrSize (String) == 0) || (Value == NULL)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ while (IsUnicodeDecimalDigit (*String)) {
+ if (!(Result <= (DivU64x32 ((((UINT64) ~0) - (*String - L'0')), 10)))) {
+ return (EFI_DEVICE_ERROR);
+ }
+
+ Result = MultU64x32 (Result, 10) + (*String - L'0');
+ String++;
+ }
+
+ *Value = Result;
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Print app usage.
+**/
+STATIC
+VOID
+PrintUsage (
+ VOID
+ )
+{
+ Print (L"MpServicesTest: usage\n");
+ Print (L" MpServicesTest -A [-O]\n");
+ Print (L" MpServicesTest -T <Timeout>\n");
+ Print (L" MpServicesTest -S <Processor #>\n");
+ Print (L" MpServicesTest -P\n");
+ Print (L" MpServicesTest -U <Processor #>\n");
+ Print (L" MpServicesTest -W\n");
+ Print (L" MpServicesTest -E <Processor #>\n");
+ Print (L" MpServicesTest -D <Processor #>\n");
+ Print (L" MpServicesTest -h\n");
+ Print (L"Parameter:\n");
+ Print (L" -A: Run all APs.\n");
+ Print (L" -O: Run APs sequentially (use with -A).\n");
+ Print (L" -T: Specify timeout in milliseconds. Default is to wait forever.\n");
+ Print (L" -S: Specify the single AP to run.\n");
+ Print (L" -P: Print processor information.\n");
+ Print (L" -U: Set the specified AP to the Unhealthy status (use with -E/-D).\n");
+ Print (L" -W: Run WhoAmI and print index of BSP.\n");
+ Print (L" -E: Enable the specified AP.\n");
+ Print (L" -D: Disable the specified AP.\n");
+ Print (L" -h: Print this help page.\n");
+}
+
+/**
+ Parses any arguments provided on the command line.
+
+ @param Options The arguments structure.
+
+ @return EFI_SUCCESS on success, or an error code.
+**/
+EFI_STATUS
+ParseArguments (
+ MP_SERVICES_TEST_OPTIONS *Options
+ )
+{
+ EFI_STATUS Status;
+ INT32 ArgIndex;
+ CONST CHAR16 *Argument;
+ BOOLEAN NeedsValue;
+ UINTN *Value;
+
+ Status = GetArg ();
+ if (EFI_ERROR (Status)) {
+ Print (L"Please use the UEFI Shell to run this application!\n", Status);
+ return Status;
+ }
+
+ if (Argc == 1) {
+ PrintUsage ();
+ }
+
+ ZeroMem (Options, sizeof (MP_SERVICES_TEST_OPTIONS));
+
+ for (ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {
+ Argument = Argv[ArgIndex];
+ NeedsValue = FALSE;
+
+ if (StrCmp (Argument, L"-A") == 0) {
+ Options->RunAllAPs = TRUE;
+ } else if (StrCmp (Argument, L"-O") == 0) {
+ Options->RunAPsSequentially = TRUE;
+ } else if (StrCmp (Argument, L"-T") == 0) {
+ NeedsValue = TRUE;
+ Value = &Options->Timeout;
+ } else if (StrCmp (Argument, L"-S") == 0) {
+ Options->RunSingleAP = TRUE;
+ NeedsValue = TRUE;
+ Value = &Options->ProcessorIndex;
+ } else if (StrCmp (Argument, L"-P") == 0) {
+ Options->PrintProcessorInformation = TRUE;
+ } else if (StrCmp (Argument, L"-U") == 0) {
+ Options->SetProcessorUnhealthy = TRUE;
+ } else if (StrCmp (Argument, L"-W") == 0) {
+ Options->PrintBspProcessorIndex = TRUE;
+ } else if (StrCmp (Argument, L"-E") == 0) {
+ Options->EnableProcessor = TRUE;
+ NeedsValue = TRUE;
+ Value = &Options->ProcessorIndex;
+ } else if (StrCmp (Argument, L"-D") == 0) {
+ Options->DisableProcessor = TRUE;
+ NeedsValue = TRUE;
+ Value = &Options->ProcessorIndex;
+ } else {
+ PrintUsage ();
+ ZeroMem (Options, sizeof (MP_SERVICES_TEST_OPTIONS));
+ return EFI_SUCCESS;
+ }
+
+ if (NeedsValue) {
+ if ((ArgIndex + 1) < Argc) {
+ Status = UnicodeStringToInteger (Argv[ArgIndex + 1], Value);
+ if (EFI_ERROR (Status)) {
+ Print (L"Error: option value must be a positive integer.\n");
+ PrintUsage ();
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ArgIndex++;
+ } else {
+ PrintUsage ();
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
--
2.30.2


[PATCH 1/2] ArmPkg: implement EFI_MP_SERVICES_PROTOCOL based on PSCI calls

Rebecca Cran <quic_rcran@...>
 

Add support for EFI_MP_SERVICES_PROTOCOL during the DXE phase under
AArch64.

PSCI_CPU_ON is called to power on the core, the supplied procedure is
executed and PSCI_CPU_OFF is called to power off the core.

Fixes contributed by Ard Biesheuvel.

Signed-off-by: Rebecca Cran <rebecca@...>
---
ArmPkg/ArmPkg.dsc | 1 +
ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf | 55 +
ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h | 351 ++++
ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c | 1774 ++++++++++++++++++++
ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S | 57 +
5 files changed, 2238 insertions(+)

diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
index 59fd8f295d4f..4716789402fc 100644
--- a/ArmPkg/ArmPkg.dsc
+++ b/ArmPkg/ArmPkg.dsc
@@ -125,6 +125,7 @@ [Components.common]
ArmPkg/Drivers/CpuPei/CpuPei.inf
ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
ArmPkg/Drivers/ArmGic/ArmGicLib.inf
+ ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf
ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
ArmPkg/Drivers/TimerDxe/TimerDxe.inf

diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf
new file mode 100644
index 000000000000..2b109c72e0a0
--- /dev/null
+++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf
@@ -0,0 +1,55 @@
+## @file
+# ARM MP services protocol driver
+#
+# Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = ArmPsciMpServicesDxe
+ FILE_GUID = 007ab472-dc4a-4df8-a5c2-abb4a327278c
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = ArmPsciMpServicesDxeInitialize
+
+[Sources.Common]
+ ArmPsciMpServicesDxe.c
+ MpFuncs.S
+ MpServicesInternal.h
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ ArmLib
+ ArmMmuLib
+ ArmSmcLib
+ BaseMemoryLib
+ CacheMaintenanceLib
+ DebugLib
+ HobLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEfiMpServiceProtocolGuid ## PRODUCES
+ gEfiLoadedImageProtocolGuid ## CONSUMES
+
+[Guids]
+ gArmMpCoreInfoGuid
+
+[Depex]
+ TRUE
+
+[BuildOptions]
+ GCC:*_*_*_CC_FLAGS = -mstrict-align
diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h
new file mode 100644
index 000000000000..ee13816ef64c
--- /dev/null
+++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h
@@ -0,0 +1,351 @@
+/** @file
+
+Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+Portions copyright (c) 2011, Apple Inc. All rights reserved.
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MP_SERVICES_INTERNAL_H_
+#define MP_SERVICES_INTERNAL_H_
+
+#include <Protocol/Cpu.h>
+#include <Protocol/MpService.h>
+
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+
+#define AP_STACK_SIZE 0x1000
+
+//
+// Internal Data Structures
+//
+
+//
+// AP state
+//
+// The state transitions for an AP when it process a procedure are:
+// Idle ----> Ready ----> Busy ----> Idle
+// [BSP] [AP] [AP]
+//
+typedef enum {
+ CpuStateIdle,
+ CpuStateReady,
+ CpuStateBlocked,
+ CpuStateBusy,
+ CpuStateFinished,
+ CpuStateDisabled
+} CPU_STATE;
+
+//
+// Define Individual Processor Data block.
+//
+typedef struct {
+ EFI_PROCESSOR_INFORMATION Info;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *Parameter;
+ CPU_STATE State;
+ EFI_EVENT CheckThisAPEvent;
+ UINTN Timeout;
+ UINTN TimeTaken;
+ VOID *Ttbr0;
+ UINTN Tcr;
+ UINTN Mair;
+} CPU_AP_DATA;
+
+//
+// Define MP data block which consumes individual processor block.
+//
+typedef struct {
+ UINTN NumberOfProcessors;
+ UINTN NumberOfEnabledProcessors;
+ EFI_EVENT CheckAllAPsEvent;
+ EFI_EVENT WaitEvent;
+ UINTN FinishCount;
+ UINTN StartCount;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *ProcedureArgument;
+ BOOLEAN SingleThread;
+ UINTN StartedNumber;
+ CPU_AP_DATA *CpuData;
+ UINTN Timeout;
+ UINTN *FailedList;
+ UINTN FailedListIndex;
+ BOOLEAN TimeoutActive;
+} CPU_MP_DATA;
+
+/** Secondary core entry point.
+
+**/
+VOID
+ApEntryPoint (
+ VOID
+ );
+
+/** C entry-point for the AP.
+ This function gets called from the assembly function ApEntryPoint.
+**/
+VOID
+ApProcedure (
+ VOID
+ );
+
+/** Turns on the specified core using PSCI and executes the user-supplied
+ function that's been configured via a previous call to SetApProcedure.
+
+ @param ProcessorIndex The index of the core to turn on.
+
+ @retval EFI_SUCCESS The processor was successfully turned on.
+ @retval EFI_DEVICE_ERROR An error occurred turning the processor on.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DispatchCpu (
+ IN UINTN ProcessorIndex
+ );
+
+/** Returns whether the specified processor is the BSP.
+
+ @param[in] ProcessorIndex The index the processor to check.
+
+ @return TRUE if the processor is the BSP, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorBSP (
+ UINTN ProcessorIndex
+ );
+
+/** Returns whether the processor executing this function is the BSP.
+
+ @return Whether the current processor is the BSP.
+**/
+STATIC
+BOOLEAN
+IsCurrentProcessorBSP (
+ VOID
+ );
+
+/** Returns whether the specified processor is enabled.
+
+ @param[in] ProcessorIndex The index of the processor to check.
+
+ @return TRUE if the processor is enabled, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorEnabled (
+ UINTN ProcessorIndex
+ );
+
+/** Configures the processor context with the user-supplied procedure and
+ argument.
+
+ @param CpuData The processor context.
+ @param Procedure The user-supplied procedure.
+ @param ProcedureArgument The user-supplied procedure argument.
+
+**/
+STATIC
+VOID
+SetApProcedure (
+ IN CPU_AP_DATA *CpuData,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument
+ );
+
+/**
+ Get the Application Processors state.
+
+ @param[in] CpuData The pointer to CPU_AP_DATA of specified AP
+
+ @return The AP status
+**/
+CPU_STATE
+GetApState (
+ IN CPU_AP_DATA *CpuData
+ );
+
+/** Returns the index of the next processor that is blocked.
+
+ @param[out] NextNumber The index of the next blocked processor.
+
+ @retval EFI_SUCCESS Successfully found the next blocked processor.
+ @retval EFI_NOT_FOUND There are no blocked processors.
+
+**/
+STATIC
+EFI_STATUS
+GetNextBlockedNumber (
+ OUT UINTN *NextNumber
+ );
+
+/** Stalls the BSP for the minimum of gPollInterval and Timeout.
+
+ @param[in] Timeout The time limit in microseconds remaining for
+ APs to return from Procedure.
+
+ @retval StallTime Time of execution stall.
+**/
+STATIC
+UINTN
+CalculateAndStallInterval (
+ IN UINTN Timeout
+ );
+
+/** Returns whether all processors are in the idle state.
+
+ @return Whether all the processors are idle.
+
+**/
+STATIC
+BOOLEAN
+CheckAllCpusReady (
+ VOID
+ );
+
+/** Sets up the state for the StartupAllAPs function.
+
+ @param SingleThread Whether the APs will execute sequentially.
+
+**/
+STATIC
+VOID
+StartupAllAPsPrepareState (
+ IN BOOLEAN SingleThread
+ );
+
+/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
+
+ @param Procedure The user-supplied procedure.
+ @param ProcedureArgument The user-supplied procedure argument.
+ @param WaitEvent The wait event to be signaled when the work is
+ complete or a timeout has occurred.
+ @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+ indicates an infinite timeout.
+
+ @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsWithWaitEvent (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument,
+ IN EFI_EVENT WaitEvent,
+ IN UINTN TimeoutInMicroseconds
+ );
+
+/** Handles execution of StartupAllAPs when no wait event has been specified.
+
+ @param Procedure The user-supplied procedure.
+ @param ProcedureArgument The user-supplied procedure argument.
+ @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+ indicates an infinite timeout.
+ @param SingleThread Whether the APs will execute sequentially.
+ @param FailedCpuList User-supplied pointer for list of failed CPUs.
+
+ @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsNoWaitEvent (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument,
+ IN UINTN TimeoutInMicroseconds,
+ IN BOOLEAN SingleThread,
+ IN UINTN **FailedCpuList
+ );
+
+/** Adds the specified processor the list of failed processors.
+
+ @param ProcessorIndex The processor index to add.
+ @param ApState Processor state.
+
+**/
+STATIC
+VOID
+AddProcessorToFailedList (
+ UINTN ProcessorIndex,
+ CPU_STATE ApState
+ );
+
+/** Handles the StartupAllAPs case where the timeout has occurred.
+
+**/
+STATIC
+VOID
+ProcessStartupAllAPsTimeout (
+ VOID
+ );
+
+/**
+ If a timeout is specified in StartupAllAps(), a timer is set, which invokes
+ this procedure periodically to check whether all APs have finished.
+
+ @param[in] Event The WaitEvent the user supplied.
+ @param[in] Context The event context.
+**/
+STATIC
+VOID
+EFIAPI
+CheckAllAPsStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/** Invoked periodically via a timer to check the state of the processor.
+
+ @param Event The event supplied by the timer expiration.
+ @param Context The processor context.
+
+**/
+STATIC
+VOID
+EFIAPI
+CheckThisAPStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ This function is called by all processors (both BSP and AP) once and collects
+ MP related data.
+
+ @param BSP TRUE if the processor is the BSP.
+ @param Mpidr The MPIDR for the specified processor. This should be
+ the full MPIDR and not only the affinity bits.
+ @param ProcessorIndex The index of the processor.
+
+ @return EFI_SUCCESS if the data for the processor collected and filled in.
+
+**/
+STATIC
+EFI_STATUS
+FillInProcessorInformation (
+ IN BOOLEAN BSP,
+ IN UINTN Mpidr,
+ IN UINTN ProcessorIndex
+ );
+
+/**
+ Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
+ signaled. After this point, non-blocking mode is no longer allowed.
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+STATIC
+VOID
+EFIAPI
+ReadyToBootSignaled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+#endif /* MP_SERVICES_INTERNAL_H_ */
diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c
new file mode 100644
index 000000000000..8cea203c7e34
--- /dev/null
+++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c
@@ -0,0 +1,1774 @@
+/** @file
+ Construct MP Services Protocol.
+
+ The MP Services Protocol provides a generalized way of performing following tasks:
+ - Retrieving information of multi-processor environment and MP-related status of
+ specific processors.
+ - Dispatching user-provided function to APs.
+ - Maintain MP-related processor status.
+
+ The MP Services Protocol must be produced on any system with more than one logical
+ processor.
+
+ The Protocol is available only during boot time.
+
+ MP Services Protocol is hardware-independent. Most of the logic of this protocol
+ is architecturally neutral. It abstracts the multi-processor environment and
+ status of processors, and provides interfaces to retrieve information, maintain,
+ and dispatch.
+
+ MP Services Protocol may be consumed by ACPI module. The ACPI module may use this
+ protocol to retrieve data that are needed for an MP platform and report them to OS.
+ MP Services Protocol may also be used to program and configure processors, such
+ as MTRR synchronization for memory space attributes setting in DXE Services.
+ MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
+ by taking advantage of the processing capabilities of the APs, for example, using
+ APs to help test system memory in parallel with other device initialization.
+ Diagnostics applications may also use this protocol for multi-processor.
+
+ Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Library/ArmLib.h>
+#include <Library/ArmMmuLib.h>
+#include <Library/ArmPlatformLib.h>
+#include <Library/ArmSmcLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <IndustryStandard/ArmStdSmc.h>
+#include <Ppi/ArmMpCoreInfo.h>
+#include <Protocol/LoadedImage.h>
+
+#include "MpServicesInternal.h"
+
+#define POLL_INTERVAL_US 50000
+
+#define GET_MPIDR_AFFINITY_BITS(x) ((x) & 0xFF00FFFFFF)
+
+#define MPIDR_MT_BIT BIT24
+
+STATIC CPU_MP_DATA mCpuMpData;
+STATIC BOOLEAN mNonBlockingModeAllowed;
+UINT64 *gApStacksBase;
+UINT64 *gProcessorIDs;
+CONST UINT64 gApStackSize = AP_STACK_SIZE;
+
+STATIC
+BOOLEAN
+IsCurrentProcessorBSP (
+ VOID
+ );
+
+/** Turns on the specified core using PSCI and executes the user-supplied
+ function that's been configured via a previous call to SetApProcedure.
+
+ @param ProcessorIndex The index of the core to turn on.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_DEVICE_ERROR The processor could not be turned on.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DispatchCpu (
+ IN UINTN ProcessorIndex
+ )
+{
+ ARM_SMC_ARGS Args;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ mCpuMpData.CpuData[ProcessorIndex].State = CpuStateBusy;
+
+ /* Turn the AP on */
+ if (sizeof (Args.Arg0) == sizeof (UINT32)) {
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH32;
+ } else {
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH64;
+ }
+
+ Args.Arg1 = gProcessorIDs[ProcessorIndex];
+ Args.Arg2 = (UINTN)ApEntryPoint;
+
+ mCpuMpData.CpuData[ProcessorIndex].Tcr = ArmGetTCR ();
+ mCpuMpData.CpuData[ProcessorIndex].Mair = ArmGetMAIR ();
+ mCpuMpData.CpuData[ProcessorIndex].Ttbr0 = ArmGetTTBR0BaseAddress ();
+ WriteBackDataCacheRange (&mCpuMpData.CpuData[ProcessorIndex], sizeof (CPU_AP_DATA));
+
+ ArmCallSmc (&Args);
+
+ if (Args.Arg0 != ARM_SMC_PSCI_RET_SUCCESS) {
+ DEBUG ((DEBUG_ERROR, "PSCI_CPU_ON call failed: %d\n", Args.Arg0));
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/** Returns whether the specified processor is the BSP.
+
+ @param[in] ProcessorIndex The index the processor to check.
+
+ @return TRUE if the processor is the BSP, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorBSP (
+ UINTN ProcessorIndex
+ )
+{
+ EFI_PROCESSOR_INFORMATION *CpuInfo;
+
+ CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
+
+ return (CpuInfo->StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
+}
+
+/** Get the Application Processors state.
+
+ @param[in] CpuData The pointer to CPU_AP_DATA of specified AP.
+
+ @return The AP status.
+**/
+CPU_STATE
+GetApState (
+ IN CPU_AP_DATA *CpuData
+ )
+{
+ return CpuData->State;
+}
+
+/** Configures the processor context with the user-supplied procedure and
+ argument.
+
+ @param CpuData The processor context.
+ @param Procedure The user-supplied procedure.
+ @param ProcedureArgument The user-supplied procedure argument.
+
+**/
+STATIC
+VOID
+SetApProcedure (
+ IN CPU_AP_DATA *CpuData,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument
+ )
+{
+ ASSERT (CpuData != NULL);
+ ASSERT (Procedure != NULL);
+
+ CpuData->Parameter = ProcedureArgument;
+ CpuData->Procedure = Procedure;
+}
+
+/** Returns the index of the next processor that is blocked.
+
+ @param[out] NextNumber The index of the next blocked processor.
+
+ @retval EFI_SUCCESS Successfully found the next blocked processor.
+ @retval EFI_NOT_FOUND There are no blocked processors.
+
+**/
+STATIC
+EFI_STATUS
+GetNextBlockedNumber (
+ OUT UINTN *NextNumber
+ )
+{
+ UINTN Index;
+ CPU_STATE State;
+ CPU_AP_DATA *CpuData;
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ CpuData = &mCpuMpData.CpuData[Index];
+ if (IsProcessorBSP (Index)) {
+ // Skip BSP
+ continue;
+ }
+
+ State = CpuData->State;
+
+ if (State == CpuStateBlocked) {
+ *NextNumber = Index;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/** Stalls the BSP for the minimum of POLL_INTERVAL_US and Timeout.
+
+ @param[in] Timeout The time limit in microseconds remaining for
+ APs to return from Procedure.
+
+ @retval StallTime Time of execution stall.
+**/
+STATIC
+UINTN
+CalculateAndStallInterval (
+ IN UINTN Timeout
+ )
+{
+ UINTN StallTime;
+
+ if ((Timeout < POLL_INTERVAL_US) && (Timeout != 0)) {
+ StallTime = Timeout;
+ } else {
+ StallTime = POLL_INTERVAL_US;
+ }
+
+ gBS->Stall (StallTime);
+
+ return StallTime;
+}
+
+/**
+ This service retrieves the number of logical processor in the platform
+ and the number of those logical processors that are enabled on this boot.
+ This service may only be called from the BSP.
+
+ This function is used to retrieve the following information:
+ - The number of logical processors that are present in the system.
+ - The number of enabled logical processors in the system at the instant
+ this call is made.
+
+ Because MP Service Protocol provides services to enable and disable processors
+ dynamically, the number of enabled logical processors may vary during the
+ course of a boot session.
+
+ If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
+ If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
+ EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
+ is returned in NumberOfProcessors, the number of currently enabled processor
+ is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the
+ EFI_MP_SERVICES_PROTOCOL instance.
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including
+ the BSP and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled
+ logical processors that exist in the
+ system, including the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL.
+ @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetNumberOfProcessors (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *NumberOfProcessors,
+ OUT UINTN *NumberOfEnabledProcessors
+ )
+{
+ if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsCurrentProcessorBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ *NumberOfProcessors = mCpuMpData.NumberOfProcessors;
+ *NumberOfEnabledProcessors = mCpuMpData.NumberOfEnabledProcessors;
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ This service retrieves detailed MP-related information about any processor
+ on the platform. Note the following:
+ - The processor information may change during the course of a boot session.
+ - The information presented here is entirely MP related.
+
+ Information regarding the number of caches and their sizes, frequency of
+ operation, slot numbers is all considered platform-related information and is
+ not provided by this service.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] ProcessorIndex The index of the processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information
+ for the requested processor is deposited.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetProcessorInfo (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorIndex,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer
+ )
+{
+ if (ProcessorInfoBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsCurrentProcessorBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ProcessorIndex &= ~CPU_V2_EXTENDED_TOPOLOGY;
+
+ if (ProcessorIndex >= mCpuMpData.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ CopyMem (
+ ProcessorInfoBuffer,
+ &mCpuMpData.CpuData[ProcessorIndex],
+ sizeof (EFI_PROCESSOR_INFORMATION)
+ );
+ return EFI_SUCCESS;
+}
+
+/**
+ This service executes a caller provided function on all enabled APs. APs can
+ run either simultaneously or one at a time in sequence. This service supports
+ both blocking and non-blocking requests. The non-blocking requests use EFI
+ events so the BSP can detect when the APs have finished. This service may only
+ be called from the BSP.
+
+ This function is used to dispatch all the enabled APs to the function
+ specified by Procedure. If any enabled AP is busy, then EFI_NOT_READY is
+ returned immediately and Procedure is not started on any AP.
+
+ If SingleThread is TRUE, all the enabled APs execute the function specified by
+ Procedure one by one, in ascending order of processor handle number.
+ Otherwise, all the enabled APs execute the function specified by Procedure
+ simultaneously.
+
+ If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
+ APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in
+ non-blocking mode, and the BSP returns from this service without waiting for
+ APs. If a non-blocking mode is requested after the UEFI Event
+ EFI_EVENT_GROUP_READY_TO_BOOT is signaled, then EFI_UNSUPPORTED must be
+ returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before all APs
+ return from Procedure, then Procedure on the failed APs is terminated.
+ All enabled APs are always available for further calls to
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
+ content points to the list of processor handle numbers in which Procedure was
+ terminated.
+
+ Note: It is the responsibility of the consumer of the
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() to make sure that the nature of the
+ code that is executed on the BSP and the dispatched APs is well controlled.
+ The MP Services Protocol does not guarantee that the Procedure function is
+ MP-safe. Hence, the tasks that can be run in parallel are limited to certain
+ independent tasks and well-controlled exclusive code. EFI services and
+ protocols may not be called by APs unless otherwise specified.
+
+ In blocking execution mode, BSP waits until all APs finish or
+ TimeoutInMicroseconds expires.
+
+ In non-blocking execution mode, BSP is freed to return to the caller and then
+ proceed to the next task without having to wait for APs. The following
+ sequence needs to occur in a non-blocking execution mode:
+
+ -# The caller that intends to use this MP Services Protocol in non-blocking
+ mode creates WaitEvent by calling the EFI CreateEvent() service. The
+ caller invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter
+ WaitEvent is not NULL, then StartupAllAPs() executes in non-blocking
+ mode. It requests the function specified by Procedure to be started on
+ all the enabled APs, and releases the BSP to continue with other tasks.
+ -# The caller can use the CheckEvent() and WaitForEvent() services to check
+ the state of the WaitEvent created in step 1.
+ -# When the APs complete their task or TimeoutInMicroSecondss expires, the
+ MP Service signals WaitEvent by calling the EFI SignalEvent() function.
+ If FailedCpuList is not NULL, its content is available when WaitEvent is
+ signaled. If all APs returned from Procedure prior to the timeout, then
+ FailedCpuList is set to NULL. If not all APs return from Procedure before
+ the timeout, then FailedCpuList is filled in with the list of the failed
+ APs. The buffer is allocated by MP Service Protocol using AllocatePool().
+ It is the caller's responsibility to free the buffer with FreePool()
+ service.
+ -# This invocation of SignalEvent() function informs the caller that invoked
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs
+ completed the specified task or a timeout occurred. The contents of
+ FailedCpuList can be examined to determine which APs did not complete the
+ specified task prior to the timeout.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor
+ handle number. If FALSE, then all the
+ enabled APs execute the function specified
+ by Procedure simultaneously.
+ @param[in] WaitEvent The event created by the caller with
+ CreateEvent() service. If it is NULL,
+ then execute in blocking mode. BSP waits
+ until all APs finish or
+ TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking
+ mode. BSP requests the function specified
+ by Procedure to be started on all the
+ enabled APs, and go on executing
+ immediately. If all return from Procedure,
+ or TimeoutInMicroseconds expires, this
+ event is signaled. The BSP can use the
+ CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds
+ for APs to return from Procedure, either
+ for blocking or non-blocking mode. Zero
+ means infinity. If the timeout expires
+ before all APs return from Procedure, then
+ Procedure on the failed APs is terminated.
+ All enabled APs are available for next
+ function assigned by
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If NULL, this parameter is ignored.
+ Otherwise, if all APs finish successfully,
+ then its content is set to NULL. If not
+ all APs finish before timeout expires,
+ then its content is set to address of the
+ buffer holding handle numbers of the
+ failed APs.
+ The buffer is allocated by MP Service
+ Protocol, and it's the caller's
+ responsibility to free the buffer with
+ FreePool() service.
+ In blocking mode, it is ready for
+ consumption when the call returns. In
+ non-blocking mode, it is ready when
+ WaitEvent is signaled. The list of failed
+ CPU is terminated by END_OF_CPU_LIST.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been
+ dispatched to all enabled APs.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR Caller processor is AP.
+ @retval EFI_NOT_STARTED No enabled APs exist in the system.
+ @retval EFI_NOT_READY Any enabled APs are busy.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ all enabled APs have finished.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StartupAllAPs (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if (!IsCurrentProcessorBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (mCpuMpData.NumberOfProcessors == 1) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (!CheckAllCpusReady ()) {
+ return EFI_NOT_READY;
+ }
+
+ if (FailedCpuList != NULL) {
+ mCpuMpData.FailedList = AllocatePool (
+ (mCpuMpData.NumberOfProcessors + 1) *
+ sizeof (UINTN)
+ );
+ if (mCpuMpData.FailedList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ SetMemN (
+ mCpuMpData.FailedList,
+ (mCpuMpData.NumberOfProcessors + 1) *
+ sizeof (UINTN),
+ END_OF_CPU_LIST
+ );
+ mCpuMpData.FailedListIndex = 0;
+ *FailedCpuList = mCpuMpData.FailedList;
+ }
+
+ StartupAllAPsPrepareState (SingleThread);
+
+ if (WaitEvent != NULL) {
+ Status = StartupAllAPsWithWaitEvent (
+ Procedure,
+ ProcedureArgument,
+ WaitEvent,
+ TimeoutInMicroseconds
+ );
+ } else {
+ Status = StartupAllAPsNoWaitEvent (
+ Procedure,
+ ProcedureArgument,
+ TimeoutInMicroseconds,
+ SingleThread,
+ FailedCpuList
+ );
+ }
+
+ return Status;
+}
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function. The caller can request the BSP to either wait for the completion
+ of the AP or just proceed with the next task by using the EFI event mechanism.
+ See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
+ execution support. This service may only be called from the BSP.
+
+ This function is used to dispatch one enabled AP to the function specified by
+ Procedure passing in the argument specified by ProcedureArgument. If WaitEvent
+ is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
+ TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
+ BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
+ is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
+ then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before the AP returns
+ from Procedure, then execution of Procedure by the AP is terminated. The AP is
+ available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] ProcessorNumber The handle number of the AP. The range is
+ from 0 to the total number of logical
+ processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] Finished If NULL, this parameter is ignored. In
+ blocking mode, this parameter is ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ by evaluating this value.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval EFI_SUCCESS In non-blocking mode, the function has been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ the specified AP has finished.
+ @retval EFI_NOT_READY The specified AP is busy.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StartupThisAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINTN Timeout;
+ CPU_AP_DATA *CpuData;
+
+ if (!IsCurrentProcessorBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ CpuData = &mCpuMpData.CpuData[ProcessorNumber];
+
+ if (IsProcessorBSP (ProcessorNumber)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsProcessorEnabled (ProcessorNumber)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GetApState (CpuData) != CpuStateIdle) {
+ return EFI_NOT_READY;
+ }
+
+ if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Timeout = TimeoutInMicroseconds;
+
+ mCpuMpData.StartCount = 1;
+ mCpuMpData.FinishCount = 0;
+
+ SetApProcedure (
+ CpuData,
+ Procedure,
+ ProcedureArgument
+ );
+
+ Status = DispatchCpu (ProcessorNumber);
+ if (EFI_ERROR (Status)) {
+ CpuData->State = CpuStateIdle;
+ return EFI_NOT_READY;
+ }
+
+ if (WaitEvent != NULL) {
+ // Non Blocking
+ mCpuMpData.WaitEvent = WaitEvent;
+ gBS->SetTimer (
+ CpuData->CheckThisAPEvent,
+ TimerPeriodic,
+ POLL_INTERVAL_US
+ );
+ return EFI_SUCCESS;
+ }
+
+ // Blocking
+ while (TRUE) {
+ if (GetApState (CpuData) == CpuStateFinished) {
+ CpuData->State = CpuStateIdle;
+ break;
+ }
+
+ if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
+ return EFI_TIMEOUT;
+ }
+
+ Timeout -= CalculateAndStallInterval (Timeout);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. This call can only be
+ performed by the current BSP.
+
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. The new BSP can take over the
+ execution of the old BSP and continue seamlessly from where the old one left
+ off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled.
+
+ If the BSP cannot be switched prior to the return from this service, then
+ EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
+ enabled AP. Otherwise, it will be disabled.
+
+ @retval EFI_SUCCESS BSP successfully switched.
+ @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
+ this service returning.
+ @retval EFI_UNSUPPORTED Switching the BSP is not supported.
+ @retval EFI_SUCCESS The calling processor is an AP.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
+ a disabled AP.
+ @retval EFI_NOT_READY The specified AP is busy.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SwitchBSP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ This service allows the caller enable or disable an AP from this point onward.
+ The caller can optionally specify the health status of the AP by Health. If
+ an AP is being disabled, then the state of the disabled AP is implementation
+ dependent. If an AP is enabled, then the implementation must guarantee that a
+ complete initialization sequence is performed on the AP, so the AP is in a state
+ that is compatible with an MP operating system. This service may not be supported
+ after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
+
+ If the enable or disable AP operation cannot be completed prior to the return
+ from this service, then EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ the new health status of the AP. This flag
+ corresponds to StatusFlag defined in
+ EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
+ the PROCESSOR_HEALTH_STATUS_BIT is used. All other
+ bits are ignored. If it is NULL, this parameter
+ is ignored.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
+ prior to this service returning.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
+ does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EnableDisableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ UINTN StatusFlag;
+ CPU_AP_DATA *CpuData;
+
+ StatusFlag = mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag;
+ CpuData = &mCpuMpData.CpuData[ProcessorNumber];
+
+ if (!IsCurrentProcessorBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (IsProcessorBSP (ProcessorNumber)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GetApState (CpuData) != CpuStateIdle) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (EnableAP) {
+ if (!IsProcessorEnabled (ProcessorNumber)) {
+ mCpuMpData.NumberOfEnabledProcessors++;
+ }
+
+ StatusFlag |= PROCESSOR_ENABLED_BIT;
+ } else {
+ if (IsProcessorEnabled (ProcessorNumber)) {
+ mCpuMpData.NumberOfEnabledProcessors--;
+ }
+
+ StatusFlag &= ~PROCESSOR_ENABLED_BIT;
+ }
+
+ if (HealthFlag != NULL) {
+ StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
+ StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
+ }
+
+ mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag = StatusFlag;
+ return EFI_SUCCESS;
+}
+
+/**
+ This return the handle number for the calling processor. This service may be
+ called from the BSP and APs.
+
+ This service returns the processor handle number for the calling processor.
+ The returned value is in the range from 0 to the total number of logical
+ processors minus 1. The total number of logical processors can be retrieved
+ with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+ called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
+ is returned. Otherwise, the current processors handle number is returned in
+ ProcessorNumber, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[out] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+
+ @retval EFI_SUCCESS The current processor handle number was returned
+ in ProcessorNumber.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+WhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ )
+{
+ UINTN Index;
+ UINT64 ProcessorId;
+
+ if (ProcessorNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ProcessorId = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ if (ProcessorId == gProcessorIDs[Index]) {
+ *ProcessorNumber = Index;
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC EFI_MP_SERVICES_PROTOCOL mMpServicesProtocol = {
+ GetNumberOfProcessors,
+ GetProcessorInfo,
+ StartupAllAPs,
+ StartupThisAP,
+ SwitchBSP,
+ EnableDisableAP,
+ WhoAmI
+};
+
+/** Adds the specified processor the list of failed processors.
+
+ @param ProcessorIndex The processor index to add.
+ @param ApState Processor state.
+
+**/
+STATIC
+VOID
+AddProcessorToFailedList (
+ UINTN ProcessorIndex,
+ CPU_STATE ApState
+ )
+{
+ UINTN Index;
+ BOOLEAN Found;
+
+ Found = FALSE;
+
+ if (ApState == CpuStateIdle) {
+ return;
+ }
+
+ // If we are retrying make sure we don't double count
+ for (Index = 0; Index < mCpuMpData.FailedListIndex; Index++) {
+ if (mCpuMpData.FailedList[Index] == ProcessorIndex) {
+ Found = TRUE;
+ break;
+ }
+ }
+
+ /* If the CPU isn't already in the FailedList, add it */
+ if (!Found) {
+ mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
+ }
+}
+
+/** Handles the StartupAllAPs case where the timeout has occurred.
+
+**/
+STATIC
+VOID
+ProcessStartupAllAPsTimeout (
+ VOID
+ )
+{
+ CPU_AP_DATA *CpuData;
+ UINTN Index;
+
+ if (mCpuMpData.FailedList == NULL) {
+ return;
+ }
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ CpuData = &mCpuMpData.CpuData[Index];
+ if (IsProcessorBSP (Index)) {
+ // Skip BSP
+ continue;
+ }
+
+ if (!IsProcessorEnabled (Index)) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ CpuData = &mCpuMpData.CpuData[Index];
+ AddProcessorToFailedList (Index, GetApState (CpuData));
+ }
+}
+
+/** Updates the status of the APs.
+
+ @param[in] ProcessorIndex The index of the AP to update.
+**/
+STATIC
+VOID
+UpdateApStatus (
+ IN UINTN ProcessorIndex
+ )
+{
+ EFI_STATUS Status;
+ CPU_AP_DATA *CpuData;
+ CPU_AP_DATA *NextCpuData;
+ CPU_STATE State;
+ UINTN NextNumber;
+
+ CpuData = &mCpuMpData.CpuData[ProcessorIndex];
+
+ if (IsProcessorBSP (ProcessorIndex)) {
+ // Skip BSP
+ return;
+ }
+
+ if (!IsProcessorEnabled (ProcessorIndex)) {
+ // Skip Disabled processors
+ return;
+ }
+
+ State = GetApState (CpuData);
+
+ switch (State) {
+ case CpuStateFinished:
+ if (mCpuMpData.SingleThread) {
+ Status = GetNextBlockedNumber (&NextNumber);
+ if (!EFI_ERROR (Status)) {
+ NextCpuData = &mCpuMpData.CpuData[NextNumber];
+
+ NextCpuData->State = CpuStateReady;
+
+ SetApProcedure (
+ NextCpuData,
+ mCpuMpData.Procedure,
+ mCpuMpData.ProcedureArgument
+ );
+ }
+ }
+
+ CpuData->State = CpuStateIdle;
+ mCpuMpData.FinishCount++;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ If a timeout is specified in StartupAllAps(), a timer is set, which invokes
+ this procedure periodically to check whether all APs have finished.
+
+ @param[in] Event The WaitEvent the user supplied.
+ @param[in] Context The event context.
+**/
+STATIC
+VOID
+EFIAPI
+CheckAllAPsStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UINTN Index;
+
+ if (mCpuMpData.TimeoutActive) {
+ mCpuMpData.Timeout -= CalculateAndStallInterval (mCpuMpData.Timeout);
+ }
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ UpdateApStatus (Index);
+ }
+
+ if (mCpuMpData.TimeoutActive && (mCpuMpData.Timeout == 0)) {
+ ProcessStartupAllAPsTimeout ();
+
+ // Force terminal exit
+ mCpuMpData.FinishCount = mCpuMpData.StartCount;
+ }
+
+ if (mCpuMpData.FinishCount != mCpuMpData.StartCount) {
+ return;
+ }
+
+ gBS->SetTimer (
+ mCpuMpData.CheckAllAPsEvent,
+ TimerCancel,
+ 0
+ );
+
+ if (mCpuMpData.FailedListIndex == 0) {
+ if (mCpuMpData.FailedList != NULL) {
+ FreePool (mCpuMpData.FailedList);
+ mCpuMpData.FailedList = NULL;
+ }
+ }
+
+ gBS->SignalEvent (mCpuMpData.WaitEvent);
+}
+
+/** Invoked periodically via a timer to check the state of the processor.
+
+ @param Event The event supplied by the timer expiration.
+ @param Context The processor context.
+
+**/
+STATIC
+VOID
+EFIAPI
+CheckThisAPStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ CPU_AP_DATA *CpuData;
+ CPU_STATE State;
+
+ CpuData = Context;
+ CpuData->TimeTaken += POLL_INTERVAL_US;
+
+ State = GetApState (CpuData);
+
+ if (State == CpuStateFinished) {
+ Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);
+ ASSERT_EFI_ERROR (Status);
+
+ if (mCpuMpData.WaitEvent != NULL) {
+ Status = gBS->SignalEvent (mCpuMpData.WaitEvent);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ CpuData->State = CpuStateIdle;
+ }
+
+ if (CpuData->TimeTaken > CpuData->Timeout) {
+ if (mCpuMpData.WaitEvent != NULL) {
+ Status = gBS->SignalEvent (mCpuMpData.WaitEvent);
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+}
+
+/**
+ This function is called by all processors (both BSP and AP) once and collects
+ MP related data.
+
+ @param BSP TRUE if the processor is the BSP.
+ @param Mpidr The MPIDR for the specified processor. This should be
+ the full MPIDR and not only the affinity bits.
+ @param ProcessorIndex The index of the processor.
+
+ @return EFI_SUCCESS if the data for the processor collected and filled in.
+
+**/
+STATIC
+EFI_STATUS
+FillInProcessorInformation (
+ IN BOOLEAN BSP,
+ IN UINTN Mpidr,
+ IN UINTN ProcessorIndex
+ )
+{
+ EFI_PROCESSOR_INFORMATION *CpuInfo;
+
+ CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
+
+ CpuInfo->ProcessorId = GET_MPIDR_AFFINITY_BITS (Mpidr);
+ CpuInfo->StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
+
+ if (BSP) {
+ CpuInfo->StatusFlag |= PROCESSOR_AS_BSP_BIT;
+ }
+
+ if ((Mpidr & MPIDR_MT_BIT) > 0) {
+ CpuInfo->Location.Package = GET_MPIDR_AFF2 (Mpidr);
+ CpuInfo->Location.Core = GET_MPIDR_AFF1 (Mpidr);
+ CpuInfo->Location.Thread = GET_MPIDR_AFF0 (Mpidr);
+
+ CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF3 (Mpidr);
+ CpuInfo->ExtendedInformation.Location2.Die = GET_MPIDR_AFF2 (Mpidr);
+ CpuInfo->ExtendedInformation.Location2.Core = GET_MPIDR_AFF1 (Mpidr);
+ CpuInfo->ExtendedInformation.Location2.Thread = GET_MPIDR_AFF0 (Mpidr);
+ } else {
+ CpuInfo->Location.Package = GET_MPIDR_AFF1 (Mpidr);
+ CpuInfo->Location.Core = GET_MPIDR_AFF0 (Mpidr);
+ CpuInfo->Location.Thread = 0;
+
+ CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF2 (Mpidr);
+ CpuInfo->ExtendedInformation.Location2.Die = GET_MPIDR_AFF1 (Mpidr);
+ CpuInfo->ExtendedInformation.Location2.Core = GET_MPIDR_AFF0 (Mpidr);
+ CpuInfo->ExtendedInformation.Location2.Thread = 0;
+ }
+
+ mCpuMpData.CpuData[ProcessorIndex].State = BSP ? CpuStateBusy : CpuStateIdle;
+
+ mCpuMpData.CpuData[ProcessorIndex].Procedure = NULL;
+ mCpuMpData.CpuData[ProcessorIndex].Parameter = NULL;
+
+ return EFI_SUCCESS;
+}
+
+/** Initializes the MP Services system data
+
+ @param NumberOfProcessors The number of processors, both BSP and AP.
+ @param CoreInfo CPU information gathered earlier during boot.
+
+**/
+STATIC
+EFI_STATUS
+MpServicesInitialize (
+ IN UINTN NumberOfProcessors,
+ IN CONST ARM_CORE_INFO *CoreInfo
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_EVENT ReadyToBootEvent;
+ BOOLEAN IsBsp;
+
+ //
+ // Clear the data structure area first.
+ //
+ ZeroMem (&mCpuMpData, sizeof (CPU_MP_DATA));
+ //
+ // First BSP fills and inits all known values, including its own records.
+ //
+ mCpuMpData.NumberOfProcessors = NumberOfProcessors;
+ mCpuMpData.NumberOfEnabledProcessors = NumberOfProcessors;
+
+ mCpuMpData.CpuData = AllocateZeroPool (
+ mCpuMpData.NumberOfProcessors * sizeof (CPU_AP_DATA)
+ );
+
+ if (mCpuMpData.CpuData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Allocate one extra for the sentinel entry at the end */
+ gProcessorIDs = AllocatePool ((mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64));
+ ASSERT (gProcessorIDs != NULL);
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CheckAllAPsStatus,
+ NULL,
+ &mCpuMpData.CheckAllAPsEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ gApStacksBase = AllocatePages (
+ EFI_SIZE_TO_PAGES (
+ mCpuMpData.NumberOfProcessors *
+ gApStackSize
+ )
+ );
+ ASSERT (gApStacksBase != NULL);
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ if (GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ()) == CoreInfo[Index].Mpidr) {
+ IsBsp = TRUE;
+ } else {
+ IsBsp = FALSE;
+ }
+
+ FillInProcessorInformation (IsBsp, CoreInfo[Index].Mpidr, Index);
+
+ gProcessorIDs[Index] = mCpuMpData.CpuData[Index].Info.ProcessorId;
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CheckThisAPStatus,
+ (VOID *)&mCpuMpData.CpuData[Index],
+ &mCpuMpData.CpuData[Index].CheckThisAPEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ gProcessorIDs[Index] = MAX_UINT64;
+
+ //
+ // The global pointer variables as well as the gProcessorIDs array contents
+ // are accessed by the other cores so we must clean them to the PoC
+ //
+ WriteBackDataCacheRange (&gProcessorIDs, sizeof (UINT64 *));
+ WriteBackDataCacheRange (&gApStacksBase, sizeof (UINT64 *));
+
+ WriteBackDataCacheRange (
+ gProcessorIDs,
+ (mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64)
+ );
+
+ Status = EfiCreateEventReadyToBootEx (
+ TPL_CALLBACK,
+ ReadyToBootSignaled,
+ NULL,
+ &ReadyToBootEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
+ signaled. After this point, non-blocking mode is no longer allowed.
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+STATIC
+VOID
+EFIAPI
+ReadyToBootSignaled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ mNonBlockingModeAllowed = FALSE;
+}
+
+/** Initialize multi-processor support.
+
+ @param ImageHandle Image handle.
+ @param SystemTable System table.
+
+ @return EFI_SUCCESS on success, or an error code.
+
+**/
+EFI_STATUS
+EFIAPI
+ArmPsciMpServicesDxeInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ UINTN MaxCpus;
+ EFI_LOADED_IMAGE_PROTOCOL *Image;
+ EFI_HOB_GENERIC_HEADER *Hob;
+ VOID *HobData;
+ UINTN HobDataSize;
+ CONST ARM_CORE_INFO *CoreInfo;
+
+ MaxCpus = 1;
+
+ DEBUG ((DEBUG_INFO, "Starting MP services\n"));
+
+ Status = gBS->HandleProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&Image
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Parts of the code in this driver may be executed by other cores running
+ // with the MMU off so we need to ensure that everything is clean to the
+ // point of coherency (PoC)
+ //
+ WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize);
+
+ Status = gBS->HandleProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&Image
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Parts of the code in this driver may be executed by other cores running
+ // with the MMU off so we need to ensure that everything is clean to the
+ // point of coherency (PoC)
+ //
+ WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize);
+
+ Hob = GetFirstGuidHob (&gArmMpCoreInfoGuid);
+ if (Hob != NULL) {
+ HobData = GET_GUID_HOB_DATA (Hob);
+ HobDataSize = GET_GUID_HOB_DATA_SIZE (Hob);
+ CoreInfo = (ARM_CORE_INFO *)HobData;
+ MaxCpus = HobDataSize / sizeof (ARM_CORE_INFO);
+ }
+
+ if (MaxCpus == 1) {
+ DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP system"));
+ // We are not MP so nothing to do
+ return EFI_NOT_FOUND;
+ }
+
+ Status = MpServicesInitialize (MaxCpus, CoreInfo);
+ if (Status != EFI_SUCCESS) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ //
+ // Now install the MP services protocol.
+ //
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEfiMpServiceProtocolGuid,
+ &mMpServicesProtocol,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/** C entry-point for the AP.
+ This function gets called from the assembly function ApEntryPoint.
+
+**/
+VOID
+ApProcedure (
+ VOID
+ )
+{
+ ARM_SMC_ARGS Args;
+ EFI_AP_PROCEDURE UserApProcedure;
+ VOID *UserApParameter;
+ UINTN ProcessorIndex;
+
+ WhoAmI (&mMpServicesProtocol, &ProcessorIndex);
+
+ /* Fetch the user-supplied procedure and parameter to execute */
+ UserApProcedure = mCpuMpData.CpuData[ProcessorIndex].Procedure;
+ UserApParameter = mCpuMpData.CpuData[ProcessorIndex].Parameter;
+
+ // Configure the MMU and caches
+ ArmSetTCR (mCpuMpData.CpuData[ProcessorIndex].Tcr);
+ ArmSetTTBR0 (mCpuMpData.CpuData[ProcessorIndex].Ttbr0);
+ ArmSetMAIR (mCpuMpData.CpuData[ProcessorIndex].Mair);
+ ArmDisableAlignmentCheck ();
+ ArmEnableStackAlignmentCheck ();
+ ArmEnableInstructionCache ();
+ ArmEnableDataCache ();
+ ArmEnableMmu ();
+
+ UserApProcedure (UserApParameter);
+
+ mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;
+
+ ArmDataMemoryBarrier ();
+
+ /* Since we're finished with this AP, turn it off */
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;
+ ArmCallSmc (&Args);
+
+ /* Should never be reached */
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
+
+/** Returns whether the processor executing this function is the BSP.
+
+ @return Whether the current processor is the BSP.
+**/
+STATIC
+BOOLEAN
+IsCurrentProcessorBSP (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN ProcessorIndex;
+
+ Status = WhoAmI (&mMpServicesProtocol, &ProcessorIndex);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return FALSE;
+ }
+
+ return IsProcessorBSP (ProcessorIndex);
+}
+
+/** Returns whether the specified processor is enabled.
+
+ @param[in] ProcessorIndex The index of the processor to check.
+
+ @return TRUE if the processor is enabled, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorEnabled (
+ UINTN ProcessorIndex
+ )
+{
+ EFI_PROCESSOR_INFORMATION *CpuInfo;
+
+ CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
+
+ return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) != 0;
+}
+
+/** Returns whether all processors are in the idle state.
+
+ @return Whether all the processors are idle.
+
+**/
+STATIC
+BOOLEAN
+CheckAllCpusReady (
+ VOID
+ )
+{
+ UINTN Index;
+ CPU_AP_DATA *CpuData;
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ CpuData = &mCpuMpData.CpuData[Index];
+ if (IsProcessorBSP (Index)) {
+ // Skip BSP
+ continue;
+ }
+
+ if (!IsProcessorEnabled (Index)) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ if (GetApState (CpuData) != CpuStateIdle) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/** Sets up the state for the StartupAllAPs function.
+
+ @param SingleThread Whether the APs will execute sequentially.
+
+**/
+STATIC
+VOID
+StartupAllAPsPrepareState (
+ IN BOOLEAN SingleThread
+ )
+{
+ UINTN Index;
+ CPU_STATE APInitialState;
+ CPU_AP_DATA *CpuData;
+
+ mCpuMpData.FinishCount = 0;
+ mCpuMpData.StartCount = 0;
+ mCpuMpData.SingleThread = SingleThread;
+
+ APInitialState = CpuStateReady;
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ CpuData = &mCpuMpData.CpuData[Index];
+
+ //
+ // Get APs prepared, and put failing APs into FailedCpuList.
+ // If "SingleThread", only 1 AP will put into ready state, other AP will be
+ // put into ready state 1 by 1, until the previous 1 finished its task.
+ // If not "SingleThread", all APs are put into ready state from the
+ // beginning
+ //
+
+ if (IsProcessorBSP (Index)) {
+ // Skip BSP
+ continue;
+ }
+
+ if (!IsProcessorEnabled (Index)) {
+ // Skip Disabled processors
+ if (mCpuMpData.FailedList != NULL) {
+ mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
+ }
+
+ continue;
+ }
+
+ ASSERT (GetApState (CpuData) == CpuStateIdle);
+ CpuData->State = APInitialState;
+
+ mCpuMpData.StartCount++;
+ if (SingleThread) {
+ APInitialState = CpuStateBlocked;
+ }
+ }
+}
+
+/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
+
+ @param Procedure The user-supplied procedure.
+ @param ProcedureArgument The user-supplied procedure argument.
+ @param WaitEvent The wait event to be signaled when the work is
+ complete or a timeout has occurred.
+ @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+ indicates an infinite timeout.
+
+ @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsWithWaitEvent (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument,
+ IN EFI_EVENT WaitEvent,
+ IN UINTN TimeoutInMicroseconds
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ CPU_AP_DATA *CpuData;
+
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ CpuData = &mCpuMpData.CpuData[Index];
+ if (IsProcessorBSP (Index)) {
+ // Skip BSP
+ continue;
+ }
+
+ if (!IsProcessorEnabled (Index)) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ if (GetApState (CpuData) == CpuStateReady) {
+ SetApProcedure (CpuData, Procedure, ProcedureArgument);
+ }
+ }
+
+ //
+ // Save data into private data structure, and create timer to poll AP state
+ // before exiting
+ //
+ mCpuMpData.Procedure = Procedure;
+ mCpuMpData.ProcedureArgument = ProcedureArgument;
+ mCpuMpData.WaitEvent = WaitEvent;
+ mCpuMpData.Timeout = TimeoutInMicroseconds;
+ mCpuMpData.TimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);
+ Status = gBS->SetTimer (
+ mCpuMpData.CheckAllAPsEvent,
+ TimerPeriodic,
+ POLL_INTERVAL_US
+ );
+ return Status;
+}
+
+/** Handles execution of StartupAllAPs when no wait event has been specified.
+
+ @param Procedure The user-supplied procedure.
+ @param ProcedureArgument The user-supplied procedure argument.
+ @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+ indicates an infinite timeout.
+ @param SingleThread Whether the APs will execute sequentially.
+ @param FailedCpuList User-supplied pointer for list of failed CPUs.
+
+ @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsNoWaitEvent (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument,
+ IN UINTN TimeoutInMicroseconds,
+ IN BOOLEAN SingleThread,
+ IN UINTN **FailedCpuList
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN NextIndex;
+ UINTN Timeout;
+ CPU_AP_DATA *CpuData;
+
+ Timeout = TimeoutInMicroseconds;
+
+ while (TRUE) {
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+ CpuData = &mCpuMpData.CpuData[Index];
+ if (IsProcessorBSP (Index)) {
+ // Skip BSP
+ continue;
+ }
+
+ if (!IsProcessorEnabled (Index)) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ switch (GetApState (CpuData)) {
+ case CpuStateReady:
+ SetApProcedure (CpuData, Procedure, ProcedureArgument);
+ Status = DispatchCpu (Index);
+ if (EFI_ERROR (Status)) {
+ CpuData->State = CpuStateIdle;
+ Status = EFI_NOT_READY;
+ goto Done;
+ }
+
+ break;
+
+ case CpuStateFinished:
+ mCpuMpData.FinishCount++;
+ if (SingleThread) {
+ Status = GetNextBlockedNumber (&NextIndex);
+ if (!EFI_ERROR (Status)) {
+ mCpuMpData.CpuData[NextIndex].State = CpuStateReady;
+ }
+ }
+
+ CpuData->State = CpuStateIdle;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (mCpuMpData.FinishCount == mCpuMpData.StartCount) {
+ Status = EFI_SUCCESS;
+ goto Done;
+ }
+
+ if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
+ Status = EFI_TIMEOUT;
+ goto Done;
+ }
+
+ Timeout -= CalculateAndStallInterval (Timeout);
+ }
+
+Done:
+ if (FailedCpuList != NULL) {
+ if (mCpuMpData.FailedListIndex == 0) {
+ FreePool (*FailedCpuList);
+ *FailedCpuList = NULL;
+ }
+ }
+
+ return Status;
+}
diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S
new file mode 100644
index 000000000000..a90fa8a0075f
--- /dev/null
+++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S
@@ -0,0 +1,57 @@
+#===============================================================================
+# Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#===============================================================================
+
+.text
+.align 3
+
+#include <AsmMacroIoLibV8.h>
+#include <IndustryStandard/ArmStdSmc.h>
+
+#include "MpServicesInternal.h"
+
+GCC_ASM_IMPORT (gApStacksBase)
+GCC_ASM_IMPORT (gProcessorIDs)
+GCC_ASM_IMPORT (ApProcedure)
+GCC_ASM_IMPORT (gApStackSize)
+
+GCC_ASM_EXPORT (ApEntryPoint)
+
+// Entry-point for the AP
+// VOID
+// ApEntryPoint (
+// VOID
+// );
+ASM_PFX(ApEntryPoint):
+ mrs x0, mpidr_el1
+ // Mask the non-affinity bits
+ bic x0, x0, 0x00ff000000
+ and x0, x0, 0xffffffffff
+ ldr x1, gProcessorIDs
+ mov x2, 0 // x2 = processor index
+
+// Find index in gProcessorIDs for current processor
+1:
+ ldr x3, [x1, x2, lsl #3] // x4 = gProcessorIDs + x2 * 8
+ cmp x3, #-1 // check if we've reached the end of gProcessorIDs
+ beq ProcessorNotFound
+ add x2, x2, 1 // x2++
+ cmp x0, x3 // if mpidr_el1 != gProcessorIDs[x] then loop
+ bne 1b
+
+// Calculate stack address
+ // x2 contains the index for the current processor plus 1
+ ldr x0, gApStacksBase
+ ldr x1, gApStackSize
+ mul x3, x2, x1 // x3 = (ProcessorIndex + 1) * gApStackSize
+ add sp, x0, x3 // sp = gApStacksBase + x3
+ mov x29, xzr
+ bl ApProcedure // doesn't return
+
+ProcessorNotFound:
+// Turn off the processor
+ MOV32 (w0, ARM_SMC_ID_PSCI_CPU_OFF)
+ smc #0
+ b .
--
2.30.2


[PATCH 0/2] Add support EFI_MP_SERVICES_PROTOCOL on AARCH64

Rebecca Cran <quic_rcran@...>
 

This is the rework of the patches I sent out in late 2021,
building on fixes done by Ard at
https://github.com/ardbiesheuvel/edk2/tree/armpkg-mpservicesdxe-refactor.

The most significant changes are the addition of code to enable
the MMU and caches on the APs, and changing MpServicesTest to
use commandline parameters to control its operation.

Rebecca Cran (2):
ArmPkg: implement EFI_MP_SERVICES_PROTOCOL based on PSCI calls
MdeModulePkg: Add new Application/MpServicesTest application

ArmPkg/ArmPkg.dsc | 1 +
MdeModulePkg/MdeModulePkg.dsc | 9 +
ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf | 55 +
MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf | 41 +
ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h | 351 ++++
MdeModulePkg/Application/MpServicesTest/Options.h | 39 +
ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c | 1774 ++++++++++++++++++++
MdeModulePkg/Application/MpServicesTest/MpServicesTest.c | 558 ++++++
MdeModulePkg/Application/MpServicesTest/Options.c | 215 +++
ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S | 57 +
10 files changed, 3100 insertions(+)
create mode 100644 ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf
create mode 100644 MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf
create mode 100644 ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h
create mode 100644 MdeModulePkg/Application/MpServicesTest/Options.h
create mode 100644 ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c
create mode 100644 MdeModulePkg/Application/MpServicesTest/MpServicesTest.c
create mode 100644 MdeModulePkg/Application/MpServicesTest/Options.c
create mode 100644 ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S

--
2.30.2


Re: [PATCH V4 2/2] OvmfPkg: Update CcProbeLib to DxeCcProbeLib

Lendacky, Thomas
 

On 8/26/22 18:07, Min Xu wrote:
From: Min M Xu <min.m.xu@...>
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3974
CcProbeLib once was designed to probe the Confidential Computing guest
type by checking the PcdOvmfWorkArea. But this memory is allocated with
either EfiACPIMemoryNVS or EfiBootServicesData. It cannot be accessed
after ExitBootService. Please see the detailed analysis in BZ#3974.
To fix this issue, CcProbeLib is redesigned as 2 implementation:
- SecPeiCcProbeLib
- DxeCcProbeLib
In SecPeiCcProbeLib we check the CC guest type by reading the
PcdOvmfWorkArea. Because it is used in SEC / PEI and we don't worry about
the issues in BZ#3974.
In DxeCcProbeLib we cache the GuestType in Ovmf work area in a variable.
After that the Guest type is returned with the cached value. So that we
don't need to worry about the access to Ovmf work area after
ExitBootService.
To gurantee the GuestType is cached, we read the value in both
s/gurantee/guarantee/

constructor and CcProbe. Because in some corner case, the constructor
may be called after CcProbe. For example in MdeModulePkg/Core/Dxe/DxeMain,
BaseDebugLibSerialPortConstructor is called before
DxeCcProbeLibConstructor. While CcProbe () is called in
BaseDebugLibSerialPortConstructor.
Is there a way to put some kind of ordering in place so that CcProbe's constructor is called before BaseDebugLibSerialPortConstructor?

The reason why we probe CC guest type in 2 different ways is the global
varialbe. Global variable cannot be used in SEC/PEI and CcProbe is called
s/varialbe/variable/

Thanks,
Tom

very frequently.
Cc: Gerd Hoffmann <kraxel@...>
Cc: Erdem Aktas <erdemaktas@...>
Cc: James Bottomley <jejb@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Tom Lendacky <thomas.lendacky@...>
Signed-off-by: Min Xu <min.m.xu@...>
---


Re: How to restrict HTTPS boot to a single address

Rafael Machado
 

Thanks Andrew / Sivaraman for the guidance. 
Definitely good places for me to start.

Thanks
Rafael


Em dom., 28 de ago. de 2022 às 08:25, Sivaraman Nainar <sivaramann@...> escreveu:

Hello Rafael.

 

HttpBootCheckUriScheme() in HttpBootDxe\HttpBootSupport.c should be the right place to filter the URI.

 

Please give a try.

 

-Siva

From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Rafael Machado via groups.io
Sent: Friday, August 26, 2022 7:46 PM
To: devel@edk2.groups.io
Subject: [EXTERNAL] [edk2-devel] How to restrict HTTPS boot to a single address

 

 

**CAUTION: The e-mail below is from an external source. Please exercise caution before opening attachments, clicking links, or following guidance.**

Hello everyone.

 

Quick question for the ones that understand better the HTTPBoot architecture at the edk2 structure.

 

Suppose I have to restrict HTTPS boot to accept only the download of images from a specific url.

For example, instead of allowing the download of images from any valid CA certificate address, I would like to restrict HTTPSBoot to allow only downloads from some specific domain I have.

 

Probably filtering some information, CN or something like that, from the url certificate.

 

What is the best way to do that?

In which driver/library should this logic be added?

 

Thanks

Rafael

-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.


Re: [PATCH] edk2-staging/RedfishClientPkg: Update Redfish converter lib

NickleLa <nickle@...>
 

Reviewed-by:  Nickle Wang <nickle@...>

Thanks,
Nickle

<abner.chang@...> 於 2022年8月28日 週日 晚上7:30寫道:

From: Abner Chang <abner.chang@...>

Temporary modified on the auto-generated lib to support
Redfish BIOS attributes. RedfishScemaToCStructure python
script needs to be updated for fixing this issue.

Signed-off-by: Abner Chang <abner.chang@...>
Cc: Yang Atom <Atom.Yang@...>
Cc: Nick Ramirez <nramirez@...>
Cc: Nickle Wang <nickle@...>
Cc: Igor Kulchytskyy <igork@...>
---
 .../ConverterLib/src/Bios/Bios.V1_0_9/Bios.V1_0_9.c  | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/RedfishClientPkg/ConverterLib/src/Bios/Bios.V1_0_9/Bios.V1_0_9.c b/RedfishClientPkg/ConverterLib/src/Bios/Bios.V1_0_9/Bios.V1_0_9.c
index d698321886..1ec97db891 100644
--- a/RedfishClientPkg/ConverterLib/src/Bios/Bios.V1_0_9/Bios.V1_0_9.c
+++ b/RedfishClientPkg/ConverterLib/src/Bios/Bios.V1_0_9/Bios.V1_0_9.c
@@ -367,17 +367,17 @@ Error:;
 }
 static RedfishCS_status CS_To_JSON_Attributes(json_t *CsJson, char *Key, RedfishBios_V1_0_9_Attributes_CS *CSPtr)
 {
-  json_t *CsParentJson;
+  //json_t *CsParentJson;

   if (CSPtr == NULL) {
     return RedfishCS_status_success;
   }

-  CsParentJson = CsJson;
-  CsJson = json_object();
-  if (CsJson == NULL) {
-    return RedfishCS_status_unsupported;
-  }
+  //CsParentJson = CsJson;
+  //CsJson = json_object();
+  //if (CsJson == NULL) {
+  //  return RedfishCS_status_unsupported;
+  //}

   // Check if this is RedfishCS_Type_CS_EmptyProp.
   CsEmptyPropLinkToJson(CsJson, Key, &CSPtr->Prop);
--
2.37.1.windows.1


Re: [PATCH] UsbNetworkPkg: add USB network devices support

RichardHo [何明忠]
 

Hi Pedro,

I will modify license and add more CC next.

Thanks,

Richard

 

From: Pedro Falcato <pedro.falcato@...>
Sent: 2022
819 11:56 PM
To: edk2-devel-groups-io <devel@edk2.groups.io>; RichardHo [
何明忠] <RichardHo@...>
Cc: TonyLo [
羅金松] <TonyLo@...>
Subject: [EXTERNAL] Re: [edk2-devel] [PATCH] UsbNetworkPkg: add USB network devices support

 

 

**CAUTION: The e-mail below is from an external source. Please exercise caution before opening attachments, clicking links, or following guidance.**

Also, I forgot, but you should CC the EDK2 stewards (they're the ones that are going to add the Reviewed-by:).

 

On Fri, Aug 19, 2022 at 4:54 PM Pedro Falcato <pedro.falcato@...> wrote:

Hi Richard,

 

What license is this code under? "Subject to AMI licensing agreement." does not sound like a valid OSS license.

 

Thanks,

Pedro

 

On Fri, Aug 19, 2022 at 10:32 AM RichardHo [何明忠] via groups.io <richardho=ami.com@groups.io> wrote:

UsbNetworkPkg provides network functions for USB ACM, USB NCM,
and USB RNDIS network device.

Signed-off-by: Richard Ho <richardho@...>
Reviewed-by: Tony Lo <tonylo@...>
---
 UsbNetworkPkg/Config/UsbNetworkPkg.inc.dsc    |    9 +
 .../Config/UsbNetworkPkgComponentsDxe.inc.dsc |   20 +
 .../Config/UsbNetworkPkgComponentsDxe.inc.fdf |   21 +
 .../Config/UsbNetworkPkgDefines.inc.dsc       |   23 +
 .../Include/Protocol/UsbEthernetProtocol.h    |  872 +++++++++
 UsbNetworkPkg/NetworkCommon/ComponentName.c   |  264 +++
 UsbNetworkPkg/NetworkCommon/DriverBinding.c   |  583 ++++++
 UsbNetworkPkg/NetworkCommon/DriverBinding.h   |  263 +++
 UsbNetworkPkg/NetworkCommon/NetworkCommon.inf |   43 +
 UsbNetworkPkg/NetworkCommon/PxeFunction.c     | 1734 +++++++++++++++++
 UsbNetworkPkg/ReadMe.md                       |   65 +
 UsbNetworkPkg/ReleaseNotes.md                 |   11 +
 UsbNetworkPkg/UsbCdcEcm/ComponentName.c       |  170 ++
 UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.c           |  504 +++++
 UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.h           |  211 ++
 UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf         |   41 +
 UsbNetworkPkg/UsbCdcEcm/UsbEcmFunction.c      |  861 ++++++++
 UsbNetworkPkg/UsbCdcNcm/ComponentName.c       |  170 ++
 UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.c           |  508 +++++
 UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.h           |  245 +++
 UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf         |   41 +
 UsbNetworkPkg/UsbCdcNcm/UsbNcmFunction.c      |  946 +++++++++
 UsbNetworkPkg/UsbNetworkPkg.dec               |   32 +
 UsbNetworkPkg/UsbRndis/ComponentName.c        |  172 ++
 UsbNetworkPkg/UsbRndis/UsbRndis.c             |  848 ++++++++
 UsbNetworkPkg/UsbRndis/UsbRndis.h             |  569 ++++++
 UsbNetworkPkg/UsbRndis/UsbRndis.inf           |   41 +
 UsbNetworkPkg/UsbRndis/UsbRndisFunction.c     | 1587 +++++++++++++++
 28 files changed, 10854 insertions(+)
 create mode 100644 UsbNetworkPkg/Config/UsbNetworkPkg.inc.dsc
 create mode 100644 UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.dsc
 create mode 100644 UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.fdf
 create mode 100644 UsbNetworkPkg/Config/UsbNetworkPkgDefines.inc.dsc
 create mode 100644 UsbNetworkPkg/Include/Protocol/UsbEthernetProtocol.h
 create mode 100644 UsbNetworkPkg/NetworkCommon/ComponentName.c
 create mode 100644 UsbNetworkPkg/NetworkCommon/DriverBinding.c
 create mode 100644 UsbNetworkPkg/NetworkCommon/DriverBinding.h
 create mode 100644 UsbNetworkPkg/NetworkCommon/NetworkCommon.inf
 create mode 100644 UsbNetworkPkg/NetworkCommon/PxeFunction.c
 create mode 100644 UsbNetworkPkg/ReadMe.md
 create mode 100644 UsbNetworkPkg/ReleaseNotes.md
 create mode 100644 UsbNetworkPkg/UsbCdcEcm/ComponentName.c
 create mode 100644 UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.c
 create mode 100644 UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.h
 create mode 100644 UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf
 create mode 100644 UsbNetworkPkg/UsbCdcEcm/UsbEcmFunction.c
 create mode 100644 UsbNetworkPkg/UsbCdcNcm/ComponentName.c
 create mode 100644 UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.c
 create mode 100644 UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.h
 create mode 100644 UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf
 create mode 100644 UsbNetworkPkg/UsbCdcNcm/UsbNcmFunction.c
 create mode 100644 UsbNetworkPkg/UsbNetworkPkg.dec
 create mode 100644 UsbNetworkPkg/UsbRndis/ComponentName.c
 create mode 100644 UsbNetworkPkg/UsbRndis/UsbRndis.c
 create mode 100644 UsbNetworkPkg/UsbRndis/UsbRndis.h
 create mode 100644 UsbNetworkPkg/UsbRndis/UsbRndis.inf
 create mode 100644 UsbNetworkPkg/UsbRndis/UsbRndisFunction.c

diff --git a/UsbNetworkPkg/Config/UsbNetworkPkg.inc.dsc b/UsbNetworkPkg/Config/UsbNetworkPkg.inc.dsc
new file mode 100644
index 0000000000..504680bc4b
--- /dev/null
+++ b/UsbNetworkPkg/Config/UsbNetworkPkg.inc.dsc
@@ -0,0 +1,9 @@
+## @file
+# Global DSC definitions to be included into project DSC file.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+[Components.X64]
+!include UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.dsc
diff --git a/UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.dsc b/UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.dsc
new file mode 100644
index 0000000000..ae2727b68c
--- /dev/null
+++ b/UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.dsc
@@ -0,0 +1,20 @@
+## @file
+# List of Core Components.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+  UsbNetworkPkg/NetworkCommon/NetworkCommon.inf
+
+!if gUsbNetworkPkgTokenSpaceGuid.UsbCdcEcmSupport
+  UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf
+!endif
+
+!if gUsbNetworkPkgTokenSpaceGuid.UsbCdcNcmSupport
+  UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf
+!endif
+
+!if gUsbNetworkPkgTokenSpaceGuid.UsbRndisSupport
+  UsbNetworkPkg/UsbRndis/UsbRndis.inf
+!endif
diff --git a/UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.fdf b/UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.fdf
new file mode 100644
index 0000000000..6a611e359c
--- /dev/null
+++ b/UsbNetworkPkg/Config/UsbNetworkPkgComponentsDxe.inc.fdf
@@ -0,0 +1,21 @@
+## @file
+# List of Core Components.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+  INF UsbNetworkPkg/NetworkCommon/NetworkCommon.inf
+
+!if gUsbNetworkPkgTokenSpaceGuid.UsbCdcEcmSupport
+  INF UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf
+!endif
+
+!if gUsbNetworkPkgTokenSpaceGuid.UsbCdcNcmSupport
+  INF UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf
+!endif
+
+!if gUsbNetworkPkgTokenSpaceGuid.UsbRndisSupport
+  INF UsbNetworkPkg/UsbRndis/UsbRndis.inf
+!endif
+
\ No newline at end of file
diff --git a/UsbNetworkPkg/Config/UsbNetworkPkgDefines.inc.dsc b/UsbNetworkPkg/Config/UsbNetworkPkgDefines.inc.dsc
new file mode 100644
index 0000000000..ef663bf253
--- /dev/null
+++ b/UsbNetworkPkg/Config/UsbNetworkPkgDefines.inc.dsc
@@ -0,0 +1,23 @@
+## @file
+# Global switches enable/disable project features.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+[Defines]
+!if "IA32" in $(ARCH) && "X64" in $(ARCH)
+  DEFINE PEI=IA32
+  DEFINE DXE=X64
+!else
+  DEFINE PEI=COMMON
+  DEFINE DXE=COMMON
+!endif
+
+[Packages]
+  UsbNetworkPkg/UsbNetworkPkg.dec
+
+[PcdsFeatureFlag]
+  gUsbNetworkPkgTokenSpaceGuid.UsbCdcEcmSupport|FALSE
+  gUsbNetworkPkgTokenSpaceGuid.UsbCdcNcmSupport|FALSE
+  gUsbNetworkPkgTokenSpaceGuid.UsbRndisSupport|TRUE
diff --git a/UsbNetworkPkg/Include/Protocol/UsbEthernetProtocol.h b/UsbNetworkPkg/Include/Protocol/UsbEthernetProtocol.h
new file mode 100644
index 0000000000..d60e83f723
--- /dev/null
+++ b/UsbNetworkPkg/Include/Protocol/UsbEthernetProtocol.h
@@ -0,0 +1,872 @@
+/** @file

+  Header file contains code for USB Ethernet Protocol

+  definitions

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#ifndef _USB_ETHERNET_PROTOCOL_H

+#define _USB_ETHERNET_PROTOCOL_H

+

+#define USB_ETHERNET_PROTOCOL_GUID \

+    {0x8d8969cc, 0xfeb0, 0x4303, {0xb2, 0x1a, 0x1f, 0x11, 0x6f, 0x38, 0x56, 0x43}}

+

+typedef struct _USB_ETHERNET_PROTOCOL USB_ETHERNET_PROTOCOL;

+

+#define USB_CDC_CLASS          0x02

+#define USB_CDC_ACM_SUBCLASS   0x02

+#define USB_CDC_ECM_SUBCLASS   0x06

+#define USB_CDC_NCM_SUBCLASS   0x0D

+#define USB_CDC_DATA_CLASS     0x0A

+#define USB_CDC_DATA_SUBCLASS  0x00

+#define USB_NO_CLASS_PROTOCOL  0x00

+#define USB_NCM_NTB_PROTOCOL   0x01

+#define USB_VENDOR_PROTOCOL    0xFF

+

+// Type Values for the DescriptorType Field

+#define CS_INTERFACE  0x24

+#define CS_ENDPOINT   0x25

+

+// Descriptor SubType in Functional Descriptors

+#define HEADER_FUN_DESCRIPTOR    0x00

+#define UNION_FUN_DESCRIPTOR     0x06

+#define ETHERNET_FUN_DESCRIPTOR  0x0F

+

+#define MAX_LAN_INTERFACE  0x10

+

+// Table 20: Class-Specific Notification Codes

+#define USB_CDC_NETWORK_CONNECTION  0x00

+

+// 6.3.1 NetworkConnection

+#define NETWORK_CONNECTED   0x01

+#define NETWORK_DISCONNECT  0x00

+

+// USB Header functional Descriptor

+typedef struct {

+  UINT8     FunctionLength;

+  UINT8     DescriptorType;

+  UINT8     DescriptorSubtype;

+  UINT16    BcdCdc;

+} USB_HEADER_FUN_DESCRIPTOR;

+

+// USB Union Functional Descriptor

+typedef struct {

+  UINT8    FunctionLength;

+  UINT8    DescriptorType;

+  UINT8    DescriptorSubtype;

+  UINT8    MasterInterface;

+  UINT8    SlaveInterface;

+} USB_UNION_FUN_DESCRIPTOR;

+

+// USB Ethernet Functional Descriptor

+typedef struct {

+  UINT8     FunctionLength;

+  UINT8     DescriptorType;

+  UINT8     DescriptorSubtype;

+  UINT8     MacAddress;

+  UINT32    EthernetStatistics;

+  UINT16    MaxSegmentSize;

+  UINT16    NumberMcFilters;

+  UINT8     NumberPowerFilters;

+} USB_ETHERNET_FUN_DESCRIPTOR;

+

+typedef struct {

+  UINT32    UsBitRate;

+  UINT32    DsBitRate;

+} USB_CONNECT_SPEED_CHANGE;

+

+// Request Type Codes for USB Ethernet

+#define USB_ETHERNET_GET_REQ_TYPE  0xA1

+#define USB_ETHRTNET_SET_REQ_TYPE  0x21

+

+// Class-Specific Request Codes for Ethernet subclass

+// USB ECM 1.2 specification, Section 6.2

+#define SET_ETH_MULTICAST_FILTERS_REQ                0x40

+#define SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ  0x41

+#define GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ  0x42

+#define SET_ETH_PACKET_FILTER_REQ                    0x43

+#define GET_ETH_STATISTIC_REQ                        0x44

+

+// USB ECM command request length

+#define USB_ETH_POWER_FILTER_LENGTH   2 // Section 6.2.3

+#define USB_ETH_PACKET_FILTER_LENGTH  0 // Section 6.2.4

+#define USB_ETH_STATISTIC             4 // Section 6.2.5

+

+// USB Ethernet Packet Filter Bitmap

+// USB ECM 1.2 specification, Section 6.2.4

+#define USB_ETH_PACKET_TYPE_PROMISCUOUS    BIT0

+#define USB_ETH_PACKET_TYPE_ALL_MULTICAST  BIT1

+#define USB_ETH_PACKET_TYPE_DIRECTED       BIT2

+#define USB_ETH_PACKET_TYPE_BROADCAST      BIT3

+#define USB_ETH_PACKET_TYPE_MULTICAST      BIT4

+

+// USB Ethernet Statistics Feature Selector Codes

+// USB ECM 1.2 specification, Section 6.2.5

+#define USB_ETH_XMIT_OK                 0x01

+#define USB_ETH_RCV_OK                  0x02

+#define USB_ETH_XMIT_ERROR              0x03

+#define USB_ETH_RCV_ERROR               0x04

+#define USB_ETH_RCV_NO_BUFFER           0x05

+#define USB_ETH_DIRECTED_BYTES_XMIT     0x06

+#define USB_ETH_DIRECTED_FRAMES_XMIT    0x07

+#define USB_ETH_MULTICAST_BYTES_XMIT    0x08

+#define USB_ETH_MULTICAST_FRAMES_XMIT   0x09

+#define USB_ETH_BROADCAST_BYTES_XMIT    0x0A

+#define USB_ETH_BROADCAST_FRAMES_XMIT   0x0B

+#define USB_ETH_DIRECTED_BYTES_RCV      0x0C

+#define USB_ETH_DIRECTED_FRAMES_RCV     0x0D

+#define USB_ETH_MULTICAST_BYTES_RCV     0x0E

+#define USB_ETH_MULTICAST_FRAMES_RCV    0x0F

+#define USB_ETH_BROADCAST_BYTES_RCV     0x10

+#define USB_ETH_BROADCAST_FRAMES_RCV    0x11

+#define USB_ETH_RCV_CRC_ERROR           0x12

+#define USB_ETH_TRANSMIT_QUEUE_LENGTH   0x13

+#define USB_ETH_RCV_ERROR_ALIGNMENT     0x14

+#define USB_ETH_XMIT_ONE_COLLISION      0x15

+#define USB_ETH_XMIT_MORE_COLLISIONS    0x16

+#define USB_ETH_XMIT_DEFERRED           0x17

+#define USB_ETH_XMIT_MAX_COLLISIONS     0x18

+#define USB_ETH_RCV_OVERRUN             0x19

+#define USB_ETH_XMIT_UNDERRUN           0x1A

+#define USB_ETH_XMIT_HEARTBEAT_FAILURE  0x1B

+#define USB_ETH_XMIT_TIMES_CRS_LOST     0x1C

+#define USB_ETH_XMIT_LATE_COLLISIONS    0x1D

+

+// NIC Information

+typedef struct {

+  UINT32                    Signature;

+  USB_ETHERNET_PROTOCOL     *UsbEth;

+  UINT16                    InterrupOpFlag;

+  UINT64                    MappedAddr;

+  PXE_MAC_ADDR              McastList[MAX_MCAST_ADDRESS_CNT];

+  UINT8                     McastCount;

+  UINT64                    MediaHeader[MAX_XMIT_BUFFERS];

+  UINT8                     TxBufferCount;

+  UINT16                    State;

+  BOOLEAN                   CanTransmit;

+  BOOLEAN                   CanReceive;

+  UINT16                    ReceiveStatus;

+  UINT8                     RxFilter;

+  UINT32                    RxFrame;

+  UINT32                    TxFrame;

+  UINT16                    NetworkConnect;

+  UINT8                     CableDetect;

+  UINT16                    MaxSegmentSize;

+  EFI_MAC_ADDRESS           MacAddr;

+  PXE_CPB_START_31          PxeStart;

+  PXE_CPB_INITIALIZE        PxeInit;

+  UINT8                     PermNodeAddress[PXE_MAC_LENGTH];

+  UINT8                     CurrentNodeAddress[PXE_MAC_LENGTH];

+  UINT8                     BroadcastNodeAddress[PXE_MAC_LENGTH];

+  EFI_USB_DEVICE_REQUEST    Request;

+} NIC_DATA;

+

+#define NIC_DATA_SIGNATURE  SIGNATURE_32('n', 'i', 'c', 'd')

+#define NIC_DATA_FROM_USB_ETHERNET_PROTOCOL(a)  CR (a, NIC_DATA, UsbEth, NIC_DATA_SIGNATURE)

+

+/**

+  This command is used to determine the operational state of the UNDI.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_GET_STATE)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to change the UNDI operational state from stopped to started.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_START)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to change the UNDI operational state from started to stopped.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_STOP)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to retrieve initialization information that is

+  needed by drivers and applications to initialized UNDI.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_GET_INIT_INFO)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to retrieve configuration information about

+  the NIC being controlled by the UNDI.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_GET_CONFIG_INFO)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command resets the network adapter and initializes UNDI using

+  the parameters supplied in the CPB.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_INITIALIZE)(

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+/**

+  This command resets the network adapter and reinitializes the UNDI

+  with the same parameters provided in the Initialize command.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_RESET)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  The Shutdown command resets the network adapter and leaves it in a

+  safe state for another driver to initialize.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_SHUTDOWN)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  The Interrupt Enables command can be used to read and/or change

+  the current external interrupt enable settings.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_INTERRUPT_ENABLE)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to read and change receive filters and,

+  if supported, read and change the multicast MAC address filter list.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_RECEIVE_FILTER)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to get current station and broadcast MAC addresses

+  and, if supported, to change the current station MAC address.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_STATION_ADDRESS)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to read and clear the NIC traffic statistics.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_STATISTICS)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  Translate a multicast IPv4 or IPv6 address to a multicast MAC address.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_MCAST_IPTOMAC)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to read and write (if supported by NIC H/W)

+  nonvolatile storage on the NIC.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_NV_DATA)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command returns the current interrupt status and/or the

+  transmitted buffer addresses and the current media status.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_GET_STATUS)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command is used to fill the media header(s) in transmit packet(s).

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_FILL_HEADER)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  The Transmit command is used to place a packet into the transmit queue.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_TRANSMIT)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  When the network adapter has received a frame, this command is used

+  to copy the frame into driver/application storage.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_UNDI_RECEIVE)(

+  IN  PXE_CDB     *Cdb,

+  IN  NIC_DATA    *Nic

+  );

+

+/**

+  This command resets the network adapter and initializes UNDI using

+  the parameters supplied in the CPB.

+

+  @param[in]      Cdb  A pointer to the command descriptor block.

+  @param[in, out] Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_INITIALIZE)(

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  );

+

+/**

+  This command is used to read and clear the NIC traffic statistics.

+

+  @param[in]  Nic     A pointer to the Network interface controller data.

+  @param[in]  DbAddr  Data Block Address.

+  @param[in]  DbSize  Data Block Size.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_STATISTICS)(

+  IN  NIC_DATA    *Nic,

+  IN  UINT64      DbAddr,

+  IN  UINT16      DbSize

+  );

+

+/**

+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk in.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in, out] Packet        A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] PacketLength  A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.

+  @retval EFI_INVALID_PARAMETE  One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.

+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_RECEIVE)(

+  IN     PXE_CDB               *Cdb,

+  IN     USB_ETHERNET_PROTOCOL *This,

+  IN OUT VOID                  *Packet,

+  IN OUT UINTN                 *PacketLength

+  );

+

+/**

+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk out.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in, out] Packet        A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] PacketLength  A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.

+  @retval EFI_INVALID_PARAMETE  One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.

+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_TRANSMIT)(

+  IN     PXE_CDB               *Cdb,

+  IN     USB_ETHERNET_PROTOCOL *This,

+  IN OUT VOID                  *Packet,

+  IN OUT UINTN                 *PacketLength

+  );

+

+/**

+  This function is used to manage a USB device with an interrupt transfer pipe.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  IsNewTransfer     If TRUE, a new transfer will be submitted to USB controller. If

+                                FALSE, the interrupt transfer is deleted from the device's interrupt

+                                transfer queue.

+  @param[in]  PollingInterval   Indicates the periodic rate, in milliseconds, that the transfer is to be

+                                executed.This parameter is required when IsNewTransfer is TRUE. The

+                                value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.

+                                The units are in milliseconds.

+  @param[in]  Request           A pointer to the EFI_USB_DEVICE_REQUEST data.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_INTERRUPT)(

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Request

+  );

+

+/**

+  Retrieves the USB Ethernet Mac Address.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] MacAddress    A pointer to the caller allocated USB Ethernet Mac Address.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_GET_ETH_MAC_ADDRESS)(

+  IN  USB_ETHERNET_PROTOCOL *This,

+  OUT EFI_MAC_ADDRESS       *MacAddress

+  );

+

+/**

+  Retrieves the USB Ethernet Bulk transfer data size.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] BulkSize      A pointer to the Bulk transfer data size.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETH_MAX_BULK_SIZE)(

+  IN  USB_ETHERNET_PROTOCOL *This,

+  OUT UINTN                 *BulkSize

+  );

+

+/**

+  Retrieves the USB Header functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_HEADER_FUNCTIONAL_DESCRIPTOR)(

+  IN USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR *UsbHeaderFunDescriptor

+  );

+

+/**

+  Retrieves the USB Union functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbUnionFunDescriptor  A pointer to the caller allocated USB Union Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Union Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbUnionFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Union Functional descriptor was not found.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_UNION_FUNCTIONAL_DESCRIPTOR)(

+  IN USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR *UsbUnionFunDescriptor

+  );

+

+/**

+  Retrieves the USB Ethernet functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbEthFunDescriptor    A pointer to the caller allocated USB Ethernet Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Ethernet Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbEthFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Ethernet Functional descriptor was not found.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_FUNCTIONAL_DESCRIPTOR)(

+  IN USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR *UsbEthFunDescriptor

+  );

+

+/**

+  This request sets the Ethernet device multicast filters as specified in the

+  sequential list of 48 bit Ethernet multicast addresses.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  Number of filters.

+  @param[in]  McastAddr              A pointer to the value of the multicast addresses.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_SET_ETH_MULTICAST_FILTERS)(

+  IN USB_ETHERNET_PROTOCOL *This,

+  IN UINT16                Value,

+  IN VOID                  *McastAddr

+  );

+

+/**

+  This request sets up the specified Ethernet power management pattern filter as

+  described in the data structure.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  Number of filters.

+  @param[in]  Length                 Size of the power management pattern filter data.

+  @param[in]  PatternFilter          A pointer to the power management pattern filter structure.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_SET_ETH_POWER_MANAGE_PATTERN_FILTER)(

+  IN USB_ETHERNET_PROTOCOL *This,

+  IN UINT16                Value,

+  IN UINT16                Length,

+  IN VOID                  *PatternFilter

+  );

+

+/**

+  This request retrieves the status of the specified Ethernet power management

+  pattern filter from the device.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  The filter number.

+  @param[out] PatternActive          A pointer to the pattern active boolean.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_GET_ETH_POWER_MANAGE_PATTERN_FILTER)(

+  IN   USB_ETHERNET_PROTOCOL *This,

+  IN   UINT16                Value,

+  OUT  BOOLEAN               *PatternActive

+  );

+

+/**

+  This request is used to configure device Ethernet packet filter settings.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value             Packet Filter Bitmap.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_SET_ETH_PACKET_FILTER)(

+  IN USB_ETHERNET_PROTOCOL *This,

+  IN UINT16                Value

+  );

+

+/**

+  This request is used to retrieve a statistic based on the feature selector.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  FeatureSelector       Value of the feature selector.

+  @param[out] Statistic             A pointer to the 32 bit unsigned integer.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+**/

+typedef

+EFI_STATUS

+(EFIAPI *USB_ETHERNET_GET_ETH_STATISTIC)(

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 FeatureSelector,

+  OUT VOID                   *Statistic

+  );

+

+typedef struct {

+  USB_ETHERNET_UNDI_GET_STATE           UsbEthUndiGetState;

+  USB_ETHERNET_UNDI_START               UsbEthUndiStart;

+  USB_ETHERNET_UNDI_STOP                UsbEthUndiStop;

+  USB_ETHERNET_UNDI_GET_INIT_INFO       UsbEthUndiGetInitInfo;

+  USB_ETHERNET_UNDI_GET_CONFIG_INFO     UsbEthUndiGetConfigInfo;

+  USB_ETHERNET_UNDI_INITIALIZE          UsbEthUndiInitialize;

+  USB_ETHERNET_UNDI_RESET               UsbEthUndiReset;

+  USB_ETHERNET_UNDI_SHUTDOWN            UsbEthUndiShutdown;

+  USB_ETHERNET_UNDI_INTERRUPT_ENABLE    UsbEthUndiInterruptEnable;

+  USB_ETHERNET_UNDI_RECEIVE_FILTER      UsbEthUndiReceiveFilter;

+  USB_ETHERNET_UNDI_STATION_ADDRESS     UsbEthUndiStationAddress;

+  USB_ETHERNET_UNDI_STATISTICS          UsbEthUndiStatistics;

+  USB_ETHERNET_UNDI_MCAST_IPTOMAC       UsbEthUndiMcastIp2Mac;

+  USB_ETHERNET_UNDI_NV_DATA             UsbEthUndiNvData;

+  USB_ETHERNET_UNDI_GET_STATUS          UsbEthUndiGetStatus;

+  USB_ETHERNET_UNDI_FILL_HEADER         UsbEthUndiFillHeader;

+  USB_ETHERNET_UNDI_TRANSMIT            UsbEthUndiTransmit;

+  USB_ETHERNET_UNDI_RECEIVE             UsbEthUndiReceive;

+} USB_ETHERNET_UNDI;

+

+// The USB_ETHERNET_PROTOCOL provides some basic USB Ethernet device relevant

+// descriptor and specific requests.

+struct _USB_ETHERNET_PROTOCOL {

+  USB_ETHERNET_UNDI                                   UsbEthUndi;

+  // for calling the UNDI child functions

+  USB_ETHERNET_INITIALIZE                             UsbEthInitialize;

+  USB_ETHERNET_STATISTICS                             UsbEthStatistics;

+  USB_ETHERNET_RECEIVE                                UsbEthReceive;

+  USB_ETHERNET_TRANSMIT                               UsbEthTransmit;

+  USB_ETHERNET_INTERRUPT                              UsbEthInterrupt;

+  USB_GET_ETH_MAC_ADDRESS                             UsbEthMacAddress;

+  USB_ETH_MAX_BULK_SIZE                               UsbEthMaxBulkSize;

+  USB_HEADER_FUNCTIONAL_DESCRIPTOR                    UsbHeaderFunDescriptor;

+  USB_UNION_FUNCTIONAL_DESCRIPTOR                     UsbUnionFunDescriptor;

+  USB_ETHERNET_FUNCTIONAL_DESCRIPTOR                  UsbEthFunDescriptor;

+  USB_ETHERNET_SET_ETH_MULTICAST_FILTERS              SetUsbEthMcastFilter;

+  USB_ETHERNET_SET_ETH_POWER_MANAGE_PATTERN_FILTER    SetUsbEthPowerPatternFilter;

+  USB_ETHERNET_GET_ETH_POWER_MANAGE_PATTERN_FILTER    GetUsbEthPoewrPatternFilter;

+  USB_ETHERNET_SET_ETH_PACKET_FILTER                  SetUsbEthPacketFilter;

+  USB_ETHERNET_GET_ETH_STATISTIC                      GetUsbEthStatistic;

+};

+

+#endif

diff --git a/UsbNetworkPkg/NetworkCommon/ComponentName.c b/UsbNetworkPkg/NetworkCommon/ComponentName.c
new file mode 100644
index 0000000000..2697bf5083
--- /dev/null
+++ b/UsbNetworkPkg/NetworkCommon/ComponentName.c
@@ -0,0 +1,264 @@
+/** @file

+  This file contains code for USB network common driver

+  component name definitions

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "DriverBinding.h"

+

+extern EFI_DRIVER_BINDING_PROTOCOL  gNetworkCommonDriverBinding;

+extern EFI_GUID                     gUsbEthProtocolGuid;

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gNetworkCommonDriverNameTable[] = {

+  {

+    "eng;en",

+    L"Network Common Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  *gNetworkCommonControllerNameTable = NULL;

+

+EFI_STATUS

+EFIAPI

+NetworkCommonComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+EFI_STATUS

+EFIAPI

+NetworkCommonComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  );

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gNetworkCommonComponentName = {

+  NetworkCommonComponentNameGetDriverName,

+  NetworkCommonComponentNameGetControllerName,

+  "eng"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gNetworkCommonComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)NetworkCommonComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)NetworkCommonComponentNameGetControllerName,

+  "en"

+};

+

+/**

+  Retrieves a Unicode string that is the user readable name of the driver.

+

+  This function retrieves the user readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+  @param[out] DriverName        A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+NetworkCommonComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           gNetworkCommonDriverNameTable,

+           DriverName,

+           (BOOLEAN)(This == &gNetworkCommonComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Controller        The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+  @param[out] ControllerName    A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+NetworkCommonComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   Controller,

+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  )

+{

+  EFI_STATUS                 Status;

+  CHAR16                     *HandleName;

+  EFI_USB_IO_PROTOCOL        *UsbIo = NULL;

+  EFI_USB_DEVICE_DESCRIPTOR  DevDesc;

+

+  if (!Language || !ControllerName) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (ChildHandle == NULL) {

+    return EFI_UNSUPPORTED;

+  }

+

+  //

+  // Make sure this driver is currently managing ControllerHandle

+  //

+  Status = EfiTestManagedDevice (

+             Controller,

+             gNetworkCommonDriverBinding.DriverBindingHandle,

+             &gUsbEthProtocolGuid

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Make sure this driver produced ChildHandle

+  //

+  Status = EfiTestChildHandle (

+             Controller,

+             ChildHandle,

+             &gUsbEthProtocolGuid

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->HandleProtocol (Controller, &gEfiUsbIoProtocolGuid, (VOID **)&UsbIo);

+

+  if (!EFI_ERROR (Status)) {

+    Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Status = UsbIo->UsbGetStringDescriptor (UsbIo, 0x409, DevDesc.StrManufacturer, &HandleName);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    *ControllerName = HandleName;

+

+    if (gNetworkCommonControllerNameTable != NULL) {

+      FreeUnicodeStringTable (gNetworkCommonControllerNameTable);

+      gNetworkCommonControllerNameTable = NULL;

+    }

+

+    Status = AddUnicodeString2 (

+               "eng",

+               gNetworkCommonComponentName.SupportedLanguages,

+               &gNetworkCommonControllerNameTable,

+               HandleName,

+               TRUE

+               );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Status = AddUnicodeString2 (

+               "en",

+               gNetworkCommonComponentName2.SupportedLanguages,

+               &gNetworkCommonControllerNameTable,

+               HandleName,

+               FALSE

+               );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    return LookupUnicodeString2 (

+             Language,

+             This->SupportedLanguages,

+             gNetworkCommonControllerNameTable,

+             ControllerName,

+             (BOOLEAN)(This == &gNetworkCommonComponentName)

+             );

+  }

+

+  return EFI_UNSUPPORTED;

+}

diff --git a/UsbNetworkPkg/NetworkCommon/DriverBinding.c b/UsbNetworkPkg/NetworkCommon/DriverBinding.c
new file mode 100644
index 0000000000..18b2daa7a8
--- /dev/null
+++ b/UsbNetworkPkg/NetworkCommon/DriverBinding.c
@@ -0,0 +1,583 @@
+/** @file

+  This file contains code for USB network binding driver

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "DriverBinding.h"

+

+PXE_SW_UNDI  *gPxe = NULL;

+NIC_DEVICE   *gLanDeviceList[MAX_LAN_INTERFACE];

+

+EFI_GUID  gUsbEthProtocolGuid = USB_ETHERNET_PROTOCOL_GUID;

+

+EFI_DRIVER_BINDING_PROTOCOL  gNetworkCommonDriverBinding = {

+  NetworkCommonSupported,

+  NetworkCommonDriverStart,

+  NetworkCommonDriverStop,

+  NETWORK_COMMON_DRIVER_VERSION,

+  NULL,

+  NULL

+};

+

+/**

+  Create MAC Device Path

+

+  @param[in, out] Dev             A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in]      BaseDev         A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in]      Nic             A pointer to the Network interface controller data.

+

+  @retval EFI_OUT_OF_RESOURCES    The device path could not be created successfully due to a lack of resources.

+  @retval EFI_SUCCESS             MAC device path created successfully.

+

+**/

+EFI_STATUS

+CreateMacDevicePath (

+  IN OUT  EFI_DEVICE_PATH_PROTOCOL  **Dev,

+  IN      EFI_DEVICE_PATH_PROTOCOL  *BaseDev,

+  IN      NIC_DATA                  *Nic

+  )

+{

+  EFI_STATUS                Status;

+  MAC_ADDR_DEVICE_PATH      MacAddrNode;

+  EFI_DEVICE_PATH_PROTOCOL  *EndNode;

+  UINT8                     *DevicePath;

+  UINT16                    TotalLength;

+  UINT16                    BaseLength;

+

+  ZeroMem (&MacAddrNode, sizeof (MAC_ADDR_DEVICE_PATH));

+  CopyMem (&MacAddrNode.MacAddress, &Nic->MacAddr, sizeof (EFI_MAC_ADDRESS));

+

+  MacAddrNode.Header.Type      = MESSAGING_DEVICE_PATH;

+  MacAddrNode.Header.SubType   = MSG_MAC_ADDR_DP;

+  MacAddrNode.Header.Length[0] = (UINT8)sizeof (MacAddrNode);

+  MacAddrNode.Header.Length[1] = 0;

+

+  EndNode = BaseDev;

+

+  while (!IsDevicePathEnd (EndNode)) {

+    EndNode = NextDevicePathNode (EndNode);

+  }

+

+  BaseLength  = (UINT16)((UINTN)(EndNode) - (UINTN)(BaseDev));

+  TotalLength = (UINT16)(BaseLength + sizeof (MacAddrNode) + sizeof (EFI_DEVICE_PATH_PROTOCOL));

+

+  Status = gBS->AllocatePool (EfiBootServicesData, TotalLength, (VOID **)&DevicePath);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  *Dev = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;

+  CopyMem (DevicePath, (CHAR8 *)BaseDev, BaseLength);

+  DevicePath += BaseLength;

+  CopyMem (DevicePath, (CHAR8 *)&MacAddrNode, sizeof (MacAddrNode));

+  DevicePath += sizeof (MacAddrNode);

+  CopyMem (DevicePath, (CHAR8 *)EndNode, sizeof (EFI_DEVICE_PATH_PROTOCOL));

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Network Common Driver Binding Support.

+

+  @param[in]  This                    Protocol instance pointer.

+  @param[in]  ControllerHandle        Handle of device to test.

+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child

+                                      device to start.

+

+  @retval EFI_SUCCESS                 This driver supports this device.

+  @retval EFI_ALREADY_STARTED         This driver is already running on this device.

+  @retval other                       This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+NetworkCommonSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS             Status;

+  USB_ETHERNET_PROTOCOL  *UsbEth;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  (VOID **)&UsbEth,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  gBS->CloseProtocol (

+         ControllerHandle,

+         &gUsbEthProtocolGuid,

+         This->DriverBindingHandle,

+         ControllerHandle

+         );

+  return Status;

+}

+

+/**

+  Network Common Driver Binding Start.

+

+  @param[in]  This                    Protocol instance pointer.

+  @param[in]  ControllerHandle        Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child

+                                      device to start.

+

+  @retval EFI_SUCCESS                 This driver is added to ControllerHandle

+  @retval EFI_DEVICE_ERROR            This driver could not be started due to a device error

+  @retval EFI_OUT_OF_RESOURCES        The driver could not install successfully due to a lack of resources.

+  @retval other                       This driver does not support this device

+

+**/

+EFI_STATUS

+EFIAPI

+NetworkCommonDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS                Status;

+  EFI_DEVICE_PATH_PROTOCOL  *UsbEthPath;

+  USB_ETHERNET_PROTOCOL     *UsbEth;

+  EFI_MAC_ADDRESS           MacAddress;

+  UINTN                     BulkDataSize;

+  NIC_DEVICE                *NicDevice;

+  UINT8                     *TmpPxePointer = NULL;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  (VOID **)&UsbEth,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **)&UsbEthPath,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return Status;

+  }

+

+  ZeroMem (&MacAddress, sizeof (EFI_MAC_ADDRESS));

+

+  Status = UsbEth->UsbEthMacAddress (UsbEth, &MacAddress);

+  ASSERT_EFI_ERROR (Status);

+  Status = UsbEth->UsbEthMaxBulkSize (UsbEth, &BulkDataSize);

+

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiDevicePathProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return Status;

+  }

+

+  NicDevice = AllocateZeroPool (sizeof (NIC_DEVICE));

+  if (!NicDevice) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiDevicePathProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  // for alignment adjustment

+  if (gPxe == NULL) {

+    TmpPxePointer = AllocateZeroPool (sizeof (PXE_SW_UNDI) + 16);

+    if (!TmpPxePointer) {

+      if (NicDevice != NULL) {

+        gBS->FreePool (NicDevice);

+      }

+

+      gBS->CloseProtocol (

+             ControllerHandle,

+             &gEfiDevicePathProtocolGuid,

+             This->DriverBindingHandle,

+             ControllerHandle

+             );

+      gBS->CloseProtocol (

+             ControllerHandle,

+             &gUsbEthProtocolGuid,

+             This->DriverBindingHandle,

+             ControllerHandle

+             );

+

+      return EFI_OUT_OF_RESOURCES;

+    } else {

+      // check for paragraph alignment here

+      if (((UINTN)TmpPxePointer & 0x0F) != 0) {

+        gPxe = (PXE_SW_UNDI *)(TmpPxePointer + 8);

+      } else {

+        gPxe = (PXE_SW_UNDI *)TmpPxePointer;

+      }

+

+      if (!gPxe) {

+        if (NicDevice != NULL) {

+          gBS->FreePool (NicDevice);

+        }

+

+        gBS->CloseProtocol (

+               ControllerHandle,

+               &gEfiDevicePathProtocolGuid,

+               This->DriverBindingHandle,

+               ControllerHandle

+               );

+        gBS->CloseProtocol (

+               ControllerHandle,

+               &gUsbEthProtocolGuid,

+               This->DriverBindingHandle,

+               ControllerHandle

+               );

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      PxeStructInit (gPxe);

+    }

+  }

+

+  NicDevice->NiiProtocol.Id    = (UINT64)(UINTN)(gPxe);

+  NicDevice->NiiProtocol.IfNum = gPxe->IFcnt | gPxe->IFcntExt << 8;

+

+  UpdateNicNum (&NicDevice->NicInfo, gPxe);

+

+  NicDevice->NicInfo.Signature = NIC_DATA_SIGNATURE;

+

+  NicDevice->NicInfo.UsbEth         = UsbEth;

+  NicDevice->NicInfo.MaxSegmentSize = (UINT16)BulkDataSize;

+  NicDevice->NicInfo.CableDetect    = 0;

+

+  CopyMem ((CHAR8 *)&(NicDevice->NicInfo.MacAddr), (CHAR8 *)&MacAddress, sizeof (MacAddress));

+

+  NicDevice->NicInfo.TxBufferCount = 0;

+

+  if (NicDevice->NiiProtocol.IfNum < MAX_LAN_INTERFACE) {

+    gLanDeviceList[NicDevice->NiiProtocol.IfNum] = NicDevice;

+  } else {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiDevicePathProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+

+    if (TmpPxePointer != NULL) {

+      gBS->FreePool (TmpPxePointer);

+    }

+

+    if (NicDevice != NULL) {

+      gBS->FreePool (NicDevice);

+    }

+

+    return EFI_DEVICE_ERROR;

+  }

+

+  Status = CreateMacDevicePath (

+             &NicDevice->DevPath,

+             UsbEthPath,

+             &NicDevice->NicInfo

+             );

+

+  if (EFI_ERROR (Status)) {

+    UpdateNicNum (NULL, gPxe);

+    if (TmpPxePointer != NULL) {

+      gBS->FreePool (TmpPxePointer);

+    }

+  }

+

+  NicDevice->Signature                 = UNDI_DEV_SIGNATURE;

+  NicDevice->NiiProtocol.Revision      = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION;

+  NicDevice->NiiProtocol.Type          = EfiNetworkInterfaceUndi;

+  NicDevice->NiiProtocol.MajorVer      = PXE_ROMID_MAJORVER;

+  NicDevice->NiiProtocol.MinorVer      = PXE_ROMID_MINORVER;

+  NicDevice->NiiProtocol.ImageSize     = 0;

+  NicDevice->NiiProtocol.ImageAddr     = 0;

+  NicDevice->NiiProtocol.Ipv6Supported = TRUE;

+

+  NicDevice->NiiProtocol.StringId[0] = 'U';

+  NicDevice->NiiProtocol.StringId[1] = 'N';

+  NicDevice->NiiProtocol.StringId[2] = 'D';

+  NicDevice->NiiProtocol.StringId[3] = 'I';

+  NicDevice->DeviceHandle            = NULL;

+

+  ZeroMem (&NicDevice->NicInfo.Request, sizeof (EFI_USB_DEVICE_REQUEST));

+

+  Status = UsbEth->UsbEthInterrupt (UsbEth, TRUE, NETWORK_COMMON_POLLING_INTERVAL, &NicDevice->NicInfo.Request);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &NicDevice->DeviceHandle,

+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,

+                  &NicDevice->NiiProtocol,

+                  &gEfiDevicePathProtocolGuid,

+                  NicDevice->DevPath,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    if (NicDevice->NiiProtocol.IfNum < MAX_LAN_INTERFACE) {

+      gLanDeviceList[NicDevice->NiiProtocol.IfNum] = NULL;

+    }

+

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiDevicePathProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+

+    if (TmpPxePointer != NULL) {

+      gBS->FreePool (TmpPxePointer);

+    }

+

+    if (NicDevice->DevPath != NULL) {

+      gBS->FreePool (NicDevice->DevPath);

+    }

+

+    if (NicDevice != NULL) {

+      gBS->FreePool (NicDevice);

+    }

+

+    return EFI_DEVICE_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  (VOID **)&UsbEth,

+                  This->DriverBindingHandle,

+                  NicDevice->DeviceHandle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+

+  return Status;

+}

+

+/**

+  Network Common Driver Binding Stop.

+

+  @param[in]  This                  Protocol instance pointer.

+  @param[in]  ControllerHandle      Handle of device to stop driver on

+  @param[in]  NumberOfChildren      Number of Handles in ChildHandleBuffer. If number of

+                                    children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer     List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS               This driver is removed ControllerHandle

+  @retval other                     This driver was not removed from this device

+

+**/

+EFI_STATUS

+EFIAPI

+NetworkCommonDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  )

+{

+  EFI_STATUS                                 Status;

+  BOOLEAN                                    AllChildrenStopped;

+  UINTN                                      Index;

+  USB_ETHERNET_PROTOCOL                      *UsbEth;

+  NIC_DEVICE                                 *NicDevice;

+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL  *NiiProtocol;

+

+  if (NumberOfChildren == 0) {

+    Status = gBS->OpenProtocol (

+                    ControllerHandle,

+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,

+                    (VOID **)&NiiProtocol,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+

+    if (EFI_ERROR (Status)) {

+      gBS->CloseProtocol (

+             ControllerHandle,

+             &gEfiDevicePathProtocolGuid,

+             This->DriverBindingHandle,

+             ControllerHandle

+             );

+      gBS->CloseProtocol (

+             ControllerHandle,

+             &gUsbEthProtocolGuid,

+             This->DriverBindingHandle,

+             ControllerHandle

+             );

+      return EFI_SUCCESS;

+    }

+

+    NicDevice = UNDI_DEV_FROM_THIS (NiiProtocol);

+    Status    = gBS->UninstallMultipleProtocolInterfaces (

+                       ControllerHandle,

+                       &gEfiNetworkInterfaceIdentifierProtocolGuid_31,

+                       &NicDevice->NiiProtocol,

+                       &gEfiDevicePathProtocolGuid,

+                       NicDevice->DevPath,

+                       NULL

+                       );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    gBS->FreePool (NicDevice->DevPath);

+    gBS->FreePool (NicDevice);

+

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiDevicePathProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return EFI_SUCCESS;

+  }

+

+  AllChildrenStopped = TRUE;

+

+  for (Index = 0; Index < NumberOfChildren; Index++) {

+    Status = gBS->OpenProtocol (

+                    ChildHandleBuffer[Index],

+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,

+                    (VOID **)&NiiProtocol,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+    if (EFI_ERROR (Status)) {

+      AllChildrenStopped = FALSE;

+      continue;

+    }

+

+    NicDevice = UNDI_DEV_FROM_THIS (NiiProtocol);

+

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gUsbEthProtocolGuid,

+           This->DriverBindingHandle,

+           ChildHandleBuffer[Index]

+           );

+

+    Status = gBS->UninstallMultipleProtocolInterfaces (

+                    ChildHandleBuffer[Index],

+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,

+                    &NicDevice->NiiProtocol,

+                    &gEfiDevicePathProtocolGuid,

+                    NicDevice->DevPath,

+                    NULL

+                    );

+    if (EFI_ERROR (Status)) {

+      Status = gBS->OpenProtocol (

+                      ControllerHandle,

+                      &gUsbEthProtocolGuid,

+                      (VOID **)&UsbEth,

+                      This->DriverBindingHandle,

+                      ChildHandleBuffer[Index],

+                      EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                      );

+    } else {

+      gBS->FreePool (NicDevice->DevPath);

+      gBS->FreePool (NicDevice);

+    }

+  }

+

+  if (!AllChildrenStopped) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  return Status;

+}

+

+/**

+  Entrypoint of Network Common Driver.

+

+  This function is the entrypoint of Network Common Driver. It installs Driver Binding

+  Protocols together with Component Name Protocols.

+

+  @param[in]  ImageHandle       The firmware allocated handle for the EFI image.

+  @param[in]  SystemTable       A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The entry point is executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+NetworkCommonEntry (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  EFI_STATUS  Status;

+

+  gNetworkCommonDriverBinding.DriverBindingHandle = ImageHandle;

+  gNetworkCommonDriverBinding.ImageHandle         = ImageHandle;

+

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &gNetworkCommonDriverBinding.DriverBindingHandle,

+                  &gEfiDriverBindingProtocolGuid,

+                  &gNetworkCommonDriverBinding,

+                  &gEfiComponentName2ProtocolGuid,

+                  &gNetworkCommonComponentName2,

+                  NULL

+                  );

+  return Status;

+}

diff --git a/UsbNetworkPkg/NetworkCommon/DriverBinding.h b/UsbNetworkPkg/NetworkCommon/DriverBinding.h
new file mode 100644
index 0000000000..223e034f2f
--- /dev/null
+++ b/UsbNetworkPkg/NetworkCommon/DriverBinding.h
@@ -0,0 +1,263 @@
+/** @file

+  Header file for for USB network common driver

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#ifndef _DRIVER_BINDING_H_

+#define _DRIVER_BINDING_H_

+

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiUsbLib.h>

+#include <Protocol/UsbIo.h>

+#include <Protocol/NetworkInterfaceIdentifier.h>

+#include <Protocol/UsbEthernetProtocol.h>

+

+#define NETWORK_COMMON_DRIVER_VERSION    1

+#define NETWORK_COMMON_POLLING_INTERVAL  0x10

+#define RX_BUFFER_COUNT                  32

+#define TX_BUFFER_COUNT                  32

+#define MEMORY_REQUIRE                   0

+

+#define UNDI_DEV_SIGNATURE  SIGNATURE_32('u','n','d','i')

+#define UNDI_DEV_FROM_THIS(a)  CR(a, NIC_DEVICE, NiiProtocol, UNDI_DEV_SIGNATURE)

+#define UNDI_DEV_FROM_NIC(a)   CR(a, NIC_DEVICE, NicInfo, UNDI_DEV_SIGNATURE)

+

+#pragma pack(1)

+typedef struct {

+  UINT8     DestAddr[PXE_HWADDR_LEN_ETHER];

+  UINT8     SrcAddr[PXE_HWADDR_LEN_ETHER];

+  UINT16    Protocol;

+} EthernetHeader;

+#pragma pack()

+

+typedef struct {

+  UINTN                                        Signature;

+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL    NiiProtocol;

+  EFI_HANDLE                                   DeviceHandle;

+  EFI_DEVICE_PATH_PROTOCOL                     *BaseDevPath;

+  EFI_DEVICE_PATH_PROTOCOL                     *DevPath;

+  NIC_DATA                                     NicInfo;

+} NIC_DEVICE;

+

+typedef VOID (*API_FUNC)(

+  PXE_CDB *,

+  NIC_DATA *

+  );

+

+extern PXE_SW_UNDI                   *gPxe;

+extern NIC_DEVICE                    *gLanDeviceList[MAX_LAN_INTERFACE];

+extern EFI_COMPONENT_NAME2_PROTOCOL  gNetworkCommonComponentName2;

+

+EFI_STATUS

+EFIAPI

+NetworkCommonSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+NetworkCommonDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+NetworkCommonDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  );

+

+VOID

+PxeStructInit (

+  OUT PXE_SW_UNDI  *PxeSw

+  );

+

+VOID

+UpdateNicNum (

+  IN      NIC_DATA     *Nic,

+  IN OUT  PXE_SW_UNDI  *PxeSw

+  );

+

+VOID

+UndiApiEntry (

+  IN  UINT64  Cdb

+  );

+

+UINTN

+MapIt (

+  IN NIC_DATA  *Nic,

+  IN UINT64    MemAddr,

+  IN UINT32    Size,

+  IN UINT32    Direction,

+  OUT UINT64   MappedAddr

+  );

+

+VOID

+UnMapIt (

+  IN NIC_DATA  *Nic,

+  IN UINT64    MemAddr,

+  IN UINT32    Size,

+  IN UINT32    Direction,

+  IN UINT64    MappedAddr

+  );

+

+VOID

+UndiGetState (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiStart (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiStop (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiGetInitInfo (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiGetConfigInfo (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiInitialize (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  );

+

+VOID

+UndiReset (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiShutdown (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  );

+

+VOID

+UndiInterruptEnable (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiReceiveFilter (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiStationAddress (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiStatistics (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiMcastIp2Mac (

+  IN OUT  PXE_CDB   *Cdb,

+  IN      NIC_DATA  *Nic

+  );

+

+VOID

+UndiNvData (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiGetStatus (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiFillHeader (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiTransmit (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+VOID

+UndiReceive (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+UINT16

+Initialize (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  );

+

+UINT16

+Transmit (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic,

+  IN      UINT64    CpbAddr,

+  IN      UINT16    OpFlags

+  );

+

+UINT16

+Receive (

+  IN PXE_CDB       *Cdb,

+  IN OUT NIC_DATA  *Nic,

+  IN UINT64        CpbAddr,

+  IN OUT UINT64    DbAddr

+  );

+

+UINT16

+Setfilter (

+  IN  NIC_DATA  *Nic,

+  IN  UINT16    SetFilter,

+  IN  UINT64    CpbAddr,

+  IN  UINT32    CpbSize

+  );

+

+UINT16

+Statistics (

+  IN NIC_DATA  *Nic,

+  IN UINT64    DbAddr,

+  IN UINT16    DbSize

+  );

+

+#endif

diff --git a/UsbNetworkPkg/NetworkCommon/NetworkCommon.inf b/UsbNetworkPkg/NetworkCommon/NetworkCommon.inf
new file mode 100644
index 0000000000..483df660f7
--- /dev/null
+++ b/UsbNetworkPkg/NetworkCommon/NetworkCommon.inf
@@ -0,0 +1,43 @@
+## @file
+#   This is Usb Network Common driver for DXE phase.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = NetworkCommon
+  FILE_GUID                      = ca6eb4f4-f1d6-4375-97d6-18856871e1bf
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = NetworkCommonEntry
+
+[Sources]
+  DriverBinding.c
+  DriverBinding.h
+  ComponentName.c
+  PxeFunction.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UsbNetworkPkg/UsbNetworkPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  DebugLib
+  UefiUsbLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31
+  gEfiUsbIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/UsbNetworkPkg/NetworkCommon/PxeFunction.c b/UsbNetworkPkg/NetworkCommon/PxeFunction.c
new file mode 100644
index 0000000000..d34b61a69f
--- /dev/null
+++ b/UsbNetworkPkg/NetworkCommon/PxeFunction.c
@@ -0,0 +1,1734 @@
+/** @file

+  This file contains code for UNDI command based on UEFI specification.

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "DriverBinding.h"

+

+// API table, defined in UEFI specification

+API_FUNC  gUndiApiTable[] = {

+  UndiGetState,

+  UndiStart,

+  UndiStop,

+  UndiGetInitInfo,

+  UndiGetConfigInfo,

+  UndiInitialize,

+  UndiReset,

+  UndiShutdown,

+  UndiInterruptEnable,

+  UndiReceiveFilter,

+  UndiStationAddress,

+  UndiStatistics,

+  UndiMcastIp2Mac,

+  UndiNvData,

+  UndiGetStatus,

+  UndiFillHeader,

+  UndiTransmit,

+  UndiReceive

+};

+

+/**

+  This command is used to determine the operational state of the UNDI.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiGetState (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_GET_STATE) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  Cdb->StatFlags = Cdb->StatFlags | Nic->State;

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetState != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetState (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to change the UNDI operational state from stopped to started.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiStart (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  PXE_CPB_START_31  *Cpb;

+  EFI_STATUS        Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_START) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != sizeof (PXE_CPB_START_31)) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_STOPPED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_ALREADY_STARTED;

+    return;

+  }

+

+  Cpb = (PXE_CPB_START_31 *)(UINTN)Cdb->CPBaddr;

+

+  Nic->PxeStart.Delay     = Cpb->Delay;

+  Nic->PxeStart.Virt2Phys = Cpb->Virt2Phys;

+  Nic->PxeStart.Block     = Cpb->Block;

+  Nic->PxeStart.Map_Mem   = 0;

+  Nic->PxeStart.UnMap_Mem = 0;

+  Nic->PxeStart.Sync_Mem  = Cpb->Sync_Mem;

+  Nic->PxeStart.Unique_ID = Cpb->Unique_ID;

+  Nic->State              = PXE_STATFLAGS_GET_STATE_STARTED;

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStart != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStart (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to change the UNDI operational state from started to stopped.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiStop (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_STOP) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;

+    return;

+  }

+

+  if (Nic->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_SHUTDOWN;

+    return;

+  }

+

+  Nic->PxeStart.Delay     = 0;

+  Nic->PxeStart.Virt2Phys = 0;

+  Nic->PxeStart.Block     = 0;

+  Nic->PxeStart.Map_Mem   = 0;

+  Nic->PxeStart.UnMap_Mem = 0;

+  Nic->PxeStart.Sync_Mem  = 0;

+  Nic->State              = PXE_STATFLAGS_GET_STATE_STOPPED;

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStop != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStop (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to retrieve initialization information that is

+  needed by drivers and applications to initialized UNDI.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiGetInitInfo (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  PXE_DB_GET_INIT_INFO  *Db;

+  EFI_STATUS            Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_GET_INIT_INFO) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||

+      (Cdb->DBsize != sizeof (PXE_DB_GET_INIT_INFO)) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;

+    return;

+  }

+

+  Db = (PXE_DB_GET_INIT_INFO *)(UINTN)Cdb->DBaddr;

+

+  Db->MemoryRequired         = MEMORY_REQUIRE;

+  Db->FrameDataLen           = PXE_MAX_TXRX_UNIT_ETHER;

+  Db->LinkSpeeds[0]          = 10;

+  Db->LinkSpeeds[1]          = 100;

+  Db->LinkSpeeds[2]          = 1000;

+  Db->LinkSpeeds[3]          = 0;

+  Db->MediaHeaderLen         = PXE_MAC_HEADER_LEN_ETHER;

+  Db->HWaddrLen              = PXE_HWADDR_LEN_ETHER;

+  Db->MCastFilterCnt         = MAX_MCAST_ADDRESS_CNT;

+  Db->TxBufCnt               = Nic->PxeInit.TxBufCnt;

+  Db->TxBufSize              = Nic->PxeInit.TxBufSize;

+  Db->RxBufCnt               = Nic->PxeInit.RxBufCnt;

+  Db->RxBufSize              = Nic->PxeInit.RxBufSize;

+  Db->IFtype                 = PXE_IFTYPE_ETHERNET;

+  Db->SupportedDuplexModes   = PXE_DUPLEX_DEFAULT;

+  Db->SupportedLoopBackModes = LOOPBACK_NORMAL;

+

+  Cdb->StatFlags |= (PXE_STATFLAGS_CABLE_DETECT_SUPPORTED |

+                     PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED);

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetInitInfo != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetInitInfo (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to retrieve configuration information about

+  the NIC being controlled by the UNDI.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiGetConfigInfo (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  PXE_DB_GET_CONFIG_INFO  *Db;

+  EFI_STATUS              Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_GET_CONFIG_INFO) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||

+      (Cdb->DBsize != sizeof (PXE_DB_GET_CONFIG_INFO)) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;

+    return;

+  }

+

+  Db = (PXE_DB_GET_CONFIG_INFO *)(UINTN)Cdb->DBaddr;

+

+  Db->pci.BusType = PXE_BUSTYPE_USB;

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetConfigInfo != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetConfigInfo (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command resets the network adapter and initializes UNDI using

+  the parameters supplied in the CPB.

+

+  @param[in]      Cdb  A pointer to the command descriptor block.

+  @param[in, out] Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiInitialize (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  )

+{

+  PXE_CPB_INITIALIZE  *Cpb;

+  PXE_DB_INITIALIZE   *Db;

+  EFI_STATUS          Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_INITIALIZE) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != sizeof (PXE_CPB_INITIALIZE)))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+  }

+

+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;

+    return;

+  }

+

+  if ((Cdb->OpFlags != PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) &&

+      (Cdb->OpFlags != PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  }

+

+  if (Nic->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_ALREADY_INITIALIZED;

+    return;

+  }

+

+  Cpb = (PXE_CPB_INITIALIZE *)(UINTN)Cdb->CPBaddr;

+  Db  = (PXE_DB_INITIALIZE *)(UINTN)Cdb->DBaddr;

+

+  Nic->PxeInit.LinkSpeed    = Cpb->LinkSpeed;

+  Nic->PxeInit.DuplexMode   = Cpb->DuplexMode;

+  Nic->PxeInit.LoopBackMode = Cpb->LoopBackMode;

+  Nic->PxeInit.MemoryAddr   = Cpb->MemoryAddr;

+  Nic->PxeInit.MemoryLength = Cpb->MemoryLength;

+  Nic->PxeInit.TxBufCnt     = TX_BUFFER_COUNT;

+  Nic->PxeInit.TxBufSize    = Nic->MaxSegmentSize;

+  Nic->PxeInit.RxBufCnt     = RX_BUFFER_COUNT;

+  Nic->PxeInit.RxBufSize    = Nic->MaxSegmentSize;

+

+  Cdb->StatCode = Initialize (Cdb, Nic);

+

+  Db->MemoryUsed = MEMORY_REQUIRE;

+  Db->TxBufCnt   = Nic->PxeInit.TxBufCnt;

+  Db->TxBufSize  = Nic->PxeInit.TxBufSize;

+  Db->RxBufCnt   = Nic->PxeInit.RxBufCnt;

+  Db->RxBufSize  = Nic->PxeInit.RxBufSize;

+

+  Nic->RxFilter    = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;

+  Nic->CanReceive  = FALSE;

+  Nic->CanTransmit = FALSE;

+

+  if (Cdb->OpFlags == PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) {

+    if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_DISCONNECT)) {

+      Nic->CableDetect = 0;

+    } else if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_CONNECTED)) {

+      Nic->CableDetect = 1;

+    }

+

+    if (Nic->CableDetect == 0) {

+      Cdb->StatFlags |= PXE_STATFLAGS_INITIALIZED_NO_MEDIA;

+    }

+  }

+

+  if (Cdb->StatCode != PXE_STATCODE_SUCCESS) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+  } else {

+    Nic->State = PXE_STATFLAGS_GET_STATE_INITIALIZED;

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiInitialize != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiInitialize (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  Initialize Network interface controller data.

+

+  @param[in]      Cdb     A pointer to the command descriptor block.

+  @param[in, out] Nic     A pointer to the Network interface controller data.

+

+  @retval Status  A value of Pxe statcode.

+

+**/

+UINT16

+Initialize (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  )

+{

+  UINTN       Status;

+  UINT32      Index;

+  EFI_STATUS  EfiStatus;

+

+  Status = MapIt (

+             Nic,

+             Nic->PxeInit.MemoryAddr,

+             Nic->PxeInit.MemoryLength,

+             TO_AND_FROM_DEVICE,

+             (UINT64)(UINTN)&Nic->MappedAddr

+             );

+

+  if (Status != 0) {

+    return (UINT16)Status;

+  }

+

+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+    Nic->PermNodeAddress[Index] = Nic->MacAddr.Addr[Index];

+  }

+

+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+    Nic->CurrentNodeAddress[Index] = Nic->PermNodeAddress[Index];

+  }

+

+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+    Nic->BroadcastNodeAddress[Index] = 0xFF;

+  }

+

+  for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) {

+    Nic->CurrentNodeAddress[Index]   = 0;

+    Nic->PermNodeAddress[Index]      = 0;

+    Nic->BroadcastNodeAddress[Index] = 0;

+  }

+

+  if (Nic->UsbEth->UsbEthInitialize != NULL) {

+    EfiStatus = Nic->UsbEth->UsbEthInitialize (Cdb, Nic);

+    if (EFI_ERROR (EfiStatus)) {

+      return PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+

+  return (UINT16)Status;

+}

+

+/**

+  This command resets the network adapter and reinitializes the UNDI

+  with the same parameters provided in the Initialize command.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiReset (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_RESET) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  if ((Cdb->OpFlags != PXE_OPFLAGS_NOT_USED) &&

+      (Cdb->OpFlags != PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) &&

+      (Cdb->OpFlags != PXE_OPFLAGS_RESET_DISABLE_FILTERS))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  }

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) {

+    Nic->RxFilter = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;

+  }

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) {

+    Nic->InterrupOpFlag = 0;

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReset != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReset (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  The Shutdown command resets the network adapter and leaves it in a

+  safe state for another driver to initialize.

+

+  @param[in]      Cdb  A pointer to the command descriptor block.

+  @param[in, out] Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiShutdown (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_SHUTDOWN) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  Nic->CanReceive  = FALSE;

+  Nic->CanTransmit = FALSE;

+

+  Nic->State = PXE_STATFLAGS_GET_STATE_STARTED;

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiShutdown != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiShutdown (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  The Interrupt Enables command can be used to read and/or change

+  the current external interrupt enable settings.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiInterruptEnable (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  Cdb->StatCode = PXE_STATCODE_UNSUPPORTED;

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiInterruptEnable != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiInterruptEnable (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    } else {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+      Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+    }

+  }

+}

+

+/**

+  This command is used to read and change receive filters and,

+  if supported, read and change the multicast MAC address filter list.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiReceiveFilter (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  UINT16                  NewFilter;

+  PXE_DB_RECEIVE_FILTERS  *Db;

+  EFI_STATUS              Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_RECEIVE_FILTERS) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  NewFilter = (UINT16)(Cdb->OpFlags & 0x1F);

+

+  switch (Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_OPMASK) {

+    case PXE_OPFLAGS_RECEIVE_FILTER_READ:

+      if (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) {

+        Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+        Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+      }

+

+      if ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) == 0) {

+        if ((Cdb->DBsize != 0)) {

+          Db = (PXE_DB_RECEIVE_FILTERS *)(UINTN)Cdb->DBaddr;

+          CopyMem (Db, &Nic->McastList, Nic->McastCount);

+        }

+      }

+

+      break;

+

+    case PXE_OPFLAGS_RECEIVE_FILTER_ENABLE:

+      if (NewFilter == 0) {

+        Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+      }

+

+      if (Cdb->CPBsize != 0) {

+        if (((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) == 0) ||

+            ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) ||

+            ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) ||

+            ((Cdb->CPBsize % sizeof (PXE_MAC_ADDR)) != 0))

+        {

+          Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+        }

+      }

+

+      if ((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {

+        if (((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) ||

+            ((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0))

+        {

+          Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+        }

+

+        if ((Cdb->CPBsize == 0)) {

+          Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+        }

+      }

+

+      Cdb->StatCode = Setfilter (Nic, NewFilter, Cdb->CPBaddr, Cdb->CPBsize);

+      break;

+

+    case PXE_OPFLAGS_RECEIVE_FILTER_DISABLE:

+      if (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) {

+        Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+        Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+      }

+

+      Nic->CanReceive = TRUE;

+      break;

+

+    default:

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+      Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+  }

+

+  if (Nic->CanReceive) {

+    Cdb->StatFlags = (PXE_STATFLAGS)(Cdb->StatFlags | Nic->RxFilter);

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReceiveFilter != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReceiveFilter (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  Set PXE receive filter.

+

+  @param[in]  Nic         A pointer to the Network interface controller data.

+  @param[in]  SetFilter   PXE receive filter

+  @param[in]  CpbAddr     Command Parameter Block Address

+  @param[in]  CpbSize     Command Parameter Block Size

+

+**/

+UINT16

+Setfilter (

+  IN  NIC_DATA  *Nic,

+  IN  UINT16    SetFilter,

+  IN  UINT64    CpbAddr,

+  IN  UINT32    CpbSize

+  )

+{

+  EFI_STATUS                   Status;

+  UINT8                        *McastList;

+  UINT8                        Count = 0;

+  UINT8                        Index1;

+  UINT8                        Index2;

+  PXE_CPB_RECEIVE_FILTERS      *Cpb = (PXE_CPB_RECEIVE_FILTERS *)(UINTN)CpbAddr;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+

+  // The Cpb could be NULL.(ref:PXE_CPBADDR_NOT_USED)

+  Nic->RxFilter = (UINT8)SetFilter;

+

+  if (((SetFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) || (Cpb != NULL)) {

+    if (Cpb != NULL) {

+      Nic->McastCount = (UINT8)(CpbSize / PXE_MAC_LENGTH);

+      CopyMem (&Nic->McastList, Cpb, Nic->McastCount);

+    }

+

+    if (Nic->CanReceive) {

+      Nic->CanReceive = FALSE;

+    }

+

+    Nic->UsbEth->UsbEthFunDescriptor (Nic->UsbEth, &UsbEthFunDescriptor);

+    if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {

+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;

+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);

+    } else {

+      Status = gBS->AllocatePool (EfiBootServicesData, Nic->McastCount * 6, (VOID **)&McastList);

+      if (EFI_ERROR (Status)) {

+        return PXE_STATCODE_INVALID_PARAMETER;

+      }

+

+      if (Cpb != NULL) {

+        for (Index1 = 0; Index1 < Nic->McastCount; Index1++) {

+          for (Index2 = 0; Index2 < 6; Index2++) {

+            McastList[Count++] = Cpb->MCastList[Index1][Index2];

+          }

+        }

+      }

+

+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;

+      if (Cpb != NULL) {

+        Nic->UsbEth->SetUsbEthMcastFilter (Nic->UsbEth, Nic->McastCount, McastList);

+      }

+

+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);

+      gBS->FreePool (McastList);

+    }

+  }

+

+  return PXE_STATCODE_SUCCESS;

+}

+

+/**

+  This command is used to get current station and broadcast MAC addresses

+  and, if supported, to change the current station MAC address.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiStationAddress (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  PXE_CPB_STATION_ADDRESS  *Cpb;

+  PXE_DB_STATION_ADDRESS   *Db;

+  UINT16                   Index;

+  EFI_STATUS               Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_STATION_ADDRESS) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->DBsize != sizeof (PXE_DB_STATION_ADDRESS)))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  if (Cdb->OpFlags == PXE_OPFLAGS_STATION_ADDRESS_RESET) {

+    if (CompareMem (&Nic->CurrentNodeAddress[0], &Nic->PermNodeAddress[0], PXE_MAC_LENGTH) != 0) {

+      for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {

+        Nic->CurrentNodeAddress[Index] = Nic->PermNodeAddress[Index];

+      }

+    }

+  }

+

+  if (Cdb->CPBaddr != 0) {

+    Cpb = (PXE_CPB_STATION_ADDRESS *)(UINTN)Cdb->CPBaddr;

+    for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {

+      Nic->CurrentNodeAddress[Index] = Cpb->StationAddr[Index];

+    }

+  }

+

+  if (Cdb->DBaddr != 0) {

+    Db = (PXE_DB_STATION_ADDRESS *)(UINTN)Cdb->DBaddr;

+    for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {

+      Db->StationAddr[Index]   = Nic->CurrentNodeAddress[Index];

+      Db->BroadcastAddr[Index] = Nic->BroadcastNodeAddress[Index];

+      Db->PermanentAddr[Index] = Nic->PermNodeAddress[Index];

+    }

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStationAddress != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStationAddress (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to read and clear the NIC traffic statistics.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiStatistics (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_STATISTICS) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  if ((Cdb->OpFlags != PXE_OPFLAGS_STATISTICS_RESET) &&

+      (Cdb->OpFlags != PXE_OPFLAGS_STATISTICS_READ))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  }

+

+  Cdb->StatCode = Statistics (Nic, Cdb->DBaddr, Cdb->DBsize);

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStatistics != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStatistics (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  Return data for DB data.

+

+  @param[in]  Nic      A pointer to the Network interface controller data.

+  @param[in]  DbAddr   Data Block Address.

+  @param[in]  DbSize   Data Block Size.

+

+**/

+UINT16

+Statistics (

+  IN NIC_DATA  *Nic,

+  IN UINT64    DbAddr,

+  IN UINT16    DbSize

+  )

+{

+  PXE_DB_STATISTICS  *DbStatistic;

+  EFI_STATUS         Status;

+

+  DbStatistic = (PXE_DB_STATISTICS *)(UINTN)DbAddr;

+

+  if (DbSize == 0) {

+    return PXE_STATCODE_SUCCESS;

+  }

+

+  DbStatistic->Supported  = 0x802;

+  DbStatistic->Data[0x01] = Nic->RxFrame;

+  DbStatistic->Data[0x0B] = Nic->TxFrame;

+

+  if (Nic->UsbEth->UsbEthStatistics != NULL) {

+    Status = Nic->UsbEth->UsbEthStatistics (Nic, DbAddr, DbSize);

+    if (EFI_ERROR (Status)) {

+      return PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+

+  return PXE_STATCODE_SUCCESS;

+}

+

+/**

+  Translate a multicast IPv4 or IPv6 address to a multicast MAC address.

+

+  @param[in, out] Cdb  A pointer to the command descriptor block.

+  @param[in]      Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiMcastIp2Mac (

+  IN OUT  PXE_CDB   *Cdb,

+  IN      NIC_DATA  *Nic

+  )

+{

+  PXE_CPB_MCAST_IP_TO_MAC  *Cpb;

+  PXE_DB_MCAST_IP_TO_MAC   *Db;

+  UINT8                    *Tmp;

+  EFI_STATUS               Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_MCAST_IP_TO_MAC) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != sizeof (PXE_CPB_MCAST_IP_TO_MAC)) ||

+      (Cdb->DBsize != sizeof (PXE_DB_MCAST_IP_TO_MAC)))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  Cpb = (PXE_CPB_MCAST_IP_TO_MAC *)(UINTN)Cdb->CPBaddr;

+  Db  = (PXE_DB_MCAST_IP_TO_MAC *)(UINTN)Cdb->DBaddr;

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_MCAST_IPV6_TO_MAC) != 0) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_UNSUPPORTED;

+    return;

+  }

+

+  Tmp = (UINT8 *)(&Cpb->IP.IPv4);

+

+  if ((Tmp[0] & 0xF0) != 0xE0) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CPB;

+  }

+

+  Db->MAC[0] = 0x01;

+  Db->MAC[1] = 0x00;

+  Db->MAC[2] = 0x5E;

+  Db->MAC[3] = Tmp[1] & 0x7F;

+  Db->MAC[4] = Tmp[2];

+  Db->MAC[5] = Tmp[3];

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiMcastIp2Mac != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiMcastIp2Mac (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to read and write (if supported by NIC H/W)

+  nonvolatile storage on the NIC.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiNvData (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  Cdb->StatCode = PXE_STATCODE_UNSUPPORTED;

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiNvData != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiNvData (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    } else {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+      Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+    }

+  }

+}

+

+/**

+  This command returns the current interrupt status and/or the

+  transmitted buffer addresses and the current media status.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiGetStatus (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  PXE_DB_GET_STATUS  *Db;

+  PXE_DB_GET_STATUS  TmpGetStatus;

+  UINT16             NumEntries;

+  UINTN              Index;

+  EFI_STATUS         Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_GET_STATUS) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||

+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  TmpGetStatus.RxFrameLen = 0;

+  TmpGetStatus.reserved   = 0;

+  Db                      = (PXE_DB_GET_STATUS *)(UINTN)Cdb->DBaddr;

+

+  if ((Cdb->DBsize > 0) && (Cdb->DBsize < sizeof (UINT32) * 2)) {

+    CopyMem (Db, &TmpGetStatus, Cdb->DBsize);

+  } else {

+    CopyMem (Db, &TmpGetStatus, sizeof (UINT32) * 2);

+  }

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS) != 0) {

+    if (Cdb->DBsize == 0) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+      Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+      return;

+    }

+

+    NumEntries  = Cdb->DBsize - sizeof (UINT64);

+    Cdb->DBsize = sizeof (UINT32) * 2;

+

+    for (Index = 0; NumEntries >= sizeof (UINT64); Index++, NumEntries -= sizeof (UINT64)) {

+      if (Nic->TxBufferCount > 0) {

+        Nic->TxBufferCount--;

+        Db->TxBuffer[Index] = Nic->MediaHeader[Nic->TxBufferCount];

+      }

+    }

+  }

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_GET_INTERRUPT_STATUS) != 0) {

+    if (Nic->ReceiveStatus != 0) {

+      Cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE;

+    }

+  }

+

+  if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_DISCONNECT)) {

+    Nic->CableDetect = 0;

+  } else if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_CONNECTED)) {

+    Nic->CableDetect = 1;

+  }

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_GET_MEDIA_STATUS) != 0) {

+    if (Nic->CableDetect == 0) {

+      Cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_MEDIA;

+    }

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetStatus != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetStatus (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  This command is used to fill the media header(s) in transmit packet(s).

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiFillHeader (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  PXE_CPB_FILL_HEADER             *CpbFillHeader;

+  PXE_CPB_FILL_HEADER_FRAGMENTED  *CpbFill;

+  EthernetHeader                  *MacHeader;

+  UINTN                           Index;

+  EFI_STATUS                      Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_FILL_HEADER) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED)) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    return;

+  }

+

+  if (Cdb->CPBsize == PXE_CPBSIZE_NOT_USED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  }

+

+  if ((Cdb->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED) != 0) {

+    CpbFill = (PXE_CPB_FILL_HEADER_FRAGMENTED *)(UINTN)Cdb->CPBaddr;

+

+    if ((CpbFill->FragCnt == 0) || (CpbFill->FragDesc[0].FragLen < PXE_MAC_HEADER_LEN_ETHER)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+      Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+      return;

+    }

+

+    MacHeader           = (EthernetHeader *)(UINTN)CpbFill->FragDesc[0].FragAddr;

+    MacHeader->Protocol = CpbFill->Protocol;

+

+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+      MacHeader->DestAddr[Index] = CpbFill->DestAddr[Index];

+      MacHeader->SrcAddr[Index]  = CpbFill->SrcAddr[Index];

+    }

+  } else {

+    CpbFillHeader = (PXE_CPB_FILL_HEADER *)(UINTN)Cdb->CPBaddr;

+

+    MacHeader           = (EthernetHeader *)(UINTN)CpbFillHeader->MediaHeader;

+    MacHeader->Protocol = CpbFillHeader->Protocol;

+

+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+      MacHeader->DestAddr[Index] = CpbFillHeader->DestAddr[Index];

+      MacHeader->SrcAddr[Index]  = CpbFillHeader->SrcAddr[Index];

+    }

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiFillHeader != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiFillHeader (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+  }

+}

+

+/**

+  The Transmit command is used to place a packet into the transmit queue.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiTransmit (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_TRANSMIT) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != sizeof (PXE_CPB_TRANSMIT)) ||

+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||

+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    return;

+  }

+

+  if (Cdb->CPBsize == PXE_CPBSIZE_NOT_USED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiTransmit != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiTransmit (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+

+    return;

+  }

+

+  Cdb->StatCode = Transmit (Cdb, Nic, Cdb->CPBaddr, Cdb->OpFlags);

+

+  if (Cdb->StatCode != PXE_STATCODE_SUCCESS) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+  }

+}

+

+/**

+  Use USB Ethernet Protocol Bulk out command to transmit data.

+

+  @param[in]      Cdb      A pointer to the command descriptor block.

+  @param[in, out] Nic      A pointer to the Network interface controller data.

+  @param[in]      CpbAddr  Command Parameter Block Address.

+  @param[in]      OpFlags  Operation Flags.

+

+**/

+UINT16

+Transmit (

+  IN      PXE_CDB   *Cdb,

+  IN OUT  NIC_DATA  *Nic,

+  IN      UINT64    CpbAddr,

+  IN      UINT16    OpFlags

+  )

+{

+  EFI_STATUS        Status;

+  PXE_CPB_TRANSMIT  *Cpb;

+  UINT64            BulkOutData = 0;

+  UINTN             DataLength;

+  UINTN             Map;

+  UINT32            Counter = 0;

+  UINT16            StatCode;

+

+  Cpb = (PXE_CPB_TRANSMIT *)(UINTN)CpbAddr;

+

+  if (Nic->CanTransmit) {

+    return PXE_STATCODE_BUSY;

+  }

+

+  Nic->CanTransmit = TRUE;

+

+  if ((OpFlags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {

+    return PXE_STATCODE_INVALID_PARAMETER;

+  }

+

+  Map = MapIt (

+          Nic,

+          Cpb->FrameAddr,

+          Cpb->DataLen + (UINT32)Cpb->MediaheaderLen,

+          TO_DEVICE,

+          (UINT64)(UINTN)&BulkOutData

+          );

+

+  if (Map != 0) {

+    Nic->CanTransmit = FALSE;

+    return PXE_STATCODE_INVALID_PARAMETER;

+  }

+

+  if (Nic->TxBufferCount < MAX_XMIT_BUFFERS) {

+    Nic->MediaHeader[Nic->TxBufferCount] = Cpb->FrameAddr;

+    Nic->TxBufferCount++;

+  }

+

+  DataLength = (UINTN)(Cpb->DataLen + (UINT32)Cpb->MediaheaderLen);

+

+  while (1) {

+    if (Counter >= 3) {

+      StatCode = PXE_STATCODE_BUSY;

+      break;

+    }

+

+    Status = Nic->UsbEth->UsbEthTransmit (Cdb, Nic->UsbEth, (VOID *)(UINTN)BulkOutData, &DataLength);

+    if (EFI_ERROR (Status)) {

+      StatCode =  PXE_STATFLAGS_COMMAND_FAILED;

+    }

+

+    if (Status == EFI_INVALID_PARAMETER) {

+      StatCode = PXE_STATCODE_INVALID_PARAMETER;

+      break;

+    }

+

+    if (Status == EFI_DEVICE_ERROR) {

+      StatCode = PXE_STATCODE_DEVICE_FAILURE;

+      break;

+    }

+

+    if (!EFI_ERROR (Status)) {

+      Nic->TxFrame++;

+      StatCode = PXE_STATCODE_SUCCESS;

+      break;

+    }

+

+    Counter++;

+  }

+

+  UnMapIt (

+    Nic,

+    Cpb->FrameAddr,

+    Cpb->DataLen + (UINT32)Cpb->MediaheaderLen,

+    TO_DEVICE,

+    BulkOutData

+    );

+

+  Nic->CanTransmit = FALSE;

+

+  return StatCode;

+}

+

+/**

+  When the network adapter has received a frame, this command is used

+  to copy the frame into driver/application storage.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+**/

+VOID

+UndiReceive (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  if ((Cdb->OpCode != PXE_OPCODE_RECEIVE) ||

+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||

+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||

+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||

+      (Cdb->CPBsize != sizeof (PXE_CPB_RECEIVE)) ||

+      (Cdb->DBsize != sizeof (PXE_DB_RECEIVE)) ||

+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))

+  {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;

+    return;

+  } else {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;

+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;

+  }

+

+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    return;

+  }

+

+  if (!Nic->CanReceive) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;

+    return;

+  }

+

+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReceive != NULL) {

+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReceive (Cdb, Nic);

+    if (EFI_ERROR (Status)) {

+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+    }

+

+    return;

+  }

+

+  Cdb->StatCode = Receive (Cdb, Nic, Cdb->CPBaddr, Cdb->DBaddr);

+

+  if (Cdb->StatCode != PXE_STATCODE_SUCCESS) {

+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;

+  }

+}

+

+/**

+  Use USB Ethernet Protocol Bulk in command to receive data.

+

+  @param[in]      Cdb      A pointer to the command descriptor block.

+  @param[in, out] Nic      A pointer to the Network interface controller data.

+  @param[in]      CpbAddr  Command Parameter Block Address.

+  @param[in, out] DbAddr   Data Block Address.

+

+**/

+UINT16

+Receive (

+  IN PXE_CDB       *Cdb,

+  IN OUT NIC_DATA  *Nic,

+  IN UINT64        CpbAddr,

+  IN OUT UINT64    DbAddr

+  )

+{

+  EFI_STATUS       Status;

+  UINTN            Index;

+  UINT16           StatCode  = PXE_STATCODE_NO_DATA;

+  PXE_FRAME_TYPE   FrameType = PXE_FRAME_TYPE_NONE;

+  PXE_CPB_RECEIVE  *Cpb;

+  PXE_DB_RECEIVE   *Db;

+  UINT8            *BulkInData;

+  UINTN            DataLength = (UINTN)Nic->MaxSegmentSize;

+  EthernetHeader   *Header;

+

+  Cpb = (PXE_CPB_RECEIVE *)(UINTN)CpbAddr;

+  Db  = (PXE_DB_RECEIVE *)(UINTN)DbAddr;

+

+  Status = gBS->AllocatePool (EfiBootServicesData, DataLength, (VOID **)&BulkInData);

+  if (EFI_ERROR (Status)) {

+    return PXE_STATCODE_INVALID_PARAMETER;

+  }

+

+  while (1) {

+    Status = Nic->UsbEth->UsbEthReceive (Cdb, Nic->UsbEth, (VOID *)BulkInData, &DataLength);

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    Nic->RxFrame++;

+

+    if (DataLength != 0) {

+      if (DataLength > Cpb->BufferLen) {

+        DataLength = (UINTN)Cpb->BufferLen;

+      }

+

+      CopyMem ((UINT8 *)(UINTN)Cpb->BufferAddr, (UINT8 *)BulkInData, DataLength);

+

+      Header = (EthernetHeader *)BulkInData;

+

+      Db->FrameLen       = (UINT32)DataLength;

+      Db->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;

+

+      for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+        if (Header->DestAddr[Index] != Nic->CurrentNodeAddress[Index]) {

+          break;

+        }

+      }

+

+      if (Index >= PXE_HWADDR_LEN_ETHER) {

+        FrameType = PXE_FRAME_TYPE_UNICAST;

+      } else {

+        for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+          if (Header->DestAddr[Index] != Nic->BroadcastNodeAddress[Index]) {

+            break;

+          }

+        }

+

+        if (Index >= PXE_HWADDR_LEN_ETHER) {

+          FrameType = PXE_FRAME_TYPE_BROADCAST;

+        } else {

+          if ((Header->DestAddr[0] & 1) == 1) {

+            FrameType = PXE_FRAME_TYPE_FILTERED_MULTICAST;

+          } else {

+            FrameType = PXE_FRAME_TYPE_PROMISCUOUS;

+          }

+        }

+      }

+

+      Db->Type     = FrameType;

+      Db->Protocol = Header->Protocol;

+

+      for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+        Db->SrcAddr[Index]  = Header->SrcAddr[Index];

+        Db->DestAddr[Index] = Header->DestAddr[Index];

+      }

+

+      StatCode = PXE_STATCODE_SUCCESS;

+      break;

+    }

+  }

+

+  if (FrameType == PXE_FRAME_TYPE_NONE) {

+    Nic->ReceiveStatus = 0;

+  } else {

+    Nic->ReceiveStatus = 1;

+  }

+

+  gBS->FreePool (BulkInData);

+

+  return StatCode;

+}

+

+/**

+  Fill out PXE SW UNDI structure.

+

+  @param[out]  PxeSw      A pointer to the PXE SW UNDI structure.

+

+**/

+VOID

+PxeStructInit (

+  OUT PXE_SW_UNDI  *PxeSw

+  )

+{

+  PxeSw->Signature = PXE_ROMID_SIGNATURE;

+  PxeSw->Len       = (UINT8)sizeof (PXE_SW_UNDI);

+  PxeSw->Fudge     = 0;

+  PxeSw->IFcnt     = 0;

+  PxeSw->IFcntExt  = 0;

+  PxeSw->Rev       = PXE_ROMID_REV;

+  PxeSw->MajorVer  = PXE_ROMID_MAJORVER;

+  PxeSw->MinorVer  = PXE_ROMID_MINORVER;

+  PxeSw->reserved1 = 0;

+

+  PxeSw->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR |

+                          PXE_ROMID_IMP_FRAG_SUPPORTED |

+                          PXE_ROMID_IMP_CMD_LINK_SUPPORTED |

+                          PXE_ROMID_IMP_STATION_ADDR_SETTABLE |

+                          PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED |

+                          PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED |

+                          PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED |

+                          PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED;

+

+  PxeSw->EntryPoint   = (UINT64)(UINTN)UndiApiEntry;

+  PxeSw->reserved2[0] = 0;

+  PxeSw->reserved2[1] = 0;

+  PxeSw->reserved2[2] = 0;

+  PxeSw->BusCnt       = 1;

+  PxeSw->BusType[0]   = PXE_BUSTYPE_USB;

+  PxeSw->Fudge        = PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len);

+}

+

+/**

+  Update NIC number.

+

+  @param[in]      Nic       A pointer to the Network interface controller data.

+  @param[in, out] PxeSw     A pointer to the PXE SW UNDI structure.

+

+**/

+VOID

+UpdateNicNum (

+  IN      NIC_DATA     *Nic,

+  IN OUT  PXE_SW_UNDI  *PxeSw

+  )

+{

+  UINT16  NicNum;

+

+  NicNum = (PxeSw->IFcnt | PxeSw->IFcntExt << 8);

+

+  if (Nic == NULL) {

+    if (NicNum > 0) {

+      NicNum--;

+    }

+

+    PxeSw->IFcnt    = (UINT8)(NicNum & 0xFF);          // Get lower byte

+    PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte

+    PxeSw->Fudge    = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len));

+    return;

+  }

+

+  NicNum++;

+

+  PxeSw->IFcnt    = (UINT8)(NicNum & 0xFF);          // Get lower byte

+  PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte

+  PxeSw->Fudge    = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len));

+}

+

+/**

+  UNDI API table entry.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+

+**/

+VOID

+UndiApiEntry (

+  IN  UINT64  Cdb

+  )

+{

+  PXE_CDB   *CdbPtr;

+  NIC_DATA  *Nic;

+

+  if (Cdb == 0) {

+    return;

+  }

+

+  CdbPtr = (PXE_CDB *)(UINTN)Cdb;

+  Nic    = &(gLanDeviceList[CdbPtr->IFnum]->NicInfo);

+  gUndiApiTable[CdbPtr->OpCode](CdbPtr, Nic);

+}

+

+/**

+  Map virtual memory address for DMA. This field can be set to

+  zero if there is no mapping service.

+

+  @param[in]  Nic           A pointer to the Network interface controller data.

+  @param[in]  MemAddr       Virtual address to be mapped.

+  @param[in]  Size          Size of memory to be mapped.

+  @param[in]  Direction     Direction of data flow for this memory's usage:

+                            cpu->device, device->cpu or both ways.

+  @param[out] MappedAddr    Pointer to return the mapped device address.

+

+**/

+UINTN

+MapIt (

+  IN NIC_DATA  *Nic,

+  IN UINT64    MemAddr,

+  IN UINT32    Size,

+  IN UINT32    Direction,

+  OUT UINT64   MappedAddr

+  )

+{

+  UINT64  *PhyAddr;

+

+  PhyAddr = (UINT64 *)(UINTN)MappedAddr;

+

+  if (Nic->PxeStart.Map_Mem == 0) {

+    *PhyAddr = MemAddr;

+  } else {

+    ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.Map_Mem)(

+  Nic->PxeStart.Unique_ID,

+  MemAddr,

+  Size,

+  Direction,

+  MappedAddr

+  );

+  }

+

+  return PXE_STATCODE_SUCCESS;

+}

+

+/**

+  Un-map previously mapped virtual memory address. This field can be set

+  to zero only if the Map_Mem() service is also set to zero.

+

+  @param[in]  Nic           A pointer to the Network interface controller data.

+  @param[in]  MemAddr       Virtual address to be mapped.

+  @param[in]  Size          Size of memory to be mapped.

+  @param[in]  Direction     Direction of data flow for this memory's usage:

+                            cpu->device, device->cpu or both ways.

+  @param[in]  MappedAddr    Pointer to return the mapped device address.

+

+**/

+VOID

+UnMapIt (

+  IN NIC_DATA  *Nic,

+  IN UINT64    MemAddr,

+  IN UINT32    Size,

+  IN UINT32    Direction,

+  IN UINT64    MappedAddr

+  )

+{

+  if (Nic->PxeStart.UnMap_Mem != 0) {

+    ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.UnMap_Mem)(

+  Nic->PxeStart.Unique_ID,

+  MemAddr,

+  Size,

+  Direction,

+  MappedAddr

+  );

+  }

+

+  return;

+}

diff --git a/UsbNetworkPkg/ReadMe.md b/UsbNetworkPkg/ReadMe.md
new file mode 100644
index 0000000000..6dc0514ba7
--- /dev/null
+++ b/UsbNetworkPkg/ReadMe.md
@@ -0,0 +1,65 @@
+# UsbNetworkPkg
+
+This document is intend to provide package information, include the interface details.
+
+# INDEX
+  * [Introduction](#introduction)
+  * [Components](#components)
+    * [[NetworkCommon]](#networkcommon)
+    * [[UsbCdcEcm]](#usbcdcecm)
+    * [[UsbCdcNcm]](#usbcdcncm)
+    * [[UsbRndis]](#usbrndis)
+
+#  Introduction
+UsbNetworkPkg provides network functions for USB LAN devices.
+
+# Components
+Below module is included in this package:<br>
+- NetworkCommon
+- UsbCdcEcm
+- UsbCdcNcm
+- UsbRndis
+
+## [NetworkCommon]
+Provides a LAN driver based on UEFI specification(UNDI). It supports USB communication class subclass devices and USB Rndis devices, depending on the UsbEthernetProtocol.
+
+## Required Components
+- NetworkPkg
+
+## [UsbCdcEcm]
+This driver provides a communication interface for USB Ethernet devices that follows the ECM protocol. The driver installs UsbEthernetProtocol with ECM functions which are consumed by the NetworkCommon driver.
+
+The driver is compatible with the following USB class codes:
+|Class Code|SubClass Code|Protocol Code|
+|:--------:|:-----------:|:-----------:|
+|0x02|0x06|0x00|
+
+## Required Components
+- NetworkCommon
+- MdeModulePkg(USB bus driver)
+
+## [UsbCdcNcm]
+This driver provides a communication interface for USB Ethernet devices that follows the NCM protocol. The driver installs UsbEthernetProtocol with NCM functions which are consumed by the NetworkCommon driver.
+
+The driver is compatible with the following USB class codes:
+|Class Code|SubClass Code|Protocol Code|
+|:--------:|:-----------:|:-----------:|
+|0x02|0x0D|0x00|
+
+## Required Components
+- NetworkCommon
+- MdeModulePkg(USB bus driver)
+
+## [UsbRndis]
+This driver provides a communication interface for USB Ethernet devices that follows the RNDIS protocol. The driver installs UsbEthernetProtocol with RNDIS functions which are consumed by the NetworkCommon driver.
+
+The driver is compatible with the following USB class codes:
+|Class Code|SubClass Code|Protocol Code|
+|:--------:|:-----------:|:-----------:|
+|0x02|0x02|0xFF|
+|0xEF|0x04|0x01|
+
+## Required Components
+- NetworkCommon
+- MdeModulePkg(USB bus driver)
+
diff --git a/UsbNetworkPkg/ReleaseNotes.md b/UsbNetworkPkg/ReleaseNotes.md
new file mode 100644
index 0000000000..03912e7788
--- /dev/null
+++ b/UsbNetworkPkg/ReleaseNotes.md
@@ -0,0 +1,11 @@
+# UsbNetworkPkg Release Notes<!-- omit in toc -->

+

+# Release History<!-- omit in toc -->

+- [1.00](#100)

+

+## 1.00

+

+**Release Date:** Mar 10, 2022

+

+**New Features**

+- UsbNetworkPkg first release.

diff --git a/UsbNetworkPkg/UsbCdcEcm/ComponentName.c b/UsbNetworkPkg/UsbCdcEcm/ComponentName.c
new file mode 100644
index 0000000000..39123994b2
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcEcm/ComponentName.c
@@ -0,0 +1,170 @@
+/** @file

+  This file contains code for USB Ecm Driver Component Name definitions

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+#include "UsbCdcEcm.h"

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gUsbEcmDriverNameTable[] = {

+  {

+    "eng;en",

+    L"USB ECM Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+EFI_STATUS

+EFIAPI

+UsbEcmComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEcmComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  );

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUsbEcmComponentName = {

+  UsbEcmComponentNameGetDriverName,

+  UsbEcmComponentNameGetControllerName,

+  "eng"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gUsbEcmComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)UsbEcmComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbEcmComponentNameGetControllerName,

+  "en"

+};

+

+/**

+  Retrieves a Unicode string that is the user readable name of the driver.

+

+  This function retrieves the user readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+  @param[out] DriverName        A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEcmComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           gUsbEcmDriverNameTable,

+           DriverName,

+           (BOOLEAN)(This == &gUsbEcmComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Controller        The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+  @param[out] ControllerName    A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEcmComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.c b/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.c
new file mode 100644
index 0000000000..34f115f48d
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.c
@@ -0,0 +1,504 @@
+/** @file

+  This file contains code for USB Ethernet Control Model

+  Driver

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+#include "UsbCdcEcm.h"

+

+EFI_GUID  gUsbEthProtocolGuid = USB_ETHERNET_PROTOCOL_GUID;

+

+EFI_DRIVER_BINDING_PROTOCOL  gUsbEcmDriverBinding = {

+  UsbEcmDriverSupported,

+  UsbEcmDriverStart,

+  UsbEcmDriverStop,

+  USB_ECM_DRIVER_VERSION,

+  NULL,

+  NULL

+};

+

+/**

+  Check if this interface is USB ECM SubType

+

+  @param[in]  UsbIo     A pointer to the EFI_USB_IO_PROTOCOL instance.

+

+  @retval TRUE          USB ECM SubType.

+  @retval FALSE         Not USB ECM SubType.

+

+**/

+BOOLEAN

+IsSupportedDevice (

+  IN EFI_USB_IO_PROTOCOL  *UsbIo

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  if ((InterfaceDescriptor.InterfaceClass == USB_CDC_CLASS) &&

+      (InterfaceDescriptor.InterfaceSubClass == USB_CDC_ECM_SUBCLASS) &&

+      (InterfaceDescriptor.InterfaceProtocol == USB_NO_CLASS_PROTOCOL))

+  {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  USB ECM Driver Binding Support.

+

+  @param[in]  This                  Protocol instance pointer.

+  @param[in]  ControllerHandle      Handle of device to test.

+  @param[in]  RemainingDevicePath   Optional parameter use to pick a specific child

+                                    device to start.

+

+  @retval EFI_SUCCESS               This driver supports this device.

+  @retval EFI_ALREADY_STARTED       This driver is already running on this device.

+  @retval other                     This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEcmDriverSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS           Status;

+  EFI_USB_IO_PROTOCOL  *UsbIo = NULL;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = IsSupportedDevice (UsbIo) ? EFI_SUCCESS : EFI_UNSUPPORTED;

+

+  gBS->CloseProtocol (

+         ControllerHandle,

+         &gEfiUsbIoProtocolGuid,

+         This->DriverBindingHandle,

+         ControllerHandle

+         );

+  return Status;

+}

+

+/**

+  Check if the USB ECM and USB CDC Data interfaces are from the same device.

+

+  @param[in]  UsbEthPath                  A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in]  UsbCdcDataPath              A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+

+  @retval EFI_SUCCESS                     Is the same device.

+  @retval EFI_NOT_FOUND                   Is not the same device.

+

+**/

+EFI_STATUS

+IsSameDevice (

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbEthPath,

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath

+  )

+{

+  while (1) {

+    if ((UsbEthPath->Type == ACPI_DEVICE_PATH) && (UsbEthPath->SubType == ACPI_DP)) {

+      if (CompareMem ((ACPI_HID_DEVICE_PATH *)UsbCdcDataPath, (ACPI_HID_DEVICE_PATH *)UsbEthPath, sizeof (ACPI_HID_DEVICE_PATH))) {

+        return EFI_NOT_FOUND;

+      }

+    }

+

+    if ((UsbEthPath->Type == HARDWARE_DEVICE_PATH) && (UsbEthPath->SubType == HW_PCI_DP)) {

+      if (CompareMem ((PCI_DEVICE_PATH *)UsbCdcDataPath, (PCI_DEVICE_PATH *)UsbEthPath, sizeof (PCI_DEVICE_PATH))) {

+        return EFI_NOT_FOUND;

+      }

+    }

+

+    if ((UsbEthPath->Type == MESSAGING_DEVICE_PATH) && (UsbEthPath->SubType == MSG_USB_DP)) {

+      if (IsDevicePathEnd (NextDevicePathNode (UsbEthPath))) {

+        if (((USB_DEVICE_PATH *)UsbEthPath)->ParentPortNumber ==

+            ((USB_DEVICE_PATH *)UsbCdcDataPath)->ParentPortNumber)

+        {

+          return EFI_SUCCESS;

+        } else {

+          return EFI_NOT_FOUND;

+        }

+      } else {

+        if (CompareMem ((USB_DEVICE_PATH *)UsbCdcDataPath, (USB_DEVICE_PATH *)UsbEthPath, sizeof (USB_DEVICE_PATH))) {

+          return EFI_NOT_FOUND;

+        }

+      }

+    }

+

+    UsbEthPath     = NextDevicePathNode (UsbEthPath);

+    UsbCdcDataPath = NextDevicePathNode (UsbCdcDataPath);

+  }

+}

+

+/**

+  Check if the USB CDC Data(UsbIo) installed and return USB CDC Data Handle.

+

+  @param[in]      UsbEthPath          A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in, out] UsbCdcDataHandle    A pointer to the EFI_HANDLE for USB CDC Data.

+

+  @retval TRUE                USB CDC Data(UsbIo) installed.

+  @retval FALSE               USB CDC Data(UsbIo) did not installed.

+

+**/

+BOOLEAN

+IsUsbCdcData (

+  IN      EFI_DEVICE_PATH_PROTOCOL  *UsbEthPath,

+  IN OUT  EFI_HANDLE                *UsbCdcDataHandle

+  )

+{

+  EFI_STATUS                    Status;

+  UINTN                         Index;

+  UINTN                         HandleCount;

+  EFI_HANDLE                    *HandleBuffer;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+  EFI_DEVICE_PATH_PROTOCOL      *UsbCdcDataPath;

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiUsbIoProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+

+    if ((Interface.InterfaceClass == USB_CDC_DATA_CLASS) &&

+        (Interface.InterfaceSubClass == USB_CDC_DATA_SUBCLASS) &&

+        (Interface.InterfaceProtocol == USB_NO_CLASS_PROTOCOL))

+    {

+      Status = gBS->HandleProtocol (

+                      HandleBuffer[Index],

+                      &gEfiDevicePathProtocolGuid,

+                      (VOID **)&UsbCdcDataPath

+                      );

+      if (EFI_ERROR (Status)) {

+        continue;

+      }

+

+      Status = IsSameDevice (UsbEthPath, UsbCdcDataPath);

+      if (!EFI_ERROR (Status)) {

+        CopyMem (UsbCdcDataHandle, &HandleBuffer[Index], sizeof (EFI_HANDLE));

+        gBS->FreePool (HandleBuffer);

+        return TRUE;

+      }

+    }

+  }

+

+  gBS->FreePool (HandleBuffer);

+  return FALSE;

+}

+

+/**

+  Call Back Function.

+

+  @param[in]  Event       Event whose notification function is being invoked.

+  @param[in]  Context     The pointer to the notification function's context,

+                          which is implementation-dependent.

+

+**/

+VOID

+EFIAPI

+CallbackFunction (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+{

+  EFI_STATUS                    Status;

+  UINTN                         Index;

+  UINTN                         HandleCount;

+  EFI_HANDLE                    *HandleBuffer;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiUsbIoProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+

+    if ((Interface.InterfaceClass == USB_CDC_CLASS) &&

+        (Interface.InterfaceSubClass == USB_CDC_ECM_SUBCLASS) &&

+        (Interface.InterfaceProtocol == USB_NO_CLASS_PROTOCOL))

+    {

+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);

+    }

+  }

+

+  gBS->FreePool (HandleBuffer);

+  gBS->CloseEvent (Event);

+}

+

+/**

+  USB ECM Driver Binding Start.

+

+  @param[in]  This                    Protocol instance pointer.

+  @param[in]  ControllerHandle        Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child

+                                      device to start.

+

+  @retval EFI_SUCCESS                 This driver is added to ControllerHandle

+  @retval EFI_DEVICE_ERROR            This driver could not be started due to a device error

+  @retval EFI_OUT_OF_RESOURCES        The driver could not install successfully due to a lack of resources.

+  @retval other                       This driver does not support this device

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEcmDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS                    Status;

+  VOID                          *Reg;

+  EFI_EVENT                     Event;

+  USB_ETHERNET_DRIVER           *UsbEthDriver;

+  EFI_DEVICE_PATH_PROTOCOL      *UsbEthPath;

+  EFI_HANDLE                    UsbCdcDataHandle;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **)&UsbEthPath,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return Status;

+  }

+

+  Status = IsUsbCdcData (UsbEthPath, &UsbCdcDataHandle) ? EFI_SUCCESS : EFI_UNSUPPORTED;

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+

+    Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CallbackFunction, NULL, &Event);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Status = gBS->RegisterProtocolNotify (&gEfiUsbIoProtocolGuid, Event, &Reg);

+    return Status;

+  }

+

+  UsbEthDriver = AllocateZeroPool (sizeof (USB_ETHERNET_DRIVER));

+  if (!UsbEthDriver) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = LoadAllDescriptor (UsbIo, &UsbEthDriver->Config);

+  ASSERT_EFI_ERROR (Status);

+

+  GetEndpoint (UsbIo, UsbEthDriver);

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+  ASSERT_EFI_ERROR (Status);

+

+  UsbEthDriver->Signature                          = USB_ETHERNET_SIGNATURE;

+  UsbEthDriver->NumOfInterface                     = Interface.InterfaceNumber;

+  UsbEthDriver->UsbCdcDataHandle                   = UsbCdcDataHandle;

+  UsbEthDriver->UsbIo                              = UsbIo;

+  UsbEthDriver->UsbEth.UsbEthReceive               = UsbEthReceive;

+  UsbEthDriver->UsbEth.UsbEthTransmit              = UsbEthTransmit;

+  UsbEthDriver->UsbEth.UsbEthInterrupt             = UsbEthInterrupt;

+  UsbEthDriver->UsbEth.UsbEthMacAddress            = GetUsbEthMacAddress;

+  UsbEthDriver->UsbEth.UsbEthMaxBulkSize           = UsbEthBulkSize;

+  UsbEthDriver->UsbEth.UsbHeaderFunDescriptor      = GetUsbHeaderFunDescriptor;

+  UsbEthDriver->UsbEth.UsbUnionFunDescriptor       = GetUsbUnionFunDescriptor;

+  UsbEthDriver->UsbEth.UsbEthFunDescriptor         = GetUsbEthFunDescriptor;

+  UsbEthDriver->UsbEth.SetUsbEthMcastFilter        = SetUsbEthMcastFilter;

+  UsbEthDriver->UsbEth.SetUsbEthPowerPatternFilter = SetUsbEthPowerFilter;

+  UsbEthDriver->UsbEth.GetUsbEthPoewrPatternFilter = GetUsbEthPowerFilter;

+  UsbEthDriver->UsbEth.SetUsbEthPacketFilter       = SetUsbEthPacketFilter;

+  UsbEthDriver->UsbEth.GetUsbEthStatistic          = GetUsbEthStatistic;

+

+  Status = gBS->InstallProtocolInterface (

+                  &ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  EFI_NATIVE_INTERFACE,

+                  &(UsbEthDriver->UsbEth)

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->FreePool (UsbEthDriver);

+    return Status;

+  }

+

+  return Status;

+}

+

+/**

+  USB ECM Driver Binding Stop.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to stop driver on

+  @param[in]  NumberOfChildren    Number of Handles in ChildHandleBuffer. If number of

+                                  children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer   List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS             This driver is removed ControllerHandle

+  @retval other                   This driver was not removed from this device

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEcmDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  )

+{

+  EFI_STATUS             Status;

+  USB_ETHERNET_PROTOCOL  *UsbEthProtocol;

+  USB_ETHERNET_DRIVER    *UsbEthDriver;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  (VOID **)&UsbEthProtocol,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (UsbEthProtocol);

+

+  Status = gBS->UninstallProtocolInterface (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  UsbEthProtocol

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->CloseProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  This->DriverBindingHandle,

+                  ControllerHandle

+                  );

+  gBS->FreePool (UsbEthDriver->Config);

+  gBS->FreePool (UsbEthDriver);

+  return Status;

+}

+

+/**

+  Entrypoint of ECM Driver.

+

+  This function is the entrypoint of ECM Driver. It installs Driver Binding

+  Protocols together with Component Name Protocols.

+

+  @param[in]  ImageHandle       The firmware allocated handle for the EFI image.

+  @param[in]  SystemTable       A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The entry point is executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEcmEntry (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  gUsbEcmDriverBinding.DriverBindingHandle = ImageHandle;

+  gUsbEcmDriverBinding.ImageHandle         = ImageHandle;

+

+  return gBS->InstallMultipleProtocolInterfaces (

+                &gUsbEcmDriverBinding.DriverBindingHandle,

+                &gEfiDriverBindingProtocolGuid,

+                &gUsbEcmDriverBinding,

+                &gEfiComponentName2ProtocolGuid,

+                &gUsbEcmComponentName2,

+                NULL

+                );

+}

diff --git a/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.h b/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.h
new file mode 100644
index 0000000000..52a818466e
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.h
@@ -0,0 +1,211 @@
+/** @file

+  Header file contains code for USB Ethernet Control Model

+  driver definitions

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#ifndef _USB_CDC_ECM_H_

+#define _USB_CDC_ECM_H_

+

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiUsbLib.h>

+#include <Protocol/UsbIo.h>

+#include <Protocol/UsbEthernetProtocol.h>

+

+typedef struct {

+  UINTN                        Signature;

+  USB_ETHERNET_PROTOCOL        UsbEth;

+  EFI_HANDLE                   UsbCdcDataHandle;

+  EFI_USB_IO_PROTOCOL          *UsbIo;

+  EFI_USB_CONFIG_DESCRIPTOR    *Config;

+  UINT8                        NumOfInterface;

+  UINT8                        BulkInEndpoint;

+  UINT8                        BulkOutEndpoint;

+  UINT8                        InterruptEndpoint;

+  EFI_MAC_ADDRESS              MacAddress;

+} USB_ETHERNET_DRIVER;

+

+#define USB_ECM_DRIVER_VERSION         1

+#define USB_ETHERNET_BULK_TIMEOUT      1

+#define USB_ETHERNET_TRANSFER_TIMEOUT  200

+

+#define USB_ETHERNET_SIGNATURE  SIGNATURE_32('u', 'e', 't', 'h')

+#define USB_ETHERNET_DEV_FROM_THIS(a)  CR (a, USB_ETHERNET_DRIVER, UsbEth, USB_ETHERNET_SIGNATURE)

+

+typedef struct {

+  UINT16    Src;

+  UINT16    Dst;

+} BIT_MAP;

+

+extern EFI_COMPONENT_NAME2_PROTOCOL  gUsbEcmComponentName2;

+

+EFI_STATUS

+EFIAPI

+UsbEcmDriverSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEcmDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEcmDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  );

+

+EFI_STATUS

+LoadAllDescriptor (

+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,

+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc

+  );

+

+BOOLEAN

+NextDescriptor (

+  IN EFI_USB_CONFIG_DESCRIPTOR  *Desc,

+  IN OUT UINTN                  *Offset

+  );

+

+EFI_STATUS

+GetFunctionalDescriptor (

+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,

+  IN  UINT8                      FunDescriptorType,

+  OUT VOID                       *DataBuffer

+  );

+

+VOID

+GetEndpoint (

+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,

+  IN OUT  USB_ETHERNET_DRIVER  *UsbEthDriver

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthReceive (

+  IN     PXE_CDB                *Cdb,

+  IN     USB_ETHERNET_PROTOCOL  *This,

+  IN OUT VOID                   *Packet,

+  IN OUT UINTN                  *PacketLength

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthTransmit (

+  IN      PXE_CDB                *Cdb,

+  IN      USB_ETHERNET_PROTOCOL  *This,

+  IN      VOID                   *Packet,

+  IN OUT  UINTN                  *PacketLength

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthInterrupt (

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Request

+  );

+

+EFI_STATUS

+EFIAPI

+InterruptCallback (

+  IN  VOID    *Data,

+  IN  UINTN   DataLength,

+  IN  VOID    *Context,

+  IN  UINT32  Status

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthMacAddress (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT EFI_MAC_ADDRESS        *MacAddress

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthBulkSize (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT UINTN                  *BulkSize

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbHeaderFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR  *UsbHeaderFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbUnionFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR  *UsbUnionFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbEthMcastFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *McastAddr

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbEthPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN UINT16                 Length,

+  IN VOID                   *PatternFilter

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthPowerFilter (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 Value,

+  OUT BOOLEAN                *PatternActive

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbEthPacketFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthStatistic (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 FeatureSelector,

+  OUT VOID                   *Statistic

+  );

+

+#endif

diff --git a/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf b/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf
new file mode 100644
index 0000000000..ed031864c6
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcEcm/UsbCdcEcm.inf
@@ -0,0 +1,41 @@
+## @file
+#   This is Usb Cdc Ecm driver for DXE phase.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UsbCdcEcm
+  FILE_GUID                      = 07a84945-685d-48ec-a6a1-1b397579fa76
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = UsbEcmEntry
+
+[Sources]
+  UsbCdcEcm.c
+  UsbCdcEcm.h
+  UsbEcmFunction.c
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UsbNetworkPkg/UsbNetworkPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  DebugLib
+  UefiUsbLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiUsbIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/UsbNetworkPkg/UsbCdcEcm/UsbEcmFunction.c b/UsbNetworkPkg/UsbCdcEcm/UsbEcmFunction.c
new file mode 100644
index 0000000000..175addddc6
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcEcm/UsbEcmFunction.c
@@ -0,0 +1,861 @@
+/** @file

+  This file contains code for USB Ethernet descriptor

+  and specific requests implement.

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "UsbCdcEcm.h"

+

+/**

+  Load All of device descriptor.

+

+  @param[in]  UsbIo                 A pointer to the EFI_USB_IO_PROTOCOL instance.

+  @param[out] ConfigDesc            A pointer to the configuration descriptor.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed because the

+                                buffer specified by DescriptorLength and Descriptor

+                                is not large enough to hold the result of the request.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error. The transfer

+                                status is returned in Status.

+

+**/

+EFI_STATUS

+LoadAllDescriptor (

+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,

+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc

+  )

+{

+  EFI_STATUS                 Status;

+  UINT32                     TransStatus;

+  EFI_USB_CONFIG_DESCRIPTOR  Tmp;

+

+  Status = UsbIo->UsbGetConfigDescriptor (UsbIo, &Tmp);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = gBS->AllocatePool (EfiBootServicesData, Tmp.TotalLength, (VOID **)ConfigDesc);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbGetDescriptor (

+             UsbIo,

+             USB_DESC_TYPE_CONFIG << 8 | (Tmp.ConfigurationValue - 1),                   // zero based

+             0,

+             Tmp.TotalLength,

+             *ConfigDesc,

+             &TransStatus

+             );

+  return Status;

+}

+

+/**

+  Returns pointer to the next descriptor for the pack of USB descriptors

+  located in continues memory segment

+

+  @param[in]      Desc   A pointer to the CONFIG_DESCRIPTOR instance.

+  @param[in, out] Offset A pointer to the sum of descriptor length.

+

+  @retval TRUE   The request executed successfully.

+  @retval FALSE  No next descriptor.

+

+**/

+BOOLEAN

+NextDescriptor (

+  IN EFI_USB_CONFIG_DESCRIPTOR  *Desc,

+  IN OUT UINTN                  *Offset

+  )

+{

+  if ((Desc == NULL) || (*Offset >= Desc->TotalLength)) {

+    return FALSE;

+  }

+

+  if (((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length == 0) {

+    return FALSE;

+  }

+

+  *Offset += ((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length;

+  if ( *Offset >= Desc->TotalLength ) {

+    return FALSE;

+  }

+

+  return TRUE;

+}

+

+/**

+  Read Function descriptor

+

+  @param[in]  Config             A pointer to all of configuration.

+  @param[in]  FunDescriptorType  USB CDC class descriptor SubType.

+  @param[out] DataBuffer         A pointer to the Data of corresponding to device capability.

+

+  @retval EFI_SUCCESS        The device capability descriptor was retrieved

+                             successfully.

+  @retval EFI_UNSUPPORTED    No supported.

+  @retval EFI_NOT_FOUND      The device capability descriptor was not found.

+

+**/

+EFI_STATUS

+GetFunctionalDescriptor (

+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,

+  IN  UINT8                      FunDescriptorType,

+  OUT VOID                       *DataBuffer

+  )

+{

+  EFI_STATUS                    Status = EFI_NOT_FOUND;

+  UINTN                         Offset;

+  EFI_USB_INTERFACE_DESCRIPTOR  *Interface;

+

+  for (Offset = 0; NextDescriptor (Config, &Offset);) {

+    Interface = (EFI_USB_INTERFACE_DESCRIPTOR *)((UINT8 *)Config + Offset);

+    if (Interface->DescriptorType == CS_INTERFACE) {

+      if (((USB_HEADER_FUN_DESCRIPTOR *)Interface)->DescriptorSubtype == FunDescriptorType) {

+        switch (FunDescriptorType) {

+          case HEADER_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_HEADER_FUN_DESCRIPTOR *)Interface,

+              sizeof (USB_HEADER_FUN_DESCRIPTOR)

+              );

+            return EFI_SUCCESS;

+          case UNION_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_UNION_FUN_DESCRIPTOR *)Interface,

+              ((USB_UNION_FUN_DESCRIPTOR *)Interface)->FunctionLength

+              );

+            return EFI_SUCCESS;

+          case ETHERNET_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_ETHERNET_FUN_DESCRIPTOR *)Interface,

+              sizeof (USB_ETHERNET_FUN_DESCRIPTOR)

+              );

+            return EFI_SUCCESS;

+          default:

+            Status = EFI_UNSUPPORTED;

+            break;

+        }

+      }

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Get USB Ethernet IO endpoint and USB CDC data IO endpoint.

+

+  @param[in]      UsbIo         A pointer to the EFI_USB_IO_PROTOCOL instance.

+  @param[in, out] UsbEthDriver  A pointer to the USB_ETHERNET_DRIVER instance.

+

+**/

+VOID

+GetEndpoint (

+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,

+  IN OUT  USB_ETHERNET_DRIVER  *UsbEthDriver

+  )

+{

+  EFI_STATUS                    Status;

+  UINT8                         Index;

+  UINT32                        Result;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+  EFI_USB_ENDPOINT_DESCRIPTOR   Endpoint;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+  ASSERT_EFI_ERROR (Status);

+

+  if (Interface.NumEndpoints == 0) {

+    Status = UsbSetInterface (UsbIo, Interface.InterfaceNumber, 1, &Result);

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+  }

+

+  for (Index = 0; Index < Interface.NumEndpoints; Index++) {

+    Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &Endpoint);

+    ASSERT_EFI_ERROR (Status);

+

+    switch ((Endpoint.Attributes & (BIT0 | BIT1))) {

+      case USB_ENDPOINT_BULK:

+        if (Endpoint.EndpointAddress & BIT7) {

+          UsbEthDriver->BulkInEndpoint = Endpoint.EndpointAddress;

+        } else {

+          UsbEthDriver->BulkOutEndpoint = Endpoint.EndpointAddress;

+        }

+

+        break;

+      case USB_ENDPOINT_INTERRUPT:

+        UsbEthDriver->InterruptEndpoint = Endpoint.EndpointAddress;

+        break;

+    }

+  }

+}

+

+/**

+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk in.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in, out] Packet        A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] PacketLength  A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.

+  @retval EFI_INVALID_PARAMETE  One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.

+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthReceive (

+  IN     PXE_CDB                *Cdb,

+  IN     USB_ETHERNET_PROTOCOL  *This,

+  IN OUT VOID                   *Packet,

+  IN OUT UINTN                  *PacketLength

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+  EFI_USB_IO_PROTOCOL  *UsbIo;

+  UINT32               TransStatus;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = gBS->HandleProtocol (

+                  UsbEthDriver->UsbCdcDataHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (UsbEthDriver->BulkInEndpoint == 0) {

+    GetEndpoint (UsbIo, UsbEthDriver);

+  }

+

+  Status = UsbIo->UsbBulkTransfer (

+                    UsbIo,

+                    UsbEthDriver->BulkInEndpoint,

+                    Packet,

+                    PacketLength,

+                    USB_ETHERNET_BULK_TIMEOUT,

+                    &TransStatus

+                    );

+  return Status;

+}

+

+/**

+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk out.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]      Packet        A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] PacketLength  A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.

+  @retval EFI_INVALID_PARAMETE  One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.

+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthTransmit (

+  IN      PXE_CDB                *Cdb,

+  IN      USB_ETHERNET_PROTOCOL  *This,

+  IN      VOID                   *Packet,

+  IN OUT  UINTN                  *PacketLength

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+  EFI_USB_IO_PROTOCOL  *UsbIo;

+  UINT32               TransStatus;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = gBS->HandleProtocol (

+                  UsbEthDriver->UsbCdcDataHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (UsbEthDriver->BulkOutEndpoint == 0) {

+    GetEndpoint (UsbIo, UsbEthDriver);

+  }

+

+  Status = UsbIo->UsbBulkTransfer (

+                    UsbIo,

+                    UsbEthDriver->BulkOutEndpoint,

+                    Packet,

+                    PacketLength,

+                    USB_ETHERNET_BULK_TIMEOUT,

+                    &TransStatus

+                    );

+  return Status;

+}

+

+/**

+  Async USB transfer callback routine.

+

+  @param[in]  Data            Data received or sent via the USB Asynchronous Transfer, if the

+                              transfer completed successfully.

+  @param[in]  DataLength      The length of Data received or sent via the Asynchronous

+                              Transfer, if transfer successfully completes.

+  @param[in]  Context         Data passed from UsbAsyncInterruptTransfer() request.

+  @param[in]  Status          Indicates the result of the asynchronous transfer.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+EFI_STATUS

+EFIAPI

+InterruptCallback (

+  IN  VOID    *Data,

+  IN  UINTN   DataLength,

+  IN  VOID    *Context,

+  IN  UINT32  Status

+  )

+{

+  if (((EFI_USB_DEVICE_REQUEST *)Data)->Request == USB_CDC_NETWORK_CONNECTION) {

+    CopyMem (

+      (EFI_USB_DEVICE_REQUEST *)Context,

+      (EFI_USB_DEVICE_REQUEST *)Data,

+      sizeof (EFI_USB_DEVICE_REQUEST)

+      );

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is used to manage a USB device with an interrupt transfer pipe.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  IsNewTransfer     If TRUE, a new transfer will be submitted to USB controller. If

+                                FALSE, the interrupt transfer is deleted from the device's interrupt

+                                transfer queue.

+  @param[in]  PollingInterval   Indicates the periodic rate, in milliseconds, that the transfer is to be

+                                executed.This parameter is required when IsNewTransfer is TRUE. The

+                                value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.

+                                The units are in milliseconds.

+  @param[in]  Request           A pointer to the EFI_USB_DEVICE_REQUEST data.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthInterrupt (

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Request

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+  UINTN                DataLength = 0;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+  if (IsNewTransfer == TRUE) {

+    DataLength = sizeof (EFI_USB_DEVICE_REQUEST) + sizeof (USB_CONNECT_SPEED_CHANGE);

+    Status     = UsbEthDriver->UsbIo->UsbAsyncInterruptTransfer (

+                                        UsbEthDriver->UsbIo,

+                                        UsbEthDriver->InterruptEndpoint,

+                                        IsNewTransfer,

+                                        PollingInterval,

+                                        DataLength,

+                                        (EFI_ASYNC_USB_TRANSFER_CALLBACK)InterruptCallback,

+                                        Request

+                                        );

+  } else {

+    Status = UsbEthDriver->UsbIo->UsbAsyncInterruptTransfer (

+                                    UsbEthDriver->UsbIo,

+                                    UsbEthDriver->InterruptEndpoint,

+                                    IsNewTransfer,

+                                    0,

+                                    0,

+                                    NULL,

+                                    NULL

+                                    );

+  }

+

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet Mac Address.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] MacAddress    A pointer to the caller allocated USB Ethernet Mac Address.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthMacAddress (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT EFI_MAC_ADDRESS        *MacAddress

+  )

+{

+  EFI_STATUS                   Status;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthDescriptor;

+  CHAR16                       *Data;

+  CHAR16                       *DataPtr;

+  CHAR16                       TmpStr[1];

+  UINT8                        Index;

+  UINT8                        Hi;

+  UINT8                        Low;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthDescriptor);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbEthDriver->UsbIo->UsbGetStringDescriptor (

+                                  UsbEthDriver->UsbIo,

+                                  0x409,                        // English-US Language ID

+                                  UsbEthDescriptor.MacAddress,

+                                  &Data

+                                  );

+  ASSERT_EFI_ERROR (Status);

+

+  DataPtr = Data;

+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));

+    DataPtr++;

+    Hi = (UINT8)StrHexToUintn (TmpStr);

+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));

+    DataPtr++;

+    Low                     = (UINT8)StrHexToUintn (TmpStr);

+    MacAddress->Addr[Index] = (Hi << 4) | Low;

+  }

+

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet Bulk transfer data size.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] BulkSize      A pointer to the Bulk transfer data size.

+

+  @retval EFI_SUCCESS       The bulk transfer data size was retrieved successfully.

+  @retval other             Failed to retrieve the bulk transfer data size.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthBulkSize (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT UINTN                  *BulkSize

+  )

+{

+  EFI_STATUS                   Status;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  *BulkSize = (UINTN)UsbEthFunDescriptor.MaxSegmentSize;

+  return Status;

+}

+

+/**

+  Retrieves the USB Header functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbHeaderFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR  *UsbHeaderFunDescriptor

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbHeaderFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (UsbEthDriver->Config, HEADER_FUN_DESCRIPTOR, UsbHeaderFunDescriptor);

+  return Status;

+}

+

+/**

+  Retrieves the USB Union functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbUnionFunDescriptor  A pointer to the caller allocated USB Union Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Union Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbUnionFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Union Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbUnionFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR  *UsbUnionFunDescriptor

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbUnionFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (UsbEthDriver->Config, UNION_FUN_DESCRIPTOR, UsbUnionFunDescriptor);

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbEthFunDescriptor    A pointer to the caller allocated USB Ethernet Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Ethernet Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbEthFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Ethernet Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbEthFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (UsbEthDriver->Config, ETHERNET_FUN_DESCRIPTOR, UsbEthFunDescriptor);

+  return Status;

+}

+

+/**

+  This request sets the Ethernet device multicast filters as specified in the

+  sequential list of 48 bit Ethernet multicast addresses.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  Number of filters.

+  @param[in]  McastAddr              A pointer to the value of the multicast addresses.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbEthMcastFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *McastAddr

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_USB_DEVICE_REQUEST       Request;

+  UINT32                       TransStatus;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_MULTICAST_FILTERS_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = Value * 6;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataOut,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                McastAddr,

+                                Request.Length,

+                                &TransStatus

+                                );

+}

+

+/**

+  This request sets up the specified Ethernet power management pattern filter as

+  described in the data structure.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                 Number of filters.

+  @param[in]  Length                Size of the power management pattern filter data.

+  @param[in]  PatternFilter         A pointer to the power management pattern filter structure.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbEthPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN UINT16                 Length,

+  IN VOID                   *PatternFilter

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_ETHERNET_DRIVER     *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = Length;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataOut,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                PatternFilter,

+                                Length,

+                                &TransStatus

+                                );

+}

+

+/**

+  This request retrieves the status of the specified Ethernet power management

+  pattern filter from the device.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  The filter number.

+  @param[out] PatternActive          A pointer to the pattern active boolean.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthPowerFilter (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 Value,

+  OUT BOOLEAN                *PatternActive

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_ETHERNET_DRIVER     *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;

+  Request.Request     = GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = USB_ETH_POWER_FILTER_LENGTH;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataIn,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                PatternActive,

+                                USB_ETH_POWER_FILTER_LENGTH,

+                                &TransStatus

+                                );

+}

+

+BIT_MAP  gTable[] = {

+  { PXE_OPFLAGS_RECEIVE_FILTER_UNICAST,            USB_ETH_PACKET_TYPE_DIRECTED      },

+  { PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST,          USB_ETH_PACKET_TYPE_BROADCAST     },

+  { PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST, USB_ETH_PACKET_TYPE_MULTICAST     },

+  { PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS,        USB_ETH_PACKET_TYPE_PROMISCUOUS   },

+  { PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST,      USB_ETH_PACKET_TYPE_ALL_MULTICAST },

+};

+

+/**

+  Convert value between PXE receive filter and USB ETH packet filter.

+

+  @param[in]  Value      PXE filter data.

+  @param[out] CdcFilter  A pointer to the Ethernet Packet Filter Bitmap value converted by PXE_OPFLAGS.

+

+**/

+VOID

+ConvertFilter (

+  IN  UINT16  Value,

+  OUT UINT16  *CdcFilter

+  )

+{

+  UINT32  Index;

+  UINT32  Count;

+

+  Count = sizeof (gTable)/sizeof (gTable[0]);

+

+  for (Index = 0; (gTable[Index].Src != 0) && (Index < Count); Index++) {

+    if (gTable[Index].Src & Value) {

+      *CdcFilter |= gTable[Index].Dst;

+    }

+  }

+}

+

+/**

+  This request is used to configure device Ethernet packet filter settings.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value             Packet Filter Bitmap.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbEthPacketFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_ETHERNET_DRIVER     *UsbEthDriver;

+  UINT16                  CommandFilter = 0;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  ConvertFilter (Value, &CommandFilter);

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_PACKET_FILTER_REQ;

+  Request.Value       = CommandFilter;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = USB_ETH_PACKET_FILTER_LENGTH;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbNoData,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                NULL,

+                                USB_ETH_PACKET_FILTER_LENGTH,

+                                &TransStatus

+                                );

+}

+

+/**

+  This request is used to retrieve a statistic based on the feature selector.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  FeatureSelector       Value of the feature selector.

+  @param[out] Statistic             A pointer to the 32 bit unsigned integer.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthStatistic (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 FeatureSelector,

+  OUT VOID                   *Statistic

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_USB_DEVICE_REQUEST       Request;

+  UINT32                       TransStatus;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (UsbEthFunDescriptor.EthernetStatistics == 0) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;

+  Request.Request     = GET_ETH_STATISTIC_REQ;

+  Request.Value       = FeatureSelector;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = USB_ETH_STATISTIC;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataIn,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                Statistic,

+                                USB_ETH_STATISTIC,

+                                &TransStatus

+                                );

+}

diff --git a/UsbNetworkPkg/UsbCdcNcm/ComponentName.c b/UsbNetworkPkg/UsbCdcNcm/ComponentName.c
new file mode 100644
index 0000000000..36792b89c5
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcNcm/ComponentName.c
@@ -0,0 +1,170 @@
+/** @file

+  This file contains code for USB Ncm Driver Component Name definitions

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+#include "UsbCdcNcm.h"

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gUsbNcmDriverNameTable[] = {

+  {

+    "eng;en",

+    L"USB NCM Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+EFI_STATUS

+EFIAPI

+UsbNcmComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+EFI_STATUS

+EFIAPI

+UsbNcmComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  );

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUsbNcmComponentName = {

+  UsbNcmComponentNameGetDriverName,

+  UsbNcmComponentNameGetControllerName,

+  "eng"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gUsbNcmComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)UsbNcmComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbNcmComponentNameGetControllerName,

+  "en"

+};

+

+/**

+  Retrieves a Unicode string that is the user readable name of the driver.

+

+  This function retrieves the user readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+  @param[out] DriverName        A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbNcmComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           gUsbNcmDriverNameTable,

+           DriverName,

+           (BOOLEAN)(This == &gUsbNcmComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Controller        The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+  @param[out] ControllerName    A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbNcmComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.c b/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.c
new file mode 100644
index 0000000000..fc7cbd8132
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.c
@@ -0,0 +1,508 @@
+/** @file

+  This file contains code for USB Network Control Model

+  binding driver

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "UsbCdcNcm.h"

+

+EFI_GUID  gUsbEthProtocolGuid = USB_ETHERNET_PROTOCOL_GUID;

+

+EFI_DRIVER_BINDING_PROTOCOL  gUsbNcmDriverBinding = {

+  UsbNcmDriverSupported,

+  UsbNcmDriverStart,

+  UsbNcmDriverStop,

+  USB_NCM_DRIVER_VERSION,

+  NULL,

+  NULL

+};

+

+/**

+  Check if this interface is USB NCM SubType

+

+  @param[in]  UsbIo   A pointer to the EFI_USB_IO_PROTOCOL instance.

+

+  @retval TRUE        USB NCM SubType.

+  @retval FALSE       Not USB NCM SubType.

+

+**/

+BOOLEAN

+IsSupportedDevice (

+  IN EFI_USB_IO_PROTOCOL  *UsbIo

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  if ((InterfaceDescriptor.InterfaceClass == USB_CDC_CLASS) &&

+      (InterfaceDescriptor.InterfaceSubClass == USB_CDC_NCM_SUBCLASS) &&

+      (InterfaceDescriptor.InterfaceProtocol == USB_NO_CLASS_PROTOCOL))

+  {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  USB NCM Driver Binding Support.

+

+  @param[in]  This                  Protocol instance pointer.

+  @param[in]  ControllerHandle      Handle of device to test.

+  @param[in]  RemainingDevicePath   Optional parameter use to pick a specific child

+                                    device to start.

+

+  @retval EFI_SUCCESS               This driver supports this device.

+  @retval EFI_ALREADY_STARTED       This driver is already running on this device.

+  @retval other                     This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbNcmDriverSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS           Status;

+  EFI_USB_IO_PROTOCOL  *UsbIo = NULL;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = IsSupportedDevice (UsbIo) ? EFI_SUCCESS : EFI_UNSUPPORTED;

+

+  gBS->CloseProtocol (

+         ControllerHandle,

+         &gEfiUsbIoProtocolGuid,

+         This->DriverBindingHandle,

+         ControllerHandle

+         );

+  return Status;

+}

+

+/**

+  Check if the USB NCM and USB CDC Data interfaces are from the same device.

+

+  @param[in]  UsbEthPath                  A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in]  UsbCdcDataPath              A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+

+  @retval EFI_SUCCESS                     Is the same device.

+  @retval EFI_NOT_FOUND                   Is not the same device.

+

+**/

+EFI_STATUS

+IsSameDevice (

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbEthPath,

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath

+  )

+{

+  while (1) {

+    if ((UsbEthPath->Type == ACPI_DEVICE_PATH) && (UsbEthPath->SubType == ACPI_DP)) {

+      if (CompareMem ((ACPI_HID_DEVICE_PATH *)UsbCdcDataPath, (ACPI_HID_DEVICE_PATH *)UsbEthPath, sizeof (ACPI_HID_DEVICE_PATH))) {

+        return EFI_NOT_FOUND;

+      }

+    }

+

+    if ((UsbEthPath->Type == HARDWARE_DEVICE_PATH) && (UsbEthPath->SubType == HW_PCI_DP)) {

+      if (CompareMem ((PCI_DEVICE_PATH *)UsbCdcDataPath, (PCI_DEVICE_PATH *)UsbEthPath, sizeof (PCI_DEVICE_PATH))) {

+        return EFI_NOT_FOUND;

+      }

+    }

+

+    if ((UsbEthPath->Type == MESSAGING_DEVICE_PATH) && (UsbEthPath->SubType == MSG_USB_DP)) {

+      if (IsDevicePathEnd (NextDevicePathNode (UsbEthPath))) {

+        if (((USB_DEVICE_PATH *)UsbEthPath)->ParentPortNumber ==

+            ((USB_DEVICE_PATH *)UsbCdcDataPath)->ParentPortNumber)

+        {

+          return EFI_SUCCESS;

+        } else {

+          return EFI_NOT_FOUND;

+        }

+      } else {

+        if (CompareMem ((USB_DEVICE_PATH *)UsbCdcDataPath, (USB_DEVICE_PATH *)UsbEthPath, sizeof (USB_DEVICE_PATH))) {

+          return EFI_NOT_FOUND;

+        }

+      }

+    }

+

+    UsbEthPath     = NextDevicePathNode (UsbEthPath);

+    UsbCdcDataPath = NextDevicePathNode (UsbCdcDataPath);

+  }

+}

+

+/**

+  Check if the USB CDC Data(UsbIo) installed and return USB CDC Data Handle.

+

+  @param[in]      UsbEthPath          A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in, out] UsbCdcDataHandle    A pointer to the EFI_HANDLE for USB CDC Data.

+

+  @retval TRUE                USB CDC Data(UsbIo) installed.

+  @retval FALSE               USB CDC Data(UsbIo) did not installed.

+

+**/

+BOOLEAN

+IsUsbCdcData (

+  IN      EFI_DEVICE_PATH_PROTOCOL  *UsbEthPath,

+  IN OUT  EFI_HANDLE                *UsbCdcDataHandle

+  )

+{

+  EFI_STATUS                    Status;

+  UINTN                         Index;

+  UINTN                         HandleCount;

+  EFI_HANDLE                    *HandleBuffer;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+  EFI_DEVICE_PATH_PROTOCOL      *UsbCdcDataPath;

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiUsbIoProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+

+    if ((Interface.InterfaceClass == USB_CDC_DATA_CLASS) &&

+        (Interface.InterfaceSubClass == USB_CDC_DATA_SUBCLASS) &&

+        (Interface.InterfaceProtocol == USB_NCM_NTB_PROTOCOL))

+    {

+      Status = gBS->HandleProtocol (

+                      HandleBuffer[Index],

+                      &gEfiDevicePathProtocolGuid,

+                      (VOID **)&UsbCdcDataPath

+                      );

+      if (EFI_ERROR (Status)) {

+        continue;

+      }

+

+      Status = IsSameDevice (UsbEthPath, UsbCdcDataPath);

+      if (!EFI_ERROR (Status)) {

+        CopyMem (UsbCdcDataHandle, &HandleBuffer[Index], sizeof (EFI_HANDLE));

+        gBS->FreePool (HandleBuffer);

+        return TRUE;

+      }

+    }

+  }

+

+  gBS->FreePool (HandleBuffer);

+  return FALSE;

+}

+

+/**

+  Call Back Function.

+

+  @param[in]  Event       Event whose notification function is being invoked.

+  @param[in]  Context     The pointer to the notification function's context,

+                          which is implementation-dependent.

+

+**/

+VOID

+EFIAPI

+CallbackFunction (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+{

+  EFI_STATUS                    Status;

+  UINTN                         Index;

+  UINTN                         HandleCount;

+  EFI_HANDLE                    *HandleBuffer;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiUsbIoProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+

+    if ((Interface.InterfaceClass == USB_CDC_CLASS) &&

+        (Interface.InterfaceSubClass == USB_CDC_NCM_SUBCLASS) &&

+        (Interface.InterfaceProtocol == USB_NO_CLASS_PROTOCOL))

+    {

+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);

+    }

+  }

+

+  gBS->FreePool (HandleBuffer);

+  gBS->CloseEvent (Event);

+}

+

+/**

+  USB NCM Driver Binding Start.

+

+  @param[in]  This                    Protocol instance pointer.

+  @param[in]  ControllerHandle        Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child

+                                      device to start.

+

+  @retval EFI_SUCCESS                 This driver is added to ControllerHandle

+  @retval EFI_DEVICE_ERROR            This driver could not be started due to a device error

+  @retval EFI_OUT_OF_RESOURCES        The driver could not install successfully due to a lack of resources.

+  @retval other                       This driver does not support this device

+

+**/

+EFI_STATUS

+EFIAPI

+UsbNcmDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS                    Status;

+  VOID                          *Reg;

+  EFI_EVENT                     Event;

+  USB_ETHERNET_DRIVER           *UsbEthDriver;

+  EFI_DEVICE_PATH_PROTOCOL      *UsbEthPath;

+  EFI_HANDLE                    UsbCdcDataHandle;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **)&UsbEthPath,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return Status;

+  }

+

+  Status = IsUsbCdcData (UsbEthPath, &UsbCdcDataHandle) ? EFI_SUCCESS : EFI_UNSUPPORTED;

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+

+    Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CallbackFunction, NULL, &Event);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Status = gBS->RegisterProtocolNotify (&gEfiUsbIoProtocolGuid, Event, &Reg);

+    return Status;

+  }

+

+  UsbEthDriver = AllocateZeroPool (sizeof (USB_ETHERNET_DRIVER));

+  if (!UsbEthDriver) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = LoadAllDescriptor (UsbIo, &UsbEthDriver->Config);

+  ASSERT_EFI_ERROR (Status);

+

+  GetEndpoint (UsbIo, UsbEthDriver);

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+  ASSERT_EFI_ERROR (Status);

+

+  UsbEthDriver->Signature                          = USB_ETHERNET_SIGNATURE;

+  UsbEthDriver->NumOfInterface                     = Interface.InterfaceNumber;

+  UsbEthDriver->UsbCdcDataHandle                   = UsbCdcDataHandle;

+  UsbEthDriver->UsbIo                              = UsbIo;

+  UsbEthDriver->UsbEth.UsbEthReceive               = UsbEthReceive;

+  UsbEthDriver->UsbEth.UsbEthTransmit              = UsbEthTransmit;

+  UsbEthDriver->UsbEth.UsbEthInterrupt             = UsbEthInterrupt;

+  UsbEthDriver->UsbEth.UsbEthMacAddress            = GetUsbEthMacAddress;

+  UsbEthDriver->UsbEth.UsbEthMaxBulkSize           = UsbEthBulkSize;

+  UsbEthDriver->UsbEth.UsbHeaderFunDescriptor      = GetUsbHeaderFunDescriptor;

+  UsbEthDriver->UsbEth.UsbUnionFunDescriptor       = GetUsbUnionFunDescriptor;

+  UsbEthDriver->UsbEth.UsbEthFunDescriptor         = GetUsbEthFunDescriptor;

+  UsbEthDriver->UsbEth.SetUsbEthMcastFilter        = SetUsbEthMcastFilter;

+  UsbEthDriver->UsbEth.SetUsbEthPowerPatternFilter = SetUsbEthPowerFilter;

+  UsbEthDriver->UsbEth.GetUsbEthPoewrPatternFilter = GetUsbEthPowerFilter;

+  UsbEthDriver->UsbEth.SetUsbEthPacketFilter       = SetUsbEthPacketFilter;

+  UsbEthDriver->UsbEth.GetUsbEthStatistic          = GetUsbEthStatistic;

+

+  UsbEthDriver->BulkBuffer = AllocateZeroPool (USB_NCM_MAX_NTB_SIZE);

+

+  Status = gBS->InstallProtocolInterface (

+                  &ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  EFI_NATIVE_INTERFACE,

+                  &(UsbEthDriver->UsbEth)

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    gBS->FreePool (UsbEthDriver);

+    return Status;

+  }

+

+  return Status;

+}

+

+/**

+  USB NCM Driver Binding Stop.

+

+  @param[in]  This                  Protocol instance pointer.

+  @param[in]  ControllerHandle      Handle of device to stop driver on

+  @param[in]  NumberOfChildren      Number of Handles in ChildHandleBuffer. If number of

+                                    children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer     List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS               This driver is removed ControllerHandle

+  @retval other                     This driver was not removed from this device

+

+**/

+EFI_STATUS

+EFIAPI

+UsbNcmDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  )

+{

+  EFI_STATUS             Status;

+  USB_ETHERNET_PROTOCOL  *UsbEthProtocol;

+  USB_ETHERNET_DRIVER    *UsbEthDriver;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  (VOID **)&UsbEthProtocol,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (UsbEthProtocol);

+

+  Status = gBS->UninstallProtocolInterface (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  UsbEthProtocol

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->CloseProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  This->DriverBindingHandle,

+                  ControllerHandle

+                  );

+  gBS->FreePool (UsbEthDriver->Config);

+  gBS->FreePool (UsbEthDriver->BulkBuffer);

+  gBS->FreePool (UsbEthDriver);

+  return Status;

+}

+

+/**

+  Entrypoint of NCM Driver.

+

+  This function is the entrypoint of NCM Driver. It installs Driver Binding

+  Protocols together with Component Name Protocols.

+

+  @param[in]  ImageHandle       The firmware allocated handle for the EFI image.

+  @param[in]  SystemTable       A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The entry point is executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbNcmEntry (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  gUsbNcmDriverBinding.DriverBindingHandle = ImageHandle;

+  gUsbNcmDriverBinding.ImageHandle         = ImageHandle;

+

+  return gBS->InstallMultipleProtocolInterfaces (

+                &gUsbNcmDriverBinding.DriverBindingHandle,

+                &gEfiDriverBindingProtocolGuid,

+                &gUsbNcmDriverBinding,

+                &gEfiComponentName2ProtocolGuid,

+                &gUsbNcmComponentName2,

+                NULL

+                );

+}

diff --git a/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.h b/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.h
new file mode 100644
index 0000000000..40b2671a1e
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.h
@@ -0,0 +1,245 @@
+/** @file

+  Header file for USB Network Control Model driver

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#ifndef _USB_CDC_NCM_H_

+#define _USB_CDC_NCM_H_

+

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiUsbLib.h>

+#include <Protocol/UsbIo.h>

+#include <Protocol/UsbEthernetProtocol.h>

+

+typedef struct {

+  UINTN                        Signature;

+  USB_ETHERNET_PROTOCOL        UsbEth;

+  EFI_HANDLE                   UsbCdcDataHandle;

+  EFI_USB_IO_PROTOCOL          *UsbIo;

+  EFI_USB_CONFIG_DESCRIPTOR    *Config;

+  UINT8                        NumOfInterface;

+  UINT8                        BulkInEndpoint;

+  UINT8                        BulkOutEndpoint;

+  UINT8                        InterruptEndpoint;

+  EFI_MAC_ADDRESS              MacAddress;

+  UINT16                       BulkOutSequence;

+  UINT8                        *BulkBuffer;

+  UINT8                        TotalDatagram;

+  UINT8                        NowDatagram;

+} USB_ETHERNET_DRIVER;

+

+#define USB_NCM_DRIVER_VERSION         1

+#define USB_ETHERNET_BULK_TIMEOUT      1

+#define USB_ETHERNET_TRANSFER_TIMEOUT  200

+#define USB_NCM_MAX_NTB_SIZE           0xFFFF

+#define USB_ETH_FRAME_SIZE             0x5F2  // frome network stack snp

+

+// Defined in USB NCM 1.0 spec., section 3.2 and 3.3

+#define USB_NCM_NTH_SIGN_16      0x484D434E

+#define USB_NCM_NDP_SIGN_16      0x304D434E

+#define USB_NCM_NDP_SIGN_16_CRC  0x314D434E

+#define USB_NCM_NTH_LENGTH       0x000C

+#define USB_NCM_NDP_LENGTH       0x0010// at least 16

+

+// USB NCM Transfer header structure - UINT16

+typedef struct {

+  UINT32    Signature;

+  UINT16    HeaderLength;

+  UINT16    Sequence;

+  UINT16    BlockLength;

+  UINT16    NdpIndex;

+} USB_NCM_TRANSFER_HEADER_16;

+

+// USB NCM Datagram pointer structure - UINT16

+typedef struct {

+  UINT32    Signature;

+  UINT16    Length;

+  UINT16    NextNdpIndex;

+} USB_NCM_DATAGRAM_POINTER_16;

+

+// USB NCM Datagram structure

+typedef struct {

+  UINT16    DatagramIndex;

+  UINT16    DatagramLength;

+} USB_NCM_DATA_GRAM;

+

+#define USB_ETHERNET_SIGNATURE  SIGNATURE_32('u', 'e', 't', 'h')

+#define USB_ETHERNET_DEV_FROM_THIS(a)  CR (a, USB_ETHERNET_DRIVER, UsbEth, USB_ETHERNET_SIGNATURE)

+

+typedef struct {

+  UINT16    Src;

+  UINT16    Dst;

+} BIT_MAP;

+

+extern EFI_COMPONENT_NAME2_PROTOCOL  gUsbNcmComponentName2;

+

+EFI_STATUS

+EFIAPI

+UsbNcmDriverSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+UsbNcmDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+UsbNcmDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  );

+

+EFI_STATUS

+LoadAllDescriptor (

+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,

+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc

+  );

+

+BOOLEAN

+NextDescriptor (

+  IN EFI_USB_CONFIG_DESCRIPTOR  *Desc,

+  IN OUT UINTN                  *Offset

+  );

+

+EFI_STATUS

+GetFunctionalDescriptor (

+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,

+  IN  UINT8                      FunDescriptorType,

+  OUT VOID                       *DataBuffer

+  );

+

+VOID

+GetEndpoint (

+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,

+  IN OUT  USB_ETHERNET_DRIVER  *UsbEthDriver

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthReceive (

+  IN     PXE_CDB                *Cdb,

+  IN     USB_ETHERNET_PROTOCOL  *This,

+  IN OUT VOID                   *Packet,

+  IN OUT UINTN                  *PacketLength

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthTransmit (

+  IN      PXE_CDB                *Cdb,

+  IN      USB_ETHERNET_PROTOCOL  *This,

+  IN      VOID                   *Packet,

+  IN OUT  UINTN                  *PacketLength

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthInterrupt (

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Request

+  );

+

+EFI_STATUS

+EFIAPI

+InterruptCallback (

+  IN  VOID    *Data,

+  IN  UINTN   DataLength,

+  IN  VOID    *Context,

+  IN  UINT32  Status

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthMacAddress (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT EFI_MAC_ADDRESS        *MacAddress

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthBulkSize (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT UINTN                  *BulkSize

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbHeaderFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR  *UsbHeaderFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbUnionFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR  *UsbUnionFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbEthMcastFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *McastAddr

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbEthPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN UINT16                 Length,

+  IN VOID                   *PatternFilter

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthPowerFilter (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 Value,

+  OUT BOOLEAN                *PatternActive

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbEthPacketFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthStatistic (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 FeatureSelector,

+  OUT VOID                   *Statistic

+  );

+

+#endif

diff --git a/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf b/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf
new file mode 100644
index 0000000000..cfbf9ad720
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcNcm/UsbCdcNcm.inf
@@ -0,0 +1,41 @@
+## @file
+#   This is Usb Cdc Ncm driver for DXE phase.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UsbCdcNcm
+  FILE_GUID                      = 52230d31-6c11-4442-b262-bec6bfe84efa
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = UsbNcmEntry
+
+[Sources]
+  UsbCdcNcm.c
+  UsbCdcNcm.h
+  UsbNcmFunction.c
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UsbNetworkPkg/UsbNetworkPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  DebugLib
+  UefiUsbLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiUsbIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+
+[Depex]
+  TRUE
\ No newline at end of file
diff --git a/UsbNetworkPkg/UsbCdcNcm/UsbNcmFunction.c b/UsbNetworkPkg/UsbCdcNcm/UsbNcmFunction.c
new file mode 100644
index 0000000000..98bcf6d6d5
--- /dev/null
+++ b/UsbNetworkPkg/UsbCdcNcm/UsbNcmFunction.c
@@ -0,0 +1,946 @@
+/** @file

+  This file contains code for USB Ethernet descriptor

+  and specific requests implement.

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "UsbCdcNcm.h"

+

+/**

+  Load All of device descriptor.

+

+  @param[in]  UsbIo                 A pointer to the EFI_USB_IO_PROTOCOL instance.

+  @param[out] ConfigDesc            A pointer to the configuration descriptor.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed because the

+                                buffer specified by DescriptorLength and Descriptor

+                                is not large enough to hold the result of the request.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error. The transfer

+                                status is returned in Status.

+

+**/

+EFI_STATUS

+LoadAllDescriptor (

+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,

+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc

+  )

+{

+  EFI_STATUS                 Status;

+  UINT32                     TransStatus;

+  EFI_USB_CONFIG_DESCRIPTOR  Tmp;

+

+  Status = UsbIo->UsbGetConfigDescriptor (UsbIo, &Tmp);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = gBS->AllocatePool (EfiBootServicesData, Tmp.TotalLength, (VOID **)ConfigDesc);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbGetDescriptor (

+             UsbIo,

+             USB_DESC_TYPE_CONFIG << 8 | (Tmp.ConfigurationValue - 1),                   // zero based

+             0,

+             Tmp.TotalLength,

+             *ConfigDesc,

+             &TransStatus

+             );

+  return Status;

+}

+

+/**

+  Returns pointer to the next descriptor for the pack of USB descriptors

+  located in continues memory segment

+

+  @param[in]      Desc   A pointer to the CONFIG_DESCRIPTOR instance.

+  @param[in, out] Offset A pointer to the sum of descriptor length.

+

+  @retval TRUE   The request executed successfully.

+  @retval FALSE  No next descriptor.

+

+**/

+BOOLEAN

+NextDescriptor (

+  IN EFI_USB_CONFIG_DESCRIPTOR  *Desc,

+  IN OUT UINTN                  *Offset

+  )

+{

+  if ((Desc == NULL) || (*Offset >= Desc->TotalLength)) {

+    return FALSE;

+  }

+

+  if (((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length == 0) {

+    return FALSE;

+  }

+

+  *Offset += ((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length;

+  if ( *Offset >= Desc->TotalLength ) {

+    return FALSE;

+  }

+

+  return TRUE;

+}

+

+/**

+  Read Function descriptor

+

+  @param[in]  Config             A pointer to all of configuration.

+  @param[in]  FunDescriptorType  USB CDC class descriptor SubType.

+  @param[out] DataBuffer         A pointer to the Data of corresponding to device capability.

+

+  @retval EFI_SUCCESS        The device capability descriptor was retrieved

+                             successfully.

+  @retval EFI_UNSUPPORTED    No supported.

+  @retval EFI_NOT_FOUND      The device capability descriptor was not found.

+

+**/

+EFI_STATUS

+GetFunctionalDescriptor (

+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,

+  IN  UINT8                      FunDescriptorType,

+  OUT VOID                       *DataBuffer

+  )

+{

+  EFI_STATUS                    Status = EFI_NOT_FOUND;

+  UINTN                         Offset;

+  EFI_USB_INTERFACE_DESCRIPTOR  *Interface;

+

+  for (Offset = 0; NextDescriptor (Config, &Offset);) {

+    Interface = (EFI_USB_INTERFACE_DESCRIPTOR *)((UINT8 *)Config + Offset);

+    if (Interface->DescriptorType == CS_INTERFACE) {

+      if (((USB_HEADER_FUN_DESCRIPTOR *)Interface)->DescriptorSubtype == FunDescriptorType) {

+        switch (FunDescriptorType) {

+          case HEADER_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_HEADER_FUN_DESCRIPTOR *)Interface,

+              sizeof (USB_HEADER_FUN_DESCRIPTOR)

+              );

+            return EFI_SUCCESS;

+          case UNION_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_UNION_FUN_DESCRIPTOR *)Interface,

+              ((USB_UNION_FUN_DESCRIPTOR *)Interface)->FunctionLength

+              );

+            return EFI_SUCCESS;

+          case ETHERNET_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_ETHERNET_FUN_DESCRIPTOR *)Interface,

+              sizeof (USB_ETHERNET_FUN_DESCRIPTOR)

+              );

+            return EFI_SUCCESS;

+          default:

+            Status = EFI_UNSUPPORTED;

+            break;

+        }

+      }

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Get USB Ethernet IO endpoint and USB CDC data IO endpoint.

+

+  @param[in]      UsbIo         A pointer to the EFI_USB_IO_PROTOCOL instance.

+  @param[in, out] UsbEthDriver  A pointer to the USB_ETHERNET_DRIVER instance.

+

+**/

+VOID

+GetEndpoint (

+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,

+  IN OUT  USB_ETHERNET_DRIVER  *UsbEthDriver

+  )

+{

+  EFI_STATUS                    Status;

+  UINT8                         Index;

+  UINT32                        Result;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+  EFI_USB_ENDPOINT_DESCRIPTOR   Endpoint;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+  ASSERT_EFI_ERROR (Status);

+

+  if (Interface.NumEndpoints == 0) {

+    Status = UsbSetInterface (UsbIo, Interface.InterfaceNumber, 1, &Result);

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+  }

+

+  for (Index = 0; Index < Interface.NumEndpoints; Index++) {

+    Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &Endpoint);

+    ASSERT_EFI_ERROR (Status);

+

+    switch ((Endpoint.Attributes & (BIT0 | BIT1))) {

+      case USB_ENDPOINT_BULK:

+        if (Endpoint.EndpointAddress & BIT7) {

+          UsbEthDriver->BulkInEndpoint = Endpoint.EndpointAddress;

+        } else {

+          UsbEthDriver->BulkOutEndpoint = Endpoint.EndpointAddress;

+        }

+

+        break;

+      case USB_ENDPOINT_INTERRUPT:

+        UsbEthDriver->InterruptEndpoint = Endpoint.EndpointAddress;

+        break;

+    }

+  }

+}

+

+/**

+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk in.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in, out] Packet        A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] PacketLength  A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.

+  @retval EFI_INVALID_PARAMETE  One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.

+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthReceive (

+  IN     PXE_CDB                *Cdb,

+  IN     USB_ETHERNET_PROTOCOL  *This,

+  IN OUT VOID                   *Packet,

+  IN OUT UINTN                  *PacketLength

+  )

+{

+  EFI_STATUS                   Status;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+  EFI_USB_IO_PROTOCOL          *UsbIo;

+  UINT32                       TransStatus;

+  UINT8                        Index;

+  UINTN                        BulkDataLenght;

+  UINTN                        TotalLength = 0;

+  USB_NCM_TRANSFER_HEADER_16   *Nth;

+  USB_NCM_DATAGRAM_POINTER_16  *Ndp;

+  USB_NCM_DATA_GRAM            *Datagram;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbEthDriver->TotalDatagram == UsbEthDriver->NowDatagram) {

+    Status = gBS->HandleProtocol (

+                    UsbEthDriver->UsbCdcDataHandle,

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    if (UsbEthDriver->BulkInEndpoint == 0) {

+      GetEndpoint (UsbIo, UsbEthDriver);

+    }

+

+    BulkDataLenght = USB_NCM_MAX_NTB_SIZE;

+    SetMem (UsbEthDriver->BulkBuffer, BulkDataLenght, 0);

+    UsbEthDriver->NowDatagram   = 0;

+    UsbEthDriver->TotalDatagram = 0;

+

+    Status = UsbIo->UsbBulkTransfer (

+                      UsbIo,

+                      UsbEthDriver->BulkInEndpoint,

+                      UsbEthDriver->BulkBuffer,

+                      &BulkDataLenght,

+                      USB_ETHERNET_BULK_TIMEOUT,

+                      &TransStatus

+                      );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Nth                         = (USB_NCM_TRANSFER_HEADER_16 *)UsbEthDriver->BulkBuffer;

+    Ndp                         = (USB_NCM_DATAGRAM_POINTER_16 *)((UINT8 *)UsbEthDriver->BulkBuffer + Nth->NdpIndex);

+    Datagram                    = (USB_NCM_DATA_GRAM *)((UINT8 *)Ndp + sizeof (USB_NCM_DATAGRAM_POINTER_16));

+    UsbEthDriver->TotalDatagram = (UINT8)((Ndp->Length - 8) / 4 - 1);

+

+    for (Index = 0; Index < UsbEthDriver->TotalDatagram; Index++) {

+      TotalLength += Datagram->DatagramLength;

+      Datagram     = (USB_NCM_DATA_GRAM *)((UINT8 *)Datagram + sizeof (USB_NCM_DATA_GRAM));

+    }

+

+    if (TotalLength < USB_ETH_FRAME_SIZE) {

+      Datagram = (USB_NCM_DATA_GRAM *)((UINT8 *)Ndp + sizeof (USB_NCM_DATAGRAM_POINTER_16));

+

+      TotalLength = 0;

+      for (Index = 0; Index < UsbEthDriver->TotalDatagram; Index++) {

+        CopyMem ((UINT8 *)Packet + TotalLength, (UINT8 *)UsbEthDriver->BulkBuffer + Datagram->DatagramIndex, Datagram->DatagramLength);

+        TotalLength += Datagram->DatagramLength;

+        Datagram     = (USB_NCM_DATA_GRAM *)((UINT8 *)Datagram + sizeof (USB_NCM_DATA_GRAM));

+      }

+

+      *PacketLength             = TotalLength;

+      UsbEthDriver->NowDatagram = UsbEthDriver->TotalDatagram;

+    } else {

+      UsbEthDriver->NowDatagram++;

+

+      Datagram = (USB_NCM_DATA_GRAM *)((UINT8 *)Ndp + sizeof (USB_NCM_DATAGRAM_POINTER_16));

+      CopyMem (Packet, (UINT8 *)UsbEthDriver->BulkBuffer + Datagram->DatagramIndex, Datagram->DatagramLength);

+      *PacketLength = Datagram->DatagramLength;

+    }

+

+    return Status;

+  } else {

+    UsbEthDriver->NowDatagram++;

+

+    Nth      = (USB_NCM_TRANSFER_HEADER_16 *)UsbEthDriver->BulkBuffer;

+    Ndp      = (USB_NCM_DATAGRAM_POINTER_16 *)((UINT8 *)UsbEthDriver->BulkBuffer + Nth->NdpIndex);

+    Datagram = (USB_NCM_DATA_GRAM *)((UINT8 *)Ndp + sizeof (USB_NCM_DATAGRAM_POINTER_16));

+    Datagram = (USB_NCM_DATA_GRAM *)((UINT8 *)Datagram + sizeof (USB_NCM_DATA_GRAM) * (UsbEthDriver->NowDatagram - 1));

+

+    CopyMem (Packet, (UINT8 *)UsbEthDriver->BulkBuffer + Datagram->DatagramIndex, Datagram->DatagramLength);

+    *PacketLength = Datagram->DatagramLength;

+

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk out.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]      Packet        A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] PacketLength  A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.

+  @retval EFI_INVALID_PARAMETE  One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.

+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthTransmit (

+  IN      PXE_CDB                *Cdb,

+  IN      USB_ETHERNET_PROTOCOL  *This,

+  IN      VOID                   *Packet,

+  IN OUT  UINTN                  *PacketLength

+  )

+{

+  EFI_STATUS                   Status;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+  EFI_USB_IO_PROTOCOL          *UsbIo;

+  UINT32                       TransStatus;

+  USB_NCM_TRANSFER_HEADER_16   *Nth;

+  USB_NCM_DATAGRAM_POINTER_16  *Ndp;

+  USB_NCM_DATA_GRAM            *Datagram;

+  UINT8                        *TotalPacket;

+  UINTN                        TotalLength;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = gBS->HandleProtocol (

+                  UsbEthDriver->UsbCdcDataHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (UsbEthDriver->BulkOutEndpoint == 0) {

+    GetEndpoint (UsbIo, UsbEthDriver);

+  }

+

+  TotalLength = (UINTN)(USB_NCM_NTH_LENGTH + USB_NCM_NDP_LENGTH + (*PacketLength));

+

+  Status = gBS->AllocatePool (EfiBootServicesData, TotalLength, (VOID **)&TotalPacket);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  SetMem (TotalPacket, TotalLength, 0);

+

+  Nth               = (USB_NCM_TRANSFER_HEADER_16 *)TotalPacket;

+  Nth->Signature    = USB_NCM_NTH_SIGN_16;

+  Nth->HeaderLength = USB_NCM_NTH_LENGTH;

+  Nth->Sequence     = UsbEthDriver->BulkOutSequence++;

+  Nth->BlockLength  = (UINT16)TotalLength;

+  Nth->NdpIndex     = Nth->HeaderLength;

+

+  Ndp               = (USB_NCM_DATAGRAM_POINTER_16 *)((UINT8 *)TotalPacket + Nth->NdpIndex);

+  Ndp->Signature    = USB_NCM_NDP_SIGN_16;

+  Ndp->Length       = USB_NCM_NDP_LENGTH;

+  Ndp->NextNdpIndex = 0x00;

+

+  Datagram                 = (USB_NCM_DATA_GRAM *)((UINT8 *)Ndp + sizeof (USB_NCM_DATAGRAM_POINTER_16));

+  Datagram->DatagramIndex  = Nth->HeaderLength + Ndp->Length;

+  Datagram->DatagramLength = (UINT16)*PacketLength;

+

+  CopyMem (TotalPacket + Datagram->DatagramIndex, Packet, *PacketLength);

+

+  *PacketLength = TotalLength;

+

+  Status = UsbIo->UsbBulkTransfer (

+                    UsbIo,

+                    UsbEthDriver->BulkOutEndpoint,

+                    TotalPacket,

+                    PacketLength,

+                    USB_ETHERNET_BULK_TIMEOUT,

+                    &TransStatus

+                    );

+  gBS->FreePool (TotalPacket);

+  return Status;

+}

+

+/**

+  Async USB transfer callback routine.

+

+  @param[in]  Data            Data received or sent via the USB Asynchronous Transfer, if the

+                              transfer completed successfully.

+  @param[in]  DataLength      The length of Data received or sent via the Asynchronous

+                              Transfer, if transfer successfully completes.

+  @param[in]  Context         Data passed from UsbAsyncInterruptTransfer() request.

+  @param[in]  Status          Indicates the result of the asynchronous transfer.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+EFI_STATUS

+EFIAPI

+InterruptCallback (

+  IN  VOID    *Data,

+  IN  UINTN   DataLength,

+  IN  VOID    *Context,

+  IN  UINT32  Status

+  )

+{

+  if (((EFI_USB_DEVICE_REQUEST *)Data)->Request == USB_CDC_NETWORK_CONNECTION) {

+    CopyMem (

+      (EFI_USB_DEVICE_REQUEST *)Context,

+      (EFI_USB_DEVICE_REQUEST *)Data,

+      sizeof (EFI_USB_DEVICE_REQUEST)

+      );

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is used to manage a USB device with an interrupt transfer pipe.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  IsNewTransfer     If TRUE, a new transfer will be submitted to USB controller. If

+                                FALSE, the interrupt transfer is deleted from the device's interrupt

+                                transfer queue.

+  @param[in]  PollingInterval   Indicates the periodic rate, in milliseconds, that the transfer is to be

+                                executed.This parameter is required when IsNewTransfer is TRUE. The

+                                value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.

+                                The units are in milliseconds.

+  @param[in]  Request           A pointer to the EFI_USB_DEVICE_REQUEST data.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthInterrupt (

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Request

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+  UINTN                DataLength = 0;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+  if (IsNewTransfer == TRUE) {

+    DataLength = sizeof (EFI_USB_DEVICE_REQUEST) + sizeof (USB_CONNECT_SPEED_CHANGE);

+    Status     = UsbEthDriver->UsbIo->UsbAsyncInterruptTransfer (

+                                        UsbEthDriver->UsbIo,

+                                        UsbEthDriver->InterruptEndpoint,

+                                        IsNewTransfer,

+                                        PollingInterval,

+                                        DataLength,

+                                        (EFI_ASYNC_USB_TRANSFER_CALLBACK)InterruptCallback,

+                                        Request

+                                        );

+  } else {

+    Status = UsbEthDriver->UsbIo->UsbAsyncInterruptTransfer (

+                                    UsbEthDriver->UsbIo,

+                                    UsbEthDriver->InterruptEndpoint,

+                                    IsNewTransfer,

+                                    0,

+                                    0,

+                                    NULL,

+                                    NULL

+                                    );

+  }

+

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet Mac Address.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] MacAddress    A pointer to the caller allocated USB Ethernet Mac Address.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthMacAddress (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT EFI_MAC_ADDRESS        *MacAddress

+  )

+{

+  EFI_STATUS                   Status;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthDescriptor;

+  CHAR16                       *Data;

+  CHAR16                       *DataPtr;

+  CHAR16                       TmpStr[1];

+  UINT8                        Index;

+  UINT8                        Hi;

+  UINT8                        Low;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthDescriptor);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbEthDriver->UsbIo->UsbGetStringDescriptor (

+                                  UsbEthDriver->UsbIo,

+                                  0x409,                        // English-US Language ID

+                                  UsbEthDescriptor.MacAddress,

+                                  &Data

+                                  );

+  ASSERT_EFI_ERROR (Status);

+

+  DataPtr = Data;

+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));

+    DataPtr++;

+    Hi = (UINT8)StrHexToUintn (TmpStr);

+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));

+    DataPtr++;

+    Low                     = (UINT8)StrHexToUintn (TmpStr);

+    MacAddress->Addr[Index] = (Hi << 4) | Low;

+  }

+

+  return Status;

+}

+

+/**

+  Get the USB NCM max NTB size.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] BulkSize      A pointer to the Bulk transfer data size.

+

+  @retval EFI_SUCCESS           Get the USB NCM max NTB size successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthBulkSize (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT UINTN                  *BulkSize

+  )

+{

+  *BulkSize = USB_NCM_MAX_NTB_SIZE;

+  return EFI_SUCCESS;

+}

+

+/**

+  Retrieves the USB Header functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbHeaderFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR  *UsbHeaderFunDescriptor

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbHeaderFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (UsbEthDriver->Config, HEADER_FUN_DESCRIPTOR, UsbHeaderFunDescriptor);

+  return Status;

+}

+

+/**

+  Retrieves the USB Union functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbUnionFunDescriptor  A pointer to the caller allocated USB Union Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Union Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbUnionFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Union Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbUnionFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR  *UsbUnionFunDescriptor

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbUnionFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (UsbEthDriver->Config, UNION_FUN_DESCRIPTOR, UsbUnionFunDescriptor);

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbEthFunDescriptor    A pointer to the caller allocated USB Ethernet Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Ethernet Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbEthFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Ethernet Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor

+  )

+{

+  EFI_STATUS           Status;

+  USB_ETHERNET_DRIVER  *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  if (UsbEthFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (UsbEthDriver->Config, ETHERNET_FUN_DESCRIPTOR, UsbEthFunDescriptor);

+  return Status;

+}

+

+/**

+  This request sets the Ethernet device multicast filters as specified in the

+  sequential list of 48 bit Ethernet multicast addresses.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  Number of filters.

+  @param[in]  McastAddr              A pointer to the value of the multicast addresses.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbEthMcastFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *McastAddr

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_USB_DEVICE_REQUEST       Request;

+  UINT32                       TransStatus;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_MULTICAST_FILTERS_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = Value * 6;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataOut,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                McastAddr,

+                                Request.Length,

+                                &TransStatus

+                                );

+}

+

+/**

+  This request sets up the specified Ethernet power management pattern filter as

+  described in the data structure.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                 Number of filters.

+  @param[in]  Length                Size of the power management pattern filter data.

+  @param[in]  PatternFilter         A pointer to the power management pattern filter structure.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbEthPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN UINT16                 Length,

+  IN VOID                   *PatternFilter

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_ETHERNET_DRIVER     *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = Length;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataOut,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                PatternFilter,

+                                Length,

+                                &TransStatus

+                                );

+}

+

+/**

+  This request retrieves the status of the specified Ethernet power management

+  pattern filter from the device.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  The filter number.

+  @param[out] PatternActive          A pointer to the pattern active boolean.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthPowerFilter (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 Value,

+  OUT BOOLEAN                *PatternActive

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_ETHERNET_DRIVER     *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;

+  Request.Request     = GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = USB_ETH_POWER_FILTER_LENGTH;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataIn,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                PatternActive,

+                                USB_ETH_POWER_FILTER_LENGTH,

+                                &TransStatus

+                                );

+}

+

+BIT_MAP  gTable[] = {

+  { PXE_OPFLAGS_RECEIVE_FILTER_UNICAST,            USB_ETH_PACKET_TYPE_DIRECTED      },

+  { PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST,          USB_ETH_PACKET_TYPE_BROADCAST     },

+  { PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST, USB_ETH_PACKET_TYPE_MULTICAST     },

+  { PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS,        USB_ETH_PACKET_TYPE_PROMISCUOUS   },

+  { PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST,      USB_ETH_PACKET_TYPE_ALL_MULTICAST },

+};

+

+/**

+  Convert value between PXE receive filter and USB ETH packet filter.

+

+  @param[in]  Value      PXE filter data.

+  @param[out] CdcFilter  A pointer to the Ethernet Packet Filter Bitmap value converted by PXE_OPFLAGS.

+

+**/

+VOID

+ConvertFilter (

+  IN  UINT16  Value,

+  OUT UINT16  *CdcFilter

+  )

+{

+  UINT32  Index;

+  UINT32  Count;

+

+  Count = sizeof (gTable)/sizeof (gTable[0]);

+

+  for (Index = 0; (gTable[Index].Src != 0) && (Index < Count); Index++) {

+    if (gTable[Index].Src & Value) {

+      *CdcFilter |= gTable[Index].Dst;

+    }

+  }

+}

+

+/**

+  This request is used to configure device Ethernet packet filter settings.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value             Packet Filter Bitmap.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbEthPacketFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_ETHERNET_DRIVER     *UsbEthDriver;

+  UINT16                  CommandFilter = 0;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  ConvertFilter (Value, &CommandFilter);

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_PACKET_FILTER_REQ;

+  Request.Value       = CommandFilter;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = USB_ETH_PACKET_FILTER_LENGTH;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbNoData,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                NULL,

+                                USB_ETH_PACKET_FILTER_LENGTH,

+                                &TransStatus

+                                );

+}

+

+/**

+  This request is used to retrieve a statistic based on the feature selector.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  FeatureSelector       Value of the feature selector.

+  @param[out] Statistic             A pointer to the 32 bit unsigned integer.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthStatistic (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 FeatureSelector,

+  OUT VOID                   *Statistic

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_USB_DEVICE_REQUEST       Request;

+  UINT32                       TransStatus;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+  USB_ETHERNET_DRIVER          *UsbEthDriver;

+

+  UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (UsbEthFunDescriptor.EthernetStatistics == 0) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;

+  Request.Request     = GET_ETH_STATISTIC_REQ;

+  Request.Value       = FeatureSelector;

+  Request.Index       = UsbEthDriver->NumOfInterface;

+  Request.Length      = USB_ETH_STATISTIC;

+

+  return UsbEthDriver->UsbIo->UsbControlTransfer (

+                                UsbEthDriver->UsbIo,

+                                &Request,

+                                EfiUsbDataIn,

+                                USB_ETHERNET_TRANSFER_TIMEOUT,

+                                Statistic,

+                                USB_ETH_STATISTIC,

+                                &TransStatus

+                                );

+}

diff --git a/UsbNetworkPkg/UsbNetworkPkg.dec b/UsbNetworkPkg/UsbNetworkPkg.dec
new file mode 100644
index 0000000000..4ffafd99ed
--- /dev/null
+++ b/UsbNetworkPkg/UsbNetworkPkg.dec
@@ -0,0 +1,32 @@
+## @file
+#  This package defines Usb network specific interfaces and library classes
+#  as well as configuration for standard edk2 packages.
+#
+#  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+#  Subject to AMI licensing agreement.
+##
+
+[Defines]
+  DEC_SPECIFICATION              = 0x00010005
+  PACKAGE_NAME                   = UsbNetworkPkg
+  PACKAGE_GUID                   = abfab91e-37ea-4cb4-80a6-563dbb0bcec6
+  PACKAGE_VERSION                = 0.1
+
+[Includes]
+  Include
+
+[Guids]
+  ## Usb Network package token space GUID
+  gUsbNetworkPkgTokenSpaceGuid  = { 0xA1231E82, 0x21B8, 0x4204, { 0x92, 0xBB, 0x37, 0x3A, 0xFB, 0x01, 0xC6, 0xA1 } }
+
+[PcdsFeatureFlag]
+
+  ## Set the PCD 'UsbCdcEcmSupport' to 'TRUE' if 'Usb Cdc Ecm device' need to be enabled.
+  gUsbNetworkPkgTokenSpaceGuid.UsbCdcEcmSupport|FALSE|BOOLEAN|0x00000001
+
+  ## Set the PCD 'UsbCdcNcmSupport' to 'TRUE' if 'Usb Cdc Ncm device' need to be enabled.
+  gUsbNetworkPkgTokenSpaceGuid.UsbCdcNcmSupport|FALSE|BOOLEAN|0x00000002
+
+  ## Set the PCD 'UsbRndisSupport' to 'TRUE' if 'Usb Rndis device' need to be enabled.
+  gUsbNetworkPkgTokenSpaceGuid.UsbRndisSupport|TRUE|BOOLEAN|0x00000003
+
diff --git a/UsbNetworkPkg/UsbRndis/ComponentName.c b/UsbNetworkPkg/UsbRndis/ComponentName.c
new file mode 100644
index 0000000000..6b7891afd9
--- /dev/null
+++ b/UsbNetworkPkg/UsbRndis/ComponentName.c
@@ -0,0 +1,172 @@
+/** @file

+  This file contains code for USB RNDIS Driver Component

+  Name definitions

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "UsbRndis.h"

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gUsbRndisDriverNameTable[] = {

+  {

+    "eng;en",

+    L"USB RNDIS Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+EFI_STATUS

+EFIAPI

+UsbRndisComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+EFI_STATUS

+EFIAPI

+UsbRndisComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  );

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUsbRndisComponentName = {

+  UsbRndisComponentNameGetDriverName,

+  UsbRndisComponentNameGetControllerName,

+  "eng"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gUsbRndisComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)UsbRndisComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbRndisComponentNameGetControllerName,

+  "en"

+};

+

+/**

+  Retrieves a Unicode string that is the user readable name of the driver.

+

+  This function retrieves the user readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+  @param[out] DriverName        A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           gUsbRndisDriverNameTable,

+           DriverName,

+           (BOOLEAN)(This == &gUsbRndisComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+  @param[in]  Controller        The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+  @param[out] ControllerName    A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisComponentNameGetControllerName (

+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN CHAR8                        *Language,

+  OUT CHAR16                      **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/UsbNetworkPkg/UsbRndis/UsbRndis.c b/UsbNetworkPkg/UsbRndis/UsbRndis.c
new file mode 100644
index 0000000000..a75355825b
--- /dev/null
+++ b/UsbNetworkPkg/UsbRndis/UsbRndis.c
@@ -0,0 +1,848 @@
+/** @file

+  This file contains code for USB Remote Network Driver

+  Interface Spec. Driver Binding

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "UsbRndis.h"

+

+EFI_GUID                             gUsbEthProtocolGuid = USB_ETHERNET_PROTOCOL_GUID;

+extern EFI_COMPONENT_NAME2_PROTOCOL  gUsbRndisComponentName2;

+

+EFI_DRIVER_BINDING_PROTOCOL  gUsbRndisDriverBinding = {

+  UsbRndisDriverSupported,

+  UsbRndisDriverStart,

+  UsbRndisDriverStop,

+  USB_RNDIS_DRIVER_VERSION,

+  NULL,

+  NULL

+};

+

+/**

+  Check if this interface is USB Rndis SubType

+

+  @param[in]  UsbIo  A pointer to the EFI_USB_IO_PROTOCOL instance.

+

+  @retval TRUE   USB Rndis SubType.

+  @retval FALSE  Not USB Rndis SubType.

+

+**/

+BOOLEAN

+IsSupportedDevice (

+  IN EFI_USB_IO_PROTOCOL  *UsbIo

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  // Check specific device/RNDIS and CDC-DATA

+  if (((InterfaceDescriptor.InterfaceClass == 0x2) &&

+       (InterfaceDescriptor.InterfaceSubClass == 0x2) &&

+       (InterfaceDescriptor.InterfaceProtocol == 0xFF)) || \

+      ((InterfaceDescriptor.InterfaceClass == 0xEF) &&

+       (InterfaceDescriptor.InterfaceSubClass == 0x4) &&

+       (InterfaceDescriptor.InterfaceProtocol == 0x1)) || \

+      ((InterfaceDescriptor.InterfaceClass == 0xA) &&

+       (InterfaceDescriptor.InterfaceSubClass == 0x0) &&

+       (InterfaceDescriptor.InterfaceProtocol == 0x00))

+      )

+  {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Check if this interface is USB Rndis SubType but not CDC Data interface

+

+  @param[in]  UsbIo  A pointer to the EFI_USB_IO_PROTOCOL instance.

+

+  @retval TRUE   USB Rndis SubType.

+  @retval FALSE  Not USB Rndis SubType.

+

+**/

+BOOLEAN

+IsRndisInterface (

+  IN EFI_USB_IO_PROTOCOL  *UsbIo

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  // Check for specific device/RNDIS and CDC-DATA

+  if (((InterfaceDescriptor.InterfaceClass == 0x2) &&

+       (InterfaceDescriptor.InterfaceSubClass == 0x2) &&

+       (InterfaceDescriptor.InterfaceProtocol == 0xFF)) || \

+      ((InterfaceDescriptor.InterfaceClass == 0xEF) &&

+       (InterfaceDescriptor.InterfaceSubClass == 0x4) &&

+       (InterfaceDescriptor.InterfaceProtocol == 0x1))

+      )

+  {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Check if the USB RNDIS and USB CDC Data interfaces are from the same device.

+

+  @param[in]  UsbRndisDataPath  A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[in]  UsbCdcDataPath    A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+

+  @retval EFI_SUCCESS           Is the same device.

+  @retval EFI_UNSUPPORTED       Is not the same device.

+

+**/

+EFI_STATUS

+IsSameDevice (

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath,

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath

+  )

+{

+  DEBUG ((EFI_D_VERBOSE, "IsSameDevice Entry \n"));

+  while (1) {

+    if (IsDevicePathEnd (NextDevicePathNode (UsbRndisDataPath))) {

+      if (((USB_DEVICE_PATH *)UsbRndisDataPath)->ParentPortNumber ==

+          ((USB_DEVICE_PATH *)UsbCdcDataPath)->ParentPortNumber)

+      {

+        return EFI_SUCCESS;

+      } else {

+        return EFI_UNSUPPORTED;

+      }

+    } else {

+      if (CompareMem (UsbCdcDataPath, UsbRndisDataPath, sizeof (EFI_DEVICE_PATH_PROTOCOL)) != 0) {

+        return EFI_UNSUPPORTED;

+      }

+

+      UsbRndisDataPath = NextDevicePathNode (UsbRndisDataPath);

+      UsbCdcDataPath   = NextDevicePathNode (UsbCdcDataPath);

+    }

+  }

+

+  DEBUG ((EFI_D_VERBOSE, "IsSameDevice Exit \n"));

+}

+

+/**

+  Check if the USB CDC Data(UsbIo) installed and return USB CDC Data Handle.

+

+  @param[in]  UsbIo  A pointer to the EFI_USB_IO_PROTOCOL instance.

+

+  @retval TRUE              USB CDC Data(UsbIo) installed.

+  @retval FALSE             USB CDC Data(UsbIo) did not installed.

+

+**/

+BOOLEAN

+IsUsbCdcData (

+  IN EFI_USB_IO_PROTOCOL  *UsbIo

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  // Check for CDC-DATA

+  if ((InterfaceDescriptor.InterfaceClass == 0xA) &&

+      (InterfaceDescriptor.InterfaceSubClass == 0x0) &&

+      (InterfaceDescriptor.InterfaceProtocol == 0x0))

+  {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Check if the USB Rndis(UsbIo) installed

+

+  @param[in]  UsbIo        A pointer to the EFI_USB_IO_PROTOCOL instance.

+

+  @retval TRUE              USB Rndis(UsbIo) installed.

+  @retval FALSE             USB Rndis(UsbIo) did not installed.

+

+**/

+BOOLEAN

+IsUsbRndis (

+  IN EFI_USB_IO_PROTOCOL  *UsbIo

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  // Check for Rndis

+  if ((InterfaceDescriptor.InterfaceClass == 0x2) &&

+      (InterfaceDescriptor.InterfaceSubClass == 0x2) &&

+      (InterfaceDescriptor.InterfaceProtocol == 0xFF))

+  {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Control comes here when a CDC device is found.Check if a RNDIS interface is already found for this device or not.

+  For one device two USBIO will be installed each for CDC and RNDIS interface.

+

+  @param[in]  UsbEthPath        A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.

+  @param[out] UsbRndisDevice    A pointer to the USB_RNDIS_DEVICE Data.

+

+  @retval EFI_SUCCESS             The USB_RNDIS_DEVICE matching this CDC Data is found.

+  @retval EFI_NOT_FOUND           The USB_RNDIS_DEVICE matching this CDC Data is not found.

+

+**/

+EFI_STATUS

+UpdateRndisDevice (

+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath,

+  OUT USB_RNDIS_DEVICE          **UsbRndisDevice

+  )

+{

+  EFI_STATUS                Status;

+  UINTN                     Index;

+  UINTN                     HandleCount;

+  EFI_HANDLE                *HandleBuffer;

+  USB_ETHERNET_PROTOCOL     *UsbEthDevice;

+  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath;

+  EFI_USB_IO_PROTOCOL       *UsbIo;

+  BOOLEAN                   IsRndisInterfaceFlag = FALSE;

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gUsbEthProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gUsbEthProtocolGuid,

+                    (VOID **)&UsbEthDevice

+                    );

+    if (EFI_ERROR (Status)) {

+      continue;

+    }

+

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    if (EFI_ERROR (Status)) {

+      continue;

+    }

+

+    IsRndisInterfaceFlag = IsRndisInterface (UsbIo);

+    if (IsRndisInterfaceFlag == FALSE) {

+      continue;

+    }

+

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiDevicePathProtocolGuid,

+                    (VOID **)&UsbRndisDataPath

+                    );

+    Status = IsSameDevice (UsbRndisDataPath, UsbCdcDataPath);

+

+    DEBUG ((EFI_D_VERBOSE, "Rndis IsSameDevice %r\n", Status));

+

+    if (!EFI_ERROR (Status)) {

+      *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDevice);

+      gBS->FreePool (HandleBuffer);

+      return EFI_SUCCESS;

+    }

+  }   // End of For loop

+

+  gBS->FreePool (HandleBuffer);

+  return EFI_NOT_FOUND;

+}

+

+/**

+

+  For the given Rndis Device, find a matching CDC device already exists or not. If found update the handle

+  and UsbIO protocol.

+

+  @param[in]  UsbRndisDevice        A pointer to the USB_RNDIS_DEVICE data.

+

+**/

+VOID

+FindMatchingCdcData (

+  IN USB_RNDIS_DEVICE  *UsbRndisDevice

+  )

+{

+  EFI_STATUS                Status;

+  UINTN                     Index;

+  UINTN                     HandleCount;

+  EFI_HANDLE                *HandleBuffer;

+  EFI_USB_IO_PROTOCOL       *UsbIo;

+  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath;

+  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath;

+

+  // Find the parent RNDIS and update the UsbIo for the CDC device

+  Status = gBS->HandleProtocol (

+                  UsbRndisDevice->UsbRndisHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **)&UsbRndisDataPath

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return;

+  }

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiUsbIoProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    ASSERT_EFI_ERROR (Status);

+

+    if (IsUsbCdcData (UsbIo)) {

+      DEBUG ((EFI_D_VERBOSE, "Rndis FindMatchingCdcData CDCData interface found\n"));

+

+      Status = gBS->HandleProtocol (

+                      HandleBuffer[Index],

+                      &gEfiDevicePathProtocolGuid,

+                      (VOID **)&UsbCdcDataPath

+                      );

+      if (EFI_ERROR (Status)) {

+        DEBUG ((EFI_D_VERBOSE, "Rndis CDCData DevicePath not found\n"));

+        gBS->FreePool (HandleBuffer);

+        return;

+      }

+

+      Status = IsSameDevice (UsbRndisDataPath, UsbCdcDataPath);

+      DEBUG ((EFI_D_VERBOSE, "Rndis IsSameDevice %r\n", Status));

+      if (!EFI_ERROR (Status)) {

+        UsbRndisDevice->UsbCdcDataHandle = HandleBuffer[Index];

+        UsbRndisDevice->UsbIoCdcData     = UsbIo;

+        GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);

+        gBS->FreePool (HandleBuffer);

+        return;

+      }

+    }

+  }   // End of For loop

+

+  gBS->FreePool (HandleBuffer);

+}

+

+/**

+

+  For the given UsbIo CdcData, find a matching RNDIS device already exists or not.

+

+  @param[in]  CdcHandle       A pointer to the EFI_HANDLE for USB CDC Data.

+  @param[out] CdcUsbIo        A pointer for retrieve the EFI_USB_IO_PROTOCOL instance.

+  @param[out] RndisHandle     A pointer for retrieve the handle of RNDIS device.

+

+  @retval EFI_SUCCESS             The USB_RNDIS_DEVICE matching this CDC Data is found.

+  @retval EFI_NOT_FOUND           The USB_RNDIS_DEVICE matching this CDC Data is not found.

+

+**/

+EFI_STATUS

+EFIAPI

+FindMatchingRndisDev (

+  IN  EFI_HANDLE           CdcHandle,

+  OUT EFI_USB_IO_PROTOCOL  **CdcUsbIo,

+  OUT EFI_HANDLE           *RndisHandle

+  )

+{

+  EFI_STATUS                Status;

+  UINTN                     Index;

+  UINTN                     HandleCount;

+  EFI_HANDLE                *HandleBuffer;

+  EFI_USB_IO_PROTOCOL       *UsbIo;

+  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath;

+  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath;

+

+  // Find the parent RNDIS and update the UsbIo for the CDC device

+  Status = gBS->HandleProtocol (

+                  CdcHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **)&UsbCdcDataPath

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiUsbIoProtocolGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[Index],

+                    &gEfiUsbIoProtocolGuid,

+                    (VOID **)&UsbIo

+                    );

+    ASSERT_EFI_ERROR (Status);

+

+    if (IsUsbRndis (UsbIo)) {

+      Status = gBS->HandleProtocol (

+                      HandleBuffer[Index],

+                      &gEfiDevicePathProtocolGuid,

+                      (VOID **)&UsbRndisDataPath

+                      );

+      if (EFI_ERROR (Status)) {

+        DEBUG ((EFI_D_ERROR, "Usb Rndis DevicePath not found\n"));

+        break;

+      }

+

+      Status = IsSameDevice (UsbRndisDataPath, UsbCdcDataPath);

+

+      if (!EFI_ERROR (Status)) {

+        *RndisHandle = HandleBuffer[Index];

+        *CdcUsbIo    = UsbIo;

+        gBS->FreePool (HandleBuffer);

+        return Status;

+      }

+    }

+  }   // End of For loop

+

+  gBS->FreePool (HandleBuffer);

+

+  return EFI_NOT_FOUND;

+}

+

+/**

+  USB Rndis Driver Binding Support.

+

+  @param[in]  This                    Protocol instance pointer.

+  @param[in]  ControllerHandle        Handle of device to test.

+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child

+                                      device to start.

+

+  @retval EFI_SUCCESS                 This driver supports this device.

+  @retval EFI_ALREADY_STARTED         This driver is already running on this device.

+  @retval other                       This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisDriverSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS           Status;

+  EFI_USB_IO_PROTOCOL  *UsbIo = NULL;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = IsSupportedDevice (UsbIo) ? EFI_SUCCESS : EFI_UNSUPPORTED;

+

+  gBS->CloseProtocol (

+         ControllerHandle,

+         &gEfiUsbIoProtocolGuid,

+         This->DriverBindingHandle,

+         ControllerHandle

+         );

+  return Status;

+}

+

+/**

+  USB RNDIS Driver Binding Start.

+

+  @param[in]  This                    Protocol instance pointer.

+  @param[in]  ControllerHandle        Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child

+                                      device to start.

+

+  @retval EFI_SUCCESS                 This driver is added to ControllerHandle

+  @retval EFI_DEVICE_ERROR            This driver could not be started due to a device error

+  @retval EFI_OUT_OF_RESOURCES        The driver could not install successfully due to a lack of resources.

+  @retval other                       This driver does not support this device

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  EFI_STATUS                    Status;

+  USB_RNDIS_DEVICE              *UsbRndisDevice;

+  EFI_DEVICE_PATH_PROTOCOL      *UsbEthPath;

+  EFI_USB_IO_PROTOCOL           *UsbIo;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+  EFI_HANDLE                    RndisHandle = ControllerHandle;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **)&UsbEthPath,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return Status;

+  }

+

+  gBS->CloseProtocol (

+         ControllerHandle,

+         &gEfiDevicePathProtocolGuid,

+         This->DriverBindingHandle,

+         ControllerHandle

+         );

+

+  DEBUG ((EFI_D_ERROR, "UsbRndisDriverStart %x\n", ControllerHandle));

+

+  // Controls come here for RNDIS and CDC. If it is CDC, check whether RNDIS is present on the same controller or not.

+  if (IsUsbCdcData (UsbIo)) {

+    // Find the parent RNDIS and update the UsbIo for the CDC device

+    Status = UpdateRndisDevice (UsbEthPath, &UsbRndisDevice);

+

+    if (!EFI_ERROR (Status)) {

+      DEBUG ((EFI_D_VERBOSE, "Rndis Matching interface found\n"));

+      UsbRndisDevice->UsbCdcDataHandle = ControllerHandle;

+      UsbRndisDevice->UsbIoCdcData     = UsbIo;

+      GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);

+      return Status;

+    } else {

+      // Check if RnDis exist

+      Status = FindMatchingRndisDev (ControllerHandle, &UsbIo, &RndisHandle);

+

+      if (EFI_ERROR (Status)) {

+        gBS->CloseProtocol (

+               ControllerHandle,

+               &gEfiUsbIoProtocolGuid,

+               This->DriverBindingHandle,

+               ControllerHandle

+               );

+        return Status;

+      }

+    }

+  }

+

+  UsbRndisDevice = AllocateZeroPool (sizeof (USB_RNDIS_DEVICE));

+

+  if (!UsbRndisDevice) {

+    DEBUG ((EFI_D_ERROR, "AllocateZeroPool Fail\n"));

+

+    gBS->CloseProtocol (

+           ControllerHandle,

+           &gEfiUsbIoProtocolGuid,

+           This->DriverBindingHandle,

+           ControllerHandle

+           );

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = LoadAllDescriptor (UsbIo, &UsbRndisDevice->Config);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+  ASSERT_EFI_ERROR (Status);

+

+  UsbRndisDevice->Signature                          = USB_RNDIS_SIGNATURE;

+  UsbRndisDevice->NumOfInterface                     = Interface.InterfaceNumber;

+  UsbRndisDevice->UsbRndisHandle                     = RndisHandle;

+  UsbRndisDevice->UsbCdcDataHandle                   = 0;

+  UsbRndisDevice->UsbIo                              = UsbIo;

+  UsbRndisDevice->UsbEth.UsbEthReceive               = RndisUndiReceive;

+  UsbRndisDevice->UsbEth.UsbEthTransmit              = RndisUndiTransmit;

+  UsbRndisDevice->UsbEth.UsbEthInterrupt             = UsbRndisInterrupt;

+  UsbRndisDevice->UsbEth.UsbEthMacAddress            = GetUsbEthMacAddress;

+  UsbRndisDevice->UsbEth.UsbEthMaxBulkSize           = UsbEthBulkSize;

+  UsbRndisDevice->UsbEth.UsbHeaderFunDescriptor      = GetUsbHeaderFunDescriptor;

+  UsbRndisDevice->UsbEth.UsbUnionFunDescriptor       = GetUsbUnionFunDescriptor;

+  UsbRndisDevice->UsbEth.UsbEthFunDescriptor         = GetUsbRndisFunDescriptor;

+  UsbRndisDevice->UsbEth.SetUsbEthMcastFilter        = SetUsbRndisMcastFilter;

+  UsbRndisDevice->UsbEth.SetUsbEthPowerPatternFilter = SetUsbRndisPowerFilter;

+  UsbRndisDevice->UsbEth.GetUsbEthPoewrPatternFilter = GetUsbRndisPowerFilter;

+  UsbRndisDevice->UsbEth.SetUsbEthPacketFilter       = SetUsbRndisPacketFilter;

+  UsbRndisDevice->UsbEth.GetUsbEthStatistic          = GetRndisStatistic;

+

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetState        = RndisDummyReturn;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStart           = RndisUndiStart;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStop            = RndisUndiStop;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetInitInfo     = RndisUndiGetInitInfo;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetConfigInfo   = RndisUndiGetConfigInfo;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiInitialize      = RndisUndiInitialize;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiReset           = RndisUndiReset;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiShutdown        = RndisUndiShutdown;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiInterruptEnable = RndisDummyReturn;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiReceiveFilter   = RndisUndiReceiveFilter;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStationAddress  = RndisDummyReturn;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStatistics      = NULL;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiMcastIp2Mac     = RndisDummyReturn;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiNvData          = RndisDummyReturn;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetStatus       = RndisUndiGetStatus;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiFillHeader      = RndisDummyReturn;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiTransmit        = NULL;

+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiReceive         = NULL;

+

+  UsbRndisDevice->MaxTransferSize       = RNDIS_MAX_TRANSFER_SIZE;

+  UsbRndisDevice->MaxPacketsPerTransfer = 1;

+  UsbRndisDevice->PacketAlignmentFactor = 0;

+

+  InitializeListHead (&UsbRndisDevice->ReceivePacketList);

+

+  // This is a RNDIS interface. See whether CDC-DATA interface has already been connected or not

+  FindMatchingCdcData (UsbRndisDevice);

+

+  if (UsbRndisDevice->UsbIoCdcData) {

+    Status = gBS->InstallProtocolInterface (

+                    &ControllerHandle,

+                    &gUsbEthProtocolGuid,

+                    EFI_NATIVE_INTERFACE,

+                    &(UsbRndisDevice->UsbEth)

+                    );

+    if (EFI_ERROR (Status)) {

+      gBS->CloseProtocol (

+             ControllerHandle,

+             &gEfiUsbIoProtocolGuid,

+             This->DriverBindingHandle,

+             ControllerHandle

+             );

+

+      gBS->FreePool (UsbRndisDevice->Config);

+      gBS->FreePool (UsbRndisDevice);

+      return Status;

+    }

+

+    GetEndpoint (UsbRndisDevice->UsbIo, UsbRndisDevice);

+

+    DEBUG ((EFI_D_ERROR, "Rndis DeviceHandle %r\n", UsbRndisDevice->UsbRndisHandle));

+    DEBUG ((EFI_D_ERROR, "CDC DeviceHandle %r\n", UsbRndisDevice->UsbCdcDataHandle));

+    return EFI_SUCCESS;

+  }

+

+  gBS->FreePool (UsbRndisDevice->Config);

+  gBS->FreePool (UsbRndisDevice);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  CheckandStopRndisDevice

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of device to bind driver to.

+

+  @retval EFI_SUCCESS          This driver is added to ControllerHandle

+  @retval EFI_DEVICE_ERROR     This driver could not be started due to a device error

+  @retval other                This driver does not support this device

+

+**/

+EFI_STATUS

+EFIAPI

+CheckandStopRndisDevice (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle

+  )

+{

+  EFI_STATUS           Status;

+  EFI_USB_IO_PROTOCOL  *UsbIo;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  (VOID **)&UsbIo,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (IsUsbRndis (UsbIo)) {

+    Status = gBS->CloseProtocol (

+                    ControllerHandle,

+                    &gEfiUsbIoProtocolGuid,

+                    This->DriverBindingHandle,

+                    ControllerHandle

+                    );

+    DEBUG ((EFI_D_ERROR, "Rndis ControllerHandle Stop %r\n", Status));

+    return Status;

+  }

+

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  USB Rndis Driver Binding Stop.

+

+  @param[in]  This                  Protocol instance pointer.

+  @param[in]  ControllerHandle      Handle of device to stop driver on

+  @param[in]  NumberOfChildren      Number of Handles in ChildHandleBuffer. If number of

+                                    children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer     List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS               This driver is removed ControllerHandle

+  @retval other                     This driver was not removed from this device

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  )

+{

+  EFI_STATUS             Status;

+  USB_ETHERNET_PROTOCOL  *UsbEthProtocol;

+  USB_RNDIS_DEVICE       *UsbRndisDevice;

+

+  DEBUG ((EFI_D_ERROR, "UsbRndisDriverStop ControllerHandle %lx\n", ControllerHandle));

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  (VOID **)&UsbEthProtocol,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    Status = CheckandStopRndisDevice (This, ControllerHandle);

+

+    DEBUG ((EFI_D_ERROR, "CheckandStopRndisDevice %r\n", Status));

+

+    return Status;

+  }

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthProtocol);

+

+  Status = gBS->CloseProtocol (

+                  UsbRndisDevice->UsbCdcDataHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  This->DriverBindingHandle,

+                  UsbRndisDevice->UsbCdcDataHandle

+                  );

+

+  Status = gBS->UninstallProtocolInterface (

+                  ControllerHandle,

+                  &gUsbEthProtocolGuid,

+                  UsbEthProtocol

+                  );

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "EFI_ERROR %r\n", Status));

+

+    return Status;

+  }

+

+  Status = gBS->CloseProtocol (

+                  ControllerHandle,

+                  &gEfiUsbIoProtocolGuid,

+                  This->DriverBindingHandle,

+                  ControllerHandle

+                  );

+

+  gBS->FreePool (UsbRndisDevice->Config);

+  gBS->FreePool (UsbRndisDevice);

+

+  DEBUG ((EFI_D_ERROR, "UsbRndisDriverStop %r\n", Status));

+  return Status;

+}

+

+/**

+  Entrypoint of RNDIS Driver.

+

+  This function is the entrypoint of RNDIS Driver. It installs Driver Binding

+  Protocols together with Component Name Protocols.

+

+  @param[in]  ImageHandle       The firmware allocated handle for the EFI image.

+  @param[in]  SystemTable       A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The entry point is executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisEntry (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  gUsbRndisDriverBinding.DriverBindingHandle = ImageHandle;

+  gUsbRndisDriverBinding.ImageHandle         = ImageHandle;

+

+  return gBS->InstallMultipleProtocolInterfaces (

+                &gUsbRndisDriverBinding.DriverBindingHandle,

+                &gEfiDriverBindingProtocolGuid,

+                &gUsbRndisDriverBinding,

+                &gEfiComponentName2ProtocolGuid,

+                &gUsbRndisComponentName2,

+                NULL

+                );

+}

diff --git a/UsbNetworkPkg/UsbRndis/UsbRndis.h b/UsbNetworkPkg/UsbRndis/UsbRndis.h
new file mode 100644
index 0000000000..680a9e67c9
--- /dev/null
+++ b/UsbNetworkPkg/UsbRndis/UsbRndis.h
@@ -0,0 +1,569 @@
+/** @file

+  Header file for for USB Rndis driver

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#ifndef _USB_RNDIS_H_

+#define _USB_RNDIS_H_

+

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiUsbLib.h>

+#include <Protocol/UsbIo.h>

+#include <Protocol/UsbEthernetProtocol.h>

+

+typedef struct _REMOTE_NDIS_MSG_HEADER REMOTE_NDIS_MSG_HEADER;

+

+typedef struct {

+  UINT32                       Signature;

+  USB_ETHERNET_PROTOCOL        UsbEth;

+  EFI_HANDLE                   UsbCdcDataHandle;

+  EFI_HANDLE                   UsbRndisHandle;

+  EFI_USB_IO_PROTOCOL          *UsbIo;

+  EFI_USB_IO_PROTOCOL          *UsbIoCdcData;

+  EFI_USB_CONFIG_DESCRIPTOR    *Config;

+  UINT8                        NumOfInterface;

+  UINT8                        BulkInEndpoint;

+  UINT8                        BulkOutEndpoint;

+  UINT8                        InterrupEndpoint;

+  EFI_MAC_ADDRESS              MacAddress;

+  UINT32                       RequestId;

+  UINT32                       Medium;

+  UINT32                       MaxPacketsPerTransfer;

+  UINT32                       MaxTransferSize;

+  UINT32                       PacketAlignmentFactor;

+  LIST_ENTRY                   ReceivePacketList;

+} USB_RNDIS_DEVICE;

+

+#define USB_RNDIS_DRIVER_VERSION       1

+#define USB_TX_ETHERNET_BULK_TIMEOUT   3000

+#define USB_RX_ETHERNET_BULK_TIMEOUT   3

+#define USB_ETHERNET_TRANSFER_TIMEOUT  200

+

+#define LAN_BULKIN_CMD_CONTROL  1

+#define MAXIMUM_STOPBULKIN_CNT  300            // Indicating maximum counts for waiting bulk in command

+#define MINIMUM_STOPBULKIN_CNT  3              // Indicating minimum counts for waiting bulk in command

+#define BULKIN_CMD_POLLING_CNT  300            // Indicating the waiting counts for send bulk in command when system pending

+

+#define USB_RNDIS_SIGNATURE  SIGNATURE_32('r', 'n', 'd', 's')

+#define USB_RNDIS_DEVICE_FROM_THIS(a)  CR (a, USB_RNDIS_DEVICE, UsbEth, USB_RNDIS_SIGNATURE)

+

+struct BIT_MAP {

+  unsigned int    Src;

+  unsigned int    Dst;

+};

+

+EFI_STATUS

+EFIAPI

+UsbRndisDriverSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+UsbRndisDriverStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+EFI_STATUS

+EFIAPI

+UsbRndisDriverStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer

+  );

+

+EFI_STATUS

+LoadAllDescriptor (

+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,

+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc

+  );

+

+BOOLEAN

+NextDescriptor (

+  IN EFI_USB_CONFIG_DESCRIPTOR  *Desc,

+  IN OUT UINTN                  *Offset

+  );

+

+EFI_STATUS

+GetFunctionalDescriptor (

+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,

+  IN  UINT8                      FunDescriptorType,

+  OUT VOID                       *DataBuffer

+  );

+

+VOID

+GetEndpoint (

+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,

+  IN OUT  USB_RNDIS_DEVICE     *UsbRndisDevice

+  );

+

+EFI_STATUS

+EFIAPI

+UsbRndisInterrupt (

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Requst

+  );

+

+EFI_STATUS

+EFIAPI

+InterruptCallback (

+  IN  VOID    *Data,

+  IN  UINTN   DataLength,

+  IN  VOID    *Context,

+  IN  UINT32  Status

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbEthMacAddress (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT EFI_MAC_ADDRESS        *MacAddress

+  );

+

+EFI_STATUS

+EFIAPI

+UsbEthBulkSize (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT UINTN                  *BulkSize

+  );

+

+EFI_STATUS

+EFIAPI

+RndisDummyReturn (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiStart (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiStop (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiGetInitInfo (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiGetConfigInfo (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiInitialize (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiTransmit (

+  IN      PXE_CDB                *Cdb,

+  IN      USB_ETHERNET_PROTOCOL  *This,

+  IN      VOID                   *BulkOutData,

+  IN OUT  UINTN                  *DataLength

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiReceive (

+  IN     PXE_CDB                *Cdb,

+  IN     USB_ETHERNET_PROTOCOL  *This,

+  IN OUT VOID                   *BulkInData,

+  IN OUT UINTN                  *DataLength

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiReset (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiShutdown (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiReceiveFilter (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+RndisUndiGetStatus (

+  IN PXE_CDB   *Cdb,

+  IN NIC_DATA  *Nic

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbHeaderFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR  *UsbHeaderFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbUnionFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR  *UsbUnionFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbRndisFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbRndisMcastFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *McastAddr

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbRndisPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN UINT16                 Length,

+  IN VOID                   *PatternFilter

+  );

+

+EFI_STATUS

+EFIAPI

+GetUsbRndisPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN BOOLEAN                *PatternActive

+  );

+

+EFI_STATUS

+EFIAPI

+SetUsbRndisPacketFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value

+  );

+

+EFI_STATUS

+EFIAPI

+GetRndisStatistic (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *Statistic

+  );

+

+EFI_STATUS

+SendRndisSetMsg (

+  IN  USB_RNDIS_DEVICE  *UsbRndisDevice,

+  UINT8                 Oid,

+  UINT32                Length,

+  UINT8                 *Buf

+  );

+

+EFI_STATUS

+RndisControlMsg (

+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,

+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,

+  OUT REMOTE_NDIS_MSG_HEADER  *RndisMsgResponse

+  );

+

+EFI_STATUS

+RndisTransmitDataMsg (

+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,

+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,

+  UINTN                       *TransferLength

+  );

+

+EFI_STATUS

+RndisReceiveDataMsg (

+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,

+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,

+  UINTN                       *TransferLength

+  );

+

+VOID

+PrintRndisMsg (

+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg

+  );

+

+#define RNDIS_MAJOR_VERSION      0x00000001

+#define RNDIS_MINOR_VERSION      0x00000000

+#define RNDIS_MAX_TRANSFER_SIZE  0x4000

+

+#define RNDIS_PACKET_MSG           0x00000001

+#define RNDIS_INITIALIZE_MSG       0x00000002

+#define RNDIS_INITIALIZE_CMPLT     0x80000002

+#define RNDIS_HLT_MSG              0x00000003

+#define RNDIS_QUERY_MSG            0x00000004

+#define RNDIS_QUERY_CMPLT          0x80000004

+#define RNDIS_SET_MSG              0x00000005

+#define RNDIS_SET_CMPLT            0x80000005

+#define RNDIS_RESET_MSG            0x00000006

+#define RNDIS_RESET_CMPLT          0x80000006

+#define RNDIS_INDICATE_STATUS_MSG  0x00000007

+#define RNDIS_KEEPALIVE_MSG        0x00000008

+#define RNDIS_KEEPALIVE_CMPLT      0x80000008

+

+#define RNDIS_STATUS_SUCCESS           0x00000000

+#define RNDIS_STATUS_FAILURE           0xC0000001

+#define RNDIS_STATUS_INVALID_DATA      0xC0010015

+#define RNDIS_STATUS_NOT_SUPPORTED     0xC00000BB

+#define RNDIS_STATUS_MEDIA_CONNECT     0x4001000B

+#define RNDIS_STATUS_MEDIA_DISCONNECT  0x4001000C

+

+#define RNDIS_CONTROL_TIMEOUT    10000            // 10sec

+#define RNDIS_KEEPALIVE_TIMEOUT  5000             // 5sec

+

+#define SEND_ENCAPSULATED_COMMAND  0x00000000

+#define GET_ENCAPSULATED_RESPONSE  0x00000001

+

+//

+// General Objects

+//

+// Taken from NTDDNDIS.H

+#define OID_GEN_SUPPORTED_LIST         0x00010101

+#define OID_GEN_HARDWARE_STATUS        0x00010102

+#define OID_GEN_MEDIA_SUPPORTED        0x00010103

+#define OID_GEN_MEDIA_IN_USE           0x00010104

+#define OID_GEN_MAXIMUM_LOOKAHEAD      0x00010105

+#define OID_GEN_MAXIMUM_FRAME_SIZE     0x00010106

+#define OID_GEN_LINK_SPEED             0x00010107

+#define OID_GEN_TRANSMIT_BUFFER_SPACE  0x00010108

+#define OID_GEN_RECEIVE_BUFFER_SPACE   0x00010109

+#define OID_GEN_TRANSMIT_BLOCK_SIZE    0x0001010A

+#define OID_GEN_RECEIVE_BLOCK_SIZE     0x0001010B

+#define OID_GEN_VENDOR_ID              0x0001010C

+#define OID_GEN_VENDOR_DESCRIPTION     0x0001010D

+#define OID_GEN_CURRENT_PACKET_FILTER  0x0001010E

+#define OID_GEN_CURRENT_LOOKAHEAD      0x0001010F

+#define OID_GEN_DRIVER_VERSION         0x00010110

+#define OID_GEN_MAXIMUM_TOTAL_SIZE     0x00010111

+#define OID_GEN_PROTOCOL_OPTIONS       0x00010112

+#define OID_GEN_MAC_OPTIONS            0x00010113

+#define OID_GEN_MEDIA_CONNECT_STATUS   0x00010114

+#define OID_GEN_MAXIMUM_SEND_PACKETS   0x00010115

+#define OID_GEN_VENDOR_DRIVER_VERSION  0x00010116

+

+#define OID_GEN_XMIT_OK        0x00020101

+#define OID_GEN_RCV_OK         0x00020102

+#define OID_GEN_XMIT_ERROR     0x00020103

+#define OID_GEN_RCV_ERROR      0x00020104

+#define OID_GEN_RCV_NO_BUFFER  0x00020105

+

+#define OID_GEN_DIRECTED_BYTES_XMIT    0x00020201

+#define OID_GEN_DIRECTED_FRAMES_XMIT   0x00020202

+#define OID_GEN_MULTICAST_BYTES_XMIT   0x00020203

+#define OID_GEN_MULTICAST_FRAMES_XMIT  0x00020204

+#define OID_GEN_BROADCAST_BYTES_XMIT   0x00020205

+#define OID_GEN_BROADCAST_FRAMES_XMIT  0x00020206

+#define OID_GEN_DIRECTED_BYTES_RCV     0x00020207

+#define OID_GEN_DIRECTED_FRAMES_RCV    0x00020208

+#define OID_GEN_MULTICAST_BYTES_RCV    0x00020209

+#define OID_GEN_MULTICAST_FRAMES_RCV   0x0002020A

+#define OID_GEN_BROADCAST_BYTES_RCV    0x0002020B

+#define OID_GEN_BROADCAST_FRAMES_RCV   0x0002020C

+#define OID_GEN_RCV_CRC_ERROR          0x0002020D

+#define OID_GEN_TRANSMIT_QUEUE_LENGTH  0x0002020E

+

+//

+// Ndis Packet Filter Bits (OID_GEN_CURRENT_PACKET_FILTER).

+//

+#define NDIS_PACKET_TYPE_DIRECTED        0x0001

+#define NDIS_PACKET_TYPE_MULTICAST       0x0002

+#define NDIS_PACKET_TYPE_ALL_MULTICAST   0x0004

+#define NDIS_PACKET_TYPE_BROADCAST       0x0008

+#define NDIS_PACKET_TYPE_SOURCE_ROUTING  0x0010

+#define NDIS_PACKET_TYPE_PROMISCUOUS     0x0020

+#define NDIS_PACKET_TYPE_SMT             0x0040

+#define NDIS_PACKET_TYPE_ALL_LOCAL       0x0080

+#define NDIS_PACKET_TYPE_MAC_FRAME       0x8000

+#define NDIS_PACKET_TYPE_FUNCTIONAL      0x4000

+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL  0x2000

+#define NDIS_PACKET_TYPE_GROUP           0x1000

+

+#pragma pack(1)

+

+typedef struct _REMOTE_NDIS_MSG_HEADER {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+} REMOTE_NDIS_MSG_HEADER;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    MajorVersion;

+  UINT32    MinorVersion;

+  UINT32    MaxTransferSize;

+} REMOTE_NDIS_INITIALIZE_MSG;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+} REMOTE_NDIS_HALT_MSG;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    Oid;

+  UINT32    InformationBufferLength;

+  UINT32    InformationBufferOffset;

+  UINT32    Reserved;

+} REMOTE_NDIS_QUERY_MSG;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    Oid;

+  UINT32    InformationBufferLength;

+  UINT32    InformationBufferOffset;

+  UINT32    Reserved;

+} REMOTE_NDIS_SET_MSG;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    Reserved;

+} REMOTE_NDIS_RESET_MSG;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    Status;

+  UINT32    StatusBufferLength;

+  UINT32    StatusBufferOffset;

+} REMOTE_NDIS_INDICATE_STATUS_MSG;

+

+typedef struct {

+  UINT32    DiagStatus;

+  UINT32    ErrorOffset;

+} RNDIS_DIAGNOSTIC_INFO;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+} REMOTE_NDIS_KEEPALIVE_MSG;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    Status;

+  UINT32    MajorVersion;

+  UINT32    MinorVersion;

+  UINT32    DeviceFlags;

+  UINT32    Medium;

+  UINT32    MaxPacketsPerTransfer;

+  UINT32    MaxTransferSize;

+  UINT32    PacketAlignmentFactor;

+  UINT64    Reserved;

+} REMOTE_NDIS_INITIALIZE_CMPLT;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    Status;

+  UINT32    InformationBufferLength;

+  UINT32    InformationBufferOffset;

+} REMOTE_NDIS_QUERY_CMPLT;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    Status;

+} REMOTE_NDIS_SET_CMPLT;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    Status;

+  UINT32    AddressingReset;

+} REMOTE_NDIS_RESET_CMPLT;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    RequestID;

+  UINT32    Status;

+} REMOTE_NDIS_KEEPALIVE_CMPLT;

+

+typedef struct {

+  UINT32    MessageType;

+  UINT32    MessageLength;

+  UINT32    DataOffset;

+  UINT32    DataLength;

+  UINT32    OutOfBandDataOffset;

+  UINT32    OutOfBandDataLength;

+  UINT32    NumOutOfBandDataElements;

+  UINT32    PerPacketInfoOffset;

+  UINT32    PerPacketInfoLength;

+  UINT32    Reserved1;

+  UINT32    Reserved2;

+} REMOTE_NDIS_PACKET_MSG;

+

+typedef struct {

+  UINT32    Size;

+  UINT32    Type;

+  UINT32    ClassInformationOffset;

+} OUT_OF_BAND_DATA_RECORD;

+

+typedef struct {

+  UINT32    Size;

+  UINT32    Type;

+  UINT32    ClassInformationOffset;

+} PER_PACKET_INFO_DATA_RECORD;

+

+typedef struct {

+  LIST_ENTRY    PacketList;

+  UINT8         *OrgBuffer;

+  UINTN         RemainingLength;

+} PACKET_LIST;

+

+#pragma pack()

+

+#endif

diff --git a/UsbNetworkPkg/UsbRndis/UsbRndis.inf b/UsbNetworkPkg/UsbRndis/UsbRndis.inf
new file mode 100644
index 0000000000..909b106059
--- /dev/null
+++ b/UsbNetworkPkg/UsbRndis/UsbRndis.inf
@@ -0,0 +1,41 @@
+## @file
+#   This is Usb Rndis driver for DXE phase.
+#
+# Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>
+# Subject to AMI licensing agreement.
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UsbRndis
+  FILE_GUID                      = 11E32C34-60B5-4991-8DEA-63D3E8C876DE
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = UsbRndisEntry
+
+[Sources]
+  UsbRndis.c
+  UsbRndis.h
+  UsbRndisFunction.c
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UsbNetworkPkg/UsbNetworkPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  DebugLib
+  UefiUsbLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiUsbIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/UsbNetworkPkg/UsbRndis/UsbRndisFunction.c b/UsbNetworkPkg/UsbRndis/UsbRndisFunction.c
new file mode 100644
index 0000000000..a9b7b54f6a
--- /dev/null
+++ b/UsbNetworkPkg/UsbRndis/UsbRndisFunction.c
@@ -0,0 +1,1587 @@
+/** @file

+  This file contains code for USB Ethernet descriptor

+  and specific requests implement.

+

+  Copyright (c) 1985 - 2022, AMI. All rights reserved.<BR>

+  Subject to AMI licensing agreement.

+**/

+

+#include "UsbRndis.h"

+

+UINT16  StopBulkInCnt  = 0;

+UINT16  BlockBulkInCnt = 0;

+

+/**

+  Load All of device descriptor.

+

+  @param[in]  UsbIo                 A pointer to the EFI_USB_IO_PROTOCOL instance.

+  @param[out] ConfigDesc            A pointer to the configuration descriptor.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed because the

+                                buffer specified by DescriptorLength and Descriptor

+                                is not large enough to hold the result of the request.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error. The transfer

+                                status is returned in Status.

+**/

+EFI_STATUS

+LoadAllDescriptor (

+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,

+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc

+  )

+{

+  EFI_STATUS                 Status;

+  UINT32                     TransStatus;

+  EFI_USB_CONFIG_DESCRIPTOR  Tmp;

+

+  Status = UsbIo->UsbGetConfigDescriptor (UsbIo, &Tmp);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = gBS->AllocatePool (

+                  EfiBootServicesData,

+                  Tmp.TotalLength,

+                  (VOID **)ConfigDesc

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbGetDescriptor (

+             UsbIo,

+             USB_DESC_TYPE_CONFIG << 8 | (Tmp.ConfigurationValue - 1),                 // zero based

+             0,

+             Tmp.TotalLength,

+             *ConfigDesc,

+             &TransStatus

+             );

+  return Status;

+}

+

+/**

+  Returns pointer to the next descriptor for the pack of USB descriptors

+  located in continues memory segment

+

+  @param[in]      Desc   A pointer to the CONFIG_DESCRIPTOR instance.

+  @param[in, out] Offset A pointer to the sum of descriptor length.

+

+  @retval TRUE   The request executed successfully.

+  @retval FALSE  No next descriptor.

+

+**/

+BOOLEAN

+NextDescriptor (

+  IN      EFI_USB_CONFIG_DESCRIPTOR  *Desc,

+  IN OUT  UINTN                      *Offset

+  )

+{

+  if ((Desc == NULL) || (*Offset >= Desc->TotalLength)) {

+    return FALSE;

+  }

+

+  if (((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length == 0) {

+    return FALSE;

+  }

+

+  *Offset += ((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length;

+  if ( *Offset >= Desc->TotalLength ) {

+    return FALSE;

+  }

+

+  return TRUE;

+}

+

+/**

+  Read Function descriptor

+

+  @param[in]  Config             A pointer to all of configuration.

+  @param[in]  FunDescriptorType  USB CDC class descriptor SubType.

+  @param[out] DataBuffer         A pointer to the Data of corresponding to device capability.

+

+  @retval EFI_SUCCESS        The device capability descriptor was retrieved

+                             successfully.

+  @retval EFI_UNSUPPORTED    No supported.

+  @retval EFI_NOT_FOUND      The device capability descriptor was not found.

+

+**/

+EFI_STATUS

+GetFunctionalDescriptor (

+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,

+  IN  UINT8                      FunDescriptorType,

+  OUT VOID                       *DataBuffer

+  )

+{

+  EFI_STATUS                    Status = EFI_NOT_FOUND;

+  UINTN                         Offset;

+  EFI_USB_INTERFACE_DESCRIPTOR  *Interface;

+

+  for (Offset = 0; NextDescriptor (Config, &Offset);) {

+    Interface = (EFI_USB_INTERFACE_DESCRIPTOR *)((UINT8 *)Config + Offset);

+    if (Interface->DescriptorType == CS_INTERFACE) {

+      if (((USB_HEADER_FUN_DESCRIPTOR *)Interface)->DescriptorSubtype == FunDescriptorType) {

+        switch (FunDescriptorType) {

+          case HEADER_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_HEADER_FUN_DESCRIPTOR *)Interface,

+              sizeof (USB_HEADER_FUN_DESCRIPTOR)

+              );

+            return EFI_SUCCESS;

+          case UNION_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_UNION_FUN_DESCRIPTOR *)Interface,

+              ((USB_UNION_FUN_DESCRIPTOR *)Interface)->FunctionLength

+              );

+            return EFI_SUCCESS;

+          case ETHERNET_FUN_DESCRIPTOR:

+            CopyMem (

+              DataBuffer,

+              (USB_ETHERNET_FUN_DESCRIPTOR *)Interface,

+              sizeof (USB_ETHERNET_FUN_DESCRIPTOR)

+              );

+            return EFI_SUCCESS;

+          default:

+            Status = EFI_UNSUPPORTED;

+            break;

+        }

+      }

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Get USB Ethernet IO endpoint and USB CDC data IO endpoint.

+

+  @param[in]      UsbIo           A pointer to the EFI_USB_IO_PROTOCOL instance.

+  @param[in, out] UsbRndisDevice  A pointer to the USB_RNDIS_DEVICE instance.

+

+**/

+VOID

+GetEndpoint (

+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,

+  IN OUT  USB_RNDIS_DEVICE     *UsbRndisDevice

+  )

+{

+  EFI_STATUS                    Status;

+  UINT8                         Index;

+  UINT32                        Result;

+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;

+  EFI_USB_ENDPOINT_DESCRIPTOR   Endpoint;

+

+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+  ASSERT_EFI_ERROR (Status);

+

+  if (Interface.NumEndpoints == 0 ) {

+    Status = UsbSetInterface (UsbIo, 1, 0, &Result);

+    ASSERT_EFI_ERROR (Status);

+

+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);

+    ASSERT_EFI_ERROR (Status);

+  }

+

+  for (Index = 0; Index < Interface.NumEndpoints; Index++) {

+    Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &Endpoint);

+

+    ASSERT_EFI_ERROR (Status);

+

+    switch ((Endpoint.Attributes & (BIT0 | BIT1))) {

+      case USB_ENDPOINT_BULK:

+        if (Endpoint.EndpointAddress & BIT7) {

+          UsbRndisDevice->BulkInEndpoint = Endpoint.EndpointAddress;

+        } else {

+          UsbRndisDevice->BulkOutEndpoint = Endpoint.EndpointAddress;

+        }

+

+        break;

+      case USB_ENDPOINT_INTERRUPT:

+        UsbRndisDevice->InterrupEndpoint = Endpoint.EndpointAddress;

+        break;

+    }

+  }

+}

+

+/**

+  Async USB transfer callback routine.

+

+  @param[in]  Data            Data received or sent via the USB Asynchronous Transfer, if the

+                              transfer completed successfully.

+  @param[in]  DataLength      The length of Data received or sent via the Asynchronous

+                              Transfer, if transfer successfully completes.

+  @param[in]  Context         Data passed from UsbAsyncInterruptTransfer() request.

+  @param[in]  Status          Indicates the result of the asynchronous transfer.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+EFI_STATUS

+EFIAPI

+InterruptCallback (

+  IN  VOID    *Data,

+  IN  UINTN   DataLength,

+  IN  VOID    *Context,

+  IN  UINT32  Status

+  )

+{

+  if (((EFI_USB_DEVICE_REQUEST *)Data)->Request == 0) {

+    CopyMem (

+      (EFI_USB_DEVICE_REQUEST *)Context,

+      (EFI_USB_DEVICE_REQUEST *)Data,

+      sizeof (EFI_USB_DEVICE_REQUEST)

+      );

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is used to manage a USB device with an interrupt transfer pipe.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  IsNewTransfer     If TRUE, a new transfer will be submitted to USB controller. If

+                                FALSE, the interrupt transfer is deleted from the device's interrupt

+                                transfer queue.

+  @param[in]  PollingInterval   Indicates the periodic rate, in milliseconds, that the transfer is to be

+                                executed.This parameter is required when IsNewTransfer is TRUE. The

+                                value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.

+                                The units are in milliseconds.

+  @param[in]  Request           A pointer to the EFI_USB_DEVICE_REQUEST data.

+

+  @retval EFI_SUCCESS           The asynchronous USB transfer request transfer has been successfully executed.

+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbRndisInterrupt (

+  IN USB_ETHERNET_PROTOCOL   *This,

+  IN BOOLEAN                 IsNewTransfer,

+  IN UINTN                   PollingInterval,

+  IN EFI_USB_DEVICE_REQUEST  *Requst

+  )

+{

+  EFI_STATUS        Status;

+  USB_RNDIS_DEVICE  *UsbRndisDevice;

+  UINTN             DataLength = 0;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  if (IsNewTransfer == TRUE) {

+    DataLength = sizeof (EFI_USB_DEVICE_REQUEST) + sizeof (USB_CONNECT_SPEED_CHANGE);

+    Status     = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (

+                                          UsbRndisDevice->UsbIo,

+                                          UsbRndisDevice->InterrupEndpoint,

+                                          IsNewTransfer,

+                                          PollingInterval,

+                                          DataLength,

+                                          InterruptCallback,

+                                          Requst

+                                          );

+

+    if (Status == EFI_INVALID_PARAMETER) {

+      // Because of Stacked AsyncInterrupt request are not supported

+      Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (

+                                        UsbRndisDevice->UsbIo,

+                                        UsbRndisDevice->InterrupEndpoint,

+                                        0,

+                                        0,

+                                        0,

+                                        NULL,

+                                        NULL

+                                        );

+    }

+  } else {

+    Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (

+                                      UsbRndisDevice->UsbIo,

+                                      UsbRndisDevice->InterrupEndpoint,

+                                      IsNewTransfer,

+                                      0,

+                                      0,

+                                      NULL,

+                                      NULL

+                                      );

+  }

+

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet Mac Address.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] MacAddress    A pointer to the caller allocated USB Ethernet Mac Address.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbEthMacAddress (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT EFI_MAC_ADDRESS        *MacAddress

+  )

+{

+  EFI_STATUS                   Status;

+  USB_RNDIS_DEVICE             *UsbRndisDevice;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthDescriptor;

+  CHAR16                       *Data;

+  CHAR16                       *DataPtr;

+  CHAR16                       TmpStr[1];

+  UINT8                        Index;

+  UINT8                        Hi;

+  UINT8                        Low;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthDescriptor);

+  ASSERT_EFI_ERROR (Status);

+

+  Status = UsbRndisDevice->UsbIo->UsbGetStringDescriptor (

+                                    UsbRndisDevice->UsbIo,

+                                    0x409,                       // English-US Language ID

+                                    UsbEthDescriptor.MacAddress,

+                                    &Data

+                                    );

+  ASSERT_EFI_ERROR (Status);

+

+  DataPtr = Data;

+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {

+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));

+    DataPtr++;

+    Hi = (UINT8)StrHexToUintn (TmpStr);

+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));

+    DataPtr++;

+    Low                     = (UINT8)StrHexToUintn (TmpStr);

+    MacAddress->Addr[Index] = (Hi << 4) | Low;

+  }

+

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet Bulk transfer data size.

+

+  @param[in]  This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] BulkSize      A pointer to the Bulk transfer data size.

+

+  @retval EFI_SUCCESS       The bulk transfer data size was retrieved successfully.

+  @retval other             Failed to retrieve the bulk transfer data size.

+

+**/

+EFI_STATUS

+EFIAPI

+UsbEthBulkSize (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  OUT UINTN                  *BulkSize

+  )

+{

+  EFI_STATUS                   Status;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  *BulkSize = (UINTN)UsbEthFunDescriptor.MaxSegmentSize;

+  return Status;

+}

+

+/**

+  Retrieves the USB Header functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.

+

+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbHeaderFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL      *This,

+  OUT USB_HEADER_FUN_DESCRIPTOR  *UsbHeaderFunDescriptor

+  )

+{

+  EFI_STATUS        Status;

+  USB_RNDIS_DEVICE  *UsbRndisDevice;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  if (UsbHeaderFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (

+             UsbRndisDevice->Config,

+             HEADER_FUN_DESCRIPTOR,

+             UsbHeaderFunDescriptor

+             );

+  return Status;

+}

+

+/**

+  Retrieves the USB Union functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbUnionFunDescriptor  A pointer to the caller allocated USB Union Functional Descriptor.

+

+  @retval EFI_SUCCESS            The USB Union Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER  UsbUnionFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND          The USB Union Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbUnionFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL     *This,

+  OUT USB_UNION_FUN_DESCRIPTOR  *UsbUnionFunDescriptor

+  )

+{

+  EFI_STATUS        Status;

+  USB_RNDIS_DEVICE  *UsbRndisDevice;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  if (UsbUnionFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (

+             UsbRndisDevice->Config,

+             UNION_FUN_DESCRIPTOR,

+             UsbUnionFunDescriptor

+             );

+  return Status;

+}

+

+/**

+  Retrieves the USB Ethernet functional Descriptor.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[out] UsbEthFunDescriptor    A pointer to the caller allocated USB Ethernet Functional Descriptor.

+

+  @retval EFI_SUCCESS            The USB Ethernet Functional descriptor was retrieved successfully.

+  @retval EFI_INVALID_PARAMETER  UsbEthFunDescriptor is NULL.

+  @retval EFI_NOT_FOUND          The USB Ethernet Functional descriptor was not found.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbRndisFunDescriptor (

+  IN  USB_ETHERNET_PROTOCOL        *This,

+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor

+  )

+{

+  EFI_STATUS        Status;

+  USB_RNDIS_DEVICE  *UsbRndisDevice;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  if (UsbEthFunDescriptor == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetFunctionalDescriptor (

+             UsbRndisDevice->Config,

+             ETHERNET_FUN_DESCRIPTOR,

+             UsbEthFunDescriptor

+             );

+  return Status;

+}

+

+/**

+  This request sets the Ethernet device multicast filters as specified in the

+  sequential list of 48 bit Ethernet multicast addresses.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  Number of filters.

+  @param[in]  McastAddr              A pointer to the value of the multicast addresses.

+

+  @retval EFI_SUCCESS            The request executed successfully.

+  @retval EFI_TIMEOUT            A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR       The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED        Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbRndisMcastFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN VOID                   *McastAddr

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_USB_DEVICE_REQUEST       Request;

+  UINT32                       TransStatus;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+  USB_RNDIS_DEVICE             *UsbRndisDevice;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_MULTICAST_FILTERS_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbRndisDevice->NumOfInterface;

+  Request.Length      = Value * 6;

+

+  return UsbRndisDevice->UsbIo->UsbControlTransfer (

+                                  UsbRndisDevice->UsbIo,

+                                  &Request,

+                                  EfiUsbDataOut,

+                                  USB_ETHERNET_TRANSFER_TIMEOUT,

+                                  McastAddr,

+                                  Request.Length,

+                                  &TransStatus

+                                  );

+}

+

+/**

+  This request sets up the specified Ethernet power management pattern filter as

+  described in the data structure.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                 Number of filters.

+  @param[in]  Length                Size of the power management pattern filter data.

+  @param[in]  PatternFilter         A pointer to the power management pattern filter structure.

+

+  @retval EFI_SUCCESS            The request executed successfully.

+  @retval EFI_TIMEOUT            A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR       The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED        Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbRndisPowerFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value,

+  IN UINT16                 Length,

+  IN VOID                   *PatternFilter

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_RNDIS_DEVICE        *UsbRndisDevice;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  Request.RequestType = USB_ETHRTNET_SET_REQ_TYPE;

+  Request.Request     = SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbRndisDevice->NumOfInterface;

+  Request.Length      = Length;

+

+  return UsbRndisDevice->UsbIo->UsbControlTransfer (

+                                  UsbRndisDevice->UsbIo,

+                                  &Request,

+                                  EfiUsbDataOut,

+                                  USB_ETHERNET_TRANSFER_TIMEOUT,

+                                  PatternFilter,

+                                  Length,

+                                  &TransStatus

+                                  );

+}

+

+/**

+  This request retrieves the status of the specified Ethernet power management

+  pattern filter from the device.

+

+  @param[in]  This                   A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value                  The filter number.

+  @param[out] PatternActive          A pointer to the pattern active boolean.

+

+  @retval EFI_SUCCESS            The request executed successfully.

+  @retval EFI_TIMEOUT            A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR       The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED        Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+GetUsbRndisPowerFilter (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 Value,

+  OUT BOOLEAN                *PatternActive

+  )

+{

+  EFI_USB_DEVICE_REQUEST  Request;

+  UINT32                  TransStatus;

+  USB_RNDIS_DEVICE        *UsbRndisDevice;

+

+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+

+  Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;

+  Request.Request     = GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;

+  Request.Value       = Value;

+  Request.Index       = UsbRndisDevice->NumOfInterface;

+  Request.Length      = USB_ETH_POWER_FILTER_LENGTH;

+

+  return UsbRndisDevice->UsbIo->UsbControlTransfer (

+                                  UsbRndisDevice->UsbIo,

+                                  &Request,

+                                  EfiUsbDataIn,

+                                  USB_ETHERNET_TRANSFER_TIMEOUT,

+                                  PatternActive,

+                                  USB_ETH_POWER_FILTER_LENGTH,

+                                  &TransStatus

+                                  );

+}

+

+/**

+

+  Converts PXE filter settings to RNDIS values

+

+  @param[in]  Value      PXE filter data.

+  @param[out] CdcFilter  A pointer to the Ethernet Packet Filter Bitmap value converted by PXE_OPFLAGS.

+

+**/

+VOID

+ConvertFilter (

+  IN  UINT16  Value,

+  OUT UINT16  *CdcFilter

+  )

+{

+  UINT32                 Index;

+  UINT32                 Count;

+  static struct BIT_MAP  Table[] = {

+    { PXE_OPFLAGS_RECEIVE_FILTER_UNICAST,            NDIS_PACKET_TYPE_DIRECTED      },

+    { PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST,          NDIS_PACKET_TYPE_BROADCAST     },

+    { PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST, NDIS_PACKET_TYPE_MULTICAST     },

+    { PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS,        NDIS_PACKET_TYPE_PROMISCUOUS   },

+    { PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST,      NDIS_PACKET_TYPE_ALL_MULTICAST },

+  };

+

+  Count = sizeof (Table)/sizeof (Table[0]);

+

+  for (Index = 0; (Table[Index].Src != 0) && (Index < Count); Index++) {

+    if (Table[Index].Src & Value) {

+      *CdcFilter |= Table[Index].Dst;

+    }

+  }

+}

+

+/**

+

+  Updates Filter settings on the device.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_STATUS

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiReceiveFilter (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS                   Status;

+  UINT8                        *McastList;

+  UINT8                        Count = 0;

+  UINT8                        Index1;

+  UINT8                        Index2;

+  UINT64                       CpbAddr   = Cdb->CPBaddr;

+  UINT32                       CpbSize   = Cdb->CPBsize;

+  UINT16                       SetFilter = (UINT16)(Cdb->OpFlags & 0x1F);

+  PXE_CPB_RECEIVE_FILTERS      *Cpb      = (PXE_CPB_RECEIVE_FILTERS *)(UINTN)CpbAddr;

+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;

+

+  // The Cpb could be NULL.(ref:PXE_CPBADDR_NOT_USED)

+  Nic->RxFilter = (UINT8)SetFilter;

+

+  if (((SetFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) || (Cpb != NULL)) {

+    if (Cpb != NULL) {

+      Nic->McastCount = (UINT8)(CpbSize / PXE_MAC_LENGTH);

+      CopyMem (&Nic->McastList, Cpb, Nic->McastCount);

+    } else {

+      Nic->McastCount = 0;

+    }

+

+    if (Nic->CanReceive) {

+      Nic->CanReceive = FALSE;

+    }

+

+    Nic->UsbEth->UsbEthFunDescriptor (Nic->UsbEth, &UsbEthFunDescriptor);

+    if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {

+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;

+      DEBUG ((DEBUG_INFO, "SetUsbEthPacketFilter Nic %lx Nic->UsbEth %lx ", Nic, Nic->UsbEth));

+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);

+    } else {

+      Status = gBS->AllocatePool (EfiBootServicesData, Nic->McastCount * 6, (VOID **)&McastList);

+      if (EFI_ERROR (Status)) {

+        return PXE_STATCODE_INVALID_PARAMETER;

+      }

+

+      if (Cpb != NULL) {

+        for (Index1 = 0; Index1 < Nic->McastCount; Index1++) {

+          for (Index2 = 0; Index2 < 6; Index2++) {

+            McastList[Count++] = Cpb->MCastList[Index1][Index2];

+          }

+        }

+      }

+

+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;

+      if (Cpb != NULL) {

+        Nic->UsbEth->SetUsbEthMcastFilter (Nic->UsbEth, Nic->McastCount, McastList);

+      }

+

+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);

+      gBS->FreePool (McastList);

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This request is used to configure device Ethernet packet filter settings.

+

+  @param[in]  This              A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  Value             Packet Filter Bitmap.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+SetUsbRndisPacketFilter (

+  IN USB_ETHERNET_PROTOCOL  *This,

+  IN UINT16                 Value

+  )

+{

+  return EFI_SUCCESS;

+}

+

+/**

+  This request is used to retrieve a statistic based on the feature selector.

+

+  @param[in]  This                  A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]  FeatureSelector       Value of the feature selector.

+  @param[out] Statistic             A pointer to the 32 bit unsigned integer.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_TIMEOUT           A timeout occurred executing the request.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+GetRndisStatistic (

+  IN  USB_ETHERNET_PROTOCOL  *This,

+  IN  UINT16                 FeatureSelector,

+  OUT VOID                   *Statistic

+  )

+{

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called when UndiStart is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiStart (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  EFI_STATUS  Status;

+

+  DEBUG ((DEBUG_INFO, "RndisUndiStart Nic %lx Cdb %lx Nic State %x\n", Nic, Cdb, Nic->State));

+

+  // Issue Rndis Reset and bring the device to RNDIS_BUS_INITIALIZED state

+  Status = RndisUndiReset (Cdb, Nic);

+  if (EFI_ERROR (Status)) {

+    RndisUndiReset (Cdb, Nic);

+  }

+

+  Status = RndisUndiInitialize (Cdb, Nic);

+  if (EFI_ERROR (Status)) {

+    RndisUndiInitialize (Cdb, Nic);

+  }

+

+  RndisUndiShutdown (Cdb, Nic);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called when Undistop is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiStop (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  DEBUG ((DEBUG_INFO, "RndisUndiStop State %x\n", Nic->State));

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called when UndiGetInitInfo is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiGetInitInfo (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  USB_ETHERNET_PROTOCOL  *UsbEthDevice   = Nic->UsbEth;

+  USB_RNDIS_DEVICE       *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDevice);

+  PXE_DB_GET_INIT_INFO   *Db;

+

+  DEBUG ((DEBUG_INFO, "RndisUndiGetInitInfo\n"));

+

+  Db = (PXE_DB_GET_INIT_INFO *)(UINTN)Cdb->DBaddr;

+

+  Db->FrameDataLen = UsbRndisDevice->MaxTransferSize - sizeof (REMOTE_NDIS_PACKET_MSG) - PXE_MAC_HEADER_LEN_ETHER;

+  // Limit Max MTU size to 1500 bytes as RNDIS spec.

+  if (Db->FrameDataLen > PXE_MAX_TXRX_UNIT_ETHER) {

+    Db->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER;

+  }

+

+  DEBUG ((DEBUG_INFO, "Db->FrameDataLen %x\n", Db->FrameDataLen));

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called when RndisUndiGetConfigInfo is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiGetConfigInfo (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  DEBUG ((DEBUG_INFO, "RndisUndiGetConfigInfo\n"));

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called when UndiInitialize is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_UNSUPPORTED       Not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiInitialize (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  USB_ETHERNET_PROTOCOL         *UsbEthDriver   = Nic->UsbEth;

+  USB_RNDIS_DEVICE              *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);

+  REMOTE_NDIS_INITIALIZE_MSG    RndisInitMsg;

+  REMOTE_NDIS_INITIALIZE_CMPLT  RndisInitMsgCmplt;

+  EFI_STATUS                    Status;

+

+  DEBUG ((DEBUG_INFO, "RndisUndiInitialize\n"));

+

+  ZeroMem (&RndisInitMsg, sizeof (REMOTE_NDIS_INITIALIZE_MSG));

+  ZeroMem (&RndisInitMsgCmplt, sizeof (REMOTE_NDIS_INITIALIZE_CMPLT));

+

+  RndisInitMsg.MessageType     = RNDIS_INITIALIZE_MSG;

+  RndisInitMsg.MessageLength   = sizeof (REMOTE_NDIS_INITIALIZE_MSG);

+  RndisInitMsg.RequestID       = UsbRndisDevice->RequestId;

+  RndisInitMsg.MajorVersion    = RNDIS_MAJOR_VERSION;

+  RndisInitMsg.MinorVersion    = RNDIS_MINOR_VERSION;

+  RndisInitMsg.MaxTransferSize = RNDIS_MAX_TRANSFER_SIZE;

+

+  RndisInitMsgCmplt.MessageType   = RNDIS_INITIALIZE_CMPLT;

+  RndisInitMsgCmplt.MessageLength = sizeof (REMOTE_NDIS_INITIALIZE_CMPLT);

+

+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisInitMsg, (REMOTE_NDIS_MSG_HEADER *)&RndisInitMsgCmplt);

+

+  UsbRndisDevice->RequestId++;

+

+  if (EFI_ERROR (Status) || (RndisInitMsgCmplt.Status & 0x80000000)) {

+    return Status;

+  }

+

+  // Only Wired Medium is supported

+  if (RndisInitMsgCmplt.Medium) {

+    return EFI_UNSUPPORTED;

+  }

+

+  UsbRndisDevice->Medium                = RndisInitMsgCmplt.Medium;

+  UsbRndisDevice->MaxPacketsPerTransfer = RndisInitMsgCmplt.MaxPacketsPerTransfer;

+  UsbRndisDevice->MaxTransferSize       = RndisInitMsgCmplt.MaxTransferSize;

+  UsbRndisDevice->PacketAlignmentFactor = RndisInitMsgCmplt.PacketAlignmentFactor;

+

+  DEBUG ((DEBUG_INFO, "Medium : %x \n", RndisInitMsgCmplt.Medium));

+  DEBUG ((DEBUG_INFO, "MaxPacketsPerTransfer : %x \n", RndisInitMsgCmplt.MaxPacketsPerTransfer));

+  DEBUG ((DEBUG_INFO, "MaxTransferSize : %x\n", RndisInitMsgCmplt.MaxTransferSize));

+  DEBUG ((DEBUG_INFO, "PacketAlignmentFactor : %x\n", RndisInitMsgCmplt.PacketAlignmentFactor));

+

+  return Status;

+}

+

+/**

+  This function is called when UndiReset is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiReset (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  USB_ETHERNET_PROTOCOL    *UsbEthDriver   = Nic->UsbEth;

+  USB_RNDIS_DEVICE         *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);

+  REMOTE_NDIS_RESET_MSG    RndisResetMsg;

+  REMOTE_NDIS_RESET_CMPLT  RndisResetCmplt;

+  EFI_STATUS               Status;

+

+  DEBUG ((DEBUG_INFO, "RndisUndiReset\n"));

+

+  ZeroMem (&RndisResetMsg, sizeof (REMOTE_NDIS_RESET_MSG));

+  ZeroMem (&RndisResetCmplt, sizeof (REMOTE_NDIS_RESET_CMPLT));

+

+  RndisResetMsg.MessageType   = RNDIS_RESET_MSG;

+  RndisResetMsg.MessageLength = sizeof (REMOTE_NDIS_RESET_MSG);

+

+  RndisResetCmplt.MessageType   = RNDIS_RESET_CMPLT;

+  RndisResetCmplt.MessageLength = sizeof (REMOTE_NDIS_RESET_CMPLT);

+

+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisResetMsg, (REMOTE_NDIS_MSG_HEADER *)&RndisResetCmplt);

+

+  UsbRndisDevice->RequestId = 1;          // Let's start with 1

+

+  if (EFI_ERROR (Status) || (RndisResetCmplt.Status & 0x80000000)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called when UndiShutdown is invoked.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiShutdown (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  USB_ETHERNET_PROTOCOL  *UsbEthDriver   = Nic->UsbEth;

+  USB_RNDIS_DEVICE       *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);

+  REMOTE_NDIS_HALT_MSG   RndisHltMsg;

+  EFI_STATUS             Status;

+

+  DEBUG ((DEBUG_INFO, "RndisUndiShutdown\n"));

+

+  ZeroMem (&RndisHltMsg, sizeof (REMOTE_NDIS_HALT_MSG));

+

+  RndisHltMsg.MessageType   = RNDIS_HLT_MSG;

+  RndisHltMsg.MessageLength = sizeof (REMOTE_NDIS_HALT_MSG);

+

+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisHltMsg, NULL);

+

+  if (Status == EFI_DEVICE_ERROR) {

+    Status = EFI_SUCCESS;

+  }

+

+  UsbRndisDevice->RequestId = 1;

+  return Status;

+}

+

+/**

+  Update the Media connection.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiGetStatus (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  Cdb->StatFlags &= ~(PXE_STATFLAGS_GET_STATUS_NO_MEDIA);

+  return EFI_SUCCESS;

+}

+

+/**

+  Transmit the data after appending RNDIS header.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in]      BulkOutData   A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] DataLength    A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiTransmit (

+  IN      PXE_CDB                *Cdb,

+  IN      USB_ETHERNET_PROTOCOL  *This,

+  IN      VOID                   *BulkOutData,

+  IN OUT  UINTN                  *DataLength

+  )

+{

+  EFI_STATUS              Status;

+  USB_RNDIS_DEVICE        *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+  REMOTE_NDIS_PACKET_MSG  *RndisPacketMsg;

+  UINTN                   TransferLength;

+

+  DEBUG ((DEBUG_INFO, "RndisUndiTransmit DataLength : %x\n", *DataLength));

+

+  Status = gBS->AllocatePool (EfiBootServicesData, sizeof (REMOTE_NDIS_PACKET_MSG) + *DataLength, (VOID **)&RndisPacketMsg);

+

+  ZeroMem (RndisPacketMsg, sizeof (REMOTE_NDIS_PACKET_MSG));

+

+  RndisPacketMsg->MessageType   = RNDIS_PACKET_MSG;

+  RndisPacketMsg->MessageLength = sizeof (REMOTE_NDIS_PACKET_MSG) + (UINT32)*DataLength;

+  RndisPacketMsg->DataOffset    = sizeof (REMOTE_NDIS_PACKET_MSG) - 8;

+  RndisPacketMsg->DataLength    = (UINT32)*DataLength;

+

+  gBS->CopyMem (((UINT8 *)RndisPacketMsg) + sizeof (REMOTE_NDIS_PACKET_MSG), BulkOutData, *DataLength);

+

+  TransferLength = RndisPacketMsg->MessageLength;

+

+  Status = RndisTransmitDataMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg, &TransferLength);

+

+  DEBUG ((DEBUG_INFO, "\nRndisUndiTransmit TransferLength %lx\n", TransferLength));

+

+  gBS->FreePool (RndisPacketMsg);

+

+  return Status;

+}

+

+/**

+  Receives and removes RNDIS header and returns the raw data.

+

+  @param[in]      Cdb           A pointer to the command descriptor block.

+  @param[in]      This          A pointer to the USB_ETHERNET_PROTOCOL instance.

+  @param[in, out] BulkInData    A pointer to the buffer of data that will be transmitted to USB

+                                device or received from USB device.

+  @param[in, out] DataLength    A pointer to the PacketLength.

+

+  @retval EFI_SUCCESS           The request executed successfully.

+  @retval EFI_BUFFER_TOO_SMALL  The user provided buffer is too small

+  @retval EFI_NOT_FOUND         No buffer was found in the list.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisUndiReceive (

+  IN     PXE_CDB                *Cdb,

+  IN     USB_ETHERNET_PROTOCOL  *This,

+  IN OUT VOID                   *BulkInData,

+  IN OUT UINTN                  *DataLength

+  )

+{

+  EFI_STATUS              Status;

+  USB_RNDIS_DEVICE        *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);

+  REMOTE_NDIS_PACKET_MSG  *RndisPacketMsg;

+  UINTN                   TransferLength;

+  VOID                    *Buffer;

+  PACKET_LIST             *HeadPacket = NULL;

+  PACKET_LIST             *PacketList;

+  UINT32                  ReceivedBytes = 0;

+

+  // Check if there is any outstanding packet to receive

+  // The buffer allocated has a linked List followed by the packet.

+  do {

+    Status         = gBS->AllocatePool (EfiBootServicesData, sizeof (PACKET_LIST) + sizeof (REMOTE_NDIS_PACKET_MSG) + UsbRndisDevice->MaxTransferSize, &Buffer);

+    RndisPacketMsg = (REMOTE_NDIS_PACKET_MSG *)(sizeof (PACKET_LIST) + (UINT8 *)Buffer);

+    PacketList     = (PACKET_LIST *)Buffer;

+    // Save the original address for freeing it up

+    PacketList->OrgBuffer       = Buffer;

+    PacketList->RemainingLength = UsbRndisDevice->MaxTransferSize;

+

+    ZeroMem (RndisPacketMsg, sizeof (REMOTE_NDIS_PACKET_MSG));

+    TransferLength = UsbRndisDevice->MaxTransferSize;

+

+    Status = RndisReceiveDataMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg, &TransferLength);

+

+    if (EFI_ERROR (Status) || (TransferLength == 0)) {

+      gBS->FreePool (Buffer);

+      break;

+    }

+

+    // Handle Multiple packets

+    if ((RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) && (RndisPacketMsg->DataOffset == sizeof (REMOTE_NDIS_PACKET_MSG) - 8)) {

+      // Insert Packet

+      InsertTailList (&UsbRndisDevice->ReceivePacketList, Buffer);

+    }

+  } while (1);

+

+  // Check if they linked list has any received buffer. If yes report it.

+  if (IsListEmpty (&UsbRndisDevice->ReceivePacketList)) {

+    return EFI_NOT_FOUND;

+  }

+

+  HeadPacket = (PACKET_LIST *)GetFirstNode (&UsbRndisDevice->ReceivePacketList);

+

+  RndisPacketMsg = (REMOTE_NDIS_PACKET_MSG *)((UINT8 *)HeadPacket + sizeof (PACKET_LIST));

+

+  PrintRndisMsg ((REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg);

+

+  if ((RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) && (RndisPacketMsg->DataOffset == sizeof (REMOTE_NDIS_PACKET_MSG) - 8)) {

+    if (*DataLength >= RndisPacketMsg->DataLength) {

+      gBS->CopyMem (BulkInData, (UINT8 *)RndisPacketMsg + RndisPacketMsg->DataOffset + 8, RndisPacketMsg->DataLength);

+

+      ReceivedBytes                                += RndisPacketMsg->DataLength;

+      BulkInData                                    = ((UINT8 *)BulkInData) + RndisPacketMsg->DataLength;

+      ((PACKET_LIST *)HeadPacket)->RemainingLength -= RndisPacketMsg->DataLength;

+    } else {

+      DEBUG ((EFI_D_ERROR, "RndisUndiReceive:Buffer too small %x\n", RndisPacketMsg->DataLength));

+      *DataLength = RndisPacketMsg->DataLength;

+      return EFI_BUFFER_TOO_SMALL;

+    }

+

+    // check if there this is a multi-packet message. If so update the pointer so that next Receive call will return that data.

+    RndisPacketMsg = (REMOTE_NDIS_PACKET_MSG *)((UINT8 *)RndisPacketMsg + RndisPacketMsg->DataLength);

+    PacketList     = (PACKET_LIST *)((UINT8 *)RndisPacketMsg - sizeof (PACKET_LIST));

+

+    if ((HeadPacket->RemainingLength > sizeof (REMOTE_NDIS_PACKET_MSG)) && \

+        (RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) && \

+        (RndisPacketMsg->DataOffset == sizeof (REMOTE_NDIS_PACKET_MSG) - 8))

+    {

+      // Multi-Packet msg is found. Since the first packet is consumed, update the linked list to point this new packet.

+      PacketList->OrgBuffer       = HeadPacket->OrgBuffer;

+      PacketList->RemainingLength = HeadPacket->RemainingLength;

+      RemoveEntryList (&HeadPacket->PacketList);

+      InsertHeadList (&UsbRndisDevice->ReceivePacketList, &(PacketList->PacketList));

+    } else {

+      RemoveEntryList (&HeadPacket->PacketList);

+      gBS->FreePool ((PACKET_LIST *)HeadPacket->OrgBuffer);

+    }

+  } else {

+    // Packet doesn't contain valid header

+    DEBUG ((DEBUG_INFO, "RndisUndiReceive:Invalid RNDIS Packet received\n"));

+    RemoveEntryList (&(HeadPacket->PacketList));

+    gBS->FreePool ((PACKET_LIST *)HeadPacket->OrgBuffer);

+  }

+

+  if (ReceivedBytes) {

+    *DataLength = ReceivedBytes;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This is a dummy function which just returns. Unimplimented USB_ETHERNET_PROTOCOL functions

+  point to this function.

+

+  @param[in]  Cdb  A pointer to the command descriptor block.

+  @param[in]  Nic  A pointer to the Network interface controller data.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+RndisDummyReturn (

+  IN  PXE_CDB   *Cdb,

+  IN  NIC_DATA  *Nic

+  )

+{

+  DEBUG ((DEBUG_INFO, "RndisDummyReturn called\n"));

+  return EFI_SUCCESS;

+}

+

+/**

+  This function sends the RNDIS SET_MSG cmd

+

+  @param[in]  UsbRndisDevice  A pointer to the USB_RNDIS_DEVICE instance.

+  @param[in]  Oid             Value of the OID.

+  @param[in]  Length          Length of the data buffer.

+  @param[in]  Buf             A pointer to the data buffer.

+

+  @retval EFI_SUCCESS         The request executed successfully.

+

+**/

+EFI_STATUS

+SendRndisSetMsg (

+  IN  USB_RNDIS_DEVICE  *UsbRndisDevice,

+  IN  UINT8             Oid,

+  IN  UINT32            Length,

+  IN  UINT8             *Buf

+  )

+{

+  REMOTE_NDIS_SET_MSG  *RndisSetMsg;

+  EFI_STATUS           Status;

+

+  RndisSetMsg = AllocateZeroPool (sizeof (USB_RNDIS_DEVICE) + Length);

+

+  RndisSetMsg->MessageType             = RNDIS_SET_MSG;

+  RndisSetMsg->MessageLength           = sizeof (REMOTE_NDIS_SET_MSG) + Length;

+  RndisSetMsg->RequestID               = UsbRndisDevice->RequestId;

+  RndisSetMsg->Oid                     = Oid;

+  RndisSetMsg->InformationBufferLength = Length;

+  RndisSetMsg->InformationBufferOffset = sizeof (REMOTE_NDIS_SET_MSG) - 8;

+

+  gBS->CopyMem (((UINT8 *)RndisSetMsg) + sizeof (REMOTE_NDIS_SET_MSG), Buf, Length);

+

+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)RndisSetMsg, NULL);

+

+  gBS->FreePool (RndisSetMsg);

+

+  return Status;

+}

+

+/**

+  This function send the RNDIS command through the device's control endpoint

+

+  @param[in]  UsbRndisDevice    A pointer to the USB_RNDIS_DEVICE instance.

+  @param[in]  RndisMsg          A pointer to the REMOTE_NDIS_MSG_HEADER data.

+  @param[out] RndisMsgResponse  A pointer to the REMOTE_NDIS_MSG_HEADER data for getting responses.

+

+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.

+

+**/

+EFI_STATUS

+RndisControlMsg (

+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,

+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,

+  OUT REMOTE_NDIS_MSG_HEADER  *RndisMsgResponse

+  )

+{

+  EFI_USB_IO_PROTOCOL           *UsbIo = UsbRndisDevice->UsbIo;

+  EFI_USB_DEVICE_REQUEST        DevReq;

+  UINT32                        UsbStatus;

+  EFI_STATUS                    Status;

+  UINT32                        SaveResponseType   = 0;

+  UINT32                        SaveResponseLength = 0;

+  UINT32                        Index;

+  REMOTE_NDIS_INITIALIZE_CMPLT  *RndisInitCmplt = (REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsgResponse;

+

+  if (RndisMsgResponse) {

+    SaveResponseType   = RndisMsgResponse->MessageType;

+    SaveResponseLength = RndisMsgResponse->MessageLength;

+  }

+

+  ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));

+

+  DevReq.RequestType = USB_REQ_TYPE_CLASS | USB_TARGET_INTERFACE;

+  DevReq.Request     = SEND_ENCAPSULATED_COMMAND;

+  DevReq.Value       = 0;

+  DevReq.Index       = 0;

+  DevReq.Length      = (UINT16)RndisMsg->MessageLength;

+

+  PrintRndisMsg (RndisMsg);

+

+  Status = UsbIo->UsbControlTransfer (

+                    UsbIo,

+                    &DevReq,

+                    EfiUsbDataOut,

+                    USB_ETHERNET_TRANSFER_TIMEOUT,

+                    RndisMsg,

+                    RndisMsg->MessageLength,

+                    &UsbStatus

+                    );

+

+  DEBUG ((DEBUG_INFO, "RndisControlMsg: UsbStatus : %x Status : %r RndisMsgResponse : %lx\n", UsbStatus, Status, RndisMsgResponse));

+

+  // Error or no response expected

+  if ((EFI_ERROR (Status)) || (RndisMsgResponse == NULL)) {

+    DEBUG ((DEBUG_INFO, "RndisControlMsg: UsbStatus : %x Status : %r\n", UsbStatus, Status));

+    return Status;

+  }

+

+  for (Index = 0; Index < (RNDIS_CONTROL_TIMEOUT/100); Index++) {

+    ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));

+

+    DevReq.RequestType = USB_ENDPOINT_DIR_IN | USB_REQ_TYPE_CLASS | USB_TARGET_INTERFACE;

+    DevReq.Request     = GET_ENCAPSULATED_RESPONSE;

+    DevReq.Value       = 0;

+    DevReq.Index       = 0;

+    DevReq.Length      = (UINT16)RndisMsgResponse->MessageLength;

+

+    Status = UsbIo->UsbControlTransfer (

+                      UsbIo,

+                      &DevReq,

+                      EfiUsbDataIn,

+                      USB_ETHERNET_TRANSFER_TIMEOUT,

+                      RndisMsgResponse,

+                      RndisMsgResponse->MessageLength,

+                      &UsbStatus

+                      );

+

+    DEBUG ((DEBUG_INFO, "RndisControlMsg Response: UsbStatus : %x Status : %r \n", UsbStatus, Status));

+

+    PrintRndisMsg (RndisMsgResponse);

+

+    if (!EFI_ERROR (Status)) {

+      if ((RndisInitCmplt->RequestID != ((REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsg)->RequestID) || (RndisInitCmplt->MessageType != SaveResponseType)) {

+        DEBUG ((DEBUG_INFO, "Retry the response\n"));

+        continue;

+      }

+

+      return Status;

+    }

+

+    RndisMsgResponse->MessageType   = SaveResponseType;

+    RndisMsgResponse->MessageLength = SaveResponseLength;

+

+    gBS->Stall (100000);    // 100msec

+  }

+

+  DEBUG ((DEBUG_INFO, "RndisControlMsg: TimeOut\n"));

+

+  return EFI_TIMEOUT;

+}

+

+/**

+  This function send the RNDIS command through the device's Data endpoint

+

+  @param[in]      UsbRndisDevice  A pointer to the USB_RNDIS_DEVICE instance.

+  @param[in]      RndisMsg        A pointer to the REMOTE_NDIS_MSG_HEADER to send out.

+  @param[in, out] TransferLength  The length of the RndisMsg data to transfer.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+RndisTransmitDataMsg (

+  IN      USB_RNDIS_DEVICE        *UsbRndisDevice,

+  IN      REMOTE_NDIS_MSG_HEADER  *RndisMsg,

+  IN OUT  UINTN                   *TransferLength

+  )

+{

+  EFI_STATUS  Status;

+  UINT32      UsbStatus;

+

+  if (UsbRndisDevice->BulkInEndpoint == 0) {

+    GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);

+  }

+

+  PrintRndisMsg (RndisMsg);

+

+  Status = UsbRndisDevice->UsbIoCdcData->UsbBulkTransfer (

+                                           UsbRndisDevice->UsbIoCdcData,

+                                           UsbRndisDevice->BulkOutEndpoint,

+                                           RndisMsg,

+                                           TransferLength,

+                                           USB_TX_ETHERNET_BULK_TIMEOUT,

+                                           &UsbStatus

+                                           );

+

+  if (Status == EFI_SUCCESS) {

+    StopBulkInCnt = MAXIMUM_STOPBULKIN_CNT;     // After sending cmd ,we will polling receive package for MAXIMUM_STOPBULKIN_CNT times

+  }

+

+  return Status;

+}

+

+/**

+  This function send the RNDIS command through the device's Data endpoint

+

+  @param[in]      UsbRndisDevice    A pointer to the USB_RNDIS_DEVICE instance.

+  @param[in, out] RndisMsg          A pointer to the REMOTE_NDIS_MSG_HEADER to send out.

+  @param[in, out] TransferLength    The length of the RndisMsg data to transfer.

+

+  @retval EFI_SUCCESS     The request executed successfully.

+

+**/

+EFI_STATUS

+RndisReceiveDataMsg (

+  IN      USB_RNDIS_DEVICE        *UsbRndisDevice,

+  IN OUT  REMOTE_NDIS_MSG_HEADER  *RndisMsg,

+  IN OUT  UINTN                   *TransferLength

+  )

+{

+  EFI_STATUS  Status;

+  UINT32      UsbStatus = 0;

+

+  if (UsbRndisDevice->BulkInEndpoint == 0) {

+    GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);

+  }

+

+  // Use StopBulkInCnt to stop BulkIn command

+  if (StopBulkInCnt || LAN_BULKIN_CMD_CONTROL) {

+    Status = UsbRndisDevice->UsbIoCdcData->UsbBulkTransfer (

+                                             UsbRndisDevice->UsbIoCdcData,

+                                             UsbRndisDevice->BulkInEndpoint,

+                                             RndisMsg,

+                                             TransferLength,

+                                             USB_RX_ETHERNET_BULK_TIMEOUT,

+                                             &UsbStatus

+                                             );

+

+    if (!EFI_ERROR (Status)) {

+      StopBulkInCnt = MINIMUM_STOPBULKIN_CNT;

+    } else {

+      StopBulkInCnt--;

+    }

+  } else {

+    Status          = EFI_TIMEOUT;

+    *TransferLength = 0;

+    BlockBulkInCnt++;

+  }

+

+  if (BlockBulkInCnt > BULKIN_CMD_POLLING_CNT) {

+    StopBulkInCnt  = MINIMUM_STOPBULKIN_CNT;

+    BlockBulkInCnt = 0;

+  }

+

+  PrintRndisMsg (RndisMsg);

+

+  return Status;

+}

+

+/**

+  Prints RNDIS Header and Data

+

+  @param[in] RndisMsg    A pointer to the REMOTE_NDIS_MSG_HEADER data.

+

+**/

+VOID

+PrintRndisMsg (

+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg

+  )

+{

+  UINTN  Length = 0;

+

+  switch (RndisMsg->MessageType) {

+    case RNDIS_PACKET_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_PACKET_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_PACKET_MSG) + 0x14;

+      break;

+    case RNDIS_INITIALIZE_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_INITIALIZE_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_INITIALIZE_MSG);

+      break;

+    case RNDIS_INITIALIZE_CMPLT:

+      DEBUG ((DEBUG_INFO, "RNDIS_INITIALIZE_CMPLT:\n"));

+      Length = sizeof (REMOTE_NDIS_INITIALIZE_CMPLT);

+      break;

+    case RNDIS_HLT_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_HLT_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_HALT_MSG);

+      break;

+    case RNDIS_QUERY_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_QUERY_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_QUERY_MSG);

+      break;

+    case RNDIS_QUERY_CMPLT:

+      DEBUG ((DEBUG_INFO, "RNDIS_QUERY_CMPLT:\n"));

+      Length = sizeof (REMOTE_NDIS_QUERY_CMPLT);

+      break;

+    case RNDIS_SET_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_SET_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_SET_MSG);

+      break;

+    case RNDIS_SET_CMPLT:

+      DEBUG ((DEBUG_INFO, "RNDIS_SET_CMPLT:\n"));

+      Length = sizeof (REMOTE_NDIS_SET_CMPLT);

+      break;

+    case RNDIS_RESET_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_RESET_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_RESET_MSG);

+      break;

+    case RNDIS_RESET_CMPLT:

+      DEBUG ((DEBUG_INFO, "RNDIS_RESET_CMPLT:\n"));

+      Length = sizeof (REMOTE_NDIS_RESET_CMPLT);

+      break;

+    case RNDIS_INDICATE_STATUS_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_INDICATE_STATUS_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_INDICATE_STATUS_MSG);

+      break;

+    case RNDIS_KEEPALIVE_MSG:

+      DEBUG ((DEBUG_INFO, "RNDIS_KEEPALIVE_MSG:\n"));

+      Length = sizeof (REMOTE_NDIS_KEEPALIVE_MSG);

+      break;

+    case RNDIS_KEEPALIVE_CMPLT:

+      DEBUG ((DEBUG_INFO, "RNDIS_KEEPALIVE_CMPLT:\n"));

+      Length = sizeof (REMOTE_NDIS_KEEPALIVE_CMPLT);

+  }

+

+  if (Length) {

+    UINTN  Index = 0;

+    for ( ; Length; Length -= 4, Index++) {

+      DEBUG ((DEBUG_INFO, "%8X\t", RndisMsg[Index]));

+      if (((Index % 4) == 3) && (Index != 0)) {

+        DEBUG ((DEBUG_INFO, "\n"));

+      }

+    }

+

+    if (Index % 4) {

+      DEBUG ((DEBUG_INFO, "\n"));

+    }

+  }

+}

--
2.35.1.windows.2
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.





 

--

Pedro Falcato


 

--

Pedro Falcato

-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.


Re: [PATCH V4 0/2] Re-design CcProbeLib

Gerd Hoffmann
 

On Sat, Aug 27, 2022 at 07:07:06AM +0800, Min Xu wrote:
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3974

CcProbeLib once was designed to probe the Confidential Computing guest
type by checking the PcdOvmfWorkArea. But this memory is allocated with
either EfiACPIMemoryNVS or EfiBootServicesData. It cannot be accessed
after ExitBootService. Please see the detailed analysis in BZ#3974.

To fix this issue, CcProbeLib is re-designed as 2 implementation:
- SecPeiCcProbeLib
- DxeCcProbeLib

In SecPeiCcProbeLib we check the CC guest type by reading the
PcdOvmfWorkArea. Because it is used in SEC / PEI and we don't worry about
the issues in BZ#3974.

In DxeCcProbeLib we cache the GuestType in Ovmf work area in a global
variable. After that the Guest type is returned with the cached value.
So that we don't need to worry about the access to Ovmf work area after
ExitBootService.

The reason why we probe CC guest type in 2 different ways is the global
varialbe. Global variable cannot be used in SEC/PEI and CcProbe is called
very frequently.

Code: https://github.com/mxu9/edk2/tree/CcProbeLib.BZ3974.v4

v4 changes:
- Read Cc guest type in both DxeCcProbeLib's constructor and CcProbe. So
that we guarantee the Cc guest type is read early enough.
Acked-by: Gerd Hoffmann <kraxel@...>

take care,
Gerd


Re: [PATCH v2] OvmfPkg/IncompatiblePciDeviceSupportDxe: Ignore OptionRom in Sev guest

Gerd Hoffmann
 

On Fri, Aug 26, 2022 at 11:15:20PM +0800, Lee, Chun-Yi wrote:
Reference: https://bugzilla.tianocore.org/show_bug.cgi?id=4031

This patch is similar to the c477b2783f patch for Td guest.

Host VMM may inject OptionRom which is untrusted in Sev guest. So PCI
OptionRom needs to be ignored if it is Sev guest. According to
"Table 20. ACPI 2.0 & 3.0 QWORD Address Space Descriptor Usage"
PI spec 1.7, type-specific flags can be set to 0 when Address
Translation Offset == 6 to skip device option ROM.

Without this patch, Sev guest may shows invalid MMIO opcode error
as following:

Invalid MMIO opcode (F6)
ASSERT /home/abuild/rpmbuild/BUILD/edk2-edk2-stable202202/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c(1041): ((BOOLEAN)(0==1))

The OptionRom must be disabled both on Td and Sev guests, so we direct
use CcProbe().

v2: Use CcProbe() instead of TdIsEnabled() and MemEncryptSevIsEnabled().
Reviewed-by: Gerd Hoffmann <kraxel@...>


[PATCH v1] BaseTools: 64bit FSP FV map file cannot be created correctly

Kuo, Ted
 

https://bugzilla.tianocore.org/show_bug.cgi?id=3D4035
64bit FSP FV map file cannot be created correctly when using CLANG
compiler. When compiling 64bit FSP with CLANG, there is no prefix
symbol '_' added to function name. Hence FSP FV map file cannot be
created properly. Updated the if condition for CLANG in GenFv to
get the issue fixed.

Cc: Bob Feng <bob.c.feng@...>
Cc: Liming Gao <gaoliming@...>
Cc: Yuwei Chen <yuwei.chen@...>
Cc: Lee Hamel <lee.m.hamel@...>
Signed-off-by: Ted Kuo <ted.kuo@...>
---
BaseTools/Source/C/GenFv/GenFvInternalLib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/BaseTools/Source/C/GenFv/GenFvInternalLib.c b/BaseTools/Source=
/C/GenFv/GenFvInternalLib.c
index d650a527a5..d28ac8f6eb 100644
--- a/BaseTools/Source/C/GenFv/GenFvInternalLib.c
+++ b/BaseTools/Source/C/GenFv/GenFvInternalLib.c
@@ -1004,7 +1004,7 @@ Returns:
if (IsUseClang) {=0D
sscanf (Line, "%llx %s %s %s", &TempLongAddress, KeyWord, KeyWord2=
, FunctionTypeName);=0D
FunctionAddress =3D (UINT64) TempLongAddress;=0D
- if (FunctionTypeName [0] =3D=3D '_' ) {=0D
+ if (FunctionTypeName [0] !=3D '/' && FunctionTypeName [0] !=3D '.'=
&& FunctionTypeName [1] !=3D ':') {=0D
fprintf (FvMapFile, " 0x%010llx ", (unsigned long long) (Ima=
geBaseAddress + FunctionAddress - LinkTimeBaseAddress));=0D
fprintf (FvMapFile, "%s\n", FunctionTypeName);=0D
}=0D
--=20
2.35.3.windows.1


EDK II Stable Tag release edk2-stable202208 completed

gaoliming
 

Hi, all

 

The tag edk2-stable202208 has been created.

https://github.com/tianocore/edk2/releases/tag/edk2-stable202208

 

  git clone -b edk2-stable202208 https://github.com/tianocore/edk2.git

 

The quiet period has now ended. Thank you for your cooperation and patience.

Normal commits can now be resumed.

 

Next edk2 stable tag (edk2-stable202211) planning has been added into wiki page.

https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Release-Planning

 

If you have ideas for features in the next stable tag, please enter a Bugzilla for evaluation.

 

Please let me know if there are existing open Bugzilla entries that should be targeted at next stable tag.

 

Thanks

Liming


[PATCH 4/4] CryptoPkg: add UnitTest for AeadAesGcm.

Qi Zhang
 

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4036

Signed-off-by: Qi Zhang <qi1.zhang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Jian J Wang <jian.j.wang@...>
Cc: Xiaoyu Lu <xiaoyu1.lu@...>
Cc: Guomin Jiang <guomin.jiang@...>
---
.../BaseCryptLib/UnitTestHostBaseCryptLib.inf | 1 +
.../Library/BaseCryptLib/AeadAesGcmTests.c | 112 ++++++++++++++++++
.../BaseCryptLib/BaseCryptLibUnitTests.c | 1 +
.../Library/BaseCryptLib/TestBaseCryptLib.h | 3 +
.../BaseCryptLib/TestBaseCryptLibHost.inf | 1 +
.../BaseCryptLib/TestBaseCryptLibShell.inf | 1 +
6 files changed, 119 insertions(+)
create mode 100644 CryptoPkg/Test/UnitTest/Library/BaseCryptLib/AeadAesGcm=
Tests.c

diff --git a/CryptoPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf b/=
CryptoPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
index 11ff1c6931..49c75ecccd 100644
--- a/CryptoPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
+++ b/CryptoPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
@@ -31,6 +31,7 @@
Hmac/CryptHmacSha256.c=0D
Kdf/CryptHkdf.c=0D
Cipher/CryptAes.c=0D
+ Cipher/CryptAeadAesGcm.c=0D
Pk/CryptRsaBasic.c=0D
Pk/CryptRsaExt.c=0D
Pk/CryptPkcs1Oaep.c=0D
diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/AeadAesGcmTests.c=
b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/AeadAesGcmTests.c
new file mode 100644
index 0000000000..989a4df788
--- /dev/null
+++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/AeadAesGcmTests.c
@@ -0,0 +1,112 @@
+/** @file=0D
+ Application for Authenticated Encryption with Associated Data=0D
+ (AEAD) Validation.=0D
+=0D
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>=0D
+SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#include "TestBaseCryptLib.h"=0D
+=0D
+/* AES-GCM test data from NIST public test vectors */=0D
+GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gcm_key[] =3D {=0D
+ 0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66,=
=0D
+ 0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69,=
=0D
+ 0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f=0D
+};=0D
+=0D
+GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gcm_iv[] =3D {=0D
+ 0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84=0D
+};=0D
+=0D
+GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gcm_pt[] =3D {=0D
+ 0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea,=
=0D
+ 0xcc, 0x2b, 0xf2, 0xa5=0D
+};=0D
+=0D
+GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gcm_aad[] =3D {=0D
+ 0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43,=
=0D
+ 0x7f, 0xec, 0x78, 0xde=0D
+};=0D
+=0D
+GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gcm_ct[] =3D {=0D
+ 0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e,=
=0D
+ 0xb9, 0xf2, 0x17, 0x36=0D
+};=0D
+=0D
+GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 gcm_tag[] =3D {=0D
+ 0x67, 0xba, 0x05, 0x10, 0x26, 0x2a, 0xe4, 0x87, 0xd7, 0x37, 0xee, 0x62,=
=0D
+ 0x98, 0xf7, 0x7e, 0x0c=0D
+};=0D
+=0D
+UNIT_TEST_STATUS=0D
+EFIAPI=0D
+TestVerifyAeadAesGcm (=0D
+ IN UNIT_TEST_CONTEXT Context=0D
+ )=0D
+{=0D
+ BOOLEAN Status;=0D
+ UINT8 OutBuffer[1024];=0D
+ UINTN OutBufferSize;=0D
+ UINT8 OutTag[1024];=0D
+ UINTN OutTagSize;=0D
+=0D
+ OutBufferSize =3D sizeof (OutBuffer);=0D
+ OutTagSize =3D sizeof (gcm_tag);=0D
+ ZeroMem (OutBuffer, sizeof (OutBuffer));=0D
+ ZeroMem (OutTag, sizeof (OutTag));=0D
+ Status =3D AeadAesGcmEncrypt (=0D
+ gcm_key,=0D
+ sizeof (gcm_key),=0D
+ gcm_iv,=0D
+ sizeof (gcm_iv),=0D
+ gcm_aad,=0D
+ sizeof (gcm_aad),=0D
+ gcm_pt,=0D
+ sizeof (gcm_pt),=0D
+ OutTag,=0D
+ OutTagSize,=0D
+ OutBuffer,=0D
+ &OutBufferSize=0D
+ );=0D
+ UT_ASSERT_TRUE (Status);=0D
+=0D
+ UT_ASSERT_EQUAL (OutBufferSize, sizeof (gcm_ct));=0D
+=0D
+ UT_ASSERT_MEM_EQUAL (OutBuffer, gcm_ct, sizeof (gcm_ct));=0D
+=0D
+ UT_ASSERT_MEM_EQUAL (OutTag, gcm_tag, sizeof (gcm_tag));=0D
+=0D
+ ZeroMem (OutBuffer, sizeof (OutBuffer));=0D
+ Status =3D AeadAesGcmDecrypt (=0D
+ gcm_key,=0D
+ sizeof (gcm_key),=0D
+ gcm_iv,=0D
+ sizeof (gcm_iv),=0D
+ gcm_aad,=0D
+ sizeof (gcm_aad),=0D
+ gcm_ct,=0D
+ sizeof (gcm_ct),=0D
+ gcm_tag,=0D
+ sizeof (gcm_tag),=0D
+ OutBuffer,=0D
+ &OutBufferSize=0D
+ );=0D
+ UT_ASSERT_TRUE (Status);=0D
+=0D
+ UT_ASSERT_EQUAL (OutBufferSize, sizeof (gcm_pt));=0D
+=0D
+ UT_ASSERT_MEM_EQUAL (OutBuffer, gcm_pt, sizeof (gcm_pt));=0D
+=0D
+ return UNIT_TEST_PASSED;=0D
+}=0D
+=0D
+TEST_DESC mAeadAesGcmTest[] =3D {=0D
+ //=0D
+ // -----Description--------------------------------------Class----------=
------------Function---------------------------------Pre-------------------=
--Post---------Context=0D
+ //=0D
+ { "TestVerifyAeadAesGcm()", "CryptoPkg.BaseCryptLib.AeadAesGcm", TestVer=
ifyAeadAesGcm, NULL, NULL, NULL },=0D
+};=0D
+=0D
+UINTN mAeadAesGcmTestNum =3D ARRAY_SIZE (mAeadAesGcmTest);=0D
diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/BaseCryptLibUnitT=
ests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/BaseCryptLibUnitTests=
.c
index 3c57aead1e..8cec308157 100644
--- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/BaseCryptLibUnitTests.c
+++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/BaseCryptLibUnitTests.c
@@ -25,6 +25,7 @@ SUITE_DESC mSuiteDesc[] =3D {
{ "DH verify tests", "CryptoPkg.BaseCryptLib", NULL, NULL, &=
mDhTestNum, mDhTest },=0D
{ "PRNG verify tests", "CryptoPkg.BaseCryptLib", NULL, NULL, &=
mPrngTestNum, mPrngTest },=0D
{ "OAEP encrypt verify tests", "CryptoPkg.BaseCryptLib", NULL, NULL, &=
mOaepTestNum, mOaepTest },=0D
+ { "Aead AES Gcm tests", "CryptoPkg.BaseCryptLib", NULL, NULL, &=
mAeadAesGcmTestNum, mAeadAesGcmTest },=0D
};=0D
=0D
EFI_STATUS=0D
diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLib.=
h b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLib.h
index a6b3482742..ca763177a7 100644
--- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLib.h
+++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLib.h
@@ -86,6 +86,9 @@ extern TEST_DESC mOaepTest[];
extern UINTN mRsaPssTestNum;=0D
extern TEST_DESC mRsaPssTest[];=0D
=0D
+extern UINTN mAeadAesGcmTestNum;=0D
+extern TEST_DESC mAeadAesGcmTest[];=0D
+=0D
/** Creates a framework you can use */=0D
EFI_STATUS=0D
EFIAPI=0D
diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibH=
ost.inf b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibHost=
.inf
index 399db596c2..cf08b6553d 100644
--- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibHost.inf
+++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibHost.inf
@@ -37,6 +37,7 @@
OaepEncryptTests.c=0D
RsaPssTests.c=0D
ParallelhashTests.c=0D
+ AeadAesGcmTests.c=0D
=0D
[Packages]=0D
MdePkg/MdePkg.dec=0D
diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibS=
hell.inf b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibShe=
ll.inf
index ca789aa6ad..dc81b78357 100644
--- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibShell.inf
+++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/TestBaseCryptLibShell.inf
@@ -36,6 +36,7 @@
Pkcs7EkuTests.c=0D
OaepEncryptTests.c=0D
RsaPssTests.c=0D
+ AeadAesGcmTests.c=0D
=0D
[Packages]=0D
MdePkg/MdePkg.dec=0D
--=20
2.26.2.windows.1


[PATCH 3/4] CryptoPkg: add AeadAesGcm to Crypto Service.

Qi Zhang
 

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4036

Signed-off-by: Qi Zhang <qi1.zhang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Jian J Wang <jian.j.wang@...>
Cc: Xiaoyu Lu <xiaoyu1.lu@...>
Cc: Guomin Jiang <guomin.jiang@...>
---
CryptoPkg/CryptoPkg.dsc | 2 +
CryptoPkg/Driver/Crypto.c | 94 ++++++++++++++++++-
.../Pcd/PcdCryptoServiceFamilyEnable.h | 7 ++
.../BaseCryptLibOnProtocolPpi/CryptLib.c | 93 ++++++++++++++++++
CryptoPkg/Private/Protocol/Crypto.h | 86 +++++++++++++++++
5 files changed, 281 insertions(+), 1 deletion(-)

diff --git a/CryptoPkg/CryptoPkg.dsc b/CryptoPkg/CryptoPkg.dsc
index 50e7721f25..0f6587b36d 100644
--- a/CryptoPkg/CryptoPkg.dsc
+++ b/CryptoPkg/CryptoPkg.dsc
@@ -212,6 +212,8 @@
gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Aes.Services.In=
it | TRUE=0D
gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Aes.Services.Cb=
cEncrypt | TRUE=0D
gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Aes.Services.Cb=
cDecrypt | TRUE=0D
+ gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.AeadAesGcm.Serv=
ices.Encrypt | TRUE=0D
+ gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.AeadAesGcm.Serv=
ices.Decrypt | TRUE=0D
!endif=0D
=0D
##########################################################################=
#########################=0D
diff --git a/CryptoPkg/Driver/Crypto.c b/CryptoPkg/Driver/Crypto.c
index 76cb9f4da0..016cb5da74 100644
--- a/CryptoPkg/Driver/Crypto.c
+++ b/CryptoPkg/Driver/Crypto.c
@@ -4582,6 +4582,95 @@ CryptoServiceParallelHash256HashAll (
return CALL_BASECRYPTLIB (ParallelHash.Services.HashAll, ParallelHash256=
HashAll, (Input, InputByteLen, BlockSize, Output, OutputByteLen, Customizat=
ion, CustomByteLen), FALSE);=0D
}=0D
=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+CryptoServiceAeadAesGcmEncrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ return CALL_BASECRYPTLIB (AeadAesGcm.Services.Encrypt, AeadAesGcmEncrypt=
, (Key, KeySize, Iv, IvSize, AData, ADataSize, DataIn, DataInSize, TagOut, =
TagSize, DataOut, DataOutSize), FALSE);=0D
+}=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+CryptoServiceAeadAesGcmDecrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ return CALL_BASECRYPTLIB (AeadAesGcm.Services.Decrypt, AeadAesGcmDecrypt=
, (Key, KeySize, Iv, IvSize, AData, ADataSize, DataIn, DataInSize, Tag, Tag=
Size, DataOut, DataOutSize), FALSE);=0D
+}=0D
+=0D
const EDKII_CRYPTO_PROTOCOL mEdkiiCrypto =3D {=0D
/// Version=0D
CryptoServiceGetCryptoVersion,=0D
@@ -4787,5 +4876,8 @@ const EDKII_CRYPTO_PROTOCOL mEdkiiCrypto =3D {
CryptoServiceRsaPssSign,=0D
CryptoServiceRsaPssVerify,=0D
/// Parallel hash=0D
- CryptoServiceParallelHash256HashAll=0D
+ CryptoServiceParallelHash256HashAll,=0D
+ /// Aead Aes GCM=0D
+ CryptoServiceAeadAesGcmEncrypt,=0D
+ CryptoServiceAeadAesGcmDecrypt=0D
};=0D
diff --git a/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h b/CryptoP=
kg/Include/Pcd/PcdCryptoServiceFamilyEnable.h
index 3d53c2f105..89f61d0d99 100644
--- a/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h
+++ b/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h
@@ -301,6 +301,13 @@ typedef struct {
} Services;=0D
UINT32 Family;=0D
} ParallelHash;=0D
+ union {=0D
+ struct {=0D
+ UINT8 Encrypt : 1;=0D
+ UINT8 Decrypt : 1;=0D
+ } Services;=0D
+ UINT32 Family;=0D
+ } AeadAesGcm;=0D
} PCD_CRYPTO_SERVICE_FAMILY_ENABLE;=0D
=0D
#endif=0D
diff --git a/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c b/Crypt=
oPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c
index 8ee1b53cf9..765d298ad1 100644
--- a/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c
+++ b/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c
@@ -1340,6 +1340,99 @@ AesCbcDecrypt (
CALL_CRYPTO_SERVICE (AesCbcDecrypt, (AesContext, Input, InputSize, Ivec,=
Output), FALSE);=0D
}=0D
=0D
+// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
+// Authenticated Encryption with Associated Data (AEAD) Cryptography Pr=
imitive=0D
+// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmEncrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ CALL_CRYPTO_SERVICE (AeadAesGcmEncrypt, (Key, KeySize, Iv, IvSize, AData=
, ADataSize, DataIn, DataInSize, TagOut, TagSize, DataOut, DataOutSize), FA=
LSE);=0D
+}=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmDecrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ CALL_CRYPTO_SERVICE (AeadAesGcmDecrypt, (Key, KeySize, Iv, IvSize, AData=
, ADataSize, DataIn, DataInSize, Tag, TagSize, DataOut, DataOutSize), FALSE=
);=0D
+}=0D
+=0D
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
// Asymmetric Cryptography Primitive=0D
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
diff --git a/CryptoPkg/Private/Protocol/Crypto.h b/CryptoPkg/Private/Protoc=
ol/Crypto.h
index c417568e96..d79cc3c540 100644
--- a/CryptoPkg/Private/Protocol/Crypto.h
+++ b/CryptoPkg/Private/Protocol/Crypto.h
@@ -3486,6 +3486,89 @@ BOOLEAN
IN UINTN CustomByteLen=0D
);=0D
=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+typedef=0D
+BOOLEAN=0D
+(EFIAPI *EDKII_AEAD_AES_GCM_ENCRYPT)(=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ );=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+typedef=0D
+BOOLEAN=0D
+(EFIAPI *EDKII_AEAD_AES_GCM_DECRYPT)(=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ );=0D
+=0D
///=0D
/// EDK II Crypto Protocol=0D
///=0D
@@ -3675,6 +3758,9 @@ struct _EDKII_CRYPTO_PROTOCOL {
EDKII_CRYPTO_RSA_PSS_VERIFY RsaPssVerify;=0D
/// Parallel hash=0D
EDKII_CRYPTO_PARALLEL_HASH_ALL ParallelHash256HashAl=
l;=0D
+ /// AEAD AES-GCM=0D
+ EDKII_AEAD_AES_GCM_ENCRYPT AeadAesGcmEncrypt;=0D
+ EDKII_AEAD_AES_GCM_DECRYPT AeadAesGcmDecrypt;=0D
};=0D
=0D
extern GUID gEdkiiCryptoProtocolGuid;=0D
--=20
2.26.2.windows.1


[PATCH 2/4] CryptoPkg: add AeadAesGcm support.

Qi Zhang
 

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4036

Signed-off-by: Qi Zhang <qi1.zhang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Jian J Wang <jian.j.wang@...>
Cc: Xiaoyu Lu <xiaoyu1.lu@...>
Cc: Guomin Jiang <guomin.jiang@...>
---
.../Library/BaseCryptLib/BaseCryptLib.inf | 1 +
.../BaseCryptLib/Cipher/CryptAeadAesGcm.c | 279 ++++++++++++++++++
.../BaseCryptLib/Cipher/CryptAeadAesGcmNull.c | 100 +++++++
.../Library/BaseCryptLib/PeiCryptLib.inf | 1 +
.../Library/BaseCryptLib/RuntimeCryptLib.inf | 1 +
.../Library/BaseCryptLib/SmmCryptLib.inf | 1 +
.../BaseCryptLibNull/BaseCryptLibNull.inf | 1 +
.../Cipher/CryptAeadAesGcmNull.c | 100 +++++++
8 files changed, 484 insertions(+)
create mode 100644 CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcm.c
create mode 100644 CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcmNu=
ll.c
create mode 100644 CryptoPkg/Library/BaseCryptLibNull/Cipher/CryptAeadAesG=
cmNull.c

diff --git a/CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf b/CryptoPkg/Li=
brary/BaseCryptLib/BaseCryptLib.inf
index 3d7b917103..3a00e16948 100644
--- a/CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+++ b/CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
@@ -38,6 +38,7 @@
Hmac/CryptHmacSha256.c=0D
Kdf/CryptHkdf.c=0D
Cipher/CryptAes.c=0D
+ Cipher/CryptAeadAesGcm.c=0D
Pk/CryptRsaBasic.c=0D
Pk/CryptRsaExt.c=0D
Pk/CryptPkcs1Oaep.c=0D
diff --git a/CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcm.c b/Cryp=
toPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcm.c
new file mode 100644
index 0000000000..b4c93d47a9
--- /dev/null
+++ b/CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcm.c
@@ -0,0 +1,279 @@
+/** @file=0D
+ AEAD (AES-GCM) Wrapper Implementation over OpenSSL.=0D
+=0D
+ RFC 5116 - An Interface and Algorithms for Authenticated Encryption=0D
+ NIST SP800-38d - Cipher Modes of Operation: Galois / Counter Mode(GCM) a=
nd GMAC=0D
+=0D
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>=0D
+SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#include "InternalCryptLib.h"=0D
+#include <openssl/aes.h>=0D
+#include <openssl/evp.h>=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmEncrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ EVP_CIPHER_CTX *Ctx;=0D
+ CONST EVP_CIPHER *Cipher;=0D
+ UINTN TempOutSize;=0D
+ BOOLEAN RetValue;=0D
+=0D
+ if (DataInSize > INT_MAX) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if (ADataSize > INT_MAX) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if (IvSize !=3D 12) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ switch (KeySize) {=0D
+ case 16:=0D
+ Cipher =3D EVP_aes_128_gcm ();=0D
+ break;=0D
+ case 24:=0D
+ Cipher =3D EVP_aes_192_gcm ();=0D
+ break;=0D
+ case 32:=0D
+ Cipher =3D EVP_aes_256_gcm ();=0D
+ break;=0D
+ default:=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if ((TagSize !=3D 12) && (TagSize !=3D 13) && (TagSize !=3D 14) && (TagS=
ize !=3D 15) && (TagSize !=3D 16)) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if (DataOutSize !=3D NULL) {=0D
+ if ((*DataOutSize > INT_MAX) || (*DataOutSize < DataInSize)) {=0D
+ return FALSE;=0D
+ }=0D
+ }=0D
+=0D
+ Ctx =3D EVP_CIPHER_CTX_new ();=0D
+ if (Ctx =3D=3D NULL) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_EncryptInit_ex (Ctx, Cipher, NULL, NULL, NULL)=
;=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_CIPHER_CTX_ctrl (Ctx, EVP_CTRL_GCM_SET_IVLEN, =
(INT32)IvSize, NULL);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_EncryptInit_ex (Ctx, NULL, NULL, Key, Iv);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_EncryptUpdate (Ctx, NULL, (INT32 *)&TempOutSiz=
e, AData, (INT32)ADataSize);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_EncryptUpdate (Ctx, DataOut, (INT32 *)&TempOut=
Size, DataIn, (INT32)DataInSize);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_EncryptFinal_ex (Ctx, DataOut, (INT32 *)&TempO=
utSize);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_CIPHER_CTX_ctrl (Ctx, EVP_CTRL_GCM_GET_TAG, (I=
NT32)TagSize, (VOID *)TagOut);=0D
+=0D
+Done:=0D
+ EVP_CIPHER_CTX_free (Ctx);=0D
+ if (!RetValue) {=0D
+ return RetValue;=0D
+ }=0D
+=0D
+ if (DataOutSize !=3D NULL) {=0D
+ *DataOutSize =3D DataInSize;=0D
+ }=0D
+=0D
+ return RetValue;=0D
+}=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmDecrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ EVP_CIPHER_CTX *Ctx;=0D
+ CONST EVP_CIPHER *Cipher;=0D
+ UINTN TempOutSize;=0D
+ BOOLEAN RetValue;=0D
+=0D
+ if (DataInSize > INT_MAX) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if (ADataSize > INT_MAX) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if (IvSize !=3D 12) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ switch (KeySize) {=0D
+ case 16:=0D
+ Cipher =3D EVP_aes_128_gcm ();=0D
+ break;=0D
+ case 24:=0D
+ Cipher =3D EVP_aes_192_gcm ();=0D
+ break;=0D
+ case 32:=0D
+ Cipher =3D EVP_aes_256_gcm ();=0D
+ break;=0D
+ default:=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if ((TagSize !=3D 12) && (TagSize !=3D 13) && (TagSize !=3D 14) && (TagS=
ize !=3D 15) && (TagSize !=3D 16)) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ if (DataOutSize !=3D NULL) {=0D
+ if ((*DataOutSize > INT_MAX) || (*DataOutSize < DataInSize)) {=0D
+ return FALSE;=0D
+ }=0D
+ }=0D
+=0D
+ Ctx =3D EVP_CIPHER_CTX_new ();=0D
+ if (Ctx =3D=3D NULL) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_DecryptInit_ex (Ctx, Cipher, NULL, NULL, NULL)=
;=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_CIPHER_CTX_ctrl (Ctx, EVP_CTRL_GCM_SET_IVLEN, =
(INT32)IvSize, NULL);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_DecryptInit_ex (Ctx, NULL, NULL, Key, Iv);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_DecryptUpdate (Ctx, NULL, (INT32 *)&TempOutSiz=
e, AData, (INT32)ADataSize);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_DecryptUpdate (Ctx, DataOut, (INT32 *)&TempOut=
Size, DataIn, (INT32)DataInSize);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_CIPHER_CTX_ctrl (Ctx, EVP_CTRL_GCM_SET_TAG, (I=
NT32)TagSize, (VOID *)Tag);=0D
+ if (!RetValue) {=0D
+ goto Done;=0D
+ }=0D
+=0D
+ RetValue =3D (BOOLEAN)EVP_DecryptFinal_ex (Ctx, DataOut, (INT32 *)&TempO=
utSize);=0D
+=0D
+Done:=0D
+ EVP_CIPHER_CTX_free (Ctx);=0D
+ if (!RetValue) {=0D
+ return RetValue;=0D
+ }=0D
+=0D
+ if (DataOutSize !=3D NULL) {=0D
+ *DataOutSize =3D DataInSize;=0D
+ }=0D
+=0D
+ return RetValue;=0D
+}=0D
diff --git a/CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcmNull.c b/=
CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcmNull.c
new file mode 100644
index 0000000000..b9f9d16ff9
--- /dev/null
+++ b/CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcmNull.c
@@ -0,0 +1,100 @@
+/** @file=0D
+ AEAD Wrapper Implementation which does not provide real capabilities.=0D
+=0D
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>=0D
+SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#include "InternalCryptLib.h"=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmEncrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ ASSERT (FALSE);=0D
+ return FALSE;=0D
+}=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmDecrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ ASSERT (FALSE);=0D
+ return FALSE;=0D
+}=0D
diff --git a/CryptoPkg/Library/BaseCryptLib/PeiCryptLib.inf b/CryptoPkg/Lib=
rary/BaseCryptLib/PeiCryptLib.inf
index 01de27e037..43b122d904 100644
--- a/CryptoPkg/Library/BaseCryptLib/PeiCryptLib.inf
+++ b/CryptoPkg/Library/BaseCryptLib/PeiCryptLib.inf
@@ -44,6 +44,7 @@
Hmac/CryptHmacSha256.c=0D
Kdf/CryptHkdf.c=0D
Cipher/CryptAesNull.c=0D
+ Cipher/CryptAeadAesGcmNull.c=0D
Pk/CryptRsaBasic.c=0D
Pk/CryptRsaExtNull.c=0D
Pk/CryptPkcs1OaepNull.c=0D
diff --git a/CryptoPkg/Library/BaseCryptLib/RuntimeCryptLib.inf b/CryptoPkg=
/Library/BaseCryptLib/RuntimeCryptLib.inf
index d28fb98b66..291e30cf5e 100644
--- a/CryptoPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
+++ b/CryptoPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
@@ -44,6 +44,7 @@
Hmac/CryptHmacSha256.c=0D
Kdf/CryptHkdf.c=0D
Cipher/CryptAes.c=0D
+ Cipher/CryptAeadAesGcmNull.c=0D
Pk/CryptRsaBasic.c=0D
Pk/CryptRsaExtNull.c=0D
Pk/CryptPkcs1OaepNull.c=0D
diff --git a/CryptoPkg/Library/BaseCryptLib/SmmCryptLib.inf b/CryptoPkg/Lib=
rary/BaseCryptLib/SmmCryptLib.inf
index 91a1715095..6c65cc7a67 100644
--- a/CryptoPkg/Library/BaseCryptLib/SmmCryptLib.inf
+++ b/CryptoPkg/Library/BaseCryptLib/SmmCryptLib.inf
@@ -45,6 +45,7 @@
Hmac/CryptHmacSha256.c=0D
Kdf/CryptHkdfNull.c=0D
Cipher/CryptAes.c=0D
+ Cipher/CryptAeadAesGcmNull.c=0D
Pk/CryptRsaBasic.c=0D
Pk/CryptRsaExtNull.c=0D
Pk/CryptPkcs1Oaep.c=0D
diff --git a/CryptoPkg/Library/BaseCryptLibNull/BaseCryptLibNull.inf b/Cryp=
toPkg/Library/BaseCryptLibNull/BaseCryptLibNull.inf
index 63d1d82d19..bfc0d6a869 100644
--- a/CryptoPkg/Library/BaseCryptLibNull/BaseCryptLibNull.inf
+++ b/CryptoPkg/Library/BaseCryptLibNull/BaseCryptLibNull.inf
@@ -38,6 +38,7 @@
Hmac/CryptHmacSha256Null.c=0D
Kdf/CryptHkdfNull.c=0D
Cipher/CryptAesNull.c=0D
+ Cipher/CryptAeadAesGcmNull.c=0D
Pk/CryptRsaBasicNull.c=0D
Pk/CryptRsaExtNull.c=0D
Pk/CryptPkcs1OaepNull.c=0D
diff --git a/CryptoPkg/Library/BaseCryptLibNull/Cipher/CryptAeadAesGcmNull.=
c b/CryptoPkg/Library/BaseCryptLibNull/Cipher/CryptAeadAesGcmNull.c
new file mode 100644
index 0000000000..b9f9d16ff9
--- /dev/null
+++ b/CryptoPkg/Library/BaseCryptLibNull/Cipher/CryptAeadAesGcmNull.c
@@ -0,0 +1,100 @@
+/** @file=0D
+ AEAD Wrapper Implementation which does not provide real capabilities.=0D
+=0D
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>=0D
+SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#include "InternalCryptLib.h"=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmEncrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ ASSERT (FALSE);=0D
+ return FALSE;=0D
+}=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmDecrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ )=0D
+{=0D
+ ASSERT (FALSE);=0D
+ return FALSE;=0D
+}=0D
--=20
2.26.2.windows.1


[PATCH 1/4] CryptoPkg: add AeadAesGcm function() definition.

Qi Zhang
 

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4036

Signed-off-by: Qi Zhang <qi1.zhang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Jian J Wang <jian.j.wang@...>
Cc: Xiaoyu Lu <xiaoyu1.lu@...>
Cc: Guomin Jiang <guomin.jiang@...>
---
CryptoPkg/Include/Library/BaseCryptLib.h | 87 ++++++++++++++++++++++++
1 file changed, 87 insertions(+)

diff --git a/CryptoPkg/Include/Library/BaseCryptLib.h b/CryptoPkg/Include/L=
ibrary/BaseCryptLib.h
index 7d1499350a..b27ec28944 100644
--- a/CryptoPkg/Include/Library/BaseCryptLib.h
+++ b/CryptoPkg/Include/Library/BaseCryptLib.h
@@ -1172,6 +1172,93 @@ AesCbcDecrypt (
OUT UINT8 *Output=0D
);=0D
=0D
+// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
+// Authenticated Encryption with Associated Data (AEAD) Cryptography Pr=
imitive=0D
+// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated encryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be encrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[out] TagOut Pointer to a buffer that receives the authentic=
ation tag output.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the encryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated encryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated encryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmEncrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ OUT UINT8 *TagOut,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ );=0D
+=0D
+/**=0D
+ Performs AEAD AES-GCM authenticated decryption on a data buffer and addi=
tional authenticated data (AAD).=0D
+=0D
+ IvSize must be 12, otherwise FALSE is returned.=0D
+ KeySize must be 16, 24 or 32, otherwise FALSE is returned.=0D
+ TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.=0D
+ If additional authenticated data verification fails, FALSE is returned.=
=0D
+=0D
+ @param[in] Key Pointer to the encryption key.=0D
+ @param[in] KeySize Size of the encryption key in bytes.=0D
+ @param[in] Iv Pointer to the IV value.=0D
+ @param[in] IvSize Size of the IV value in bytes.=0D
+ @param[in] AData Pointer to the additional authenticated data (A=
AD).=0D
+ @param[in] ADataSize Size of the additional authenticated data (AAD)=
in bytes.=0D
+ @param[in] DataIn Pointer to the input data buffer to be decrypte=
d.=0D
+ @param[in] DataInSize Size of the input data buffer in bytes.=0D
+ @param[in] Tag Pointer to a buffer that contains the authentic=
ation tag.=0D
+ @param[in] TagSize Size of the authentication tag in bytes.=0D
+ @param[out] DataOut Pointer to a buffer that receives the decryptio=
n output.=0D
+ @param[out] DataOutSize Size of the output data buffer in bytes.=0D
+=0D
+ @retval TRUE AEAD AES-GCM authenticated decryption succeeded.=0D
+ @retval FALSE AEAD AES-GCM authenticated decryption failed.=0D
+=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+AeadAesGcmDecrypt (=0D
+ IN CONST UINT8 *Key,=0D
+ IN UINTN KeySize,=0D
+ IN CONST UINT8 *Iv,=0D
+ IN UINTN IvSize,=0D
+ IN CONST UINT8 *AData,=0D
+ IN UINTN ADataSize,=0D
+ IN CONST UINT8 *DataIn,=0D
+ IN UINTN DataInSize,=0D
+ IN CONST UINT8 *Tag,=0D
+ IN UINTN TagSize,=0D
+ OUT UINT8 *DataOut,=0D
+ OUT UINTN *DataOutSize=0D
+ );=0D
+=0D
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
// Asymmetric Cryptography Primitive=0D
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0D
--=20
2.26.2.windows.1


[PATCH 0/4] CryptoPkg: add AeadAesGcm support.

Qi Zhang
 

Add AeadAesGcm Encrypt and Decrypt.
With this change, the size increase of BaseCyrptLib is about 60K bytes.
The new functions are verifed by the Host UnitTest.
And also it has been integratd in
https://github.com/tianocore/edk2-staging/tree/DeviceSecurity and been
verified.

All the code change is on the PR https://github.com/tianocore/edk2/pull/3252.


REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4036

Signed-off-by: Qi Zhang <qi1.zhang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Jian J Wang <jian.j.wang@...>
Cc: Xiaoyu Lu <xiaoyu1.lu@...>
Cc: Guomin Jiang <guomin.jiang@...>

Qi Zhang (4):
CryptoPkg: add AeadAesGcm function() definition.
CryptoPkg: add AeadAesGcm support.
CryptoPkg: add AeadAesGcm to Crypto Service.
CryptoPkg: add UnitTest for AeadAesGcm.

CryptoPkg/CryptoPkg.dsc | 2 +
CryptoPkg/Driver/Crypto.c | 94 +++++-
CryptoPkg/Include/Library/BaseCryptLib.h | 87 ++++++
.../Pcd/PcdCryptoServiceFamilyEnable.h | 7 +
.../Library/BaseCryptLib/BaseCryptLib.inf | 1 +
.../BaseCryptLib/Cipher/CryptAeadAesGcm.c | 279 ++++++++++++++++++
.../BaseCryptLib/Cipher/CryptAeadAesGcmNull.c | 100 +++++++
.../Library/BaseCryptLib/PeiCryptLib.inf | 1 +
.../Library/BaseCryptLib/RuntimeCryptLib.inf | 1 +
.../Library/BaseCryptLib/SmmCryptLib.inf | 1 +
.../BaseCryptLib/UnitTestHostBaseCryptLib.inf | 1 +
.../BaseCryptLibNull/BaseCryptLibNull.inf | 1 +
.../Cipher/CryptAeadAesGcmNull.c | 100 +++++++
.../BaseCryptLibOnProtocolPpi/CryptLib.c | 93 ++++++
CryptoPkg/Private/Protocol/Crypto.h | 86 ++++++
.../Library/BaseCryptLib/AeadAesGcmTests.c | 112 +++++++
.../BaseCryptLib/BaseCryptLibUnitTests.c | 1 +
.../Library/BaseCryptLib/TestBaseCryptLib.h | 3 +
.../BaseCryptLib/TestBaseCryptLibHost.inf | 1 +
.../BaseCryptLib/TestBaseCryptLibShell.inf | 1 +
20 files changed, 971 insertions(+), 1 deletion(-)
create mode 100644 CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcm.c
create mode 100644 CryptoPkg/Library/BaseCryptLib/Cipher/CryptAeadAesGcmNull.c
create mode 100644 CryptoPkg/Library/BaseCryptLibNull/Cipher/CryptAeadAesGcmNull.c
create mode 100644 CryptoPkg/Test/UnitTest/Library/BaseCryptLib/AeadAesGcmTests.c

--
2.26.2.windows.1


回复: [edk2-devel] [PATCH edk2-stable202208 1/1] BaseTools: Fix DevicePath GNUmakefile for macOS

gaoliming
 

-----邮件原件-----
发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 Michael D
Kinney
发送时间: 2022年8月26日 9:39
收件人: devel@edk2.groups.io; rebecca@...; Andrew Fish
<afish@...>; Leif Lindholm <quic_llindhol@...>; Feng, Bob C
<bob.c.feng@...>; Gao, Liming <gaoliming@...>; Chen,
Christine <yuwei.chen@...>; Kinney, Michael D
<michael.d.kinney@...>
主题: Re: [edk2-devel] [PATCH edk2-stable202208 1/1] BaseTools: Fix
DevicePath GNUmakefile for macOS

Reviewed-by: Michael D Kinney <michael.d.kinney@...>

I approve for edk2-stable202208

Mike

-----Original Message-----
From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
Rebecca Cran
Sent: Thursday, August 25, 2022 6:32 PM
To: devel@edk2.groups.io; Andrew Fish <afish@...>; Leif Lindholm
<quic_llindhol@...>; Kinney, Michael D
<michael.d.kinney@...>; Feng, Bob C <bob.c.feng@...>; Gao,
Liming <gaoliming@...>; Chen, Christine
<yuwei.chen@...>
Cc: Rebecca Cran <rebecca@...>
Subject: [edk2-devel] [PATCH edk2-stable202208 1/1] BaseTools: Fix
DevicePath GNUmakefile for macOS

On macOS, /usr/bin/gcc is clang, and so doesn't have
the -Wno-error=stringop-overflow flag that was added
for gcc 12.
Update the GNUmakefile for DevicePath to skip setting
that on macOS.

Signed-off-by: Rebecca Cran <rebecca@...>
---
BaseTools/Source/C/DevicePath/GNUmakefile | 2 ++
1 file changed, 2 insertions(+)

diff --git a/BaseTools/Source/C/DevicePath/GNUmakefile
b/BaseTools/Source/C/DevicePath/GNUmakefile
index c217674345b1..17f213879e82 100644
--- a/BaseTools/Source/C/DevicePath/GNUmakefile
+++ b/BaseTools/Source/C/DevicePath/GNUmakefile
@@ -16,10 +16,12 @@ include $(MAKEROOT)/Makefiles/app.makefile
GCCVERSION = $(shell gcc -dumpversion | awk -F'.' '{print $$1}')
ifneq ("$(GCCVERSION)", "5")
ifneq ($(CXX), llvm)
+ifneq ($(DARWIN),Darwin)
# gcc 12 trips over device path handling
BUILD_CFLAGS += -Wno-error=stringop-overflow
endif
endif
+endif

LIBS = -lCommon
ifeq ($(CYGWIN), CYGWIN)
--
2.30.1 (Apple Git-130)