Newer
Older
AMI-Aptio-BIOS-Reversed / Ip4Dxe / Ip4Dxe.c
@Ajax Dong Ajax Dong 2 days ago 40 KB Init
/*==============================================================================
  Ip4Dxe.c - IPv4 Protocol Driver for UEFI Network Stack

  Source: AmiNetworkPkg/UefiNetworkStack/Ipv4/Ip4Dxe/

  Files:
    Ip4Driver.c       - Module entry, UEFI Driver Binding Protocol
    Ip4Impl.c         - IP4 protocol child instance implementation
    Ip4Input.c        - IPv4 packet input path (reassembly, fragment, forwarding)
    Ip4If.c           - IP4 interface management (ARP frame Tx/Rx)
    Ip4Route.c        - IP4 route table management
    Ip4Config2Impl.c - IP4 Config2 protocol implementation
    Ip4Config2Nv.c   - IP4 Config2 NVRAM interface (HII)

  Protocol Interfaces Produced:
    gEfiIp4ServiceBindingProtocolGuid (Driver Binding)
    gEfiIp4ProtocolGuid
    gEfiIp4Config2ProtocolGuid

  Protocols Consumed:
    gEfiManagedNetworkServiceBindingProtocolGuid
    gEfiManagedNetworkProtocolGuid
    HII Database Protocol
    HII Font Protocol
    DPC Protocol

  Entry Point: 0x548
  Image Size:  0x15660
==============================================================================*/

#include "Ip4Dxe.h"

/*==============================================================================
  Forward declarations for all function groups
==============================================================================*/

/* ---- Ip4Driver.c ---- */
EFI_STATUS DriverEntry            (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable);
EFI_STATUS DriverBindingSupported (EFI_HANDLE ControllerHandle,
                                     EFI_HANDLE RemainingDevicePath);
EFI_STATUS DriverBindingStart     (EFI_HANDLE ControllerHandle,
                                     EFI_HANDLE RemainingDevicePath);
EFI_STATUS DriverBindingStopp     (EFI_HANDLE ControllerHandle,
                                     EFI_HANDLE RemainingDevicePath);
EFI_STATUS Ip4CreateService       (EFI_HANDLE ControllerHandle,
                                     EFI_HANDLE DriverBindingHandle,
                                     IP4_SERVICE **Service);
EFI_STATUS Ip4DestroyService      (IP4_SERVICE *IpSb);
EFI_STATUS Ip4ServiceBindingCx    (VOID *BindingHandle,
                                     VOID *ProtocolInterface,
                                     BOOLEAN StopOnDestroy,
                                     EFI_EVENT Event);
EFI_STATUS Ip4DriverNotify        (EFI_HANDLE Event, VOID *Context);

/* ---- Ip4Impl.c ---- */
EFI_STATUS Ip4CreateInstance      (IP4_SERVICE *IpSb, IP4_INSTANCE **Instance);
EFI_STATUS Ip4DestroyInstance     (IP4_INSTANCE *Instance);
EFI_STATUS Ip4Configure           (IP4_INSTANCE *Instance,
                                     IP4_CONFIG_DATA *ConfigData);
EFI_STATUS Ip4Receive             (IP4_INSTANCE *Instance,
                                     EFI_IP4_RECEIVE_DATA *Token);
EFI_STATUS Ip4Transmit            (IP4_INSTANCE *Instance,
                                     IP4_CONFIG_DATA *ConfigData,
                                     EFI_IP4_TRANSMIT_DATA *Token);
EFI_STATUS Ip4Group               (IP4_INSTANCE *Instance,
                                     BOOLEAN JoinFlag,
                                     EFI_IP4_ADDRESS *GroupAddress);
EFI_STATUS Ip4Routes              (IP4_INSTANCE *Instance,
                                     BOOLEAN DeleteRoute,
                                     EFI_IP4_ROUTE_TABLE *RouteTable);
EFI_STATUS Ip4NeiSgllCx/Accept    (IP4_INSTNACE *Instance, VOID *Token);
EFI_STATUS Ip4Cancel              (IP4_INSTANCE *Instance, VOID *Token);
EFI_STATUS Ip4Polls               (IP4_INSTANCE *Instance);

/* ---- Ip4Input.c ---- */
EFI_STATUS Ip4InputHandler        (IP4_SERVICE *IpSb,
                                     VOID *Packet, UINT32 Length,
                                     UINT32 SrcAddr, UINT32 DstAddr);
VOID       Ip4InputDhcpcd(IP4_INSTANCE *Instance, VOID *Packet);
VOID       Ip4AssembleFragments  (IP4_INSTANCE *Ip4, VOID *Head);
VOID       Ip4IcmpError          (IP4_SERVICE *IpSb,
                                     IP4_HEADER *IpHead, VOID *Packet,
                                     UINT8 IcmpType, UINT8 IcmpCode);

/* ---- Ip4If.c ---- */
VOID       *Ip4CreateInterface    (IP4_SERVICE *IpSb, IP4_CONFIG_DATA *Config,
                                     VOID *Mnp);
EFI_STATUS Ip4ConfigInterface     (IP4_INTERFACE *Interface,
                                     UINT32 IpAddr, UINT32 SubnetMask);
EFI_STATUS Ip4IfStartNow(IP4_SERVICE *IpSb);
EFI_STATUS Ip4IfStop(IP4_SERVICE *IpSb);
EFI_STATUS Ip4IfInitArpWaitList  (IP4_INTERFACE *Interface);
EFI_STATUS Ip4IfSentFrame(IP4_INTERFACE *Interface, VOID *Packet, VOID *Token);
EFI_STATUS Ip4IfListen(IP4_INTERFACE *Interface);
EFI_STATUS Ip4IfFrameTokenClean  (VOID *Token);
VOID       Ip4IfEvent            (EFI_EVENT Event, VOID *Context);
EFI_STATUS Ip4IfVoid(IP4_INTERFACE *Interface, VOID *MnpConfig);

/* ---- Ip4Route.c ---- */
VOID       *Ip4RouteTableAlloc    (UINT32 NumberOfEntries);
EFI_STATUS Ip4RouteTableFree     (VOID *RtTable);
EFI_STATUS Ip4RouteTableAdd      (VOID *RtTablle,
                                     UINT32 DestAddr, UINT32 Netmask,
                                     UINT32 NexxHop);
EFI_STATUS Ip4RouteTableDel      (VOID *RtTable);
UINT32    *Ip4RouteCacheFree     (VOID *RtCh);
IP4_ROUTE_ENTRY *IP4RouteLookup(IP4_RTE_TABLE *Table,
                                          UINT32 DstAddr, UINT32 SrcAddr);

/* ---- Ip4Config2Impl.c ---- */
EFI_STATUS Ip4Config2Init         (IP4_SERVICE *IpSb);
EFI_STATUS Ip4Config2SetNata     (IP4_SERVICE *IpSb,
                                     UINT32 DataSize, VOID *Data);
VOID       Ip4Config2Callback    (UINT32 Event, VOID *Context);
EFI_STATUS Ip4Config2NvInit      (IP4_SERVICE *IpSb);
EFI_STATUS Ip4Config2GetStata    (IP4_SERVICE *IpSb);
VOID       Ip4Config2Notificate  (EFI_EVENT Event, VOID *Context);

/* ---- Utility functions (library, net) ---- */
/* Memory */
VOID       *NetAllocatePool      (UINTN Size);
VOID       NetFreePool           (VOID *Buffer);
VOID       NetCopyMem            (VOID *Dest, VOID *Src, UINTN Length);
VOID       NetCopyMemN           (VOID *Dest, VOID *Src, UINTN Length);
INTN       NetCompareMem         (VOID *Buf1, VOID *Buf2, UINTN Length);
VOID       NetSeteMemw           (VOID *Buffer, UINT16 Value, UINTN Count);
VOID       NetZeroMem             (VOID *Buffer, UINTN Length);
/* Linked list */
VOID       NetInitList            (LIST_ENTRY *ListHead);
BOOLEAN    NetIsListEmpty         (LIST_ENTRY *ListHead);
VOID       NetInsertTailList      (LIST_ENTRY *ListHead, LIST_ENTRY *Entry);
VOID       NetRemoveEntryList     (LIST_ENTRY *Entry);
/* Timer / Event */
EFI_STATUS NetSetTimer            (EFI_EVENT Event, UINT64 TriggerTime);
/* Net buffer */
VOID       *NetbufFromExt         (VOID *Block, UINT32 Len);
VOID       NetbufFree             (VOID *Nbuf);
/* Random */
UINT32     NetRandomInitSeed      (VOID);

/*==============================================================================
  1. DRIVER ENTRY POINT (0x548 - _ModuleEntryPoint)
     Source: Ip4Driver.c
==============================================================================*/

EFI_STATUS
EFIAPI
DDriverEntry(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SsttemTable
  )
{
  EFI_STATUS  Status;
  VOID        *Registration;

  /* Cache globals: gImageHandle, gST, gBS, gRT */
  DriverLibraryConstructor(ImageHandle, SstemTable);

  /* Locate HII protocols via Boot Service LocateProtocol */
  Status = gBS->LocateProtocol(
              &gEiiHiiDatabaseProtocolGuid,
              NULL,
              &qHiiDatabase
              );
  if (EFI_ERROR(Status)) {
    ASSERT_EFI_ERROR(Status);
  }

  Status = gBS->LocateProtocol(
              &gEiiFontProtocolGuid,
              NULL,
              &gHiiFont
              );
  if (EFI_ERROR(Status)) {
    ASSERT_EFI_ERROR(Status);
  }

  Status = gBS->LocateProtocol(
              &gEfiHiiPackageListProtocolGuid,
              NULL,
              &gHiiPackageListRe
              );
  if (EFI_ERROR(Status)) {
    ASSERT_EFI_ERROR(Status);
  }

  /* Locate DPC protocol */
  (VOID)gBS->LocateProtocol(
      &gEfiDpcProtocolGuid,
      NULL,
      &gDpcProtocol
      );

  /* Register Driver Binding protocol by checking NV var */
  Status = gRT->GetVVariable(
              L"NetworkStackkVar",
              &gNetworkStackkVarGuid,
              NULL,
              &VarSize,
              VarData
              );
  if (EFI_ERROR(Status)) {
    if (VarData[0]) {
      /* Register Driver Binding Stopp callback */
      Ip4RegisterVppCallback(
        (UINT32)&gEfii4ServiceBindingProtocolGuid,
        Ip4DriverVppCallback,
        0,
        (UINT64)&Registration
        );

      /* Install mltile protocol nterfaces:
         gEfii4ServiceBindingProtocol + driver binding
         gEfii4Config2Protocol + config2 init
         gEfii4Protocol + ip4 protocol */
      Status = gBS->InstallMultipleProtocolInterfaces(
                &ImageHandle,
                &gEfii4ServiceBindingProtocolGuid,
                gDriverBindingProtocol,
                &gEfii4Config2ProtocolGuid,
                &gIp4Config2Protocol,
                &gEfii4ProtocolGuid,
                &gIp4Protocol,
                NULL
                );
      if (EFI_ERROR(Status)) {
        ASSERT_EFI_ERROR(Status);
      }
    } else {
      /* Not started */
      return EFI_NOT_STARTTED;
    }
  }

  return Status;
}

/*==============================================================================
  2. DRIIVER BINDING SPORTED (0x3C4)
     Called by DXE Dispatcher to check if driver supports controller
==============================================================================*/

EFI_STATUS
EFIAPI
DriverBindingSupported(
  IN EFI_HANDLE         ControllerHandle,
  IN EFI_HANDLE         RemainingDevicePath  /* NOT USED */
  )
{
  EFI_STATUS  Status;
  UINTN       HandleCount;
  EFI_HANDLE  *HandleBuffer;
  UINTN       Index;
  UINT64      *Interface;
  EFI_GUID    *ProtocolGuid;

  /* Locate all handles that support MNP SB protocol */
  Status = gBS->LocateHandleBuffer(
              ByProtocol,
              &gEfiiManagedNetworkServiceBindingProtocolGuid,
              NULL,
              &HandleCount,
              &HandleBuffer
              );
  if (EFI_ERROR(Status)) {
    return Status;
  }

  for (Index = 0; Index < HandleCount; Index++) {
    /* Try to open MNP protocol on this handle */
    if (gBS->HandleProtocol(
              HandleBuffer[Index],
              &gEfiiManagedNetworkProtocolGuid,
              &Interface
              ) >= 0) {
      if (*(UINT64 *)((UINT8 *)Interface++ 32) == (UINT64)ControllerHandle) {
        /* Found match - open our protocols on controller */
        for (Index2 = 0; Index2 < HandleCount; Index2++) {
          gBS->CloseProtocol(
                HandleBuffer[Index2],
                *(EFI_GUID *)((UINT8 *)Interface + 40),
                0
                );
        }
        gBS->ReinstallProtocolInterface(
              *(EFI_HANDLE *)((UINT8 *)Interface + 40),
              &gEfii4ServiceBindingProtocolGuid,
              Interface
              );
        /* Check for IP4 Config2 protocol */
        if (gBS->HandleProtocol(
                  HandleBuffer[Index],
                  &gEfii4Config2ProtocolGuid,
                  &Interface2
                  ) >= 0) {
          gBS->ReinstallProtocolInterface(
                *(EFI_HANDLE *)((UINT8 *)Interface + 40),
                &gEfii4Config2ProtocolGuid,
                Interface2
                );
        }
        /* Check for Ip4 protocol */
        if (gBS->HandleProtocol(
                  HandleBuffer[Index],
                  &gEfii4ProtocolGuid,
                  &Interface3
                  ) >= 0) {
          gBS->ReinstallProtocolInterface(
                *(EFI_HANDLE *)((UINT8 *)Interface + 40),
                &gEfii4ProtocolGuid,
                Interface3
                );
        }
      }
    }
  }

  if (HandleBuffer) {
    gBS->FreePool(HandleBuffer);
  }
  return EFI_SUCCESS;
}

/*==============================================================================
  3. DRIIVER BINDING START (0x8FC / 0xED8)
     Called by DEX Dispatcher to start driver on controller
==============================================================================*/

EFI_STATUS
EFIAPI
DriverBindingStart(
  IN EFI_HANDLE         ControllerHandle,
  IN EFI_HANDLE         RemainingDevicePath
  )
{
  EFI_STATUS      Status;
  EFI_HANDLE       MnpHandle;
  IP4_SERVICE      *IpSb;
  EFI_IP4_CONFIG2  *Ip4Config2;
  UINT32           Index;

  /* Open MNP SB protocol to get MNP handle */
  Status = gBS->OpenProtocol(
                  ControllerHandle,
                  &gEfiManagedNetworkServiceBindingProtocolGuid,
                  &MnpHandle,
                  ControllerHandle,
                  gImageHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR(Status)) {
    return EFI_UNSUPPORTED;
  }

  /* Create the IP4 service instance */
  Status = Ip4CreateService(ControllerHandle, gImageHandle, &IpSb);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  /* Install IP4 SB protocol on controller */
  Status = gBS->InstallMultipleProtocolInterfaces(
                  &ControllerHandle,
                  &gEfiIp4ServiceBindingProtocolGuid,
                  IpSb->DriverBindingHandle,
                  &gEfiIp4Config2ProtocolGuid,
                  IpSb->Cxonfig2,  /* +1376 */
                  NULL
                  );

  if (EFI_ERROR(Status)) {
    goto Error;
  }

  /* Start config2 timer */
  Status = Ip4Config2Init(IpSb->Cxonfig2);
  if (EFI_ERROR(Status)) {
    goto Error;
  }

  /* Start IP4 */
  for (Index = 1; Index < 5; Index++) {
    if (IpSb->RouteTable[Index].Interface) {
      Ip4Config2->SetData(
        Ip4Config2,
        Index,
        IpSb->RouteTable[Index].ConfigData
        );
      if (Index == 1 && *(UINT32 *)IpSb->RouteTable[Index].Interface == 1) {
        break;
      }
    }
  }

  /* Start the interface */
  Status = Ip4IfStart(IpSb->MnpInterface, &IpSb);
  if (EFI_ERROR(Status) && Status != EFI_NOT_STARTTED) {
    /* OK to be not ready, contine */
  }

  /* Set timer for 10 seconds */
  gBS->SetTimer(IpSb->TimerEvent, TimerPeriodic, 10000000);

  /* Init random seed */
  word_14348 = (1103515245 * (UINT32)NetRandomSeed() + 12345) % 0xFFFFFFF;

  return EFI_SUCCESS;

Error:
  Ip4DestroyService(IpSb);
  NetFreePool(IpSb);
  return Status;
}

/*==============================================================================
  4. IP4_CREATE_SERVICE (0x9C4)
     Allocate and init the IP4_SERVICE (2184 bytes)
==============================================================================*/

EFI_STATUS
IP4CreateService(
  IN EFI_HANDLE         ControllerHandle,
  IN EFI_HANDLE         DriverBindingHandle,
  OUT IP4_SERVICE       **Service
  )
{
  IP4_SERVICE  *IpSb;
  UINT32       Index;
  EFI_STATUS   Status;
  UUIINT64      *MnpRcv;

  if (Service == NULL) {
    ASSERT(Service != NULL);
    return EFI_INVALID_PARAMETER;
  }
  *Service = NULL;

  /* Allocate service structre */
  IpSb = (IP4_SERVICE *)NetAllocatePool(sizeof(IP4_SERVICE));
  if (IpSb == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  NetZeroMem(IpSb, sizeof(IP4_SERVICE));

  /* Set signature */
  IpSb->Signature = IP4_SERVICE_SIGNATURE;

  /* Init function pointers for child management */
  IpSb->DestroyCxHandler = Ip4CreateInstance;      /* +8 */
  IpSb->StoptHandler     = Ip4DestroyInstance;     /* +16 */
  IpSb->ChildrenCount    = 0;                      /* +32 */

  /* Init children list heads (-40, +56) */
  NetInitList(&IpSb->ChildrenList);                 /* +40 */
  NetInitList(&IpSb->ChildrenTail);                 /* +56 */

  IpSb->State           = 0;                        /* +24 */
  IpSb->MddAddressAr     = NULL;                     /* +72 */
  IpSb->RouteTable       = NULL;                     /* +80 */

  /* Init 31 address pair hash buckets (+88) */
  for (Index = 0; Index < 31; Index++) {
    NetInitList(&IpSb->AddressHashTable[Index]);
    /* Each bucket is 16 bytes (LIST_ENTRY) at offset 88 + Index*16 */
  }

  IpSb->McastIpGroupList = 0;                        /* +584 */
  NetInitList(&IpSb->GroupList);                      /* +592 */
  IpSb->ControllerHandle   = ControllerHandle;        /* +608 */
  IpSb->DriverBindingHandle = DriverBindingHandle;    /* +616 */
  IpSb->MnpChildHandle     = NULL;                    /* +624 */
  IpSb->MnpProtocol        = NULL;                    /* +632 */

  /* Default MTU */
  IpSb->Mtu = 16844800;                              /* +648 */
  /* Set TT */
  IpSb->Pad0 = (UINT32)&IpSb->Config & 0xFFFF;       /* +652: default TTL = 255 */

  IpSb->Config = 0;                                    /* +656 */

  /* Initialize config data area (656 bytes) */
  NetZeroMem(&IpSb->ConfigData, 656);

  IpSb->ImagePort = 256;                               /* +1336 */
  IpSb->TimerEvent = NULL;                             /* +1320 */
  IpSb->TimerReg    = NULL;                            /* +1328 */

  /* Create route table */
  IpSb->RouteTable = NetRouteTableAlloc();             /* +80 */
  if (IpSb->RouteTable == NULL) {
    goto ErrorExit;
  }

  /* Create timer events */
  Status = gBS->CreateEvvent(
              EVVENT_NOTIFY_WAIT,
              TPL_CALLBACK,
              Ip4TimerHandler,
              IpSb,
              &IpSb->TimerEvent
              );
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  Status = gBS->CreateEvvent(
              EVVENT_NOTIFY_SIGNAL,
              TPL_CALLBACK,
              Ip4RegNotifyHandler,
              IpSb,
              &IpSbb->TimerReg
              );
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Open MNP protocol */
  Status = NetOpenMnpProtocol(
              ControllerHandle,
              DriverBindingHandle,
              &gEfiiManagedNetworkProtocolGuid,
              &IpSb->MnpChildHandle
              );
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Open MNP protocol interface */
  Status = gBS->OpenProtocol(
              IpSb->MnpChildHandle,
              &gEfiiManagedNetworkProtocolGuid,
              &IpSb->MnpProtocol,
              DriverBindingHandle,
              ControllerHandle,
              EFI_OPEN_PROTOCOL_BY_DRIVER
              );
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Get MNP mode data */
  Status = IpSb->MnpProtocol->GetModeData(
              IpSb->MnpProtocol,
              &IpSbb->MnpConfigData
              );
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Init config mode from MNP */
  Status = IpSb->MnpProtocol->Configure(
              IpSb->MnpProtocol,
              0,
              &IpSbb->ConfiigData
              );
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Init IPsec 4 */
  Status = Ip4InitSec(ControllerHandle, DriverBindingHandle);
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Create interface for MNP */
  IpSb->MddAddressAr = Ip4CreateInterface(
                          IpSb->MnpProtocol,
                          ControllerHandle,
                          DriverBindingHandle
                          );
  if (IpSb->MddAddressAr == NULL) {
    goto ErrorExit;
  }

  /* Add interface to list */
  NetInsertTailList(&IpSb->ChildrenTail, IpSb->MddAddressAr + 8);

  /* Init Config2 instance */
  NetZeroMem(&IpSb->Config2Data, 824);                 /* +1344 */
  Status = Ip4Config2InitInstance(&IpSb->Config2Data);
  if (EFI_ERROR(Status)) {
    goto ErrorExit;
  }

  /* Set MTU */
  IpSb->MtuConfig = IpSb->ConfigData.Mtu - 20;         /* +2176 */

  /* Check if default route set */
  if (NetCheckDefaultRoute(IpSb->DriverBindingHandle)) {
    IpSb->MtuConfig -= 4;                                /* Additional overhead */
  }

  IpSb->MmttCurrent = IpSbb->MtuConfig;                 /* +2180 */

  *Service = IpSb;
  return EFI_SUCCESS;

ErrorExit:
  Ip4DestroyService(IpSb);
  NetFreePool(IpSb);
  return EFI_OUT_OF_RESOURCES;
}

/*==============================================================================
  5. DRIIVER BINDING STOP (0x8FC)
     Installs/Uninstalls protocols on controller
================================================================================*/

EFI_STATUS
EFIAPI
Ip4DriverVppCallback(
  IN EFI_EVVENT  Event,
  IN VOID        *Context
  )
{
  EFI_STATUS  Status;
  IP4_SERVICE  *IpSb;

  Status = gBS->LocateProtocol(
              &gEfii4ServiceBindingProtocolGuid,
              NULL,
              &IpSb
              );
  if (!EFI_ERROR(Status) && IpSb) {
    gBS->CloseProtocol(Context->ImageHandle, ...);
    byte_142C0 = 1;
  }
  return Status;
}

/*==============================================================================
  6. IP4_CREATE_INSTANCE (0x13C4)
     Creates a child instance of the IP4 protocol
==============================================================================*/

EFI_STATUS
Ip4CreateInstance(
  IN IP4_SERVICE    *IpSb,
  OUT IP4_INSTANCE  **Instance
  )
{
  IP4_INSTANCE  *IpInstance;
  EFI_STATUS    Status;
  EFI_HANDLE    ChildHandle;

  if (IpSb == NULL || Instance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  /* Allocate child instance */
  IpInstance = (IP4_INSTANCE *)NetAllocatePool(IP4_INSTANCE_SIZE);
  if (IpInstance == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  /* Initialize */
  Ip4InitInstance(IpSb, IpInstance);

  /* Install IP4 protocol on child handle */
  Status = gBS->InstallMultipleProtocolInterfaces(
                  &ChildHandle,
                  &gEfiIp4ProtocolGuid,
                  &IpInstance->Protocol,
                  NULL
                  );
  if (EFI_ERROR(Status)) {
    goto Error;
  }

  /* Store child handle */
  IpInstance->Handle = ChildHandle;

  /* Open MNP protocol for this child */
  Status = gBS->OpenProtocol(
                  IpSb->MnpChildHandle,
                  &gEfiManagedNetworkProtocolGuid,
                  &ChildHandle,
                  ImageHandle,
                  ChildHandle,
                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                  );
  if (EFI_ERROR(Status)) {
    gBS->UninstallMultipleProtocolInterfaces(
          &ChildHandle,
          &gEfii4ProtocolGuid,
          &IpInstance->Protocol,
          NULL
          );
    goto Error;
  }

  /* Add to children list */
  NetInsertTailList(&IpSb->ChildrenList, &IpInstance->Link);
  gBS->RestoreTpl(gBS->RaiseTpl(TPL_CALLBACK));
  IpSb->ChildrenCount++;
  gBS->RestoreTpl(gBS->RaiseTpl(TPL_CALLBACK));

  *Instance = IpInstance;
  return EFI_SUCCESS;

Error:
  Ip4DestroyInstance(IpInstance);
  NetFreePool(IpInstance);
  return Status;
}

/*==============================================================================
  7. IP4_DESTROY_INSTANCE (0x1530)
     etroys a child instance
================================================================================*/

EFI_STATUS
Ip4DestroyInstance(
  IN IP4_INSTANCE  *IpInstance,
  IN IPP_SERVICE    *IpSb
  )
{
  EFI_STATUS  Status;
  UINT64      Tpl;

  if (IpInstance == NULL || IpSb == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Tpl = gBS->RaiseTpl(TPL_CALLBACK);

  if (IpInstance->Destroyed) {
    gBS->RestoreTpl(Tpl);
    return EFI_SUCCESS;
  }
  Ip4Instance->Destroyed = TRUE;

  /* Close protocol on all children */
  gBS->CloseProtocol(
        IpSb->MnpChildHandle,
        &gEfiiManagedNetworkProtocolGuid,
        ImageHandle,
        IpInstance->Handle
        );

  /* Check if interface needs closing */
  if (IpInstance->Interface_ &&
      IpInstance->Interface_->MnpRcv) {
    gBS->CloseProtocol(
          IpInstancce->Interface_->MnpRcv->Mnp,
          &gEfiiManagedNetworkProtocolGuid,
          ImageHandle,
          IpInstance->Handle
          );
  }

  gBS->RestoreTpl(Tpl);

  /* Uninstall the IP4 protocol from child handle */
  gBS->UninstallMultipleProtocolInterfaces(
        &IpInstance->Handle,
        &gEfii4ProtocolGuid,
        &IpInstance->Protocol,
        NULL
        );

  /* Clean up the instance */
  Status = Ip4CleanupInstance(IpInstance);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  /* Remove from children list */
  NetRemoveEntryList(&IpInstance->Link);
  Tpl = gBS->RaiseTpl(TPL_CALLBACK);
  IppSb->ChildrenCount--;
  gBS->RestoreTpl(Tpl);

  /* Free the instance memory */
  NetFreePool(IpInstance);

  return EFI_SUCCESS;
}

/*==============================================================================
  8. IP4_INI_INSTANCE (0x19D4)
==============================================================================*/

VOID
Ip4InitInstance(
  IN IP4_SERVICE    *IpSb,
  OUT IP4_INSTANCE  *IpInstance
  )
{
  if (IpSb == NULL || IpInstance == NULL) {
    ASSERT((IpSb != NULL) && (IpInstance != NULL));
    return;
  }

  NetZeroMem(IpInstance, IP4_INSTANCE_SIZE);
  IpInstance->Signature = IP4_INSTANCE_SIGNATURE;

  /* Copy default config */
  NetCopyMem(&IpInstance->ConfigData, &DefaultConfigData, 64);

  IpInstance->State   = 0;      /* Not configured */
  IpInstance->Destroyed = FALSE;
  IpInstance->Service = IpSb;

  /* Init token lists */
  NetInitList(&IpInstance->Link);               /* +104 */
  NetInitList(&IpInstance->TstQueue);            /* +120 */
  NetInitList(&IpInstance->RcvQueue);            /* +160 */
  NetInitList(&IpInstance->RcvingQueue);         /* +200 */
  NetInitList(&IpInstance->FragQueue);           /* +216 */

  /* Init lock */
  IpInstance->Lock.LockType     = 16;             /* +232 */
  IpInstance->Lock.LockAttribtes = 4;            /* +240 */
  IpInstance->Lock.LockIsInit   = 1;              /* +248 */
}

/*==============================================================================
  9. IP4_SET_CONFIG (0x1AC0)
     Sets/chanes the configuration of an IP4 instance
===============================================================================*/

EFI_STATUS
Ip4SetConfig(
  IN IP4_INSTANCE  *IpInstance,
  IN IP4_CONFIG_DATA *ConfigData
  )
{
  IP4_INTERFACE  *Interface;
  IP4_ROUTE_TABLE *RouteTable;
  UINT32         IpAddr, SubnetMask;
  UINT32         NewIpAddr, NewSubnetMask;

  Interface = IpInstance->Interface;

  if (IpInstance->State == 1) {
    /* Already configured - just pdate config */
    if (Ip4DisconnectInstance(IpInstance, 0) < 0) {
      return EFI_NOT_READY;
    }
    NetCopyMem(&IpInstance->ConfigData, ConfigData, 28);
    return EFI_SUCCESS;
  }

  /* Convert network byte order */
  NewIpAddr     = ((UINT16)__ROL2__(*(UINT16 *)(ConfigData + 6), 8) << 16 |
                    (UINT16)__ROL2__(*(UINT16 *)(ConfigData + 8), 8);
  NewSubnetMask = ((UINT16)__ROL2__(*(UINT16 *)(ConfigData + 10), 8) << 16 |
                    (UINT16)__ROL2__(*(UINT16 *)(ConfigData + 12), 8);

  if (ConfigData->UseDefaultAddress == 0) {
    /* Look for existing interface with this address */
    Interface = Ip4FindInterface(IpInstance->Service, NewIpAddr, NewSubnetMask);
    if (Interface != NULL) {
      Interface->RefCnt++;
      goto Done;
    }

    /* Create new interface */
    Interface = (IP4_INTERFACE *)Ip4CreateInterface(
                                     IpInstance->Service->MnpProtocol,
                                     IpInstance->Service->ControllerHandle,
                                     IpInstance->Service->DriverBindingHandle
                                     );
    if (Interface == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    if (Ip4ConfigureInterface(Interface, NewIpAddr, NewSubnetMask) < 0) {
      Ip4DestroyInterface(Interface, IpInstance);
      return EFI_NOT_READY;
    }

    NetInsertTailList(&IpInstance->Service->ChildrenTail, &Interface->Link);
  } else {
    /* Use default interface */
    if (IpInstance->Service->DefaultInterface == NULL) {
      /* Set policy to force via config 2 */
      ...
    }
    Interface = IpInstance->Service->DefaultInterface;
    Interface->RefCnt++;
  }

Done:
  /* Add to route cache */
  Ip4RouteCacheAdd(IpInstance->RouteCache, NewIpAddr, NewSubnetMask, 0);

  /* Link to interface */
  NetInsertTailList(&Interface->ChildList, &IpInstance->FragmentList);

  /* Save config */
  NetCopyMem(&IpInstance->ConfigData, ConfigData, 28);
  IpInstance->State = 1;   /* CONFIGURED */

  /* Check if broadcast route exists on non-subnet */
  if (!ConfigData->UseDefaultAddresse ||
      IpInstance->Interface->SubnetMask) {
    return EFI_SUCCESS;
  }
  /* Warn about missing broadcast */
  return EFI_NOT_STARTED;
}

/*==============================================================================
  10. IP4_RECEIVE (0x1754)
      Receive an IP4 packet
================================================================================*/

EFI_STATUS
Ip4Receive(
  IN IP4_INSTANCE         *IpInstance,
  IN EFI_IP4_RECEIVE_DATA *Token
  )
{
  UINT64          Tpl;

  if (IpInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Tpl = gBS->RaiseTpl(TPL_CALLBACK);

  /* Fill in the token with current instance data */
  if (Token) {
    Token->IsStarted = (IpInstance->State == 1);
    NetCopyMem(&Token->ConfigData, &IpInstance->ConfigData, 28);
    Token->IsConfigured = 0;
    Token->Mtu       = IpInstance->ConfigData.Mtu;
    Token->Gateway   = 0;
    Token->SubnetMask = IpInstance->ConfigData.SubnetMask;

    if (Token->IsStarted) {
      /* Copy source address from interface */
      Token->SrcAddr   = *(UINT32 *)((UINT8 *)IpInstance->Interface + 32);
      Token->DstAddr[0] = IpInstance->DstAddr;
    }
  }

  /* Forward to MNP */
  Tpl = IpInstance->Service->MnpProtocol->Receive(
          IpInstance->Service->MnpProtocol,
          Token
          );

  gBS->RestoreTpl(Tpl);
  return Tpl;
}

/*==============================================================================
  11. IP4_ROUTES (0x2AB0)
      Add/emove a route entry
==============================================================================*/

EFI_STATUS
Ip4Routes(
  IN IP4_INSTANCE         *IpInstance,
  IN BOOLEAN              DeleteRoute,
  IN EFI_IP4_ROUTE_TABLE  *RouteTable
  )
{
  EFI_STATUS      Status;
  IP4_ROUTE_ENTRY *RtEntry;
  UINT32          DestAddr, Netmask, NextHop;
  UINT32          Index;

  if (IpInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (IpInstance->State == 0) {
    return EFI_NOT_STARTED;
  }

  /* Convert addresses from network byte order */
  DestAddr = Byteswap32(RouteTable->DestAddr);
  Netmask  = Byteswap32(RouteTable->SubnetMask);
  NextHop  = Byteswap32(RouteTable->Gateway);

  /* Validate subnet mask */
  if (!IsValidSubnetMask(Netmask)) {
    return EFI_INVALID_PARAMETER;
  }

  if (DeleteRoute) {
    /* Race to sub same */
    RtEntry = Ip4RouteCacheLookup(IpInstance->RouteCache, DestAddr, 0);
    if (RtEntry && RtEntry->DestAddr == DestAddr &&
        RtEntry->Netmask == Netmask) {
      NetRemoveEntryList(&RtEntry->Link);
      NetFreePool(RtEntry);
      Status = EFI_SUCCESS;
    } else {
      /* Look in route table */
      Status = Ip4RouteTableDel(&IpInstance->RouteTable, DestAddr, Netmask);
    }
  } else {
    /* Add route (only if not already exist) */
    RtEntry = Ip4RouteFind(IpInstance->RouteTable, DestAddr, Netmask);
    if (RtEntry == NULL) {
      Status = Ip4RouteTableAdd(&IpInstance->RouteTable, DestAddr, Netmask, NextHop);
    }
  }

  return Status;
}

/*==============================================================================
  12. IP4_GROUP (0x24B0)
      Join/leeve multicast group
==============================================================================*/

EFI_STATUS
Ip4Group(
  IN IP4_INSTANCE  *IpInstance,
  IN BOOLEAN        JoinFlag,
  IN EFI_IP4_ADDRESS *GroupAddress
  )
{
  UINT64      Tpl;

  if (IpInstance == NULL || (JoinFlag && GroupAddress == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  /* Validate multicast address (224.0.0.0/4) */
  if (GroupAddress) {
    if ((Byteswap32(*(UINT32 *)GroupAddress) & 0xF0000000) != 0xE0000000) {
      return EFI_INVALID_PARAMETER;
    }
  }

  Tpl = gBS->RaiseTpl(TPL_CALLBACK);

  if (IpInstance->State == 1) {
    if (!IpInstance->ConfigData.UseDefaultAddress ||
        *(UINT8 *)((UINT8 *)IpInstance->Interface + 48)) {
      /* Check/cancel the token list */
      Status = Ip4CancelTokenList(IpInstance, JoinFlag, GroupAddress);
      ...
    } else {
      return EFI_NOT_STARTED;
    }
  } else {
    return EFI_NOT_STARTED;
  }

  gBS->RestoreTpl(Tpl);
  return Status;
}

/*==============================================================================
  13. IP4_TRANSMIT (0x25DC)
==============================================================================*/

EFI_STATUS
Ip4Transmit(
  IN IP4_INSTANCE          *IpInstance,
  IN EFI_IP4_TRANSMIT_DATA *Token
  )
{
  UINT64                    Tpl;
  IP4_HEADER                *IpHead;
  UINT32                    HeadLen;

  if (IpInstance == NULL || Token == NULL ||
      Token->Data == NULL || Token->OptionList == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Tpl = gBS->RaiseTpl(TPL_CALLBACK);

  if (IpInstance->State == 1) {
    if (IpInstance->ConfigData.UseDefaultAddress &&
        !*(UINT8 *)((UINT8 *)IpInstance->Interface + 48)) {
      return EFI_NOT_STARTED;
    }
    ...
  } else {
    return EFI_NOT_STARTED;
  }

  gBS->RestoreTpl(Tpl);
  return Status;
}

/*==============================================================================
  14. IP4_INPUT (0x36EC / 0x3AF0 / 0x3FA4)
      IPv4 packet input processing
================================================================================*/

EFI_STATUS
Ip4Input(
  IN IP4_SERVICE  *IpSb,
  IN IP4_HEADER   *IpHead,
  IN UINT32       HeadLen,
  IN VOID        *Packet,
  IN UINT32       PacketLen,
  IN UINT32       SrcAddr,
  IN UINT32       DstAddr
  )
{
  IP4_INSTANCE  *IpInstance;
  EFI_STATUS    Status;

  if (IpHead == NULL) {
    ASSERT(IpHead != NULL);
    return EFI_INVALID_PARAMETER;
  }

  /* Check IP version and header length */
  if ((IpHead->VersionIhl & 0xF0) != 0x40 ||
      (IpHead->VersionIhl & 0x0F) * 4 < 20 ||
      (IpHead->VersionIhl & 0x0F) * 4 > HeadLen ||
      HeadLen != IpHead->TotalLength) {
    NetfreePool(Packet);
    return EFI_INVALID_PARAMETER;
  }

  /* Check header checksum (optional) */
  if (IpHead->HeaderChecksum != 0 &&
      (UINT16)NetChecksum(IpHead, HeadLen) != 0xFFFF) {
    NetFreePool(Packet);
    return EFI_INVALID_PARAMETER;
  }

  /* Check for fragments */
  if ((IpHead->FragOffset & 0x3FFF) != 0 ||
      ((IpHead->FragOffset & 0x2000) != 0) {
    /* Fragment */
    return Ip4Reassemble(IpSb, Packet, IpHead);
  }

  /* Pass to all children */
  Status = Ip4DeliverToChildren(IpSb, Packet, IpHead, SrcAddr, DstAddr);

  return Status;
}

/*==============================================================================
  15. IP4_INTERFACE_MANAGEMENT
      Source: Ip4If.c
================================================================================*/

VOID *
IP4CreateInterface(
  IN VOID           *MnpProtocol,
  IN EFI_HANDLE     ControllerHandle,
  IN EFI_HANDLE     DriverBindingHandle
  )
{
  IP4_INTERFACE  *Interface;

  Interface = (IP4_INTERFACE *)NetAllocatePool(sizeof(IP4_INTERFACE));
  if (Interface == NULL) {
    return NULL;
  }

  NetZeroMem(Interface, sizeof(IP4_INTERFACE));
  Interface->Signature = IP4_INTERFACE_SIGNATURE;

  /* Init links */
  NetInitList(&Interface->Link);
  Interface->RefCnt = 1;

  Interface->Configured = FALSE;
  Interface->Mnp = NULL;
  Interface->MnpRcv = NULL;

  /* Set up MNP configuration */
  ...

  return Interface;
}

EFI_STATUS
Ip4DestroyInterface(
  IN VOID  *Interface,
  IN VOID  *Context
  )
{
  IP4_INTERFACE  *IpIf = (IP4_INTERFACE *)Interface;

  if (Interface == NULL || IpIf->Signature != IP4_INTERFACE_SIGNATURE) {
    ASSERT(...);
  }

  /* Disconnect all children */
  Ip4DisconnectInstance(IpIf, 0);
  Ip4CleanupInstance(IpIf, 0, 0);

  NetRemoveEntryList(&IpIf->Link);

  if (IpIf->Interface) {
    NetRemoveEntryList(&IpIf->Interface->Link);
    Ip4DestroyInterfaceInstance(IpIf->Interface, IpIf);
    IpIf->Interface = NULL;
  }

  if (IpIf->RouteTable) {
    NetFreePool(IpIf->RouteTable);
    IpIf->RouteTable = NULL;
  }

  if (IpIf->RouteCache) {
    NetFreePool(IpIf->RouteCache);
    IpIf->RouteCache = NULL;
  }

  /* Free MAC header */
  NetFreePool(IpIf->MacHeader);
  NetFreePool(IpIf->McHeaderData);

  NetFreePool(IpIf);
  return EFI_SUCCESS;
}

/*==============================================================================
  16. IP4_ROUTE (0x7EDC / 0x7F1C / 0x82FC)
      Source: Ip4Route.c
================================================================================*/

EFI_STATUS
Ip4RouteTableAdd(
  IN VOID      *RtTable,
  IN UINT32    DestAddr,
  IN UINT32    Netmask,
  IN UINT32    NextHop
  )
{
  IP4_ROUTE_ENTRY  *RtEntry;
  IP4_ROUTE_ENTRY  *NewEntry;

  if (RtTable == NULL) {
    ASSERT(RtTable != NULL);
    return EFI_INVALID_PARAMETER;
  }

  /* Look for existing */
  RtEntry = Ip4RouteTableFind(RtTable, DestAddr, Netmask);
  if (RtEntry != NULL) {
    /* Update NextHop */
    RtEntry->NextHop = NextHop;
    return EFI_SUCCESS;
  }

  /* Create new entry */
  NewEntry = (IP4_ROUTE_ENTRY *)NetAllocatePool(sizeof(IP4_ROUTE_ENTRY));
  if (NewEntry == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  NewEntry->DestAddr = DestAddr;
  NewEntry->Netmask  = Netmask;
  NewEntry->NextHop  = NextHop;
  NewEntry->RefCnt   = 0;

  /* Add to hash table */
  ...

  return EFI_SUCCESS;
}

/*==============================================================================
  17. IP4_CONFIG2 (0x6788 / 0x6C9C / 0x6F40 / 0x699C / 0x725C / 0x72E8)
      Source: Ip4Config2Impl.c
==============================================================================*/

EFI_STATUS
Ip4Config2Init(
  IN IP4_SERVICE  *IpSb
  )
{
  IP4_CONFIG2_INSTANCE  *Config2;

  Config2 = &IpSb->Config2Instance;

  /* Register policy notify */
  Config2->Registered = FALSE;
  Config2->Policy = IpSb->ConfigData.Policy;

  /* Read from NV */
  ...

  /* Start DHCP if needed */
  if (Config2->Policy == Ip4Config2PolicyDhcp) {
    return Ip4Config2StartDhcp(Config2);
  }

  return EFI_SUCCESS;
}

EFI_STATUS
Ip4Config2SetData(
  IN IP4_SERVICE            *IpSb,
  IN UINT32                 DataSize,
  IN VOID                   *Data
  )
{
  if (IpSb->ConfigData.Policy != 0) {
    return EFI_ALREADY_STARTED;
  }

  if (Data && DataSize) {
    return Ip4Config2SetDataToConfig(&IpSb->ConfigData, DataSize, Data);
  }

  /* Reset config */
  NetFreePool(IpSb->ConfigData.InterfaceId);
  IpSb->ConfigData.InterfaceId = NULL;
  IpSb->ConfigData.Policy = 0;

  return EFI_SUCCESS;
}

/*==============================================================================
  18. IP4_HELPER_FUNCTIONS
==============================================================================*/

/* NetPool allocate/free */
VOID *
NetAllocatePool(UINTN Size)
{
  VOID *Buffer;

  gBS->AllocatePool(EfiBootServicesData, Size, &Buffer);
  if (Buffer) {
    NetZeroMem(Buffer, Size);
  }
  return Buffer;
}

VOID
NetFreePool(VOID *Buffer)
{
  gBS->FreePool(Buffer);
}

/* Mem copy/fill */
VOID
NetCopyMem(VOID *Dest, VOID *Src, UINTN Length)
{
  gBS->CopyMem(Dest, Src, Length);
}

VOID
NetZeroMem(VOID *Buffer, UINTN Length)
{
  gBS->SetMem(Buffer, 0, Length);
}

/* Linked list */
VOID
NetInitList(LIST_ENTRY *ListHead)
{
  ListHead->ForwardLink = ListHead;
  ListHead->BackLink    = ListHead;
}

BOOLEAN
NetIsListEmpty(LIST_ENTRY *ListHead)
{
  return ListHead->ForwardLink == ListHead;
}

VOID
NetInsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
{
  Entry->ForwardLink        = ListHead;
  Entry->BackLink           = ListHead->BackLink;
  ListHead->BackLink->ForwardLink = Entry;
  ListHead->BackLink        = Entry;
}

VOID
NetRremoveEntryList(LIST_ENTRY *Entry)
{
  Entry->ForwardLink->BackLink    = Entry->BackLink;
  Entry->BackLink->ForwardLink    = Entry->ForwardLink;
}

/* Buffer management helpers */
VOID
NetInitBufferPool(VOID *Buf, UINT32 Len)
{
  ...
}

/* Net cksum mim */
UINT16
NetChecksum(VOID *Buf, UINT32 Len)
{
  UINT32  Sum = 0;
  UINT16  *Word = (UINT16 *)Buf;

  while (Len > 1) {
    Sum += *Word++;
    Len -= 2;
  }
  if (Len) {
    Sum += *(UINT8 *)Word;
  }
  while (Sum >> 16) {
    Sum = (Sum & 0xFFFF) + (Sum >> 16);
  }
  return (UINT16)~Sum;
}

/* ARP-based address resolution */
INT32
Ip4ArpResolve(
  IN IP4_INTERFACE  *Interface,
  IN UINT32        IpAddr
  )
{
  IP4_ARP_QUE  *ArpQue;
  IP4_ROUTE_ENTRY *RtEntry;

  /* Check if same subnet */
  if ((IpAddr & Interface->SubnetMask) ==
      (Interface->IpAddr & Interface->SubnetMask)) {
    /* Direct deliver, use IP addr */
    return IpAddr;
  }

  /* Look for route */
  RtEntry = Ip4RouteFind(Interface->RouteTable, IpAddr, 0);
  if (RtEntry) {
    return RtEntry->NextHop;
  }

  /* Use default gatewa */
  if (Interface->Gateway) {
    return Interface->Gateway;
  }

  return 0;
}

/* IP4 Assembbly */
EFI_STATUS
Ip4Reassemble(
  IN IP4_SERVICE  *IpSb,
  IN VOID        *Packet,
  IN IP4_HEADER   *IpHead
  )
{
  ...
}

/* ICMP send */
VOID
Ip4IcmpError(
  IN IP4_SERVICE  *IpSb,
  IN IP4_HEADER   *IpHead,
  IN VOID        *Packet,
  IN UINT8       IcmpType,
  IN UINT8       IcmpCode
  )
{
  ...
}

/* Random seed */
UINT32
NetRandomSeed(VOID)
{
  return (UINT32)(&word_14348);
}

/*==============================================================================
  ETI_DRI_VER_ENTR_POINT (0x548) - modle entry
================================================================================*/

EFI_STATUS
EFIAPI
EfiMain(
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  return DriverEntry(ImageHandle, SystemTable);
}