summaryrefslogtreecommitdiff
path: root/Core/MdeModulePkg/Universal/Network
diff options
context:
space:
mode:
Diffstat (limited to 'Core/MdeModulePkg/Universal/Network')
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c817
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h340
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf68
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.uni24
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c1673
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h776
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c745
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c225
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c437
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c738
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h152
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf72
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.uni24
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c1788
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h199
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c1686
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h195
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c896
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h234
-rw-r--r--Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.c347
-rw-r--r--Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.h86
-rw-r--r--Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.inf52
-rw-r--r--Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.uni22
-rw-r--r--Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.c283
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.h165
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4Dxe.uni25
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.c430
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.h106
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCommon.h22
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.c1264
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.h166
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxe.vfr219
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxeStrings.uni62
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigNVDataStruc.h109
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c472
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.h55
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.c676
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.h140
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDxe.inf125
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.c412
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.h22
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c539
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.h38
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiImpl.h169
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.c116
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.h74
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.c945
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.h317
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.c2830
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.h1002
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.c487
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.h142
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.c350
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.h80
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c434
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c329
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h223
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2.vfr100
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c2086
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h300
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.c1439
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.h51
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c1038
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h190
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf115
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.uni25
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni35
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c366
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h103
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c1254
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h343
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c621
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h207
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c2297
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h405
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c1609
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h252
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4NvData.h51
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c210
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h72
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c487
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h126
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c661
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h224
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c348
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.h151
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c1946
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c690
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h275
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf74
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.uni24
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h905
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c1140
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c796
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.c739
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.h212
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c431
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c723
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h137
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf75
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.uni23
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c1103
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h216
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c534
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h111
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c795
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c634
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h203
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c531
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Callback.c360
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c436
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Get_status.c263
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Initialize.c283
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Mcast_ip_to_mac.c179
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Nvdata.c223
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Receive.c257
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Receive_filters.c484
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Reset.c136
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Shutdown.c152
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.c868
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.h1039
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf83
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.uni23
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Start.c168
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Station_address.c249
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Statistics.c230
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Stop.c126
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Transmit.c355
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c92
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c433
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c1236
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h131
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c1031
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h986
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c717
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c782
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h342
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf84
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.uni23
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h781
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c1478
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c112
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c674
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h494
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c939
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c380
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h145
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c1212
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h351
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c584
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c435
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c590
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h154
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf70
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.uni23
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c1914
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h695
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c908
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/ComponentName.c365
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.c1996
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.h502
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.c665
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.h102
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.c2971
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.h188
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.c454
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.h137
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.c201
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.h120
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxe.uni25
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf93
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/ComponentName.c171
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfig.vfr79
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDriver.c306
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.inf72
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.uni23
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni20
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c671
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.h388
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigNvData.h47
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigStrings.uni38
192 files changed, 85471 insertions, 0 deletions
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c
new file mode 100644
index 0000000000..91edcccc0d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c
@@ -0,0 +1,817 @@
+/** @file
+ ARP driver functions.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "ArpDriver.h"
+#include "ArpImpl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding = {
+ ArpDriverBindingSupported,
+ ArpDriverBindingStart,
+ ArpDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+
+/**
+ Create and initialize the arp service context data.
+
+ @param[in] ImageHandle The image handle representing the loaded driver
+ image.
+ @param[in] ControllerHandle The controller handle the driver binds to.
+ @param[in, out] ArpService Pointer to the buffer containing the arp service
+ context data.
+
+ @retval EFI_SUCCESS The arp service context is initialized.
+
+ @retval EFI_UNSUPPORTED The underlayer Snp mode type is not ethernet.
+ Failed to initialize the service context.
+ @retval other Failed to initialize the arp service context.
+
+**/
+EFI_STATUS
+ArpCreateService (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle,
+ IN OUT ARP_SERVICE_DATA *ArpService
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (ArpService != NULL);
+
+ ArpService->Signature = ARP_SERVICE_DATA_SIGNATURE;
+
+ //
+ // Init the lists.
+ //
+ InitializeListHead (&ArpService->ChildrenList);
+ InitializeListHead (&ArpService->PendingRequestTable);
+ InitializeListHead (&ArpService->DeniedCacheTable);
+ InitializeListHead (&ArpService->ResolvedCacheTable);
+
+ //
+ // Init the servicebinding protocol members.
+ //
+ ArpService->ServiceBinding.CreateChild = ArpServiceBindingCreateChild;
+ ArpService->ServiceBinding.DestroyChild = ArpServiceBindingDestroyChild;
+
+ //
+ // Save the handles.
+ //
+ ArpService->ImageHandle = ImageHandle;
+ ArpService->ControllerHandle = ControllerHandle;
+
+ //
+ // Create a MNP child instance.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &ArpService->MnpChildHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open the MNP protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **)&ArpService->Mnp,
+ ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Get the underlayer Snp mode data.
+ //
+ Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode);
+ if ((Status != EFI_NOT_STARTED) && EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ if (ArpService->SnpMode.IfType != NET_IFTYPE_ETHERNET) {
+ //
+ // Only support the ethernet.
+ //
+ Status = EFI_UNSUPPORTED;
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Set the Mnp config parameters.
+ //
+ ArpService->MnpConfigData.ReceivedQueueTimeoutValue = 0;
+ ArpService->MnpConfigData.TransmitQueueTimeoutValue = 0;
+ ArpService->MnpConfigData.ProtocolTypeFilter = ARP_ETHER_PROTO_TYPE;
+ ArpService->MnpConfigData.EnableUnicastReceive = TRUE;
+ ArpService->MnpConfigData.EnableMulticastReceive = FALSE;
+ ArpService->MnpConfigData.EnableBroadcastReceive = TRUE;
+ ArpService->MnpConfigData.EnablePromiscuousReceive = FALSE;
+ ArpService->MnpConfigData.FlushQueuesOnReset = TRUE;
+ ArpService->MnpConfigData.EnableReceiveTimestamps = FALSE;
+ ArpService->MnpConfigData.DisableBackgroundPolling = FALSE;
+
+ //
+ // Configure the Mnp child.
+ //
+ Status = ArpService->Mnp->Configure (ArpService->Mnp, &ArpService->MnpConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Create the event used in the RxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ ArpOnFrameRcvd,
+ ArpService,
+ &ArpService->RxToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Create the Arp heartbeat timer.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ ArpTimerHandler,
+ ArpService,
+ &ArpService->PeriodicTimer
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Start the heartbeat timer.
+ //
+ Status = gBS->SetTimer (
+ ArpService->PeriodicTimer,
+ TimerPeriodic,
+ ARP_PERIODIC_TIMER_INTERVAL
+ );
+
+ERROR_EXIT:
+
+ return Status;
+}
+
+
+/**
+ Clean the arp service context data.
+
+ @param[in, out] ArpService Pointer to the buffer containing the arp service
+ context data.
+
+ @return None.
+
+**/
+VOID
+ArpCleanService (
+ IN OUT ARP_SERVICE_DATA *ArpService
+ )
+{
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ if (ArpService->PeriodicTimer != NULL) {
+ //
+ // Cancle and close the PeriodicTimer.
+ //
+ gBS->SetTimer (ArpService->PeriodicTimer, TimerCancel, 0);
+ gBS->CloseEvent (ArpService->PeriodicTimer);
+ }
+
+ if (ArpService->RxToken.Event != NULL) {
+ //
+ // Cancle the RxToken and close the event in the RxToken.
+ //
+ ArpService->Mnp->Cancel (ArpService->Mnp, NULL);
+ gBS->CloseEvent (ArpService->RxToken.Event);
+ }
+
+ if (ArpService->Mnp != NULL) {
+ //
+ // Reset the Mnp child and close the Mnp protocol.
+ //
+ ArpService->Mnp->Configure (ArpService->Mnp, NULL);
+ gBS->CloseProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ ArpService->ImageHandle,
+ ArpService->ControllerHandle
+ );
+ }
+
+ if (ArpService->MnpChildHandle != NULL) {
+ //
+ // Destroy the mnp child.
+ //
+ NetLibDestroyServiceChild(
+ ArpService->ControllerHandle,
+ ArpService->ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ ArpService->MnpChildHandle
+ );
+ }
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NET_LIST_USER_STRUCT_S (Entry, ARP_INSTANCE_DATA, List, ARP_INSTANCE_DATA_SIGNATURE);
+ ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+}
+
+/**
+ Tests to see if this driver supports a given controller.
+
+ If a child device is provided, it further tests to see if this driver supports
+ creating a handle for the specified child device.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers,
+ and is optional for bus drivers.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver
+ specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by
+ a different driver or an application that
+ requires exclusive acces. Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the
+ driver specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test to see if Arp SB is already installed.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test to see if MNP SB is installed.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been
+ moved into this common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior
+ will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally
+ aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified
+ by This must have been called with the same calling parameters, and Supported()
+ must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers,
+ and is optional for bus drivers.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.
+ Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of
+ resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+
+ //
+ // Allocate a zero pool for ArpService.
+ //
+ ArpService = AllocateZeroPool (sizeof(ARP_SERVICE_DATA));
+ if (ArpService == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the arp service context data.
+ //
+ Status = ArpCreateService (This->DriverBindingHandle, ControllerHandle, ArpService);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ //
+ // Install the ARP service binding protocol.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ &ArpService->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ //
+ // OK, start to receive arp packets from Mnp.
+ //
+ Status = ArpService->Mnp->Receive (ArpService->Mnp, &ArpService->RxToken);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ return Status;
+
+ERROR:
+
+ //
+ // On error, clean the arp service context data, and free the memory allocated.
+ //
+ ArpCleanService (ArpService);
+ FreePool (ArpService);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ Release the control of this controller and remove the IScsi functions. The Stop()
+ function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior
+ will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ Not used.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.Not used.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ ARP_SERVICE_DATA *ArpService;
+ LIST_ENTRY *List;
+
+ //
+ // Get the NicHandle which the arp servicebinding is installed on.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to get the arp servicebinding protocol on the NicHandle.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ (VOID **)&ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "ArpDriverBindingStop: Open ArpSb failed, %r.\n", Status));
+ return EFI_DEVICE_ERROR;
+ }
+
+ ArpService = ARP_SERVICE_DATA_FROM_THIS (ServiceBinding);
+
+ if (NumberOfChildren != 0) {
+ //
+ // NumberOfChildren is not zero, destroy all the ARP children instances.
+ //
+ List = &ArpService->ChildrenList;
+ Status = NetDestroyLinkList (
+ List,
+ ArpDestroyChildEntryInHandleBuffer,
+ ServiceBinding,
+ NULL
+ );
+ ASSERT (IsListEmpty (&ArpService->PendingRequestTable));
+ ASSERT (IsListEmpty (&ArpService->DeniedCacheTable));
+ ASSERT (IsListEmpty (&ArpService->ResolvedCacheTable));
+ } else if (IsListEmpty (&ArpService->ChildrenList)) {
+ //
+ // Uninstall the ARP ServiceBinding protocol.
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ &ArpService->ServiceBinding,
+ NULL
+ );
+
+ //
+ // Clean the arp servicebinding context data and free the memory allocated.
+ //
+ ArpCleanService (ArpService);
+
+ FreePool (ArpService);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned
+ in ChildHandle. If ChildHandle is not a pointer to NULL, then the protocol
+ installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing
+ UEFI handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+ArpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_INSTANCE_DATA *Instance;
+ VOID *Mnp;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ArpService = ARP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate memory for the instance context data.
+ //
+ Instance = AllocateZeroPool (sizeof(ARP_INSTANCE_DATA));
+ if (Instance == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpSBCreateChild: Failed to allocate memory for Instance.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Init the instance context data.
+ //
+ ArpInitInstance (ArpService, Instance);
+
+ //
+ // Install the ARP protocol onto the ChildHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ (VOID *)&Instance->ArpProto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "ArpSBCreateChild: faild to install ARP protocol, %r.\n", Status));
+
+ FreePool (Instance);
+ return Status;
+ }
+
+ //
+ // Save the ChildHandle.
+ //
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Managed Network protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp,
+ gArpDriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Insert the instance into children list managed by the arp service context data.
+ //
+ InsertTailList (&ArpService->ChildrenList, &Instance->List);
+ ArpService->ChildrenNumber++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ERROR:
+
+ if (EFI_ERROR (Status)) {
+
+ gBS->CloseProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gArpDriverBinding.DriverBindingHandle,
+ Instance->Handle
+ );
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiArpProtocolGuid,
+ &Instance->ArpProto,
+ NULL
+ );
+
+ //
+ // Free the allocated memory.
+ //
+ FreePool (Instance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is
+ being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+ArpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ArpService = ARP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Get the arp protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **)&Arp,
+ ArpService->ImageHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (Arp);
+
+ if (Instance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Use the InDestroy as a flag to avoid re-entrance.
+ //
+ Instance->InDestroy = TRUE;
+
+ //
+ // Close the Managed Network protocol.
+ //
+ gBS->CloseProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gArpDriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the ARP protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ &Instance->ArpProto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "ArpSBDestroyChild: Failed to uninstall the arp protocol, %r.\n",
+ Status));
+
+ Instance->InDestroy = FALSE;
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Instance->Configured) {
+ //
+ // Delete the related cache entry.
+ //
+ ArpDeleteCacheEntry (Instance, FALSE, NULL, TRUE);
+
+ //
+ // Reset the instance configuration.
+ //
+ ArpConfigureInstance (Instance, NULL);
+ }
+
+ //
+ // Remove this instance from the ChildrenList.
+ //
+ RemoveEntryList (&Instance->List);
+ ArpService->ChildrenNumber--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+
+ return Status;
+}
+
+/**
+ The entry point for Arp driver which installs the driver binding and component name
+ protocol on its ImageHandle.
+
+ @param[in] ImageHandle The image handle of the driver.
+ @param[in] SystemTable The system table.
+
+ @retval EFI_SUCCESS if the driver binding and component name protocols
+ are successfully
+ @retval Others Failed to install the protocols.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gArpDriverBinding,
+ ImageHandle,
+ &gArpComponentName,
+ &gArpComponentName2
+ );
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h
new file mode 100644
index 0000000000..d5fd3ea5c2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h
@@ -0,0 +1,340 @@
+/** @file
+ ARP driver header file.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ARP_DRIVER_H_
+#define _ARP_DRIVER_H_
+
+
+#include <Uefi.h>
+
+#include <Protocol/Arp.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+
+//
+// Global variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gArpComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gArpComponentName2;
+
+//
+// Function prototypes for the Drivr Binding Protocol
+//
+/**
+ Tests to see if this driver supports a given controller.
+
+ If a child device is provided, it further tests to see if this driver supports
+ creating a handle for the specified child device.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers,
+ and is optional for bus drivers.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver
+ specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by
+ a different driver or an application that
+ requires exclusive acces. Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the
+ driver specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been
+ moved into this common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior
+ will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally
+ aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified
+ by This must have been called with the same calling parameters, and Supported()
+ must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers,
+ and is optional for bus drivers.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.
+ Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of
+ resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ Release the control of this controller and remove the IScsi functions. The Stop()
+ function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior
+ will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ Not used.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.Not used.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned
+ in ChildHandle. If ChildHandle is not a pointer to NULL, then the protocol
+ installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing
+ UEFI handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+ArpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is
+ being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+ArpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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
+ArpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+ArpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
new file mode 100644
index 0000000000..9dff604a94
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
@@ -0,0 +1,68 @@
+## @file
+# This module produces EFI ARP Protocol and EFI ARP Service Binding Protocol.
+#
+# This module produces EFI ARP Protocol upon EFI MNP Protocol, to provide a generic
+# implementation of the Address Resolution Protocol that is described in RFCs 826
+# and 1122.
+#
+# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = ArpDxe
+ MODULE_UNI_FILE = ArpDxe.uni
+ FILE_GUID = 529D3F93-E8E9-4e73-B1E1-BDF6A9D50113
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = ArpDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gArpDriverBinding
+# COMPONENT_NAME = gArpComponentName
+# COMPONENT_NAME2 = gArpComponentName2
+#
+
+[Sources]
+ ArpMain.c
+ ArpDriver.h
+ ComponentName.c
+ ArpImpl.h
+ ArpImpl.c
+ ArpDriver.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ DpcLib
+
+[Protocols]
+ gEfiArpServiceBindingProtocolGuid ## BY_START
+ gEfiManagedNetworkServiceBindingProtocolGuid ## TO_START
+ gEfiArpProtocolGuid ## BY_START
+ gEfiManagedNetworkProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ ArpDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.uni b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.uni
new file mode 100644
index 0000000000..4f02ae151d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.uni
@@ -0,0 +1,24 @@
+// /** @file
+// This module produces EFI ARP Protocol and EFI ARP Service Binding Protocol.
+//
+// This module produces EFI ARP Protocol upon EFI MNP Protocol, to provide a generic
+// implementation of the Address Resolution Protocol that is described in RFCs 826
+// and 1122.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "EFI Address Resolution Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI ARP Protocol using the EFI MNP Protocol to provide a generic implementation of the Address Resolution Protocol that is described in RFCs 826 and 1122."
+
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni
new file mode 100644
index 0000000000..9af9fb1ac3
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// ArpDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"ARP DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c
new file mode 100644
index 0000000000..a02de203c0
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c
@@ -0,0 +1,1673 @@
+/** @file
+ The implementation of the ARP protocol.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "ArpImpl.h"
+
+//
+// Global variable of EFI ARP Protocol Interface.
+//
+EFI_ARP_PROTOCOL mEfiArpProtocolTemplate = {
+ ArpConfigure,
+ ArpAdd,
+ ArpFind,
+ ArpDelete,
+ ArpFlush,
+ ArpRequest,
+ ArpCancel
+};
+
+
+/**
+ Initialize the instance context data.
+
+ @param[in] ArpService Pointer to the arp service context data this
+ instance belongs to.
+ @param[out] Instance Pointer to the instance context data.
+
+ @return None.
+
+**/
+VOID
+ArpInitInstance (
+ IN ARP_SERVICE_DATA *ArpService,
+ OUT ARP_INSTANCE_DATA *Instance
+ )
+{
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ Instance->Signature = ARP_INSTANCE_DATA_SIGNATURE;
+ Instance->ArpService = ArpService;
+
+ CopyMem (&Instance->ArpProto, &mEfiArpProtocolTemplate, sizeof (Instance->ArpProto));
+
+ Instance->Configured = FALSE;
+ Instance->InDestroy = FALSE;
+
+ InitializeListHead (&Instance->List);
+}
+
+
+/**
+ Process the Arp packets received from Mnp, the procedure conforms to RFC826.
+
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameRcvdDpc (
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData;
+ ARP_HEAD *Head;
+ ARP_ADDRESS ArpAddress;
+ ARP_CACHE_ENTRY *CacheEntry;
+ LIST_ENTRY *Entry;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_ARP_CONFIG_DATA *ConfigData;
+ NET_ARP_ADDRESS SenderAddress[2];
+ BOOLEAN ProtoMatched;
+ BOOLEAN IsTarget;
+ BOOLEAN MergeFlag;
+
+ ArpService = (ARP_SERVICE_DATA *)Context;
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ RxToken = &ArpService->RxToken;
+
+ if (RxToken->Status == EFI_ABORTED) {
+ //
+ // The Token is aborted, possibly by arp itself, just return and the receiving
+ // process is stopped.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (RxToken->Status)) {
+ //
+ // Restart the receiving if any other error Status occurs.
+ //
+ goto RESTART_RECEIVE;
+ }
+
+ //
+ // Status is EFI_SUCCESS, process the received frame.
+ //
+ RxData = RxToken->Packet.RxData;
+ //
+ // Sanity check.
+ //
+ if (RxData->DataLength < sizeof (ARP_HEAD)) {
+ //
+ // Restart the receiving if packet size is not correct.
+ //
+ goto RESTART_RECEIVE;
+ }
+
+ //
+ // Convert the byte order of the multi-byte fields.
+ //
+ Head = (ARP_HEAD *) RxData->PacketData;
+ Head->HwType = NTOHS (Head->HwType);
+ Head->ProtoType = NTOHS (Head->ProtoType);
+ Head->OpCode = NTOHS (Head->OpCode);
+
+ if (RxData->DataLength < (sizeof (ARP_HEAD) + 2 * Head->HwAddrLen + 2 * Head->ProtoAddrLen)) {
+ goto RESTART_RECEIVE;
+ }
+
+ if ((Head->HwType != ArpService->SnpMode.IfType) ||
+ (Head->HwAddrLen != ArpService->SnpMode.HwAddressSize) ||
+ (RxData->ProtocolType != ARP_ETHER_PROTO_TYPE)) {
+ //
+ // The hardware type or the hardware address length doesn't match.
+ // There is a sanity check for the protocol type too.
+ //
+ goto RECYCLE_RXDATA;
+ }
+
+ //
+ // Set the pointers to the addresses contained in the arp packet.
+ //
+ ArpAddress.SenderHwAddr = (UINT8 *)(Head + 1);
+ ArpAddress.SenderProtoAddr = ArpAddress.SenderHwAddr + Head->HwAddrLen;
+ ArpAddress.TargetHwAddr = ArpAddress.SenderProtoAddr + Head->ProtoAddrLen;
+ ArpAddress.TargetProtoAddr = ArpAddress.TargetHwAddr + Head->HwAddrLen;
+
+ SenderAddress[Hardware].Type = Head->HwType;
+ SenderAddress[Hardware].Length = Head->HwAddrLen;
+ SenderAddress[Hardware].AddressPtr = ArpAddress.SenderHwAddr;
+
+ SenderAddress[Protocol].Type = Head->ProtoType;
+ SenderAddress[Protocol].Length = Head->ProtoAddrLen;
+ SenderAddress[Protocol].AddressPtr = ArpAddress.SenderProtoAddr;
+
+ //
+ // First, check the denied cache table.
+ //
+ CacheEntry = ArpFindDeniedCacheEntry (
+ ArpService,
+ &SenderAddress[Protocol],
+ &SenderAddress[Hardware]
+ );
+ if (CacheEntry != NULL) {
+ //
+ // This address (either hardware or protocol address, or both) is configured to
+ // be a deny entry, silently skip the normal process.
+ //
+ goto RECYCLE_RXDATA;
+ }
+
+ ProtoMatched = FALSE;
+ IsTarget = FALSE;
+ Instance = NULL;
+ NET_LIST_FOR_EACH (Entry, &ArpService->ChildrenList) {
+ //
+ // Iterate all the children.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, ARP_INSTANCE_DATA, List);
+ NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);
+ ConfigData = &Instance->ConfigData;
+
+ if ((Instance->Configured) &&
+ (Head->ProtoType == ConfigData->SwAddressType) &&
+ (Head->ProtoAddrLen == ConfigData->SwAddressLength)) {
+ //
+ // The protocol type is matched for the received arp packet.
+ //
+ ProtoMatched = TRUE;
+ if (0 == CompareMem (
+ (VOID *)ArpAddress.TargetProtoAddr,
+ ConfigData->StationAddress,
+ ConfigData->SwAddressLength
+ )) {
+ //
+ // The arp driver has the target address required by the received arp packet.
+ //
+ IsTarget = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!ProtoMatched) {
+ //
+ // Protocol type unmatchable, skip.
+ //
+ goto RECYCLE_RXDATA;
+ }
+
+ //
+ // Check whether the sender's address information is already in the cache.
+ //
+ MergeFlag = FALSE;
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ NULL,
+ ByProtoAddress,
+ &SenderAddress[Protocol],
+ NULL
+ );
+ if (CacheEntry != NULL) {
+ //
+ // Update the entry with the new information.
+ //
+ ArpFillAddressInCacheEntry (CacheEntry, &SenderAddress[Hardware], NULL);
+ CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;
+ MergeFlag = TRUE;
+ }
+
+ if (!IsTarget) {
+ //
+ // This arp packet isn't targeted to us, skip now.
+ //
+ goto RECYCLE_RXDATA;
+ }
+
+ if (!MergeFlag) {
+ //
+ // Add the triplet <protocol type, sender protocol address, sender hardware address>
+ // to the translation table.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->PendingRequestTable,
+ NULL,
+ ByProtoAddress,
+ &SenderAddress[Protocol],
+ NULL
+ );
+ if (CacheEntry == NULL) {
+ //
+ // Allocate a new CacheEntry.
+ //
+ CacheEntry = ArpAllocCacheEntry (NULL);
+ if (CacheEntry == NULL) {
+ goto RECYCLE_RXDATA;
+ }
+ }
+
+ if (!IsListEmpty (&CacheEntry->List)) {
+ RemoveEntryList (&CacheEntry->List);
+ }
+
+ //
+ // Fill the addresses into the CacheEntry.
+ //
+ ArpFillAddressInCacheEntry (
+ CacheEntry,
+ &SenderAddress[Hardware],
+ &SenderAddress[Protocol]
+ );
+
+ //
+ // Inform the user.
+ //
+ ArpAddressResolved (CacheEntry, NULL, NULL);
+
+ //
+ // Add this entry into the ResolvedCacheTable
+ //
+ InsertHeadList (&ArpService->ResolvedCacheTable, &CacheEntry->List);
+ }
+
+ if (Head->OpCode == ARP_OPCODE_REQUEST) {
+ //
+ // Send back the ARP Reply. If we reach here, Instance is not NULL and CacheEntry
+ // is not NULL.
+ //
+ ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REPLY);
+ }
+
+RECYCLE_RXDATA:
+
+ //
+ // Signal Mnp to recycle the RxData.
+ //
+ gBS->SignalEvent (RxData->RecycleEvent);
+
+RESTART_RECEIVE:
+
+ //
+ // Continue to receive packets from Mnp.
+ //
+ Status = ArpService->Mnp->Receive (ArpService->Mnp, RxToken);
+
+ DEBUG_CODE (
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "ArpOnFrameRcvd: ArpService->Mnp->Receive "
+ "failed, %r\n.", Status));
+ }
+ );
+}
+
+/**
+ Queue ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameRcvd (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, ArpOnFrameRcvdDpc, Context);
+}
+
+/**
+ Process the already sent arp packets.
+
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameSentDpc (
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+
+ ASSERT (Context != NULL);
+
+ TxToken = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Context;
+ TxData = TxToken->Packet.TxData;
+
+ DEBUG_CODE (
+ if (EFI_ERROR (TxToken->Status)) {
+ DEBUG ((EFI_D_ERROR, "ArpOnFrameSent: TxToken->Status, %r.\n", TxToken->Status));
+ }
+ );
+
+ //
+ // Free the allocated memory and close the event.
+ //
+ FreePool (TxData->FragmentTable[0].FragmentBuffer);
+ FreePool (TxData);
+ gBS->CloseEvent (TxToken->Event);
+ FreePool (TxToken);
+}
+
+/**
+ Request ArpOnFrameSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request ArpOnFrameSentDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, ArpOnFrameSentDpc, Context);
+}
+
+
+/**
+ Process the arp cache olding and drive the retrying arp requests.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpTimerHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ ARP_SERVICE_DATA *ArpService;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ LIST_ENTRY *ContextEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ USER_REQUEST_CONTEXT *RequestContext;
+
+ ASSERT (Context != NULL);
+ ArpService = (ARP_SERVICE_DATA *)Context;
+
+ //
+ // Iterate all the pending requests to see whether a retry is needed to send out
+ // or the request finally fails because the retry time reaches the limitation.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if (CacheEntry->NextRetryTime <= ARP_PERIODIC_TIMER_INTERVAL) {
+ //
+ // Timeout, if we can retry more, send out the request again, otherwise abort
+ // this request.
+ //
+ if (CacheEntry->RetryCount == 0) {
+ //
+ // Abort this request.
+ //
+ ArpAddressResolved (CacheEntry, NULL, NULL);
+ ASSERT (IsListEmpty (&CacheEntry->UserRequestList));
+
+ RemoveEntryList (&CacheEntry->List);
+ FreePool (CacheEntry);
+ } else {
+ //
+ // resend the ARP request.
+ //
+ ASSERT (!IsListEmpty(&CacheEntry->UserRequestList));
+
+ ContextEntry = CacheEntry->UserRequestList.ForwardLink;
+ RequestContext = NET_LIST_USER_STRUCT (ContextEntry, USER_REQUEST_CONTEXT, List);
+
+ ArpSendFrame (RequestContext->Instance, CacheEntry, ARP_OPCODE_REQUEST);
+
+ CacheEntry->RetryCount--;
+ CacheEntry->NextRetryTime = RequestContext->Instance->ConfigData.RetryTimeOut;
+ }
+ } else {
+ //
+ // Update the NextRetryTime.
+ //
+ CacheEntry->NextRetryTime -= ARP_PERIODIC_TIMER_INTERVAL;
+ }
+ }
+
+ //
+ // Check the timeouts for the DeniedCacheTable.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->DeniedCacheTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+ ASSERT (IsListEmpty (&CacheEntry->UserRequestList));
+
+ if (CacheEntry->DefaultDecayTime == 0) {
+ //
+ // It's a static entry, skip it.
+ //
+ continue;
+ }
+
+ if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) {
+ //
+ // Time out, remove it.
+ //
+ RemoveEntryList (&CacheEntry->List);
+ FreePool (CacheEntry);
+ } else {
+ //
+ // Update the DecayTime.
+ //
+ CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL;
+ }
+ }
+
+ //
+ // Check the timeouts for the ResolvedCacheTable.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->ResolvedCacheTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+ ASSERT (IsListEmpty (&CacheEntry->UserRequestList));
+
+ if (CacheEntry->DefaultDecayTime == 0) {
+ //
+ // It's a static entry, skip it.
+ //
+ continue;
+ }
+
+ if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) {
+ //
+ // Time out, remove it.
+ //
+ RemoveEntryList (&CacheEntry->List);
+ FreePool (CacheEntry);
+ } else {
+ //
+ // Update the DecayTime.
+ //
+ CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL;
+ }
+ }
+}
+
+
+/**
+ Match the two NET_ARP_ADDRESSes.
+
+ @param[in] AddressOne Pointer to the first address to match.
+ @param[in] AddressTwo Pointer to the second address to match.
+
+ @return The two addresses match or not.
+
+**/
+BOOLEAN
+ArpMatchAddress (
+ IN NET_ARP_ADDRESS *AddressOne,
+ IN NET_ARP_ADDRESS *AddressTwo
+ )
+{
+ ASSERT (AddressOne != NULL && AddressTwo != NULL);
+
+ if ((AddressOne->Type != AddressTwo->Type) ||
+ (AddressOne->Length != AddressTwo->Length)) {
+ //
+ // Either Type or Length doesn't match.
+ //
+ return FALSE;
+ }
+
+ if ((AddressOne->AddressPtr != NULL) &&
+ (CompareMem (
+ AddressOne->AddressPtr,
+ AddressTwo->AddressPtr,
+ AddressOne->Length
+ ) != 0)) {
+ //
+ // The address is not the same.
+ //
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Find the CacheEntry which matches the requirements in the specified CacheTable.
+
+ @param[in] CacheTable Pointer to the arp cache table.
+ @param[in] StartEntry Pointer to the start entry this search begins with
+ in the cache table.
+ @param[in] FindOpType The search type.
+ @param[in] ProtocolAddress Pointer to the protocol address to match.
+ @param[in] HardwareAddress Pointer to the hardware address to match.
+
+ @return Pointer to the matched arp cache entry, if NULL, no match is found.
+
+**/
+ARP_CACHE_ENTRY *
+ArpFindNextCacheEntryInTable (
+ IN LIST_ENTRY *CacheTable,
+ IN LIST_ENTRY *StartEntry,
+ IN FIND_OPTYPE FindOpType,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ ARP_CACHE_ENTRY *CacheEntry;
+
+ if (StartEntry == NULL) {
+ //
+ // Start from the beginning of the table if no StartEntry is specified.
+ //
+ StartEntry = CacheTable;
+ }
+
+ for (Entry = StartEntry->ForwardLink; Entry != CacheTable; Entry = Entry->ForwardLink) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if ((FindOpType & MATCH_SW_ADDRESS) != 0) {
+ //
+ // Find by the software address.
+ //
+ if (!ArpMatchAddress (ProtocolAddress, &CacheEntry->Addresses[Protocol])) {
+ //
+ // The ProtocolAddress doesn't match, continue to the next cache entry.
+ //
+ continue;
+ }
+ }
+
+ if ((FindOpType & MATCH_HW_ADDRESS) != 0) {
+ //
+ // Find by the hardware address.
+ //
+ if (!ArpMatchAddress (HardwareAddress, &CacheEntry->Addresses[Hardware])) {
+ //
+ // The HardwareAddress doesn't match, continue to the next cache entry.
+ //
+ continue;
+ }
+ }
+
+ //
+ // The CacheEntry meets the requirements now, return this entry.
+ //
+ return CacheEntry;
+ }
+
+ //
+ // No matching.
+ //
+ return NULL;
+}
+
+
+/**
+ Find the CacheEntry, using ProtocolAddress or HardwareAddress or both, as the keyword,
+ in the DeniedCacheTable.
+
+ @param[in] ArpService Pointer to the arp service context data.
+ @param[in] ProtocolAddress Pointer to the protocol address.
+ @param[in] HardwareAddress Pointer to the hardware address.
+
+ @return Pointer to the matched cache entry, if NULL no match is found.
+
+**/
+ARP_CACHE_ENTRY *
+ArpFindDeniedCacheEntry (
+ IN ARP_SERVICE_DATA *ArpService,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ )
+{
+ ARP_CACHE_ENTRY *CacheEntry;
+
+ ASSERT ((ProtocolAddress != NULL) || (HardwareAddress != NULL));
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ CacheEntry = NULL;
+
+ if ((ProtocolAddress != NULL) && (ProtocolAddress->AddressPtr != NULL)) {
+ //
+ // Find the cache entry in the DeniedCacheTable by the protocol address.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ NULL,
+ ByProtoAddress,
+ ProtocolAddress,
+ NULL
+ );
+ if (CacheEntry != NULL) {
+ //
+ // There is a match.
+ //
+ return CacheEntry;
+ }
+ }
+
+ if ((HardwareAddress != NULL) && (HardwareAddress->AddressPtr != NULL)) {
+ //
+ // Find the cache entry in the DeniedCacheTable by the hardware address.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ NULL,
+ ByHwAddress,
+ NULL,
+ HardwareAddress
+ );
+ }
+
+ return CacheEntry;
+}
+
+
+/**
+ Allocate a cache entry and initialize it.
+
+ @param[in] Instance Pointer to the instance context data.
+
+ @return Pointer to the new created cache entry.
+
+**/
+ARP_CACHE_ENTRY *
+ArpAllocCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance
+ )
+{
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_ARP_ADDRESS *Address;
+ UINT16 Index;
+
+ //
+ // Allocate memory for the cache entry.
+ //
+ CacheEntry = AllocatePool (sizeof (ARP_CACHE_ENTRY));
+ if (CacheEntry == NULL) {
+ return NULL;
+ }
+
+ //
+ // Init the lists.
+ //
+ InitializeListHead (&CacheEntry->List);
+ InitializeListHead (&CacheEntry->UserRequestList);
+
+ for (Index = 0; Index < 2; Index++) {
+ //
+ // Init the address pointers to point to the concrete buffer.
+ //
+ Address = &CacheEntry->Addresses[Index];
+ Address->AddressPtr = Address->Buffer.ProtoAddress;
+ }
+
+ //
+ // Zero the hardware address first.
+ //
+ ZeroMem (CacheEntry->Addresses[Hardware].AddressPtr, ARP_MAX_HARDWARE_ADDRESS_LEN);
+
+ if (Instance != NULL) {
+ //
+ // Inherit the parameters from the instance configuration.
+ //
+ CacheEntry->RetryCount = Instance->ConfigData.RetryCount;
+ CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut;
+ CacheEntry->DefaultDecayTime = Instance->ConfigData.EntryTimeOut;
+ CacheEntry->DecayTime = Instance->ConfigData.EntryTimeOut;
+ } else {
+ //
+ // Use the default parameters if this cache entry isn't allocate in a
+ // instance's scope.
+ //
+ CacheEntry->RetryCount = ARP_DEFAULT_RETRY_COUNT;
+ CacheEntry->NextRetryTime = ARP_DEFAULT_RETRY_INTERVAL;
+ CacheEntry->DefaultDecayTime = ARP_DEFAULT_TIMEOUT_VALUE;
+ CacheEntry->DecayTime = ARP_DEFAULT_TIMEOUT_VALUE;
+ }
+
+ return CacheEntry;
+}
+
+
+/**
+ Turn the CacheEntry into the resolved status.
+
+ @param[in] CacheEntry Pointer to the resolved cache entry.
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] UserEvent Pointer to the UserEvent to notify.
+
+ @return The count of notifications sent to the instance.
+
+**/
+UINTN
+ArpAddressResolved (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN ARP_INSTANCE_DATA *Instance OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ USER_REQUEST_CONTEXT *Context;
+ UINTN Count;
+
+ Count = 0;
+
+ //
+ // Iterate all the linked user requests to notify them.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &CacheEntry->UserRequestList) {
+ Context = NET_LIST_USER_STRUCT (Entry, USER_REQUEST_CONTEXT, List);
+
+ if (((Instance == NULL) || (Context->Instance == Instance)) &&
+ ((UserEvent == NULL) || (Context->UserRequestEvent == UserEvent))) {
+ //
+ // Copy the address to the user-provided buffer and notify the user.
+ //
+ CopyMem (
+ Context->UserHwAddrBuffer,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ CacheEntry->Addresses[Hardware].Length
+ );
+ gBS->SignalEvent (Context->UserRequestEvent);
+
+ //
+ // Remove this user request and free the context data.
+ //
+ RemoveEntryList (&Context->List);
+ FreePool (Context);
+
+ Count++;
+ }
+ }
+
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the Context->UserRequestEvent.
+ //
+ DispatchDpc ();
+
+ return Count;
+}
+
+
+/**
+ Fill the addresses in the CacheEntry using the information passed in by
+ HwAddr and SwAddr.
+
+ @param[in] CacheEntry Pointer to the cache entry.
+ @param[in] HwAddr Pointer to the software address.
+ @param[in] SwAddr Pointer to the hardware address.
+
+ @return None.
+
+**/
+VOID
+ArpFillAddressInCacheEntry (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN NET_ARP_ADDRESS *HwAddr OPTIONAL,
+ IN NET_ARP_ADDRESS *SwAddr OPTIONAL
+ )
+{
+ NET_ARP_ADDRESS *Address[2];
+ NET_ARP_ADDRESS *CacheAddress;
+ UINT32 Index;
+
+ Address[Hardware] = HwAddr;
+ Address[Protocol] = SwAddr;
+
+ for (Index = 0; Index < 2; Index++) {
+ if (Address[Index] != NULL) {
+ //
+ // Fill the address if the passed in pointer is not NULL.
+ //
+ CacheAddress = &CacheEntry->Addresses[Index];
+
+ CacheAddress->Type = Address[Index]->Type;
+ CacheAddress->Length = Address[Index]->Length;
+
+ if (Address[Index]->AddressPtr != NULL) {
+ //
+ // Copy it if the AddressPtr points to some buffer.
+ //
+ CopyMem (
+ CacheAddress->AddressPtr,
+ Address[Index]->AddressPtr,
+ CacheAddress->Length
+ );
+ } else {
+ //
+ // Zero the corresponding address buffer in the CacheEntry.
+ //
+ ZeroMem (CacheAddress->AddressPtr, CacheAddress->Length);
+ }
+ }
+ }
+}
+
+
+/**
+ Configure the instance using the ConfigData. ConfigData is already validated.
+
+ @param[in] Instance Pointer to the instance context data to be
+ configured.
+ @param[in] ConfigData Pointer to the configuration data used to
+ configure the instance.
+
+ @retval EFI_SUCCESS The instance is configured with the ConfigData.
+ @retval EFI_ACCESS_DENIED The instance is already configured and the
+ ConfigData tries to reset some unchangeable
+ fields.
+ @retval EFI_INVALID_PARAMETER The ConfigData provides a non-unicast IPv4 address
+ when the SwAddressType is IPv4.
+ @retval EFI_OUT_OF_RESOURCES The instance fails to configure due to memory
+ limitation.
+
+**/
+EFI_STATUS
+ArpConfigureInstance (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ )
+{
+ EFI_ARP_CONFIG_DATA *OldConfigData;
+ IP4_ADDR Ip;
+
+ OldConfigData = &Instance->ConfigData;
+
+ if (ConfigData != NULL) {
+
+ if (Instance->Configured) {
+ //
+ // The instance is configured, check the unchangeable fields.
+ //
+ if ((OldConfigData->SwAddressType != ConfigData->SwAddressType) ||
+ (OldConfigData->SwAddressLength != ConfigData->SwAddressLength) ||
+ (CompareMem (
+ OldConfigData->StationAddress,
+ ConfigData->StationAddress,
+ OldConfigData->SwAddressLength
+ ) != 0)) {
+ //
+ // Deny the unallowed changes.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+ } else {
+ //
+ // The instance is not configured.
+ //
+
+ if (ConfigData->SwAddressType == IPV4_ETHER_PROTO_TYPE) {
+ CopyMem (&Ip, ConfigData->StationAddress, sizeof (IP4_ADDR));
+
+ if (IP4_IS_UNSPECIFIED (Ip) || IP4_IS_LOCAL_BROADCAST (Ip)) {
+ //
+ // The station address should not be zero or broadcast address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Save the configuration.
+ //
+ CopyMem (OldConfigData, ConfigData, sizeof (*OldConfigData));
+
+ OldConfigData->StationAddress = AllocatePool (OldConfigData->SwAddressLength);
+ if (OldConfigData->StationAddress == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpConfigInstance: AllocatePool for the StationAddress "
+ "failed.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save the StationAddress.
+ //
+ CopyMem (
+ OldConfigData->StationAddress,
+ ConfigData->StationAddress,
+ OldConfigData->SwAddressLength
+ );
+
+ //
+ // Set the state to configured.
+ //
+ Instance->Configured = TRUE;
+ }
+
+ //
+ // Use the implementation specific values if the following field is zero.
+ //
+ OldConfigData->EntryTimeOut = (ConfigData->EntryTimeOut == 0) ?
+ ARP_DEFAULT_TIMEOUT_VALUE : ConfigData->EntryTimeOut;
+
+ OldConfigData->RetryCount = (ConfigData->RetryCount == 0) ?
+ ARP_DEFAULT_RETRY_COUNT : ConfigData->RetryCount;
+
+ OldConfigData->RetryTimeOut = (ConfigData->RetryTimeOut == 0) ?
+ ARP_DEFAULT_RETRY_INTERVAL : ConfigData->RetryTimeOut;
+ } else {
+ //
+ // Reset the configuration.
+ //
+
+ if (Instance->Configured) {
+ //
+ // Cancel the arp requests issued by this instance.
+ //
+ Instance->ArpProto.Cancel (&Instance->ArpProto, NULL, NULL);
+
+ //
+ // Free the buffer previously allocated to hold the station address.
+ //
+ FreePool (OldConfigData->StationAddress);
+ }
+
+ Instance->Configured = FALSE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Send out an arp frame using the CachEntry and the ArpOpCode.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] CacheEntry Pointer to the configuration data used to
+ configure the instance.
+ @param[in] ArpOpCode The opcode used to send out this Arp frame, either
+ request or reply.
+
+ @return None.
+
+**/
+VOID
+ArpSendFrame (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN UINT16 ArpOpCode
+ )
+{
+ EFI_STATUS Status;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+ UINT32 TotalLength;
+ UINT8 *Packet;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_ARP_CONFIG_DATA *ConfigData;
+ UINT8 *TmpPtr;
+ ARP_HEAD *ArpHead;
+
+ ASSERT ((Instance != NULL) && (CacheEntry != NULL));
+
+ //
+ // Allocate memory for the TxToken.
+ //
+ TxToken = AllocatePool (sizeof(EFI_MANAGED_NETWORK_COMPLETION_TOKEN));
+ if (TxToken == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpSendFrame: Allocate memory for TxToken failed.\n"));
+ return;
+ }
+
+ TxToken->Event = NULL;
+ TxData = NULL;
+ Packet = NULL;
+
+ //
+ // Create the event for this TxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ ArpOnFrameSent,
+ (VOID *)TxToken,
+ &TxToken->Event
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "ArpSendFrame: CreateEvent failed for TxToken->Event.\n"));
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Allocate memory for the TxData used in the TxToken.
+ //
+ TxData = AllocatePool (sizeof(EFI_MANAGED_NETWORK_TRANSMIT_DATA));
+ if (TxData == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpSendFrame: Allocate memory for TxData failed.\n"));
+ goto CLEAN_EXIT;
+ }
+
+ ArpService = Instance->ArpService;
+ SnpMode = &ArpService->SnpMode;
+ ConfigData = &Instance->ConfigData;
+
+ //
+ // Calculate the buffer length for this arp frame.
+ //
+ TotalLength = SnpMode->MediaHeaderSize + sizeof (ARP_HEAD) +
+ 2 * (ConfigData->SwAddressLength + SnpMode->HwAddressSize);
+
+ //
+ // Allocate buffer for the arp frame.
+ //
+ Packet = AllocatePool (TotalLength);
+ if (Packet == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpSendFrame: Allocate memory for Packet failed.\n"));
+ ASSERT (Packet != NULL);
+ }
+
+ TmpPtr = Packet;
+
+ //
+ // The destination MAC address.
+ //
+ if (ArpOpCode == ARP_OPCODE_REQUEST) {
+ CopyMem (TmpPtr, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize);
+ } else {
+ CopyMem (
+ TmpPtr,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ SnpMode->HwAddressSize
+ );
+ }
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The source MAC address.
+ //
+ CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The ethernet protocol type.
+ //
+ *(UINT16 *)TmpPtr = HTONS (ARP_ETHER_PROTO_TYPE);
+ TmpPtr += 2;
+
+ //
+ // The ARP Head.
+ //
+ ArpHead = (ARP_HEAD *) TmpPtr;
+ ArpHead->HwType = HTONS ((UINT16)SnpMode->IfType);
+ ArpHead->ProtoType = HTONS (ConfigData->SwAddressType);
+ ArpHead->HwAddrLen = (UINT8)SnpMode->HwAddressSize;
+ ArpHead->ProtoAddrLen = ConfigData->SwAddressLength;
+ ArpHead->OpCode = HTONS (ArpOpCode);
+ TmpPtr += sizeof (ARP_HEAD);
+
+ //
+ // The sender hardware address.
+ //
+ CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The sender protocol address.
+ //
+ CopyMem (TmpPtr, ConfigData->StationAddress, ConfigData->SwAddressLength);
+ TmpPtr += ConfigData->SwAddressLength;
+
+ //
+ // The target hardware address.
+ //
+ CopyMem (
+ TmpPtr,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ SnpMode->HwAddressSize
+ );
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The target protocol address.
+ //
+ CopyMem (
+ TmpPtr,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ ConfigData->SwAddressLength
+ );
+
+ //
+ // Set all the fields of the TxData.
+ //
+ TxData->DestinationAddress = NULL;
+ TxData->SourceAddress = NULL;
+ TxData->ProtocolType = 0;
+ TxData->DataLength = TotalLength - SnpMode->MediaHeaderSize;
+ TxData->HeaderLength = (UINT16) SnpMode->MediaHeaderSize;
+ TxData->FragmentCount = 1;
+
+ TxData->FragmentTable[0].FragmentBuffer = Packet;
+ TxData->FragmentTable[0].FragmentLength = TotalLength;
+
+ //
+ // Associate the TxData with the TxToken.
+ //
+ TxToken->Packet.TxData = TxData;
+ TxToken->Status = EFI_NOT_READY;
+
+ //
+ // Send out this arp packet by Mnp.
+ //
+ Status = ArpService->Mnp->Transmit (ArpService->Mnp, TxToken);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Mnp->Transmit failed, %r.\n", Status));
+ goto CLEAN_EXIT;
+ }
+
+ return;
+
+CLEAN_EXIT:
+
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+
+ if (TxData != NULL) {
+ FreePool (TxData);
+ }
+
+ if (TxToken->Event != NULL) {
+ gBS->CloseEvent (TxToken->Event);
+ }
+
+ FreePool (TxToken);
+}
+
+
+/**
+ Delete the cache entries in the specified CacheTable, using the BySwAddress,
+ SwAddressType, AddressBuffer combination as the matching key, if Force is TRUE,
+ the cache is deleted event it's a static entry.
+
+ @param[in] CacheTable Pointer to the cache table to do the deletion.
+ @param[in] BySwAddress Delete the cache entry by software address or by
+ hardware address.
+ @param[in] SwAddressType The software address used to do the deletion.
+ @param[in] AddressBuffer Pointer to the buffer containing the address to
+ match for the deletion.
+ @param[in] Force This deletion is forced or not.
+
+ @return The count of the deleted cache entries.
+
+**/
+UINTN
+ArpDeleteCacheEntryInTable (
+ IN LIST_ENTRY *CacheTable,
+ IN BOOLEAN BySwAddress,
+ IN UINT16 SwAddressType,
+ IN UINT8 *AddressBuffer OPTIONAL,
+ IN BOOLEAN Force
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ UINTN Count;
+
+ Count = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, CacheTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if ((CacheEntry->DefaultDecayTime == 0) && !Force) {
+ //
+ // It's a static entry and we are not forced to delete it, skip.
+ //
+ continue;
+ }
+
+ if (BySwAddress) {
+ if (SwAddressType == CacheEntry->Addresses[Protocol].Type) {
+ //
+ // Protocol address type matched. Check the address.
+ //
+ if ((AddressBuffer == NULL) ||
+ (CompareMem (
+ AddressBuffer,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ CacheEntry->Addresses[Protocol].Length
+ ) == 0)) {
+ //
+ // Address matched.
+ //
+ goto MATCHED;
+ }
+ }
+ } else {
+ if ((AddressBuffer == NULL) ||
+ (CompareMem (
+ AddressBuffer,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ CacheEntry->Addresses[Hardware].Length
+ ) == 0)) {
+ //
+ // Address matched.
+ //
+ goto MATCHED;
+ }
+ }
+
+ continue;
+
+MATCHED:
+
+ //
+ // Delete this entry.
+ //
+ RemoveEntryList (&CacheEntry->List);
+ ASSERT (IsListEmpty (&CacheEntry->UserRequestList));
+ FreePool (CacheEntry);
+
+ Count++;
+ }
+
+ return Count;
+}
+
+
+/**
+ Delete cache entries in all the cache tables.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] BySwAddress Delete the cache entry by software address or by
+ hardware address.
+ @param[in] AddressBuffer Pointer to the buffer containing the address to
+ match for the deletion.
+ @param[in] Force This deletion is forced or not.
+
+ @return The count of the deleted cache entries.
+
+**/
+UINTN
+ArpDeleteCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN UINT8 *AddressBuffer OPTIONAL,
+ IN BOOLEAN Force
+ )
+{
+ ARP_SERVICE_DATA *ArpService;
+ UINTN Count;
+
+ NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);
+
+ ArpService = Instance->ArpService;
+
+ //
+ // Delete the cache entries in the DeniedCacheTable.
+ //
+ Count = ArpDeleteCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ BySwAddress,
+ Instance->ConfigData.SwAddressType,
+ AddressBuffer,
+ Force
+ );
+
+ //
+ // Delete the cache entries inthe ResolvedCacheTable.
+ //
+ Count += ArpDeleteCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ BySwAddress,
+ Instance->ConfigData.SwAddressType,
+ AddressBuffer,
+ Force
+ );
+
+ return Count;
+}
+
+
+/**
+ Cancel the arp request.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] TargetSwAddress Pointer to the buffer containing the target
+ software address to match the arp request.
+ @param[in] UserEvent The user event used to notify this request
+ cancellation.
+
+ @return The count of the cancelled requests.
+
+**/
+UINTN
+ArpCancelRequest (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ )
+{
+ ARP_SERVICE_DATA *ArpService;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ UINTN Count;
+
+ NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);
+
+ ArpService = Instance->ArpService;
+
+ Count = 0;
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if ((TargetSwAddress == NULL) ||
+ (CompareMem (
+ TargetSwAddress,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ CacheEntry->Addresses[Protocol].Length
+ ) == 0)) {
+ //
+ // This request entry matches the TargetSwAddress or all requests are to be
+ // cancelled as TargetSwAddress is NULL.
+ //
+ Count += ArpAddressResolved (CacheEntry, Instance, UserEvent);
+
+ if (IsListEmpty (&CacheEntry->UserRequestList)) {
+ //
+ // No user requests any more, remove this request cache entry.
+ //
+ RemoveEntryList (&CacheEntry->List);
+ FreePool (CacheEntry);
+ }
+ }
+ }
+
+ return Count;
+}
+
+
+/**
+ Find the cache entry in the cache table.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] BySwAddress Set to TRUE to look for matching software protocol
+ addresses. Set to FALSE to look for matching
+ hardware protocol addresses.
+ @param[in] AddressBuffer Pointer to address buffer. Set to NULL to match
+ all addresses.
+ @param[out] EntryLength The size of an entry in the entries buffer.
+ @param[out] EntryCount The number of ARP cache entries that are found by
+ the specified criteria.
+ @param[out] Entries Pointer to the buffer that will receive the ARP
+ cache entries.
+ @param[in] Refresh Set to TRUE to refresh the timeout value of the
+ matching ARP cache entry.
+
+ @retval EFI_SUCCESS The requested ARP cache entries are copied into
+ the buffer.
+ @retval EFI_NOT_FOUND No matching entries found.
+ @retval EFI_OUT_OF_RESOURCE There is a memory allocation failure.
+
+**/
+EFI_STATUS
+ArpFindCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ NET_ARP_ADDRESS MatchAddress;
+ FIND_OPTYPE FindOpType;
+ LIST_ENTRY *StartEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_MAP FoundEntries;
+ UINT32 FoundCount;
+ EFI_ARP_FIND_DATA *FindData;
+ LIST_ENTRY *CacheTable;
+ UINT32 FoundEntryLength;
+
+ ArpService = Instance->ArpService;
+
+ //
+ // Init the FounEntries used to hold the found cache entries.
+ //
+ NetMapInit (&FoundEntries);
+
+ //
+ // Set the MatchAddress.
+ //
+ if (BySwAddress) {
+ MatchAddress.Type = Instance->ConfigData.SwAddressType;
+ MatchAddress.Length = Instance->ConfigData.SwAddressLength;
+ FindOpType = ByProtoAddress;
+ } else {
+ MatchAddress.Type = ArpService->SnpMode.IfType;
+ MatchAddress.Length = (UINT8)ArpService->SnpMode.HwAddressSize;
+ FindOpType = ByHwAddress;
+ }
+
+ MatchAddress.AddressPtr = AddressBuffer;
+
+ //
+ // Search the DeniedCacheTable
+ //
+ StartEntry = NULL;
+ while (TRUE) {
+ //
+ // Try to find the matched entries in the DeniedCacheTable.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ StartEntry,
+ FindOpType,
+ &MatchAddress,
+ &MatchAddress
+ );
+ if (CacheEntry == NULL) {
+ //
+ // Once the CacheEntry is NULL, there are no more matches.
+ //
+ break;
+ }
+
+ //
+ // Insert the found entry into the map.
+ //
+ NetMapInsertTail (
+ &FoundEntries,
+ (VOID *)CacheEntry,
+ (VOID *)&ArpService->DeniedCacheTable
+ );
+
+ //
+ // Let the next search start from this cache entry.
+ //
+ StartEntry = &CacheEntry->List;
+
+ if (Refresh) {
+ //
+ // Refresh the DecayTime if needed.
+ //
+ CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;
+ }
+ }
+
+ //
+ // Search the ResolvedCacheTable
+ //
+ StartEntry = NULL;
+ while (TRUE) {
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ StartEntry,
+ FindOpType,
+ &MatchAddress,
+ &MatchAddress
+ );
+ if (CacheEntry == NULL) {
+ //
+ // Once the CacheEntry is NULL, there are no more matches.
+ //
+ break;
+ }
+
+ //
+ // Insert the found entry into the map.
+ //
+ NetMapInsertTail (
+ &FoundEntries,
+ (VOID *)CacheEntry,
+ (VOID *)&ArpService->ResolvedCacheTable
+ );
+
+ //
+ // Let the next search start from this cache entry.
+ //
+ StartEntry = &CacheEntry->List;
+
+ if (Refresh) {
+ //
+ // Refresh the DecayTime if needed.
+ //
+ CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ FoundCount = (UINT32) NetMapGetCount (&FoundEntries);
+ if (FoundCount == 0) {
+ Status = EFI_NOT_FOUND;
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Found the entry length, make sure its 8 bytes alignment.
+ //
+ FoundEntryLength = (((sizeof (EFI_ARP_FIND_DATA) + Instance->ConfigData.SwAddressLength +
+ ArpService->SnpMode.HwAddressSize) + 3) & ~(0x3));
+
+ if (EntryLength != NULL) {
+ *EntryLength = FoundEntryLength;
+ }
+
+ if (EntryCount != NULL) {
+ //
+ // Return the found entry count.
+ //
+ *EntryCount = FoundCount;
+ }
+
+ if (Entries == NULL) {
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Allocate buffer to copy the found entries.
+ //
+ FindData = AllocatePool (FoundCount * FoundEntryLength);
+ if (FindData == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpFindCacheEntry: Failed to allocate memory.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Return the address to the user.
+ //
+ *Entries = FindData;
+
+ //
+ // Dump the entries.
+ //
+ while (!NetMapIsEmpty (&FoundEntries)) {
+ //
+ // Get a cache entry from the map.
+ //
+ CacheEntry = NetMapRemoveHead (&FoundEntries, (VOID **)&CacheTable);
+
+ //
+ // Set the fields in FindData.
+ //
+ FindData->Size = FoundEntryLength;
+ FindData->DenyFlag = (BOOLEAN)(CacheTable == &ArpService->DeniedCacheTable);
+ FindData->StaticFlag = (BOOLEAN)(CacheEntry->DefaultDecayTime == 0);
+ FindData->HwAddressType = ArpService->SnpMode.IfType;
+ FindData->SwAddressType = Instance->ConfigData.SwAddressType;
+ FindData->HwAddressLength = (UINT8)ArpService->SnpMode.HwAddressSize;
+ FindData->SwAddressLength = Instance->ConfigData.SwAddressLength;
+
+ //
+ // Copy the software address.
+ //
+ CopyMem (
+ FindData + 1,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ FindData->SwAddressLength
+ );
+
+ //
+ // Copy the hardware address.
+ //
+ CopyMem (
+ (UINT8 *)(FindData + 1) + FindData->SwAddressLength,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ FindData->HwAddressLength
+ );
+
+ //
+ // Slip to the next FindData.
+ //
+ FindData = (EFI_ARP_FIND_DATA *)((UINT8 *)FindData + FoundEntryLength);
+ }
+
+CLEAN_EXIT:
+
+ NetMapClean (&FoundEntries);
+
+ return Status;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h
new file mode 100644
index 0000000000..a5dce7d9a5
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h
@@ -0,0 +1,776 @@
+/** @file
+ EFI Address Resolution Protocol (ARP) Protocol interface header file.
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ARP_IMPL_H_
+#define _ARP_IMPL_H_
+
+
+#include <Uefi.h>
+
+#include <Protocol/Arp.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+
+//
+// Ethernet protocol type definitions.
+//
+#define ARP_ETHER_PROTO_TYPE 0x0806
+#define IPV4_ETHER_PROTO_TYPE 0x0800
+#define IPV6_ETHER_PROTO_TYPE 0x86DD
+
+//
+// ARP opcode definitions.
+//
+#define ARP_OPCODE_REQUEST 0x0001
+#define ARP_OPCODE_REPLY 0x0002
+
+//
+// ARP timeout, retry count and interval definitions.
+//
+#define ARP_DEFAULT_TIMEOUT_VALUE (400 * TICKS_PER_SECOND)
+#define ARP_DEFAULT_RETRY_COUNT 2
+#define ARP_DEFAULT_RETRY_INTERVAL (5 * TICKS_PER_MS)
+#define ARP_PERIODIC_TIMER_INTERVAL (500 * TICKS_PER_MS)
+
+//
+// ARP packet head definition.
+//
+#pragma pack(1)
+typedef struct {
+ UINT16 HwType;
+ UINT16 ProtoType;
+ UINT8 HwAddrLen;
+ UINT8 ProtoAddrLen;
+ UINT16 OpCode;
+} ARP_HEAD;
+#pragma pack()
+
+//
+// ARP Address definition for internal use.
+//
+typedef struct {
+ UINT8 *SenderHwAddr;
+ UINT8 *SenderProtoAddr;
+ UINT8 *TargetHwAddr;
+ UINT8 *TargetProtoAddr;
+} ARP_ADDRESS;
+
+#define MATCH_SW_ADDRESS 0x1
+#define MATCH_HW_ADDRESS 0x2
+
+//
+// Enumeration for the search type. A search type is specified as the keyword to find
+// a cache entry in the cache table.
+//
+typedef enum {
+ ByNone = 0,
+ ByProtoAddress = MATCH_SW_ADDRESS,
+ ByHwAddress = MATCH_HW_ADDRESS,
+ ByBoth = MATCH_SW_ADDRESS | MATCH_HW_ADDRESS
+} FIND_OPTYPE;
+
+#define ARP_INSTANCE_DATA_SIGNATURE SIGNATURE_32('A', 'R', 'P', 'I')
+
+/**
+ Returns a pointer to the ARP_INSTANCE_DATA structure from the input a.
+
+ If the signatures matches, then a pointer to the data structure that contains
+ a specified field of that data structure is returned.
+
+ @param a Pointer to the field specified by ArpProto within a data
+ structure of type ARP_INSTANCE_DATA.
+
+**/
+#define ARP_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ ARP_INSTANCE_DATA, \
+ ArpProto, \
+ ARP_INSTANCE_DATA_SIGNATURE \
+ )
+
+typedef struct _ARP_SERVICE_DATA ARP_SERVICE_DATA;
+
+//
+// ARP instance context data structure.
+//
+typedef struct {
+ UINT32 Signature;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_HANDLE Handle;
+ EFI_ARP_PROTOCOL ArpProto;
+ LIST_ENTRY List;
+ EFI_ARP_CONFIG_DATA ConfigData;
+ BOOLEAN Configured;
+ BOOLEAN InDestroy;
+} ARP_INSTANCE_DATA;
+
+#define ARP_SERVICE_DATA_SIGNATURE SIGNATURE_32('A', 'R', 'P', 'S')
+
+/**
+ Returns a pointer to the ARP_SERVICE_DATA structure from the input a.
+
+ If the signatures matches, then a pointer to the data structure that contains
+ a specified field of that data structure is returned.
+
+ @param a Pointer to the field specified by ServiceBinding within
+ a data structure of type ARP_SERVICE_DATA.
+
+**/
+#define ARP_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ ARP_SERVICE_DATA, \
+ ServiceBinding, \
+ ARP_SERVICE_DATA_SIGNATURE \
+ )
+
+//
+// ARP service data structure.
+//
+struct _ARP_SERVICE_DATA {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ EFI_HANDLE MnpChildHandle;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE ControllerHandle;
+
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN RxToken;
+
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ UINTN ChildrenNumber;
+ LIST_ENTRY ChildrenList;
+
+ LIST_ENTRY PendingRequestTable;
+ LIST_ENTRY DeniedCacheTable;
+ LIST_ENTRY ResolvedCacheTable;
+
+ EFI_EVENT PeriodicTimer;
+};
+
+//
+// User request context structure.
+//
+typedef struct {
+ LIST_ENTRY List;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_EVENT UserRequestEvent;
+ VOID *UserHwAddrBuffer;
+} USER_REQUEST_CONTEXT;
+
+#define ARP_MAX_PROTOCOL_ADDRESS_LEN sizeof(EFI_IP_ADDRESS)
+#define ARP_MAX_HARDWARE_ADDRESS_LEN sizeof(EFI_MAC_ADDRESS)
+
+typedef union {
+ UINT8 ProtoAddress[ARP_MAX_PROTOCOL_ADDRESS_LEN];
+ UINT8 HwAddress[ARP_MAX_HARDWARE_ADDRESS_LEN];
+} NET_ARP_ADDRESS_UNION;
+
+//
+// ARP address structure in an ARP packet.
+//
+typedef struct {
+ UINT16 Type;
+ UINT8 Length;
+ UINT8 *AddressPtr;
+ NET_ARP_ADDRESS_UNION Buffer;
+} NET_ARP_ADDRESS;
+
+//
+// Enumeration for ARP address type.
+//
+typedef enum {
+ Hardware,
+ Protocol
+} ARP_ADDRESS_TYPE;
+
+//
+// ARP cache entry definition.
+//
+typedef struct {
+ LIST_ENTRY List;
+
+ UINT32 RetryCount;
+ UINT32 DefaultDecayTime;
+ UINT32 DecayTime;
+ UINT32 NextRetryTime;
+
+ NET_ARP_ADDRESS Addresses[2];
+
+ LIST_ENTRY UserRequestList;
+} ARP_CACHE_ENTRY;
+
+/**
+ This function is used to assign a station address to the ARP cache for this instance
+ of the ARP driver.
+
+ Each ARP instance has one station address. The EFI_ARP_PROTOCOL driver will
+ respond to ARP requests that match this registered station address. A call to
+ this function with the ConfigData field set to NULL will reset this ARP instance.
+
+ Once a protocol type and station address have been assigned to this ARP instance,
+ all the following ARP functions will use this information. Attempting to change
+ the protocol type or station address to a configured ARP instance will result in errors.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param ConfigData Pointer to the EFI_ARP_CONFIG_DATA structure.
+
+ @retval EFI_SUCCESS The new station address was successfully
+ registered.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. SwAddressLength is zero when
+ ConfigData is not NULL. StationAddress is NULL
+ when ConfigData is not NULL.
+ @retval EFI_ACCESS_DENIED The SwAddressType, SwAddressLength, or
+ StationAddress is different from the one that is
+ already registered.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new StationAddress could not be
+ allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpConfigure (
+ IN EFI_ARP_PROTOCOL *This,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ );
+
+/**
+ This function is used to insert entries into the ARP cache.
+
+ ARP cache entries are typically inserted and updated by network protocol drivers
+ as network traffic is processed. Most ARP cache entries will time out and be
+ deleted if the network traffic stops. ARP cache entries that were inserted
+ by the Add() function may be static (will not time out) or dynamic (will time out).
+ Default ARP cache timeout values are not covered in most network protocol
+ specifications (although RFC 1122 comes pretty close) and will only be
+ discussed in general in this specification. The timeout values that are
+ used in the EFI Sample Implementation should be used only as a guideline.
+ Final product implementations of the EFI network stack should be tuned for
+ their expected network environments.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param DenyFlag Set to TRUE if this entry is a deny entry. Set to
+ FALSE if this entry is a normal entry.
+ @param TargetSwAddress Pointer to a protocol address to add (or deny).
+ May be set to NULL if DenyFlag is TRUE.
+ @param TargetHwAddress Pointer to a hardware address to add (or deny).
+ May be set to NULL if DenyFlag is TRUE.
+ @param TimeoutValue Time in 100-ns units that this entry will remain
+ in the ARP cache. A value of zero means that the
+ entry is permanent. A nonzero value will override
+ the one given by Configure() if the entry to be
+ added is a dynamic entry.
+ @param Overwrite If TRUE, the matching cache entry will be
+ overwritten with the supplied parameters. If
+ FALSE, EFI_ACCESS_DENIED is returned if the
+ corresponding cache entry already exists.
+
+ @retval EFI_SUCCESS The entry has been added or updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. DenyFlag is FALSE and
+ TargetHwAddress is NULL. DenyFlag is FALSE and
+ TargetSwAddress is NULL. TargetHwAddress is NULL
+ and TargetSwAddress is NULL. Both TargetSwAddress
+ and TargetHwAddress are not NULL when DenyFlag is
+ TRUE.
+ @retval EFI_OUT_OF_RESOURCES The new ARP cache entry could not be allocated.
+ @retval EFI_ACCESS_DENIED The ARP cache entry already exists and Overwrite
+ is not true.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpAdd (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN DenyFlag,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN VOID *TargetHwAddress OPTIONAL,
+ IN UINT32 TimeoutValue,
+ IN BOOLEAN Overwrite
+ );
+
+/**
+ This function searches the ARP cache for matching entries and allocates a buffer into
+ which those entries are copied.
+
+ The first part of the allocated buffer is EFI_ARP_FIND_DATA, following which
+ are protocol address pairs and hardware address pairs.
+ When finding a specific protocol address (BySwAddress is TRUE and AddressBuffer
+ is not NULL), the ARP cache timeout for the found entry is reset if Refresh is
+ set to TRUE. If the found ARP cache entry is a permanent entry, it is not
+ affected by Refresh.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param BySwAddress Set to TRUE to look for matching software protocol
+ addresses. Set to FALSE to look for matching
+ hardware protocol addresses.
+ @param AddressBuffer Pointer to address buffer. Set to NULL to match
+ all addresses.
+ @param EntryLength The size of an entry in the entries buffer.
+ @param EntryCount The number of ARP cache entries that are found by
+ the specified criteria.
+ @param Entries Pointer to the buffer that will receive the ARP
+ cache entries.
+ @param Refresh Set to TRUE to refresh the timeout value of the
+ matching ARP cache entry.
+
+ @retval EFI_SUCCESS The requested ARP cache entries were copied into
+ the buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. Both EntryCount and EntryLength are
+ NULL, when Refresh is FALSE.
+ @retval EFI_NOT_FOUND No matching entries were found.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpFind (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ );
+
+/**
+ This function removes specified ARP cache entries.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param BySwAddress Set to TRUE to delete matching protocol addresses.
+ Set to FALSE to delete matching hardware
+ addresses.
+ @param AddressBuffer Pointer to the address buffer that is used as a
+ key to look for the cache entry. Set to NULL to
+ delete all entries.
+
+ @retval EFI_SUCCESS The entry was removed from the ARP cache.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND The specified deletion key was not found.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDelete (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL
+ );
+
+/**
+ This function delete all dynamic entries from the ARP cache that match the specified
+ software protocol type.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The cache has been flushed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND There are no matching dynamic cache entries.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpFlush (
+ IN EFI_ARP_PROTOCOL *This
+ );
+
+/**
+ This function tries to resolve the TargetSwAddress and optionally returns a
+ TargetHwAddress if it already exists in the ARP cache.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param TargetSwAddress Pointer to the protocol address to resolve.
+ @param ResolvedEvent Pointer to the event that will be signaled when
+ the address is resolved or some error occurs.
+ @param TargetHwAddress Pointer to the buffer for the resolved hardware
+ address in network byte order.
+
+ @retval EFI_SUCCESS The data is copied from the ARP cache into the
+ TargetHwAddress buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. TargetHwAddress is NULL.
+ @retval EFI_ACCESS_DENIED The requested address is not present in the normal
+ ARP cache but is present in the deny address list.
+ Outgoing traffic to that address is forbidden.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+ @retval EFI_NOT_READY The request has been started and is not finished.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpRequest (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL,
+ OUT VOID *TargetHwAddress
+ );
+
+/**
+ This function aborts the previous ARP request (identified by This, TargetSwAddress
+ and ResolvedEvent) that is issued by EFI_ARP_PROTOCOL.Request().
+
+ If the request is in the internal ARP request queue, the request is aborted
+ immediately and its ResolvedEvent is signaled. Only an asynchronous address
+ request needs to be canceled. If TargeSwAddress and ResolveEvent are both
+ NULL, all the pending asynchronous requests that have been issued by This
+ instance will be cancelled and their corresponding events will be signaled.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param TargetSwAddress Pointer to the protocol address in previous
+ request session.
+ @param ResolvedEvent Pointer to the event that is used as the
+ notification event in previous request session.
+
+ @retval EFI_SUCCESS The pending request session(s) is/are aborted and
+ corresponding event(s) is/are signaled.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. TargetSwAddress is not NULL and
+ ResolvedEvent is NULL. TargetSwAddress is NULL and
+ ResolvedEvent is not NULL.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+ @retval EFI_NOT_FOUND The request is not issued by
+ EFI_ARP_PROTOCOL.Request().
+
+**/
+EFI_STATUS
+EFIAPI
+ArpCancel (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL
+ );
+
+/**
+ Configure the instance using the ConfigData. ConfigData is already validated.
+
+ @param[in] Instance Pointer to the instance context data to be
+ configured.
+ @param[in] ConfigData Pointer to the configuration data used to
+ configure the instance.
+
+ @retval EFI_SUCCESS The instance is configured with the ConfigData.
+ @retval EFI_ACCESS_DENIED The instance is already configured and the
+ ConfigData tries to reset some unchangeable
+ fields.
+ @retval EFI_INVALID_PARAMETER The ConfigData provides a non-unicast IPv4 address
+ when the SwAddressType is IPv4.
+ @retval EFI_OUT_OF_RESOURCES The instance fails to configure due to memory
+ limitation.
+
+**/
+EFI_STATUS
+ArpConfigureInstance (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ );
+
+/**
+ Find the CacheEntry, using ProtocolAddress or HardwareAddress or both, as the keyword,
+ in the DeniedCacheTable.
+
+ @param[in] ArpService Pointer to the arp service context data.
+ @param[in] ProtocolAddress Pointer to the protocol address.
+ @param[in] HardwareAddress Pointer to the hardware address.
+
+ @return Pointer to the matched cache entry, if NULL no match is found.
+
+**/
+ARP_CACHE_ENTRY *
+ArpFindDeniedCacheEntry (
+ IN ARP_SERVICE_DATA *ArpService,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ );
+
+/**
+ Find the CacheEntry which matches the requirements in the specified CacheTable.
+
+ @param[in] CacheTable Pointer to the arp cache table.
+ @param[in] StartEntry Pointer to the start entry this search begins with
+ in the cache table.
+ @param[in] FindOpType The search type.
+ @param[in] ProtocolAddress Pointer to the protocol address to match.
+ @param[in] HardwareAddress Pointer to the hardware address to match.
+
+ @return Pointer to the matched arp cache entry, if NULL, no match is found.
+
+**/
+ARP_CACHE_ENTRY *
+ArpFindNextCacheEntryInTable (
+ IN LIST_ENTRY *CacheTable,
+ IN LIST_ENTRY *StartEntry,
+ IN FIND_OPTYPE FindOpType,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ );
+
+/**
+ Allocate a cache entry and initialize it.
+
+ @param[in] Instance Pointer to the instance context data.
+
+ @return Pointer to the new created cache entry.
+
+**/
+ARP_CACHE_ENTRY *
+ArpAllocCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance
+ );
+
+/**
+ Fill the addresses in the CacheEntry using the information passed in by
+ HwAddr and SwAddr.
+
+ @param[in] CacheEntry Pointer to the cache entry.
+ @param[in] HwAddr Pointer to the software address.
+ @param[in] SwAddr Pointer to the hardware address.
+
+ @return None.
+
+**/
+VOID
+ArpFillAddressInCacheEntry (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN NET_ARP_ADDRESS *HwAddr OPTIONAL,
+ IN NET_ARP_ADDRESS *SwAddr OPTIONAL
+ );
+
+/**
+ Turn the CacheEntry into the resolved status.
+
+ @param[in] CacheEntry Pointer to the resolved cache entry.
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] UserEvent Pointer to the UserEvent to notify.
+
+ @return The count of notifications sent to the instance.
+
+**/
+UINTN
+ArpAddressResolved (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN ARP_INSTANCE_DATA *Instance OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ );
+
+/**
+ Delete cache entries in all the cache tables.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] BySwAddress Delete the cache entry by software address or by
+ hardware address.
+ @param[in] AddressBuffer Pointer to the buffer containing the address to
+ match for the deletion.
+ @param[in] Force This deletion is forced or not.
+
+ @return The count of the deleted cache entries.
+
+**/
+UINTN
+ArpDeleteCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN UINT8 *AddressBuffer OPTIONAL,
+ IN BOOLEAN Force
+ );
+
+/**
+ Send out an arp frame using the CachEntry and the ArpOpCode.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] CacheEntry Pointer to the configuration data used to
+ configure the instance.
+ @param[in] ArpOpCode The opcode used to send out this Arp frame, either
+ request or reply.
+
+ @return None.
+
+**/
+VOID
+ArpSendFrame (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN UINT16 ArpOpCode
+ );
+
+/**
+ Initialize the instance context data.
+
+ @param[in] ArpService Pointer to the arp service context data this
+ instance belongs to.
+ @param[out] Instance Pointer to the instance context data.
+
+ @return None.
+
+**/
+VOID
+ArpInitInstance (
+ IN ARP_SERVICE_DATA *ArpService,
+ OUT ARP_INSTANCE_DATA *Instance
+ );
+
+/**
+ Process the Arp packets received from Mnp, the procedure conforms to RFC826.
+
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameRcvdDpc (
+ IN VOID *Context
+ );
+
+/**
+ Queue ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameRcvd (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Process the already sent arp packets.
+
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameSentDpc (
+ IN VOID *Context
+ );
+
+/**
+ Request ArpOnFrameSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Process the arp cache olding and drive the retrying arp requests.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpTimerHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Cancel the arp request.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] TargetSwAddress Pointer to the buffer containing the target
+ software address to match the arp request.
+ @param[in] UserEvent The user event used to notify this request
+ cancellation.
+
+ @return The count of the cancelled requests.
+
+**/
+UINTN
+ArpCancelRequest (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ );
+
+/**
+ Find the cache entry in the cache table.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] BySwAddress Set to TRUE to look for matching software protocol
+ addresses. Set to FALSE to look for matching
+ hardware protocol addresses.
+ @param[in] AddressBuffer Pointer to address buffer. Set to NULL to match
+ all addresses.
+ @param[out] EntryLength The size of an entry in the entries buffer.
+ @param[out] EntryCount The number of ARP cache entries that are found by
+ the specified criteria.
+ @param[out] Entries Pointer to the buffer that will receive the ARP
+ cache entries.
+ @param[in] Refresh Set to TRUE to refresh the timeout value of the
+ matching ARP cache entry.
+
+ @retval EFI_SUCCESS The requested ARP cache entries are copied into
+ the buffer.
+ @retval EFI_NOT_FOUND No matching entries found.
+ @retval EFI_OUT_OF_RESOURCE There is a memory allocation failure.
+
+**/
+EFI_STATUS
+ArpFindCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c
new file mode 100644
index 0000000000..abe02c1af4
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c
@@ -0,0 +1,745 @@
+/** @file
+ Implementation of EFI Address Resolution Protocol (ARP) Protocol interface functions.
+
+Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "ArpImpl.h"
+
+
+/**
+ This function is used to assign a station address to the ARP cache for this instance
+ of the ARP driver.
+
+ Each ARP instance has one station address. The EFI_ARP_PROTOCOL driver will
+ respond to ARP requests that match this registered station address. A call to
+ this function with the ConfigData field set to NULL will reset this ARP instance.
+
+ Once a protocol type and station address have been assigned to this ARP instance,
+ all the following ARP functions will use this information. Attempting to change
+ the protocol type or station address to a configured ARP instance will result in errors.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param ConfigData Pointer to the EFI_ARP_CONFIG_DATA structure.
+
+ @retval EFI_SUCCESS The new station address was successfully
+ registered.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. SwAddressLength is zero when
+ ConfigData is not NULL. StationAddress is NULL
+ when ConfigData is not NULL.
+ @retval EFI_ACCESS_DENIED The SwAddressType, SwAddressLength, or
+ StationAddress is different from the one that is
+ already registered.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new StationAddress could not be
+ allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpConfigure (
+ IN EFI_ARP_PROTOCOL *This,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((ConfigData != NULL) &&
+ ((ConfigData->SwAddressLength == 0) ||
+ (ConfigData->StationAddress == NULL) ||
+ (ConfigData->SwAddressType <= 1500))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Configure this instance, the ConfigData has already passed the basic checks.
+ //
+ Status = ArpConfigureInstance (Instance, ConfigData);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to insert entries into the ARP cache.
+
+ ARP cache entries are typically inserted and updated by network protocol drivers
+ as network traffic is processed. Most ARP cache entries will time out and be
+ deleted if the network traffic stops. ARP cache entries that were inserted
+ by the Add() function may be static (will not time out) or dynamic (will time out).
+ Default ARP cache timeout values are not covered in most network protocol
+ specifications (although RFC 1122 comes pretty close) and will only be
+ discussed in general in this specification. The timeout values that are
+ used in the EFI Sample Implementation should be used only as a guideline.
+ Final product implementations of the EFI network stack should be tuned for
+ their expected network environments.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param DenyFlag Set to TRUE if this entry is a deny entry. Set to
+ FALSE if this entry is a normal entry.
+ @param TargetSwAddress Pointer to a protocol address to add (or deny).
+ May be set to NULL if DenyFlag is TRUE.
+ @param TargetHwAddress Pointer to a hardware address to add (or deny).
+ May be set to NULL if DenyFlag is TRUE.
+ @param TimeoutValue Time in 100-ns units that this entry will remain
+ in the ARP cache. A value of zero means that the
+ entry is permanent. A nonzero value will override
+ the one given by Configure() if the entry to be
+ added is a dynamic entry.
+ @param Overwrite If TRUE, the matching cache entry will be
+ overwritten with the supplied parameters. If
+ FALSE, EFI_ACCESS_DENIED is returned if the
+ corresponding cache entry already exists.
+
+ @retval EFI_SUCCESS The entry has been added or updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. DenyFlag is FALSE and
+ TargetHwAddress is NULL. DenyFlag is FALSE and
+ TargetSwAddress is NULL. TargetHwAddress is NULL
+ and TargetSwAddress is NULL. Both TargetSwAddress
+ and TargetHwAddress are not NULL when DenyFlag is
+ TRUE.
+ @retval EFI_OUT_OF_RESOURCES The new ARP cache entry could not be allocated.
+ @retval EFI_ACCESS_DENIED The ARP cache entry already exists and Overwrite
+ is not true.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpAdd (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN DenyFlag,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN VOID *TargetHwAddress OPTIONAL,
+ IN UINT32 TimeoutValue,
+ IN BOOLEAN Overwrite
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_CACHE_ENTRY *CacheEntry;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ NET_ARP_ADDRESS MatchAddress[2];
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((!DenyFlag) && ((TargetHwAddress == NULL) || (TargetSwAddress == NULL))) ||
+ (DenyFlag && (TargetHwAddress != NULL) && (TargetSwAddress != NULL)) ||
+ ((TargetHwAddress == NULL) && (TargetSwAddress == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Status = EFI_SUCCESS;
+ ArpService = Instance->ArpService;
+ SnpMode = &Instance->ArpService->SnpMode;
+
+ //
+ // Fill the hardware address part in the MatchAddress.
+ //
+ MatchAddress[Hardware].Type = SnpMode->IfType;
+ MatchAddress[Hardware].Length = (UINT8) SnpMode->HwAddressSize;
+ MatchAddress[Hardware].AddressPtr = TargetHwAddress;
+
+ //
+ // Fill the software address part in the MatchAddress.
+ //
+ MatchAddress[Protocol].Type = Instance->ConfigData.SwAddressType;
+ MatchAddress[Protocol].Length = Instance->ConfigData.SwAddressLength;
+ MatchAddress[Protocol].AddressPtr = TargetSwAddress;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // See whether the entry to add exists. Check the DeinedCacheTable first.
+ //
+ CacheEntry = ArpFindDeniedCacheEntry (
+ ArpService,
+ &MatchAddress[Protocol],
+ &MatchAddress[Hardware]
+ );
+
+ if (CacheEntry == NULL) {
+ //
+ // Check the ResolvedCacheTable
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ NULL,
+ ByBoth,
+ &MatchAddress[Protocol],
+ &MatchAddress[Hardware]
+ );
+ }
+
+ if ((CacheEntry != NULL) && !Overwrite) {
+ //
+ // The entry to add exists, if not Overwirte, deny this add request.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto UNLOCK_EXIT;
+ }
+
+ if ((CacheEntry == NULL) && (TargetSwAddress != NULL)) {
+ //
+ // Check whether there are pending requests matching the entry to be added.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->PendingRequestTable,
+ NULL,
+ ByProtoAddress,
+ &MatchAddress[Protocol],
+ NULL
+ );
+ }
+
+ if (CacheEntry != NULL) {
+ //
+ // Remove it from the Table.
+ //
+ RemoveEntryList (&CacheEntry->List);
+ } else {
+ //
+ // It's a new entry, allocate memory for the entry.
+ //
+ CacheEntry = ArpAllocCacheEntry (Instance);
+
+ if (CacheEntry == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpAdd: Failed to allocate pool for CacheEntry.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UNLOCK_EXIT;
+ }
+ }
+
+ //
+ // Overwrite these parameters.
+ //
+ CacheEntry->DefaultDecayTime = TimeoutValue;
+ CacheEntry->DecayTime = TimeoutValue;
+
+ //
+ // Fill in the addresses.
+ //
+ ArpFillAddressInCacheEntry (
+ CacheEntry,
+ &MatchAddress[Hardware],
+ &MatchAddress[Protocol]
+ );
+
+ //
+ // Inform the user if there is any.
+ //
+ ArpAddressResolved (CacheEntry, NULL, NULL);
+
+ //
+ // Add this CacheEntry to the corresponding CacheTable.
+ //
+ if (DenyFlag) {
+ InsertHeadList (&ArpService->DeniedCacheTable, &CacheEntry->List);
+ } else {
+ InsertHeadList (&ArpService->ResolvedCacheTable, &CacheEntry->List);
+ }
+
+UNLOCK_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function searches the ARP cache for matching entries and allocates a buffer into
+ which those entries are copied.
+
+ The first part of the allocated buffer is EFI_ARP_FIND_DATA, following which
+ are protocol address pairs and hardware address pairs.
+ When finding a specific protocol address (BySwAddress is TRUE and AddressBuffer
+ is not NULL), the ARP cache timeout for the found entry is reset if Refresh is
+ set to TRUE. If the found ARP cache entry is a permanent entry, it is not
+ affected by Refresh.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param BySwAddress Set to TRUE to look for matching software protocol
+ addresses. Set to FALSE to look for matching
+ hardware protocol addresses.
+ @param AddressBuffer Pointer to address buffer. Set to NULL to match
+ all addresses.
+ @param EntryLength The size of an entry in the entries buffer.
+ @param EntryCount The number of ARP cache entries that are found by
+ the specified criteria.
+ @param Entries Pointer to the buffer that will receive the ARP
+ cache entries.
+ @param Refresh Set to TRUE to refresh the timeout value of the
+ matching ARP cache entry.
+
+ @retval EFI_SUCCESS The requested ARP cache entries were copied into
+ the buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. Both EntryCount and EntryLength are
+ NULL, when Refresh is FALSE.
+ @retval EFI_NOT_FOUND No matching entries were found.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpFind (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) ||
+ (!Refresh && (EntryCount == NULL) && (EntryLength == NULL)) ||
+ ((Entries != NULL) && ((EntryLength == NULL) || (EntryCount == NULL)))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // All the check passed, find the cache entries now.
+ //
+ Status = ArpFindCacheEntry (
+ Instance,
+ BySwAddress,
+ AddressBuffer,
+ EntryLength,
+ EntryCount,
+ Entries,
+ Refresh
+ );
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function removes specified ARP cache entries.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param BySwAddress Set to TRUE to delete matching protocol addresses.
+ Set to FALSE to delete matching hardware
+ addresses.
+ @param AddressBuffer Pointer to the address buffer that is used as a
+ key to look for the cache entry. Set to NULL to
+ delete all entries.
+
+ @retval EFI_SUCCESS The entry was removed from the ARP cache.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND The specified deletion key was not found.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDelete (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ UINTN Count;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Delete the specified cache entries.
+ //
+ Count = ArpDeleteCacheEntry (Instance, BySwAddress, AddressBuffer, TRUE);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
+
+
+/**
+ This function delete all dynamic entries from the ARP cache that match the specified
+ software protocol type.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The cache has been flushed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND There are no matching dynamic cache entries.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpFlush (
+ IN EFI_ARP_PROTOCOL *This
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ UINTN Count;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Delete the dynamic entries from the cache table.
+ //
+ Count = ArpDeleteCacheEntry (Instance, FALSE, NULL, FALSE);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
+
+
+/**
+ This function tries to resolve the TargetSwAddress and optionally returns a
+ TargetHwAddress if it already exists in the ARP cache.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param TargetSwAddress Pointer to the protocol address to resolve.
+ @param ResolvedEvent Pointer to the event that will be signaled when
+ the address is resolved or some error occurs.
+ @param TargetHwAddress Pointer to the buffer for the resolved hardware
+ address in network byte order.
+
+ @retval EFI_SUCCESS The data is copied from the ARP cache into the
+ TargetHwAddress buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. TargetHwAddress is NULL.
+ @retval EFI_ACCESS_DENIED The requested address is not present in the normal
+ ARP cache but is present in the deny address list.
+ Outgoing traffic to that address is forbidden.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+ @retval EFI_NOT_READY The request has been started and is not finished.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpRequest (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL,
+ OUT VOID *TargetHwAddress
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_ARP_ADDRESS HardwareAddress;
+ NET_ARP_ADDRESS ProtocolAddress;
+ USER_REQUEST_CONTEXT *RequestContext;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (TargetHwAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Status = EFI_SUCCESS;
+ ArpService = Instance->ArpService;
+ SnpMode = &ArpService->SnpMode;
+
+ if ((TargetSwAddress == NULL) ||
+ ((Instance->ConfigData.SwAddressType == IPV4_ETHER_PROTO_TYPE) &&
+ IP4_IS_LOCAL_BROADCAST (*((UINT32 *)TargetSwAddress)))) {
+ //
+ // Return the hardware broadcast address.
+ //
+ CopyMem (TargetHwAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize);
+
+ goto SIGNAL_USER;
+ }
+
+ if ((Instance->ConfigData.SwAddressType == IPV4_ETHER_PROTO_TYPE) &&
+ IP4_IS_MULTICAST (NTOHL (*((UINT32 *)TargetSwAddress)))) {
+ //
+ // If the software address is an IPv4 multicast address, invoke Mnp to
+ // resolve the address.
+ //
+ Status = ArpService->Mnp->McastIpToMac (
+ ArpService->Mnp,
+ FALSE,
+ TargetSwAddress,
+ TargetHwAddress
+ );
+ goto SIGNAL_USER;
+ }
+
+ HardwareAddress.Type = SnpMode->IfType;
+ HardwareAddress.Length = (UINT8)SnpMode->HwAddressSize;
+ HardwareAddress.AddressPtr = NULL;
+
+ ProtocolAddress.Type = Instance->ConfigData.SwAddressType;
+ ProtocolAddress.Length = Instance->ConfigData.SwAddressLength;
+ ProtocolAddress.AddressPtr = TargetSwAddress;
+
+ //
+ // Initialize the TargetHwAddrss to a zero address.
+ //
+ ZeroMem (TargetHwAddress, SnpMode->HwAddressSize);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Check whether the software address is in the denied table.
+ //
+ CacheEntry = ArpFindDeniedCacheEntry (ArpService, &ProtocolAddress, NULL);
+ if (CacheEntry != NULL) {
+ Status = EFI_ACCESS_DENIED;
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Check whether the software address is already resolved.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ NULL,
+ ByProtoAddress,
+ &ProtocolAddress,
+ NULL
+ );
+ if (CacheEntry != NULL) {
+ //
+ // Resolved, copy the address into the user buffer.
+ //
+ CopyMem (
+ TargetHwAddress,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ CacheEntry->Addresses[Hardware].Length
+ );
+
+ goto UNLOCK_EXIT;
+ }
+
+ if (ResolvedEvent == NULL) {
+ Status = EFI_NOT_READY;
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Create a request context for this arp request.
+ //
+ RequestContext = AllocatePool (sizeof(USER_REQUEST_CONTEXT));
+ if (RequestContext == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpRequest: Allocate memory for RequestContext failed.\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UNLOCK_EXIT;
+ }
+
+ RequestContext->Instance = Instance;
+ RequestContext->UserRequestEvent = ResolvedEvent;
+ RequestContext->UserHwAddrBuffer = TargetHwAddress;
+ InitializeListHead (&RequestContext->List);
+
+ //
+ // Check whether there is a same request.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->PendingRequestTable,
+ NULL,
+ ByProtoAddress,
+ &ProtocolAddress,
+ NULL
+ );
+ if (CacheEntry != NULL) {
+
+ CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut;
+ CacheEntry->RetryCount = Instance->ConfigData.RetryCount;
+ } else {
+ //
+ // Allocate a cache entry for this request.
+ //
+ CacheEntry = ArpAllocCacheEntry (Instance);
+ if (CacheEntry == NULL) {
+ DEBUG ((EFI_D_ERROR, "ArpRequest: Allocate memory for CacheEntry failed.\n"));
+ FreePool (RequestContext);
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Fill the software address.
+ //
+ ArpFillAddressInCacheEntry (CacheEntry, &HardwareAddress, &ProtocolAddress);
+
+ //
+ // Add this entry into the PendingRequestTable.
+ //
+ InsertTailList (&ArpService->PendingRequestTable, &CacheEntry->List);
+ }
+
+ //
+ // Link this request context into the cache entry.
+ //
+ InsertHeadList (&CacheEntry->UserRequestList, &RequestContext->List);
+
+ //
+ // Send out the ARP Request frame.
+ //
+ ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REQUEST);
+ Status = EFI_NOT_READY;
+
+UNLOCK_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+SIGNAL_USER:
+
+ if ((ResolvedEvent != NULL) && (Status == EFI_SUCCESS)) {
+ gBS->SignalEvent (ResolvedEvent);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of ResolvedEvent.
+ //
+ DispatchDpc ();
+ }
+
+ return Status;
+}
+
+
+/**
+ This function aborts the previous ARP request (identified by This, TargetSwAddress
+ and ResolvedEvent) that is issued by EFI_ARP_PROTOCOL.Request().
+
+ If the request is in the internal ARP request queue, the request is aborted
+ immediately and its ResolvedEvent is signaled. Only an asynchronous address
+ request needs to be canceled. If TargeSwAddress and ResolveEvent are both
+ NULL, all the pending asynchronous requests that have been issued by This
+ instance will be cancelled and their corresponding events will be signaled.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param TargetSwAddress Pointer to the protocol address in previous
+ request session.
+ @param ResolvedEvent Pointer to the event that is used as the
+ notification event in previous request session.
+
+ @retval EFI_SUCCESS The pending request session(s) is/are aborted and
+ corresponding event(s) is/are signaled.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. TargetSwAddress is not NULL and
+ ResolvedEvent is NULL. TargetSwAddress is NULL and
+ ResolvedEvent is not NULL.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+ @retval EFI_NOT_FOUND The request is not issued by
+ EFI_ARP_PROTOCOL.Request().
+
+**/
+EFI_STATUS
+EFIAPI
+ArpCancel (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ UINTN Count;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) ||
+ ((TargetSwAddress != NULL) && (ResolvedEvent == NULL)) ||
+ ((TargetSwAddress == NULL) && (ResolvedEvent != NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Cancel the specified request.
+ //
+ Count = ArpCancelRequest (Instance, TargetSwAddress, ResolvedEvent);
+
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the events signaled
+ // by ArpCancleRequest.
+ //
+ DispatchDpc ();
+
+ gBS->RestoreTPL (OldTpl);
+
+ return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c
new file mode 100644
index 0000000000..ee47fb1ef7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c
@@ -0,0 +1,225 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for ArpDxe driver.
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "ArpDriver.h"
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gArpComponentName = {
+ ArpComponentNameGetDriverName,
+ ArpComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gArpComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ArpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ArpComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mArpDriverNameTable[] = {
+ { "eng;en", L"ARP Network Service Driver" },
+ { NULL, NULL }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mArpControllerNameTable[] = {
+ { "eng;en", L"ARP Controller" },
+ { NULL, NULL }
+};
+
+/**
+ 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
+ArpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mArpDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gArpComponentName)
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+ArpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_ARP_PROTOCOL *Arp;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **)&Arp,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mArpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gArpComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..2ea07608f1
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c
@@ -0,0 +1,437 @@
+/** @file
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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
+DhcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+DhcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName = {
+ DhcpComponentNameGetDriverName,
+ DhcpComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DhcpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DhcpComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcpDriverNameTable[] = {
+ {
+ "eng;en",
+ L"DHCP Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gDhcpControllerNameTable = NULL;
+
+CHAR16 *mDhcp4ControllerName[] = {
+ L"DHCPv4 (State=0, Stopped)",
+ L"DHCPv4 (State=1, Init)",
+ L"DHCPv4 (State=2, Selecting)",
+ L"DHCPv4 (State=3, Requesting)",
+ L"DHCPv4 (State=4, Bound)",
+ L"DHCPv4 (State=5, Renewing)",
+ L"DHCPv4 (State=6, Rebinding)",
+ L"DHCPv4 (State=7, InitReboot)",
+ L"DHCPv4 (State=8, Rebooting)"
+};
+
+/**
+ 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
+DhcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDhcpDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gDhcp4ComponentName)
+ );
+}
+
+/**
+ Update the component name for the Dhcp4 child handle.
+
+ @param Dhcp4[in] A pointer to the EFI_DHCP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_DEVICE_ERROR DHCP is in unknown state.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_DHCP4_PROTOCOL *Dhcp4
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP4_MODE_DATA Dhcp4ModeData;
+
+ if (Dhcp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer.
+ //
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (gDhcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gDhcpControllerNameTable);
+ gDhcpControllerNameTable = NULL;
+ }
+
+ if (Dhcp4ModeData.State > Dhcp4Rebooting) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gDhcp4ComponentName.SupportedLanguages,
+ &gDhcpControllerNameTable,
+ mDhcp4ControllerName[Dhcp4ModeData.State],
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gDhcp4ComponentName2.SupportedLanguages,
+ &gDhcpControllerNameTable,
+ mDhcp4ControllerName[Dhcp4ModeData.State],
+ FALSE
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+DhcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **)&Dhcp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Dhcp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gDhcpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gDhcp4ComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c
new file mode 100644
index 0000000000..1f1e21c3cb
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c
@@ -0,0 +1,738 @@
+/** @file
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp4Impl.h"
+#include "Dhcp4Driver.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = {
+ Dhcp4DriverBindingSupported,
+ Dhcp4DriverBindingStart,
+ Dhcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplate = {
+ Dhcp4ServiceBindingCreateChild,
+ Dhcp4ServiceBindingDestroyChild
+};
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ Entry point of the DHCP driver to install various protocols.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDhcp4DriverBinding,
+ ImageHandle,
+ &gDhcp4ComponentName,
+ &gDhcp4ComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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
+Dhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Configure the default UDP child to receive all the DHCP traffics
+ on this network interface.
+
+ @param[in] UdpIo The UDP IO to configure
+ @param[in] Context The context to the function
+
+ @retval EFI_SUCCESS The UDP IO is successfully configured.
+ @retval Others Failed to configure the UDP child.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+
+ UdpConfigData.AcceptBroadcast = TRUE;
+ UdpConfigData.AcceptPromiscuous = FALSE;
+ UdpConfigData.AcceptAnyPort = FALSE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TypeOfService = 0;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = FALSE;
+ UdpConfigData.ReceiveTimeout = 0;
+ UdpConfigData.TransmitTimeout = 0;
+
+ UdpConfigData.UseDefaultAddress = FALSE;
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ UdpConfigData.RemotePort = DHCP_SERVER_PORT;
+
+ ZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);;
+}
+
+
+
+/**
+ Destroy the DHCP service. The Dhcp4 service may be partly initialized,
+ or partly destroyed. If a resource is destroyed, it is marked as so in
+ case the destroy failed and being called again later.
+
+ @param[in] DhcpSb The DHCP service instance to destroy.
+
+ @retval EFI_SUCCESS Always return success.
+
+**/
+EFI_STATUS
+Dhcp4CloseService (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpCleanLease (DhcpSb);
+
+ if (DhcpSb->UdpIo != NULL) {
+ UdpIoFreeIo (DhcpSb->UdpIo);
+ DhcpSb->UdpIo = NULL;
+ }
+
+ if (DhcpSb->Timer != NULL) {
+ gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (DhcpSb->Timer);
+
+ DhcpSb->Timer = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Create a new DHCP service binding instance for the controller.
+
+ @param[in] Controller The controller to install DHCP service binding
+ protocol onto
+ @param[in] ImageHandle The driver's image handle
+ @param[out] Service The variable to receive the created DHCP service
+ instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource .
+ @retval EFI_SUCCESS The DHCP service instance is created.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Dhcp4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT DHCP_SERVICE **Service
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ *Service = NULL;
+ DhcpSb = AllocateZeroPool (sizeof (DHCP_SERVICE));
+
+ if (DhcpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DhcpSb->Signature = DHCP_SERVICE_SIGNATURE;
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+ DhcpSb->Controller = Controller;
+ DhcpSb->Image = ImageHandle;
+ InitializeListHead (&DhcpSb->Children);
+ DhcpSb->DhcpState = Dhcp4Stopped;
+ DhcpSb->Xid = NET_RANDOM (NetRandomInitSeed ());
+ CopyMem (
+ &DhcpSb->ServiceBinding,
+ &mDhcp4ServiceBindingTemplate,
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)
+ );
+ //
+ // Create various resources, UdpIo, Timer, and get Mac address
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ DhcpOnTimerTick,
+ DhcpSb,
+ &DhcpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ DhcpSb->UdpIo = UdpIoCreateIo (
+ Controller,
+ ImageHandle,
+ DhcpConfigUdpIo,
+ UDP_IO_UDP4_VERSION,
+ NULL
+ );
+
+ if (DhcpSb->UdpIo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->HwLen = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;
+ DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;
+ CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (DhcpSb->Mac));
+
+ *Service = DhcpSb;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Dhcp4CloseService (DhcpSb);
+ FreePool (DhcpSb);
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ //
+ // First: test for the DHCP4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ASSERT (DhcpSb != NULL);
+
+ //
+ // Start the receiving
+ //
+ Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &DhcpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return Status;
+
+ON_ERROR:
+ Dhcp4CloseService (DhcpSb);
+ FreePool (DhcpSb);
+ return Status;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DestroyChildEntry (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP_PROTOCOL, Link, DHCP_PROTOCOL_SIGNATURE);
+ ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @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
+Dhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ DHCP_SERVICE *DhcpSb;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ LIST_ENTRY *List;
+ UINTN ListLength;
+
+ //
+ // DHCP driver opens UDP child, So, the ControllerHandle is the
+ // UDP child handle. locate the Nic handle first.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);
+ if (!IsListEmpty (&DhcpSb->Children)) {
+ //
+ // Destroy all the children instances before destory the service.
+ //
+ List = &DhcpSb->Children;
+ Status = NetDestroyLinkList (
+ List,
+ Dhcp4DestroyChildEntry,
+ ServiceBinding,
+ &ListLength
+ );
+ if (EFI_ERROR (Status) || ListLength != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ if (NumberOfChildren == 0 && !IsListEmpty (&DhcpSb->Children)) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ if (NumberOfChildren == 0 && IsListEmpty (&DhcpSb->Children)) {
+ //
+ // Destroy the service itself if no child instance left.
+ //
+ DhcpSb->ServiceState = DHCP_DESTROY;
+
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ Dhcp4CloseService (DhcpSb);
+
+ if (gDhcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gDhcpControllerNameTable);
+ gDhcpControllerNameTable = NULL;
+ }
+ FreePool (DhcpSb);
+
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Initialize a new DHCP instance.
+
+ @param DhcpSb The dhcp service instance
+ @param Instance The dhcp instance to initialize
+
+**/
+VOID
+DhcpInitProtocol (
+ IN DHCP_SERVICE *DhcpSb,
+ IN OUT DHCP_PROTOCOL *Instance
+ )
+{
+ Instance->Signature = DHCP_PROTOCOL_SIGNATURE;
+ CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (Instance->Dhcp4Protocol));
+ InitializeListHead (&Instance->Link);
+ Instance->Handle = NULL;
+ Instance->Service = DhcpSb;
+ Instance->InDestroy = FALSE;
+ Instance->CompletionEvent = NULL;
+ Instance->RenewRebindEvent = NULL;
+ Instance->Token = NULL;
+ Instance->UdpIo = NULL;
+ Instance->ElaspedTime = 0;
+ NetbufQueInit (&Instance->ResponseQueue);
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = AllocatePool (sizeof (*Instance));
+
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DhcpSb = DHCP_SERVICE_FROM_THIS (This);
+ DhcpInitProtocol (DhcpSb, Instance);
+
+ //
+ // Install DHCP4 onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ &Instance->Dhcp4Protocol,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Instance);
+ return Status;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp4 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ DhcpSb->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiDhcp4ProtocolGuid,
+ &Instance->Dhcp4Protocol,
+ NULL
+ );
+
+ FreePool (Instance);
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&DhcpSb->Children, &Instance->Link);
+ DhcpSb->NumChildren++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_DHCP4_PROTOCOL *Dhcp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Dhcp,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (Dhcp);
+ DhcpSb = DHCP_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != DhcpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // A child can be destroyed more than once. For example,
+ // Dhcp4DriverBindingStop will destroy all of its children.
+ // when caller driver is being stopped, it will destroy the
+ // dhcp child it opens.
+ //
+ if (Instance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->InDestroy = TRUE;
+
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ DhcpSb->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the DHCP4 protocol first to enable a top down destruction.
+ //
+ gBS->RestoreTPL (OldTpl);
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ Dhcp
+ );
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (EFI_ERROR (Status)) {
+ Instance->InDestroy = FALSE;
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ if (DhcpSb->ActiveChild == Instance) {
+ DhcpYieldControl (DhcpSb);
+ }
+
+ RemoveEntryList (&Instance->Link);
+ DhcpSb->NumChildren--;
+
+ if (Instance->UdpIo != NULL) {
+ UdpIoCleanIo (Instance->UdpIo);
+ gBS->CloseProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ Instance->Service->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ Instance->Token = NULL;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h
new file mode 100644
index 0000000000..a74cb64488
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h
@@ -0,0 +1,152 @@
+/** @file
+ Header for the DHCP4 driver.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP4_DRIVER_H__
+#define __EFI_DHCP4_DRIVER_H__
+
+extern EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp4ComponentName2;
+extern EFI_UNICODE_STRING_TABLE *gDhcpControllerNameTable;
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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
+Dhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @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
+Dhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
new file mode 100644
index 0000000000..9b6c9c1dcc
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
@@ -0,0 +1,72 @@
+## @file
+# This module produces EFI DHCPv4 Protocol and EFI DHCPv4 Service Binding Protocol.
+#
+# This module produces EFI DHCPv4 Protocol upon EFI UDPv4 Protocol, to provide the
+# capability to collect configuration information for the EFI IPv4 Protocol drivers
+# and to provide DHCPv4 server and PXE boot server discovery services.
+#
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Dhcp4Dxe
+ MODULE_UNI_FILE = Dhcp4Dxe.uni
+ FILE_GUID = 94734718-0BBC-47fb-96A5-EE7A5AE6A2AD
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Dhcp4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gDhcp4DriverBinding
+# COMPONENT_NAME = gDhcp4ComponentName
+# COMPONENT_NAME2 = gDhcp4ComponentName2
+#
+
+[Sources]
+ Dhcp4Impl.c
+ Dhcp4Io.c
+ Dhcp4Io.h
+ ComponentName.c
+ Dhcp4Driver.h
+ Dhcp4Driver.c
+ Dhcp4Option.c
+ Dhcp4Option.h
+ Dhcp4Impl.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiDhcp4ServiceBindingProtocolGuid ## BY_START
+ gEfiUdp4ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp4ProtocolGuid ## BY_START
+ gEfiUdp4ProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Dhcp4DxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.uni b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.uni
new file mode 100644
index 0000000000..1684cc592a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.uni
@@ -0,0 +1,24 @@
+// /** @file
+// This module produces EFI DHCPv4 Protocol and EFI DHCPv4 Service Binding Protocol.
+//
+// This module produces EFI DHCPv4 Protocol upon EFI UDPv4 Protocol, to provide the
+// capability to collect configuration information for the EFI IPv4 Protocol drivers
+// and to provide DHCPv4 server and PXE boot server discovery services.
+//
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "EFI DHCPv4 Driver"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI DHCPv4 Protocol using the EFI UDPv4 Protocol, providing the capability to collect configuration information for the EFI IPv4 Protocol drivers and providing DHCPv4 server and PXE boot server discovery services."
+
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni
new file mode 100644
index 0000000000..dca59ae452
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Dhcp4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"DHCP v4 DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
new file mode 100644
index 0000000000..1db4c667d7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
@@ -0,0 +1,1788 @@
+/** @file
+ This file implement the EFI_DHCP4_PROTOCOL interface.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+/**
+ Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
+
+ The GetModeData() function returns the current operating mode and cached data
+ packet for the EFI DHCPv4 Protocol driver.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4GetModeData (
+ IN EFI_DHCP4_PROTOCOL *This,
+ OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData
+ );
+
+/**
+ Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
+
+ The Configure() function is used to initialize, change, or reset the operational
+ settings of the EFI DHCPv4 Protocol driver for the communication device on which
+ the EFI DHCPv4 Service Binding Protocol is installed. This function can be
+ successfully called only if both of the following are true:
+ * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
+ Dhcp4InitReboot, or Dhcp4Bound states.
+ * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
+ DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
+ Protocol driver.
+ When this driver is in the Dhcp4Stopped state, it can transfer into one of the
+ following two possible initial states:
+ * Dhcp4Init
+ * Dhcp4InitReboot
+ The driver can transfer into these states by calling Configure() with a non-NULL
+ Dhcp4CfgData. The driver will transfer into the appropriate state based on the
+ supplied client network address in the ClientAddress parameter and DHCP options
+ in the OptionList parameter as described in RFC 2131.
+ When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
+ default configuring data will be reset in the EFI DHCPv4 Protocol driver and
+ the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
+ wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
+ driver, it must call this function with Dhcp4CfgData set to NULL.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Dhcp4CfgData Pointer to the EFI_DHCP4_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
+ Dhcp4InitReboot state, if the original state of this driver
+ was Dhcp4Stopped and the value of Dhcp4CfgData was
+ not NULL. Otherwise, the state was left unchanged.
+ @retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the
+ Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
+ Or onother instance of this EFI DHCPv4 Protocol driver is already
+ in a valid configured state.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Configure (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL
+ );
+
+/**
+ Starts the DHCP configuration process.
+
+ The Start() function starts the DHCP configuration process. This function can
+ be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
+ Dhcp4InitReboot state.
+ If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
+ driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
+ Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
+ If the process aborts, either by the user or by some unexpected network error,
+ the state is restored to the Dhcp4Init state. The Start() function can be called
+ again to restart the process.
+ Refer to RFC 2131 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
+ opportunity to control the process.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] CompletionEvent If not NULL, indicates the event that will be signaled when the
+ EFI DHCPv4 Protocol driver is transferred into the
+ Dhcp4Bound state or when the DHCP process is aborted.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.Start() will wait until the driver
+ is transferred into the Dhcp4Bound state or the process fails.
+
+ @retval EFI_SUCCESS The DHCP configuration process has started, or it has completed
+ when CompletionEvent is NULL.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCP configuration process failed because no response was
+ received from the server within the specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCP process.
+ @retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the
+ DHCP process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Start (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ );
+
+/**
+ Extends the lease time by sending a request packet.
+
+ The RenewRebind() function is used to manually extend the lease time when the
+ EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
+ not expired yet. This function will send a request packet to the previously
+ found server (or to any server when RebindRequest is TRUE) and transfer the
+ state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
+ TRUE). When a response is received, the state is returned to Dhcp4Bound.
+ If no response is received before the try count is exceeded (the RequestTryCount
+ field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
+ was issued by the previous server expires, the driver will return to the Dhcp4Bound
+ state and the previous configuration is restored. The outgoing and incoming packets
+ can be captured by the EFI_DHCP4_CALLBACK function.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] RebindRequest If TRUE, this function broadcasts the request packets and enters
+ the Dhcp4Rebinding state. Otherwise, it sends a unicast
+ request packet and enters the Dhcp4Renewing state.
+ @param[in] CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
+ completes or some error occurs.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
+ until the DHCP process finishes.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the
+ Dhcp4Renewing state or is back to the Dhcp4Bound state.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL.Configure() needs to
+ be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_TIMEOUT There was no response from the server when the try count was
+ exceeded.
+ @retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4RenewRebind (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN BOOLEAN RebindRequest,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ );
+
+/**
+ Releases the current address configuration.
+
+ The Release() function releases the current configured IP address by doing either
+ of the following:
+ * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
+ Dhcp4Bound state
+ * Setting the previously assigned IP address that was provided with the
+ EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
+ Dhcp4InitReboot state
+ After a successful call to this function, the EFI DHCPv4 Protocol driver returns
+ to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Release (
+ IN EFI_DHCP4_PROTOCOL *This
+ );
+
+/**
+ Stops the current address configuration.
+
+ The Stop() function is used to stop the DHCP configuration process. After this
+ function is called successfully, the EFI DHCPv4 Protocol driver is transferred
+ into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
+ before DHCP configuration process can be started again. This function can be
+ called when the EFI DHCPv4 Protocol driver is in any state.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Stop (
+ IN EFI_DHCP4_PROTOCOL *This
+ );
+
+/**
+ Builds a DHCP packet, given the options to be appended or deleted or replaced.
+
+ The Build() function is used to assemble a new packet from the original packet
+ by replacing or deleting existing options or appending new options. This function
+ does not change any state of the EFI DHCPv4 Protocol driver and can be used at
+ any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] SeedPacket Initial packet to be used as a base for building new packet.
+ @param[in] DeleteCount Number of opcodes in the DeleteList.
+ @param[in] DeleteList List of opcodes to be deleted from the seed packet.
+ Ignored if DeleteCount is zero.
+ @param[in] AppendCount Number of entries in the OptionList.
+ @param[in] AppendList Pointer to a DHCP option list to be appended to SeedPacket.
+ If SeedPacket also contains options in this list, they are
+ replaced by new options (except pad option). Ignored if
+ AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
+ @param[out] NewPacket Pointer to storage for the pointer to the new allocated packet.
+ Use the EFI Boot Service FreePool() on the resulting pointer
+ when done with the packet.
+
+ @retval EFI_SUCCESS The new packet was built.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Build (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ );
+
+/**
+ Transmits a DHCP formatted packet and optionally waits for responses.
+
+ The TransmitReceive() function is used to transmit a DHCP packet and optionally
+ wait for the response from servers. This function does not change the state of
+ the EFI DHCPv4 Protocol driver and thus can be used at any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
+
+ @retval EFI_SUCCESS The packet was successfully queued for transmission.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call
+ this function after collection process completes.
+ @retval EFI_NO_MAPPING The default station address is not available yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Some other unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4TransmitReceive (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token
+ );
+
+/**
+ Parses the packed DHCP option data.
+
+ The Parse() function is used to retrieve the option list from a DHCP packet.
+ If *OptionCount isn't zero, and there is enough space for all the DHCP options
+ in the Packet, each element of PacketOptionList is set to point to somewhere in
+ the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
+ the caller should reassemble the parsed DHCP options to get the finial result.
+ If *OptionCount is zero or there isn't enough space for all of them, the number
+ of DHCP options in the Packet is returned in OptionCount.
+
+ @param This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param Packet Pointer to packet to be parsed.
+ @param OptionCount On input, the number of entries in the PacketOptionList.
+ On output, the number of entries that were written into the
+ PacketOptionList.
+ @param PacketOptionList List of packet option entries to be filled in. End option or pad
+ options are not included.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE:
+ 1) *OptionCount is smaller than the number of options that
+ were found in the Packet.
+ 2) PacketOptionList is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Parse (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ );
+
+EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate = {
+ EfiDhcp4GetModeData,
+ EfiDhcp4Configure,
+ EfiDhcp4Start,
+ EfiDhcp4RenewRebind,
+ EfiDhcp4Release,
+ EfiDhcp4Stop,
+ EfiDhcp4Build,
+ EfiDhcp4TransmitReceive,
+ EfiDhcp4Parse
+};
+
+/**
+ Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
+
+ The GetModeData() function returns the current operating mode and cached data
+ packet for the EFI DHCPv4 Protocol driver.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4GetModeData (
+ IN EFI_DHCP4_PROTOCOL *This,
+ OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PARAMETER *Para;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+
+ //
+ // First validate the parameters.
+ //
+ if ((This == NULL) || (Dhcp4ModeData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ //
+ // Caller can use GetModeData to retrieve current DHCP states
+ // no matter whether it is the active child or not.
+ //
+ Dhcp4ModeData->State = (EFI_DHCP4_STATE) DhcpSb->DhcpState;
+ CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (Dhcp4ModeData->ConfigData));
+ CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (Dhcp4ModeData->ClientMacAddress));
+
+ Ip = HTONL (DhcpSb->ClientAddr);
+ CopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Netmask);
+ CopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->ServerAddr);
+ CopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Para = DhcpSb->Para;
+
+ if (Para != NULL) {
+ Ip = HTONL (Para->Router);
+ CopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+ Dhcp4ModeData->LeaseTime = Para->Lease;
+ } else {
+ ZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+ Dhcp4ModeData->LeaseTime = 0xffffffff;
+ }
+
+ Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Free the resource related to the configure parameters.
+ DHCP driver will make a copy of the user's configure
+ such as the time out value.
+
+ @param Config The DHCP configure data
+
+**/
+VOID
+DhcpCleanConfigure (
+ IN OUT EFI_DHCP4_CONFIG_DATA *Config
+ )
+{
+ UINT32 Index;
+
+ if (Config->DiscoverTimeout != NULL) {
+ FreePool (Config->DiscoverTimeout);
+ }
+
+ if (Config->RequestTimeout != NULL) {
+ FreePool (Config->RequestTimeout);
+ }
+
+ if (Config->OptionList != NULL) {
+ for (Index = 0; Index < Config->OptionCount; Index++) {
+ if (Config->OptionList[Index] != NULL) {
+ FreePool (Config->OptionList[Index]);
+ }
+ }
+
+ FreePool (Config->OptionList);
+ }
+
+ ZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+}
+
+
+/**
+ Allocate memory for configure parameter such as timeout value for Dst,
+ then copy the configure parameter from Src to Dst.
+
+ @param[out] Dst The destination DHCP configure data.
+ @param[in] Src The source DHCP configure data.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_SUCCESS The configure is copied.
+
+**/
+EFI_STATUS
+DhcpCopyConfigure (
+ OUT EFI_DHCP4_CONFIG_DATA *Dst,
+ IN EFI_DHCP4_CONFIG_DATA *Src
+ )
+{
+ EFI_DHCP4_PACKET_OPTION **DstOptions;
+ EFI_DHCP4_PACKET_OPTION **SrcOptions;
+ UINTN Len;
+ UINT32 Index;
+
+ CopyMem (Dst, Src, sizeof (*Dst));
+ Dst->DiscoverTimeout = NULL;
+ Dst->RequestTimeout = NULL;
+ Dst->OptionList = NULL;
+
+ //
+ // Allocate a memory then copy DiscoverTimeout to it
+ //
+ if (Src->DiscoverTimeout != NULL) {
+ Len = Src->DiscoverTryCount * sizeof (UINT32);
+ Dst->DiscoverTimeout = AllocatePool (Len);
+
+ if (Dst->DiscoverTimeout == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < Src->DiscoverTryCount; Index++) {
+ Dst->DiscoverTimeout[Index] = MAX (Src->DiscoverTimeout[Index], 1);
+ }
+ }
+
+ //
+ // Allocate a memory then copy RequestTimeout to it
+ //
+ if (Src->RequestTimeout != NULL) {
+ Len = Src->RequestTryCount * sizeof (UINT32);
+ Dst->RequestTimeout = AllocatePool (Len);
+
+ if (Dst->RequestTimeout == NULL) {
+ goto ON_ERROR;
+ }
+
+ for (Index = 0; Index < Src->RequestTryCount; Index++) {
+ Dst->RequestTimeout[Index] = MAX (Src->RequestTimeout[Index], 1);
+ }
+ }
+
+ //
+ // Allocate an array of dhcp option point, then allocate memory
+ // for each option and copy the source option to it
+ //
+ if (Src->OptionList != NULL) {
+ Len = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);
+ Dst->OptionList = AllocateZeroPool (Len);
+
+ if (Dst->OptionList == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstOptions = Dst->OptionList;
+ SrcOptions = Src->OptionList;
+
+ for (Index = 0; Index < Src->OptionCount; Index++) {
+ Len = sizeof (EFI_DHCP4_PACKET_OPTION) + MAX (SrcOptions[Index]->Length - 1, 0);
+
+ DstOptions[Index] = AllocatePool (Len);
+
+ if (DstOptions[Index] == NULL) {
+ goto ON_ERROR;
+ }
+
+ CopyMem (DstOptions[Index], SrcOptions[Index], Len);
+ }
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ DhcpCleanConfigure (Dst);
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+/**
+ Give up the control of the DHCP service to let other child
+ resume. Don't change the service's DHCP state and the Client
+ address and option list configure as required by RFC2131.
+
+ @param DhcpSb The DHCP service instance.
+
+**/
+VOID
+DhcpYieldControl (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+
+ Config = &DhcpSb->ActiveConfig;
+
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+ DhcpSb->ActiveChild = NULL;
+
+ if (Config->DiscoverTimeout != NULL) {
+ FreePool (Config->DiscoverTimeout);
+
+ Config->DiscoverTryCount = 0;
+ Config->DiscoverTimeout = NULL;
+ }
+
+ if (Config->RequestTimeout != NULL) {
+ FreePool (Config->RequestTimeout);
+
+ Config->RequestTryCount = 0;
+ Config->RequestTimeout = NULL;
+ }
+
+ Config->Dhcp4Callback = NULL;
+ Config->CallbackContext = NULL;
+}
+
+
+/**
+ Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
+
+ The Configure() function is used to initialize, change, or reset the operational
+ settings of the EFI DHCPv4 Protocol driver for the communication device on which
+ the EFI DHCPv4 Service Binding Protocol is installed. This function can be
+ successfully called only if both of the following are true:
+ * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
+ Dhcp4InitReboot, or Dhcp4Bound states.
+ * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
+ DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
+ Protocol driver.
+ When this driver is in the Dhcp4Stopped state, it can transfer into one of the
+ following two possible initial states:
+ * Dhcp4Init
+ * Dhcp4InitReboot
+ The driver can transfer into these states by calling Configure() with a non-NULL
+ Dhcp4CfgData. The driver will transfer into the appropriate state based on the
+ supplied client network address in the ClientAddress parameter and DHCP options
+ in the OptionList parameter as described in RFC 2131.
+ When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
+ default configuring data will be reset in the EFI DHCPv4 Protocol driver and
+ the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
+ wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
+ driver, it must call this function with Dhcp4CfgData set to NULL.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Dhcp4CfgData Pointer to the EFI_DHCP4_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
+ Dhcp4InitReboot state, if the original state of this driver
+ was Dhcp4Stopped and the value of Dhcp4CfgData was
+ not NULL. Otherwise, the state was left unchanged.
+ @retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the
+ Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
+ Or onother instance of this EFI DHCPv4 Protocol driver is already
+ in a valid configured state.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Configure (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ UINT32 Index;
+ IP4_ADDR Ip;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp4CfgData != NULL) {
+ if ((Dhcp4CfgData->DiscoverTryCount != 0) && (Dhcp4CfgData->DiscoverTimeout == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Dhcp4CfgData->RequestTryCount != 0) && (Dhcp4CfgData->RequestTimeout == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Dhcp4CfgData->OptionCount != 0) && (Dhcp4CfgData->OptionList == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));
+ if (IP4_IS_LOCAL_BROADCAST(NTOHL (Ip))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ DhcpSb = Instance->Service;
+ Config = &DhcpSb->ActiveConfig;
+
+ Status = EFI_ACCESS_DENIED;
+
+ if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
+ (DhcpSb->DhcpState != Dhcp4Init) &&
+ (DhcpSb->DhcpState != Dhcp4InitReboot) &&
+ (DhcpSb->DhcpState != Dhcp4Bound)) {
+
+ goto ON_EXIT;
+ }
+
+ if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
+ goto ON_EXIT;
+ }
+
+ if (Dhcp4CfgData != NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DhcpCleanConfigure (Config);
+
+ if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
+ goto ON_EXIT;
+ }
+
+ DhcpSb->UserOptionLen = 0;
+
+ for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
+ DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
+ }
+
+ DhcpSb->ActiveChild = Instance;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
+
+ if (DhcpSb->ClientAddr != 0) {
+ DhcpSb->DhcpState = Dhcp4InitReboot;
+ } else {
+ DhcpSb->DhcpState = Dhcp4Init;
+ }
+ }
+
+ DhcpSb->ServiceState = DHCP_CONFIGED;
+ Status = EFI_SUCCESS;
+
+ } else if (DhcpSb->ActiveChild == Instance) {
+ Status = EFI_SUCCESS;
+ DhcpYieldControl (DhcpSb);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Starts the DHCP configuration process.
+
+ The Start() function starts the DHCP configuration process. This function can
+ be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
+ Dhcp4InitReboot state.
+ If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
+ driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
+ Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
+ If the process aborts, either by the user or by some unexpected network error,
+ the state is restored to the Dhcp4Init state. The Start() function can be called
+ again to restart the process.
+ Refer to RFC 2131 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
+ opportunity to control the process.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] CompletionEvent If not NULL, indicates the event that will be signaled when the
+ EFI DHCPv4 Protocol driver is transferred into the
+ Dhcp4Bound state or when the DHCP process is aborted.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.Start() will wait until the driver
+ is transferred into the Dhcp4Bound state or the process fails.
+
+ @retval EFI_SUCCESS The DHCP configuration process has started, or it has completed
+ when CompletionEvent is NULL.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCP configuration process failed because no response was
+ received from the server within the specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCP process.
+ @retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the
+ DHCP process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Start (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ Status = EFI_NOT_STARTED;
+ goto ON_ERROR;
+ }
+
+ if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->IoStatus = EFI_ALREADY_STARTED;
+
+ if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
+ goto ON_ERROR;
+ }
+
+
+ Instance->CompletionEvent = CompletionEvent;
+
+ //
+ // Restore the TPL now, don't call poll function at TPL_CALLBACK.
+ //
+ gBS->RestoreTPL (OldTpl);
+
+ if (CompletionEvent == NULL) {
+ while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
+ DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
+ }
+
+ return DhcpSb->IoStatus;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Extends the lease time by sending a request packet.
+
+ The RenewRebind() function is used to manually extend the lease time when the
+ EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
+ not expired yet. This function will send a request packet to the previously
+ found server (or to any server when RebindRequest is TRUE) and transfer the
+ state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
+ TRUE). When a response is received, the state is returned to Dhcp4Bound.
+ If no response is received before the try count is exceeded (the RequestTryCount
+ field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
+ was issued by the previous server expires, the driver will return to the Dhcp4Bound
+ state and the previous configuration is restored. The outgoing and incoming packets
+ can be captured by the EFI_DHCP4_CALLBACK function.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] RebindRequest If TRUE, this function broadcasts the request packets and enters
+ the Dhcp4Rebinding state. Otherwise, it sends a unicast
+ request packet and enters the Dhcp4Renewing state.
+ @param[in] CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
+ completes or some error occurs.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
+ until the DHCP process finishes.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the
+ Dhcp4Renewing state or is back to the Dhcp4Bound state.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL.Configure() needs to
+ be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_TIMEOUT There was no response from the server when the try count was
+ exceeded.
+ @retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4RenewRebind (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN BOOLEAN RebindRequest,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (DhcpSb->DhcpState != Dhcp4Bound) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ if (DHCP_IS_BOOTP (DhcpSb->Para)) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Transit the states then send a extra DHCP request
+ //
+ if (!RebindRequest) {
+ DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
+ } else {
+ DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
+ }
+
+ //
+ // Clear initial time to make sure that elapsed-time
+ // is set to 0 for first REQUEST in renewal process.
+ //
+ Instance->ElaspedTime = 0;
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ (UINT8 *) "Extra renew/rebind by the application"
+ );
+
+ if (EFI_ERROR (Status)) {
+ DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
+ goto ON_EXIT;
+ }
+
+ DhcpSb->ExtraRefresh = TRUE;
+ DhcpSb->IoStatus = EFI_ALREADY_STARTED;
+ Instance->RenewRebindEvent = CompletionEvent;
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (CompletionEvent == NULL) {
+ while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
+ DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
+
+ }
+
+ return DhcpSb->IoStatus;
+ }
+
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Releases the current address configuration.
+
+ The Release() function releases the current configured IP address by doing either
+ of the following:
+ * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
+ Dhcp4Bound state
+ * Setting the previously assigned IP address that was provided with the
+ EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
+ Dhcp4InitReboot state
+ After a successful call to this function, the EFI DHCPv4 Protocol driver returns
+ to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Release (
+ IN EFI_DHCP4_PROTOCOL *This
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_RELEASE,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ }
+
+ DhcpCleanLease (DhcpSb);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Stops the current address configuration.
+
+ The Stop() function is used to stop the DHCP configuration process. After this
+ function is called successfully, the EFI DHCPv4 Protocol driver is transferred
+ into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
+ before DHCP configuration process can be started again. This function can be
+ called when the EFI DHCPv4 Protocol driver is in any state.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Stop (
+ IN EFI_DHCP4_PROTOCOL *This
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ DhcpCleanLease (DhcpSb);
+
+ DhcpSb->DhcpState = Dhcp4Stopped;
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Builds a DHCP packet, given the options to be appended or deleted or replaced.
+
+ The Build() function is used to assemble a new packet from the original packet
+ by replacing or deleting existing options or appending new options. This function
+ does not change any state of the EFI DHCPv4 Protocol driver and can be used at
+ any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] SeedPacket Initial packet to be used as a base for building new packet.
+ @param[in] DeleteCount Number of opcodes in the DeleteList.
+ @param[in] DeleteList List of opcodes to be deleted from the seed packet.
+ Ignored if DeleteCount is zero.
+ @param[in] AppendCount Number of entries in the OptionList.
+ @param[in] AppendList Pointer to a DHCP option list to be appended to SeedPacket.
+ If SeedPacket also contains options in this list, they are
+ replaced by new options (except pad option). Ignored if
+ AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
+ @param[out] NewPacket Pointer to storage for the pointer to the new allocated packet.
+ Use the EFI Boot Service FreePool() on the resulting pointer
+ when done with the packet.
+
+ @retval EFI_SUCCESS The new packet was built.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Build (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ )
+{
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (NewPacket == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((DeleteCount == 0) && (AppendCount == 0)) ||
+ ((DeleteCount != 0) && (DeleteList == NULL)) ||
+ ((AppendCount != 0) && (AppendList == NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return DhcpBuild (
+ SeedPacket,
+ DeleteCount,
+ DeleteList,
+ AppendCount,
+ AppendList,
+ NewPacket
+ );
+}
+
+/**
+ Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance.
+
+ @param[in] UdpIo The UdpIo being created.
+ @param[in] Context Dhcp4 instance.
+
+ @retval EFI_SUCCESS UdpIo is configured successfully.
+ @retval other Other error occurs.
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4InstanceConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+ IP4_ADDR ClientAddr;
+ IP4_ADDR Ip;
+ INTN Class;
+ IP4_ADDR SubnetMask;
+
+ Instance = (DHCP_PROTOCOL *) Context;
+ DhcpSb = Instance->Service;
+ Token = Instance->Token;
+
+ ZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA));
+
+ UdpConfigData.AcceptBroadcast = TRUE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = TRUE;
+
+ ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
+ Ip = HTONL (ClientAddr);
+ CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ if (DhcpSb->Netmask == 0) {
+ //
+ // The Dhcp4.TransmitReceive() API should be able to used at any time according to
+ // UEFI spec, while in classless addressing network, the netmask must be explicitly
+ // provided together with the station address.
+ // If the DHCP instance haven't be configured with a valid netmask, we could only
+ // compute it according to the classful addressing rule.
+ //
+ Class = NetGetIpClass (ClientAddr);
+ ASSERT (Class < IP4_ADDR_CLASSE);
+ SubnetMask = gIp4AllMasks[Class << 3];
+ } else {
+ SubnetMask = DhcpSb->Netmask;
+ }
+
+ Ip = HTONL (SubnetMask);
+ CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ } else {
+ UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
+ }
+
+ return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
+}
+
+/**
+ Create UdpIo for this Dhcp4 instance.
+
+ @param Instance The Dhcp4 instance.
+
+ @retval EFI_SUCCESS UdpIo is created successfully.
+ @retval EFI_OUT_OF_RESOURCES Fails to create UdpIo because of limited
+ resources or configuration failure.
+**/
+EFI_STATUS
+Dhcp4InstanceCreateUdpIo (
+ IN OUT DHCP_PROTOCOL *Instance
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ VOID *Udp4;
+
+ ASSERT (Instance->Token != NULL);
+
+ DhcpSb = Instance->Service;
+ Instance->UdpIo = UdpIoCreateIo (
+ DhcpSb->Controller,
+ DhcpSb->Image,
+ Dhcp4InstanceConfigUdpIo,
+ UDP_IO_UDP4_VERSION,
+ Instance
+ );
+ if (Instance->UdpIo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ } else {
+ Status = gBS->OpenProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ Instance->Service->Image,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ }
+ return Status;
+ }
+}
+
+/**
+ Callback of Dhcp packet. Does nothing.
+
+ @param Arg The context.
+
+**/
+VOID
+EFIAPI
+DhcpDummyExtFree (
+ IN VOID *Arg
+ )
+{
+}
+
+/**
+ Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.
+
+ Only BOOTP responses will be handled that correspond to the Xid of the request
+ sent out. The packet will be queued to the response queue.
+
+ @param UdpPacket The Dhcp4 packet.
+ @param EndPoint Udp4 address pair.
+ @param IoStatus Status of the input.
+ @param Context Extra info for the input.
+
+**/
+VOID
+EFIAPI
+PxeDhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ EFI_DHCP4_HEADER *Head;
+ NET_BUF *Wrap;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+ UINT32 Len;
+ EFI_STATUS Status;
+
+ Wrap = NULL;
+ Instance = (DHCP_PROTOCOL *) Context;
+ Token = Instance->Token;
+
+ //
+ // Don't restart receive if error occurs or DHCP is destroyed.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ //
+ // Validate the packet received
+ //
+ if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
+ goto RESTART;
+ }
+
+ //
+ // Copy the DHCP message to a continuous memory block, make the buffer size
+ // of the EFI_DHCP4_PACKET a multiple of 4-byte.
+ //
+ Len = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
+ Wrap = NetbufAlloc (Len);
+ if (Wrap == NULL) {
+ goto RESTART;
+ }
+
+ Packet = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
+ ASSERT (Packet != NULL);
+
+ Packet->Size = Len;
+ Head = &Packet->Dhcp4.Header;
+ Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length != UdpPacket->TotalSize) {
+ goto RESTART;
+ }
+
+ //
+ // Is this packet the answer to our packet?
+ //
+ if ((Head->OpCode != BOOTP_REPLY) ||
+ (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
+ (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the options and retrieve the interested options
+ //
+ if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
+ (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
+ EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
+
+ goto RESTART;
+ }
+
+ //
+ // Keep this packet in the ResponseQueue.
+ //
+ NET_GET_REF (Wrap);
+ NetbufQueAppend (&Instance->ResponseQueue, Wrap);
+
+RESTART:
+
+ NetbufFree (UdpPacket);
+
+ if (Wrap != NULL) {
+ NetbufFree (Wrap);
+ }
+
+ Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
+ if (EFI_ERROR (Status)) {
+ PxeDhcpDone (Instance);
+ }
+}
+
+/**
+ Complete a Dhcp4 transaction and signal the upper layer.
+
+ @param Instance Dhcp4 instance.
+
+**/
+VOID
+PxeDhcpDone (
+ IN DHCP_PROTOCOL *Instance
+ )
+{
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+
+ Token = Instance->Token;
+
+ Token->ResponseCount = Instance->ResponseQueue.BufNum;
+ if (Token->ResponseCount != 0) {
+ Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
+ if (Token->ResponseList == NULL) {
+ Token->Status = EFI_OUT_OF_RESOURCES;
+ goto SIGNAL_USER;
+ }
+
+ //
+ // Copy the received DHCP responses.
+ //
+ NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
+ Token->Status = EFI_SUCCESS;
+ } else {
+ Token->ResponseList = NULL;
+ Token->Status = EFI_TIMEOUT;
+ }
+
+SIGNAL_USER:
+ //
+ // Clean up the resources dedicated for this transmit receive transaction.
+ //
+ NetbufQueFlush (&Instance->ResponseQueue);
+ UdpIoCleanIo (Instance->UdpIo);
+ gBS->CloseProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ Instance->Service->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ Instance->Token = NULL;
+
+ if (Token->CompletionEvent != NULL) {
+ gBS->SignalEvent (Token->CompletionEvent);
+ }
+}
+
+
+/**
+ Transmits a DHCP formatted packet and optionally waits for responses.
+
+ The TransmitReceive() function is used to transmit a DHCP packet and optionally
+ wait for the response from servers. This function does not change the state of
+ the EFI DHCPv4 Protocol driver and thus can be used at any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
+
+ @retval EFI_SUCCESS The packet was successfully queued for transmission.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call
+ this function after collection process completes.
+ @retval EFI_NO_MAPPING The default station address is not available yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Some other unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4TransmitReceive (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ NET_FRAGMENT Frag;
+ NET_BUF *Wrap;
+ UDP_END_POINT EndPoint;
+ IP4_ADDR Ip;
+ DHCP_SERVICE *DhcpSb;
+ EFI_IP_ADDRESS Gateway;
+ IP4_ADDR ClientAddr;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+ DhcpSb = Instance->Service;
+
+ if (Instance->Token != NULL) {
+ //
+ // The previous call to TransmitReceive is not finished.
+ //
+ return EFI_NOT_READY;
+ }
+
+ if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
+ (Token->TimeoutValue == 0) ||
+ ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL)) ||
+ EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL)) ||
+ EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)
+ ) {
+ //
+ // The DHCP packet isn't well-formed, the Transaction ID is already used,
+ // the timeout value is zero, the ListenPoint is invalid, or the
+ // RemoteAddress is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
+
+ if (ClientAddr == 0) {
+ return EFI_NO_MAPPING;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Save the token and the timeout value.
+ //
+ Instance->Token = Token;
+ Instance->Timeout = Token->TimeoutValue;
+
+ //
+ // Create a UDP IO for this transmit receive transaction.
+ //
+ Status = Dhcp4InstanceCreateUdpIo (Instance);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Save the Client Address is sent out
+ //
+ CopyMem (
+ &DhcpSb->ClientAddressSendOut[0],
+ &Token->Packet->Dhcp4.Header.ClientHwAddr[0],
+ Token->Packet->Dhcp4.Header.HwAddrLen
+ );
+
+ //
+ // Wrap the DHCP packet into a net buffer.
+ //
+ Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
+ Frag.Len = Token->Packet->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
+ if (Wrap == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Set the local address and local port to ZERO.
+ //
+ ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
+
+ //
+ // Set the destination address and destination port.
+ //
+ CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+ EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip);
+
+ if (Token->RemotePort == 0) {
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ } else {
+ EndPoint.RemotePort = Token->RemotePort;
+ }
+
+ //
+ // Get the gateway.
+ //
+ ZeroMem (&Gateway, sizeof (Gateway));
+ if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], DhcpSb->Netmask)) {
+ CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ Gateway.Addr[0] = NTOHL (Gateway.Addr[0]);
+ }
+
+ //
+ // Transmit the DHCP packet.
+ //
+ Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL);
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ goto ON_ERROR;
+ }
+
+ //
+ // Start to receive the DHCP response.
+ //
+ Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ON_ERROR:
+
+ if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
+ UdpIoCleanIo (Instance->UdpIo);
+ gBS->CloseProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ Instance->Service->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ Instance->Token = NULL;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
+ //
+ // Keep polling until timeout if no error happens and the CompletionEvent
+ // is NULL.
+ //
+ while (TRUE) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ //
+ // Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick
+ // free it when timeout.
+ //
+ if (Instance->Timeout > 0) {
+ Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4);
+ gBS->RestoreTPL (OldTpl);
+ } else {
+ gBS->RestoreTPL (OldTpl);
+ break;
+ }
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Callback function for DhcpIterateOptions. This callback sets the
+ EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
+ the individual DHCP option in the packet.
+
+ @param[in] Tag The DHCP option type
+ @param[in] Len Length of the DHCP option data
+ @param[in] Data The DHCP option data
+ @param[in] Context The context, to pass several parameters in.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS
+
+**/
+EFI_STATUS
+Dhcp4ParseCheckOption (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_PARSE_CONTEXT *Parse;
+
+ Parse = (DHCP_PARSE_CONTEXT *) Context;
+ Parse->Index++;
+
+ if (Parse->Index <= Parse->OptionCount) {
+ //
+ // Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
+ // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
+ // pass in the point to option data.
+ //
+ Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parses the packed DHCP option data.
+
+ The Parse() function is used to retrieve the option list from a DHCP packet.
+ If *OptionCount isn't zero, and there is enough space for all the DHCP options
+ in the Packet, each element of PacketOptionList is set to point to somewhere in
+ the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
+ the caller should reassemble the parsed DHCP options to get the finial result.
+ If *OptionCount is zero or there isn't enough space for all of them, the number
+ of DHCP options in the Packet is returned in OptionCount.
+
+ @param This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param Packet Pointer to packet to be parsed.
+ @param OptionCount On input, the number of entries in the PacketOptionList.
+ On output, the number of entries that were written into the
+ PacketOptionList.
+ @param PacketOptionList List of packet option entries to be filled in. End option or pad
+ options are not included.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE:
+ 1) *OptionCount is smaller than the number of options that
+ were found in the Packet.
+ 2) PacketOptionList is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Parse (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ )
+{
+ DHCP_PARSE_CONTEXT Context;
+ EFI_STATUS Status;
+
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
+ (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+
+ Context.Option = PacketOptionList;
+ Context.OptionCount = *OptionCount;
+ Context.Index = 0;
+
+ Status = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *OptionCount = Context.Index;
+
+ if (Context.Index > Context.OptionCount) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp4 instance.
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP_PROTOCOL *Instance
+ )
+{
+ WriteUnaligned16 (Elapsed, HTONS(Instance->ElaspedTime));
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h
new file mode 100644
index 0000000000..e546a08f92
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h
@@ -0,0 +1,199 @@
+/** @file
+ EFI DHCP protocol implementation.
+ RFCs supported are:
+ RFC 2131: Dynamic Host Configuration Protocol
+ RFC 2132: DHCP Options and BOOTP Vendor Extensions
+ RFC 1534: Interoperation Between DHCP and BOOTP
+ RFC 3396: Encoding Long Options in DHCP.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP4_IMPL_H__
+#define __EFI_DHCP4_IMPL_H__
+
+
+
+#include <Uefi.h>
+
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Udp4.h>
+#include <IndustryStandard/Dhcp.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+typedef struct _DHCP_SERVICE DHCP_SERVICE;
+typedef struct _DHCP_PROTOCOL DHCP_PROTOCOL;
+
+#include "Dhcp4Option.h"
+#include "Dhcp4Io.h"
+
+#define DHCP_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', 'C', 'P')
+#define DHCP_PROTOCOL_SIGNATURE SIGNATURE_32 ('d', 'h', 'c', 'p')
+
+
+//
+// The state of the DHCP service. It starts as UNCONFIGED. If
+// and active child configures the service successfully, it
+// goes to CONFIGED. If the active child configures NULL, it
+// goes back to UNCONFIGED. It becomes DESTROY if it is (partly)
+// destroyed.
+//
+#define DHCP_UNCONFIGED 0
+#define DHCP_CONFIGED 1
+#define DHCP_DESTROY 2
+
+
+struct _DHCP_PROTOCOL {
+ UINT32 Signature;
+ EFI_DHCP4_PROTOCOL Dhcp4Protocol;
+ LIST_ENTRY Link;
+ EFI_HANDLE Handle;
+ DHCP_SERVICE *Service;
+
+ BOOLEAN InDestroy;
+
+ EFI_EVENT CompletionEvent;
+ EFI_EVENT RenewRebindEvent;
+
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+ UDP_IO *UdpIo; // The UDP IO used for TransmitReceive.
+ UINT32 Timeout;
+ UINT16 ElaspedTime;
+ NET_BUF_QUEUE ResponseQueue;
+};
+
+//
+// DHCP driver is specical in that it is a singleton. Although it
+// has a service binding, there can be only one active child.
+//
+struct _DHCP_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ INTN ServiceState; // CONFIGED, UNCONFIGED, and DESTROY
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ LIST_ENTRY Children;
+ UINTN NumChildren;
+
+ INTN DhcpState;
+ EFI_STATUS IoStatus; // the result of last user operation
+ UINT32 Xid;
+
+ IP4_ADDR ClientAddr; // lease IP or configured client address
+ IP4_ADDR Netmask;
+ IP4_ADDR ServerAddr;
+
+ EFI_DHCP4_PACKET *LastOffer; // The last received offer
+ EFI_DHCP4_PACKET *Selected;
+ DHCP_PARAMETER *Para;
+
+ UINT32 Lease;
+ UINT32 T1;
+ UINT32 T2;
+ INTN ExtraRefresh; // This refresh is reqested by user
+
+ UDP_IO *UdpIo; // Udp child receiving all DHCP message
+ UDP_IO *LeaseIoPort; // Udp child with lease IP
+ EFI_DHCP4_PACKET *LastPacket; // The last sent packet for retransmission
+ EFI_MAC_ADDRESS Mac;
+ UINT8 HwType;
+ UINT8 HwLen;
+ UINT8 ClientAddressSendOut[16];
+
+ DHCP_PROTOCOL *ActiveChild;
+ EFI_DHCP4_CONFIG_DATA ActiveConfig;
+ UINT32 UserOptionLen;
+
+ //
+ // Timer event and various timer
+ //
+ EFI_EVENT Timer;
+
+ UINT32 PacketToLive; // Retransmission timer for our packets
+ UINT32 LastTimeout; // Record the init value of PacketToLive every time
+ INTN CurRetry;
+ INTN MaxRetries;
+ UINT32 LeaseLife;
+};
+
+typedef struct {
+ EFI_DHCP4_PACKET_OPTION **Option;
+ UINT32 OptionCount;
+ UINT32 Index;
+} DHCP_PARSE_CONTEXT;
+
+#define DHCP_INSTANCE_FROM_THIS(Proto) \
+ CR ((Proto), DHCP_PROTOCOL, Dhcp4Protocol, DHCP_PROTOCOL_SIGNATURE)
+
+#define DHCP_SERVICE_FROM_THIS(Sb) \
+ CR ((Sb), DHCP_SERVICE, ServiceBinding, DHCP_SERVICE_SIGNATURE)
+
+extern EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate;
+
+/**
+ Give up the control of the DHCP service to let other child
+ resume. Don't change the service's DHCP state and the Client
+ address and option list configure as required by RFC2131.
+
+ @param DhcpSb The DHCP service instance.
+
+**/
+VOID
+DhcpYieldControl (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+/**
+ Complete a Dhcp4 transaction and signal the upper layer.
+
+ @param Instance Dhcp4 instance.
+
+**/
+VOID
+PxeDhcpDone (
+ IN DHCP_PROTOCOL *Instance
+ );
+
+/**
+ Free the resource related to the configure parameters.
+ DHCP driver will make a copy of the user's configure
+ such as the time out value.
+
+ @param Config The DHCP configure data
+
+**/
+VOID
+DhcpCleanConfigure (
+ IN OUT EFI_DHCP4_CONFIG_DATA *Config
+ );
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp4 instance.
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP_PROTOCOL *Instance
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
new file mode 100644
index 0000000000..3898223af5
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
@@ -0,0 +1,1686 @@
+/** @file
+ EFI DHCP protocol implementation.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };
+
+
+/**
+ Send an initial DISCOVER or REQUEST message according to the
+ DHCP service's current state.
+
+ @param[in] DhcpSb The DHCP service instance
+
+ @retval EFI_SUCCESS The request has been sent
+ @retval other Some error occurs when sending the request.
+
+**/
+EFI_STATUS
+DhcpInitRequest (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));
+
+ //
+ // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
+ //
+ DhcpSb->ActiveChild->ElaspedTime= 0;
+
+ if (DhcpSb->DhcpState == Dhcp4Init) {
+ DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
+ Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
+
+ if (EFI_ERROR (Status)) {
+ DhcpSb->DhcpState = Dhcp4Init;
+ return Status;
+ }
+ } else {
+ DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
+ Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);
+
+ if (EFI_ERROR (Status)) {
+ DhcpSb->DhcpState = Dhcp4InitReboot;
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call user provided callback function, and return the value the
+ function returns. If the user doesn't provide a callback, a
+ proper return value is selected to let the caller continue the
+ normal process.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Event The event as defined in the spec
+ @param[in] Packet The current packet trigger the event
+ @param[out] NewPacket The user's return new packet
+
+ @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
+ @retval EFI_SUCCESS The user function returns success.
+ @retval EFI_ABORTED The user function ask it to abort.
+
+**/
+EFI_STATUS
+DhcpCallUser (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_EVENT Event,
+ IN EFI_DHCP4_PACKET *Packet, OPTIONAL
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+
+ if (NewPacket != NULL) {
+ *NewPacket = NULL;
+ }
+
+ //
+ // If user doesn't provide the call back function, return the value
+ // that directs the client to continue the normal process.
+ // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
+ // the offers and select a offer, EFI_NOT_READY tells the client to
+ // collect more offers.
+ //
+ Config = &DhcpSb->ActiveConfig;
+
+ if (Config->Dhcp4Callback == NULL) {
+ if (Event == Dhcp4RcvdOffer) {
+ return EFI_NOT_READY;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ Status = Config->Dhcp4Callback (
+ &DhcpSb->ActiveChild->Dhcp4Protocol,
+ Config->CallbackContext,
+ (EFI_DHCP4_STATE) DhcpSb->DhcpState,
+ Event,
+ Packet,
+ NewPacket
+ );
+
+ //
+ // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
+ // and EFI_ABORTED. If it returns values other than those, assume
+ // it to be EFI_ABORTED.
+ //
+ if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
+ return Status;
+ }
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Notify the user about the operation result.
+
+ @param DhcpSb DHCP service instance
+ @param Which Which notify function to signal
+
+**/
+VOID
+DhcpNotifyUser (
+ IN DHCP_SERVICE *DhcpSb,
+ IN INTN Which
+ )
+{
+ DHCP_PROTOCOL *Child;
+
+ if ((Child = DhcpSb->ActiveChild) == NULL) {
+ return ;
+ }
+
+ if ((Child->CompletionEvent != NULL) &&
+ ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))
+ ) {
+
+ gBS->SignalEvent (Child->CompletionEvent);
+ Child->CompletionEvent = NULL;
+ }
+
+ if ((Child->RenewRebindEvent != NULL) &&
+ ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))
+ ) {
+
+ gBS->SignalEvent (Child->RenewRebindEvent);
+ Child->RenewRebindEvent = NULL;
+ }
+}
+
+
+
+/**
+ Set the DHCP state. If CallUser is true, it will try to notify
+ the user before change the state by DhcpNotifyUser. It returns
+ EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
+ EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
+ the return value of this function.
+
+ @param DhcpSb The DHCP service instance
+ @param State The new DHCP state to change to
+ @param CallUser Whether we need to call user
+
+ @retval EFI_SUCCESS The state is changed
+ @retval EFI_ABORTED The user asks to abort the DHCP process.
+
+**/
+EFI_STATUS
+DhcpSetState (
+ IN OUT DHCP_SERVICE *DhcpSb,
+ IN INTN State,
+ IN BOOLEAN CallUser
+ )
+{
+ EFI_STATUS Status;
+
+ if (CallUser) {
+ Status = EFI_SUCCESS;
+
+ if (State == Dhcp4Renewing) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);
+
+ } else if (State == Dhcp4Rebinding) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);
+
+ } else if (State == Dhcp4Bound) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);
+
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Update the retransmission timer during the state transition.
+ // This will clear the retry count. This is also why the rule
+ // first transit the state, then send packets.
+ //
+ if (State == Dhcp4Selecting) {
+ DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
+ } else {
+ DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
+ }
+
+ if (DhcpSb->MaxRetries == 0) {
+ DhcpSb->MaxRetries = 4;
+ }
+
+ DhcpSb->CurRetry = 0;
+ DhcpSb->PacketToLive = 0;
+ DhcpSb->LastTimeout = 0;
+ DhcpSb->DhcpState = State;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Set the retransmit timer for the packet. It will select from either
+ the discover timeouts/request timeouts or the default timeout values.
+
+ @param DhcpSb The DHCP service instance.
+
+**/
+VOID
+DhcpSetTransmitTimer (
+ IN OUT DHCP_SERVICE *DhcpSb
+ )
+{
+ UINT32 *Times;
+
+ ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);
+
+ if (DhcpSb->DhcpState == Dhcp4Selecting) {
+ Times = DhcpSb->ActiveConfig.DiscoverTimeout;
+ } else {
+ Times = DhcpSb->ActiveConfig.RequestTimeout;
+ }
+
+ if (Times == NULL) {
+ Times = mDhcp4DefaultTimeout;
+ }
+
+ DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
+ DhcpSb->LastTimeout = DhcpSb->PacketToLive;
+
+ return;
+}
+
+/**
+ Compute the lease. If the server grants a permanent lease, just
+ process it as a normal timeout value since the lease will last
+ more than 100 years.
+
+ @param DhcpSb The DHCP service instance
+ @param Para The DHCP parameter extracted from the server's
+ response.
+**/
+VOID
+DhcpComputeLease (
+ IN OUT DHCP_SERVICE *DhcpSb,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ ASSERT (Para != NULL);
+
+ DhcpSb->Lease = Para->Lease;
+ DhcpSb->T2 = Para->T2;
+ DhcpSb->T1 = Para->T1;
+
+ if (DhcpSb->Lease == 0) {
+ DhcpSb->Lease = DHCP_DEFAULT_LEASE;
+ }
+
+ if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
+ DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
+ }
+
+ if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
+ DhcpSb->T1 = DhcpSb->Lease >> 1;
+ }
+}
+
+
+/**
+ Configure a UDP IO port to use the acquired lease address.
+ DHCP driver needs this port to unicast packet to the server
+ such as DHCP release.
+
+ @param[in] UdpIo The UDP IO to configure
+ @param[in] Context Dhcp service instance.
+
+ @retval EFI_SUCCESS The UDP IO port is successfully configured.
+ @retval Others It failed to configure the port.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpConfigLeaseIoPort (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+ EFI_IPv4_ADDRESS Subnet;
+ EFI_IPv4_ADDRESS Gateway;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ UdpConfigData.AcceptBroadcast = FALSE;
+ UdpConfigData.AcceptPromiscuous = FALSE;
+ UdpConfigData.AcceptAnyPort = FALSE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TypeOfService = 0;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = FALSE;
+ UdpConfigData.ReceiveTimeout = 1;
+ UdpConfigData.TransmitTimeout = 0;
+
+ UdpConfigData.UseDefaultAddress = FALSE;
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ UdpConfigData.RemotePort = DHCP_SERVER_PORT;
+
+ Ip = HTONL (DhcpSb->ClientAddr);
+ CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Netmask);
+ CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Add a default route if received from the server.
+ //
+ if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
+ ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Para->Router);
+ CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Update the lease states when a new lease is acquired. It will not only
+ save the acquired the address and lease time, it will also create a UDP
+ child to provide address resolution for the address.
+
+ @param DhcpSb The DHCP service instance
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS The lease is recorded.
+
+**/
+EFI_STATUS
+DhcpLeaseAcquired (
+ IN OUT DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
+
+ if (DhcpSb->Para != NULL) {
+ DhcpSb->Netmask = DhcpSb->Para->NetMask;
+ DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
+ }
+
+ if (DhcpSb->Netmask == 0) {
+ return EFI_ABORTED;
+ }
+
+ if (DhcpSb->LeaseIoPort != NULL) {
+ UdpIoFreeIo (DhcpSb->LeaseIoPort);
+ }
+
+ //
+ // Create a UDP/IP child to provide ARP service for the Leased IP,
+ // and transmit unicast packet with it as source address. Don't
+ // start receive on this port, the queued packet will be timeout.
+ //
+ DhcpSb->LeaseIoPort = UdpIoCreateIo (
+ DhcpSb->Controller,
+ DhcpSb->Image,
+ DhcpConfigLeaseIoPort,
+ UDP_IO_UDP4_VERSION,
+ DhcpSb
+ );
+
+ if (DhcpSb->LeaseIoPort == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
+ DhcpComputeLease (DhcpSb, DhcpSb->Para);
+ }
+
+ return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
+}
+
+
+/**
+ Clean up the DHCP related states, IoStatus isn't reset.
+
+ @param DhcpSb The DHCP instance service.
+
+**/
+VOID
+DhcpCleanLease (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpSb->DhcpState = Dhcp4Init;
+ DhcpSb->Xid = DhcpSb->Xid + 1;
+ DhcpSb->ClientAddr = 0;
+ DhcpSb->Netmask = 0;
+ DhcpSb->ServerAddr = 0;
+
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ DhcpSb->LastOffer = NULL;
+ }
+
+ if (DhcpSb->Selected != NULL) {
+ FreePool (DhcpSb->Selected);
+ DhcpSb->Selected = NULL;
+ }
+
+ if (DhcpSb->Para != NULL) {
+ FreePool (DhcpSb->Para);
+ DhcpSb->Para = NULL;
+ }
+
+ DhcpSb->Lease = 0;
+ DhcpSb->T1 = 0;
+ DhcpSb->T2 = 0;
+ DhcpSb->ExtraRefresh = FALSE;
+
+ if (DhcpSb->LeaseIoPort != NULL) {
+ UdpIoFreeIo (DhcpSb->LeaseIoPort);
+ DhcpSb->LeaseIoPort = NULL;
+ }
+
+ if (DhcpSb->LastPacket != NULL) {
+ FreePool (DhcpSb->LastPacket);
+ DhcpSb->LastPacket = NULL;
+ }
+
+ DhcpSb->PacketToLive = 0;
+ DhcpSb->LastTimeout = 0;
+ DhcpSb->CurRetry = 0;
+ DhcpSb->MaxRetries = 0;
+ DhcpSb->LeaseLife = 0;
+
+ //
+ // Clean active config data.
+ //
+ DhcpCleanConfigure (&DhcpSb->ActiveConfig);
+}
+
+
+/**
+ Select a offer among all the offers collected. If the offer selected is
+ of BOOTP, the lease is recorded and user notified. If the offer is of
+ DHCP, it will request the offer from the server.
+
+ @param[in] DhcpSb The DHCP service instance.
+
+ @retval EFI_SUCCESS One of the offer is selected.
+
+**/
+EFI_STATUS
+DhcpChooseOffer (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_DHCP4_PACKET *Selected;
+ EFI_DHCP4_PACKET *NewPacket;
+ EFI_DHCP4_PACKET *TempPacket;
+ EFI_STATUS Status;
+
+ ASSERT (DhcpSb->LastOffer != NULL);
+
+ //
+ // User will cache previous offers if he wants to select
+ // from multiple offers. If user provides an invalid packet,
+ // use the last offer, otherwise use the provided packet.
+ //
+ NewPacket = NULL;
+ Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Selected = DhcpSb->LastOffer;
+
+ if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
+ TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);
+ if (TempPacket != NULL) {
+ CopyMem (TempPacket, NewPacket, NewPacket->Size);
+ FreePool (Selected);
+ Selected = TempPacket;
+ }
+ }
+
+ DhcpSb->Selected = Selected;
+ DhcpSb->LastOffer = NULL;
+ DhcpSb->Para = NULL;
+ DhcpValidateOptions (Selected, &DhcpSb->Para);
+
+ //
+ // A bootp offer has been selected, save the lease status,
+ // enter bound state then notify the user.
+ //
+ if (DHCP_IS_BOOTP (DhcpSb->Para)) {
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Send a DHCP requests
+ //
+ Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
+}
+
+
+/**
+ Terminate the current address acquire. All the allocated resources
+ are released. Be careful when calling this function. A rule related
+ to this is: only call DhcpEndSession at the highest level, such as
+ DhcpInput, DhcpOnTimerTick...At the other level, just return error.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Status The result of the DHCP process.
+
+**/
+VOID
+DhcpEndSession (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_STATUS Status
+ )
+{
+ if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
+ } else {
+ DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
+ }
+
+ DhcpCleanLease (DhcpSb);
+
+ DhcpSb->IoStatus = Status;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
+}
+
+
+/**
+ Handle packets in DHCP select state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+EFI_STATUS
+DhcpHandleSelect (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // First validate the message:
+ // 1. the offer is a unicast
+ // 2. if it is a DHCP message, it must contains a server ID.
+ // Don't return a error for these two case otherwise the session is ended.
+ //
+ if (!DHCP_IS_BOOTP (Para) &&
+ ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
+ ) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Call the user's callback. The action according to the return is as:
+ // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
+ // 2. EFI_NOT_READY: wait for more offers
+ // 3. EFI_ABORTED: abort the address acquiring.
+ //
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
+
+ if (Status == EFI_SUCCESS) {
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ }
+
+ DhcpSb->LastOffer = Packet;
+
+ return DhcpChooseOffer (DhcpSb);
+
+ } else if (Status == EFI_NOT_READY) {
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ }
+
+ DhcpSb->LastOffer = Packet;
+
+ } else if (Status == EFI_ABORTED) {
+ //
+ // DhcpInput will end the session upon error return. Remember
+ // only to call DhcpEndSession at the top level call.
+ //
+ goto ON_EXIT;
+ }
+
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP request state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+EFI_STATUS
+DhcpHandleRequest (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *Selected;
+ EFI_STATUS Status;
+ UINT8 *Message;
+
+ ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
+
+ Head = &Packet->Dhcp4.Header;
+ Selected = &DhcpSb->Selected->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ (Para->ServerId != DhcpSb->Para->ServerId) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
+ ) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Received a NAK, end the session no matter what the user returns
+ //
+ Status = EFI_DEVICE_ERROR;
+
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the ACK matches the selected offer
+ //
+ Message = NULL;
+
+ if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
+ Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
+ goto REJECT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+
+ if (EFI_ERROR (Status)) {
+ Message = (UINT8 *) "Lease is denied upon received ACK";
+ goto REJECT;
+ }
+
+ //
+ // Record the lease, transit to BOUND state, then notify the user
+ //
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ Message = (UINT8 *) "Lease is denied upon entering bound";
+ goto REJECT;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
+
+ FreePool (Packet);
+ return EFI_SUCCESS;
+
+REJECT:
+ DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP renew/rebound state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+EFI_STATUS
+DhcpHandleRenewRebind (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *Selected;
+ EFI_STATUS Status;
+
+ ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
+
+ Head = &Packet->Dhcp4.Header;
+ Selected = &DhcpSb->Selected->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ (Para->ServerId != DhcpSb->Para->ServerId) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
+ ) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Received a NAK, ignore the user's return then terminate the process
+ //
+ Status = EFI_DEVICE_ERROR;
+
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // The lease is different from the selected. Don't send a DECLINE
+ // since it isn't existed in the client's FSM.
+ //
+ if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
+ goto ON_EXIT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Record the lease, start timer for T1 and T2,
+ //
+ DhcpComputeLease (DhcpSb, Para);
+ DhcpSb->LeaseLife = 0;
+ DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
+
+ if (DhcpSb->ExtraRefresh != 0) {
+ DhcpSb->ExtraRefresh = FALSE;
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
+ }
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP reboot state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+EFI_STATUS
+DhcpHandleReboot (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_STATUS Status;
+
+ Head = &Packet->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
+ ) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // If a NAK is received, transit to INIT and try again.
+ //
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+
+ DhcpSb->ClientAddr = 0;
+ DhcpSb->DhcpState = Dhcp4Init;
+
+ Status = DhcpInitRequest (DhcpSb);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the ACK matches the selected offer
+ //
+ if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, get the parameter from server, record the lease
+ //
+ DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
+ if (DhcpSb->Para == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ DhcpSb->Selected = Packet;
+ Status = DhcpLeaseAcquired (DhcpSb);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle the received DHCP packets. This function drives the DHCP
+ state machine.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_PACKET *Packet;
+ DHCP_PARAMETER *Para;
+ EFI_STATUS Status;
+ UINT32 Len;
+
+ Packet = NULL;
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ //
+ // Don't restart receive if error occurs or DHCP is destroyed.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
+ NetbufFree (UdpPacket);
+ return ;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the packet received
+ //
+ if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
+ goto RESTART;
+ }
+
+ //
+ // Copy the DHCP message to a continuous memory block
+ //
+ Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
+ Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);
+
+ if (Packet == NULL) {
+ goto RESTART;
+ }
+
+ Packet->Size = Len;
+ Head = &Packet->Dhcp4.Header;
+ Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length != UdpPacket->TotalSize) {
+ goto RESTART;
+ }
+
+ //
+ // Is this packet the answer to our packet?
+ //
+ if ((Head->OpCode != BOOTP_REPLY) ||
+ (NTOHL (Head->Xid) != DhcpSb->Xid) ||
+ (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the options and retrieve the interested options
+ //
+ Para = NULL;
+ if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
+ (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
+ EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
+
+ goto RESTART;
+ }
+
+ //
+ // Call the handler for each state. The handler should return
+ // EFI_SUCCESS if the process can go on no matter whether the
+ // packet is ignored or not. If the return is EFI_ERROR, the
+ // session will be terminated. Packet's ownership is handled
+ // over to the handlers. If operation succeeds, the handler
+ // must notify the user. It isn't necessary to do if EFI_ERROR
+ // is returned because the DhcpEndSession will notify the user.
+ //
+ Status = EFI_SUCCESS;
+
+ switch (DhcpSb->DhcpState) {
+ case Dhcp4Selecting:
+ Status = DhcpHandleSelect (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4Requesting:
+ Status = DhcpHandleRequest (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4InitReboot:
+ case Dhcp4Init:
+ case Dhcp4Bound:
+ //
+ // Ignore the packet in INITREBOOT, INIT and BOUND states
+ //
+ FreePool (Packet);
+ Status = EFI_SUCCESS;
+ break;
+
+ case Dhcp4Renewing:
+ case Dhcp4Rebinding:
+ Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4Rebooting:
+ Status = DhcpHandleReboot (DhcpSb, Packet, Para);
+ break;
+ }
+
+ if (Para != NULL) {
+ FreePool (Para);
+ }
+
+ Packet = NULL;
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (UdpPacket);
+ UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+ DhcpEndSession (DhcpSb, Status);
+ return ;
+ }
+
+RESTART:
+ NetbufFree (UdpPacket);
+
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+
+ Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+
+ if (EFI_ERROR (Status)) {
+ DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
+ }
+}
+
+
+/**
+ Release the packet.
+
+ @param[in] Arg The packet to release
+
+**/
+VOID
+EFIAPI
+DhcpReleasePacket (
+ IN VOID *Arg
+ )
+{
+ FreePool (Arg);
+}
+
+
+/**
+ Release the net buffer when packet is sent.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpOnPacketSent (
+ NET_BUF *Packet,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+
+/**
+ Build and transmit a DHCP message according to the current states.
+ This function implement the Table 5. of RFC 2131. Always transits
+ the state (as defined in Figure 5. of the same RFC) before sending
+ a DHCP message. The table is adjusted accordingly.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Seed The seed packet which the new packet is based on
+ @param[in] Para The DHCP parameter of the Seed packet
+ @param[in] Type The message type to send
+ @param[in] Msg The human readable message to include in the packet
+ sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
+ @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
+ @retval EFI_SUCCESS The message is sent
+ @retval other Other error occurs
+
+**/
+EFI_STATUS
+DhcpSendMessage (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Seed,
+ IN DHCP_PARAMETER *Para,
+ IN UINT8 Type,
+ IN UINT8 *Msg
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_DHCP4_PACKET *NewPacket;
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *SeedHead;
+ UDP_IO *UdpIo;
+ UDP_END_POINT EndPoint;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ EFI_STATUS Status;
+ IP4_ADDR IpAddr;
+ UINT8 *Buf;
+ UINT16 MaxMsg;
+ UINT32 Len;
+ UINT32 Index;
+
+ //
+ // Allocate a big enough memory block to hold the DHCP packet
+ //
+ Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
+
+ if (Msg != NULL) {
+ Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);
+ }
+
+ Packet = AllocatePool (Len);
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = Len;
+ Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
+
+ //
+ // Fill in the DHCP header fields
+ //
+ Config = &DhcpSb->ActiveConfig;
+ SeedHead = NULL;
+
+ if (Seed != NULL) {
+ SeedHead = &Seed->Dhcp4.Header;
+ }
+
+ Head = &Packet->Dhcp4.Header;
+ ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
+
+ Head->OpCode = BOOTP_REQUEST;
+ Head->HwType = DhcpSb->HwType;
+ Head->HwAddrLen = DhcpSb->HwLen;
+ Head->Xid = HTONL (DhcpSb->Xid);
+ Head->Reserved = HTONS (0x8000); //Server, broadcast the message please.
+
+ EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
+ CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
+
+ if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
+ Head->Seconds = 0;
+ } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
+ //
+ // Use the same value as the original DHCPDISCOVER message.
+ //
+ Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
+ } else {
+ SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild);
+ }
+
+ //
+ // Append the DHCP message type
+ //
+ Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
+ Buf = Packet->Dhcp4.Option;
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_MSG_TYPE, 1, &Type);
+
+ //
+ // Append the serverid option if necessary:
+ // 1. DHCP decline message
+ // 2. DHCP release message
+ // 3. DHCP request to confirm one lease.
+ //
+ if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
+ ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
+ ) {
+
+ ASSERT ((Para != NULL) && (Para->ServerId != 0));
+
+ IpAddr = HTONL (Para->ServerId);
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
+ }
+
+ //
+ // Append the requested IP option if necessary:
+ // 1. DHCP request to use the previously allocated address
+ // 2. DHCP request to confirm one lease
+ // 3. DHCP decline to decline one lease
+ //
+ IpAddr = 0;
+
+ if (Type == DHCP_MSG_REQUEST) {
+ if (DhcpSb->DhcpState == Dhcp4Rebooting) {
+ IpAddr = EFI_IP4 (Config->ClientAddress);
+
+ } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
+ ASSERT (SeedHead != NULL);
+ IpAddr = EFI_IP4 (SeedHead->YourAddr);
+ }
+
+ } else if (Type == DHCP_MSG_DECLINE) {
+ ASSERT (SeedHead != NULL);
+ IpAddr = EFI_IP4 (SeedHead->YourAddr);
+ }
+
+ if (IpAddr != 0) {
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
+ }
+
+ //
+ // Append the Max Message Length option if it isn't a DECLINE
+ // or RELEASE to direct the server use large messages instead of
+ // override the BOOTFILE and SERVER fields in the message head.
+ //
+ if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
+ MaxMsg = HTONS (0xFF00);
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
+ }
+
+ //
+ // Append the user's message if it isn't NULL
+ //
+ if (Msg != NULL) {
+ Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_MESSAGE, (UINT16) Len, Msg);
+ }
+
+ //
+ // Append the user configured options
+ //
+ if (DhcpSb->UserOptionLen != 0) {
+ for (Index = 0; Index < Config->OptionCount; Index++) {
+ //
+ // We can't use any option other than the client ID from user
+ // if it is a DHCP decline or DHCP release .
+ //
+ if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
+ (Config->OptionList[Index]->OpCode != DHCP4_TAG_CLIENT_ID)) {
+ continue;
+ }
+
+ Buf = DhcpAppendOption (
+ Buf,
+ Config->OptionList[Index]->OpCode,
+ Config->OptionList[Index]->Length,
+ Config->OptionList[Index]->Data
+ );
+ }
+ }
+
+ *(Buf++) = DHCP4_TAG_EOP;
+ Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
+
+ //
+ // OK, the message is built, call the user to override it.
+ //
+ Status = EFI_SUCCESS;
+ NewPacket = NULL;
+
+ if (Type == DHCP_MSG_DISCOVER) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
+
+ } else if (Type == DHCP_MSG_REQUEST) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
+
+ } else if (Type == DHCP_MSG_DECLINE) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
+ }
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ if (NewPacket != NULL) {
+ FreePool (Packet);
+ Packet = NewPacket;
+ }
+
+ //
+ // Save the Client Address will be sent out
+ //
+ CopyMem (
+ &DhcpSb->ClientAddressSendOut[0],
+ &Packet->Dhcp4.Header.ClientHwAddr[0],
+ Packet->Dhcp4.Header.HwAddrLen
+ );
+
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
+ Frag.Len = Packet->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);
+
+ if (Wrap == NULL) {
+ FreePool (Packet);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save it as the last sent packet for retransmission
+ //
+ if (DhcpSb->LastPacket != NULL) {
+ FreePool (DhcpSb->LastPacket);
+ }
+
+ DhcpSb->LastPacket = Packet;
+ DhcpSetTransmitTimer (DhcpSb);
+
+ //
+ // Broadcast the message, unless we know the server address.
+ // Use the lease UdpIo port to send the unicast packet.
+ //
+ EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
+ EndPoint.LocalAddr.Addr[0] = 0;
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ EndPoint.LocalPort = DHCP_CLIENT_PORT;
+ UdpIo = DhcpSb->UdpIo;
+
+ if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
+ EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
+ EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
+ UdpIo = DhcpSb->LeaseIoPort;
+ }
+
+ ASSERT (UdpIo != NULL);
+ NET_GET_REF (Wrap);
+
+ Status = UdpIoSendDatagram (
+ UdpIo,
+ Wrap,
+ &EndPoint,
+ NULL,
+ DhcpOnPacketSent,
+ DhcpSb
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Wrap);
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retransmit a saved packet. Only DISCOVER and REQUEST messages
+ will be retransmitted.
+
+ @param[in] DhcpSb The DHCP service instance
+
+ @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
+ @retval EFI_SUCCESS The packet is retransmitted.
+
+**/
+EFI_STATUS
+DhcpRetransmit (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ UDP_IO *UdpIo;
+ UDP_END_POINT EndPoint;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ EFI_STATUS Status;
+
+ ASSERT (DhcpSb->LastPacket != NULL);
+
+ //
+ // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
+ //
+ if (DhcpSb->DhcpState != Dhcp4Requesting) {
+ SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
+ }
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;
+ Frag.Len = DhcpSb->LastPacket->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Broadcast the message, unless we know the server address.
+ //
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ EndPoint.LocalPort = DHCP_CLIENT_PORT;
+ EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
+ EndPoint.LocalAddr.Addr[0] = 0;
+ UdpIo = DhcpSb->UdpIo;
+
+ if (DhcpSb->DhcpState == Dhcp4Renewing) {
+ EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
+ EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
+ UdpIo = DhcpSb->LeaseIoPort;
+ }
+
+ ASSERT (UdpIo != NULL);
+
+ NET_GET_REF (Wrap);
+ Status = UdpIoSendDatagram (
+ UdpIo,
+ Wrap,
+ &EndPoint,
+ NULL,
+ DhcpOnPacketSent,
+ DhcpSb
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Wrap);
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Each DHCP service has three timer. Two of them are count down timer.
+ One for the packet retransmission. The other is to collect the offers.
+ The third timer increaments the lease life which is compared to T1, T2,
+ and lease to determine the time to renew and rebind the lease.
+ DhcpOnTimerTick will be called once every second.
+
+ @param[in] Event The timer event
+ @param[in] Context The context, which is the DHCP service instance.
+
+**/
+VOID
+EFIAPI
+DhcpOnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_STATUS Status;
+
+ DhcpSb = (DHCP_SERVICE *) Context;
+ Instance = DhcpSb->ActiveChild;
+
+ //
+ // 0xffff is the maximum supported value for elapsed time according to RFC.
+ //
+ if (Instance != NULL && Instance->ElaspedTime < 0xffff) {
+ Instance->ElaspedTime++;
+ }
+
+ //
+ // Check the retransmit timer
+ //
+ if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
+
+ //
+ // Select offer at each timeout if any offer received.
+ //
+ if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {
+
+ Status = DhcpChooseOffer (DhcpSb);
+
+ if (EFI_ERROR(Status)) {
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ DhcpSb->LastOffer = NULL;
+ }
+ } else {
+ goto ON_EXIT;
+ }
+ }
+
+ if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
+ //
+ // Still has another try
+ //
+ DhcpRetransmit (DhcpSb);
+ DhcpSetTransmitTimer (DhcpSb);
+
+ } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+
+ //
+ // Retransmission failed, if the DHCP request is initiated by
+ // user, adjust the current state according to the lease life.
+ // Otherwise do nothing to wait the lease to timeout
+ //
+ if (DhcpSb->ExtraRefresh != 0) {
+ Status = EFI_SUCCESS;
+
+ if (DhcpSb->LeaseLife < DhcpSb->T1) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
+
+ } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
+
+ } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
+
+ } else {
+ goto END_SESSION;
+
+ }
+
+ DhcpSb->IoStatus = EFI_TIMEOUT;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
+ }
+ } else {
+ goto END_SESSION;
+ }
+ }
+
+ //
+ // If an address has been acquired, check whether need to
+ // refresh or whether it has expired.
+ //
+ if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ DhcpSb->LeaseLife++;
+
+ //
+ // Don't timeout the lease, only count the life if user is
+ // requesting extra renew/rebind. Adjust the state after that.
+ //
+ if (DhcpSb->ExtraRefresh != 0) {
+ return ;
+ }
+
+ if (DhcpSb->LeaseLife == DhcpSb->Lease) {
+ //
+ // Lease expires, end the session
+ //
+ goto END_SESSION;
+
+ } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
+ //
+ // T2 expires, transit to rebinding then send a REQUEST to any server
+ //
+ if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
+ goto END_SESSION;
+ }
+
+ if (Instance != NULL) {
+ Instance->ElaspedTime= 0;
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto END_SESSION;
+ }
+
+ } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
+ //
+ // T1 expires, transit to renewing, then send a REQUEST to the server
+ //
+ if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
+ goto END_SESSION;
+ }
+
+ if (Instance != NULL) {
+ Instance->ElaspedTime= 0;
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto END_SESSION;
+ }
+ }
+ }
+
+ON_EXIT:
+ //
+ // Iterate through all the DhcpSb Children.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
+
+ if ((Instance != NULL) && (Instance->Token != NULL)) {
+ Instance->Timeout--;
+ if (Instance->Timeout == 0) {
+ PxeDhcpDone (Instance);
+ }
+ }
+ }
+
+ return ;
+
+END_SESSION:
+ DhcpEndSession (DhcpSb, EFI_TIMEOUT);
+
+ return ;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h
new file mode 100644
index 0000000000..1e85651554
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h
@@ -0,0 +1,195 @@
+/** @file
+ The DHCP4 protocol implementation.
+
+Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP4_IO_H__
+#define __EFI_DHCP4_IO_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+
+#define DHCP_WAIT_OFFER 3 // Time to wait the offers
+#define DHCP_DEFAULT_LEASE 7 * 24 * 60 * 60 // Seven days as default.
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+//
+// BOOTP header "op" field
+//
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+//
+// DHCP message types
+//
+#define DHCP_MSG_DISCOVER 1
+#define DHCP_MSG_OFFER 2
+#define DHCP_MSG_REQUEST 3
+#define DHCP_MSG_DECLINE 4
+#define DHCP_MSG_ACK 5
+#define DHCP_MSG_NAK 6
+#define DHCP_MSG_RELEASE 7
+#define DHCP_MSG_INFORM 8
+
+//
+// DHCP notify user type
+//
+#define DHCP_NOTIFY_COMPLETION 1
+#define DHCP_NOTIFY_RENEWREBIND 2
+#define DHCP_NOTIFY_ALL 3
+
+#define DHCP_IS_BOOTP(Parameter) (((Parameter) == NULL) || ((Parameter)->DhcpType == 0))
+
+#define DHCP_CONNECTED(State) \
+ (((State) == Dhcp4Bound) || ((State) == (Dhcp4Renewing)) || ((State) == Dhcp4Rebinding))
+
+/**
+ Set the DHCP state. If CallUser is true, it will try to notify
+ the user before change the state by DhcpNotifyUser. It returns
+ EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
+ EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
+ the return value of this function.
+
+ @param DhcpSb The DHCP service instance
+ @param State The new DHCP state to change to
+ @param CallUser Whether we need to call user
+
+ @retval EFI_SUCCESS The state is changed
+ @retval EFI_ABORTED The user asks to abort the DHCP process.
+
+**/
+EFI_STATUS
+DhcpSetState (
+ IN OUT DHCP_SERVICE *DhcpSb,
+ IN INTN State,
+ IN BOOLEAN CallUser
+ );
+
+/**
+ Build and transmit a DHCP message according to the current states.
+ This function implement the Table 5. of RFC 2131. Always transits
+ the state (as defined in Figure 5. of the same RFC) before sending
+ a DHCP message. The table is adjusted accordingly.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Seed The seed packet which the new packet is based on
+ @param[in] Para The DHCP parameter of the Seed packet
+ @param[in] Type The message type to send
+ @param[in] Msg The human readable message to include in the packet
+ sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
+ @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
+ @retval EFI_SUCCESS The message is sent
+ @retval other Other error occurs
+
+**/
+EFI_STATUS
+DhcpSendMessage (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Seed,
+ IN DHCP_PARAMETER *Para,
+ IN UINT8 Type,
+ IN UINT8 *Msg
+ );
+
+/**
+ Each DHCP service has three timer. Two of them are count down timer.
+ One for the packet retransmission. The other is to collect the offers.
+ The third timer increaments the lease life which is compared to T1, T2,
+ and lease to determine the time to renew and rebind the lease.
+ DhcpOnTimerTick will be called once every second.
+
+ @param[in] Event The timer event
+ @param[in] Context The context, which is the DHCP service instance.
+
+**/
+VOID
+EFIAPI
+DhcpOnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Handle the received DHCP packets. This function drives the DHCP
+ state machine.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ );
+
+/**
+ Send an initial DISCOVER or REQUEST message according to the
+ DHCP service's current state.
+
+ @param[in] DhcpSb The DHCP service instance
+
+ @retval EFI_SUCCESS The request has been sent
+ @retval other Some error occurs when sending the request.
+
+**/
+EFI_STATUS
+DhcpInitRequest (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+/**
+ Clean up the DHCP related states, IoStatus isn't reset.
+
+ @param DhcpSb The DHCP instance service.
+
+**/
+VOID
+DhcpCleanLease (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+/**
+ Release the net buffer when packet is sent.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpOnPacketSent (
+ NET_BUF *Packet,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
new file mode 100644
index 0000000000..485c9c98ed
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
@@ -0,0 +1,896 @@
+/** @file
+ Function to validate, parse, process the DHCP options.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp4Impl.h"
+
+///
+/// A list of the format of DHCP Options sorted by option tag
+/// to validate a dhcp message. Refere the comments of the
+/// DHCP_OPTION_FORMAT structure.
+///
+DHCP_OPTION_FORMAT DhcpOptionFormats[] = {
+ {DHCP4_TAG_NETMASK, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP4_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_ROUTER, DHCP_OPTION_IP, 1, -1 , TRUE},
+ {DHCP4_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_DUMP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP4_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+ {DHCP4_TAG_EMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP4_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16, 1, -1 , FALSE},
+
+ {DHCP4_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+
+ {DHCP4_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP4_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP4_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP4_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_NBNS, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NBDD, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP4_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_XFONT, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_XDM, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP4_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_LEASE, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP4_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP4_TAG_MSG_TYPE, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP4_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP4_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_T1, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP4_TAG_T2, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP4_TAG_VENDOR_CLASS_ID,DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1 , FALSE},
+
+ {DHCP4_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP4_TAG_TFTP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP4_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1 , FALSE},
+ {DHCP4_TAG_SMTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_POP3, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NNTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_WWW, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_FINGER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_IRC, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_STTALK, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_STDA, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP4_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8, 5, -1 , FALSE},
+};
+
+
+/**
+ Binary search the DhcpOptionFormats array to find the format
+ information about a specific option.
+
+ @param[in] Tag The option's tag.
+
+ @return The point to the option's format, NULL if not found.
+
+**/
+DHCP_OPTION_FORMAT *
+DhcpFindOptionFormat (
+ IN UINT8 Tag
+ )
+{
+ INTN Left;
+ INTN Right;
+ INTN Middle;
+
+ Left = 0;
+ Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;
+
+ while (Right >= Left) {
+ Middle = (Left + Right) / 2;
+
+ if (Tag == DhcpOptionFormats[Middle].Tag) {
+ return &DhcpOptionFormats[Middle];
+ }
+
+ if (Tag < DhcpOptionFormats[Middle].Tag) {
+ Right = Middle - 1;
+ } else {
+ Left = Middle + 1;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Validate whether a single DHCP option is valid according to its format.
+
+ @param[in] Format The option's format
+ @param[in] OptValue The value of the option
+ @param[in] Len The length of the option value
+
+ @retval TRUE The option is valid.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+DhcpOptionIsValid (
+ IN DHCP_OPTION_FORMAT *Format,
+ IN UINT8 *OptValue,
+ IN INTN Len
+ )
+{
+ INTN Unit;
+ INTN Occur;
+ INTN Index;
+
+ Unit = 0;
+
+ switch (Format->Type) {
+ case DHCP_OPTION_SWITCH:
+ case DHCP_OPTION_INT8:
+ Unit = 1;
+ break;
+
+ case DHCP_OPTION_INT16:
+ Unit = 2;
+ break;
+
+ case DHCP_OPTION_INT32:
+ case DHCP_OPTION_IP:
+ Unit = 4;
+ break;
+
+ case DHCP_OPTION_IPPAIR:
+ Unit = 8;
+ break;
+ }
+
+ ASSERT (Unit != 0);
+
+ //
+ // Validate that the option appears in the full units.
+ //
+ if ((Len % Unit) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Validate the occurance of the option unit is with in [MinOccur, MaxOccur]
+ //
+ Occur = Len / Unit;
+
+ if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||
+ ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))
+ ) {
+ return FALSE;
+ }
+
+ //
+ // If the option is of type switch, only 0/1 are valid values.
+ //
+ if (Format->Type == DHCP_OPTION_SWITCH) {
+ for (Index = 0; Index < Occur; Index++) {
+ if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Extract the client interested options, all the parameters are
+ converted to host byte order.
+
+ @param[in] Tag The DHCP option tag
+ @param[in] Len The length of the option
+ @param[in] Data The value of the DHCP option
+ @param[out] Para The variable to save the interested parameter
+
+ @retval EFI_SUCCESS The DHCP option is successfully extracted.
+ @retval EFI_INVALID_PARAMETER The DHCP option is mal-formated
+
+**/
+EFI_STATUS
+DhcpGetParameter (
+ IN UINT8 Tag,
+ IN INTN Len,
+ IN UINT8 *Data,
+ OUT DHCP_PARAMETER *Para
+ )
+{
+ switch (Tag) {
+ case DHCP4_TAG_NETMASK:
+ Para->NetMask = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_ROUTER:
+ //
+ // Return the first router to consumer which is the preferred one
+ //
+ Para->Router = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_LEASE:
+ Para->Lease = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_OVERLOAD:
+ Para->Overload = *Data;
+
+ if ((Para->Overload < 1) || (Para->Overload > 3)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP4_TAG_MSG_TYPE:
+ Para->DhcpType = *Data;
+
+ if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP4_TAG_SERVER_ID:
+ Para->ServerId = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_T1:
+ Para->T1 = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_T2:
+ Para->T2 = NetGetUint32 (Data);
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Inspect all the options in a single buffer. DHCP options may be contained
+ in several buffers, such as the BOOTP options filed, boot file or server
+ name. Each option buffer is required to end with DHCP4_TAG_EOP.
+
+ @param[in] Buffer The buffer which contains DHCP options
+ @param[in] BufLen The length of the buffer
+ @param[in] Check The callback function for each option found
+ @param[in] Context The opaque parameter for the Check
+ @param[out] Overload Variable to save the value of DHCP4_TAG_OVERLOAD
+ option.
+
+ @retval EFI_SUCCESS All the options are valid
+ @retval EFI_INVALID_PARAMETER The options are mal-formated.
+
+**/
+EFI_STATUS
+DhcpIterateBufferOptions (
+ IN UINT8 *Buffer,
+ IN INTN BufLen,
+ IN DHCP_CHECK_OPTION Check OPTIONAL,
+ IN VOID *Context,
+ OUT UINT8 *Overload OPTIONAL
+ )
+{
+ INTN Cur;
+ UINT8 Tag;
+ UINT8 Len;
+
+ Cur = 0;
+
+ while (Cur < BufLen) {
+ Tag = Buffer[Cur];
+
+ if (Tag == DHCP4_TAG_PAD) {
+ Cur++;
+ continue;
+ } else if (Tag == DHCP4_TAG_EOP) {
+ return EFI_SUCCESS;
+ }
+
+ Cur++;
+
+ if (Cur == BufLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Len = Buffer[Cur++];
+
+ if (Cur + Len > BufLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Tag == DHCP4_TAG_OVERLOAD) && (Overload != NULL)) {
+ if (Len != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Overload = Buffer[Cur];
+ }
+
+ if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cur += Len;
+ }
+
+ //
+ // Each option buffer is expected to end with an EOP
+ //
+ return EFI_INVALID_PARAMETER;
+}
+
+
+/**
+ Iterate through a DHCP message to visit each option. First inspect
+ all the options in the OPTION field. Then if overloaded, inspect
+ the options in FILENAME and SERVERNAME fields. One option may be
+ encoded in several places. See RFC 3396 Encoding Long Options in DHCP
+
+ @param[in] Packet The DHCP packet to check the options for
+ @param[in] Check The callback function to be called for each option
+ found
+ @param[in] Context The opaque parameter for Check
+
+ @retval EFI_SUCCESS The DHCP packet's options are well formated
+ @retval EFI_INVALID_PARAMETER The DHCP packet's options are not well formated
+
+**/
+EFI_STATUS
+DhcpIterateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_CHECK_OPTION Check OPTIONAL,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Overload;
+
+ Overload = 0;
+
+ Status = DhcpIterateBufferOptions (
+ Packet->Dhcp4.Option,
+ Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),
+ Check,
+ Context,
+ &Overload
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
+ Status = DhcpIterateBufferOptions (
+ (UINT8 *) Packet->Dhcp4.Header.BootFileName,
+ 128,
+ Check,
+ Context,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
+ Status = DhcpIterateBufferOptions (
+ (UINT8 *) Packet->Dhcp4.Header.ServerName,
+ 64,
+ Check,
+ Context,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call back function to DhcpIterateOptions to compute each option's
+ length. It just adds the data length of all the occurances of this
+ Tag. Context is an array of 256 DHCP_OPTION_COUNT.
+
+ @param[in] Tag The current option to check
+ @param[in] Len The length of the option data
+ @param[in] Data The option data
+ @param[in] Context The context, which is a array of 256
+ DHCP_OPTION_COUNT.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+DhcpGetOptionLen (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_OPTION_COUNT *OpCount;
+
+ OpCount = (DHCP_OPTION_COUNT *) Context;
+ OpCount[Tag].Offset = (UINT16) (OpCount[Tag].Offset + Len);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call back function to DhcpIterateOptions to consolidate each option's
+ data. There are maybe several occurrence of the same option.
+
+ @param[in] Tag The option to consolidate its data
+ @param[in] Len The length of option data
+ @param[in] Data The data of the option's current occurance
+ @param[in] Context The context, which is DHCP_OPTION_CONTEXT. This
+ array is just a wrap to pass THREE parameters.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS
+
+**/
+EFI_STATUS
+DhcpFillOption (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_OPTION_CONTEXT *OptContext;
+ DHCP_OPTION_COUNT *OptCount;
+ DHCP_OPTION *Options;
+ UINT8 *Buf;
+ UINT8 Index;
+
+ OptContext = (DHCP_OPTION_CONTEXT *) Context;
+
+ OptCount = OptContext->OpCount;
+ Index = OptCount[Tag].Index;
+ Options = OptContext->Options;
+ Buf = OptContext->Buf;
+
+ if (Options[Index].Data == NULL) {
+ Options[Index].Tag = Tag;
+ Options[Index].Data = Buf + OptCount[Tag].Offset;
+ }
+
+ CopyMem (Buf + OptCount[Tag].Offset, Data, Len);
+
+ OptCount[Tag].Offset = (UINT16) (OptCount[Tag].Offset + Len);
+ Options[Index].Len = (UINT16) (Options[Index].Len + Len);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the options of a DHCP packet. It supports RFC 3396: Encoding
+ Long Options in DHCP. That is, it will combine all the option value
+ of all the occurances of each option.
+ A little bit of implemenation:
+ It adopts the "Key indexed counting" algorithm. First, it allocates
+ an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
+ as a UINT8. It then iterates the DHCP packet to get data length of
+ each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
+ knows the number of present options and their length. It allocates a
+ array of DHCP_OPTION and a continuous buffer after the array to put
+ all the options' data. Each option's data is pointed to by the Data
+ field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
+ with DhcpFillOption to fill each option's data to its position in the
+ buffer.
+
+ @param[in] Packet The DHCP packet to parse the options
+ @param[out] Count The number of valid dhcp options present in the
+ packet
+ @param[out] OptionPoint The array that contains the DHCP options. Caller
+ should free it.
+
+ @retval EFI_NOT_FOUND Cannot find any option.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formated
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpParseOption (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT INTN *Count,
+ OUT DHCP_OPTION **OptionPoint
+ )
+{
+ DHCP_OPTION_CONTEXT Context;
+ DHCP_OPTION *Options;
+ DHCP_OPTION_COUNT *OptCount;
+ EFI_STATUS Status;
+ UINT16 TotalLen;
+ INTN OptNum;
+ INTN Index;
+
+ ASSERT ((Count != NULL) && (OptionPoint != NULL));
+
+ //
+ // First compute how many options and how long each option is
+ // with the "Key indexed counting" algorithms.
+ //
+ OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));
+
+ if (OptCount == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Before the loop, Offset is the length of the option. After loop,
+ // OptCount[Index].Offset specifies the offset into the continuous
+ // option value buffer to put the data.
+ //
+ TotalLen = 0;
+ OptNum = 0;
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (OptCount[Index].Offset != 0) {
+ OptCount[Index].Index = (UINT8) OptNum;
+
+ TotalLen = (UINT16) (TotalLen + OptCount[Index].Offset);
+ OptCount[Index].Offset = (UINT16) (TotalLen - OptCount[Index].Offset);
+
+ OptNum++;
+ }
+ }
+
+ *Count = OptNum;
+ *OptionPoint = NULL;
+
+ if (OptNum == 0) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Allocate a buffer to hold the DHCP options, and after that, a
+ // continuous buffer to put all the options' data.
+ //
+ Options = AllocateZeroPool ((UINTN) (OptNum * sizeof (DHCP_OPTION)) + TotalLen);
+
+ if (Options == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Context.OpCount = OptCount;
+ Context.Options = Options;
+ Context.Buf = (UINT8 *) (Options + OptNum);
+
+ Status = DhcpIterateOptions (Packet, DhcpFillOption, &Context);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Options);
+ goto ON_EXIT;
+ }
+
+ *OptionPoint = Options;
+
+ON_EXIT:
+ FreePool (OptCount);
+ return Status;
+}
+
+
+/**
+ Validate the packet's options. If necessary, allocate
+ and fill in the interested parameters.
+
+ @param[in] Packet The packet to validate the options
+ @param[out] Para The variable to save the DHCP parameters.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formated
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpValidateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT DHCP_PARAMETER **Para OPTIONAL
+ )
+{
+ DHCP_PARAMETER Parameter;
+ DHCP_OPTION_FORMAT *Format;
+ DHCP_OPTION *AllOption;
+ DHCP_OPTION *Option;
+ EFI_STATUS Status;
+ BOOLEAN Updated;
+ INTN Count;
+ INTN Index;
+
+ if (Para != NULL) {
+ *Para = NULL;
+ }
+
+ AllOption = NULL;
+
+ Status = DhcpParseOption (Packet, &Count, &AllOption);
+ if (EFI_ERROR (Status) || (Count == 0)) {
+ return Status;
+ }
+ ASSERT (AllOption != NULL);
+
+ Updated = FALSE;
+ ZeroMem (&Parameter, sizeof (Parameter));
+
+ for (Index = 0; Index < Count; Index++) {
+ Option = &AllOption[Index];
+
+ //
+ // Find the format of the option then validate it.
+ //
+ Format = DhcpFindOptionFormat (Option->Tag);
+
+ if (Format == NULL) {
+ continue;
+ }
+
+ if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ //
+ // Get the client interested parameters
+ //
+ if (Format->Alert && (Para != NULL)) {
+ Updated = TRUE;
+ Status = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if (Updated && (Para != NULL)) {
+ *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter);
+ if (*Para == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ }
+
+ON_EXIT:
+ FreePool (AllOption);
+ return Status;
+}
+
+
+
+/**
+ Append an option to the memory, if the option is longer than
+ 255 bytes, splits it into several options.
+
+ @param[out] Buf The buffer to append the option to
+ @param[in] Tag The option's tag
+ @param[in] DataLen The length of the option's data
+ @param[in] Data The option's data
+
+ @return The position to append the next option
+
+**/
+UINT8 *
+DhcpAppendOption (
+ OUT UINT8 *Buf,
+ IN UINT8 Tag,
+ IN UINT16 DataLen,
+ IN UINT8 *Data
+ )
+{
+ INTN Index;
+ INTN Len;
+
+ ASSERT (DataLen != 0);
+
+ for (Index = 0; Index < (DataLen + 254) / 255; Index++) {
+ Len = MIN (255, DataLen - Index * 255);
+
+ *(Buf++) = Tag;
+ *(Buf++) = (UINT8) Len;
+ CopyMem (Buf, Data + Index * 255, (UINTN) Len);
+
+ Buf += Len;
+ }
+
+ return Buf;
+}
+
+
+/**
+ Build a new DHCP packet from a seed packet. Options may be deleted or
+ appended. The caller should free the NewPacket when finished using it.
+
+ @param[in] SeedPacket The seed packet to start with
+ @param[in] DeleteCount The number of options to delete
+ @param[in] DeleteList The options to delete from the packet
+ @param[in] AppendCount The number of options to append
+ @param[in] AppendList The options to append to the packet
+ @param[out] NewPacket The new packet, allocated and built by this
+ function.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_INVALID_PARAMETER The options in SeekPacket are mal-formated
+ @retval EFI_SUCCESS The packet is build.
+
+**/
+EFI_STATUS
+DhcpBuild (
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ )
+{
+ DHCP_OPTION *Mark;
+ DHCP_OPTION *SeedOptions;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_STATUS Status;
+ INTN Count;
+ UINT32 Index;
+ UINT32 Len;
+ UINT8 *Buf;
+
+ //
+ // Use an array of DHCP_OPTION to mark the existance
+ // and position of each valid options.
+ //
+ Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);
+
+ if (Mark == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ Mark[Index].Tag = (UINT8) Index;
+ Mark[Index].Len = 0;
+ }
+
+ //
+ // Get list of the options from the seed packet, then put
+ // them to the mark array according to their tags.
+ //
+ SeedOptions = NULL;
+ Status = DhcpParseOption (SeedPacket, &Count, &SeedOptions);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (SeedOptions != NULL) {
+ for (Index = 0; Index < (UINT32) Count; Index++) {
+ Mark[SeedOptions[Index].Tag] = SeedOptions[Index];
+ }
+ }
+
+ //
+ // Mark the option's length is zero if it is in the DeleteList.
+ //
+ for (Index = 0; Index < DeleteCount; Index++) {
+ Mark[DeleteList[Index]].Len = 0;
+ }
+
+ //
+ // Add or replace the option if it is in the append list.
+ //
+ for (Index = 0; Index < AppendCount; Index++) {
+ Mark[AppendList[Index]->OpCode].Len = AppendList[Index]->Length;
+ Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;
+ }
+
+ //
+ // compute the new packet length. No need to add 1 byte for
+ // EOP option since EFI_DHCP4_PACKET includes one extra byte
+ // for option. It is necessary to split the option if it is
+ // longer than 255 bytes.
+ //
+ Len = sizeof (EFI_DHCP4_PACKET);
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (Mark[Index].Len != 0) {
+ Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;
+ }
+ }
+
+ Status = EFI_OUT_OF_RESOURCES;
+ Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);
+
+ if (Packet == NULL) {
+ goto ON_ERROR;
+ }
+
+ Packet->Size = Len;
+ Packet->Length = 0;
+ CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header));
+ Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
+ Buf = Packet->Dhcp4.Option;
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (Mark[Index].Len != 0) {
+ Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);
+ }
+ }
+
+ *(Buf++) = DHCP4_TAG_EOP;
+ Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)
+ + (UINT32) (Buf - Packet->Dhcp4.Option);
+
+ *NewPacket = Packet;
+ Status = EFI_SUCCESS;
+
+ON_ERROR:
+ if (SeedOptions != NULL) {
+ FreePool (SeedOptions);
+ }
+
+ FreePool (Mark);
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h
new file mode 100644
index 0000000000..02ffa25aba
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h
@@ -0,0 +1,234 @@
+/** @file
+ To validate, parse and process the DHCP options.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP4_OPTION_H__
+#define __EFI_DHCP4_OPTION_H__
+
+///
+/// DHCP option tags (types)
+///
+
+#define DHCP_OPTION_MAGIC 0x63538263 // Network byte order
+#define DHCP_MAX_OPTIONS 256
+
+
+//
+// DHCP option types, this is used to validate the DHCP options.
+//
+#define DHCP_OPTION_SWITCH 1
+#define DHCP_OPTION_INT8 2
+#define DHCP_OPTION_INT16 3
+#define DHCP_OPTION_INT32 4
+#define DHCP_OPTION_IP 5
+#define DHCP_OPTION_IPPAIR 6
+
+//
+// Value of DHCP overload option
+//
+#define DHCP_OVERLOAD_FILENAME 1
+#define DHCP_OVERLOAD_SVRNAME 2
+#define DHCP_OVERLOAD_BOTH 3
+
+///
+/// The DHCP option structure. This structure extends the EFI_DHCP_OPTION
+/// structure to support options longer than 255 bytes, such as classless route.
+///
+typedef struct {
+ UINT8 Tag;
+ UINT16 Len;
+ UINT8 *Data;
+} DHCP_OPTION;
+
+///
+/// Structures used to parse the DHCP options with RFC3396 support.
+///
+typedef struct {
+ UINT8 Index;
+ UINT16 Offset;
+} DHCP_OPTION_COUNT;
+
+typedef struct {
+ DHCP_OPTION_COUNT *OpCount;
+ DHCP_OPTION *Options;
+ UINT8 *Buf;
+} DHCP_OPTION_CONTEXT;
+
+///
+/// The options that matters to DHCP driver itself. The user of
+/// DHCP clients may be interested in other options, such as
+/// classless route, who can parse the DHCP offer to get them.
+///
+typedef struct {
+ IP4_ADDR NetMask; // DHCP4_TAG_NETMASK
+ IP4_ADDR Router; // DHCP4_TAG_ROUTER, only the first router is used
+
+ //
+ // DHCP specific options
+ //
+ UINT8 DhcpType; // DHCP4_TAG_MSG_TYPE
+ UINT8 Overload; // DHCP4_TAG_OVERLOAD
+ IP4_ADDR ServerId; // DHCP4_TAG_SERVER_ID
+ UINT32 Lease; // DHCP4_TAG_LEASE
+ UINT32 T1; // DHCP4_TAG_T1
+ UINT32 T2; // DHCP4_TAG_T2
+} DHCP_PARAMETER;
+
+///
+/// Structure used to describe and validate the format of DHCP options.
+/// Type is the options' data type, such as DHCP_OPTION_INT8. MinOccur
+/// is the minium occurance of this data type. MaxOccur is defined
+/// similarly. If MaxOccur is -1, it means that there is no limit on the
+/// maximum occurance. Alert tells whether DHCP client should further
+/// inspect the option to parse DHCP_PARAMETER.
+///
+typedef struct {
+ UINT8 Tag;
+ INTN Type;
+ INTN MinOccur;
+ INTN MaxOccur;
+ BOOLEAN Alert;
+} DHCP_OPTION_FORMAT;
+
+typedef
+EFI_STATUS
+(*DHCP_CHECK_OPTION) (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ );
+
+/**
+ Iterate through a DHCP message to visit each option. First inspect
+ all the options in the OPTION field. Then if overloaded, inspect
+ the options in FILENAME and SERVERNAME fields. One option may be
+ encoded in several places. See RFC 3396 Encoding Long Options in DHCP
+
+ @param[in] Packet The DHCP packet to check the options for
+ @param[in] Check The callback function to be called for each option
+ found
+ @param[in] Context The opaque parameter for Check
+
+ @retval EFI_SUCCESS The DHCP packet's options are well formated
+ @retval EFI_INVALID_PARAMETER The DHCP packet's options are not well formated
+
+**/
+EFI_STATUS
+DhcpIterateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_CHECK_OPTION Check OPTIONAL,
+ IN VOID *Context
+ );
+
+/**
+ Validate the packet's options. If necessary, allocate
+ and fill in the interested parameters.
+
+ @param[in] Packet The packet to validate the options
+ @param[out] Para The variable to save the DHCP parameters.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formated
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpValidateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT DHCP_PARAMETER **Para OPTIONAL
+ );
+
+/**
+ Parse the options of a DHCP packet. It supports RFC 3396: Encoding
+ Long Options in DHCP. That is, it will combine all the option value
+ of all the occurances of each option.
+ A little bit of implemenation:
+ It adopts the "Key indexed counting" algorithm. First, it allocates
+ an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
+ as a UINT8. It then iterates the DHCP packet to get data length of
+ each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
+ knows the number of present options and their length. It allocates a
+ array of DHCP_OPTION and a continuous buffer after the array to put
+ all the options' data. Each option's data is pointed to by the Data
+ field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
+ with DhcpFillOption to fill each option's data to its position in the
+ buffer.
+
+ @param[in] Packet The DHCP packet to parse the options
+ @param[out] Count The number of valid dhcp options present in the
+ packet
+ @param[out] OptionPoint The array that contains the DHCP options. Caller
+ should free it.
+
+ @retval EFI_NOT_FOUND Cannot find any option.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formated
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpParseOption (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT INTN *Count,
+ OUT DHCP_OPTION **OptionPoint
+ );
+
+/**
+ Append an option to the memory, if the option is longer than
+ 255 bytes, splits it into several options.
+
+ @param[out] Buf The buffer to append the option to
+ @param[in] Tag The option's tag
+ @param[in] DataLen The length of the option's data
+ @param[in] Data The option's data
+
+ @return The position to append the next option
+
+**/
+UINT8 *
+DhcpAppendOption (
+ OUT UINT8 *Buf,
+ IN UINT8 Tag,
+ IN UINT16 DataLen,
+ IN UINT8 *Data
+ );
+
+/**
+ Build a new DHCP packet from a seed packet. Options may be deleted or
+ appended. The caller should free the NewPacket when finished using it.
+
+ @param[in] SeedPacket The seed packet to start with
+ @param[in] DeleteCount The number of options to delete
+ @param[in] DeleteList The options to delete from the packet
+ @param[in] AppendCount The number of options to append
+ @param[in] AppendList The options to append to the packet
+ @param[out] NewPacket The new packet, allocated and built by this
+ function.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_INVALID_PARAMETER The options in SeekPacket are mal-formated
+ @retval EFI_SUCCESS The packet is build.
+
+**/
+EFI_STATUS
+DhcpBuild (
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.c b/Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.c
new file mode 100644
index 0000000000..c605f721eb
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.c
@@ -0,0 +1,347 @@
+/** @file
+
+Copyright (c) 2007 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dpc.c
+
+Abstract:
+
+
+**/
+
+#include "Dpc.h"
+
+//
+// Handle for the EFI_DPC_PROTOCOL instance
+//
+EFI_HANDLE mDpcHandle = NULL;
+
+//
+// The EFI_DPC_PROTOCOL instances that is installed onto mDpcHandle
+//
+EFI_DPC_PROTOCOL mDpc = {
+ DpcQueueDpc,
+ DpcDispatchDpc
+};
+
+//
+// Global variables used to meaasure the DPC Queue Depths
+//
+UINTN mDpcQueueDepth = 0;
+UINTN mMaxDpcQueueDepth = 0;
+
+//
+// Free list of DPC entries. As DPCs are queued, entries are removed from this
+// free list. As DPC entries are dispatched, DPC entries are added to the free list.
+// If the free list is empty and a DPC is queued, the free list is grown by allocating
+// an additional set of DPC entries.
+//
+LIST_ENTRY mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE(mDpcEntryFreeList);
+
+//
+// An array of DPC queues. A DPC queue is allocated for every leval EFI_TPL value.
+// As DPCs are queued, they are added to the end of the linked list.
+// As DPCs are dispatched, they are removed from the beginning of the linked list.
+//
+LIST_ENTRY mDpcQueue[TPL_HIGH_LEVEL + 1];
+
+/**
+ Add a Deferred Procedure Call to the end of the DPC queue.
+
+ @param This Protocol instance pointer.
+ @param DpcTpl The EFI_TPL that the DPC should be invoked.
+ @param DpcProcedure Pointer to the DPC's function.
+ @param DpcContext Pointer to the DPC's context. Passed to DpcProcedure
+ when DpcProcedure is invoked.
+
+ @retval EFI_SUCCESS The DPC was queued.
+ @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL.
+ @retval EFI_INVALID_PARAMETER DpcProcedure is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ add the DPC to the queue.
+
+**/
+EFI_STATUS
+EFIAPI
+DpcQueueDpc (
+ IN EFI_DPC_PROTOCOL *This,
+ IN EFI_TPL DpcTpl,
+ IN EFI_DPC_PROCEDURE DpcProcedure,
+ IN VOID *DpcContext OPTIONAL
+ )
+{
+ EFI_STATUS ReturnStatus;
+ EFI_TPL OriginalTpl;
+ DPC_ENTRY *DpcEntry;
+ UINTN Index;
+
+ //
+ // Make sure DpcTpl is valid
+ //
+ if (DpcTpl < TPL_APPLICATION || DpcTpl > TPL_HIGH_LEVEL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure DpcProcedure is valid
+ //
+ if (DpcProcedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Assume this function will succeed
+ //
+ ReturnStatus = EFI_SUCCESS;
+
+ //
+ // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the
+ // current TPL value so it can be restored when this function returns.
+ //
+ OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+ //
+ // Check to see if there are any entries in the DPC free list
+ //
+ if (IsListEmpty (&mDpcEntryFreeList)) {
+ //
+ // If the current TPL is greater than TPL_NOTIFY, then memory allocations
+ // can not be performed, so the free list can not be expanded. In this case
+ // return EFI_OUT_OF_RESOURCES.
+ //
+ if (OriginalTpl > TPL_NOTIFY) {
+ ReturnStatus = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ //
+ // Add 64 DPC entries to the free list
+ //
+ for (Index = 0; Index < 64; Index++) {
+ //
+ // Lower the TPL level to perform a memory allocation
+ //
+ gBS->RestoreTPL (OriginalTpl);
+
+ //
+ // Allocate a new DPC entry
+ //
+ DpcEntry = AllocatePool (sizeof (DPC_ENTRY));
+
+ //
+ // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
+ //
+ gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+ //
+ // If the allocation of a DPC entry fails, and the free list is empty,
+ // then return EFI_OUT_OF_RESOURCES.
+ //
+ if (DpcEntry == NULL) {
+ if (IsListEmpty (&mDpcEntryFreeList)) {
+ ReturnStatus = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+ }
+
+ //
+ // Add the newly allocated DPC entry to the DPC free list
+ //
+ InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
+ }
+ }
+
+ //
+ // Retrieve the first node from the free list of DPCs
+ //
+ DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));
+
+ //
+ // Remove the first node from the free list of DPCs
+ //
+ RemoveEntryList (&DpcEntry->ListEntry);
+
+ //
+ // Fill in the DPC entry with the DpcProcedure and DpcContext
+ //
+ DpcEntry->DpcProcedure = DpcProcedure;
+ DpcEntry->DpcContext = DpcContext;
+
+ //
+ // Add the DPC entry to the end of the list for the specified DplTpl.
+ //
+ InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);
+
+ //
+ // Increment the measured DPC queue depth across all TPLs
+ //
+ mDpcQueueDepth++;
+
+ //
+ // Measure the maximum DPC queue depth across all TPLs
+ //
+ if (mDpcQueueDepth > mMaxDpcQueueDepth) {
+ mMaxDpcQueueDepth = mDpcQueueDepth;
+ }
+
+Done:
+ //
+ // Restore the original TPL level when this function was called
+ //
+ gBS->RestoreTPL (OriginalTpl);
+
+ return ReturnStatus;
+}
+
+/**
+ Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl
+ value greater than or equal to the current TPL are invoked in the order that
+ they were queued. DPCs with higher DpcTpl values are invoked before DPCs with
+ lower DpcTpl values.
+
+ @param This Protocol instance pointer.
+
+ @retval EFI_SUCCESS One or more DPCs were invoked.
+ @retval EFI_NOT_FOUND No DPCs were invoked.
+
+**/
+EFI_STATUS
+EFIAPI
+DpcDispatchDpc (
+ IN EFI_DPC_PROTOCOL *This
+ )
+{
+ EFI_STATUS ReturnStatus;
+ EFI_TPL OriginalTpl;
+ EFI_TPL Tpl;
+ DPC_ENTRY *DpcEntry;
+
+ //
+ // Assume that no DPCs will be invoked
+ //
+ ReturnStatus = EFI_NOT_FOUND;
+
+ //
+ // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the
+ // current TPL value so it can be restored when this function returns.
+ //
+ OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+ //
+ // Check to see if there are 1 or more DPCs currently queued
+ //
+ if (mDpcQueueDepth > 0) {
+ //
+ // Loop from TPL_HIGH_LEVEL down to the current TPL value
+ //
+ for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {
+ //
+ // Check to see if the DPC queue is empty
+ //
+ while (!IsListEmpty (&mDpcQueue[Tpl])) {
+ //
+ // Retrieve the first DPC entry from the DPC queue specified by Tpl
+ //
+ DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));
+
+ //
+ // Remove the first DPC entry from the DPC queue specified by Tpl
+ //
+ RemoveEntryList (&DpcEntry->ListEntry);
+
+ //
+ // Decrement the measured DPC Queue Depth across all TPLs
+ //
+ mDpcQueueDepth--;
+
+ //
+ // Lower the TPL to TPL value of the current DPC queue
+ //
+ gBS->RestoreTPL (Tpl);
+
+ //
+ // Invoke the DPC passing in its context
+ //
+ (DpcEntry->DpcProcedure) (DpcEntry->DpcContext);
+
+ //
+ // At least one DPC has been invoked, so set the return status to EFI_SUCCESS
+ //
+ ReturnStatus = EFI_SUCCESS;
+
+ //
+ // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
+ //
+ gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+ //
+ // Add the invoked DPC entry to the DPC free list
+ //
+ InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
+ }
+ }
+ }
+
+ //
+ // Restore the original TPL level when this function was called
+ //
+ gBS->RestoreTPL (OriginalTpl);
+
+ return ReturnStatus;
+}
+
+/**
+ The entry point for DPC driver which installs the EFI_DPC_PROTOCOL onto a new handle.
+
+ @param ImageHandle The image handle of the driver.
+ @param SystemTable The system table.
+
+ @retval EFI_SUCCES The DPC queues were initialized and the EFI_DPC_PROTOCOL was
+ installed onto a new handle.
+ @retval Others Failed to install EFI_DPC_PROTOCOL.
+
+**/
+EFI_STATUS
+EFIAPI
+DpcDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+
+ //
+ // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database
+ //
+ ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiDpcProtocolGuid);
+
+ //
+ // Initialize the DPC queue for all possible TPL values
+ //
+ for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {
+ InitializeListHead (&mDpcQueue[Index]);
+ }
+
+ //
+ // Install the EFI_DPC_PROTOCOL instance onto a new handle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mDpcHandle,
+ &gEfiDpcProtocolGuid,
+ &mDpc,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.h b/Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.h
new file mode 100644
index 0000000000..443bc3904e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/Dpc.h
@@ -0,0 +1,86 @@
+/** @file
+
+Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dpc.h
+
+Abstract:
+
+
+**/
+
+#ifndef _DPC_H_
+#define _DPC_H_
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Protocol/Dpc.h>
+
+//
+// Internal data struture for managing DPCs. A DPC entry is either on the free
+// list or on a DPC queue at a specific EFI_TPL.
+//
+typedef struct {
+ LIST_ENTRY ListEntry;
+ EFI_DPC_PROCEDURE DpcProcedure;
+ VOID *DpcContext;
+} DPC_ENTRY;
+
+/**
+ Add a Deferred Procedure Call to the end of the DPC queue.
+
+ @param This Protocol instance pointer.
+ @param DpcTpl The EFI_TPL that the DPC should be invoked.
+ @param DpcProcedure Pointer to the DPC's function.
+ @param DpcContext Pointer to the DPC's context. Passed to DpcProcedure
+ when DpcProcedure is invoked.
+
+ @retval EFI_SUCCESS The DPC was queued.
+ @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL.
+ @retval EFI_INVALID_PARAMETER DpcProcedure is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ add the DPC to the queue.
+
+**/
+EFI_STATUS
+EFIAPI
+DpcQueueDpc (
+ IN EFI_DPC_PROTOCOL *This,
+ IN EFI_TPL DpcTpl,
+ IN EFI_DPC_PROCEDURE DpcProcedure,
+ IN VOID *DpcContext OPTIONAL
+ );
+
+/**
+ Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl
+ value greater than or equal to the current TPL are invoked in the order that
+ they were queued. DPCs with higher DpcTpl values are invoked before DPCs with
+ lower DpcTpl values.
+
+ @param This Protocol instance pointer.
+
+ @retval EFI_SUCCESS One or more DPCs were invoked.
+ @retval EFI_NOT_FOUND No DPCs were invoked.
+
+**/
+EFI_STATUS
+EFIAPI
+DpcDispatchDpc (
+ IN EFI_DPC_PROTOCOL *This
+ );
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.inf b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.inf
new file mode 100644
index 0000000000..8a1f8baf79
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.inf
@@ -0,0 +1,52 @@
+## @file
+# This module produces Deferred Procedure Call Protocol.
+#
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DpcDxe
+ MODULE_UNI_FILE = DpcDxe.uni
+ FILE_GUID = A210F973-229D-4f4d-AA37-9895E6C9EABA
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = DpcDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ Dpc.c
+ Dpc.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+
+[Protocols]
+ gEfiDpcProtocolGuid ## PRODUCES
+
+[Depex]
+ TRUE
+[UserExtensions.TianoCore."ExtraFiles"]
+ DpcDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.uni b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.uni
new file mode 100644
index 0000000000..db33b33b40
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.uni
@@ -0,0 +1,22 @@
+// /** @file
+// This module produces Deferred Procedure Call Protocol.
+//
+// This module produces Deferred Procedure Call Protocol.
+//
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces Deferred Procedure Call Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces Deferred Procedure Call Protocol."
+
diff --git a/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni
new file mode 100644
index 0000000000..7c57022cb8
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// DpcDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Deferred Procedure Call DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.c
new file mode 100644
index 0000000000..063b372292
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.c
@@ -0,0 +1,283 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for iSCSI.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName = {
+ IScsiComponentNameGetDriverName,
+ IScsiComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IScsiComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IScsiComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIScsiDriverNameTable[] = {
+ {"eng;en", L"iSCSI Driver"},
+ {NULL, NULL}
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *mIScsiControllerNameTable = NULL;
+
+/**
+ Retrieves a Unicode string that is the user readable name of the EFI 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_NAME_PROTOCOL instance.
+ @param[in] Language A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that 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.
+ @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
+IScsiComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mIScsiDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gIScsiComponentName)
+ );
+}
+
+/**
+ Update the component name for the iSCSI instance.
+
+ @param[in] IScsiExtScsiPassThru A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_UNSUPPORTED Can't get the corresponding NIC info from the Controller handle.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *IScsiExtScsiPassThru
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[150];
+ ISCSI_DRIVER_DATA *Private;
+ EFI_MAC_ADDRESS MacAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 MacString[70];
+
+ if (IScsiExtScsiPassThru == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (IScsiExtScsiPassThru);
+
+ //
+ // Get the mac string, it's the name of various variable
+ //
+ Status = NetLibGetMacAddress (Private->Controller, &MacAddress, &HwAddressSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ VlanId = NetLibGetVlanId (Private->Controller);
+ IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, MacString);
+
+ UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"iSCSI IPv4 (MacString=%s)",
+ MacString
+ );
+
+ if (mIScsiControllerNameTable != NULL) {
+ FreeUnicodeStringTable (mIScsiControllerNameTable);
+ mIScsiControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gIScsiComponentName.SupportedLanguages,
+ &mIScsiControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gIScsiComponentName2.SupportedLanguages,
+ &mIScsiControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.Currently not implemented.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] ControllerHandle 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 three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that 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.
+ @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 NULL.
+ @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
+IScsiComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+
+ EFI_HANDLE IScsiController;
+ ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
+
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *IScsiExtScsiPassThru;
+
+ if (ControllerHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Get the handle of the controller we are controling.
+ //
+ IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);
+ if (IScsiController == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ IScsiController,
+ &gEfiCallerIdGuid,
+ (VOID **)&IScsiIdentifier,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (ChildHandle != NULL) {
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **)&IScsiExtScsiPassThru,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (IScsiExtScsiPassThru);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ return Status;
+ }
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mIScsiControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gIScsiComponentName)
+ );
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.h
new file mode 100644
index 0000000000..b400abaa41
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/ComponentName.h
@@ -0,0 +1,165 @@
+/** @file
+ The header file of UEFI Component Name(2) protocol.
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _COMPONENT_NAME_H_
+#define _COMPONENT_NAME_H_
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName;
+
+//
+// EFI Component Name Protocol for iSCSI driver.
+//
+
+/**
+ Retrieves a Unicode string that is the user readable name of the EFI 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_NAME_PROTOCOL instance.
+ @param[in] Language A pointer to a three characters ISO 639-2 language identifier.
+ This is the language of the driver name that 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.
+ @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
+IScsiComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver. Currently not implemented.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] ControllerHandle 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 three characters ISO 639-2 language
+ identifier. This is the language of the controller name
+ that 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.
+ @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 NULL.
+ @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
+IScsiComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI iSCSI Initiator Name Protocol for IScsi driver.
+//
+
+/**
+ Retrieves the current set value of iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer / Actual size of the
+ variable data buffer.
+ @param[out] Buffer Pointer to the buffer for data to be read.
+
+ @retval EFI_SUCCESS Data was successfully retrieved into the provided buffer and the
+ BufferSize was sufficient to handle the iSCSI initiator name
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result.
+ @retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL.
+ @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved due to a hardware error.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+/**
+ Sets the iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer.
+ @param[in] Buffer Pointer to the buffer for data to be written.
+
+ @retval EFI_SUCCESS Data was successfully stored by the protocol.
+ @retval EFI_UNSUPPORTED Platform policies do not allow for data to be written.
+ Currently not implemented.
+ @retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL, or BufferSize exceeds the maximum allowed limit.
+ @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware error.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data.
+ @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC 3720
+ (and other related protocols)
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4Dxe.uni b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4Dxe.uni
new file mode 100644
index 0000000000..80bdc2c7e2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4Dxe.uni
@@ -0,0 +1,25 @@
+// /** @file
+// This module produces EFI iSCSI Initiator Name Protocol.
+//
+// This module produces EFI iSCSI Initiator Name Protocol upon EFI TCPv4 Protocol
+// and EFI DHCPv4 Protocol, to provide the capability to do the transport for SCSI
+// data over TCP/IP. It installs EFI HII Configuration Access Protocol to provide
+// one way to configurate the iSCSI setting.
+//
+// Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI iSCSI Initiator Name Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI iSCSI Initiator Name Protocol upon EFI TCPv4 Protocol and EFI DHCPv4 Protocol, to provide the capability to do the transport for SCSI data over TCP/IP. It installs EFI HII Configuration Access Protocol to provide a way to configure the iSCSI setting."
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni
new file mode 100644
index 0000000000..9b717d49d3
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// IScsi4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"iSCSI DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.c
new file mode 100644
index 0000000000..6307684ff0
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.c
@@ -0,0 +1,430 @@
+/** @file
+ This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+#include "Md5.h"
+
+/**
+ Initator calculates its own expected hash value.
+
+ @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
+ @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
+ @param[in] SecretLength The length of iSCSI CHAP secret.
+ @param[in] ChapChallenge The challenge message sent by authenticator.
+ @param[in] ChallengeLength The length of iSCSI CHAP challenge message.
+ @param[out] ChapResponse The calculation of the expected hash value.
+
+ @retval EFI_SUCCESS The expected hash value was calculatedly successfully.
+ @retval EFI_PROTOCOL_ERROR The length of the secret should be at least the
+ length of the hash value for the hashing algorithm chosen.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiCHAPCalculateResponse (
+ IN UINT32 ChapIdentifier,
+ IN CHAR8 *ChapSecret,
+ IN UINT32 SecretLength,
+ IN UINT8 *ChapChallenge,
+ IN UINT32 ChallengeLength,
+ OUT UINT8 *ChapResponse
+ )
+{
+ MD5_CTX Md5Ctx;
+ CHAR8 IdByte[1];
+ EFI_STATUS Status;
+
+ Status = MD5Init (&Md5Ctx);
+
+ //
+ // Hash Identifier - Only calculate 1 byte data (RFC1994)
+ //
+ IdByte[0] = (CHAR8) ChapIdentifier;
+ MD5Update (&Md5Ctx, IdByte, 1);
+
+ //
+ // Hash Secret
+ //
+ if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN - 1) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ MD5Update (&Md5Ctx, ChapSecret, SecretLength);
+
+ //
+ // Hash Challenge received from Target
+ //
+ MD5Update (&Md5Ctx, ChapChallenge, ChallengeLength);
+
+ Status = MD5Final (&Md5Ctx, ChapResponse);
+
+ return Status;
+}
+
+/**
+ The initator checks the CHAP response replied by target against its own
+ calculation of the expected hash value.
+
+ @param[in] AuthData iSCSI CHAP authentication data.
+ @param[in] TargetResponse The response from target.
+
+ @retval EFI_SUCCESS The response from target passed authentication.
+ @retval EFI_SECURITY_VIOLATION The response from target was not expected value.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiCHAPAuthTarget (
+ IN ISCSI_CHAP_AUTH_DATA *AuthData,
+ IN UINT8 *TargetResponse
+ )
+{
+ EFI_STATUS Status;
+ UINT32 SecretSize;
+ UINT8 VerifyRsp[ISCSI_CHAP_RSP_LEN];
+
+ Status = EFI_SUCCESS;
+
+ SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig.ReverseCHAPSecret);
+ Status = IScsiCHAPCalculateResponse (
+ AuthData->OutIdentifier,
+ AuthData->AuthConfig.ReverseCHAPSecret,
+ SecretSize,
+ AuthData->OutChallenge,
+ AuthData->OutChallengeLength,
+ VerifyRsp
+ );
+
+ if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {
+ Status = EFI_SECURITY_VIOLATION;
+ }
+
+ return Status;
+}
+
+/**
+ This function checks the received iSCSI Login Response during the security
+ negotiation stage.
+
+ @param[in] Conn The iSCSI connection.
+
+ @retval EFI_SUCCESS The Login Response passed the CHAP validation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error happend.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiCHAPOnRspReceived (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_CHAP_AUTH_DATA *AuthData;
+ CHAR8 *Value;
+ UINT8 *Data;
+ UINT32 Len;
+ LIST_ENTRY *KeyValueList;
+ UINTN Algorithm;
+ CHAR8 *Identifier;
+ CHAR8 *Challenge;
+ CHAR8 *Name;
+ CHAR8 *Response;
+ UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN];
+ UINT32 RspLen;
+
+ ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
+ ASSERT (Conn->RspQue.BufNum != 0);
+
+ Session = Conn->Session;
+ AuthData = &Session->AuthData;
+
+ Len = Conn->RspQue.BufSize;
+ Data = AllocatePool (Len);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Copy the data in case the data spans over multiple PDUs.
+ //
+ NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
+
+ //
+ // Build the key-value list from the data segment of the Login Response.
+ //
+ KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);
+ if (KeyValueList == NULL) {
+ FreePool (Data);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_PROTOCOL_ERROR;
+
+ switch (Conn->CHAPStep) {
+ case ISCSI_CHAP_INITIAL:
+ //
+ // The first Login Response.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
+ if (Value == NULL) {
+ goto ON_EXIT;
+ }
+
+ Session->TargetPortalGroupTag = (UINT16) AsciiStrDecimalToUintn (Value);
+
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);
+ if (Value == NULL) {
+ goto ON_EXIT;
+ }
+ //
+ // Initiator mandates CHAP authentication but target replies without "CHAP" or
+ // initiator suggets "None" but target replies with some kind of auth method.
+ //
+ if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) == 0) {
+ if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {
+ goto ON_EXIT;
+ }
+ } else {
+ if (AuthData->AuthConfig.CHAPType != ISCSI_CHAP_NONE) {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Transit to CHAP step one.
+ //
+ Conn->CHAPStep = ISCSI_CHAP_STEP_ONE;
+ Status = EFI_SUCCESS;
+ break;
+
+ case ISCSI_CHAP_STEP_TWO:
+ //
+ // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);
+ if (Value == NULL) {
+ goto ON_EXIT;
+ }
+
+ Algorithm = AsciiStrDecimalToUintn (Value);
+ if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {
+ //
+ // Unsupported algorithm is chosen by target.
+ //
+ goto ON_EXIT;
+ }
+
+ Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);
+ if (Identifier == NULL) {
+ goto ON_EXIT;
+ }
+
+ Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);
+ if (Challenge == NULL) {
+ goto ON_EXIT;
+ }
+ //
+ // Process the CHAP identifier and CHAP Challenge from Target
+ // Calculate Response value
+ //
+ AuthData->InIdentifier = (UINT32) AsciiStrDecimalToUintn (Identifier);
+ AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;
+ IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);
+ Status = IScsiCHAPCalculateResponse (
+ AuthData->InIdentifier,
+ AuthData->AuthConfig.CHAPSecret,
+ (UINT32) AsciiStrLen (AuthData->AuthConfig.CHAPSecret),
+ AuthData->InChallenge,
+ AuthData->InChallengeLength,
+ AuthData->CHAPResponse
+ );
+
+ //
+ // Transit to next step.
+ //
+ Conn->CHAPStep = ISCSI_CHAP_STEP_THREE;
+ break;
+
+ case ISCSI_CHAP_STEP_THREE:
+ //
+ // one way CHAP authentication and the target would like to
+ // authenticate us.
+ //
+ Status = EFI_SUCCESS;
+ break;
+
+ case ISCSI_CHAP_STEP_FOUR:
+ ASSERT (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL);
+ //
+ // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
+ //
+ Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
+ if (Name == NULL) {
+ goto ON_EXIT;
+ }
+
+ Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);
+ if (Response == NULL) {
+ goto ON_EXIT;
+ }
+
+ RspLen = ISCSI_CHAP_RSP_LEN;
+ IScsiHexToBin (TargetRsp, &RspLen, Response);
+
+ //
+ // Check the CHAP Response replied by Target.
+ //
+ Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
+ break;
+
+ default:
+ break;
+ }
+
+ON_EXIT:
+
+ IScsiFreeKeyValueList (KeyValueList);
+
+ FreePool (Data);
+
+ return Status;
+}
+
+/**
+ This function fills the CHAP authentication information into the login PDU
+ during the security negotiation stage in the iSCSI connection login.
+
+ @param[in] Conn The iSCSI connection.
+ @param[in, out] Pdu The PDU to send out.
+
+ @retval EFI_SUCCESS All check passed and the phase-related CHAP
+ authentication info is filled into the iSCSI PDU.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error happend.
+**/
+EFI_STATUS
+IScsiCHAPToSendReq (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_LOGIN_REQUEST *LoginReq;
+ ISCSI_CHAP_AUTH_DATA *AuthData;
+ CHAR8 *Value;
+ CHAR8 ValueStr[256];
+ CHAR8 *Response;
+ UINT32 RspLen;
+ CHAR8 *Challenge;
+ UINT32 ChallengeLen;
+
+ ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
+
+ Session = Conn->Session;
+ AuthData = &Session->AuthData;
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);
+ if (LoginReq == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ Status = EFI_SUCCESS;
+
+ RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
+ Response = AllocatePool (RspLen);
+ if (Response == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
+ Challenge = AllocatePool (ChallengeLen);
+ if (Challenge == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ switch (Conn->CHAPStep) {
+ case ISCSI_CHAP_INITIAL:
+ //
+ // It's the initial Login Request. Fill in the key=value pairs mandatory
+ // for the initial Login Request.
+ //
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, Session->InitiatorName);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_TARGET_NAME, Session->ConfigData.NvData.TargetName);
+
+ if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {
+ Value = ISCSI_KEY_VALUE_NONE;
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ } else {
+ Value = ISCSI_AUTH_METHOD_CHAP;
+ }
+
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
+
+ break;
+
+ case ISCSI_CHAP_STEP_ONE:
+ //
+ // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.
+ //
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
+
+ Conn->CHAPStep = ISCSI_CHAP_STEP_TWO;
+ break;
+
+ case ISCSI_CHAP_STEP_THREE:
+ //
+ // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
+ // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target ahtentication is
+ // required too.
+ //
+ // CHAP_N=<N>
+ //
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig.CHAPName);
+ //
+ // CHAP_R=<R>
+ //
+ IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
+
+ if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL) {
+ //
+ // CHAP_I=<I>
+ //
+ IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
+ //
+ // CHAP_C=<C>
+ //
+ IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
+ AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;
+ IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
+
+ Conn->CHAPStep = ISCSI_CHAP_STEP_FOUR;
+ }
+ //
+ // set the stage transition flag.
+ //
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ break;
+
+ default:
+ Status = EFI_PROTOCOL_ERROR;
+ break;
+ }
+
+ FreePool (Response);
+ FreePool (Challenge);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.h
new file mode 100644
index 0000000000..f6a64e56da
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.h
@@ -0,0 +1,106 @@
+/** @file
+ The header file of CHAP configuration.
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_CHAP_H_
+#define _ISCSI_CHAP_H_
+
+#define ISCSI_AUTH_METHOD_CHAP "CHAP"
+
+#define ISCSI_KEY_CHAP_ALGORITHM "CHAP_A"
+#define ISCSI_KEY_CHAP_IDENTIFIER "CHAP_I"
+#define ISCSI_KEY_CHAP_CHALLENGE "CHAP_C"
+#define ISCSI_KEY_CHAP_NAME "CHAP_N"
+#define ISCSI_KEY_CHAP_RESPONSE "CHAP_R"
+
+#define ISCSI_CHAP_ALGORITHM_MD5 5
+
+#define ISCSI_CHAP_AUTH_MAX_LEN 1024
+///
+/// MD5_HASHSIZE
+///
+#define ISCSI_CHAP_RSP_LEN 16
+
+#define ISCSI_CHAP_INITIAL 0
+#define ISCSI_CHAP_STEP_ONE 1
+#define ISCSI_CHAP_STEP_TWO 2
+#define ISCSI_CHAP_STEP_THREE 3
+#define ISCSI_CHAP_STEP_FOUR 4
+
+#pragma pack(1)
+
+typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA {
+ UINT8 CHAPType;
+ CHAR8 CHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR8 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+ CHAR8 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR8 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+} ISCSI_CHAP_AUTH_CONFIG_NVDATA;
+
+#pragma pack()
+
+///
+/// ISCSI CHAP Authentication Data
+///
+typedef struct _ISCSI_CHAP_AUTH_DATA {
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA AuthConfig;
+ UINT32 InIdentifier;
+ UINT8 InChallenge[ISCSI_CHAP_AUTH_MAX_LEN];
+ UINT32 InChallengeLength;
+ //
+ // Calculated CHAP Response (CHAP_R) value
+ //
+ UINT8 CHAPResponse[ISCSI_CHAP_RSP_LEN];
+
+ //
+ // Auth-data to be sent out for mutual authentication
+ //
+ UINT32 OutIdentifier;
+ UINT8 OutChallenge[ISCSI_CHAP_AUTH_MAX_LEN];
+ UINT32 OutChallengeLength;
+} ISCSI_CHAP_AUTH_DATA;
+
+/**
+ This function checks the received iSCSI Login Response during the security
+ negotiation stage.
+
+ @param[in] Conn The iSCSI connection.
+
+ @retval EFI_SUCCESS The Login Response passed the CHAP validation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error happend.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiCHAPOnRspReceived (
+ IN ISCSI_CONNECTION *Conn
+ );
+/**
+ This function fills the CHAP authentication information into the login PDU
+ during the security negotiation stage in the iSCSI connection login.
+
+ @param[in] Conn The iSCSI connection.
+ @param[in, out] Pdu The PDU to send out.
+
+ @retval EFI_SUCCESS All check passed and the phase-related CHAP
+ authentication info is filled into the iSCSI PDU.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error happend.
+**/
+EFI_STATUS
+IScsiCHAPToSendReq (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCommon.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCommon.h
new file mode 100644
index 0000000000..650c68d53b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiCommon.h
@@ -0,0 +1,22 @@
+/** @file
+ The common header file of Iscsi.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_COMMON_H_
+#define _ISCSI_COMMON_H_
+
+typedef struct _ISCSI_SESSION ISCSI_SESSION;
+typedef struct _ISCSI_CONNECTION ISCSI_CONNECTION;
+typedef struct _ISCSI_DRIVER_DATA ISCSI_DRIVER_DATA;
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.c
new file mode 100644
index 0000000000..ea727af1ff
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.c
@@ -0,0 +1,1264 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to iSCSI.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+CHAR16 mVendorStorageName[] = L"ISCSI_CONFIG_IFR_NVDATA";
+BOOLEAN mIScsiDeviceListUpdated = FALSE;
+UINTN mNumberOfIScsiDevices = 0;
+ISCSI_FORM_CALLBACK_INFO *mCallbackInfo = NULL;
+
+LIST_ENTRY mIScsiConfigFormList = {
+ &mIScsiConfigFormList,
+ &mIScsiConfigFormList
+};
+
+HII_VENDOR_DEVICE_PATH mIScsiHiiVendorDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ IP4_ISCSI_CONFIG_GUID
+ },
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ (UINT8) (END_DEVICE_PATH_LENGTH),
+ (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
+ }
+ }
+};
+
+/**
+ Convert the IPv4 address into a dotted string.
+
+ @param[in] Ip The IPv4 address.
+ @param[out] Str The dotted IP string.
+**/
+VOID
+IScsiIpToStr (
+ IN EFI_IPv4_ADDRESS *Ip,
+ OUT CHAR16 *Str
+ )
+{
+ UnicodeSPrint ( Str, 2 * IP4_STR_MAX_SIZE, L"%d.%d.%d.%d", Ip->Addr[0], Ip->Addr[1], Ip->Addr[2], Ip->Addr[3]);
+}
+
+
+/**
+ Parse IsId in string format and convert it to binary.
+
+ @param[in] String The buffer of the string to be parsed.
+ @param[in, out] IsId The buffer to store IsId.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+IScsiParseIsIdFromString (
+ IN CONST CHAR16 *String,
+ IN OUT UINT8 *IsId
+ )
+{
+ UINT8 Index;
+ CHAR16 *IsIdStr;
+ CHAR16 TempStr[3];
+ UINTN NodeVal;
+ CHAR16 PortString[ISCSI_NAME_IFR_MAX_SIZE];
+ EFI_INPUT_KEY Key;
+
+ if ((String == NULL) || (IsId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IsIdStr = (CHAR16 *) String;
+
+ if (StrLen (IsIdStr) != 6) {
+ UnicodeSPrint (
+ PortString,
+ (UINTN) sizeof (PortString),
+ L"Error! Input is incorrect, please input 6 hex numbers!\n"
+ );
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ PortString,
+ NULL
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 3; Index < 6; Index++) {
+ CopyMem (TempStr, IsIdStr, sizeof (TempStr));
+ TempStr[2] = L'\0';
+
+ //
+ // Convert the string to IsId. StrHexToUintn stops at the first character
+ // that is not a valid hex character, '\0' here.
+ //
+ NodeVal = StrHexToUintn (TempStr);
+
+ IsId[Index] = (UINT8) NodeVal;
+
+ IsIdStr = IsIdStr + 2;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert IsId from binary to string format.
+
+ @param[out] String The buffer to store the converted string.
+ @param[in] IsId The buffer to store IsId.
+
+ @retval EFI_SUCCESS The string converted successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+IScsiConvertIsIdToString (
+ OUT CHAR16 *String,
+ IN UINT8 *IsId
+ )
+{
+ UINT8 Index;
+ UINTN Number;
+
+ if ((String == NULL) || (IsId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < 6; Index++) {
+ if (IsId[Index] <= 0xF) {
+ Number = UnicodeSPrint (
+ String,
+ 2 * ISID_CONFIGURABLE_STORAGE,
+ L"0%X",
+ (UINTN) IsId[Index]
+ );
+ } else {
+ Number = UnicodeSPrint (
+ String,
+ 2 * ISID_CONFIGURABLE_STORAGE,
+ L"%X",
+ (UINTN) IsId[Index]
+ );
+
+ }
+
+ String = String + Number;
+ }
+
+ *String = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Update the list of iSCSI devices the iSCSI driver is controlling.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiUpdateDeviceList (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_DEVICE_LIST *DeviceList;
+ UINTN DataSize;
+ UINTN NumHandles;
+ EFI_HANDLE *Handles;
+ UINTN HandleIndex;
+ UINTN Index;
+ UINTN LastDeviceIndex;
+ EFI_MAC_ADDRESS MacAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ ISCSI_MAC_INFO *CurMacInfo;
+ ISCSI_MAC_INFO TempMacInfo;
+ CHAR16 MacString[70];
+ UINTN DeviceListSize;
+
+ //
+ // Dump all the handles the Managed Network Service Binding Protocol is installed on.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ &NumHandles,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DataSize = 0;
+ Status = gRT->GetVariable (
+ L"iSCSIDeviceList",
+ &gIp4IScsiConfigGuid,
+ NULL,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DeviceList = (ISCSI_DEVICE_LIST *) AllocatePool (DataSize);
+ ASSERT (DeviceList != NULL);
+
+ gRT->GetVariable (
+ L"iSCSIDeviceList",
+ &gIp4IScsiConfigGuid,
+ NULL,
+ &DataSize,
+ DeviceList
+ );
+
+ LastDeviceIndex = 0;
+
+ for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {
+ Status = NetLibGetMacAddress (Handles[HandleIndex], &MacAddress, &HwAddressSize);
+ ASSERT (Status == EFI_SUCCESS);
+ VlanId = NetLibGetVlanId (Handles[HandleIndex]);
+
+ for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {
+ CurMacInfo = &DeviceList->MacInfo[Index];
+ if ((CurMacInfo->Len == HwAddressSize) &&
+ (CurMacInfo->VlanId == VlanId) &&
+ (NET_MAC_EQUAL (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize))
+ ) {
+ //
+ // The previous configured NIC is still here.
+ //
+ if (Index != LastDeviceIndex) {
+ //
+ // Swap the current MAC address entry with the one indexed by
+ // LastDeviceIndex.
+ //
+ CopyMem (&TempMacInfo, CurMacInfo, sizeof (ISCSI_MAC_INFO));
+ CopyMem (CurMacInfo, &DeviceList->MacInfo[LastDeviceIndex], sizeof (ISCSI_MAC_INFO));
+ CopyMem (&DeviceList->MacInfo[LastDeviceIndex], &TempMacInfo, sizeof (ISCSI_MAC_INFO));
+ }
+
+ LastDeviceIndex++;
+ }
+ }
+
+ if (LastDeviceIndex == DeviceList->NumDevice) {
+ break;
+ }
+ }
+
+ for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {
+ //
+ // delete the variables
+ //
+ CurMacInfo = &DeviceList->MacInfo[Index];
+ IScsiMacAddrToStr (&CurMacInfo->Mac, CurMacInfo->Len, CurMacInfo->VlanId, MacString);
+ gRT->SetVariable (MacString, &gEfiIScsiInitiatorNameProtocolGuid, 0, 0, NULL);
+ gRT->SetVariable (MacString, &gIScsiCHAPAuthInfoGuid, 0, 0, NULL);
+ }
+
+ FreePool (DeviceList);
+ } else if (Status != EFI_NOT_FOUND) {
+ FreePool (Handles);
+ return Status;
+ }
+ //
+ // Construct the new iSCSI device list.
+ //
+ DeviceListSize = sizeof (ISCSI_DEVICE_LIST) + (NumHandles - 1) * sizeof (ISCSI_MAC_INFO);
+ DeviceList = (ISCSI_DEVICE_LIST *) AllocatePool (DeviceListSize);
+ ASSERT (DeviceList != NULL);
+ DeviceList->NumDevice = (UINT8) NumHandles;
+
+ for (Index = 0; Index < NumHandles; Index++) {
+ NetLibGetMacAddress (Handles[Index], &MacAddress, &HwAddressSize);
+
+ CurMacInfo = &DeviceList->MacInfo[Index];
+ CopyMem (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize);
+ CurMacInfo->Len = (UINT8) HwAddressSize;
+ CurMacInfo->VlanId = NetLibGetVlanId (Handles[Index]);
+ }
+
+ gRT->SetVariable (
+ L"iSCSIDeviceList",
+ &gIp4IScsiConfigGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ DeviceListSize,
+ DeviceList
+ );
+
+ FreePool (DeviceList);
+ FreePool (Handles);
+
+ return Status;
+}
+
+/**
+ Get the iSCSI configuration form entry by the index of the goto opcode actived.
+
+ @param[in] Index The 0-based index of the goto opcode actived.
+
+ @return The iSCSI configuration form entry found.
+**/
+ISCSI_CONFIG_FORM_ENTRY *
+IScsiGetConfigFormEntryByIndex (
+ IN UINT32 Index
+ )
+{
+ UINT32 CurrentIndex;
+ LIST_ENTRY *Entry;
+ ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;
+
+ CurrentIndex = 0;
+ ConfigFormEntry = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
+ if (CurrentIndex == Index) {
+ ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
+ break;
+ }
+
+ CurrentIndex++;
+ }
+
+ return ConfigFormEntry;
+}
+
+/**
+ Convert the iSCSI configuration data into the IFR data.
+
+ @param[in] ConfigFormEntry The iSCSI configuration form entry.
+ @param[out] IfrNvData The IFR nv data.
+
+**/
+VOID
+IScsiConvertDeviceConfigDataToIfrNvData (
+ IN ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry,
+ OUT ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ ISCSI_SESSION_CONFIG_NVDATA *SessionConfigData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;
+
+ //
+ // Normal session configuration parameters.
+ //
+ SessionConfigData = &ConfigFormEntry->SessionConfigData;
+ IfrNvData->Enabled = SessionConfigData->Enabled;
+
+ IfrNvData->InitiatorInfoFromDhcp = SessionConfigData->InitiatorInfoFromDhcp;
+ IfrNvData->TargetInfoFromDhcp = SessionConfigData->TargetInfoFromDhcp;
+ IfrNvData->TargetPort = SessionConfigData->TargetPort;
+
+ IScsiIpToStr (&SessionConfigData->LocalIp, IfrNvData->LocalIp);
+ IScsiIpToStr (&SessionConfigData->SubnetMask, IfrNvData->SubnetMask);
+ IScsiIpToStr (&SessionConfigData->Gateway, IfrNvData->Gateway);
+ IScsiIpToStr (&SessionConfigData->TargetIp, IfrNvData->TargetIp);
+
+ IScsiAsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName);
+
+ IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);
+
+ IScsiConvertIsIdToString (IfrNvData->IsId, SessionConfigData->IsId);
+
+ //
+ // CHAP authentication parameters.
+ //
+ AuthConfigData = &ConfigFormEntry->AuthConfigData;
+
+ IfrNvData->CHAPType = AuthConfigData->CHAPType;
+
+ IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName);
+ IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret);
+ IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName);
+ IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret);
+}
+
+/**
+ This function allows the caller to request the current
+ configuration for one or more named elements. The resulting
+ string is in <ConfigAltResp> format. Any and all alternative
+ configuration strings shall also be appended to the end of the
+ current configuration string. If they are, they must appear
+ after the current configuration. They must contain the same
+ routing (GUID, NAME, PATH) as the current configuration string.
+ They must have an additional description indicating the type of
+ alternative configuration the string represents,
+ "ALTCFG=<StringToken>". That <StringToken> (when
+ converted from Hex UNICODE to binary) is a reference to a
+ string in the associated string pack.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Request A null-terminated Unicode string in
+ <ConfigRequest> format. Note that this
+ includes the routing information as well as
+ the configurable name / value pairs. It is
+ invalid for this string to be in
+ <MultiConfigRequest> format.
+ @param[out] Progress On return, points to a character in the
+ Request string. Points to the string's null
+ terminator if request was successful. Points
+ to the most recent "&" before the first
+ failing name / value pair (or the beginning
+ of the string if the failure is in the first
+ name / value pair) if the request was not
+ successful.
+ @param[out] Results A null-terminated Unicode string in
+ <ConfigAltResp> format which has all values
+ filled in for the names in the Request string.
+ String to be allocated by the called function.
+
+ @retval EFI_SUCCESS The Results string is filled with the
+ values corresponding to all requested
+ names.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+ @retval EFI_INVALID_PARAMETER For example, passing in a NULL
+ for the Request parameter
+ would result in this type of
+ error. In this case, the
+ Progress parameter would be
+ set to NULL.
+ @retval EFI_NOT_FOUND Routing data doesn't match any
+ known driver. Progress set to the
+ first character in the routing header.
+ Note: There is no requirement that the
+ driver validate the routing data. It
+ must skip the <ConfigHdr> in order to
+ process the names.
+ @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set
+ to most recent & before the
+ error or the beginning of the
+ string.
+ @retval EFI_INVALID_PARAMETER Unknown name. Progress points
+ to the & before the name in
+ question.Currently not implemented.
+**/
+EFI_STATUS
+EFIAPI
+IScsiFormExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+ EFI_STATUS Status;
+ CHAR8 InitiatorName[ISCSI_NAME_MAX_SIZE];
+ UINTN BufferSize;
+ ISCSI_CONFIG_IFR_NVDATA *IfrNvData;
+ ISCSI_FORM_CALLBACK_INFO *Private;
+ EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ UINTN Size;
+
+ if (Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Request;
+ if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gIp4IScsiConfigGuid, mVendorStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ConfigRequestHdr = NULL;
+ ConfigRequest = NULL;
+ AllocatedRequest = FALSE;
+ Size = 0;
+
+ if (!mIScsiDeviceListUpdated) {
+ //
+ // Update the device list.
+ //
+ IScsiUpdateDeviceList ();
+ mIScsiDeviceListUpdated = TRUE;
+ }
+
+ Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
+ IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
+ ASSERT (IfrNvData != NULL);
+ if (Private->Current != NULL) {
+ IScsiConvertDeviceConfigDataToIfrNvData (Private->Current, IfrNvData);
+ }
+
+ BufferSize = ISCSI_NAME_MAX_SIZE;
+ Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);
+ if (EFI_ERROR (Status)) {
+ IfrNvData->InitiatorName[0] = L'\0';
+ } else {
+ IScsiAsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName);
+ }
+
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ HiiConfigRouting = Private->ConfigRouting;
+ BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
+ ConfigRequest = Request;
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
+ //
+ // Request has no request element, construct full request string.
+ // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
+ // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
+ //
+ ConfigRequestHdr = HiiConstructConfigHdr (&gIp4IScsiConfigGuid, mVendorStorageName, Private->DriverHandle);
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
+ ConfigRequest = AllocateZeroPool (Size);
+ ASSERT (ConfigRequest != NULL);
+ AllocatedRequest = TRUE;
+ UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
+ FreePool (ConfigRequestHdr);
+ }
+ Status = HiiConfigRouting->BlockToConfig (
+ HiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) IfrNvData,
+ BufferSize,
+ Results,
+ Progress
+ );
+ FreePool (IfrNvData);
+ //
+ // Free the allocated config request string.
+ //
+ if (AllocatedRequest) {
+ FreePool (ConfigRequest);
+ ConfigRequest = NULL;
+ }
+
+ //
+ // Set Progress string to the original request string.
+ //
+ if (Request == NULL) {
+ *Progress = NULL;
+ } else if (StrStr (Request, L"OFFSET") == NULL) {
+ *Progress = Request + StrLen (Request);
+ }
+
+ return Status;
+}
+
+/**
+ This function applies changes in a driver's configuration.
+ Input is a Configuration, which has the routing data for this
+ driver followed by name / value configuration pairs. The driver
+ must apply those pairs to its configurable storage. If the
+ driver's configuration is stored in a linear block of data
+ and the driver's name / value pairs are in <BlockConfig>
+ format, it may use the ConfigToBlock helper function (above) to
+ simplify the job. Currently not implemented.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+ @param[out] Progress A pointer to a string filled in with the
+ offset of the most recent '&' before the
+ first failing name / value pair (or the
+ beginn ing of the string if the failure
+ is in the first name / value pair) or
+ the terminating NULL if all was
+ successful.
+
+ @retval EFI_SUCCESS The results have been distributed or are
+ awaiting distribution.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+ @retval EFI_INVALID_PARAMETERS Passing in a NULL for the
+ Results parameter would result
+ in this type of error.
+ @retval EFI_NOT_FOUND Target for the specified routing data
+ was not found.
+**/
+EFI_STATUS
+EFIAPI
+IScsiFormRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ if (Configuration == NULL || Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check routing data in <ConfigHdr>.
+ // Note: if only one Storage is used, then this checking could be skipped.
+ //
+ if (!HiiIsConfigHdrMatch (Configuration, &gIp4IScsiConfigGuid, mVendorStorageName)) {
+ *Progress = Configuration;
+ return EFI_NOT_FOUND;
+ }
+
+ *Progress = Configuration + StrLen (Configuration);
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called to provide results data to the driver.
+ This data consists of a unique key that is used to identify
+ which data is either being passed back or being asked for.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Action Specifies the type of action taken by the browser.
+ @param[in] QuestionId A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect. The format of the data tends to
+ vary based on the opcode that enerated the callback.
+ @param[in] Type The type of value for the question.
+ @param[in] Value A pointer to the data being sent to the original
+ exporting driver.
+ @param[out] ActionRequest On return, points to the action requested by the
+ callback function.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
+ variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be saved.
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the
+ callback.Currently not implemented.
+ @retval EFI_INVALID_PARAMETERS Passing in wrong parameter.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiFormCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ ISCSI_FORM_CALLBACK_INFO *Private;
+ UINTN BufferSize;
+ CHAR8 IScsiName[ISCSI_NAME_MAX_SIZE];
+ CHAR16 PortString[128];
+ CHAR8 Ip4String[IP4_STR_MAX_SIZE];
+ CHAR8 LunString[ISCSI_LUN_STR_MAX_LEN];
+ UINT64 Lun;
+ EFI_STRING_ID DeviceFormTitleToken;
+ ISCSI_CONFIG_IFR_NVDATA *IfrNvData;
+ ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;
+ EFI_IP_ADDRESS HostIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS Gateway;
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+
+ if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
+ //
+ // Retrieve uncommitted data from Browser
+ //
+ IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
+ ASSERT (IfrNvData != NULL);
+ if (!HiiGetBrowserData (&gIp4IScsiConfigGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData)) {
+ FreePool (IfrNvData);
+ return EFI_NOT_FOUND;
+ }
+ Status = EFI_SUCCESS;
+
+ if (Action == EFI_BROWSER_ACTION_CHANGING) {
+ if ((QuestionId >= KEY_DEVICE_ENTRY_BASE) && (QuestionId < (mNumberOfIScsiDevices + KEY_DEVICE_ENTRY_BASE))) {
+ //
+ // In case goto the device configuration form, update the device form title.
+ //
+ ConfigFormEntry = IScsiGetConfigFormEntryByIndex ((UINT32) (QuestionId - KEY_DEVICE_ENTRY_BASE));
+ ASSERT (ConfigFormEntry != NULL);
+
+ UnicodeSPrint (PortString, (UINTN) sizeof (PortString), L"Port %s", ConfigFormEntry->MacString);
+ DeviceFormTitleToken = (EFI_STRING_ID) STR_ISCSI_DEVICE_FORM_TITLE;
+ HiiSetString (Private->RegisteredHandle, DeviceFormTitleToken, PortString, NULL);
+
+ IScsiConvertDeviceConfigDataToIfrNvData (ConfigFormEntry, IfrNvData);
+
+ Private->Current = ConfigFormEntry;
+ }
+ } else if (Action == EFI_BROWSER_ACTION_CHANGED) {
+ switch (QuestionId) {
+ case KEY_INITIATOR_NAME:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName);
+ BufferSize = AsciiStrSize (IScsiName);
+
+ Status = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
+ break;
+
+ case KEY_LOCAL_IP:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->LocalIp, Ip4String);
+ Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);
+ if (EFI_ERROR (Status) ||
+ ((Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
+ !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));
+ }
+
+ break;
+
+ case KEY_SUBNET_MASK:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->SubnetMask, Ip4String);
+ Status = IScsiAsciiStrToIp (Ip4String, &SubnetMask.v4);
+ if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));
+ }
+
+ break;
+
+ case KEY_GATE_WAY:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->Gateway, Ip4String);
+ Status = IScsiAsciiStrToIp (Ip4String, &Gateway.v4);
+ if (EFI_ERROR (Status) ||
+ ((Gateway.Addr[0] != 0) &&
+ (Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
+ !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));
+ }
+
+ break;
+
+ case KEY_TARGET_IP:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->TargetIp, Ip4String);
+ Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);
+ if (EFI_ERROR (Status) || IP4_IS_LOCAL_BROADCAST (EFI_NTOHL(HostIp.v4)) || IP4_IS_UNSPECIFIED (EFI_NTOHL(HostIp.v4))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp.v4, sizeof (HostIp.v4));
+ }
+
+ break;
+
+ case KEY_TARGET_NAME:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName);
+ Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);
+ } else {
+ AsciiStrCpyS (Private->Current->SessionConfigData.TargetName, ISCSI_NAME_MAX_SIZE, IScsiName);
+ }
+
+ break;
+
+ case KEY_DHCP_ENABLE:
+ if (IfrNvData->InitiatorInfoFromDhcp == 0) {
+ IfrNvData->TargetInfoFromDhcp = 0;
+ }
+
+ break;
+
+ case KEY_BOOT_LUN:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->BootLun, LunString);
+ Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid LUN string!", NULL);
+ } else {
+ CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));
+ }
+
+ break;
+
+ case KEY_CHAP_NAME:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPName, Private->Current->AuthConfigData.CHAPName);
+ break;
+
+ case KEY_CHAP_SECRET:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPSecret, Private->Current->AuthConfigData.CHAPSecret);
+ break;
+
+ case KEY_REVERSE_CHAP_NAME:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPName, Private->Current->AuthConfigData.ReverseCHAPName);
+ break;
+
+ case KEY_REVERSE_CHAP_SECRET:
+ IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPSecret, Private->Current->AuthConfigData.ReverseCHAPSecret);
+ break;
+
+ case KEY_CONFIG_ISID:
+ IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
+ IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
+
+ break;
+
+ case KEY_SAVE_CHANGES:
+ //
+ // First, update those fields which don't have INTERACTIVE set.
+ //
+ Private->Current->SessionConfigData.Enabled = IfrNvData->Enabled;
+ Private->Current->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;
+ Private->Current->SessionConfigData.TargetPort = IfrNvData->TargetPort;
+ if (Private->Current->SessionConfigData.TargetPort == 0) {
+ Private->Current->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+
+ Private->Current->SessionConfigData.TargetInfoFromDhcp = IfrNvData->TargetInfoFromDhcp;
+ Private->Current->AuthConfigData.CHAPType = IfrNvData->CHAPType;
+
+ //
+ // Only do full parameter validation if iSCSI is enabled on this device.
+ //
+ if (Private->Current->SessionConfigData.Enabled) {
+ //
+ // Validate the address configuration of the Initiator if DHCP isn't
+ // deployed.
+ //
+ if (!Private->Current->SessionConfigData.InitiatorInfoFromDhcp) {
+ CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.LocalIp, sizeof (HostIp.v4));
+ CopyMem (&SubnetMask.v4, &Private->Current->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));
+ CopyMem (&Gateway.v4, &Private->Current->SessionConfigData.Gateway, sizeof (Gateway.v4));
+
+ if ((Gateway.Addr[0] != 0)) {
+ if (SubnetMask.Addr[0] == 0) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Gateway address is set but subnet mask is zero.", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Local IP and Gateway are not in the same subnet.", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ }
+ }
+ //
+ // Validate target configuration if DHCP isn't deployed.
+ //
+ if (!Private->Current->SessionConfigData.TargetInfoFromDhcp) {
+ CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.TargetIp, sizeof (HostIp.v4));
+ if (IP4_IS_UNSPECIFIED (NTOHL (HostIp.Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (HostIp.Addr[0]))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Target IP is invalid!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // Validate iSCSI target name configuration again:
+ // The format of iSCSI target name is already verified when user input the name;
+ // here we only check the case user does not input the name.
+ //
+ if (Private->Current->SessionConfigData.TargetName[0] == '\0') {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"iSCSI target name is NULL!",
+ NULL
+ );
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ }
+
+ if (IfrNvData->CHAPType != ISCSI_CHAP_NONE) {
+ if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"CHAP Name or CHAP Secret is invalid!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&
+ ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))
+ ) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Reverse CHAP Name or Reverse CHAP Secret is invalid!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ }
+ }
+
+ BufferSize = sizeof (Private->Current->SessionConfigData);
+ gRT->SetVariable (
+ Private->Current->MacString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ BufferSize,
+ &Private->Current->SessionConfigData
+ );
+
+ BufferSize = sizeof (Private->Current->AuthConfigData);
+ gRT->SetVariable (
+ Private->Current->MacString,
+ &gIScsiCHAPAuthInfoGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ BufferSize,
+ &Private->Current->AuthConfigData
+ );
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Pass changed uncommitted data back to Form Browser
+ //
+ HiiSetBrowserData (&gIp4IScsiConfigGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData, NULL);
+ }
+
+ FreePool (IfrNvData);
+
+ return Status;
+}
+
+/**
+ Updates the iSCSI configuration form to add/delete an entry for the iSCSI
+ device specified by the Controller.
+
+ @param[in] DriverBindingHandle The driverbinding handle.
+ @param[in] Controller The controller handle of the iSCSI device.
+ @param[in] AddForm Whether to add or delete a form entry.
+
+ @retval EFI_SUCCESS The iSCSI configuration form is updated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiConfigUpdateForm (
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_HANDLE Controller,
+ IN BOOLEAN AddForm
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;
+ BOOLEAN EntryExisted;
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS MacAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 PortString[128];
+ UINT16 FormIndex;
+ UINTN BufferSize;
+ VOID *StartOpCodeHandle;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ EFI_IFR_GUID_LABEL *EndLabel;
+
+ ConfigFormEntry = NULL;
+ EntryExisted = FALSE;
+
+ NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
+ ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
+
+ if (ConfigFormEntry->Controller == Controller) {
+ EntryExisted = TRUE;
+ break;
+ }
+ }
+
+ if (AddForm) {
+ if (EntryExisted) {
+ return EFI_SUCCESS;
+ } else {
+ //
+ // Add a new form.
+ //
+ ConfigFormEntry = (ISCSI_CONFIG_FORM_ENTRY *) AllocateZeroPool (sizeof (ISCSI_CONFIG_FORM_ENTRY));
+ if (ConfigFormEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&ConfigFormEntry->Link);
+ ConfigFormEntry->Controller = Controller;
+
+ //
+ // Get the MAC address and convert it into the formatted string.
+ //
+ Status = NetLibGetMacAddress (Controller, &MacAddress, &HwAddressSize);
+ ASSERT (Status == EFI_SUCCESS);
+ VlanId = NetLibGetVlanId (Controller);
+
+ IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, ConfigFormEntry->MacString);
+
+ //
+ // Get the normal session configuration data.
+ //
+ BufferSize = sizeof (ConfigFormEntry->SessionConfigData);
+ Status = gRT->GetVariable (
+ ConfigFormEntry->MacString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ &BufferSize,
+ &ConfigFormEntry->SessionConfigData
+ );
+ if (EFI_ERROR (Status)) {
+ ZeroMem (&ConfigFormEntry->SessionConfigData, sizeof (ConfigFormEntry->SessionConfigData));
+
+ //
+ // Generate OUI-format ISID based on MAC address.
+ //
+ CopyMem (ConfigFormEntry->SessionConfigData.IsId, &MacAddress, 6);
+ ConfigFormEntry->SessionConfigData.IsId[0] =
+ (UINT8) (ConfigFormEntry->SessionConfigData.IsId[0] & 0x3F);
+ }
+ //
+ // Get the CHAP authentication configuration data.
+ //
+ BufferSize = sizeof (ConfigFormEntry->AuthConfigData);
+ Status = gRT->GetVariable (
+ ConfigFormEntry->MacString,
+ &gIScsiCHAPAuthInfoGuid,
+ NULL,
+ &BufferSize,
+ &ConfigFormEntry->AuthConfigData
+ );
+ if (EFI_ERROR (Status)) {
+ ZeroMem (&ConfigFormEntry->AuthConfigData, sizeof (ConfigFormEntry->AuthConfigData));
+ }
+ //
+ // Compose the Port string and create a new EFI_STRING_ID.
+ //
+ UnicodeSPrint (PortString, sizeof (PortString), L"Port %s", ConfigFormEntry->MacString);
+ ConfigFormEntry->PortTitleToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);
+
+ //
+ // Compose the help string of this port and create a new EFI_STRING_ID.
+ //
+ UnicodeSPrint (PortString, sizeof (PortString), L"Set the iSCSI parameters on port %s", ConfigFormEntry->MacString);
+ ConfigFormEntry->PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);
+
+ InsertTailList (&mIScsiConfigFormList, &ConfigFormEntry->Link);
+ mNumberOfIScsiDevices++;
+ }
+ } else {
+ ASSERT (EntryExisted);
+
+ mNumberOfIScsiDevices--;
+ RemoveEntryList (&ConfigFormEntry->Link);
+ FreePool (ConfigFormEntry);
+ mCallbackInfo->Current = NULL;
+ }
+ //
+ // Allocate space for creation of Buffer
+ //
+
+ //
+ // Init OpCode Handle
+ //
+ StartOpCodeHandle = HiiAllocateOpCodeHandle ();
+ ASSERT (StartOpCodeHandle != NULL);
+
+ EndOpCodeHandle = HiiAllocateOpCodeHandle ();
+ ASSERT (EndOpCodeHandle != NULL);
+
+ //
+ // Create Hii Extend Label OpCode as the start opcode
+ //
+ StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
+ StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ StartLabel->Number = DEVICE_ENTRY_LABEL;
+
+ //
+ // Create Hii Extend Label OpCode as the end opcode
+ //
+ EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
+ EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ EndLabel->Number = LABEL_END;
+
+ FormIndex = 0;
+ NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
+ ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
+
+ HiiCreateGotoOpCode (
+ StartOpCodeHandle, // Container for dynamic created opcodes
+ FORMID_DEVICE_FORM, // Target Form ID
+ ConfigFormEntry->PortTitleToken, // Prompt text
+ ConfigFormEntry->PortTitleHelpToken, // Help text
+ EFI_IFR_FLAG_CALLBACK, // Question flag
+ (UINT16)(KEY_DEVICE_ENTRY_BASE + FormIndex) // Question ID
+ );
+
+ FormIndex++;
+ }
+
+ HiiUpdateForm (
+ mCallbackInfo->RegisteredHandle,
+ &gIp4IScsiConfigGuid,
+ FORMID_MAIN_FORM,
+ StartOpCodeHandle, // Label DEVICE_ENTRY_LABEL
+ EndOpCodeHandle // LABEL_END
+ );
+
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the iSCSI configuration form.
+
+ @param[in] DriverBindingHandle The iSCSI driverbinding handle.
+
+ @retval EFI_SUCCESS The iSCSI configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiConfigFormInit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HII_DATABASE_PROTOCOL *HiiDatabase;
+ ISCSI_FORM_CALLBACK_INFO *CallbackInfo;
+
+ Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **)&HiiDatabase);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO));
+ if (CallbackInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CallbackInfo->Signature = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;
+ CallbackInfo->HiiDatabase = HiiDatabase;
+ CallbackInfo->Current = NULL;
+
+ CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;
+ CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig;
+ CallbackInfo->ConfigAccess.Callback = IScsiFormCallback;
+
+ Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **)&CallbackInfo->ConfigRouting);
+ if (EFI_ERROR (Status)) {
+ FreePool(CallbackInfo);
+ return Status;
+ }
+
+ //
+ // Install Device Path Protocol and Config Access protocol to driver handle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &CallbackInfo->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mIScsiHiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->ConfigAccess,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Publish our HII data
+ //
+ CallbackInfo->RegisteredHandle = HiiAddPackages (
+ &gIp4IScsiConfigGuid,
+ CallbackInfo->DriverHandle,
+ IScsi4DxeStrings,
+ IScsiConfigDxeBin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ FreePool(CallbackInfo);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ mCallbackInfo = CallbackInfo;
+
+ return Status;
+}
+
+/**
+ Unload the iSCSI configuration form, this includes: delete all the iSCSI
+ device configuration entries, uninstall the form callback protocol and
+ free the resources used.
+
+ @param[in] DriverBindingHandle The iSCSI driverbinding handle.
+
+ @retval EFI_SUCCESS The iSCSI configuration form is unloaded.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+IScsiConfigFormUnload (
+ IN EFI_HANDLE DriverBindingHandle
+ )
+{
+ ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;
+
+ while (!IsListEmpty (&mIScsiConfigFormList)) {
+ //
+ // Uninstall the device forms as the iSCSI driver instance may fail to
+ // control the controller but still install the device configuration form.
+ // In such case, upon driver unloading, the driver instance's driverbinding.
+ // stop () won't be called, so we have to take this chance here to uninstall
+ // the device form.
+ //
+ ConfigFormEntry = NET_LIST_USER_STRUCT (mIScsiConfigFormList.ForwardLink, ISCSI_CONFIG_FORM_ENTRY, Link);
+ IScsiConfigUpdateForm (DriverBindingHandle, ConfigFormEntry->Controller, FALSE);
+ }
+
+ //
+ // Remove HII package list
+ //
+ mCallbackInfo->HiiDatabase->RemovePackageList (
+ mCallbackInfo->HiiDatabase,
+ mCallbackInfo->RegisteredHandle
+ );
+
+ //
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ mCallbackInfo->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mIScsiHiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &mCallbackInfo->ConfigAccess,
+ NULL
+ );
+ FreePool (mCallbackInfo);
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.h
new file mode 100644
index 0000000000..ef881d8827
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.h
@@ -0,0 +1,166 @@
+/** @file
+ The header file of IScsiConfig.c.
+
+Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_CONFIG_H_
+#define _ISCSI_CONFIG_H_
+
+#include <Guid/MdeModuleHii.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Library/HiiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+extern UINT8 IScsiConfigDxeBin[];
+extern UINT8 IScsi4DxeStrings[];
+
+#define ISCSI_INITATOR_NAME_VAR_NAME L"I_NAME"
+
+#define ISCSI_CONFIG_VAR_ATTR (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE)
+
+#define ISCSI_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'f', 'c', 'i')
+
+
+
+/**
+ If the DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugProperyMask is clear,
+ then this macro return a pointer to a data structure ISCSI_FORM_CALLBACK_INFO.
+
+ If the DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugProperyMask is set,
+ The Signature field of the data structure ISCSI_FORM_CALLBACK_INFO
+ is compared to TestSignature. If the signatures match, then a pointer
+ to the pointer to a data structure ISCSI_FORM_CALLBACK_INFO is returned.
+ If the signatures do not match, then DebugAssert() is called with a description
+ of "CR has a bad signature" and Callback is returned.
+
+ If the data type ISCSI_FORM_CALLBACK_INFO_SIGNATURE does not contain the field
+ specified by Callback, then the module will not compile.
+
+ If ISCSI_FORM_CALLBACK_INFO_SIGNATURE does not contain a field called Signature,
+ then the module will not compile.
+
+ @param Callback Pointer to the specified field within the data
+ structure ISCSI_FORM_CALLBACK_INFO.
+ @return A pointer to the pointer to a data structure ISCSI_FORM_CALLBACK_INFO.
+ @retval Others Some unexpected error happened.
+**/
+
+#define ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK(Callback) \
+ CR ( \
+ Callback, \
+ ISCSI_FORM_CALLBACK_INFO, \
+ ConfigAccess, \
+ ISCSI_FORM_CALLBACK_INFO_SIGNATURE \
+ )
+
+#pragma pack(1)
+
+typedef struct _ISCSI_MAC_INFO {
+ EFI_MAC_ADDRESS Mac;
+ UINT8 Len;
+ UINT16 VlanId;
+} ISCSI_MAC_INFO;
+
+typedef struct _ISCSI_DEVICE_LIST {
+ UINT8 NumDevice;
+ ISCSI_MAC_INFO MacInfo[1];
+} ISCSI_DEVICE_LIST;
+
+#pragma pack()
+
+typedef struct _ISCSI_CONFIG_FORM_ENTRY {
+ LIST_ENTRY Link;
+ EFI_HANDLE Controller;
+ CHAR16 MacString[95];
+ EFI_STRING_ID PortTitleToken;
+ EFI_STRING_ID PortTitleHelpToken;
+
+ ISCSI_SESSION_CONFIG_NVDATA SessionConfigData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA AuthConfigData;
+} ISCSI_CONFIG_FORM_ENTRY;
+
+typedef struct _ISCSI_FORM_CALLBACK_INFO {
+ UINTN Signature;
+ EFI_HANDLE DriverHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
+ EFI_HII_DATABASE_PROTOCOL *HiiDatabase;
+ EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting;
+ UINT16 *KeyList;
+ VOID *FormBuffer;
+ EFI_HII_HANDLE RegisteredHandle;
+ ISCSI_CONFIG_FORM_ENTRY *Current;
+} ISCSI_FORM_CALLBACK_INFO;
+
+#pragma pack(1)
+
+///
+/// HII specific Vendor Device Path definition.
+///
+typedef struct {
+ VENDOR_DEVICE_PATH VendorDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} HII_VENDOR_DEVICE_PATH;
+
+#pragma pack()
+
+/**
+ Updates the iSCSI configuration form to add/delete an entry for the iSCSI
+ device specified by the Controller.
+
+ @param[in] DriverBindingHandle The driverbinding handle.
+ @param[in] Controller The controller handle of the iSCSI device.
+ @param[in] AddForm Whether to add or delete a form entry.
+
+ @retval EFI_SUCCESS The iSCSI configuration form is updated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiConfigUpdateForm (
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_HANDLE Controller,
+ IN BOOLEAN AddForm
+ );
+
+/**
+ Initialize the iSCSI configuration form.
+
+ @param[in] DriverBindingHandle The iSCSI driverbinding handle.
+
+ @retval EFI_SUCCESS The iSCSI configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiConfigFormInit (
+ VOID
+ );
+
+/**
+ Unload the iSCSI configuration form, this includes: delete all the iSCSI
+ device configuration entries, uninstall the form callback protocol and
+ free the resources used.
+
+ @param[in] DriverBindingHandle The iSCSI driverbinding handle.
+
+ @retval EFI_SUCCESS The iSCSI configuration form is unloaded.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+IScsiConfigFormUnload (
+ IN EFI_HANDLE DriverBindingHandle
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxe.vfr b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxe.vfr
new file mode 100644
index 0000000000..2456c51c8c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxe.vfr
@@ -0,0 +1,219 @@
+/** @file
+ Vfr file for iSCSI config.
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "IScsiConfigNVDataStruc.h"
+#define EFI_NETWORK_DEVICE_CLASS 0x04
+
+formset
+ guid = IP4_ISCSI_CONFIG_GUID,
+ title = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_TITLE),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_HELP),
+
+ varstore ISCSI_CONFIG_IFR_NVDATA,
+ name = ISCSI_CONFIG_IFR_NVDATA,
+ guid = IP4_ISCSI_CONFIG_GUID;
+ form formid = FORMID_MAIN_FORM,
+ title = STRING_TOKEN(STR_ISCSI_MAIN_FORM_TITLE);
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorName,
+ prompt = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME_HELP),
+ flags = INTERACTIVE,
+ key = KEY_INITIATOR_NAME,
+ minsize = ISCSI_NAME_IFR_MIN_SIZE,
+ maxsize = ISCSI_NAME_IFR_MAX_SIZE,
+ endstring;
+
+ label DEVICE_ENTRY_LABEL;
+ label LABEL_END;
+
+ endform;
+
+ form formid = FORMID_DEVICE_FORM,
+ title = STRING_TOKEN(STR_ISCSI_DEVICE_FORM_TITLE);
+
+ checkbox varid = ISCSI_CONFIG_IFR_NVDATA.Enabled,
+ prompt = STRING_TOKEN(STR_ISCSI_DEVICE_ENABLE),
+ help = STRING_TOKEN(STR_NULL),
+ flags = 0,
+ endcheckbox;
+
+ checkbox varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp,
+ prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),
+ help = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),
+ flags = INTERACTIVE,
+ key = KEY_DHCP_ENABLE,
+ endcheckbox;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x01;
+ string varid = ISCSI_CONFIG_IFR_NVDATA.LocalIp,
+ prompt = STRING_TOKEN(STR_ISCSI_LOCAL_IP_ADDRESS),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_LOCAL_IP,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.SubnetMask,
+ prompt = STRING_TOKEN(STR_ISCSI_LOCAL_MASK),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_SUBNET_MASK,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.Gateway,
+ prompt = STRING_TOKEN(STR_ISCSI_LOCAL_GATEWAY),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_GATE_WAY,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+ endif;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x00;
+ checkbox varid = ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp,
+ prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),
+ help = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),
+ flags = 0,
+ endcheckbox;
+ endif;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp == 0x01;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.TargetName,
+ prompt = STRING_TOKEN(STR_ISCSI_TARGET_NAME),
+ help = STRING_TOKEN(STR_ISCSI_TARGET_NAME),
+ flags = INTERACTIVE,
+ key = KEY_TARGET_NAME,
+ minsize = ISCSI_NAME_IFR_MIN_SIZE,
+ maxsize = ISCSI_NAME_IFR_MAX_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.TargetIp,
+ prompt = STRING_TOKEN(STR_ISCSI_TARGET_IP_ADDRESS),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TARGET_IP,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ numeric varid = ISCSI_CONFIG_IFR_NVDATA.TargetPort,
+ prompt = STRING_TOKEN(STR_ISCSI_TARGET_PORT),
+ help = STRING_TOKEN(STR_ISCSI_TARGET_PORT),
+ flags = 0,
+ minimum = TARGET_PORT_MIN_NUM,
+ maximum = TARGET_PORT_MAX_NUM,
+ step = 0,
+ endnumeric;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.BootLun,
+ prompt = STRING_TOKEN(STR_ISCSI_BOOT_LUN),
+ help = STRING_TOKEN(STR_ISCSI_BOOT_LUN_HELP),
+ flags = INTERACTIVE,
+ key = KEY_BOOT_LUN,
+ minsize = LUN_MIN_SIZE,
+ maxsize = LUN_MAX_SIZE,
+ endstring;
+ endif;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ oneof varid = ISCSI_CONFIG_IFR_NVDATA.CHAPType,
+ prompt = STRING_TOKEN(STR_CHAP_TYPE_PROMPT),
+ help = STRING_TOKEN(STR_CHAP_TYPE_HELP),
+ option text = STRING_TOKEN(STR_CHAP_TYPE_NONE), value = ISCSI_CHAP_NONE, flags = DEFAULT;
+ option text = STRING_TOKEN(STR_CHAP_TYPE_UNI), value = ISCSI_CHAP_UNI, flags = 0;
+ option text = STRING_TOKEN(STR_CHAP_TYPE_MUTUAL), value = ISCSI_CHAP_MUTUAL, flags = 0;
+ endoneof;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_NONE;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.CHAPName,
+ prompt = STRING_TOKEN(STR_ISCSI_CHAP_NAME),
+ help = STRING_TOKEN(STR_ISCSI_CHAP_NAME),
+ flags = INTERACTIVE,
+ key = KEY_CHAP_NAME,
+ minsize = 0,
+ maxsize = ISCSI_CHAP_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.CHAPSecret,
+ prompt = STRING_TOKEN(STR_ISCSI_CHAP_SECRET),
+ help = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),
+ flags = INTERACTIVE,
+ key = KEY_CHAP_SECRET,
+ minsize = ISCSI_CHAP_SECRET_MIN_LEN,
+ maxsize = ISCSI_CHAP_SECRET_MAX_LEN,
+ endstring;
+
+ endif;
+
+ suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_MUTUAL;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPName,
+ prompt = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),
+ help = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),
+ flags = INTERACTIVE,
+ key = KEY_REVERSE_CHAP_NAME,
+ minsize = 0,
+ maxsize = ISCSI_CHAP_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPSecret,
+ prompt = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_SECRET),
+ help = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),
+ flags = INTERACTIVE,
+ key = KEY_REVERSE_CHAP_SECRET,
+ minsize = ISCSI_CHAP_SECRET_MIN_LEN,
+ maxsize = ISCSI_CHAP_SECRET_MAX_LEN,
+ endstring;
+
+ endif;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.IsId,
+ prompt = STRING_TOKEN(STR_ISCSI_CONFIG_ISID),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_ISID_HELP),
+ flags = INTERACTIVE,
+ key = KEY_CONFIG_ISID,
+ minsize = ISID_CONFIGURABLE_MIN_LEN,
+ maxsize = ISID_CONFIGURABLE_MAX_LEN,
+ endstring;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ text
+ help = STRING_TOKEN (STR_SAVE_CHANGES),
+ text = STRING_TOKEN (STR_SAVE_CHANGES),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_CHANGES;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_RETURN_MAIN_FORM),
+ help = STRING_TOKEN (STR_RETURN_MAIN_FORM),
+ flags = 0;
+
+ endform;
+
+endformset;
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxeStrings.uni b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxeStrings.uni
new file mode 100644
index 0000000000..6acf139cd0
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxeStrings.uni
@@ -0,0 +1,62 @@
+// *++
+//
+// Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// Module Name:
+//
+// IScsiConfigStrings.uni
+//
+// Abstract:
+//
+// String definitions for iSCSI configuration.
+//
+// Revision History:
+//
+// --*/
+
+
+/=#
+
+#langdef en-US "English"
+
+#string STR_ISCSI_CONFIG_FORM_TITLE #language en-US "iSCSI Configuration"
+#string STR_ISCSI_CONFIG_FORM_HELP #language en-US "Configure the iSCSI parameters."
+#string STR_ISCSI_MAIN_FORM_TITLE #language en-US "iSCSI Configuration"
+#string STR_ISCSI_CONFIG_INIT_NAME #language en-US "iSCSI Initiator Name"
+#string STR_ISCSI_CONFIG_INIT_NAME_HELP #language en-US "The worldwide unique name of the initiator. Only iqn. format is accepted."
+#string STR_ISCSI_DEVICE_FORM_TITLE #language en-US ""
+#string STR_ISCSI_DEVICE_ENABLE #language en-US "Enable iSCSI"
+#string STR_ISCSI_LOCAL_IP_ADDRESS #language en-US " Initiator IP Address"
+#string STR_ISCSI_LOCAL_MASK #language en-US " Initiator Subnet Mask"
+#string STR_ISCSI_LOCAL_GATEWAY #language en-US " Gateway"
+#string STR_ISCSI_IP_ADDRESS_HELP #language en-US "Enter IP address in dotted-decimal notation."
+#string STR_ISCSI_TARGET_NAME #language en-US " Target Name"
+#string STR_ISCSI_TARGET_IP_ADDRESS #language en-US " Target IP Address"
+#string STR_ISCSI_TARGET_PORT #language en-US " Target Port"
+#string STR_ISCSI_BOOT_LUN #language en-US " Boot LUN"
+#string STR_ISCSI_BOOT_LUN_HELP #language en-US "Hexadecimal representation of the LU number. Examples are: 4752-3A4F-6b7e-2F99, 6734-9-156f-127, 4186-9"
+#string STR_ISCSI_ENABLE_DHCP #language en-US "Enable DHCP"
+#string STR_ISCSI_ENABLE_DHCP_ON_TARGET #language en-US "Get target info via DHCP"
+#string STR_CHAP_TYPE_PROMPT #language en-US "CHAP Type"
+#string STR_CHAP_TYPE_HELP #language en-US "None, One way CHAP or mutual CHAP"
+#string STR_CHAP_TYPE_NONE #language en-US "None"
+#string STR_CHAP_TYPE_UNI #language en-US "One way"
+#string STR_CHAP_TYPE_MUTUAL #language en-US "Mutual"
+#string STR_ISCSI_CHAP_NAME #language en-US " CHAP Name"
+#string STR_ISCSI_CHAP_SECRET #language en-US " CHAP Secret"
+#string STR_ISCSI_CHAP_SECRET_HELP #language en-US "The minimum length is 12 bytes and the maximum length is 16 bytes."
+#string STR_ISCSI_REVERSE_CHAP_NAME #language en-US " Reverse CHAP Name"
+#string STR_ISCSI_REVERSE_CHAP_SECRET #language en-US " Reverse CHAP Secret"
+#string STR_ISCSI_CONFIG_ISID #language en-US "ISID"
+#string STR_ISCSI_CONFIG_ISID_HELP #language en-US "OUI-format ISID in 6 bytes, default value are derived from MAC address. Only last 3 bytes are configurable. Example: update 0ABBCCDDEEFF to 0ABBCCF07901 by input F07901."
+#string STR_RETURN_MAIN_FORM #language en-US "Back to Previous Page"
+#string STR_SAVE_CHANGES #language en-US "Save Changes"
+#string STR_NULL #language en-US ""
+ \ No newline at end of file
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigNVDataStruc.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigNVDataStruc.h
new file mode 100644
index 0000000000..7c2e4e65fa
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigNVDataStruc.h
@@ -0,0 +1,109 @@
+/** @file
+ Define NVData structures used by the iSCSI configuration component
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_NVDATASTRUC_H_
+#define _ISCSI_NVDATASTRUC_H_
+
+#include <Guid/Ip4IScsiConfigHii.h>
+
+#define VAR_EQ_TEST_NAME 0x100
+
+#define FORMID_MAIN_FORM 1
+#define FORMID_DEVICE_FORM 2
+
+#define ISCSI_NAME_MAX_SIZE 224
+
+//
+// Vfr has a limit on the size, it's 255 bytes.
+//
+#define ISCSI_NAME_IFR_MIN_SIZE 4
+#define ISCSI_NAME_IFR_MAX_SIZE 223
+
+#define IP_MIN_SIZE 7
+#define IP_MAX_SIZE 15
+#define IP4_STR_MAX_SIZE 16
+
+#define LUN_MIN_SIZE 1
+#define LUN_MAX_SIZE 20
+
+#define ISCSI_CHAP_NONE 0
+#define ISCSI_CHAP_UNI 1
+#define ISCSI_CHAP_MUTUAL 2
+
+#define TARGET_PORT_MIN_NUM 0
+#define TARGET_PORT_MAX_NUM 65535
+
+#define DEVICE_ENTRY_LABEL 0x1234
+#define LABEL_END 0xffff
+
+#define KEY_INITIATOR_NAME 0x101
+#define KEY_DHCP_ENABLE 0x102
+#define KEY_LOCAL_IP 0x103
+#define KEY_SUBNET_MASK 0x104
+#define KEY_GATE_WAY 0x105
+#define KEY_TARGET_IP 0x106
+#define KEY_CHAP_NAME 0x107
+#define KEY_CHAP_SECRET 0x108
+#define KEY_REVERSE_CHAP_NAME 0x109
+#define KEY_REVERSE_CHAP_SECRET 0x10a
+#define KEY_SAVE_CHANGES 0x10b
+#define KEY_TARGET_NAME 0x10c
+#define KEY_BOOT_LUN 0x10d
+#define KEY_CONFIG_ISID 0x10e
+
+#define KEY_DEVICE_ENTRY_BASE 0x1000
+
+#define ISCSI_LUN_STR_MAX_LEN 21
+#define ISCSI_CHAP_SECRET_MIN_LEN 12
+#define ISCSI_CHAP_SECRET_MAX_LEN 16
+//
+// ISCSI_CHAP_SECRET_STORAGE = ISCSI_CHAP_SECRET_MAX_LEN + sizeof (NULL-Terminator)
+//
+#define ISCSI_CHAP_SECRET_STORAGE 17
+
+#define ISCSI_CHAP_NAME_MAX_LEN 126
+#define ISCSI_CHAP_NAME_STORAGE 127
+
+#define ISID_CONFIGURABLE_MIN_LEN 6
+#define ISID_CONFIGURABLE_MAX_LEN 12
+#define ISID_CONFIGURABLE_STORAGE 13
+
+#pragma pack(1)
+typedef struct {
+ CHAR16 InitiatorName[ISCSI_NAME_MAX_SIZE];
+
+ UINT8 Enabled;
+
+ UINT8 InitiatorInfoFromDhcp;
+ CHAR16 LocalIp[IP4_STR_MAX_SIZE];
+ CHAR16 SubnetMask[IP4_STR_MAX_SIZE];
+ CHAR16 Gateway[IP4_STR_MAX_SIZE];
+
+ CHAR16 TargetName[ISCSI_NAME_MAX_SIZE];
+ CHAR16 TargetIp[IP4_STR_MAX_SIZE];
+ UINT16 TargetPort;
+ CHAR16 BootLun[ISCSI_LUN_STR_MAX_LEN];
+ UINT8 TargetInfoFromDhcp;
+
+ UINT8 CHAPType;
+ CHAR16 CHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR16 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+ CHAR16 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR16 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+
+ CHAR16 IsId[ISID_CONFIGURABLE_STORAGE];
+} ISCSI_CONFIG_IFR_NVDATA;
+#pragma pack()
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c
new file mode 100644
index 0000000000..869a56a491
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c
@@ -0,0 +1,472 @@
+/** @file
+ iSCSI DHCP related configuration routines.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+/**
+ Extract the Root Path option and get the required target information.
+
+ @param[in] RootPath The RootPath.
+ @param[in] Length Length of the RootPath option payload.
+ @param[in, out] ConfigNvData The iSCSI session configuration data read from nonvolatile device.
+
+ @retval EFI_SUCCESS All required information is extracted from the RootPath option.
+ @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER The RootPath is mal-formatted.
+**/
+EFI_STATUS
+IScsiDhcpExtractRootPath (
+ IN CHAR8 *RootPath,
+ IN UINT8 Length,
+ IN OUT ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData
+ )
+{
+ EFI_STATUS Status;
+ UINT8 IScsiRootPathIdLen;
+ CHAR8 *TmpStr;
+ ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
+ ISCSI_ROOT_PATH_FIELD *Field;
+ UINT32 FieldIndex;
+ UINT8 Index;
+
+ //
+ // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
+ //
+ IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);
+
+ if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
+ return EFI_NOT_FOUND;
+ }
+ //
+ // Skip the iSCSI RootPath ID "iscsi:".
+ //
+ RootPath += IScsiRootPathIdLen;
+ Length = (UINT8) (Length - IScsiRootPathIdLen);
+
+ TmpStr = (CHAR8 *) AllocatePool (Length + 1);
+ if (TmpStr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (TmpStr, RootPath, Length);
+ TmpStr[Length] = '\0';
+
+ Index = 0;
+ FieldIndex = RP_FIELD_IDX_SERVERNAME;
+ ZeroMem (&Fields[0], sizeof (Fields));
+
+ //
+ // Extract the fields in the Root Path option string.
+ //
+ for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
+ if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
+ Fields[FieldIndex].Str = &TmpStr[Index];
+ }
+
+ while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
+ Index++;
+ }
+
+ if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
+ if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
+ TmpStr[Index] = '\0';
+ Index++;
+ }
+
+ if (Fields[FieldIndex].Str != NULL) {
+ Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
+ }
+ }
+ }
+
+ if (FieldIndex != RP_FIELD_IDX_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
+ (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
+ (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
+ ) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Get the IP address of the target.
+ //
+ Field = &Fields[RP_FIELD_IDX_SERVERNAME];
+ Status = IScsiAsciiStrToIp (Field->Str, &ConfigNvData->TargetIp);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Check the protocol type.
+ //
+ Field = &Fields[RP_FIELD_IDX_PROTOCOL];
+ if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Get the port of the iSCSI target.
+ //
+ Field = &Fields[RP_FIELD_IDX_PORT];
+ if (Field->Str != NULL) {
+ ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
+ } else {
+ ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+ //
+ // Get the LUN.
+ //
+ Field = &Fields[RP_FIELD_IDX_LUN];
+ if (Field->Str != NULL) {
+ Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else {
+ ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
+ }
+ //
+ // Get the target iSCSI Name.
+ //
+ Field = &Fields[RP_FIELD_IDX_TARGETNAME];
+
+ if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Validate the iSCSI name.
+ //
+ Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);
+
+ON_EXIT:
+
+ FreePool (TmpStr);
+
+ return Status;
+}
+
+/**
+ The callback function registerd to the DHCP4 instance which is used to select
+ the qualified DHCP OFFER.
+
+ @param[in] This The DHCP4 protocol.
+ @param[in] Context The context set when configuring the DHCP4 protocol.
+ @param[in] CurrentState The current state of the DHCP4 protocol.
+ @param[in] Dhcp4Event The event occurs in the current state.
+ @param[in] Packet The DHCP packet that is to be sent or already received.
+ @param[out] NewPacket The packet used to replace the above Packet.
+
+ @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
+ in the Dhcp4Event.
+ @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDhcpSelectOffer (
+ IN EFI_DHCP4_PROTOCOL * This,
+ IN VOID *Context,
+ IN EFI_DHCP4_STATE CurrentState,
+ IN EFI_DHCP4_EVENT Dhcp4Event,
+ IN EFI_DHCP4_PACKET * Packet, OPTIONAL
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINT32 OptionCount;
+ EFI_DHCP4_PACKET_OPTION **OptionList;
+ UINT32 Index;
+
+ if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
+ return EFI_SUCCESS;
+ }
+
+ OptionCount = 0;
+
+ Status = This->Parse (This, Packet, &OptionCount, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_NOT_READY;
+ }
+
+ OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+ if (OptionList == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ Status = This->Parse (This, Packet, &OptionCount, OptionList);
+ if (EFI_ERROR (Status)) {
+ FreePool (OptionList);
+ return EFI_NOT_READY;
+ }
+
+ for (Index = 0; Index < OptionCount; Index++) {
+ if (OptionList[Index]->OpCode != DHCP4_TAG_ROOTPATH) {
+ continue;
+ }
+
+ Status = IScsiDhcpExtractRootPath (
+ (CHAR8 *) &OptionList[Index]->Data[0],
+ OptionList[Index]->Length,
+ (ISCSI_SESSION_CONFIG_NVDATA *) Context
+ );
+
+ break;
+ }
+
+ if (Index == OptionCount) {
+ Status = EFI_NOT_READY;
+ }
+
+ FreePool (OptionList);
+
+ return Status;
+}
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Dhcp4 The DHCP4 protocol.
+ @param[in, out] ConfigData The session configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.
+ @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is mal-formatted.
+ @retval EFI_DEVICE_ERROR Other errors as indicated.
+**/
+EFI_STATUS
+IScsiParseDhcpAck (
+ IN EFI_DHCP4_PROTOCOL *Dhcp4,
+ IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP4_MODE_DATA Dhcp4ModeData;
+ UINT32 OptionCount;
+ EFI_DHCP4_PACKET_OPTION **OptionList;
+ UINT32 Index;
+
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Dhcp4ModeData.State != Dhcp4Bound) {
+ return EFI_NO_MAPPING;
+ }
+
+ CopyMem (&ConfigData->NvData.LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&ConfigData->NvData.SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&ConfigData->NvData.Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ OptionCount = 0;
+ OptionList = NULL;
+
+ Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+ if (OptionList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
+ if (EFI_ERROR (Status)) {
+ FreePool (OptionList);
+ return EFI_DEVICE_ERROR;
+ }
+
+ for (Index = 0; Index < OptionCount; Index++) {
+ //
+ // Get DNS server addresses and DHCP server address from this offer.
+ //
+ if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {
+
+ if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ //
+ // Primary DNS server address.
+ //
+ CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
+
+ if (OptionList[Index]->Length > 4) {
+ //
+ // Secondary DNS server address
+ //
+ CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
+ }
+ } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
+ if (OptionList[Index]->Length != 4) {
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
+ }
+ }
+
+ FreePool (OptionList);
+
+ return Status;
+}
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller;
+ @param[in, out] ConfigData The session configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiDoDhcp (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData
+ )
+{
+ EFI_HANDLE Dhcp4Handle;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_STATUS Status;
+ EFI_DHCP4_PACKET_OPTION *ParaList;
+ EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
+ BOOLEAN MediaPresent;
+ UINT8 *Data;
+
+ Dhcp4Handle = NULL;
+ Dhcp4 = NULL;
+ ParaList = NULL;
+
+ //
+ // Check media status before do DHCP
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Create a DHCP4 child instance and get the protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &Dhcp4Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **)&Dhcp4,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
+ if (ParaList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ //
+ // Ask the server to reply with Netmask, Router, DNS and RootPath options.
+ //
+ ParaList->OpCode = DHCP4_TAG_PARA_LIST;
+ ParaList->Length = (UINT8) (ConfigData->NvData.TargetInfoFromDhcp ? 4 : 3);
+ Data = &ParaList->Data[0];
+ Data[0] = DHCP4_TAG_NETMASK;
+ Data[1] = DHCP4_TAG_ROUTER;
+ Data[2] = DHCP4_TAG_DNS_SERVER;
+ Data[3] = DHCP4_TAG_ROOTPATH;
+
+ ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dhcp4ConfigData.OptionCount = 1;
+ Dhcp4ConfigData.OptionList = &ParaList;
+
+ if (ConfigData->NvData.TargetInfoFromDhcp) {
+ //
+ // Use callback to select an offer which contains target information.
+ //
+ Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
+ Dhcp4ConfigData.CallbackContext = &ConfigData->NvData;
+ }
+
+ Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Dhcp4->Start (Dhcp4, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Parse the ACK to get required information.
+ //
+ Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
+
+ON_EXIT:
+
+ if (ParaList != NULL) {
+ FreePool (ParaList);
+ }
+
+ if (Dhcp4 != NULL) {
+ Dhcp4->Stop (Dhcp4);
+ Dhcp4->Configure (Dhcp4, NULL);
+
+ gBS->CloseProtocol (
+ Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ Image,
+ Controller
+ );
+ }
+
+ NetLibDestroyServiceChild (
+ Controller,
+ Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Dhcp4Handle
+ );
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.h
new file mode 100644
index 0000000000..59f8672719
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.h
@@ -0,0 +1,55 @@
+/** @file
+ The header file of IScsiDhcp.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_DHCP_H_
+#define _ISCSI_DHCP_H_
+
+#include <Protocol/Dhcp4.h>
+
+#define ISCSI_ROOT_PATH_ID "iscsi:"
+#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':'
+
+#define RP_FIELD_IDX_SERVERNAME 0
+#define RP_FIELD_IDX_PROTOCOL 1
+#define RP_FIELD_IDX_PORT 2
+#define RP_FIELD_IDX_LUN 3
+#define RP_FIELD_IDX_TARGETNAME 4
+#define RP_FIELD_IDX_MAX 5
+
+typedef struct _ISCSI_ROOT_PATH_FIELD {
+ CHAR8 *Str;
+ UINT8 Len;
+} ISCSI_ROOT_PATH_FIELD;
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller;
+ @param[in, out] ConfigData The session configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiDoDhcp (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.c
new file mode 100644
index 0000000000..5295d346ec
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.c
@@ -0,0 +1,676 @@
+/** @file
+ The entry point of IScsi driver.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gIScsiDriverBinding = {
+ IScsiDriverBindingSupported,
+ IScsiDriverBindingStart,
+ IScsiDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ Tests to see if this driver supports the RemainingDevicePath.
+
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The RemainingDevicePath is supported or NULL.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+IScsiIsDevicePathSupported (
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *CurrentDevicePath;
+
+ CurrentDevicePath = RemainingDevicePath;
+ if (CurrentDevicePath != NULL) {
+ while (!IsDevicePathEnd (CurrentDevicePath)) {
+ if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {
+ return EFI_SUCCESS;
+ }
+
+ CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
+ }
+
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is optional for bus drivers.
+
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive acces.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = IScsiIsDevicePathSupported (RemainingDevicePath);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (IScsiDhcpIsConfigured (ControllerHandle)) {
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Start this driver on ControllerHandle.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations, but the following calling
+ restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is optional for bus drivers.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.
+ Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_DRIVER_DATA *Private;
+ VOID *Interface;
+
+ Private = IScsiCreateDriverData (This->DriverBindingHandle, ControllerHandle);
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create a underlayer child instance, but not need to configure it. Just open ChildHandle
+ // via BY_DRIVER. That is, establishing the relationship between ControllerHandle and ChildHandle.
+ // Therefore, when DisconnectController(), especially VLAN virtual controller handle,
+ // IScsiDriverBindingStop() will be called.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ &Private->ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ &Interface,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Always install private protocol no matter what happens later. We need to
+ // keep the relationship between ControllerHandle and ChildHandle.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ &gEfiCallerIdGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->IScsiIdentifier
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Try to add a port configuration page for this controller.
+ //
+ IScsiConfigUpdateForm (This->DriverBindingHandle, ControllerHandle, TRUE);
+
+ //
+ // Get the iSCSI configuration data of this controller.
+ //
+ Status = IScsiGetConfigData (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ //
+ // Try to login and create an iSCSI session according to the configuration.
+ //
+ Status = IScsiSessionLogin (Private);
+ if (Status == EFI_MEDIA_CHANGED) {
+ //
+ // The specified target is not available and the redirection information is
+ // got, login the session again with the updated target address.
+ //
+ Status = IScsiSessionLogin (Private);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ //
+ // Duplicate the Session's tcp connection device path. The source port field
+ // will be set to zero as one iSCSI session is comprised of several iSCSI
+ // connections.
+ //
+ Private->DevicePath = IScsiGetTcpConnDevicePath (Private);
+ if (Private->DevicePath == NULL) {
+ goto ON_ERROR;
+ }
+ //
+ // Install the updated device path onto the ExtScsiPassThruHandle.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ Private->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // ISCSI children should share the default Tcp child, just open the default Tcp child via BY_CHILD_CONTROLLER.
+ //
+ Status = gBS->OpenProtocol (
+ Private->ChildHandle, /// Default Tcp child
+ &gEfiTcp4ProtocolGuid,
+ &Interface,
+ This->DriverBindingHandle,
+ Private->ExtScsiPassThruHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru,
+ &gEfiDevicePathProtocolGuid,
+ Private->DevicePath,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Update/Publish the iSCSI Boot Firmware Table.
+ //
+ IScsiPublishIbft ();
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ IScsiSessionAbort (&Private->Session);
+
+ return Status;
+}
+
+/**
+ Stop this driver on ControllerHandle.
+
+ Release the control of this controller and remove the IScsi functions. The Stop()
+ function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.Not used.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.Not used.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the Handle
+ because its interfaces are being used.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_HANDLE IScsiController;
+ EFI_STATUS Status;
+ ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
+ ISCSI_DRIVER_DATA *Private;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+ ISCSI_CONNECTION *Conn;
+
+ if (NumberOfChildren != 0) {
+ //
+ // We should have only one child.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandleBuffer[0],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **) &PassThru,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
+ Conn = NET_LIST_HEAD (&Private->Session.Conns, ISCSI_CONNECTION, Link);
+
+ //
+ // Previously the TCP4 protocol is opened BY_CHILD_CONTROLLER. Just close
+ // the protocol here but not uninstall the device path protocol and
+ // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.
+ //
+ gBS->CloseProtocol (
+ Private->ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ Private->Image,
+ Private->ExtScsiPassThruHandle
+ );
+
+ gBS->CloseProtocol (
+ Conn->Tcp4Io.Handle,
+ &gEfiTcp4ProtocolGuid,
+ Private->Image,
+ Private->ExtScsiPassThruHandle
+ );
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Get the handle of the controller we are controling.
+ //
+ IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);
+
+ Status = gBS->OpenProtocol (
+ IScsiController,
+ &gEfiCallerIdGuid,
+ (VOID **)&IScsiIdentifier,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
+
+ if (Private->ChildHandle != NULL) {
+ Status = gBS->CloseProtocol (
+ Private->ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ This->DriverBindingHandle,
+ IScsiController
+ );
+
+ ASSERT (!EFI_ERROR (Status));
+
+ Status = NetLibDestroyServiceChild (
+ IScsiController,
+ This->DriverBindingHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ Private->ChildHandle
+ );
+ ASSERT (!EFI_ERROR (Status));
+ }
+
+ IScsiConfigUpdateForm (This->DriverBindingHandle, IScsiController, FALSE);
+
+ //
+ // Uninstall the private protocol.
+ //
+ gBS->UninstallProtocolInterface (
+ IScsiController,
+ &gEfiCallerIdGuid,
+ &Private->IScsiIdentifier
+ );
+
+ //
+ // Update the iSCSI Boot Firware Table.
+ //
+ IScsiPublishIbft ();
+
+ IScsiSessionAbort (&Private->Session);
+ Status = IScsiCleanDriverData (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Unloads an image(the iSCSI driver).
+
+ @param[in] ImageHandle Handle that identifies the image to be unloaded.
+
+ @retval EFI_SUCCESS The image has been unloaded.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+EfiIScsiUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN DeviceHandleCount;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN Index;
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
+
+ //
+ // Try to disonnect the driver from the devices it's controlling.
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = IScsiTestManagedDevice (
+ DeviceHandleBuffer[Index],
+ gIScsiDriverBinding.DriverBindingHandle,
+ &gEfiTcp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ gIScsiDriverBinding.DriverBindingHandle,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Unload the iSCSI configuration form.
+ //
+ Status = IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Uninstall the ComponentName and ComponentName2 protocol from iSCSI4 driver binding handle
+ // if it has been installed.
+ //
+ Status = gBS->HandleProtocol (
+ gIScsiDriverBinding.DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiDriverBinding.DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = gBS->HandleProtocol (
+ gIScsiDriverBinding.DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiDriverBinding.DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ ComponentName2,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiDriverBinding,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ &gIScsiInitiatorName,
+ NULL
+ );
+ON_EXIT:
+
+ if (DeviceHandleBuffer != NULL) {
+ FreePool (DeviceHandleBuffer);
+ }
+
+ return Status;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers. It initialize the global variables and
+ publish the driver binding protocol.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_ACCESS_DENIED EFI_ISCSI_INITIATOR_NAME_PROTOCOL was installed unexpectedly.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_ISCSI_INITIATOR_NAME_PROTOCOL *IScsiInitiatorName;
+
+ //
+ // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ (VOID**) &IScsiInitiatorName
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Initialize the EFI Driver Library
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIScsiDriverBinding,
+ ImageHandle,
+ &gIScsiComponentName,
+ &gIScsiComponentName2
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Install the iSCSI Initiator Name Protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &gIScsiInitiatorName
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiDriverBinding,
+ &gEfiComponentName2ProtocolGuid,
+ &gIScsiComponentName2,
+ &gEfiComponentNameProtocolGuid,
+ &gIScsiComponentName,
+ NULL
+ );
+ return Status;
+ }
+
+ //
+ // Initialize the configuration form of iSCSI.
+ //
+ Status = IScsiConfigFormInit ();
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiDriverBinding,
+ &gEfiComponentName2ProtocolGuid,
+ &gIScsiComponentName2,
+ &gEfiComponentNameProtocolGuid,
+ &gIScsiComponentName,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ &gIScsiInitiatorName,
+ NULL
+ );
+ }
+ }
+ return Status;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.h
new file mode 100644
index 0000000000..14f640444f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.h
@@ -0,0 +1,140 @@
+/** @file
+ The header file of IScsiDriver.c.
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_DRIVER_H_
+#define _ISCSI_DRIVER_H_
+
+#include <Uefi.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/HiiConfigAccess.h>
+#include <Protocol/HiiDatabase.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ScsiPassThruExt.h>
+
+#define ISCSI_INITIATOR_NAME_VAR_NAME L"I_NAME"
+
+typedef struct _ISCSI_PRIVATE_PROTOCOL {
+ UINT32 Reserved;
+} ISCSI_PRIVATE_PROTOCOL;
+
+//
+// EFI Driver Binding Protocol for iSCSI driver.
+//
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is optional for bus drivers.
+
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive acces.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. The Start() function is designed to be
+ invoked from the EFI boot service ConnectController(). As a result, much of
+ the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is optional for bus drivers.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.
+ Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ Release the control of this controller and remove the IScsi functions. The Stop()
+ function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.Not used.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.Not used.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDxe.inf b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDxe.inf
new file mode 100644
index 0000000000..4fd2a62af9
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDxe.inf
@@ -0,0 +1,125 @@
+## @file
+# This module produces EFI iSCSI Initiator Name Protocol.
+#
+# This module produces EFI iSCSI Initiator Name Protocol upon EFI TCPv4 Protocol
+# and EFI DHCPv4 Protocol, to provide the capability to do the transport for SCSI
+# data over TCP/IP. It installs EFI HII Configuration Access Protocol to provide
+# one way to configurate the iSCSI setting.
+#
+# Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = IScsi4Dxe
+ MODULE_UNI_FILE = IScsi4Dxe.uni
+ FILE_GUID = 4579B72D-7EC4-4dd4-8486-083C86B182A7
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = IScsiDriverEntryPoint
+ UNLOAD_IMAGE = EfiIScsiUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gIScsiDriverBinding
+# COMPONENT_NAME = gIScsiComponentName
+# COMPONENT_NAME2 = gIScsiComponentName2
+#
+
+[Sources]
+ IScsiTcp4Io.h
+ IScsiProto.h
+ IScsiMisc.h
+ IScsiIbft.h
+ IScsiExtScsiPassThru.h
+ IScsiDriver.h
+ IScsiDhcp.h
+ IScsiCommon.h
+ IScsiCHAP.h
+ IScsiInitiatorName.h
+ ComponentName.h
+ Md5.h
+ IScsiTcp4Io.c
+ IScsiProto.c
+ IScsiMisc.c
+ IScsiInitiatorName.c
+ IScsiIbft.c
+ IScsiExtScsiPassThru.c
+ IScsiDriver.c
+ IScsiDhcp.c
+ IScsiCHAP.c
+ ComponentName.c
+ Md5.c
+ IScsiConfigDxeStrings.uni
+ IScsiConfigDxe.vfr
+ IScsiConfig.c
+ IScsiConfig.h
+ IScsiImpl.h
+ IScsiConfigNVDataStruc.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ DevicePathLib
+ DebugLib
+ PrintLib
+ HiiLib
+ NetLib
+
+[Protocols]
+ ## PRODUCES
+ ## UNDEFINED # Variable
+ gEfiIScsiInitiatorNameProtocolGuid
+ gEfiExtScsiPassThruProtocolGuid ## BY_START
+ gEfiTcp4ProtocolGuid ## TO_START
+ gEfiTcp4ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp4ProtocolGuid ## TO_START
+ gEfiDhcp4ServiceBindingProtocolGuid ## TO_START
+ ## TO_START
+ ## PRODUCES
+ gEfiDevicePathProtocolGuid
+ gEfiHiiConfigAccessProtocolGuid ## PRODUCES
+ gEfiHiiDatabaseProtocolGuid ## CONSUMES
+ gEfiPciIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiAcpiTableProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event
+ gEfiIfrTianoGuid ## SOMETIMES_PRODUCES ## UNDEFINED
+ gEfiAcpiTableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAcpi10TableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAcpi20TableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ ## SOMETIMES_PRODUCES ## Variable:L"iSCSIDeviceList"
+ ## SOMETIMES_CONSUMES ## Variable:L"iSCSIDeviceList"
+ ## SOMETIMES_CONSUMES ## UNDEFINED # HiiIsConfigHdrMatch mVendorStorageName
+ ## SOMETIMES_PRODUCES ## UNDEFINED # HiiConstructConfigHdr mVendorStorageName
+ ## SOMETIMES_PRODUCES ## UNDEFINED # HiiGetBrowserData mVendorStorageName
+ ## SOMETIMES_CONSUMES ## UNDEFINED # HiiSetBrowserData mVendorStorageName
+ ## SOMETIMES_CONSUMES ## HII
+ gIp4IScsiConfigGuid
+ ## SOMETIMES_PRODUCES ## Variable
+ ## SOMETIMES_CONSUMES ## Variable
+ gIScsiCHAPAuthInfoGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ IScsi4DxeExtra.uni \ No newline at end of file
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.c
new file mode 100644
index 0000000000..e9e37b7e37
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.c
@@ -0,0 +1,412 @@
+/** @file
+ The IScsi's EFI_EXT_SCSI_PASS_THRU_PROTOCOL driver.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+/**
+ Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function
+ supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the
+ nonblocking I/O functionality is optional.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in] Target The Target is an array of size TARGET_MAX_BYTES and it represents
+ the id of the SCSI device to send the SCSI Request Packet. Each
+ transport driver may chose to utilize a subset of this size to suit the needs
+ of transport target representation. For example, a Fibre Channel driver
+ may use only 8 bytes (WWN) to represent an FC target.
+ @param[in] Lun The LUN of the SCSI device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to the SCSI device
+ specified by Target and Lun.
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored, and blocking
+ I/O is performed. If Event is NULL, then blocking I/O is performed. If
+ Event is not NULL and non blocking I/O is supported, then
+ nonblocking I/O is performed, and Event will be signaled when the
+ SCSI Request Packet completes.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that
+ could be transferred is returned in InTransferLength. For write
+ and bi-directional commands, OutTransferLength bytes were
+ transferred by OutDataBuffer. Currently not implemeted.
+ @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many
+ SCSI Request Packets already queued. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid.
+ @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported
+ by the host adapter. This includes the case of Bi-directional SCSI
+ commands not supported by the implementation. The SCSI Request
+ Packet was not sent, so no additional status information is available.
+ Currently not implemeted.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruFunction (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ EFI_STATUS Status;
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ ConfigNvData = &Private->Session.ConfigData.NvData;
+
+ if (Target[0] != 0 || (CompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet == NULL) || (Packet->Cdb == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = IScsiExecuteScsiCommand (This, Target, Lun, Packet);
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
+ //
+ // Try to reinstate the session and re-execute the Scsi command.
+ //
+ if (EFI_ERROR (IScsiSessionReinstatement (Private))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = IScsiExecuteScsiCommand (This, Target, Lun, Packet);
+ }
+
+ return Status;
+}
+
+/**
+ Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These
+ can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal
+ Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the
+ Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI
+ channel.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in, out] Target On input, a pointer to the Target ID (an array of size
+ TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel.
+ On output, a pointer to the Target ID (an array of
+ TARGET_MAX_BYTES) of the next SCSI device present on a SCSI
+ channel. An input value of 0xF(all bytes in the array are 0xF) in the
+ Target array retrieves the Target ID of the first SCSI device present on a
+ SCSI channel.
+ @param[in, out] Lun On input, a pointer to the LUN of a SCSI device present on the SCSI
+ channel. On output, a pointer to the LUN of the next SCSI device present
+ on a SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI
+ channel was returned in Target and Lun.
+ @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were
+ not returned on a previous call to GetNextTargetLun().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target,
+ IN OUT UINT64 *Lun
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ UINT8 TargetId[TARGET_MAX_BYTES];
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ ConfigNvData = &Private->Session.ConfigData.NvData;
+
+ if ((*Target)[0] == 0 && (CompareMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)) == 0)) {
+ //
+ // Only one <Target, Lun> pair per iSCSI Driver instance.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);
+ if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {
+ (*Target)[0] = 0;
+ CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));
+
+ return EFI_SUCCESS;
+ }
+
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Used to allocate and build a device path node for a SCSI device on a SCSI channel.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in] Target The Target is an array of size TARGET_MAX_BYTES and it specifies the
+ Target ID of the SCSI device for which a device path node is to be
+ allocated and built. Transport drivers may chose to utilize a subset of
+ this size to suit the representation of targets. For example, a Fibre
+ Channel driver may use only 8 bytes (WWN) in the array to represent a
+ FC target.
+ @param[in] Lun The LUN of the SCSI device for which a device path node is to be
+ allocated and built.
+ @param[in, out] DevicePath A pointer to a single device path node that describes the SCSI device
+ specified by Target and Lun. This function is responsible for
+ allocating the buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's responsibility to free
+ DevicePath when the caller is finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SCSI device specified by
+ Target and Lun was allocated and returned in
+ DevicePath.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist
+ on the SCSI channel.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION *Session;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
+ EFI_DEV_PATH *Node;
+ UINTN DevPathNodeLen;
+
+ if (DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Target[0] != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ Session = &Private->Session;
+ ConfigNvData = &Session->ConfigData.NvData;
+ AuthConfig = &Session->AuthData.AuthConfig;
+
+ if (CompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ DevPathNodeLen = sizeof (ISCSI_DEVICE_PATH) + AsciiStrLen (ConfigNvData->TargetName) + 1;
+ Node = AllocatePool (DevPathNodeLen);
+ if (Node == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_ISCSI_DP;
+ SetDevicePathNodeLength (&Node->DevPath, (UINT16)DevPathNodeLen);
+
+ //
+ // 0 for TCP, others are reserved.
+ //
+ Node->Iscsi.NetworkProtocol = 0;
+
+ Node->Iscsi.LoginOption = 0;
+ switch (AuthConfig->CHAPType) {
+ case ISCSI_CHAP_NONE:
+ Node->Iscsi.LoginOption |= 0x0800;
+ break;
+
+ case ISCSI_CHAP_UNI:
+ Node->Iscsi.LoginOption |= 0x1000;
+ break;
+
+ default:
+ break;
+ }
+
+ CopyMem (&Node->Iscsi.Lun, ConfigNvData->BootLun, sizeof (UINT64));
+ Node->Iscsi.TargetPortalGroupTag = Session->TargetPortalGroupTag;
+ AsciiStrCpyS ((CHAR8 *) Node + sizeof (ISCSI_DEVICE_PATH), AsciiStrLen (ConfigNvData->TargetName) + 1, ConfigNvData->TargetName);
+
+ *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Used to translate a device path node to a Target ID and LUN.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in] DevicePath A pointer to a single device path node that describes the SCSI device
+ on the SCSI channel.
+ @param[out] Target A pointer to the Target Array which represents the ID of a SCSI device
+ on the SCSI channel.
+ @param[out] Lun A pointer to the LUN of a SCSI device on the SCSI channel.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and
+ LUN, and they were returned in Target and Lun.
+ @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL.
+ @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN
+ does not exist.Currently not implemented.
+ @retval EFI_UNSUPPORTED This driver does not support the device path node type in
+ DevicePath.
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **Target,
+ OUT UINT64 *Lun
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+
+ if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
+ (DevicePath->SubType != MSG_ISCSI_DP) ||
+ (DevicePathNodeLength (DevicePath) <= sizeof (ISCSI_DEVICE_PATH))
+ ) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ ConfigNvData = &Private->Session.ConfigData.NvData;
+
+ SetMem (*Target, TARGET_MAX_BYTES, 0xFF);
+ (*Target)[0] = 0;
+
+ if (AsciiStrCmp (ConfigNvData->TargetName, (CHAR8 *) DevicePath + sizeof (ISCSI_DEVICE_PATH)) != 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel.
+ Currently not implemented.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The SCSI channel was reset.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel.
+ @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel.
+ @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation.
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Resets a SCSI logical unit that is connected to a SCSI channel. Currently not implemented.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in] Target The Target is an array of size TARGET_MAX_BYTE and it represents the
+ target port ID of the SCSI device containing the SCSI logical unit to
+ reset. Transport drivers may chose to utilize a subset of this array to suit
+ the representation of their targets.
+ @param[in] Lun The LUN of the SCSI device to reset.
+
+ @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device
+ specified by Target and Lun.
+ @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device
+ specified by Target and Lun.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either
+ be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs
+ for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to
+ see if a SCSI device is actually present at that location on the SCSI channel.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in, out] Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel.
+ On output, a pointer to the Target ID (an array of
+ TARGET_MAX_BYTES) of the next SCSI device present on a SCSI
+ channel. An input value of 0xF(all bytes in the array are 0xF) in the
+ Target array retrieves the Target ID of the first SCSI device present on a
+ SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI
+ channel was returned in Target.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT Target array is not all 0xF, and Target were not
+ returned on a previous call to GetNextTarget().
+ Currently not implemented.
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target
+ )
+{
+ UINT8 TargetId[TARGET_MAX_BYTES];
+
+ SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);
+
+ if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {
+ (*Target)[0] = 0;
+ return EFI_SUCCESS;
+ } else if ((*Target)[0] == 0) {
+ return EFI_NOT_FOUND;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
+EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate = {
+ NULL,
+ IScsiExtScsiPassThruFunction,
+ IScsiExtScsiPassThruGetNextTargetLun,
+ IScsiExtScsiPassThruBuildDevicePath,
+ IScsiExtScsiPassThruGetTargetLun,
+ IScsiExtScsiPassThruResetChannel,
+ IScsiExtScsiPassThruResetTargetLun,
+ IScsiExtScsiPassThruGetNextTarget
+};
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.h
new file mode 100644
index 0000000000..e635e47b92
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiExtScsiPassThru.h
@@ -0,0 +1,22 @@
+/** @file
+ The header file of IScsiExtScsiPassThru.c.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_EXT_SCSI_PASS_THRU_H_
+#define _ISCSI_EXT_SCSI_PASS_THRU_H_
+
+#include <Protocol/ScsiPassThruExt.h>
+
+extern EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate;
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c
new file mode 100644
index 0000000000..45d89a6933
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c
@@ -0,0 +1,539 @@
+/** @file
+ Implementation for iSCSI Boot Firmware Table publication.
+
+Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+BOOLEAN mIbftInstalled = FALSE;
+UINTN mTableKey;
+
+/**
+ Initialize the header of the iSCSI Boot Firmware Table.
+
+ @param[out] Header The header of the iSCSI Boot Firmware Table.
+ @param[in] OemId The OEM ID.
+ @param[in] OemTableId The OEM table ID for the iBFT.
+**/
+VOID
+IScsiInitIbfTableHeader (
+ OUT EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Header,
+ IN UINT8 *OemId,
+ IN UINT64 *OemTableId
+ )
+{
+ ZeroMem (Header, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER));
+
+ Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE;
+ Header->Length = IBFT_HEAP_OFFSET;
+ Header->Revision = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION;
+ Header->Checksum = 0;
+
+ CopyMem (Header->OemId, OemId, sizeof (Header->OemId));
+ CopyMem (&Header->OemTableId, OemTableId, sizeof (UINT64));
+}
+
+/**
+ Initialize the control section of the iSCSI Boot Firmware Table.
+
+ @param[in] Table The ACPI table.
+ @param[in] HandleCount The number of the handles associated with iSCSI sessions, it's
+ equal to the number of iSCSI sessions.
+**/
+VOID
+IScsiInitControlSection (
+ IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
+ IN UINTN HandleCount
+ )
+{
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
+ UINTN NumOffset;
+
+ Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
+
+ ZeroMem (Control, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE));
+
+ Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID;
+ Control->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION;
+ Control->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE);
+
+ //
+ // Each session occupies two offsets, one for the NIC section,
+ // the other for the Target section.
+ //
+ NumOffset = 2 * HandleCount;
+ if (NumOffset > 4) {
+ //
+ // Need expand the control section if more than 2 NIC/Target sections
+ // exist.
+ //
+ Control->Header.Length = (UINT16) (Control->Header.Length + (NumOffset - 4) * sizeof (UINT16));
+ }
+}
+
+/**
+ Add one item into the heap.
+
+ @param[in, out] Heap On input, the current address of the heap; On output, the address of
+ the heap after the item is added.
+ @param[in] Data The data to add into the heap.
+ @param[in] Len Length of the Data in byte.
+**/
+VOID
+IScsiAddHeapItem (
+ IN OUT UINT8 **Heap,
+ IN VOID *Data,
+ IN UINTN Len
+ )
+{
+ //
+ // Add one byte for the NULL delimiter.
+ //
+ *Heap -= Len + 1;
+
+ CopyMem (*Heap, Data, Len);
+ *(*Heap + Len) = 0;
+}
+
+/**
+ Fill the Initiator section of the iSCSI Boot Firmware Table.
+
+ @param[in] Table The ACPI table.
+ @param[in, out] Heap The heap.
+ @param[in] Handle The handle associated with the iSCSI session.
+**/
+VOID
+IScsiFillInitiatorSection (
+ IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
+ IN OUT UINT8 **Heap,
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *Initiator;
+ ISCSI_DRIVER_DATA *DriverData;
+ ISCSI_SESSION *Session;
+ ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
+ EFI_STATUS Status;
+
+ Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
+
+ //
+ // Initiator section immediately follows the control section.
+ //
+ Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *) ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length));
+
+ Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table);
+
+ ZeroMem (Initiator, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE));
+
+ Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID;
+ Initiator->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION;
+ Initiator->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE);
+ Initiator->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED;
+
+ //
+ // Get the identifier from the handle.
+ //
+ Status = gBS->HandleProtocol (Handle, &gEfiCallerIdGuid, (VOID **) &IScsiIdentifier);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return ;
+ }
+
+ DriverData = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
+ Session = &DriverData->Session;
+
+ //
+ // Fill the iSCSI Initiator Name into the heap.
+ //
+ IScsiAddHeapItem (Heap, Session->InitiatorName, Session->InitiatorNameLength - 1);
+
+ Initiator->IScsiNameLength = (UINT16) (Session->InitiatorNameLength - 1);
+ Initiator->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+}
+
+/**
+ Map the v4 IP address into v6 IP address.
+
+ @param[in] V4 The v4 IP address.
+ @param[out] V6 The v6 IP address.
+**/
+VOID
+IScsiMapV4ToV6Addr (
+ IN EFI_IPv4_ADDRESS *V4,
+ OUT EFI_IPv6_ADDRESS *V6
+ )
+{
+ UINTN Index;
+
+ ZeroMem (V6, sizeof (EFI_IPv6_ADDRESS));
+
+ V6->Addr[10] = 0xff;
+ V6->Addr[11] = 0xff;
+
+ for (Index = 0; Index < 4; Index++) {
+ V6->Addr[12 + Index] = V4->Addr[Index];
+ }
+}
+
+/**
+ Get the NIC's PCI location and return it according to the composited
+ format defined in iSCSI Boot Firmware Table.
+
+ @param[in] Controller The handle of the controller.
+
+ @return UINT16 The composited representation of the NIC PCI location.
+ @retval 0 Other errors as indicated.
+**/
+UINT16
+IScsiGetNICPciLocation (
+ IN EFI_HANDLE Controller
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE PciIoHandle;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINTN Segment;
+ UINTN Bus;
+ UINTN Device;
+ UINTN Function;
+
+ Status = gBS->HandleProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ Status = gBS->LocateDevicePath (
+ &gEfiPciIoProtocolGuid,
+ &DevicePath,
+ &PciIoHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **)&PciIo);
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ return (UINT16) ((Bus << 8) | (Device << 3) | Function);
+}
+
+/**
+ Fill the NIC and target sections in iSCSI Boot Firmware Table.
+
+ @param[in] Table The buffer of the ACPI table.
+ @param[in, out] Heap The heap buffer used to store the variable length parameters such as iSCSI name.
+ @param[in] HandleCount Count The number of handles having iSCSI private protocol installed.
+ @param[in] Handles The handle buffer.
+**/
+VOID
+IScsiFillNICAndTargetSections (
+ IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
+ IN OUT UINT8 **Heap,
+ IN UINTN HandleCount,
+ IN EFI_HANDLE *Handles
+ )
+{
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *Nic;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *Target;
+ ISCSI_DRIVER_DATA *DriverData;
+ ISCSI_SESSION_CONFIG_DATA *SessionConfigData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
+ UINT16 *SectionOffset;
+ UINTN Index;
+ UINT16 Length;
+ EFI_MAC_ADDRESS MacAddress;
+ UINTN HwAddressSize;
+ ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
+ EFI_STATUS Status;
+
+ //
+ // Get the offset of the first Nic and Target section.
+ //
+ Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
+ Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table +
+ Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE)));
+ Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +
+ IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));
+
+ SectionOffset = &Control->NIC0Offset;
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (Handles[Index], &gEfiCallerIdGuid, (VOID **)&IScsiIdentifier);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return ;
+ }
+
+ DriverData = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
+ SessionConfigData = &DriverData->Session.ConfigData;
+ AuthConfig = &DriverData->Session.AuthData.AuthConfig;
+
+ //
+ // Fill the Nic section.
+ //
+ ZeroMem (Nic, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE));
+
+ Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID;
+ Nic->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION;
+ Nic->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE);
+ Nic->Header.Index = (UINT8) Index;
+ Nic->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID |
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED |
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL;
+
+ //
+ // Get the subnet mask prefix length.
+ //
+ Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&SessionConfigData->NvData.SubnetMask);
+
+ if (SessionConfigData->NvData.InitiatorInfoFromDhcp) {
+ Nic->Origin = IpPrefixOriginDhcp;
+ } else {
+ Nic->Origin = IpPrefixOriginManual;
+ }
+ //
+ // Map the various v4 addresses into v6 addresses.
+ //
+ IScsiMapV4ToV6Addr (&SessionConfigData->NvData.LocalIp, &Nic->Ip);
+ IScsiMapV4ToV6Addr (&SessionConfigData->NvData.Gateway, &Nic->Gateway);
+ IScsiMapV4ToV6Addr (&SessionConfigData->PrimaryDns, &Nic->PrimaryDns);
+ IScsiMapV4ToV6Addr (&SessionConfigData->SecondaryDns, &Nic->SecondaryDns);
+ IScsiMapV4ToV6Addr (&SessionConfigData->DhcpServer, &Nic->DhcpServer);
+
+ Nic->VLanTag = NetLibGetVlanId (DriverData->Controller);
+
+ Status = NetLibGetMacAddress (DriverData->Controller, &MacAddress, &HwAddressSize);
+ ASSERT (Status == EFI_SUCCESS);
+ CopyMem (Nic->Mac, MacAddress.Addr, sizeof (Nic->Mac));
+
+ //
+ // Get the PCI location of the Nic.
+ //
+ Nic->PciLocation = IScsiGetNICPciLocation (DriverData->Controller);
+
+ *SectionOffset = (UINT16) ((UINTN) Nic - (UINTN) Table);
+ SectionOffset++;
+
+ //
+ // Fill the Target section.
+ //
+ ZeroMem (Target, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE));
+
+ Target->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID;
+ Target->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION;
+ Target->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE);
+ Target->Header.Index = (UINT8) Index;
+ Target->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED;
+ Target->Port = SessionConfigData->NvData.TargetPort;
+ Target->NicIndex = (UINT8) Index;
+
+ if (AuthConfig->CHAPType == ISCSI_CHAP_NONE) {
+ Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_NO_CHAP;
+ } if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) {
+ Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_CHAP;
+ } else if (AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
+ Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_MUTUAL_CHAP;
+ }
+
+ IScsiMapV4ToV6Addr (&SessionConfigData->NvData.TargetIp, &Target->Ip);
+ CopyMem (Target->BootLun, SessionConfigData->NvData.BootLun, sizeof (Target->BootLun));
+
+ //
+ // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret.
+ //
+ Length = (UINT16) AsciiStrLen (SessionConfigData->NvData.TargetName);
+ IScsiAddHeapItem (Heap, SessionConfigData->NvData.TargetName, Length);
+
+ Target->IScsiNameLength = Length;
+ Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ if (Target->CHAPType != EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_NO_CHAP) {
+ //
+ // CHAP Name
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName);
+ IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length);
+ Target->CHAPNameLength = Length;
+ Target->CHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ //
+ // CHAP Secret
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret);
+ IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length);
+ Target->CHAPSecretLength = Length;
+ Target->CHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ if (Target->CHAPType == EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_MUTUAL_CHAP) {
+ //
+ // Reverse CHAP Name
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName);
+ IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length);
+ Target->ReverseCHAPNameLength = Length;
+ Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ //
+ // Reverse CHAP Secret
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret);
+ IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length);
+ Target->ReverseCHAPSecretLength = Length;
+ Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+ }
+ }
+
+ *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table);
+ SectionOffset++;
+
+ //
+ // Advance to the next NIC/Target pair
+ //
+ Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target +
+ IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE)));
+ Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +
+ IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));
+ }
+}
+
+/**
+ Publish and remove the iSCSI Boot Firmware Table according to the iSCSI
+ session status.
+**/
+VOID
+IScsiPublishIbft (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINT8 *Heap;
+ UINT8 Checksum;
+ EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
+ EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
+ EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
+
+ Rsdt = NULL;
+ Xsdt = NULL;
+
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **)&AcpiTableProtocol);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+
+ //
+ // Find ACPI table RSD_PTR from system table
+ //
+ Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **) &Rsdp);
+ if (EFI_ERROR (Status)) {
+ Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **) &Rsdp);
+ }
+
+ if (EFI_ERROR (Status) || (Rsdp == NULL)) {
+ return ;
+ } else if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION && Rsdp->XsdtAddress != 0) {
+ Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->XsdtAddress;
+ } else if (Rsdp->RsdtAddress != 0) {
+ Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress;
+ }
+
+ if ((Xsdt == NULL) && (Rsdt == NULL)) {
+ return ;
+ }
+
+ if (mIbftInstalled) {
+ Status = AcpiTableProtocol->UninstallAcpiTable (
+ AcpiTableProtocol,
+ mTableKey
+ );
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+ mIbftInstalled = FALSE;
+ }
+
+ //
+ // Get all iSCSI private protocols.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiCallerIdGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+ //
+ // Allocate 4k bytes to hold the ACPI table.
+ //
+ Table = AllocateZeroPool (IBFT_MAX_SIZE);
+ if (Table == NULL) {
+ return ;
+ }
+
+ Heap = (UINT8 *) Table + IBFT_HEAP_OFFSET;
+
+ //
+ // Fill in the various section of the iSCSI Boot Firmware Table.
+ //
+ if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
+ IScsiInitIbfTableHeader (Table, Xsdt->OemId, &Xsdt->OemTableId);
+ } else {
+ IScsiInitIbfTableHeader (Table, Rsdt->OemId, &Rsdt->OemTableId);
+ }
+
+ IScsiInitControlSection (Table, HandleCount);
+ IScsiFillInitiatorSection (Table, &Heap, HandleBuffer[0]);
+ IScsiFillNICAndTargetSections (Table, &Heap, HandleCount, HandleBuffer);
+
+ Checksum = CalculateCheckSum8((UINT8 *)Table, Table->Length);
+ Table->Checksum = Checksum;
+
+ FreePool (HandleBuffer);
+
+ //
+ // Install or update the iBFT table.
+ //
+ Status = AcpiTableProtocol->InstallAcpiTable (
+ AcpiTableProtocol,
+ Table,
+ Table->Length,
+ &mTableKey
+ );
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+
+ mIbftInstalled = TRUE;
+ FreePool (Table);
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.h
new file mode 100644
index 0000000000..4f3828db63
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.h
@@ -0,0 +1,38 @@
+/** @file
+ Some extra definitions for iBFT.
+
+Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_IBFT_H_
+#define _ISCSI_IBFT_H_
+
+#include <IndustryStandard/Acpi.h>
+#include <IndustryStandard/IScsiBootFirmwareTable.h>
+#include <Protocol/AcpiTable.h>
+#include <Protocol/PciIo.h>
+
+#define IBFT_TABLE_VAR_NAME L"iBFT"
+#define IBFT_MAX_SIZE 4096
+#define IBFT_HEAP_OFFSET 2048
+
+#define IBFT_ROUNDUP(size) NET_ROUNDUP ((size), EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_STRUCTURE_ALIGNMENT)
+
+/**
+ Publish and remove the iSCSI Boot Firmware Table according to the iSCSI
+ session status.
+**/
+VOID
+IScsiPublishIbft (
+ VOID
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiImpl.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiImpl.h
new file mode 100644
index 0000000000..082199c1b7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiImpl.h
@@ -0,0 +1,169 @@
+/** @file
+ The header file of IScsiImpl.c.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_IMPL_H_
+#define _ISCSI_IMPL_H_
+
+#include <Uefi.h>
+
+#include <IndustryStandard/Dhcp.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/NetLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Guid/EventGroup.h>
+#include <Guid/Acpi.h>
+
+#include "IScsiCommon.h"
+#include "IScsiDriver.h"
+#include "IScsiInitiatorName.h"
+#include "ComponentName.h"
+#include "IScsiConfigNVDataStruc.h"
+#include "IScsiExtScsiPassThru.h"
+#include "IScsiProto.h"
+#include "IScsiMisc.h"
+#include "IScsiCHAP.h"
+#include "IScsiConfig.h"
+#include "IScsiDhcp.h"
+#include "IScsiTcp4Io.h"
+#include "IScsiIbft.h"
+
+
+#define ISCSI_SESSION_SIGNATURE SIGNATURE_32 ('I', 'S', 'S', 'N')
+
+struct _ISCSI_SESSION {
+ UINT32 Signature;
+
+ ISCSI_SESSION_CONFIG_DATA ConfigData;
+ ISCSI_CHAP_AUTH_DATA AuthData;
+
+ CHAR8 InitiatorName[ISCSI_NAME_MAX_SIZE];
+ UINTN InitiatorNameLength;
+ UINT8 State;
+
+ UINT8 Isid[6];
+ UINT16 Tsih;
+
+ UINT32 CmdSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+
+ UINT32 InitiatorTaskTag;
+ UINT16 NextCid;
+
+ LIST_ENTRY Conns;
+ UINT32 NumConns;
+
+ LIST_ENTRY TcbList;
+
+ //
+ // session-wide parameters
+ //
+ UINT16 TargetPortalGroupTag;
+ UINT32 MaxConnections;
+ BOOLEAN InitialR2T;
+ BOOLEAN ImmediateData;
+ UINT32 MaxBurstLength;
+ UINT32 FirstBurstLength;
+ UINT32 DefaultTime2Wait;
+ UINT32 DefaultTime2Retain;
+ UINT16 MaxOutstandingR2T;
+ BOOLEAN DataPDUInOrder;
+ BOOLEAN DataSequenceInOrder;
+ UINT8 ErrorRecoveryLevel;
+};
+
+#define ISCSI_CONNECTION_SIGNATURE SIGNATURE_32 ('I', 'S', 'C', 'N')
+
+struct _ISCSI_CONNECTION {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ EFI_EVENT TimeoutEvent;
+
+ ISCSI_SESSION *Session;
+
+ UINT8 State;
+ UINT8 CurrentStage;
+ UINT8 NextStage;
+
+ UINT8 CHAPStep;
+
+ BOOLEAN PartialReqSent;
+ BOOLEAN PartialRspRcvd;
+
+ BOOLEAN TransitInitiated;
+
+ UINT16 Cid;
+ UINT32 ExpStatSN;
+
+ //
+ // queues...
+ //
+ NET_BUF_QUEUE RspQue;
+
+ TCP4_IO Tcp4Io;
+
+ //
+ // connection-only parameters
+ //
+ UINT32 MaxRecvDataSegmentLength;
+ ISCSI_DIGEST_TYPE HeaderDigest;
+ ISCSI_DIGEST_TYPE DataDigest;
+};
+
+#define ISCSI_DRIVER_DATA_SIGNATURE SIGNATURE_32 ('I', 'S', 'D', 'A')
+
+#define ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU(PassThru) \
+ CR ( \
+ PassThru, \
+ ISCSI_DRIVER_DATA, \
+ IScsiExtScsiPassThru, \
+ ISCSI_DRIVER_DATA_SIGNATURE \
+ )
+#define ISCSI_DRIVER_DATA_FROM_IDENTIFIER(Identifier) \
+ CR ( \
+ Identifier, \
+ ISCSI_DRIVER_DATA, \
+ IScsiIdentifier, \
+ ISCSI_DRIVER_DATA_SIGNATURE \
+ )
+#define ISCSI_DRIVER_DATA_FROM_SESSION(s) \
+ CR ( \
+ s, \
+ ISCSI_DRIVER_DATA, \
+ Session, \
+ ISCSI_DRIVER_DATA_SIGNATURE \
+ )
+
+struct _ISCSI_DRIVER_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+ ISCSI_PRIVATE_PROTOCOL IScsiIdentifier;
+ EFI_HANDLE ChildHandle;
+ EFI_EVENT ExitBootServiceEvent;
+
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL IScsiExtScsiPassThru;
+ EFI_EXT_SCSI_PASS_THRU_MODE ExtScsiPassThruMode;
+ EFI_HANDLE ExtScsiPassThruHandle;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ ISCSI_SESSION Session;
+};
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.c
new file mode 100644
index 0000000000..e753d9eebd
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.c
@@ -0,0 +1,116 @@
+/** @file
+ Implementation for EFI iSCSI Initiator Name Protocol.
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName = {
+ IScsiGetInitiatorName,
+ IScsiSetInitiatorName
+};
+
+/**
+ Retrieves the current set value of iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer / Actual size of the
+ variable data buffer.
+ @param[out] Buffer Pointer to the buffer for data to be read. The data is a null-terminated UTF-8 encoded string.
+ The maximum length is 223 characters, including the null-terminator.
+
+ @retval EFI_SUCCESS Data was successfully retrieved into the provided buffer and the
+ BufferSize was sufficient to handle the iSCSI initiator name.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result.
+ @retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL.
+ @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved due to a hardware error.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ if ((BufferSize == NULL) || (Buffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gRT->GetVariable (
+ ISCSI_INITIATOR_NAME_VAR_NAME,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ BufferSize,
+ Buffer
+ );
+
+ return Status;
+}
+
+/**
+ Sets the iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer.
+ @param[in] Buffer Pointer to the buffer for data to be written. The data is a null-terminated UTF-8 encoded string.
+ The maximum length is 223 characters, including the null-terminator.
+
+ @retval EFI_SUCCESS Data was successfully stored by the protocol.
+ @retval EFI_UNSUPPORTED Platform policies do not allow for data to be written.
+ Currently not implemented.
+ @retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL, or BufferSize exceeds the maximum allowed limit.
+ @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware error.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data.
+ @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC 3720
+ (and other related protocols).
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ if ((BufferSize == NULL) || (Buffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*BufferSize > ISCSI_NAME_MAX_SIZE) {
+ *BufferSize = ISCSI_NAME_MAX_SIZE;
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // only support iqn iSCSI names.
+ //
+ Status = IScsiNormalizeName ((CHAR8 *) Buffer, *BufferSize - 1);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gRT->SetVariable (
+ ISCSI_INITIATOR_NAME_VAR_NAME,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ *BufferSize,
+ Buffer
+ );
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.h
new file mode 100644
index 0000000000..f967fb3830
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiInitiatorName.h
@@ -0,0 +1,74 @@
+/** @file
+ The header file for EFI iSCSI Initiator Name Protocol.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_INITIATOR_NAME_H_
+#define _ISCSI_INITIATOR_NAME_H_
+
+#include <Protocol/IScsiInitiatorName.h>
+
+extern EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName;
+
+//
+// EFI iSCSI Initiator Name Protocol for IScsi driver.
+//
+
+/**
+ Retrieves the current set value of iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer / Actual size of the
+ variable data buffer.
+ @param[out] Buffer Pointer to the buffer for data to be read.
+
+ @retval EFI_SUCCESS Data was successfully retrieved into the provided buffer and the
+ BufferSize was sufficient to handle the iSCSI initiator name.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result.
+ @retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL.
+ @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved due to a hardware error.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+/**
+ Sets the iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer.
+ @param[in] Buffer Pointer to the buffer for data to be written.
+
+ @retval EFI_SUCCESS Data was successfully stored by the protocol.
+ @retval EFI_UNSUPPORTED Platform policies do not allow for data to be written.
+ Currently not implemented.
+ @retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL, or BufferSize exceeds the maximum allowed limit.
+ @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware error.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data.
+ @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC 3720
+ (and other related protocols).
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.c
new file mode 100644
index 0000000000..ae202c3fe2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.c
@@ -0,0 +1,945 @@
+/** @file
+ Miscellaneous routines for iSCSI driver.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 IScsiHexString[] = "0123456789ABCDEFabcdef";
+
+/**
+ Removes (trims) specified leading and trailing characters from a string.
+
+ @param[in, out] Str Pointer to the null-terminated string to be trimmed. On return,
+ Str will hold the trimmed string.
+
+ @param[in] CharC Character will be trimmed from str.
+**/
+VOID
+StrTrim (
+ IN OUT CHAR16 *Str,
+ IN CHAR16 CharC
+ )
+{
+ CHAR16 *Pointer1;
+ CHAR16 *Pointer2;
+
+ if (*Str == 0) {
+ return;
+ }
+
+ //
+ // Trim off the leading and trailing characters c
+ //
+ for (Pointer1 = Str; (*Pointer1 != 0) && (*Pointer1 == CharC); Pointer1++) {
+ ;
+ }
+
+ Pointer2 = Str;
+ if (Pointer2 == Pointer1) {
+ while (*Pointer1 != 0) {
+ Pointer2++;
+ Pointer1++;
+ }
+ } else {
+ while (*Pointer1 != 0) {
+ *Pointer2 = *Pointer1;
+ Pointer1++;
+ Pointer2++;
+ }
+ *Pointer2 = 0;
+ }
+
+
+ for (Pointer1 = Str + StrLen(Str) - 1; Pointer1 >= Str && *Pointer1 == CharC; Pointer1--) {
+ ;
+ }
+ if (Pointer1 != Str + StrLen(Str) - 1) {
+ *(Pointer1 + 1) = 0;
+ }
+}
+
+/**
+ Calculate the prefix length of the IPv4 subnet mask.
+
+ @param[in] SubnetMask The IPv4 subnet mask.
+
+ @return The prefix length of the subnet mask.
+ @retval 0 Other errors as indicated.
+**/
+UINT8
+IScsiGetSubnetMaskPrefixLength (
+ IN EFI_IPv4_ADDRESS *SubnetMask
+ )
+{
+ UINT8 Len;
+ UINT32 ReverseMask;
+
+ //
+ // The SubnetMask is in network byte order.
+ //
+ ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]);
+
+ //
+ // Reverse it.
+ //
+ ReverseMask = ~ReverseMask;
+
+ if ((ReverseMask & (ReverseMask + 1)) != 0) {
+ return 0;
+ }
+
+ Len = 0;
+
+ while (ReverseMask != 0) {
+ ReverseMask = ReverseMask >> 1;
+ Len++;
+ }
+
+ return (UINT8) (32 - Len);
+}
+
+/**
+ Convert the hexadecimal encoded LUN string into the 64-bit LUN.
+
+ @param[in] Str The hexadecimal encoded LUN string.
+ @param[out] Lun Storage to return the 64-bit LUN.
+
+ @retval EFI_SUCCESS The 64-bit LUN is stored in Lun.
+ @retval EFI_INVALID_PARAMETER The string is malformatted.
+**/
+EFI_STATUS
+IScsiAsciiStrToLun (
+ IN CHAR8 *Str,
+ OUT UINT8 *Lun
+ )
+{
+ UINTN Index, IndexValue, IndexNum, SizeStr;
+ CHAR8 TemStr[2];
+ UINT8 TemValue;
+ UINT16 Value[4];
+
+ ZeroMem (Lun, 8);
+ ZeroMem (TemStr, 2);
+ ZeroMem ((UINT8 *) Value, sizeof (Value));
+ SizeStr = AsciiStrLen (Str);
+ IndexValue = 0;
+ IndexNum = 0;
+
+ for (Index = 0; Index < SizeStr; Index ++) {
+ TemStr[0] = Str[Index];
+ TemValue = (UINT8) AsciiStrHexToUint64 (TemStr);
+ if (TemValue == 0 && TemStr[0] != '0') {
+ if ((TemStr[0] != '-') || (IndexNum == 0)) {
+ //
+ // Invalid Lun Char
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if ((TemValue == 0) && (TemStr[0] == '-')) {
+ //
+ // Next Lun value
+ //
+ if (++IndexValue >= 4) {
+ //
+ // Max 4 Lun value
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Restart str index for the next lun value
+ //
+ IndexNum = 0;
+ continue;
+ }
+
+ if (++IndexNum > 4) {
+ //
+ // Each Lun Str can't exceed size 4, because it will be as UINT16 value
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Combine UINT16 value
+ //
+ Value[IndexValue] = (UINT16) ((Value[IndexValue] << 4) + TemValue);
+ }
+
+ for (Index = 0; Index <= IndexValue; Index ++) {
+ *((UINT16 *) &Lun[Index * 2]) = HTONS (Value[Index]);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the 64-bit LUN into the hexadecimal encoded LUN string.
+
+ @param[in] Lun The 64-bit LUN.
+ @param[out] Str The storage to return the hexadecimal encoded LUN string.
+**/
+VOID
+IScsiLunToUnicodeStr (
+ IN UINT8 *Lun,
+ OUT CHAR16 *Str
+ )
+{
+ UINTN Index;
+ CHAR16 *TempStr;
+
+ TempStr = Str;
+
+ for (Index = 0; Index < 4; Index++) {
+
+ if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) {
+ CopyMem(TempStr, L"0-", sizeof (L"0-"));
+ } else {
+ TempStr[0] = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4];
+ TempStr[1] = (CHAR16) IScsiHexString[Lun[2 * Index] & 0x0F];
+ TempStr[2] = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4];
+ TempStr[3] = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0x0F];
+ TempStr[4] = L'-';
+ TempStr[5] = 0;
+
+ StrTrim (TempStr, L'0');
+ }
+
+ TempStr += StrLen (TempStr);
+ }
+
+ ASSERT (StrLen(Str) >= 1);
+ Str[StrLen (Str) - 1] = 0;
+
+ for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) {
+ if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) {
+ Str[Index - 1] = 0;
+ } else {
+ break;
+ }
+ }
+}
+
+/**
+ Convert the ASCII string into a UNICODE string.
+
+ @param[in] Source The ASCII string.
+ @param[out] Destination The storage to return the UNICODE string.
+
+ @return CHAR16 * Pointer to the UNICODE string.
+**/
+CHAR16 *
+IScsiAsciiStrToUnicodeStr (
+ IN CHAR8 *Source,
+ OUT CHAR16 *Destination
+ )
+{
+ ASSERT (Destination != NULL);
+ ASSERT (Source != NULL);
+
+ while (*Source != '\0') {
+ *(Destination++) = (CHAR16) *(Source++);
+ }
+
+ *Destination = '\0';
+
+ return Destination;
+}
+
+/**
+ Convert the UNICODE string into an ASCII string.
+
+ @param[in] Source The UNICODE string.
+ @param[out] Destination The storage to return the ASCII string.
+
+ @return CHAR8 * Pointer to the ASCII string.
+**/
+CHAR8 *
+IScsiUnicodeStrToAsciiStr (
+ IN CHAR16 *Source,
+ OUT CHAR8 *Destination
+ )
+{
+ ASSERT (Destination != NULL);
+ ASSERT (Source != NULL);
+
+ while (*Source != '\0') {
+ //
+ // If any Unicode characters in Source contain
+ // non-zero value in the upper 8 bits, then ASSERT().
+ //
+ ASSERT (*Source < 0x100);
+ *(Destination++) = (CHAR8) *(Source++);
+ }
+
+ *Destination = '\0';
+
+ return Destination;
+}
+
+/**
+ Convert the decimal dotted IPv4 address into the binary IPv4 address.
+
+ @param[in] Str The UNICODE string.
+ @param[out] Ip The storage to return the ASCII string.
+
+ @retval EFI_SUCCESS The binary IP address is returned in Ip.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+**/
+EFI_STATUS
+IScsiAsciiStrToIp (
+ IN CHAR8 *Str,
+ OUT EFI_IPv4_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+ UINTN Number;
+
+ Index = 0;
+
+ while (*Str != 0) {
+
+ if (Index > 3) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Number = 0;
+ while (NET_IS_DIGIT (*Str)) {
+ Number = Number * 10 + (*Str - '0');
+ Str++;
+ }
+
+ if (Number > 0xFF) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip->Addr[Index] = (UINT8) Number;
+
+ if ((*Str != '\0') && (*Str != '.')) {
+ //
+ // The current character should be either the NULL terminator or
+ // the dot delimiter.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*Str == '.') {
+ //
+ // Skip the delimiter.
+ //
+ Str++;
+ }
+
+ Index++;
+ }
+
+ if (Index != 4) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the mac address into a hexadecimal encoded "-" seperated string.
+
+ @param[in] Mac The mac address.
+ @param[in] Len Length in bytes of the mac address.
+ @param[in] VlanId VLAN ID of the network device.
+ @param[out] Str The storage to return the mac string.
+**/
+VOID
+IScsiMacAddrToStr (
+ IN EFI_MAC_ADDRESS *Mac,
+ IN UINT32 Len,
+ IN UINT16 VlanId,
+ OUT CHAR16 *Str
+ )
+{
+ UINT32 Index;
+ CHAR16 *String;
+
+ for (Index = 0; Index < Len; Index++) {
+ Str[3 * Index] = (CHAR16) IScsiHexString[(Mac->Addr[Index] >> 4) & 0x0F];
+ Str[3 * Index + 1] = (CHAR16) IScsiHexString[Mac->Addr[Index] & 0x0F];
+ Str[3 * Index + 2] = L'-';
+ }
+
+ String = &Str[3 * Index - 1] ;
+ if (VlanId != 0) {
+ String += UnicodeSPrint (String, 6 * sizeof (CHAR16), L"\\%04x", (UINTN) VlanId);
+ }
+
+ *String = L'\0';
+}
+
+/**
+ Convert the binary encoded buffer into a hexadecimal encoded string.
+
+ @param[in] BinBuffer The buffer containing the binary data.
+ @param[in] BinLength Length of the binary buffer.
+ @param[in, out] HexStr Pointer to the string.
+ @param[in, out] HexLength The length of the string.
+
+ @retval EFI_SUCCESS The binary data is converted to the hexadecimal string
+ and the length of the string is updated.
+ @retval EFI_BUFFER_TOO_SMALL The string is too small.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+**/
+EFI_STATUS
+IScsiBinToHex (
+ IN UINT8 *BinBuffer,
+ IN UINT32 BinLength,
+ IN OUT CHAR8 *HexStr,
+ IN OUT UINT32 *HexLength
+ )
+{
+ UINTN Index;
+
+ if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((*HexLength) - 3) < BinLength * 2) {
+ *HexLength = BinLength * 2 + 3;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *HexLength = BinLength * 2 + 3;
+ //
+ // Prefix for Hex String
+ //
+ HexStr[0] = '0';
+ HexStr[1] = 'x';
+
+ for (Index = 0; Index < BinLength; Index++) {
+ HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4];
+ HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0x0F];
+ }
+
+ HexStr[Index * 2 + 2] = '\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the hexadecimal string into a binary encoded buffer.
+
+ @param[in, out] BinBuffer The binary buffer.
+ @param[in, out] BinLength Length of the binary buffer.
+ @param[in] HexStr The hexadecimal string.
+
+ @retval EFI_SUCCESS The hexadecimal string is converted into a binary
+ encoded buffer.
+ @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.
+**/
+EFI_STATUS
+IScsiHexToBin (
+ IN OUT UINT8 *BinBuffer,
+ IN OUT UINT32 *BinLength,
+ IN CHAR8 *HexStr
+ )
+{
+ UINTN Index;
+ UINTN Length;
+ UINT8 Digit;
+ CHAR8 TemStr[2];
+
+ ZeroMem (TemStr, sizeof (TemStr));
+
+ //
+ // Find out how many hex characters the string has.
+ //
+ if ((HexStr[0] == '0') && ((HexStr[1] == 'x') || (HexStr[1] == 'X'))) {
+ HexStr += 2;
+ }
+
+ Length = AsciiStrLen (HexStr);
+
+ for (Index = 0; Index < Length; Index ++) {
+ TemStr[0] = HexStr[Index];
+ Digit = (UINT8) AsciiStrHexToUint64 (TemStr);
+ if (Digit == 0 && TemStr[0] != '0') {
+ //
+ // Invalid Lun Char
+ //
+ break;
+ }
+ if ((Index & 1) == 0) {
+ BinBuffer [Index/2] = Digit;
+ } else {
+ BinBuffer [Index/2] = (UINT8) ((BinBuffer [Index/2] << 4) + Digit);
+ }
+ }
+
+ *BinLength = (UINT32) ((Index + 1)/2);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Generate random numbers.
+
+ @param[in, out] Rand The buffer to contain random numbers.
+ @param[in] RandLength The length of the Rand buffer.
+**/
+VOID
+IScsiGenRandom (
+ IN OUT UINT8 *Rand,
+ IN UINTN RandLength
+ )
+{
+ UINT32 Random;
+
+ while (RandLength > 0) {
+ Random = NET_RANDOM (NetRandomInitSeed ());
+ *Rand++ = (UINT8) (Random);
+ RandLength--;
+ }
+}
+
+/**
+ Create the iSCSI driver data..
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+
+ @return The iSCSI driver data created.
+ @retval NULL Other errors as indicated.
+**/
+ISCSI_DRIVER_DATA *
+IScsiCreateDriverData (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ EFI_STATUS Status;
+
+ Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));
+ if (Private == NULL) {
+ return NULL;
+ }
+
+ Private->Signature = ISCSI_DRIVER_DATA_SIGNATURE;
+ Private->Image = Image;
+ Private->Controller = Controller;
+
+ //
+ // Create an event to be signal when the BS to RT transition is triggerd so
+ // as to abort the iSCSI session.
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ IScsiOnExitBootService,
+ Private,
+ &gEfiEventExitBootServicesGuid,
+ &Private->ExitBootServiceEvent
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Private);
+ return NULL;
+ }
+
+ CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));
+
+ //
+ // 0 is designated to the TargetId, so use another value for the AdapterId.
+ //
+ Private->ExtScsiPassThruMode.AdapterId = 2;
+ Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
+ Private->ExtScsiPassThruMode.IoAlign = 4;
+ Private->IScsiExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;
+
+ //
+ // Install the Ext SCSI PASS THRU protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+ FreePool (Private);
+
+ return NULL;
+ }
+
+ IScsiSessionInit (&Private->Session, FALSE);
+
+ return Private;
+}
+
+/**
+ Clean the iSCSI driver data.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS The clean operation is successful.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiCleanDriverData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (Private->DevicePath != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ Private->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ FreePool (Private->DevicePath);
+ }
+
+ if (Private->ExtScsiPassThruHandle != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ }
+
+EXIT:
+
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+
+ FreePool (Private);
+ return Status;
+}
+
+/**
+ Check wheather the Controller is configured to use DHCP protocol.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval TRUE The handle of the controller need the Dhcp protocol.
+ @retval FALSE The handle of the controller does not need the Dhcp protocol.
+
+**/
+BOOLEAN
+IScsiDhcpIsConfigured (
+ IN EFI_HANDLE Controller
+ )
+{
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS MacAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 MacString[70];
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigDataTmp;
+
+ //
+ // Get the mac string, it's the name of various variable
+ //
+ Status = NetLibGetMacAddress (Controller, &MacAddress, &HwAddressSize);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ VlanId = NetLibGetVlanId (Controller);
+ IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, MacString);
+
+ //
+ // Get the normal configuration.
+ //
+ Status = GetVariable2 (
+ MacString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ (VOID**)&ConfigDataTmp,
+ NULL
+ );
+ if (ConfigDataTmp == NULL || EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ if (ConfigDataTmp->Enabled && ConfigDataTmp->InitiatorInfoFromDhcp) {
+ FreePool (ConfigDataTmp);
+ return TRUE;
+ }
+
+ FreePool (ConfigDataTmp);
+ return FALSE;
+}
+
+/**
+ Get the various configuration data of this iSCSI instance.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS The configuration of this instance is got.
+ @retval EFI_ABORTED The operation was aborted.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiGetConfigData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ UINTN BufferSize;
+ EFI_MAC_ADDRESS MacAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 MacString[70];
+
+ //
+ // get the iSCSI Initiator Name
+ //
+ Session = &Private->Session;
+ Session->InitiatorNameLength = ISCSI_NAME_MAX_SIZE;
+ Status = gIScsiInitiatorName.Get (
+ &gIScsiInitiatorName,
+ &Session->InitiatorNameLength,
+ Session->InitiatorName
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the mac string, it's the name of various variable
+ //
+ Status = NetLibGetMacAddress (Private->Controller, &MacAddress, &HwAddressSize);
+ ASSERT (Status == EFI_SUCCESS);
+ VlanId = NetLibGetVlanId (Private->Controller);
+ IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, MacString);
+
+ //
+ // Get the normal configuration.
+ //
+ BufferSize = sizeof (Session->ConfigData.NvData);
+ Status = gRT->GetVariable (
+ MacString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ &BufferSize,
+ &Session->ConfigData.NvData
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!Session->ConfigData.NvData.Enabled) {
+ return EFI_ABORTED;
+ }
+ //
+ // Get the CHAP Auth information.
+ //
+ BufferSize = sizeof (Session->AuthData.AuthConfig);
+ Status = gRT->GetVariable (
+ MacString,
+ &gIScsiCHAPAuthInfoGuid,
+ NULL,
+ &BufferSize,
+ &Session->AuthData.AuthConfig
+ );
+
+ if (!EFI_ERROR (Status) && Session->ConfigData.NvData.InitiatorInfoFromDhcp) {
+ //
+ // Start dhcp.
+ //
+ Status = IScsiDoDhcp (Private->Image, Private->Controller, &Session->ConfigData);
+ }
+
+ return Status;
+}
+
+/**
+ Get the device path of the iSCSI tcp connection and update it.
+
+ @param[in] Private The iSCSI driver data.
+
+ @return The updated device path.
+ @retval NULL Other errors as indicated.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+IScsiGetTcpConnDevicePath (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ ISCSI_SESSION *Session;
+ ISCSI_CONNECTION *Conn;
+ TCP4_IO *Tcp4Io;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+ EFI_DEV_PATH *DPathNode;
+
+ Session = &Private->Session;
+ if (Session->State != SESSION_STATE_LOGGED_IN) {
+ return NULL;
+ }
+
+ Conn = NET_LIST_USER_STRUCT_S (
+ Session->Conns.ForwardLink,
+ ISCSI_CONNECTION,
+ Link,
+ ISCSI_CONNECTION_SIGNATURE
+ );
+ Tcp4Io = &Conn->Tcp4Io;
+
+ Status = gBS->HandleProtocol (
+ Tcp4Io->Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ //
+ // Duplicate it.
+ //
+ DevicePath = DuplicateDevicePath (DevicePath);
+ if (DevicePath == NULL) {
+ return NULL;
+ }
+
+ DPathNode = (EFI_DEV_PATH *) DevicePath;
+
+ while (!IsDevicePathEnd (&DPathNode->DevPath)) {
+ if ((DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP)
+ ) {
+
+ DPathNode->Ipv4.LocalPort = 0;
+ DPathNode->Ipv4.StaticIpAddress =
+ (BOOLEAN) (!Session->ConfigData.NvData.InitiatorInfoFromDhcp);
+
+ //
+ // Add a judgement here to support previous versions of IPv4_DEVICE_PATH.
+ // In previous versions of IPv4_DEVICE_PATH, GatewayIpAddress and SubnetMask
+ // do not exist.
+ // In new version of IPv4_DEVICE_PATH, structcure length is 27.
+ //
+ if (DevicePathNodeLength (&DPathNode->Ipv4) == IP4_NODE_LEN_NEW_VERSIONS) {
+
+ IP4_COPY_ADDRESS (
+ &DPathNode->Ipv4.GatewayIpAddress,
+ &Session->ConfigData.NvData.Gateway
+ );
+
+ IP4_COPY_ADDRESS (
+ &DPathNode->Ipv4.SubnetMask,
+ &Session->ConfigData.NvData.SubnetMask
+ );
+ }
+
+ break;
+ }
+
+ DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);
+ }
+
+ return DevicePath;
+}
+
+/**
+ Abort the session when the transition from BS to RT is initiated.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The iSCSI driver data.
+**/
+VOID
+EFIAPI
+IScsiOnExitBootService (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+
+ Private = (ISCSI_DRIVER_DATA *) Context;
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+
+ IScsiSessionAbort (&Private->Session);
+}
+
+/**
+ Tests whether a controller handle is being managed by IScsi driver.
+
+ This function tests whether the driver specified by DriverBindingHandle is
+ currently managing the controller specified by ControllerHandle. This test
+ is performed by evaluating if the the protocol specified by ProtocolGuid is
+ present on ControllerHandle and is was opened by DriverBindingHandle and Nic
+ Device handle with an attribute of EFI_OPEN_PROTOCOL_BY_DRIVER.
+ If ProtocolGuid is NULL, then ASSERT().
+
+ @param ControllerHandle A handle for a controller to test.
+ @param DriverBindingHandle Specifies the driver binding handle for the
+ driver.
+ @param ProtocolGuid Specifies the protocol that the driver specified
+ by DriverBindingHandle opens in its Start()
+ function.
+
+ @retval EFI_SUCCESS ControllerHandle is managed by the driver
+ specified by DriverBindingHandle.
+ @retval EFI_UNSUPPORTED ControllerHandle is not managed by the driver
+ specified by DriverBindingHandle.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiTestManagedDevice (
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_GUID *ProtocolGuid
+ )
+{
+ EFI_STATUS Status;
+ VOID *ManagedInterface;
+ EFI_HANDLE NicControllerHandle;
+
+ ASSERT (ProtocolGuid != NULL);
+
+ NicControllerHandle = NetLibGetNicHandle (ControllerHandle, ProtocolGuid);
+ if (NicControllerHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ (EFI_GUID *) ProtocolGuid,
+ &ManagedInterface,
+ DriverBindingHandle,
+ NicControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ (EFI_GUID *) ProtocolGuid,
+ DriverBindingHandle,
+ NicControllerHandle
+ );
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Status != EFI_ALREADY_STARTED) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.h
new file mode 100644
index 0000000000..0ab44d06ae
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.h
@@ -0,0 +1,317 @@
+/** @file
+ Miscellaneous definitions for iSCSI driver.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_MISC_H_
+#define _ISCSI_MISC_H_
+
+#include <Library/BaseLib.h>
+
+typedef struct _ISCSI_SESSION_CONFIG_DATA ISCSI_SESSION_CONFIG_DATA;
+
+///
+/// IPv4 Device Path Node Length
+///
+#define IP4_NODE_LEN_NEW_VERSIONS 27
+
+#pragma pack(1)
+typedef struct {
+ BOOLEAN Enabled;
+
+ BOOLEAN InitiatorInfoFromDhcp;
+ EFI_IPv4_ADDRESS LocalIp;
+ EFI_IPv4_ADDRESS SubnetMask;
+ EFI_IPv4_ADDRESS Gateway;
+
+ BOOLEAN TargetInfoFromDhcp;
+ CHAR8 TargetName[ISCSI_NAME_MAX_SIZE];
+ EFI_IPv4_ADDRESS TargetIp;
+ UINT16 TargetPort;
+ UINT8 BootLun[8];
+
+ UINT8 IsId[6];
+} ISCSI_SESSION_CONFIG_NVDATA;
+#pragma pack()
+
+struct _ISCSI_SESSION_CONFIG_DATA {
+ ISCSI_SESSION_CONFIG_NVDATA NvData;
+
+ EFI_IPv4_ADDRESS PrimaryDns;
+ EFI_IPv4_ADDRESS SecondaryDns;
+ EFI_IPv4_ADDRESS DhcpServer;
+};
+
+/**
+ Calculate the prefix length of the IPv4 subnet mask.
+
+ @param[in] SubnetMask The IPv4 subnet mask.
+
+ @return The prefix length of the subnet mask.
+ @retval 0 Other errors as indicated.
+**/
+UINT8
+IScsiGetSubnetMaskPrefixLength (
+ IN EFI_IPv4_ADDRESS *SubnetMask
+ );
+
+/**
+ Convert the hexadecimal encoded LUN string into the 64-bit LUN.
+
+ @param[in] Str The hexadecimal encoded LUN string.
+ @param[out] Lun Storage to return the 64-bit LUN.
+
+ @retval EFI_SUCCESS The 64-bit LUN is stored in Lun.
+ @retval EFI_INVALID_PARAMETER The string is malformatted.
+**/
+EFI_STATUS
+IScsiAsciiStrToLun (
+ IN CHAR8 *Str,
+ OUT UINT8 *Lun
+ );
+
+/**
+ Convert the 64-bit LUN into the hexadecimal encoded LUN string.
+
+ @param[in] Lun The 64-bit LUN.
+ @param[out] Str The storage to return the hexadecimal encoded LUN string.
+**/
+VOID
+IScsiLunToUnicodeStr (
+ IN UINT8 *Lun,
+ OUT CHAR16 *Str
+ );
+
+/**
+ Convert the ASCII string into a UNICODE string.
+
+ @param[in] Source The ASCII string.
+ @param[out] Destination The storage to return the UNICODE string.
+
+ @return CHAR16 * Pointer to the UNICODE string.
+**/
+CHAR16 *
+IScsiAsciiStrToUnicodeStr (
+ IN CHAR8 *Source,
+ OUT CHAR16 *Destination
+ );
+
+/**
+ Convert the UNICODE string into an ASCII string.
+
+ @param[in] Source The UNICODE string.
+ @param[out] Destination The storage to return the ASCII string.
+
+ @return CHAR8 * Pointer to the ASCII string.
+**/
+CHAR8 *
+IScsiUnicodeStrToAsciiStr (
+ IN CHAR16 *Source,
+ OUT CHAR8 *Destination
+ );
+
+/**
+ Convert the mac address into a hexadecimal encoded "-" seperated string.
+
+ @param[in] Mac The mac address.
+ @param[in] Len Length in bytes of the mac address.
+ @param[in] VlanId VLAN ID of the network device.
+ @param[out] Str The storage to return the mac string.
+**/
+VOID
+IScsiMacAddrToStr (
+ IN EFI_MAC_ADDRESS *Mac,
+ IN UINT32 Len,
+ IN UINT16 VlanId,
+ OUT CHAR16 *Str
+ );
+
+/**
+ Convert the decimal dotted IPv4 address into the binary IPv4 address.
+
+ @param[in] Str The UNICODE string.
+ @param[out] Ip The storage to return the ASCII string.
+
+ @retval EFI_SUCCESS The binary IP address is returned in Ip.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+**/
+EFI_STATUS
+IScsiAsciiStrToIp (
+ IN CHAR8 *Str,
+ OUT EFI_IPv4_ADDRESS *Ip
+ );
+
+/**
+ Convert the binary encoded buffer into a hexadecimal encoded string.
+
+ @param[in] BinBuffer The buffer containing the binary data.
+ @param[in] BinLength Length of the binary buffer.
+ @param[in, out] HexStr Pointer to the string.
+ @param[in, out] HexLength The length of the string.
+
+ @retval EFI_SUCCESS The binary data is converted to the hexadecimal string
+ and the length of the string is updated.
+ @retval EFI_BUFFER_TOO_SMALL The string is too small.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+**/
+EFI_STATUS
+IScsiBinToHex (
+ IN UINT8 *BinBuffer,
+ IN UINT32 BinLength,
+ IN OUT CHAR8 *HexStr,
+ IN OUT UINT32 *HexLength
+ );
+
+/**
+ Convert the hexadecimal string into a binary encoded buffer.
+
+ @param[in, out] BinBuffer The binary buffer.
+ @param[in, out] BinLength Length of the binary buffer.
+ @param[in] HexStr The hexadecimal string.
+
+ @retval EFI_SUCCESS The hexadecimal string is converted into a binary
+ encoded buffer.
+ @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.
+**/
+EFI_STATUS
+IScsiHexToBin (
+ IN OUT UINT8 *BinBuffer,
+ IN OUT UINT32 *BinLength,
+ IN CHAR8 *HexStr
+ );
+
+/**
+ Generate random numbers.
+
+ @param[in, out] Rand The buffer to contain random numbers.
+ @param[in] RandLength The length of the Rand buffer.
+**/
+VOID
+IScsiGenRandom (
+ IN OUT UINT8 *Rand,
+ IN UINTN RandLength
+ );
+
+/**
+ Create the iSCSI driver data..
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+
+ @return The iSCSI driver data created.
+ @retval NULL Other errors as indicated.
+**/
+ISCSI_DRIVER_DATA *
+IScsiCreateDriverData (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller
+ );
+
+/**
+ Clean the iSCSI driver data.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCES The clean operation is successful.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiCleanDriverData (
+ IN ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Check wheather the Controller is configured to use DHCP protocol.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval TRUE The handle of the controller need the Dhcp protocol.
+ @retval FALSE The handle of the controller does not need the Dhcp protocol.
+
+**/
+BOOLEAN
+IScsiDhcpIsConfigured (
+ IN EFI_HANDLE Controller
+ );
+
+/**
+ Get the various configuration data of this iSCSI instance.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS The configuration of this instance is got.
+ @retval EFI_ABORTED The operation was aborted.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiGetConfigData (
+ IN ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Get the device path of the iSCSI tcp connection and update it.
+
+ @param[in] Private The iSCSI driver data.
+
+ @return The updated device path.
+ @retval NULL Other errors as indicated.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+IScsiGetTcpConnDevicePath (
+ IN ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Abort the session when the transition from BS to RT is initiated.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The iSCSI driver data.
+**/
+VOID
+EFIAPI
+IScsiOnExitBootService (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Tests whether a controller handle is being managed by IScsi driver.
+
+ This function tests whether the driver specified by DriverBindingHandle is
+ currently managing the controller specified by ControllerHandle. This test
+ is performed by evaluating if the the protocol specified by ProtocolGuid is
+ present on ControllerHandle and is was opened by DriverBindingHandle and Nic
+ Device handle with an attribute of EFI_OPEN_PROTOCOL_BY_DRIVER.
+ If ProtocolGuid is NULL, then ASSERT().
+
+ @param ControllerHandle A handle for a controller to test.
+ @param DriverBindingHandle Specifies the driver binding handle for the
+ driver.
+ @param ProtocolGuid Specifies the protocol that the driver specified
+ by DriverBindingHandle opens in its Start()
+ function.
+
+ @retval EFI_SUCCESS ControllerHandle is managed by the driver
+ specified by DriverBindingHandle.
+ @retval EFI_UNSUPPORTED ControllerHandle is not managed by the driver
+ specified by DriverBindingHandle.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiTestManagedDevice (
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_GUID *ProtocolGuid
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.c
new file mode 100644
index 0000000000..14f13f8fce
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.c
@@ -0,0 +1,2830 @@
+/** @file
+ The implementation of iSCSI protocol based on RFC3720.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+UINT32 mDataSegPad = 0;
+
+/**
+ Attach the iSCSI connection to the iSCSI session.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in, out] Conn The iSCSI connection.
+**/
+VOID
+IScsiAttatchConnection (
+ IN OUT ISCSI_SESSION *Session,
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ InsertTailList (&Session->Conns, &Conn->Link);
+ Conn->Session = Session;
+ Session->NumConns++;
+}
+
+/**
+ Detach the iSCSI connection from the session it belongs to.
+
+ @param[in, out] Conn The iSCSI connection.
+**/
+VOID
+IScsiDetatchConnection (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ RemoveEntryList (&Conn->Link);
+ Conn->Session->NumConns--;
+ Conn->Session = NULL;
+}
+
+/**
+ Check the sequence number according to RFC3720.
+
+ @param[in, out] ExpSN The currently expected sequence number.
+ @param[in] NewSN The sequence number to check.
+
+ @retval EFI_SUCCESS The check passed and the ExpSN is increased.
+ @retval EFI_NOT_READY Response was sent due to a retransmission request.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+**/
+EFI_STATUS
+IScsiCheckSN (
+ IN OUT UINT32 *ExpSN,
+ IN UINT32 NewSN
+ )
+{
+ if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) {
+ if (ISCSI_SEQ_LT (NewSN, *ExpSN)) {
+ //
+ // Duplicate
+ //
+ return EFI_NOT_READY;
+ } else {
+ return EFI_PROTOCOL_ERROR;
+ }
+ } else {
+ //
+ // Advance the ExpSN
+ //
+ (*ExpSN)++;
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Update the sequence numbers for the iSCSI command.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] MaxCmdSN Maximum CmdSN from the target.
+ @param[in] ExpCmdSN Next expected CmdSN from the target.
+**/
+VOID
+IScsiUpdateCmdSN (
+ IN OUT ISCSI_SESSION *Session,
+ IN UINT32 MaxCmdSN,
+ IN UINT32 ExpCmdSN
+ )
+{
+ if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) {
+ return ;
+ }
+
+ if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) {
+ Session->MaxCmdSN = MaxCmdSN;
+ }
+
+ if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) {
+ Session->ExpCmdSN = ExpCmdSN;
+ }
+}
+
+/**
+ This function does the iSCSI connection login.
+
+ @param[in, out] Conn The iSCSI connection to login.
+
+ @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target.
+ @retval EFI_TIMEOUT Timeout happened during the login procedure.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiConnLogin (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Start the timer, wait 16 seconds to establish the TCP connection.
+ //
+ Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, 16 * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // try to establish the tcp connection
+ //
+ Status = Tcp4IoConnect (&Conn->Tcp4Io, Conn->TimeoutEvent);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0);
+ Conn->State = CONN_STATE_IN_LOGIN;
+
+ //
+ // connection is established, start the iSCSI Login
+ //
+ do {
+ Status = IScsiSendLoginReq (Conn);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Status = IScsiReceiveLoginRsp (Conn);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE);
+
+ return Status;
+}
+
+/**
+ Reset the iSCSI connection.
+
+ @param[in, out] Conn The iSCSI connection to reset.
+**/
+VOID
+IScsiConnReset (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ Tcp4IoReset (&Conn->Tcp4Io);
+}
+
+/**
+ Create a TCP connection for the iSCSI session.
+
+ @param[in] Private The iSCSI driver data.
+ @param[in] Session Maximum CmdSN from the target.
+
+ @return The newly created iSCSI connection.
+**/
+ISCSI_CONNECTION *
+IScsiCreateConnection (
+ IN ISCSI_DRIVER_DATA *Private,
+ IN ISCSI_SESSION *Session
+ )
+{
+ ISCSI_CONNECTION *Conn;
+ TCP4_IO_CONFIG_DATA Tcp4IoConfig;
+ EFI_STATUS Status;
+
+ Conn = AllocatePool (sizeof (ISCSI_CONNECTION));
+ if (Conn == NULL) {
+ return NULL;
+ }
+
+ Conn->Signature = ISCSI_CONNECTION_SIGNATURE;
+ Conn->State = CONN_STATE_FREE;
+ Conn->CurrentStage = ISCSI_SECURITY_NEGOTIATION;
+ Conn->NextStage = ISCSI_LOGIN_OPERATIONAL_NEGOTIATION;
+ Conn->CHAPStep = ISCSI_CHAP_INITIAL;
+ Conn->ExpStatSN = 0;
+ Conn->PartialReqSent = FALSE;
+ Conn->PartialRspRcvd = FALSE;
+ Conn->Cid = Session->NextCid++;
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Conn->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Conn);
+ return NULL;
+ }
+
+ NetbufQueInit (&Conn->RspQue);
+
+ //
+ // set the default connection-only parameters
+ //
+ Conn->MaxRecvDataSegmentLength = DEFAULT_MAX_RECV_DATA_SEG_LEN;
+ Conn->HeaderDigest = IScsiDigestNone;
+ Conn->DataDigest = IScsiDigestNone;
+
+ CopyMem (&Tcp4IoConfig.LocalIp, &Session->ConfigData.NvData.LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Tcp4IoConfig.SubnetMask, &Session->ConfigData.NvData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Tcp4IoConfig.Gateway, &Session->ConfigData.NvData.Gateway, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Tcp4IoConfig.RemoteIp, &Session->ConfigData.NvData.TargetIp, sizeof (EFI_IPv4_ADDRESS));
+
+ Tcp4IoConfig.RemotePort = Session->ConfigData.NvData.TargetPort;
+
+ //
+ // Create the tcp4 IO for this connection
+ //
+ Status = Tcp4IoCreateSocket (
+ Private->Image,
+ Private->Controller,
+ &Tcp4IoConfig,
+ &Conn->Tcp4Io
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (Conn->TimeoutEvent);
+ FreePool (Conn);
+ Conn = NULL;
+ }
+
+ return Conn;
+}
+
+/**
+ Destroy an iSCSI connection.
+
+ @param[in] Conn The connection to destroy.
+**/
+VOID
+IScsiDestroyConnection (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ Tcp4IoDestroySocket (&Conn->Tcp4Io);
+ NetbufQueFlush (&Conn->RspQue);
+ gBS->CloseEvent (Conn->TimeoutEvent);
+ FreePool (Conn);
+}
+
+/**
+ Login the iSCSI session.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS The iSCSI session login procedure finished.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiSessionLogin (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_CONNECTION *Conn;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ BOOLEAN MediaPresent;
+
+ Session = &Private->Session;
+
+ //
+ // Check media status before session login
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Private->Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Set session identifier
+ //
+ CopyMem (Session->Isid, Session->ConfigData.NvData.IsId, 6);
+
+ //
+ // Create a connection for the session.
+ //
+ Conn = IScsiCreateConnection (Private, Session);
+ if (Conn == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IScsiAttatchConnection (Session, Conn);
+
+ //
+ // Login througth the newly created connection.
+ //
+ Status = IScsiConnLogin (Conn);
+ if (EFI_ERROR (Status)) {
+ IScsiConnReset (Conn);
+ IScsiDetatchConnection (Conn);
+ IScsiDestroyConnection (Conn);
+ } else {
+ Session->State = SESSION_STATE_LOGGED_IN;
+
+ gBS->OpenProtocol (
+ Conn->Tcp4Io.Handle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **)&Tcp4,
+ Private->Image,
+ Private->ExtScsiPassThruHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ }
+
+ return Status;
+}
+
+/**
+ Build and send the iSCSI login request to the iSCSI target according to
+ the current login stage.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
+ connection.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR Some kind of device error happened.
+**/
+EFI_STATUS
+IScsiSendLoginReq (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ NET_BUF *Pdu;
+ EFI_STATUS Status;
+
+ //
+ // build the Login Request PDU
+ //
+ Pdu = IScsiPrepareLoginReq (Conn);
+ if (Pdu == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Send it to the iSCSI target.
+ //
+ Status = Tcp4IoTransmit (&Conn->Tcp4Io, Pdu);
+
+ NetbufFree (Pdu);
+
+ return Status;
+}
+
+/**
+ Receive and process the iSCSI login response.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiReceiveLoginRsp (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF *Pdu;
+
+ Pdu = NULL;
+
+ //
+ // Receive the iSCSI login response.
+ //
+ Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // A Login Response is received, process it.
+ //
+ ASSERT (Pdu != NULL);
+ Status = IScsiProcessLoginRsp (Conn, Pdu);
+
+ NetbufFree (Pdu);
+
+ return Status;
+}
+
+/**
+ Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
+ The DataSegmentLength and the actual size of the net buffer containing this PDU will be
+ updated.
+
+ @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
+ be added to.
+ @param[in] Key The key name string.
+ @param[in] Value The value string.
+
+ @retval EFI_SUCCESS The key-valu pair is added to the PDU's datasegment and
+ the correspondence length fields are updated.
+ @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
+ pair.
+**/
+EFI_STATUS
+IScsiAddKeyValuePair (
+ IN OUT NET_BUF *Pdu,
+ IN CHAR8 *Key,
+ IN CHAR8 *Value
+ )
+{
+ UINT32 DataSegLen;
+ UINT32 KeyLen;
+ UINT32 ValueLen;
+ UINT32 TotalLen;
+ ISCSI_LOGIN_REQUEST *LoginReq;
+ CHAR8 *Data;
+
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);
+ if (LoginReq == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ DataSegLen = NTOH24 (LoginReq->DataSegmentLength);
+
+ KeyLen = (UINT32) AsciiStrLen (Key);
+ ValueLen = (UINT32) AsciiStrLen (Value);
+
+ //
+ // 1 byte for the key value separator '=' and 1 byte for the null
+ // delimiter after the value.
+ //
+ TotalLen = KeyLen + 1 + ValueLen + 1;
+
+ //
+ // Allocate the space for the key-value pair.
+ //
+ Data = (CHAR8 *)NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Add the key.
+ //
+ CopyMem (Data, Key, KeyLen);
+ Data += KeyLen;
+
+ *Data = '=';
+ Data++;
+
+ //
+ // Add the value.
+ //
+ CopyMem (Data, Value, ValueLen);
+ Data += ValueLen;
+
+ *Data = '\0';
+
+ //
+ // update the DataSegmentLength
+ //
+ ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Prepare the iSCSI login request to be sent according to the current login status.
+
+ @param[in, out] Conn The connection in the iSCSI login phase.
+
+ @return The pointer to the net buffer containing the iSCSI login request built.
+ @retval Others Other errors as indicated.
+**/
+NET_BUF *
+IScsiPrepareLoginReq (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ ISCSI_SESSION *Session;
+ NET_BUF *Nbuf;
+ ISCSI_LOGIN_REQUEST *LoginReq;
+ EFI_STATUS Status;
+
+ Session = Conn->Session;
+
+ Nbuf = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL);
+ ASSERT (LoginReq != NULL);
+ ZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST));
+
+ //
+ // Init the login request pdu
+ //
+ ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE);
+ ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage);
+ LoginReq->VersionMax = ISCSI_VERSION_MAX;
+ LoginReq->VersionMin = ISCSI_VERSION_MIN;
+ LoginReq->Tsih = HTONS (Session->Tsih);
+ LoginReq->InitiatorTaskTag = HTONL (Session->InitiatorTaskTag);
+ LoginReq->Cid = HTONS (Conn->Cid);
+ LoginReq->CmdSN = HTONL (Session->CmdSN);
+
+ //
+ // For the first Login Request on a coonection this is ExpStatSN for the
+ // old connection and this field is only valid if the Login Request restarts
+ // a connection.
+ // For subsequent Login Requests it is used to acknowledge the Login Responses
+ // with their increasing StatSN values.
+ //
+ LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);
+ CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid));
+
+ if (Conn->PartialRspRcvd) {
+ //
+ // A partial response, initiator must send an empty Login Request.
+ //
+ return Nbuf;
+ }
+
+ switch (Conn->CurrentStage) {
+ case ISCSI_SECURITY_NEGOTIATION:
+ Status = IScsiCHAPToSendReq (Conn, Nbuf);
+ break;
+
+ case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
+ Status = IScsiFillOpParams (Conn, Nbuf);
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ break;
+
+ default:
+ //
+ // something error happens...
+ //
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Nbuf);
+ Nbuf = NULL;
+ } else {
+ //
+ // Pad the data segment if needed.
+ //
+ IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));
+ //
+ // Check whether we will issue the stage transition signal?
+ //
+ Conn->TransitInitiated = (BOOLEAN) ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ }
+
+ return Nbuf;
+}
+
+/**
+ Process the iSCSI Login Response.
+
+ @param[in, out] Conn The connection on which the iSCSI login response is received.
+ @param[in, out] Pdu The iSCSI login response PDU.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is processed and all check are passed.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval EFI_MEDIA_CHANGED Target is redirected.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiProcessLoginRsp (
+ IN OUT ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_LOGIN_RESPONSE *LoginRsp;
+ BOOLEAN Transit;
+ BOOLEAN Continue;
+ UINT8 CurrentStage;
+ UINT8 NextStage;
+ UINT8 *DataSeg;
+ UINT32 DataSegLen;
+
+ Session = Conn->Session;
+
+ LoginRsp = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
+ if (LoginRsp == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {
+ //
+ // It's not a Login Response
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Get the data segment if any.
+ //
+ DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);
+ if (DataSegLen != 0) {
+ DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);
+ } else {
+ DataSeg = NULL;
+ }
+ //
+ // Check the status class in the login response PDU.
+ //
+ switch (LoginRsp->StatusClass) {
+ case ISCSI_LOGIN_STATUS_SUCCESS:
+ //
+ // Just break here, the response and the data segment will be processed later.
+ //
+ break;
+
+ case ISCSI_LOGIN_STATUS_REDIRECTION:
+ //
+ // The target may be moved to a different address
+ //
+ if (DataSeg == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Process the TargetAddress key-value strings in the data segment to update the
+ // target address info.
+ //
+ Status = IScsiUpdateTargetAddress (Session, (CHAR8 *)DataSeg, DataSegLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Session will be restarted on this error status because the Target is
+ // redirected by this Login Response.
+ //
+ return EFI_MEDIA_CHANGED;
+
+ default:
+ //
+ // Initiator Error, Target Error, or any other undefined error code.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // The status is sucess, extract the wanted fields from the header segment.
+ //
+ Transit = (BOOLEAN) ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);
+ Continue = (BOOLEAN) ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);
+
+ CurrentStage = (UINT8) ISCSI_GET_CURRENT_STAGE (LoginRsp);
+ NextStage = (UINT8) ISCSI_GET_NEXT_STAGE (LoginRsp);
+
+ LoginRsp->InitiatorTaskTag = NTOHL (LoginRsp->InitiatorTaskTag);
+
+ if ((Transit && Continue) ||
+ (CurrentStage != Conn->CurrentStage) ||
+ (!Conn->TransitInitiated && Transit) ||
+ (Transit && (NextStage != Conn->NextStage)) ||
+ (CompareMem (Session->Isid, LoginRsp->Isid, sizeof (LoginRsp->Isid)) != 0) ||
+ (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag)
+ ) {
+ //
+ // A Login Response with the C bit set to 1 MUST have the T bit set to 0;
+ // The CSG in the Login Response MUST be the same with the I-end of this connection;
+ // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
+ // initiate the transistion;
+ // The NSG MUST be the same with the I-end of this connection if Transit is required.
+ // The ISID in the Login Response MUST be the same with this session.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ LoginRsp->StatSN = NTOHL (LoginRsp->StatSN);
+ LoginRsp->ExpCmdSN = NTOHL (LoginRsp->ExpCmdSN);
+ LoginRsp->MaxCmdSN = NTOHL (LoginRsp->MaxCmdSN);
+
+ if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->CHAPStep == ISCSI_CHAP_INITIAL)) {
+ //
+ // If the Login Request is a leading Login Request, the target MUST use
+ // the value presented in CmdSN as the target value for ExpCmdSN.
+ //
+ if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
+ // and ExpCmdSN.
+ //
+ Conn->ExpStatSN = LoginRsp->StatSN + 1;
+ Session->MaxCmdSN = LoginRsp->MaxCmdSN;
+ Session->ExpCmdSN = LoginRsp->ExpCmdSN;
+ } else {
+ //
+ // Check the StatSN of this PDU
+ //
+ Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Update the MaxCmdSN and ExpCmdSN
+ //
+ IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);
+ } else {
+ return Status;
+ }
+ }
+ //
+ // Trim off the header segment.
+ //
+ NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);
+
+ //
+ // Queue this login response first in case it's a partial response so that
+ // later when the full response list is received we can combine these scattered
+ // responses' data segment and then process it.
+ //
+ NET_GET_REF (Pdu);
+ NetbufQueAppend (&Conn->RspQue, Pdu);
+
+ Conn->PartialRspRcvd = Continue;
+ if (Continue) {
+ //
+ // It's a partial response, have to wait for another or more Request/Response
+ // conversations to get the full response.
+ //
+ return EFI_SUCCESS;
+ }
+
+ switch (CurrentStage) {
+ case ISCSI_SECURITY_NEGOTIATION:
+ //
+ // In security negotiation stage, let CHAP module handle it.
+ //
+ Status = IScsiCHAPOnRspReceived (Conn);
+ break;
+
+ case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
+ //
+ // Response received with negotiation resonse on iSCSI parameters, check them.
+ //
+ Status = IScsiCheckOpParams (Conn);
+ break;
+
+ default:
+ //
+ // Should never get here.
+ //
+ Status = EFI_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (Transit && (Status == EFI_SUCCESS)) {
+ //
+ // Do the state transition.
+ //
+ Conn->CurrentStage = Conn->NextStage;
+
+ if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {
+ Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;
+ } else {
+ //
+ // CurrentStage is iSCSI Full Feature, it's the Login-Final Response,
+ // get the TSIH from the Login Response.
+ //
+ Session->Tsih = NTOHS (LoginRsp->Tsih);
+ }
+ }
+ //
+ // Flush the response(s) received.
+ //
+ NetbufQueFlush (&Conn->RspQue);
+
+ return Status;
+}
+
+/**
+ Updated the target information according the data received in the iSCSI
+ login response with an target redirection status.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Data The data segment which should contain the
+ TargetAddress key-value list.
+ @param[in] Len Length of the data.
+
+ @retval EFI_SUCCESS The target address is updated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_FOUND The TargetAddress key is not found.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiUpdateTargetAddress (
+ IN OUT ISCSI_SESSION *Session,
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *KeyValueList;
+ CHAR8 *TargetAddress;
+ CHAR8 *IpStr;
+ EFI_STATUS Status;
+ UINTN Number;
+
+ KeyValueList = IScsiBuildKeyValueList (Data, Len);
+ if (KeyValueList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ while (TRUE) {
+ TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);
+ if (TargetAddress == NULL) {
+ break;
+ }
+
+ if (!NET_IS_DIGIT (TargetAddress[0])) {
+ //
+ // The domainname of the target may be presented in three formats: a DNS host name,
+ // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
+ // IPv4 address.
+ //
+ continue;
+ }
+
+ IpStr = TargetAddress;
+
+ while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) {
+ //
+ // NULL, ':' or ',' ends the IPv4 string.
+ //
+ TargetAddress++;
+ }
+
+ if (*TargetAddress == ',') {
+ //
+ // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
+ // as the result of a redirection.
+ //
+ continue;
+ } else if (*TargetAddress == ':') {
+ *TargetAddress = '\0';
+
+ TargetAddress++;
+
+ Number = AsciiStrDecimalToUintn (TargetAddress);
+ if (Number > 0xFFFF) {
+ continue;
+ } else {
+ Session->ConfigData.NvData.TargetPort = (UINT16) Number;
+ }
+ } else {
+ //
+ // The string only contains the IPv4 address. Use the well known port.
+ //
+ Session->ConfigData.NvData.TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+ //
+ // Update the target IP address.
+ //
+ Status = IScsiAsciiStrToIp (IpStr, &Session->ConfigData.NvData.TargetIp);
+ if (EFI_ERROR (Status)) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ IScsiFreeKeyValueList (KeyValueList);
+
+ return Status;
+}
+
+/**
+ The callback function to free the net buffer list.
+
+ @param[in] Arg The opaque parameter.
+**/
+VOID
+EFIAPI
+IScsiFreeNbufList (
+ VOID *Arg
+ )
+{
+ ASSERT (Arg != NULL);
+
+ NetbufFreeList ((LIST_ENTRY *) Arg);
+ FreePool (Arg);
+}
+
+/**
+ The callback function called in NetBufFree, it does nothing.
+
+ @param[in] Arg The opaque parameter.
+**/
+VOID
+EFIAPI
+IScsiNbufExtFree (
+ VOID *Arg
+ )
+{
+}
+
+/**
+ Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
+ an optional data segment. The two parts will be put into two blocks of buffers in the
+ net buffer. The digest check will be conducted in this function if needed and the digests
+ will be trimmed from the PDU buffer.
+
+ @param[in] Conn The iSCSI connection to receive data from.
+ @param[out] Pdu The received iSCSI pdu.
+ @param[in] Context The context used to describe information on the caller provided
+ buffer to receive data segment of the iSCSI pdu, it's optional.
+ @param[in] HeaderDigest Whether there will be header digest received.
+ @param[in] DataDigest Whether there will be data digest.
+ @param[in] TimeoutEvent The timeout event, it's optional.
+
+ @retval EFI_SUCCESS An iSCSI pdu is received.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiReceivePdu (
+ IN ISCSI_CONNECTION *Conn,
+ OUT NET_BUF **Pdu,
+ IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL
+ IN BOOLEAN HeaderDigest,
+ IN BOOLEAN DataDigest,
+ IN EFI_EVENT TimeoutEvent OPTIONAL
+ )
+{
+ LIST_ENTRY *NbufList;
+ UINT32 Len;
+ NET_BUF *PduHdr;
+ UINT8 *Header;
+ EFI_STATUS Status;
+ UINT32 PadLen;
+ UINT32 InDataOffset;
+ NET_FRAGMENT Fragment[2];
+ UINT32 FragmentCount;
+ NET_BUF *DataSeg;
+ UINT32 PadAndCRC32[2];
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (NbufList);
+
+ //
+ // The header digest will be received together with the PDU header if exists.
+ //
+ Len = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);
+ PduHdr = NetbufAlloc (Len);
+ if (PduHdr == NULL) {
+ FreePool (NbufList);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
+ ASSERT (Header != NULL);
+ InsertTailList (NbufList, &PduHdr->List);
+
+ //
+ // First step, receive the BHS of the PDU.
+ //
+ Status = Tcp4IoReceive (&Conn->Tcp4Io, PduHdr, FALSE, TimeoutEvent);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (HeaderDigest) {
+ //
+ // TODO: check the header-digest.
+ //
+ //
+ // Trim off the digest.
+ //
+ NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);
+ }
+
+ Len = ISCSI_GET_DATASEG_LEN (Header);
+ if (Len == 0) {
+ //
+ // No data segment.Form the pdu from a list of pdu segments.
+ //
+ *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (*Pdu == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ return Status;
+ }
+ //
+ // Get the length of the padding bytes of the data segment.
+ //
+ PadLen = ISCSI_GET_PAD_LEN (Len);
+
+ switch (ISCSI_GET_OPCODE (Header)) {
+ case ISCSI_OPCODE_SCSI_DATA_IN:
+ //
+ // Try to use the buffer described by Context if the PDU is an
+ // iSCSI SCSI data in pdu so as to reduce memory copy overhead.
+ //
+ InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);
+ if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {
+ Status = EFI_PROTOCOL_ERROR;
+ goto ON_EXIT;
+ }
+
+ Fragment[0].Len = Len;
+ Fragment[0].Bulk = Context->InData + InDataOffset;
+
+ if (DataDigest || (PadLen != 0)) {
+ //
+ // The data segment is padded, use two fragments to receive it.
+ // The first to receive the useful data. The second to receive the padding.
+ //
+ Fragment[1].Len = PadLen + (DataDigest ? sizeof (UINT32) : 0);
+ Fragment[1].Bulk = (UINT8 *)PadAndCRC32 + (4 - PadLen);
+ FragmentCount = 2;
+ } else {
+ FragmentCount = 1;
+ }
+
+ DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
+ if (DataSeg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ break;
+
+ case ISCSI_OPCODE_SCSI_RSP:
+ case ISCSI_OPCODE_NOP_IN:
+ case ISCSI_OPCODE_LOGIN_RSP:
+ case ISCSI_OPCODE_TEXT_RSP:
+ case ISCSI_OPCODE_ASYNC_MSG:
+ case ISCSI_OPCODE_REJECT:
+ case ISCSI_OPCODE_VENDOR_T0:
+ case ISCSI_OPCODE_VENDOR_T1:
+ case ISCSI_OPCODE_VENDOR_T2:
+ //
+ // Allocate buffer to receive the data segment.
+ //
+ Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);
+ DataSeg = NetbufAlloc (Len);
+ if (DataSeg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
+ break;
+
+ default:
+ Status = EFI_PROTOCOL_ERROR;
+ goto ON_EXIT;
+ }
+
+ InsertTailList (NbufList, &DataSeg->List);
+
+ //
+ // Receive the data segment with the data digest if any.
+ //
+ Status = Tcp4IoReceive (&Conn->Tcp4Io, DataSeg, FALSE, TimeoutEvent);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (DataDigest) {
+ //
+ // TODO: Check the data digest.
+ //
+ NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);
+ }
+
+ if (PadLen != 0) {
+ //
+ // Trim off the padding bytes in the data segment.
+ //
+ NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);
+ }
+
+ //
+ // Form the pdu from a list of pdu segments.
+ //
+ *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (*Pdu == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ON_EXIT:
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Free the Nbufs in this NbufList and the NbufList itself.
+ //
+ IScsiFreeNbufList (NbufList);
+ }
+
+ return Status;
+}
+
+/**
+ Check and get the result of the prameter negotiation.
+
+ @param[in, out] Conn The connection in iSCSI login.
+
+ @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+IScsiCheckOpParams (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *KeyValueList;
+ CHAR8 *Data;
+ UINT32 Len;
+ ISCSI_SESSION *Session;
+ CHAR8 *Value;
+ UINTN NumericValue;
+
+ ASSERT (Conn->RspQue.BufNum != 0);
+
+ Session = Conn->Session;
+
+ Len = Conn->RspQue.BufSize;
+ Data = AllocatePool (Len);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data);
+
+ Status = EFI_PROTOCOL_ERROR;
+
+ //
+ // Extract the Key-Value pairs into a list.
+ //
+ KeyValueList = IScsiBuildKeyValueList (Data, Len);
+ if (KeyValueList == NULL) {
+ FreePool (Data);
+ return Status;
+ }
+ //
+ // HeaderDigest
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (AsciiStrCmp (Value, "CRC32") == 0) {
+ if (Conn->HeaderDigest != IScsiDigestCRC32) {
+ goto ON_ERROR;
+ }
+ } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
+ Conn->HeaderDigest = IScsiDigestNone;
+ } else {
+ goto ON_ERROR;
+ }
+ //
+ // DataDigest
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (AsciiStrCmp (Value, "CRC32") == 0) {
+ if (Conn->DataDigest != IScsiDigestCRC32) {
+ goto ON_ERROR;
+ }
+ } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
+ Conn->DataDigest = IScsiDigestNone;
+ } else {
+ goto ON_ERROR;
+ }
+ //
+ // ErrorRecoveryLevel, result fuction is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ if (NumericValue > 2) {
+ goto ON_ERROR;
+ }
+
+ Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);
+
+ //
+ // InitialR2T, result function is OR.
+ //
+ if (!Session->InitialR2T) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
+ }
+
+ //
+ // ImmediateData, result function is AND.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0));
+
+ //
+ // MaxRecvDataSegmentLength is declarative.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);
+ if (Value != NULL) {
+ Conn->MaxRecvDataSegmentLength = (UINT32) AsciiStrDecimalToUintn (Value);
+ }
+ //
+ // MaxBurstLength, result funtion is Mininum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);
+
+ //
+ // FirstBurstLength, result function is Minimum. Irrelevant when InitialR2T=Yes and
+ // ImmediateData=No.
+ // This Key/Value is negotiation type.
+ //
+ if (!(Session->InitialR2T && !Session->ImmediateData)) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);
+ }
+
+ //
+ // MaxConnections, result function is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ if ((NumericValue == 0) || (NumericValue > 65535)) {
+ goto ON_ERROR;
+ }
+
+ Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);
+
+ //
+ // DataPDUInOrder, result function is OR.
+ //
+ if (!Session->DataPDUInOrder) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
+ }
+
+ //
+ // DataSequenceInorder, result function is OR.
+ //
+ if (!Session->DataSequenceInOrder) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
+ }
+
+ //
+ // DefaultTime2Wait, result function is Maximum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ if (NumericValue == 0) {
+ Session->DefaultTime2Wait = 0;
+ } else if (NumericValue > 3600) {
+ goto ON_ERROR;
+ } else {
+ Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);
+ }
+ //
+ // DefaultTime2Retain, result function is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ if (NumericValue == 0) {
+ Session->DefaultTime2Retain = 0;
+ } else if (NumericValue > 3600) {
+ goto ON_ERROR;
+ } else {
+ Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);
+ }
+ //
+ // MaxOutstandingR2T, result function is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = AsciiStrDecimalToUintn (Value);
+ if ((NumericValue == 0) || (NumericValue > 65535)) {
+ goto ON_ERROR;
+ }
+
+ Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);
+
+ //
+ // Remove declarative key-value pairs, if any.
+ //
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
+ //
+ // Remove the key-value that may not needed for result function is OR.
+ //
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
+
+ //
+ // Remove irrelevant parameter, if any.
+ //
+ if (Session->InitialR2T && !Session->ImmediateData) {
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
+ }
+
+ if (IsListEmpty (KeyValueList)) {
+ //
+ // Succeed if no more keys in the list.
+ //
+ Status = EFI_SUCCESS;
+ }
+
+ON_ERROR:
+
+ IScsiFreeKeyValueList (KeyValueList);
+
+ FreePool (Data);
+
+ return Status;
+}
+
+/**
+ Fill the oprational prameters.
+
+ @param[in] Conn The connection in iSCSI login.
+ @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
+
+ @retval EFI_SUCCESS The parmeters are filled into the iSCSI login request PDU.
+**/
+EFI_STATUS
+IScsiFillOpParams (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ )
+{
+ ISCSI_SESSION *Session;
+ CHAR8 Value[256];
+
+ Session = Conn->Session;
+
+ AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
+
+ @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
+ @param[in] Len The length of the last semgnet in the PDU.
+
+ @retval EFI_SUCCESS The segment is padded or no need to pad it.
+ @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
+ padding bytes.
+**/
+EFI_STATUS
+IScsiPadSegment (
+ IN OUT NET_BUF *Pdu,
+ IN UINT32 Len
+ )
+{
+ UINT32 PadLen;
+ UINT8 *Data;
+
+ PadLen = ISCSI_GET_PAD_LEN (Len);
+
+ if (PadLen != 0) {
+ Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (Data, PadLen);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Build a key-value list from the data segment.
+
+ @param[in] Data The data segment containing the key-value pairs.
+ @param[in] Len Length of the data segment.
+
+ @return The key-value list.
+ @retval NULL Other errors as indicated.
+**/
+LIST_ENTRY *
+IScsiBuildKeyValueList (
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *ListHead;
+ ISCSI_KEY_VALUE_PAIR *KeyValuePair;
+
+ ListHead = AllocatePool (sizeof (LIST_ENTRY));
+ if (ListHead == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (ListHead);
+
+ while (Len > 0) {
+ KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));
+ if (KeyValuePair == NULL) {
+ goto ON_ERROR;
+ }
+
+ InitializeListHead (&KeyValuePair->List);
+
+ KeyValuePair->Key = Data;
+
+ while ((Len > 0) && (*Data != '=')) {
+ Len--;
+ Data++;
+ }
+
+ if (*Data == '=') {
+ *Data = '\0';
+
+ Data++;
+ Len--;
+ } else {
+ FreePool (KeyValuePair);
+ goto ON_ERROR;
+ }
+
+ KeyValuePair->Value = Data;
+
+ InsertTailList (ListHead, &KeyValuePair->List);;
+
+ Data += AsciiStrLen (KeyValuePair->Value) + 1;
+ Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;
+ }
+
+ return ListHead;
+
+ON_ERROR:
+
+ IScsiFreeKeyValueList (ListHead);
+
+ return NULL;
+}
+
+/**
+ Get the value string by the key name from the key-value list. If found,
+ the key-value entry will be removed from the list.
+
+ @param[in, out] KeyValueList The key-value list.
+ @param[in] Key The key name to find.
+
+ @return The value string.
+**/
+CHAR8 *
+IScsiGetValueByKeyFromList (
+ IN OUT LIST_ENTRY *KeyValueList,
+ IN CHAR8 *Key
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_KEY_VALUE_PAIR *KeyValuePair;
+ CHAR8 *Value;
+
+ Value = NULL;
+
+ NET_LIST_FOR_EACH (Entry, KeyValueList) {
+ KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
+
+ if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {
+ Value = KeyValuePair->Value;
+
+ RemoveEntryList (&KeyValuePair->List);
+ FreePool (KeyValuePair);
+ break;
+ }
+ }
+
+ return Value;
+}
+
+/**
+ Free the key-value list.
+
+ @param[in] KeyValueList The key-value list.
+**/
+VOID
+IScsiFreeKeyValueList (
+ IN LIST_ENTRY *KeyValueList
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_KEY_VALUE_PAIR *KeyValuePair;
+
+ while (!IsListEmpty (KeyValueList)) {
+ Entry = NetListRemoveHead (KeyValueList);
+ KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
+
+ FreePool (KeyValuePair);
+ }
+
+ FreePool (KeyValueList);
+}
+
+/**
+ Normalize the iSCSI name according to RFC.
+
+ @param[in, out] Name The iSCSI name.
+ @param[in] Len length of the iSCSI name.
+
+ @retval EFI_SUCCESS The iSCSI name is valid and normalized.
+ @retval EFI_PROTOCOL_ERROR The iSCSI name is mal-formatted or not in the IQN format.
+**/
+EFI_STATUS
+IScsiNormalizeName (
+ IN OUT CHAR8 *Name,
+ IN UINTN Len
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Len; Index++) {
+ if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {
+ //
+ // Convert the upper-case characters to lower-case ones
+ //
+ Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');
+ }
+
+ if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&
+ !NET_IS_DIGIT (Name[Index]) &&
+ (Name[Index] != '-') &&
+ (Name[Index] != '.') &&
+ (Name[Index] != ':')
+ ) {
+ //
+ // ASCII dash, dot, colon lower-case characters and digit characters
+ // are allowed.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ }
+
+ if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {
+ //
+ // Only IQN format is accepted now.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create an iSCSI task control block.
+
+ @param[in] Conn The connection on which the task control block will be created.
+ @param[out] Tcb The newly created task control block.
+
+ @retval EFI_SUCCESS The task control block is created.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_READY The target can not accept new commands.
+**/
+EFI_STATUS
+IScsiNewTcb (
+ IN ISCSI_CONNECTION *Conn,
+ OUT ISCSI_TCB **Tcb
+ )
+{
+ ISCSI_SESSION *Session;
+ ISCSI_TCB *NewTcb;
+
+ ASSERT (Tcb != NULL);
+
+ Session = Conn->Session;
+
+ if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {
+ return EFI_NOT_READY;
+ }
+
+ NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));
+ if (NewTcb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&NewTcb->Link);
+
+ NewTcb->SoFarInOrder = TRUE;
+ NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag;
+ NewTcb->CmdSN = Session->CmdSN;
+ NewTcb->Conn = Conn;
+
+ InsertTailList (&Session->TcbList, &NewTcb->Link);
+
+ //
+ // Advance the initiator task tag.
+ //
+ Session->InitiatorTaskTag++;
+ Session->CmdSN++;
+
+ *Tcb = NewTcb;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Delete the tcb from the connection and destroy it.
+
+ @param[in] Tcb The tcb to delete.
+**/
+VOID
+IScsiDelTcb (
+ IN ISCSI_TCB *Tcb
+ )
+{
+ RemoveEntryList (&Tcb->Link);
+
+ FreePool (Tcb);
+}
+
+/**
+ Find the task control block by the initator task tag.
+
+ @param[in] TcbList The tcb list.
+ @param[in] InitiatorTaskTag The initiator task tag.
+
+ @return The task control block found.
+**/
+ISCSI_TCB *
+IScsiFindTcbByITT (
+ IN LIST_ENTRY *TcbList,
+ IN UINT32 InitiatorTaskTag
+ )
+{
+ ISCSI_TCB *Tcb;
+ LIST_ENTRY *Entry;
+
+ Tcb = NULL;
+
+ NET_LIST_FOR_EACH (Entry, TcbList) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);
+
+ if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {
+ break;
+ }
+
+ Tcb = NULL;
+ }
+
+ return Tcb;
+}
+
+/**
+ Create a data segment, pad it and calculate the CRC if needed.
+
+ @param[in] Data The data to fill into the data segment.
+ @param[in] Len Length of the data.
+ @param[in] DataDigest Whether to calculate CRC for this data segment.
+
+ @return The net buffer wrapping the data segment.
+**/
+NET_BUF *
+IScsiNewDataSegment (
+ IN UINT8 *Data,
+ IN UINT32 Len,
+ IN BOOLEAN DataDigest
+ )
+{
+ NET_FRAGMENT Fragment[2];
+ UINT32 FragmentCount;
+ UINT32 PadLen;
+ NET_BUF *DataSeg;
+
+ Fragment[0].Len = Len;
+ Fragment[0].Bulk = Data;
+
+ PadLen = ISCSI_GET_PAD_LEN (Len);
+ if (PadLen != 0) {
+ Fragment[1].Len = PadLen;
+ Fragment[1].Bulk = (UINT8 *) &mDataSegPad;
+
+ FragmentCount = 2;
+ } else {
+ FragmentCount = 1;
+ }
+
+ DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
+
+ return DataSeg;
+}
+
+/**
+ Create a iSCSI SCSI command PDU to encapsulate the command issued
+ by SCSI through the EXT SCSI PASS THRU Protocol.
+
+ @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
+ @param[in] Lun The LUN.
+ @param[in] Tcb The tcb associated with this SCSI command.
+
+ @return The created iSCSI SCSI command PDU.
+ @retval NULL Other errors as indicated.
+**/
+NET_BUF *
+IScsiNewScsiCmdPdu (
+ IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN UINT64 Lun,
+ IN ISCSI_TCB *Tcb
+ )
+{
+ LIST_ENTRY *NbufList;
+ NET_BUF *Pdu;
+ NET_BUF *PduHeader;
+ NET_BUF *DataSeg;
+ SCSI_COMMAND *ScsiCmd;
+ UINT8 AHSLength;
+ UINT32 Length;
+ ISCSI_ADDITIONAL_HEADER *Header;
+ ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS;
+ ISCSI_SESSION *Session;
+ UINT32 ImmediateDataLen;
+
+ AHSLength = 0;
+
+ if (Packet->DataDirection == DataBi) {
+ //
+ // Bi directional Read/Write command, the bidirectional expected
+ // read data length AHS is required.
+ //
+ AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);
+ }
+
+ if (Packet->CdbLength > 16) {
+ //
+ // The CDB exceeds 16 bytes, an extended CDB AHS is required.
+ //
+ AHSLength = (UINT8) (AHSLength + (ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER)));
+ }
+
+ Length = sizeof (SCSI_COMMAND) + AHSLength;
+ PduHeader = NetbufAlloc (Length);
+ if (PduHeader == NULL) {
+ return NULL;
+ }
+
+ ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);
+ if (ScsiCmd == NULL) {
+ NetbufFree (PduHeader);
+ return NULL;
+ }
+ Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);
+
+ ZeroMem (ScsiCmd, Length);
+
+ ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);
+ ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);
+
+ //
+ // Set the READ/WRITE flags according to the IO type of this request.
+ //
+ switch (Packet->DataDirection) {
+ case DataIn:
+ ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);
+ ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);
+ break;
+
+ case DataOut:
+ ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);
+ ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
+ break;
+
+ case DataBi:
+ ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);
+ ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
+
+ //
+ // Fill the bidirectional expected read data length AHS.
+ //
+ BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;
+ Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);
+
+ BiExpReadDataLenAHS->Length = NTOHS (5);
+ BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;
+ BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);
+
+ break;
+ }
+
+ ScsiCmd->TotalAHSLength = AHSLength;
+ CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));
+ ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);
+ ScsiCmd->CmdSN = NTOHL (Tcb->CmdSN);
+ ScsiCmd->ExpStatSN = NTOHL (Tcb->Conn->ExpStatSN);
+
+ CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));
+
+ if (Packet->CdbLength > 16) {
+ Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15));
+ Header->Type = ISCSI_AHS_TYPE_EXT_CDB;
+
+ CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);
+ }
+
+ Pdu = PduHeader;
+ Session = Tcb->Conn->Session;
+ ImmediateDataLen = 0;
+
+ if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {
+ //
+ // Send immediate data in this SCSI Command PDU. The length of the immeidate
+ // data is the minimum of FirstBurstLength, the data length to be xfered and
+ // the MaxRecvdataSegmentLength on this connection.
+ //
+ ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength);
+ ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);
+
+ //
+ // Update the data segment length in the PDU header.
+ //
+ ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);
+
+ //
+ // Create the data segment.
+ //
+ DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);
+ if (DataSeg == NULL) {
+ NetbufFree (PduHeader);
+ Pdu = NULL;
+ goto ON_EXIT;
+ }
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ NetbufFree (PduHeader);
+ NetbufFree (DataSeg);
+
+ Pdu = NULL;
+ goto ON_EXIT;
+ }
+
+ InitializeListHead (NbufList);
+ InsertTailList (NbufList, &PduHeader->List);
+ InsertTailList (NbufList, &DataSeg->List);
+
+ Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (Pdu == NULL) {
+ IScsiFreeNbufList (NbufList);
+ }
+ }
+
+ if (Session->InitialR2T ||
+ (ImmediateDataLen == Session->FirstBurstLength) ||
+ (ImmediateDataLen == Packet->OutTransferLength)
+ ) {
+ //
+ // Unsolicited data out sequence is not allowed,
+ // or FirstBurstLength data is already sent out by immediate data
+ // or all the OUT data accompany this SCSI packet is sent as
+ // immediate data, the final flag should be set on this SCSI Command
+ // PDU.
+ //
+ ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);
+ }
+
+ON_EXIT:
+
+ return Pdu;
+}
+
+/**
+ Create a new iSCSI SCSI Data Out PDU.
+
+ @param[in] Data The data to put into the Data Out PDU.
+ @param[in] Len Length of the data.
+ @param[in] DataSN The DataSN of the Data Out PDU.
+ @param[in] Tcb The task control block of this Data Out PDU.
+ @param[in] Lun The LUN.
+
+ @return The net buffer wrapping the Data Out PDU.
+ @retval NULL Other errors as indicated.
+**/
+NET_BUF *
+IScsiNewDataOutPdu (
+ IN UINT8 *Data,
+ IN UINT32 Len,
+ IN UINT32 DataSN,
+ IN ISCSI_TCB *Tcb,
+ IN UINT64 Lun
+ )
+{
+ LIST_ENTRY *NbufList;
+ NET_BUF *PduHdr;
+ NET_BUF *DataSeg;
+ NET_BUF *Pdu;
+ ISCSI_SCSI_DATA_OUT *DataOutHdr;
+ ISCSI_XFER_CONTEXT *XferContext;
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (NbufList);
+
+ //
+ // Allocate memory for the BHS.
+ //
+ PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));
+ if (PduHdr == NULL) {
+ FreePool (NbufList);
+ return NULL;
+ }
+ //
+ // Insert the BHS into the buffer list.
+ //
+ InsertTailList (NbufList, &PduHdr->List);
+
+ DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);
+ ASSERT (DataOutHdr != NULL);
+ XferContext = &Tcb->XferContext;
+
+ ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));
+
+ //
+ // Set the flags and fields of the Data Out PDU BHS.
+ //
+ ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);
+ ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);
+
+ DataOutHdr->InitiatorTaskTag = HTONL (Tcb->InitiatorTaskTag);
+ DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);
+ DataOutHdr->ExpStatSN = HTONL (Tcb->Conn->ExpStatSN);
+ DataOutHdr->DataSN = HTONL (DataSN);
+ DataOutHdr->BufferOffset = HTONL (XferContext->Offset);
+
+ if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {
+ CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));
+ }
+ //
+ // Build the data segment for this Data Out PDU.
+ //
+ DataSeg = IScsiNewDataSegment (Data, Len, FALSE);
+ if (DataSeg == NULL) {
+ IScsiFreeNbufList (NbufList);
+ return NULL;
+ }
+ //
+ // Put the data segment into the buffer list and combine it with the BHS
+ // into a full Data Out PDU.
+ //
+ InsertTailList (NbufList, &DataSeg->List);
+ Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (Pdu == NULL) {
+ IScsiFreeNbufList (NbufList);
+ }
+
+ return Pdu;
+}
+
+/**
+ Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
+
+ @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
+ @param[in] Tcb The task control block of the data to send out.
+ @param[in] Lun The LUN the data will be sent to.
+
+ @return A list of net buffers with each of them wraps an iSCSI SCSI Data Out PDU.
+ @retval NULL Other errors as indicated.
+**/
+LIST_ENTRY *
+IScsiGenerateDataOutPduSequence (
+ IN UINT8 *Data,
+ IN ISCSI_TCB *Tcb,
+ IN UINT64 Lun
+ )
+{
+ LIST_ENTRY *PduList;
+ UINT32 DataSN;
+ UINT32 DataLen;
+ NET_BUF *DataOutPdu;
+ ISCSI_CONNECTION *Conn;
+ ISCSI_XFER_CONTEXT *XferContext;
+ UINT8 *DataOutPacket;
+
+ PduList = AllocatePool (sizeof (LIST_ENTRY));
+ if (PduList == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (PduList);
+
+ DataSN = 0;
+ Conn = Tcb->Conn;
+ DataOutPdu = NULL;
+ XferContext = &Tcb->XferContext;
+
+ while (XferContext->DesiredLength > 0) {
+ //
+ // Determine the length of data this Data Out PDU can carry.
+ //
+ DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);
+
+ //
+ // Create a Data Out PDU.
+ //
+ DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);
+ if (DataOutPdu == NULL) {
+ IScsiFreeNbufList (PduList);
+ PduList = NULL;
+
+ goto ON_EXIT;
+ }
+
+ InsertTailList (PduList, &DataOutPdu->List);
+
+ //
+ // Update the context and DataSN.
+ //
+ XferContext->Offset += DataLen;
+ XferContext->DesiredLength -= DataLen;
+ DataSN++;
+ Data += DataLen;
+ }
+ //
+ // Set the F bit for the last data out PDU in this sequence.
+ //
+ DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL);
+ if (DataOutPacket == NULL) {
+ IScsiFreeNbufList (PduList);
+ PduList = NULL;
+ goto ON_EXIT;
+ }
+
+ ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL);
+
+ON_EXIT:
+
+ return PduList;
+}
+
+/**
+ Send the Data in a sequence of Data Out PDUs one by one.
+
+ @param[in] Data The data to carry by Data Out PDUs.
+ @param[in] Lun The LUN the data will be sent to.
+ @param[in] Tcb The task control block.
+
+ @retval EFI_SUCCES The data is sent out to the LUN.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiSendDataOutPduSequence (
+ IN UINT8 *Data,
+ IN UINT64 Lun,
+ IN ISCSI_TCB *Tcb
+ )
+{
+ LIST_ENTRY *DataOutPduList;
+ LIST_ENTRY *Entry;
+ NET_BUF *Pdu;
+ EFI_STATUS Status;
+
+ //
+ // Generate the Data Out PDU sequence.
+ //
+ DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);
+ if (DataOutPduList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Send the Data Out PDU's one by one.
+ //
+ NET_LIST_FOR_EACH (Entry, DataOutPduList) {
+ Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ Status = Tcp4IoTransmit (&Tcb->Conn->Tcp4Io, Pdu);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ IScsiFreeNbufList (DataOutPduList);
+
+ return Status;
+}
+
+/**
+ Process the received iSCSI SCSI Data In PDU.
+
+ @param[in] Pdu The Data In PDU received.
+ @param[in] Tcb The task control block.
+ @param[in, out] Packet The EXT SCSI PASS THRU request packet.
+
+ @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
+ actions are taken.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
+ @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiOnDataInRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ ISCSI_SCSI_DATA_IN *DataInHdr;
+ EFI_STATUS Status;
+
+ DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);
+ if (DataInHdr == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);
+ DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN);
+ DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN);
+ DataInHdr->DataSN = NTOHL (DataInHdr->DataSN);
+
+ //
+ // Check the DataSN.
+ //
+ Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Update the command related sequence numbers.
+ //
+ IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {
+ if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {
+ //
+ // The S bit is on but the F bit is off.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Tcb->StatusXferd = TRUE;
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {
+ //
+ // Underflow and Overflow are mutual flags.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // S bit is on, the StatSN is valid.
+ //
+ Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Packet->HostAdapterStatus = 0;
+ Packet->TargetStatus = DataInHdr->Status;
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
+ Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
+ Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Process the received iSCSI R2T PDU.
+
+ @param[in] Pdu The R2T PDU received.
+ @param[in] Tcb The task control block.
+ @param[in] Lun The Lun.
+ @param[in, out] Packet The EXT SCSI PASS THRU request packet.
+
+ @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiOnR2TRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ ISCSI_READY_TO_TRANSFER *R2THdr;
+ EFI_STATUS Status;
+ ISCSI_XFER_CONTEXT *XferContext;
+ UINT8 *Data;
+
+ R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);
+ if (R2THdr == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);
+ R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);
+ R2THdr->StatSN = NTOHL (R2THdr->StatSN);
+ R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);
+ R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);
+ R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);
+
+ if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {
+ return EFI_PROTOCOL_ERROR;;
+ }
+ //
+ // Check the sequence number.
+ //
+ Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ XferContext = &Tcb->XferContext;
+ XferContext->TargetTransferTag = R2THdr->TargetTransferTag;
+ XferContext->Offset = R2THdr->BufferOffset;
+ XferContext->DesiredLength = R2THdr->DesiredDataTransferLength;
+
+ if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||
+ (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)
+ ) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Send the data solicited by this R2T.
+ //
+ Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
+ Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
+
+ return Status;
+}
+
+/**
+ Process the received iSCSI SCSI Response PDU.
+
+ @param[in] Pdu The Response PDU received.
+ @param[in] Tcb The task control block.
+ @param[in, out] Packet The EXT SCSI PASS THRU request packet.
+
+ @retval EFI_SUCCES The Response PDU is processed.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
+ @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiOnScsiRspRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ SCSI_RESPONSE *ScsiRspHdr;
+ ISCSI_SENSE_DATA *SenseData;
+ EFI_STATUS Status;
+ UINT32 DataSegLen;
+
+ ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
+ if (ScsiRspHdr == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag);
+ if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN);
+
+ Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN);
+ ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN);
+ IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);
+
+ Tcb->StatusXferd = TRUE;
+
+ Packet->HostAdapterStatus = ScsiRspHdr->Response;
+ if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {
+ return EFI_SUCCESS;
+ }
+
+ Packet->TargetStatus = ScsiRspHdr->Status;
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||
+ ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)
+ ) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {
+ Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {
+ Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
+ if (Packet->DataDirection == DataIn) {
+ Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
+ } else {
+ Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
+ }
+
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
+ if (Packet->DataDirection == DataIn) {
+ Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
+ } else {
+ Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
+ }
+ }
+
+ DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);
+ if (DataSegLen != 0) {
+ SenseData = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);
+ if (SenseData == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ SenseData->Length = NTOHS (SenseData->Length);
+
+ Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);
+ if (Packet->SenseDataLength != 0) {
+ CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);
+ }
+ } else {
+ Packet->SenseDataLength = 0;
+ }
+
+ return Status;
+}
+
+/**
+ Process the received NOP In PDU.
+
+ @param[in] Pdu The NOP In PDU received.
+ @param[in] Tcb The task control block.
+
+ @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
+ numbers are updated.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
+**/
+EFI_STATUS
+IScsiOnNopInRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb
+ )
+{
+ ISCSI_NOP_IN *NopInHdr;
+ EFI_STATUS Status;
+
+ NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);
+ if (NopInHdr == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ NopInHdr->StatSN = NTOHL (NopInHdr->StatSN);
+ NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN);
+ NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN);
+
+ if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {
+ if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ } else {
+ Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
+
+ @param[in] PassThru The EXT SCSI PASS THRU protocol.
+ @param[in] Target The target ID.
+ @param[in] Lun The LUN.
+ @param[in, out] Packet The request packet containing IO request, SCSI command
+ buffer and buffers to read/write.
+
+ @retval EFI_SUCCES The SCSI command is executed and the result is updated to
+ the Packet.
+ @retval EFI_DEVICE_ERROR Session state was not as required.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_READY The target can not accept new commands.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiExecuteScsiCommand (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION *Session;
+ EFI_EVENT TimeoutEvent;
+ ISCSI_CONNECTION *Conn;
+ ISCSI_TCB *Tcb;
+ NET_BUF *Pdu;
+ ISCSI_XFER_CONTEXT *XferContext;
+ UINT8 *Data;
+ ISCSI_IN_BUFFER_CONTEXT InBufferContext;
+ UINT64 Timeout;
+ UINT8 *PduHdr;
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
+ Session = &Private->Session;
+ Status = EFI_SUCCESS;
+ Tcb = NULL;
+ TimeoutEvent = NULL;
+ Timeout = 0;
+
+ if (Session->State != SESSION_STATE_LOGGED_IN) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Conn = NET_LIST_USER_STRUCT_S (
+ Session->Conns.ForwardLink,
+ ISCSI_CONNECTION,
+ Link,
+ ISCSI_CONNECTION_SIGNATURE
+ );
+
+ if (Packet->Timeout != 0) {
+ Timeout = MultU64x32 (Packet->Timeout, 2);
+ }
+
+ Status = IScsiNewTcb (Conn, &Tcb);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
+ //
+ Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);
+ if (Pdu == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ XferContext = &Tcb->XferContext;
+ PduHdr = NetbufGetByte (Pdu, 0, NULL);
+ if (PduHdr == NULL) {
+ Status = EFI_PROTOCOL_ERROR;
+ NetbufFree (Pdu);
+ goto ON_EXIT;
+ }
+ XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);
+
+ //
+ // Transmit the SCSI Command PDU.
+ //
+ Status = Tcp4IoTransmit (&Conn->Tcp4Io, Pdu);
+
+ NetbufFree (Pdu);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (!Session->InitialR2T &&
+ (XferContext->Offset < Session->FirstBurstLength) &&
+ (XferContext->Offset < Packet->OutTransferLength)
+ ) {
+ //
+ // Unsolicited Data-Out sequence is allowed, there is remaining SCSI
+ // OUT data and the limit of FirstBurstLength is not reached.
+ //
+ XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;
+ XferContext->DesiredLength = MIN (
+ Session->FirstBurstLength,
+ Packet->OutTransferLength - XferContext->Offset
+ );
+
+ Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
+ Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ InBufferContext.InData = (UINT8 *) Packet->InDataBuffer;
+ InBufferContext.InDataLen = Packet->InTransferLength;
+
+ while (!Tcb->StatusXferd) {
+ //
+ // Start the timeout timer.
+ //
+ if (Timeout != 0) {
+ Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ TimeoutEvent = Conn->TimeoutEvent;
+ }
+ //
+ // try to receive PDU from target.
+ //
+ Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ PduHdr = NetbufGetByte (Pdu, 0, NULL);
+ if (PduHdr == NULL) {
+ Status = EFI_PROTOCOL_ERROR;
+ NetbufFree (Pdu);
+ goto ON_EXIT;
+ }
+ switch (ISCSI_GET_OPCODE (PduHdr)) {
+ case ISCSI_OPCODE_SCSI_DATA_IN:
+ Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);
+ break;
+
+ case ISCSI_OPCODE_R2T:
+ Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);
+ break;
+
+ case ISCSI_OPCODE_SCSI_RSP:
+ Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);
+ break;
+
+ case ISCSI_OPCODE_NOP_IN:
+ Status = IScsiOnNopInRcvd (Pdu, Tcb);
+ break;
+
+ case ISCSI_OPCODE_VENDOR_T0:
+ case ISCSI_OPCODE_VENDOR_T1:
+ case ISCSI_OPCODE_VENDOR_T2:
+ //
+ // These messages are vendor specific, skip them.
+ //
+ break;
+
+ default:
+ Status = EFI_PROTOCOL_ERROR;
+ break;
+ }
+
+ NetbufFree (Pdu);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ON_EXIT:
+
+ if (TimeoutEvent != NULL) {
+ gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
+ }
+
+ if (Tcb != NULL) {
+ IScsiDelTcb (Tcb);
+ }
+
+ return Status;
+}
+
+/**
+ Reinstate the session on some error.
+
+ @param[in, out] Private The iSCSI driver data.
+
+ @retval EFI_SUCCES The session is reinstated from some error.
+ @retval Other Reinstatement failed.
+**/
+EFI_STATUS
+IScsiSessionReinstatement (
+ IN OUT ISCSI_DRIVER_DATA *Private
+ )
+{
+ ISCSI_SESSION *Session;
+ EFI_STATUS Status;
+
+ Session = &Private->Session;
+ ASSERT (Session->State != SESSION_STATE_FREE);
+
+ //
+ // Abort the session and re-init it.
+ //
+ IScsiSessionAbort (Session);
+ IScsiSessionInit (Session, TRUE);
+
+ //
+ // Login again.
+ //
+ Status = IScsiSessionLogin (Private);
+
+ return Status;
+}
+
+/**
+ Initialize some session parameters before login.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Recovery Whether the request is from a fresh new start or recovery.
+**/
+VOID
+IScsiSessionInit (
+ IN OUT ISCSI_SESSION *Session,
+ IN BOOLEAN Recovery
+ )
+{
+ if (!Recovery) {
+ Session->Signature = ISCSI_SESSION_SIGNATURE;
+ Session->State = SESSION_STATE_FREE;
+
+ InitializeListHead (&Session->Conns);
+ InitializeListHead (&Session->TcbList);
+ }
+
+ Session->Tsih = 0;
+
+ Session->CmdSN = 1;
+ Session->InitiatorTaskTag = 1;
+ Session->NextCid = 1;
+
+ Session->TargetPortalGroupTag = 0;
+ Session->MaxConnections = ISCSI_MAX_CONNS_PER_SESSION;
+ Session->InitialR2T = FALSE;
+ Session->ImmediateData = TRUE;
+ Session->MaxBurstLength = 262144;
+ Session->FirstBurstLength = MAX_RECV_DATA_SEG_LEN_IN_FFP;
+ Session->DefaultTime2Wait = 2;
+ Session->DefaultTime2Retain = 20;
+ Session->MaxOutstandingR2T = DEFAULT_MAX_OUTSTANDING_R2T;
+ Session->DataPDUInOrder = TRUE;
+ Session->DataSequenceInOrder = TRUE;
+ Session->ErrorRecoveryLevel = 0;
+}
+
+/**
+ Abort the iSCSI session, that is, reset all the connection and free the
+ resources.
+
+ @param[in, out] Session The iSCSI session.
+
+ @retval EFI_SUCCES The session is aborted.
+**/
+EFI_STATUS
+IScsiSessionAbort (
+ IN OUT ISCSI_SESSION *Session
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_CONNECTION *Conn;
+
+ if (Session->State != SESSION_STATE_LOGGED_IN) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (!IsListEmpty (&Session->Conns));
+
+ Private = ISCSI_DRIVER_DATA_FROM_SESSION (Session);
+
+ while (!IsListEmpty (&Session->Conns)) {
+ Conn = NET_LIST_USER_STRUCT_S (
+ Session->Conns.ForwardLink,
+ ISCSI_CONNECTION,
+ Link,
+ ISCSI_CONNECTION_SIGNATURE
+ );
+
+ gBS->CloseProtocol (
+ Conn->Tcp4Io.Handle,
+ &gEfiTcp4ProtocolGuid,
+ Private->Image,
+ Private->ExtScsiPassThruHandle
+ );
+
+ IScsiConnReset (Conn);
+
+ IScsiDetatchConnection (Conn);
+ IScsiDestroyConnection (Conn);
+ }
+
+ Session->State = SESSION_STATE_FAILED;
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.h
new file mode 100644
index 0000000000..48b4e7251f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiProto.h
@@ -0,0 +1,1002 @@
+/** @file
+ The header file of iSCSI Protocol that defines many specific data structures.
+
+Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_PROTO_H_
+#define _ISCSI_PROTO_H_
+
+#include <Protocol/ScsiPassThruExt.h>
+
+//
+// RFC 1982 Serial Number Arithmetic, SERIAL_BITS = 32
+//
+#define ISCSI_SEQ_EQ(s1, s2) ((s1) == (s2))
+#define ISCSI_SEQ_LT(s1, s2) \
+ ( \
+ (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) < ((UINT32) 1 << 31)) || \
+ (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) > ((UINT32) 1 << 31)) \
+ )
+#define ISCSI_SEQ_GT(s1, s2) \
+ ( \
+ (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) > ((UINT32) 1 << 31)) || \
+ (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) < ((UINT32) 1 << 31)) \
+ )
+
+#define ISCSI_WELL_KNOWN_PORT 3260
+#define ISCSI_MAX_CONNS_PER_SESSION 1
+
+#define DEFAULT_MAX_RECV_DATA_SEG_LEN 8192
+#define MAX_RECV_DATA_SEG_LEN_IN_FFP 65536
+#define DEFAULT_MAX_OUTSTANDING_R2T 1
+
+#define ISCSI_VERSION_MAX 0x00
+#define ISCSI_VERSION_MIN 0x00
+
+#define ISCSI_KEY_AUTH_METHOD "AuthMethod"
+#define ISCSI_KEY_HEADER_DIGEST "HeaderDigest"
+#define ISCSI_KEY_DATA_DIGEST "DataDigest"
+#define ISCSI_KEY_MAX_CONNECTIONS "MaxConnections"
+#define ISCSI_KEY_TARGET_NAME "TargetName"
+#define ISCSI_KEY_INITIATOR_NAME "InitiatorName"
+#define ISCSI_KEY_TARGET_ALIAS "TargetAlias"
+#define ISCSI_KEY_INITIATOR_ALIAS "InitiatorAlias"
+#define ISCSI_KEY_TARGET_ADDRESS "TargetAddress"
+#define ISCSI_KEY_INITIAL_R2T "InitialR2T"
+#define ISCSI_KEY_IMMEDIATE_DATA "ImmediateData"
+#define ISCSI_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag"
+#define ISCSI_KEY_MAX_BURST_LENGTH "MaxBurstLength"
+#define ISCSI_KEY_FIRST_BURST_LENGTH "FirstBurstLength"
+#define ISCSI_KEY_DEFAULT_TIME2WAIT "DefaultTime2Wait"
+#define ISCSI_KEY_DEFAULT_TIME2RETAIN "DefaultTime2Retain"
+#define ISCSI_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T"
+#define ISCSI_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder"
+#define ISCSI_KEY_DATA_SEQUENCE_IN_ORDER "DataSequenceInOrder"
+#define ISCSI_KEY_ERROR_RECOVERY_LEVEL "ErrorRecoveryLevel"
+#define ISCSI_KEY_SESSION_TYPE "SessionType"
+#define ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH "MaxRecvDataSegmentLength"
+
+#define ISCSI_KEY_VALUE_NONE "None"
+
+///
+/// connection state for initiator
+///
+
+#define CONN_STATE_FREE 0
+#define CONN_STATE_XPT_WAIT 1
+#define CONN_STATE_IN_LOGIN 2
+#define CONN_STATE_LOGGED_IN 3
+#define CONN_STATE_IN_LOGOUT 4
+#define CONN_STATE_LOGOUT_REQUESTED 5
+#define CONN_STATE_CLEANUP_WAIT 6
+#define CONN_STATE_IN_CLEANUP 7
+
+///
+/// session state for initiator
+///
+#define SESSION_STATE_FREE 0
+#define SESSION_STATE_LOGGED_IN 1
+#define SESSION_STATE_FAILED 2
+
+typedef enum {
+ DataIn = 0,
+ DataOut = 1,
+ DataBi = 2
+} DATA_DIRECTION;
+
+#define ISCSI_RESERVED_TAG 0xffffffff
+
+#define ISCSI_REQ_IMMEDIATE 0x40
+#define ISCSI_OPCODE_MASK 0x3F
+
+#define ISCSI_SET_OPCODE(PduHdr, Op, Flgs) ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) = ((Op) | (Flgs)))
+#define ISCSI_GET_OPCODE(PduHdr) ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) & ISCSI_OPCODE_MASK)
+#define ISCSI_CHECK_OPCODE(PduHdr, Op) ((((PduHdr)->OpCode) & ISCSI_OPCODE_MASK) == (Op))
+#define ISCSI_IMMEDIATE_ON(PduHdr) ((PduHdr)->OpCode & ISCSI_REQ_IMMEDIATE)
+#define ISCSI_SET_FLAG(PduHdr, Flag) (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags |= (BOOLEAN)(Flag))
+#define ISCSI_CLEAR_FLAG(PduHdr, Flag) (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags &= ~(Flag))
+#define ISCSI_FLAG_ON(PduHdr, Flag) ((((ISCSI_BASIC_HEADER *) (PduHdr))->Flags & (Flag)) == (Flag))
+#define ISCSI_SET_STAGES(PduHdr, Cur, Nxt) ((PduHdr)->Flags = (UINT8) ((PduHdr)->Flags | ((Cur) << 2 | (Nxt))))
+#define ISCSI_GET_CURRENT_STAGE(PduHdr) (((PduHdr)->Flags >> 2) & 0x3)
+#define ISCSI_GET_NEXT_STAGE(PduHdr) (((PduHdr)->Flags) & 0x3)
+
+#define ISCSI_GET_PAD_LEN(DataLen) ((~(DataLen) + 1) & 0x3)
+#define ISCSI_ROUNDUP(DataLen) (((DataLen) + 3) &~(0x3))
+
+#define HTON24(Dst, Src) \
+ do { \
+ (Dst)[0] = (UINT8) (((Src) >> 16) & 0xFF); \
+ (Dst)[1] = (UINT8) (((Src) >> 8) & 0xFF); \
+ (Dst)[2] = (UINT8) ((Src) & 0xFF); \
+ } while (0);
+
+#define NTOH24(src) (((src)[0] << 16) | ((src)[1] << 8) | ((src)[2]))
+
+#define ISCSI_GET_DATASEG_LEN(PduHdr) NTOH24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength)
+#define ISCSI_SET_DATASEG_LEN(PduHdr, Len) HTON24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength, (Len))
+
+//
+// initiator opcodes
+//
+#define ISCSI_OPCODE_NOP_OUT 0x00
+#define ISCSI_OPCODE_SCSI_CMD 0x01
+#define ISCSI_OPCODE_SCSI_TMF_REQ 0x02
+#define ISCSI_OPCODE_LOGIN_REQ 0x03
+#define ISCSI_OPCODE_TEXT_REQ 0x04
+#define ISCSI_OPCODE_SCSI_DATA_OUT 0x05
+#define ISCSI_OPCODE_LOGOUT_REQ 0x06
+#define ISCSI_OPCODE_SNACK_REQ 0x10
+#define ISCSI_OPCODE_VENDOR_I0 0x1c
+#define ISCSI_OPCODE_VENDOR_I1 0x1d
+#define ISCSI_OPCODE_VENDOR_I2 0x1e
+
+//
+// target opcodes
+//
+#define ISCSI_OPCODE_NOP_IN 0x20
+#define ISCSI_OPCODE_SCSI_RSP 0x21
+#define ISCSI_OPCODE_SCSI_TMF_RSP 0x22
+#define ISCSI_OPCODE_LOGIN_RSP 0x23
+#define ISCSI_OPCODE_TEXT_RSP 0x24
+#define ISCSI_OPCODE_SCSI_DATA_IN 0x25
+#define ISCSI_OPCODE_LOGOUT_RSP 0x26
+#define ISCSI_OPCODE_R2T 0x31
+#define ISCSI_OPCODE_ASYNC_MSG 0x32
+#define ISCSI_OPCODE_VENDOR_T0 0x3c
+#define ISCSI_OPCODE_VENDOR_T1 0x3d
+#define ISCSI_OPCODE_VENDOR_T2 0x3e
+#define ISCSI_OPCODE_REJECT 0x3f
+
+#define ISCSI_BHS_FLAG_FINAL 0x80
+
+///
+/// iSCSI Basic Header Segment
+///
+typedef struct _ISCSI_BASIC_HEADER {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT16 OpCodeSpecific1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 OpCodeSpecific2[7];
+} ISCSI_BASIC_HEADER;
+
+//
+// Defined AHS types, others are reserved.
+//
+#define ISCSI_AHS_TYPE_EXT_CDB 0x1
+#define ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN 0x2
+
+typedef struct _ISCSI_ADDTIONAL_HEADER {
+ UINT16 Length;
+ UINT8 Type;
+ UINT8 TypeSpecific[1];
+} ISCSI_ADDITIONAL_HEADER;
+
+typedef struct _ISCSI_BI_EXP_READ_DATA_LEN_AHS {
+ UINT16 Length;
+ UINT8 Type;
+ UINT8 Reserved;
+ UINT32 ExpReadDataLength;
+} ISCSI_BI_EXP_READ_DATA_LEN_AHS;
+
+#define SCSI_CMD_PDU_FLAG_READ 0x40
+#define SCSI_CMD_PDU_FLAG_WRITE 0x20
+
+#define ISCSI_CMD_PDU_TASK_ATTR_MASK 0x07
+
+//
+// task attributes
+//
+#define ISCSI_TASK_ATTR_UNTAGGED 0x00
+#define ISCSI_TASK_ATTR_SIMPLE 0x01
+#define ISCSI_TASK_ATTR_ORDERD 0x02
+#define ISCSI_TASK_ATTR_HOQ 0x03
+#define ISCSI_TASK_ATTR_ACA 0x04
+
+///
+/// SCSI Command
+///
+typedef struct _SCSI_COMMAND {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT16 Reserved;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 ExpDataXferLength;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT8 Cdb[16];
+} SCSI_COMMAND;
+
+//
+// flag bit definitions in SCSI response
+//
+#define SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW 0x10
+#define SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW 0x08
+#define SCSI_RSP_PDU_FLAG_OVERFLOW 0x04
+#define SCSI_RSP_PDU_FLAG_UNDERFLOW 0x02
+
+//
+// iSCSI service response codes
+//
+#define ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET 0x00
+#define ISCSI_SERVICE_RSP_TARGET_FAILURE 0x01
+
+///
+/// SCSI Response
+///
+typedef struct _SCSI_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 Response;
+ UINT8 Status;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Reserved[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 SNACKTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 ExpDataSN;
+ UINT32 BiReadResidualCount;
+ UINT32 ResidualCount;
+} SCSI_RESPONSE;
+
+typedef struct _ISCSI_SENSE_DATA {
+ UINT16 Length;
+ UINT8 Data[2];
+} ISCSI_SENSE_DATA;
+
+///
+/// iSCSI Task Managment Function Request
+///
+typedef struct _ISCSI_TMF_REQUEST {
+ UINT8 OpCode;
+ UINT8 Fuction;
+ UINT16 Reserved1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 ReferencedTaskTag;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 RefCmdSN;
+ UINT32 ExpDataSN;
+ UINT32 Reserved2[2];
+} ISCSI_TMF_REQUEST;
+
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_COMPLETE 0
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_NOT_EXIST 1
+#define ISCSI_TMF_RSP_PDU_RSP_LUN_NOT_EXIST 2
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_STILL_ALLEGIANT 3
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_REASSGIN_NOT_SUPPORTED 4
+#define ISCSI_TMF_RSP_PDU_RSP_NOT_SUPPORTED 5
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_AHTH_FAILED 6
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_REJECTED 255
+
+///
+/// iSCSI Task Management Function Response
+///
+typedef struct _ISCSI_TMF_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Reserved1;
+ UINT8 Response;
+ UINT8 Reserved2;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserver3[2];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved4;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 Reserved[3];
+} ISCSI_TMF_RESPONSE;
+
+///
+/// SCSI Data-Out
+///
+typedef struct _ISCSI_SCSI_DATA_OUT {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 Reserved2;
+ UINT32 ExpStatSN;
+ UINT32 Reserved3;
+ UINT32 DataSN;
+ UINT32 BufferOffset;
+ UINT32 Reserved4;
+} ISCSI_SCSI_DATA_OUT;
+
+#define SCSI_DATA_IN_PDU_FLAG_ACKKNOWLEDGE 0x40
+#define SCSI_DATA_IN_PDU_FLAG_OVERFLOW SCSI_RSP_PDU_FLAG_OVERFLOW
+#define SCSI_DATA_IN_PDU_FLAG_UNDERFLOW SCSI_RSP_PDU_FLAG_UNDERFLOW
+#define SCSI_DATA_IN_PDU_FLAG_STATUS_VALID 0x01
+
+///
+/// SCSI Data-In
+///
+typedef struct _ISCSI_SCSI_DATA_IN {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 Reserved1;
+ UINT8 Status;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 DataSN;
+ UINT32 BufferOffset;
+ UINT32 ResidualCount;
+} ISCSI_SCSI_DATA_IN;
+
+#define ISCSI_GET_BUFFER_OFFSET(PduHdr) NTOHL (((ISCSI_SCSI_DATA_IN *) (PduHdr))->BufferOffset)
+
+///
+/// Ready To Transfer
+///
+typedef struct _ISCSI_READY_TO_TRANSFER {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 R2TSeqNum;
+ UINT32 BufferOffset;
+ UINT32 DesiredDataTransferLength;
+} ISCSI_READY_TO_TRANSFER;
+
+typedef struct _ISCSI_ASYNC_MESSAGE {
+ UINT8 OpCode;
+ UINT8 Reserved1[8];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved2;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT8 AsyncEvent;
+ UINT8 AsyncVCode;
+ UINT16 Parameter1;
+ UINT16 Parameter2;
+ UINT16 Parameter3;
+ UINT32 Reserved3;
+} ISCSI_ASYNC_MESSAGE;
+
+#define ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT 0x80
+#define ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE 0x40
+
+///
+/// Login Request
+///
+typedef struct _ISCSI_LOGIN_REQUEST {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 VersionMax;
+ UINT8 VersionMin;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Isid[6];
+ UINT16 Tsih;
+ UINT32 InitiatorTaskTag;
+ UINT16 Cid;
+ UINT16 Reserved1;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 Reserved2[4];
+} ISCSI_LOGIN_REQUEST;
+
+#define ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
+#define ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE
+
+#define ISCSI_LOGIN_STATUS_SUCCESS 0
+#define ISCSI_LOGIN_STATUS_REDIRECTION 1
+#define ISCSI_LOGIN_STATUS_INITIATOR_ERROR 2
+#define ISCSI_LOGIN_STATUS_TARGET_ERROR 3
+
+///
+/// Login Response
+///
+typedef struct _ISCSI_LOGIN_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 VersionMax;
+ UINT8 VersionActive;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Isid[6];
+ UINT16 Tsih;
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved1;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT8 StatusClass;
+ UINT8 StatusDetail;
+ UINT8 Reserved2[10];
+} ISCSI_LOGIN_RESPONSE;
+
+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION 0
+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION 1
+#define ISCSI_LOGOUT_REASON_REMOVE_CONNECTION_FOR_RECOVERY 2
+
+///
+/// Logout Request
+///
+typedef struct _ISCSI_LOGOUT_REQUEST {
+ UINT8 OpCode;
+ UINT8 ReasonCode;
+ UINT16 Reserved1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserved2[2];
+ UINT32 InitiatorTaskTag;
+ UINT16 Cid;
+ UINT16 Reserved3;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 Reserved4[4];
+} ISCSI_LOGOUT_REQUEST;
+
+#define ISCSI_LOGOUT_RESPONSE_SESSION_CLOSED_SUCCESS 0
+#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 1
+#define ISCSI_LOGOUT_RESPONSE_RECOVERY_NOT_SUPPORTED 2
+#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 3
+
+///
+/// Logout Response
+///
+typedef struct _ISCSI_LOGOUT_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Reserved1;
+ UINT8 Response;
+ UINT8 Reserved2;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserved3[2];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved4;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 Reserved5;
+ UINT16 Time2Wait;
+ UINT16 Time2Retain;
+ UINT32 Reserved6;
+} ISCSI_LOGOUT_RESPONSE;
+
+#define ISCSI_SNACK_REQUEST_TYPE_DATA_OR_R2T 0
+#define ISCSI_SNACK_REQUEST_TYPE_STATUS 1
+#define ISCSI_SNACK_REQUEST_TYPE_DATA_ACK 2
+#define ISCSI_SNACK_REQUEST_TYPE_RDATA 3
+
+///
+/// SNACK Request
+///
+typedef struct _ISCSI_SNACK_REQUEST {
+ UINT8 OpCode;
+ UINT8 Type;
+ UINT16 Reserved1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 Reserved2;
+ UINT32 ExpStatSN;
+ UINT32 Reserved[2];
+ UINT32 BegRun;
+ UINT32 RunLength;
+} ISCSI_SNACK_REQUEST;
+
+///
+/// Reject
+///
+typedef struct _ISCSI_REJECT {
+ UINT8 OpCode;
+ UINT8 Reserved1;
+ UINT8 Reason;
+ UINT8 Reserved2;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserved3[2];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved4;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 DataSN;
+ UINT32 Reserved5[2];
+} ISCSI_REJECT;
+
+///
+/// NOP-Out
+///
+typedef struct _ISCSI_NOP_OUT {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 Reserved2[4];
+} ISCSI_NOP_OUT;
+
+///
+/// NOP-In
+///
+typedef struct _ISCSI_NOP_IN {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 Reserved2[3];
+} ISCSI_NOP_IN;
+
+#define ISCSI_SECURITY_NEGOTIATION 0
+#define ISCSI_LOGIN_OPERATIONAL_NEGOTIATION 1
+#define ISCSI_FULL_FEATURE_PHASE 3
+
+typedef enum {
+ IScsiDigestNone,
+ IScsiDigestCRC32
+} ISCSI_DIGEST_TYPE;
+
+typedef struct _ISCSI_XFER_CONTEXT {
+ UINT32 TargetTransferTag;
+ UINT32 Offset;
+ UINT32 DesiredLength;
+ UINT32 ExpDataSN;
+} ISCSI_XFER_CONTEXT;
+
+typedef struct _ISCSI_IN_BUFFER_CONTEXT {
+ UINT8 *InData;
+ UINT32 InDataLen;
+} ISCSI_IN_BUFFER_CONTEXT;
+
+typedef struct _ISCSI_TCB {
+ LIST_ENTRY Link;
+
+ BOOLEAN SoFarInOrder;
+ UINT32 ExpDataSN;
+ BOOLEAN FbitReceived;
+ BOOLEAN StatusXferd;
+ UINT32 ActiveR2Ts;
+ UINT32 Response;
+ CHAR8 *Reason;
+ UINT32 InitiatorTaskTag;
+ UINT32 CmdSN;
+ UINT32 SNACKTag;
+
+ ISCSI_XFER_CONTEXT XferContext;
+
+ ISCSI_CONNECTION *Conn;
+} ISCSI_TCB;
+
+typedef struct _ISCSI_KEY_VALUE_PAIR {
+ LIST_ENTRY List;
+
+ CHAR8 *Key;
+ CHAR8 *Value;
+} ISCSI_KEY_VALUE_PAIR;
+
+/**
+ Attach the iSCSI connection to the iSCSI session.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in, out] Conn The iSCSI connection.
+**/
+VOID
+IScsiAttatchConnection (
+ IN OUT ISCSI_SESSION *Session,
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Detach the iSCSI connection from the session it belongs to.
+
+ @param[in, out] Conn The iSCSI connection.
+**/
+VOID
+IScsiDetatchConnection (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ This function does the iSCSI connection login.
+
+ @param[in, out] Conn The iSCSI connection to login.
+
+ @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target.
+ @retval EFI_TIMEOUT Timeout happened during the login procedure.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiConnLogin (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Create a TCP connection for the iSCSI session.
+
+ @param[in] Private The iSCSI driver data.
+ @param[in] Session Maximum CmdSN from the target.
+
+ @return The newly created iSCSI connection.
+**/
+ISCSI_CONNECTION *
+IScsiCreateConnection (
+ IN ISCSI_DRIVER_DATA *Private,
+ IN ISCSI_SESSION *Session
+ );
+
+/**
+ Destroy an iSCSI connection.
+
+ @param[in] Conn The connection to destroy.
+**/
+VOID
+IScsiDestroyConnection (
+ IN ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Login the iSCSI session.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS The iSCSI session login procedure finished.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiSessionLogin (
+ IN ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Build and send the iSCSI login request to the iSCSI target according to
+ the current login stage.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
+ connection.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR Some kind of device error happened.
+**/
+EFI_STATUS
+IScsiSendLoginReq (
+ IN ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Receive and process the iSCSI login response.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiReceiveLoginRsp (
+ IN ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
+ The DataSegmentLength and the actual size of the net buffer containing this PDU will be
+ updated.
+
+ @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
+ be added to.
+ @param[in] Key The key name string.
+ @param[in] Value The value string.
+
+ @retval EFI_SUCCESS The key-valu pair is added to the PDU's datasegment and
+ the correspondence length fields are updated.
+ @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
+ pair.
+**/
+EFI_STATUS
+IScsiAddKeyValuePair (
+ IN OUT NET_BUF *Pdu,
+ IN CHAR8 *Key,
+ IN CHAR8 *Value
+ );
+
+/**
+ Prepare the iSCSI login request to be sent according to the current login status.
+
+ @param[in, out] Conn The connection in the iSCSI login phase.
+
+ @return The pointer to the net buffer containing the iSCSI login request built.
+ @retval Others Other errors as indicated.
+**/
+NET_BUF *
+IScsiPrepareLoginReq (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Process the iSCSI Login Response.
+
+ @param[in, out] Conn The connection on which the iSCSI login response is received.
+ @param[in, out] Pdu The iSCSI login response PDU.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is processed and all check are passed.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval EFI_MEDIA_CHANGED Target is redirected.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiProcessLoginRsp (
+ IN OUT ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ );
+
+/**
+ Updated the target information according the data received in the iSCSI
+ login response with an target redirection status.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Data The data segment which should contain the
+ TargetAddress key-value list.
+ @param[in] Len Length of the data.
+
+ @retval EFI_SUCCESS The target address is updated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_FOUND The TargetAddress key is not found.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiUpdateTargetAddress (
+ IN OUT ISCSI_SESSION *Session,
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ );
+
+/**
+ The callback function to free the net buffer list.
+
+ @param[in] Arg The opaque parameter.
+**/
+VOID
+EFIAPI
+IScsiFreeNbufList (
+ VOID *Arg
+ );
+
+/**
+ Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
+ an optional data segment. The two parts will be put into two blocks of buffers in the
+ net buffer. The digest check will be conducted in this function if needed and the digests
+ will be trimmed from the PDU buffer.
+
+ @param[in] Conn The iSCSI connection to receive data from.
+ @param[out] Pdu The received iSCSI pdu.
+ @param[in] Context The context used to describe information on the caller provided
+ buffer to receive data segment of the iSCSI pdu, it's optional.
+ @param[in] HeaderDigest Whether there will be header digest received.
+ @param[in] DataDigest Whether there will be data digest.
+ @param[in] TimeoutEvent The timeout event, it's optional.
+
+ @retval EFI_SUCCESS An iSCSI pdu is received.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiReceivePdu (
+ IN ISCSI_CONNECTION *Conn,
+ OUT NET_BUF **Pdu,
+ IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL
+ IN BOOLEAN HeaderDigest,
+ IN BOOLEAN DataDigest,
+ IN EFI_EVENT TimeoutEvent OPTIONAL
+ );
+
+/**
+ Check and get the result of the prameter negotiation.
+
+ @param[in, out] Conn The connection in iSCSI login.
+
+ @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+IScsiCheckOpParams (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Fill the oprational prameters.
+
+ @param[in] Conn The connection in iSCSI login.
+ @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
+
+ @retval EFI_SUCCESS The parmeters are filled into the iSCSI login request PDU.
+**/
+EFI_STATUS
+IScsiFillOpParams (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ );
+
+/**
+ Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
+
+ @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
+ @param[in] Len The length of the last semgnet in the PDU.
+
+ @retval EFI_SUCCESS The segment is padded or no need to pad it.
+ @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
+ padding bytes.
+**/
+EFI_STATUS
+IScsiPadSegment (
+ IN OUT NET_BUF *Pdu,
+ IN UINT32 Len
+ );
+
+/**
+ Build a key-value list from the data segment.
+
+ @param[in] Data The data segment containing the key-value pairs.
+ @param[in] Len Length of the data segment.
+
+ @return The key-value list.
+ @retval NULL Other errors as indicated.
+**/
+LIST_ENTRY *
+IScsiBuildKeyValueList (
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ );
+
+/**
+ Get the value string by the key name from the key-value list. If found,
+ the key-value entry will be removed from the list.
+
+ @param[in, out] KeyValueList The key-value list.
+ @param[in] Key The key name to find.
+
+ @return The value string.
+**/
+CHAR8 *
+IScsiGetValueByKeyFromList (
+ IN OUT LIST_ENTRY *KeyValueList,
+ IN CHAR8 *Key
+ );
+
+/**
+ Free the key-value list.
+
+ @param[in] KeyValueList The key-value list.
+**/
+VOID
+IScsiFreeKeyValueList (
+ IN LIST_ENTRY *KeyValueList
+ );
+
+/**
+ Normalize the iSCSI name according to RFC.
+
+ @param[in, out] Name The iSCSI name.
+ @param[in] Len length of the iSCSI name.
+
+ @retval EFI_SUCCESS The iSCSI name is valid and normalized.
+ @retval EFI_PROTOCOL_ERROR The iSCSI name is mal-formatted or not in the IQN format.
+**/
+EFI_STATUS
+IScsiNormalizeName (
+ IN OUT CHAR8 *Name,
+ IN UINTN Len
+ );
+
+/**
+ Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
+
+ @param[in] PassThru The EXT SCSI PASS THRU protocol.
+ @param[in] Target The target ID.
+ @param[in] Lun The LUN.
+ @param[in, out] Packet The request packet containing IO request, SCSI command
+ buffer and buffers to read/write.
+
+ @retval EFI_SUCCES The SCSI command is executed and the result is updated to
+ the Packet.
+ @retval EFI_DEVICE_ERROR Session state was not as required.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_READY The target can not accept new commands.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+IScsiExecuteScsiCommand (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ );
+
+/**
+ Reinstate the session on some error.
+
+ @param[in, out] Private The iSCSI driver data.
+
+ @retval EFI_SUCCES The session is reinstated from some error.
+ @retval Other Reinstatement failed.
+**/
+EFI_STATUS
+IScsiSessionReinstatement (
+ IN OUT ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Initialize some session parameters before login.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Recovery Whether the request is from a fresh new start or recovery.
+**/
+VOID
+IScsiSessionInit (
+ IN OUT ISCSI_SESSION *Session,
+ IN BOOLEAN Recovery
+ );
+
+/**
+ Abort the iSCSI session, that is, reset all the connection and free the
+ resources.
+
+ @param[in, out] Session The iSCSI session.
+
+ @retval EFI_SUCCES The session is aborted.
+**/
+EFI_STATUS
+IScsiSessionAbort (
+ IN OUT ISCSI_SESSION *Session
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.c
new file mode 100644
index 0000000000..e73a6dd5f7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.c
@@ -0,0 +1,487 @@
+/** @file
+ The wrap of TCP/IP Socket interface.
+
+Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IScsiImpl.h"
+
+/**
+ The common notify function associated with various Tcp4Io events.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The context.
+**/
+VOID
+EFIAPI
+Tcp4IoCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Create a TCP socket with the specified configuration data.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] ConfigData The Tcp4 configuration data.
+ @param[in] Tcp4Io The Tcp4Io.
+
+ @retval EFI_SUCCESS The TCP socket is created and configured.
+ @retval Others Failed to create the TCP socket or configure it.
+**/
+EFI_STATUS
+Tcp4IoCreateSocket (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN TCP4_IO_CONFIG_DATA *ConfigData,
+ IN TCP4_IO *Tcp4Io
+ )
+{
+ EFI_STATUS Status;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP4_CONFIG_DATA Tcp4ConfigData;
+ EFI_TCP4_OPTION ControlOption;
+ EFI_TCP4_ACCESS_POINT *AccessPoint;
+
+ Tcp4Io->Handle = NULL;
+ Tcp4Io->ConnToken.CompletionToken.Event = NULL;
+ Tcp4Io->TxToken.CompletionToken.Event = NULL;
+ Tcp4Io->RxToken.CompletionToken.Event = NULL;
+ Tcp4Io->CloseToken.CompletionToken.Event = NULL;
+ Tcp4 = NULL;
+
+ //
+ // Create the TCP4 child instance and get the TCP4 protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ &Tcp4Io->Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Tcp4Io->Handle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **)&Tcp4Io->Tcp4,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Tcp4Io->Image = Image;
+ Tcp4Io->Controller = Controller;
+ Tcp4 = Tcp4Io->Tcp4;
+
+ //
+ // Set the configuration parameters.
+ //
+ ControlOption.ReceiveBufferSize = 0x200000;
+ ControlOption.SendBufferSize = 0x200000;
+ ControlOption.MaxSynBackLog = 0;
+ ControlOption.ConnectionTimeout = 0;
+ ControlOption.DataRetries = 6;
+ ControlOption.FinTimeout = 0;
+ ControlOption.TimeWaitTimeout = 0;
+ ControlOption.KeepAliveProbes = 4;
+ ControlOption.KeepAliveTime = 0;
+ ControlOption.KeepAliveInterval = 0;
+ ControlOption.EnableNagle = FALSE;
+ ControlOption.EnableTimeStamp = FALSE;
+ ControlOption.EnableWindowScaling = TRUE;
+ ControlOption.EnableSelectiveAck = FALSE;
+ ControlOption.EnablePathMtuDiscovery = FALSE;
+
+ Tcp4ConfigData.TypeOfService = 8;
+ Tcp4ConfigData.TimeToLive = 255;
+ Tcp4ConfigData.ControlOption = &ControlOption;
+
+ AccessPoint = &Tcp4ConfigData.AccessPoint;
+
+ AccessPoint->UseDefaultAddress = FALSE;
+ AccessPoint->StationPort = 0;
+ AccessPoint->RemotePort = ConfigData->RemotePort;
+ AccessPoint->ActiveFlag = TRUE;
+
+ CopyMem (&AccessPoint->StationAddress, &ConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&AccessPoint->SubnetMask, &ConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&AccessPoint->RemoteAddress, &ConfigData->RemoteIp, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Configure the TCP4 protocol.
+ //
+ Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (!EFI_IP4_EQUAL (&ConfigData->Gateway, &mZeroIp4Addr)) {
+ //
+ // the gateway is not zero, add the default route by hand
+ //
+ Status = Tcp4->Routes (Tcp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, &ConfigData->Gateway);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+ //
+ // Create events for variuos asynchronous operations.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Tcp4IoCommonNotify,
+ &Tcp4Io->IsConnDone,
+ &Tcp4Io->ConnToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Tcp4IoCommonNotify,
+ &Tcp4Io->IsTxDone,
+ &Tcp4Io->TxToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Tcp4IoCommonNotify,
+ &Tcp4Io->IsRxDone,
+ &Tcp4Io->RxToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Tcp4IoCommonNotify,
+ &Tcp4Io->IsCloseDone,
+ &Tcp4Io->CloseToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Tcp4Io->IsTxDone = FALSE;
+ Tcp4Io->IsRxDone = FALSE;
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Tcp4Io->RxToken.CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event);
+ }
+
+ if (Tcp4Io->TxToken.CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event);
+ }
+
+ if (Tcp4Io->ConnToken.CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event);
+ }
+
+ if (Tcp4 != NULL) {
+ Tcp4->Configure (Tcp4, NULL);
+
+ gBS->CloseProtocol (
+ Tcp4Io->Handle,
+ &gEfiTcp4ProtocolGuid,
+ Image,
+ Controller
+ );
+ }
+
+ NetLibDestroyServiceChild (
+ Controller,
+ Image,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ Tcp4Io->Handle
+ );
+
+ return Status;
+}
+
+/**
+ Destroy the socket.
+
+ @param[in] Tcp4Io The Tcp4Io which wraps the socket to be destroyeds.
+**/
+VOID
+Tcp4IoDestroySocket (
+ IN TCP4_IO *Tcp4Io
+ )
+{
+ EFI_TCP4_PROTOCOL *Tcp4;
+
+ Tcp4 = Tcp4Io->Tcp4;
+
+ Tcp4->Configure (Tcp4, NULL);
+
+ gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event);
+ gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event);
+ gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event);
+
+ gBS->CloseProtocol (
+ Tcp4Io->Handle,
+ &gEfiTcp4ProtocolGuid,
+ Tcp4Io->Image,
+ Tcp4Io->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Tcp4Io->Controller,
+ Tcp4Io->Image,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ Tcp4Io->Handle
+ );
+}
+
+/**
+ Connect to the other endpoint of the TCP socket.
+
+ @param[in, out] Tcp4Io The Tcp4Io wrapping the TCP socket.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS Connect to the other endpoint of the TCP socket successfully.
+ @retval EFI_TIMEOUT Failed to connect to the other endpoint of the TCP socket in the specified time period.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+Tcp4IoConnect (
+ IN OUT TCP4_IO *Tcp4Io,
+ IN EFI_EVENT Timeout
+ )
+{
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_STATUS Status;
+
+ Tcp4Io->IsConnDone = FALSE;
+ Tcp4 = Tcp4Io->Tcp4;
+ Status = Tcp4->Connect (Tcp4, &Tcp4Io->ConnToken);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while (!Tcp4Io->IsConnDone && EFI_ERROR (gBS->CheckEvent (Timeout))) {
+ Tcp4->Poll (Tcp4);
+ }
+
+ if (!Tcp4Io->IsConnDone) {
+ Status = EFI_TIMEOUT;
+ } else {
+ Status = Tcp4Io->ConnToken.CompletionToken.Status;
+ }
+
+ return Status;
+}
+
+/**
+ Reset the socket.
+
+ @param[in, out] Tcp4Io The Tcp4Io wrapping the TCP socket.
+**/
+VOID
+Tcp4IoReset (
+ IN OUT TCP4_IO *Tcp4Io
+ )
+{
+ EFI_STATUS Status;
+ EFI_TCP4_PROTOCOL *Tcp4;
+
+ Tcp4Io->CloseToken.AbortOnClose = TRUE;
+ Tcp4Io->IsCloseDone = FALSE;
+
+ Tcp4 = Tcp4Io->Tcp4;
+ Status = Tcp4->Close (Tcp4, &Tcp4Io->CloseToken);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ while (!Tcp4Io->IsCloseDone) {
+ Tcp4->Poll (Tcp4);
+ }
+}
+
+/**
+ Transmit the Packet to the other endpoint of the socket.
+
+ @param[in] Tcp4Io The Tcp4Io wrapping the TCP socket.
+ @param[in] Packet The packet to transmit.
+
+ @retval EFI_SUCCESS The packet is trasmitted.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+Tcp4IoTransmit (
+ IN TCP4_IO *Tcp4Io,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_STATUS Status;
+
+ TxData = AllocatePool (sizeof (EFI_TCP4_TRANSMIT_DATA) + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA));
+ if (TxData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TxData->Push = TRUE;
+ TxData->Urgent = FALSE;
+ TxData->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table.
+ //
+ TxData->FragmentCount = Packet->BlockOpNum;
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) &TxData->FragmentTable[0], &TxData->FragmentCount);
+
+ Tcp4Io->TxToken.Packet.TxData = TxData;
+
+ //
+ // Trasnmit the packet.
+ //
+ Tcp4 = Tcp4Io->Tcp4;
+ Status = Tcp4->Transmit (Tcp4, &Tcp4Io->TxToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!Tcp4Io->IsTxDone) {
+ Tcp4->Poll (Tcp4);
+ }
+
+ Tcp4Io->IsTxDone = FALSE;
+
+ Status = Tcp4Io->TxToken.CompletionToken.Status;
+
+ON_EXIT:
+
+ FreePool (TxData);
+
+ return Status;
+}
+
+/**
+ Receive data from the socket.
+
+ @param[in] Tcp4Io The Tcp4Io which wraps the socket to be destroyed.
+ @param[in] Packet The buffer to hold the data copy from the soket rx buffer.
+ @param[in] AsyncMode Is this receive asyncronous or not.
+ @param[in] Timeout The time to wait for receiving the amount of data the Packet
+ can hold.
+
+ @retval EFI_SUCCESS The required amount of data is received from the socket.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate momery.
+ @retval EFI_TIMEOUT Failed to receive the required amount of data in the
+ specified time period.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+Tcp4IoReceive (
+ IN TCP4_IO *Tcp4Io,
+ IN NET_BUF *Packet,
+ IN BOOLEAN AsyncMode,
+ IN EFI_EVENT Timeout
+ )
+{
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP4_RECEIVE_DATA RxData;
+ EFI_STATUS Status;
+ NET_FRAGMENT *Fragment;
+ UINT32 FragmentCount;
+ UINT32 CurrentFragment;
+
+ FragmentCount = Packet->BlockOpNum;
+ Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
+ if (Fragment == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Build the fragment table.
+ //
+ NetbufBuildExt (Packet, Fragment, &FragmentCount);
+
+ RxData.FragmentCount = 1;
+ Tcp4Io->RxToken.Packet.RxData = &RxData;
+ CurrentFragment = 0;
+ Tcp4 = Tcp4Io->Tcp4;
+ Status = EFI_SUCCESS;
+
+ while (CurrentFragment < FragmentCount) {
+ RxData.DataLength = Fragment[CurrentFragment].Len;
+ RxData.FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len;
+ RxData.FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk;
+
+ Status = Tcp4->Receive (Tcp4, &Tcp4Io->RxToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!Tcp4Io->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ //
+ // Poll until some data is received or something error happens.
+ //
+ Tcp4->Poll (Tcp4);
+ }
+
+ if (!Tcp4Io->IsRxDone) {
+ //
+ // Timeout occurs, cancel the receive request.
+ //
+ Tcp4->Cancel (Tcp4, &Tcp4Io->RxToken.CompletionToken);
+
+ Status = EFI_TIMEOUT;
+ goto ON_EXIT;
+ } else {
+ Tcp4Io->IsRxDone = FALSE;
+ }
+
+ if (EFI_ERROR (Tcp4Io->RxToken.CompletionToken.Status)) {
+ Status = Tcp4Io->RxToken.CompletionToken.Status;
+ goto ON_EXIT;
+ }
+
+ Fragment[CurrentFragment].Len -= RxData.FragmentTable[0].FragmentLength;
+ if (Fragment[CurrentFragment].Len == 0) {
+ CurrentFragment++;
+ } else {
+ Fragment[CurrentFragment].Bulk += RxData.FragmentTable[0].FragmentLength;
+ }
+ }
+
+ON_EXIT:
+ Tcp4Io->RxToken.Packet.RxData = NULL;
+ FreePool (Fragment);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.h
new file mode 100644
index 0000000000..2b3c0b77d9
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiTcp4Io.h
@@ -0,0 +1,142 @@
+/** @file
+ iSCSI Tcp4 IO related definitions.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _ISCSI_TCP4_IO_H_
+#define _ISCSI_TCP4_IO_H_
+
+#include <Library/NetLib.h>
+#include <Protocol/Tcp4.h>
+
+typedef struct _TCP4_IO_CONFIG_DATA {
+ EFI_IPv4_ADDRESS LocalIp;
+ EFI_IPv4_ADDRESS SubnetMask;
+ EFI_IPv4_ADDRESS Gateway;
+
+ EFI_IPv4_ADDRESS RemoteIp;
+ UINT16 RemotePort;
+} TCP4_IO_CONFIG_DATA;
+
+typedef struct _TCP4_IO {
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+
+ EFI_HANDLE Handle;
+ EFI_TCP4_PROTOCOL *Tcp4;
+
+ EFI_TCP4_CONNECTION_TOKEN ConnToken;
+ EFI_TCP4_IO_TOKEN TxToken;
+ EFI_TCP4_IO_TOKEN RxToken;
+ EFI_TCP4_CLOSE_TOKEN CloseToken;
+
+ BOOLEAN IsConnDone;
+ BOOLEAN IsTxDone;
+ BOOLEAN IsRxDone;
+ BOOLEAN IsCloseDone;
+} TCP4_IO;
+
+/**
+ Create a TCP socket with the specified configuration data.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] ConfigData The Tcp4 configuration data.
+ @param[in] Tcp4Io The Tcp4Io.
+
+ @retval EFI_SUCCESS The TCP socket is created and configured.
+ @retval Others Failed to create the TCP socket or configure it.
+**/
+EFI_STATUS
+Tcp4IoCreateSocket (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN TCP4_IO_CONFIG_DATA *ConfigData,
+ IN TCP4_IO *Tcp4Io
+ );
+
+/**
+ Destroy the socket.
+
+ @param[in] Tcp4Io The Tcp4Io which wraps the socket to be destroyeds.
+**/
+VOID
+Tcp4IoDestroySocket (
+ IN TCP4_IO *Tcp4Io
+ );
+
+/**
+ Connect to the other endpoint of the TCP socket.
+
+ @param[in, out] Tcp4Io The Tcp4Io wrapping the TCP socket.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS Connect to the other endpoint of the TCP socket successfully.
+ @retval EFI_TIMEOUT Failed to connect to the other endpoint of the TCP socket in the specified time period.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+Tcp4IoConnect (
+ IN OUT TCP4_IO *Tcp4Io,
+ IN EFI_EVENT Timeout
+ );
+
+/**
+ Reset the socket.
+
+ @param[in, out] Tcp4Io The Tcp4Io wrapping the TCP socket.
+**/
+VOID
+Tcp4IoReset (
+ IN OUT TCP4_IO *Tcp4Io
+ );
+
+/**
+ Transmit the Packet to the other endpoint of the socket.
+
+ @param[in] Tcp4Io The Tcp4Io wrapping the TCP socket.
+ @param[in] Packet The packet to transmit.
+
+ @retval EFI_SUCCESS The packet is trasmitted.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+Tcp4IoTransmit (
+ IN TCP4_IO *Tcp4Io,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Receive data from the socket.
+
+ @param[in] Tcp4Io The Tcp4Io which wraps the socket to be destroyed.
+ @param[in] Packet The buffer to hold the data copy from the soket rx buffer.
+ @param[in] AsyncMode Is this receive asyncronous or not.
+ @param[in] Timeout The time to wait for receiving the amount of data the Packet
+ can hold.
+
+ @retval EFI_SUCCESS The required amount of data is received from the socket.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate momery.
+ @retval EFI_TIMEOUT Failed to receive the required amount of data in the
+ specified time period.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+Tcp4IoReceive (
+ IN TCP4_IO *Tcp4Io,
+ IN NET_BUF *Packet,
+ IN BOOLEAN AsyncMode,
+ IN EFI_EVENT Timeout
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.c b/Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.c
new file mode 100644
index 0000000000..5dc7d994d6
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.c
@@ -0,0 +1,350 @@
+/** @file
+ Implementation of MD5 algorithm.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Md5.h"
+
+CONST UINT32 Md5_Data[][2] = {
+ { 0, 1 },
+ { 1, 5 },
+ { 5, 3 },
+ { 0, 7 }
+};
+
+CONST UINT32 Md5_S[][4] = {
+ { 7, 22, 17, 12 },
+ { 5, 20, 14, 9 },
+ { 4, 23, 16 ,11 },
+ { 6, 21, 15, 10 },
+};
+
+CONST UINT32 Md5_T[] = {
+ 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
+ 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
+ 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
+ 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
+ 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
+ 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
+ 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
+ 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
+ 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
+ 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
+ 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
+ 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
+ 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
+ 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
+ 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
+ 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391
+};
+
+CONST UINT8 Md5HashPadding[] =
+{
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+//
+// ROTATE_LEFT rotates x left n bits.
+//
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+#define SA MedStates[Index2 & 3]
+#define SB MedStates[(Index2 + 1) & 3]
+#define SC MedStates[(Index2 + 2) & 3]
+#define SD MedStates[(Index2 + 3) & 3]
+
+/**
+ Tf1 is one basic MD5 transform function.
+
+ @param[in] A A 32-bit quantity.
+ @param[in] B A 32-bit quantity.
+ @param[in] C A 32-bit quantity.
+
+ @return Output was produced as a 32-bit quantity based on the
+ three 32-bit input quantity.
+**/
+UINT32
+Tf1 (
+ IN UINT32 A,
+ IN UINT32 B,
+ IN UINT32 C
+ )
+{
+ return (A & B) | (~A & C);
+}
+
+/**
+ Tf2 is one basic MD5 transform function.
+
+ @param[in] A A 32-bit quantity.
+ @param[in] B A 32-bit quantity.
+ @param[in] C A 32-bit quantity.
+
+ @return Output was produced as a 32-bit quantity based on the
+ three 32-bit input quantity.
+**/
+UINT32
+Tf2 (
+ IN UINT32 A,
+ IN UINT32 B,
+ IN UINT32 C
+ )
+{
+ return (A & C) | (B & ~C);
+}
+
+/**
+ Tf3 is one basic MD5 transform function.
+
+ @param[in] A A 32-bit quantity.
+ @param[in] B A 32-bit quantity.
+ @param[in] C A 32-bit quantity.
+
+ @return Output was produced as a 32-bit quantity based on the
+ three 32-bit input quantity.
+**/
+UINT32
+Tf3 (
+ IN UINT32 A,
+ IN UINT32 B,
+ IN UINT32 C
+ )
+{
+ return A ^ B ^ C;
+}
+
+/**
+ Tf4 is one basic MD5 transform function.
+
+ @param[in] A A 32-bit quantity.
+ @param[in] B A 32-bit quantity.
+ @param[in] C A 32-bit quantity.
+
+ @return Output was produced as a 32-bit quantity based on the
+ three 32-bit input quantity.
+**/
+UINT32
+Tf4 (
+ IN UINT32 A,
+ IN UINT32 B,
+ IN UINT32 C
+ )
+{
+ return B ^ (A | ~C);
+}
+
+typedef
+UINT32
+(*MD5_TRANSFORM_FUNC) (
+ IN UINT32 A,
+ IN UINT32 B,
+ IN UINT32 C
+ );
+
+CONST MD5_TRANSFORM_FUNC Md5_F[] = {
+ Tf1,
+ Tf2,
+ Tf3,
+ Tf4
+};
+
+/**
+ Perform the MD5 transform on 64 bytes data segment.
+
+ @param[in, out] Md5Ctx It includes the data segment for Md5 transform.
+**/
+VOID
+MD5Transform (
+ IN OUT MD5_CTX *Md5Ctx
+ )
+{
+ UINT32 Index1;
+ UINT32 Index2;
+ UINT32 MedStates[MD5_HASHSIZE >> 2];
+ UINT32 *Data;
+ UINT32 IndexD;
+ UINT32 IndexT;
+
+ Data = (UINT32 *) Md5Ctx->M;
+
+ //
+ // Copy MD5 states to MedStates
+ //
+ CopyMem (MedStates, Md5Ctx->States, MD5_HASHSIZE);
+
+ IndexT = 0;
+ for (Index1 = 0; Index1 < 4; Index1++) {
+ IndexD = Md5_Data[Index1][0];
+ for (Index2 = 16; Index2 > 0; Index2--) {
+ SA += (*Md5_F[Index1]) (SB, SC, SD) + Data[IndexD] + Md5_T[IndexT];
+ SA = ROTATE_LEFT (SA, Md5_S[Index1][Index2 & 3]);
+ SA += SB;
+
+ IndexD += Md5_Data[Index1][1];
+ IndexD &= 15;
+
+ IndexT++;
+ }
+ }
+
+ for (Index1 = 0; Index1 < 4; Index1++) {
+ Md5Ctx->States[Index1] += MedStates[Index1];
+ }
+}
+
+/**
+ Copy data segment into the M field of MD5_CTX structure for later transform.
+ If the length of data segment is larger than 64 bytes, then does the transform
+ immediately and the generated Md5 code is stored in the States field of MD5_CTX
+ data struct for later accumulation.
+ All of Md5 code generated for the sequential 64-bytes data segaments are be
+ accumulated in MD5Final() function.
+
+ @param[in, out] Md5Ctx The data structure of storing the original data
+ segment and the final result.
+ @param[in] Data The data wanted to be transformed.
+ @param[in] DataLen The length of data.
+**/
+VOID
+MD5UpdateBlock (
+ IN OUT MD5_CTX *Md5Ctx,
+ IN CONST UINT8 *Data,
+ IN UINTN DataLen
+ )
+{
+ UINTN Limit;
+
+ for (Limit = 64 - Md5Ctx->Count; DataLen >= 64 - Md5Ctx->Count; Limit = 64) {
+ CopyMem (Md5Ctx->M + Md5Ctx->Count, (VOID *)Data, Limit);
+ MD5Transform (Md5Ctx);
+
+ Md5Ctx->Count = 0;
+ Data += Limit;
+ DataLen -= Limit;
+ }
+
+ CopyMem (Md5Ctx->M + Md5Ctx->Count, (VOID *)Data, DataLen);
+ Md5Ctx->Count += DataLen;
+}
+
+/**
+ Initialize four 32-bits chaining variables and use them to do the Md5 transform.
+
+ @param[out] Md5Ctx The data structure of Md5.
+
+ @retval EFI_SUCCESS Initialization is ok.
+**/
+EFI_STATUS
+MD5Init (
+ OUT MD5_CTX *Md5Ctx
+ )
+{
+ ZeroMem (Md5Ctx, sizeof (*Md5Ctx));
+
+ //
+ // Set magic initialization constants.
+ //
+ Md5Ctx->States[0] = 0x67452301;
+ Md5Ctx->States[1] = 0xefcdab89;
+ Md5Ctx->States[2] = 0x98badcfe;
+ Md5Ctx->States[3] = 0x10325476;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ the external interface of Md5 algorithm
+
+ @param[in, out] Md5Ctx The data structure of storing the original data
+ segment and the final result.
+ @param[in] Data The data wanted to be transformed.
+ @param[in] DataLen The length of data.
+
+ @retval EFI_SUCCESS The transform is ok.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+MD5Update (
+ IN OUT MD5_CTX *Md5Ctx,
+ IN VOID *Data,
+ IN UINTN DataLen
+ )
+{
+ if (EFI_ERROR (Md5Ctx->Status)) {
+ return Md5Ctx->Status;
+ }
+
+ MD5UpdateBlock (Md5Ctx, (CONST UINT8 *) Data, DataLen);
+ Md5Ctx->Length += DataLen;
+ return EFI_SUCCESS;
+}
+
+/**
+ Accumulate the MD5 value of every data segment and generate the finial
+ result according to MD5 algorithm.
+
+ @param[in, out] Md5Ctx The data structure of storing the original data
+ segment and the final result.
+ @param[out] HashVal The final 128-bits output.
+
+ @retval EFI_SUCCESS The transform is ok.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+MD5Final (
+ IN OUT MD5_CTX *Md5Ctx,
+ OUT UINT8 *HashVal
+ )
+{
+ UINTN PadLength;
+
+ if (Md5Ctx->Status == EFI_ALREADY_STARTED) {
+ //
+ // Store Hashed value & Zeroize sensitive context information.
+ //
+ CopyMem (HashVal, (UINT8 *) Md5Ctx->States, MD5_HASHSIZE);
+ ZeroMem ((UINT8 *)Md5Ctx, sizeof (*Md5Ctx));
+
+ return EFI_SUCCESS;
+ }
+
+ if (EFI_ERROR (Md5Ctx->Status)) {
+ return Md5Ctx->Status;
+ }
+
+ PadLength = Md5Ctx->Count >= 56 ? 120 : 56;
+ PadLength -= Md5Ctx->Count;
+ MD5UpdateBlock (Md5Ctx, Md5HashPadding, PadLength);
+ Md5Ctx->Length = LShiftU64 (Md5Ctx->Length, 3);
+ MD5UpdateBlock (Md5Ctx, (CONST UINT8 *) &Md5Ctx->Length, 8);
+
+ ZeroMem (Md5Ctx->M, sizeof (Md5Ctx->M));
+ Md5Ctx->Length = 0;
+ Md5Ctx->Status = EFI_ALREADY_STARTED;
+ return MD5Final (Md5Ctx, HashVal);
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.h b/Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.h
new file mode 100644
index 0000000000..8de4e869b4
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/Md5.h
@@ -0,0 +1,80 @@
+/** @file
+ Header file for Md5.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _MD5_H_
+#define _MD5_H_
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/NetLib.h>
+
+#define MD5_HASHSIZE 16
+
+typedef struct _MD5_CTX {
+ EFI_STATUS Status;
+ UINT64 Length;
+ UINT32 States[MD5_HASHSIZE / sizeof (UINT32)];
+ UINT8 M[64];
+ UINTN Count;
+} MD5_CTX;
+
+/**
+ Initialize four 32-bits chaining variables and use them to do the Md5 transform.
+
+ @param[out] Md5Ctx The data structure of Md5.
+
+ @retval EFI_SUCCESS Initialization is ok.
+**/
+EFI_STATUS
+MD5Init (
+ OUT MD5_CTX *Md5Ctx
+ );
+
+/**
+ the external interface of Md5 algorithm
+
+ @param[in, out] Md5Ctx The data structure of storing the original data
+ segment and the final result.
+ @param[in] Data The data wanted to be transformed.
+ @param[in] DataLen The length of data.
+
+ @retval EFI_SUCCESS The transform is ok.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+MD5Update (
+ IN OUT MD5_CTX *Md5Ctx,
+ IN VOID *Data,
+ IN UINTN DataLen
+ );
+
+/**
+ Accumulate the MD5 value of every data segment and generate the finial
+ result according to MD5 algorithm.
+
+ @param[in, out] Md5Ctx The data structure of storing the original data
+ segment and the final result.
+ @param[out] HashVal The final 128-bits output.
+
+ @retval EFI_SUCCESS The transform is ok.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+MD5Final (
+ IN OUT MD5_CTX *Md5Ctx,
+ OUT UINT8 *HashVal
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c
new file mode 100644
index 0000000000..2ccfb3cc1c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c
@@ -0,0 +1,434 @@
+/** @file
+
+Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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
+Ip4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+Ip4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName = {
+ Ip4ComponentNameGetDriverName,
+ Ip4ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip4ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip4ComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp4DriverNameTable[] = {
+ {
+ "eng;en",
+ L"IP4 Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gIp4ControllerNameTable = NULL;
+
+/**
+ 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
+Ip4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mIp4DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gIp4ComponentName)
+ );
+
+}
+
+/**
+ Update the component name for the IP4 child handle.
+
+ @param Ip4[in] A pointer to the EFI_IP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_IP4_PROTOCOL *Ip4
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ EFI_IP4_MODE_DATA Ip4ModeData;
+
+ if (Ip4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer as:
+ // IPv4 (SrcIP=127.0.0.1, DestIP=127.0.0.1)
+ //
+ Status = Ip4->GetModeData (Ip4, &Ip4ModeData, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!Ip4ModeData.IsStarted || !Ip4ModeData.IsConfigured) {
+ UnicodeSPrint (HandleName, sizeof (HandleName), L"IPv4 (Not started)");
+ } else {
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"IPv4 (SrcIP=%d.%d.%d.%d)",
+ Ip4ModeData.ConfigData.StationAddress.Addr[0],
+ Ip4ModeData.ConfigData.StationAddress.Addr[1],
+ Ip4ModeData.ConfigData.StationAddress.Addr[2],
+ Ip4ModeData.ConfigData.StationAddress.Addr[3]
+ );
+ }
+
+ if (gIp4ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gIp4ControllerNameTable);
+ gIp4ControllerNameTable = NULL;
+ }
+ Status = AddUnicodeString2 (
+ "eng",
+ gIp4ComponentName.SupportedLanguages,
+ &gIp4ControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gIp4ComponentName2.SupportedLanguages,
+ &gIp4ControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+Ip4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_PROTOCOL *Ip4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **)&Ip4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Ip4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gIp4ControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gIp4ComponentName)
+ );
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c
new file mode 100644
index 0000000000..7c7d182073
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c
@@ -0,0 +1,329 @@
+/** @file
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Return the cast type (Unicast/Boradcast) specific to an
+ interface. All the addresses are host byte ordered.
+
+ @param[in] IpAddr The IP address to classify in host byte order
+ @param[in] IpIf The interface that IpAddr received from
+
+ @return The cast type of this IP address specific to the interface.
+ @retval IP4_LOCAL_HOST The IpAddr equals to the interface's address
+ @retval IP4_SUBNET_BROADCAST The IpAddr is a directed subnet boradcast to the
+ interface
+ @retval IP4_NET_BROADCAST The IpAddr is a network broadcast to the interface
+ @retval 0 Otherwise.
+
+**/
+INTN
+Ip4GetNetCast (
+ IN IP4_ADDR IpAddr,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ if (IpAddr == IpIf->Ip) {
+ return IP4_LOCAL_HOST;
+
+ } else if (IpAddr == IpIf->SubnetBrdcast) {
+ return IP4_SUBNET_BROADCAST;
+
+ } else if (IpAddr == IpIf->NetBrdcast) {
+ return IP4_NET_BROADCAST;
+
+ }
+
+ return 0;
+}
+
+
+/**
+ Find the cast type of the packet related to the local host.
+ This isn't the same as link layer cast type. For example, DHCP
+ server may send local broadcast to the local unicast MAC.
+
+ @param[in] IpSb The IP4 service binding instance that received the
+ packet
+ @param[in] Dst The destination address in the packet (host byte
+ order)
+ @param[in] Src The source address in the packet (host byte order)
+
+ @return The cast type for the Dst, it will return on the first non-promiscuous
+ cast type to a configured interface. If the packet doesn't match any of
+ the interface, multicast address and local broadcast address are checked.
+
+**/
+INTN
+Ip4GetHostCast (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+ INTN Type;
+ INTN Class;
+
+ Type = 0;
+
+ if (IpSb->MnpConfigData.EnablePromiscuousReceive) {
+ Type = IP4_PROMISCUOUS;
+ }
+
+ //
+ // Go through the interface list of the IP service, most likely.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ //
+ // Skip the unconfigured interface and invalid source address:
+ // source address can't be broadcast.
+ //
+ if (!IpIf->Configured || IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {
+ continue;
+ }
+
+ if ((Class = Ip4GetNetCast (Dst, IpIf)) > Type) {
+ return Class;
+ }
+ }
+
+ //
+ // If it is local broadcast address. The source address must
+ // be a unicast address on one of the direct connected network.
+ // If it is a multicast address, accept it only if we are in
+ // the group.
+ //
+ if (Dst == IP4_ALLONE_ADDRESS) {
+ IpIf = Ip4FindNet (IpSb, Src);
+
+ if (IpIf != NULL && !IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {
+ return IP4_LOCAL_BROADCAST;
+ }
+
+ } else if (IP4_IS_MULTICAST (Dst) && Ip4FindGroup (&IpSb->IgmpCtrl, Dst) != NULL) {
+ return IP4_MULTICAST;
+ }
+
+ return Type;
+}
+
+
+/**
+ Find an interface whose configured IP address is Ip.
+
+ @param[in] IpSb The IP4 service binding instance
+ @param[in] Ip The Ip address (host byte order) to find
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindInterface (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && (IpIf->Ip == Ip)) {
+ return IpIf;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Find an interface that Ip is on that connected network.
+
+ @param[in] IpSb The IP4 service binding instance
+ @param[in] Ip The Ip address (host byte order) to find
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindNet (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && IP4_NET_EQUAL (Ip, IpIf->Ip, IpIf->SubnetMask)) {
+ return IpIf;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Find an interface of the service with the same Ip/Netmask pair.
+
+ @param[in] IpSb Ip4 service binding instance
+ @param[in] Ip The Ip adress to find (host byte order)
+ @param[in] Netmask The network to find (host byte order)
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindStationAddress (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && (IpIf->Ip == Ip) && (IpIf->SubnetMask == Netmask)) {
+ return IpIf;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Get the MAC address for a multicast IP address. Call
+ Mnp's McastIpToMac to find the MAC address in stead of
+ hard code the NIC to be Ethernet.
+
+ @param[in] Mnp The Mnp instance to get the MAC address.
+ @param[in] Multicast The multicast IP address to translate.
+ @param[out] Mac The buffer to hold the translated address.
+
+ @retval EFI_SUCCESS if the multicast IP is successfully translated to a
+ multicast MAC address.
+ @retval other Otherwise some error.
+
+**/
+EFI_STATUS
+Ip4GetMulticastMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN IP4_ADDR Multicast,
+ OUT EFI_MAC_ADDRESS *Mac
+ )
+{
+ EFI_IP_ADDRESS EfiIp;
+
+ EFI_IP4 (EfiIp.v4) = HTONL (Multicast);
+ return Mnp->McastIpToMac (Mnp, FALSE, &EfiIp, Mac);
+}
+
+
+/**
+ Convert the multibyte field in IP header's byter order.
+ In spite of its name, it can also be used to convert from
+ host to network byte order.
+
+ @param[in] Head The IP head to convert
+
+ @return Point to the converted IP head
+
+**/
+IP4_HEAD *
+Ip4NtohHead (
+ IN IP4_HEAD *Head
+ )
+{
+ Head->TotalLen = NTOHS (Head->TotalLen);
+ Head->Id = NTOHS (Head->Id);
+ Head->Fragment = NTOHS (Head->Fragment);
+ Head->Src = NTOHL (Head->Src);
+ Head->Dst = NTOHL (Head->Dst);
+
+ return Head;
+}
+
+
+/**
+ Validate that Ip/Netmask pair is OK to be used as station
+ address. Only continuous netmasks are supported. and check
+ that StationAddress is a unicast address on the newtwork.
+
+ @param[in] Ip The IP address to validate.
+ @param[in] Netmask The netmaks of the IP.
+
+ @retval TRUE The Ip/Netmask pair is valid.
+ @retval FALSE The Ip/Netmask pair is invalid.
+
+**/
+BOOLEAN
+Ip4StationAddressValid (
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ )
+{
+ IP4_ADDR NetBrdcastMask;
+ INTN Len;
+ INTN Type;
+
+ //
+ // Only support the station address with 0.0.0.0/0 to enable DHCP client.
+ //
+ if (Netmask == IP4_ALLZERO_ADDRESS) {
+ return (BOOLEAN) (Ip == IP4_ALLZERO_ADDRESS);
+ }
+
+ //
+ // Only support the continuous net masks
+ //
+ if ((Len = NetGetMaskLength (Netmask)) == (IP4_MASK_MAX + 1)) {
+ return FALSE;
+ }
+
+ //
+ // Station address can't be class D or class E address
+ //
+ if ((Type = NetGetIpClass (Ip)) > IP4_ADDR_CLASSC) {
+ return FALSE;
+ }
+
+ //
+ // Station address can't be subnet broadcast/net broadcast address
+ //
+ if ((Ip == (Ip & Netmask)) || (Ip == (Ip | ~Netmask))) {
+ return FALSE;
+ }
+
+ NetBrdcastMask = gIp4AllMasks[MIN (Len, Type << 3)];
+
+ if (Ip == (Ip | ~NetBrdcastMask)) {
+ return FALSE;
+ }
+
+ return TRUE;
+} \ No newline at end of file
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
new file mode 100644
index 0000000000..9689f373cf
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
@@ -0,0 +1,223 @@
+/** @file
+ Common definition for IP4.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_COMMON_H__
+#define __EFI_IP4_COMMON_H__
+
+typedef struct _IP4_INTERFACE IP4_INTERFACE;
+typedef struct _IP4_PROTOCOL IP4_PROTOCOL;
+typedef struct _IP4_SERVICE IP4_SERVICE;
+
+#define IP4_ETHER_PROTO 0x0800
+
+//
+// The packet is received as link level broadcast/multicast/promiscuous.
+//
+#define IP4_LINK_BROADCAST 0x00000001
+#define IP4_LINK_MULTICAST 0x00000002
+#define IP4_LINK_PROMISC 0x00000004
+
+//
+// IP4 address cast type classfication. Keep it true that any
+// type bigger than or equal to LOCAL_BROADCAST is broadcast.
+//
+#define IP4_PROMISCUOUS 1
+#define IP4_LOCAL_HOST 2
+#define IP4_MULTICAST 3
+#define IP4_LOCAL_BROADCAST 4 // Destination is 255.255.255.255
+#define IP4_SUBNET_BROADCAST 5
+#define IP4_NET_BROADCAST 6
+
+//
+// IP4 header flags
+//
+#define IP4_HEAD_DF_MASK 0x4000
+#define IP4_HEAD_MF_MASK 0x2000
+#define IP4_HEAD_OFFSET_MASK 0x1fff
+
+#define IP4_ALLZERO_ADDRESS 0x00000000u
+#define IP4_ALLONE_ADDRESS 0xFFFFFFFFu
+#define IP4_ALLSYSTEM_ADDRESS 0xE0000001u
+#define IP4_ALLROUTER_ADDRESS 0xE0000002u
+
+///
+/// Compose the fragment field to be used in the IP4 header.
+///
+#define IP4_HEAD_FRAGMENT_FIELD(Df, Mf, Offset) \
+ ((UINT16)(((Df) ? 0x4000 : 0) | ((Mf) ? 0x2000 : 0) | (((Offset) >> 3) & 0x1fff)))
+
+#define IP4_LAST_FRAGMENT(FragmentField) \
+ (((FragmentField) & IP4_HEAD_MF_MASK) == 0)
+
+#define IP4_FIRST_FRAGMENT(FragmentField) \
+ ((BOOLEAN)(((FragmentField) & IP4_HEAD_OFFSET_MASK) == 0))
+
+#define IP4_DO_NOT_FRAGMENT(FragmentField) \
+ ((BOOLEAN)(((FragmentField) & IP4_HEAD_DF_MASK) == IP4_HEAD_DF_MASK))
+
+#define IP4_IS_BROADCAST(CastType) ((CastType) >= IP4_LOCAL_BROADCAST)
+
+///
+/// Conver the Microsecond to second. IP transmit/receive time is
+/// in the unit of microsecond. IP ticks once per second.
+///
+#define IP4_US_TO_SEC(Us) (((Us) + 999999) / 1000000)
+
+/**
+ Return the cast type (Unicast/Boradcast) specific to an
+ interface. All the addresses are host byte ordered.
+
+ @param[in] IpAddr The IP address to classify in host byte order
+ @param[in] IpIf The interface that IpAddr received from
+
+ @return The cast type of this IP address specific to the interface.
+ @retval IP4_LOCAL_HOST The IpAddr equals to the interface's address
+ @retval IP4_SUBNET_BROADCAST The IpAddr is a directed subnet boradcast to the
+ interface
+ @retval IP4_NET_BROADCAST The IpAddr is a network broadcast to the interface
+ @retval 0 Otherwise.
+
+**/
+INTN
+Ip4GetNetCast (
+ IN IP4_ADDR IpAddr,
+ IN IP4_INTERFACE *IpIf
+ );
+
+/**
+ Find the cast type of the packet related to the local host.
+ This isn't the same as link layer cast type. For example, DHCP
+ server may send local broadcast to the local unicast MAC.
+
+ @param[in] IpSb The IP4 service binding instance that received the
+ packet
+ @param[in] Dst The destination address in the packet (host byte
+ order)
+ @param[in] Src The source address in the packet (host byte order)
+
+ @return The cast type for the Dst, it will return on the first non-promiscuous
+ cast type to a configured interface. If the packet doesn't match any of
+ the interface, multicast address and local broadcast address are checked.
+
+**/
+INTN
+Ip4GetHostCast (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src
+ );
+
+/**
+ Find an interface whose configured IP address is Ip.
+
+ @param[in] IpSb The IP4 service binding instance
+ @param[in] Ip The Ip address (host byte order) to find
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindInterface (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip
+ );
+
+/**
+ Find an interface that Ip is on that connected network.
+
+ @param[in] IpSb The IP4 service binding instance
+ @param[in] Ip The Ip address (host byte order) to find
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindNet (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip
+ );
+
+/**
+ Find an interface of the service with the same Ip/Netmask pair.
+
+ @param[in] IpSb Ip4 service binding instance
+ @param[in] Ip The Ip adress to find (host byte order)
+ @param[in] Netmask The network to find (host byte order)
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindStationAddress (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ );
+
+/**
+ Get the MAC address for a multicast IP address. Call
+ Mnp's McastIpToMac to find the MAC address in stead of
+ hard code the NIC to be Ethernet.
+
+ @param[in] Mnp The Mnp instance to get the MAC address.
+ @param[in] Multicast The multicast IP address to translate.
+ @param[out] Mac The buffer to hold the translated address.
+
+ @retval EFI_SUCCESS if the multicast IP is successfully translated to a
+ multicast MAC address.
+ @retval other Otherwise some error.
+
+**/
+EFI_STATUS
+Ip4GetMulticastMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN IP4_ADDR Multicast,
+ OUT EFI_MAC_ADDRESS *Mac
+ );
+
+/**
+ Convert the multibyte field in IP header's byter order.
+ In spite of its name, it can also be used to convert from
+ host to network byte order.
+
+ @param[in] Head The IP head to convert
+
+ @return Point to the converted IP head
+
+**/
+IP4_HEAD *
+Ip4NtohHead (
+ IN IP4_HEAD *Head
+ );
+
+
+/**
+ Validate that Ip/Netmask pair is OK to be used as station
+ address. Only continuous netmasks are supported. and check
+ that StationAddress is a unicast address on the newtwork.
+
+ @param[in] Ip The IP address to validate.
+ @param[in] Netmask The netmaks of the IP.
+
+ @retval TRUE The Ip/Netmask pair is valid.
+ @retval FALSE The Ip/Netmask pair is invalid.
+
+**/
+BOOLEAN
+Ip4StationAddressValid (
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2.vfr b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2.vfr
new file mode 100644
index 0000000000..a18db22846
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2.vfr
@@ -0,0 +1,100 @@
+/** @file
+ Vfr file for IP4Dxe.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#include "Ip4NvData.h"
+
+#define EFI_NETWORK_DEVICE_CLASS 0x04
+
+formset
+ guid = IP4_CONFIG2_NVDATA_GUID,
+ title = STRING_TOKEN(STR_IP4_CONFIG2_FORM_TITLE),
+ help = STRING_TOKEN(STR_IP4_CONFIG2_FORM_HELP),
+ class = EFI_NETWORK_DEVICE_CLASS,
+ subclass = 0x03,
+
+ varstore IP4_CONFIG2_IFR_NVDATA,
+ name = IP4_CONFIG2_IFR_NVDATA,
+ guid = IP4_CONFIG2_NVDATA_GUID;
+
+ form formid = FORMID_MAIN_FORM,
+ title = STRING_TOKEN(STR_IP4_DEVICE_FORM_TITLE);
+
+ checkbox varid = IP4_CONFIG2_IFR_NVDATA.Configure,
+ prompt = STRING_TOKEN(STR_IP4_CONFIGURE),
+ help = STRING_TOKEN(STR_IP4_CONFIGURE),
+ flags = INTERACTIVE,
+ key = KEY_ENABLE,
+ endcheckbox;
+
+ suppressif ideqval IP4_CONFIG2_IFR_NVDATA.Configure == 0x00;
+
+ checkbox varid = IP4_CONFIG2_IFR_NVDATA.DhcpEnable,
+ prompt = STRING_TOKEN(STR_IP4_ENABLE_DHCP),
+ help = STRING_TOKEN(STR_IP4_ENABLE_DHCP),
+ flags = INTERACTIVE,
+ key = KEY_DHCP_ENABLE,
+ endcheckbox;
+ endif;
+
+ suppressif ideqval IP4_CONFIG2_IFR_NVDATA.DhcpEnable == 0x01 OR ideqval IP4_CONFIG2_IFR_NVDATA.Configure == 0x00;
+
+ string varid = IP4_CONFIG2_IFR_NVDATA.StationAddress,
+ prompt = STRING_TOKEN(STR_IP4_LOCAL_IP_ADDRESS),
+ help = STRING_TOKEN(STR_IP4_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_LOCAL_IP,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ string varid = IP4_CONFIG2_IFR_NVDATA.SubnetMask,
+ prompt = STRING_TOKEN(STR_IP4_LOCAL_MASK),
+ help = STRING_TOKEN(STR_IP4_MASK_HELP),
+ flags = INTERACTIVE,
+ key = KEY_SUBNET_MASK,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ string varid = IP4_CONFIG2_IFR_NVDATA.GatewayAddress,
+ prompt = STRING_TOKEN(STR_IP4_LOCAL_GATEWAY),
+ help = STRING_TOKEN(STR_IP4_GATEWAY_HELP),
+ flags = INTERACTIVE,
+ key = KEY_GATE_WAY,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ string varid = IP4_CONFIG2_IFR_NVDATA.DnsAddress,
+ prompt = STRING_TOKEN(STR_IP4_LOCAL_DNS),
+ help = STRING_TOKEN(STR_IP4_DNS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_DNS,
+ minsize = IP_MIN_SIZE,
+ maxsize = ADDRESS_STR_MAX_SIZE,
+ endstring;
+
+ endif;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ text
+ help = STRING_TOKEN(STR_SAVE_CHANGES),
+ text = STRING_TOKEN(STR_SAVE_CHANGES),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_CHANGES;
+
+ endform;
+
+endformset;
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c
new file mode 100644
index 0000000000..f4dfbb6e31
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c
@@ -0,0 +1,2086 @@
+/** @file
+ The implementation of EFI IPv4 Configuration II Protocol.
+
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+LIST_ENTRY mIp4Config2InstanceList = {&mIp4Config2InstanceList, &mIp4Config2InstanceList};
+
+/**
+ The event process routine when the DHCPv4 service binding protocol is installed
+ in the system.
+
+ @param[in] Event Not used.
+ @param[in] Context Pointer to the IP4 config2 instance data.
+
+**/
+VOID
+EFIAPI
+Ip4Config2OnDhcp4SbInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Destroy the Dhcp4 child in IP4_CONFIG2_INSTANCE and release the resources.
+
+ @param[in, out] Instance The buffer of IP4 config2 instance to be freed.
+
+ @retval EFI_SUCCESS The child was successfully destroyed.
+ @retval Others Failed to destroy the child.
+
+**/
+EFI_STATUS
+Ip4Config2DestroyDhcp4 (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ IP4_SERVICE *IpSb;
+ EFI_STATUS Status;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+
+ Dhcp4 = Instance->Dhcp4;
+ ASSERT (Dhcp4 != NULL);
+
+ Dhcp4->Stop (Dhcp4);
+ Dhcp4->Configure (Dhcp4, NULL);
+ Instance->Dhcp4 = NULL;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ //
+ // Close DHCPv4 protocol and destroy the child.
+ //
+ Status = gBS->CloseProtocol (
+ Instance->Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = NetLibDestroyServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Instance->Dhcp4Handle
+ );
+
+ Instance->Dhcp4Handle = NULL;
+
+ return Status;
+}
+
+/**
+ Update the current policy to NewPolicy. During the transition
+ period, the default router list
+ and address list in all interfaces will be released.
+
+ @param[in] IpSb The IP4 service binding instance.
+ @param[in] NewPolicy The new policy to be updated to.
+
+**/
+VOID
+Ip4Config2OnPolicyChanged (
+ IN IP4_SERVICE *IpSb,
+ IN EFI_IP4_CONFIG2_POLICY NewPolicy
+ )
+{
+ IP4_INTERFACE *IpIf;
+ IP4_ROUTE_TABLE *RouteTable;
+
+ //
+ // Currently there are only two policies: static and dhcp. Regardless of
+ // what transition is going on, i.e., static -> dhcp and dhcp ->
+ // static, we have to free default router table and all addresses.
+ //
+
+ if (IpSb->DefaultInterface != NULL) {
+ if (IpSb->DefaultRouteTable != NULL) {
+ Ip4FreeRouteTable (IpSb->DefaultRouteTable);
+ IpSb->DefaultRouteTable = NULL;
+ }
+
+ Ip4CancelReceive (IpSb->DefaultInterface);
+
+ Ip4FreeInterface (IpSb->DefaultInterface, NULL);
+ IpSb->DefaultInterface = NULL;
+ }
+
+ Ip4CleanAssembleTable (&IpSb->Assemble);
+
+ //
+ // Create new default interface and route table.
+ //
+ IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);
+ if (IpIf == NULL) {
+ return ;
+ }
+
+ RouteTable = Ip4CreateRouteTable ();
+ if (RouteTable == NULL) {
+ Ip4FreeInterface (IpIf, NULL);
+ return ;
+ }
+
+ IpSb->DefaultInterface = IpIf;
+ InsertHeadList (&IpSb->Interfaces, &IpIf->Link);
+ IpSb->DefaultRouteTable = RouteTable;
+ Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb);
+
+ if (IpSb->State == IP4_SERVICE_CONFIGED || IpSb->State == IP4_SERVICE_STARTED) {
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+ }
+
+ //
+ // Start the dhcp configuration.
+ //
+ if (NewPolicy == Ip4Config2PolicyDhcp) {
+ Ip4StartAutoConfig (&IpSb->Ip4Config2Instance);
+ }
+
+}
+
+/**
+ Signal the registered event. It is the callback routine for NetMapIterate.
+
+ @param[in] Map Points to the list of registered event.
+ @param[in] Item The registered event.
+ @param[in] Arg Not used.
+
+ @retval EFI_SUCCESS The event was signaled successfully.
+**/
+EFI_STATUS
+EFIAPI
+Ip4Config2SignalEvent (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ )
+{
+ gBS->SignalEvent ((EFI_EVENT) Item->Key);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read the configuration data from variable storage according to the VarName and
+ gEfiIp4Config2ProtocolGuid. It checks the integrity of variable data. If the
+ data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the
+ configuration data to IP4_CONFIG2_INSTANCE.
+
+ @param[in] VarName The pointer to the variable name
+ @param[in, out] Instance The pointer to the IP4 config2 instance data.
+
+ @retval EFI_NOT_FOUND The variable can not be found or already corrupted.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_SUCCESS The configuration data was retrieved successfully.
+
+**/
+EFI_STATUS
+Ip4Config2ReadConfigData (
+ IN CHAR16 *VarName,
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ UINTN VarSize;
+ IP4_CONFIG2_VARIABLE *Variable;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+ UINTN Index;
+ IP4_CONFIG2_DATA_RECORD DataRecord;
+ CHAR8 *Data;
+
+ //
+ // Try to read the configuration variable.
+ //
+ VarSize = 0;
+ Status = gRT->GetVariable (
+ VarName,
+ &gEfiIp4Config2ProtocolGuid,
+ NULL,
+ &VarSize,
+ NULL
+ );
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate buffer and read the config variable.
+ //
+ Variable = AllocatePool (VarSize);
+ if (Variable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gRT->GetVariable (
+ VarName,
+ &gEfiIp4Config2ProtocolGuid,
+ NULL,
+ &VarSize,
+ Variable
+ );
+ if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) {
+ //
+ // GetVariable still error or the variable is corrupted.
+ // Fall back to the default value.
+ //
+ FreePool (Variable);
+
+ //
+ // Remove the problematic variable and return EFI_NOT_FOUND, a new
+ // variable will be set again.
+ //
+ gRT->SetVariable (
+ VarName,
+ &gEfiIp4Config2ProtocolGuid,
+ IP4_CONFIG2_VARIABLE_ATTRIBUTE,
+ 0,
+ NULL
+ );
+
+ return EFI_NOT_FOUND;
+ }
+
+
+ for (Index = 0; Index < Variable->DataRecordCount; Index++) {
+
+ CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord));
+
+ DataItem = &Instance->DataItem[DataRecord.DataType];
+ if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) &&
+ (DataItem->DataSize != DataRecord.DataSize)
+ ) {
+ //
+ // Perhaps a corrupted data record...
+ //
+ continue;
+ }
+
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {
+ //
+ // This data item has variable length data.
+ //
+ DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize);
+ if (DataItem->Data.Ptr == NULL) {
+ //
+ // no memory resource
+ //
+ continue;
+ }
+ }
+
+ Data = (CHAR8 *) Variable + DataRecord.Offset;
+ CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize);
+
+ DataItem->DataSize = DataRecord.DataSize;
+ DataItem->Status = EFI_SUCCESS;
+ }
+
+ FreePool (Variable);
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Write the configuration data from IP4_CONFIG2_INSTANCE to variable storage.
+
+ @param[in] VarName The pointer to the variable name.
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_SUCCESS The configuration data is written successfully.
+
+**/
+EFI_STATUS
+Ip4Config2WriteConfigData (
+ IN CHAR16 *VarName,
+ IN IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ UINTN Index;
+ UINTN VarSize;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+ IP4_CONFIG2_VARIABLE *Variable;
+ IP4_CONFIG2_DATA_RECORD *DataRecord;
+ CHAR8 *Heap;
+ EFI_STATUS Status;
+
+ VarSize = sizeof (IP4_CONFIG2_VARIABLE) - sizeof (IP4_CONFIG2_DATA_RECORD);
+
+ for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) {
+
+ DataItem = &Instance->DataItem[Index];
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {
+
+ VarSize += sizeof (IP4_CONFIG2_DATA_RECORD) + DataItem->DataSize;
+ }
+ }
+
+ Variable = AllocatePool (VarSize);
+ if (Variable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Heap = (CHAR8 *) Variable + VarSize;
+ Variable->DataRecordCount = 0;
+
+ for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) {
+
+ DataItem = &Instance->DataItem[Index];
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {
+
+ Heap -= DataItem->DataSize;
+ CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize);
+
+ DataRecord = &Variable->DataRecord[Variable->DataRecordCount];
+ DataRecord->DataType = (EFI_IP4_CONFIG2_DATA_TYPE) Index;
+ DataRecord->DataSize = (UINT32) DataItem->DataSize;
+ DataRecord->Offset = (UINT16) (Heap - (CHAR8 *) Variable);
+
+ Variable->DataRecordCount++;
+ }
+ }
+
+ Variable->Checksum = 0;
+ Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize);
+
+ Status = gRT->SetVariable (
+ VarName,
+ &gEfiIp4Config2ProtocolGuid,
+ IP4_CONFIG2_VARIABLE_ATTRIBUTE,
+ VarSize,
+ Variable
+ );
+
+ FreePool (Variable);
+
+ return Status;
+}
+
+
+/**
+ Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of GetModeData.
+ The EFI_IP4_ROUTE_TABLE is clumsy to use in the internal operation of the
+ IP4 driver.
+
+ @param[in] IpSb The IP4 service binding instance.
+ @param[out] Table The built IP4 route table.
+
+ @retval EFI_SUCCESS The route table is successfully build
+ @retval EFI_NOT_FOUND Failed to allocate the memory for the rotue table.
+
+**/
+EFI_STATUS
+Ip4Config2BuildDefaultRouteTable (
+ IN IP4_SERVICE *IpSb,
+ OUT EFI_IP4_ROUTE_TABLE *Table
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_ROUTE_ENTRY *RtEntry;
+ UINT32 Count;
+ INT32 Index;
+
+ if (IpSb->DefaultRouteTable == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Count = IpSb->DefaultRouteTable->TotalNum;
+
+ if (Count == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Copy the route entry to EFI route table. Keep the order of
+ // route entry copied from most specific to default route. That
+ // is, interlevel the route entry from the instance's route area
+ // and those from the default route table's route area.
+ //
+ Count = 0;
+
+ for (Index = IP4_MASK_MAX; Index >= 0; Index--) {
+
+ NET_LIST_FOR_EACH (Entry, &(IpSb->DefaultRouteTable->RouteArea[Index])) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ EFI_IP4 (Table[Count].SubnetAddress) = HTONL (RtEntry->Dest & RtEntry->Netmask);
+ EFI_IP4 (Table[Count].SubnetMask) = HTONL (RtEntry->Netmask);
+ EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop);
+
+ Count++;
+ }
+
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The event process routine when the DHCPv4 service binding protocol is installed
+ in the system.
+
+ @param[in] Event Not used.
+ @param[in] Context The pointer to the IP4 config2 instance data.
+
+**/
+VOID
+EFIAPI
+Ip4Config2OnDhcp4SbInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_CONFIG2_INSTANCE *Instance;
+
+ Instance = (IP4_CONFIG2_INSTANCE *) Context;
+
+ if ((Instance->Dhcp4Handle != NULL) || (Instance->Policy != Ip4Config2PolicyDhcp)) {
+ //
+ // The DHCP4 child is already created or the policy is no longer DHCP.
+ //
+ return ;
+ }
+
+ Ip4StartAutoConfig (Instance);
+}
+
+/**
+ Set the station address and subnetmask for the default interface.
+
+ @param[in] IpSb The pointer to the IP4 service binding instance.
+ @param[in] StationAddress Ip address to be set.
+ @param[in] SubnetMask Subnet to be set.
+
+ @retval EFI_SUCCESS Set default address successful.
+ @retval Others Some errors occur in setting.
+
+**/
+EFI_STATUS
+Ip4Config2SetDefaultAddr (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR StationAddress,
+ IN IP4_ADDR SubnetMask
+ )
+{
+ EFI_STATUS Status;
+ IP4_INTERFACE *IpIf;
+ IP4_PROTOCOL *Ip4Instance;
+ EFI_ARP_PROTOCOL *Arp;
+ LIST_ENTRY *Entry;
+ IP4_ADDR Subnet;
+ IP4_ROUTE_TABLE *RouteTable;
+
+ IpIf = IpSb->DefaultInterface;
+ ASSERT (IpIf != NULL);
+
+ if ((IpIf->Ip == StationAddress) && (IpIf->SubnetMask == SubnetMask)) {
+ IpSb->State = IP4_SERVICE_CONFIGED;
+ return EFI_SUCCESS;
+ }
+
+ if (IpSb->Reconfig) {
+ //
+ // The default address is changed, free the previous interface first.
+ //
+ if (IpSb->DefaultRouteTable != NULL) {
+ Ip4FreeRouteTable (IpSb->DefaultRouteTable);
+ IpSb->DefaultRouteTable = NULL;
+ }
+
+ Ip4CancelReceive (IpSb->DefaultInterface);
+ Ip4FreeInterface (IpSb->DefaultInterface, NULL);
+ IpSb->DefaultInterface = NULL;
+ //
+ // Create new default interface and route table.
+ //
+ IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);
+ if (IpIf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RouteTable = Ip4CreateRouteTable ();
+ if (RouteTable == NULL) {
+ Ip4FreeInterface (IpIf, NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IpSb->DefaultInterface = IpIf;
+ InsertHeadList (&IpSb->Interfaces, &IpIf->Link);
+ IpSb->DefaultRouteTable = RouteTable;
+ Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb);
+ }
+
+ if (IpSb->State == IP4_SERVICE_CONFIGED) {
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+ }
+
+ Status = Ip4SetAddress (IpIf, StationAddress, SubnetMask);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (IpIf->Arp != NULL) {
+ //
+ // A non-NULL IpIf->Arp here means a new ARP child is created when setting default address,
+ // but some IP children may have referenced the default interface before it is configured,
+ // these IP instances also consume this ARP protocol so they need to open it BY_CHILD_CONTROLLER.
+ //
+ Arp = NULL;
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ Ip4Instance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, AddrLink, IP4_PROTOCOL_SIGNATURE);
+ Status = gBS->OpenProtocol (
+ IpIf->ArpHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **) &Arp,
+ gIp4DriverBinding.DriverBindingHandle,
+ Ip4Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+
+ Ip4AddRoute (
+ IpSb->DefaultRouteTable,
+ StationAddress,
+ SubnetMask,
+ IP4_ALLZERO_ADDRESS
+ );
+
+ //
+ // Add a route for the connected network.
+ //
+ Subnet = StationAddress & SubnetMask;
+
+ Ip4AddRoute (
+ IpSb->DefaultRouteTable,
+ Subnet,
+ SubnetMask,
+ IP4_ALLZERO_ADDRESS
+ );
+
+ IpSb->State = IP4_SERVICE_CONFIGED;
+ IpSb->Reconfig = FALSE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the station address, subnetmask and gateway address for the default interface.
+
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+ @param[in] StationAddress Ip address to be set.
+ @param[in] SubnetMask Subnet to be set.
+ @param[in] GatewayAddress Gateway to be set.
+
+ @retval EFI_SUCCESS Set default If successful.
+ @retval Others Errors occur as indicated.
+
+**/
+EFI_STATUS
+Ip4Config2SetDefaultIf (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN IP4_ADDR StationAddress,
+ IN IP4_ADDR SubnetMask,
+ IN IP4_ADDR GatewayAddress
+ )
+{
+ EFI_STATUS Status;
+ IP4_SERVICE *IpSb;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ //
+ // Check whether the StationAddress/SubnetMask pair is valid.
+ //
+ if (!Ip4StationAddressValid (StationAddress, SubnetMask)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Ip4Config2SetDefaultAddr (IpSb, StationAddress, SubnetMask);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a route if there is a default router.
+ //
+ if (GatewayAddress != IP4_ALLZERO_ADDRESS) {
+ Ip4AddRoute (
+ IpSb->DefaultRouteTable,
+ IP4_ALLZERO_ADDRESS,
+ IP4_ALLZERO_ADDRESS,
+ GatewayAddress
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Release all the DHCP related resources.
+
+ @param Instance The IP4 config2 instance.
+
+ @return None
+
+**/
+VOID
+Ip4Config2CleanDhcp4 (
+ IN IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ IP4_SERVICE *IpSb;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ if (Instance->Dhcp4 != NULL) {
+ Instance->Dhcp4->Stop (Instance->Dhcp4);
+
+ gBS->CloseProtocol (
+ Instance->Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+
+ Instance->Dhcp4 = NULL;
+ }
+
+ if (Instance->Dhcp4Handle != NULL) {
+ NetLibDestroyServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Instance->Dhcp4Handle
+ );
+
+ Instance->Dhcp4Handle = NULL;
+ }
+
+ if (Instance->Dhcp4Event != NULL) {
+ gBS->CloseEvent (Instance->Dhcp4Event);
+ Instance->Dhcp4Event = NULL;
+ }
+}
+
+/**
+ This worker function sets the DNS server list for the EFI IPv4 network
+ stack running on the communication device that this EFI_IP4_CONFIG2_PROTOCOL
+ manages. The DNS server addresses must be unicast IPv4 addresses.
+
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set, points to an array of
+ EFI_IPv4_ADDRESS instances.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_ABORTED The DNS server addresses to be set equal the current
+ configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv4
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip4Config2SetDnsServerWorker (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ UINTN OldIndex;
+ UINTN NewIndex;
+ EFI_IPv4_ADDRESS *OldDns;
+ EFI_IPv4_ADDRESS *NewDns;
+ UINTN OldDnsCount;
+ UINTN NewDnsCount;
+ IP4_CONFIG2_DATA_ITEM *Item;
+ BOOLEAN OneAdded;
+ VOID *Tmp;
+ IP4_ADDR DnsAddress;
+
+ if ((DataSize % sizeof (EFI_IPv4_ADDRESS) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Item = &Instance->DataItem[Ip4Config2DataTypeDnsServer];
+ NewDns = (EFI_IPv4_ADDRESS *) Data;
+ OldDns = Item->Data.DnsServers;
+ NewDnsCount = DataSize / sizeof (EFI_IPv4_ADDRESS);
+ OldDnsCount = Item->DataSize / sizeof (EFI_IPv4_ADDRESS);
+ OneAdded = FALSE;
+
+ if (NewDnsCount != OldDnsCount) {
+ Tmp = AllocatePool (DataSize);
+ if (Tmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ Tmp = NULL;
+ }
+
+ for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {
+ CopyMem (&DnsAddress, NewDns + NewIndex, sizeof (IP4_ADDR));
+ if (IP4_IS_UNSPECIFIED (NTOHL (DnsAddress)) || IP4_IS_LOCAL_BROADCAST (NTOHL (DnsAddress))) {
+ //
+ // The dns server address must be unicast.
+ //
+ if (Tmp != NULL) {
+ FreePool (Tmp);
+ }
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OneAdded) {
+ //
+ // If any address in the new setting is not in the old settings, skip the
+ // comparision below.
+ //
+ continue;
+ }
+
+ for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {
+ if (EFI_IP4_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {
+ //
+ // If found break out.
+ //
+ break;
+ }
+ }
+
+ if (OldIndex == OldDnsCount) {
+ OneAdded = TRUE;
+ }
+ }
+
+ if (!OneAdded && (DataSize == Item->DataSize)) {
+ //
+ // No new item is added and the size is the same.
+ //
+ Item->Status = EFI_SUCCESS;
+ return EFI_ABORTED;
+ } else {
+ if (Tmp != NULL) {
+ if (Item->Data.Ptr != NULL) {
+ FreePool (Item->Data.Ptr);
+ }
+ Item->Data.Ptr = Tmp;
+ }
+
+ CopyMem (Item->Data.Ptr, Data, DataSize);
+ Item->DataSize = DataSize;
+ Item->Status = EFI_SUCCESS;
+ return EFI_SUCCESS;
+ }
+}
+
+
+
+/**
+ Callback function when DHCP process finished. It will save the
+ retrieved IP configure parameter from DHCP to the NVRam.
+
+ @param Event The callback event
+ @param Context Opaque context to the callback
+
+ @return None
+
+**/
+VOID
+EFIAPI
+Ip4Config2OnDhcp4Complete (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_CONFIG2_INSTANCE *Instance;
+ EFI_DHCP4_MODE_DATA Dhcp4Mode;
+ EFI_STATUS Status;
+ IP4_ADDR StationAddress;
+ IP4_ADDR SubnetMask;
+ IP4_ADDR GatewayAddress;
+ UINT32 Index;
+ UINT32 OptionCount;
+ EFI_DHCP4_PACKET_OPTION **OptionList;
+
+ Instance = (IP4_CONFIG2_INSTANCE *) Context;
+ ASSERT (Instance->Dhcp4 != NULL);
+
+ //
+ // Get the DHCP retrieved parameters
+ //
+ Status = Instance->Dhcp4->GetModeData (Instance->Dhcp4, &Dhcp4Mode);
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (Dhcp4Mode.State == Dhcp4Bound) {
+ StationAddress = EFI_NTOHL (Dhcp4Mode.ClientAddress);
+ SubnetMask = EFI_NTOHL (Dhcp4Mode.SubnetMask);
+ GatewayAddress = EFI_NTOHL (Dhcp4Mode.RouterAddress);
+
+ Status = Ip4Config2SetDefaultIf (Instance, StationAddress, SubnetMask, GatewayAddress);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Parse the ACK to get required DNS server information.
+ //
+ OptionCount = 0;
+ OptionList = NULL;
+
+ Status = Instance->Dhcp4->Parse (Instance->Dhcp4, Dhcp4Mode.ReplyPacket, &OptionCount, OptionList);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ goto Exit;
+ }
+
+ OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+ if (OptionList == NULL) {
+ goto Exit;
+ }
+
+ Status = Instance->Dhcp4->Parse (Instance->Dhcp4, Dhcp4Mode.ReplyPacket, &OptionCount, OptionList);
+ if (EFI_ERROR (Status)) {
+ FreePool (OptionList);
+ goto Exit;
+ }
+
+ for (Index = 0; Index < OptionCount; Index++) {
+ //
+ // Look for DNS Server opcode (6).
+ //
+ if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {
+ if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
+ break;
+ }
+
+ Ip4Config2SetDnsServerWorker (Instance, OptionList[Index]->Length, &OptionList[Index]->Data[0]);
+ break;
+ }
+ }
+
+ FreePool (OptionList);
+
+ Instance->DhcpSuccess = TRUE;
+ }
+
+Exit:
+ Ip4Config2CleanDhcp4 (Instance);
+ DispatchDpc ();
+}
+
+
+/**
+ Start the DHCP configuration for this IP service instance.
+ It will locates the EFI_IP4_CONFIG2_PROTOCOL, then start the
+ DHCP configuration.
+
+ @param[in] Instance The IP4 config2 instance to configure
+
+ @retval EFI_SUCCESS The auto configuration is successfully started
+ @retval Others Failed to start auto configuration.
+
+**/
+EFI_STATUS
+Ip4StartAutoConfig (
+ IN IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ IP4_SERVICE *IpSb;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_DHCP4_MODE_DATA Dhcp4Mode;
+ EFI_DHCP4_PACKET_OPTION *OptionList[1];
+ IP4_CONFIG2_DHCP4_OPTION ParaList;
+ EFI_STATUS Status;
+
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ if (IpSb->State > IP4_SERVICE_UNSTARTED) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // A host must not invoke DHCP configuration if it is already
+ // participating in the DHCP configuraiton process.
+ //
+ if (Instance->Dhcp4Handle != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = NetLibCreateServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &Instance->Dhcp4Handle
+ );
+
+ if (Status == EFI_UNSUPPORTED) {
+ //
+ // No DHCPv4 Service Binding protocol, register a notify.
+ //
+ if (Instance->Dhcp4SbNotifyEvent == NULL) {
+ Instance->Dhcp4SbNotifyEvent = EfiCreateProtocolNotifyEvent (
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ TPL_CALLBACK,
+ Ip4Config2OnDhcp4SbInstalled,
+ (VOID *) Instance,
+ &Instance->Registration
+ );
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Instance->Dhcp4SbNotifyEvent != NULL) {
+ gBS->CloseEvent (Instance->Dhcp4SbNotifyEvent);
+ }
+
+ Status = gBS->OpenProtocol (
+ Instance->Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Instance->Dhcp4,
+ IpSb->Image,
+ IpSb->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ ASSERT_EFI_ERROR (Status);
+
+
+ //
+ // Check the current DHCP status, if the DHCP process has
+ // already finished, return now.
+ //
+ Dhcp4 = Instance->Dhcp4;
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode);
+
+ if (Dhcp4Mode.State == Dhcp4Bound) {
+ Ip4Config2OnDhcp4Complete (NULL, Instance);
+ return EFI_SUCCESS;
+
+ }
+
+ //
+ // Try to start the DHCP process. Use most of the current
+ // DHCP configuration to avoid problems if some DHCP client
+ // yields the control of this DHCP service to us.
+ //
+ ParaList.Head.OpCode = DHCP4_TAG_PARA_LIST;
+ ParaList.Head.Length = 3;
+ ParaList.Head.Data[0] = DHCP4_TAG_NETMASK;
+ ParaList.Route = DHCP4_TAG_ROUTER;
+ ParaList.Dns = DHCP4_TAG_DNS_SERVER;
+ OptionList[0] = &ParaList.Head;
+ Dhcp4Mode.ConfigData.OptionCount = 1;
+ Dhcp4Mode.ConfigData.OptionList = OptionList;
+
+ Status = Dhcp4->Configure (Dhcp4, &Dhcp4Mode.ConfigData);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Start the DHCP process
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip4Config2OnDhcp4Complete,
+ Instance,
+ &Instance->Dhcp4Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Dhcp4->Start (Dhcp4, Instance->Dhcp4Event);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ IpSb->State = IP4_SERVICE_STARTED;
+ DispatchDpc ();
+ return EFI_SUCCESS;
+
+}
+
+
+
+/**
+ The work function is to get the interface information of the communication
+ device this IP4_CONFIG2_INSTANCE manages.
+
+ @param[in] Instance Pointer to the IP4 config2 instance data.
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in
+ bytes, the size of buffer required to store the specified
+ configuration data.
+ @param[in] Data The data buffer in which the configuration data is returned.
+ Ignored if DataSize is ZERO.
+
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified
+ configuration data, and the required size is
+ returned in DataSize.
+ @retval EFI_SUCCESS The specified configuration data was obtained.
+
+**/
+EFI_STATUS
+Ip4Config2GetIfInfo (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data OPTIONAL
+ )
+{
+ IP4_SERVICE *IpSb;
+ UINTN Length;
+ IP4_CONFIG2_DATA_ITEM *Item;
+ EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo;
+ IP4_ADDR Address;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+ Length = sizeof (EFI_IP4_CONFIG2_INTERFACE_INFO);
+
+ if (IpSb->DefaultRouteTable != NULL) {
+ Length += IpSb->DefaultRouteTable->TotalNum * sizeof (EFI_IP4_ROUTE_TABLE);
+ }
+
+ if (*DataSize < Length) {
+ *DataSize = Length;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy the fixed size part of the interface info.
+ //
+ Item = &Instance->DataItem[Ip4Config2DataTypeInterfaceInfo];
+ IfInfo = (EFI_IP4_CONFIG2_INTERFACE_INFO *) Data;
+ CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP4_CONFIG2_INTERFACE_INFO));
+
+ //
+ // Update the address info.
+ //
+ if (IpSb->DefaultInterface != NULL) {
+ Address = HTONL (IpSb->DefaultInterface->Ip);
+ CopyMem (&IfInfo->StationAddress, &Address, sizeof (EFI_IPv4_ADDRESS));
+ Address = HTONL (IpSb->DefaultInterface->SubnetMask);
+ CopyMem (&IfInfo->SubnetMask, &Address, sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ if (IpSb->DefaultRouteTable != NULL) {
+ IfInfo->RouteTableSize = IpSb->DefaultRouteTable->TotalNum;
+ IfInfo->RouteTable = (EFI_IP4_ROUTE_TABLE *) ((UINT8 *) Data + sizeof (EFI_IP4_CONFIG2_INTERFACE_INFO));
+
+ Ip4Config2BuildDefaultRouteTable (IpSb, IfInfo->RouteTable);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The work function is to set the general configuration policy for the EFI IPv4 network
+ stack that is running on the communication device managed by this IP4_CONFIG2_INSTANCE.
+ The policy will affect other configuration settings.
+
+ @param[in] Instance Pointer to the IP4 config2 instance data.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_INVALID_PARAMETER The to be set policy is invalid.
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_ABORTED The new policy equals the current policy.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip4Config2SetPolicy (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_IP4_CONFIG2_POLICY NewPolicy;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+ IP4_SERVICE *IpSb;
+
+ if (DataSize != sizeof (EFI_IP4_CONFIG2_POLICY)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NewPolicy = *((EFI_IP4_CONFIG2_POLICY *) Data);
+
+ if (NewPolicy >= Ip4Config2PolicyMax) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewPolicy == Instance->Policy) {
+ if (NewPolicy != Ip4Config2PolicyDhcp || Instance->DhcpSuccess) {
+ return EFI_ABORTED;
+ }
+ } else {
+ //
+ // The policy is changed. Clean the ManualAddress, Gateway and DnsServers,
+ // shrink the variable data size, and fire up all the related events.
+ //
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ DataItem->Status = EFI_NOT_FOUND;
+ NetMapIterate (&DataItem->EventMap, Ip4Config2SignalEvent, NULL);
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeGateway];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ DataItem->Status = EFI_NOT_FOUND;
+ NetMapIterate (&DataItem->EventMap, Ip4Config2SignalEvent, NULL);
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeDnsServer];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ DataItem->Status = EFI_NOT_FOUND;
+ NetMapIterate (&DataItem->EventMap, Ip4Config2SignalEvent, NULL);
+
+ if (NewPolicy == Ip4Config2PolicyDhcp) {
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_VOLATILE);
+ } else {
+ //
+ // The policy is changed from dhcp to static. Stop the DHCPv4 process
+ // and destroy the DHCPv4 child.
+ //
+ if (Instance->Dhcp4Handle != NULL) {
+ Ip4Config2DestroyDhcp4 (Instance);
+ }
+
+ //
+ // Close the event.
+ //
+ if (Instance->Dhcp4Event != NULL) {
+ gBS->CloseEvent (Instance->Dhcp4Event);
+ Instance->Dhcp4Event = NULL;
+ }
+ }
+ }
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+ Ip4Config2OnPolicyChanged (IpSb, NewPolicy);
+
+ Instance->Policy = NewPolicy;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The work function is to set the station addresses manually for the EFI IPv4
+ network stack. It is only configurable when the policy is Ip4Config2PolicyStatic.
+
+ @param[in] Instance Pointer to the IP4 config2 instance data.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set
+ under the current policy.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_NOT_READY An asynchrous process is invoked to set the specified
+ configuration data, and the process is not finished.
+ @retval EFI_ABORTED The manual addresses to be set equal current
+ configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip4Config2SetMaunualAddress (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_IP4_CONFIG2_MANUAL_ADDRESS NewAddress;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+ EFI_STATUS Status;
+ IP4_ADDR StationAddress;
+ IP4_ADDR SubnetMask;
+ VOID *Ptr;
+ IP4_SERVICE *IpSb;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ ASSERT (Instance->DataItem[Ip4Config2DataTypeManualAddress].Status != EFI_NOT_READY);
+
+ if (((DataSize % sizeof (EFI_IP4_CONFIG2_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip4Config2PolicyStatic) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ NewAddress = *((EFI_IP4_CONFIG2_MANUAL_ADDRESS *) Data);
+
+ StationAddress = EFI_NTOHL (NewAddress.Address);
+ SubnetMask = EFI_NTOHL (NewAddress.SubnetMask);
+
+ //
+ // Check whether the StationAddress/SubnetMask pair is valid.
+ //
+ if (!Ip4StationAddressValid (StationAddress, SubnetMask)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Store the new data, and init the DataItem status to EFI_NOT_READY because
+ // we may have an asynchronous configuration process.
+ //
+ Ptr = AllocateCopyPool (DataSize, Data);
+ if (Ptr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+
+ DataItem->Data.Ptr = Ptr;
+ DataItem->DataSize = DataSize;
+ DataItem->Status = EFI_NOT_READY;
+
+ IpSb->Reconfig = TRUE;
+ Status = Ip4Config2SetDefaultAddr (IpSb, StationAddress, SubnetMask);
+
+ DataItem->Status = Status;
+
+ if (EFI_ERROR (DataItem->Status) && DataItem->Status != EFI_NOT_READY) {
+ if (Ptr != NULL) {
+ FreePool (Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ The work function is to set the gateway addresses manually for the EFI IPv4
+ network stack that is running on the communication device that this EFI IPv4
+ Configuration Protocol manages. It is not configurable when the policy is
+ Ip4Config2PolicyDhcp. The gateway addresses must be unicast IPv4 addresses.
+
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set. This points to an array of
+ EFI_IPv6_ADDRESS instances.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set
+ under the current policy.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation.
+ @retval EFI_ABORTED The manual gateway addresses to be set equal the
+ current configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip4Config2SetGateway (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+ IP4_ADDR Gateway;
+
+ UINTN Index1;
+ UINTN Index2;
+ EFI_IPv4_ADDRESS *OldGateway;
+ EFI_IPv4_ADDRESS *NewGateway;
+ UINTN OldGatewayCount;
+ UINTN NewGatewayCount;
+ BOOLEAN OneRemoved;
+ BOOLEAN OneAdded;
+ VOID *Tmp;
+
+ if ((DataSize % sizeof (EFI_IPv4_ADDRESS) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip4Config2PolicyStatic) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ NewGateway = (EFI_IPv4_ADDRESS *) Data;
+ NewGatewayCount = DataSize / sizeof (EFI_IPv4_ADDRESS);
+ for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {
+ CopyMem (&Gateway, NewGateway + Index1, sizeof (IP4_ADDR));
+
+ if ((IpSb->DefaultInterface->SubnetMask != 0) &&
+ !NetIp4IsUnicast (NTOHL (Gateway), IpSb->DefaultInterface->SubnetMask)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) {
+ if (EFI_IP4_EQUAL (NewGateway + Index1, NewGateway + Index2)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeGateway];
+ OldGateway = DataItem->Data.Gateway;
+ OldGatewayCount = DataItem->DataSize / sizeof (EFI_IPv4_ADDRESS);
+ OneRemoved = FALSE;
+ OneAdded = FALSE;
+
+ if (NewGatewayCount != OldGatewayCount) {
+ Tmp = AllocatePool (DataSize);
+ if (Tmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ Tmp = NULL;
+ }
+
+ for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {
+ //
+ // Remove this route entry.
+ //
+ CopyMem (&Gateway, OldGateway + Index1, sizeof (IP4_ADDR));
+ Ip4DelRoute (
+ IpSb->DefaultRouteTable,
+ IP4_ALLZERO_ADDRESS,
+ IP4_ALLZERO_ADDRESS,
+ NTOHL (Gateway)
+ );
+ OneRemoved = TRUE;
+
+ }
+
+ for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {
+ CopyMem (&Gateway, NewGateway + Index1, sizeof (IP4_ADDR));
+ Ip4AddRoute (
+ IpSb->DefaultRouteTable,
+ IP4_ALLZERO_ADDRESS,
+ IP4_ALLZERO_ADDRESS,
+ NTOHL (Gateway)
+ );
+
+ OneAdded = TRUE;
+ }
+
+
+ if (!OneRemoved && !OneAdded) {
+ DataItem->Status = EFI_SUCCESS;
+ return EFI_ABORTED;
+ } else {
+
+ if (Tmp != NULL) {
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = Tmp;
+ }
+
+ CopyMem (DataItem->Data.Ptr, Data, DataSize);
+ DataItem->DataSize = DataSize;
+ DataItem->Status = EFI_SUCCESS;
+ return EFI_SUCCESS;
+ }
+
+}
+
+/**
+ The work function is to set the DNS server list for the EFI IPv4 network
+ stack running on the communication device that this EFI_IP4_CONFIG2_PROTOCOL
+ manages. It is not configurable when the policy is Ip4Config2PolicyDhcp.
+ The DNS server addresses must be unicast IPv4 addresses.
+
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set, points to an array of
+ EFI_IPv4_ADDRESS instances.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set
+ under the current policy.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_ABORTED The DNS server addresses to be set equal the current
+ configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv4
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip4Config2SetDnsServer (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ IP4_CONFIG2_DATA_ITEM *Item;
+
+ Item = NULL;
+
+ if (Instance->Policy != Ip4Config2PolicyStatic) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ Item = &Instance->DataItem[Ip4Config2DataTypeDnsServer];
+
+ if (DATA_ATTRIB_SET (Item->Attribute, DATA_ATTRIB_VOLATILE)) {
+ REMOVE_DATA_ATTRIB (Item->Attribute, DATA_ATTRIB_VOLATILE);
+ }
+
+ return Ip4Config2SetDnsServerWorker (Instance, DataSize, Data);
+}
+
+/**
+ Generate the operational state of the interface this IP4 config2 instance manages
+ and output in EFI_IP4_CONFIG2_INTERFACE_INFO.
+
+ @param[in] IpSb The pointer to the IP4 service binding instance.
+ @param[out] IfInfo The pointer to the IP4 config2 interface information structure.
+
+**/
+VOID
+Ip4Config2InitIfInfo (
+ IN IP4_SERVICE *IpSb,
+ OUT EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo
+ )
+{
+ UnicodeSPrint (
+ IfInfo->Name,
+ EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE,
+ L"eth%d",
+ IpSb->Ip4Config2Instance.IfIndex
+ );
+
+ IfInfo->IfType = IpSb->SnpMode.IfType;
+ IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize;
+ CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize);
+}
+
+/**
+ The event handle routine when DHCPv4 process is finished or is updated.
+
+ @param[in] Event Not used.
+ @param[in] Context The pointer to the IP4 configuration instance data.
+
+**/
+VOID
+EFIAPI
+Ip4Config2OnDhcp4Event (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ return ;
+}
+
+
+/**
+ Set the configuration for the EFI IPv4 network stack running on the communication
+ device this EFI_IP4_CONFIG2_PROTOCOL instance manages.
+
+ This function is used to set the configuration data of type DataType for the EFI
+ IPv4 network stack that is running on the communication device that this EFI IPv4
+ Configuration Protocol instance manages.
+
+ DataSize is used to calculate the count of structure instances in the Data for
+ a DataType in which multiple structure instances are allowed.
+
+ This function is always non-blocking. When setting some type of configuration data,
+ an asynchronous process is invoked to check the correctness of the data, such as
+ performing Duplicate Address Detection on the manually set local IPv4 addresses.
+ EFI_NOT_READY is returned immediately to indicate that such an asynchronous process
+ is invoked, and the process is not finished yet. The caller wanting to get the result
+ of the asynchronous process is required to call RegisterDataNotify() to register an
+ event on the specified configuration data. Once the event is signaled, the caller
+ can call GetData() to obtain the configuration data and know the result.
+ For other types of configuration data that do not require an asynchronous configuration
+ process, the result of the operation is immediately returned.
+
+ @param[in] This The pointer to the EFI_IP4_CONFIG2_PROTOCOL instance.
+ @param[in] DataType The type of data to set.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set. The type of the data buffer is
+ associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - Data is NULL.
+ - One or more fields in Data do not match the requirement of the
+ data type indicated by DataType.
+ @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified
+ configuration data cannot be set under the current policy.
+ @retval EFI_ACCESS_DENIED Another set operation on the specified configuration
+ data is already in process.
+ @retval EFI_NOT_READY An asynchronous process was invoked to set the specified
+ configuration data, and the process is not finished yet.
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type
+ indicated by DataType.
+ @retval EFI_UNSUPPORTED This DataType is not supported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Config2SetData (
+ IN EFI_IP4_CONFIG2_PROTOCOL *This,
+ IN EFI_IP4_CONFIG2_DATA_TYPE DataType,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP4_CONFIG2_INSTANCE *Instance;
+ IP4_SERVICE *IpSb;
+
+ if ((This == NULL) || (Data == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip4Config2DataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+ NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);
+
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Status = Instance->DataItem[DataType].Status;
+ if (Status != EFI_NOT_READY) {
+
+ if (Instance->DataItem[DataType].SetData == NULL) {
+ //
+ // This type of data is readonly.
+ //
+ Status = EFI_WRITE_PROTECTED;
+ } else {
+
+ Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Fire up the events registered with this type of data.
+ //
+ NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip4Config2SignalEvent, NULL);
+ Ip4Config2WriteConfigData (IpSb->MacString, Instance);
+ } else if (Status == EFI_ABORTED) {
+ //
+ // The SetData is aborted because the data to set is the same with
+ // the one maintained.
+ //
+ Status = EFI_SUCCESS;
+ NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip4Config2SignalEvent, NULL);
+ }
+ }
+ } else {
+ //
+ // Another asynchornous process is on the way.
+ //
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Get the configuration data for the EFI IPv4 network stack running on the communication
+ device that this EFI_IP4_CONFIG2_PROTOCOL instance manages.
+
+ This function returns the configuration data of type DataType for the EFI IPv4 network
+ stack running on the communication device that this EFI IPv4 Configuration Protocol instance
+ manages.
+
+ The caller is responsible for allocating the buffer used to return the specified
+ configuration data. The required size will be returned to the caller if the size of
+ the buffer is too small.
+
+ EFI_NOT_READY is returned if the specified configuration data is not ready due to an
+ asynchronous configuration process already in progress. The caller can call RegisterDataNotify()
+ to register an event on the specified configuration data. Once the asynchronous configuration
+ process is finished, the event will be signaled, and a subsequent GetData() call will return
+ the specified configuration data.
+
+ @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance.
+ @param[in] DataType The type of data to get.
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the
+ size of buffer required to store the specified configuration data.
+ @param[in] Data The data buffer in which the configuration data is returned. The
+ type of the data buffer is associated with the DataType.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - This is NULL.
+ - DataSize is NULL.
+ - Data is NULL if *DataSize is not zero.
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data,
+ and the required size is returned in DataSize.
+ @retval EFI_NOT_READY The specified configuration data is not ready due to an
+ asynchronous configuration process already in progress.
+ @retval EFI_NOT_FOUND The specified configuration data is not found.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Config2GetData (
+ IN EFI_IP4_CONFIG2_PROTOCOL *This,
+ IN EFI_IP4_CONFIG2_DATA_TYPE DataType,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP4_CONFIG2_INSTANCE *Instance;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+
+ if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip4Config2DataTypeMaximum) {
+ return EFI_NOT_FOUND;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This);
+ DataItem = &Instance->DataItem[DataType];
+
+ Status = Instance->DataItem[DataType].Status;
+ if (!EFI_ERROR (Status)) {
+
+ if (DataItem->GetData != NULL) {
+
+ Status = DataItem->GetData (Instance, DataSize, Data);
+ } else if (*DataSize < Instance->DataItem[DataType].DataSize) {
+ //
+ // Update the buffer length.
+ //
+ *DataSize = Instance->DataItem[DataType].DataSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+
+ *DataSize = Instance->DataItem[DataType].DataSize;
+ CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize);
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Register an event that is signaled whenever a configuration process on the specified
+ configuration data is done.
+
+ This function registers an event that is to be signaled whenever a configuration
+ process on the specified configuration data is performed. An event can be registered
+ for a different DataType simultaneously. The caller is responsible for determining
+ which type of configuration data causes the signaling of the event in such an event.
+
+ @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance.
+ @param[in] DataType The type of data to unregister the event for.
+ @param[in] Event The event to register.
+
+ @retval EFI_SUCCESS The notification event for the specified configuration data is
+ registered.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not
+ supported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Config2RegisterDataNotify (
+ IN EFI_IP4_CONFIG2_PROTOCOL *This,
+ IN EFI_IP4_CONFIG2_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP4_CONFIG2_INSTANCE *Instance;
+ NET_MAP *EventMap;
+ NET_MAP_ITEM *Item;
+
+ if ((This == NULL) || (Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip4Config2DataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This);
+ EventMap = &Instance->DataItem[DataType].EventMap;
+
+ //
+ // Check whether this event is already registered for this DataType.
+ //
+ Item = NetMapFindKey (EventMap, Event);
+ if (Item == NULL) {
+
+ Status = NetMapInsertTail (EventMap, Event, NULL);
+
+ if (EFI_ERROR (Status)) {
+
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ } else {
+
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Remove a previously registered event for the specified configuration data.
+
+ @param This The pointer to the EFI_IP4_CONFIG2_PROTOCOL instance.
+ @param DataType The type of data to remove from the previously
+ registered event.
+ @param Event The event to be unregistered.
+
+ @retval EFI_SUCCESS The event registered for the specified
+ configuration data was removed.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_NOT_FOUND The Event has not been registered for the
+ specified DataType.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Config2UnregisterDataNotify (
+ IN EFI_IP4_CONFIG2_PROTOCOL *This,
+ IN EFI_IP4_CONFIG2_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP4_CONFIG2_INSTANCE *Instance;
+ NET_MAP_ITEM *Item;
+
+ if ((This == NULL) || (Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip4Config2DataTypeMaximum) {
+ return EFI_NOT_FOUND;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This);
+
+ Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event);
+ if (Item != NULL) {
+
+ NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL);
+ Status = EFI_SUCCESS;
+ } else {
+
+ Status = EFI_NOT_FOUND;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Initialize an IP4_CONFIG2_INSTANCE.
+
+ @param[out] Instance The buffer of IP4_CONFIG2_INSTANCE to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_SUCCESS The IP4_CONFIG2_INSTANCE initialized successfully.
+
+**/
+EFI_STATUS
+Ip4Config2InitInstance (
+ OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_CONFIG2_INSTANCE *TmpInstance;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT16 IfIndex;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ Instance->Signature = IP4_CONFIG2_INSTANCE_SIGNATURE;
+
+
+ //
+ // Determine the index of this interface.
+ //
+ IfIndex = 0;
+ NET_LIST_FOR_EACH (Entry, &mIp4Config2InstanceList) {
+ TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_CONFIG2_INSTANCE, Link, IP4_CONFIG2_INSTANCE_SIGNATURE);
+
+ if (TmpInstance->IfIndex > IfIndex) {
+ //
+ // There is a sequence hole because some interface is down.
+ //
+ break;
+ }
+
+ IfIndex++;
+ }
+
+ Instance->IfIndex = IfIndex;
+ NetListInsertBefore (Entry, &Instance->Link);
+
+ for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) {
+ //
+ // Initialize the event map for each data item.
+ //
+ NetMapInit (&Instance->DataItem[Index].EventMap);
+ }
+
+
+ //
+ // Initialize each data type: associate storage and set data size for the
+ // fixed size data types, hook the SetData function, set the data attribute.
+ //
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeInterfaceInfo];
+ DataItem->GetData = Ip4Config2GetIfInfo;
+ DataItem->Data.Ptr = &Instance->InterfaceInfo;
+ DataItem->DataSize = sizeof (Instance->InterfaceInfo);
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE);
+ Ip4Config2InitIfInfo (IpSb, &Instance->InterfaceInfo);
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypePolicy];
+ DataItem->SetData = Ip4Config2SetPolicy;
+ DataItem->Data.Ptr = &Instance->Policy;
+ DataItem->DataSize = sizeof (Instance->Policy);
+ Instance->Policy = Ip4Config2PolicyStatic;
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeManualAddress];
+ DataItem->SetData = Ip4Config2SetMaunualAddress;
+ DataItem->Status = EFI_NOT_FOUND;
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeGateway];
+ DataItem->SetData = Ip4Config2SetGateway;
+ DataItem->Status = EFI_NOT_FOUND;
+
+ DataItem = &Instance->DataItem[Ip4Config2DataTypeDnsServer];
+ DataItem->SetData = Ip4Config2SetDnsServer;
+ DataItem->Status = EFI_NOT_FOUND;
+
+ //
+ // Create the event used for DHCP.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip4Config2OnDhcp4Event,
+ Instance,
+ &Instance->Dhcp4Event
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Instance->Configured = TRUE;
+
+ //
+ // Try to read the config data from NV variable.
+ // If not found, write initialized config data into NV variable
+ // as a default config data.
+ //
+ Status = Ip4Config2ReadConfigData (IpSb->MacString, Instance);
+ if (Status == EFI_NOT_FOUND) {
+ Status = Ip4Config2WriteConfigData (IpSb->MacString, Instance);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Instance->Ip4Config2.SetData = EfiIp4Config2SetData;
+ Instance->Ip4Config2.GetData = EfiIp4Config2GetData;
+ Instance->Ip4Config2.RegisterDataNotify = EfiIp4Config2RegisterDataNotify;
+ Instance->Ip4Config2.UnregisterDataNotify = EfiIp4Config2UnregisterDataNotify;
+
+ //
+ // Publish the IP4 configuration form
+ //
+ return Ip4Config2FormInit (Instance);
+}
+
+
+/**
+ Release an IP4_CONFIG2_INSTANCE.
+
+ @param[in, out] Instance The buffer of IP4_CONFIG2_INSTANCE to be freed.
+
+**/
+VOID
+Ip4Config2CleanInstance (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ UINTN Index;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+
+ if (Instance->DeclineAddress != NULL) {
+ FreePool (Instance->DeclineAddress);
+ }
+
+ if (!Instance->Configured) {
+ return ;
+ }
+
+ if (Instance->Dhcp4Handle != NULL) {
+
+ Ip4Config2DestroyDhcp4 (Instance);
+ }
+
+ //
+ // Close the event.
+ //
+ if (Instance->Dhcp4Event != NULL) {
+ gBS->CloseEvent (Instance->Dhcp4Event);
+ Instance->Dhcp4Event = NULL;
+ }
+
+ for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) {
+
+ DataItem = &Instance->DataItem[Index];
+
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ }
+
+ NetMapClean (&Instance->DataItem[Index].EventMap);
+ }
+
+ Ip4Config2FormUnload (Instance);
+
+ RemoveEntryList (&Instance->Link);
+}
+
+/**
+ The event handle for IP4 auto reconfiguration. The original default
+ interface and route table will be removed as the default.
+
+ @param[in] Context The IP4 service binding instance.
+
+**/
+VOID
+EFIAPI
+Ip4AutoReconfigCallBackDpc (
+ IN VOID *Context
+ )
+{
+ IP4_SERVICE *IpSb;
+
+ IpSb = (IP4_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);
+
+ if (IpSb->State > IP4_SERVICE_UNSTARTED) {
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+ }
+
+ IpSb->Reconfig = TRUE;
+
+ Ip4StartAutoConfig (&IpSb->Ip4Config2Instance);
+
+ return ;
+}
+
+
+/**
+ Request Ip4AutoReconfigCallBackDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The event that is signalled.
+ @param Context The IP4 service binding instance.
+
+**/
+VOID
+EFIAPI
+Ip4AutoReconfigCallBack (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request Ip4AutoReconfigCallBackDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, Ip4AutoReconfigCallBackDpc, Context);
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
new file mode 100644
index 0000000000..a431f6f732
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
@@ -0,0 +1,300 @@
+/** @file
+ Definitions for EFI IPv4 Configuration II Protocol implementation.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __IP4_CONFIG2_IMPL_H__
+#define __IP4_CONFIG2_IMPL_H__
+
+#define IP4_CONFIG2_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', 'C', '2')
+#define IP4_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I')
+
+#define IP4_CONFIG2_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+
+#define DATA_ATTRIB_SIZE_FIXED 0x1
+#define DATA_ATTRIB_VOLATILE 0x2
+
+#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits))
+#define SET_DATA_ATTRIB(Attrib, Bits) ((Attrib) |= (Bits))
+#define REMOVE_DATA_ATTRIB(Attrib, Bits) ((Attrib) &= (~Bits))
+
+typedef struct _IP4_CONFIG2_INSTANCE IP4_CONFIG2_INSTANCE;
+
+#define IP4_CONFIG2_INSTANCE_FROM_PROTOCOL(Proto) \
+ CR ((Proto), \
+ IP4_CONFIG2_INSTANCE, \
+ Ip4Config2, \
+ IP4_CONFIG2_INSTANCE_SIGNATURE \
+ )
+
+#define IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE(Instance) \
+ CR ((Instance), \
+ IP4_SERVICE, \
+ Ip4Config2Instance, \
+ IP4_SERVICE_SIGNATURE \
+ )
+
+#define IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Callback) \
+ CR ((Callback), \
+ IP4_CONFIG2_INSTANCE, \
+ CallbackInfo, \
+ IP4_CONFIG2_INSTANCE_SIGNATURE \
+ )
+
+#define IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \
+ CR ((ConfigAccess), \
+ IP4_FORM_CALLBACK_INFO, \
+ HiiConfigAccessProtocol, \
+ IP4_FORM_CALLBACK_INFO_SIGNATURE \
+ )
+
+/**
+ The prototype of work function for EfiIp4Config2SetData().
+
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+ @param[in] DataSize In bytes, the size of the buffer pointed to by Data.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type,
+ 8 bytes.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv4
+ network stack was set successfully.
+
+**/
+typedef
+EFI_STATUS
+(*IP4_CONFIG2_SET_DATA) (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ );
+
+/**
+ The prototype of work function for EfiIp4Config2GetData().
+
+ @param[in] Instance The pointer to the IP4 config2 instance data.
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in
+ bytes, the size of buffer required to store the specified
+ configuration data.
+ @param[in] Data The data buffer in which the configuration data is returned.
+ Ignored if DataSize is ZERO.
+
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified
+ configuration data, and the required size is
+ returned in DataSize.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+typedef
+EFI_STATUS
+(*IP4_CONFIG2_GET_DATA) (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data OPTIONAL
+ );
+
+typedef union {
+ VOID *Ptr;
+ EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo;
+ EFI_IP4_CONFIG2_POLICY *Policy;
+ EFI_IP4_CONFIG2_MANUAL_ADDRESS *ManualAddress;
+ EFI_IPv4_ADDRESS *Gateway;
+ EFI_IPv4_ADDRESS *DnsServers;
+} IP4_CONFIG2_DATA;
+
+typedef struct {
+ IP4_CONFIG2_SET_DATA SetData;
+ IP4_CONFIG2_GET_DATA GetData;
+ EFI_STATUS Status;
+ UINT8 Attribute;
+ NET_MAP EventMap;
+ IP4_CONFIG2_DATA Data;
+ UINTN DataSize;
+} IP4_CONFIG2_DATA_ITEM;
+
+typedef struct {
+ UINT16 Offset;
+ UINT32 DataSize;
+ EFI_IP4_CONFIG2_DATA_TYPE DataType;
+} IP4_CONFIG2_DATA_RECORD;
+
+#pragma pack(1)
+
+//
+// heap data that contains the data for each data record.
+//
+// EFI_IP4_CONFIG2_POLICY Policy;
+// UINT32 ManualaddressCount;
+// UINT32 GatewayCount;
+// UINT32 DnsServersCount;
+// EFI_IP4_CONFIG2_MANUAL_ADDRESS ManualAddress[];
+// EFI_IPv4_ADDRESS Gateway[];
+// EFI_IPv4_ADDRESS DnsServers[];
+//
+typedef struct {
+ UINT16 Checksum;
+ UINT16 DataRecordCount;
+ IP4_CONFIG2_DATA_RECORD DataRecord[1];
+} IP4_CONFIG2_VARIABLE;
+
+#pragma pack()
+
+typedef struct {
+ EFI_IP4_CONFIG2_POLICY Policy; ///< manual or automatic
+ EFI_IP4_CONFIG2_MANUAL_ADDRESS *ManualAddress; ///< IP addresses
+ UINT32 ManualAddressCount; ///< IP addresses count
+ EFI_IPv4_ADDRESS *GatewayAddress; ///< Gateway address
+ UINT32 GatewayAddressCount; ///< Gateway address count
+ EFI_IPv4_ADDRESS *DnsAddress; ///< DNS server address
+ UINT32 DnsAddressCount; ///< DNS server address count
+} IP4_CONFIG2_NVDATA;
+
+typedef struct _IP4_FORM_CALLBACK_INFO {
+ UINT32 Signature;
+ EFI_HANDLE ChildHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccessProtocol;
+ EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath;
+ EFI_HII_HANDLE RegisteredHandle;
+} IP4_FORM_CALLBACK_INFO;
+
+struct _IP4_CONFIG2_INSTANCE {
+ UINT32 Signature;
+ BOOLEAN Configured;
+ LIST_ENTRY Link;
+ UINT16 IfIndex;
+
+ EFI_IP4_CONFIG2_PROTOCOL Ip4Config2;
+
+ EFI_IP4_CONFIG2_INTERFACE_INFO InterfaceInfo;
+ EFI_IP4_CONFIG2_POLICY Policy;
+ IP4_CONFIG2_DATA_ITEM DataItem[Ip4Config2DataTypeMaximum];
+
+ EFI_EVENT Dhcp4SbNotifyEvent;
+ VOID *Registration;
+ EFI_HANDLE Dhcp4Handle;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ BOOLEAN DhcpSuccess;
+ BOOLEAN OtherInfoOnly;
+ EFI_EVENT Dhcp4Event;
+ UINT32 FailedIaAddressCount;
+ EFI_IPv4_ADDRESS *DeclineAddress;
+ UINT32 DeclineAddressCount;
+
+ IP4_FORM_CALLBACK_INFO CallbackInfo;
+
+ IP4_CONFIG2_NVDATA Ip4NvData;
+};
+
+//
+// Configure the DHCP to request the routers and netmask
+// from server. The DHCP4_TAG_NETMASK is included in Head.
+//
+#pragma pack(1)
+typedef struct {
+ EFI_DHCP4_PACKET_OPTION Head;
+ UINT8 Route;
+ UINT8 Dns;
+} IP4_CONFIG2_DHCP4_OPTION;
+#pragma pack()
+
+/**
+ Read the configuration data from variable storage according to the VarName and
+ gEfiIp4Config2ProtocolGuid. It checks the integrity of variable data. If the
+ data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the
+ configuration data to IP4_CONFIG2_INSTANCE.
+
+ @param[in] VarName The pointer to the variable name
+ @param[in, out] Instance The pointer to the IP4 config2 instance data.
+
+ @retval EFI_NOT_FOUND The variable can not be found or already corrupted.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_SUCCESS The configuration data was retrieved successfully.
+
+**/
+EFI_STATUS
+Ip4Config2ReadConfigData (
+ IN CHAR16 *VarName,
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ );
+
+/**
+ Start the DHCP configuration for this IP service instance.
+ It will locates the EFI_IP4_CONFIG2_PROTOCOL, then start the
+ DHCP configuration.
+
+ @param[in] Instance The IP4 config2 instance to configure.
+
+ @retval EFI_SUCCESS The auto configuration is successfully started.
+ @retval Others Failed to start auto configuration.
+
+**/
+EFI_STATUS
+Ip4StartAutoConfig (
+ IN IP4_CONFIG2_INSTANCE *Instance
+ );
+
+/**
+ Initialize an IP4_CONFIG2_INSTANCE.
+
+ @param[out] Instance The buffer of IP4_CONFIG2_INSTANCE to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_SUCCESS The IP4_CONFIG2_INSTANCE initialized successfully.
+
+**/
+EFI_STATUS
+Ip4Config2InitInstance (
+ OUT IP4_CONFIG2_INSTANCE *Instance
+ );
+
+/**
+ Release an IP4_CONFIG2_INSTANCE.
+
+ @param[in, out] Instance The buffer of IP4_CONFIG2_INSTANCE to be freed.
+
+**/
+VOID
+Ip4Config2CleanInstance (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ );
+
+/**
+ Request Ip4AutoReconfigCallBackDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The event that is signalled.
+ @param Context The IP4 service binding instance.
+
+**/
+VOID
+EFIAPI
+Ip4AutoReconfigCallBack (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Destroy the Dhcp4 child in IP4_CONFIG2_INSTANCE and release the resources.
+
+ @param[in, out] Instance The buffer of IP4 config2 instance to be freed.
+
+ @retval EFI_SUCCESS The child was successfully destroyed.
+ @retval Others Failed to destroy the child.
+
+**/
+EFI_STATUS
+Ip4Config2DestroyDhcp4 (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.c
new file mode 100644
index 0000000000..c8dc6976d7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.c
@@ -0,0 +1,1439 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to Ip4.
+
+Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+CHAR16 mIp4Config2StorageName[] = L"IP4_CONFIG2_IFR_NVDATA";
+
+/**
+ Calculate the prefix length of the IPv4 subnet mask.
+
+ @param[in] SubnetMask The IPv4 subnet mask.
+
+ @return The prefix length of the subnet mask.
+ @retval 0 Other errors as indicated.
+
+**/
+UINT8
+GetSubnetMaskPrefixLength (
+ IN EFI_IPv4_ADDRESS *SubnetMask
+ )
+{
+ UINT8 Len;
+ UINT32 ReverseMask;
+
+ //
+ // The SubnetMask is in network byte order.
+ //
+ ReverseMask = SwapBytes32 (*(UINT32 *)&SubnetMask[0]);
+
+ //
+ // Reverse it.
+ //
+ ReverseMask = ~ReverseMask;
+
+ if ((ReverseMask & (ReverseMask + 1)) != 0) {
+ return 0;
+ }
+
+ Len = 0;
+
+ while (ReverseMask != 0) {
+ ReverseMask = ReverseMask >> 1;
+ Len++;
+ }
+
+ return (UINT8) (32 - Len);
+}
+
+/**
+ Convert the decimal dotted IPv4 address into the binary IPv4 address.
+
+ @param[in] Str The UNICODE string.
+ @param[out] Ip The storage to return the IPv4 address.
+
+ @retval EFI_SUCCESS The binary IP address is returned in Ip.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+
+**/
+EFI_STATUS
+Ip4Config2StrToIp (
+ IN CHAR16 *Str,
+ OUT EFI_IPv4_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+ UINTN Number;
+
+ Index = 0;
+
+ while (*Str != L'\0') {
+
+ if (Index > 3) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Number = 0;
+ while ((*Str >= L'0') && (*Str <= L'9')) {
+ Number = Number * 10 + (*Str - L'0');
+ Str++;
+ }
+
+ if (Number > 0xFF) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip->Addr[Index] = (UINT8) Number;
+
+ if ((*Str != L'\0') && (*Str != L'.')) {
+ //
+ // The current character should be either the NULL terminator or
+ // the dot delimiter.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*Str == L'.') {
+ //
+ // Skip the delimiter.
+ //
+ Str++;
+ }
+
+ Index++;
+ }
+
+ if (Index != 4) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the decimal dotted IPv4 addresses separated by space into the binary IPv4 address list.
+
+ @param[in] Str The UNICODE string contains IPv4 addresses.
+ @param[out] PtrIpList The storage to return the IPv4 address list.
+ @param[out] IpCount The size of the IPv4 address list.
+
+ @retval EFI_SUCCESS The binary IP address list is returned in PtrIpList.
+ @retval EFI_OUT_OF_RESOURCES Error occurs in allocating memory.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+
+**/
+EFI_STATUS
+Ip4Config2StrToIpList (
+ IN CHAR16 *Str,
+ OUT EFI_IPv4_ADDRESS **PtrIpList,
+ OUT UINTN *IpCount
+ )
+{
+ UINTN BeginIndex;
+ UINTN EndIndex;
+ UINTN Index;
+ UINTN IpIndex;
+ CHAR16 *StrTemp;
+ BOOLEAN SpaceTag;
+
+ BeginIndex = 0;
+ EndIndex = BeginIndex;
+ Index = 0;
+ IpIndex = 0;
+ StrTemp = NULL;
+ SpaceTag = TRUE;
+
+ *PtrIpList = NULL;
+ *IpCount = 0;
+
+ if (Str == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Get the number of Ip.
+ //
+ while (*(Str + Index) != L'\0') {
+ if (*(Str + Index) == L' ') {
+ SpaceTag = TRUE;
+ } else {
+ if (SpaceTag) {
+ (*IpCount)++;
+ SpaceTag = FALSE;
+ }
+ }
+
+ Index++;
+ }
+
+ if (*IpCount == 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Allocate buffer for IpList.
+ //
+ *PtrIpList = AllocateZeroPool(*IpCount * sizeof(EFI_IPv4_ADDRESS));
+ if (*PtrIpList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get IpList from Str.
+ //
+ Index = 0;
+ while (*(Str + Index) != L'\0') {
+ if (*(Str + Index) == L' ') {
+ if(!SpaceTag) {
+ StrTemp = AllocateZeroPool((EndIndex - BeginIndex + 1) * sizeof(CHAR16));
+ if (StrTemp == NULL) {
+ FreePool(*PtrIpList);
+ *PtrIpList = NULL;
+ *IpCount = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (StrTemp, Str + BeginIndex, (EndIndex - BeginIndex) * sizeof(CHAR16));
+ *(StrTemp + (EndIndex - BeginIndex)) = L'\0';
+
+ if (Ip4Config2StrToIp (StrTemp, &((*PtrIpList)[IpIndex])) != EFI_SUCCESS) {
+ FreePool(StrTemp);
+ FreePool(*PtrIpList);
+ *PtrIpList = NULL;
+ *IpCount = 0;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BeginIndex = EndIndex;
+ IpIndex++;
+
+ FreePool(StrTemp);
+ }
+
+ BeginIndex++;
+ EndIndex++;
+ SpaceTag = TRUE;
+ } else {
+ EndIndex++;
+ SpaceTag = FALSE;
+ }
+
+ Index++;
+
+ if (*(Str + Index) == L'\0') {
+ if (!SpaceTag) {
+ StrTemp = AllocateZeroPool((EndIndex - BeginIndex + 1) * sizeof(CHAR16));
+ if (StrTemp == NULL) {
+ FreePool(*PtrIpList);
+ *PtrIpList = NULL;
+ *IpCount = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (StrTemp, Str + BeginIndex, (EndIndex - BeginIndex) * sizeof(CHAR16));
+ *(StrTemp + (EndIndex - BeginIndex)) = L'\0';
+
+ if (Ip4Config2StrToIp (StrTemp, &((*PtrIpList)[IpIndex])) != EFI_SUCCESS) {
+ FreePool(StrTemp);
+ FreePool(*PtrIpList);
+ *PtrIpList = NULL;
+ *IpCount = 0;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool(StrTemp);
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the IPv4 address into a dotted string.
+
+ @param[in] Ip The IPv4 address.
+ @param[out] Str The dotted IP string.
+
+**/
+VOID
+Ip4Config2IpToStr (
+ IN EFI_IPv4_ADDRESS *Ip,
+ OUT CHAR16 *Str
+ )
+{
+ UnicodeSPrint (
+ Str,
+ 2 * IP4_STR_MAX_SIZE,
+ L"%d.%d.%d.%d",
+ Ip->Addr[0],
+ Ip->Addr[1],
+ Ip->Addr[2],
+ Ip->Addr[3]
+ );
+}
+
+
+/**
+ Convert the IPv4 address list into string consists of several decimal
+ dotted IPv4 addresses separated by space.
+
+ @param[in] Ip The IPv4 address list.
+ @param[in] IpCount The size of IPv4 address list.
+ @param[out] Str The string contains several decimal dotted
+ IPv4 addresses separated by space.
+**/
+VOID
+Ip4Config2IpListToStr (
+ IN EFI_IPv4_ADDRESS *Ip,
+ IN UINTN IpCount,
+ OUT CHAR16 *Str
+ )
+{
+ UINTN Index;
+ UINTN TemIndex;
+ UINTN StrIndex;
+ CHAR16 *TempStr;
+ EFI_IPv4_ADDRESS *TempIp;
+
+ Index = 0;
+ TemIndex = 0;
+ StrIndex = 0;
+ TempStr = NULL;
+ TempIp = NULL;
+
+ for (Index = 0; Index < IpCount; Index ++) {
+ TempIp = Ip + Index;
+ if (TempStr == NULL) {
+ TempStr = AllocateZeroPool(2 * IP4_STR_MAX_SIZE);
+ ASSERT(TempStr != NULL);
+ }
+
+ UnicodeSPrint (
+ TempStr,
+ 2 * IP4_STR_MAX_SIZE,
+ L"%d.%d.%d.%d",
+ TempIp->Addr[0],
+ TempIp->Addr[1],
+ TempIp->Addr[2],
+ TempIp->Addr[3]
+ );
+
+ for (TemIndex = 0; TemIndex < IP4_STR_MAX_SIZE; TemIndex ++) {
+ if (*(TempStr + TemIndex) == L'\0') {
+ if (Index == IpCount - 1) {
+ Str[StrIndex++] = L'\0';
+ } else {
+ Str[StrIndex++] = L' ';
+ }
+ break;
+ } else {
+ Str[StrIndex++] = *(TempStr + TemIndex);
+ }
+ }
+ }
+
+ if (TempStr != NULL) {
+ FreePool(TempStr);
+ }
+}
+
+/**
+ The notify function of create event when performing a manual configuration.
+
+ @param[in] Event The pointer of Event.
+ @param[in] Context The pointer of Context.
+
+**/
+VOID
+EFIAPI
+Ip4Config2ManualAddressNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Convert the network configuration data into the IFR data.
+
+ @param[in] Instance The IP4 config2 instance.
+ @param[in, out] IfrNvData The IFR nv data.
+
+ @retval EFI_SUCCESS The configure parameter to IFR data was
+ set successfully.
+ @retval EFI_INVALID_PARAMETER Source instance or target IFR data is not available.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip4Config2ConvertConfigNvDataToIfrNvData (
+ IN IP4_CONFIG2_INSTANCE *Instance,
+ IN OUT IP4_CONFIG2_IFR_NVDATA *IfrNvData
+ )
+{
+ IP4_SERVICE *IpSb;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ EFI_IP4_CONFIG2_INTERFACE_INFO *Ip4Info;
+ EFI_IP4_CONFIG2_POLICY Policy;
+ UINTN DataSize;
+ UINTN GatewaySize;
+ EFI_IPv4_ADDRESS GatewayAddress;
+ EFI_STATUS Status;
+ UINTN DnsSize;
+ UINTN DnsCount;
+ EFI_IPv4_ADDRESS *DnsAddress;
+
+ Status = EFI_SUCCESS;
+ Ip4Config2 = &Instance->Ip4Config2;
+ Ip4Info = NULL;
+ DnsAddress = NULL;
+ GatewaySize = sizeof (EFI_IPv4_ADDRESS);
+
+ if ((IfrNvData == NULL) || (Instance == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_CHECK_SIGNATURE (Instance, IP4_CONFIG2_INSTANCE_SIGNATURE);
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+
+ if (IpSb->DefaultInterface->Configured) {
+ IfrNvData->Configure = 1;
+ } else {
+ IfrNvData->Configure = 0;
+ goto Exit;
+ }
+
+ //
+ // Get the Policy info.
+ //
+ DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypePolicy,
+ &DataSize,
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (Policy == Ip4Config2PolicyStatic) {
+ IfrNvData->DhcpEnable = FALSE;
+ } else if (Policy == Ip4Config2PolicyDhcp) {
+ IfrNvData->DhcpEnable = TRUE;
+ goto Exit;
+ }
+
+ //
+ // Get the interface info.
+ //
+ DataSize = 0;
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypeInterfaceInfo,
+ &DataSize,
+ NULL
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+
+ Ip4Info = AllocateZeroPool (DataSize);
+ if (Ip4Info == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypeInterfaceInfo,
+ &DataSize,
+ Ip4Info
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get the Gateway info.
+ //
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypeGateway,
+ &GatewaySize,
+ &GatewayAddress
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get the Dns info.
+ //
+ DnsSize = 0;
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypeDnsServer,
+ &DnsSize,
+ NULL
+ );
+ if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ DnsCount = (UINT32) (DnsSize / sizeof (EFI_IPv4_ADDRESS));
+
+ if (DnsSize > 0) {
+ DnsAddress = AllocateZeroPool(DnsSize);
+ if (DnsAddress == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypeDnsServer,
+ &DnsSize,
+ DnsAddress
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ Ip4Config2IpToStr (&Ip4Info->StationAddress, IfrNvData->StationAddress);
+ Ip4Config2IpToStr (&Ip4Info->SubnetMask, IfrNvData->SubnetMask);
+ Ip4Config2IpToStr (&GatewayAddress, IfrNvData->GatewayAddress);
+ Ip4Config2IpListToStr (DnsAddress, DnsCount, IfrNvData->DnsAddress);
+
+Exit:
+
+ if (DnsAddress != NULL) {
+ FreePool(DnsAddress);
+ }
+
+ if (Ip4Info != NULL) {
+ FreePool(Ip4Info);
+ }
+
+ return Status;
+}
+
+/**
+ Convert the IFR data into the network configuration data and set the IP
+ configure parameters for the NIC.
+
+ @param[in] IfrFormNvData The IFR NV data.
+ @param[in, out] Instance The IP4 config2 instance.
+
+ @retval EFI_SUCCESS The configure parameter for this NIC was
+ set successfully.
+ @retval EFI_INVALID_PARAMETER The address information for setting is invalid.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip4Config2ConvertIfrNvDataToConfigNvData (
+ IN IP4_CONFIG2_IFR_NVDATA *IfrFormNvData,
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Cfg2;
+ IP4_CONFIG2_NVDATA *Ip4NvData;
+
+ EFI_IP_ADDRESS StationAddress;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS Gateway;
+ IP4_ADDR Ip;
+ EFI_IPv4_ADDRESS *DnsAddress;
+ UINTN DnsCount;
+ UINTN Index;
+
+ EFI_EVENT TimeoutEvent;
+ EFI_EVENT SetAddressEvent;
+ BOOLEAN IsAddressOk;
+ UINTN DataSize;
+ EFI_INPUT_KEY Key;
+
+ Status = EFI_SUCCESS;
+ Ip4Cfg2 = &Instance->Ip4Config2;
+ Ip4NvData = &Instance->Ip4NvData;
+
+ DnsCount = 0;
+ DnsAddress = NULL;
+
+ TimeoutEvent = NULL;
+ SetAddressEvent = NULL;
+
+
+
+ if (Instance == NULL || IfrFormNvData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IfrFormNvData->Configure != TRUE) {
+ return EFI_SUCCESS;
+ }
+
+ if (IfrFormNvData->DhcpEnable == TRUE) {
+ Ip4NvData->Policy = Ip4Config2PolicyDhcp;
+
+ Status = Ip4Cfg2->SetData (
+ Ip4Cfg2,
+ Ip4Config2DataTypePolicy,
+ sizeof (EFI_IP4_CONFIG2_POLICY),
+ &Ip4NvData->Policy
+ );
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ } else {
+ //
+ // Get Ip4NvData from IfrFormNvData if it is valid.
+ //
+ Ip4NvData->Policy = Ip4Config2PolicyStatic;
+
+ Status = Ip4Config2StrToIp (IfrFormNvData->SubnetMask, &SubnetMask.v4);
+ if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (GetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Ip4Config2StrToIp (IfrFormNvData->StationAddress, &StationAddress.v4);
+ if (EFI_ERROR (Status) ||
+ (SubnetMask.Addr[0] != 0 && !NetIp4IsUnicast (NTOHL (StationAddress.Addr[0]), NTOHL (SubnetMask.Addr[0]))) ||
+ !Ip4StationAddressValid (NTOHL (StationAddress.Addr[0]), NTOHL (SubnetMask.Addr[0]))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Ip4Config2StrToIp (IfrFormNvData->GatewayAddress, &Gateway.v4);
+ if (EFI_ERROR (Status) ||
+ (Gateway.Addr[0] != 0 && SubnetMask.Addr[0] != 0 && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL (SubnetMask.Addr[0])))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Ip4Config2StrToIpList (IfrFormNvData->DnsAddress, &DnsAddress, &DnsCount);
+ if (!EFI_ERROR (Status) && DnsCount > 0) {
+ for (Index = 0; Index < DnsCount; Index ++) {
+ CopyMem (&Ip, &DnsAddress[Index], sizeof (IP4_ADDR));
+ if (IP4_IS_UNSPECIFIED (NTOHL (Ip)) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL);
+ FreePool(DnsAddress);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ } else {
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL);
+ }
+ }
+
+ if (Ip4NvData->ManualAddress != NULL) {
+ FreePool(Ip4NvData->ManualAddress);
+ }
+ Ip4NvData->ManualAddressCount = 1;
+ Ip4NvData->ManualAddress = AllocateZeroPool(sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS));
+ if (Ip4NvData->ManualAddress == NULL) {
+ if (DnsAddress != NULL) {
+ FreePool(DnsAddress);
+ }
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem(&Ip4NvData->ManualAddress->Address, &StationAddress.v4, sizeof(EFI_IPv4_ADDRESS));
+ CopyMem(&Ip4NvData->ManualAddress->SubnetMask, &SubnetMask.v4, sizeof(EFI_IPv4_ADDRESS));
+
+ if (Ip4NvData->GatewayAddress != NULL) {
+ FreePool(Ip4NvData->GatewayAddress);
+ }
+ Ip4NvData->GatewayAddressCount = 1;
+ Ip4NvData->GatewayAddress = AllocateZeroPool(sizeof(EFI_IPv4_ADDRESS));
+ if (Ip4NvData->GatewayAddress == NULL) {
+ if (DnsAddress != NULL) {
+ FreePool(DnsAddress);
+ }
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem(Ip4NvData->GatewayAddress, &Gateway.v4, sizeof(EFI_IPv4_ADDRESS));
+
+ if (Ip4NvData->DnsAddress != NULL) {
+ FreePool(Ip4NvData->DnsAddress);
+ }
+ Ip4NvData->DnsAddressCount = (UINT32) DnsCount;
+ Ip4NvData->DnsAddress = DnsAddress;
+
+ //
+ // Setting Ip4NvData.
+ //
+ Status = Ip4Cfg2->SetData (
+ Ip4Cfg2,
+ Ip4Config2DataTypePolicy,
+ sizeof (EFI_IP4_CONFIG2_POLICY),
+ &Ip4NvData->Policy
+ );
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Create events & timers for asynchronous settings.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4Config2ManualAddressNotify,
+ &IsAddressOk,
+ &SetAddressEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ IsAddressOk = FALSE;
+
+ Status = Ip4Cfg2->RegisterDataNotify (
+ Ip4Cfg2,
+ Ip4Config2DataTypeManualAddress,
+ SetAddressEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Set ManualAddress.
+ //
+ DataSize = Ip4NvData->ManualAddressCount * sizeof (EFI_IP4_CONFIG2_MANUAL_ADDRESS);
+ Status = Ip4Cfg2->SetData (
+ Ip4Cfg2,
+ Ip4Config2DataTypeManualAddress,
+ DataSize,
+ (VOID *) Ip4NvData->ManualAddress
+ );
+
+ if (Status == EFI_NOT_READY) {
+ gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000);
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ if (IsAddressOk) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ Ip4Cfg2->UnregisterDataNotify (
+ Ip4Cfg2,
+ Ip4Config2DataTypeManualAddress,
+ SetAddressEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Set gateway.
+ //
+ DataSize = Ip4NvData->GatewayAddressCount * sizeof (EFI_IPv4_ADDRESS);
+ Status = Ip4Cfg2->SetData (
+ Ip4Cfg2,
+ Ip4Config2DataTypeGateway,
+ DataSize,
+ Ip4NvData->GatewayAddress
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Set DNS addresses.
+ //
+ if (Ip4NvData->DnsAddressCount > 0 && Ip4NvData->DnsAddress != NULL) {
+ DataSize = Ip4NvData->DnsAddressCount * sizeof (EFI_IPv4_ADDRESS);
+ Status = Ip4Cfg2->SetData (
+ Ip4Cfg2,
+ Ip4Config2DataTypeDnsServer,
+ DataSize,
+ Ip4NvData->DnsAddress
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+ }
+
+Exit:
+ if (SetAddressEvent != NULL) {
+ gBS->CloseEvent (SetAddressEvent);
+ }
+
+ if (TimeoutEvent != NULL) {
+ gBS->CloseEvent (TimeoutEvent);
+ }
+
+ return Status;
+}
+
+/**
+ This function allows the caller to request the current
+ configuration for one or more named elements. The resulting
+ string is in <ConfigAltResp> format. Any and all alternative
+ configuration strings shall also be appended to the end of the
+ current configuration string. If they are, they must appear
+ after the current configuration. They must contain the same
+ routing (GUID, NAME, PATH) as the current configuration string.
+ They must have an additional description indicating the type of
+ alternative configuration the string represents,
+ "ALTCFG=<StringToken>". That <StringToken> (when
+ converted from Hex UNICODE to binary) is a reference to a
+ string in the associated string pack.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Request A null-terminated Unicode string in
+ <ConfigRequest> format. Note that this
+ includes the routing information as well as
+ the configurable name / value pairs. It is
+ invalid for this string to be in
+ <MultiConfigRequest> format.
+ @param[out] Progress On return, points to a character in the
+ Request string. Points to the string's null
+ terminator if request was successful. Points
+ to the most recent "&" before the first
+ failing name / value pair (or the beginning
+ of the string if the failure is in the first
+ name / value pair) if the request was not
+ successful.
+ @param[out] Results A null-terminated Unicode string in
+ <ConfigAltResp> format which has all values
+ filled in for the names in the Request string.
+ String to be allocated by the called function.
+
+ @retval EFI_SUCCESS The Results string is filled with the
+ values corresponding to all requested
+ names.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+ @retval EFI_NOT_FOUND Routing data doesn't match any
+ known driver. Progress set to the
+ first character in the routing header.
+ Note: There is no requirement that the
+ driver validate the routing data. It
+ must skip the <ConfigHdr> in order to
+ process the names.
+ @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set
+ to most recent & before the
+ error or the beginning of the
+ string.
+ @retval EFI_INVALID_PARAMETER Unknown name. Progress points
+ to the & before the name in
+ question.Currently not implemented.
+**/
+EFI_STATUS
+EFIAPI
+Ip4FormExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+ EFI_STATUS Status;
+ IP4_CONFIG2_INSTANCE *Ip4Config2Instance;
+ IP4_FORM_CALLBACK_INFO *Private;
+ IP4_CONFIG2_IFR_NVDATA *IfrFormNvData;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ EFI_STRING FormResult;
+ UINTN Size;
+ UINTN BufferSize;
+
+ if (Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ IfrFormNvData = NULL;
+ ConfigRequest = NULL;
+ FormResult = NULL;
+ Size = 0;
+ AllocatedRequest = FALSE;
+ ConfigRequest = Request;
+ Private = IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(This);
+ Ip4Config2Instance = IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Private);
+ BufferSize = sizeof (IP4_CONFIG2_IFR_NVDATA);
+ *Progress = Request;
+
+ //
+ // Check Request data in <ConfigHdr>.
+ //
+ if ((Request == NULL) || HiiIsConfigHdrMatch (Request, &gIp4Config2NvDataGuid, mIp4Config2StorageName)) {
+ IfrFormNvData = AllocateZeroPool (sizeof (IP4_CONFIG2_IFR_NVDATA));
+ if (IfrFormNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip4Config2ConvertConfigNvDataToIfrNvData (Ip4Config2Instance, IfrFormNvData);
+
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
+ //
+ // Request has no request element, construct full request string.
+ // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
+ // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
+ //
+ ConfigRequestHdr = HiiConstructConfigHdr (&gIp4Config2NvDataGuid, mIp4Config2StorageName, Private->ChildHandle);
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
+ ConfigRequest = AllocateZeroPool (Size);
+ ASSERT (ConfigRequest != NULL);
+ AllocatedRequest = TRUE;
+
+ UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
+ FreePool (ConfigRequestHdr);
+ }
+
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ Status = gHiiConfigRouting->BlockToConfig (
+ gHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) IfrFormNvData,
+ BufferSize,
+ &FormResult,
+ Progress
+ );
+
+ FreePool (IfrFormNvData);
+
+ //
+ // Free the allocated config request string.
+ //
+ if (AllocatedRequest) {
+ FreePool (ConfigRequest);
+ ConfigRequest = NULL;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto Failure;
+ }
+ }
+
+ if (Request == NULL || HiiIsConfigHdrMatch (Request, &gIp4Config2NvDataGuid, mIp4Config2StorageName)) {
+ *Results = FormResult;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+Failure:
+ //
+ // Set Progress string to the original request string.
+ //
+ if (Request == NULL) {
+ *Progress = NULL;
+ } else if (StrStr (Request, L"OFFSET") == NULL) {
+ *Progress = Request + StrLen (Request);
+ }
+
+ return Status;
+}
+
+/**
+ This function applies changes in a driver's configuration.
+ Input is a Configuration, which has the routing data for this
+ driver followed by name / value configuration pairs. The driver
+ must apply those pairs to its configurable storage. If the
+ driver's configuration is stored in a linear block of data
+ and the driver's name / value pairs are in <BlockConfig>
+ format, it may use the ConfigToBlock helper function (above) to
+ simplify the job. Currently not implemented.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+ @param[out] Progress A pointer to a string filled in with the
+ offset of the most recent '&' before the
+ first failing name / value pair (or the
+ beginn ing of the string if the failure
+ is in the first name / value pair) or
+ the terminating NULL if all was
+ successful.
+
+ @retval EFI_SUCCESS The results have been distributed or are
+ awaiting distribution.
+ @retval EFI_OUT_OF_MEMORY Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+ @retval EFI_INVALID_PARAMETERS Passing in a NULL for the
+ Results parameter would result
+ in this type of error.
+ @retval EFI_NOT_FOUND Target for the specified routing data
+ was not found.
+**/
+EFI_STATUS
+EFIAPI
+Ip4FormRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ IP4_CONFIG2_IFR_NVDATA *IfrFormNvData;
+ IP4_CONFIG2_INSTANCE *Ip4Config2Instance;
+ IP4_FORM_CALLBACK_INFO *Private;
+
+ Status = EFI_SUCCESS;
+ IfrFormNvData = NULL;
+
+ if (Configuration == NULL || Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Configuration;
+
+ Private = IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(This);
+ Ip4Config2Instance = IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Private);
+
+ //
+ // Check Routing data in <ConfigHdr>.
+ //
+ if (HiiIsConfigHdrMatch (Configuration, &gIp4Config2NvDataGuid, mIp4Config2StorageName)) {
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ IfrFormNvData = AllocateZeroPool (sizeof (IP4_CONFIG2_IFR_NVDATA));
+ if (IfrFormNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ BufferSize = 0;
+
+ Status = gHiiConfigRouting->ConfigToBlock (
+ gHiiConfigRouting,
+ Configuration,
+ (UINT8 *) IfrFormNvData,
+ &BufferSize,
+ Progress
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+
+ Status = gHiiConfigRouting->ConfigToBlock (
+ gHiiConfigRouting,
+ Configuration,
+ (UINT8 *) IfrFormNvData,
+ &BufferSize,
+ Progress
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = Ip4Config2ConvertIfrNvDataToConfigNvData (IfrFormNvData, Ip4Config2Instance);
+ }
+
+ FreePool (IfrFormNvData);
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+ return Status;
+
+}
+
+/**
+ This function is called to provide results data to the driver.
+ This data consists of a unique key that is used to identify
+ which data is either being passed back or being asked for.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Action Specifies the type of action taken by the browser.
+ @param[in] QuestionId A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect. The format of the data tends to
+ vary based on the opcode that enerated the callback.
+ @param[in] Type The type of value for the question.
+ @param[in] Value A pointer to the data being sent to the original
+ exporting driver.
+ @param[out] ActionRequest On return, points to the action requested by the
+ callback function.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
+ variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be saved.
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the
+ callback.Currently not implemented.
+ @retval EFI_INVALID_PARAMETERS Passing in wrong parameter.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4FormCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ EFI_STATUS Status;
+ IP4_CONFIG2_INSTANCE *Instance;
+ IP4_CONFIG2_IFR_NVDATA *IfrFormNvData;
+ IP4_FORM_CALLBACK_INFO *Private;
+
+ EFI_IP_ADDRESS StationAddress;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS Gateway;
+ IP4_ADDR Ip;
+ EFI_IPv4_ADDRESS *DnsAddress;
+ UINTN DnsCount;
+ UINTN Index;
+ EFI_INPUT_KEY Key;
+
+ IfrFormNvData = NULL;
+ DnsCount = 0;
+ DnsAddress = NULL;
+
+ if (Action == EFI_BROWSER_ACTION_CHANGED) {
+ Private = IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(This);
+ Instance = IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Private);
+
+ IfrFormNvData = AllocateZeroPool (sizeof (IP4_CONFIG2_IFR_NVDATA));
+ if (IfrFormNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Retrieve uncommitted data from Browser
+ //
+ if (!HiiGetBrowserData (&gIp4Config2NvDataGuid, mIp4Config2StorageName, sizeof (IP4_CONFIG2_IFR_NVDATA), (UINT8 *) IfrFormNvData)) {
+ FreePool (IfrFormNvData);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = EFI_SUCCESS;
+
+ switch (QuestionId) {
+ case KEY_LOCAL_IP:
+ Status = Ip4Config2StrToIp (IfrFormNvData->StationAddress, &StationAddress.v4);
+ if (EFI_ERROR (Status) || IP4_IS_UNSPECIFIED (NTOHL (StationAddress.Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (StationAddress.Addr[0]))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case KEY_SUBNET_MASK:
+ Status = Ip4Config2StrToIp (IfrFormNvData->SubnetMask, &SubnetMask.v4);
+ if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (GetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case KEY_GATE_WAY:
+ Status = Ip4Config2StrToIp (IfrFormNvData->GatewayAddress, &Gateway.v4);
+ if (EFI_ERROR (Status) || IP4_IS_LOCAL_BROADCAST(NTOHL(Gateway.Addr[0]))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case KEY_DNS:
+ Status = Ip4Config2StrToIpList (IfrFormNvData->DnsAddress, &DnsAddress, &DnsCount);
+ if (!EFI_ERROR (Status) && DnsCount > 0) {
+ for (Index = 0; Index < DnsCount; Index ++) {
+ CopyMem (&Ip, &DnsAddress[Index], sizeof (IP4_ADDR));
+ if (IP4_IS_UNSPECIFIED (NTOHL (Ip)) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ }
+ } else {
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL);
+ }
+ }
+
+ if(DnsAddress != NULL) {
+ FreePool(DnsAddress);
+ }
+ break;
+
+ case KEY_SAVE_CHANGES:
+ Status = Ip4Config2ConvertIfrNvDataToConfigNvData (IfrFormNvData, Instance);
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ default:
+ break;
+ }
+
+ FreePool (IfrFormNvData);
+
+ return Status;
+ }
+
+ //
+ // All other action return unsupported.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Install HII Config Access protocol for network device and allocate resource.
+
+ @param[in, out] Instance The IP4 config2 Instance.
+
+ @retval EFI_SUCCESS The HII Config Access protocol is installed.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip4Config2FormInit (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ IP4_SERVICE *IpSb;
+ IP4_FORM_CALLBACK_INFO *CallbackInfo;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
+ VENDOR_DEVICE_PATH VendorDeviceNode;
+ EFI_SERVICE_BINDING_PROTOCOL *MnpSb;
+ CHAR16 *MacString;
+ CHAR16 MenuString[128];
+ CHAR16 PortString[128];
+ CHAR16 *OldMenuString;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+ ASSERT (IpSb != NULL);
+
+ CallbackInfo = &Instance->CallbackInfo;
+
+ CallbackInfo->Signature = IP4_FORM_CALLBACK_INFO_SIGNATURE;
+
+ Status = gBS->HandleProtocol (
+ IpSb->Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ParentDevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Construct device path node for EFI HII Config Access protocol,
+ // which consists of controller physical device path and one hardware
+ // vendor guid node.
+ //
+ ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));
+ VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH;
+ VendorDeviceNode.Header.SubType = HW_VENDOR_DP;
+
+ CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid);
+
+ SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));
+ CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (
+ ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode
+ );
+ if (CallbackInfo->HiiVendorDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ ConfigAccess = &CallbackInfo->HiiConfigAccessProtocol;
+ ConfigAccess->ExtractConfig = Ip4FormExtractConfig;
+ ConfigAccess->RouteConfig = Ip4FormRouteConfig;
+ ConfigAccess->Callback = Ip4FormCallback;
+
+ //
+ // Install Device Path Protocol and Config Access protocol on new handle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ ConfigAccess,
+ NULL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Open the Parent Handle for the child
+ //
+ Status = gBS->OpenProtocol (
+ IpSb->Controller,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &MnpSb,
+ IpSb->Image,
+ CallbackInfo->ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // Publish our HII data
+ //
+ CallbackInfo->RegisteredHandle = HiiAddPackages (
+ &gIp4Config2NvDataGuid,
+ CallbackInfo->ChildHandle,
+ Ip4DxeStrings,
+ Ip4Config2Bin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Append MAC string in the menu help string and tile help string
+ //
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString);
+ if (!EFI_ERROR (Status)) {
+ OldMenuString = HiiGetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP4_CONFIG2_FORM_HELP),
+ NULL
+ );
+ UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP4_CONFIG2_FORM_HELP),
+ MenuString,
+ NULL
+ );
+
+ UnicodeSPrint (PortString, 128, L"MAC:%s", MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP4_DEVICE_FORM_HELP),
+ PortString,
+ NULL
+ );
+
+ FreePool (MacString);
+ FreePool (OldMenuString);
+
+ return EFI_SUCCESS;
+ }
+
+Error:
+ Ip4Config2FormUnload (Instance);
+ return Status;
+}
+
+/**
+ Uninstall the HII Config Access protocol for network devices and free up the resources.
+
+ @param[in, out] Instance The IP4 config2 instance to unload a form.
+
+**/
+VOID
+Ip4Config2FormUnload (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_FORM_CALLBACK_INFO *CallbackInfo;
+ IP4_CONFIG2_NVDATA *Ip4NvData;
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+ ASSERT (IpSb != NULL);
+
+ CallbackInfo = &Instance->CallbackInfo;
+
+ if (CallbackInfo->ChildHandle != NULL) {
+ //
+ // Close the child handle
+ //
+ gBS->CloseProtocol (
+ IpSb->Controller,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ IpSb->Image,
+ CallbackInfo->ChildHandle
+ );
+
+ //
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->HiiConfigAccessProtocol,
+ NULL
+ );
+ }
+
+ if (CallbackInfo->HiiVendorDevicePath != NULL) {
+ FreePool (CallbackInfo->HiiVendorDevicePath);
+ }
+
+ if (CallbackInfo->RegisteredHandle != NULL) {
+ //
+ // Remove HII package list
+ //
+ HiiRemovePackages (CallbackInfo->RegisteredHandle);
+ }
+
+ Ip4NvData = &Instance->Ip4NvData;
+
+ if(Ip4NvData->ManualAddress != NULL) {
+ FreePool(Ip4NvData->ManualAddress);
+ }
+
+ if(Ip4NvData->GatewayAddress != NULL) {
+ FreePool(Ip4NvData->GatewayAddress);
+ }
+
+ if(Ip4NvData->DnsAddress != NULL) {
+ FreePool(Ip4NvData->DnsAddress);
+ }
+
+ Ip4NvData->ManualAddressCount = 0;
+ Ip4NvData->GatewayAddressCount = 0;
+ Ip4NvData->DnsAddressCount = 0;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.h
new file mode 100644
index 0000000000..5a82e6d229
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.h
@@ -0,0 +1,51 @@
+/** @file
+ The header file of IP4Config2Nv.c
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP4_CONFIG2NV_H_
+#define _IP4_CONFIG2NV_H_
+
+#include "Ip4Impl.h"
+
+extern UINT8 Ip4Config2Bin[];
+extern UINT8 Ip4DxeStrings[];
+
+#define NIC_ITEM_CONFIG_SIZE (sizeof (IP4_CONFIG2_INSTANCE) + (sizeof (EFI_IPv4_ADDRESS) * MAX_IP4_CONFIG_DNS))
+
+/**
+ Install HII Config Access protocol for network device and allocate resource.
+
+ @param[in, out] Instance The IP4 config2 Instance.
+
+ @retval EFI_SUCCESS The HII Config Access protocol is installed.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip4Config2FormInit (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ );
+
+/**
+ Uninstall the HII Config Access protocol for network devices and free up the resources.
+
+ @param[in, out] Instance The IP4 config2 instance to unload a form.
+
+**/
+VOID
+Ip4Config2FormUnload (
+ IN OUT IP4_CONFIG2_INSTANCE *Instance
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c
new file mode 100644
index 0000000000..792db5c173
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c
@@ -0,0 +1,1038 @@
+/** @file
+ The driver binding and service binding protocol for IP4 driver.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding = {
+ Ip4DriverBindingSupported,
+ Ip4DriverBindingStart,
+ Ip4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+BOOLEAN mIpSec2Installed = FALSE;
+
+/**
+ Callback function for IpSec2 Protocol install.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+
+**/
+VOID
+EFIAPI
+IpSec2InstalledCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Close the event so it does not get called again.
+ //
+ gBS->CloseEvent (Event);
+
+ mIpSec2Installed = TRUE;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for IP4 driver which install the driver
+ binding and component name protocol on its image.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ VOID *Registration;
+
+ EfiCreateProtocolNotifyEvent (
+ &gEfiIpSec2ProtocolGuid,
+ TPL_CALLBACK,
+ IpSec2InstalledCallback,
+ NULL,
+ &Registration
+ );
+
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIp4DriverBinding,
+ ImageHandle,
+ &gIp4ComponentName,
+ &gIp4ComponentName2
+ );
+}
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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
+Ip4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the MNP service binding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Test for the Arp service binding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+/**
+ Clean up a IP4 service binding instance. It will release all
+ the resource allocated by the instance. The instance may be
+ partly initialized, or partly destroyed. If a resource is
+ destroyed, it is marked as that in case the destroy failed and
+ being called again later.
+
+ @param[in] IpSb The IP4 service binding instance to clean up
+
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up
+ @retval other Failed to clean up some of the resources.
+
+**/
+EFI_STATUS
+Ip4CleanService (
+ IN IP4_SERVICE *IpSb
+ );
+
+
+/**
+ Create a new IP4 driver service binding private instance.
+
+ @param Controller The controller that has MNP service binding
+ installed
+ @param ImageHandle The IP4 driver's image handle
+ @param Service The variable to receive the newly created IP4
+ service.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource
+ @retval EFI_SUCCESS A new IP4 service binding private is created.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT IP4_SERVICE **Service
+ )
+{
+ IP4_SERVICE *IpSb;
+ EFI_STATUS Status;
+
+ ASSERT (Service != NULL);
+
+ *Service = NULL;
+
+ //
+ // allocate a service private data then initialize all the filed to
+ // empty resources, so if any thing goes wrong when allocating
+ // resources, Ip4CleanService can be called to clean it up.
+ //
+ IpSb = AllocateZeroPool (sizeof (IP4_SERVICE));
+
+ if (IpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IpSb->Signature = IP4_SERVICE_SIGNATURE;
+ IpSb->ServiceBinding.CreateChild = Ip4ServiceBindingCreateChild;
+ IpSb->ServiceBinding.DestroyChild = Ip4ServiceBindingDestroyChild;
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+
+ IpSb->NumChildren = 0;
+ InitializeListHead (&IpSb->Children);
+
+ InitializeListHead (&IpSb->Interfaces);
+ IpSb->DefaultInterface = NULL;
+ IpSb->DefaultRouteTable = NULL;
+
+ Ip4InitAssembleTable (&IpSb->Assemble);
+
+ IpSb->IgmpCtrl.Igmpv1QuerySeen = 0;
+ InitializeListHead (&IpSb->IgmpCtrl.Groups);
+
+ IpSb->Image = ImageHandle;
+ IpSb->Controller = Controller;
+
+ IpSb->MnpChildHandle = NULL;
+ IpSb->Mnp = NULL;
+
+ IpSb->MnpConfigData.ReceivedQueueTimeoutValue = 0;
+ IpSb->MnpConfigData.TransmitQueueTimeoutValue = 0;
+ IpSb->MnpConfigData.ProtocolTypeFilter = IP4_ETHER_PROTO;
+ IpSb->MnpConfigData.EnableUnicastReceive = TRUE;
+ IpSb->MnpConfigData.EnableMulticastReceive = TRUE;
+ IpSb->MnpConfigData.EnableBroadcastReceive = TRUE;
+ IpSb->MnpConfigData.EnablePromiscuousReceive = FALSE;
+ IpSb->MnpConfigData.FlushQueuesOnReset = TRUE;
+ IpSb->MnpConfigData.EnableReceiveTimestamps = FALSE;
+ IpSb->MnpConfigData.DisableBackgroundPolling = FALSE;
+
+ ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));
+
+ IpSb->Timer = NULL;
+
+ IpSb->ReconfigEvent = NULL;
+
+ IpSb->Reconfig = FALSE;
+
+ IpSb->MediaPresent = TRUE;
+
+ //
+ // Create various resources. First create the route table, timer
+ // event, ReconfigEvent and MNP child. IGMP, interface's initialization depend
+ // on the MNP child.
+ //
+ IpSb->DefaultRouteTable = Ip4CreateRouteTable ();
+
+ if (IpSb->DefaultRouteTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Ip4TimerTicking,
+ IpSb,
+ &IpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4AutoReconfigCallBack,
+ IpSb,
+ &IpSb->ReconfigEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibCreateServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &IpSb->MnpChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &IpSb->Mnp,
+ ImageHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4ServiceConfigMnp (IpSb, TRUE);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4InitIgmp (IpSb);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpSb->MacString = NULL;
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpSb->DefaultInterface = Ip4CreateInterface (IpSb->Mnp, Controller, ImageHandle);
+
+ if (IpSb->DefaultInterface == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);
+
+ ZeroMem (&IpSb->Ip4Config2Instance, sizeof (IP4_CONFIG2_INSTANCE));
+
+ Status = Ip4Config2InitInstance (&IpSb->Ip4Config2Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpSb->MaxPacketSize = IpSb->SnpMode.MaxPacketSize - sizeof (IP4_HEAD);
+ if (NetLibGetVlanId (IpSb->Controller) != 0) {
+ //
+ // This is a VLAN device, reduce MTU by VLAN tag length
+ //
+ IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN;
+ }
+ IpSb->OldMaxPacketSize = IpSb->MaxPacketSize;
+ *Service = IpSb;
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4CleanService (IpSb);
+ FreePool (IpSb);
+
+ return Status;
+}
+
+
+/**
+ Clean up a IP4 service binding instance. It will release all
+ the resource allocated by the instance. The instance may be
+ partly initialized, or partly destroyed. If a resource is
+ destroyed, it is marked as that in case the destroy failed and
+ being called again later.
+
+ @param[in] IpSb The IP4 service binding instance to clean up
+
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up
+ @retval other Failed to clean up some of the resources.
+
+**/
+EFI_STATUS
+Ip4CleanService (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ EFI_STATUS Status;
+
+ IpSb->State = IP4_SERVICE_DESTROY;
+
+ if (IpSb->Timer != NULL) {
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (IpSb->Timer);
+
+ IpSb->Timer = NULL;
+ }
+
+ if (IpSb->DefaultInterface != NULL) {
+ Status = Ip4FreeInterface (IpSb->DefaultInterface, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ IpSb->DefaultInterface = NULL;
+ }
+
+ if (IpSb->DefaultRouteTable != NULL) {
+ Ip4FreeRouteTable (IpSb->DefaultRouteTable);
+ IpSb->DefaultRouteTable = NULL;
+ }
+
+ Ip4CleanAssembleTable (&IpSb->Assemble);
+
+ if (IpSb->MnpChildHandle != NULL) {
+ if (IpSb->Mnp != NULL) {
+ gBS->CloseProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+
+ IpSb->Mnp = NULL;
+ }
+
+ NetLibDestroyServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ IpSb->MnpChildHandle
+ );
+
+ IpSb->MnpChildHandle = NULL;
+ }
+
+ if (IpSb->ReconfigEvent != NULL) {
+ gBS->CloseEvent (IpSb->ReconfigEvent);
+
+ IpSb->ReconfigEvent = NULL;
+ }
+
+ IpSb->Reconfig = FALSE;
+
+ if (IpSb->MacString != NULL) {
+ FreePool (IpSb->MacString);
+ }
+
+ Ip4Config2CleanInstance (&IpSb->Ip4Config2Instance);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE);
+ ServiceBinding = ((IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
+ NumberOfChildren = ((IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
+ ChildHandleBuffer = ((IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
+
+ if (!NetIsInHandleBuffer (IpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) {
+ return EFI_SUCCESS;
+ }
+
+ return ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);
+}
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ IP4_SERVICE *IpSb;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Cfg2;
+ UINTN Index;
+ IP4_CONFIG2_DATA_ITEM *DataItem;
+
+ IpSb = NULL;
+ Ip4Cfg2 = NULL;
+ DataItem = NULL;
+
+ //
+ // Test for the Ip4 service binding protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Ip4CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (IpSb != NULL);
+
+ Ip4Cfg2 = &IpSb->Ip4Config2Instance.Ip4Config2;
+
+ //
+ // Install the Ip4ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding,
+ &gEfiIp4Config2ProtocolGuid,
+ Ip4Cfg2,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_SERVICE;
+ }
+
+ //
+ // Read the config data from NV variable again.
+ // The default data can be changed by other drivers.
+ //
+ Status = Ip4Config2ReadConfigData (IpSb->MacString, &IpSb->Ip4Config2Instance);
+ if (EFI_ERROR (Status)) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ //
+ // Consume the installed EFI_IP4_CONFIG2_PROTOCOL to set the default data items.
+ //
+ for (Index = Ip4Config2DataTypePolicy; Index < Ip4Config2DataTypeMaximum; Index++) {
+ DataItem = &IpSb->Ip4Config2Instance.DataItem[Index];
+ if (DataItem->Data.Ptr != NULL) {
+ Status = Ip4Cfg2->SetData (
+ Ip4Cfg2,
+ Index,
+ DataItem->DataSize,
+ DataItem->Data.Ptr
+ );
+ if (EFI_ERROR(Status)) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ if (Index == Ip4Config2DataTypePolicy && (*(DataItem->Data.Policy) == Ip4Config2PolicyDhcp)) {
+ break;
+ }
+ }
+ }
+
+ //
+ // Ready to go: start the receiving and timer.
+ // Ip4Config2SetPolicy maybe call Ip4ReceiveFrame() to set the default interface's RecvRequest first after
+ // Ip4Config2 instance is initialized. So, EFI_ALREADY_STARTED is the allowed return status.
+ //
+ Status = Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);
+
+ if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ //
+ // Initialize the IP4 ID
+ //
+ mIp4Id = (UINT16)NET_RANDOM (NetRandomInitSeed ());
+
+ return Status;
+
+UNINSTALL_PROTOCOL:
+ gBS->UninstallProtocolInterface (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding
+ );
+
+FREE_SERVICE:
+ Ip4CleanService (IpSb);
+ FreePool (IpSb);
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @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
+Ip4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ IP4_SERVICE *IpSb;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ INTN State;
+ LIST_ENTRY *List;
+ IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
+ IP4_INTERFACE *IpIf;
+ IP4_ROUTE_TABLE *RouteTable;
+
+ BOOLEAN IsDhcp4;
+
+ IsDhcp4 = FALSE;
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+ if (NicHandle != NULL) {
+ IsDhcp4 = TRUE;
+ } else {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding);
+
+ if (IsDhcp4) {
+ Status = Ip4Config2DestroyDhcp4 (&IpSb->Ip4Config2Instance);
+ gBS->CloseEvent (IpSb->Ip4Config2Instance.Dhcp4Event);
+ IpSb->Ip4Config2Instance.Dhcp4Event = NULL;
+ } else if (NumberOfChildren != 0) {
+ List = &IpSb->Children;
+ Context.ServiceBinding = ServiceBinding;
+ Context.NumberOfChildren = NumberOfChildren;
+ Context.ChildHandleBuffer = ChildHandleBuffer;
+ Status = NetDestroyLinkList (
+ List,
+ Ip4DestroyChildEntryInHandleBuffer,
+ &Context,
+ NULL
+ );
+ } else if (IpSb->DefaultInterface->ArpHandle == ControllerHandle) {
+
+ //
+ // The ARP protocol for the default interface is being uninstalled and all
+ // its IP child handles should have been destroyed before. So, release the
+ // default interface and route table, create a new one and mark it as not started.
+ //
+ Ip4CancelReceive (IpSb->DefaultInterface);
+ Ip4FreeInterface (IpSb->DefaultInterface, NULL);
+ Ip4FreeRouteTable (IpSb->DefaultRouteTable);
+
+ IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);
+ if (IpIf == NULL) {
+ goto ON_ERROR;
+ }
+ RouteTable = Ip4CreateRouteTable ();
+ if (RouteTable == NULL) {
+ Ip4FreeInterface (IpIf, NULL);
+ goto ON_ERROR;;
+ }
+
+ IpSb->DefaultInterface = IpIf;
+ InsertHeadList (&IpSb->Interfaces, &IpIf->Link);
+ IpSb->DefaultRouteTable = RouteTable;
+ Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb);
+
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+
+ } else if (IsListEmpty (&IpSb->Children)) {
+ State = IpSb->State;
+ //
+ // OK, clean other resources then uninstall the service binding protocol.
+ //
+ Status = Ip4CleanService (IpSb);
+ if (EFI_ERROR (Status)) {
+ IpSb->State = State;
+ goto ON_ERROR;
+ }
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ ServiceBinding,
+ &gEfiIp4Config2ProtocolGuid,
+ &IpSb->Ip4Config2Instance.Ip4Config2,
+ NULL
+ );
+
+ if (gIp4ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gIp4ControllerNameTable);
+ gIp4ControllerNameTable = NULL;
+ }
+ FreePool (IpSb);
+ }
+
+ON_ERROR:
+ return Status;
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ VOID *Mnp;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (This);
+ IpInstance = AllocatePool (sizeof (IP4_PROTOCOL));
+
+ if (IpInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip4InitProtocol (IpSb, IpInstance);
+
+ //
+ // Install Ip4 onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &IpInstance->Ip4Proto,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpInstance->Handle = *ChildHandle;
+
+ //
+ // Open the Managed Network protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp,
+ gIp4DriverBinding.DriverBindingHandle,
+ IpInstance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &IpInstance->Ip4Proto,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Insert it into the service binding instance.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&IpSb->Children, &IpInstance->Link);
+ IpSb->NumChildren++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ON_ERROR:
+
+ if (EFI_ERROR (Status)) {
+
+ Ip4CleanProtocol (IpInstance);
+
+ FreePool (IpInstance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_PROTOCOL *Ip4;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (This);
+
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ gIp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (Ip4);
+
+ if (IpInstance->Service != IpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // A child can be destroyed more than once. For example,
+ // Ip4DriverBindingStop will destroy all of its children.
+ // when UDP driver is being stopped, it will destroy all
+ // the IP child it opens.
+ //
+ if (IpInstance->InDestroy) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ IpInstance->InDestroy = TRUE;
+
+ //
+ // Close the Managed Network protocol.
+ //
+ gBS->CloseProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gIp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ if (IpInstance->Interface != NULL && IpInstance->Interface->Arp != NULL) {
+ gBS->CloseProtocol (
+ IpInstance->Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ gIp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+ }
+
+ //
+ // Uninstall the IP4 protocol first. Many thing happens during
+ // this:
+ // 1. The consumer of the IP4 protocol will be stopped if it
+ // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is
+ // stopped, IP driver's stop function will be called, and uninstall
+ // EFI_IP4_PROTOCOL will trigger the UDP's stop function. This
+ // makes it possible to create the network stack bottom up, and
+ // stop it top down.
+ // 2. the upper layer will recycle the received packet. The recycle
+ // event's TPL is higher than this function. The recycle events
+ // will be called back before preceeding. If any packets not recycled,
+ // that means there is a resource leak.
+ //
+ gBS->RestoreTPL (OldTpl);
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &IpInstance->Ip4Proto
+ );
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (EFI_ERROR (Status)) {
+ IpInstance->InDestroy = FALSE;
+ goto ON_ERROR;
+ }
+
+ Status = Ip4CleanProtocol (IpInstance);
+ if (EFI_ERROR (Status)) {
+ gBS->InstallMultipleProtocolInterfaces (
+ &ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ Ip4,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ RemoveEntryList (&IpInstance->Link);
+ IpSb->NumChildren--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (IpInstance);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h
new file mode 100644
index 0000000000..8c5298b645
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h
@@ -0,0 +1,190 @@
+/** @file
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_DRIVER_H__
+#define __EFI_IP4_DRIVER_H__
+
+#include <Protocol/ServiceBinding.h>
+
+extern EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gIp4ComponentName2;
+extern EFI_UNICODE_STRING_TABLE *gIp4ControllerNameTable;
+
+typedef struct {
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+} IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
+
+//
+// Function prototype for the driver's entry point
+//
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for IP4 driver which install the driver
+ binding and component name protocol on its image.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+//
+// Function prototypes for the Drivr Binding Protocol
+//
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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
+Ip4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @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
+Ip4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+//
+// Function prototypes for the ServiceBinding Protocol
+//
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
new file mode 100644
index 0000000000..313528783d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
@@ -0,0 +1,115 @@
+## @file
+# This module produces EFI IPv4 Protocol and EFI IPv4 Service Binding Protocol.
+#
+# This module produces EFI IPv4 Protocol upon EFI MNP Protocol and EFI ARP Protocol,
+# to provide basic network IPv4 packet I/O services, which includes support for a
+# subset of the Internet Control Message Protocol (ICMP) and may include support for
+# the Internet Group Management Protocol (IGMP).
+#
+# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Ip4Dxe
+ MODULE_UNI_FILE = Ip4Dxe.uni
+ FILE_GUID = 9FB1A1F3-3B71-4324-B39A-745CBB015FFF
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Ip4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gIp4DriverBinding
+# COMPONENT_NAME = gIp4ComponentName
+# COMPONENT_NAME2 = gIp4ComponentName2
+#
+
+[Sources]
+ Ip4Driver.c
+ Ip4Option.h
+ Ip4Route.h
+ Ip4If.c
+ Ip4Igmp.h
+ Ip4Output.c
+ Ip4Icmp.c
+ Ip4Igmp.c
+ Ip4Impl.c
+ Ip4Common.h
+ Ip4Impl.h
+ Ip4Driver.h
+ Ip4Common.c
+ Ip4If.h
+ Ip4Option.c
+ Ip4Output.h
+ ComponentName.c
+ Ip4Input.h
+ Ip4Route.c
+ Ip4Icmp.h
+ Ip4Input.c
+ Ip4Config2Impl.c
+ Ip4Config2Impl.h
+ Ip4Config2.vfr
+ Ip4DxeStrings.uni
+ Ip4NvData.h
+ Ip4Config2Nv.h
+ Ip4Config2Nv.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DebugLib
+ NetLib
+ DpcLib
+ HiiLib
+ PrintLib
+ DevicePathLib
+ UefiHiiServicesLib
+
+[Protocols]
+ ## BY_START
+ ## UNDEFINED # variable
+ gEfiIp4ServiceBindingProtocolGuid
+ gEfiIp4ProtocolGuid ## BY_START
+ gEfiManagedNetworkServiceBindingProtocolGuid ## TO_START
+ gEfiManagedNetworkProtocolGuid ## TO_START
+ gEfiArpServiceBindingProtocolGuid ## TO_START
+ gEfiIp4Config2ProtocolGuid ## BY_START
+ gEfiArpProtocolGuid ## TO_START
+ gEfiDhcp4ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp4ProtocolGuid ## TO_START
+ gEfiIpSec2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiHiiConfigAccessProtocolGuid ## BY_START
+ gEfiDevicePathProtocolGuid ## TO_START
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch EFI_NIC_IP4_CONFIG_VARIABLE
+ ## SOMETIMES_PRODUCES ## GUID # HiiConstructConfigHdr EFI_NIC_IP4_CONFIG_VARIABLE
+ ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData EFI_NIC_IP4_CONFIG_VARIABLE
+ ## SOMETIMES_CONSUMES ## HII
+ gIp4Config2NvDataGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Ip4DxeExtra.uni
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.uni b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.uni
new file mode 100644
index 0000000000..e25b0242d6
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.uni
@@ -0,0 +1,25 @@
+// /** @file
+// This module produces EFI IPv4 Protocol and EFI IPv4 Service Binding Protocol.
+//
+// This module produces EFI IPv4 Protocol upon EFI MNP Protocol and EFI ARP Protocol,
+// to provide basic network IPv4 packet I/O services, which includes support for a
+// subset of the Internet Control Message Protocol (ICMP) and may include support for
+// the Internet Group Management Protocol (IGMP).
+//
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI IPv4 Protocol and EFI IPv4 Service Binding Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI IPv4 Protocol upon EFI MNP Protocol and EFI ARP Protocol to provide basic network IPv4 packet I/O services, which includes support for a subset of the Internet Control Message Protocol (ICMP), and may include support for the Internet Group Management Protocol (IGMP)."
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni
new file mode 100644
index 0000000000..9335e9cfa3
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Ip4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"IP v4 DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni
new file mode 100644
index 0000000000..a491378ab5
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni
@@ -0,0 +1,35 @@
+// /** @file
+// String definitions for Ip4Config2 formset
+
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+//**/
+
+
+/=#
+
+#langdef en-US "English"
+
+#string STR_IP4_CONFIG2_FORM_TITLE #language en-US "IPv4 Network Configuration"
+#string STR_IP4_CONFIG2_FORM_HELP #language en-US "Configure network parameters."
+#string STR_IP4_DEVICE_FORM_TITLE #language en-US ""
+#string STR_IP4_DEVICE_FORM_HELP #language en-US ""
+#string STR_IP4_CONFIGURE #language en-US "Configured"
+#string STR_IP4_ENABLE_DHCP #language en-US "Enable DHCP"
+#string STR_IP4_LOCAL_IP_ADDRESS #language en-US "Local IP Address"
+#string STR_IP4_IP_ADDRESS_HELP #language en-US "Enter IP address in dotted-decimal notation. Example: 192.168.10.12\r\n"
+#string STR_IP4_LOCAL_MASK #language en-US "Local NetMask"
+#string STR_IP4_MASK_HELP #language en-US "Enter NetMask in dotted-decimal notation. Example: 255.255.255.0\r\n"
+#string STR_IP4_LOCAL_GATEWAY #language en-US "Local Gateway"
+#string STR_IP4_GATEWAY_HELP #language en-US "Enter Gateway in dotted-decimal notation. Example: 192.168.10.1\r\n"
+#string STR_IP4_LOCAL_DNS #language en-US "Local DNS Servers"
+#string STR_IP4_DNS_HELP #language en-US "Enter DNS Servers in dotted-decimal notation. Example: 192.168.10.8 192.168.10.9\r\n"
+#string STR_SAVE_CHANGES #language en-US "Save Changes and Exit"
+#string STR_NULL #language en-US ""
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c
new file mode 100644
index 0000000000..b4b086496c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c
@@ -0,0 +1,366 @@
+/** @file
+
+Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+IP4_ICMP_CLASS
+mIcmpClass[] = {
+ {ICMP_ECHO_REPLY, ICMP_QUERY_MESSAGE },
+ {1, ICMP_INVALID_MESSAGE},
+ {2, ICMP_INVALID_MESSAGE},
+ {ICMP_DEST_UNREACHABLE, ICMP_ERROR_MESSAGE },
+ {ICMP_SOURCE_QUENCH, ICMP_ERROR_MESSAGE },
+ {ICMP_REDIRECT, ICMP_ERROR_MESSAGE },
+ {6, ICMP_INVALID_MESSAGE},
+ {7, ICMP_INVALID_MESSAGE},
+ {ICMP_ECHO_REQUEST, ICMP_QUERY_MESSAGE },
+ {9, ICMP_INVALID_MESSAGE},
+ {10, ICMP_INVALID_MESSAGE},
+ {ICMP_TIME_EXCEEDED, ICMP_ERROR_MESSAGE },
+ {ICMP_PARAMETER_PROBLEM, ICMP_ERROR_MESSAGE },
+ {ICMP_TIMESTAMP , ICMP_QUERY_MESSAGE },
+ {14, ICMP_INVALID_MESSAGE},
+ {ICMP_INFO_REQUEST , ICMP_QUERY_MESSAGE },
+ {ICMP_INFO_REPLY , ICMP_QUERY_MESSAGE },
+};
+
+EFI_IP4_ICMP_TYPE
+mIp4SupportedIcmp[23] = {
+ {ICMP_ECHO_REPLY, ICMP_DEFAULT_CODE },
+
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_PROTO_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_PORT_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_FRAGMENT_FAILED },
+ {ICMP_DEST_UNREACHABLE, ICMP_SOURCEROUTE_FAILED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNKNOWN },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNKNOWN },
+ {ICMP_DEST_UNREACHABLE, ICMP_SOURCE_ISOLATED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_PROHIBITED },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_PROHIBITED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE_TOS },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE_TOS},
+
+ {ICMP_SOURCE_QUENCH, ICMP_DEFAULT_CODE },
+
+ {ICMP_REDIRECT, ICMP_NET_REDIRECT },
+ {ICMP_REDIRECT, ICMP_HOST_REDIRECT },
+ {ICMP_REDIRECT, ICMP_NET_TOS_REDIRECT },
+ {ICMP_REDIRECT, ICMP_HOST_TOS_REDIRECT },
+
+ {ICMP_ECHO_REQUEST, ICMP_DEFAULT_CODE },
+
+ {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_IN_TRANSIT },
+ {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_REASSEMBLE },
+
+ {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE },
+};
+
+
+
+/**
+ Process the ICMP redirect. Find the instance then update
+ its route cache.
+
+ All kinds of redirect is treated as host redirect as
+ specified by RFC1122 3.3.1.2:
+ "Since the subnet mask appropriate to the destination
+ address is generally not known, a Network Redirect
+ message SHOULD be treated identically to a Host Redirect
+ message;"
+
+ @param[in] IpSb The IP4 service binding instance that received
+ the packet.
+ @param[in] Head The IP head of the received ICMPpacket.
+ @param[in] Packet The content of the ICMP redirect packet with IP
+ head removed.
+ @param[in] Icmp The buffer to store the ICMP error message if
+ something is wrong.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_SUCCESS Successfully updated the route caches
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpRedirect (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN IP4_ICMP_ERROR_HEAD *Icmp
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_PROTOCOL *Ip4Instance;
+ IP4_ROUTE_CACHE_ENTRY *CacheEntry;
+ IP4_INTERFACE *IpIf;
+ IP4_ADDR Gateway;
+ IP4_ADDR Src;
+ IP4_ADDR Dst;
+
+ //
+ // Find the interface whose IP address is the source of the
+ // orgianl IP packet.
+ //
+ IpIf = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src));
+ Gateway = NTOHL (Icmp->Fourth);
+
+ //
+ // discard the packet if the new gateway address it specifies
+ // is not on the same connected net through which the Redirect
+ // arrived. (RFC1122 3.2.2.2).
+ //
+ if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Update each IP child's route cache on the interface.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+
+ if (Ip4Instance->RouteTable == NULL) {
+ continue;
+ }
+
+ Dst = NTOHL (Icmp->IpHead.Dst);
+ Src = NTOHL (Icmp->IpHead.Src);
+ CacheEntry = Ip4FindRouteCache (Ip4Instance->RouteTable, Dst, Src);
+
+ //
+ // Only update the route cache's gateway if the source of the
+ // Redirect is the current first-hop gateway
+ //
+ if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) {
+ CacheEntry->NextHop = Gateway;
+ }
+ }
+
+ NetbufFree (Packet);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Process the ICMP error packet. If it is an ICMP redirect packet,
+ update call Ip4ProcessIcmpRedirect to update the IP instance's
+ route cache, otherwise, deliver the packet to upper layer.
+
+ @param[in] IpSb The IP4 service that received the packet.
+ @param[in] Head The IP4 head of the ICMP error packet
+ @param[in] Packet The content of the ICMP error with IP4 head
+ removed.
+
+ @retval EFI_SUCCESS The ICMP error is processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpError (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_ERROR_HEAD Icmp;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ //
+ // If it is an ICMP redirect error, update the route cache
+ // as RFC1122. Otherwise, demultiplex it to IP instances.
+ //
+ if (Icmp.Head.Type == ICMP_REDIRECT) {
+ return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp);
+ }
+
+ IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
+ return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
+}
+
+
+/**
+ Replay an ICMP echo request.
+
+ @param[in] IpSb The IP4 service that receivd the packet
+ @param[in] Head The IP4 head of the ICMP error packet
+ @param[in] Packet The content of the ICMP error with IP4 head
+ removed.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_SUCCESS The ICMP Echo request is successfully answered.
+ @retval Others Failed to answer the ICMP echo request.
+
+**/
+EFI_STATUS
+Ip4IcmpReplyEcho (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_QUERY_HEAD *Icmp;
+ NET_BUF *Data;
+ EFI_STATUS Status;
+ IP4_HEAD ReplyHead;
+
+ //
+ // make a copy the packet, it is really a bad idea to
+ // send the MNP's buffer back to MNP.
+ //
+ Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);
+
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Change the ICMP type to echo reply, exchange the source
+ // and destination, then send it. The source is updated to
+ // use specific destination. See RFC1122. SRR/RR option
+ // update is omitted.
+ //
+ Icmp = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL);
+ ASSERT (Icmp != NULL);
+ Icmp->Head.Type = ICMP_ECHO_REPLY;
+ Icmp->Head.Checksum = 0;
+ Icmp->Head.Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize));
+
+ ReplyHead.Tos = 0;
+ ReplyHead.Fragment = 0;
+ ReplyHead.Ttl = 64;
+ ReplyHead.Protocol = EFI_IP_PROTO_ICMP;
+ ReplyHead.Src = 0;
+
+ //
+ // Ip4Output will select a source for us
+ //
+ ReplyHead.Dst = Head->Src;
+
+ Status = Ip4Output (
+ IpSb,
+ NULL,
+ Data,
+ &ReplyHead,
+ NULL,
+ 0,
+ IP4_ALLZERO_ADDRESS,
+ Ip4SysPacketSent,
+ NULL
+ );
+
+ON_EXIT:
+ NetbufFree (Packet);
+ return Status;
+}
+
+
+/**
+ Process the ICMP query message. If it is an ICMP echo
+ request, answer it. Otherwise deliver it to upper layer.
+
+ @param[in] IpSb The IP4 service that receivd the packet
+ @param[in] Head The IP4 head of the ICMP query packet
+ @param[in] Packet The content of the ICMP query with IP4 head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is invalid
+ @retval EFI_SUCCESS The ICMP query message is processed
+ @retval Others Failed to process ICMP query.
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpQuery (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_QUERY_HEAD Icmp;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Head.Type == ICMP_ECHO_REQUEST) {
+ return Ip4IcmpReplyEcho (IpSb, Head, Packet);
+ }
+
+ return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
+}
+
+
+/**
+ Handle the ICMP packet. First validate the message format,
+ then according to the message types, process it as query or
+ error packet.
+
+ @param[in] IpSb The IP4 service that receivd the packet.
+ @param[in] Head The IP4 head of the ICMP query packet.
+ @param[in] Packet The content of the ICMP query with IP4 head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_SUCCESS The ICMP message is successfully processed.
+ @retval Others Failed to handle ICMP packet.
+
+**/
+EFI_STATUS
+Ip4IcmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_HEAD Icmp;
+ UINT16 Checksum;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ goto DROP;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Type > ICMP_TYPE_MAX) {
+ goto DROP;
+ }
+
+ Checksum = (UINT16) (~NetbufChecksum (Packet));
+ if ((Icmp.Checksum != 0) && (Checksum != 0)) {
+ goto DROP;
+ }
+
+ if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
+ return Ip4ProcessIcmpError (IpSb, Head, Packet);
+
+ } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) {
+ return Ip4ProcessIcmpQuery (IpSb, Head, Packet);
+
+ }
+
+DROP:
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h
new file mode 100644
index 0000000000..30199dced6
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h
@@ -0,0 +1,103 @@
+/** @file
+ Header file for ICMP protocol.
+
+Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_ICMP_H__
+#define __EFI_IP4_ICMP_H__
+
+ //
+ // ICMP type definations
+ //
+#define ICMP_ECHO_REPLY 0
+#define ICMP_DEST_UNREACHABLE 3
+#define ICMP_SOURCE_QUENCH 4
+#define ICMP_REDIRECT 5
+#define ICMP_ECHO_REQUEST 8
+#define ICMP_TIME_EXCEEDED 11
+#define ICMP_PARAMETER_PROBLEM 12
+#define ICMP_TIMESTAMP 13
+#define ICMP_INFO_REQUEST 15
+#define ICMP_INFO_REPLY 16
+#define ICMP_TYPE_MAX ICMP_INFO_REPLY
+
+#define ICMP_DEFAULT_CODE 0
+
+ //
+ // ICMP code definations for ICMP_DEST_UNREACHABLE
+ //
+#define ICMP_NET_UNREACHABLE 0
+#define ICMP_HOST_UNREACHABLE 1
+#define ICMP_PROTO_UNREACHABLE 2 // Host may generate
+#define ICMP_PORT_UNREACHABLE 3 // Host may generate
+#define ICMP_FRAGMENT_FAILED 4
+#define ICMP_SOURCEROUTE_FAILED 5 // Host may generate
+#define ICMP_NET_UNKNOWN 6
+#define ICMP_HOST_UNKNOWN 7
+#define ICMP_SOURCE_ISOLATED 8
+#define ICMP_NET_PROHIBITED 9
+#define ICMP_HOST_PROHIBITED 10
+#define ICMP_NET_UNREACHABLE_TOS 11
+#define ICMP_HOST_UNREACHABLE_TOS 12
+
+ //
+ // ICMP code definations for ICMP_TIME_EXCEEDED
+ //
+#define ICMP_TIMEOUT_IN_TRANSIT 0
+#define ICMP_TIMEOUT_REASSEMBLE 1 // Host may generate
+
+ //
+ // ICMP code definations for ICMP_TIME_EXCEEDED
+ //
+#define ICMP_NET_REDIRECT 0
+#define ICMP_HOST_REDIRECT 1
+#define ICMP_NET_TOS_REDIRECT 2
+#define ICMP_HOST_TOS_REDIRECT 3
+
+ //
+ // ICMP message classes, each class of ICMP message shares
+ // a common message format. INVALID_MESSAGE is only a flag.
+ //
+#define ICMP_INVALID_MESSAGE 0
+#define ICMP_ERROR_MESSAGE 1
+#define ICMP_QUERY_MESSAGE 2
+
+typedef struct {
+ UINT8 IcmpType;
+ UINT8 IcmpClass;
+} IP4_ICMP_CLASS;
+
+extern IP4_ICMP_CLASS mIcmpClass[];
+extern EFI_IP4_ICMP_TYPE mIp4SupportedIcmp[];
+
+/**
+ Handle the ICMP packet. First validate the message format,
+ then according to the message types, process it as query or
+ error packet.
+
+ @param[in] IpSb The IP4 service that receivd the packet.
+ @param[in] Head The IP4 head of the ICMP query packet.
+ @param[in] Packet The content of the ICMP query with IP4 head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_SUCCESS The ICMP message is successfully processed.
+ @retval Others Failed to handle ICMP packet.
+
+**/
+EFI_STATUS
+Ip4IcmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ );
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c
new file mode 100644
index 0000000000..7512a00f4b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c
@@ -0,0 +1,1254 @@
+/** @file
+ Implement IP4 pesudo interface.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+//
+// Mac address with all zero, used to determine whethter the ARP
+// resolve succeeded. Failed ARP requests zero the MAC address buffer.
+//
+EFI_MAC_ADDRESS mZeroMacAddress;
+
+/**
+ Callback funtion when frame transmission is finished. It will
+ call the frame owner's callback function to tell it the result.
+
+ @param[in] Context Context which is point to the token.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameSentDpc (
+ IN VOID *Context
+ );
+
+/**
+ Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The transmit token's event.
+ @param[in] Context Context which is point to the token.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Callback function when ARP request are finished. It will cancelled
+ all the queued frame if the ARP requests failed. Or transmit them
+ if the request succeed.
+
+ @param[in] Context The context of the callback, a point to the ARP
+ queue
+
+**/
+VOID
+EFIAPI
+Ip4OnArpResolvedDpc (
+ IN VOID *Context
+ );
+
+/**
+ Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The Arp request event.
+ @param Context The context of the callback, a point to the ARP
+ queue.
+
+**/
+VOID
+EFIAPI
+Ip4OnArpResolved (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Received a frame from MNP, wrap it in net buffer then deliver
+ it to IP's input function. The ownship of the packet also
+ transferred to IP. When Ip is finished with this packet, it
+ will call NetbufFree to release the packet, NetbufFree will
+ again call the Ip4RecycleFrame to signal MNP's event and free
+ the token used.
+
+ @param Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameReceivedDpc (
+ IN VOID *Context
+ );
+
+/**
+ Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The receive event delivered to MNP for receive.
+ @param Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Remove all the frames on the ARP queue that pass the FrameToCancel,
+ that is, either FrameToCancel is NULL or it returns true for the frame.
+
+ @param[in] ArpQue ARP frame to remove the frames from.
+ @param[in] IoStatus The status returned to the cancelled frames'
+ callback function.
+ @param[in] FrameToCancel Function to select which frame to cancel.
+ @param[in] Context Opaque parameter to the FrameToCancel.
+
+**/
+VOID
+Ip4CancelFrameArp (
+ IN IP4_ARP_QUE *ArpQue,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context
+ );
+
+
+/**
+ Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN.
+
+ @param[in] Interface The interface to send out to.
+ @param[in] IpInstance The IpInstance that transmit the packet. NULL if
+ the packet is sent by the IP4 driver itself.
+ @param[in] Packet The packet to transmit
+ @param[in] CallBack Call back function to execute if transmission
+ finished.
+ @param[in] Context Opaque parameter to the call back.
+
+ @retval Token The wrapped token if succeed
+ @retval NULL The wrapped token if NULL
+
+**/
+IP4_LINK_TX_TOKEN *
+Ip4WrapLinkTxToken (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData;
+ IP4_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT32 Count;
+
+ Token = AllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \
+ (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));
+
+ if (Token == NULL) {
+ return NULL;
+ }
+
+ Token->Signature = IP4_FRAME_TX_SIGNATURE;
+ InitializeListHead (&Token->Link);
+
+ Token->Interface = Interface;
+ Token->IpInstance = IpInstance;
+ Token->CallBack = CallBack;
+ Token->Packet = Packet;
+ Token->Context = Context;
+ CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (Token->DstMac));
+ CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (Token->SrcMac));
+
+ MnpToken = &(Token->MnpToken);
+ MnpToken->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4OnFrameSent,
+ Token,
+ &MnpToken->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Token);
+ return NULL;
+ }
+
+ MnpTxData = &Token->MnpTxData;
+ MnpToken->Packet.TxData = MnpTxData;
+
+ MnpTxData->DestinationAddress = &Token->DstMac;
+ MnpTxData->SourceAddress = &Token->SrcMac;
+ MnpTxData->ProtocolType = IP4_ETHER_PROTO;
+ MnpTxData->DataLength = Packet->TotalSize;
+ MnpTxData->HeaderLength = 0;
+
+ Count = Packet->BlockOpNum;
+
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);
+ MnpTxData->FragmentCount = (UINT16)Count;
+
+ return Token;
+}
+
+
+/**
+ Free the link layer transmit token. It will close the event
+ then free the memory used.
+
+ @param[in] Token Token to free
+
+**/
+VOID
+Ip4FreeLinkTxToken (
+ IN IP4_LINK_TX_TOKEN *Token
+ )
+{
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);
+
+ gBS->CloseEvent (Token->MnpToken.Event);
+ FreePool (Token);
+}
+
+
+/**
+ Create an IP_ARP_QUE structure to request ARP service.
+
+ @param[in] Interface The interface to send ARP from.
+ @param[in] DestIp The destination IP (host byte order) to request MAC
+ for
+
+ @return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL.
+
+**/
+IP4_ARP_QUE *
+Ip4CreateArpQue (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_ADDR DestIp
+ )
+{
+ IP4_ARP_QUE *ArpQue;
+ EFI_STATUS Status;
+
+ ArpQue = AllocatePool (sizeof (IP4_ARP_QUE));
+
+ if (ArpQue == NULL) {
+ return NULL;
+ }
+
+ ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE;
+ InitializeListHead (&ArpQue->Link);
+
+ InitializeListHead (&ArpQue->Frames);
+ ArpQue->Interface = Interface;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4OnArpResolved,
+ ArpQue,
+ &ArpQue->OnResolved
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (ArpQue);
+ return NULL;
+ }
+
+ ArpQue->Ip = DestIp;
+ CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (ArpQue->Mac));
+
+ return ArpQue;
+}
+
+
+/**
+ Remove all the transmit requests queued on the ARP queue, then free it.
+
+ @param[in] ArpQue Arp queue to free
+ @param[in] IoStatus The transmit status returned to transmit requests'
+ callback.
+
+**/
+VOID
+Ip4FreeArpQue (
+ IN IP4_ARP_QUE *ArpQue,
+ IN EFI_STATUS IoStatus
+ )
+{
+ NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);
+
+ //
+ // Remove all the frame waiting the ARP response
+ //
+ Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL);
+
+ gBS->CloseEvent (ArpQue->OnResolved);
+ FreePool (ArpQue);
+}
+
+
+/**
+ Create a link layer receive token to wrap the receive request
+
+ @param[in] Interface The interface to receive from
+ @param[in] IpInstance The instance that request the receive (NULL for IP4
+ driver itself)
+ @param[in] CallBack Call back function to execute when finished.
+ @param[in] Context Opaque parameters to the callback
+
+ @return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL.
+
+**/
+IP4_LINK_RX_TOKEN *
+Ip4CreateLinkRxToken (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ IP4_LINK_RX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ Token = AllocatePool (sizeof (IP4_LINK_RX_TOKEN));
+ if (Token == NULL) {
+ return NULL;
+ }
+
+ Token->Signature = IP4_FRAME_RX_SIGNATURE;
+ Token->Interface = Interface;
+ Token->IpInstance = IpInstance;
+ Token->CallBack = CallBack;
+ Token->Context = Context;
+
+ MnpToken = &Token->MnpToken;
+ MnpToken->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4OnFrameReceived,
+ Token,
+ &MnpToken->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Token);
+ return NULL;
+ }
+
+ MnpToken->Packet.RxData = NULL;
+ return Token;
+}
+
+
+/**
+ Free the link layer request token. It will close the event
+ then free the memory used.
+
+ @param[in] Token Request token to free.
+
+**/
+VOID
+Ip4FreeFrameRxToken (
+ IN IP4_LINK_RX_TOKEN *Token
+ )
+{
+
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);
+
+ gBS->CloseEvent (Token->MnpToken.Event);
+ FreePool (Token);
+}
+
+
+/**
+ Remove all the frames on the ARP queue that pass the FrameToCancel,
+ that is, either FrameToCancel is NULL or it returns true for the frame.
+
+ @param[in] ArpQue ARP frame to remove the frames from.
+ @param[in] IoStatus The status returned to the cancelled frames'
+ callback function.
+ @param[in] FrameToCancel Function to select which frame to cancel.
+ @param[in] Context Opaque parameter to the FrameToCancel.
+
+**/
+VOID
+Ip4CancelFrameArp (
+ IN IP4_ARP_QUE *ArpQue,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_LINK_TX_TOKEN *Token;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
+ Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
+
+ if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
+ RemoveEntryList (Entry);
+
+ Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);
+ Ip4FreeLinkTxToken (Token);
+ }
+ }
+}
+
+
+/**
+ Remove all the frames on the interface that pass the FrameToCancel,
+ either queued on ARP queues or that have already been delivered to
+ MNP and not yet recycled.
+
+ @param[in] Interface Interface to remove the frames from.
+ @param[in] IoStatus The transmit status returned to the frames'
+ callback.
+ @param[in] FrameToCancel Function to select the frame to cancel, NULL to
+ select all.
+ @param[in] Context Opaque parameters passed to FrameToCancel.
+
+**/
+VOID
+Ip4CancelFrames (
+ IN IP4_INTERFACE *Interface,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ARP_QUE *ArpQue;
+ IP4_LINK_TX_TOKEN *Token;
+
+ //
+ // Cancel all the pending frames on ARP requests
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);
+
+ Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context);
+
+ if (IsListEmpty (&ArpQue->Frames)) {
+ Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved);
+ }
+ }
+
+ //
+ // Cancel all the frames that have been delivered to MNP
+ // but not yet recycled.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {
+ Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
+
+ if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
+ Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);
+ }
+ }
+}
+
+
+/**
+ Create an IP4_INTERFACE. Delay the creation of ARP instance until
+ the interface is configured.
+
+ @param[in] Mnp The shared MNP child of this IP4 service binding
+ instance.
+ @param[in] Controller The controller this IP4 service binding instance
+ is installed. Most like the UNDI handle.
+ @param[in] ImageHandle This driver's image handle.
+
+ @return Point to the created IP4_INTERFACE, otherwise NULL.
+
+**/
+IP4_INTERFACE *
+Ip4CreateInterface (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ IP4_INTERFACE *Interface;
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ Interface = AllocatePool (sizeof (IP4_INTERFACE));
+
+ if ((Interface == NULL) || (Mnp == NULL)) {
+ return NULL;
+ }
+
+ Interface->Signature = IP4_INTERFACE_SIGNATURE;
+ InitializeListHead (&Interface->Link);
+ Interface->RefCnt = 1;
+
+ Interface->Ip = IP4_ALLZERO_ADDRESS;
+ Interface->SubnetMask = IP4_ALLZERO_ADDRESS;
+ Interface->Configured = FALSE;
+
+ Interface->Controller = Controller;
+ Interface->Image = ImageHandle;
+ Interface->Mnp = Mnp;
+ Interface->Arp = NULL;
+ Interface->ArpHandle = NULL;
+
+ InitializeListHead (&Interface->ArpQues);
+ InitializeListHead (&Interface->SentFrames);
+
+ Interface->RecvRequest = NULL;
+
+ //
+ // Get the interface's Mac address and broadcast mac address from SNP
+ //
+ if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) {
+ FreePool (Interface);
+ return NULL;
+ }
+
+ CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (Interface->Mac));
+ CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (Interface->BroadcastMac));
+ Interface->HwaddrLen = SnpMode.HwAddressSize;
+
+ InitializeListHead (&Interface->IpInstances);
+ Interface->PromiscRecv = FALSE;
+
+ return Interface;
+}
+
+
+/**
+ Set the interface's address, create and configure
+ the ARP child if necessary.
+
+ @param Interface The interface to set the address.
+ @param IpAddr The interface's IP address.
+ @param SubnetMask The interface's netmask.
+
+ @retval EFI_SUCCESS The interface is configured with Ip/netmask pair,
+ and a ARP is created for it.
+ @retval Others Failed to set the interface's address.
+
+**/
+EFI_STATUS
+Ip4SetAddress (
+ IN OUT IP4_INTERFACE *Interface,
+ IN IP4_ADDR IpAddr,
+ IN IP4_ADDR SubnetMask
+ )
+{
+ EFI_ARP_CONFIG_DATA ArpConfig;
+ EFI_STATUS Status;
+
+ NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
+
+ //
+ // Set the ip/netmask, then compute the subnet broadcast
+ // and network broadcast for easy access. When computing
+ // nework broadcast, the subnet mask is most like longer
+ // than the default netmask (not subneted) as defined in
+ // RFC793. If that isn't the case, we are aggregating the
+ // networks, use the subnet's mask instead.
+ //
+ Interface->Ip = IpAddr;
+ Interface->SubnetMask = SubnetMask;
+ Interface->SubnetBrdcast = (IpAddr | ~SubnetMask);
+ Interface->NetBrdcast = (IpAddr | ~SubnetMask);
+
+ //
+ // Do clean up for Arp child
+ //
+ if (Interface->ArpHandle != NULL) {
+ if (Interface->Arp != NULL) {
+ gBS->CloseProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ Interface->Image,
+ Interface->Controller
+ );
+
+ Interface->Arp = NULL;
+ }
+
+ NetLibDestroyServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Interface->ArpHandle
+ );
+
+ Interface->ArpHandle = NULL;
+ }
+
+ //
+ // If the address is NOT all zero, create then configure an ARP child.
+ // Pay attention: DHCP configures its station address as 0.0.0.0/0
+ //
+ if (IpAddr != IP4_ALLZERO_ADDRESS) {
+ Status = NetLibCreateServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Interface->ArpHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **) &Interface->Arp,
+ Interface->Image,
+ Interface->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpAddr = HTONL (IpAddr);
+ ArpConfig.SwAddressType = IP4_ETHER_PROTO;
+ ArpConfig.SwAddressLength = 4;
+ ArpConfig.StationAddress = &IpAddr;
+ ArpConfig.EntryTimeOut = 0;
+ ArpConfig.RetryCount = 0;
+ ArpConfig.RetryTimeOut = 0;
+
+ Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig);
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ Interface->Image,
+ Interface->Controller
+ );
+
+ goto ON_ERROR;
+ }
+ }
+
+ Interface->Configured = TRUE;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ NetLibDestroyServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Interface->ArpHandle
+ );
+
+ return Status;
+}
+
+
+/**
+ Filter function to cancel all the frame related to an IP instance.
+
+ @param[in] Frame The transmit request to test whether to cancel
+ @param[in] Context The context which is the Ip instance that issued
+ the transmit.
+
+ @retval TRUE The frame belongs to this instance and is to be
+ removed
+ @retval FALSE The frame doesn't belong to this instance.
+
+**/
+BOOLEAN
+Ip4CancelInstanceFrame (
+ IN IP4_LINK_TX_TOKEN *Frame,
+ IN VOID *Context
+ )
+{
+ if (Frame->IpInstance == (IP4_PROTOCOL *) Context) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+/**
+ If there is a pending receive request, cancel it. Don't call
+ the receive request's callback because this function can be only
+ called if the instance or driver is tearing itself down. It
+ doesn't make sense to call it back. But it is necessary to call
+ the transmit token's callback to give it a chance to free the
+ packet and update the upper layer's transmit request status, say
+ that from the UDP.
+
+ @param[in] Interface The interface used by the IpInstance
+
+**/
+VOID
+Ip4CancelReceive (
+ IN IP4_INTERFACE *Interface
+ )
+{
+ EFI_TPL OldTpl;
+ IP4_LINK_RX_TOKEN *Token;
+
+ if ((Token = Interface->RecvRequest) != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Interface->RecvRequest = NULL;
+ Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);
+
+ gBS->RestoreTPL (OldTpl);
+ }
+}
+
+
+/**
+ Free the interface used by IpInstance. All the IP instance with
+ the same Ip/Netmask pair share the same interface. It is reference
+ counted. All the frames haven't been sent will be cancelled.
+ Because the IpInstance is optional, the caller must remove
+ IpInstance from the interface's instance list itself.
+
+ @param[in] Interface The interface used by the IpInstance.
+ @param[in] IpInstance The Ip instance that free the interface. NULL if
+ the Ip driver is releasing the default interface.
+
+ @retval EFI_SUCCESS The interface use IpInstance is freed.
+
+**/
+EFI_STATUS
+Ip4FreeInterface (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL
+ )
+{
+ NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
+ ASSERT (Interface->RefCnt > 0);
+
+ //
+ // Remove all the pending transmit token related to this IP instance.
+ //
+ Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance);
+
+ if (--Interface->RefCnt > 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Destroy the interface if this is the last IP instance that
+ // has the address. Remove all the system transmitted packets
+ // from this interface, cancel the receive request if there is
+ // one, and destroy the ARP requests.
+ //
+ Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL);
+ Ip4CancelReceive (Interface);
+
+ ASSERT (IsListEmpty (&Interface->IpInstances));
+ ASSERT (IsListEmpty (&Interface->ArpQues));
+ ASSERT (IsListEmpty (&Interface->SentFrames));
+
+ if (Interface->Arp != NULL) {
+ gBS->CloseProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ Interface->Image,
+ Interface->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ Interface->ArpHandle
+ );
+ }
+
+ RemoveEntryList (&Interface->Link);
+ FreePool (Interface);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Callback function when ARP request are finished. It will cancelled
+ all the queued frame if the ARP requests failed. Or transmit them
+ if the request succeed.
+
+ @param[in] Context The context of the callback, a point to the ARP
+ queue
+
+**/
+VOID
+EFIAPI
+Ip4OnArpResolvedDpc (
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ARP_QUE *ArpQue;
+ IP4_INTERFACE *Interface;
+ IP4_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ ArpQue = (IP4_ARP_QUE *) Context;
+ NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);
+
+ RemoveEntryList (&ArpQue->Link);
+
+ //
+ // ARP resolve failed for some reason. Release all the frame
+ // and ARP queue itself. Ip4FreeArpQue will call the frame's
+ // owner back.
+ //
+ if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) {
+ Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);
+
+ return ;
+ }
+
+ //
+ // ARP resolve succeeded, Transmit all the frame. Release the ARP
+ // queue. It isn't necessary for us to cache the ARP binding because
+ // we always check the ARP cache first before transmit.
+ //
+ Interface = ArpQue->Interface;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
+ RemoveEntryList (Entry);
+
+ Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
+ CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (Token->DstMac));
+
+ //
+ // Insert the tx token before transmitting it via MNP as the FrameSentDpc
+ // may be called before Mnp->Transmit returns which will remove this tx
+ // token from the SentFrames list. Remove it from the list if the returned
+ // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the
+ // FrameSentDpc won't be queued.
+ //
+ InsertTailList (&Interface->SentFrames, &Token->Link);
+
+ Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);
+ if (EFI_ERROR (Status)) {
+ RemoveEntryList (Entry);
+ Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context);
+
+ Ip4FreeLinkTxToken (Token);
+ continue;
+ }
+ }
+
+ Ip4FreeArpQue (ArpQue, EFI_SUCCESS);
+}
+
+/**
+ Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The Arp request event.
+ @param Context The context of the callback, a point to the ARP
+ queue.
+
+**/
+VOID
+EFIAPI
+Ip4OnArpResolved (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, Ip4OnArpResolvedDpc, Context);
+}
+
+
+
+/**
+ Callback funtion when frame transmission is finished. It will
+ call the frame owner's callback function to tell it the result.
+
+ @param[in] Context Context which is point to the token.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameSentDpc (
+ IN VOID *Context
+ )
+{
+ IP4_LINK_TX_TOKEN *Token;
+
+ Token = (IP4_LINK_TX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);
+
+ RemoveEntryList (&Token->Link);
+
+ Token->CallBack (
+ Token->IpInstance,
+ Token->Packet,
+ Token->MnpToken.Status,
+ 0,
+ Token->Context
+ );
+
+ Ip4FreeLinkTxToken (Token);
+}
+
+/**
+ Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The transmit token's event.
+ @param[in] Context Context which is point to the token.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, Ip4OnFrameSentDpc, Context);
+}
+
+
+
+/**
+ Send a frame from the interface. If the next hop is broadcast or
+ multicast address, it is transmitted immediately. If the next hop
+ is a unicast, it will consult ARP to resolve the NextHop's MAC.
+ If some error happened, the CallBack won't be called. So, the caller
+ must test the return value, and take action when there is an error.
+
+ @param[in] Interface The interface to send the frame from
+ @param[in] IpInstance The IP child that request the transmission. NULL
+ if it is the IP4 driver itself.
+ @param[in] Packet The packet to transmit.
+ @param[in] NextHop The immediate destination to transmit the packet
+ to.
+ @param[in] CallBack Function to call back when transmit finished.
+ @param[in] Context Opaque parameter to the call back.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop
+ @retval EFI_SUCCESS The packet is successfully transmitted.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip4SendFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN IP4_ADDR NextHop,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ IP4_LINK_TX_TOKEN *Token;
+ LIST_ENTRY *Entry;
+ IP4_ARP_QUE *ArpQue;
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_STATUS Status;
+
+ ASSERT (Interface->Configured);
+
+ Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);
+
+ if (Token == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get the destination MAC address for multicast and broadcasts.
+ // Don't depend on ARP to solve the address since there maybe no
+ // ARP at all. Ip4Output has set NextHop to 255.255.255.255 for
+ // all the broadcasts.
+ //
+ if (NextHop == IP4_ALLONE_ADDRESS) {
+ CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (Token->DstMac));
+ goto SEND_NOW;
+
+ } else if (IP4_IS_MULTICAST (NextHop)) {
+
+ Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ goto SEND_NOW;
+ }
+
+ //
+ // Can only send out multicast/broadcast if the IP address is zero
+ //
+ if ((Arp = Interface->Arp) == NULL) {
+ Status = EFI_NO_MAPPING;
+ goto ON_ERROR;
+ }
+
+ //
+ // First check whether this binding is in the ARP cache.
+ //
+ NextHop = HTONL (NextHop);
+ Status = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac);
+
+ if (Status == EFI_SUCCESS) {
+ goto SEND_NOW;
+
+ } else if (Status != EFI_NOT_READY) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Have to do asynchronous ARP resolution. First check
+ // whether there is already a pending request.
+ //
+ ArpQue = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);
+
+ if (ArpQue->Ip == NextHop) {
+ break;
+ }
+ }
+
+ //
+ // Found a pending ARP request, enqueue the frame then return
+ //
+ if (Entry != &Interface->ArpQues) {
+ InsertTailList (&ArpQue->Frames, &Token->Link);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // First frame to NextHop, issue an asynchronous ARP requests
+ //
+ ArpQue = Ip4CreateArpQue (Interface, NextHop);
+
+ if (ArpQue == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr);
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
+ Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);
+ goto ON_ERROR;
+ }
+
+ InsertHeadList (&ArpQue->Frames, &Token->Link);
+ InsertHeadList (&Interface->ArpQues, &ArpQue->Link);
+ return EFI_SUCCESS;
+
+SEND_NOW:
+ //
+ // Insert the tx token into the SentFrames list before calling Mnp->Transmit.
+ // Remove it if the returned status is not EFI_SUCCESS.
+ //
+ InsertTailList (&Interface->SentFrames, &Token->Link);
+ Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);
+ if (EFI_ERROR (Status)) {
+ RemoveEntryList (&Interface->SentFrames);
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4FreeLinkTxToken (Token);
+ return Status;
+}
+
+
+/**
+ Call back function when the received packet is freed.
+ Check Ip4OnFrameReceived for information.
+
+ @param Context Context, which is the IP4_LINK_RX_TOKEN.
+
+**/
+VOID
+EFIAPI
+Ip4RecycleFrame (
+ IN VOID *Context
+ )
+{
+ IP4_LINK_RX_TOKEN *Frame;
+
+ Frame = (IP4_LINK_RX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE);
+
+ gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent);
+ Ip4FreeFrameRxToken (Frame);
+}
+
+
+/**
+ Received a frame from MNP, wrap it in net buffer then deliver
+ it to IP's input function. The ownship of the packet also
+ transferred to IP. When Ip is finished with this packet, it
+ will call NetbufFree to release the packet, NetbufFree will
+ again call the Ip4RecycleFrame to signal MNP's event and free
+ the token used.
+
+ @param Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameReceivedDpc (
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData;
+ IP4_LINK_RX_TOKEN *Token;
+ NET_FRAGMENT Netfrag;
+ NET_BUF *Packet;
+ UINT32 Flag;
+
+ Token = (IP4_LINK_RX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);
+
+ //
+ // First clear the interface's receive request in case the
+ // caller wants to call Ip4ReceiveFrame in the callback.
+ //
+ Token->Interface->RecvRequest = NULL;
+
+ MnpToken = &Token->MnpToken;
+ MnpRxData = MnpToken->Packet.RxData;
+
+ if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {
+ Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context);
+ Ip4FreeFrameRxToken (Token);
+
+ return ;
+ }
+
+ //
+ // Wrap the frame in a net buffer then deliever it to IP input.
+ // IP will reassemble the packet, and deliver it to upper layer
+ //
+ Netfrag.Len = MnpRxData->DataLength;
+ Netfrag.Bulk = MnpRxData->PacketData;
+
+ Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token);
+
+ if (Packet == NULL) {
+ gBS->SignalEvent (MnpRxData->RecycleEvent);
+
+ Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);
+ Ip4FreeFrameRxToken (Token);
+
+ return ;
+ }
+
+ Flag = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0);
+ Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0);
+ Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0);
+
+ Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context);
+}
+
+/**
+ Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The receive event delivered to MNP for receive.
+ @param Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip4OnFrameReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, Ip4OnFrameReceivedDpc, Context);
+}
+
+
+/**
+ Request to receive the packet from the interface.
+
+ @param[in] Interface The interface to receive the frames from.
+ @param[in] IpInstance The instance that requests the receive. NULL for
+ the driver itself.
+ @param[in] CallBack Function to call when receive finished.
+ @param[in] Context Opaque parameter to the callback.
+
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive.
+ @retval EFI_SUCCESS The recieve request has been started.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip4ReceiveFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ IP4_LINK_RX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
+
+ if (Interface->RecvRequest != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context);
+
+ if (Token == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Interface->RecvRequest = Token;
+ Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken);
+ if (EFI_ERROR (Status)) {
+ Interface->RecvRequest = NULL;
+ Ip4FreeFrameRxToken (Token);
+ return Status;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h
new file mode 100644
index 0000000000..577286360b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h
@@ -0,0 +1,343 @@
+/** @file
+ Definition for IP4 pesudo interface structure.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_IF_H__
+#define __EFI_IP4_IF_H__
+
+#define IP4_FRAME_RX_SIGNATURE SIGNATURE_32 ('I', 'P', 'F', 'R')
+#define IP4_FRAME_TX_SIGNATURE SIGNATURE_32 ('I', 'P', 'F', 'T')
+#define IP4_FRAME_ARP_SIGNATURE SIGNATURE_32 ('I', 'P', 'F', 'A')
+#define IP4_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', 'I', 'F')
+
+/**
+ This prototype is used by both receive and transmission.
+ When receiving Netbuf is allocated by IP4_INTERFACE, and
+ released by IP4. Flag shows whether the frame is received
+ as link broadcast/multicast...
+
+ When transmitting, the Netbuf is from IP4, and provided
+ to the callback as a reference. Flag isn't used.
+
+ @param[in] IpInstance The instance that sent or received the packet.
+ IpInstance can be NULL which means that it is the IP4 driver
+ itself sending the packets. IP4 driver may send packets that
+ don't belong to any instance, such as ICMP errors, ICMP echo
+ responses, or IGMP packets. IpInstance is used as a tag in
+ this module.
+ @param[in] Packet The sent or received packet.
+ @param[in] IoStatus Status of sending or receiving.
+ @param[in] LinkFlag Indicate if the frame is received as link broadcast/multicast.
+ When transmitting, it is not used.
+ @param[in] Context Additional data for callback.
+
+ @retval None.
+**/
+typedef
+VOID
+(*IP4_FRAME_CALLBACK)(
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 LinkFlag,
+ IN VOID *Context
+ );
+
+///
+/// Each receive request is wrapped in an IP4_LINK_RX_TOKEN.
+/// Upon completion, the Callback will be called. Only one
+/// receive request is send to MNP. IpInstance is always NULL.
+/// Reference MNP's spec for information.
+///
+typedef struct {
+ UINT32 Signature;
+ IP4_INTERFACE *Interface;
+
+ IP4_PROTOCOL *IpInstance;
+ IP4_FRAME_CALLBACK CallBack;
+ VOID *Context;
+
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;
+} IP4_LINK_RX_TOKEN;
+
+///
+/// Each transmit request is wrapped in an IP4_LINK_TX_TOKEN.
+/// Upon completion, the Callback will be called.
+///
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ IP4_INTERFACE *Interface;
+
+ IP4_PROTOCOL *IpInstance;
+ IP4_FRAME_CALLBACK CallBack;
+ NET_BUF *Packet;
+ VOID *Context;
+
+ EFI_MAC_ADDRESS DstMac;
+ EFI_MAC_ADDRESS SrcMac;
+
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData;
+} IP4_LINK_TX_TOKEN;
+
+///
+/// Only one ARP request is requested for all the frames in
+/// a time. It is started for the first frames to the Ip. Any
+/// subsequent transmission frame will be linked to Frames, and
+/// be sent all at once the ARP requests succeed.
+///
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ LIST_ENTRY Frames;
+ IP4_INTERFACE *Interface;
+
+ //
+ // ARP requesting staffs
+ //
+ EFI_EVENT OnResolved;
+ IP4_ADDR Ip;
+ EFI_MAC_ADDRESS Mac;
+} IP4_ARP_QUE;
+
+/**
+ Callback to select which frame to cancel. Caller can cancel a
+ single frame, or all the frame from an IP instance.
+
+ @param Frame The sending frame to check for cancellation.
+ @param Context Additional data for callback.
+
+ @retval TRUE The sending of the frame should be cancelled.
+ @retval FALSE Do not cancel the frame sending.
+**/
+typedef
+BOOLEAN
+(*IP4_FRAME_TO_CANCEL)(
+ IP4_LINK_TX_TOKEN *Frame,
+ VOID *Context
+ );
+
+//
+// Each IP4 instance has its own station address. All the instances
+// with the same station address share a single interface structure.
+// Each interface has its own ARP child, and shares one MNP child.
+// Notice the special cases that DHCP can configure the interface
+// with 0.0.0.0/0.0.0.0.
+//
+struct _IP4_INTERFACE {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ INTN RefCnt;
+
+ //
+ // IP address and subnet mask of the interface. It also contains
+ // the subnet/net broadcast address for quick access. The fields
+ // are invalid if (Configured == FALSE)
+ //
+ IP4_ADDR Ip;
+ IP4_ADDR SubnetMask;
+ IP4_ADDR SubnetBrdcast;
+ IP4_ADDR NetBrdcast;
+ BOOLEAN Configured;
+
+ //
+ // Handle used to create/destroy ARP child. All the IP children
+ // share one MNP which is owned by IP service binding.
+ //
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_HANDLE ArpHandle;
+
+ //
+ // Queues to keep the frames sent and waiting ARP request.
+ //
+ LIST_ENTRY ArpQues;
+ LIST_ENTRY SentFrames;
+ IP4_LINK_RX_TOKEN *RecvRequest;
+
+ //
+ // The interface's MAC and broadcast MAC address.
+ //
+ EFI_MAC_ADDRESS Mac;
+ EFI_MAC_ADDRESS BroadcastMac;
+ UINT32 HwaddrLen;
+
+ //
+ // All the IP instances that have the same IP/SubnetMask are linked
+ // together through IpInstances. If any of the instance enables
+ // promiscuous receive, PromiscRecv is true.
+ //
+ LIST_ENTRY IpInstances;
+ BOOLEAN PromiscRecv;
+};
+
+/**
+ Create an IP4_INTERFACE. Delay the creation of ARP instance until
+ the interface is configured.
+
+ @param[in] Mnp The shared MNP child of this IP4 service binding
+ instance.
+ @param[in] Controller The controller this IP4 service binding instance
+ is installed. Most like the UNDI handle.
+ @param[in] ImageHandle This driver's image handle.
+
+ @return Point to the created IP4_INTERFACE, otherwise NULL.
+
+**/
+IP4_INTERFACE *
+Ip4CreateInterface (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle
+ );
+
+/**
+ Set the interface's address, create and configure
+ the ARP child if necessary.
+
+ @param Interface The interface to set the address.
+ @param IpAddr The interface's IP address.
+ @param SubnetMask The interface's netmask.
+
+ @retval EFI_SUCCESS The interface is configured with Ip/netmask pair,
+ and a ARP is created for it.
+ @retval Others Failed to set the interface's address.
+
+**/
+EFI_STATUS
+Ip4SetAddress (
+ IN OUT IP4_INTERFACE *Interface,
+ IN IP4_ADDR IpAddr,
+ IN IP4_ADDR SubnetMask
+ );
+
+/**
+ Free the interface used by IpInstance. All the IP instance with
+ the same Ip/Netmask pair share the same interface. It is reference
+ counted. All the frames haven't been sent will be cancelled.
+ Because the IpInstance is optional, the caller must remove
+ IpInstance from the interface's instance list itself.
+
+ @param[in] Interface The interface used by the IpInstance.
+ @param[in] IpInstance The Ip instance that free the interface. NULL if
+ the Ip driver is releasing the default interface.
+
+ @retval EFI_SUCCESS The interface use IpInstance is freed.
+
+**/
+EFI_STATUS
+Ip4FreeInterface (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL
+ );
+
+/**
+ Send a frame from the interface. If the next hop is broadcast or
+ multicast address, it is transmitted immediately. If the next hop
+ is a unicast, it will consult ARP to resolve the NextHop's MAC.
+ If some error happened, the CallBack won't be called. So, the caller
+ must test the return value, and take action when there is an error.
+
+ @param[in] Interface The interface to send the frame from
+ @param[in] IpInstance The IP child that request the transmission. NULL
+ if it is the IP4 driver itself.
+ @param[in] Packet The packet to transmit.
+ @param[in] NextHop The immediate destination to transmit the packet
+ to.
+ @param[in] CallBack Function to call back when transmit finished.
+ @param[in] Context Opaque parameter to the call back.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop
+ @retval EFI_SUCCESS The packet is successfully transmitted.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip4SendFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN IP4_ADDR NextHop,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ );
+
+/**
+ Remove all the frames on the interface that pass the FrameToCancel,
+ either queued on ARP queues or that have already been delivered to
+ MNP and not yet recycled.
+
+ @param[in] Interface Interface to remove the frames from.
+ @param[in] IoStatus The transmit status returned to the frames'
+ callback.
+ @param[in] FrameToCancel Function to select the frame to cancel, NULL to
+ select all.
+ @param[in] Context Opaque parameters passed to FrameToCancel.
+
+**/
+VOID
+Ip4CancelFrames (
+ IN IP4_INTERFACE *Interface,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context
+ );
+
+/**
+ If there is a pending receive request, cancel it. Don't call
+ the receive request's callback because this function can be only
+ called if the instance or driver is tearing itself down. It
+ doesn't make sense to call it back. But it is necessary to call
+ the transmit token's callback to give it a chance to free the
+ packet and update the upper layer's transmit request status, say
+ that from the UDP.
+
+ @param[in] Interface The interface used by the IpInstance
+
+**/
+VOID
+Ip4CancelReceive (
+ IN IP4_INTERFACE *Interface
+ );
+
+/**
+ Request to receive the packet from the interface.
+
+ @param[in] Interface The interface to receive the frames from.
+ @param[in] IpInstance The instance that requests the receive. NULL for
+ the driver itself.
+ @param[in] CallBack Function to call when receive finished.
+ @param[in] Context Opaque parameter to the callback.
+
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive.
+ @retval EFI_SUCCESS The recieve request has been started.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip4ReceiveFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c
new file mode 100644
index 0000000000..e0edc93ff8
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c
@@ -0,0 +1,621 @@
+/** @file
+ This file implements the RFC2236: IGMP v2.
+
+Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+//
+// Route Alert option in IGMP report to direct routers to
+// examine the packet more closely.
+//
+UINT32 mRouteAlertOption = 0x00000494;
+
+
+/**
+ Init the IGMP control data of the IP4 service instance, configure
+ MNP to receive ALL SYSTEM multicast.
+
+ @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.
+
+ @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
+ @retval Others Failed to initialize the IGMP of IpSb.
+
+**/
+EFI_STATUS
+Ip4InitIgmp (
+ IN OUT IP4_SERVICE *IpSb
+ )
+{
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ IGMP_GROUP *Group;
+ EFI_STATUS Status;
+
+ IgmpCtrl = &IpSb->IgmpCtrl;
+
+ //
+ // Configure MNP to receive ALL_SYSTEM multicast
+ //
+ Group = AllocatePool (sizeof (IGMP_GROUP));
+
+ if (Group == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mnp = IpSb->Mnp;
+
+ Group->Address = IP4_ALLSYSTEM_ADDRESS;
+ Group->RefCnt = 1;
+ Group->DelayTime = 0;
+ Group->ReportByUs = FALSE;
+
+ Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ InsertHeadList (&IgmpCtrl->Groups, &Group->Link);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ FreePool (Group);
+ return Status;
+}
+
+
+/**
+ Find the IGMP_GROUP structure which contains the status of multicast
+ group Address in this IGMP control block
+
+ @param[in] IgmpCtrl The IGMP control block to search from.
+ @param[in] Address The multicast address to search.
+
+ @return NULL if the multicast address isn't in the IGMP control block. Otherwise
+ the point to the IGMP_GROUP which contains the status of multicast group
+ for Address.
+
+**/
+IGMP_GROUP *
+Ip4FindGroup (
+ IN IGMP_SERVICE_DATA *IgmpCtrl,
+ IN IP4_ADDR Address
+ )
+{
+ LIST_ENTRY *Entry;
+ IGMP_GROUP *Group;
+
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+
+ if (Group->Address == Address) {
+ return Group;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Count the number of IP4 multicast groups that are mapped to the
+ same MAC address. Several IP4 multicast address may be mapped to
+ the same MAC address.
+
+ @param[in] IgmpCtrl The IGMP control block to search in.
+ @param[in] Mac The MAC address to search.
+
+ @return The number of the IP4 multicast group that mapped to the same
+ multicast group Mac.
+
+**/
+INTN
+Ip4FindMac (
+ IN IGMP_SERVICE_DATA *IgmpCtrl,
+ IN EFI_MAC_ADDRESS *Mac
+ )
+{
+ LIST_ENTRY *Entry;
+ IGMP_GROUP *Group;
+ INTN Count;
+
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+
+ if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {
+ Count++;
+ }
+ }
+
+ return Count;
+}
+
+
+/**
+ Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.
+
+ @param[in] IpSb The IP4 service instance that requests the
+ transmission.
+ @param[in] Dst The destinaton to send to.
+ @param[in] Type The IGMP message type, such as IGMP v1 membership
+ report.
+ @param[in] Group The group address in the IGMP message head.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
+ @retval EFI_SUCCESS The IGMP message is successfully send.
+ @retval Others Failed to send the IGMP message.
+
+**/
+EFI_STATUS
+Ip4SendIgmpMessage (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN UINT8 Type,
+ IN IP4_ADDR Group
+ )
+{
+ IP4_HEAD Head;
+ NET_BUF *Packet;
+ IGMP_HEAD *Igmp;
+
+ //
+ // Allocate a net buffer to hold the message
+ //
+ Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD));
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill in the IGMP and IP header, then transmit the message
+ //
+ NetbufReserve (Packet, IP4_MAX_HEADLEN);
+
+ Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE);
+ if (Igmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Igmp->Type = Type;
+ Igmp->MaxRespTime = 0;
+ Igmp->Checksum = 0;
+ Igmp->Group = HTONL (Group);
+ Igmp->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD)));
+
+ Head.Tos = 0;
+ Head.Protocol = IP4_PROTO_IGMP;
+ Head.Ttl = 1;
+ Head.Fragment = 0;
+ Head.Dst = Dst;
+ Head.Src = IP4_ALLZERO_ADDRESS;
+
+ return Ip4Output (
+ IpSb,
+ NULL,
+ Packet,
+ &Head,
+ (UINT8 *) &mRouteAlertOption,
+ sizeof (UINT32),
+ IP4_ALLZERO_ADDRESS,
+ Ip4SysPacketSent,
+ NULL
+ );
+}
+
+
+/**
+ Send an IGMP membership report. Depends on whether the server is
+ v1 or v2, it will send either a V1 or V2 membership report.
+
+ @param[in] IpSb The IP4 service instance that requests the
+ transmission.
+ @param[in] Group The group address to report.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
+ @retval EFI_SUCCESS The IGMP report message is successfully send.
+ @retval Others Failed to send the report.
+
+**/
+EFI_STATUS
+Ip4SendIgmpReport (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Group
+ )
+{
+ if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) {
+ return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group);
+ } else {
+ return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group);
+ }
+}
+
+
+/**
+ Join the multicast group on behalf of this IP4 child
+
+ @param[in] IpInstance The IP4 child that wants to join the group.
+ @param[in] Address The group to join.
+
+ @retval EFI_SUCCESS Successfully join the multicast group.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval Others Failed to join the multicast group.
+
+**/
+EFI_STATUS
+Ip4JoinGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ )
+{
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ IP4_SERVICE *IpSb;
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ IGMP_GROUP *Group;
+ EFI_STATUS Status;
+
+ IpSb = IpInstance->Service;
+ IgmpCtrl = &IpSb->IgmpCtrl;
+ Mnp = IpSb->Mnp;
+
+ //
+ // If the IP service already is a member in the group, just
+ // increase the refernce count and return.
+ //
+ Group = Ip4FindGroup (IgmpCtrl, Address);
+
+ if (Group != NULL) {
+ Group->RefCnt++;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
+ // send a report, then direct MNP to receive the multicast.
+ //
+ Group = AllocatePool (sizeof (IGMP_GROUP));
+
+ if (Group == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Group->Address = Address;
+ Group->RefCnt = 1;
+ Group->DelayTime = IGMP_UNSOLICIATED_REPORT;
+ Group->ReportByUs = TRUE;
+
+ Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4SendIgmpReport (IpSb, Address);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ InsertHeadList (&IgmpCtrl->Groups, &Group->Link);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ FreePool (Group);
+ return Status;
+}
+
+
+/**
+ Leave the IP4 multicast group on behalf of IpInstance.
+
+ @param[in] IpInstance The IP4 child that wants to leave the group
+ address.
+ @param[in] Address The group address to leave.
+
+ @retval EFI_NOT_FOUND The IP4 service instance isn't in the group.
+ @retval EFI_SUCCESS Successfully leave the multicast group.
+ @retval Others Failed to leave the multicast group.
+
+**/
+EFI_STATUS
+Ip4LeaveGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ )
+{
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ IP4_SERVICE *IpSb;
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ IGMP_GROUP *Group;
+ EFI_STATUS Status;
+
+ IpSb = IpInstance->Service;
+ IgmpCtrl = &IpSb->IgmpCtrl;
+ Mnp = IpSb->Mnp;
+
+ Group = Ip4FindGroup (IgmpCtrl, Address);
+
+ if (Group == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If more than one instance is in the group, decrease
+ // the RefCnt then return.
+ //
+ if (--Group->RefCnt > 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // If multiple IP4 group addresses are mapped to the same
+ // multicast MAC address, don't configure the MNP to leave
+ // the MAC.
+ //
+ if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) {
+ Status = Mnp->Groups (Mnp, FALSE, &Group->Mac);
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ return Status;
+ }
+ }
+
+ //
+ // Send a leave report if the membership is reported by us
+ // and we are talking IGMPv2.
+ //
+ if (Group->ReportByUs && IgmpCtrl->Igmpv1QuerySeen == 0) {
+ Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address);
+ }
+
+ RemoveEntryList (&Group->Link);
+ FreePool (Group);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Handle the received IGMP message for the IP4 service instance.
+
+ @param[in] IpSb The IP4 service instance that received the message.
+ @param[in] Head The IP4 header of the received message.
+ @param[in] Packet The IGMP message, without IP4 header.
+
+ @retval EFI_INVALID_PARAMETER The IGMP message is malformated.
+ @retval EFI_SUCCESS The IGMP message is successfully processed.
+
+**/
+EFI_STATUS
+Ip4IgmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ IGMP_HEAD Igmp;
+ IGMP_GROUP *Group;
+ IP4_ADDR Address;
+ LIST_ENTRY *Entry;
+
+ IgmpCtrl = &IpSb->IgmpCtrl;
+
+ //
+ // Must checksum over the whole packet, later IGMP version
+ // may employ message longer than 8 bytes. IP's header has
+ // already been trimmed off.
+ //
+ if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Copy the packet in case it is fragmented
+ //
+ NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp);
+
+ switch (Igmp.Type) {
+ case IGMP_MEMBERSHIP_QUERY:
+ //
+ // If MaxRespTime is zero, it is most likely that we are
+ // talking to a V1 router
+ //
+ if (Igmp.MaxRespTime == 0) {
+ IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT;
+ Igmp.MaxRespTime = 100;
+ }
+
+ //
+ // Igmp is ticking once per second but MaxRespTime is in
+ // the unit of 100ms.
+ //
+ Igmp.MaxRespTime /= 10;
+ Address = NTOHL (Igmp.Group);
+
+ if (Address == IP4_ALLSYSTEM_ADDRESS) {
+ break;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+
+ //
+ // If address is all zero, all the memberships will be reported.
+ // otherwise only one is reported.
+ //
+ if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) {
+ //
+ // If the timer is pending, only update it if the time left
+ // is longer than the MaxRespTime. TODO: randomize the DelayTime.
+ //
+ if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) {
+ Group->DelayTime = MAX (1, Igmp.MaxRespTime);
+ }
+ }
+ }
+
+ break;
+
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ Address = NTOHL (Igmp.Group);
+ Group = Ip4FindGroup (IgmpCtrl, Address);
+
+ if ((Group != NULL) && (Group->DelayTime > 0)) {
+ Group->DelayTime = 0;
+ Group->ReportByUs = FALSE;
+ }
+
+ break;
+ }
+
+ NetbufFree (Packet);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The periodical timer function for IGMP. It does the following
+ things:
+ 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
+ the IGMP server type.
+ 2. Decrease the report timer for each IGMP group in "delaying
+ member" state.
+
+ @param[in] IpSb The IP4 service instance that is ticking.
+
+**/
+VOID
+Ip4IgmpTicking (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ LIST_ENTRY *Entry;
+ IGMP_GROUP *Group;
+
+ IgmpCtrl = &IpSb->IgmpCtrl;
+
+ if (IgmpCtrl->Igmpv1QuerySeen > 0) {
+ IgmpCtrl->Igmpv1QuerySeen--;
+ }
+
+ //
+ // Decrease the report timer for each IGMP group in "delaying member"
+ //
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+ ASSERT (Group->DelayTime >= 0);
+
+ if (Group->DelayTime > 0) {
+ Group->DelayTime--;
+
+ if (Group->DelayTime == 0) {
+ Ip4SendIgmpReport (IpSb, Group->Address);
+ Group->ReportByUs = TRUE;
+ }
+ }
+ }
+}
+
+
+/**
+ Add a group address to the array of group addresses.
+ The caller should make sure that no duplicated address
+ existed in the array. Although the function doesn't
+ assume the byte order of the both Source and Addr, the
+ network byte order is used by the caller.
+
+ @param[in] Source The array of group addresses to add to.
+ @param[in] Count The number of group addresses in the Source.
+ @param[in] Addr The IP4 multicast address to add.
+
+ @return NULL if failed to allocate memory for the new groups,
+ otherwise the new combined group addresses.
+
+**/
+IP4_ADDR *
+Ip4CombineGroups (
+ IN IP4_ADDR *Source,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ )
+{
+ IP4_ADDR *Groups;
+
+ Groups = AllocatePool (sizeof (IP4_ADDR) * (Count + 1));
+
+ if (Groups == NULL) {
+ return NULL;
+ }
+
+ CopyMem (Groups, Source, Count * sizeof (IP4_ADDR));
+ Groups[Count] = Addr;
+
+ return Groups;
+}
+
+
+/**
+ Remove a group address from the array of group addresses.
+ Although the function doesn't assume the byte order of the
+ both Groups and Addr, the network byte order is used by
+ the caller.
+
+ @param Groups The array of group addresses to remove from.
+ @param Count The number of group addresses in the Groups.
+ @param Addr The IP4 multicast address to remove.
+
+ @return The nubmer of group addresses in the Groups after remove.
+ It is Count if the Addr isn't in the Groups.
+
+**/
+INTN
+Ip4RemoveGroupAddr (
+ IN OUT IP4_ADDR *Groups,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < Count; Index++) {
+ if (Groups[Index] == Addr) {
+ break;
+ }
+ }
+
+ while (Index < Count - 1) {
+ Groups[Index] = Groups[Index + 1];
+ Index++;
+ }
+
+ return Index;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h
new file mode 100644
index 0000000000..756c697a3a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h
@@ -0,0 +1,207 @@
+/** @file
+
+Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_IGMP_H__
+#define __EFI_IP4_IGMP_H__
+
+//
+// IGMP message type
+//
+#define IGMP_MEMBERSHIP_QUERY 0x11
+#define IGMP_V1_MEMBERSHIP_REPORT 0x12
+#define IGMP_V2_MEMBERSHIP_REPORT 0x16
+#define IGMP_LEAVE_GROUP 0x17
+
+#define IGMP_V1ROUTER_PRESENT 400
+#define IGMP_UNSOLICIATED_REPORT 10
+
+#pragma pack(1)
+typedef struct {
+ UINT8 Type;
+ UINT8 MaxRespTime;
+ UINT16 Checksum;
+ IP4_ADDR Group;
+} IGMP_HEAD;
+#pragma pack()
+
+///
+/// The status of multicast group. It isn't necessary to maintain
+/// explicit state of host state diagram. A group with non-zero
+/// DelayTime is in "delaying member" state. otherwise, it is in
+/// "idle member" state.
+///
+typedef struct {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ IP4_ADDR Address;
+ INTN DelayTime;
+ BOOLEAN ReportByUs;
+ EFI_MAC_ADDRESS Mac;
+} IGMP_GROUP;
+
+///
+/// The IGMP status. Each IP4 service instance has a IGMP_SERVICE_DATA
+/// attached. The Igmpv1QuerySeen remember whether the server on this
+/// connected network is v1 or v2.
+///
+typedef struct {
+ INTN Igmpv1QuerySeen;
+ LIST_ENTRY Groups;
+} IGMP_SERVICE_DATA;
+
+/**
+ Init the IGMP control data of the IP4 service instance, configure
+ MNP to receive ALL SYSTEM multicast.
+
+ @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.
+
+ @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
+ @retval Others Failed to initialize the IGMP of IpSb.
+
+**/
+EFI_STATUS
+Ip4InitIgmp (
+ IN OUT IP4_SERVICE *IpSb
+ );
+
+/**
+ Join the multicast group on behalf of this IP4 child
+
+ @param[in] IpInstance The IP4 child that wants to join the group.
+ @param[in] Address The group to join.
+
+ @retval EFI_SUCCESS Successfully join the multicast group.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval Others Failed to join the multicast group.
+
+**/
+EFI_STATUS
+Ip4JoinGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ );
+
+/**
+ Leave the IP4 multicast group on behalf of IpInstance.
+
+ @param[in] IpInstance The IP4 child that wants to leave the group
+ address.
+ @param[in] Address The group address to leave.
+
+ @retval EFI_NOT_FOUND The IP4 service instance isn't in the group.
+ @retval EFI_SUCCESS Successfully leave the multicast group.
+ @retval Others Failed to leave the multicast group.
+
+**/
+EFI_STATUS
+Ip4LeaveGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ );
+
+/**
+ Handle the received IGMP message for the IP4 service instance.
+
+ @param[in] IpSb The IP4 service instance that received the message.
+ @param[in] Head The IP4 header of the received message.
+ @param[in] Packet The IGMP message, without IP4 header.
+
+ @retval EFI_INVALID_PARAMETER The IGMP message is malformated.
+ @retval EFI_SUCCESS The IGMP message is successfully processed.
+
+**/
+EFI_STATUS
+Ip4IgmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ The periodical timer function for IGMP. It does the following
+ things:
+ 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
+ the IGMP server type.
+ 2. Decrease the report timer for each IGMP group in "delaying
+ member" state.
+
+ @param[in] IpSb The IP4 service instance that is ticking.
+
+**/
+VOID
+Ip4IgmpTicking (
+ IN IP4_SERVICE *IpSb
+ );
+
+/**
+ Add a group address to the array of group addresses.
+ The caller should make sure that no duplicated address
+ existed in the array. Although the function doesn't
+ assume the byte order of the both Source and Addr, the
+ network byte order is used by the caller.
+
+ @param[in] Source The array of group addresses to add to.
+ @param[in] Count The number of group addresses in the Source.
+ @param[in] Addr The IP4 multicast address to add.
+
+ @return NULL if failed to allocate memory for the new groups,
+ otherwise the new combined group addresses.
+
+**/
+IP4_ADDR *
+Ip4CombineGroups (
+ IN IP4_ADDR *Source,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ );
+
+/**
+ Remove a group address from the array of group addresses.
+ Although the function doesn't assume the byte order of the
+ both Groups and Addr, the network byte order is used by
+ the caller.
+
+ @param Groups The array of group addresses to remove from.
+ @param Count The number of group addresses in the Groups.
+ @param Addr The IP4 multicast address to remove.
+
+ @return The nubmer of group addresses in the Groups after remove.
+ It is Count if the Addr isn't in the Groups.
+
+**/
+INTN
+Ip4RemoveGroupAddr (
+ IN OUT IP4_ADDR *Groups,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ );
+
+/**
+ Find the IGMP_GROUP structure which contains the status of multicast
+ group Address in this IGMP control block
+
+ @param[in] IgmpCtrl The IGMP control block to search from.
+ @param[in] Address The multicast address to search.
+
+ @return NULL if the multicast address isn't in the IGMP control block. Otherwise
+ the point to the IGMP_GROUP which contains the status of multicast group
+ for Address.
+
+**/
+IGMP_GROUP *
+Ip4FindGroup (
+ IN IGMP_SERVICE_DATA *IgmpCtrl,
+ IN IP4_ADDR Address
+ );
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c
new file mode 100644
index 0000000000..3cdf8ecaad
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c
@@ -0,0 +1,2297 @@
+/** @file
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+EFI_IPSEC2_PROTOCOL *mIpSec = NULL;
+
+/**
+ Gets the current operational settings for this instance of the EFI IPv4 Protocol driver.
+
+ The GetModeData() function returns the current operational mode data for this
+ driver instance. The data fields in EFI_IP4_MODE_DATA are read only. This
+ function is used optionally to retrieve the operational mode data of underlying
+ networks or drivers.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[out] Ip4ModeData Pointer to the EFI IPv4 Protocol mode data structure.
+ @param[out] MnpConfigData Pointer to the managed network configuration data structure.
+ @param[out] SnpModeData Pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4GetModeData (
+ IN CONST EFI_IP4_PROTOCOL *This,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ Assigns an IPv4 address and subnet mask to this EFI IPv4 Protocol driver instance.
+
+ The Configure() function is used to set, change, or reset the operational
+ parameters and filter settings for this EFI IPv4 Protocol instance. Until these
+ parameters have been set, no network traffic can be sent or received by this
+ instance. Once the parameters have been reset (by calling this function with
+ IpConfigData set to NULL), no more traffic can be sent or received until these
+ parameters have been set again. Each EFI IPv4 Protocol instance can be started
+ and stopped independently of each other by enabling or disabling their receive
+ filter settings with the Configure() function.
+
+ When IpConfigData.UseDefaultAddress is set to FALSE, the new station address will
+ be appended as an alias address into the addresses list in the EFI IPv4 Protocol
+ driver. While set to TRUE, Configure() will trigger the EFI_IP4_CONFIG_PROTOCOL
+ to retrieve the default IPv4 address if it is not available yet. Clients could
+ frequently call GetModeData() to check the status to ensure that the default IPv4
+ address is ready.
+
+ If operational parameters are reset or changed, any pending transmit and receive
+ requests will be cancelled. Their completion token status will be set to EFI_ABORTED
+ and their events will be signaled.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] IpConfigData Pointer to the EFI IPv4 Protocol configuration data structure.
+
+ @retval EFI_SUCCESS The driver instance was successfully opened.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ @retval EFI_UNSUPPORTED One or more of the following conditions is TRUE:
+ A configuration protocol (DHCP, BOOTP, RARP, etc.) could
+ not be located when clients choose to use the default IPv4
+ address. This EFI IPv4 Protocol implementation does not
+ support this requested filter or timeout setting.
+ @retval EFI_OUT_OF_RESOURCES The EFI IPv4 Protocol driver instance data could not be allocated.
+ @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the
+ IPv4 address or subnet mask can be changed. The interface must
+ also be stopped when switching to/from raw packet mode.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv4
+ Protocol driver instance is not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Configure (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL
+ );
+
+/**
+ Joins and leaves multicast groups.
+
+ The Groups() function is used to join and leave multicast group sessions. Joining
+ a group will enable reception of matching multicast packets. Leaving a group will
+ disable the multicast packet reception.
+
+ If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave.
+ @param[in] GroupAddress Pointer to the IPv4 multicast address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:
+ - This is NULL.
+ - JoinFlag is TRUE and GroupAddress is NULL.
+ - GroupAddress is not NULL and *GroupAddress is
+ not a multicast IPv4 address.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES System resources could not be allocated.
+ @retval EFI_UNSUPPORTED This EFI IPv4 Protocol implementation does not support multicast groups.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when
+ JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Groups (
+ IN EFI_IP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL
+ );
+
+/**
+ Adds and deletes routing table entries.
+
+ The Routes() function adds a route to or deletes a route from the routing table.
+
+ Routes are determined by comparing the SubnetAddress with the destination IPv4
+ address arithmetically AND-ed with the SubnetMask. The gateway address must be
+ on the same subnet as the configured station address.
+
+ The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0.
+ The default route matches all destination IPv4 addresses that do not match any
+ other routes.
+
+ A GatewayAddress that is zero is a nonroute. Packets are sent to the destination
+ IP address if it can be found in the ARP cache or on the local subnet. One automatic
+ nonroute entry will be inserted into the routing table for outgoing packets that
+ are addressed to a local subnet (gateway address of 0.0.0.0).
+
+ Each EFI IPv4 Protocol instance has its own independent routing table. Those EFI
+ IPv4 Protocol instances that use the default IPv4 address will also have copies
+ of the routing table that was provided by the EFI_IP4_CONFIG_PROTOCOL, and these
+ copies will be updated whenever the EIF IPv4 Protocol driver reconfigures its
+ instances. As a result, client modification to the routing table will be lost.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to
+ FALSE to add this route to the routing table. SubnetAddress
+ and SubnetMask are used as the key to each route entry.
+ @param[in] SubnetAddress The address of the subnet that needs to be routed.
+ @param[in] SubnetMask The subnet mask of SubnetAddress.
+ @param[in] GatewayAddress The unicast gateway IPv4 address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - SubnetAddress is NULL.
+ - SubnetMask is NULL.
+ - GatewayAddress is NULL.
+ - *SubnetAddress is not a valid subnet address.
+ - *SubnetMask is not a valid subnet mask.
+ - *GatewayAddress is not a valid unicast IPv4 address.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when
+ DeleteRoute is FALSE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Routes (
+ IN EFI_IP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ );
+
+/**
+ Places outgoing data packets into the transmit queue.
+
+ The Transmit() function places a sending request in the transmit queue of this
+ EFI IPv4 Protocol instance. Whenever the packet in the token is sent out or some
+ errors occur, the event in the token will be signaled and the status is updated.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] Token Pointer to the transmit token.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more pameters are invalid.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.Event
+ was already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because the transmit
+ queue is full.
+ @retval EFI_NOT_FOUND Not route is found to destination address.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too
+ short to transmit.
+ @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length + total data length is
+ greater than MTU (or greater than the maximum packet size if
+ Token.Packet.TxData.OverrideData.
+ DoNotFragment is TRUE.)
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Transmit (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Places a receiving request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous.
+
+ The Token.Event field in the completion token must be filled in by the caller
+ and cannot be NULL. When the receive operation completes, the EFI IPv4 Protocol
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event
+ is signaled.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI IPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, RARP, etc.)
+ is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system
+ resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI IPv4 Protocol instance has been reset to startup defaults.
+ EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already
+ in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Receive (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Abort an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token->Status will be set to EFI_ABORTED and then Token->Event will
+ be signaled. If the token is not in one of the queues, which usually means the
+ asynchronous operation has completed, this function will not signal the token
+ and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_IP4_PROTOCOL.Transmit() or
+ EFI_IP4_PROTOCOL.Receive(). If NULL, all pending
+ tokens are aborted. Type EFI_IP4_COMPLETION_TOKEN is
+ defined in EFI_IP4_PROTOCOL.Transmit().
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token.->Event was signaled. When Token is NULL, all
+ pending requests were aborted and their events were signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was
+ not found in the transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Cancel (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function polls for incoming data packets and processes outgoing data
+ packets. Network drivers and applications can call the EFI_IP4_PROTOCOL.Poll()
+ function to increase the rate that data packets are moved between the communications
+ device and the transmit and receive queues.
+
+ In some systems the periodic timer event may not poll the underlying communications
+ device fast enough to transmit and/or receive all data packets without missing
+ incoming packets or dropping outgoing packets. Drivers and applications that are
+ experiencing packet loss should try calling the EFI_IP4_PROTOCOL.Poll() function
+ more often.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI IPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Poll (
+ IN EFI_IP4_PROTOCOL *This
+ );
+
+EFI_IP4_PROTOCOL
+mEfiIp4ProtocolTemplete = {
+ EfiIp4GetModeData,
+ EfiIp4Configure,
+ EfiIp4Groups,
+ EfiIp4Routes,
+ EfiIp4Transmit,
+ EfiIp4Receive,
+ EfiIp4Cancel,
+ EfiIp4Poll
+};
+
+/**
+ Gets the current operational settings for this instance of the EFI IPv4 Protocol driver.
+
+ The GetModeData() function returns the current operational mode data for this
+ driver instance. The data fields in EFI_IP4_MODE_DATA are read only. This
+ function is used optionally to retrieve the operational mode data of underlying
+ networks or drivers.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[out] Ip4ModeData Pointer to the EFI IPv4 Protocol mode data structure.
+ @param[out] MnpConfigData Pointer to the managed network configuration data structure.
+ @param[out] SnpModeData Pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4GetModeData (
+ IN CONST EFI_IP4_PROTOCOL *This,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ IP4_SERVICE *IpSb;
+ EFI_IP4_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (Ip4ModeData != NULL) {
+ //
+ // IsStarted is "whether the EfiIp4Configure has been called".
+ // IsConfigured is "whether the station address has been configured"
+ //
+ Ip4ModeData->IsStarted = (BOOLEAN)(IpInstance->State == IP4_STATE_CONFIGED);
+ CopyMem (&Ip4ModeData->ConfigData, &IpInstance->ConfigData, sizeof (Ip4ModeData->ConfigData));
+ Ip4ModeData->IsConfigured = FALSE;
+
+ Ip4ModeData->GroupCount = IpInstance->GroupCount;
+ Ip4ModeData->GroupTable = (EFI_IPv4_ADDRESS *) IpInstance->Groups;
+
+ Ip4ModeData->IcmpTypeCount = 23;
+ Ip4ModeData->IcmpTypeList = mIp4SupportedIcmp;
+
+ Ip4ModeData->RouteTable = NULL;
+ Ip4ModeData->RouteCount = 0;
+
+ Ip4ModeData->MaxPacketSize = IpSb->MaxPacketSize;
+
+ //
+ // return the current station address for this IP child. So,
+ // the user can get the default address through this. Some
+ // application wants to know it station address even it is
+ // using the default one, such as a ftp server.
+ //
+ if (Ip4ModeData->IsStarted) {
+ Config = &Ip4ModeData->ConfigData;
+
+ Ip = HTONL (IpInstance->Interface->Ip);
+ CopyMem (&Config->StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (IpInstance->Interface->SubnetMask);
+ CopyMem (&Config->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip4ModeData->IsConfigured = IpInstance->Interface->Configured;
+
+ //
+ // Build a EFI route table for user from the internal route table.
+ //
+ Status = Ip4BuildEfiRouteTable (IpInstance);
+
+ if (EFI_ERROR (Status)) {
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ Ip4ModeData->RouteTable = IpInstance->EfiRouteTable;
+ Ip4ModeData->RouteCount = IpInstance->EfiRouteCount;
+ }
+ }
+
+ //
+ // Get fresh mode data from MNP, since underlying media status may change
+ //
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData);
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Config the MNP parameter used by IP. The IP driver use one MNP
+ child to transmit/receive frames. By default, it configures MNP
+ to receive unicast/multicast/broadcast. And it will enable/disable
+ the promiscous receive according to whether there is IP child
+ enable that or not. If Force is FALSE, it will iterate through
+ all the IP children to check whether the promiscuous receive
+ setting has been changed. If it hasn't been changed, it won't
+ reconfigure the MNP. If Force is TRUE, the MNP is configured no
+ matter whether that is changed or not.
+
+ @param[in] IpSb The IP4 service instance that is to be changed.
+ @param[in] Force Force the configuration or not.
+
+ @retval EFI_SUCCESS The MNP is successfully configured/reconfigured.
+ @retval Others Configuration failed.
+
+**/
+EFI_STATUS
+Ip4ServiceConfigMnp (
+ IN IP4_SERVICE *IpSb,
+ IN BOOLEAN Force
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *ProtoEntry;
+ IP4_INTERFACE *IpIf;
+ IP4_PROTOCOL *IpInstance;
+ BOOLEAN Reconfig;
+ BOOLEAN PromiscReceive;
+ EFI_STATUS Status;
+
+ Reconfig = FALSE;
+ PromiscReceive = FALSE;
+
+ if (!Force) {
+ //
+ // Iterate through the IP children to check whether promiscuous
+ // receive setting has been changed. Update the interface's receive
+ // filter also.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+ IpIf->PromiscRecv = FALSE;
+
+ NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP4_PROTOCOL, AddrLink);
+
+ if (IpInstance->ConfigData.AcceptPromiscuous) {
+ IpIf->PromiscRecv = TRUE;
+ PromiscReceive = TRUE;
+ }
+ }
+ }
+
+ //
+ // If promiscuous receive isn't changed, it isn't necessary to reconfigure.
+ //
+ if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {
+ return EFI_SUCCESS;
+ }
+
+ Reconfig = TRUE;
+ IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;
+ }
+
+ Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);
+
+ //
+ // recover the original configuration if failed to set the configure.
+ //
+ if (EFI_ERROR (Status) && Reconfig) {
+ IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive;
+ }
+
+ return Status;
+}
+
+
+/**
+ Intiialize the IP4_PROTOCOL structure to the unconfigured states.
+
+ @param IpSb The IP4 service instance.
+ @param IpInstance The IP4 child instance.
+
+**/
+VOID
+Ip4InitProtocol (
+ IN IP4_SERVICE *IpSb,
+ IN OUT IP4_PROTOCOL *IpInstance
+ )
+{
+ ASSERT ((IpSb != NULL) && (IpInstance != NULL));
+
+ ZeroMem (IpInstance, sizeof (IP4_PROTOCOL));
+
+ IpInstance->Signature = IP4_PROTOCOL_SIGNATURE;
+ CopyMem (&IpInstance->Ip4Proto, &mEfiIp4ProtocolTemplete, sizeof (IpInstance->Ip4Proto));
+ IpInstance->State = IP4_STATE_UNCONFIGED;
+ IpInstance->InDestroy = FALSE;
+ IpInstance->Service = IpSb;
+
+ InitializeListHead (&IpInstance->Link);
+ NetMapInit (&IpInstance->RxTokens);
+ NetMapInit (&IpInstance->TxTokens);
+ InitializeListHead (&IpInstance->Received);
+ InitializeListHead (&IpInstance->Delivered);
+ InitializeListHead (&IpInstance->AddrLink);
+
+ EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY);
+}
+
+
+/**
+ Configure the IP4 child. If the child is already configured,
+ change the configuration parameter. Otherwise configure it
+ for the first time. The caller should validate the configuration
+ before deliver them to it. It also don't do configure NULL.
+
+ @param[in, out] IpInstance The IP4 child to configure.
+ @param[in] Config The configure data.
+
+ @retval EFI_SUCCESS The IP4 child is successfully configured.
+ @retval EFI_DEVICE_ERROR Failed to free the pending transive or to
+ configure underlying MNP or other errors.
+ @retval EFI_NO_MAPPING The IP4 child is configured to use default
+ address, but the default address hasn't been
+ configured. The IP4 child doesn't need to be
+ reconfigured when default address is configured.
+ @retval EFI_OUT_OF_RESOURCES No more memory space is available.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip4ConfigProtocol (
+ IN OUT IP4_PROTOCOL *IpInstance,
+ IN EFI_IP4_CONFIG_DATA *Config
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_INTERFACE *IpIf;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ EFI_ARP_PROTOCOL *Arp;
+
+ IpSb = IpInstance->Service;
+
+ //
+ // User is changing packet filters. It must be stopped
+ // before the station address can be changed.
+ //
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ //
+ // Cancel all the pending transmit/receive from upper layer
+ //
+ Status = Ip4Cancel (IpInstance, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (&IpInstance->ConfigData, Config, sizeof (IpInstance->ConfigData));
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Configure a fresh IP4 protocol instance. Create a route table.
+ // Each IP child has its own route table, which may point to the
+ // default table if it is using default address.
+ //
+ Status = EFI_OUT_OF_RESOURCES;
+ IpInstance->RouteTable = Ip4CreateRouteTable ();
+
+ if (IpInstance->RouteTable == NULL) {
+ return Status;
+ }
+
+ //
+ // Set up the interface.
+ //
+ CopyMem (&Ip, &Config->StationAddress, sizeof (IP4_ADDR));
+ CopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));
+
+ Ip = NTOHL (Ip);
+ Netmask = NTOHL (Netmask);
+
+ if (!Config->UseDefaultAddress) {
+ //
+ // Find whether there is already an interface with the same
+ // station address. All the instances with the same station
+ // address shares one interface.
+ //
+ IpIf = Ip4FindStationAddress (IpSb, Ip, Netmask);
+
+ if (IpIf != NULL) {
+ NET_GET_REF (IpIf);
+
+ } else {
+ IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);
+
+ if (IpIf == NULL) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4SetAddress (IpIf, Ip, Netmask);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ Ip4FreeInterface (IpIf, IpInstance);
+ goto ON_ERROR;
+ }
+
+ InsertTailList (&IpSb->Interfaces, &IpIf->Link);
+ }
+
+ //
+ // Add a route to this connected network in the route table
+ //
+ Ip4AddRoute (IpInstance->RouteTable, Ip, Netmask, IP4_ALLZERO_ADDRESS);
+
+ } else {
+ //
+ // Use the default address. Check the state.
+ //
+ if (IpSb->State == IP4_SERVICE_UNSTARTED) {
+ Status = Ip4StartAutoConfig (&IpSb->Ip4Config2Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ IpIf = IpSb->DefaultInterface;
+ NET_GET_REF (IpSb->DefaultInterface);
+
+ //
+ // If default address is used, so is the default route table.
+ // Any route set by the instance has the precedence over the
+ // routes in the default route table. Link the default table
+ // after the instance's table. Routing will search the local
+ // table first.
+ //
+ NET_GET_REF (IpSb->DefaultRouteTable);
+ IpInstance->RouteTable->Next = IpSb->DefaultRouteTable;
+ }
+
+ IpInstance->Interface = IpIf;
+ if (IpIf->Arp != NULL) {
+ Arp = NULL;
+ Status = gBS->OpenProtocol (
+ IpIf->ArpHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **) &Arp,
+ gIp4DriverBinding.DriverBindingHandle,
+ IpInstance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+ InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink);
+
+ CopyMem (&IpInstance->ConfigData, Config, sizeof (IpInstance->ConfigData));
+ IpInstance->State = IP4_STATE_CONFIGED;
+
+ //
+ // Although EFI_NO_MAPPING is an error code, the IP child has been
+ // successfully configured and doesn't need reconfiguration when
+ // default address is acquired.
+ //
+ if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ return EFI_NO_MAPPING;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4FreeRouteTable (IpInstance->RouteTable);
+ IpInstance->RouteTable = NULL;
+ return Status;
+}
+
+
+/**
+ Clean up the IP4 child, release all the resources used by it.
+
+ @param[in] IpInstance The IP4 child to clean up.
+
+ @retval EFI_SUCCESS The IP4 child is cleaned up.
+ @retval EFI_DEVICE_ERROR Some resources failed to be released.
+
+**/
+EFI_STATUS
+Ip4CleanProtocol (
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ if (EFI_ERROR (Ip4Cancel (IpInstance, NULL))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (EFI_ERROR (Ip4Groups (IpInstance, FALSE, NULL))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Some packets haven't been recycled. It is because either the
+ // user forgets to recycle the packets, or because the callback
+ // hasn't been called. Just leave it alone.
+ //
+ if (!IsListEmpty (&IpInstance->Delivered)) {
+ ;
+ }
+
+ if (IpInstance->Interface != NULL) {
+ RemoveEntryList (&IpInstance->AddrLink);
+ if (IpInstance->Interface->Arp != NULL) {
+ gBS->CloseProtocol (
+ IpInstance->Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ gIp4DriverBinding.DriverBindingHandle,
+ IpInstance->Handle
+ );
+ }
+ Ip4FreeInterface (IpInstance->Interface, IpInstance);
+ IpInstance->Interface = NULL;
+ }
+
+ if (IpInstance->RouteTable != NULL) {
+ if (IpInstance->RouteTable->Next != NULL) {
+ Ip4FreeRouteTable (IpInstance->RouteTable->Next);
+ }
+
+ Ip4FreeRouteTable (IpInstance->RouteTable);
+ IpInstance->RouteTable = NULL;
+ }
+
+ if (IpInstance->EfiRouteTable != NULL) {
+ FreePool (IpInstance->EfiRouteTable);
+ IpInstance->EfiRouteTable = NULL;
+ IpInstance->EfiRouteCount = 0;
+ }
+
+ if (IpInstance->Groups != NULL) {
+ FreePool (IpInstance->Groups);
+ IpInstance->Groups = NULL;
+ IpInstance->GroupCount = 0;
+ }
+
+ NetMapClean (&IpInstance->TxTokens);
+
+ NetMapClean (&IpInstance->RxTokens);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Assigns an IPv4 address and subnet mask to this EFI IPv4 Protocol driver instance.
+
+ The Configure() function is used to set, change, or reset the operational
+ parameters and filter settings for this EFI IPv4 Protocol instance. Until these
+ parameters have been set, no network traffic can be sent or received by this
+ instance. Once the parameters have been reset (by calling this function with
+ IpConfigData set to NULL), no more traffic can be sent or received until these
+ parameters have been set again. Each EFI IPv4 Protocol instance can be started
+ and stopped independently of each other by enabling or disabling their receive
+ filter settings with the Configure() function.
+
+ When IpConfigData.UseDefaultAddress is set to FALSE, the new station address will
+ be appended as an alias address into the addresses list in the EFI IPv4 Protocol
+ driver. While set to TRUE, Configure() will trigger the EFI_IP4_CONFIG_PROTOCOL
+ to retrieve the default IPv4 address if it is not available yet. Clients could
+ frequently call GetModeData() to check the status to ensure that the default IPv4
+ address is ready.
+
+ If operational parameters are reset or changed, any pending transmit and receive
+ requests will be cancelled. Their completion token status will be set to EFI_ABORTED
+ and their events will be signaled.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] IpConfigData Pointer to the EFI IPv4 Protocol configuration data structure.
+
+ @retval EFI_SUCCESS The driver instance was successfully opened.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ @retval EFI_UNSUPPORTED One or more of the following conditions is TRUE:
+ A configuration protocol (DHCP, BOOTP, RARP, etc.) could
+ not be located when clients choose to use the default IPv4
+ address. This EFI IPv4 Protocol implementation does not
+ support this requested filter or timeout setting.
+ @retval EFI_OUT_OF_RESOURCES The EFI IPv4 Protocol driver instance data could not be allocated.
+ @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the
+ IPv4 address or subnet mask can be changed. The interface must
+ also be stopped when switching to/from raw packet mode.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv4
+ Protocol driver instance is not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Configure (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_CONFIG_DATA *Current;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ BOOLEAN AddrOk;
+ IP4_ADDR IpAddress;
+ IP4_ADDR SubnetMask;
+
+ //
+ // First, validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Validate the configuration first.
+ //
+ if (IpConfigData != NULL) {
+
+ CopyMem (&IpAddress, &IpConfigData->StationAddress, sizeof (IP4_ADDR));
+ CopyMem (&SubnetMask, &IpConfigData->SubnetMask, sizeof (IP4_ADDR));
+
+ IpAddress = NTOHL (IpAddress);
+ SubnetMask = NTOHL (SubnetMask);
+
+ //
+ // Check whether the station address is a valid unicast address
+ //
+ if (!IpConfigData->UseDefaultAddress) {
+ AddrOk = Ip4StationAddressValid (IpAddress, SubnetMask);
+
+ if (!AddrOk) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // User can only update packet filters when already configured.
+ // If it wants to change the station address, it must configure(NULL)
+ // the instance first.
+ //
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ Current = &IpInstance->ConfigData;
+
+ if (Current->UseDefaultAddress != IpConfigData->UseDefaultAddress) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (!Current->UseDefaultAddress &&
+ (!EFI_IP4_EQUAL (&Current->StationAddress, &IpConfigData->StationAddress) ||
+ !EFI_IP4_EQUAL (&Current->SubnetMask, &IpConfigData->SubnetMask))) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (Current->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ //
+ // Configure the instance or clean it up.
+ //
+ if (IpConfigData != NULL) {
+ Status = Ip4ConfigProtocol (IpInstance, IpConfigData);
+ } else {
+ Status = Ip4CleanProtocol (IpInstance);
+
+ //
+ // Consider the following valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,
+ // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,
+ // the unload fails miserably.
+ //
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ IpInstance->State = IP4_STATE_UNCONFIGED;
+ }
+ }
+
+ //
+ // Update the MNP's configure data. Ip4ServiceConfigMnp will check
+ // whether it is necessary to reconfigure the MNP.
+ //
+ Ip4ServiceConfigMnp (IpInstance->Service, FALSE);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+
+}
+
+
+/**
+ Change the IP4 child's multicast setting. The caller
+ should make sure that the parameters is valid.
+
+ @param[in] IpInstance The IP4 child to change the setting.
+ @param[in] JoinFlag TRUE to join the group, otherwise leave it.
+ @param[in] GroupAddress The target group address.
+
+ @retval EFI_ALREADY_STARTED Want to join the group, but already a member of it.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton.
+ @retval EFI_SUCCESS Successfully updated the group setting.
+ @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.
+
+**/
+EFI_STATUS
+Ip4Groups (
+ IN IP4_PROTOCOL *IpInstance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL
+ )
+{
+ IP4_ADDR *Members;
+ IP4_ADDR Group;
+ UINT32 Index;
+
+ //
+ // Add it to the instance's Groups, and join the group by IGMP.
+ // IpInstance->Groups is in network byte order. IGMP operates in
+ // host byte order
+ //
+ if (JoinFlag) {
+ //
+ // When JoinFlag is TRUE, GroupAddress shouldn't be NULL.
+ //
+ ASSERT (GroupAddress != NULL);
+ CopyMem (&Group, GroupAddress, sizeof (IP4_ADDR));
+
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {
+ if (IpInstance->Groups[Index] == Group) {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+
+ Members = Ip4CombineGroups (IpInstance->Groups, IpInstance->GroupCount, Group);
+
+ if (Members == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (EFI_ERROR (Ip4JoinGroup (IpInstance, NTOHL (Group)))) {
+ FreePool (Members);
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (IpInstance->Groups != NULL) {
+ FreePool (IpInstance->Groups);
+ }
+
+ IpInstance->Groups = Members;
+ IpInstance->GroupCount++;
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Leave the group. Leave all the groups if GroupAddress is NULL.
+ // Must iterate from the end to the beginning because the GroupCount
+ // is decreamented each time an address is removed..
+ //
+ for (Index = IpInstance->GroupCount; Index > 0 ; Index--) {
+ Group = IpInstance->Groups[Index - 1];
+
+ if ((GroupAddress == NULL) || EFI_IP4_EQUAL (&Group, GroupAddress)) {
+ if (EFI_ERROR (Ip4LeaveGroup (IpInstance, NTOHL (Group)))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Ip4RemoveGroupAddr (IpInstance->Groups, IpInstance->GroupCount, Group);
+ IpInstance->GroupCount--;
+
+ if (IpInstance->GroupCount == 0) {
+ ASSERT (Index == 1);
+
+ FreePool (IpInstance->Groups);
+ IpInstance->Groups = NULL;
+ }
+
+ if (GroupAddress != NULL) {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);
+}
+
+
+/**
+ Joins and leaves multicast groups.
+
+ The Groups() function is used to join and leave multicast group sessions. Joining
+ a group will enable reception of matching multicast packets. Leaving a group will
+ disable the multicast packet reception.
+
+ If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave.
+ @param[in] GroupAddress Pointer to the IPv4 multicast address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:
+ - This is NULL.
+ - JoinFlag is TRUE and GroupAddress is NULL.
+ - GroupAddress is not NULL and *GroupAddress is
+ not a multicast IPv4 address.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES System resources could not be allocated.
+ @retval EFI_UNSUPPORTED This EFI IPv4 Protocol implementation does not support multicast groups.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when
+ JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Groups (
+ IN EFI_IP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ IP4_ADDR McastIp;
+
+ if ((This == NULL) || (JoinFlag && (GroupAddress == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GroupAddress != NULL) {
+ CopyMem (&McastIp, GroupAddress, sizeof (IP4_ADDR));
+
+ if (!IP4_IS_MULTICAST (NTOHL (McastIp))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Groups (IpInstance, JoinFlag, GroupAddress);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Adds and deletes routing table entries.
+
+ The Routes() function adds a route to or deletes a route from the routing table.
+
+ Routes are determined by comparing the SubnetAddress with the destination IPv4
+ address arithmetically AND-ed with the SubnetMask. The gateway address must be
+ on the same subnet as the configured station address.
+
+ The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0.
+ The default route matches all destination IPv4 addresses that do not match any
+ other routes.
+
+ A GatewayAddress that is zero is a nonroute. Packets are sent to the destination
+ IP address if it can be found in the ARP cache or on the local subnet. One automatic
+ nonroute entry will be inserted into the routing table for outgoing packets that
+ are addressed to a local subnet (gateway address of 0.0.0.0).
+
+ Each EFI IPv4 Protocol instance has its own independent routing table. Those EFI
+ IPv4 Protocol instances that use the default IPv4 address will also have copies
+ of the routing table that was provided by the EFI_IP4_CONFIG_PROTOCOL, and these
+ copies will be updated whenever the EIF IPv4 Protocol driver reconfigures its
+ instances. As a result, client modification to the routing table will be lost.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to
+ FALSE to add this route to the routing table. SubnetAddress
+ and SubnetMask are used as the key to each route entry.
+ @param[in] SubnetAddress The address of the subnet that needs to be routed.
+ @param[in] SubnetMask The subnet mask of SubnetAddress.
+ @param[in] GatewayAddress The unicast gateway IPv4 address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - SubnetAddress is NULL.
+ - SubnetMask is NULL.
+ - GatewayAddress is NULL.
+ - *SubnetAddress is not a valid subnet address.
+ - *SubnetMask is not a valid subnet mask.
+ - *GatewayAddress is not a valid unicast IPv4 address.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when
+ DeleteRoute is FALSE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Routes (
+ IN EFI_IP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ IP4_INTERFACE *IpIf;
+ IP4_ADDR Dest;
+ IP4_ADDR Netmask;
+ IP4_ADDR Nexthop;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First, validate the parameters
+ //
+ if ((This == NULL) || (SubnetAddress == NULL) ||
+ (SubnetMask == NULL) || (GatewayAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ CopyMem (&Dest, SubnetAddress, sizeof (IP4_ADDR));
+ CopyMem (&Netmask, SubnetMask, sizeof (IP4_ADDR));
+ CopyMem (&Nexthop, GatewayAddress, sizeof (IP4_ADDR));
+
+ Dest = NTOHL (Dest);
+ Netmask = NTOHL (Netmask);
+ Nexthop = NTOHL (Nexthop);
+
+ IpIf = IpInstance->Interface;
+
+ if (!IP4_IS_VALID_NETMASK (Netmask)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ //
+ // the gateway address must be a unicast on the connected network if not zero.
+ //
+ if ((Nexthop != IP4_ALLZERO_ADDRESS) &&
+ (!IP4_NET_EQUAL (Nexthop, IpIf->Ip, IpIf->SubnetMask) ||
+ IP4_IS_BROADCAST (Ip4GetNetCast (Nexthop, IpIf)))) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (DeleteRoute) {
+ Status = Ip4DelRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop);
+ } else {
+ Status = Ip4AddRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Check whether the user's token or event has already
+ been enqueued on IP4's list.
+
+ @param[in] Map The container of either user's transmit or receive
+ token.
+ @param[in] Item Current item to check against.
+ @param[in] Context The Token to check againist.
+
+ @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP.
+ @retval EFI_SUCCESS The current item isn't the same token/event as the
+ context.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ EFI_IP4_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_IP4_COMPLETION_TOKEN *) Context;
+ TokenInItem = (EFI_IP4_COMPLETION_TOKEN *) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validate the user's token against current station address.
+
+ @param[in] Token User's token to validate.
+ @param[in] IpIf The IP4 child's interface.
+ @param[in] RawData Set to TRUE to send unformatted packets.
+
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid.
+ @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long.
+ @retval EFI_SUCCESS The token is valid.
+
+**/
+EFI_STATUS
+Ip4TxTokenValid (
+ IN EFI_IP4_COMPLETION_TOKEN *Token,
+ IN IP4_INTERFACE *IpIf,
+ IN BOOLEAN RawData
+ )
+{
+ EFI_IP4_TRANSMIT_DATA *TxData;
+ EFI_IP4_OVERRIDE_DATA *Override;
+ IP4_ADDR Src;
+ IP4_ADDR Gateway;
+ UINT32 Offset;
+ UINT32 Index;
+ UINT32 HeadLen;
+
+ if ((Token == NULL) || (Token->Event == NULL) || (Token->Packet.TxData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TxData = Token->Packet.TxData;
+
+ //
+ // Check the fragment table: no empty fragment, and length isn't bogus.
+ //
+ if ((TxData->TotalDataLength == 0) || (TxData->FragmentCount == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Offset = TxData->TotalDataLength;
+
+ if (Offset > IP4_MAX_PACKET_SIZE) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||
+ (TxData->FragmentTable[Index].FragmentLength == 0)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Offset -= TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ if (Offset != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // NOTE that OptionsLength/OptionsBuffer/OverrideData are ignored if RawData
+ // is TRUE.
+ //
+ if (RawData) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Check the IP options: no more than 40 bytes and format is OK
+ //
+ if (TxData->OptionsLength != 0) {
+ if ((TxData->OptionsLength > 40) || (TxData->OptionsBuffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Ip4OptionIsValid (TxData->OptionsBuffer, TxData->OptionsLength, FALSE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Check the source and gateway: they must be a valid unicast.
+ // Gateway must also be on the connected network.
+ //
+ if (TxData->OverrideData != NULL) {
+ Override = TxData->OverrideData;
+
+ CopyMem (&Src, &Override->SourceAddress, sizeof (IP4_ADDR));
+ CopyMem (&Gateway, &Override->GatewayAddress, sizeof (IP4_ADDR));
+
+ Src = NTOHL (Src);
+ Gateway = NTOHL (Gateway);
+
+ if ((NetGetIpClass (Src) > IP4_ADDR_CLASSC) ||
+ (Src == IP4_ALLONE_ADDRESS) ||
+ IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If gateway isn't zero, it must be a unicast address, and
+ // on the connected network.
+ //
+ if ((Gateway != IP4_ALLZERO_ADDRESS) &&
+ ((NetGetIpClass (Gateway) > IP4_ADDR_CLASSC) ||
+ !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask) ||
+ IP4_IS_BROADCAST (Ip4GetNetCast (Gateway, IpIf)))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Check the packet length: Head length and packet length all has a limit
+ //
+ HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03);
+
+ if ((HeadLen > IP4_MAX_HEADLEN) ||
+ (TxData->TotalDataLength + HeadLen > IP4_MAX_PACKET_SIZE)) {
+
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The callback function for the net buffer which wraps the user's
+ transmit token. Although it seems this function is pretty simple,
+ there are some subtle things.
+ When user requests the IP to transmit a packet by passing it a
+ token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data
+ is wrapped in an net buffer. the net buffer's Free function is
+ set to Ip4FreeTxToken. The Token and token wrap are added to the
+ IP child's TxToken map. Then the buffer is passed to Ip4Output for
+ transmission. If something error happened before that, the buffer
+ is freed, which in turn will free the token wrap. The wrap may
+ have been added to the TxToken map or not, and the user's event
+ shouldn't be fired because we are still in the EfiIp4Transmit. If
+ the buffer has been sent by Ip4Output, it should be removed from
+ the TxToken map and user's event signaled. The token wrap and buffer
+ are bound together. Check the comments in Ip4Output for information
+ about IP fragmentation.
+
+ @param[in] Context The token's wrap.
+
+**/
+VOID
+EFIAPI
+Ip4FreeTxToken (
+ IN VOID *Context
+ )
+{
+ IP4_TXTOKEN_WRAP *Wrap;
+ NET_MAP_ITEM *Item;
+
+ Wrap = (IP4_TXTOKEN_WRAP *) Context;
+
+ //
+ // Signal IpSecRecycleEvent to inform IPsec free the memory
+ //
+ if (Wrap->IpSecRecycleSignal != NULL) {
+ gBS->SignalEvent (Wrap->IpSecRecycleSignal);
+ }
+
+ //
+ // Find the token in the instance's map. EfiIp4Transmit put the
+ // token to the map. If that failed, NetMapFindKey will return NULL.
+ //
+ Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);
+
+ if (Item != NULL) {
+ NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);
+ }
+
+ if (Wrap->Sent) {
+ gBS->SignalEvent (Wrap->Token->Event);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.
+ //
+ DispatchDpc ();
+ }
+
+ FreePool (Wrap);
+}
+
+
+/**
+ The callback function to Ip4Output to update the transmit status.
+
+ @param Ip4Instance The Ip4Instance that request the transmit.
+ @param Packet The user's transmit request.
+ @param IoStatus The result of the transmission.
+ @param Flag Not used during transmission.
+ @param Context The token's wrap.
+
+**/
+VOID
+Ip4OnPacketSent (
+ IP4_PROTOCOL *Ip4Instance,
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 Flag,
+ VOID *Context
+ )
+{
+ IP4_TXTOKEN_WRAP *Wrap;
+
+ //
+ // This is the transmission request from upper layer,
+ // not the IP4 driver itself.
+ //
+ ASSERT (Ip4Instance != NULL);
+
+ //
+ // The first fragment of the packet has been sent. Update
+ // the token's status. That is, if fragmented, the transmit's
+ // status is the first fragment's status. The Wrap will be
+ // release when all the fragments are release. Check the comments
+ // in Ip4FreeTxToken and Ip4Output for information.
+ //
+ Wrap = (IP4_TXTOKEN_WRAP *) Context;
+ Wrap->Token->Status = IoStatus;
+
+ NetbufFree (Wrap->Packet);
+}
+
+
+/**
+ Places outgoing data packets into the transmit queue.
+
+ The Transmit() function places a sending request in the transmit queue of this
+ EFI IPv4 Protocol instance. Whenever the packet in the token is sent out or some
+ errors occur, the event in the token will be signaled and the status is updated.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] Token Pointer to the transmit token.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more pameters are invalid.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.Event
+ was already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because the transmit
+ queue is full.
+ @retval EFI_NOT_FOUND Not route is found to destination address.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too
+ short to transmit.
+ @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length + total data length is
+ greater than MTU (or greater than the maximum packet size if
+ Token.Packet.TxData.OverrideData.
+ DoNotFragment is TRUE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Transmit (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ IP4_INTERFACE *IpIf;
+ IP4_TXTOKEN_WRAP *Wrap;
+ EFI_IP4_TRANSMIT_DATA *TxData;
+ EFI_IP4_CONFIG_DATA *Config;
+ EFI_IP4_OVERRIDE_DATA *Override;
+ IP4_HEAD Head;
+ IP4_ADDR GateWay;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ BOOLEAN DontFragment;
+ UINT32 HeadLen;
+ UINT8 RawHdrLen;
+ UINT32 OptionsLength;
+ UINT8 *OptionsBuffer;
+ VOID *FirstFragment;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ IpSb = IpInstance->Service;
+ IpIf = IpInstance->Interface;
+ Config = &IpInstance->ConfigData;
+
+ if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ //
+ // make sure that token is properly formated
+ //
+ Status = Ip4TxTokenValid (Token, IpIf, Config->RawData);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the token or signal already existed.
+ //
+ if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip4TokenExist, Token))) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Build the IP header, need to fill in the Tos, TotalLen, Id,
+ // fragment, Ttl, protocol, Src, and Dst.
+ //
+ TxData = Token->Packet.TxData;
+
+ FirstFragment = NULL;
+
+ if (Config->RawData) {
+ //
+ // When RawData is TRUE, first buffer in FragmentTable points to a raw
+ // IPv4 fragment including IPv4 header and options.
+ //
+ FirstFragment = TxData->FragmentTable[0].FragmentBuffer;
+ CopyMem (&RawHdrLen, FirstFragment, sizeof (UINT8));
+
+ RawHdrLen = (UINT8) (RawHdrLen & 0x0f);
+ if (RawHdrLen < 5) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ RawHdrLen = (UINT8) (RawHdrLen << 2);
+
+ CopyMem (&Head, FirstFragment, IP4_MIN_HEADLEN);
+
+ Ip4NtohHead (&Head);
+ HeadLen = 0;
+ DontFragment = IP4_DO_NOT_FRAGMENT (Head.Fragment);
+
+ if (!DontFragment) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ GateWay = IP4_ALLZERO_ADDRESS;
+
+ //
+ // Get IPv4 options from first fragment.
+ //
+ if (RawHdrLen == IP4_MIN_HEADLEN) {
+ OptionsLength = 0;
+ OptionsBuffer = NULL;
+ } else {
+ OptionsLength = RawHdrLen - IP4_MIN_HEADLEN;
+ OptionsBuffer = (UINT8 *) FirstFragment + IP4_MIN_HEADLEN;
+ }
+
+ //
+ // Trim off IPv4 header and options from first fragment.
+ //
+ TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment + RawHdrLen;
+ TxData->FragmentTable[0].FragmentLength = TxData->FragmentTable[0].FragmentLength - RawHdrLen;
+ } else {
+ CopyMem (&Head.Dst, &TxData->DestinationAddress, sizeof (IP4_ADDR));
+ Head.Dst = NTOHL (Head.Dst);
+
+ if (TxData->OverrideData != NULL) {
+ Override = TxData->OverrideData;
+ Head.Protocol = Override->Protocol;
+ Head.Tos = Override->TypeOfService;
+ Head.Ttl = Override->TimeToLive;
+ DontFragment = Override->DoNotFragment;
+
+ CopyMem (&Head.Src, &Override->SourceAddress, sizeof (IP4_ADDR));
+ CopyMem (&GateWay, &Override->GatewayAddress, sizeof (IP4_ADDR));
+
+ Head.Src = NTOHL (Head.Src);
+ GateWay = NTOHL (GateWay);
+ } else {
+ Head.Src = IpIf->Ip;
+ GateWay = IP4_ALLZERO_ADDRESS;
+ Head.Protocol = Config->DefaultProtocol;
+ Head.Tos = Config->TypeOfService;
+ Head.Ttl = Config->TimeToLive;
+ DontFragment = Config->DoNotFragment;
+ }
+
+ Head.Fragment = IP4_HEAD_FRAGMENT_FIELD (DontFragment, FALSE, 0);
+ HeadLen = (TxData->OptionsLength + 3) & (~0x03);
+
+ OptionsLength = TxData->OptionsLength;
+ OptionsBuffer = (UINT8 *) (TxData->OptionsBuffer);
+ }
+
+ //
+ // If don't fragment and fragment needed, return error
+ //
+ if (DontFragment && (TxData->TotalDataLength + HeadLen > IpSb->MaxPacketSize)) {
+ Status = EFI_BAD_BUFFER_SIZE;
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, it survives all the validation check. Wrap the token in
+ // a IP4_TXTOKEN_WRAP and the data in a netbuf
+ //
+ Status = EFI_OUT_OF_RESOURCES;
+ Wrap = AllocateZeroPool (sizeof (IP4_TXTOKEN_WRAP));
+ if (Wrap == NULL) {
+ goto ON_EXIT;
+ }
+
+ Wrap->IpInstance = IpInstance;
+ Wrap->Token = Token;
+ Wrap->Sent = FALSE;
+ Wrap->Life = IP4_US_TO_SEC (Config->TransmitTimeout);
+ Wrap->Packet = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ TxData->FragmentCount,
+ IP4_MAX_HEADLEN,
+ 0,
+ Ip4FreeTxToken,
+ Wrap
+ );
+
+ if (Wrap->Packet == NULL) {
+ FreePool (Wrap);
+ goto ON_EXIT;
+ }
+
+ Token->Status = EFI_NOT_READY;
+
+ if (EFI_ERROR (NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap))) {
+ //
+ // NetbufFree will call Ip4FreeTxToken, which in turn will
+ // free the IP4_TXTOKEN_WRAP. Now, the token wrap hasn't been
+ // enqueued.
+ //
+ if (Config->RawData) {
+ //
+ // Restore pointer of first fragment in RawData mode.
+ //
+ TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment;
+ }
+
+ NetbufFree (Wrap->Packet);
+ goto ON_EXIT;
+ }
+
+ //
+ // Mark the packet sent before output it. Mark it not sent again if the
+ // returned status is not EFI_SUCCESS;
+ //
+ Wrap->Sent = TRUE;
+
+ Status = Ip4Output (
+ IpSb,
+ IpInstance,
+ Wrap->Packet,
+ &Head,
+ OptionsBuffer,
+ OptionsLength,
+ GateWay,
+ Ip4OnPacketSent,
+ Wrap
+ );
+
+ if (EFI_ERROR (Status)) {
+ Wrap->Sent = FALSE;
+
+ if (Config->RawData) {
+ //
+ // Restore pointer of first fragment in RawData mode.
+ //
+ TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment;
+ }
+
+ NetbufFree (Wrap->Packet);
+ }
+
+ if (Config->RawData) {
+ //
+ // Restore pointer of first fragment in RawData mode.
+ //
+ TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Places a receiving request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous.
+
+ The Token.Event field in the completion token must be filled in by the caller
+ and cannot be NULL. When the receive operation completes, the EFI IPv4 Protocol
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event
+ is signaled.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI IPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, RARP, etc.)
+ is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system
+ resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI IPv4 Protocol instance has been reset to startup defaults.
+ EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already
+ in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Receive (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the toke is already on the receive queue.
+ //
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip4TokenExist, Token);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Queue the token then check whether there is pending received packet.
+ //
+ Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip4InstanceDeliverPacket (IpInstance);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of this instane's receive
+ // event.
+ //
+ DispatchDpc ();
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Cancel the transmitted but not recycled packet. If a matching
+ token is found, it will call Ip4CancelPacket to cancel the
+ packet. Ip4CancelPacket will cancel all the fragments of the
+ packet. When all the fragments are freed, the IP4_TXTOKEN_WRAP
+ will be deleted from the Map, and user's event signalled.
+ Because Ip4CancelPacket and other functions are all called in
+ line, so, after Ip4CancelPacket returns, the Item has been freed.
+
+ @param[in] Map The IP4 child's transmit queue.
+ @param[in] Item The current transmitted packet to test.
+ @param[in] Context The user's token to cancel.
+
+ @retval EFI_SUCCESS Continue to check the next Item.
+ @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4CancelTxTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ IP4_TXTOKEN_WRAP *Wrap;
+
+ Token = (EFI_IP4_COMPLETION_TOKEN *) Context;
+
+ //
+ // Return EFI_SUCCESS to check the next item in the map if
+ // this one doesn't match.
+ //
+ if ((Token != NULL) && (Token != Item->Key)) {
+ return EFI_SUCCESS;
+ }
+
+ Wrap = (IP4_TXTOKEN_WRAP *) Item->Value;
+ ASSERT (Wrap != NULL);
+
+ //
+ // Don't access the Item, Wrap and Token's members after this point.
+ // Item and wrap has been freed. And we no longer own the Token.
+ //
+ Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
+
+ //
+ // If only one item is to be cancel, return EFI_ABORTED to stop
+ // iterating the map any more.
+ //
+ if (Token != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the receive request. This is quiet simple, because
+ it is only enqueued in our local receive map.
+
+ @param[in] Map The IP4 child's receive queue.
+ @param[in] Item Current receive request to cancel.
+ @param[in] Context The user's token to cancel.
+
+ @retval EFI_SUCCESS Continue to check the next receive request on the
+ queue.
+ @retval EFI_ABORTED The user's token (token != NULL) has been
+ cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4CancelRxTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ EFI_IP4_COMPLETION_TOKEN *This;
+
+ Token = (EFI_IP4_COMPLETION_TOKEN *) Context;
+ This = Item->Key;
+
+ if ((Token != NULL) && (Token != This)) {
+ return EFI_SUCCESS;
+ }
+
+ NetMapRemoveItem (Map, Item, NULL);
+
+ This->Status = EFI_ABORTED;
+ This->Packet.RxData = NULL;
+ gBS->SignalEvent (This->Event);
+
+ if (Token != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the user's receive/transmit request.
+
+ @param[in] IpInstance The IP4 child.
+ @param[in] Token The token to cancel. If NULL, all token will be
+ cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled.
+ @retval EFI_NOT_FOUND The token isn't found on either the
+ transmit/receive queue.
+ @retval EFI_DEVICE_ERROR Not all token is cancelled when Token is NULL.
+
+**/
+EFI_STATUS
+Ip4Cancel (
+ IN IP4_PROTOCOL *IpInstance,
+ IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // First check the transmitted packet. Ip4CancelTxTokens returns
+ // EFI_ABORTED to mean that the token has been cancelled when
+ // token != NULL. So, return EFI_SUCCESS for this condition.
+ //
+ Status = NetMapIterate (&IpInstance->TxTokens, Ip4CancelTxTokens, Token);
+
+ if (EFI_ERROR (Status)) {
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ //
+ // Check the receive queue. Ip4CancelRxTokens also returns EFI_ABORT
+ // for Token!=NULL and it is cancelled.
+ //
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip4CancelRxTokens, Token);
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's
+ // events.
+ //
+ DispatchDpc ();
+ if (EFI_ERROR (Status)) {
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ //
+ // OK, if the Token is found when Token != NULL, the NetMapIterate
+ // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.
+ //
+ if (Token != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Token == NULL, cancel all the tokens. return error if no
+ // all of them are cancelled.
+ //
+ if (!NetMapIsEmpty (&IpInstance->TxTokens) ||
+ !NetMapIsEmpty (&IpInstance->RxTokens)) {
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Abort an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token->Status will be set to EFI_ABORTED and then Token->Event will
+ be signaled. If the token is not in one of the queues, which usually means the
+ asynchronous operation has completed, this function will not signal the token
+ and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_IP4_PROTOCOL.Transmit() or
+ EFI_IP4_PROTOCOL.Receive(). If NULL, all pending
+ tokens are aborted. Type EFI_IP4_COMPLETION_TOKEN is
+ defined in EFI_IP4_PROTOCOL.Transmit().
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token.->Event was signaled. When Token is NULL, all
+ pending requests were aborted and their events were signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was
+ not found in the transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Cancel (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Cancel (IpInstance, Token);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function polls for incoming data packets and processes outgoing data
+ packets. Network drivers and applications can call the EFI_IP4_PROTOCOL.Poll()
+ function to increase the rate that data packets are moved between the communications
+ device and the transmit and receive queues.
+
+ In some systems the periodic timer event may not poll the underlying communications
+ device fast enough to transmit and/or receive all data packets without missing
+ incoming packets or dropping outgoing packets. Drivers and applications that are
+ experiencing packet loss should try calling the EFI_IP4_PROTOCOL.Poll() function
+ more often.
+
+ @param[in] This Pointer to the EFI_IP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI IPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp4Poll (
+ IN EFI_IP4_PROTOCOL *This
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ if (IpInstance->State == IP4_STATE_UNCONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ Mnp = IpInstance->Service->Mnp;
+
+ //
+ // Don't lock the Poll function to enable the deliver of
+ // the packet polled up.
+ //
+ return Mnp->Poll (Mnp);
+}
+
+/**
+ Decrease the life of the transmitted packets. If it is
+ decreased to zero, cancel the packet. This function is
+ called by Ip4PacketTimerTicking which time out both the
+ received-but-not-delivered and transmitted-but-not-recycle
+ packets.
+
+ @param[in] Map The IP4 child's transmit map.
+ @param[in] Item Current transmitted packet.
+ @param[in] Context Not used.
+
+ @retval EFI_SUCCESS Always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4SentPacketTicking (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ IP4_TXTOKEN_WRAP *Wrap;
+
+ Wrap = (IP4_TXTOKEN_WRAP *) Item->Value;
+ ASSERT (Wrap != NULL);
+
+ if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {
+ Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ There are two steps for this the heart beat timer of IP4 service instance.
+ First, it times out all of its IP4 children's received-but-not-delivered
+ and transmitted-but-not-recycle packets, and provides time input for its
+ IGMP protocol.
+ Second, a dedicated timer is used to poll underlying media status. In case
+ of cable swap, a new round auto configuration will be initiated. The timer
+ will signal the IP4 to run DHCP configuration again. IP4 driver will free
+ old IP address related resource, such as route table and Interface, then
+ initiate a DHCP process to acquire new IP, eventually create route table
+ for new IP address.
+
+ @param[in] Event The IP4 service instance's heart beat timer.
+ @param[in] Context The IP4 service instance.
+
+**/
+VOID
+EFIAPI
+Ip4TimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_SERVICE *IpSb;
+ BOOLEAN OldMediaPresent;
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_MODE SnpModeData;
+
+ IpSb = (IP4_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);
+
+ OldMediaPresent = IpSb->MediaPresent;
+
+ Ip4PacketTimerTicking (IpSb);
+ Ip4IgmpTicking (IpSb);
+
+ //
+ // Get fresh mode data from MNP, since underlying media status may change.
+ // Here, it needs to mention that the MediaPresent can also be checked even if
+ // EFI_NOT_STARTED returned while this MNP child driver instance isn't configured.
+ //
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &SnpModeData);
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {
+ return;
+ }
+
+ IpSb->MediaPresent = SnpModeData.MediaPresent;
+ //
+ // Media transimit Unpresent to Present means new link movement is detected.
+ //
+ if (!OldMediaPresent && IpSb->MediaPresent && (IpSb->Ip4Config2Instance.Policy == Ip4Config2PolicyDhcp)) {
+ //
+ // Signal the IP4 to run the dhcp configuration again. IP4 driver will free
+ // old IP address related resource, such as route table and Interface, then
+ // initiate a DHCP round to acquire new IP, eventually
+ // create route table for new IP address.
+ //
+ if (IpSb->ReconfigEvent != NULL) {
+ Status = gBS->SignalEvent (IpSb->ReconfigEvent);
+ DispatchDpc ();
+ }
+ }
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h
new file mode 100644
index 0000000000..f6b4047941
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h
@@ -0,0 +1,405 @@
+/** @file
+ Ip4 internal functions and type defintions.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_IMPL_H__
+#define __EFI_IP4_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/IpSec.h>
+#include <Protocol/Ip4.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Arp.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Protocol/HiiConfigAccess.h>
+
+#include <IndustryStandard/Dhcp.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiHiiServicesLib.h>
+
+#include "Ip4Common.h"
+#include "Ip4Driver.h"
+#include "Ip4If.h"
+#include "Ip4Icmp.h"
+#include "Ip4Option.h"
+#include "Ip4Igmp.h"
+#include "Ip4Route.h"
+#include "Ip4Input.h"
+#include "Ip4Output.h"
+#include "Ip4Config2Impl.h"
+#include "Ip4Config2Nv.h"
+#include "Ip4NvData.h"
+
+#define IP4_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '4', 'P')
+#define IP4_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '4', 'S')
+
+//
+// The state of IP4 protocol. It starts from UNCONFIGED. if it is
+// successfully configured, it goes to CONFIGED. if configure NULL
+// is called, it becomes UNCONFIGED again.
+//
+#define IP4_STATE_UNCONFIGED 0
+#define IP4_STATE_CONFIGED 1
+
+//
+// The state of IP4 service. It starts from UNSTARTED. It transits
+// to STARTED if autoconfigure is started. If default address is
+// configured, it becomes CONFIGED. and if partly destroyed, it goes
+// to DESTROY.
+//
+#define IP4_SERVICE_UNSTARTED 0
+#define IP4_SERVICE_STARTED 1
+#define IP4_SERVICE_CONFIGED 2
+#define IP4_SERVICE_DESTROY 3
+
+
+///
+/// IP4_TXTOKEN_WRAP wraps the upper layer's transmit token.
+/// The user's data is kept in the Packet. When fragment is
+/// needed, each fragment of the Packet has a reference to the
+/// Packet, no data is actually copied. The Packet will be
+/// released when all the fragments of it have been recycled by
+/// MNP. Upon then, the IP4_TXTOKEN_WRAP will be released, and
+/// user's event signalled.
+///
+typedef struct {
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ EFI_EVENT IpSecRecycleSignal;
+ NET_BUF *Packet;
+ BOOLEAN Sent;
+ INTN Life;
+} IP4_TXTOKEN_WRAP;
+
+///
+/// IP4_IPSEC_WRAP wraps the packet received from MNP layer. The packet
+/// will be released after it has been processed by the receiver. Upon then,
+/// the IP4_IPSEC_WRAP will be released, and the IpSecRecycleSignal will be signaled
+/// to notice IPsec to free the resources.
+///
+typedef struct {
+ EFI_EVENT IpSecRecycleSignal;
+ NET_BUF *Packet;
+} IP4_IPSEC_WRAP;
+
+///
+/// IP4_RXDATA_WRAP wraps the data IP4 child delivers to the
+/// upper layers. The received packet is kept in the Packet.
+/// The Packet itself may be constructured from some fragments.
+/// All the fragments of the Packet is organized by a
+/// IP4_ASSEMBLE_ENTRY structure. If the Packet is recycled by
+/// the upper layer, the assemble entry and its associated
+/// fragments will be freed at last.
+///
+typedef struct {
+ LIST_ENTRY Link;
+ IP4_PROTOCOL *IpInstance;
+ NET_BUF *Packet;
+ EFI_IP4_RECEIVE_DATA RxData;
+} IP4_RXDATA_WRAP;
+
+
+struct _IP4_PROTOCOL {
+ UINT32 Signature;
+
+ EFI_IP4_PROTOCOL Ip4Proto;
+ EFI_HANDLE Handle;
+ INTN State;
+
+ BOOLEAN InDestroy;
+
+ IP4_SERVICE *Service;
+ LIST_ENTRY Link; // Link to all the IP protocol from the service
+
+ //
+ // User's transmit/receive tokens, and received/deliverd packets
+ //
+ NET_MAP RxTokens;
+ NET_MAP TxTokens; // map between (User's Token, IP4_TXTOKE_WRAP)
+ LIST_ENTRY Received; // Received but not delivered packet
+ LIST_ENTRY Delivered; // Delivered and to be recycled packets
+ EFI_LOCK RecycleLock;
+
+ //
+ // Instance's address and route tables. There are two route tables.
+ // RouteTable is used by the IP4 driver to route packet. EfiRouteTable
+ // is used to communicate the current route info to the upper layer.
+ //
+ IP4_INTERFACE *Interface;
+ LIST_ENTRY AddrLink; // Ip instances with the same IP address.
+ IP4_ROUTE_TABLE *RouteTable;
+
+ EFI_IP4_ROUTE_TABLE *EfiRouteTable;
+ UINT32 EfiRouteCount;
+
+ //
+ // IGMP data for this instance
+ //
+ IP4_ADDR *Groups; // stored in network byte order
+ UINT32 GroupCount;
+
+ EFI_IP4_CONFIG_DATA ConfigData;
+
+};
+
+struct _IP4_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ INTN State;
+
+ //
+ // List of all the IP instances and interfaces, and default
+ // interface and route table and caches.
+ //
+ UINTN NumChildren;
+ LIST_ENTRY Children;
+
+ LIST_ENTRY Interfaces;
+
+ IP4_INTERFACE *DefaultInterface;
+ IP4_ROUTE_TABLE *DefaultRouteTable;
+
+ //
+ // Ip reassemble utilities, and IGMP data
+ //
+ IP4_ASSEMBLE_TABLE Assemble;
+ IGMP_SERVICE_DATA IgmpCtrl;
+
+ //
+ // Low level protocol used by this service instance
+ //
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+
+ EFI_HANDLE MnpChildHandle;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ EFI_EVENT Timer;
+
+ EFI_EVENT ReconfigEvent;
+
+ BOOLEAN Reconfig;
+
+ //
+ // Underlying media present status.
+ //
+ BOOLEAN MediaPresent;
+
+ //
+ // IPv4 Configuration II Protocol instance
+ //
+ IP4_CONFIG2_INSTANCE Ip4Config2Instance;
+
+ CHAR16 *MacString;
+
+ UINT32 MaxPacketSize;
+ UINT32 OldMaxPacketSize; ///< The MTU before IPsec enable.
+};
+
+#define IP4_INSTANCE_FROM_PROTOCOL(Ip4) \
+ CR ((Ip4), IP4_PROTOCOL, Ip4Proto, IP4_PROTOCOL_SIGNATURE)
+
+#define IP4_SERVICE_FROM_PROTOCOL(Sb) \
+ CR ((Sb), IP4_SERVICE, ServiceBinding, IP4_SERVICE_SIGNATURE)
+
+#define IP4_SERVICE_FROM_CONFIG2_INSTANCE(This) \
+ CR (This, IP4_SERVICE, Ip4Config2Instance, IP4_SERVICE_SIGNATURE)
+
+
+#define IP4_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured)
+
+extern EFI_IP4_PROTOCOL mEfiIp4ProtocolTemplete;
+
+/**
+ Config the MNP parameter used by IP. The IP driver use one MNP
+ child to transmit/receive frames. By default, it configures MNP
+ to receive unicast/multicast/broadcast. And it will enable/disable
+ the promiscous receive according to whether there is IP child
+ enable that or not. If Force is FALSE, it will iterate through
+ all the IP children to check whether the promiscuous receive
+ setting has been changed. If it hasn't been changed, it won't
+ reconfigure the MNP. If Force is TRUE, the MNP is configured no
+ matter whether that is changed or not.
+
+ @param[in] IpSb The IP4 service instance that is to be changed.
+ @param[in] Force Force the configuration or not.
+
+ @retval EFI_SUCCESS The MNP is successfully configured/reconfigured.
+ @retval Others Configuration failed.
+
+**/
+EFI_STATUS
+Ip4ServiceConfigMnp (
+ IN IP4_SERVICE *IpSb,
+ IN BOOLEAN Force
+ );
+
+/**
+ Intiialize the IP4_PROTOCOL structure to the unconfigured states.
+
+ @param IpSb The IP4 service instance.
+ @param IpInstance The IP4 child instance.
+
+**/
+VOID
+Ip4InitProtocol (
+ IN IP4_SERVICE *IpSb,
+ IN OUT IP4_PROTOCOL *IpInstance
+ );
+
+/**
+ Clean up the IP4 child, release all the resources used by it.
+
+ @param[in] IpInstance The IP4 child to clean up.
+
+ @retval EFI_SUCCESS The IP4 child is cleaned up.
+ @retval EFI_DEVICE_ERROR Some resources failed to be released.
+
+**/
+EFI_STATUS
+Ip4CleanProtocol (
+ IN IP4_PROTOCOL *IpInstance
+ );
+
+/**
+ Cancel the user's receive/transmit request.
+
+ @param[in] IpInstance The IP4 child.
+ @param[in] Token The token to cancel. If NULL, all token will be
+ cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled.
+ @retval EFI_NOT_FOUND The token isn't found on either the
+ transmit/receive queue.
+ @retval EFI_DEVICE_ERROR Not all token is cancelled when Token is NULL.
+
+**/
+EFI_STATUS
+Ip4Cancel (
+ IN IP4_PROTOCOL *IpInstance,
+ IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Change the IP4 child's multicast setting. The caller
+ should make sure that the parameters is valid.
+
+ @param[in] IpInstance The IP4 child to change the setting.
+ @param[in] JoinFlag TRUE to join the group, otherwise leave it
+ @param[in] GroupAddress The target group address
+
+ @retval EFI_ALREADY_STARTED Want to join the group, but already a member of it
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton
+ @retval EFI_SUCCESS Successfully updated the group setting.
+ @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.
+
+**/
+EFI_STATUS
+Ip4Groups (
+ IN IP4_PROTOCOL *IpInstance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL
+ );
+
+/**
+ The heart beat timer of IP4 service instance. It times out
+ all of its IP4 children's received-but-not-delivered and
+ transmitted-but-not-recycle packets, and provides time input
+ for its IGMP protocol.
+
+ @param[in] Event The IP4 service instance's heart beat timer.
+ @param[in] Context The IP4 service instance.
+
+**/
+VOID
+EFIAPI
+Ip4TimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Decrease the life of the transmitted packets. If it is
+ decreased to zero, cancel the packet. This function is
+ called by Ip4PacketTimerTicking which time out both the
+ received-but-not-delivered and transmitted-but-not-recycle
+ packets.
+
+ @param[in] Map The IP4 child's transmit map.
+ @param[in] Item Current transmitted packet.
+ @param[in] Context Not used.
+
+ @retval EFI_SUCCESS Always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4SentPacketTicking (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ );
+
+/**
+ The callback function for the net buffer which wraps the user's
+ transmit token. Although it seems this function is pretty simple,
+ there are some subtle things.
+ When user requests the IP to transmit a packet by passing it a
+ token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data
+ is wrapped in an net buffer. the net buffer's Free function is
+ set to Ip4FreeTxToken. The Token and token wrap are added to the
+ IP child's TxToken map. Then the buffer is passed to Ip4Output for
+ transmission. If something error happened before that, the buffer
+ is freed, which in turn will free the token wrap. The wrap may
+ have been added to the TxToken map or not, and the user's event
+ shouldn't be fired because we are still in the EfiIp4Transmit. If
+ the buffer has been sent by Ip4Output, it should be removed from
+ the TxToken map and user's event signaled. The token wrap and buffer
+ are bound together. Check the comments in Ip4Output for information
+ about IP fragmentation.
+
+ @param[in] Context The token's wrap.
+
+**/
+VOID
+EFIAPI
+Ip4FreeTxToken (
+ IN VOID *Context
+ );
+
+extern EFI_IPSEC2_PROTOCOL *mIpSec;
+extern BOOLEAN mIpSec2Installed;
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c
new file mode 100644
index 0000000000..09b8f2bac2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c
@@ -0,0 +1,1609 @@
+/** @file
+ IP4 input process.
+
+Copyright (c) 2005 - 2014, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Create an empty assemble entry for the packet identified by
+ (Dst, Src, Id, Protocol). The default life for the packet is
+ 120 seconds.
+
+ @param[in] Dst The destination address
+ @param[in] Src The source address
+ @param[in] Id The ID field in IP header
+ @param[in] Protocol The protocol field in IP header
+
+ @return NULL if failed to allocate memory for the entry, otherwise
+ the point to just created reassemble entry.
+
+**/
+IP4_ASSEMBLE_ENTRY *
+Ip4CreateAssembleEntry (
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src,
+ IN UINT16 Id,
+ IN UINT8 Protocol
+ )
+{
+
+ IP4_ASSEMBLE_ENTRY *Assemble;
+
+ Assemble = AllocatePool (sizeof (IP4_ASSEMBLE_ENTRY));
+
+ if (Assemble == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Assemble->Link);
+ InitializeListHead (&Assemble->Fragments);
+
+ Assemble->Dst = Dst;
+ Assemble->Src = Src;
+ Assemble->Id = Id;
+ Assemble->Protocol = Protocol;
+ Assemble->TotalLen = 0;
+ Assemble->CurLen = 0;
+ Assemble->Head = NULL;
+ Assemble->Info = NULL;
+ Assemble->Life = IP4_FRAGMENT_LIFE;
+
+ return Assemble;
+}
+
+
+/**
+ Release all the fragments of a packet, then free the assemble entry.
+
+ @param[in] Assemble The assemble entry to free
+
+**/
+VOID
+Ip4FreeAssembleEntry (
+ IN IP4_ASSEMBLE_ENTRY *Assemble
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ NET_BUF *Fragment;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {
+ Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ RemoveEntryList (Entry);
+ NetbufFree (Fragment);
+ }
+
+ FreePool (Assemble);
+}
+
+
+/**
+ Initialize an already allocated assemble table. This is generally
+ the assemble table embedded in the IP4 service instance.
+
+ @param[in, out] Table The assemble table to initialize.
+
+**/
+VOID
+Ip4InitAssembleTable (
+ IN OUT IP4_ASSEMBLE_TABLE *Table
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
+ InitializeListHead (&Table->Bucket[Index]);
+ }
+}
+
+
+/**
+ Clean up the assemble table: remove all the fragments
+ and assemble entries.
+
+ @param[in] Table The assemble table to clean up
+
+**/
+VOID
+Ip4CleanAssembleTable (
+ IN IP4_ASSEMBLE_TABLE *Table
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ASSEMBLE_ENTRY *Assemble;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);
+
+ RemoveEntryList (Entry);
+ Ip4FreeAssembleEntry (Assemble);
+ }
+ }
+}
+
+
+/**
+ Trim the packet to fit in [Start, End), and update the per
+ packet information.
+
+ @param Packet Packet to trim
+ @param Start The sequence of the first byte to fit in
+ @param End One beyond the sequence of last byte to fit in.
+
+**/
+VOID
+Ip4TrimPacket (
+ IN OUT NET_BUF *Packet,
+ IN INTN Start,
+ IN INTN End
+ )
+{
+ IP4_CLIP_INFO *Info;
+ INTN Len;
+
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ ASSERT (Info->Start + Info->Length == Info->End);
+ ASSERT ((Info->Start < End) && (Start < Info->End));
+
+ if (Info->Start < Start) {
+ Len = Start - Info->Start;
+
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);
+ Info->Start = Start;
+ Info->Length -= Len;
+ }
+
+ if (End < Info->End) {
+ Len = End - Info->End;
+
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);
+ Info->End = End;
+ Info->Length -= Len;
+ }
+}
+
+
+/**
+ Release all the fragments of the packet. This is the callback for
+ the assembled packet's OnFree. It will free the assemble entry,
+ which in turn will free all the fragments of the packet.
+
+ @param[in] Arg The assemble entry to free
+
+**/
+VOID
+EFIAPI
+Ip4OnFreeFragments (
+ IN VOID *Arg
+ )
+{
+ Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *) Arg);
+}
+
+
+/**
+ Reassemble the IP fragments. If all the fragments of the packet
+ have been received, it will wrap the packet in a net buffer then
+ return it to caller. If the packet can't be assembled, NULL is
+ return.
+
+ @param Table The assemble table used. New assemble entry will be created
+ if the Packet is from a new chain of fragments.
+ @param Packet The fragment to assemble. It might be freed if the fragment
+ can't be re-assembled.
+
+ @return NULL if the packet can't be reassemble. The point to just assembled
+ packet if all the fragments of the packet have arrived.
+
+**/
+NET_BUF *
+Ip4Reassemble (
+ IN OUT IP4_ASSEMBLE_TABLE *Table,
+ IN OUT NET_BUF *Packet
+ )
+{
+ IP4_HEAD *IpHead;
+ IP4_CLIP_INFO *This;
+ IP4_CLIP_INFO *Node;
+ IP4_ASSEMBLE_ENTRY *Assemble;
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Prev;
+ LIST_ENTRY *Cur;
+ NET_BUF *Fragment;
+ NET_BUF *NewPacket;
+ INTN Index;
+
+ IpHead = Packet->Ip.Ip4;
+ This = IP4_GET_CLIP_INFO (Packet);
+
+ ASSERT (IpHead != NULL);
+
+ //
+ // First: find the related assemble entry
+ //
+ Assemble = NULL;
+ Index = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol);
+
+ NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link);
+
+ if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) &&
+ (Assemble->Id == IpHead->Id) && (Assemble->Protocol == IpHead->Protocol)) {
+ break;
+ }
+ }
+
+ //
+ // Create a new assemble entry if no assemble entry is related to this packet
+ //
+ if (Cur == &Table->Bucket[Index]) {
+ Assemble = Ip4CreateAssembleEntry (
+ IpHead->Dst,
+ IpHead->Src,
+ IpHead->Id,
+ IpHead->Protocol
+ );
+
+ if (Assemble == NULL) {
+ goto DROP;
+ }
+
+ InsertHeadList (&Table->Bucket[Index], &Assemble->Link);
+ }
+ //
+ // Assemble shouldn't be NULL here
+ //
+ ASSERT (Assemble != NULL);
+
+ //
+ // Find the point to insert the packet: before the first
+ // fragment with THIS.Start < CUR.Start. the previous one
+ // has PREV.Start <= THIS.Start < CUR.Start.
+ //
+ Head = &Assemble->Fragments;
+
+ NET_LIST_FOR_EACH (Cur, Head) {
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (This->Start < IP4_GET_CLIP_INFO (Fragment)->Start) {
+ break;
+ }
+ }
+
+ //
+ // Check whether the current fragment overlaps with the previous one.
+ // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to
+ // check whether THIS.Start < PREV.End for overlap. If two fragments
+ // overlaps, trim the overlapped part off THIS fragment.
+ //
+ if ((Prev = Cur->BackLink) != Head) {
+ Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
+ Node = IP4_GET_CLIP_INFO (Fragment);
+
+ if (This->Start < Node->End) {
+ if (This->End <= Node->End) {
+ NetbufFree (Packet);
+ return NULL;
+ }
+
+ Ip4TrimPacket (Packet, Node->End, This->End);
+ }
+ }
+
+ //
+ // Insert the fragment into the packet. The fragment may be removed
+ // from the list by the following checks.
+ //
+ NetListInsertBefore (Cur, &Packet->List);
+
+ //
+ // Check the packets after the insert point. It holds that:
+ // THIS.Start <= NODE.Start < NODE.End. The equality holds
+ // if PREV and NEXT are continuous. THIS fragment may fill
+ // several holes. Remove the completely overlapped fragments
+ //
+ while (Cur != Head) {
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Node = IP4_GET_CLIP_INFO (Fragment);
+
+ //
+ // Remove fragments completely overlapped by this fragment
+ //
+ if (Node->End <= This->End) {
+ Cur = Cur->ForwardLink;
+
+ RemoveEntryList (&Fragment->List);
+ Assemble->CurLen -= Node->Length;
+
+ NetbufFree (Fragment);
+ continue;
+ }
+
+ //
+ // The conditions are: THIS.Start <= NODE.Start, and THIS.End <
+ // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.
+ // If two fragments start at the same offset, remove THIS fragment
+ // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).
+ //
+ if (Node->Start < This->End) {
+ if (This->Start == Node->Start) {
+ RemoveEntryList (&Packet->List);
+ goto DROP;
+ }
+
+ Ip4TrimPacket (Packet, This->Start, Node->Start);
+ }
+
+ break;
+ }
+
+ //
+ // Update the assemble info: increase the current length. If it is
+ // the frist fragment, update the packet's IP head and per packet
+ // info. If it is the last fragment, update the total length.
+ //
+ Assemble->CurLen += This->Length;
+
+ if (This->Start == 0) {
+ //
+ // Once the first fragment is enqueued, it can't be removed
+ // from the fragment list. So, Assemble->Head always point
+ // to valid memory area.
+ //
+ ASSERT (Assemble->Head == NULL);
+
+ Assemble->Head = IpHead;
+ Assemble->Info = IP4_GET_CLIP_INFO (Packet);
+ }
+
+ //
+ // Don't update the length more than once.
+ //
+ if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (Assemble->TotalLen == 0)) {
+ Assemble->TotalLen = This->End;
+ }
+
+ //
+ // Deliver the whole packet if all the fragments received.
+ // All fragments received if:
+ // 1. received the last one, so, the total length is know
+ // 2. received all the data. If the last fragment on the
+ // queue ends at the total length, all data is received.
+ //
+ if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {
+
+ RemoveEntryList (&Assemble->Link);
+
+ //
+ // If the packet is properly formated, the last fragment's End
+ // equals to the packet's total length. Otherwise, the packet
+ // is a fake, drop it now.
+ //
+ Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List);
+
+ if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) {
+ Ip4FreeAssembleEntry (Assemble);
+ return NULL;
+ }
+
+ //
+ // Wrap the packet in a net buffer then deliver it up
+ //
+ NewPacket = NetbufFromBufList (
+ &Assemble->Fragments,
+ 0,
+ 0,
+ Ip4OnFreeFragments,
+ Assemble
+ );
+
+ if (NewPacket == NULL) {
+ Ip4FreeAssembleEntry (Assemble);
+ return NULL;
+ }
+
+ NewPacket->Ip.Ip4 = Assemble->Head;
+
+ ASSERT (Assemble->Info != NULL);
+
+ CopyMem (
+ IP4_GET_CLIP_INFO (NewPacket),
+ Assemble->Info,
+ sizeof (*IP4_GET_CLIP_INFO (NewPacket))
+ );
+
+ return NewPacket;
+ }
+
+ return NULL;
+
+DROP:
+ NetbufFree (Packet);
+ return NULL;
+}
+
+/**
+ The callback function for the net buffer which wraps the packet processed by
+ IPsec. It releases the wrap packet and also signals IPsec to free the resources.
+
+ @param[in] Arg The wrap context
+
+**/
+VOID
+EFIAPI
+Ip4IpSecFree (
+ IN VOID *Arg
+ )
+{
+ IP4_IPSEC_WRAP *Wrap;
+
+ Wrap = (IP4_IPSEC_WRAP *) Arg;
+
+ if (Wrap->IpSecRecycleSignal != NULL) {
+ gBS->SignalEvent (Wrap->IpSecRecycleSignal);
+ }
+
+ NetbufFree (Wrap->Packet);
+
+ FreePool (Wrap);
+
+ return;
+}
+
+/**
+ The work function to locate IPsec protocol to process the inbound or
+ outbound IP packets. The process routine handls the packet with following
+ actions: bypass the packet, discard the packet, or protect the packet.
+
+ @param[in] IpSb The IP4 service instance.
+ @param[in, out] Head The The caller supplied IP4 header.
+ @param[in, out] Netbuf The IP4 packet to be processed by IPsec.
+ @param[in, out] Options The caller supplied options.
+ @param[in, out] OptionsLen The length of the option.
+ @param[in] Direction The directionality in an SPD entry,
+ EfiIPsecInBound or EfiIPsecOutBound.
+ @param[in] Context The token's wrap.
+
+ @retval EFI_SUCCESS The IPsec protocol is not available or disabled.
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.
+ @retval EFI_SUCCESS The packet was protected.
+ @retval EFI_ACCESS_DENIED The packet was discarded.
+ @retval EFI_OUT_OF_RESOURCES There is no suffcient resource to complete the operation.
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the
+ number of input data blocks when build a fragment table.
+
+**/
+EFI_STATUS
+Ip4IpSecProcessPacket (
+ IN IP4_SERVICE *IpSb,
+ IN OUT IP4_HEAD **Head,
+ IN OUT NET_BUF **Netbuf,
+ IN OUT UINT8 **Options,
+ IN OUT UINT32 *OptionsLen,
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,
+ IN VOID *Context
+ )
+{
+ NET_FRAGMENT *FragmentTable;
+ NET_FRAGMENT *OriginalFragmentTable;
+ UINT32 FragmentCount;
+ UINT32 OriginalFragmentCount;
+ EFI_EVENT RecycleEvent;
+ NET_BUF *Packet;
+ IP4_TXTOKEN_WRAP *TxWrap;
+ IP4_IPSEC_WRAP *IpSecWrap;
+ EFI_STATUS Status;
+ IP4_HEAD ZeroHead;
+
+ Status = EFI_SUCCESS;
+
+ if (!mIpSec2Installed) {
+ goto ON_EXIT;
+ }
+
+ Packet = *Netbuf;
+ RecycleEvent = NULL;
+ IpSecWrap = NULL;
+ FragmentTable = NULL;
+ TxWrap = (IP4_TXTOKEN_WRAP *) Context;
+ FragmentCount = Packet->BlockOpNum;
+
+ ZeroMem (&ZeroHead, sizeof (IP4_HEAD));
+
+ if (mIpSec == NULL) {
+ gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &mIpSec);
+ if (mIpSec == NULL) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Check whether the IPsec enable variable is set.
+ //
+ if (mIpSec->DisabledFlag) {
+ //
+ // If IPsec is disabled, restore the original MTU
+ //
+ IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;
+ goto ON_EXIT;
+ } else {
+ //
+ // If IPsec is enabled, use the MTU which reduce the IPsec header length.
+ //
+ IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP4_MAX_IPSEC_HEADLEN;
+ }
+
+ //
+ // Rebuild fragment table from netbuf to ease IPsec process.
+ //
+ FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));
+
+ if (FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);
+
+ //
+ // Record the original FragmentTable and count.
+ //
+ OriginalFragmentTable = FragmentTable;
+ OriginalFragmentCount = FragmentCount;
+
+ if (EFI_ERROR (Status)) {
+ FreePool (FragmentTable);
+ goto ON_EXIT;
+ }
+
+ //
+ // Convert host byte order to network byte order
+ //
+ Ip4NtohHead (*Head);
+
+ Status = mIpSec->ProcessExt (
+ mIpSec,
+ IpSb->Controller,
+ IP_VERSION_4,
+ (VOID *) (*Head),
+ &(*Head)->Protocol,
+ (VOID **) Options,
+ OptionsLen,
+ (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable),
+ &FragmentCount,
+ Direction,
+ &RecycleEvent
+ );
+ //
+ // Convert back to host byte order
+ //
+ Ip4NtohHead (*Head);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (OriginalFragmentTable);
+ goto ON_EXIT;
+ }
+
+ if (OriginalFragmentTable == FragmentTable && OriginalFragmentCount == FragmentCount) {
+ //
+ // For ByPass Packet
+ //
+ FreePool (FragmentTable);
+ goto ON_EXIT;
+ } else {
+ //
+ // Free the FragmentTable which allocated before calling the IPsec.
+ //
+ FreePool (OriginalFragmentTable);
+ }
+
+ if (Direction == EfiIPsecOutBound && TxWrap != NULL) {
+
+ TxWrap->IpSecRecycleSignal = RecycleEvent;
+ TxWrap->Packet = NetbufFromExt (
+ FragmentTable,
+ FragmentCount,
+ IP4_MAX_HEADLEN,
+ 0,
+ Ip4FreeTxToken,
+ TxWrap
+ );
+ if (TxWrap->Packet == NULL) {
+ //
+ // Recover the TxWrap->Packet, if meet a error, and the caller will free
+ // the TxWrap.
+ //
+ TxWrap->Packet = *Netbuf;
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Free orginal Netbuf.
+ //
+ NetIpSecNetbufFree (*Netbuf);
+ *Netbuf = TxWrap->Packet;
+
+ } else {
+
+ IpSecWrap = AllocateZeroPool (sizeof (IP4_IPSEC_WRAP));
+
+ if (IpSecWrap == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ gBS->SignalEvent (RecycleEvent);
+ goto ON_EXIT;
+ }
+
+ IpSecWrap->IpSecRecycleSignal = RecycleEvent;
+ IpSecWrap->Packet = Packet;
+ Packet = NetbufFromExt (
+ FragmentTable,
+ FragmentCount,
+ IP4_MAX_HEADLEN,
+ 0,
+ Ip4IpSecFree,
+ IpSecWrap
+ );
+
+ if (Packet == NULL) {
+ Packet = IpSecWrap->Packet;
+ gBS->SignalEvent (RecycleEvent);
+ FreePool (IpSecWrap);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ if (Direction == EfiIPsecInBound && 0 != CompareMem (*Head, &ZeroHead, sizeof (IP4_HEAD))) {
+ Ip4PrependHead (Packet, *Head, *Options, *OptionsLen);
+ Ip4NtohHead (Packet->Ip.Ip4);
+ NetbufTrim (Packet, ((*Head)->HeadLen << 2), TRUE);
+
+ CopyMem (
+ IP4_GET_CLIP_INFO (Packet),
+ IP4_GET_CLIP_INFO (IpSecWrap->Packet),
+ sizeof (IP4_CLIP_INFO)
+ );
+ }
+ *Netbuf = Packet;
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Pre-process the IPv4 packet. First validates the IPv4 packet, and
+ then reassembles packet if it is necessary.
+
+ @param[in] IpSb Pointer to IP4_SERVICE.
+ @param[in, out] Packet Pointer to the Packet to be processed.
+ @param[in] Head Pointer to the IP4_HEAD.
+ @param[in] Option Pointer to a buffer which contains the IPv4 option.
+ @param[in] OptionLen The length of Option in bytes.
+ @param[in] Flag The link layer flag for the packet received, such
+ as multicast.
+
+ @retval EFI_SEUCCESS The recieved packet is in well form.
+ @retval EFI_INVAILD_PARAMETER The recieved packet is malformed.
+
+**/
+EFI_STATUS
+Ip4PreProcessPacket (
+ IN IP4_SERVICE *IpSb,
+ IN OUT NET_BUF **Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN UINT32 Flag
+ )
+{
+ IP4_CLIP_INFO *Info;
+ UINT32 HeadLen;
+ UINT32 TotalLen;
+ UINT16 Checksum;
+
+ //
+ // Check if the IP4 header is correctly formatted.
+ //
+ if ((*Packet)->TotalSize < IP4_MIN_HEADLEN) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HeadLen = (Head->HeadLen << 2);
+ TotalLen = NTOHS (Head->TotalLen);
+
+ //
+ // Mnp may deliver frame trailer sequence up, trim it off.
+ //
+ if (TotalLen < (*Packet)->TotalSize) {
+ NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE);
+ }
+
+ if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) ||
+ (TotalLen < HeadLen) || (TotalLen != (*Packet)->TotalSize)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Some OS may send IP packets without checksum.
+ //
+ Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Head, HeadLen));
+
+ if ((Head->Checksum != 0) && (Checksum != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the IP header to host byte order, then get the per packet info.
+ //
+ (*Packet)->Ip.Ip4 = Ip4NtohHead (Head);
+
+ Info = IP4_GET_CLIP_INFO (*Packet);
+ Info->LinkFlag = Flag;
+ Info->CastType = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);
+ Info->Start = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;
+ Info->Length = Head->TotalLen - HeadLen;
+ Info->End = Info->Start + Info->Length;
+ Info->Status = EFI_SUCCESS;
+
+ //
+ // The packet is destinated to us if the CastType is non-zero.
+ //
+ if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Validate the options. Don't call the Ip4OptionIsValid if
+ // there is no option to save some CPU process.
+ //
+
+ if ((OptionLen > 0) && !Ip4OptionIsValid (Option, OptionLen, TRUE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Trim the head off, after this point, the packet is headless,
+ // and Packet->TotalLen == Info->Length.
+ //
+ NetbufTrim (*Packet, HeadLen, TRUE);
+
+ //
+ // Reassemble the packet if this is a fragment. The packet is a
+ // fragment if its head has MF (more fragment) set, or it starts
+ // at non-zero byte.
+ //
+ if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) || (Info->Start != 0)) {
+ //
+ // Drop the fragment if DF is set but it is fragmented. Gateway
+ // need to send a type 4 destination unreache ICMP message here.
+ //
+ if ((Head->Fragment & IP4_HEAD_DF_MASK) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The length of all but the last fragments is in the unit of 8 bytes.
+ //
+ if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) && (Info->Length % 8 != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Packet = Ip4Reassemble (&IpSb->Assemble, *Packet);
+
+ //
+ // Packet assembly isn't complete, start receive more packet.
+ //
+ if (*Packet == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The IP4 input routine. It is called by the IP4_INTERFACE when a
+ IP4 fragment is received from MNP.
+
+ @param[in] Ip4Instance The IP4 child that request the receive, most like
+ it is NULL.
+ @param[in] Packet The IP4 packet received.
+ @param[in] IoStatus The return status of receive request.
+ @param[in] Flag The link layer flag for the packet received, such
+ as multicast.
+ @param[in] Context The IP4 service instance that own the MNP.
+
+**/
+VOID
+Ip4AccpetFrame (
+ IN IP4_PROTOCOL *Ip4Instance,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_HEAD *Head;
+ EFI_STATUS Status;
+ IP4_HEAD ZeroHead;
+ UINT8 *Option;
+ UINT32 OptionLen;
+
+ IpSb = (IP4_SERVICE *) Context;
+ Option = NULL;
+
+ if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTROY)) {
+ goto DROP;
+ }
+
+ Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Head != NULL);
+ OptionLen = (Head->HeadLen << 2) - IP4_MIN_HEADLEN;
+ if (OptionLen > 0) {
+ Option = (UINT8 *) (Head + 1);
+ }
+
+ //
+ // Validate packet format and reassemble packet if it is necessary.
+ //
+ Status = Ip4PreProcessPacket (
+ IpSb,
+ &Packet,
+ Head,
+ Option,
+ OptionLen,
+ Flag
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto RESTART;
+ }
+
+ //
+ // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,
+ // and no need consider any other ahead ext headers.
+ //
+ Status = Ip4IpSecProcessPacket (
+ IpSb,
+ &Head,
+ &Packet,
+ &Option,
+ &OptionLen,
+ EfiIPsecInBound,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto RESTART;
+ }
+
+ //
+ // If the packet is protected by tunnel mode, parse the inner Ip Packet.
+ //
+ ZeroMem (&ZeroHead, sizeof (IP4_HEAD));
+ if (0 == CompareMem (Head, &ZeroHead, sizeof (IP4_HEAD))) {
+ // Packet may have been changed. Head, HeadLen, TotalLen, and
+ // info must be reloaded bofore use. The ownership of the packet
+ // is transfered to the packet process logic.
+ //
+ Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Head != NULL);
+ Status = Ip4PreProcessPacket (
+ IpSb,
+ &Packet,
+ Head,
+ Option,
+ OptionLen,
+ Flag
+ );
+ if (EFI_ERROR (Status)) {
+ goto RESTART;
+ }
+ }
+
+ ASSERT (Packet != NULL);
+ Head = Packet->Ip.Ip4;
+ IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;
+
+ switch (Head->Protocol) {
+ case EFI_IP_PROTO_ICMP:
+ Ip4IcmpHandle (IpSb, Head, Packet);
+ break;
+
+ case IP4_PROTO_IGMP:
+ Ip4IgmpHandle (IpSb, Head, Packet);
+ break;
+
+ default:
+ Ip4Demultiplex (IpSb, Head, Packet, Option, OptionLen);
+ }
+
+ Packet = NULL;
+
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the rx token's events
+ // which are signaled with received data.
+ //
+ DispatchDpc ();
+
+RESTART:
+ Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);
+
+DROP:
+ if (Packet != NULL) {
+ NetbufFree (Packet);
+ }
+
+ return ;
+}
+
+
+/**
+ Check whether this IP child accepts the packet.
+
+ @param[in] IpInstance The IP child to check
+ @param[in] Head The IP header of the packet
+ @param[in] Packet The data of the packet
+
+ @retval TRUE If the child wants to receive the packet.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Ip4InstanceFrameAcceptable (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_ERROR_HEAD Icmp;
+ EFI_IP4_CONFIG_DATA *Config;
+ IP4_CLIP_INFO *Info;
+ UINT16 Proto;
+ UINT32 Index;
+
+ Config = &IpInstance->ConfigData;
+
+ //
+ // Dirty trick for the Tiano UEFI network stack implmentation. If
+ // ReceiveTimeout == -1, the receive of the packet for this instance
+ // is disabled. The UEFI spec don't have such capability. We add
+ // this to improve the performance because IP will make a copy of
+ // the received packet for each accepting instance. Some IP instances
+ // used by UDP/TCP only send packets, they don't wants to receive.
+ //
+ if (Config->ReceiveTimeout == (UINT32)(-1)) {
+ return FALSE;
+ }
+
+ if (Config->AcceptPromiscuous) {
+ return TRUE;
+ }
+
+ //
+ // Use protocol from the IP header embedded in the ICMP error
+ // message to filter, instead of ICMP itself. ICMP handle will
+ // call Ip4Demultiplex to deliver ICMP errors.
+ //
+ Proto = Head->Protocol;
+
+ if ((Proto == EFI_IP_PROTO_ICMP) && (!Config->AcceptAnyProtocol) && (Proto != Config->DefaultProtocol)) {
+ NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head);
+
+ if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
+ if (!Config->AcceptIcmpErrors) {
+ return FALSE;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ Proto = Icmp.IpHead.Protocol;
+ }
+ }
+
+ //
+ // Match the protocol
+ //
+ if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) {
+ return FALSE;
+ }
+
+ //
+ // Check for broadcast, the caller has computed the packet's
+ // cast type for this child's interface.
+ //
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ if (IP4_IS_BROADCAST (Info->CastType)) {
+ return Config->AcceptBroadcast;
+ }
+
+ //
+ // If it is a multicast packet, check whether we are in the group.
+ //
+ if (Info->CastType == IP4_MULTICAST) {
+ //
+ // Receive the multicast if the instance wants to receive all packets.
+ //
+ if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) {
+ return TRUE;
+ }
+
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {
+ if (IpInstance->Groups[Index] == HTONL (Head->Dst)) {
+ break;
+ }
+ }
+
+ return (BOOLEAN)(Index < IpInstance->GroupCount);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Enqueue a shared copy of the packet to the IP4 child if the
+ packet is acceptable to it. Here the data of the packet is
+ shared, but the net buffer isn't.
+
+ @param[in] IpInstance The IP4 child to enqueue the packet to
+ @param[in] Head The IP header of the received packet
+ @param[in] Packet The data of the received packet
+
+ @retval EFI_NOT_STARTED The IP child hasn't been configured.
+ @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource
+ @retval EFI_SUCCESS A shared copy the packet is enqueued to the child.
+
+**/
+EFI_STATUS
+Ip4InstanceEnquePacket (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_CLIP_INFO *Info;
+ NET_BUF *Clone;
+
+ //
+ // Check whether the packet is acceptable to this instance.
+ //
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Enque a shared copy of the packet.
+ //
+ Clone = NetbufClone (Packet);
+
+ if (Clone == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Set the receive time out for the assembled packet. If it expires,
+ // packet will be removed from the queue.
+ //
+ Info = IP4_GET_CLIP_INFO (Clone);
+ Info->Life = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);
+
+ InsertTailList (&IpInstance->Received, &Clone->List);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The signal handle of IP4's recycle event. It is called back
+ when the upper layer release the packet.
+
+ @param Event The IP4's recycle event.
+ @param Context The context of the handle, which is a
+ IP4_RXDATA_WRAP
+
+**/
+VOID
+EFIAPI
+Ip4OnRecyclePacket (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_RXDATA_WRAP *Wrap;
+
+ Wrap = (IP4_RXDATA_WRAP *) Context;
+
+ EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);
+ RemoveEntryList (&Wrap->Link);
+ EfiReleaseLock (&Wrap->IpInstance->RecycleLock);
+
+ ASSERT (!NET_BUF_SHARED (Wrap->Packet));
+ NetbufFree (Wrap->Packet);
+
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);
+ FreePool (Wrap);
+}
+
+
+/**
+ Wrap the received packet to a IP4_RXDATA_WRAP, which will be
+ delivered to the upper layer. Each IP4 child that accepts the
+ packet will get a not-shared copy of the packet which is wrapped
+ in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed
+ to the upper layer. Upper layer will signal the recycle event in
+ it when it is done with the packet.
+
+ @param[in] IpInstance The IP4 child to receive the packet.
+ @param[in] Packet The packet to deliver up.
+
+ @retval Wrap if warp the packet succeed.
+ @retval NULL failed to wrap the packet .
+
+**/
+IP4_RXDATA_WRAP *
+Ip4WrapRxData (
+ IN IP4_PROTOCOL *IpInstance,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_RXDATA_WRAP *Wrap;
+ EFI_IP4_RECEIVE_DATA *RxData;
+ EFI_STATUS Status;
+ BOOLEAN RawData;
+
+ Wrap = AllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum));
+
+ if (Wrap == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Wrap->Link);
+
+ Wrap->IpInstance = IpInstance;
+ Wrap->Packet = Packet;
+ RxData = &Wrap->RxData;
+
+ ZeroMem (RxData, sizeof (EFI_IP4_RECEIVE_DATA));
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4OnRecyclePacket,
+ Wrap,
+ &RxData->RecycleSignal
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Wrap);
+ return NULL;
+ }
+
+ ASSERT (Packet->Ip.Ip4 != NULL);
+
+ ASSERT (IpInstance != NULL);
+ RawData = IpInstance->ConfigData.RawData;
+
+ //
+ // The application expects a network byte order header.
+ //
+ if (!RawData) {
+ RxData->HeaderLength = (Packet->Ip.Ip4->HeadLen << 2);
+ RxData->Header = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip.Ip4);
+ RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;
+ RxData->Options = NULL;
+
+ if (RxData->OptionsLength != 0) {
+ RxData->Options = (VOID *) (RxData->Header + 1);
+ }
+ }
+
+ RxData->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table to be delivered up.
+ //
+ RxData->FragmentCount = Packet->BlockOpNum;
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);
+
+ return Wrap;
+}
+
+
+/**
+ Deliver the received packets to upper layer if there are both received
+ requests and enqueued packets. If the enqueued packet is shared, it will
+ duplicate it to a non-shared packet, release the shared packet, then
+ deliver the non-shared packet up.
+
+ @param[in] IpInstance The IP child to deliver the packet up.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the
+ packets.
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered
+ are delivered up.
+
+**/
+EFI_STATUS
+Ip4InstanceDeliverPacket (
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ IP4_RXDATA_WRAP *Wrap;
+ NET_BUF *Packet;
+ NET_BUF *Dup;
+ UINT8 *Head;
+ UINT32 HeadLen;
+
+ //
+ // Deliver a packet if there are both a packet and a receive token.
+ //
+ while (!IsListEmpty (&IpInstance->Received) &&
+ !NetMapIsEmpty (&IpInstance->RxTokens)) {
+
+ Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);
+
+ if (!NET_BUF_SHARED (Packet)) {
+ //
+ // If this is the only instance that wants the packet, wrap it up.
+ //
+ Wrap = Ip4WrapRxData (IpInstance, Packet);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RemoveEntryList (&Packet->List);
+
+ } else {
+ //
+ // Create a duplicated packet if this packet is shared
+ //
+ if (IpInstance->ConfigData.RawData) {
+ HeadLen = 0;
+ } else {
+ HeadLen = IP4_MAX_HEADLEN;
+ }
+
+ Dup = NetbufDuplicate (Packet, NULL, HeadLen);
+
+ if (Dup == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!IpInstance->ConfigData.RawData) {
+ //
+ // Copy the IP head over. The packet to deliver up is
+ // headless. Trim the head off after copy. The IP head
+ // may be not continuous before the data.
+ //
+ Head = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD);
+ ASSERT (Head != NULL);
+
+ Dup->Ip.Ip4 = (IP4_HEAD *) Head;
+
+ CopyMem (Head, Packet->Ip.Ip4, Packet->Ip.Ip4->HeadLen << 2);
+ NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);
+ }
+
+ Wrap = Ip4WrapRxData (IpInstance, Dup);
+
+ if (Wrap == NULL) {
+ NetbufFree (Dup);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RemoveEntryList (&Packet->List);
+ NetbufFree (Packet);
+
+ Packet = Dup;
+ }
+
+ //
+ // Insert it into the delivered packet, then get a user's
+ // receive token, pass the wrapped packet up.
+ //
+ EfiAcquireLockOrFail (&IpInstance->RecycleLock);
+ InsertHeadList (&IpInstance->Delivered, &Wrap->Link);
+ EfiReleaseLock (&IpInstance->RecycleLock);
+
+ Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL);
+ Token->Status = IP4_GET_CLIP_INFO (Packet)->Status;
+ Token->Packet.RxData = &Wrap->RxData;
+
+ gBS->SignalEvent (Token->Event);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Enqueue a received packet to all the IP children that share
+ the same interface.
+
+ @param[in] IpSb The IP4 service instance that receive the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+ @param[in] Option Point to the IP4 packet header options.
+ @param[in] OptionLen Length of the IP4 packet header options.
+ @param[in] IpIf The interface to enqueue the packet to.
+
+ @return The number of the IP4 children that accepts the packet
+
+**/
+INTN
+Ip4InterfaceEnquePacket (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ IP4_CLIP_INFO *Info;
+ LIST_ENTRY *Entry;
+ INTN Enqueued;
+ INTN LocalType;
+ INTN SavedType;
+
+ //
+ // First, check that the packet is acceptable to this interface
+ // and find the local cast type for the interface. A packet sent
+ // to say 192.168.1.1 should NOT be delliever to 10.0.0.1 unless
+ // promiscuous receiving.
+ //
+ LocalType = 0;
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) {
+ //
+ // If the CastType is multicast, don't need to filter against
+ // the group address here, Ip4InstanceFrameAcceptable will do
+ // that later.
+ //
+ LocalType = Info->CastType;
+
+ } else {
+ //
+ // Check the destination againist local IP. If the station
+ // address is 0.0.0.0, it means receiving all the IP destined
+ // to local non-zero IP. Otherwise, it is necessary to compare
+ // the destination to the interface's IP address.
+ //
+ if (IpIf->Ip == IP4_ALLZERO_ADDRESS) {
+ LocalType = IP4_LOCAL_HOST;
+
+ } else {
+ LocalType = Ip4GetNetCast (Head->Dst, IpIf);
+
+ if ((LocalType == 0) && IpIf->PromiscRecv) {
+ LocalType = IP4_PROMISCUOUS;
+ }
+ }
+ }
+
+ if (LocalType == 0) {
+ return 0;
+ }
+
+ //
+ // Iterate through the ip instances on the interface, enqueue
+ // the packet if filter passed. Save the original cast type,
+ // and pass the local cast type to the IP children on the
+ // interface. The global cast type will be restored later.
+ //
+ SavedType = Info->CastType;
+ Info->CastType = LocalType;
+
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+ NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE);
+
+ //
+ // In RawData mode, add IPv4 headers and options back to packet.
+ //
+ if ((IpInstance->ConfigData.RawData) && (Option != NULL) && (OptionLen != 0)){
+ Ip4PrependHead (Packet, Head, Option, OptionLen);
+ }
+
+ if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {
+ Enqueued++;
+ }
+ }
+
+ Info->CastType = SavedType;
+ return Enqueued;
+}
+
+
+/**
+ Deliver the packet for each IP4 child on the interface.
+
+ @param[in] IpSb The IP4 service instance that received the packet
+ @param[in] IpIf The IP4 interface to deliver the packet.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS now
+
+**/
+EFI_STATUS
+Ip4InterfaceDeliverPacket (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ IP4_PROTOCOL *Ip4Instance;
+ LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+ Ip4InstanceDeliverPacket (Ip4Instance);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Demultiple the packet. the packet delivery is processed in two
+ passes. The first pass will enque a shared copy of the packet
+ to each IP4 child that accepts the packet. The second pass will
+ deliver a non-shared copy of the packet to each IP4 child that
+ has pending receive requests. Data is copied if more than one
+ child wants to consume the packet because each IP child needs
+ its own copy of the packet to make changes.
+
+ @param[in] IpSb The IP4 service instance that received the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+ @param[in] Option Point to the IP4 packet header options.
+ @param[in] OptionLen Length of the IP4 packet header options.
+
+ @retval EFI_NOT_FOUND No IP child accepts the packet.
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP
+ children.
+
+**/
+EFI_STATUS
+Ip4Demultiplex (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN UINT8 *Option,
+ IN UINT32 OptionLen
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+ INTN Enqueued;
+
+ //
+ // Two pass delivery: first, enque a shared copy of the packet
+ // to each instance that accept the packet.
+ //
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured) {
+ Enqueued += Ip4InterfaceEnquePacket (
+ IpSb,
+ Head,
+ Packet,
+ Option,
+ OptionLen,
+ IpIf
+ );
+ }
+ }
+
+ //
+ // Second: deliver a duplicate of the packet to each instance.
+ // Release the local reference first, so that the last instance
+ // getting the packet will not copy the data.
+ //
+ NetbufFree (Packet);
+
+ if (Enqueued == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured) {
+ Ip4InterfaceDeliverPacket (IpSb, IpIf);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Timeout the fragment and enqueued packets.
+
+ @param[in] IpSb The IP4 service instance to timeout
+
+**/
+VOID
+Ip4PacketTimerTicking (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ LIST_ENTRY *InstanceEntry;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_PROTOCOL *IpInstance;
+ IP4_ASSEMBLE_ENTRY *Assemble;
+ NET_BUF *Packet;
+ IP4_CLIP_INFO *Info;
+ UINT32 Index;
+
+ //
+ // First, time out the fragments. The packet's life is counting down
+ // once the first-arrived fragment was received.
+ //
+ for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);
+
+ if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {
+ RemoveEntryList (Entry);
+ Ip4FreeAssembleEntry (Assemble);
+ }
+ }
+ }
+
+ NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_PROTOCOL, Link);
+
+ //
+ // Second, time out the assembled packets enqueued on each IP child.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {
+ Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ if ((Info->Life > 0) && (--Info->Life == 0)) {
+ RemoveEntryList (Entry);
+ NetbufFree (Packet);
+ }
+ }
+
+ //
+ // Third: time out the transmitted packets.
+ //
+ NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL);
+ }
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h
new file mode 100644
index 0000000000..1c8e8b2a15
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h
@@ -0,0 +1,252 @@
+/** @file
+
+Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_INPUT_H__
+#define __EFI_IP4_INPUT_H__
+
+#define IP4_MIN_HEADLEN 20
+#define IP4_MAX_HEADLEN 60
+///
+/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54
+///
+#define IP4_MAX_IPSEC_HEADLEN 54
+
+#define IP4_ASSEMLE_HASH_SIZE 31
+#define IP4_FRAGMENT_LIFE 120
+#define IP4_MAX_PACKET_SIZE 65535
+
+///
+/// Per packet information for input process. LinkFlag specifies whether
+/// the packet is received as Link layer unicast, multicast or broadcast.
+/// The CastType is the IP layer cast type, such as IP multicast or unicast.
+/// Start, End and Length are staffs used to assemble the packets. Start
+/// is the sequence number of the first byte of data in the packet. Length
+/// is the number of bytes of data. End = Start + Length, that is, the
+/// sequence number of last byte + 1. Each assembled packet has a count down
+/// life. If it isn't consumed before Life reaches zero, the packet is released.
+///
+typedef struct {
+ UINTN LinkFlag;
+ INTN CastType;
+ INTN Start;
+ INTN End;
+ INTN Length;
+ UINT32 Life;
+ EFI_STATUS Status;
+} IP4_CLIP_INFO;
+
+///
+/// Structure used to assemble IP packets.
+///
+typedef struct {
+ LIST_ENTRY Link;
+
+ //
+ // Identity of one IP4 packet. Each fragment of a packet has
+ // the same (Dst, Src, Id, Protocol).
+ //
+ IP4_ADDR Dst;
+ IP4_ADDR Src;
+ UINT16 Id;
+ UINT8 Protocol;
+
+ INTN TotalLen;
+ INTN CurLen;
+ LIST_ENTRY Fragments; // List of all the fragments of this packet
+
+ IP4_HEAD *Head; // IP head of the first fragment
+ IP4_CLIP_INFO *Info; // Per packet info of the first fragment
+ INTN Life; // Count down life for the packet.
+} IP4_ASSEMBLE_ENTRY;
+
+///
+/// Each Ip service instance has an assemble table to reassemble
+/// the packets before delivery to its children. It is organized
+/// as hash table.
+///
+typedef struct {
+ LIST_ENTRY Bucket[IP4_ASSEMLE_HASH_SIZE];
+} IP4_ASSEMBLE_TABLE;
+
+#define IP4_GET_CLIP_INFO(Packet) ((IP4_CLIP_INFO *) ((Packet)->ProtoData))
+
+#define IP4_ASSEMBLE_HASH(Dst, Src, Id, Proto) \
+ (((Dst) + (Src) + ((Id) << 16) + (Proto)) % IP4_ASSEMLE_HASH_SIZE)
+
+#define IP4_RXDATA_WRAP_SIZE(NumFrag) \
+ (sizeof (IP4_RXDATA_WRAP) + sizeof (EFI_IP4_FRAGMENT_DATA) * ((NumFrag) - 1))
+
+/**
+ Initialize an already allocated assemble table. This is generally
+ the assemble table embedded in the IP4 service instance.
+
+ @param[in, out] Table The assemble table to initialize.
+
+**/
+VOID
+Ip4InitAssembleTable (
+ IN OUT IP4_ASSEMBLE_TABLE *Table
+ );
+
+/**
+ Clean up the assemble table: remove all the fragments
+ and assemble entries.
+
+ @param[in] Table The assemble table to clean up
+
+**/
+VOID
+Ip4CleanAssembleTable (
+ IN IP4_ASSEMBLE_TABLE *Table
+ );
+
+/**
+ The IP4 input routine. It is called by the IP4_INTERFACE when a
+ IP4 fragment is received from MNP.
+
+ @param[in] Ip4Instance The IP4 child that request the receive, most like
+ it is NULL.
+ @param[in] Packet The IP4 packet received.
+ @param[in] IoStatus The return status of receive request.
+ @param[in] Flag The link layer flag for the packet received, such
+ as multicast.
+ @param[in] Context The IP4 service instance that own the MNP.
+
+**/
+VOID
+Ip4AccpetFrame (
+ IN IP4_PROTOCOL *Ip4Instance,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ );
+
+/**
+ Demultiple the packet. the packet delivery is processed in two
+ passes. The first pass will enque a shared copy of the packet
+ to each IP4 child that accepts the packet. The second pass will
+ deliver a non-shared copy of the packet to each IP4 child that
+ has pending receive requests. Data is copied if more than one
+ child wants to consume the packet because each IP child needs
+ its own copy of the packet to make changes.
+
+ @param[in] IpSb The IP4 service instance that received the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+ @param[in] Option Point to the IP4 packet header options.
+ @param[in] OptionLen Length of the IP4 packet header options.
+
+ @retval EFI_NOT_FOUND No IP child accepts the packet.
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP
+ children.
+
+**/
+EFI_STATUS
+Ip4Demultiplex (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN UINT8 *Option,
+ IN UINT32 OptionLen
+ );
+
+/**
+ Enqueue a received packet to all the IP children that share
+ the same interface.
+
+ @param[in] IpSb The IP4 service instance that receive the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+ @param[in] Option Point to the IP4 packet header options.
+ @param[in] OptionLen Length of the IP4 packet header options.
+ @param[in] IpIf The interface to enqueue the packet to.
+
+ @return The number of the IP4 children that accepts the packet
+
+**/
+INTN
+Ip4InterfaceEnquePacket (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN IP4_INTERFACE *IpIf
+ );
+
+/**
+ Deliver the received packets to upper layer if there are both received
+ requests and enqueued packets. If the enqueued packet is shared, it will
+ duplicate it to a non-shared packet, release the shared packet, then
+ deliver the non-shared packet up.
+
+ @param[in] IpInstance The IP child to deliver the packet up.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the
+ packets.
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered
+ are delivered up.
+
+**/
+EFI_STATUS
+Ip4InstanceDeliverPacket (
+ IN IP4_PROTOCOL *IpInstance
+ );
+
+/**
+ Timeout the fragment and enqueued packets.
+
+ @param[in] IpSb The IP4 service instance to timeout
+
+**/
+VOID
+Ip4PacketTimerTicking (
+ IN IP4_SERVICE *IpSb
+ );
+
+/**
+ The work function to locate IPsec protocol to process the inbound or
+ outbound IP packets. The process routine handls the packet with following
+ actions: bypass the packet, discard the packet, or protect the packet.
+
+ @param[in] IpSb The IP4 service instance.
+ @param[in, out] Head The The caller supplied IP4 header.
+ @param[in, out] Netbuf The IP4 packet to be processed by IPsec.
+ @param[in, out] Options The caller supplied options.
+ @param[in, out] OptionsLen The length of the option.
+ @param[in] Direction The directionality in an SPD entry,
+ EfiIPsecInBound or EfiIPsecOutBound.
+ @param[in] Context The token's wrap.
+
+ @retval EFI_SUCCESS The IPsec protocol is not available or disabled.
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.
+ @retval EFI_SUCCESS The packet was protected.
+ @retval EFI_ACCESS_DENIED The packet was discarded.
+ @retval EFI_OUT_OF_RESOURCES There is no suffcient resource to complete the operation.
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the
+ number of input data blocks when build a fragment table.
+
+**/
+EFI_STATUS
+Ip4IpSecProcessPacket (
+ IN IP4_SERVICE *IpSb,
+ IN OUT IP4_HEAD **Head,
+ IN OUT NET_BUF **Netbuf,
+ IN OUT UINT8 **Options,
+ IN OUT UINT32 *OptionsLen,
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4NvData.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4NvData.h
new file mode 100644
index 0000000000..a02b331980
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4NvData.h
@@ -0,0 +1,51 @@
+/** @file
+ Routines used to operate the Ip4Dxe.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP4_NV_DATA_H_
+#define _IP4_NV_DATA_H_
+
+#include <Guid/Ip4Config2Hii.h>
+
+#define FORMID_MAIN_FORM 1
+#define FORMID_DEVICE_FORM 2
+
+#define KEY_ENABLE 0x100
+#define KEY_DHCP_ENABLE 0x101
+#define KEY_LOCAL_IP 0x102
+#define KEY_SUBNET_MASK 0x103
+#define KEY_GATE_WAY 0x104
+#define KEY_DNS 0x105
+#define KEY_SAVE_CHANGES 0x106
+
+#define IP_MIN_SIZE 7
+#define IP_MAX_SIZE 15
+#define IP4_STR_MAX_SIZE 16
+#define ADDRESS_STR_MAX_SIZE 255
+#define MAX_IP4_CONFIG_DNS 16
+
+///
+/// IP4_CONFIG2_IFR_NVDATA contains the IP4 configure
+/// parameters for that NIC.
+///
+typedef struct {
+ UINT8 Configure; ///< NIC configure status
+ UINT8 DhcpEnable; ///< Static or DHCP
+ CHAR16 StationAddress[IP4_STR_MAX_SIZE]; ///< IP addresses
+ CHAR16 SubnetMask[IP4_STR_MAX_SIZE]; ///< Subnet address
+ CHAR16 GatewayAddress[IP4_STR_MAX_SIZE]; ///< Gateway address
+ CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address
+} IP4_CONFIG2_IFR_NVDATA;
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c
new file mode 100644
index 0000000000..6a92573275
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c
@@ -0,0 +1,210 @@
+/** @file
+ IP4 option support functions.
+
+Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Validate the IP4 option format for both the packets we received
+ and will transmit.
+
+ @param[in] Option The first byte of the option
+ @param[in] OptionLen The length of the whole option
+ @param[in] Rcvd The option is from the packet we received if TRUE,
+ otherwise the option we wants to transmit.
+
+ @retval TRUE The option is properly formatted
+ @retval FALSE The option is mal-formated
+
+**/
+BOOLEAN
+Ip4OptionIsValid (
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN BOOLEAN Rcvd
+ )
+{
+ UINT32 Cur;
+ UINT32 Len;
+ UINT32 Point;
+
+ Cur = 0;
+
+ while (Cur < OptionLen) {
+ switch (Option[Cur]) {
+ case IP4_OPTION_NOP:
+ Cur++;
+ break;
+
+ case IP4_OPTION_EOP:
+ Cur = OptionLen;
+ break;
+
+ case IP4_OPTION_LSRR:
+ case IP4_OPTION_SSRR:
+ case IP4_OPTION_RR:
+ Len = Option[Cur + 1];
+ Point = Option[Cur + 2];
+
+ //
+ // SRR/RR options are formatted as |Type|Len|Point|Ip1|Ip2|...
+ //
+ if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) {
+ return FALSE;
+ }
+
+ if ((Point > Len + 1) || (Point % 4 != 0)) {
+ return FALSE;
+ }
+
+ //
+ // The Point must point pass the last entry if the packet is received
+ // by us. It must point to 4 if the packet is to be sent by us for
+ // source route option.
+ //
+ if ((Option[Cur] != IP4_OPTION_RR) &&
+ ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) {
+
+ return FALSE;
+ }
+
+ Cur += Len;
+ break;
+
+ default:
+ Len = Option[Cur + 1];
+
+ if ((OptionLen - Cur < Len) || (Len < 2)) {
+ return FALSE;
+ }
+
+ Cur = Cur + Len;
+ break;
+ }
+
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Copy the option from the original option to buffer. It
+ handles the details such as:
+ 1. whether copy the single IP4 option to the first/non-first
+ fragments.
+ 2. Pad the options copied over to aligned to 4 bytes.
+
+ @param[in] Option The original option to copy from
+ @param[in] OptionLen The length of the original option
+ @param[in] FirstFragment Whether it is the first fragment
+ @param[in, out] Buf The buffer to copy options to. NULL
+ @param[in, out] BufLen The length of the buffer
+
+ @retval EFI_SUCCESS The options are copied over
+ @retval EFI_BUFFER_TOO_SMALL Buf is NULL or BufLen provided is too small.
+
+**/
+EFI_STATUS
+Ip4CopyOption (
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN BOOLEAN FirstFragment,
+ IN OUT UINT8 *Buf, OPTIONAL
+ IN OUT UINT32 *BufLen
+ )
+{
+ UINT8 OptBuf[40];
+ UINT32 Cur;
+ UINT32 Next;
+ UINT8 Type;
+ UINT32 Len;
+
+ ASSERT ((BufLen != NULL) && (OptionLen <= 40));
+
+ Cur = 0;
+ Next = 0;
+
+ while (Cur < OptionLen) {
+ Type = Option[Cur];
+ Len = Option[Cur + 1];
+
+ if (Type == IP4_OPTION_NOP) {
+ //
+ // Keep the padding, in case that the sender wants to align
+ // the option, say, to 4 bytes
+ //
+ OptBuf[Next] = IP4_OPTION_NOP;
+ Next++;
+ Cur++;
+
+ } else if (Type == IP4_OPTION_EOP) {
+ //
+ // Don't append the EOP to avoid including only a EOP option
+ //
+ break;
+
+ } else {
+ //
+ // don't copy options that is only valid for the first fragment
+ //
+ if (FirstFragment || (Type & IP4_OPTION_COPY_MASK) != 0) {
+ CopyMem (OptBuf + Next, Option + Cur, Len);
+ Next += Len;
+ }
+
+ Cur += Len;
+ }
+ }
+
+ //
+ // Don't append an EOP only option.
+ //
+ if (Next == 0) {
+ *BufLen = 0;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Append an EOP if the end of option doesn't coincide with the
+ // end of the IP header, that is, isn't aligned to 4 bytes..
+ //
+ if ((Next % 4) != 0) {
+ OptBuf[Next] = IP4_OPTION_EOP;
+ Next++;
+ }
+
+ //
+ // Head length is in the unit of 4 bytes. Now, Len is the
+ // acutal option length to appear in the IP header.
+ //
+ Len = ((Next + 3) &~0x03);
+
+ //
+ // If the buffer is too small, set the BufLen then return
+ //
+ if ((Buf == NULL) || (*BufLen < Len)) {
+ *BufLen = Len;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy the option to the Buf, zero the buffer first to pad
+ // the options with NOP to align to 4 bytes.
+ //
+ ZeroMem (Buf, Len);
+ CopyMem (Buf, OptBuf, Next);
+ *BufLen = Len;
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h
new file mode 100644
index 0000000000..f9c65d026d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h
@@ -0,0 +1,72 @@
+/** @file
+ IP4 option support routines.
+
+Copyright (c) 2005 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_OPTION_H__
+#define __EFI_IP4_OPTION_H__
+
+#define IP4_OPTION_EOP 0
+#define IP4_OPTION_NOP 1
+#define IP4_OPTION_LSRR 131 // Loss source and record routing, 10000011
+#define IP4_OPTION_SSRR 137 // Strict source and record routing, 10001001
+#define IP4_OPTION_RR 7 // Record routing, 00000111
+
+#define IP4_OPTION_COPY_MASK 0x80
+
+/**
+ Validate the IP4 option format for both the packets we received
+ and will transmit. It will compute the ICMP error message fields
+ if the option is mal-formated. But this information isn't used.
+
+ @param[in] Option The first byte of the option
+ @param[in] OptionLen The length of the whole option
+ @param[in] Rcvd The option is from the packet we received if TRUE,
+ otherwise the option we wants to transmit.
+
+ @retval TRUE The option is properly formatted
+ @retval FALSE The option is mal-formated
+
+**/
+BOOLEAN
+Ip4OptionIsValid (
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN BOOLEAN Rcvd
+ );
+
+/**
+ Copy the option from the original option to buffer. It
+ handles the details such as:
+ 1. whether copy the single IP4 option to the first/non-first
+ fragments.
+ 2. Pad the options copied over to aligned to 4 bytes.
+
+ @param[in] Option The original option to copy from
+ @param[in] OptionLen The length of the original option
+ @param[in] FirstFragment Whether it is the first fragment
+ @param[in, out] Buf The buffer to copy options to. NULL
+ @param[in, out] BufLen The length of the buffer
+
+ @retval EFI_SUCCESS The options are copied over
+ @retval EFI_BUFFER_TOO_SMALL Buf is NULL or BufLen provided is too small.
+
+**/
+EFI_STATUS
+Ip4CopyOption (
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN BOOLEAN FirstFragment,
+ IN OUT UINT8 *Buf, OPTIONAL
+ IN OUT UINT32 *BufLen
+ );
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c
new file mode 100644
index 0000000000..68d1f0943f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c
@@ -0,0 +1,487 @@
+/** @file
+ Transmit the IP4 packet.
+
+Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+UINT16 mIp4Id;
+
+
+/**
+ Prepend an IP4 head to the Packet. It will copy the options and
+ build the IP4 header fields. Used for IP4 fragmentation.
+
+ @param Packet The packet to prepend IP4 header to
+ @param Head The caller supplied header. The caller should set
+ the following header fields: Tos, TotalLen, Id,
+ Fragment, Ttl, Protocol, Src and Dst. All the fields
+ are in host byte order. This function will fill in
+ the Ver, HeadLen, and checksum.
+ @param Option The orginal IP4 option to copy from
+ @param OptLen The length of the IP4 option
+
+ @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of
+ Packet.
+ @retval EFI_SUCCESS The IP4 header is successfully added to the packet.
+
+**/
+EFI_STATUS
+Ip4PrependHead (
+ IN OUT NET_BUF *Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen
+ )
+{
+ UINT32 HeadLen;
+ UINT32 Len;
+ IP4_HEAD *PacketHead;
+ BOOLEAN FirstFragment;
+
+ //
+ // Prepend the options: first get the option length, then copy it over.
+ //
+ HeadLen = 0;
+ FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment);
+
+ Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len);
+
+ HeadLen = IP4_MIN_HEADLEN + Len;
+ ASSERT (((Len % 4) == 0) && (HeadLen <= IP4_MAX_HEADLEN));
+
+ PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
+
+ if (PacketHead == NULL) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len);
+
+ //
+ // Set the head up, convert the host byte order to network byte order
+ //
+ PacketHead->Ver = 4;
+ PacketHead->HeadLen = (UINT8) (HeadLen >> 2);
+ PacketHead->Tos = Head->Tos;
+ PacketHead->TotalLen = HTONS ((UINT16) Packet->TotalSize);
+ PacketHead->Id = HTONS (Head->Id);
+ PacketHead->Fragment = HTONS (Head->Fragment);
+ PacketHead->Checksum = 0;
+ PacketHead->Ttl = Head->Ttl;
+ PacketHead->Protocol = Head->Protocol;
+ PacketHead->Src = HTONL (Head->Src);
+ PacketHead->Dst = HTONL (Head->Dst);
+ PacketHead->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen));
+
+ Packet->Ip.Ip4 = PacketHead;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Select an interface to send the packet generated in the IP4 driver
+ itself, that is, not by the requests of IP4 child's consumer. Such
+ packets include the ICMP echo replies, and other ICMP error packets.
+
+ @param[in] IpSb The IP4 service that wants to send the packets.
+ @param[in] Dst The destination of the packet
+ @param[in] Src The source of the packet
+
+ @return NULL if no proper interface is found, otherwise the interface that
+ can be used to send the system packet from.
+
+**/
+IP4_INTERFACE *
+Ip4SelectInterface (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src
+ )
+{
+ IP4_INTERFACE *IpIf;
+ IP4_INTERFACE *Selected;
+ LIST_ENTRY *Entry;
+
+ //
+ // Select the interface the Dst is on if one of the connected
+ // network. Some IP instance may be configured with 0.0.0.0/0,
+ // don't select that interface now.
+ //
+ IpIf = Ip4FindNet (IpSb, Dst);
+
+ if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
+ return IpIf;
+ }
+
+ //
+ // If source is one of the interface address, select it.
+ //
+ IpIf = Ip4FindInterface (IpSb, Src);
+
+ if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
+ return IpIf;
+ }
+
+ //
+ // Select a configured interface as the fall back. Always prefer
+ // an interface with non-zero address.
+ //
+ Selected = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) {
+ Selected = IpIf;
+ }
+ }
+
+ return Selected;
+}
+
+
+/**
+ The default callback function for system generated packet.
+ It will free the packet.
+
+ @param Ip4Instance The IP4 child that issued the transmission. It most
+ like is NULL.
+ @param Packet The packet that transmitted.
+ @param IoStatus The result of the transmission, succeeded or failed.
+ @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK
+ for reference.
+ @param Context The context provided by us
+
+**/
+VOID
+Ip4SysPacketSent (
+ IP4_PROTOCOL *Ip4Instance,
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+/**
+ Transmit an IP4 packet. The packet comes either from the IP4
+ child's consumer (IpInstance != NULL) or the IP4 driver itself
+ (IpInstance == NULL). It will route the packet, fragment it,
+ then transmit all the fragments through some interface.
+
+ @param[in] IpSb The IP4 service instance to transmit the packet
+ @param[in] IpInstance The IP4 child that issues the transmission. It is
+ NULL if the packet is from the system.
+ @param[in] Packet The user data to send, excluding the IP header.
+ @param[in] Head The caller supplied header. The caller should set
+ the following header fields: Tos, TotalLen, Id, tl,
+ Fragment, Protocol, Src and Dst. All the fields are
+ in host byte order. This function will fill in the
+ Ver, HeadLen, Fragment, and checksum. The Fragment
+ only need to include the DF flag. Ip4Output will
+ compute the MF and offset for you.
+ @param[in] Option The original option to append to the IP headers
+ @param[in] OptLen The length of the option
+ @param[in] GateWay The next hop address to transmit packet to.
+ 255.255.255.255 means broadcast.
+ @param[in] Callback The callback function to issue when transmission
+ completed.
+ @param[in] Context The opaque context for the callback
+
+ @retval EFI_NO_MAPPING There is no interface to the destination.
+ @retval EFI_NOT_FOUND There is no route to the destination
+ @retval EFI_SUCCESS The packet is successfully transmitted.
+ @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length +
+ total data length is greater than MTU (or greater
+ than the maximum packet size if Token.Packet.TxData.
+ OverrideData.DoNotFragment is TRUE.)
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Ip4Output (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen,
+ IN IP4_ADDR GateWay,
+ IN IP4_FRAME_CALLBACK Callback,
+ IN VOID *Context
+ )
+{
+ IP4_INTERFACE *IpIf;
+ IP4_ROUTE_CACHE_ENTRY *CacheEntry;
+ IP4_ADDR Dest;
+ EFI_STATUS Status;
+ NET_BUF *Fragment;
+ UINT32 Index;
+ UINT32 HeadLen;
+ UINT32 PacketLen;
+ UINT32 Offset;
+ UINT32 Mtu;
+ UINT32 Num;
+ BOOLEAN RawData;
+
+ //
+ // Select an interface/source for system packet, application
+ // should select them itself.
+ //
+ if (IpInstance == NULL) {
+ IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src);
+ } else {
+ IpIf = IpInstance->Interface;
+ }
+
+ if (IpIf == NULL) {
+ return EFI_NO_MAPPING;
+ }
+
+ if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) {
+ Head->Src = IpIf->Ip;
+ }
+
+ //
+ // Before IPsec process, prepared the IP head.
+ // If Ip4Output is transmitting RawData, don't update IPv4 header.
+ //
+ HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));
+
+ if ((IpInstance != NULL) && IpInstance->ConfigData.RawData) {
+ RawData = TRUE;
+ } else {
+ Head->HeadLen = (UINT8) (HeadLen >> 2);
+ Head->Id = mIp4Id++;
+ Head->Ver = 4;
+ RawData = FALSE;
+ }
+
+ //
+ // Call IPsec process.
+ //
+ Status = Ip4IpSecProcessPacket (
+ IpSb,
+ &Head,
+ &Packet,
+ &Option,
+ &OptLen,
+ EfiIPsecOutBound,
+ Context
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Dest = Head->Dst;
+ if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {
+ //
+ // Set the gateway to local broadcast if the Dest is
+ // the broadcast address for the connected network or
+ // it is local broadcast.
+ //
+ GateWay = IP4_ALLONE_ADDRESS;
+
+ } else if (IP4_IS_MULTICAST (Dest)) {
+ //
+ // Set the gateway to the destination if it is an multicast
+ // address. The IP4_INTERFACE won't consult ARP to send local
+ // broadcast and multicast.
+ //
+ GateWay = Head->Dst;
+
+ } else if (GateWay == IP4_ALLZERO_ADDRESS) {
+ //
+ // Route the packet unless overrided, that is, GateWay isn't zero.
+ //
+ if (IpInstance == NULL) {
+ CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
+ } else {
+ CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);
+ //
+ // If failed to route the packet by using the instance's route table,
+ // try to use the default route table.
+ //
+ if (CacheEntry == NULL) {
+ CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
+ }
+ }
+
+ if (CacheEntry == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ GateWay = CacheEntry->NextHop;
+ Ip4FreeRouteCacheEntry (CacheEntry);
+ }
+
+ //
+ // OK, selected the source and route, fragment the packet then send
+ // them. Tag each fragment other than the first one as spawn from it.
+ //
+ Mtu = IpSb->MaxPacketSize + sizeof (IP4_HEAD);
+
+ if (Packet->TotalSize + HeadLen > Mtu) {
+ //
+ // Fragmentation is diabled for RawData mode.
+ //
+ if (RawData) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Packet is fragmented from the tail to the head, that is, the
+ // first frame sent is the last fragment of the packet. The first
+ // fragment is NOT sent in this loop. First compute how many
+ // fragments there are.
+ //
+ Mtu = (Mtu - HeadLen) & (~0x07);
+ Num = (Packet->TotalSize + Mtu - 1) / Mtu;
+
+ //
+ // Initialize the packet length and Offset. Other than the last
+ // fragment, the packet length equals to MTU. The offset is always
+ // aligned to MTU.
+ //
+ PacketLen = Packet->TotalSize - (Num - 1) * Mtu;
+ Offset = Mtu * (Num - 1);
+
+ for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) {
+ Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN);
+
+ if (Fragment == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Update the header's fragment. The caller fills the IP4 header
+ // fields that are required by Ip4PrependHead except the fragment.
+ //
+ Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset);
+ Ip4PrependHead (Fragment, Head, Option, OptLen);
+
+ //
+ // Transmit the fragments, pass the Packet address as the context.
+ // So, we can find all the fragments spawned from the Packet by
+ // compare the NetBuf and Context to the Packet.
+ //
+ Status = Ip4SendFrame (
+ IpIf,
+ IpInstance,
+ Fragment,
+ GateWay,
+ Ip4SysPacketSent,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ PacketLen = Mtu;
+ }
+
+ //
+ // Trim the already sent data, then adjust the head's fragment field.
+ //
+ NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE);
+ Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0);
+ }
+
+ //
+ // Send the first fragment, it is either the orginal packet, or the
+ // first fragment of a fragmented packet. It seems that there is a subtle
+ // bug here: what if the caller free the packet in Callback and IpIf (or
+ // MNP child used by that interface) still holds the fragments and try
+ // to access the data? The caller can free the packet if it recycles the
+ // consumer's (such as UDP) data in the Callback. But this can't happen.
+ // The detailed sequence is:
+ // 1. for the packets generated by IP4 driver itself:
+ // The Callback is Ip4SysPacketSent, which is the same as the
+ // fragments' callback. Ip4SysPacketSent simply calls NetbufFree
+ // to release its reference to the packet. So, no problem for
+ // system packets.
+ //
+ // 2. for the upper layer's packets (use UDP as an example):
+ // UDP requests the IP layer to transmit some data which is
+ // wrapped in an asynchronous token, the token is wrapped
+ // in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data
+ // in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP
+ // is bound with the Packet. It will only be freed when all
+ // the references to Packet have been released. Upon then, the
+ // Packet's OnFree callback will release the IP4_TXTOKEN_WRAP,
+ // and singal the user's recycle event. So, also no problem for
+ // upper layer's packets.
+ //
+ Ip4PrependHead (Packet, Head, Option, OptLen);
+ Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4CancelPacket (IpIf, Packet, Status);
+ return Status;
+}
+
+
+/**
+ The filter function to find a packet and all its fragments.
+ The packet's fragments have their Context set to the packet.
+
+ @param[in] Frame The frames hold by the low level interface
+ @param[in] Context Context to the function, which is the packet.
+
+ @retval TRUE This is the packet to cancel or its fragments.
+ @retval FALSE This is unrelated packet.
+
+**/
+BOOLEAN
+Ip4CancelPacketFragments (
+ IN IP4_LINK_TX_TOKEN *Frame,
+ IN VOID *Context
+ )
+{
+ if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Cancel the Packet and all its fragments.
+
+ @param IpIf The interface from which the Packet is sent
+ @param Packet The Packet to cancel
+ @param IoStatus The status returns to the sender.
+
+**/
+VOID
+Ip4CancelPacket (
+ IN IP4_INTERFACE *IpIf,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus
+ )
+{
+ Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h
new file mode 100644
index 0000000000..e833352c1b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h
@@ -0,0 +1,126 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_OUTPUT_H__
+#define __EFI_IP4_OUTPUT_H__
+
+/**
+ The default callback function for system generated packet.
+ It will free the packet.
+
+ @param Ip4Instance The IP4 child that issued the transmission. It most
+ like is NULL.
+ @param Packet The packet that transmitted.
+ @param IoStatus The result of the transmission, succeeded or failed.
+ @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK
+ for reference.
+ @param Context The context provided by us
+
+**/
+VOID
+Ip4SysPacketSent (
+ IP4_PROTOCOL *Ip4Instance,
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ );
+
+/**
+ Transmit an IP4 packet. The packet comes either from the IP4
+ child's consumer (IpInstance != NULL) or the IP4 driver itself
+ (IpInstance == NULL). It will route the packet, fragment it,
+ then transmit all the fragments through some interface.
+
+ @param[in] IpSb The IP4 service instance to transmit the packet
+ @param[in] IpInstance The IP4 child that issues the transmission. It is
+ NULL if the packet is from the system.
+ @param[in] Packet The user data to send, excluding the IP header.
+ @param[in] Head The caller supplied header. The caller should set
+ the following header fields: Tos, TotalLen, Id, tl,
+ Fragment, Protocol, Src and Dst. All the fields are
+ in host byte order. This function will fill in the
+ Ver, HeadLen, Fragment, and checksum. The Fragment
+ only need to include the DF flag. Ip4Output will
+ compute the MF and offset for you.
+ @param[in] Option The original option to append to the IP headers
+ @param[in] OptLen The length of the option
+ @param[in] GateWay The next hop address to transmit packet to.
+ 255.255.255.255 means broadcast.
+ @param[in] Callback The callback function to issue when transmission
+ completed.
+ @param[in] Context The opaque context for the callback
+
+ @retval EFI_NO_MAPPING There is no interface to the destination.
+ @retval EFI_NOT_FOUND There is no route to the destination
+ @retval EFI_SUCCESS The packet is successfully transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Ip4Output (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen,
+ IN IP4_ADDR GateWay,
+ IN IP4_FRAME_CALLBACK Callback,
+ IN VOID *Context
+ );
+
+/**
+ Cancel the Packet and all its fragments.
+
+ @param IpIf The interface from which the Packet is sent
+ @param Packet The Packet to cancel
+ @param IoStatus The status returns to the sender.
+
+**/
+VOID
+Ip4CancelPacket (
+ IN IP4_INTERFACE *IpIf,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus
+ );
+
+/**
+ Prepend an IP4 head to the Packet. It will copy the options and
+ build the IP4 header fields. Used for IP4 fragmentation.
+
+ @param Packet The packet to prepend IP4 header to
+ @param Head The caller supplied header. The caller should set
+ the following header fields: Tos, TotalLen, Id,
+ Fragment, Ttl, Protocol, Src and Dst. All the fields
+ are in host byte order. This function will fill in
+ the Ver, HeadLen, and checksum.
+ @param Option The orginal IP4 option to copy from
+ @param OptLen The length of the IP4 option
+
+ @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of
+ Packet.
+ @retval EFI_SUCCESS The IP4 header is successfully added to the packet.
+
+**/
+EFI_STATUS
+Ip4PrependHead (
+ IN OUT NET_BUF *Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen
+ );
+
+extern UINT16 mIp4Id;
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c
new file mode 100644
index 0000000000..d240d5343a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c
@@ -0,0 +1,661 @@
+/** @file
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Allocate a route entry then initialize it with the Dest/Netmaks
+ and Gateway.
+
+ @param[in] Dest The destination network
+ @param[in] Netmask The destination network mask
+ @param[in] GateWay The nexthop address
+
+ @return NULL if failed to allocate memeory, otherwise the newly created
+ route entry.
+
+**/
+IP4_ROUTE_ENTRY *
+Ip4CreateRouteEntry (
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR GateWay
+ )
+{
+ IP4_ROUTE_ENTRY *RtEntry;
+
+ RtEntry = AllocatePool (sizeof (IP4_ROUTE_ENTRY));
+
+ if (RtEntry == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&RtEntry->Link);
+
+ RtEntry->RefCnt = 1;
+ RtEntry->Dest = Dest;
+ RtEntry->Netmask = Netmask;
+ RtEntry->NextHop = GateWay;
+ RtEntry->Flag = 0;
+
+ return RtEntry;
+}
+
+
+/**
+ Free the route table entry. It is reference counted.
+
+ @param RtEntry The route entry to free.
+
+**/
+VOID
+Ip4FreeRouteEntry (
+ IN IP4_ROUTE_ENTRY *RtEntry
+ )
+{
+ ASSERT (RtEntry->RefCnt > 0);
+
+ if (--RtEntry->RefCnt == 0) {
+ FreePool (RtEntry);
+ }
+}
+
+
+/**
+ Allocate and initialize an IP4 route cache entry.
+
+ @param[in] Dst The destination address
+ @param[in] Src The source address
+ @param[in] GateWay The next hop address
+ @param[in] Tag The tag from the caller. This marks all the cache
+ entries spawned from one route table entry.
+
+ @return NULL if failed to allocate memory for the cache, other point
+ to the created route cache entry.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4CreateRouteCacheEntry (
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src,
+ IN IP4_ADDR GateWay,
+ IN UINTN Tag
+ )
+{
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+
+ RtCacheEntry = AllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY));
+
+ if (RtCacheEntry == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&RtCacheEntry->Link);
+
+ RtCacheEntry->RefCnt = 1;
+ RtCacheEntry->Dest = Dst;
+ RtCacheEntry->Src = Src;
+ RtCacheEntry->NextHop = GateWay;
+ RtCacheEntry->Tag = Tag;
+
+ return RtCacheEntry;
+}
+
+
+/**
+ Free the route cache entry. It is reference counted.
+
+ @param RtCacheEntry The route cache entry to free.
+
+**/
+VOID
+Ip4FreeRouteCacheEntry (
+ IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry
+ )
+{
+ ASSERT (RtCacheEntry->RefCnt > 0);
+
+ if (--RtCacheEntry->RefCnt == 0) {
+ FreePool (RtCacheEntry);
+ }
+}
+
+
+/**
+ Initialize an empty route cache table.
+
+ @param[in, out] RtCache The rotue cache table to initialize.
+
+**/
+VOID
+Ip4InitRouteCache (
+ IN OUT IP4_ROUTE_CACHE *RtCache
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ROUTE_CACHE_HASH_VALUE; Index++) {
+ InitializeListHead (&(RtCache->CacheBucket[Index]));
+ }
+}
+
+
+/**
+ Clean up a route cache, that is free all the route cache
+ entries enqueued in the cache.
+
+ @param[in] RtCache The route cache table to clean up
+
+**/
+VOID
+Ip4CleanRouteCache (
+ IN IP4_ROUTE_CACHE *RtCache
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ROUTE_CACHE_HASH_VALUE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtCache->CacheBucket[Index])) {
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ RemoveEntryList (Entry);
+ Ip4FreeRouteCacheEntry (RtCacheEntry);
+ }
+ }
+}
+
+
+
+/**
+ Create an empty route table, includes its internal route cache
+
+ @return NULL if failed to allocate memory for the route table, otherwise
+ the point to newly created route table.
+
+**/
+IP4_ROUTE_TABLE *
+Ip4CreateRouteTable (
+ VOID
+ )
+{
+ IP4_ROUTE_TABLE *RtTable;
+ UINT32 Index;
+
+ RtTable = AllocatePool (sizeof (IP4_ROUTE_TABLE));
+
+ if (RtTable == NULL) {
+ return NULL;
+ }
+
+ RtTable->RefCnt = 1;
+ RtTable->TotalNum = 0;
+
+ for (Index = 0; Index <= IP4_MASK_MAX; Index++) {
+ InitializeListHead (&(RtTable->RouteArea[Index]));
+ }
+
+ RtTable->Next = NULL;
+
+ Ip4InitRouteCache (&RtTable->Cache);
+ return RtTable;
+}
+
+
+/**
+ Free the route table and its associated route cache. Route
+ table is reference counted.
+
+ @param[in] RtTable The route table to free.
+
+**/
+VOID
+Ip4FreeRouteTable (
+ IN IP4_ROUTE_TABLE *RtTable
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ROUTE_ENTRY *RtEntry;
+ UINT32 Index;
+
+ ASSERT (RtTable->RefCnt > 0);
+
+ if (--RtTable->RefCnt > 0) {
+ return ;
+ }
+
+ //
+ // Free all the route table entry and its route cache.
+ //
+ for (Index = 0; Index <= IP4_MASK_MAX; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtTable->RouteArea[Index])) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ RemoveEntryList (Entry);
+ Ip4FreeRouteEntry (RtEntry);
+ }
+ }
+
+ Ip4CleanRouteCache (&RtTable->Cache);
+
+ FreePool (RtTable);
+}
+
+
+
+/**
+ Remove all the cache entries bearing the Tag. When a route cache
+ entry is created, it is tagged with the address of route entry
+ from which it is spawned. When a route entry is deleted, the cache
+ entries spawned from it are also deleted.
+
+ @param RtCache Route cache to remove the entries from
+ @param Tag The Tag of the entries to remove
+
+**/
+VOID
+Ip4PurgeRouteCache (
+ IN OUT IP4_ROUTE_CACHE *RtCache,
+ IN UINTN Tag
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ROUTE_CACHE_HASH_VALUE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {
+
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ if (RtCacheEntry->Tag == Tag) {
+ RemoveEntryList (Entry);
+ Ip4FreeRouteCacheEntry (RtCacheEntry);
+ }
+ }
+ }
+}
+
+
+/**
+ Add a route entry to the route table. All the IP4_ADDRs are in
+ host byte order.
+
+ @param[in, out] RtTable Route table to add route to
+ @param[in] Dest The destination of the network
+ @param[in] Netmask The netmask of the destination
+ @param[in] Gateway The next hop address
+
+ @retval EFI_ACCESS_DENIED The same route already exists
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry
+ @retval EFI_SUCCESS The route is added successfully.
+
+**/
+EFI_STATUS
+Ip4AddRoute (
+ IN OUT IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Entry;
+ IP4_ROUTE_ENTRY *RtEntry;
+
+ //
+ // All the route entries with the same netmask length are
+ // linke to the same route area
+ //
+ Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);
+
+ //
+ // First check whether the route exists
+ //
+ NET_LIST_FOR_EACH (Entry, Head) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {
+ return EFI_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Create a route entry and insert it to the route area.
+ //
+ RtEntry = Ip4CreateRouteEntry (Dest, Netmask, Gateway);
+
+ if (RtEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (Gateway == IP4_ALLZERO_ADDRESS) {
+ RtEntry->Flag = IP4_DIRECT_ROUTE;
+ }
+
+ InsertHeadList (Head, &RtEntry->Link);
+ RtTable->TotalNum++;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Remove a route entry and all the route caches spawn from it.
+
+ @param RtTable The route table to remove the route from
+ @param Dest The destination network
+ @param Netmask The netmask of the Dest
+ @param Gateway The next hop address
+
+ @retval EFI_SUCCESS The route entry is successfully removed
+ @retval EFI_NOT_FOUND There is no route entry in the table with that
+ properity.
+
+**/
+EFI_STATUS
+Ip4DelRoute (
+ IN OUT IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ROUTE_ENTRY *RtEntry;
+
+ Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {
+ Ip4PurgeRouteCache (&RtTable->Cache, (UINTN) RtEntry);
+ RemoveEntryList (Entry);
+ Ip4FreeRouteEntry (RtEntry);
+
+ RtTable->TotalNum--;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Find a route cache with the dst and src. This is used by ICMP
+ redirect messasge process. All kinds of redirect is treated as
+ host redirect according to RFC1122. So, only route cache entries
+ are modified according to the ICMP redirect message.
+
+ @param[in] RtTable The route table to search the cache for
+ @param[in] Dest The destination address
+ @param[in] Src The source address
+
+ @return NULL if no route entry to the (Dest, Src). Otherwise the point
+ to the correct route cache entry.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4FindRouteCache (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ Index = IP4_ROUTE_CACHE_HASH (Dest, Src);
+
+ NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ if ((RtCacheEntry->Dest == Dest) && (RtCacheEntry->Src == Src)) {
+ NET_GET_REF (RtCacheEntry);
+ return RtCacheEntry;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Search the route table for a most specific match to the Dst. It searches
+ from the longest route area (mask length == 32) to the shortest route area
+ (default routes). In each route area, it will first search the instance's
+ route table, then the default route table. This is required by the following
+ requirements:
+ 1. IP search the route table for a most specific match
+ 2. The local route entries have precedence over the default route entry.
+
+ @param[in] RtTable The route table to search from
+ @param[in] Dst The destionation address to search
+
+ @return NULL if no route matches the Dst, otherwise the point to the
+ most specific route to the Dst.
+
+**/
+IP4_ROUTE_ENTRY *
+Ip4FindRouteEntry (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dst
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_ROUTE_ENTRY *RtEntry;
+ IP4_ROUTE_TABLE *Table;
+ INTN Index;
+
+ RtEntry = NULL;
+
+ for (Index = IP4_MASK_MAX; Index >= 0; Index--) {
+ for (Table = RtTable; Table != NULL; Table = Table->Next) {
+ NET_LIST_FOR_EACH (Entry, &Table->RouteArea[Index]) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ if (IP4_NET_EQUAL (RtEntry->Dest, Dst, RtEntry->Netmask)) {
+ NET_GET_REF (RtEntry);
+ return RtEntry;
+ }
+ }
+ }
+ }
+
+
+ return NULL;
+}
+
+
+/**
+ Search the route table to route the packet. Return/create a route
+ cache if there is a route to the destination.
+
+ @param[in] RtTable The route table to search from
+ @param[in] Dest The destination address to search for
+ @param[in] Src The source address to search for
+
+ @return NULL if failed to route packet, otherwise a route cache
+ entry that can be used to route packet.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4Route (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ IP4_ROUTE_CACHE_ENTRY *Cache;
+ IP4_ROUTE_ENTRY *RtEntry;
+ IP4_ADDR NextHop;
+ UINT32 Count;
+
+ ASSERT (RtTable != NULL);
+
+ Head = &RtTable->Cache.CacheBucket[IP4_ROUTE_CACHE_HASH (Dest, Src)];
+ RtCacheEntry = Ip4FindRouteCache (RtTable, Dest, Src);
+
+ //
+ // If found, promote the cache entry to the head of the hash bucket. LRU
+ //
+ if (RtCacheEntry != NULL) {
+ RemoveEntryList (&RtCacheEntry->Link);
+ InsertHeadList (Head, &RtCacheEntry->Link);
+ return RtCacheEntry;
+ }
+
+ //
+ // Search the route table for the most specific route
+ //
+ RtEntry = Ip4FindRouteEntry (RtTable, Dest);
+
+ if (RtEntry == NULL) {
+ return NULL;
+ }
+
+ //
+ // Found a route to the Dest, if it is a direct route, the packet
+ // will be sent directly to the destination, such as for connected
+ // network. Otherwise, it is an indirect route, the packet will be
+ // sent to the next hop router.
+ //
+ if ((RtEntry->Flag & IP4_DIRECT_ROUTE) != 0) {
+ NextHop = Dest;
+ } else {
+ NextHop = RtEntry->NextHop;
+ }
+
+ Ip4FreeRouteEntry (RtEntry);
+
+ //
+ // Create a route cache entry, and tag it as spawned from this route entry
+ //
+ RtCacheEntry = Ip4CreateRouteCacheEntry (Dest, Src, NextHop, (UINTN) RtEntry);
+
+ if (RtCacheEntry == NULL) {
+ return NULL;
+ }
+
+ InsertHeadList (Head, &RtCacheEntry->Link);
+ NET_GET_REF (RtCacheEntry);
+
+ //
+ // Each bucket of route cache can contain at most 64 entries.
+ // Remove the entries at the tail of the bucket. These entries
+ // are likely to be used least.
+ //
+ Count = 0;
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ if (++Count < IP4_ROUTE_CACHE_MAX) {
+ continue;
+ }
+
+ Cache = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ RemoveEntryList (Entry);
+ Ip4FreeRouteCacheEntry (Cache);
+ }
+
+ return RtCacheEntry;
+}
+
+
+/**
+ Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of
+ GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the
+ internal operation of the IP4 driver.
+
+ @param[in] IpInstance The IP4 child that requests the route table.
+
+ @retval EFI_SUCCESS The route table is successfully build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the rotue table.
+
+**/
+EFI_STATUS
+Ip4BuildEfiRouteTable (
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_ROUTE_TABLE *RtTable;
+ IP4_ROUTE_ENTRY *RtEntry;
+ EFI_IP4_ROUTE_TABLE *Table;
+ UINT32 Count;
+ INT32 Index;
+
+ RtTable = IpInstance->RouteTable;
+
+ if (IpInstance->EfiRouteTable != NULL) {
+ FreePool (IpInstance->EfiRouteTable);
+
+ IpInstance->EfiRouteTable = NULL;
+ IpInstance->EfiRouteCount = 0;
+ }
+
+ Count = RtTable->TotalNum;
+
+ if (RtTable->Next != NULL) {
+ Count += RtTable->Next->TotalNum;
+ }
+
+ if (Count == 0) {
+ return EFI_SUCCESS;
+ }
+
+ Table = AllocatePool (sizeof (EFI_IP4_ROUTE_TABLE) * Count);
+
+ if (Table == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the route entry to EFI route table. Keep the order of
+ // route entry copied from most specific to default route. That
+ // is, interlevel the route entry from the instance's route area
+ // and those from the default route table's route area.
+ //
+ Count = 0;
+
+ for (Index = IP4_MASK_MAX; Index >= 0; Index--) {
+ for (RtTable = IpInstance->RouteTable; RtTable != NULL; RtTable = RtTable->Next) {
+ NET_LIST_FOR_EACH (Entry, &(RtTable->RouteArea[Index])) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ EFI_IP4 (Table[Count].SubnetAddress) = HTONL (RtEntry->Dest & RtEntry->Netmask);
+ EFI_IP4 (Table[Count].SubnetMask) = HTONL (RtEntry->Netmask);
+ EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop);
+
+ Count++;
+ }
+ }
+ }
+
+ IpInstance->EfiRouteTable = Table;
+ IpInstance->EfiRouteCount = Count;
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h
new file mode 100644
index 0000000000..778c97dc8b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h
@@ -0,0 +1,224 @@
+/** @file
+ EFI IP4 route table and route cache table defintions.
+
+Copyright (c) 2005 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP4_ROUTE_H__
+#define __EFI_IP4_ROUTE_H__
+
+#include "Ip4Common.h"
+
+#define IP4_DIRECT_ROUTE 0x00000001
+
+#define IP4_ROUTE_CACHE_HASH_VALUE 31
+#define IP4_ROUTE_CACHE_MAX 64 // Max NO. of cache entry per hash bucket
+
+#define IP4_ROUTE_CACHE_HASH(Dst, Src) (((Dst) ^ (Src)) % IP4_ROUTE_CACHE_HASH_VALUE)
+
+///
+/// The route entry in the route table. Dest/Netmask is the destion
+/// network. The nexthop is the gateway to send the packet to in
+/// order to reach the Dest/Netmask. If the Flag has IP4_DIRECT_ROUTE
+/// on, the gateway is the destination of the IP packet itself. Route
+/// enties of the connected network have the flag on.
+///
+typedef struct {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ IP4_ADDR Dest;
+ IP4_ADDR Netmask;
+ IP4_ADDR NextHop;
+ UINT32 Flag;
+} IP4_ROUTE_ENTRY;
+
+///
+/// The route cache entry. The route cache entry is optional.
+/// But it is necessary to support the ICMP redirect message.
+/// Check Ip4ProcessIcmpRedirect for information.
+///
+/// The cache entry field Tag is used to tag all the route
+/// cache entry spawned from a route table entry. This makes
+/// it simple to delete all the route cache entries from a
+/// to-be-deleted route entry.
+///
+typedef struct {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ IP4_ADDR Dest;
+ IP4_ADDR Src;
+ IP4_ADDR NextHop;
+ UINTN Tag;
+} IP4_ROUTE_CACHE_ENTRY;
+
+///
+/// The route cache table is organized as a hash table. Each
+/// IP4 route table has a embedded route cache. For now the
+/// route cache and route table are binded togehter. But keep
+/// the route cache a seperated structure in case we want to
+/// detach them later.
+///
+typedef struct {
+ LIST_ENTRY CacheBucket[IP4_ROUTE_CACHE_HASH_VALUE];
+} IP4_ROUTE_CACHE;
+
+///
+/// Each IP4 instance has its own route table. Each ServiceBinding
+/// instance has a default route table and default address.
+///
+/// All the route table entries with the same mask are linked
+/// together in one route area. For example, RouteArea[0] contains
+/// the default routes. A route table also contains a route cache.
+///
+typedef struct _IP4_ROUTE_TABLE IP4_ROUTE_TABLE;
+
+struct _IP4_ROUTE_TABLE {
+ INTN RefCnt;
+ UINT32 TotalNum;
+ LIST_ENTRY RouteArea[IP4_MASK_NUM];
+ IP4_ROUTE_TABLE *Next;
+ IP4_ROUTE_CACHE Cache;
+};
+
+/**
+ Create an empty route table, includes its internal route cache
+
+ @return NULL if failed to allocate memory for the route table, otherwise
+ the point to newly created route table.
+
+**/
+IP4_ROUTE_TABLE *
+Ip4CreateRouteTable (
+ VOID
+ );
+
+/**
+ Free the route table and its associated route cache. Route
+ table is reference counted.
+
+ @param[in] RtTable The route table to free.
+
+**/
+VOID
+Ip4FreeRouteTable (
+ IN IP4_ROUTE_TABLE *RtTable
+ );
+
+/**
+ Add a route entry to the route table. All the IP4_ADDRs are in
+ host byte order.
+
+ @param[in, out] RtTable Route table to add route to
+ @param[in] Dest The destination of the network
+ @param[in] Netmask The netmask of the destination
+ @param[in] Gateway The next hop address
+
+ @retval EFI_ACCESS_DENIED The same route already exists
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry
+ @retval EFI_SUCCESS The route is added successfully.
+
+**/
+EFI_STATUS
+Ip4AddRoute (
+ IN OUT IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ );
+
+/**
+ Remove a route entry and all the route caches spawn from it.
+
+ @param RtTable The route table to remove the route from
+ @param Dest The destination network
+ @param Netmask The netmask of the Dest
+ @param Gateway The next hop address
+
+ @retval EFI_SUCCESS The route entry is successfully removed
+ @retval EFI_NOT_FOUND There is no route entry in the table with that
+ properity.
+
+**/
+EFI_STATUS
+Ip4DelRoute (
+ IN OUT IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ );
+
+/**
+ Find a route cache with the dst and src. This is used by ICMP
+ redirect messasge process. All kinds of redirect is treated as
+ host redirect according to RFC1122. So, only route cache entries
+ are modified according to the ICMP redirect message.
+
+ @param[in] RtTable The route table to search the cache for
+ @param[in] Dest The destination address
+ @param[in] Src The source address
+
+ @return NULL if no route entry to the (Dest, Src). Otherwise the point
+ to the correct route cache entry.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4FindRouteCache (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ );
+
+/**
+ Free the route cache entry. It is reference counted.
+
+ @param RtCacheEntry The route cache entry to free.
+
+**/
+VOID
+Ip4FreeRouteCacheEntry (
+ IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry
+ );
+
+/**
+ Search the route table to route the packet. Return/create a route
+ cache if there is a route to the destination.
+
+ @param[in] RtTable The route table to search from
+ @param[in] Dest The destination address to search for
+ @param[in] Src The source address to search for
+
+ @return NULL if failed to route packet, otherwise a route cache
+ entry that can be used to route packet.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4Route (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ );
+
+/**
+ Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of
+ GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the
+ internal operation of the IP4 driver.
+
+ @param[in] IpInstance The IP4 child that requests the route table.
+
+ @retval EFI_SUCCESS The route table is successfully build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the rotue table.
+
+**/
+EFI_STATUS
+Ip4BuildEfiRouteTable (
+ IN IP4_PROTOCOL *IpInstance
+ );
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c
new file mode 100644
index 0000000000..b6f96087ed
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c
@@ -0,0 +1,348 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for MnpDxe driver.
+
+Copyright (c) 2005 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MnpImpl.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMnpComponentName = {
+ MnpComponentNameGetDriverName,
+ MnpComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMnpComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) MnpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) MnpComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMnpDriverNameTable[] = {
+ {
+ "eng;en",
+ L"MNP Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gMnpControllerNameTable = NULL;
+
+/**
+ 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
+MnpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mMnpDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gMnpComponentName)
+ );
+}
+
+/**
+ Update the component name for the MNP child handle.
+
+ @param Mnp[in] A pointer to the EFI_MANAGED_NETWORK_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ CHAR16 HandleName[80];
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE SnpModeData;
+ UINTN OffSet;
+ UINTN Index;
+
+ if (Mnp == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (Mnp);
+ //
+ // Format the child name into the string buffer as:
+ // MNP (MAC=FF-FF-FF-FF-FF-FF, ProtocolType=0x0800, VlanId=0)
+ //
+ Status = Mnp->GetModeData (Mnp, &MnpConfigData, &SnpModeData);
+ if (!EFI_ERROR (Status)) {
+ OffSet = 0;
+ //
+ // Print the MAC address.
+ //
+ OffSet += UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"MNP (MAC="
+ );
+ for (Index = 0; Index < SnpModeData.HwAddressSize; Index++) {
+ OffSet += UnicodeSPrint (
+ HandleName + OffSet,
+ sizeof (HandleName) - OffSet * sizeof (CHAR16),
+ L"%02X-",
+ SnpModeData.CurrentAddress.Addr[Index]
+ );
+ }
+ ASSERT (OffSet > 0);
+ //
+ // Remove the last '-'
+ //
+ OffSet--;
+ //
+ // Print the ProtocolType and VLAN ID for this instance.
+ //
+ OffSet += UnicodeSPrint (
+ HandleName + OffSet,
+ sizeof (HandleName) - OffSet * sizeof (CHAR16),
+ L", ProtocolType=0x%X, VlanId=%d)",
+ MnpConfigData.ProtocolTypeFilter,
+ Instance->MnpServiceData->VlanId
+ );
+ } else if (Status == EFI_NOT_STARTED) {
+ UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"MNP (Not started)"
+ );
+ } else {
+ return Status;
+ }
+
+ if (gMnpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gMnpControllerNameTable);
+ gMnpControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gMnpComponentName.SupportedLanguages,
+ &gMnpControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gMnpComponentName2.SupportedLanguages,
+ &gMnpControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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.
+ Currently not implemented.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle 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
+ specified by This, ControllerHandle, ChildHandle,
+ and Language was returned in ControllerName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @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
+MnpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ //
+ // Only provide names for MNP child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver is currently managing ControllerHandle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gMnpDriverBinding.DriverBindingHandle,
+ &gEfiSimpleNetworkProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **)&Mnp,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Mnp);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gMnpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gMnpComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.h b/Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.h
new file mode 100644
index 0000000000..6232c3e1ed
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.h
@@ -0,0 +1,151 @@
+/** @file
+ The header file of UEFI Component Name(2) protocol.
+
+Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _COMPONENT_NAME_H_
+#define _COMPONENT_NAME_H_
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gMnpComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gMnpComponentName;
+extern EFI_UNICODE_STRING_TABLE *gMnpControllerNameTable;
+
+/**
+ 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
+MnpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ 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.
+ Currently not implemented.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle 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
+ specified by This, ControllerHandle, ChildHandle,
+ and Language was returned in ControllerName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @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
+MnpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
new file mode 100644
index 0000000000..d1a4cb5dd2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
@@ -0,0 +1,1946 @@
+/** @file
+ Implementation of Managed Network Protocol private services.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MnpImpl.h"
+#include "MnpVlan.h"
+
+EFI_SERVICE_BINDING_PROTOCOL mMnpServiceBindingProtocol = {
+ MnpServiceBindingCreateChild,
+ MnpServiceBindingDestroyChild
+};
+
+EFI_MANAGED_NETWORK_PROTOCOL mMnpProtocolTemplate = {
+ MnpGetModeData,
+ MnpConfigure,
+ MnpMcastIpToMac,
+ MnpGroups,
+ MnpTransmit,
+ MnpReceive,
+ MnpCancel,
+ MnpPoll
+};
+
+EFI_MANAGED_NETWORK_CONFIG_DATA mMnpDefaultConfigData = {
+ 10000000,
+ 10000000,
+ 0,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE
+};
+
+/**
+ Add Count of net buffers to MnpDeviceData->FreeNbufQue. The length of the net
+ buffer is specified by MnpDeviceData->BufferLength.
+
+ @param[in, out] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+ @param[in] Count Number of NET_BUFFERs to add.
+
+ @retval EFI_SUCCESS The specified amount of NET_BUFs are allocated
+ and added to MnpDeviceData->FreeNbufQue.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate a NET_BUF structure.
+
+**/
+EFI_STATUS
+MnpAddFreeNbuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINTN Count
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ NET_BUF *Nbuf;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+ ASSERT ((Count > 0) && (MnpDeviceData->BufferLength > 0));
+
+ Status = EFI_SUCCESS;
+ for (Index = 0; Index < Count; Index++) {
+ Nbuf = NetbufAlloc (MnpDeviceData->BufferLength + MnpDeviceData->PaddingSize);
+ if (Nbuf == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpAddFreeNbuf: NetBufAlloc failed.\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ if (MnpDeviceData->PaddingSize > 0) {
+ //
+ // Pad padding bytes before the media header
+ //
+ NetbufAllocSpace (Nbuf, MnpDeviceData->PaddingSize, NET_BUF_TAIL);
+ NetbufTrim (Nbuf, MnpDeviceData->PaddingSize, NET_BUF_HEAD);
+ }
+
+ NetbufQueAppend (&MnpDeviceData->FreeNbufQue, Nbuf);
+ }
+
+ MnpDeviceData->NbufCnt += Index;
+ return Status;
+}
+
+
+/**
+ Allocate a free NET_BUF from MnpDeviceData->FreeNbufQue. If there is none
+ in the queue, first try to allocate some and add them into the queue, then
+ fetch the NET_BUF from the updated FreeNbufQue.
+
+ @param[in, out] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+
+ @return Pointer to the allocated free NET_BUF structure, if NULL the
+ operation is failed.
+
+**/
+NET_BUF *
+MnpAllocNbuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF_QUEUE *FreeNbufQue;
+ NET_BUF *Nbuf;
+ EFI_TPL OldTpl;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ FreeNbufQue = &MnpDeviceData->FreeNbufQue;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+ //
+ // Check whether there are available buffers, or else try to add some.
+ //
+ if (FreeNbufQue->BufNum == 0) {
+ if ((MnpDeviceData->NbufCnt + MNP_NET_BUFFER_INCREASEMENT) > MNP_MAX_NET_BUFFER_NUM) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpAllocNbuf: The maximum NET_BUF size is reached for MNP driver instance %p.\n",
+ MnpDeviceData)
+ );
+
+ Nbuf = NULL;
+ goto ON_EXIT;
+ }
+
+ Status = MnpAddFreeNbuf (MnpDeviceData, MNP_NET_BUFFER_INCREASEMENT);
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpAllocNbuf: Failed to add NET_BUFs into the FreeNbufQue, %r.\n",
+ Status)
+ );
+
+ //
+ // Don't return NULL, perhaps MnpAddFreeNbuf does add some NET_BUFs but
+ // the amount is less than MNP_NET_BUFFER_INCREASEMENT.
+ //
+ }
+ }
+
+ Nbuf = NetbufQueRemove (FreeNbufQue);
+
+ //
+ // Increase the RefCnt.
+ //
+ if (Nbuf != NULL) {
+ NET_GET_REF (Nbuf);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Nbuf;
+}
+
+
+/**
+ Try to reclaim the Nbuf into the buffer pool.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in, out] Nbuf Pointer to the NET_BUF to free.
+
+**/
+VOID
+MnpFreeNbuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN OUT NET_BUF *Nbuf
+ )
+{
+ EFI_TPL OldTpl;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+ ASSERT (Nbuf->RefCnt > 1);
+
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+ NET_PUT_REF (Nbuf);
+
+ if (Nbuf->RefCnt == 1) {
+ //
+ // Trim all buffer contained in the Nbuf, then append it to the NbufQue.
+ //
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_TAIL);
+
+ if (NetbufAllocSpace (Nbuf, NET_VLAN_TAG_LEN, NET_BUF_HEAD) != NULL) {
+ //
+ // There is space reserved for vlan tag in the head, reclaim it
+ //
+ NetbufTrim (Nbuf, NET_VLAN_TAG_LEN, NET_BUF_TAIL);
+ }
+
+ NetbufQueAppend (&MnpDeviceData->FreeNbufQue, Nbuf);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+}
+
+/**
+ Add Count of TX buffers to MnpDeviceData->AllTxBufList and MnpDeviceData->FreeTxBufList.
+ The length of the buffer is specified by MnpDeviceData->BufferLength.
+
+ @param[in, out] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+ @param[in] Count Number of TX buffers to add.
+
+ @retval EFI_SUCCESS The specified amount of TX buffers are allocated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate a TX buffer.
+
+**/
+EFI_STATUS
+MnpAddFreeTxBuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINTN Count
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ MNP_TX_BUF_WRAP *TxBufWrap;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+ ASSERT ((Count > 0) && (MnpDeviceData->BufferLength > 0));
+
+ Status = EFI_SUCCESS;
+ for (Index = 0; Index < Count; Index++) {
+ TxBufWrap = (MNP_TX_BUF_WRAP*) AllocatePool (sizeof (MNP_TX_BUF_WRAP) + MnpDeviceData->BufferLength - 1);
+ if (TxBufWrap == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpAddFreeTxBuf: TxBuf Alloc failed.\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ DEBUG ((EFI_D_INFO, "MnpAddFreeTxBuf: Add TxBufWrap %p, TxBuf %p\n", TxBufWrap, TxBufWrap->TxBuf));
+ TxBufWrap->Signature = MNP_TX_BUF_WRAP_SIGNATURE;
+ TxBufWrap->InUse = FALSE;
+ InsertTailList (&MnpDeviceData->FreeTxBufList, &TxBufWrap->WrapEntry);
+ InsertTailList (&MnpDeviceData->AllTxBufList, &TxBufWrap->AllEntry);
+ }
+
+ MnpDeviceData->TxBufCount += Index;
+ return Status;
+}
+
+/**
+ Allocate a free TX buffer from MnpDeviceData->FreeTxBufList. If there is none
+ in the queue, first try to recycle some from SNP, then try to allocate some and add
+ them into the queue, then fetch the NET_BUF from the updated FreeTxBufList.
+
+ @param[in, out] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+
+ @return Pointer to the allocated free NET_BUF structure, if NULL the
+ operation is failed.
+
+**/
+UINT8 *
+MnpAllocTxBuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ )
+{
+ EFI_TPL OldTpl;
+ UINT8 *TxBuf;
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ MNP_TX_BUF_WRAP *TxBufWrap;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IsListEmpty (&MnpDeviceData->FreeTxBufList)) {
+ //
+ // First try to recycle some TX buffer from SNP
+ //
+ Status = MnpRecycleTxBuf (MnpDeviceData);
+ if (EFI_ERROR (Status)) {
+ TxBuf = NULL;
+ goto ON_EXIT;
+ }
+
+ //
+ // If still no free TX buffer, allocate more.
+ //
+ if (IsListEmpty (&MnpDeviceData->FreeTxBufList)) {
+ if ((MnpDeviceData->TxBufCount + MNP_TX_BUFFER_INCREASEMENT) > MNP_MAX_TX_BUFFER_NUM) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpAllocTxBuf: The maximum TxBuf size is reached for MNP driver instance %p.\n",
+ MnpDeviceData)
+ );
+
+ TxBuf = NULL;
+ goto ON_EXIT;
+ }
+
+ Status = MnpAddFreeTxBuf (MnpDeviceData, MNP_TX_BUFFER_INCREASEMENT);
+ if (IsListEmpty (&MnpDeviceData->FreeTxBufList)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpAllocNbuf: Failed to add TxBuf into the FreeTxBufList, %r.\n",
+ Status)
+ );
+
+ TxBuf = NULL;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ ASSERT (!IsListEmpty (&MnpDeviceData->FreeTxBufList));
+ Entry = MnpDeviceData->FreeTxBufList.ForwardLink;
+ RemoveEntryList (MnpDeviceData->FreeTxBufList.ForwardLink);
+ TxBufWrap = NET_LIST_USER_STRUCT_S (Entry, MNP_TX_BUF_WRAP, WrapEntry, MNP_TX_BUF_WRAP_SIGNATURE);
+ TxBufWrap->InUse = TRUE;
+ TxBuf = TxBufWrap->TxBuf;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return TxBuf;
+}
+
+/**
+ Try to reclaim the TX buffer into the buffer pool.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in, out] TxBuf Pointer to the TX buffer to free.
+
+**/
+VOID
+MnpFreeTxBuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN OUT UINT8 *TxBuf
+ )
+{
+ MNP_TX_BUF_WRAP *TxBufWrap;
+ EFI_TPL OldTpl;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ if (TxBuf == NULL) {
+ return;
+ }
+
+ TxBufWrap = NET_LIST_USER_STRUCT (TxBuf, MNP_TX_BUF_WRAP, TxBuf);
+ if (TxBufWrap->Signature != MNP_TX_BUF_WRAP_SIGNATURE) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpFreeTxBuf: Signature check failed in MnpFreeTxBuf.\n")
+ );
+ return;
+ }
+
+ if (!TxBufWrap->InUse) {
+ DEBUG (
+ (EFI_D_WARN,
+ "MnpFreeTxBuf: Duplicated recycle report from SNP.\n")
+ );
+ return;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ InsertTailList (&MnpDeviceData->FreeTxBufList, &TxBufWrap->WrapEntry);
+ TxBufWrap->InUse = FALSE;
+ gBS->RestoreTPL (OldTpl);
+}
+
+/**
+ Try to recycle all the transmitted buffer address from SNP.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+
+ @retval EFI_SUCCESS Successed to recyclethe transmitted buffer address.
+ @retval Others Failed to recyclethe transmitted buffer address.
+
+**/
+EFI_STATUS
+MnpRecycleTxBuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ )
+{
+ UINT8 *TxBuf;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_STATUS Status;
+
+ Snp = MnpDeviceData->Snp;
+ ASSERT (Snp != NULL);
+
+ do {
+ TxBuf = NULL;
+ Status = Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (TxBuf != NULL) {
+ MnpFreeTxBuf (MnpDeviceData, TxBuf);
+ }
+ } while (TxBuf != NULL);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the mnp device context data.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] ImageHandle The driver image handle.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+
+ @retval EFI_SUCCESS The mnp service context is initialized.
+ @retval EFI_UNSUPPORTED ControllerHandle does not support Simple Network Protocol.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpInitializeDeviceData (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+
+ MnpDeviceData->Signature = MNP_DEVICE_DATA_SIGNATURE;
+ MnpDeviceData->ImageHandle = ImageHandle;
+ MnpDeviceData->ControllerHandle = ControllerHandle;
+
+ //
+ // Copy the MNP Protocol interfaces from the template.
+ //
+ CopyMem (&MnpDeviceData->VlanConfig, &mVlanConfigProtocolTemplate, sizeof (EFI_VLAN_CONFIG_PROTOCOL));
+
+ //
+ // Open the Simple Network protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp,
+ ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Get MTU from Snp.
+ //
+ SnpMode = Snp->Mode;
+ MnpDeviceData->Snp = Snp;
+
+ //
+ // Initialize the lists.
+ //
+ InitializeListHead (&MnpDeviceData->ServiceList);
+ InitializeListHead (&MnpDeviceData->GroupAddressList);
+
+ //
+ // Get the buffer length used to allocate NET_BUF to hold data received
+ // from SNP. Do this before fill the FreeNetBufQue.
+ //
+ //
+ MnpDeviceData->BufferLength = SnpMode->MediaHeaderSize + NET_VLAN_TAG_LEN + SnpMode->MaxPacketSize + NET_ETHER_FCS_SIZE;
+
+ //
+ // Make sure the protocol headers immediately following the media header
+ // 4-byte aligned, and also preserve additional space for VLAN tag
+ //
+ MnpDeviceData->PaddingSize = ((4 - SnpMode->MediaHeaderSize) & 0x3) + NET_VLAN_TAG_LEN;
+
+ //
+ // Initialize MAC string which will be used as VLAN configuration variable name
+ //
+ Status = NetLibGetMacString (ControllerHandle, ImageHandle, &MnpDeviceData->MacString);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ //
+ // Initialize the FreeNetBufQue and pre-allocate some NET_BUFs.
+ //
+ NetbufQueInit (&MnpDeviceData->FreeNbufQue);
+ Status = MnpAddFreeNbuf (MnpDeviceData, MNP_INIT_NET_BUFFER_NUM);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpInitializeDeviceData: MnpAddFreeNbuf failed, %r.\n", Status));
+
+ goto ERROR;
+ }
+
+ //
+ // Get one NET_BUF from the FreeNbufQue for rx cache.
+ //
+ MnpDeviceData->RxNbufCache = MnpAllocNbuf (MnpDeviceData);
+ NetbufAllocSpace (
+ MnpDeviceData->RxNbufCache,
+ MnpDeviceData->BufferLength,
+ NET_BUF_TAIL
+ );
+
+ //
+ // Allocate buffer pool for tx.
+ //
+ InitializeListHead (&MnpDeviceData->FreeTxBufList);
+ InitializeListHead (&MnpDeviceData->AllTxBufList);
+ MnpDeviceData->TxBufCount = 0;
+
+ //
+ // Create the system poll timer.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ MnpSystemPoll,
+ MnpDeviceData,
+ &MnpDeviceData->PollTimer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpInitializeDeviceData: CreateEvent for poll timer failed.\n"));
+
+ goto ERROR;
+ }
+
+ //
+ // Create the timer for packet timeout check.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ MnpCheckPacketTimeout,
+ MnpDeviceData,
+ &MnpDeviceData->TimeoutCheckTimer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpInitializeDeviceData: CreateEvent for packet timeout check failed.\n"));
+
+ goto ERROR;
+ }
+
+ //
+ // Create the timer for media detection.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ MnpCheckMediaStatus,
+ MnpDeviceData,
+ &MnpDeviceData->MediaDetectTimer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpInitializeDeviceData: CreateEvent for media detection failed.\n"));
+
+ goto ERROR;
+ }
+
+ERROR:
+ if (EFI_ERROR (Status)) {
+ //
+ // Free the dynamic allocated resources if necessary.
+ //
+ if (MnpDeviceData->MacString != NULL) {
+ FreePool (MnpDeviceData->MacString);
+ }
+
+ if (MnpDeviceData->TimeoutCheckTimer != NULL) {
+ gBS->CloseEvent (MnpDeviceData->TimeoutCheckTimer);
+ }
+
+ if (MnpDeviceData->MediaDetectTimer != NULL) {
+ gBS->CloseEvent (MnpDeviceData->MediaDetectTimer);
+ }
+
+ if (MnpDeviceData->PollTimer != NULL) {
+ gBS->CloseEvent (MnpDeviceData->PollTimer);
+ }
+
+ if (MnpDeviceData->RxNbufCache != NULL) {
+ MnpFreeNbuf (MnpDeviceData, MnpDeviceData->RxNbufCache);
+ }
+
+ if (MnpDeviceData->FreeNbufQue.BufNum != 0) {
+ NetbufQueFlush (&MnpDeviceData->FreeNbufQue);
+ }
+
+ //
+ // Close the Simple Network Protocol.
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ ImageHandle,
+ ControllerHandle
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroy the MNP device context data.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] ImageHandle The driver image handle.
+
+**/
+VOID
+MnpDestroyDeviceData (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ MNP_TX_BUF_WRAP *TxBufWrap;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ //
+ // Free Vlan Config variable name string
+ //
+ if (MnpDeviceData->MacString != NULL) {
+ FreePool (MnpDeviceData->MacString);
+ }
+
+ //
+ // The GroupAddressList must be empty.
+ //
+ ASSERT (IsListEmpty (&MnpDeviceData->GroupAddressList));
+
+ //
+ // Close the event.
+ //
+ gBS->CloseEvent (MnpDeviceData->TimeoutCheckTimer);
+ gBS->CloseEvent (MnpDeviceData->MediaDetectTimer);
+ gBS->CloseEvent (MnpDeviceData->PollTimer);
+
+ //
+ // Free the Tx buffer pool.
+ //
+ NET_LIST_FOR_EACH_SAFE(Entry, NextEntry, &MnpDeviceData->AllTxBufList) {
+ TxBufWrap = NET_LIST_USER_STRUCT (Entry, MNP_TX_BUF_WRAP, AllEntry);
+ RemoveEntryList (Entry);
+ FreePool (TxBufWrap);
+ MnpDeviceData->TxBufCount--;
+ }
+ ASSERT (IsListEmpty (&MnpDeviceData->AllTxBufList));
+ ASSERT (MnpDeviceData->TxBufCount == 0);
+
+ //
+ // Free the RxNbufCache.
+ //
+ MnpFreeNbuf (MnpDeviceData, MnpDeviceData->RxNbufCache);
+
+ //
+ // Flush the FreeNbufQue.
+ //
+ MnpDeviceData->NbufCnt -= MnpDeviceData->FreeNbufQue.BufNum;
+ NetbufQueFlush (&MnpDeviceData->FreeNbufQue);
+
+ //
+ // Close the Simple Network Protocol.
+ //
+ gBS->CloseProtocol (
+ MnpDeviceData->ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ ImageHandle,
+ MnpDeviceData->ControllerHandle
+ );
+}
+
+
+/**
+ Create mnp service context data.
+
+ @param[in] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] VlanId The VLAN ID.
+ @param[in] Priority The VLAN priority. If VlanId is 0,
+ Priority is ignored.
+
+ @return A pointer to MNP_SERVICE_DATA or NULL if failed to create MNP service context.
+
+**/
+MNP_SERVICE_DATA *
+MnpCreateServiceData (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINT16 VlanId,
+ IN UINT8 Priority OPTIONAL
+ )
+{
+ EFI_HANDLE MnpServiceHandle;
+ MNP_SERVICE_DATA *MnpServiceData;
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+
+ //
+ // Initialize the Mnp Service Data.
+ //
+ MnpServiceData = AllocateZeroPool (sizeof (MNP_SERVICE_DATA));
+ if (MnpServiceData == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpCreateServiceData: Faild to allocate memory for the new Mnp Service Data.\n"));
+
+ return NULL;
+ }
+
+ //
+ // Add to MNP service list
+ //
+ InsertTailList (&MnpDeviceData->ServiceList, &MnpServiceData->Link);
+
+ MnpServiceData->Signature = MNP_SERVICE_DATA_SIGNATURE;
+ MnpServiceData->MnpDeviceData = MnpDeviceData;
+
+ //
+ // Copy the ServiceBinding structure.
+ //
+ CopyMem (&MnpServiceData->ServiceBinding, &mMnpServiceBindingProtocol, sizeof (EFI_SERVICE_BINDING_PROTOCOL));
+
+ //
+ // Initialize the lists.
+ //
+ InitializeListHead (&MnpServiceData->ChildrenList);
+
+ SnpMode = MnpDeviceData->Snp->Mode;
+ if (VlanId != 0) {
+ //
+ // Create VLAN child handle
+ //
+ MnpServiceHandle = MnpCreateVlanChild (
+ MnpDeviceData->ImageHandle,
+ MnpDeviceData->ControllerHandle,
+ VlanId,
+ &MnpServiceData->DevicePath
+ );
+ if (MnpServiceHandle == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpCreateServiceData: Faild to create child handle.\n"));
+
+ return NULL;
+ }
+
+ //
+ // Open VLAN Config Protocol by child
+ //
+ Status = gBS->OpenProtocol (
+ MnpDeviceData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **) &VlanConfig,
+ MnpDeviceData->ImageHandle,
+ MnpServiceHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Reduce MTU for VLAN device
+ //
+ MnpServiceData->Mtu = SnpMode->MaxPacketSize - NET_VLAN_TAG_LEN;
+ } else {
+ //
+ // VlanId set to 0 means rx/tx untagged frame
+ //
+ MnpServiceHandle = MnpDeviceData->ControllerHandle;
+ MnpServiceData->Mtu = SnpMode->MaxPacketSize;
+ }
+
+ MnpServiceData->ServiceHandle = MnpServiceHandle;
+ MnpServiceData->VlanId = VlanId;
+ MnpServiceData->Priority = Priority;
+
+ //
+ // Install the MNP Service Binding Protocol
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &MnpServiceHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &MnpServiceData->ServiceBinding,
+ NULL
+ );
+
+Exit:
+ if (EFI_ERROR (Status)) {
+ MnpDestroyServiceData (MnpServiceData);
+ MnpServiceData = NULL;
+ }
+
+ return MnpServiceData;
+}
+
+/**
+ Destroy the MNP service context data.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS The mnp service context is destroyed.
+ @retval Others Errors as indicated.
+
+**/
+EFI_STATUS
+MnpDestroyServiceData (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Uninstall the MNP Service Binding Protocol
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ MnpServiceData->ServiceHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &MnpServiceData->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (MnpServiceData->VlanId != 0) {
+ //
+ // Close VlanConfig Protocol opened by VLAN child handle
+ //
+ Status = gBS->CloseProtocol (
+ MnpServiceData->MnpDeviceData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ MnpServiceData->MnpDeviceData->ImageHandle,
+ MnpServiceData->ServiceHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Uninstall Device Path Protocol to destroy the VLAN child handle
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ MnpServiceData->ServiceHandle,
+ &gEfiDevicePathProtocolGuid,
+ MnpServiceData->DevicePath,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (MnpServiceData->DevicePath != NULL) {
+ FreePool (MnpServiceData->DevicePath);
+ }
+ }
+
+ //
+ // Remove from MnpDeviceData service list
+ //
+ RemoveEntryList (&MnpServiceData->Link);
+
+ FreePool (MnpServiceData);
+
+ return Status;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDestoryChildEntry (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+
+ ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;
+ Instance = CR (Entry, MNP_INSTANCE_DATA, InstEntry, MNP_INSTANCE_DATA_SIGNATURE);
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+}
+
+/**
+ Destroy all child of the MNP service data.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS All child are destroyed.
+ @retval Others Failed to destroy all child.
+
+**/
+EFI_STATUS
+MnpDestroyServiceChild (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ LIST_ENTRY *List;
+ EFI_STATUS Status;
+ UINTN ListLength;
+
+ List = &MnpServiceData->ChildrenList;
+
+ Status = NetDestroyLinkList (
+ List,
+ MnpDestoryChildEntry,
+ &MnpServiceData->ServiceBinding,
+ &ListLength
+ );
+ if (EFI_ERROR (Status) || ListLength != 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find the MNP Service Data for given VLAN ID.
+
+ @param[in] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] VlanId The VLAN ID.
+
+ @return A pointer to MNP_SERVICE_DATA or NULL if not found.
+
+**/
+MNP_SERVICE_DATA *
+MnpFindServiceData (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINT16 VlanId
+ )
+{
+ LIST_ENTRY *Entry;
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {
+ //
+ // Check VLAN ID of each Mnp Service Data
+ //
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+ if (MnpServiceData->VlanId == VlanId) {
+ return MnpServiceData;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Initialize the mnp instance context data.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in, out] Instance Pointer to the mnp instance context data
+ to initialize.
+
+**/
+VOID
+MnpInitializeInstanceData (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN OUT MNP_INSTANCE_DATA *Instance
+ )
+{
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+ ASSERT (Instance != NULL);
+
+ //
+ // Set the signature.
+ //
+ Instance->Signature = MNP_INSTANCE_DATA_SIGNATURE;
+
+ //
+ // Copy the MNP Protocol interfaces from the template.
+ //
+ CopyMem (&Instance->ManagedNetwork, &mMnpProtocolTemplate, sizeof (Instance->ManagedNetwork));
+
+ //
+ // Copy the default config data.
+ //
+ CopyMem (&Instance->ConfigData, &mMnpDefaultConfigData, sizeof (Instance->ConfigData));
+
+ //
+ // Initialize the lists.
+ //
+ InitializeListHead (&Instance->GroupCtrlBlkList);
+ InitializeListHead (&Instance->RcvdPacketQueue);
+ InitializeListHead (&Instance->RxDeliveredPacketQueue);
+
+ //
+ // Initialize the RxToken Map.
+ //
+ NetMapInit (&Instance->RxTokenMap);
+
+ //
+ // Save the MnpServiceData info.
+ //
+ Instance->MnpServiceData = MnpServiceData;
+}
+
+
+/**
+ Check whether the token specified by Arg matches the token in Item.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's a pointer to the token to
+ check.
+
+ @retval EFI_SUCCESS The token specified by Arg is different from the
+ token in Item.
+ @retval EFI_ACCESS_DENIED The token specified by Arg is the same as that in
+ Item.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpTokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Arg;
+ TokenInItem = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ //
+ // The token is the same either the two tokens equals or the Events in
+ // the two tokens are the same.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Cancel the token specified by Arg if it matches the token in Item.
+
+ @param[in, out] Map Pointer to the NET_MAP.
+ @param[in, out] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's a pointer to the
+ token to cancel.
+
+ @retval EFI_SUCCESS The Arg is NULL, and the token in Item is cancelled,
+ or the Arg isn't NULL, and the token in Item is
+ different from the Arg.
+ @retval EFI_ABORTED The Arg isn't NULL, the token in Item mathces the
+ Arg, and the token is cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpCancelTokens (
+ IN OUT NET_MAP *Map,
+ IN OUT NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TokenToCancel;
+
+ if ((Arg != NULL) && (Item->Key != Arg)) {
+ //
+ // The token in Item is not the token specified by Arg.
+ //
+ return EFI_SUCCESS;
+ }
+
+ TokenToCancel = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Item->Key;
+
+ //
+ // Remove the item from the map.
+ //
+ NetMapRemoveItem (Map, Item, NULL);
+
+ //
+ // Cancel this token with status set to EFI_ABORTED.
+ //
+ TokenToCancel->Status = EFI_ABORTED;
+ gBS->SignalEvent (TokenToCancel->Event);
+
+ if (Arg != NULL) {
+ //
+ // Only abort the token specified by Arg if Arg isn't NULL.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start and initialize the simple network.
+
+ @param[in] Snp Pointer to the simple network protocol.
+
+ @retval EFI_SUCCESS The simple network protocol is started.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpStartSnp (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Snp != NULL);
+
+ //
+ // Start the simple network.
+ //
+ Status = Snp->Start (Snp);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Initialize the simple network.
+ //
+ Status = Snp->Initialize (Snp, 0, 0);
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop the simple network.
+
+ @param[in] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+
+ @retval EFI_SUCCESS The simple network is stopped.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpStopSnp (
+ IN MNP_DEVICE_DATA *MnpDeviceData
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ Snp = MnpDeviceData->Snp;
+ ASSERT (Snp != NULL);
+
+ //
+ // Recycle all the transmit buffer from SNP.
+ //
+ Status = MnpRecycleTxBuf (MnpDeviceData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Shut down the simple network.
+ //
+ Status = Snp->Shutdown (Snp);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Stop the simple network.
+ //
+ Status = Snp->Stop (Snp);
+ }
+
+ return Status;
+}
+
+
+/**
+ Start the managed network, this function is called when one instance is configured
+ or reconfigured.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+ @param[in] IsConfigUpdate The instance is reconfigured or it's the first
+ time the instanced is configured.
+ @param[in] EnableSystemPoll Enable the system polling or not.
+
+ @retval EFI_SUCCESS The managed network is started and some
+ configuration is updated.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpStart (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData,
+ IN BOOLEAN IsConfigUpdate,
+ IN BOOLEAN EnableSystemPoll
+ )
+{
+ EFI_STATUS Status;
+ EFI_TIMER_DELAY TimerOpType;
+ MNP_DEVICE_DATA *MnpDeviceData;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+
+ if (!IsConfigUpdate) {
+ //
+ // If it's not a configuration update, increase the configured children number.
+ //
+ MnpDeviceData->ConfiguredChildrenNumber++;
+
+ if (MnpDeviceData->ConfiguredChildrenNumber == 1) {
+ //
+ // It's the first configured child, start the simple network.
+ //
+ Status = MnpStartSnp (MnpDeviceData->Snp);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpStart: MnpStartSnp failed, %r.\n", Status));
+
+ goto ErrorExit;
+ }
+
+ //
+ // Start the timeout timer.
+ //
+ Status = gBS->SetTimer (
+ MnpDeviceData->TimeoutCheckTimer,
+ TimerPeriodic,
+ MNP_TIMEOUT_CHECK_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpStart, gBS->SetTimer for TimeoutCheckTimer %r.\n",
+ Status)
+ );
+
+ goto ErrorExit;
+ }
+
+ //
+ // Start the media detection timer.
+ //
+ Status = gBS->SetTimer (
+ MnpDeviceData->MediaDetectTimer,
+ TimerPeriodic,
+ MNP_MEDIA_DETECT_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpStart, gBS->SetTimer for MediaDetectTimer %r.\n",
+ Status)
+ );
+
+ goto ErrorExit;
+ }
+ }
+ }
+
+ if (MnpDeviceData->EnableSystemPoll ^ EnableSystemPoll) {
+ //
+ // The EnableSystemPoll differs with the current state, disable or enable
+ // the system poll.
+ //
+ TimerOpType = EnableSystemPoll ? TimerPeriodic : TimerCancel;
+
+ Status = gBS->SetTimer (MnpDeviceData->PollTimer, TimerOpType, MNP_SYS_POLL_INTERVAL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpStart: gBS->SetTimer for PollTimer failed, %r.\n", Status));
+
+ goto ErrorExit;
+ }
+
+ MnpDeviceData->EnableSystemPoll = EnableSystemPoll;
+ }
+
+ //
+ // Change the receive filters if need.
+ //
+ Status = MnpConfigReceiveFilters (MnpDeviceData);
+
+ErrorExit:
+ return Status;
+}
+
+
+/**
+ Stop the managed network.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS The managed network is stopped.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpStop (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ EFI_STATUS Status;
+ MNP_DEVICE_DATA *MnpDeviceData;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+ ASSERT (MnpDeviceData->ConfiguredChildrenNumber > 0);
+
+ //
+ // Configure the receive filters.
+ //
+ MnpConfigReceiveFilters (MnpDeviceData);
+
+ //
+ // Decrease the children number.
+ //
+ MnpDeviceData->ConfiguredChildrenNumber--;
+
+ if (MnpDeviceData->ConfiguredChildrenNumber > 0) {
+ //
+ // If there are other configured chilren, return and keep the timers and
+ // simple network unchanged.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // No configured children now.
+ //
+ if (MnpDeviceData->EnableSystemPoll) {
+ //
+ // The system poll in on, cancel the poll timer.
+ //
+ Status = gBS->SetTimer (MnpDeviceData->PollTimer, TimerCancel, 0);
+ MnpDeviceData->EnableSystemPoll = FALSE;
+ }
+
+ //
+ // Cancel the timeout timer.
+ //
+ Status = gBS->SetTimer (MnpDeviceData->TimeoutCheckTimer, TimerCancel, 0);
+
+ //
+ // Cancel the media detect timer.
+ //
+ Status = gBS->SetTimer (MnpDeviceData->MediaDetectTimer, TimerCancel, 0);
+
+ //
+ // Stop the simple network.
+ //
+ Status = MnpStopSnp (MnpDeviceData);
+ return Status;
+}
+
+
+/**
+ Flush the instance's received data.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+
+**/
+VOID
+MnpFlushRcvdDataQueue (
+ IN OUT MNP_INSTANCE_DATA *Instance
+ )
+{
+ EFI_TPL OldTpl;
+ MNP_RXDATA_WRAP *RxDataWrap;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+ while (!IsListEmpty (&Instance->RcvdPacketQueue)) {
+ //
+ // Remove all the Wraps.
+ //
+ RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry);
+
+ //
+ // Recycle the RxDataWrap.
+ //
+ MnpRecycleRxData (NULL, (VOID *) RxDataWrap);
+ Instance->RcvdPacketQueueSize--;
+ }
+
+ ASSERT (Instance->RcvdPacketQueueSize == 0);
+
+ gBS->RestoreTPL (OldTpl);
+}
+
+
+/**
+ Configure the Instance using ConfigData.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+ @param[in] ConfigData Pointer to the configuration data used to configure
+ the isntance.
+
+ @retval EFI_SUCCESS The Instance is configured.
+ @retval EFI_UNSUPPORTED EnableReceiveTimestamps is on and the
+ implementation doesn't support it.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpConfigureInstance (
+ IN OUT MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *OldConfigData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *NewConfigData;
+ BOOLEAN IsConfigUpdate;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ if ((ConfigData != NULL) && ConfigData->EnableReceiveTimestamps) {
+ //
+ // Don't support timestamp.
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = EFI_SUCCESS;
+
+ MnpServiceData = Instance->MnpServiceData;
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ IsConfigUpdate = (BOOLEAN) ((Instance->Configured) && (ConfigData != NULL));
+
+ OldConfigData = &Instance->ConfigData;
+ NewConfigData = ConfigData;
+ if (NewConfigData == NULL) {
+ //
+ // Restore back the default config data if a reset of this instance
+ // is required.
+ //
+ NewConfigData = &mMnpDefaultConfigData;
+ }
+
+ //
+ // Reset the instance's receive filter.
+ //
+ Instance->ReceiveFilter = 0;
+
+ //
+ // Clear the receive counters according to the old ConfigData.
+ //
+ if (OldConfigData->EnableUnicastReceive) {
+ MnpDeviceData->UnicastCount--;
+ }
+
+ if (OldConfigData->EnableMulticastReceive) {
+ MnpDeviceData->MulticastCount--;
+ }
+
+ if (OldConfigData->EnableBroadcastReceive) {
+ MnpDeviceData->BroadcastCount--;
+ }
+
+ if (OldConfigData->EnablePromiscuousReceive) {
+ MnpDeviceData->PromiscuousCount--;
+ }
+
+ //
+ // Set the receive filter counters and the receive filter of the
+ // instance according to the new ConfigData.
+ //
+ if (NewConfigData->EnableUnicastReceive) {
+ MnpDeviceData->UnicastCount++;
+ Instance->ReceiveFilter |= MNP_RECEIVE_UNICAST;
+ }
+
+ if (NewConfigData->EnableMulticastReceive) {
+ MnpDeviceData->MulticastCount++;
+ }
+
+ if (NewConfigData->EnableBroadcastReceive) {
+ MnpDeviceData->BroadcastCount++;
+ Instance->ReceiveFilter |= MNP_RECEIVE_BROADCAST;
+ }
+
+ if (NewConfigData->EnablePromiscuousReceive) {
+ MnpDeviceData->PromiscuousCount++;
+ }
+
+ if (OldConfigData->FlushQueuesOnReset) {
+ MnpFlushRcvdDataQueue (Instance);
+ }
+
+ if (ConfigData == NULL) {
+ Instance->ManagedNetwork.Cancel (&Instance->ManagedNetwork, NULL);
+ }
+
+ if (!NewConfigData->EnableMulticastReceive) {
+ MnpGroupOp (Instance, FALSE, NULL, NULL);
+ }
+
+ //
+ // Save the new configuration data.
+ //
+ CopyMem (OldConfigData, NewConfigData, sizeof (*OldConfigData));
+
+ Instance->Configured = (BOOLEAN) (ConfigData != NULL);
+ if (Instance->Configured) {
+ //
+ // The instance is configured, start the Mnp.
+ //
+ Status = MnpStart (
+ MnpServiceData,
+ IsConfigUpdate,
+ (BOOLEAN) !NewConfigData->DisableBackgroundPolling
+ );
+ } else {
+ //
+ // The instance is changed to the unconfigured state, stop the Mnp.
+ //
+ Status = MnpStop (MnpServiceData);
+ }
+
+ return Status;
+}
+
+/**
+ Configure the Snp receive filters according to the instances' receive filter
+ settings.
+
+ @param[in] MnpDeviceData Pointer to the mnp device context data.
+
+ @retval EFI_SUCCESS The receive filters is configured.
+ @retval EFI_OUT_OF_RESOURCES The receive filters can't be configured due
+ to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpConfigReceiveFilters (
+ IN MNP_DEVICE_DATA *MnpDeviceData
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_MAC_ADDRESS *MCastFilter;
+ UINT32 MCastFilterCnt;
+ UINT32 EnableFilterBits;
+ UINT32 DisableFilterBits;
+ BOOLEAN ResetMCastFilters;
+ LIST_ENTRY *Entry;
+ UINT32 Index;
+ MNP_GROUP_ADDRESS *GroupAddress;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ Snp = MnpDeviceData->Snp;
+
+ //
+ // Initialize the enable filter and disable filter.
+ //
+ EnableFilterBits = 0;
+ DisableFilterBits = Snp->Mode->ReceiveFilterMask;
+
+ if (MnpDeviceData->UnicastCount != 0) {
+ //
+ // Enable unicast if any instance wants to receive unicast.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+ }
+
+ if (MnpDeviceData->BroadcastCount != 0) {
+ //
+ // Enable broadcast if any instance wants to receive broadcast.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+ }
+
+ MCastFilter = NULL;
+ MCastFilterCnt = 0;
+ ResetMCastFilters = TRUE;
+
+ if ((MnpDeviceData->MulticastCount != 0) && (MnpDeviceData->GroupAddressCount != 0)) {
+ //
+ // There are instances configured to receive multicast and already some group
+ // addresses are joined.
+ //
+
+ ResetMCastFilters = FALSE;
+
+ if (MnpDeviceData->GroupAddressCount <= Snp->Mode->MaxMCastFilterCount) {
+ //
+ // The joind group address is less than simple network's maximum count.
+ // Just configure the snp to do the multicast filtering.
+ //
+
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+
+ //
+ // Allocate pool for the mulicast addresses.
+ //
+ MCastFilterCnt = MnpDeviceData->GroupAddressCount;
+ MCastFilter = AllocatePool (sizeof (EFI_MAC_ADDRESS) * MCastFilterCnt);
+ if (MCastFilter == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpConfigReceiveFilters: Failed to allocate memory resource for MCastFilter.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill the multicast HW address buffer.
+ //
+ Index = 0;
+ NET_LIST_FOR_EACH (Entry, &MnpDeviceData->GroupAddressList) {
+
+ GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);
+ CopyMem (MCastFilter + Index, &GroupAddress->Address, sizeof (*(MCastFilter + Index)));
+ Index++;
+
+ ASSERT (Index <= MCastFilterCnt);
+ }
+ } else {
+ //
+ // The maximum multicast is reached, set the filter to be promiscuous
+ // multicast.
+ //
+
+ if ((Snp->Mode->ReceiveFilterMask & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+ } else {
+ //
+ // Either MULTICAST or PROMISCUOUS_MULTICAST is not supported by Snp,
+ // set the NIC to be promiscuous although this will tremendously degrade
+ // the performance.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ }
+ }
+ }
+
+ if (MnpDeviceData->PromiscuousCount != 0) {
+ //
+ // Enable promiscuous if any instance wants to receive promiscuous.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ }
+
+ //
+ // Set the disable filter.
+ //
+ DisableFilterBits ^= EnableFilterBits;
+
+ //
+ // Configure the receive filters of SNP.
+ //
+ Status = Snp->ReceiveFilters (
+ Snp,
+ EnableFilterBits,
+ DisableFilterBits,
+ ResetMCastFilters,
+ MCastFilterCnt,
+ MCastFilter
+ );
+ DEBUG_CODE (
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpConfigReceiveFilters: Snp->ReceiveFilters failed, %r.\n",
+ Status)
+ );
+ }
+ );
+
+ if (MCastFilter != NULL) {
+ //
+ // Free the buffer used to hold the group addresses.
+ //
+ FreePool (MCastFilter);
+ }
+
+ return Status;
+}
+
+
+/**
+ Add a group address control block which controls the MacAddress for
+ this instance.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+ @param[in, out] CtrlBlk Pointer to the group address control block.
+ @param[in, out] GroupAddress Pointer to the group adress.
+ @param[in] MacAddress Pointer to the mac address.
+ @param[in] HwAddressSize The hardware address size.
+
+ @retval EFI_SUCCESS The group address control block is added.
+ @retval EFI_OUT_OF_RESOURCES Failed due to lack of memory resources.
+
+**/
+EFI_STATUS
+MnpGroupOpAddCtrlBlk (
+ IN OUT MNP_INSTANCE_DATA *Instance,
+ IN OUT MNP_GROUP_CONTROL_BLOCK *CtrlBlk,
+ IN OUT MNP_GROUP_ADDRESS *GroupAddress OPTIONAL,
+ IN EFI_MAC_ADDRESS *MacAddress,
+ IN UINT32 HwAddressSize
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ MnpDeviceData = Instance->MnpServiceData->MnpDeviceData;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ if (GroupAddress == NULL) {
+ ASSERT (MacAddress != NULL);
+
+ //
+ // Allocate a new GroupAddress to be added into MNP's GroupAddressList.
+ //
+ GroupAddress = AllocatePool (sizeof (MNP_GROUP_ADDRESS));
+ if (GroupAddress == NULL) {
+
+ DEBUG ((EFI_D_ERROR, "MnpGroupOpFormCtrlBlk: Failed to allocate memory resource.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (&GroupAddress->Address, MacAddress, sizeof (GroupAddress->Address));
+ GroupAddress->RefCnt = 0;
+ InsertTailList (
+ &MnpDeviceData->GroupAddressList,
+ &GroupAddress->AddrEntry
+ );
+ MnpDeviceData->GroupAddressCount++;
+ }
+
+ //
+ // Increase the RefCnt.
+ //
+ GroupAddress->RefCnt++;
+
+ //
+ // Add the CtrlBlk into the instance's GroupCtrlBlkList.
+ //
+ CtrlBlk->GroupAddress = GroupAddress;
+ InsertTailList (&Instance->GroupCtrlBlkList, &CtrlBlk->CtrlBlkEntry);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Delete a group control block from the instance. If the controlled group address's
+ reference count reaches zero, the group address is removed too.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] CtrlBlk Pointer to the group control block to delete.
+
+ @return The group address controlled by the control block is no longer used or not.
+
+**/
+BOOLEAN
+MnpGroupOpDelCtrlBlk (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_GROUP_ADDRESS *GroupAddress;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ MnpDeviceData = Instance->MnpServiceData->MnpDeviceData;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ //
+ // Remove and free the CtrlBlk.
+ //
+ GroupAddress = CtrlBlk->GroupAddress;
+ RemoveEntryList (&CtrlBlk->CtrlBlkEntry);
+ FreePool (CtrlBlk);
+
+ ASSERT (GroupAddress->RefCnt > 0);
+
+ //
+ // Count down the RefCnt.
+ //
+ GroupAddress->RefCnt--;
+
+ if (GroupAddress->RefCnt == 0) {
+ //
+ // Free this GroupAddress entry if no instance uses it.
+ //
+ MnpDeviceData->GroupAddressCount--;
+ RemoveEntryList (&GroupAddress->AddrEntry);
+ FreePool (GroupAddress);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Do the group operations for this instance.
+
+ @param[in, out] Instance Pointer to the instance context data.
+ @param[in] JoinFlag Set to TRUE to join a group. Set to TRUE to
+ leave a group/groups.
+ @param[in] MacAddress Pointer to the group address to join or leave.
+ @param[in] CtrlBlk Pointer to the group control block if JoinFlag
+ is FALSE.
+
+ @retval EFI_SUCCESS The group operation finished.
+ @retval EFI_OUT_OF_RESOURCES Failed due to lack of memory resources.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpGroupOp (
+ IN OUT MNP_INSTANCE_DATA *Instance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk OPTIONAL
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ MNP_GROUP_ADDRESS *GroupAddress;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ MNP_GROUP_CONTROL_BLOCK *NewCtrlBlk;
+ EFI_STATUS Status;
+ BOOLEAN AddressExist;
+ BOOLEAN NeedUpdate;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ MnpDeviceData = Instance->MnpServiceData->MnpDeviceData;
+ SnpMode = MnpDeviceData->Snp->Mode;
+
+ if (JoinFlag) {
+ //
+ // A new gropu address is to be added.
+ //
+ GroupAddress = NULL;
+ AddressExist = FALSE;
+
+ //
+ // Allocate memory for the control block.
+ //
+ NewCtrlBlk = AllocatePool (sizeof (MNP_GROUP_CONTROL_BLOCK));
+ if (NewCtrlBlk == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpGroupOp: Failed to allocate memory resource.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &MnpDeviceData->GroupAddressList) {
+ //
+ // Check whether the MacAddress is already joined by other instances.
+ //
+ GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);
+ if (CompareMem (MacAddress, &GroupAddress->Address, SnpMode->HwAddressSize) == 0) {
+ AddressExist = TRUE;
+ break;
+ }
+ }
+
+ if (!AddressExist) {
+ GroupAddress = NULL;
+ }
+
+ //
+ // Add the GroupAddress for this instance.
+ //
+ Status = MnpGroupOpAddCtrlBlk (
+ Instance,
+ NewCtrlBlk,
+ GroupAddress,
+ MacAddress,
+ SnpMode->HwAddressSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NeedUpdate = TRUE;
+ } else {
+ if (MacAddress != NULL) {
+ ASSERT (CtrlBlk != NULL);
+
+ //
+ // Leave the specific multicast mac address.
+ //
+ NeedUpdate = MnpGroupOpDelCtrlBlk (Instance, CtrlBlk);
+ } else {
+ //
+ // Leave all multicast mac addresses.
+ //
+ NeedUpdate = FALSE;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->GroupCtrlBlkList) {
+
+ NewCtrlBlk = NET_LIST_USER_STRUCT (
+ Entry,
+ MNP_GROUP_CONTROL_BLOCK,
+ CtrlBlkEntry
+ );
+ //
+ // Update is required if the group address left is no longer used
+ // by other instances.
+ //
+ NeedUpdate = MnpGroupOpDelCtrlBlk (Instance, NewCtrlBlk);
+ }
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ if (NeedUpdate) {
+ //
+ // Reconfigure the receive filters if necessary.
+ //
+ Status = MnpConfigReceiveFilters (MnpDeviceData);
+ }
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
new file mode 100644
index 0000000000..5718e4df24
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
@@ -0,0 +1,690 @@
+/** @file
+ Implementation of driver entry point and driver binding protocol.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MnpDriver.h"
+#include "MnpImpl.h"
+#include "MnpVlan.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding = {
+ MnpDriverBindingSupported,
+ MnpDriverBindingStart,
+ MnpDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDestroyServiceDataEntry (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+ return MnpDestroyServiceData (MnpServiceData);
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDestroyServiceChildEntry (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+ return MnpDestroyServiceChild (MnpServiceData);
+}
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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 Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ //
+ // Test to open the Simple Network protocol BY_DRIVER.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Close the openned SNP protocol.
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make drivers as small
+ as possible, there are a few calling restrictions for this service.
+ ConnectController() must follow these calling restrictions. If any other
+ agent wishes to call Start() it must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Mnp Service Data.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ LIST_ENTRY *Entry;
+ VLAN_TCI *VlanVariable;
+ UINTN NumberOfVlan;
+ UINTN Index;
+
+ VlanVariable = NULL;
+
+ //
+ // Initialize the Mnp Device Data
+ //
+ MnpDeviceData = AllocateZeroPool (sizeof (MNP_DEVICE_DATA));
+ if (MnpDeviceData == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpDriverBindingStart(): Failed to allocate the Mnp Device Data.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = MnpInitializeDeviceData (MnpDeviceData, This->DriverBindingHandle, ControllerHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpDriverBindingStart: MnpInitializeDeviceData failed, %r.\n", Status));
+
+ FreePool (MnpDeviceData);
+ return Status;
+ }
+
+ //
+ // Check whether NIC driver has already produced VlanConfig protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // NIC hardware already implement VLAN,
+ // no need to provide software VLAN implementation in MNP driver
+ //
+ MnpDeviceData->NumberOfVlan = 0;
+ ZeroMem (&MnpDeviceData->VlanConfig, sizeof (EFI_VLAN_CONFIG_PROTOCOL));
+ MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);
+ Status = (MnpServiceData != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Install VLAN Config Protocol
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ &MnpDeviceData->VlanConfig,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get current VLAN configuration from EFI Variable
+ //
+ NumberOfVlan = 0;
+ Status = MnpGetVlanVariable (MnpDeviceData, &NumberOfVlan, &VlanVariable);
+ if (EFI_ERROR (Status)) {
+ //
+ // No VLAN is set, create a default MNP service data for untagged frame
+ //
+ MnpDeviceData->NumberOfVlan = 0;
+ MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);
+ Status = (MnpServiceData != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Create MNP service data for each VLAN
+ //
+ MnpDeviceData->NumberOfVlan = NumberOfVlan;
+ for (Index = 0; Index < NumberOfVlan; Index++) {
+ MnpServiceData = MnpCreateServiceData (
+ MnpDeviceData,
+ VlanVariable[Index].Bits.Vid,
+ (UINT8) VlanVariable[Index].Bits.Priority
+ );
+
+ if (MnpServiceData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+
+ goto Exit;
+ }
+ }
+
+Exit:
+ if (VlanVariable != NULL) {
+ FreePool (VlanVariable);
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Destroy all MNP service data
+ //
+ while (!IsListEmpty (&MnpDeviceData->ServiceList)) {
+ Entry = GetFirstNode (&MnpDeviceData->ServiceList);
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+ MnpDestroyServiceData (MnpServiceData);
+ }
+
+ //
+ // Uninstall the VLAN Config Protocol if any
+ //
+ if (MnpDeviceData->VlanConfig.Set != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ MnpDeviceData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ &MnpDeviceData->VlanConfig,
+ NULL
+ );
+ }
+
+ //
+ // Destroy Mnp Device Data
+ //
+ MnpDestroyDeviceData (MnpDeviceData, This->DriverBindingHandle);
+ FreePool (MnpDeviceData);
+ }
+
+ return Status;
+}
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to make drivers as
+ small as possible, there are a few calling restrictions for this service.
+ DisconnectController() must follow these calling restrictions. If any other
+ agent wishes to call Stop() it must also follow these calling restrictions.
+
+ @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 EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_SERVICE_DATA *MnpServiceData;
+ LIST_ENTRY *List;
+ UINTN ListLength;
+
+ //
+ // Try to retrieve MNP service binding protocol from the ControllerHandle
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Retrieve VLAN Config Protocol from the ControllerHandle
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **) &VlanConfig,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpDriverBindingStop: try to stop unknown Controller.\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (VlanConfig);
+ } else {
+ MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (ServiceBinding);
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+ }
+
+ if (NumberOfChildren == 0) {
+ //
+ // Destroy all MNP service data
+ //
+ List = &MnpDeviceData->ServiceList;
+ Status = NetDestroyLinkList (
+ List,
+ MnpDestroyServiceDataEntry,
+ NULL,
+ &ListLength
+ );
+ if (EFI_ERROR (Status) || ListLength !=0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Uninstall the VLAN Config Protocol if any
+ //
+ if (MnpDeviceData->VlanConfig.Set != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ MnpDeviceData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ &MnpDeviceData->VlanConfig,
+ NULL
+ );
+ }
+
+ //
+ // Destroy Mnp Device Data
+ //
+ MnpDestroyDeviceData (MnpDeviceData, This->DriverBindingHandle);
+ FreePool (MnpDeviceData);
+
+ if (gMnpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gMnpControllerNameTable);
+ gMnpControllerNameTable = NULL;
+ }
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stop all MNP child
+ //
+ List = &MnpDeviceData->ServiceList;
+ Status = NetDestroyLinkList (
+ List,
+ MnpDestroyServiceChildEntry,
+ NULL,
+ &ListLength
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param[in] This Protocol instance pointer.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If
+ it is not NULL, then the I/O services are added
+ to the existing child handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ create the child.
+ @retval Others The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_INSTANCE_DATA *Instance;
+ VOID *MnpSb;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate buffer for the new instance.
+ //
+ Instance = AllocateZeroPool (sizeof (MNP_INSTANCE_DATA));
+ if (Instance == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpServiceBindingCreateChild: Faild to allocate memory for the new instance.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Init the instance data.
+ //
+ MnpInitializeInstanceData (MnpServiceData, Instance);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ &Instance->ManagedNetwork,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpServiceBindingCreateChild: Failed to install the MNP protocol, %r.\n",
+ Status)
+ );
+
+ goto ErrorExit;
+ }
+
+ //
+ // Save the instance's childhandle.
+ //
+ Instance->Handle = *ChildHandle;
+
+ Status = gBS->OpenProtocol (
+ MnpServiceData->ServiceHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &MnpSb,
+ gMnpDriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Add the child instance into ChildrenList.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&MnpServiceData->ChildrenList, &Instance->InstEntry);
+ MnpServiceData->ChildrenNumber++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ErrorExit:
+
+ if (EFI_ERROR (Status)) {
+
+ if (Instance->Handle != NULL) {
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiManagedNetworkProtocolGuid,
+ &Instance->ManagedNetwork,
+ NULL
+ );
+ }
+
+ FreePool (Instance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a
+ protocol that was installed by CreateChild() from ChildHandle. If the removed
+ protocol is the last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL
+ instance.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that
+ is being removed.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the
+ ChildHandle because its services are being
+ used.
+ @retval Others The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ EFI_MANAGED_NETWORK_PROTOCOL *ManagedNetwork;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Try to retrieve ManagedNetwork Protocol from ChildHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &ManagedNetwork,
+ gMnpDriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (ManagedNetwork);
+
+ //
+ // MnpServiceBindingDestroyChild may be called twice: first called by
+ // MnpServiceBindingStop, second called by uninstalling the MNP protocol
+ // in this ChildHandle. Use destroyed to make sure the resource clean code
+ // will only excecute once.
+ //
+ if (Instance->Destroyed) {
+ return EFI_SUCCESS;
+ }
+
+ Instance->Destroyed = TRUE;
+
+ //
+ // Close the Simple Network protocol.
+ //
+ gBS->CloseProtocol (
+ MnpServiceData->ServiceHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ MnpServiceData->MnpDeviceData->ImageHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the ManagedNetwork protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ &Instance->ManagedNetwork,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "MnpServiceBindingDestroyChild: Failed to uninstall the ManagedNetwork protocol, %r.\n",
+ Status)
+ );
+
+ Instance->Destroyed = FALSE;
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Reset the configuration.
+ //
+ ManagedNetwork->Configure (ManagedNetwork, NULL);
+
+ //
+ // Try to flush the RcvdPacketQueue.
+ //
+ MnpFlushRcvdDataQueue (Instance);
+
+ //
+ // Clean the RxTokenMap.
+ //
+ NetMapClean (&Instance->RxTokenMap);
+
+ //
+ // Remove this instance from the ChildrenList.
+ //
+ RemoveEntryList (&Instance->InstEntry);
+ MnpServiceData->ChildrenNumber--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+
+ return Status;
+}
+
+/**
+ The entry point for Mnp driver which installs the driver binding and component
+ name protocol on its ImageHandle.
+
+ @param[in] ImageHandle The image handle of the driver.
+ @param[in] SystemTable The system table.
+
+ @retval EFI_SUCCES The driver binding and component name protocols are
+ successfully installed.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gMnpDriverBinding,
+ ImageHandle,
+ &gMnpComponentName,
+ &gMnpComponentName2
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
new file mode 100644
index 0000000000..f83ff9fa05
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
@@ -0,0 +1,275 @@
+/** @file
+ Declaration of strctures and functions for MnpDxe driver.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _MNP_DRIVER_H_
+#define _MNP_DRIVER_H_
+
+#include <Uefi.h>
+
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/VlanConfig.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/DpcLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+
+#include "ComponentName.h"
+
+#define MNP_DEVICE_DATA_SIGNATURE SIGNATURE_32 ('M', 'n', 'p', 'D')
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding;
+
+typedef struct {
+ UINT32 Signature;
+
+ EFI_HANDLE ControllerHandle;
+ EFI_HANDLE ImageHandle;
+
+ EFI_VLAN_CONFIG_PROTOCOL VlanConfig;
+ UINTN NumberOfVlan;
+ CHAR16 *MacString;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ //
+ // List of MNP_SERVICE_DATA
+ //
+ LIST_ENTRY ServiceList;
+ //
+ // Number of configured MNP Service Binding child
+ //
+ UINTN ConfiguredChildrenNumber;
+
+ LIST_ENTRY GroupAddressList;
+ UINT32 GroupAddressCount;
+
+ LIST_ENTRY FreeTxBufList;
+ LIST_ENTRY AllTxBufList;
+ UINT32 TxBufCount;
+
+ NET_BUF_QUEUE FreeNbufQue;
+ INTN NbufCnt;
+
+ EFI_EVENT PollTimer;
+ BOOLEAN EnableSystemPoll;
+
+ EFI_EVENT TimeoutCheckTimer;
+ EFI_EVENT MediaDetectTimer;
+
+ UINT32 UnicastCount;
+ UINT32 BroadcastCount;
+ UINT32 MulticastCount;
+ UINT32 PromiscuousCount;
+
+ //
+ // The size of the data buffer in the MNP_PACKET_BUFFER used to
+ // store a packet.
+ //
+ UINT32 BufferLength;
+ UINT32 PaddingSize;
+ NET_BUF *RxNbufCache;
+} MNP_DEVICE_DATA;
+
+#define MNP_DEVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ MNP_DEVICE_DATA, \
+ VlanConfig, \
+ MNP_DEVICE_DATA_SIGNATURE \
+ )
+
+#define MNP_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('M', 'n', 'p', 'S')
+
+typedef struct {
+ UINT32 Signature;
+
+ LIST_ENTRY Link;
+
+ MNP_DEVICE_DATA *MnpDeviceData;
+ EFI_HANDLE ServiceHandle;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ LIST_ENTRY ChildrenList;
+ UINTN ChildrenNumber;
+
+ UINT32 Mtu;
+
+ UINT16 VlanId;
+ UINT8 Priority;
+} MNP_SERVICE_DATA;
+
+
+#define MNP_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ MNP_SERVICE_DATA, \
+ ServiceBinding, \
+ MNP_SERVICE_DATA_SIGNATURE \
+ )
+
+#define MNP_SERVICE_DATA_FROM_LINK(a) \
+ CR ( \
+ (a), \
+ MNP_SERVICE_DATA, \
+ Link, \
+ MNP_SERVICE_DATA_SIGNATURE \
+ )
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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 Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make drivers as small
+ as possible, there are a few calling restrictions for this service.
+ ConnectController() must follow these calling restrictions. If any other
+ agent wishes to call Start() it must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Mnp Service Data.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to make drivers as
+ small as possible, there are a few calling restrictions for this service.
+ DisconnectController() must follow these calling restrictions. If any other
+ agent wishes to call Stop() it must also follow these calling restrictions.
+
+ @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 EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param[in] This Protocol instance pointer.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If
+ it is not NULL, then the I/O services are added
+ to the existing child handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ create the child.
+ @retval Others The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a
+ protocol that was installed by CreateChild() from ChildHandle. If the removed
+ protocol is the last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL
+ instance.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that
+ is being removed.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the
+ ChildHandle because its services are being
+ used.
+ @retval Others The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
new file mode 100644
index 0000000000..07e62f5ba2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
@@ -0,0 +1,74 @@
+## @file
+# This module produces EFI MNP Protocol, EFI MNP Servie Binding Protocol and EFI VLAN Protocol.
+#
+# This module produces EFI Managed Network Protocol upon EFI Simple Network Protocol,
+# to provide raw asynchronous network I/O services. It also produces EFI VLAN Protocol
+# to provide manageability interface for VLAN configuration.
+#
+# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = MnpDxe
+ MODULE_UNI_FILE = MnpDxe.uni
+ FILE_GUID = 025BBFC7-E6A9-4b8b-82AD-6815A1AEAF4A
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = MnpDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gMnpDriverBinding
+# COMPONENT_NAME = gMnpComponentName
+# COMPONENT_NAME2 = gMnpComponentName2
+#
+
+[Sources]
+ MnpMain.c
+ MnpIo.c
+ ComponentName.h
+ MnpDriver.h
+ ComponentName.c
+ MnpDriver.c
+ MnpConfig.c
+ MnpImpl.h
+ MnpVlan.h
+ MnpVlan.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ DpcLib
+
+[Protocols]
+ gEfiManagedNetworkServiceBindingProtocolGuid ## BY_START
+ gEfiSimpleNetworkProtocolGuid ## TO_START
+ gEfiManagedNetworkProtocolGuid ## BY_START
+ ## BY_START
+ ## UNDEFINED # variable
+ gEfiVlanConfigProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ MnpDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.uni b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.uni
new file mode 100644
index 0000000000..645cc1bf36
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.uni
@@ -0,0 +1,24 @@
+// /** @file
+// This module produces EFI MNP Protocol, EFI MNP Servie Binding Protocol and EFI VLAN Protocol.
+//
+// This module produces EFI Managed Network Protocol upon EFI Simple Network Protocol,
+// to provide raw asynchronous network I/O services. It also produces EFI VLAN Protocol
+// to provide manageability interface for VLAN configuration.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI MNP Protocol, EFI MNP Servie Binding Protocol and EFI VLAN Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI Managed Network Protocol upon EFI Simple Network Protocol to provide raw asynchronous network I/O services. It also produces EFI VLAN Protocol to provide manageability interface for VLAN configuration."
+
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni
new file mode 100644
index 0000000000..4cecf1f868
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// MnpDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"MNP DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h
new file mode 100644
index 0000000000..51391afe26
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h
@@ -0,0 +1,905 @@
+/** @file
+ Declaration of structures and functions of MnpDxe driver.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _MNP_IMPL_H_
+#define _MNP_IMPL_H_
+
+#include "MnpDriver.h"
+
+#define NET_ETHER_FCS_SIZE 4
+
+#define MNP_SYS_POLL_INTERVAL (10 * TICKS_PER_MS) // 10 milliseconds
+#define MNP_TIMEOUT_CHECK_INTERVAL (50 * TICKS_PER_MS) // 50 milliseconds
+#define MNP_MEDIA_DETECT_INTERVAL (500 * TICKS_PER_MS) // 500 milliseconds
+#define MNP_TX_TIMEOUT_TIME (500 * TICKS_PER_MS) // 500 milliseconds
+#define MNP_INIT_NET_BUFFER_NUM 512
+#define MNP_NET_BUFFER_INCREASEMENT 64
+#define MNP_MAX_NET_BUFFER_NUM 65536
+#define MNP_TX_BUFFER_INCREASEMENT 32 // Same as the recycling Q length for xmit_done in UNDI command.
+#define MNP_MAX_TX_BUFFER_NUM 65536
+
+#define MNP_MAX_RCVD_PACKET_QUE_SIZE 256
+
+#define MNP_RECEIVE_UNICAST 0x01
+#define MNP_RECEIVE_BROADCAST 0x02
+
+#define UNICAST_PACKET MNP_RECEIVE_UNICAST
+#define BROADCAST_PACKET MNP_RECEIVE_BROADCAST
+
+#define MNP_INSTANCE_DATA_SIGNATURE SIGNATURE_32 ('M', 'n', 'p', 'I')
+
+#define MNP_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ MNP_INSTANCE_DATA, \
+ ManagedNetwork, \
+ MNP_INSTANCE_DATA_SIGNATURE \
+ )
+
+typedef struct {
+ UINT32 Signature;
+
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ EFI_HANDLE Handle;
+
+ LIST_ENTRY InstEntry;
+
+ EFI_MANAGED_NETWORK_PROTOCOL ManagedNetwork;
+
+ BOOLEAN Configured;
+ BOOLEAN Destroyed;
+
+ LIST_ENTRY GroupCtrlBlkList;
+
+ NET_MAP RxTokenMap;
+
+ LIST_ENTRY RxDeliveredPacketQueue;
+ LIST_ENTRY RcvdPacketQueue;
+ UINTN RcvdPacketQueueSize;
+
+ EFI_MANAGED_NETWORK_CONFIG_DATA ConfigData;
+
+ UINT8 ReceiveFilter;
+} MNP_INSTANCE_DATA;
+
+typedef struct {
+ LIST_ENTRY AddrEntry;
+ EFI_MAC_ADDRESS Address;
+ INTN RefCnt;
+} MNP_GROUP_ADDRESS;
+
+typedef struct {
+ LIST_ENTRY CtrlBlkEntry;
+ MNP_GROUP_ADDRESS *GroupAddress;
+} MNP_GROUP_CONTROL_BLOCK;
+
+typedef struct {
+ LIST_ENTRY WrapEntry;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA RxData;
+ NET_BUF *Nbuf;
+ UINT64 TimeoutTick;
+} MNP_RXDATA_WRAP;
+
+#define MNP_TX_BUF_WRAP_SIGNATURE SIGNATURE_32 ('M', 'T', 'B', 'W')
+
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY WrapEntry; // Link to FreeTxBufList
+ LIST_ENTRY AllEntry; // Link to AllTxBufList
+ BOOLEAN InUse;
+ UINT8 TxBuf[1];
+} MNP_TX_BUF_WRAP;
+
+/**
+ Initialize the mnp device context data.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] ImageHandle The driver image handle.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+
+ @retval EFI_SUCCESS The mnp service context is initialized.
+ @retval EFI_UNSUPPORTED ControllerHandle does not support Simple Network Protocol.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpInitializeDeviceData (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+/**
+ Destroy the MNP device context data.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] ImageHandle The driver image handle.
+
+**/
+VOID
+MnpDestroyDeviceData (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN EFI_HANDLE ImageHandle
+ );
+
+/**
+ Create mnp service context data.
+
+ @param[in] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] VlanId The VLAN ID.
+ @param[in] Priority The VLAN priority. If VlanId is 0,
+ Priority is ignored.
+
+ @return A pointer to MNP_SERVICE_DATA or NULL if failed to create MNP service context.
+
+**/
+MNP_SERVICE_DATA *
+MnpCreateServiceData (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINT16 VlanId,
+ IN UINT8 Priority OPTIONAL
+ );
+
+/**
+ Initialize the mnp service context data.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+ @param[in] ImageHandle The driver image handle.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+
+ @retval EFI_SUCCESS The mnp service context is initialized.
+ @retval EFI_UNSUPPORTED ControllerHandle does not support Simple Network Protocol.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpInitializeServiceData (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+/**
+ Destroy the MNP service context data.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS The mnp service context is destroyed.
+ @retval Others Errors as indicated.
+
+**/
+EFI_STATUS
+MnpDestroyServiceData (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData
+ );
+
+/**
+ Destroy all child of the MNP service data.
+
+ @param[in, out] MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS All child are destroyed.
+ @retval Others Failed to destroy all child.
+
+**/
+EFI_STATUS
+MnpDestroyServiceChild (
+ IN OUT MNP_SERVICE_DATA *MnpServiceData
+ );
+
+/**
+ Find the MNP Service Data for given VLAN ID.
+
+ @param[in] MnpDeviceData Pointer to the mnp device context data.
+ @param[in] VlanId The VLAN ID.
+
+ @return A pointer to MNP_SERVICE_DATA or NULL if not found.
+
+**/
+MNP_SERVICE_DATA *
+MnpFindServiceData (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINT16 VlanId
+ );
+
+/**
+ Initialize the mnp instance context data.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in, out] Instance Pointer to the mnp instance context data
+ to initialize.
+
+**/
+VOID
+MnpInitializeInstanceData (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN OUT MNP_INSTANCE_DATA *Instance
+ );
+
+/**
+ Check whether the token specified by Arg matches the token in Item.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's a pointer to the token to
+ check.
+
+ @retval EFI_SUCCESS The token specified by Arg is different from the
+ token in Item.
+ @retval EFI_ACCESS_DENIED The token specified by Arg is the same as that in
+ Item.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpTokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ );
+
+/**
+ Cancel the token specified by Arg if it matches the token in Item.
+
+ @param[in, out] Map Pointer to the NET_MAP.
+ @param[in, out] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's a pointer to the
+ token to cancel.
+
+ @retval EFI_SUCCESS The Arg is NULL, and the token in Item is cancelled,
+ or the Arg isn't NULL, and the token in Item is
+ different from the Arg.
+ @retval EFI_ABORTED The Arg isn't NULL, the token in Item mathces the
+ Arg, and the token is cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpCancelTokens (
+ IN OUT NET_MAP *Map,
+ IN OUT NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ );
+
+/**
+ Flush the instance's received data.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+
+**/
+VOID
+MnpFlushRcvdDataQueue (
+ IN OUT MNP_INSTANCE_DATA *Instance
+ );
+
+/**
+ Configure the Instance using ConfigData.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+ @param[in] ConfigData Pointer to the configuration data used to configure
+ the isntance.
+
+ @retval EFI_SUCCESS The Instance is configured.
+ @retval EFI_UNSUPPORTED EnableReceiveTimestamps is on and the
+ implementation doesn't support it.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpConfigureInstance (
+ IN OUT MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData OPTIONAL
+ );
+
+/**
+ Do the group operations for this instance.
+
+ @param[in, out] Instance Pointer to the instance context data.
+ @param[in] JoinFlag Set to TRUE to join a group. Set to TRUE to
+ leave a group/groups.
+ @param[in] MacAddress Pointer to the group address to join or leave.
+ @param[in] CtrlBlk Pointer to the group control block if JoinFlag
+ is FALSE.
+
+ @retval EFI_SUCCESS The group operation finished.
+ @retval EFI_OUT_OF_RESOURCES Failed due to lack of memory resources.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+MnpGroupOp (
+ IN OUT MNP_INSTANCE_DATA *Instance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk OPTIONAL
+ );
+
+/**
+ Validates the Mnp transmit token.
+
+ @param[in] Instance Pointer to the Mnp instance context data.
+ @param[in] Token Pointer to the transmit token to check.
+
+ @return The Token is valid or not.
+
+**/
+BOOLEAN
+MnpIsValidTxToken (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Build the packet to transmit from the TxData passed in.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in] TxData Pointer to the transmit data containing the information
+ to build the packet.
+ @param[out] PktBuf Pointer to record the address of the packet.
+ @param[out] PktLen Pointer to a UINT32 variable used to record the packet's
+ length.
+
+ @retval EFI_SUCCESS TxPackage is built.
+ @retval EFI_OUT_OF_RESOURCES The deliver fails due to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpBuildTxPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData,
+ OUT UINT8 **PktBuf,
+ OUT UINT32 *PktLen
+ );
+
+/**
+ Synchronously send out the packet.
+
+ This functon places the packet buffer to SNP driver's tansmit queue. The packet
+ can be considered successfully sent out once SNP acccetp the packet, while the
+ packet buffer recycle is deferred for better performance.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in] Packet Pointer to the pakcet buffer.
+ @param[in] Length The length of the packet.
+ @param[in, out] Token Pointer to the token the packet generated from.
+
+ @retval EFI_SUCCESS The packet is sent out.
+ @retval EFI_TIMEOUT Time out occurs, the packet isn't sent.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurs.
+
+**/
+EFI_STATUS
+MnpSyncSendPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN UINT8 *Packet,
+ IN UINT32 Length,
+ IN OUT EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Try to deliver the received packet to the instance.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+
+ @retval EFI_SUCCESS The received packet is delivered, or there is no
+ packet to deliver, or there is no available receive
+ token.
+ @retval EFI_OUT_OF_RESOURCES The deliver fails due to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpInstanceDeliverPacket (
+ IN OUT MNP_INSTANCE_DATA *Instance
+ );
+
+/**
+ Recycle the RxData and other resources used to hold and deliver the received
+ packet.
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the Event.
+
+**/
+VOID
+EFIAPI
+MnpRecycleRxData (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Try to receive a packet and deliver it.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+
+ @retval EFI_SUCCESS add return value to function comment
+ @retval EFI_NOT_STARTED The simple network protocol is not started.
+ @retval EFI_NOT_READY No packet received.
+ @retval EFI_DEVICE_ERROR An unexpected error occurs.
+
+**/
+EFI_STATUS
+MnpReceivePacket (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ );
+
+/**
+ Allocate a free NET_BUF from MnpDeviceData->FreeNbufQue. If there is none
+ in the queue, first try to allocate some and add them into the queue, then
+ fetch the NET_BUF from the updated FreeNbufQue.
+
+ @param[in, out] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+
+ @return Pointer to the allocated free NET_BUF structure, if NULL the
+ operation is failed.
+
+**/
+NET_BUF *
+MnpAllocNbuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ );
+
+/**
+ Try to reclaim the Nbuf into the buffer pool.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in, out] Nbuf Pointer to the NET_BUF to free.
+
+**/
+VOID
+MnpFreeNbuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN OUT NET_BUF *Nbuf
+ );
+
+/**
+ Allocate a free TX buffer from MnpDeviceData->FreeTxBufList. If there is none
+ in the queue, first try to recycle some from SNP, then try to allocate some and add
+ them into the queue, then fetch the NET_BUF from the updated FreeTxBufList.
+
+ @param[in, out] MnpDeviceData Pointer to the MNP_DEVICE_DATA.
+
+ @return Pointer to the allocated free NET_BUF structure, if NULL the
+ operation is failed.
+
+**/
+UINT8 *
+MnpAllocTxBuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ );
+
+/**
+ Try to recycle all the transmitted buffer address from SNP.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+
+ @retval EFI_SUCCESS Successed to recyclethe transmitted buffer address.
+ @retval Others Failed to recyclethe transmitted buffer address.
+
+**/
+EFI_STATUS
+MnpRecycleTxBuf (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ );
+
+/**
+ Remove the received packets if timeout occurs.
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the event.
+
+**/
+VOID
+EFIAPI
+MnpCheckPacketTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Poll to update MediaPresent field in SNP ModeData by Snp.GetStatus().
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the event.
+
+**/
+VOID
+EFIAPI
+MnpCheckMediaStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Poll to receive the packets from Snp. This function is either called by upperlayer
+ protocols/applications or the system poll timer notify mechanism.
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the event.
+
+**/
+VOID
+EFIAPI
+MnpSystemPoll (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Returns the operational parameters for the current MNP child driver. May also
+ support returning the underlying SNP driver mode data.
+
+ The GetModeData() function is used to read the current mode data (operational
+ parameters) from the MNP or the underlying SNP.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[out] MnpConfigData Pointer to storage for MNP operational parameters. Type
+ EFI_MANAGED_NETWORK_CONFIG_DATA is defined in "Related
+ Definitions" below.
+ @param[out] SnpModeData Pointer to storage for SNP operational parameters. This
+ feature may be unsupported. Type EFI_SIMPLE_NETWORK_MODE
+ is defined in the EFI_SIMPLE_NETWORK_PROTOCOL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in this
+ MNP implementation.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured. The default values are returned in
+ MnpConfigData if it is not NULL.
+ @retval Others The mode data could not be read.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpGetModeData (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ Sets or clears the operational parameters for the MNP child driver.
+
+ The Configure() function is used to set, change, or reset the operational
+ parameters for the MNP child driver instance. Until the operational parameters
+ have been set, no network traffic can be sent or received by this MNP child
+ driver instance. Once the operational parameters have been reset, no more
+ traffic can be sent or received until the operational parameters have been set
+ again.
+ Each MNP child driver instance can be started and stopped independently of
+ each other by setting or resetting their receive filter settings with the
+ Configure() function.
+ After any successful call to Configure(), the MNP child driver instance is
+ started. The internal periodic timer (if supported) is enabled. Data can be
+ transmitted and may be received if the receive filters have also been enabled.
+ Note: If multiple MNP child driver instances will receive the same packet
+ because of overlapping receive filter settings, then the first MNP child
+ driver instance will receive the original packet and additional instances will
+ receive copies of the original packet.
+ Note: Warning: Receive filter settings that overlap will consume extra
+ processor and/or DMA resources and degrade system and network performance.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] MnpConfigData Pointer to configuration data that will be assigned
+ to the MNP child driver instance. If NULL, the MNP
+ child driver instance is reset to startup defaults
+ and all pending transmit and receive requests are
+ flushed. Type EFI_MANAGED_NETWORK_CONFIG_DATA is
+ defined in EFI_MANAGED_NETWORK_PROTOCOL.GetModeData().
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is
+ TRUE:
+ * This is NULL.
+ * MnpConfigData.ProtocolTypeFilter is not
+ valid.
+ The operational data for the MNP child driver
+ instance is unchanged.
+ @retval EFI_OUT_OF_RESOURCES Required system resources (usually memory)
+ could not be allocated.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in
+ this [MNP] implementation. The operational data
+ for the MNP child driver instance is unchanged.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error
+ occurred. The MNP child driver instance has
+ been reset to startup defaults.
+ @retval Others The MNP child driver instance has been reset to
+ startup defaults.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpConfigure (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL
+ );
+
+/**
+ Translates an IP multicast address to a hardware (MAC) multicast address. This
+ function may be unsupported in some MNP implementations.
+
+ The McastIpToMac() function translates an IP multicast address to a hardware
+ (MAC) multicast address. This function may be implemented by calling the
+ underlying EFI_SIMPLE_NETWORK. MCastIpToMac() function, which may also be
+ unsupported in some MNP implementations.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Ipv6Flag Set to TRUE to if IpAddress is an IPv6 multicast address.
+ Set to FALSE if IpAddress is an IPv4 multicast address.
+ @param[in] IpAddress Pointer to the multicast IP address (in network byte
+ order) to convert.
+ @param[out] MacAddress Pointer to the resulting multicast MAC address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One of the following conditions is TRUE:
+ * This is NULL.
+ * IpAddress is NULL.
+ * IpAddress is not a valid multicast IP
+ address.
+ * MacAddress is NULL.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in this
+ MNP implementation.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others The address could not be converted.
+**/
+EFI_STATUS
+EFIAPI
+MnpMcastIpToMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Ipv6Flag,
+ IN EFI_IP_ADDRESS *IpAddress,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ );
+
+/**
+ Enables and disables receive filters for multicast address. This function may
+ be unsupported in some MNP implementations.
+
+ The Groups() function only adds and removes multicast MAC addresses from the
+ filter list. The MNP driver does not transmit or process Internet Group
+ Management Protocol (IGMP) packets. If JoinFlag is FALSE and MacAddress is
+ NULL, then all joined groups are left.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join this multicast group.
+ Set to FALSE to leave this multicast group.
+ @param[in] MacAddress Pointer to the multicast MAC group (address) to join or
+ leave.
+
+ @retval EFI_SUCCESS The requested operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * This is NULL.
+ * JoinFlag is TRUE and MacAddress is NULL.
+ * MacAddress is not a valid multicast MAC
+ address.
+ * The MNP multicast group settings are
+ unchanged.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_ALREADY_STARTED The supplied multicast group is already joined.
+ @retval EFI_NOT_FOUND The supplied multicast group is not joined.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in this MNP
+ implementation.
+ @retval Others The requested operation could not be completed.
+ The MNP multicast group settings are unchanged.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpGroups (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL
+ );
+
+/**
+ Places asynchronous outgoing data packets into the transmit queue.
+
+ The Transmit() function places a completion token into the transmit packet
+ queue. This function is always asynchronous.
+ The caller must fill in the Token.Event and Token.TxData fields in the
+ completion token, and these fields cannot be NULL. When the transmit operation
+ completes, the MNP updates the Token.Status field and the Token.Event is
+ signaled.
+ Note: There may be a performance penalty if the packet needs to be
+ defragmented before it can be transmitted by the network device. Systems in
+ which performance is critical should review the requirements and features of
+ the underlying communications device and drivers.
+
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Token Pointer to a token associated with the transmit data
+ descriptor. Type EFI_MANAGED_NETWORK_COMPLETION_TOKEN
+ is defined in "Related Definitions" below.
+
+ @retval EFI_SUCCESS The transmit completion token was cached.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is
+ TRUE:
+ * This is NULL.
+ * Token is NULL.
+ * Token.Event is NULL.
+ * Token.TxData is NULL.
+ * Token.TxData.DestinationAddress is not
+ NULL and Token.TxData.HeaderLength is zero.
+ * Token.TxData.FragmentCount is zero.
+ * (Token.TxData.HeaderLength +
+ Token.TxData.DataLength) is not equal to the
+ sum of the
+ Token.TxData.FragmentTable[].FragmentLength
+ fields.
+ * One or more of the
+ Token.TxData.FragmentTable[].FragmentLength
+ fields is zero.
+ * One or more of the
+ Token.TxData.FragmentTable[].FragmentBufferfields
+ is NULL.
+ * Token.TxData.DataLength is greater than MTU.
+ @retval EFI_ACCESS_DENIED The transmit completion token is already in the
+ transmit queue.
+ @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a
+ lack of system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_NOT_READY The transmit request could not be queued because
+ the transmit queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpTransmit (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Aborts an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token.Status will be set to EFI_ABORTED and then Token.Event will be
+ signaled. If the token is not in one of the queues, which usually means that
+ the asynchronous operation has completed, this function will not signal the
+ token and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_MANAGED_NETWORK_PROTOCOL.Transmit() or
+ EFI_MANAGED_NETWORK_PROTOCOL.Receive(). If NULL, all
+ pending tokens are aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token.Event was signaled. When Token is NULL,
+ all pending requests were aborted and their
+ events were signaled.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O
+ request was not found in the transmit or
+ receive queue. It has either completed or was
+ not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+MnpCancel (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Places an asynchronous receiving request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet
+ queue. This function is always asynchronous.
+ The caller must fill in the Token.Event field in the completion token, and
+ this field cannot be NULL. When the receive operation completes, the MNP
+ updates the Token.Status and Token.RxData fields and the Token.Event is
+ signaled.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Token Pointer to a token associated with the receive
+ data descriptor. Type
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN is defined in
+ EFI_MANAGED_NETWORK_PROTOCOL.Transmit().
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is
+ TRUE:
+ * This is NULL.
+ * Token is NULL.
+ * Token.Event is NULL
+ @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a
+ lack of system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_ACCESS_DENIED The receive completion token was already in the
+ receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpReceive (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications to
+ increase the rate that data packets are moved between the communications
+ device and the transmit and receive queues.
+ Normally, a periodic timer event internally calls the Poll() function. But, in
+ some systems, the periodic timer event may not call Poll() fast enough to
+ transmit and/or receive all data packets without missing packets. Drivers and
+ applications that are experiencing packet loss should try calling the Poll()
+ function more often.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The
+ MNP child driver instance has been reset to startup
+ defaults.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed. Consider
+ increasing the polling rate.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive
+ queue. Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpPoll (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This
+ );
+
+/**
+ Configure the Snp receive filters according to the instances' receive filter
+ settings.
+
+ @param[in] MnpDeviceData Pointer to the mnp device context data.
+
+ @retval EFI_SUCCESS The receive filters is configured.
+ @retval EFI_OUT_OF_RESOURCES The receive filters can't be configured due
+ to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpConfigReceiveFilters (
+ IN MNP_DEVICE_DATA *MnpDeviceData
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
new file mode 100644
index 0000000000..ba0f36e710
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
@@ -0,0 +1,1140 @@
+/** @file
+ Implementation of Managed Network Protocol I/O functions.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MnpImpl.h"
+#include "MnpVlan.h"
+
+/**
+ Validates the Mnp transmit token.
+
+ @param[in] Instance Pointer to the Mnp instance context data.
+ @param[in] Token Pointer to the transmit token to check.
+
+ @return The Token is valid or not.
+
+**/
+BOOLEAN
+MnpIsValidTxToken (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+ UINT32 Index;
+ UINT32 TotalLength;
+ EFI_MANAGED_NETWORK_FRAGMENT_DATA *FragmentTable;
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ TxData = Token->Packet.TxData;
+
+ if ((Token->Event == NULL) || (TxData == NULL) || (TxData->FragmentCount == 0)) {
+ //
+ // The token is invalid if the Event is NULL, or the TxData is NULL, or
+ // the fragment count is zero.
+ //
+ DEBUG ((EFI_D_WARN, "MnpIsValidTxToken: Invalid Token.\n"));
+ return FALSE;
+ }
+
+ if ((TxData->DestinationAddress != NULL) && (TxData->HeaderLength != 0)) {
+ //
+ // The token is invalid if the HeaderLength isn't zero while the DestinationAddress
+ // is NULL (The destination address is already put into the packet).
+ //
+ DEBUG ((EFI_D_WARN, "MnpIsValidTxToken: DestinationAddress isn't NULL, HeaderLength must be 0.\n"));
+ return FALSE;
+ }
+
+ TotalLength = 0;
+ FragmentTable = TxData->FragmentTable;
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+
+ if ((FragmentTable[Index].FragmentLength == 0) || (FragmentTable[Index].FragmentBuffer == NULL)) {
+ //
+ // The token is invalid if any FragmentLength is zero or any FragmentBuffer is NULL.
+ //
+ DEBUG ((EFI_D_WARN, "MnpIsValidTxToken: Invalid FragmentLength or FragmentBuffer.\n"));
+ return FALSE;
+ }
+
+ TotalLength += FragmentTable[Index].FragmentLength;
+ }
+
+ if ((TxData->DestinationAddress == NULL) && (FragmentTable[0].FragmentLength < TxData->HeaderLength)) {
+ //
+ // Media header is split between fragments.
+ //
+ return FALSE;
+ }
+
+ if (TotalLength != (TxData->DataLength + TxData->HeaderLength)) {
+ //
+ // The length calculated from the fragment information doesn't equal to the
+ // sum of the DataLength and the HeaderLength.
+ //
+ DEBUG ((EFI_D_WARN, "MnpIsValidTxData: Invalid Datalength compared with the sum of fragment length.\n"));
+ return FALSE;
+ }
+
+ if (TxData->DataLength > MnpServiceData->Mtu) {
+ //
+ // The total length is larger than the MTU.
+ //
+ DEBUG ((EFI_D_WARN, "MnpIsValidTxData: TxData->DataLength exceeds Mtu.\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Build the packet to transmit from the TxData passed in.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in] TxData Pointer to the transmit data containing the information
+ to build the packet.
+ @param[out] PktBuf Pointer to record the address of the packet.
+ @param[out] PktLen Pointer to a UINT32 variable used to record the packet's
+ length.
+
+ @retval EFI_SUCCESS TxPackage is built.
+ @retval EFI_OUT_OF_RESOURCES The deliver fails due to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpBuildTxPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData,
+ OUT UINT8 **PktBuf,
+ OUT UINT32 *PktLen
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ UINT8 *DstPos;
+ UINT16 Index;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ UINT8 *TxBuf;
+
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+
+ TxBuf = MnpAllocTxBuf (MnpDeviceData);
+ if (TxBuf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Reserve space for vlan tag if needed.
+ //
+ if (MnpServiceData->VlanId != 0) {
+ *PktBuf = TxBuf + NET_VLAN_TAG_LEN;
+ } else {
+ *PktBuf = TxBuf;
+ }
+
+ if ((TxData->DestinationAddress == NULL) && (TxData->FragmentCount == 1)) {
+ CopyMem (
+ *PktBuf,
+ TxData->FragmentTable[0].FragmentBuffer,
+ TxData->FragmentTable[0].FragmentLength
+ );
+
+ *PktLen = TxData->FragmentTable[0].FragmentLength;
+ } else {
+ //
+ // Either media header isn't in FragmentTable or there is more than
+ // one fragment, copy the data into the packet buffer. Reserve the
+ // media header space if necessary.
+ //
+ SnpMode = MnpDeviceData->Snp->Mode;
+ DstPos = *PktBuf;
+ *PktLen = 0;
+ if (TxData->DestinationAddress != NULL) {
+ //
+ // If dest address is not NULL, move DstPos to reserve space for the
+ // media header. Add the media header length to buflen.
+ //
+ DstPos += SnpMode->MediaHeaderSize;
+ *PktLen += SnpMode->MediaHeaderSize;
+ }
+
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+ //
+ // Copy the data.
+ //
+ CopyMem (
+ DstPos,
+ TxData->FragmentTable[Index].FragmentBuffer,
+ TxData->FragmentTable[Index].FragmentLength
+ );
+ DstPos += TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ //
+ // Set the buffer length.
+ //
+ *PktLen += TxData->DataLength + TxData->HeaderLength;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Synchronously send out the packet.
+
+ This functon places the packet buffer to SNP driver's tansmit queue. The packet
+ can be considered successfully sent out once SNP acccetp the packet, while the
+ packet buffer recycle is deferred for better performance.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in] Packet Pointer to the pakcet buffer.
+ @param[in] Length The length of the packet.
+ @param[in, out] Token Pointer to the token the packet generated from.
+
+ @retval EFI_SUCCESS The packet is sent out.
+ @retval EFI_TIMEOUT Time out occurs, the packet isn't sent.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurs.
+
+**/
+EFI_STATUS
+MnpSyncSendPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN UINT8 *Packet,
+ IN UINT32 Length,
+ IN OUT EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+ UINT32 HeaderSize;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ UINT16 ProtocolType;
+
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+ Snp = MnpDeviceData->Snp;
+ TxData = Token->Packet.TxData;
+ Token->Status = EFI_SUCCESS;
+ HeaderSize = Snp->Mode->MediaHeaderSize - TxData->HeaderLength;
+
+ //
+ // Check media status before transmit packet.
+ // Note: media status will be updated by periodic timer MediaDetectTimer.
+ //
+ if (Snp->Mode->MediaPresentSupported && !Snp->Mode->MediaPresent) {
+ //
+ // Media not present, skip packet transmit and report EFI_NO_MEDIA
+ //
+ DEBUG ((EFI_D_WARN, "MnpSyncSendPacket: No network cable detected.\n"));
+ Token->Status = EFI_NO_MEDIA;
+ goto SIGNAL_TOKEN;
+ }
+
+
+ if (MnpServiceData->VlanId != 0) {
+ //
+ // Insert VLAN tag
+ //
+ MnpInsertVlanTag (MnpServiceData, TxData, &ProtocolType, &Packet, &Length);
+ } else {
+ ProtocolType = TxData->ProtocolType;
+ }
+
+ //
+ // Transmit the packet through SNP.
+ //
+ Status = Snp->Transmit (
+ Snp,
+ HeaderSize,
+ Length,
+ Packet,
+ TxData->SourceAddress,
+ TxData->DestinationAddress,
+ &ProtocolType
+ );
+ if (Status == EFI_NOT_READY) {
+ Status = MnpRecycleTxBuf (MnpDeviceData);
+ if (EFI_ERROR (Status)) {
+ Token->Status = EFI_DEVICE_ERROR;
+ goto SIGNAL_TOKEN;
+ }
+
+ Status = Snp->Transmit (
+ Snp,
+ HeaderSize,
+ Length,
+ Packet,
+ TxData->SourceAddress,
+ TxData->DestinationAddress,
+ &ProtocolType
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ Token->Status = EFI_DEVICE_ERROR;
+ }
+
+SIGNAL_TOKEN:
+
+ gBS->SignalEvent (Token->Event);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.
+ //
+ DispatchDpc ();
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Try to deliver the received packet to the instance.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+
+ @retval EFI_SUCCESS The received packet is delivered, or there is no
+ packet to deliver, or there is no available receive
+ token.
+ @retval EFI_OUT_OF_RESOURCES The deliver fails due to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpInstanceDeliverPacket (
+ IN OUT MNP_INSTANCE_DATA *Instance
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_RXDATA_WRAP *RxDataWrap;
+ NET_BUF *DupNbuf;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken;
+
+ MnpDeviceData = Instance->MnpServiceData->MnpDeviceData;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ if (NetMapIsEmpty (&Instance->RxTokenMap) || IsListEmpty (&Instance->RcvdPacketQueue)) {
+ //
+ // No pending received data or no available receive token, return.
+ //
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (Instance->RcvdPacketQueueSize != 0);
+
+ RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry);
+ if (RxDataWrap->Nbuf->RefCnt > 2) {
+ //
+ // There are other instances share this Nbuf, duplicate to get a
+ // copy to allow the instance to do R/W operations.
+ //
+ DupNbuf = MnpAllocNbuf (MnpDeviceData);
+ if (DupNbuf == NULL) {
+ DEBUG ((EFI_D_WARN, "MnpDeliverPacket: Failed to allocate a free Nbuf.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Duplicate the net buffer.
+ //
+ NetbufDuplicate (RxDataWrap->Nbuf, DupNbuf, 0);
+ MnpFreeNbuf (MnpDeviceData, RxDataWrap->Nbuf);
+ RxDataWrap->Nbuf = DupNbuf;
+ }
+
+ //
+ // All resources are OK, remove the packet from the queue.
+ //
+ NetListRemoveHead (&Instance->RcvdPacketQueue);
+ Instance->RcvdPacketQueueSize--;
+
+ RxData = &RxDataWrap->RxData;
+ SnpMode = MnpDeviceData->Snp->Mode;
+
+ //
+ // Set all the buffer pointers.
+ //
+ RxData->MediaHeader = NetbufGetByte (RxDataWrap->Nbuf, 0, NULL);
+ RxData->DestinationAddress = RxData->MediaHeader;
+ RxData->SourceAddress = (UINT8 *) RxData->MediaHeader + SnpMode->HwAddressSize;
+ RxData->PacketData = (UINT8 *) RxData->MediaHeader + SnpMode->MediaHeaderSize;
+
+ //
+ // Insert this RxDataWrap into the delivered queue.
+ //
+ InsertTailList (&Instance->RxDeliveredPacketQueue, &RxDataWrap->WrapEntry);
+
+ //
+ // Get the receive token from the RxTokenMap.
+ //
+ RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL);
+
+ //
+ // Signal this token's event.
+ //
+ RxToken->Packet.RxData = &RxDataWrap->RxData;
+ RxToken->Status = EFI_SUCCESS;
+ gBS->SignalEvent (RxToken->Event);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Deliver the received packet for the instances belonging to the MnpServiceData.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+
+**/
+VOID
+MnpDeliverPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ LIST_ENTRY *Entry;
+ MNP_INSTANCE_DATA *Instance;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
+ Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ //
+ // Try to deliver packet for this instance.
+ //
+ MnpInstanceDeliverPacket (Instance);
+ }
+}
+
+
+/**
+ Recycle the RxData and other resources used to hold and deliver the received
+ packet.
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registerd to the Event.
+
+**/
+VOID
+EFIAPI
+MnpRecycleRxData (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_RXDATA_WRAP *RxDataWrap;
+ MNP_DEVICE_DATA *MnpDeviceData;
+
+ ASSERT (Context != NULL);
+
+ RxDataWrap = (MNP_RXDATA_WRAP *) Context;
+ NET_CHECK_SIGNATURE (RxDataWrap->Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ ASSERT (RxDataWrap->Nbuf != NULL);
+
+ MnpDeviceData = RxDataWrap->Instance->MnpServiceData->MnpDeviceData;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ //
+ // Free this Nbuf.
+ //
+ MnpFreeNbuf (MnpDeviceData, RxDataWrap->Nbuf);
+ RxDataWrap->Nbuf = NULL;
+
+ //
+ // Close the recycle event.
+ //
+ gBS->CloseEvent (RxDataWrap->RxData.RecycleEvent);
+
+ //
+ // Remove this Wrap entry from the list.
+ //
+ RemoveEntryList (&RxDataWrap->WrapEntry);
+
+ FreePool (RxDataWrap);
+}
+
+
+/**
+ Queue the received packet into instance's receive queue.
+
+ @param[in, out] Instance Pointer to the mnp instance context data.
+ @param[in, out] RxDataWrap Pointer to the Wrap structure containing the
+ received data and other information.
+**/
+VOID
+MnpQueueRcvdPacket (
+ IN OUT MNP_INSTANCE_DATA *Instance,
+ IN OUT MNP_RXDATA_WRAP *RxDataWrap
+ )
+{
+ MNP_RXDATA_WRAP *OldRxDataWrap;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ //
+ // Check the queue size. If it exceeds the limit, drop one packet
+ // from the head.
+ //
+ if (Instance->RcvdPacketQueueSize == MNP_MAX_RCVD_PACKET_QUE_SIZE) {
+
+ DEBUG ((EFI_D_WARN, "MnpQueueRcvdPacket: Drop one packet bcz queue size limit reached.\n"));
+
+ //
+ // Get the oldest packet.
+ //
+ OldRxDataWrap = NET_LIST_HEAD (
+ &Instance->RcvdPacketQueue,
+ MNP_RXDATA_WRAP,
+ WrapEntry
+ );
+
+ //
+ // Recycle this OldRxDataWrap, this entry will be removed by the callee.
+ //
+ MnpRecycleRxData (NULL, (VOID *) OldRxDataWrap);
+ Instance->RcvdPacketQueueSize--;
+ }
+
+ //
+ // Update the timeout tick using the configured parameter.
+ //
+ RxDataWrap->TimeoutTick = Instance->ConfigData.ReceivedQueueTimeoutValue;
+
+ //
+ // Insert this Wrap into the instance queue.
+ //
+ InsertTailList (&Instance->RcvdPacketQueue, &RxDataWrap->WrapEntry);
+ Instance->RcvdPacketQueueSize++;
+}
+
+
+/**
+ Match the received packet with the instance receive filters.
+
+ @param[in] Instance Pointer to the mnp instance context data.
+ @param[in] RxData Pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA.
+ @param[in] GroupAddress Pointer to the GroupAddress, the GroupAddress is
+ non-NULL and it contains the destination multicast
+ mac address of the received packet if the packet
+ destinated to a multicast mac address.
+ @param[in] PktAttr The received packets attribute.
+
+ @return The received packet matches the instance's receive filters or not.
+
+**/
+BOOLEAN
+MnpMatchPacket (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData,
+ IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL,
+ IN UINT8 PktAttr
+ )
+{
+ EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData;
+ LIST_ENTRY *Entry;
+ MNP_GROUP_CONTROL_BLOCK *GroupCtrlBlk;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ ConfigData = &Instance->ConfigData;
+
+ //
+ // Check the protocol type.
+ //
+ if ((ConfigData->ProtocolTypeFilter != 0) && (ConfigData->ProtocolTypeFilter != RxData->ProtocolType)) {
+ return FALSE;
+ }
+
+ if (ConfigData->EnablePromiscuousReceive) {
+ //
+ // Always match if this instance is configured to be promiscuous.
+ //
+ return TRUE;
+ }
+
+ //
+ // The protocol type is matched, check receive filter, include unicast and broadcast.
+ //
+ if ((Instance->ReceiveFilter & PktAttr) != 0) {
+ return TRUE;
+ }
+
+ //
+ // Check multicast addresses.
+ //
+ if (ConfigData->EnableMulticastReceive && RxData->MulticastFlag) {
+
+ ASSERT (GroupAddress != NULL);
+
+ NET_LIST_FOR_EACH (Entry, &Instance->GroupCtrlBlkList) {
+
+ GroupCtrlBlk = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_CONTROL_BLOCK, CtrlBlkEntry);
+ if (GroupCtrlBlk->GroupAddress == GroupAddress) {
+ //
+ // The instance is configured to receiveing packets destinated to this
+ // multicast address.
+ //
+ return TRUE;
+ }
+ }
+ }
+
+ //
+ // No match.
+ //
+ return FALSE;
+}
+
+
+/**
+ Analyse the received packets.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in] Nbuf Pointer to the net buffer holding the received
+ packet.
+ @param[in, out] RxData Pointer to the buffer used to save the analysed
+ result in EFI_MANAGED_NETWORK_RECEIVE_DATA.
+ @param[out] GroupAddress Pointer to pointer to a MNP_GROUP_ADDRESS used to
+ pass out the address of the multicast address the
+ received packet destinated to.
+ @param[out] PktAttr Pointer to the buffer used to save the analysed
+ packet attribute.
+
+**/
+VOID
+MnpAnalysePacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN NET_BUF *Nbuf,
+ IN OUT EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData,
+ OUT MNP_GROUP_ADDRESS **GroupAddress,
+ OUT UINT8 *PktAttr
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ UINT8 *BufPtr;
+ LIST_ENTRY *Entry;
+
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+ SnpMode = MnpDeviceData->Snp->Mode;
+
+ //
+ // Get the packet buffer.
+ //
+ BufPtr = NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (BufPtr != NULL);
+
+ //
+ // Set the initial values.
+ //
+ RxData->BroadcastFlag = FALSE;
+ RxData->MulticastFlag = FALSE;
+ RxData->PromiscuousFlag = FALSE;
+ *PktAttr = UNICAST_PACKET;
+
+ if (!NET_MAC_EQUAL (&SnpMode->CurrentAddress, BufPtr, SnpMode->HwAddressSize)) {
+ //
+ // This packet isn't destinated to our current mac address, it't not unicast.
+ //
+ *PktAttr = 0;
+
+ if (NET_MAC_EQUAL (&SnpMode->BroadcastAddress, BufPtr, SnpMode->HwAddressSize)) {
+ //
+ // It's broadcast.
+ //
+ RxData->BroadcastFlag = TRUE;
+ *PktAttr = BROADCAST_PACKET;
+ } else if ((*BufPtr & 0x01) == 0x1) {
+ //
+ // It's multicast, try to match the multicast filters.
+ //
+ NET_LIST_FOR_EACH (Entry, &MnpDeviceData->GroupAddressList) {
+
+ *GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);
+ if (NET_MAC_EQUAL (BufPtr, &((*GroupAddress)->Address), SnpMode->HwAddressSize)) {
+ RxData->MulticastFlag = TRUE;
+ break;
+ }
+ }
+
+ if (!RxData->MulticastFlag) {
+ //
+ // No match, set GroupAddress to NULL. This multicast packet must
+ // be the result of PROMISUCOUS or PROMISUCOUS_MULTICAST flag is on.
+ //
+ *GroupAddress = NULL;
+ RxData->PromiscuousFlag = TRUE;
+
+ if (MnpDeviceData->PromiscuousCount == 0) {
+ //
+ // Skip the below code, there is no receiver of this packet.
+ //
+ return ;
+ }
+ }
+ } else {
+ RxData->PromiscuousFlag = TRUE;
+ }
+ }
+
+ ZeroMem (&RxData->Timestamp, sizeof (EFI_TIME));
+
+ //
+ // Fill the common parts of RxData.
+ //
+ RxData->PacketLength = Nbuf->TotalSize;
+ RxData->HeaderLength = SnpMode->MediaHeaderSize;
+ RxData->AddressLength = SnpMode->HwAddressSize;
+ RxData->DataLength = RxData->PacketLength - RxData->HeaderLength;
+ RxData->ProtocolType = NTOHS (*(UINT16 *) (BufPtr + 2 * SnpMode->HwAddressSize));
+}
+
+
+/**
+ Wrap the RxData.
+
+ @param[in] Instance Pointer to the mnp instance context data.
+ @param[in] RxData Pointer to the receive data to wrap.
+
+ @return Pointer to a MNP_RXDATA_WRAP which wraps the RxData.
+
+**/
+MNP_RXDATA_WRAP *
+MnpWrapRxData (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData
+ )
+{
+ EFI_STATUS Status;
+ MNP_RXDATA_WRAP *RxDataWrap;
+
+ //
+ // Allocate memory.
+ //
+ RxDataWrap = AllocatePool (sizeof (MNP_RXDATA_WRAP));
+ if (RxDataWrap == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpDispatchPacket: Failed to allocate a MNP_RXDATA_WRAP.\n"));
+ return NULL;
+ }
+
+ RxDataWrap->Instance = Instance;
+
+ //
+ // Fill the RxData in RxDataWrap,
+ //
+ CopyMem (&RxDataWrap->RxData, RxData, sizeof (RxDataWrap->RxData));
+
+ //
+ // Create the recycle event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ MnpRecycleRxData,
+ RxDataWrap,
+ &RxDataWrap->RxData.RecycleEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "MnpDispatchPacket: gBS->CreateEvent failed, %r.\n", Status));
+
+ FreePool (RxDataWrap);
+ return NULL;
+ }
+
+ return RxDataWrap;
+}
+
+
+/**
+ Enqueue the received the packets to the instances belonging to the
+ MnpServiceData.
+
+ @param[in] MnpServiceData Pointer to the mnp service context data.
+ @param[in] Nbuf Pointer to the net buffer representing the received
+ packet.
+
+**/
+VOID
+MnpEnqueuePacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN NET_BUF *Nbuf
+ )
+{
+ LIST_ENTRY *Entry;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA RxData;
+ UINT8 PktAttr;
+ MNP_GROUP_ADDRESS *GroupAddress;
+ MNP_RXDATA_WRAP *RxDataWrap;
+
+
+ GroupAddress = NULL;
+ //
+ // First, analyse the packet header.
+ //
+ MnpAnalysePacket (MnpServiceData, Nbuf, &RxData, &GroupAddress, &PktAttr);
+
+ if (RxData.PromiscuousFlag && (MnpServiceData->MnpDeviceData->PromiscuousCount == 0)) {
+ //
+ // No receivers, no more action need.
+ //
+ return ;
+ }
+
+ //
+ // Iterate the children to find match.
+ //
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ //
+ // Check the packet against the instance receive filters.
+ //
+ if (MnpMatchPacket (Instance, &RxData, GroupAddress, PktAttr)) {
+ //
+ // Wrap the RxData.
+ //
+ RxDataWrap = MnpWrapRxData (Instance, &RxData);
+ if (RxDataWrap == NULL) {
+ continue;
+ }
+
+ //
+ // Associate RxDataWrap with Nbuf and increase the RefCnt.
+ //
+ RxDataWrap->Nbuf = Nbuf;
+ NET_GET_REF (RxDataWrap->Nbuf);
+
+ //
+ // Queue the packet into the instance queue.
+ //
+ MnpQueueRcvdPacket (Instance, RxDataWrap);
+ }
+ }
+}
+
+
+/**
+ Try to receive a packet and deliver it.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+
+ @retval EFI_SUCCESS add return value to function comment
+ @retval EFI_NOT_STARTED The simple network protocol is not started.
+ @retval EFI_NOT_READY No packet received.
+ @retval EFI_DEVICE_ERROR An unexpected error occurs.
+
+**/
+EFI_STATUS
+MnpReceivePacket (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ NET_BUF *Nbuf;
+ UINT8 *BufPtr;
+ UINTN BufLen;
+ UINTN HeaderSize;
+ UINT32 Trimmed;
+ MNP_SERVICE_DATA *MnpServiceData;
+ UINT16 VlanId;
+ BOOLEAN IsVlanPacket;
+
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ Snp = MnpDeviceData->Snp;
+ if (Snp->Mode->State != EfiSimpleNetworkInitialized) {
+ //
+ // The simple network protocol is not started.
+ //
+ return EFI_NOT_STARTED;
+ }
+
+ if (MnpDeviceData->RxNbufCache == NULL) {
+ //
+ // Try to get a new buffer as there may be buffers recycled.
+ //
+ MnpDeviceData->RxNbufCache = MnpAllocNbuf (MnpDeviceData);
+
+ if (MnpDeviceData->RxNbufCache == NULL) {
+ //
+ // No available buffer in the buffer pool.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ NetbufAllocSpace (
+ MnpDeviceData->RxNbufCache,
+ MnpDeviceData->BufferLength,
+ NET_BUF_TAIL
+ );
+ }
+
+ Nbuf = MnpDeviceData->RxNbufCache;
+ BufLen = Nbuf->TotalSize;
+ BufPtr = NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (BufPtr != NULL);
+
+ //
+ // Receive packet through Snp.
+ //
+ Status = Snp->Receive (Snp, &HeaderSize, &BufLen, BufPtr, NULL, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG_CODE (
+ if (Status != EFI_NOT_READY) {
+ DEBUG ((EFI_D_WARN, "MnpReceivePacket: Snp->Receive() = %r.\n", Status));
+ }
+ );
+
+ return Status;
+ }
+
+ //
+ // Sanity check.
+ //
+ if ((HeaderSize != Snp->Mode->MediaHeaderSize) || (BufLen < HeaderSize)) {
+ DEBUG (
+ (EFI_D_WARN,
+ "MnpReceivePacket: Size error, HL:TL = %d:%d.\n",
+ HeaderSize,
+ BufLen)
+ );
+ return EFI_DEVICE_ERROR;
+ }
+
+ Trimmed = 0;
+ if (Nbuf->TotalSize != BufLen) {
+ //
+ // Trim the packet from tail.
+ //
+ Trimmed = NetbufTrim (Nbuf, Nbuf->TotalSize - (UINT32) BufLen, NET_BUF_TAIL);
+ ASSERT (Nbuf->TotalSize == BufLen);
+ }
+
+ VlanId = 0;
+ if (MnpDeviceData->NumberOfVlan != 0) {
+ //
+ // VLAN is configured, remove the VLAN tag if any
+ //
+ IsVlanPacket = MnpRemoveVlanTag (MnpDeviceData, Nbuf, &VlanId);
+ } else {
+ IsVlanPacket = FALSE;
+ }
+
+ MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);
+ if (MnpServiceData == NULL) {
+ //
+ // VLAN is not set for this tagged frame, ignore this packet
+ //
+ if (Trimmed > 0) {
+ NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);
+ }
+
+ if (IsVlanPacket) {
+ NetbufAllocSpace (Nbuf, NET_VLAN_TAG_LEN, NET_BUF_HEAD);
+ }
+
+ goto EXIT;
+ }
+
+ //
+ // Enqueue the packet to the matched instances.
+ //
+ MnpEnqueuePacket (MnpServiceData, Nbuf);
+
+ if (Nbuf->RefCnt > 2) {
+ //
+ // RefCnt > 2 indicates there is at least one receiver of this packet.
+ // Free the current RxNbufCache and allocate a new one.
+ //
+ MnpFreeNbuf (MnpDeviceData, Nbuf);
+
+ Nbuf = MnpAllocNbuf (MnpDeviceData);
+ MnpDeviceData->RxNbufCache = Nbuf;
+ if (Nbuf == NULL) {
+ DEBUG ((EFI_D_ERROR, "MnpReceivePacket: Alloc packet for receiving cache failed.\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ NetbufAllocSpace (Nbuf, MnpDeviceData->BufferLength, NET_BUF_TAIL);
+ } else {
+ //
+ // No receiver for this packet.
+ //
+ if (Trimmed > 0) {
+ NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);
+ }
+ if (IsVlanPacket) {
+ NetbufAllocSpace (Nbuf, NET_VLAN_TAG_LEN, NET_BUF_HEAD);
+ }
+
+ goto EXIT;
+ }
+ //
+ // Deliver the queued packets.
+ //
+ MnpDeliverPacket (MnpServiceData);
+
+EXIT:
+
+ ASSERT (Nbuf->TotalSize == MnpDeviceData->BufferLength);
+
+ return Status;
+}
+
+
+/**
+ Remove the received packets if timeout occurs.
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the event.
+
+**/
+VOID
+EFIAPI
+MnpCheckPacketTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_SERVICE_DATA *MnpServiceData;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *ServiceEntry;
+ LIST_ENTRY *RxEntry;
+ LIST_ENTRY *NextEntry;
+ MNP_INSTANCE_DATA *Instance;
+ MNP_RXDATA_WRAP *RxDataWrap;
+ EFI_TPL OldTpl;
+
+ MnpDeviceData = (MNP_DEVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (ServiceEntry, &MnpDeviceData->ServiceList) {
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (ServiceEntry);
+
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured || (Instance->ConfigData.ReceivedQueueTimeoutValue == 0)) {
+ //
+ // This instance is not configured or there is no receive time out,
+ // just skip to the next instance.
+ //
+ continue;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+ NET_LIST_FOR_EACH_SAFE (RxEntry, NextEntry, &Instance->RcvdPacketQueue) {
+
+ RxDataWrap = NET_LIST_USER_STRUCT (RxEntry, MNP_RXDATA_WRAP, WrapEntry);
+
+ //
+ // TimeoutTick unit is microsecond, MNP_TIMEOUT_CHECK_INTERVAL unit is 100ns.
+ //
+ if (RxDataWrap->TimeoutTick >= (MNP_TIMEOUT_CHECK_INTERVAL / 10)) {
+ RxDataWrap->TimeoutTick -= (MNP_TIMEOUT_CHECK_INTERVAL / 10);
+ } else {
+ //
+ // Drop the timeout packet.
+ //
+ DEBUG ((EFI_D_WARN, "MnpCheckPacketTimeout: Received packet timeout.\n"));
+ MnpRecycleRxData (NULL, RxDataWrap);
+ Instance->RcvdPacketQueueSize--;
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ }
+ }
+}
+
+/**
+ Poll to update MediaPresent field in SNP ModeData by Snp->GetStatus().
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the event.
+
+**/
+VOID
+EFIAPI
+MnpCheckMediaStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ UINT32 InterruptStatus;
+
+ MnpDeviceData = (MNP_DEVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ Snp = MnpDeviceData->Snp;
+ if (Snp->Mode->MediaPresentSupported) {
+ //
+ // Upon successful return of GetStatus(), the MediaPresent field of
+ // EFI_SIMPLE_NETWORK_MODE will be updated to reflect any change of media status
+ //
+ Snp->GetStatus (Snp, &InterruptStatus, NULL);
+ }
+}
+
+/**
+ Poll to receive the packets from Snp. This function is either called by upperlayer
+ protocols/applications or the system poll timer notify mechanism.
+
+ @param[in] Event The event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the event.
+
+**/
+VOID
+EFIAPI
+MnpSystemPoll (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+
+ MnpDeviceData = (MNP_DEVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (MnpDeviceData, MNP_DEVICE_DATA_SIGNATURE);
+
+ //
+ // Try to receive packets from Snp.
+ //
+ MnpReceivePacket (MnpDeviceData);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of rx token's events.
+ //
+ DispatchDpc ();
+}
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c
new file mode 100644
index 0000000000..31c2e3e5b8
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c
@@ -0,0 +1,796 @@
+/** @file
+ Implementation of Managed Network Protocol public services.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MnpImpl.h"
+
+/**
+ Returns the operational parameters for the current MNP child driver. May also
+ support returning the underlying SNP driver mode data.
+
+ The GetModeData() function is used to read the current mode data (operational
+ parameters) from the MNP or the underlying SNP.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[out] MnpConfigData Pointer to storage for MNP operational parameters. Type
+ EFI_MANAGED_NETWORK_CONFIG_DATA is defined in "Related
+ Definitions" below.
+ @param[out] SnpModeData Pointer to storage for SNP operational parameters. This
+ feature may be unsupported. Type EFI_SIMPLE_NETWORK_MODE
+ is defined in the EFI_SIMPLE_NETWORK_PROTOCOL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in this
+ MNP implementation.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured. The default values are returned in
+ MnpConfigData if it is not NULL.
+ @retval Others The mode data could not be read.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpGetModeData (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT32 InterruptStatus;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (MnpConfigData != NULL) {
+ //
+ // Copy the instance configuration data.
+ //
+ CopyMem (MnpConfigData, &Instance->ConfigData, sizeof (*MnpConfigData));
+ }
+
+ if (SnpModeData != NULL) {
+ //
+ // Copy the underlayer Snp mode data.
+ //
+ Snp = Instance->MnpServiceData->MnpDeviceData->Snp;
+
+ //
+ // Upon successful return of GetStatus(), the Snp->Mode->MediaPresent
+ // will be updated to reflect any change of media status
+ //
+ Snp->GetStatus (Snp, &InterruptStatus, NULL);
+ CopyMem (SnpModeData, Snp->Mode, sizeof (*SnpModeData));
+ }
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Sets or clears the operational parameters for the MNP child driver.
+
+ The Configure() function is used to set, change, or reset the operational
+ parameters for the MNP child driver instance. Until the operational parameters
+ have been set, no network traffic can be sent or received by this MNP child
+ driver instance. Once the operational parameters have been reset, no more
+ traffic can be sent or received until the operational parameters have been set
+ again.
+ Each MNP child driver instance can be started and stopped independently of
+ each other by setting or resetting their receive filter settings with the
+ Configure() function.
+ After any successful call to Configure(), the MNP child driver instance is
+ started. The internal periodic timer (if supported) is enabled. Data can be
+ transmitted and may be received if the receive filters have also been enabled.
+ Note: If multiple MNP child driver instances will receive the same packet
+ because of overlapping receive filter settings, then the first MNP child
+ driver instance will receive the original packet and additional instances will
+ receive copies of the original packet.
+ Note: Warning: Receive filter settings that overlap will consume extra
+ processor and/or DMA resources and degrade system and network performance.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] MnpConfigData Pointer to configuration data that will be assigned
+ to the MNP child driver instance. If NULL, the MNP
+ child driver instance is reset to startup defaults
+ and all pending transmit and receive requests are
+ flushed. Type EFI_MANAGED_NETWORK_CONFIG_DATA is
+ defined in EFI_MANAGED_NETWORK_PROTOCOL.GetModeData().
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is
+ TRUE:
+ * This is NULL.
+ * MnpConfigData.ProtocolTypeFilter is not
+ valid.
+ The operational data for the MNP child driver
+ instance is unchanged.
+ @retval EFI_OUT_OF_RESOURCES Required system resources (usually memory)
+ could not be allocated.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in
+ this [MNP] implementation. The operational data
+ for the MNP child driver instance is unchanged.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error
+ occurred. The MNP child driver instance has
+ been reset to startup defaults.
+ @retval Others The MNP child driver instance has been reset to
+ startup defaults.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpConfigure (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if ((This == NULL) ||
+ ((MnpConfigData != NULL) &&
+ (MnpConfigData->ProtocolTypeFilter > 0) &&
+ (MnpConfigData->ProtocolTypeFilter <= 1500))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if ((MnpConfigData == NULL) && (!Instance->Configured)) {
+ //
+ // If the instance is not configured and a reset is requested, just return.
+ //
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Configure the instance.
+ //
+ Status = MnpConfigureInstance (Instance, MnpConfigData);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Translates an IP multicast address to a hardware (MAC) multicast address. This
+ function may be unsupported in some MNP implementations.
+
+ The McastIpToMac() function translates an IP multicast address to a hardware
+ (MAC) multicast address. This function may be implemented by calling the
+ underlying EFI_SIMPLE_NETWORK. MCastIpToMac() function, which may also be
+ unsupported in some MNP implementations.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Ipv6Flag Set to TRUE to if IpAddress is an IPv6 multicast address.
+ Set to FALSE if IpAddress is an IPv4 multicast address.
+ @param[in] IpAddress Pointer to the multicast IP address (in network byte
+ order) to convert.
+ @param[out] MacAddress Pointer to the resulting multicast MAC address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One of the following conditions is TRUE:
+ * This is NULL.
+ * IpAddress is NULL.
+ * IpAddress is not a valid multicast IP
+ address.
+ * MacAddress is NULL.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in this
+ MNP implementation.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others The address could not be converted.
+**/
+EFI_STATUS
+EFIAPI
+MnpMcastIpToMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Ipv6Flag,
+ IN EFI_IP_ADDRESS *IpAddress,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_TPL OldTpl;
+ EFI_IPv6_ADDRESS *Ip6Address;
+
+ if ((This == NULL) || (IpAddress == NULL) || (MacAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip6Address = &IpAddress->v6;
+
+ if ((Ipv6Flag && !IP6_IS_MULTICAST (Ip6Address)) ||
+ (!Ipv6Flag && !IP4_IS_MULTICAST (EFI_NTOHL (*IpAddress)))
+ ) {
+ //
+ // The IP address passed in is not a multicast address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ Snp = Instance->MnpServiceData->MnpDeviceData->Snp;
+ ASSERT (Snp != NULL);
+
+ ZeroMem (MacAddress, sizeof (EFI_MAC_ADDRESS));
+
+ if (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) {
+ if (!Ipv6Flag) {
+ //
+ // Translate the IPv4 address into a multicast MAC address if the NIC is an
+ // ethernet NIC according to RFC1112..
+ //
+ MacAddress->Addr[0] = 0x01;
+ MacAddress->Addr[1] = 0x00;
+ MacAddress->Addr[2] = 0x5E;
+ MacAddress->Addr[3] = (UINT8) (IpAddress->v4.Addr[1] & 0x7F);
+ MacAddress->Addr[4] = IpAddress->v4.Addr[2];
+ MacAddress->Addr[5] = IpAddress->v4.Addr[3];
+ } else {
+ //
+ // Translate the IPv6 address into a multicast MAC address if the NIC is an
+ // ethernet NIC according to RFC2464.
+ //
+
+ MacAddress->Addr[0] = 0x33;
+ MacAddress->Addr[1] = 0x33;
+ MacAddress->Addr[2] = Ip6Address->Addr[12];
+ MacAddress->Addr[3] = Ip6Address->Addr[13];
+ MacAddress->Addr[4] = Ip6Address->Addr[14];
+ MacAddress->Addr[5] = Ip6Address->Addr[15];
+ }
+
+ Status = EFI_SUCCESS;
+ } else {
+ //
+ // Invoke Snp to translate the multicast IP address.
+ //
+ Status = Snp->MCastIpToMac (
+ Snp,
+ Ipv6Flag,
+ IpAddress,
+ MacAddress
+ );
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Enables and disables receive filters for multicast address. This function may
+ be unsupported in some MNP implementations.
+
+ The Groups() function only adds and removes multicast MAC addresses from the
+ filter list. The MNP driver does not transmit or process Internet Group
+ Management Protocol (IGMP) packets. If JoinFlag is FALSE and MacAddress is
+ NULL, then all joined groups are left.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join this multicast group.
+ Set to FALSE to leave this multicast group.
+ @param[in] MacAddress Pointer to the multicast MAC group (address) to join or
+ leave.
+
+ @retval EFI_SUCCESS The requested operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * This is NULL.
+ * JoinFlag is TRUE and MacAddress is NULL.
+ * MacAddress is not a valid multicast MAC
+ address.
+ * The MNP multicast group settings are
+ unchanged.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_ALREADY_STARTED The supplied multicast group is already joined.
+ @retval EFI_NOT_FOUND The supplied multicast group is not joined.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_UNSUPPORTED The requested feature is unsupported in this MNP
+ implementation.
+ @retval Others The requested operation could not be completed.
+ The MNP multicast group settings are unchanged.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpGroups (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ MNP_GROUP_CONTROL_BLOCK *GroupCtrlBlk;
+ MNP_GROUP_ADDRESS *GroupAddress;
+ LIST_ENTRY *ListEntry;
+ BOOLEAN AddressExist;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL || (JoinFlag && (MacAddress == NULL))) {
+ //
+ // This is NULL, or it's a join operation but MacAddress is NULL.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+ SnpMode = Instance->MnpServiceData->MnpDeviceData->Snp->Mode;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if ((!Instance->ConfigData.EnableMulticastReceive) ||
+ ((MacAddress != NULL) && !NET_MAC_IS_MULTICAST (MacAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize))) {
+ //
+ // The instance isn't configured to do mulitcast receive. OR
+ // the passed in MacAddress is not a mutlticast mac address.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = EFI_SUCCESS;
+ AddressExist = FALSE;
+ GroupCtrlBlk = NULL;
+
+ if (MacAddress != NULL) {
+ //
+ // Search the instance's GroupCtrlBlkList to find the specific address.
+ //
+ NET_LIST_FOR_EACH (ListEntry, &Instance->GroupCtrlBlkList) {
+
+ GroupCtrlBlk = NET_LIST_USER_STRUCT (
+ ListEntry,
+ MNP_GROUP_CONTROL_BLOCK,
+ CtrlBlkEntry
+ );
+ GroupAddress = GroupCtrlBlk->GroupAddress;
+ if (0 == CompareMem (
+ MacAddress,
+ &GroupAddress->Address,
+ SnpMode->HwAddressSize
+ )) {
+ //
+ // There is already the same multicast mac address configured.
+ //
+ AddressExist = TRUE;
+ break;
+ }
+ }
+
+ if (JoinFlag && AddressExist) {
+ //
+ // The multicast mac address to join already exists.
+ //
+ Status = EFI_ALREADY_STARTED;
+ }
+
+ if (!JoinFlag && !AddressExist) {
+ //
+ // The multicast mac address to leave doesn't exist in this instance.
+ //
+ Status = EFI_NOT_FOUND;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else if (IsListEmpty (&Instance->GroupCtrlBlkList)) {
+ //
+ // The MacAddress is NULL and there is no configured multicast mac address,
+ // just return.
+ //
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, it is time to take action.
+ //
+ Status = MnpGroupOp (Instance, JoinFlag, MacAddress, GroupCtrlBlk);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Places asynchronous outgoing data packets into the transmit queue.
+
+ The Transmit() function places a completion token into the transmit packet
+ queue. This function is always asynchronous.
+ The caller must fill in the Token.Event and Token.TxData fields in the
+ completion token, and these fields cannot be NULL. When the transmit operation
+ completes, the MNP updates the Token.Status field and the Token.Event is
+ signaled.
+ Note: There may be a performance penalty if the packet needs to be
+ defragmented before it can be transmitted by the network device. Systems in
+ which performance is critical should review the requirements and features of
+ the underlying communications device and drivers.
+
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Token Pointer to a token associated with the transmit data
+ descriptor. Type EFI_MANAGED_NETWORK_COMPLETION_TOKEN
+ is defined in "Related Definitions" below.
+
+ @retval EFI_SUCCESS The transmit completion token was cached.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is
+ TRUE:
+ * This is NULL.
+ * Token is NULL.
+ * Token.Event is NULL.
+ * Token.TxData is NULL.
+ * Token.TxData.DestinationAddress is not
+ NULL and Token.TxData.HeaderLength is zero.
+ * Token.TxData.FragmentCount is zero.
+ * (Token.TxData.HeaderLength +
+ Token.TxData.DataLength) is not equal to the
+ sum of the
+ Token.TxData.FragmentTable[].FragmentLength
+ fields.
+ * One or more of the
+ Token.TxData.FragmentTable[].FragmentLength
+ fields is zero.
+ * One or more of the
+ Token.TxData.FragmentTable[].FragmentBufferfields
+ is NULL.
+ * Token.TxData.DataLength is greater than MTU.
+ @retval EFI_ACCESS_DENIED The transmit completion token is already in the
+ transmit queue.
+ @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a
+ lack of system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_NOT_READY The transmit request could not be queued because
+ the transmit queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpTransmit (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ MNP_SERVICE_DATA *MnpServiceData;
+ UINT8 *PktBuf;
+ UINT32 PktLen;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (!MnpIsValidTxToken (Instance, Token)) {
+ //
+ // The Token is invalid.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ //
+ // Build the tx packet
+ //
+ Status = MnpBuildTxPacket (MnpServiceData, Token->Packet.TxData, &PktBuf, &PktLen);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, send the packet synchronously.
+ //
+ Status = MnpSyncSendPacket (MnpServiceData, PktBuf, PktLen, Token);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Places an asynchronous receiving request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet
+ queue. This function is always asynchronous.
+ The caller must fill in the Token.Event field in the completion token, and
+ this field cannot be NULL. When the receive operation completes, the MNP
+ updates the Token.Status and Token.RxData fields and the Token.Event is
+ signaled.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Token Pointer to a token associated with the receive
+ data descriptor. Type
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN is defined in
+ EFI_MANAGED_NETWORK_PROTOCOL.Transmit().
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is
+ TRUE:
+ * This is NULL.
+ * Token is NULL.
+ * Token.Event is NULL
+ @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a
+ lack of system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_ACCESS_DENIED The receive completion token was already in the
+ receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpReceive (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether this token(event) is already in the rx token queue.
+ //
+ Status = NetMapIterate (&Instance->RxTokenMap, MnpTokenExist, (VOID *) Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Insert the Token into the RxTokenMap.
+ //
+ Status = NetMapInsertTail (&Instance->RxTokenMap, (VOID *) Token, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Try to deliver any buffered packets.
+ //
+ Status = MnpInstanceDeliverPacket (Instance);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.
+ //
+ DispatchDpc ();
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Aborts an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token.Status will be set to EFI_ABORTED and then Token.Event will be
+ signaled. If the token is not in one of the queues, which usually means that
+ the asynchronous operation has completed, this function will not signal the
+ token and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_MANAGED_NETWORK_PROTOCOL.Transmit() or
+ EFI_MANAGED_NETWORK_PROTOCOL.Receive(). If NULL, all
+ pending tokens are aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token.Event was signaled. When Token is NULL,
+ all pending requests were aborted and their
+ events were signaled.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O
+ request was not found in the transmit or
+ receive queue. It has either completed or was
+ not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+MnpCancel (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Iterate the RxTokenMap to cancel the specified Token.
+ //
+ Status = NetMapIterate (&Instance->RxTokenMap, MnpCancelTokens, (VOID *) Token);
+ if (Token != NULL) {
+ Status = (Status == EFI_ABORTED) ? EFI_SUCCESS : EFI_NOT_FOUND;
+ }
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the cancled token's events.
+ //
+ DispatchDpc ();
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications to
+ increase the rate that data packets are moved between the communications
+ device and the transmit and receive queues.
+ Normally, a periodic timer event internally calls the Poll() function. But, in
+ some systems, the periodic timer event may not call Poll() fast enough to
+ transmit and/or receive all data packets without missing packets. Drivers and
+ applications that are experiencing packet loss should try calling the Poll()
+ function more often.
+
+ @param[in] This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The
+ MNP child driver instance has been reset to startup
+ defaults.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed. Consider
+ increasing the polling rate.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive
+ queue. Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpPoll (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Try to receive packets.
+ //
+ Status = MnpReceivePacket (Instance->MnpServiceData->MnpDeviceData);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of rx token's events.
+ //
+ DispatchDpc ();
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.c b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.c
new file mode 100644
index 0000000000..98cbc2e35d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.c
@@ -0,0 +1,739 @@
+/** @file
+ VLAN Config Protocol implementation and VLAN packet process routine.
+
+Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MnpImpl.h"
+#include "MnpVlan.h"
+
+VLAN_DEVICE_PATH mVlanDevicePathTemplate = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_VLAN_DP,
+ {
+ (UINT8) (sizeof (VLAN_DEVICE_PATH)),
+ (UINT8) ((sizeof (VLAN_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0
+};
+
+EFI_VLAN_CONFIG_PROTOCOL mVlanConfigProtocolTemplate = {
+ VlanConfigSet,
+ VlanConfigFind,
+ VlanConfigRemove
+};
+
+
+/**
+ Create a child handle for the VLAN ID.
+
+ @param[in] ImageHandle The driver image handle.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] VlanId The VLAN ID.
+ @param[out] Devicepath Pointer to returned device path for child handle.
+
+ @return The handle of VLAN child or NULL if failed to create VLAN child.
+
+**/
+EFI_HANDLE
+MnpCreateVlanChild (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT16 VlanId,
+ OUT EFI_DEVICE_PATH_PROTOCOL **Devicepath OPTIONAL
+ )
+{
+ EFI_HANDLE ChildHandle;
+ VLAN_DEVICE_PATH VlanNode;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *VlanDevicePath;
+ EFI_STATUS Status;
+
+ //
+ // Try to get parent device path
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ParentDevicePath,
+ ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ //
+ // Construct device path for child handle: MAC + VLAN
+ //
+ CopyMem (&VlanNode, &mVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH));
+ VlanNode.VlanId = VlanId;
+ VlanDevicePath = AppendDevicePathNode (
+ ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VlanNode
+ );
+ if (VlanDevicePath == NULL) {
+ return NULL;
+ }
+
+ //
+ // Create child VLAN handle by installing DevicePath protocol
+ //
+ ChildHandle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ VlanDevicePath,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (VlanDevicePath);
+ return NULL;
+ }
+
+ if (Devicepath != NULL) {
+ *Devicepath = VlanDevicePath;
+ }
+
+ return ChildHandle;
+}
+
+/**
+ Remove VLAN tag from a packet.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in, out] Nbuf Pointer to the NET_BUF to remove VLAN tag.
+ @param[out] VlanId Pointer to the returned VLAN ID.
+
+ @retval TRUE VLAN tag is removed from this packet.
+ @retval FALSE There is no VLAN tag in this packet.
+
+**/
+BOOLEAN
+MnpRemoveVlanTag (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN OUT NET_BUF *Nbuf,
+ OUT UINT16 *VlanId
+ )
+{
+ UINT8 *Packet;
+ UINTN ProtocolOffset;
+ UINT16 ProtocolType;
+ VLAN_TCI VlanTag;
+
+ ProtocolOffset = MnpDeviceData->Snp->Mode->HwAddressSize * 2;
+
+ //
+ // Get the packet buffer.
+ //
+ Packet = NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Packet != NULL);
+
+ //
+ // Check whether this is VLAN tagged frame by Ether Type
+ //
+ *VlanId = 0;
+ ProtocolType = NTOHS (*(UINT16 *) (Packet + ProtocolOffset));
+ if (ProtocolType != ETHER_TYPE_VLAN) {
+ //
+ // Not a VLAN tagged frame
+ //
+ return FALSE;
+ }
+
+ VlanTag.Uint16 = NTOHS (*(UINT16 *) (Packet + ProtocolOffset + sizeof (ProtocolType)));
+ *VlanId = VlanTag.Bits.Vid;
+
+ //
+ // Move hardware address (DA + SA) 4 bytes right to override VLAN tag
+ //
+ CopyMem (Packet + NET_VLAN_TAG_LEN, Packet, ProtocolOffset);
+
+ //
+ // Remove VLAN tag from the Nbuf
+ //
+ NetbufTrim (Nbuf, NET_VLAN_TAG_LEN, NET_BUF_HEAD);
+
+ return TRUE;
+}
+
+
+/**
+ Build the vlan packet to transmit from the TxData passed in.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param TxData Pointer to the transmit data containing the
+ information to build the packet.
+ @param ProtocolType Pointer to the Ethernet protocol type.
+ @param Packet Pointer to record the address of the packet.
+ @param Length Pointer to a UINT32 variable used to record the
+ packet's length.
+
+**/
+VOID
+MnpInsertVlanTag (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData,
+ OUT UINT16 *ProtocolType,
+ IN OUT UINT8 **Packet,
+ IN OUT UINT32 *Length
+ )
+{
+ VLAN_TCI *VlanTci;
+ UINT16 *Tpid;
+ UINT16 *EtherType;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+
+ MnpDeviceData = MnpServiceData->MnpDeviceData;
+ SnpMode = MnpDeviceData->Snp->Mode;
+
+ *ProtocolType = ETHER_TYPE_VLAN;
+ *Length = *Length + NET_VLAN_TAG_LEN;
+ *Packet = *Packet - NET_VLAN_TAG_LEN;
+
+ Tpid = (UINT16 *) (*Packet + SnpMode->MediaHeaderSize - sizeof (*ProtocolType));
+ VlanTci = (VLAN_TCI *) (UINTN) (Tpid + 1);
+ if (TxData->HeaderLength != 0) {
+ //
+ // Media header is in packet, move DA+SA 4 bytes left
+ //
+ CopyMem (
+ *Packet,
+ *Packet + NET_VLAN_TAG_LEN,
+ SnpMode->MediaHeaderSize - sizeof (*ProtocolType)
+ );
+ *Tpid = HTONS (ETHER_TYPE_VLAN);
+ } else {
+ //
+ // Media header not in packet, VLAN TCI and original protocol type becomes payload
+ //
+ EtherType = (UINT16 *) (UINTN) (VlanTci + 1);
+ *EtherType = HTONS (TxData->ProtocolType);
+ }
+
+ VlanTci->Bits.Vid = MnpServiceData->VlanId;
+ VlanTci->Bits.Cfi = VLAN_TCI_CFI_CANONICAL_MAC;
+ VlanTci->Bits.Priority = MnpServiceData->Priority;
+ VlanTci->Uint16 = HTONS (VlanTci->Uint16);
+}
+
+/**
+ Check VLAN configuration variable and delete the duplicative content if has identical Vlan ID.
+
+ @param[in] MnpDeviceData Pointer to the MNP device context data.
+ @param[in] Buffer Pointer to the buffer contains the array of VLAN_TCI.
+ @param[in] NumberOfVlan Pointer to number of VLAN.
+ @param[out] NewNumberOfVlan Pointer to number of unique VLAN.
+
+ @retval EFI_SUCCESS The VLAN variable is successfully checked.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource to set the configuration.
+
+**/
+EFI_STATUS
+MnpCheckVlanVariable (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN VLAN_TCI *Buffer,
+ IN UINTN NumberOfVlan,
+ OUT UINTN *NewNumberOfVlan
+ )
+{
+ UINTN Index;
+ UINTN Index2;
+ UINTN Count;
+ BOOLEAN FoundDuplicateItem;
+ EFI_STATUS Status;
+
+ Count = 0;
+ FoundDuplicateItem = FALSE;
+ Status = EFI_SUCCESS;
+
+ for (Index = 0; Index < NumberOfVlan; Index++) {
+ for (Index2 = Index + 1; Index2 < NumberOfVlan; Index2++) {
+ if (Buffer[Index].Bits.Vid == Buffer[Index2].Bits.Vid) {
+ FoundDuplicateItem = TRUE;
+ Count++;
+ break;
+ }
+ }
+ if (FoundDuplicateItem) {
+ for (Index2 = Index +1; Index2 < NumberOfVlan; Index++, Index2++) {
+ CopyMem (Buffer + Index, Buffer + Index2, sizeof (VLAN_TCI));
+ }
+ }
+ FoundDuplicateItem = FALSE;
+ }
+
+ *NewNumberOfVlan = NumberOfVlan - Count;
+ if (Count != 0) {
+ Status = MnpSetVlanVariable (MnpDeviceData, *NewNumberOfVlan, Buffer);
+ }
+
+ return Status;
+}
+
+/**
+ Get VLAN configuration variable.
+
+ @param[in] MnpDeviceData Pointer to the MNP device context data.
+ @param[out] NumberOfVlan Pointer to number of VLAN to be returned.
+ @param[out] VlanVariable Pointer to the buffer to return requested
+ array of VLAN_TCI.
+
+ @retval EFI_SUCCESS The array of VLAN_TCI was returned in VlanVariable
+ and number of VLAN was returned in NumberOfVlan.
+ @retval EFI_NOT_FOUND VLAN configuration variable not found.
+ @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the configuration.
+
+**/
+EFI_STATUS
+MnpGetVlanVariable (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ OUT UINTN *NumberOfVlan,
+ OUT VLAN_TCI **VlanVariable
+ )
+{
+ UINTN BufferSize;
+ EFI_STATUS Status;
+ VLAN_TCI *Buffer;
+ UINTN NewNumberOfVlan;
+
+ //
+ // Get VLAN configuration from EFI Variable
+ //
+ Buffer = NULL;
+ BufferSize = 0;
+ Status = gRT->GetVariable (
+ MnpDeviceData->MacString,
+ &gEfiVlanConfigProtocolGuid,
+ NULL,
+ &BufferSize,
+ NULL
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Allocate buffer to read the variable
+ //
+ Buffer = AllocateZeroPool (BufferSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gRT->GetVariable (
+ MnpDeviceData->MacString,
+ &gEfiVlanConfigProtocolGuid,
+ NULL,
+ &BufferSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Status = MnpCheckVlanVariable (MnpDeviceData, Buffer, BufferSize / sizeof (VLAN_TCI), &NewNumberOfVlan);
+ if (!EFI_ERROR (Status)) {
+ *NumberOfVlan = NewNumberOfVlan;
+ *VlanVariable = Buffer;
+ }
+
+ return Status;
+}
+
+/**
+ Set VLAN configuration variable.
+
+ @param[in] MnpDeviceData Pointer to the MNP device context data.
+ @param[in] NumberOfVlan Number of VLAN in array VlanVariable.
+ @param[in] VlanVariable Pointer to array of VLAN_TCI.
+
+ @retval EFI_SUCCESS The VLAN variable is successfully set.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource to set the configuration.
+
+**/
+EFI_STATUS
+MnpSetVlanVariable (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINTN NumberOfVlan,
+ IN VLAN_TCI *VlanVariable
+ )
+{
+ return gRT->SetVariable (
+ MnpDeviceData->MacString,
+ &gEfiVlanConfigProtocolGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ NumberOfVlan * sizeof (VLAN_TCI),
+ VlanVariable
+ );
+}
+
+
+/**
+ Create a VLAN device or modify the configuration parameter of an
+ already-configured VLAN.
+
+ The Set() function is used to create a new VLAN device or change the VLAN
+ configuration parameters. If the VlanId hasn't been configured in the
+ physical Ethernet device, a new VLAN device will be created. If a VLAN with
+ this VlanId is already configured, then related configuration will be updated
+ as the input parameters.
+
+ If VlanId is zero, the VLAN device will send and receive untagged frames.
+ Otherwise, the VLAN device will send and receive VLAN-tagged frames containing the VlanId.
+ If VlanId is out of scope of (0-4094), EFI_INVALID_PARAMETER is returned.
+ If Priority is out of the scope of (0-7), then EFI_INVALID_PARAMETER is returned.
+ If there is not enough system memory to perform the registration, then
+ EFI_OUT_OF_RESOURCES is returned.
+
+ @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL.
+ @param[in] VlanId A unique identifier (1-4094) of the VLAN which is being created
+ or modified, or zero (0).
+ @param[in] Priority 3 bit priority in VLAN header. Priority 0 is default value. If
+ VlanId is zero (0), Priority is ignored.
+
+ @retval EFI_SUCCESS The VLAN is successfully configured.
+ @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE:
+ - This is NULL.
+ - VlanId is an invalid VLAN Identifier.
+ - Priority is invalid.
+ @retval EFI_OUT_OF_RESOURCES There is not enough system memory to perform the registration.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigSet (
+ IN EFI_VLAN_CONFIG_PROTOCOL *This,
+ IN UINT16 VlanId,
+ IN UINT8 Priority
+ )
+{
+ EFI_STATUS Status;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_SERVICE_DATA *MnpServiceData;
+ VLAN_TCI *OldVariable;
+ VLAN_TCI *NewVariable;
+ UINTN NumberOfVlan;
+ UINTN Index;
+ BOOLEAN IsAdd;
+ LIST_ENTRY *Entry;
+
+ if ((This == NULL) || (VlanId > 4094) || (Priority > 7)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IsAdd = FALSE;
+ MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);
+ if (MnpDeviceData->NumberOfVlan == 0) {
+ //
+ // No existing VLAN, this is the first VLAN to add
+ //
+ IsAdd = TRUE;
+ Entry = GetFirstNode (&MnpDeviceData->ServiceList);
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+
+ if (VlanId != 0) {
+ //
+ // VlanId is not 0, need destroy the default MNP service data
+ //
+ Status = MnpDestroyServiceChild (MnpServiceData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = MnpDestroyServiceData (MnpServiceData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a new MNP service data for this VLAN
+ //
+ MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);
+ if (MnpServiceData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+ } else {
+ //
+ // Try to find VlanId in existing VLAN list
+ //
+ MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);
+ if (MnpServiceData == NULL) {
+ //
+ // VlanId not found, create a new MNP service data
+ //
+ IsAdd = TRUE;
+ MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);
+ if (MnpServiceData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+ }
+
+ MnpServiceData->VlanId = VlanId;
+ MnpServiceData->Priority = Priority;
+ if (IsAdd) {
+ MnpDeviceData->NumberOfVlan++;
+ }
+
+ //
+ // Update VLAN configuration variable
+ //
+ OldVariable = NULL;
+ NewVariable = NULL;
+ NumberOfVlan = 0;
+ MnpGetVlanVariable (MnpDeviceData, &NumberOfVlan, &OldVariable);
+
+ if (IsAdd) {
+ //
+ // VLAN not exist - add
+ //
+ NewVariable = AllocateZeroPool ((NumberOfVlan + 1) * sizeof (VLAN_TCI));
+ if (NewVariable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ if (OldVariable != NULL) {
+ CopyMem (NewVariable, OldVariable, NumberOfVlan * sizeof (VLAN_TCI));
+ }
+
+ Index = NumberOfVlan++;
+ } else {
+ //
+ // VLAN already exist - update
+ //
+ for (Index = 0; Index < NumberOfVlan; Index++) {
+ if (OldVariable[Index].Bits.Vid == VlanId) {
+ break;
+ }
+ }
+ ASSERT (Index < NumberOfVlan);
+
+ NewVariable = OldVariable;
+ OldVariable = NULL;
+ }
+
+ NewVariable[Index].Bits.Vid = VlanId;
+ NewVariable[Index].Bits.Priority = Priority;
+
+ Status = MnpSetVlanVariable (MnpDeviceData, NumberOfVlan, NewVariable);
+ FreePool (NewVariable);
+
+Exit:
+ if (OldVariable != NULL) {
+ FreePool (OldVariable);
+ }
+
+ return Status;
+}
+
+
+/**
+ Find configuration information for specified VLAN or all configured VLANs.
+
+ The Find() function is used to find the configuration information for matching
+ VLAN and allocate a buffer into which those entries are copied.
+
+ @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL.
+ @param[in] VlanId Pointer to VLAN identifier. Set to NULL to find all
+ configured VLANs.
+ @param[out] NumberOfVlan The number of VLANs which is found by the specified criteria.
+ @param[out] Entries The buffer which receive the VLAN configuration.
+
+ @retval EFI_SUCCESS The VLAN is successfully found.
+ @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE:
+ - This is NULL.
+ - Specified VlanId is invalid.
+ @retval EFI_NOT_FOUND No matching VLAN is found.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigFind (
+ IN EFI_VLAN_CONFIG_PROTOCOL *This,
+ IN UINT16 *VlanId OPTIONAL,
+ OUT UINT16 *NumberOfVlan,
+ OUT EFI_VLAN_FIND_DATA **Entries
+ )
+{
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_SERVICE_DATA *MnpServiceData;
+ LIST_ENTRY *Entry;
+ EFI_VLAN_FIND_DATA *VlanData;
+
+ if ((This == NULL) || (VlanId != NULL && *VlanId > 4094) || (NumberOfVlan == NULL) || (Entries == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *NumberOfVlan = 0;
+ *Entries = NULL;
+
+ MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);
+ if (MnpDeviceData->NumberOfVlan == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (VlanId == NULL) {
+ //
+ // Return all current VLAN configuration
+ //
+ *NumberOfVlan = (UINT16) MnpDeviceData->NumberOfVlan;
+ VlanData = AllocateZeroPool (*NumberOfVlan * sizeof (EFI_VLAN_FIND_DATA));
+ if (VlanData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *Entries = VlanData;
+ NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+
+ VlanData->VlanId = MnpServiceData->VlanId;
+ VlanData->Priority = MnpServiceData->Priority;
+ VlanData++;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // VlanId is specified, try to find it in current VLAN list
+ //
+ MnpServiceData = MnpFindServiceData (MnpDeviceData, *VlanId);
+ if (MnpServiceData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ VlanData = AllocateZeroPool (sizeof (EFI_VLAN_FIND_DATA));
+ if (VlanData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ VlanData->VlanId = MnpServiceData->VlanId;
+ VlanData->Priority = MnpServiceData->Priority;
+
+ *NumberOfVlan = 1;
+ *Entries = VlanData;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Remove the configured VLAN device.
+
+ The Remove() function is used to remove the specified VLAN device.
+ If the VlanId is out of the scope of (0-4094), EFI_INVALID_PARAMETER is returned.
+ If specified VLAN hasn't been previously configured, EFI_NOT_FOUND is returned.
+
+ @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL.
+ @param[in] VlanId Identifier (0-4094) of the VLAN to be removed.
+
+ @retval EFI_SUCCESS The VLAN is successfully removed.
+ @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE:
+ - This is NULL.
+ - VlanId is an invalid parameter.
+ @retval EFI_NOT_FOUND The to-be-removed VLAN does not exist.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigRemove (
+ IN EFI_VLAN_CONFIG_PROTOCOL *This,
+ IN UINT16 VlanId
+ )
+{
+ EFI_STATUS Status;
+ MNP_DEVICE_DATA *MnpDeviceData;
+ MNP_SERVICE_DATA *MnpServiceData;
+ LIST_ENTRY *Entry;
+ VLAN_TCI *VlanVariable;
+ VLAN_TCI *VlanData;
+
+ if ((This == NULL) || (VlanId > 4094)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);
+ if (MnpDeviceData->NumberOfVlan == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Try to find the VlanId
+ //
+ MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);
+ if (MnpServiceData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ MnpDeviceData->NumberOfVlan--;
+
+ if ((VlanId != 0) || (MnpDeviceData->NumberOfVlan != 0)) {
+ //
+ // If VlanId is not 0 or VlanId is 0 and it is not the last VLAN to remove,
+ // destroy its MNP service data
+ //
+ Status = MnpDestroyServiceChild (MnpServiceData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = MnpDestroyServiceData (MnpServiceData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if ((VlanId != 0) && (MnpDeviceData->NumberOfVlan == 0)) {
+ //
+ // This is the last VLAN to be removed, restore the default MNP service data
+ //
+ MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);
+ if (MnpServiceData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ //
+ // Update VLAN configuration variable
+ //
+ VlanVariable = NULL;
+ if (MnpDeviceData->NumberOfVlan != 0) {
+ VlanVariable = AllocatePool (MnpDeviceData->NumberOfVlan * sizeof (VLAN_TCI));
+ if (VlanVariable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ VlanData = VlanVariable;
+ NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {
+ MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);
+
+ VlanData->Bits.Vid = MnpServiceData->VlanId;
+ VlanData->Bits.Priority = MnpServiceData->Priority;
+ VlanData++;
+ }
+ }
+
+ Status = MnpSetVlanVariable (MnpDeviceData, MnpDeviceData->NumberOfVlan, VlanVariable);
+
+ if (VlanVariable != NULL) {
+ FreePool (VlanVariable);
+ }
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.h b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.h
new file mode 100644
index 0000000000..d8eac8d07a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.h
@@ -0,0 +1,212 @@
+/** @file
+ Header file to be included by MnpVlan.c.
+
+Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __MNP_VLAN_H__
+#define __MNP_VLAN_H__
+
+#include "MnpDriver.h"
+
+extern EFI_VLAN_CONFIG_PROTOCOL mVlanConfigProtocolTemplate;
+
+
+/**
+ Create a child handle for the VLAN ID.
+
+ @param[in] ImageHandle The driver image handle.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] VlanId The VLAN ID.
+ @param[out] Devicepath Pointer to returned device path for child handle.
+
+ @return The handle of VLAN child or NULL if failed to create VLAN child.
+
+**/
+EFI_HANDLE
+MnpCreateVlanChild (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT16 VlanId,
+ OUT EFI_DEVICE_PATH_PROTOCOL **Devicepath OPTIONAL
+ );
+
+/**
+ Remove VLAN tag of a packet.
+
+ @param[in, out] MnpDeviceData Pointer to the mnp device context data.
+ @param[in, out] Nbuf Pointer to the NET_BUF to remove VLAN tag.
+ @param[out] VlanId Pointer to the returned VLAN ID.
+
+ @retval TRUE VLAN tag is removed from this packet.
+ @retval FALSE There is no VLAN tag in this packet.
+
+**/
+BOOLEAN
+MnpRemoveVlanTag (
+ IN OUT MNP_DEVICE_DATA *MnpDeviceData,
+ IN OUT NET_BUF *Nbuf,
+ OUT UINT16 *VlanId
+ );
+
+/**
+ Build the vlan packet to transmit from the TxData passed in.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param TxData Pointer to the transmit data containing the
+ information to build the packet.
+ @param ProtocolType Pointer to the Ethernet protocol type.
+ @param Packet Pointer to record the address of the packet.
+ @param Length Pointer to a UINT32 variable used to record the
+ packet's length.
+
+**/
+VOID
+MnpInsertVlanTag (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData,
+ OUT UINT16 *ProtocolType,
+ IN OUT UINT8 **Packet,
+ IN OUT UINT32 *Length
+ );
+
+/**
+ Get VLAN configuration variable.
+
+ @param[in] MnpDeviceData Pointer to the MNP device context data.
+ @param[out] NumberOfVlan Pointer to number of VLAN to be returned.
+ @param[out] VlanVariable Pointer to the buffer to return requested
+ array of VLAN_TCI.
+
+ @retval EFI_SUCCESS The array of VLAN_TCI was returned in VlanVariable
+ and number of VLAN was returned in NumberOfVlan.
+ @retval EFI_NOT_FOUND VLAN configuration variable not found.
+ @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the configuration.
+
+**/
+EFI_STATUS
+MnpGetVlanVariable (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ OUT UINTN *NumberOfVlan,
+ OUT VLAN_TCI **VlanVariable
+ );
+
+/**
+ Set VLAN configuration variable.
+
+ @param[in] MnpDeviceData Pointer to the MNP device context data.
+ @param[in] NumberOfVlan Number of VLAN in array VlanVariable.
+ @param[in] VlanVariable Pointer to array of VLAN_TCI.
+
+ @retval EFI_SUCCESS The VLAN variable is successfully set.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource to set the configuration.
+
+**/
+EFI_STATUS
+MnpSetVlanVariable (
+ IN MNP_DEVICE_DATA *MnpDeviceData,
+ IN UINTN NumberOfVlan,
+ IN VLAN_TCI *VlanVariable
+ );
+
+/**
+ Create a VLAN device or modify the configuration parameter of an
+ already-configured VLAN.
+
+ The Set() function is used to create a new VLAN device or change the VLAN
+ configuration parameters. If the VlanId hasn't been configured in the
+ physical Ethernet device, a new VLAN device will be created. If a VLAN with
+ this VlanId is already configured, then related configuration will be updated
+ as the input parameters.
+
+ If VlanId is zero, the VLAN device will send and receive untagged frames.
+ Otherwise, the VLAN device will send and receive VLAN-tagged frames containing the VlanId.
+ If VlanId is out of scope of (0-4094), EFI_INVALID_PARAMETER is returned.
+ If Priority is out of the scope of (0-7), then EFI_INVALID_PARAMETER is returned.
+ If there is not enough system memory to perform the registration, then
+ EFI_OUT_OF_RESOURCES is returned.
+
+ @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL.
+ @param[in] VlanId A unique identifier (1-4094) of the VLAN which is being created
+ or modified, or zero (0).
+ @param[in] Priority 3 bit priority in VLAN header. Priority 0 is default value. If
+ VlanId is zero (0), Priority is ignored.
+
+ @retval EFI_SUCCESS The VLAN is successfully configured.
+ @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE:
+ - This is NULL.
+ - VlanId is an invalid VLAN Identifier.
+ - Priority is invalid.
+ @retval EFI_OUT_OF_RESOURCES There is not enough system memory to perform the registration.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigSet (
+ IN EFI_VLAN_CONFIG_PROTOCOL *This,
+ IN UINT16 VlanId,
+ IN UINT8 Priority
+ );
+
+/**
+ Find configuration information for specified VLAN or all configured VLANs.
+
+ The Find() function is used to find the configuration information for matching
+ VLAN and allocate a buffer into which those entries are copied.
+
+ @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL.
+ @param[in] VlanId Pointer to VLAN identifier. Set to NULL to find all
+ configured VLANs.
+ @param[out] NumberOfVlan The number of VLANs which is found by the specified criteria.
+ @param[out] Entries The buffer which receive the VLAN configuration.
+
+ @retval EFI_SUCCESS The VLAN is successfully found.
+ @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE:
+ - This is NULL.
+ - Specified VlanId is invalid.
+ @retval EFI_NOT_FOUND No matching VLAN is found.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigFind (
+ IN EFI_VLAN_CONFIG_PROTOCOL *This,
+ IN UINT16 *VlanId OPTIONAL,
+ OUT UINT16 *NumberOfVlan,
+ OUT EFI_VLAN_FIND_DATA **Entries
+ );
+
+/**
+ Remove the configured VLAN device.
+
+ The Remove() function is used to remove the specified VLAN device.
+ If the VlanId is out of the scope of (0-4094), EFI_INVALID_PARAMETER is returned.
+ If specified VLAN hasn't been previously configured, EFI_NOT_FOUND is returned.
+
+ @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL.
+ @param[in] VlanId Identifier (0-4094) of the VLAN to be removed.
+
+ @retval EFI_SUCCESS The VLAN is successfully removed.
+ @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE:
+ - This is NULL.
+ - VlanId is an invalid parameter.
+ @retval EFI_NOT_FOUND The to-be-removed VLAN does not exist.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigRemove (
+ IN EFI_VLAN_CONFIG_PROTOCOL *This,
+ IN UINT16 VlanId
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..901c2df064
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c
@@ -0,0 +1,431 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Mtftp4Dxe driver.
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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
+Mtftp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+Mtftp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+///
+/// EFI Component Name Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName = {
+ Mtftp4ComponentNameGetDriverName,
+ Mtftp4ComponentNameGetControllerName,
+ "eng"
+};
+
+///
+/// EFI Component Name 2 Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp4ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp4ComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp4DriverNameTable[] = {
+ {
+ "eng;en",
+ L"MTFTP4 Network Service"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gMtftp4ControllerNameTable = NULL;
+
+/**
+ 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
+Mtftp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mMtftp4DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gMtftp4ComponentName)
+ );
+}
+
+/**
+ Update the component name for the Mtftp4 child handle.
+
+ @param Mtftp4[in] A pointer to the EFI_MTFTP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ EFI_MTFTP4_MODE_DATA ModeData;
+
+ if (Mtftp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer as:
+ // MTFTPv4 (ServerIp=192.168.1.10, ServerPort=69)
+ //
+ Status = Mtftp4->GetModeData (Mtftp4, &ModeData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"MTFTPv4 (ServerIp=%d.%d.%d.%d, ServerPort=%d)",
+ ModeData.ConfigData.ServerIp.Addr[0],
+ ModeData.ConfigData.ServerIp.Addr[1],
+ ModeData.ConfigData.ServerIp.Addr[2],
+ ModeData.ConfigData.ServerIp.Addr[3],
+ ModeData.ConfigData.InitialServerPort
+ );
+
+ if (gMtftp4ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gMtftp4ControllerNameTable);
+ gMtftp4ControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gMtftp4ComponentName.SupportedLanguages,
+ &gMtftp4ControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gMtftp4ComponentName2.SupportedLanguages,
+ &gMtftp4ControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+Mtftp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **)&Mtftp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Mtftp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gMtftp4ControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gMtftp4ComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c
new file mode 100644
index 0000000000..a23d405baa
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c
@@ -0,0 +1,723 @@
+/** @file
+ Implementation of Mtftp drivers.
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding = {
+ Mtftp4DriverBindingSupported,
+ Mtftp4DriverBindingStart,
+ Mtftp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gMtftp4ServiceBindingTemplete = {
+ Mtftp4ServiceBindingCreateChild,
+ Mtftp4ServiceBindingDestroyChild
+};
+
+
+/**
+ The driver entry point which installs multiple protocols to the ImageHandle.
+
+ @param ImageHandle The MTFTP's image handle.
+ @param SystemTable The system table.
+
+ @retval EFI_SUCCESS The handles are successfully installed on the image.
+ @retval others some EFI_ERROR occured.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gMtftp4DriverBinding,
+ ImageHandle,
+ &gMtftp4ComponentName,
+ &gMtftp4ComponentName2
+ );
+}
+
+
+/**
+ Test whether MTFTP driver support this controller.
+
+ @param This The MTFTP driver binding instance
+ @param Controller The controller to test
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS The controller has UDP service binding protocol
+ installed, MTFTP can support it.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by
+ the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a
+ different driver or an application that requires
+ exclusive access.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver
+ specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Config a NULL UDP that is used to keep the connection between UDP and MTFTP.
+
+ Just leave the Udp child unconfigured. When UDP is unloaded,
+ MTFTP will be informed with DriverBinding Stop.
+
+ @param UdpIo The UDP_IO to configure
+ @param Context The opaque parameter to the callback
+
+ @retval EFI_SUCCESS It always return EFI_SUCCESS directly.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ConfigNullUdp (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create then initialize a MTFTP service binding instance.
+
+ @param Controller The controller to install the MTFTP service
+ binding on
+ @param Image The driver binding image of the MTFTP driver
+ @param Service The variable to receive the created service
+ binding instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance
+ @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep
+ connection with UDP.
+ @retval EFI_SUCCESS The service instance is created for the
+ controller.
+
+**/
+EFI_STATUS
+Mtftp4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ OUT MTFTP4_SERVICE **Service
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_STATUS Status;
+
+ *Service = NULL;
+ MtftpSb = AllocatePool (sizeof (MTFTP4_SERVICE));
+
+ if (MtftpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MtftpSb->Signature = MTFTP4_SERVICE_SIGNATURE;
+ MtftpSb->ServiceBinding = gMtftp4ServiceBindingTemplete;
+ MtftpSb->ChildrenNum = 0;
+ InitializeListHead (&MtftpSb->Children);
+
+ MtftpSb->Timer = NULL;
+ MtftpSb->TimerToGetMap = NULL;
+ MtftpSb->Controller = Controller;
+ MtftpSb->Image = Image;
+ MtftpSb->ConnectUdp = NULL;
+
+ //
+ // Create the timer and a udp to be notified when UDP is uninstalled
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Mtftp4OnTimerTick,
+ MtftpSb,
+ &MtftpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (MtftpSb);
+ return Status;
+ }
+
+ //
+ // Create the timer used to time out the procedure which is used to
+ // get the default IP address.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &MtftpSb->TimerToGetMap
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (MtftpSb->Timer);
+ FreePool (MtftpSb);
+ return Status;
+ }
+
+ MtftpSb->ConnectUdp = UdpIoCreateIo (
+ Controller,
+ Image,
+ Mtftp4ConfigNullUdp,
+ UDP_IO_UDP4_VERSION,
+ NULL
+ );
+
+ if (MtftpSb->ConnectUdp == NULL) {
+ gBS->CloseEvent (MtftpSb->TimerToGetMap);
+ gBS->CloseEvent (MtftpSb->Timer);
+ FreePool (MtftpSb);
+ return EFI_DEVICE_ERROR;
+ }
+
+ *Service = MtftpSb;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Release all the resource used the MTFTP service binding instance.
+
+ @param MtftpSb The MTFTP service binding instance.
+
+**/
+VOID
+Mtftp4CleanService (
+ IN MTFTP4_SERVICE *MtftpSb
+ )
+{
+ UdpIoFreeIo (MtftpSb->ConnectUdp);
+ gBS->CloseEvent (MtftpSb->TimerToGetMap);
+ gBS->CloseEvent (MtftpSb->Timer);
+}
+
+
+/**
+ Start the MTFTP driver on this controller.
+
+ MTFTP driver will install a MTFTP SERVICE BINDING protocol on the supported
+ controller, which can be used to create/destroy MTFTP children.
+
+ @param This The MTFTP driver binding protocol.
+ @param Controller The controller to manage.
+ @param RemainingDevicePath Remaining device path.
+
+ @retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
+ started on the controller.
+ @retval EFI_SUCCESS The MTFTP service binding is installed on the
+ controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_STATUS Status;
+
+ //
+ // Directly return if driver is already running.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Mtftp4CreateService (Controller, This->DriverBindingHandle, &MtftpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ASSERT (MtftpSb != NULL);
+
+ Status = gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Mtftp4ServiceBinding Protocol onto Controller
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ &MtftpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Mtftp4CleanService (MtftpSb);
+ FreePool (MtftpSb);
+
+ return Status;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NET_LIST_USER_STRUCT_S (Entry, MTFTP4_PROTOCOL, Link, MTFTP4_PROTOCOL_SIGNATURE);
+ ServiceBinding = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
+ NumberOfChildren = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
+ ChildHandleBuffer = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
+
+ if (!NetIsInHandleBuffer (Instance->Handle, NumberOfChildren, ChildHandleBuffer)) {
+ return EFI_SUCCESS;
+ }
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+}
+
+/**
+ Stop the MTFTP driver on controller. The controller is a UDP
+ child handle.
+
+ @param This The MTFTP driver binding protocol
+ @param Controller The controller to stop
+ @param NumberOfChildren The number of children
+ @param ChildHandleBuffer The array of the child handle.
+
+ @retval EFI_SUCCESS The driver is stopped on the controller.
+ @retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ LIST_ENTRY *List;
+ MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
+
+ //
+ // MTFTP driver opens UDP child, So, Controller is a UDP
+ // child handle. Locate the Nic handle first. Then get the
+ // MTFTP private data back.
+ //
+ NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (!IsListEmpty (&MtftpSb->Children)) {
+ //
+ // Destroy the Mtftp4 child instance in ChildHandleBuffer.
+ //
+ List = &MtftpSb->Children;
+ Context.ServiceBinding = ServiceBinding;
+ Context.NumberOfChildren = NumberOfChildren;
+ Context.ChildHandleBuffer = ChildHandleBuffer;
+ Status = NetDestroyLinkList (
+ List,
+ Mtftp4DestroyChildEntryInHandleBuffer,
+ &Context,
+ NULL
+ );
+ }
+
+ if (NumberOfChildren == 0 && IsListEmpty (&MtftpSb->Children)) {
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ Mtftp4CleanService (MtftpSb);
+ if (gMtftp4ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gMtftp4ControllerNameTable);
+ gMtftp4ControllerNameTable = NULL;
+ }
+ FreePool (MtftpSb);
+
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Initialize a MTFTP protocol instance which is the child of MtftpSb.
+
+ @param MtftpSb The MTFTP service binding protocol.
+ @param Instance The MTFTP instance to initialize.
+
+**/
+VOID
+Mtftp4InitProtocol (
+ IN MTFTP4_SERVICE *MtftpSb,
+ OUT MTFTP4_PROTOCOL *Instance
+ )
+{
+ ZeroMem (Instance, sizeof (MTFTP4_PROTOCOL));
+
+ Instance->Signature = MTFTP4_PROTOCOL_SIGNATURE;
+ InitializeListHead (&Instance->Link);
+ CopyMem (&Instance->Mtftp4, &gMtftp4ProtocolTemplate, sizeof (Instance->Mtftp4));
+ Instance->State = MTFTP4_STATE_UNCONFIGED;
+ Instance->Service = MtftpSb;
+
+ InitializeListHead (&Instance->Blocks);
+}
+
+
+/**
+ Create a MTFTP child for the service binding instance, then
+ install the MTFTP protocol to the ChildHandle.
+
+ @param This The MTFTP service binding instance.
+ @param ChildHandle The Child handle to install the MTFTP protocol.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
+ @retval EFI_SUCCESS The child is successfully create.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = AllocatePool (sizeof (*Instance));
+
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
+
+ Mtftp4InitProtocol (MtftpSb, Instance);
+
+ Instance->UnicastPort = UdpIoCreateIo (
+ MtftpSb->Controller,
+ MtftpSb->Image,
+ Mtftp4ConfigNullUdp,
+ UDP_IO_UDP4_VERSION,
+ Instance
+ );
+
+ if (Instance->UnicastPort == NULL) {
+ FreePool (Instance);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Install the MTFTP protocol onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ &Instance->Mtftp4,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ UdpIoFreeIo (Instance->UnicastPort);
+ FreePool (Instance);
+ return Status;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp4 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open the Udp4 protocol by child.
+ //
+ Status = gBS->OpenProtocol (
+ Instance->UnicastPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+ goto ON_ERROR;
+ }
+
+ //
+ // Add it to the parent's child list.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&MtftpSb->Children, &Instance->Link);
+ MtftpSb->ChildrenNum++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Instance->Handle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiMtftp4ProtocolGuid,
+ &Instance->Mtftp4,
+ NULL
+ );
+ }
+
+ UdpIoFreeIo (Instance->UnicastPort);
+ FreePool (Instance);
+
+ return Status;
+}
+
+
+/**
+ Destroy one of the service binding's child.
+
+ @param This The service binding instance
+ @param ChildHandle The child handle to destroy
+
+ @retval EFI_INVALID_PARAMETER The parameter is invaid.
+ @retval EFI_UNSUPPORTED The child may have already been destroyed.
+ @retval EFI_SUCCESS The child is destroyed and removed from the
+ parent's child list.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **) &Mtftp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (Mtftp4);
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != MtftpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Instance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ Instance->InDestroy = TRUE;
+
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ gBS->CloseProtocol (
+ Instance->UnicastPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ if (Instance->McastUdpPort != NULL) {
+ gBS->CloseProtocol (
+ Instance->McastUdpPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+ }
+
+ //
+ // Uninstall the MTFTP4 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ Mtftp4
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestroy = FALSE;
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR);
+ UdpIoFreeIo (Instance->UnicastPort);
+
+ RemoveEntryList (&Instance->Link);
+ MtftpSb->ChildrenNum--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h
new file mode 100644
index 0000000000..936eab1ba1
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h
@@ -0,0 +1,137 @@
+/** @file
+ Mtftp drivers function header.
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_MTFTP4_DRIVER_H__
+#define __EFI_MTFTP4_DRIVER_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+
+extern EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp4ComponentName2;
+extern EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding;
+extern EFI_UNICODE_STRING_TABLE *gMtftp4ControllerNameTable;
+
+/**
+ Test whether MTFTP driver support this controller.
+
+ @param This The MTFTP driver binding instance
+ @param Controller The controller to test
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS The controller has UDP service binding protocol
+ installed, MTFTP can support it.
+ @retval Others MTFTP can't support the controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Start the MTFTP driver on this controller.
+
+ MTFTP driver will install a MTFTP SERVICE BINDING protocol on the supported
+ controller, which can be used to create/destroy MTFTP children.
+
+ @param This The MTFTP driver binding protocol.
+ @param Controller The controller to manage.
+ @param RemainingDevicePath Remaining device path.
+
+ @retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
+ started on the controller.
+ @retval EFI_SUCCESS The MTFTP service binding is installed on the
+ controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Stop the MTFTP driver on controller. The controller is a UDP
+ child handle.
+
+ @param This The MTFTP driver binding protocol
+ @param Controller The controller to stop
+ @param NumberOfChildren The number of children
+ @param ChildHandleBuffer The array of the child handle.
+
+ @retval EFI_SUCCESS The driver is stopped on the controller.
+ @retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Create a MTFTP child for the service binding instance, then
+ install the MTFTP protocol to the ChildHandle.
+
+ @param This The MTFTP service binding instance.
+ @param ChildHandle The Child handle to install the MTFTP protocol.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
+ @retval EFI_SUCCESS The child is successfully create.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroy one of the service binding's child.
+
+ @param This The service binding instance
+ @param ChildHandle The child handle to destroy
+
+ @retval EFI_INVALID_PARAMETER The parameter is invaid.
+ @retval EFI_UNSUPPORTED The child may have already been destroyed.
+ @retval EFI_SUCCESS The child is destroyed and removed from the
+ parent's child list.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf
new file mode 100644
index 0000000000..3d7c8c00e0
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf
@@ -0,0 +1,75 @@
+## @file
+# This module produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol.
+#
+# This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide
+# basic services for client-side unicast and/or multicase TFTP operations.
+#
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Mtftp4Dxe
+ MODULE_UNI_FILE = Mtftp4Dxe.uni
+ FILE_GUID = DC3641B8-2FA8-4ed3-BC1F-F9962A03454B
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Mtftp4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gMtftp4DriverBinding
+# COMPONENT_NAME = gMtftp4ComponentName
+# COMPONENT_NAME2 = gMtftp4ComponentName2
+#
+
+[Sources]
+ Mtftp4Option.c
+ Mtftp4Rrq.c
+ Mtftp4Impl.h
+ ComponentName.c
+ Mtftp4Support.c
+ Mtftp4Impl.c
+ Mtftp4Option.h
+ Mtftp4Support.h
+ Mtftp4Driver.h
+ Mtftp4Driver.c
+ Mtftp4Wrq.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+ MemoryAllocationLib
+ BaseMemoryLib
+
+
+[Protocols]
+ gEfiMtftp4ServiceBindingProtocolGuid ## BY_START
+ gEfiUdp4ServiceBindingProtocolGuid ## TO_START
+ gEfiMtftp4ProtocolGuid ## BY_START
+ gEfiUdp4ProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Mtftp4DxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.uni b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.uni
new file mode 100644
index 0000000000..74f7ff7fed
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// This module produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol.
+//
+// This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide
+// basic services for client-side unicast and/or multicase TFTP operations.
+//
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide basic services for client-side unicast or multicase TFTP operations or both."
+
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni
new file mode 100644
index 0000000000..6d69022e70
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Mtftp4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"MTFTP v4 DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c
new file mode 100644
index 0000000000..5494231166
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c
@@ -0,0 +1,1103 @@
+/** @file
+ Interface routine for Mtftp4.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ Clean up the MTFTP session to get ready for new operation.
+
+ @param Instance The MTFTP session to clean up
+ @param Result The result to return to the caller who initiated
+ the operation.
+**/
+VOID
+Mtftp4CleanOperation (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_STATUS Result
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP4_BLOCK_RANGE *Block;
+ EFI_MTFTP4_TOKEN *Token;
+
+ //
+ // Free various resources.
+ //
+ Token = Instance->Token;
+
+ if (Token != NULL) {
+ Token->Status = Result;
+
+ if (Token->Event != NULL) {
+ gBS->SignalEvent (Token->Event);
+ }
+
+ Instance->Token = NULL;
+ }
+
+ ASSERT (Instance->UnicastPort != NULL);
+ UdpIoCleanIo (Instance->UnicastPort);
+
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ Instance->LastPacket = NULL;
+ }
+
+ if (Instance->McastUdpPort != NULL) {
+ gBS->CloseProtocol (
+ Instance->McastUdpPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->McastUdpPort);
+ Instance->McastUdpPort = NULL;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->Blocks) {
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
+ RemoveEntryList (Entry);
+ FreePool (Block);
+ }
+
+ ZeroMem (&Instance->RequestOption, sizeof (MTFTP4_OPTION));
+
+ Instance->Operation = 0;
+
+ Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;
+ Instance->LastBlock = 0;
+ Instance->ServerIp = 0;
+ Instance->ListeningPort = 0;
+ Instance->ConnectedPort = 0;
+ Instance->Gateway = 0;
+ Instance->PacketToLive = 0;
+ Instance->MaxRetry = 0;
+ Instance->CurRetry = 0;
+ Instance->Timeout = 0;
+ Instance->McastIp = 0;
+ Instance->McastPort = 0;
+ Instance->Master = TRUE;
+}
+
+
+/**
+ Check packet for GetInfo.
+
+ GetInfo is implemented with EfiMtftp4ReadFile. It use Mtftp4GetInfoCheckPacket
+ to inspect the first packet from server, then abort the session.
+
+ @param This The MTFTP4 protocol instance
+ @param Token The user's token
+ @param PacketLen The length of the packet
+ @param Packet The received packet.
+
+ @retval EFI_ABORTED Abort the ReadFile operation and return.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4GetInfoCheckPacket (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet
+ )
+{
+ MTFTP4_GETINFO_STATE *State;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+ EFI_MTFTP4_ERROR_HEADER *ErrorHeader;
+
+ State = (MTFTP4_GETINFO_STATE *) Token->Context;
+ OpCode = NTOHS (Packet->OpCode);
+
+ //
+ // Set the GetInfo's return status according to the OpCode.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP4_OPCODE_ERROR:
+ ErrorHeader = (EFI_MTFTP4_ERROR_HEADER *) Packet;
+ if (ErrorHeader->ErrorCode == EFI_MTFTP4_ERRORCODE_FILE_NOT_FOUND) {
+ DEBUG ((EFI_D_ERROR, "TFTP error code 1 (File Not Found)\n"));
+ } else {
+ DEBUG ((EFI_D_ERROR, "TFTP error code %d\n", ErrorHeader->ErrorCode));
+ }
+ State->Status = EFI_TFTP_ERROR;
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ State->Status = EFI_SUCCESS;
+ break;
+
+ default:
+ State->Status = EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Allocate buffer then copy the packet over. Use gBS->AllocatePool
+ // in case AllocatePool will implements something tricky.
+ //
+ Status = gBS->AllocatePool (EfiBootServicesData, PacketLen, (VOID **) State->Packet);
+
+ if (EFI_ERROR (Status)) {
+ State->Status = EFI_OUT_OF_RESOURCES;
+ return EFI_ABORTED;
+ }
+
+ *(State->PacketLen) = PacketLen;
+ CopyMem (*(State->Packet), Packet, PacketLen);
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Check whether the override data is valid.
+
+ It will first validate whether the server is a valid unicast. If a gateway
+ is provided in the Override, it also check that it is a unicast on the
+ connected network.
+
+ @param Instance The MTFTP instance
+ @param Override The override data to validate.
+
+ @retval TRUE The override data is valid
+ @retval FALSE The override data is invalid
+
+**/
+BOOLEAN
+Mtftp4OverrideValid (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_OVERRIDE_DATA *Override
+ )
+{
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ IP4_ADDR Gateway;
+
+ CopyMem (&Ip, &Override->ServerIp, sizeof (IP4_ADDR));
+ if (IP4_IS_UNSPECIFIED (NTOHL (Ip)) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ return FALSE;
+ }
+
+ Config = &Instance->Config;
+
+ CopyMem (&Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));
+ Gateway = NTOHL (Gateway);
+
+ if (!Config->UseDefaultSetting && (Gateway != 0)) {
+ CopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));
+ CopyMem (&Ip, &Config->StationIp, sizeof (IP4_ADDR));
+
+ Netmask = NTOHL (Netmask);
+ Ip = NTOHL (Ip);
+
+ if ((Netmask != 0 && !NetIp4IsUnicast (Gateway, Netmask)) || !IP4_NET_EQUAL (Gateway, Ip, Netmask)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Poll the UDP to get the IP4 default address, which may be retrieved
+ by DHCP.
+
+ The default time out value is 5 seconds. If IP has retrieved the default address,
+ the UDP is reconfigured.
+
+ @param Instance The Mtftp instance
+ @param UdpIo The UDP_IO to poll
+ @param UdpCfgData The UDP configure data to reconfigure the UDP_IO
+
+ @retval TRUE The default address is retrieved and UDP is reconfigured.
+ @retval FALSE Some error occured.
+
+**/
+BOOLEAN
+Mtftp4GetMapping (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UDP_IO *UdpIo,
+ IN EFI_UDP4_CONFIG_DATA *UdpCfgData
+ )
+{
+ MTFTP4_SERVICE *Service;
+ EFI_IP4_MODE_DATA Ip4Mode;
+ EFI_UDP4_PROTOCOL *Udp;
+ EFI_STATUS Status;
+
+ ASSERT (Instance->Config.UseDefaultSetting);
+
+ Service = Instance->Service;
+ Udp = UdpIo->Protocol.Udp4;
+
+ Status = gBS->SetTimer (
+ Service->TimerToGetMap,
+ TimerRelative,
+ MTFTP4_TIME_TO_GETMAP * TICKS_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ while (EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) {
+ Udp->Poll (Udp);
+
+ if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) &&
+ Ip4Mode.IsConfigured) {
+
+ Udp->Configure (Udp, NULL);
+ return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS);
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Configure the UDP port for unicast receiving.
+
+ @param UdpIo The UDP_IO instance
+ @param Instance The MTFTP session
+
+ @retval EFI_SUCCESS The UDP port is successfully configured for the
+ session to unicast receive.
+
+**/
+EFI_STATUS
+Mtftp4ConfigUnicastPort (
+ IN UDP_IO *UdpIo,
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_UDP4_CONFIG_DATA UdpConfig;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ Config = &Instance->Config;
+
+ UdpConfig.AcceptBroadcast = FALSE;
+ UdpConfig.AcceptPromiscuous = FALSE;
+ UdpConfig.AcceptAnyPort = FALSE;
+ UdpConfig.AllowDuplicatePort = FALSE;
+ UdpConfig.TypeOfService = 0;
+ UdpConfig.TimeToLive = 64;
+ UdpConfig.DoNotFragment = FALSE;
+ UdpConfig.ReceiveTimeout = 0;
+ UdpConfig.TransmitTimeout = 0;
+ UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
+ IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);
+ IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);
+ UdpConfig.StationPort = 0;
+ UdpConfig.RemotePort = 0;
+
+ Ip = HTONL (Instance->ServerIp);
+ IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);
+
+ Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfig);
+
+ if ((Status == EFI_NO_MAPPING) && Mtftp4GetMapping (Instance, UdpIo, &UdpConfig)) {
+ return EFI_SUCCESS;
+ }
+
+ if (!Config->UseDefaultSetting && !EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {
+ //
+ // The station IP address is manually configured and the Gateway IP is not 0.
+ // Add the default route for this UDP instance.
+ //
+ Status = UdpIo->Protocol.Udp4->Routes (
+ UdpIo->Protocol.Udp4,
+ FALSE,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr,
+ &Config->GatewayIp
+ );
+ if (EFI_ERROR (Status)) {
+ UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, NULL);
+ }
+ }
+ return Status;
+}
+
+
+/**
+ Start the MTFTP session to do the operation, such as read file,
+ write file, and read directory.
+
+ @param This The MTFTP session
+ @param Token The token than encapsues the user's request.
+ @param Operation The operation to do
+
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.
+ @retval EFI_SUCCESS The operation is successfully started.
+
+**/
+EFI_STATUS
+Mtftp4Start (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 Operation
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_OVERRIDE_DATA *Override;
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Validate the parameters
+ //
+ if ((This == NULL) || (Token == NULL) || (Token->Filename == NULL) ||
+ ((Token->OptionCount != 0) && (Token->OptionList == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // User must provide at least one method to collect the data for download.
+ //
+ if (((Operation == EFI_MTFTP4_OPCODE_RRQ) || (Operation == EFI_MTFTP4_OPCODE_DIR)) &&
+ ((Token->Buffer == NULL) && (Token->CheckPacket == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // User must provide at least one method to provide the data for upload.
+ //
+ if ((Operation == EFI_MTFTP4_OPCODE_WRQ) &&
+ ((Token->Buffer == NULL) && (Token->PacketNeeded == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ Status = EFI_SUCCESS;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Instance->State != MTFTP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ }
+
+ if (Instance->Operation != 0) {
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ if (EFI_ERROR (Status)) {
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Set the Operation now to prevent the application start other
+ // operations.
+ //
+ Instance->Operation = Operation;
+ Override = Token->OverrideData;
+
+ if ((Override != NULL) && !Mtftp4OverrideValid (Instance, Override)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_ERROR;
+ }
+
+ if (Token->OptionCount != 0) {
+ Status = Mtftp4ParseOption (
+ Token->OptionList,
+ Token->OptionCount,
+ TRUE,
+ &Instance->RequestOption
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Set the operation parameters from the configuration or override data.
+ //
+ Config = &Instance->Config;
+ Instance->Token = Token;
+ Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;
+
+ CopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR));
+ Instance->ServerIp = NTOHL (Instance->ServerIp);
+
+ Instance->ListeningPort = Config->InitialServerPort;
+ Instance->ConnectedPort = 0;
+
+ CopyMem (&Instance->Gateway, &Config->GatewayIp, sizeof (IP4_ADDR));
+ Instance->Gateway = NTOHL (Instance->Gateway);
+
+ Instance->MaxRetry = Config->TryCount;
+ Instance->Timeout = Config->TimeoutValue;
+ Instance->Master = TRUE;
+
+ if (Override != NULL) {
+ CopyMem (&Instance->ServerIp, &Override->ServerIp, sizeof (IP4_ADDR));
+ CopyMem (&Instance->Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));
+
+ Instance->ServerIp = NTOHL (Instance->ServerIp);
+ Instance->Gateway = NTOHL (Instance->Gateway);
+
+ Instance->ListeningPort = Override->ServerPort;
+ Instance->MaxRetry = Override->TryCount;
+ Instance->Timeout = Override->TimeoutValue;
+ }
+
+ if (Instance->ListeningPort == 0) {
+ Instance->ListeningPort = MTFTP4_DEFAULT_SERVER_PORT;
+ }
+
+ if (Instance->MaxRetry == 0) {
+ Instance->MaxRetry = MTFTP4_DEFAULT_RETRY;
+ }
+
+ if (Instance->Timeout == 0) {
+ Instance->Timeout = MTFTP4_DEFAULT_TIMEOUT;
+ }
+
+ //
+ // Config the unicast UDP child to send initial request
+ //
+ Status = Mtftp4ConfigUnicastPort (Instance->UnicastPort, Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Set initial status.
+ //
+ Token->Status = EFI_NOT_READY;
+
+ //
+ // Build and send an initial requests
+ //
+ if (Operation == EFI_MTFTP4_OPCODE_WRQ) {
+ Status = Mtftp4WrqStart (Instance, Operation);
+ } else {
+ Status = Mtftp4RrqStart (Instance, Operation);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (Token->Event != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Return immediately for asynchronous operation or poll the
+ // instance for synchronous operation.
+ //
+ while (Token->Status == EFI_NOT_READY) {
+ This->Poll (This);
+ }
+
+ return Token->Status;
+
+ON_ERROR:
+ Mtftp4CleanOperation (Instance, Status);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Reads the current operational settings.
+
+ The GetModeData()function reads the current operational settings of this
+ EFI MTFTPv4 Protocol driver instance.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance.
+ @param ModeData Pointer to storage for the EFI MTFTPv4 Protocol
+ driver mode data.
+
+ @retval EFI_SUCCESS The configuration data was successfully returned.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+ @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4GetModeData (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ OUT EFI_MTFTP4_MODE_DATA *ModeData
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ModeData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+ CopyMem(&ModeData->ConfigData, &Instance->Config, sizeof (Instance->Config));
+ ModeData->SupportedOptionCount = MTFTP4_SUPPORTED_OPTIONS;
+ ModeData->SupportedOptoins = (UINT8 **) mMtftp4SupportedOptions;
+ ModeData->UnsupportedOptionCount = 0;
+ ModeData->UnsupportedOptoins = NULL;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Initializes, changes, or resets the default operational setting for this
+ EFI MTFTPv4 Protocol driver instance.
+
+ The Configure() function is used to set and change the configuration data for
+ this EFI MTFTPv4 Protocol driver instance. The configuration data can be reset
+ to startup defaults by calling Configure() with MtftpConfigData set to NULL.
+ Whenever the instance is reset, any pending operation is aborted. By changing
+ the EFI MTFTPv4 Protocol driver instance configuration data, the client can
+ connect to different MTFTPv4 servers. The configuration parameters in
+ MtftpConfigData are used as the default parameters in later MTFTPv4 operations
+ and can be overridden in later operations.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param ConfigData MtftpConfigDataPointer to the configuration data
+ structure
+
+ @retval EFI_SUCCESS The EFI MTFTPv4 Protocol driver was configured
+ successfully.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ 1.This is NULL.
+ 2.MtftpConfigData.UseDefaultSetting is FALSE and
+ MtftpConfigData.StationIp is not a valid IPv4
+ unicast address.
+ 3.MtftpCofigData.UseDefaultSetting is FALSE and
+ MtftpConfigData.SubnetMask is invalid.
+ 4.MtftpCofigData.ServerIp is not a valid IPv4
+ unicast address.
+ 5.MtftpConfigData.UseDefaultSetting is FALSE and
+ MtftpConfigData.GatewayIp is not a valid IPv4
+ unicast address or is not in the same subnet
+ with station address.
+ @retval EFI_ACCESS_DENIED The EFI configuration could not be changed at this
+ time because there is one MTFTP background operation
+ in progress.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) has not finished yet.
+ @retval EFI_UNSUPPORTED A configuration protocol (DHCP, BOOTP, RARP, etc.)
+ could not be located when clients choose to use
+ the default address settings.
+ @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv4 Protocol driver instance data could
+ not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI MTFTPv4 Protocol driver instance is not
+ configured.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4Configure (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_CONFIG_DATA *ConfigData
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ IP4_ADDR Gateway;
+ IP4_ADDR ServerIp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ if (ConfigData == NULL) {
+ //
+ // Reset the operation if ConfigData is NULL
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Mtftp4CleanOperation (Instance, EFI_ABORTED);
+ ZeroMem (&Instance->Config, sizeof (EFI_MTFTP4_CONFIG_DATA));
+ Instance->State = MTFTP4_STATE_UNCONFIGED;
+
+ gBS->RestoreTPL (OldTpl);
+
+ } else {
+ //
+ // Configure the parameters for new operation.
+ //
+ CopyMem (&Ip, &ConfigData->StationIp, sizeof (IP4_ADDR));
+ CopyMem (&Netmask, &ConfigData->SubnetMask, sizeof (IP4_ADDR));
+ CopyMem (&Gateway, &ConfigData->GatewayIp, sizeof (IP4_ADDR));
+ CopyMem (&ServerIp, &ConfigData->ServerIp, sizeof (IP4_ADDR));
+
+ Ip = NTOHL (Ip);
+ Netmask = NTOHL (Netmask);
+ Gateway = NTOHL (Gateway);
+ ServerIp = NTOHL (ServerIp);
+
+ if (!ConfigData->UseDefaultSetting &&
+ ((!IP4_IS_VALID_NETMASK (Netmask) || (Netmask != 0 && !NetIp4IsUnicast (Ip, Netmask))))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Gateway != 0) &&
+ (!IP4_NET_EQUAL (Gateway, Ip, Netmask) || (Netmask != 0 && !NetIp4IsUnicast (Gateway, Netmask)))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if ((Instance->State == MTFTP4_STATE_CONFIGED) && (Instance->Operation != 0)) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ CopyMem(&Instance->Config, ConfigData, sizeof (*ConfigData));;
+ Instance->State = MTFTP4_STATE_CONFIGED;
+
+ gBS->RestoreTPL (OldTpl);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Parses the options in an MTFTPv4 OACK packet.
+
+ The ParseOptions() function parses the option fields in an MTFTPv4 OACK packet
+ and returns the number of options that were found and optionally a list of
+ pointers to the options in the packet.
+ If one or more of the option fields are not valid, then EFI_PROTOCOL_ERROR is
+ returned and *OptionCount and *OptionList stop at the last valid option.
+ The OptionList is allocated by this function, and caller should free it when used.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance.
+ @param PacketLen Length of the OACK packet to be parsed.
+ @param Packet Pointer to the OACK packet to be parsed.
+ @param OptionCount Pointer to the number of options in following OptionList.
+ @param OptionList Pointer to EFI_MTFTP4_OPTION storage. Call the
+ EFI Boot Service FreePool() to release theOptionList
+ if the options in this OptionList are not needed
+ any more
+
+ @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and
+ OptionList parameters have been updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1.PacketLen is 0.
+ 2.Packet is NULL or Packet is not a valid MTFTPv4 packet.
+ 3.OptionCount is NULL.
+ @retval EFI_NOT_FOUND No options were found in the OACK packet.
+ @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array cannot be allocated.
+ @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4ParseOptions (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN UINT32 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (PacketLen < MTFTP4_OPCODE_LEN) ||
+ (Packet == NULL) || (OptionCount == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Mtftp4ExtractOptions (Packet, PacketLen, OptionCount, OptionList);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (*OptionCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Downloads a file from an MTFTPv4 server.
+
+ The ReadFile() function is used to initialize and start an MTFTPv4 download
+ process and optionally wait for completion. When the download operation completes,
+ whether successfully or not, the Token.Status field is updated by the EFI MTFTPv4
+ Protocol driver and then Token.Event is signaled (if it is not NULL).
+ Data can be downloaded from the MTFTPv4 server into either of the following locations:
+ 1.A fixed buffer that is pointed to by Token.Buffer
+ 2.A download service function that is pointed to by Token.CheckPacket
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket
+ will be called first. If the call is successful, the packet will be stored in
+ Token.Buffer.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param Token Pointer to the token structure to provide the
+ parameters that are used in this operation.
+
+ @retval EFI_SUCCESS The data file has been transferred successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is not large enough to hold the downloaded
+ data in downloading process.
+ @retval EFI_ABORTED Current operation is aborted by user.
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv4 server.
+ @retval EFI_TFTP_ERROR An MTFTPv4 ERROR packet was received.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_RRQ);
+}
+
+
+/**
+ Sends a data file to an MTFTPv4 server. May be unsupported in some EFI implementations
+
+ The WriteFile() function is used to initialize an uploading operation with the
+ given option list and optionally wait for completion. If one or more of the
+ options is not supported by the server, the unsupported options are ignored and
+ a standard TFTP process starts instead. When the upload process completes,
+ whether successfully or not, Token.Event is signaled, and the EFI MTFTPv4 Protocol
+ driver updates Token.Status.
+ The caller can supply the data to be uploaded in the following two modes:
+ 1.Through the user-provided buffer
+ 2.Through a callback function
+ With the user-provided buffer, the Token.BufferSize field indicates the length
+ of the buffer, and the driver will upload the data in the buffer. With an
+ EFI_MTFTP4_PACKET_NEEDED callback function, the driver will call this callback
+ function to get more data from the user to upload. See the definition of
+ EFI_MTFTP4_PACKET_NEEDED for more information. These two modes cannot be used at
+ the same time. The callback function will be ignored if the user provides the buffer.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance.
+ @param Token Pointer to the token structure to provide the
+ parameters that are used in this function
+
+ @retval EFI_SUCCESS The upload session has started.
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1. This is NULL.
+ 2. Token is NULL.
+ 3. Token.Filename is NULL.
+ 4. Token.OptionCount is not zero and
+ Token.OptionList is NULL.
+ 5. One or more options in Token.OptionList have wrong
+ format.
+ 6. Token.Buffer and Token.PacketNeeded are both
+ NULL.
+ 7. One or more IPv4 addresses in Token.OverrideData
+ are not valid unicast IPv4 addresses if
+ Token.OverrideData is not NULL.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are in the
+ unsupported list of structure EFI_MTFTP4_MODE_DATA.
+ @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv4
+ session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4WriteFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_WRQ);
+}
+
+
+/**
+ Downloads a data file "directory" from an MTFTPv4 server.
+ May be unsupported in some EFI implementations
+
+ The ReadDirectory() function is used to return a list of files on the MTFTPv4
+ server that are logically (or operationally) related to Token.Filename. The
+ directory request packet that is sent to the server is built with the option
+ list that was provided by caller, if present.
+ The file information that the server returns is put into either of the following
+ locations:
+ 1.A fixed buffer that is pointed to by Token.Buffer
+ 2.A download service function that is pointed to by Token.CheckPacket
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket will
+ be called first. If the call is successful, the packet will be stored in Token.Buffer.
+ The returned directory listing in the Token.Buffer or EFI_MTFTP4_PACKET consists
+ of a list of two or three variable-length ASCII strings, each terminated by a
+ null character, for each file in the directory. If the multicast option is involved,
+ the first field of each directory entry is the static multicast IP address and
+ UDP port number that is associated with the file name. The format of the field
+ is ip:ip:ip:ip:port. If the multicast option is not involved, this field and its
+ terminating null character are not present.
+ The next field of each directory entry is the file name and the last field is
+ the file information string. The information string contains the file size and
+ the create/modify timestamp. The format of the information string is filesize
+ yyyy-mm-dd hh:mm:ss:ffff. The timestamp is Coordinated Universal Time
+ (UTC; also known as Greenwich Mean Time [GMT]).
+ The only difference between ReadFile and ReadDirectory is the opcode used.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param Token Pointer to the token structure to provide the
+ parameters that are used in this function
+
+ @retval EFI_SUCCESS The MTFTPv4 related file "directory" has been downloaded.
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1. This is NULL.
+ 2. Token is NULL.
+ 3. Token.Filename is NULL.
+ 4. Token.OptionCount is not zero and
+ Token.OptionList is NULL.
+ 5. One or more options in Token.OptionList have wrong
+ format.
+ 6. Token.Buffer and Token.PacketNeeded are both
+ NULL.
+ 7. One or more IPv4 addresses in Token.OverrideData
+ are not valid unicast IPv4 addresses if
+ Token.OverrideData is not NULL.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are in the
+ unsupported list of structure EFI_MTFTP4_MODE_DATA.
+ @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv4
+ session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadDirectory (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_DIR);
+}
+
+
+/**
+ Gets information about a file from an MTFTPv4 server.
+
+ The GetInfo() function assembles an MTFTPv4 request packet with options;
+ sends it to the MTFTPv4 server; and may return an MTFTPv4 OACK, MTFTPv4 ERROR,
+ or ICMP ERROR packet. Retries occur only if no response packets are received
+ from the MTFTPv4 server before the timeout expires.
+ It is implemented with EfiMtftp4ReadFile: build a token, then pass it to
+ EfiMtftp4ReadFile. In its check packet callback abort the opertions.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param OverrideData Data that is used to override the existing
+ parameters. If NULL, the default parameters that
+ were set in the EFI_MTFTP4_PROTOCOL.Configure()
+ function are used
+ @param Filename Pointer to null-terminated ASCII file name string
+ @param ModeStr Pointer to null-terminated ASCII mode string. If NULL, "octet"
+ will be used
+ @param OptionCount Number of option/value string pairs in OptionList
+ @param OptionList Pointer to array of option/value string pairs.
+ Ignored if OptionCount is zero
+ @param PacketLength The number of bytes in the returned packet
+ @param Packet PacketThe pointer to the received packet. This
+ buffer must be freed by the caller.
+
+ @retval EFI_SUCCESS An MTFTPv4 OACK packet was received and is in
+ the Buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1.This is NULL.
+ 2.Filename is NULL.
+ 3.OptionCount is not zero and OptionList is NULL.
+ 4.One or more options in OptionList have wrong format.
+ 5.PacketLength is NULL.
+ 6.One or more IPv4 addresses in OverrideData are
+ not valid unicast IPv4 addresses if OverrideData
+ is not NULL.
+ @retval EFI_UNSUPPORTED One or more options in the OptionList are in the
+ unsupported list of structure EFI_MTFTP4_MODE_DATA
+ @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) has not finished yet.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TFTP_ERROR An MTFTPv4 ERROR packet was received and is in
+ the Buffer.
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received and the Packet
+ is set to NULL.
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv4 packet was received and is
+ in the Buffer.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv4 server.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4GetInfo (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_OVERRIDE_DATA *OverrideData OPTIONAL,
+ IN UINT8 *Filename,
+ IN UINT8 *ModeStr OPTIONAL,
+ IN UINT8 OptionCount,
+ IN EFI_MTFTP4_OPTION *OptionList OPTIONAL,
+ OUT UINT32 *PacketLength,
+ OUT EFI_MTFTP4_PACKET **Packet OPTIONAL
+ )
+{
+ EFI_MTFTP4_TOKEN Token;
+ MTFTP4_GETINFO_STATE State;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (Filename == NULL) || (PacketLength == NULL) ||
+ ((OptionCount != 0) && (OptionList == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet != NULL) {
+ *Packet = NULL;
+ }
+
+ *PacketLength = 0;
+ State.Packet = Packet;
+ State.PacketLen = PacketLength;
+ State.Status = EFI_SUCCESS;
+
+ //
+ // Fill in the Token to issue an synchronous ReadFile operation
+ //
+ Token.Status = EFI_SUCCESS;
+ Token.Event = NULL;
+ Token.OverrideData = OverrideData;
+ Token.Filename = Filename;
+ Token.ModeStr = ModeStr;
+ Token.OptionCount = OptionCount;
+ Token.OptionList = OptionList;
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ Token.Context = &State;
+ Token.CheckPacket = Mtftp4GetInfoCheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = EfiMtftp4ReadFile (This, &Token);
+
+ if (EFI_ABORTED == Status) {
+ return State.Status;
+ }
+
+ return Status;
+}
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications to increase
+ the rate that data packets are moved between the communications device and the
+ transmit and receive queues.
+ In some systems, the periodic timer event in the managed network driver may not
+ poll the underlying communications device fast enough to transmit and/or receive
+ all data packets without missing incoming packets or dropping outgoing packets.
+ Drivers and applications that are experiencing packet loss should try calling
+ the Poll() function more often.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI MTFTPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive
+ queue. Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4Poll (
+ IN EFI_MTFTP4_PROTOCOL *This
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_UDP4_PROTOCOL *Udp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ if (Instance->State == MTFTP4_STATE_UNCONFIGED) {
+ return EFI_NOT_STARTED;
+ } else if (Instance->State == MTFTP4_STATE_DESTROY) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp = Instance->UnicastPort->Protocol.Udp4;
+ return Udp->Poll (Udp);
+}
+
+EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate = {
+ EfiMtftp4GetModeData,
+ EfiMtftp4Configure,
+ EfiMtftp4GetInfo,
+ EfiMtftp4ParseOptions,
+ EfiMtftp4ReadFile,
+ EfiMtftp4WriteFile,
+ EfiMtftp4ReadDirectory,
+ EfiMtftp4Poll
+};
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h
new file mode 100644
index 0000000000..527fd1db10
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h
@@ -0,0 +1,216 @@
+/** @file
+
+ Mtftp4 Implementation.
+
+ Mtftp4 Implementation, it supports the following RFCs:
+ RFC1350 - THE TFTP PROTOCOL (REVISION 2)
+ RFC2090 - TFTP Multicast Option
+ RFC2347 - TFTP Option Extension
+ RFC2348 - TFTP Blocksize Option
+ RFC2349 - TFTP Timeout Interval and Transfer Size Options
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#ifndef __EFI_MTFTP4_IMPL_H__
+#define __EFI_MTFTP4_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/Udp4.h>
+#include <Protocol/Mtftp4.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/PrintLib.h>
+
+extern EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate;
+
+typedef struct _MTFTP4_SERVICE MTFTP4_SERVICE;
+typedef struct _MTFTP4_PROTOCOL MTFTP4_PROTOCOL;
+
+#include "Mtftp4Driver.h"
+#include "Mtftp4Option.h"
+#include "Mtftp4Support.h"
+
+
+///
+/// Some constant value of Mtftp service.
+///
+#define MTFTP4_SERVICE_SIGNATURE SIGNATURE_32 ('T', 'F', 'T', 'P')
+#define MTFTP4_PROTOCOL_SIGNATURE SIGNATURE_32 ('t', 'f', 't', 'p')
+
+#define MTFTP4_DEFAULT_SERVER_PORT 69
+#define MTFTP4_DEFAULT_TIMEOUT 3
+#define MTFTP4_DEFAULT_RETRY 5
+#define MTFTP4_DEFAULT_BLKSIZE 512
+#define MTFTP4_TIME_TO_GETMAP 5
+
+#define MTFTP4_STATE_UNCONFIGED 0
+#define MTFTP4_STATE_CONFIGED 1
+#define MTFTP4_STATE_DESTROY 2
+
+///
+/// Mtftp service block
+///
+struct _MTFTP4_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ UINT16 ChildrenNum;
+ LIST_ENTRY Children;
+
+ EFI_EVENT Timer; ///< Ticking timer for all the MTFTP clients
+ EFI_EVENT TimerToGetMap;
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ //
+ // This UDP child is used to keep the connection between the UDP
+ // and MTFTP, so MTFTP will be notified when UDP is uninstalled.
+ //
+ UDP_IO *ConnectUdp;
+};
+
+
+typedef struct {
+ EFI_MTFTP4_PACKET **Packet;
+ UINT32 *PacketLen;
+ EFI_STATUS Status;
+} MTFTP4_GETINFO_STATE;
+
+struct _MTFTP4_PROTOCOL {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ EFI_MTFTP4_PROTOCOL Mtftp4;
+
+ INTN State;
+ BOOLEAN InDestroy;
+
+ MTFTP4_SERVICE *Service;
+ EFI_HANDLE Handle;
+
+ EFI_MTFTP4_CONFIG_DATA Config;
+
+ //
+ // Operation parameters: token and requested options.
+ //
+ EFI_MTFTP4_TOKEN *Token;
+ MTFTP4_OPTION RequestOption;
+ UINT16 Operation;
+
+ //
+ // Blocks is a list of MTFTP4_BLOCK_RANGE which contains
+ // holes in the file
+ //
+ UINT16 BlkSize;
+ UINT16 LastBlock;
+ LIST_ENTRY Blocks;
+
+ //
+ // The server's communication end point: IP and two ports. one for
+ // initial request, one for its selected port.
+ //
+ IP4_ADDR ServerIp;
+ UINT16 ListeningPort;
+ UINT16 ConnectedPort;
+ IP4_ADDR Gateway;
+ UDP_IO *UnicastPort;
+
+ //
+ // Timeout and retransmit status
+ //
+ NET_BUF *LastPacket;
+ UINT32 PacketToLive;
+ UINT32 CurRetry;
+ UINT32 MaxRetry;
+ UINT32 Timeout;
+
+ //
+ // Parameter used by RRQ's multicast download.
+ //
+ IP4_ADDR McastIp;
+ UINT16 McastPort;
+ BOOLEAN Master;
+ UDP_IO *McastUdpPort;
+};
+
+typedef struct {
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+} MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
+
+/**
+ Clean up the MTFTP session to get ready for new operation.
+
+ @param Instance The MTFTP session to clean up
+ @param Result The result to return to the caller who initiated
+ the operation.
+**/
+VOID
+Mtftp4CleanOperation (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_STATUS Result
+ );
+
+/**
+ Start the MTFTP session for upload.
+
+ It will first init some states, then send the WRQ request packet,
+ and start receiving the packet.
+
+ @param Instance The MTFTP session
+ @param Operation Redundant parameter, which is always
+ EFI_MTFTP4_OPCODE_WRQ here.
+
+ @retval EFI_SUCCESS The upload process has been started.
+ @retval Others Failed to start the upload.
+
+**/
+EFI_STATUS
+Mtftp4WrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ );
+
+/**
+ Start the MTFTP session to download.
+
+ It will first initialize some of the internal states then build and send a RRQ
+ reqeuest packet, at last, it will start receive for the downloading.
+
+ @param Instance The Mtftp session
+ @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
+ or EFI_MTFTP4_OPCODE_DIR.
+
+ @retval EFI_SUCCESS The mtftp download session is started.
+ @retval Others Failed to start downloading.
+
+**/
+EFI_STATUS
+Mtftp4RrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ );
+
+#define MTFTP4_SERVICE_FROM_THIS(a) \
+ CR (a, MTFTP4_SERVICE, ServiceBinding, MTFTP4_SERVICE_SIGNATURE)
+
+#define MTFTP4_PROTOCOL_FROM_THIS(a) \
+ CR (a, MTFTP4_PROTOCOL, Mtftp4, MTFTP4_PROTOCOL_SIGNATURE)
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c
new file mode 100644
index 0000000000..e40561a96b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c
@@ -0,0 +1,534 @@
+/** @file
+ Routines to process MTFTP4 options.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp4Impl.h"
+
+CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {
+ "blksize",
+ "timeout",
+ "tsize",
+ "multicast"
+};
+
+
+/**
+ Check whether two ascii strings are equel, ignore the case.
+
+ @param Str1 The first ascii string
+ @param Str2 The second ascii string
+
+ @retval TRUE Two strings are equal when case is ignored.
+ @retval FALSE Two string are not equal.
+
+**/
+BOOLEAN
+NetStringEqualNoCase (
+ IN UINT8 *Str1,
+ IN UINT8 *Str2
+ )
+{
+ UINT8 Ch1;
+ UINT8 Ch2;
+
+ ASSERT ((Str1 != NULL) && (Str2 != NULL));
+
+ for (; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) {
+ Ch1 = *Str1;
+ Ch2 = *Str2;
+
+ //
+ // Convert them to lower case then compare two
+ //
+ if (('A' <= Ch1) && (Ch1 <= 'Z')) {
+ Ch1 += 'a' - 'A';
+ }
+
+ if (('A' <= Ch2) && (Ch2 <= 'Z')) {
+ Ch2 += 'a' - 'A';
+ }
+
+ if (Ch1 != Ch2) {
+ return FALSE;
+ }
+ }
+
+ return (BOOLEAN) (*Str1 == *Str2);
+}
+
+
+/**
+ Convert a string to a UINT32 number.
+
+ @param Str The string to convert from
+
+ @return The number get from the string
+
+**/
+UINT32
+NetStringToU32 (
+ IN UINT8 *Str
+ )
+{
+ UINT32 Num;
+
+ ASSERT (Str != NULL);
+
+ Num = 0;
+
+ for (; NET_IS_DIGIT (*Str); Str++) {
+ Num = Num * 10 + (*Str - '0');
+ }
+
+ return Num;
+}
+
+
+/**
+ Convert a string of the format "192.168.0.1" to an IP address.
+
+ @param Str The string representation of IP
+ @param Ip The varible to get IP.
+
+ @retval EFI_INVALID_PARAMETER The IP string is invalid.
+ @retval EFI_SUCCESS The IP is parsed into the Ip
+
+**/
+EFI_STATUS
+NetStringToIp (
+ IN UINT8 *Str,
+ OUT IP4_ADDR *Ip
+ )
+{
+ UINT32 Byte;
+ UINT32 Addr;
+ UINTN Index;
+
+ *Ip = 0;
+ Addr = 0;
+
+ for (Index = 0; Index < 4; Index++) {
+ if (!NET_IS_DIGIT (*Str)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Byte = NetStringToU32 (Str);
+
+ if (Byte > 255) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Addr = (Addr << 8) | Byte;
+
+ //
+ // Skip all the digitals and check whether the sepeator is the dot
+ //
+ while (NET_IS_DIGIT (*Str)) {
+ Str++;
+ }
+
+ if ((Index < 3) && (*Str != '.')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Str++;
+ }
+
+ *Ip = Addr;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Go through the packet to fill the Options array with the start
+ addresses of each MTFTP option name/value pair.
+
+ @param Packet The packet to check
+ @param PacketLen The packet's length
+ @param Count The size of the Options on input. The actual
+ options on output
+ @param Options The option array to fill in
+
+ @retval EFI_INVALID_PARAMETER The packet is mal-formated
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small
+ @retval EFI_SUCCESS The packet has been parsed into the Options array.
+
+**/
+EFI_STATUS
+Mtftp4FillOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *Count,
+ OUT EFI_MTFTP4_OPTION *Options OPTIONAL
+ )
+{
+ UINT8 *Cur;
+ UINT8 *Last;
+ UINT8 Num;
+ UINT8 *Name;
+ UINT8 *Value;
+
+ Num = 0;
+ Cur = (UINT8 *) Packet + MTFTP4_OPCODE_LEN;
+ Last = (UINT8 *) Packet + PacketLen - 1;
+
+ //
+ // process option name and value pairs. The last byte is always zero
+ //
+ while (Cur < Last) {
+ Name = Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ if (Cur == Last) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value = ++Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ Num++;
+
+ if ((Options != NULL) && (Num <= *Count)) {
+ Options[Num - 1].OptionStr = Name;
+ Options[Num - 1].ValueStr = Value;
+ }
+
+ Cur++;
+ }
+
+ if ((*Count < Num) || (Options == NULL)) {
+ *Count = Num;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *Count = Num;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate and fill in a array of Mtftp options from the Packet.
+
+ It first calls Mtftp4FillOption to get the option number, then allocate
+ the array, at last, call Mtftp4FillOption again to save the options.
+
+ @param Packet The packet to parse
+ @param PacketLen The length of the packet
+ @param OptionCount The number of options in the packet
+ @param OptionList The point to get the option array.
+
+ @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
+ well-formated OACK packet.
+ @retval EFI_SUCCESS The option array is build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
+
+**/
+EFI_STATUS
+Mtftp4ExtractOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ *OptionCount = 0;
+
+ if (OptionList != NULL) {
+ *OptionList = NULL;
+ }
+
+ if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (PacketLen == MTFTP4_OPCODE_LEN) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // The last byte must be zero to terminate the options
+ //
+ if (*((UINT8 *) Packet + PacketLen - 1) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the number of options
+ //
+ Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL);
+
+ if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) {
+ return Status;
+ }
+
+ //
+ // Allocate memory for the options, then call Mtftp4FillOptions to
+ // fill it if caller want that.
+ //
+ if (OptionList == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ *OptionList = AllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION));
+
+ if (*OptionList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the MTFTP multicast option.
+
+ @param Value The Mtftp multicast value string
+ @param Option The option to save the info into.
+
+ @retval EFI_INVALID_PARAMETER The multicast value string is invalid.
+ @retval EFI_SUCCESS The multicast value is parsed into the Option
+
+**/
+EFI_STATUS
+Mtftp4ExtractMcast (
+ IN UINT8 *Value,
+ IN OUT MTFTP4_OPTION *Option
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Num;
+
+ //
+ // The multicast option is formated like "204.0.0.1,1857,1"
+ // The server can also omit the ip and port, use ",,1"
+ //
+ if (*Value == ',') {
+ Option->McastIp = 0;
+ } else {
+ Status = NetStringToIp (Value, &Option->McastIp);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while ((*Value != 0) && (*Value != ',')) {
+ Value++;
+ }
+ }
+
+ if (*Value != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value++;
+
+ //
+ // Convert the port setting. the server can send us a port number or
+ // empty string. such as the port in ",,1"
+ //
+ if (*Value == ',') {
+ Option->McastPort = 0;
+ } else {
+ Num = NetStringToU32 (Value);
+
+ if (Num > 65535) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option->McastPort = (UINT16) Num;
+
+ while (NET_IS_DIGIT (*Value)) {
+ Value++;
+ }
+ }
+
+ if (*Value != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value++;
+
+ //
+ // Check the master/slave setting, 1 for master, 0 for slave.
+ //
+ Num = NetStringToU32 (Value);
+
+ if ((Num != 0) && (Num != 1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option->Master = (BOOLEAN) (Num == 1);
+
+ while (NET_IS_DIGIT (*Value)) {
+ Value++;
+ }
+
+ if (*Value != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the option in Options array to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Options The option array, which contains addresses of each
+ option's name/value string.
+ @param Count The number of options in the Options
+ @param Request Whether this is a request or OACK. The format of
+ multicast is different according to this setting.
+ @param MtftpOption The MTFTP4_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The option is mal-formated
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOption (
+ IN EFI_MTFTP4_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN Request,
+ OUT MTFTP4_OPTION *MtftpOption
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT32 Value;
+ EFI_MTFTP4_OPTION *This;
+
+ MtftpOption->Exist = 0;
+
+ for (Index = 0; Index < Count; Index++) {
+ This = Options + Index;
+
+ if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "blksize")) {
+ //
+ // block size option, valid value is between [8, 65464]
+ //
+ Value = NetStringToU32 (This->ValueStr);
+
+ if ((Value < 8) || (Value > 65464)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->BlkSize = (UINT16) Value;
+ MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "timeout")) {
+ //
+ // timeout option, valid value is between [1, 255]
+ //
+ Value = NetStringToU32 (This->ValueStr);
+
+ if ((Value < 1) || (Value > 255)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->Timeout = (UINT8) Value;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "tsize")) {
+ //
+ // tsize option, the biggest transfer supported is 4GB with block size option
+ //
+ MtftpOption->Tsize = NetStringToU32 (This->ValueStr);
+ MtftpOption->Exist |= MTFTP4_TSIZE_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "multicast")) {
+ //
+ // Multicast option, if it is a request, the value must be a zero
+ // length string, otherwise, it is formated like "204.0.0.1,1857,1\0"
+ //
+ if (Request) {
+ if (*(This->ValueStr) != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else {
+ Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ MtftpOption->Exist |= MTFTP4_MCAST_EXIST;
+
+ } else if (Request) {
+ //
+ // Ignore the unsupported option if it is a reply, and return
+ // EFI_UNSUPPORTED if it's a request according to the UEFI spec.
+ //
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the options in the OACK packet to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Packet The OACK packet to parse
+ @param PacketLen The length of the packet
+ @param MtftpOption The MTFTP_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The packet option is mal-formated
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOptionOack (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT MTFTP4_OPTION *MtftpOption
+ )
+{
+ EFI_MTFTP4_OPTION *OptionList;
+ EFI_STATUS Status;
+ UINT32 Count;
+
+ MtftpOption->Exist = 0;
+
+ Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList);
+
+ if (EFI_ERROR (Status) || (Count == 0)) {
+ return Status;
+ }
+ ASSERT (OptionList != NULL);
+
+ Status = Mtftp4ParseOption (OptionList, Count, FALSE, MtftpOption);
+
+ FreePool (OptionList);
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h
new file mode 100644
index 0000000000..b7fdbf23e1
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h
@@ -0,0 +1,111 @@
+/** @file
+ Routines to process MTFTP4 options.
+
+Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#ifndef __EFI_MTFTP4_OPTION_H__
+#define __EFI_MTFTP4_OPTION_H__
+
+#define MTFTP4_SUPPORTED_OPTIONS 4
+#define MTFTP4_OPCODE_LEN 2
+#define MTFTP4_ERRCODE_LEN 2
+#define MTFTP4_BLKNO_LEN 2
+#define MTFTP4_DATA_HEAD_LEN 4
+
+#define MTFTP4_BLKSIZE_EXIST 0x01
+#define MTFTP4_TIMEOUT_EXIST 0x02
+#define MTFTP4_TSIZE_EXIST 0x04
+#define MTFTP4_MCAST_EXIST 0x08
+
+typedef struct {
+ UINT16 BlkSize;
+ UINT8 Timeout;
+ UINT32 Tsize;
+ IP4_ADDR McastIp;
+ UINT16 McastPort;
+ BOOLEAN Master;
+ UINT32 Exist;
+} MTFTP4_OPTION;
+
+/**
+ Allocate and fill in a array of Mtftp options from the Packet.
+
+ It first calls Mtftp4FillOption to get the option number, then allocate
+ the array, at last, call Mtftp4FillOption again to save the options.
+
+ @param Packet The packet to parse
+ @param PacketLen The length of the packet
+ @param OptionCount The number of options in the packet
+ @param OptionList The point to get the option array.
+
+ @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
+ well-formated OACK packet.
+ @retval EFI_SUCCESS The option array is build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
+
+**/
+EFI_STATUS
+Mtftp4ExtractOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ );
+
+/**
+ Parse the option in Options array to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Options The option array, which contains addresses of each
+ option's name/value string.
+ @param Count The number of options in the Options
+ @param Request Whether this is a request or OACK. The format of
+ multicast is different according to this setting.
+ @param MtftpOption The MTFTP4_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The option is mal-formated
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOption (
+ IN EFI_MTFTP4_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN Request,
+ OUT MTFTP4_OPTION *MtftpOption
+ );
+
+/**
+ Parse the options in the OACK packet to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Packet The OACK packet to parse
+ @param PacketLen The length of the packet
+ @param MtftpOption The MTFTP_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The packet option is mal-formated
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOptionOack (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT MTFTP4_OPTION *MtftpOption
+ );
+
+extern CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS];
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c
new file mode 100644
index 0000000000..e983d79791
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c
@@ -0,0 +1,795 @@
+/** @file
+ Routines to process Rrq (download).
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ The packet process callback for MTFTP download.
+
+ @param UdpPacket The packet received
+ @param EndPoint The local/remote access point of the packet
+ @param IoStatus The status of the receiving
+ @param Context Opaque parameter, which is the MTFTP session
+
+**/
+VOID
+EFIAPI
+Mtftp4RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+
+/**
+ Start the MTFTP session to download.
+
+ It will first initialize some of the internal states then build and send a RRQ
+ reqeuest packet, at last, it will start receive for the downloading.
+
+ @param Instance The Mtftp session
+ @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
+ or EFI_MTFTP4_OPCODE_DIR.
+
+ @retval EFI_SUCCESS The mtftp download session is started.
+ @retval Others Failed to start downloading.
+
+**/
+EFI_STATUS
+Mtftp4RrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [1, 0xffff]. For example:
+ // the client sends an RRQ request to the server, the server
+ // transfers the DATA1 block. If option negoitation is ongoing,
+ // the server will send back an OACK, then client will send ACK0.
+ //
+ Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp4SendRequest (Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
+}
+
+
+/**
+ Build and send a ACK packet for the download session.
+
+ @param Instance The Mtftp session
+ @param BlkNo The BlkNo to ack.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
+ @retval EFI_SUCCESS The ACK has been sent
+ @retval Others Failed to send the ACK.
+
+**/
+EFI_STATUS
+Mtftp4RrqSendAck (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 BlkNo
+ )
+{
+ EFI_MTFTP4_PACKET *Ack;
+ NET_BUF *Packet;
+
+ Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (
+ Packet,
+ sizeof (EFI_MTFTP4_ACK_HEADER),
+ FALSE
+ );
+ ASSERT (Ack != NULL);
+
+ Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
+ Ack->Ack.Block[0] = HTONS (BlkNo);
+
+ return Mtftp4SendPacket (Instance, Packet);
+}
+
+
+/**
+ Deliver the received data block to the user, which can be saved
+ in the user provide buffer or through the CheckPacket callback.
+
+ @param Instance The Mtftp session
+ @param Packet The received data packet
+ @param Len The packet length
+
+ @retval EFI_SUCCESS The data is saved successfully
+ @retval EFI_ABORTED The user tells to abort by return an error through
+ CheckPacket
+ @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
+ updated to the actual buffer size needed.
+
+**/
+EFI_STATUS
+Mtftp4RrqSaveBlock (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len
+ )
+{
+ EFI_MTFTP4_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT16 Block;
+ UINT64 Start;
+ UINT32 DataLen;
+ UINT64 TotalBlock;
+ BOOLEAN Completed;
+
+ Completed = FALSE;
+ Token = Instance->Token;
+ Block = NTOHS (Packet->Data.Block);
+ DataLen = Len - MTFTP4_DATA_HEAD_LEN;
+
+ //
+ // This is the last block, save the block no
+ //
+ if (DataLen < Instance->BlkSize) {
+ Completed = TRUE;
+ Instance->LastBlock = Block;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, Block);
+ }
+
+ //
+ // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
+ // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
+ // Note that : For bigger files, allowing the block counter to roll over
+ // to accept transfers of unlimited size. So TotalBlock is memorised as
+ // continuous block counter.
+ //
+ Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &TotalBlock);
+
+ if (Status == EFI_NOT_FOUND) {
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Token->CheckPacket != NULL) {
+ Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "User aborted download"
+ );
+
+ return EFI_ABORTED;
+ }
+ }
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);
+
+ if (Start + DataLen <= Token->BufferSize) {
+ CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);
+
+ //
+ // Update the file size when received the last block
+ //
+ if ((Instance->LastBlock == Block) && Completed) {
+ Token->BufferSize = Start + DataLen;
+ }
+
+ } else if (Instance->LastBlock != 0) {
+ //
+ // Don't save the data if the buffer is too small, return
+ // EFI_BUFFER_TOO_SMALL if received the last packet. This
+ // will give a accurate file length.
+ //
+ Token->BufferSize = Start + DataLen;
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_DISK_FULL,
+ (UINT8 *) "User provided memory block is too small"
+ );
+
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Function to process the received data packets.
+
+ It will save the block then send back an ACK if it is active.
+
+ @param Instance The downloading MTFTP session
+ @param Packet The packet received
+ @param Len The length of the packet
+ @param Multicast Whether this packet is multicast or unicast
+ @param Completed Return whether the download has completed
+
+ @retval EFI_SUCCESS The data packet is successfully processed
+ @retval EFI_ABORTED The download is aborted by the user
+ @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
+
+**/
+EFI_STATUS
+Mtftp4RrqHandleData (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ IN BOOLEAN Multicast,
+ OUT BOOLEAN *Completed
+ )
+{
+ EFI_STATUS Status;
+ UINT16 BlockNum;
+ INTN Expected;
+
+ *Completed = FALSE;
+ BlockNum = NTOHS (Packet->Data.Block);
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // If we are active and received an unexpected packet, retransmit
+ // the last ACK then restart receiving. If we are passive, save
+ // the block.
+ //
+ if (Instance->Master && (Expected != BlockNum)) {
+ Mtftp4Retransmit (Instance);
+ return EFI_SUCCESS;
+ }
+
+ Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Reset the passive client's timer whenever it received a
+ // valid data packet.
+ //
+ if (!Instance->Master) {
+ Mtftp4SetTimeout (Instance);
+ }
+
+ //
+ // Check whether we have received all the blocks. Send the ACK if we
+ // are active (unicast client or master client for multicast download).
+ // If we have received all the blocks, send an ACK even if we are passive
+ // to tell the server that we are done.
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Instance->Master || (Expected < 0)) {
+ if (Expected < 0) {
+ //
+ // If we are passive client, then the just received Block maybe
+ // isn't the last block. We need to send an ACK to the last block
+ // to inform the server that we are done. If we are active client,
+ // the Block == Instance->LastBlock.
+ //
+ BlockNum = Instance->LastBlock;
+ *Completed = TRUE;
+
+ } else {
+ BlockNum = (UINT16) (Expected - 1);
+ }
+
+ Mtftp4RrqSendAck (Instance, BlockNum);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Validate whether the options received in the server's OACK packet is valid.
+
+ The options are valid only if:
+ 1. The server doesn't include options not requested by us
+ 2. The server can only use smaller blksize than that is requested
+ 3. The server can only use the same timeout as requested
+ 4. The server doesn't change its multicast channel.
+
+ @param This The downloading Mtftp session
+ @param Reply The options in the OACK packet
+ @param Request The requested options
+
+ @retval TRUE The options in the OACK is OK.
+ @retval FALSE The options in the OACK is invalid.
+
+**/
+BOOLEAN
+Mtftp4RrqOackValid (
+ IN MTFTP4_PROTOCOL *This,
+ IN MTFTP4_OPTION *Reply,
+ IN MTFTP4_OPTION *Request
+ )
+{
+
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((Reply->Exist &~Request->Exist) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0)&& (Reply->BlkSize > Request->BlkSize)) ||
+ (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {
+ return FALSE;
+ }
+
+ //
+ // The server can send ",,master" to client to change its master
+ // setting. But if it use the specific multicast channel, it can't
+ // change the setting.
+ //
+ if (((Reply->Exist & MTFTP4_MCAST_EXIST) != 0) && (This->McastIp != 0)) {
+ if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) {
+ return FALSE;
+ }
+
+ if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Configure a UDP IO port to receive the multicast.
+
+ @param McastIo The UDP IO to configure
+ @param Context The opaque parameter to the function which is the
+ MTFTP session.
+
+ @retval EFI_SUCCESS The UDP child is successfully configured.
+ @retval Others Failed to configure the UDP child.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4RrqConfigMcastPort (
+ IN UDP_IO *McastIo,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_UDP4_CONFIG_DATA UdpConfig;
+ EFI_IPv4_ADDRESS Group;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ Config = &Instance->Config;
+
+ UdpConfig.AcceptBroadcast = FALSE;
+ UdpConfig.AcceptPromiscuous = FALSE;
+ UdpConfig.AcceptAnyPort = FALSE;
+ UdpConfig.AllowDuplicatePort = FALSE;
+ UdpConfig.TypeOfService = 0;
+ UdpConfig.TimeToLive = 64;
+ UdpConfig.DoNotFragment = FALSE;
+ UdpConfig.ReceiveTimeout = 0;
+ UdpConfig.TransmitTimeout = 0;
+ UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
+ IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);
+ IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);
+ UdpConfig.StationPort = Instance->McastPort;
+ UdpConfig.RemotePort = 0;
+
+ Ip = HTONL (Instance->ServerIp);
+ IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);
+
+ Status = McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, &UdpConfig);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!Config->UseDefaultSetting &&
+ !EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {
+ //
+ // The station IP address is manually configured and the Gateway IP is not 0.
+ // Add the default route for this UDP instance.
+ //
+ Status = McastIo->Protocol.Udp4->Routes (
+ McastIo->Protocol.Udp4,
+ FALSE,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr,
+ &Config->GatewayIp
+ );
+
+ if (EFI_ERROR (Status)) {
+ McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, NULL);
+ return Status;
+ }
+ }
+
+ //
+ // join the multicast group
+ //
+ Ip = HTONL (Instance->McastIp);
+ IP4_COPY_ADDRESS (&Group, &Ip);
+
+ return McastIo->Protocol.Udp4->Groups (McastIo->Protocol.Udp4, TRUE, &Group);
+}
+
+
+/**
+ Function to process the OACK.
+
+ It will first validate the OACK packet, then update the various negotiated parameters.
+
+ @param Instance The download MTFTP session
+ @param Packet The packet received
+ @param Len The packet length
+ @param Multicast Whether this packet is received as a multicast
+ @param Completed Returns whether the download has completed. NOT
+ used by this function.
+
+ @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
+ @retval EFI_TFTP_ERROR Some error happened during the process
+ @retval EFI_SUCCESS The OACK is successfully processed.
+
+**/
+EFI_STATUS
+Mtftp4RrqHandleOack (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ IN BOOLEAN Multicast,
+ OUT BOOLEAN *Completed
+ )
+{
+ MTFTP4_OPTION Reply;
+ EFI_STATUS Status;
+ INTN Expected;
+ EFI_UDP4_PROTOCOL *Udp4;
+
+ *Completed = FALSE;
+
+ //
+ // If already started the master download, don't change the
+ // setting. Master download always succeeds.
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+ ASSERT (Expected != -1);
+
+ if (Instance->Master && (Expected != 1)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
+
+ Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);
+
+ if (EFI_ERROR (Status) ||
+ !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {
+ //
+ // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if ((Reply.Exist & MTFTP4_MCAST_EXIST) != 0) {
+
+ //
+ // Save the multicast info. Always update the Master, only update the
+ // multicast IP address, block size, timeoute at the first time. If IP
+ // address is updated, create a UDP child to receive the multicast.
+ //
+ Instance->Master = Reply.Master;
+
+ if (Instance->McastIp == 0) {
+ if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Illegal multicast setting"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+
+ //
+ // Create a UDP child then start receive the multicast from it.
+ //
+ Instance->McastIp = Reply.McastIp;
+ Instance->McastPort = Reply.McastPort;
+ if (Instance->McastUdpPort == NULL) {
+ Instance->McastUdpPort = UdpIoCreateIo (
+ Instance->Service->Controller,
+ Instance->Service->Image,
+ Mtftp4RrqConfigMcastPort,
+ UDP_IO_UDP4_VERSION,
+ Instance
+ );
+ if (Instance->McastUdpPort != NULL) {
+ Status = gBS->OpenProtocol (
+ Instance->McastUdpPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ Instance->Service->Image,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ UdpIoFreeIo (Instance->McastUdpPort);
+ Instance->McastUdpPort = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ }
+
+
+ if (Instance->McastUdpPort == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION,
+ (UINT8 *) "Failed to create socket to receive multicast packet"
+ );
+
+ return Status;
+ }
+
+ //
+ // Update the parameters used.
+ //
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+ }
+
+ } else {
+ Instance->Master = TRUE;
+
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+ }
+
+ //
+ // Send an ACK to (Expected - 1) which is 0 for unicast download,
+ // or tell the server we want to receive the Expected block.
+ //
+ return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
+}
+
+
+/**
+ The packet process callback for MTFTP download.
+
+ @param UdpPacket The packet received
+ @param EndPoint The local/remote access point of the packet
+ @param IoStatus The status of the receiving
+ @param Context Opaque parameter, which is the MTFTP session
+
+**/
+VOID
+EFIAPI
+Mtftp4RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PACKET *Packet;
+ BOOLEAN Completed;
+ BOOLEAN Multicast;
+ EFI_STATUS Status;
+ UINT16 Opcode;
+ UINT32 Len;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+ Packet = NULL;
+ Completed = FALSE;
+ Multicast = FALSE;
+
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ //
+ // Find the port this packet is from to restart receive correctly.
+ //
+ Multicast = (BOOLEAN) (EndPoint->LocalAddr.Addr[0] == Instance->McastIp);
+
+ if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client. The server
+ // is required to use the same port as RemotePort to multicast the
+ // data.
+ //
+ if (EndPoint->RemotePort != Instance->ConnectedPort) {
+ if (Instance->ConnectedPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ConnectedPort = EndPoint->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+
+ if (UdpPacket->BlockOpNum > 1) {
+ Packet = AllocatePool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Call the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp4,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ switch (Opcode) {
+ case EFI_MTFTP4_OPCODE_DATA:
+ if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) ||
+ (Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_ERROR:
+ Status = EFI_TFTP_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ON_EXIT:
+
+ //
+ // Free the resources, then if !EFI_ERROR (Status), restart the
+ // receive, otherwise end the session.
+ //
+ if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
+ FreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !Completed) {
+ if (Multicast) {
+ Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
+ } else {
+ Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
+ }
+ }
+
+ if (EFI_ERROR (Status) || Completed) {
+ Mtftp4CleanOperation (Instance, Status);
+ }
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
new file mode 100644
index 0000000000..c625fda020
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
@@ -0,0 +1,634 @@
+/** @file
+ Support routines for Mtftp.
+
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ Allocate a MTFTP4 block range, then init it to the range of [Start, End]
+
+ @param Start The start block number
+ @param End The last block number in the range
+
+ @return Pointer to the created block range, NULL if failed to allocate memory.
+
+**/
+MTFTP4_BLOCK_RANGE *
+Mtftp4AllocateRange (
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));
+
+ if (Range == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Range->Link);
+ Range->Start = Start;
+ Range->End = End;
+ Range->Bound = End;
+
+ return Range;
+}
+
+
+/**
+ Initialize the block range for either RRQ or WRQ.
+
+ RRQ and WRQ have different requirements for Start and End.
+ For example, during start up, WRQ initializes its whole valid block range
+ to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
+ to start the upload. When the client received ACK0, it will remove 0 from the
+ range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send us the BLOCK1 in
+ response to the client's RRQ. When received BLOCK1, the client will remove
+ it from the block range and send an ACK. It also works if there is option
+ negotiation.
+
+ @param Head The block range head to initialize
+ @param Start The Start block number.
+ @param End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp4InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ Range = Mtftp4AllocateRange (Start, End);
+
+ if (Range == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (Head, &Range->Link);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the first valid block number on the range list.
+
+ @param Head The block range head
+
+ @return The first valid block number, -1 if the block range is empty.
+
+**/
+INTN
+Mtftp4GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ if (IsListEmpty (Head)) {
+ return -1;
+ }
+
+ Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);
+ return Range->Start;
+}
+
+
+/**
+ Set the last block number of the block range list.
+
+ It will remove all the blocks after the Last. MTFTP initialize the block range
+ to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
+ last block number, it will call this function to set the last block number.
+
+ @param Head The block range list
+ @param Last The last block number
+
+**/
+VOID
+Mtftp4SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ //
+ // Iterate from the tail to head to remove the block number
+ // after the last.
+ //
+ while (!IsListEmpty (Head)) {
+ Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);
+
+ if (Range->Start > Last) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ continue;
+ }
+
+ if (Range->End > Last) {
+ Range->End = Last;
+ }
+
+ return ;
+ }
+}
+
+
+/**
+ Remove the block number from the block range list.
+
+ @param Head The block range list to remove from
+ @param Num The block number to remove
+ @param Completed Whether Num is the last block number
+ @param TotalBlock The continuous block number in all
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list
+ @retval EFI_SUCCESS The block number has been removed from the list
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+
+**/
+EFI_STATUS
+Mtftp4RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *TotalBlock
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+ MTFTP4_BLOCK_RANGE *NewRange;
+ LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+
+ //
+ // Each block represents a hole [Start, End] in the file,
+ // skip to the first range with End >= Num
+ //
+ Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
+
+ if (Range->End < Num) {
+ continue;
+ }
+
+ //
+ // There are three different cases for Start
+ // 1. (Start > Num) && (End >= Num):
+ // because all the holes before this one has the condition of
+ // End < Num, so this block number has been removed.
+ //
+ // 2. (Start == Num) && (End >= Num):
+ // Need to increase the Start by one, and if End == Num, this
+ // hole has been removed completely, remove it.
+ //
+ // 3. (Start < Num) && (End >= Num):
+ // if End == Num, only need to decrease the End by one because
+ // we have (Start < Num) && (Num == End), so (Start <= End - 1).
+ // if (End > Num), the hold is splited into two holes, with
+ // [Start, Num - 1] and [Num + 1, End].
+ //
+ if (Range->Start > Num) {
+ return EFI_NOT_FOUND;
+
+ } else if (Range->Start == Num) {
+ Range->Start++;
+
+ //
+ // Note that: RFC 1350 does not mention block counter roll-over,
+ // but several TFTP hosts implement the roll-over be able to accept
+ // transfers of unlimited size. There is no consensus, however, whether
+ // the counter should wrap around to zero or to one. Many implementations
+ // wrap to zero, because this is the simplest to implement. Here we choose
+ // this solution.
+ //
+ *TotalBlock = Num;
+
+ if (Range->Round > 0) {
+ *TotalBlock += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;
+ }
+
+ if (Range->Start > Range->Bound) {
+ Range->Start = 0;
+ Range->Round ++;
+ }
+
+ if ((Range->Start > Range->End) || Completed) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ }
+
+ return EFI_SUCCESS;
+
+ } else {
+ if (Range->End == Num) {
+ Range->End--;
+ } else {
+ NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
+
+ if (NewRange == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Range->End = Num - 1;
+ NetListInsertAfter (&Range->Link, &NewRange->Link);
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Build then transmit the request packet for the MTFTP session.
+
+ @param Instance The Mtftp session
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
+ @retval EFI_SUCCESS The request is built and sent
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendRequest (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_OPTION *Options;
+ EFI_MTFTP4_TOKEN *Token;
+ RETURN_STATUS Status;
+ NET_BUF *Nbuf;
+ UINT8 *Mode;
+ UINT8 *Cur;
+ UINTN Index;
+ UINT32 BufferLength;
+ UINTN FileNameLength;
+ UINTN ModeLength;
+ UINTN OptionStrLength;
+ UINTN ValueStrLength;
+
+ Token = Instance->Token;
+ Options = Token->OptionList;
+ Mode = Instance->Token->ModeStr;
+
+ if (Mode == NULL) {
+ Mode = (UINT8 *) "octet";
+ }
+
+ //
+ // Compute the packet length
+ //
+ FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
+ ModeLength = AsciiStrLen ((CHAR8 *) Mode);
+ BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
+
+ for (Index = 0; Index < Token->OptionCount; Index++) {
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+ BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
+ }
+ //
+ // Allocate a packet then copy the data over
+ //
+ if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
+ ASSERT (Packet != NULL);
+
+ Packet->OpCode = HTONS (Instance->Operation);
+ BufferLength -= sizeof (Packet->OpCode);
+
+ Cur = Packet->Rrq.Filename;
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (FileNameLength + 1);
+ Cur += FileNameLength + 1;
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (ModeLength + 1);
+ Cur += ModeLength + 1;
+
+ for (Index = 0; Index < Token->OptionCount; ++Index) {
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (OptionStrLength + 1);
+ Cur += OptionStrLength + 1;
+
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (ValueStrLength + 1);
+ Cur += ValueStrLength + 1;
+
+ }
+
+ return Mtftp4SendPacket (Instance, Nbuf);
+}
+
+
+/**
+ Build then send an error message.
+
+ @param Instance The MTFTP session
+ @param ErrCode The error code
+ @param ErrInfo The error message
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendError (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8 *ErrInfo
+ )
+{
+ NET_BUF *Packet;
+ EFI_MTFTP4_PACKET *TftpError;
+ UINT32 Len;
+
+ Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));
+ Packet = NetbufAlloc (Len);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);
+ ASSERT (TftpError != NULL);
+
+ TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);
+ TftpError->Error.ErrorCode = HTONS (ErrCode);
+
+ AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);
+
+ return Mtftp4SendPacket (Instance, Packet);
+}
+
+
+/**
+ The callback function called when the packet is transmitted.
+
+ It simply frees the packet.
+
+ @param Packet The transmitted (or failed to) packet
+ @param EndPoint The local and remote UDP access point
+ @param IoStatus The result of the transmission
+ @param Context Opaque parameter to the callback
+
+**/
+VOID
+EFIAPI
+Mtftp4OnPacketSent (
+ IN NET_BUF *Packet,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+/**
+ Set the timeout for the instance. User a longer time for passive instances.
+
+ @param Instance The Mtftp session to set time out
+
+**/
+VOID
+Mtftp4SetTimeout (
+ IN OUT MTFTP4_PROTOCOL *Instance
+ )
+{
+ if (Instance->Master) {
+ Instance->PacketToLive = Instance->Timeout;
+ } else {
+ Instance->PacketToLive = Instance->Timeout * 2;
+ }
+}
+
+
+/**
+ Send the packet for the instance.
+
+ It will first save a reference to the packet for later retransmission.
+ Then determine the destination port, listen port for requests, and connected
+ port for others. At last, send the packet out.
+
+ @param Instance The Mtftp instance
+ @param Packet The packet to send
+
+ @retval EFI_SUCCESS The packet is sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendPacket (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN OUT NET_BUF *Packet
+ )
+{
+ UDP_END_POINT UdpPoint;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+ UINT8 *Buffer;
+
+ //
+ // Save the packet for retransmission
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Packet;
+
+ Instance->CurRetry = 0;
+ Mtftp4SetTimeout (Instance);
+
+ ZeroMem (&UdpPoint, sizeof (UdpPoint));
+ UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
+
+ //
+ // Send the requests to the listening port, other packets
+ // to the connected port
+ //
+ Buffer = NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Buffer != NULL);
+ OpCode = NTOHS (*(UINT16 *)Buffer);
+
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||
+ (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
+ (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
+ UdpPoint.RemotePort = Instance->ListeningPort;
+ } else {
+ UdpPoint.RemotePort = Instance->ConnectedPort;
+ }
+
+ NET_GET_REF (Packet);
+
+ Status = UdpIoSendDatagram (
+ Instance->UnicastPort,
+ Packet,
+ &UdpPoint,
+ NULL,
+ Mtftp4OnPacketSent,
+ Instance
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ }
+
+ return Status;
+}
+
+
+/**
+ Retransmit the last packet for the instance.
+
+ @param Instance The Mtftp instance
+
+ @retval EFI_SUCCESS The last packet is retransmitted.
+ @retval Others Failed to retransmit.
+
+**/
+EFI_STATUS
+Mtftp4Retransmit (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ UDP_END_POINT UdpPoint;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+ UINT8 *Buffer;
+
+ ASSERT (Instance->LastPacket != NULL);
+
+ ZeroMem (&UdpPoint, sizeof (UdpPoint));
+ UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
+
+ //
+ // Set the requests to the listening port, other packets to the connected port
+ //
+ Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);
+ ASSERT (Buffer != NULL);
+ OpCode = NTOHS (*(UINT16 *) Buffer);
+
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
+ (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
+ UdpPoint.RemotePort = Instance->ListeningPort;
+ } else {
+ UdpPoint.RemotePort = Instance->ConnectedPort;
+ }
+
+ NET_GET_REF (Instance->LastPacket);
+
+ Status = UdpIoSendDatagram (
+ Instance->UnicastPort,
+ Instance->LastPacket,
+ &UdpPoint,
+ NULL,
+ Mtftp4OnPacketSent,
+ Instance
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Instance->LastPacket);
+ }
+
+ return Status;
+}
+
+
+/**
+ The timer ticking function for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_TOKEN *Token;
+
+ MtftpSb = (MTFTP4_SERVICE *) Context;
+
+ //
+ // Iterate through all the children of the Mtftp service instance. Time
+ // out the packet. If maximum retries reached, clean the session up.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
+
+ if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {
+ continue;
+ }
+
+ //
+ // Call the user's time out handler
+ //
+ Token = Instance->Token;
+
+ if ((Token->TimeoutCallback != NULL) &&
+ EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer in time out"
+ );
+
+ Mtftp4CleanOperation (Instance, EFI_ABORTED);
+ continue;
+ }
+
+ //
+ // Retransmit the packet if haven't reach the maxmium retry count,
+ // otherwise exit the transfer.
+ //
+ if (++Instance->CurRetry < Instance->MaxRetry) {
+ Mtftp4Retransmit (Instance);
+ Mtftp4SetTimeout (Instance);
+ } else {
+ Mtftp4CleanOperation (Instance, EFI_TIMEOUT);
+ continue;
+ }
+ }
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h
new file mode 100644
index 0000000000..802e3867db
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h
@@ -0,0 +1,203 @@
+/** @file
+ Support routines for MTFTP.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_MTFTP4_SUPPORT_H__
+#define __EFI_MTFTP4_SUPPORT_H__
+
+//
+// The structure representing a range of block numbers, [Start, End].
+// It is used to remember the holes in the MTFTP block space. If all
+// the holes are filled in, then the download or upload has completed.
+//
+typedef struct {
+ LIST_ENTRY Link;
+ INTN Start;
+ INTN End;
+ INTN Round;
+ INTN Bound;
+} MTFTP4_BLOCK_RANGE;
+
+
+/**
+ Initialize the block range for either RRQ or WRQ.
+
+ RRQ and WRQ have different requirements for Start and End.
+ For example, during start up, WRQ initializes its whole valid block range
+ to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
+ to start the upload. When the client received ACK0, it will remove 0 from the
+ range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send us the BLOCK1 in
+ response to the client's RRQ. When received BLOCK1, the client will remove
+ it from the block range and send an ACK. It also works if there is option
+ negotiation.
+
+ @param Head The block range head to initialize
+ @param Start The Start block number.
+ @param End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp4InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ );
+
+/**
+ Get the first valid block number on the range list.
+
+ @param Head The block range head
+
+ @return The first valid block number, -1 if the block range is empty.
+
+**/
+INTN
+Mtftp4GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ );
+
+/**
+ Set the last block number of the block range list.
+
+ It will remove all the blocks after the Last. MTFTP initialize the block range
+ to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
+ last block number, it will call this function to set the last block number.
+
+ @param Head The block range list
+ @param Last The last block number
+
+**/
+VOID
+Mtftp4SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ );
+
+/**
+ Remove the block number from the block range list.
+
+ @param Head The block range list to remove from
+ @param Num The block number to remove
+ @param Completed Wether Num is the last block number
+ @param TotalBlock The continuous block number in all
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list
+ @retval EFI_SUCCESS The block number has been removed from the list
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+
+**/
+EFI_STATUS
+Mtftp4RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *TotalBlock
+ );
+
+/**
+ Set the timeout for the instance. User a longer time for passive instances.
+
+ @param Instance The Mtftp session to set time out
+
+**/
+VOID
+Mtftp4SetTimeout (
+ IN OUT MTFTP4_PROTOCOL *Instance
+ );
+
+/**
+ Send the packet for the instance.
+
+ It will first save a reference to the packet for later retransmission.
+ Then determine the destination port, listen port for requests, and connected
+ port for others. At last, send the packet out.
+
+ @param Instance The Mtftp instance
+ @param Packet The packet to send
+
+ @retval EFI_SUCCESS The packet is sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendPacket (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN OUT NET_BUF *Packet
+ );
+
+/**
+ Build then transmit the request packet for the MTFTP session.
+
+ @param Instance The Mtftp session
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
+ @retval EFI_SUCCESS The request is built and sent
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendRequest (
+ IN MTFTP4_PROTOCOL *Instance
+ );
+
+/**
+ Build then send an error message.
+
+ @param Instance The MTFTP session
+ @param ErrCode The error code
+ @param ErrInfo The error message
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendError (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8 *ErrInfo
+ );
+
+/**
+ Retransmit the last packet for the instance.
+
+ @param Instance The Mtftp instance
+
+ @retval EFI_SUCCESS The last packet is retransmitted.
+ @retval Others Failed to retransmit.
+
+**/
+EFI_STATUS
+Mtftp4Retransmit (
+ IN MTFTP4_PROTOCOL *Instance
+ );
+
+/**
+ The timer ticking function for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
new file mode 100644
index 0000000000..e825714700
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
@@ -0,0 +1,531 @@
+/** @file
+ Routines to process Wrq (upload).
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp4Impl.h"
+
+
+
+/**
+ Build then send a MTFTP data packet for the MTFTP upload session.
+
+ @param Instance The MTFTP upload session.
+ @param BlockNum The block number to send.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to build the packet.
+ @retval EFI_ABORTED The consumer of this child directs to abort the
+ transmission by return an error through PacketNeeded.
+ @retval EFI_SUCCESS The data is sent.
+
+**/
+EFI_STATUS
+Mtftp4WrqSendBlock (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN UINT16 BlockNum
+ )
+{
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_TOKEN *Token;
+ NET_BUF *UdpPacket;
+ EFI_STATUS Status;
+ UINT16 DataLen;
+ UINT8 *DataBuf;
+ UINT64 Start;
+
+ //
+ // Allocate a buffer to hold the user data
+ //
+ UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);
+
+ if (UdpPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);
+ ASSERT (Packet != NULL);
+
+ Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);
+ Packet->Data.Block = HTONS (BlockNum);
+
+ //
+ // Read the block from either the buffer or PacketNeeded callback
+ //
+ Token = Instance->Token;
+ DataLen = Instance->BlkSize;
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
+
+ if (Token->BufferSize < Start + Instance->BlkSize) {
+ DataLen = (UINT16) (Token->BufferSize - Start);
+ Instance->LastBlock = BlockNum;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
+ }
+
+ } else {
+ //
+ // Get data from PacketNeeded
+ //
+ DataBuf = NULL;
+ Status = Token->PacketNeeded (
+ &Instance->Mtftp4,
+ Token,
+ &DataLen,
+ (VOID **) &DataBuf
+ );
+
+ if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
+ if (DataBuf != NULL) {
+ FreePool (DataBuf);
+ }
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+
+ return EFI_ABORTED;
+ }
+
+ if (DataLen < Instance->BlkSize) {
+ Instance->LastBlock = BlockNum;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, DataBuf, DataLen);
+ FreePool (DataBuf);
+ }
+ }
+
+ return Mtftp4SendPacket (Instance, UdpPacket);
+}
+
+
+/**
+ Function to handle received ACK packet.
+
+ If the ACK number matches the expected block number, and there are more
+ data pending, send the next block. Otherwise tell the caller that we are done.
+
+ @param Instance The MTFTP upload session
+ @param Packet The MTFTP packet received
+ @param Len The packet length
+ @param Completed Return whether the upload has finished.
+
+ @retval EFI_SUCCESS The ACK is successfully processed.
+ @retval EFI_TFTP_ERROR The block number loops back.
+ @retval Others Failed to transmit the next data packet.
+
+**/
+EFI_STATUS
+Mtftp4WrqHandleAck (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ OUT BOOLEAN *Completed
+ )
+{
+ UINT16 AckNum;
+ INTN Expected;
+ UINT64 TotalBlock;
+
+ *Completed = FALSE;
+ AckNum = NTOHS (Packet->Ack.Block[0]);
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
+ // restart receive.
+ //
+ if (Expected != AckNum) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove the acked block number, if this is the last block number,
+ // tell the Mtftp4WrqInput to finish the transfer. This is the last
+ // block number if the block range are empty..
+ //
+ Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed,&TotalBlock);
+
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Expected < 0) {
+
+ //
+ // The block range is empty. It may either because the the last
+ // block has been ACKed, or the sequence number just looped back,
+ // that is, there is more than 0xffff blocks.
+ //
+ if (Instance->LastBlock == AckNum) {
+ ASSERT (Instance->LastBlock >= 1);
+ *Completed = TRUE;
+ return EFI_SUCCESS;
+
+ } else {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "Block number rolls back, not supported, try blksize option"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+ }
+
+ return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);
+}
+
+
+/**
+ Check whether the received OACK is valid.
+
+ The OACK is valid only if:
+ 1. It only include options requested by us
+ 2. It can only include a smaller block size
+ 3. It can't change the proposed time out value.
+ 4. Other requirements of the individal MTFTP options as required.
+
+ @param Reply The options included in the OACK
+ @param Request The options we requested
+
+ @retval TRUE The options included in OACK is valid.
+ @retval FALSE The options included in OACK is invalid.
+
+**/
+BOOLEAN
+Mtftp4WrqOackValid (
+ IN MTFTP4_OPTION *Reply,
+ IN MTFTP4_OPTION *Request
+ )
+{
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((Reply->Exist & ~Request->Exist) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||
+ (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Function to handle the MTFTP OACK packet.
+
+ It parses the packet's options, and update the internal states of the session.
+
+ @param Instance The MTFTP session
+ @param Packet The received OACK packet
+ @param Len The length of the packet
+ @param Completed Whether the transmisson has completed. NOT used by
+ this function.
+
+ @retval EFI_SUCCESS The OACK process is OK
+ @retval EFI_TFTP_ERROR Some error occured, and the session reset.
+
+**/
+EFI_STATUS
+Mtftp4WrqHandleOack (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ OUT BOOLEAN *Completed
+ )
+{
+ MTFTP4_OPTION Reply;
+ EFI_MTFTP4_PACKET Bogus;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *Completed = FALSE;
+
+ //
+ // Ignore the OACK if already started the upload
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Expected != 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
+ Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);
+
+ if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {
+ //
+ // Don't send a MTFTP error packet when out of resource, it can
+ // only make it worse.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+
+ //
+ // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
+ // which will start the transmission of the first data block.
+ //
+ Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
+ Bogus.Ack.Block[0] = 0;
+
+ Status = Mtftp4WrqHandleAck (
+ Instance,
+ &Bogus,
+ sizeof (EFI_MTFTP4_ACK_HEADER),
+ Completed
+ );
+
+ return Status;
+}
+
+
+/**
+ The input process routine for MTFTP upload.
+
+ @param UdpPacket The received MTFTP packet.
+ @param EndPoint The local/remote access point
+ @param IoStatus The result of the packet receiving
+ @param Context Opaque parameter for the callback, which is the
+ MTFTP session.
+**/
+VOID
+EFIAPI
+Mtftp4WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PACKET *Packet;
+ BOOLEAN Completed;
+ EFI_STATUS Status;
+ UINT32 Len;
+ UINT16 Opcode;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
+
+ Completed = FALSE;
+ Packet = NULL;
+ Status = EFI_SUCCESS;
+
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client.
+ //
+ if (EndPoint->RemotePort != Instance->ConnectedPort) {
+ if (Instance->ConnectedPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ConnectedPort = EndPoint->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+
+ if (UdpPacket->BlockOpNum > 1) {
+ Packet = AllocatePool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Call the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp4,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ switch (Opcode) {
+ case EFI_MTFTP4_OPCODE_ACK:
+ if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ if (Len <= MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_ERROR:
+ Status = EFI_TFTP_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ON_EXIT:
+ //
+ // Free the resources, then if !EFI_ERROR (Status) and not completed,
+ // restart the receive, otherwise end the session.
+ //
+ if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
+ FreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !Completed) {
+ Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
+ }
+
+ //
+ // Status may have been updated by UdpIoRecvDatagram
+ //
+ if (EFI_ERROR (Status) || Completed) {
+ Mtftp4CleanOperation (Instance, Status);
+ }
+}
+
+
+
+/**
+ Start the MTFTP session for upload.
+
+ It will first init some states, then send the WRQ request packet,
+ and start receiving the packet.
+
+ @param Instance The MTFTP session
+ @param Operation Redundant parameter, which is always
+ EFI_MTFTP4_OPCODE_WRQ here.
+
+ @retval EFI_SUCCESS The upload process has been started.
+ @retval Others Failed to start the upload.
+
+**/
+EFI_STATUS
+Mtftp4WrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [0, 0xffff]. For example:
+ // the client sends an WRQ request to the server, the server
+ // ACK with an ACK0 to let client start transfer the first
+ // packet.
+ //
+ Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp4SendRequest (Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Callback.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Callback.c
new file mode 100644
index 0000000000..7f35642937
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Callback.c
@@ -0,0 +1,360 @@
+/** @file
+ This file contains the callback routines for undi3.1.
+ the callback routines for Undi3.1 have an extra parameter UniqueId which
+ stores the interface context for the NIC that snp is trying to talk.
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+/**
+ Acquire or release a lock of the exclusive access to a critical section of the
+ code/data.
+
+ This is a callback routine supplied to UNDI3.1 at undi_start time.
+ New callbacks for 3.1: there won't be a virtual2physical callback for UNDI 3.1
+ because undi3.1 uses the MemMap call to map the required address by itself!
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store Undi interface context (Undi does not read or write
+ this variable).
+ @param Enable Non-zero indicates acquire; Zero indicates release.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackBlock (
+ IN UINT64 UniqueId,
+ IN UINT32 Enable
+ )
+{
+ SNP_DRIVER *Snp;
+
+ Snp = (SNP_DRIVER *) (UINTN) UniqueId;
+ //
+ // tcpip was calling snp at tpl_notify and when we acquire a lock that was
+ // created at a lower level (TPL_CALLBACK) it gives an assert!
+ //
+ if (Enable != 0) {
+ EfiAcquireLock (&Snp->Lock);
+ } else {
+ EfiReleaseLock (&Snp->Lock);
+ }
+}
+
+/**
+ Delay MicroSeconds of micro seconds.
+
+ This is a callback routine supplied to UNDI at undi_start time.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store Undi interface context (Undi does not read or write
+ this variable).
+ @param MicroSeconds Number of micro seconds to pause, ususlly multiple of 10.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackDelay (
+ IN UINT64 UniqueId,
+ IN UINT64 MicroSeconds
+ )
+{
+ if (MicroSeconds != 0) {
+ gBS->Stall ((UINTN) MicroSeconds);
+ }
+}
+
+/**
+ IO routine for UNDI3.1.
+
+ This is a callback routine supplied to UNDI at undi_start time.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this
+ to store Undi interface context (Undi does not read or
+ write this variable).
+ @param ReadOrWrite Indicates read or write, IO or Memory.
+ @param NumBytes Number of bytes to read or write.
+ @param MemOrPortAddr IO or memory address to read from or write to.
+ @param BufferPtr Memory location to read into or that contains the bytes
+ to write.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackMemio (
+ IN UINT64 UniqueId,
+ IN UINT8 ReadOrWrite,
+ IN UINT8 NumBytes,
+ IN UINT64 MemOrPortAddr,
+ IN OUT UINT64 BufferPtr
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+
+ Snp = (SNP_DRIVER *) (UINTN) UniqueId;
+
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;
+ switch (NumBytes) {
+ case 2:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;
+ break;
+
+ case 4:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;
+ break;
+
+ case 8:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;
+ break;
+ }
+
+ switch (ReadOrWrite) {
+ case PXE_IO_READ:
+ Snp->PciIo->Io.Read (
+ Snp->PciIo,
+ Width,
+ Snp->IoBarIndex, // BAR 1 (for 32bit regs), IO base address
+ MemOrPortAddr,
+ 1, // count
+ (VOID *) (UINTN) BufferPtr
+ );
+ break;
+
+ case PXE_IO_WRITE:
+ Snp->PciIo->Io.Write (
+ Snp->PciIo,
+ Width,
+ Snp->IoBarIndex, // BAR 1 (for 32bit regs), IO base address
+ MemOrPortAddr,
+ 1, // count
+ (VOID *) (UINTN) BufferPtr
+ );
+ break;
+
+ case PXE_MEM_READ:
+ Snp->PciIo->Mem.Read (
+ Snp->PciIo,
+ Width,
+ Snp->MemoryBarIndex, // BAR 0, Memory base address
+ MemOrPortAddr,
+ 1, // count
+ (VOID *) (UINTN) BufferPtr
+ );
+ break;
+
+ case PXE_MEM_WRITE:
+ Snp->PciIo->Mem.Write (
+ Snp->PciIo,
+ Width,
+ Snp->MemoryBarIndex, // BAR 0, Memory base address
+ MemOrPortAddr,
+ 1, // count
+ (VOID *) (UINTN) BufferPtr
+ );
+ break;
+ }
+
+ return ;
+}
+
+/**
+ Map a CPU address to a device address.
+
+ This is a callback routine supplied to UNDI at undi_start time.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store Undi interface context (Undi does not read or write
+ this variable).
+ @param CpuAddr Virtual address to be mapped.
+ @param NumBytes Size of memory to be mapped.
+ @param Direction Direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways.
+ @param DeviceAddrPtr Pointer to return the mapped device address.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackMap (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN OUT UINT64 DeviceAddrPtr
+ )
+{
+ EFI_PHYSICAL_ADDRESS *DevAddrPtr;
+ EFI_PCI_IO_PROTOCOL_OPERATION DirectionFlag;
+ UINTN BuffSize;
+ SNP_DRIVER *Snp;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ BuffSize = (UINTN) NumBytes;
+ Snp = (SNP_DRIVER *) (UINTN) UniqueId;
+ DevAddrPtr = (EFI_PHYSICAL_ADDRESS *) (UINTN) DeviceAddrPtr;
+
+ if (CpuAddr == 0) {
+ *DevAddrPtr = 0;
+ return ;
+ }
+
+ switch (Direction) {
+ case TO_AND_FROM_DEVICE:
+ DirectionFlag = EfiPciIoOperationBusMasterCommonBuffer;
+ break;
+
+ case FROM_DEVICE:
+ DirectionFlag = EfiPciIoOperationBusMasterWrite;
+ break;
+
+ case TO_DEVICE:
+ DirectionFlag = EfiPciIoOperationBusMasterRead;
+ break;
+
+ default:
+ *DevAddrPtr = 0;
+ //
+ // any non zero indicates error!
+ //
+ return ;
+ }
+ //
+ // find an unused map_list entry
+ //
+ for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {
+ if (Snp->MapList[Index].VirtualAddress == 0) {
+ break;
+ }
+ }
+
+ if (Index >= MAX_MAP_LENGTH) {
+ DEBUG ((EFI_D_INFO, "SNP maplist is FULL\n"));
+ *DevAddrPtr = 0;
+ return ;
+ }
+
+ Snp->MapList[Index].VirtualAddress = (EFI_PHYSICAL_ADDRESS) CpuAddr;
+
+ Status = Snp->PciIo->Map (
+ Snp->PciIo,
+ DirectionFlag,
+ (VOID *) (UINTN) CpuAddr,
+ &BuffSize,
+ DevAddrPtr,
+ &(Snp->MapList[Index].MapCookie)
+ );
+ if (Status != EFI_SUCCESS) {
+ *DevAddrPtr = 0;
+ Snp->MapList[Index].VirtualAddress = 0;
+ }
+
+ return ;
+}
+
+/**
+ Unmap an address that was previously mapped using map callback.
+
+ This is a callback routine supplied to UNDI at undi_start time.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store. Undi interface context (Undi does not read or write
+ this variable).
+ @param CpuAddr Virtual address that was mapped.
+ @param NumBytes Size of memory mapped.
+ @param Direction Direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways.
+ @param DeviceAddr The mapped device address.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackUnmap (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr
+ )
+{
+ SNP_DRIVER *Snp;
+ UINT16 Index;
+
+ Snp = (SNP_DRIVER *) (UINTN) UniqueId;
+
+ for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {
+ if (Snp->MapList[Index].VirtualAddress == CpuAddr) {
+ break;
+ }
+ }
+
+ if (Index >= MAX_MAP_LENGTH) {
+ DEBUG ((EFI_D_ERROR, "SNP could not find a mapping, failed to unmap.\n"));
+ return ;
+ }
+
+ Snp->PciIo->Unmap (Snp->PciIo, Snp->MapList[Index].MapCookie);
+ Snp->MapList[Index].VirtualAddress = 0;
+ Snp->MapList[Index].MapCookie = NULL;
+ return ;
+}
+
+/**
+ Synchronize the virtual buffer contents with the mapped buffer contents.
+
+ This is a callback routine supplied to UNDI at undi_start time. The virtual
+ and mapped buffers need not correspond to the same physical memory (especially
+ if the virtual address is > 4GB). Depending on the direction for which the
+ buffer is mapped, undi will need to synchronize their contents whenever it
+ writes to/reads from the buffer using either the cpu address or the device
+ address.
+ EFI does not provide a sync call since virt=physical, we should just do the
+ synchronization ourselves here.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store Undi interface context (Undi does not read or write
+ this variable).
+ @param CpuAddr Virtual address that was mapped.
+ @param NumBytes Size of memory mapped.
+ @param Direction Direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways.
+ @param DeviceAddr The mapped device address.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackSync (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr
+ )
+{
+ if ((CpuAddr == 0) || (DeviceAddr == 0) || (NumBytes == 0)) {
+ return ;
+
+ }
+
+ switch (Direction) {
+ case FROM_DEVICE:
+ CopyMem ((UINT8 *) (UINTN) CpuAddr, (UINT8 *) (UINTN) DeviceAddr, NumBytes);
+ break;
+
+ case TO_DEVICE:
+ CopyMem ((UINT8 *) (UINTN) DeviceAddr, (UINT8 *) (UINTN) CpuAddr, NumBytes);
+ break;
+ }
+
+ return ;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c
new file mode 100644
index 0000000000..d8b8184018
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c
@@ -0,0 +1,436 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for SnpDxe driver.
+
+Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Snp.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] 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 DriverName[out] 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
+SimpleNetworkComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] 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 ChildHandle[in] 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 Language[in] 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 ControllerName[out] 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 NULL.
+
+ @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
+SimpleNetworkComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gSimpleNetworkComponentName = {
+ SimpleNetworkComponentNameGetDriverName,
+ SimpleNetworkComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gSimpleNetworkComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SimpleNetworkComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SimpleNetworkComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSimpleNetworkDriverNameTable[] = {
+ {
+ "eng;en",
+ L"Simple Network Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gSimpleNetworkControllerNameTable = NULL;
+
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] 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 DriverName[out] 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
+SimpleNetworkComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mSimpleNetworkDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gSimpleNetworkComponentName)
+ );
+}
+
+/**
+ Update the component name for the Snp child handle.
+
+ @param Snp[in] A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ UINTN OffSet;
+ UINTN Index;
+
+ if (Snp == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OffSet = 0;
+ OffSet += UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"SNP (MAC="
+ );
+ for (Index = 0; Index < Snp->Mode->HwAddressSize; Index++) {
+ OffSet += UnicodeSPrint (
+ HandleName + OffSet,
+ sizeof (HandleName) - OffSet * sizeof (CHAR16),
+ L"%02X-",
+ Snp->Mode->CurrentAddress.Addr[Index]
+ );
+ }
+ ASSERT (OffSet > 0);
+ //
+ // Remove the last '-'
+ //
+ OffSet--;
+ OffSet += UnicodeSPrint (
+ HandleName + OffSet,
+ sizeof (HandleName) - OffSet * sizeof (CHAR16),
+ L")"
+ );
+ if (gSimpleNetworkControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gSimpleNetworkControllerNameTable);
+ gSimpleNetworkControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gSimpleNetworkComponentName.SupportedLanguages,
+ &gSimpleNetworkControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gSimpleNetworkComponentName2.SupportedLanguages,
+ &gSimpleNetworkControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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.
+ Currently not implemented.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] 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 ChildHandle[in] 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 Language[in] 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 ControllerName[out] 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 NULL.
+
+ @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
+SimpleNetworkComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver is currently managing ControllHandle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gSimpleNetworkDriverBinding.DriverBindingHandle,
+ &gEfiSimpleNetworkProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ControllerHandle
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **)&Snp,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Snp);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gSimpleNetworkControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gSimpleNetworkComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Get_status.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Get_status.c
new file mode 100644
index 0000000000..4c3bdae107
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Get_status.c
@@ -0,0 +1,263 @@
+/** @file
+ Implementation of reading the current interrupt status and recycled transmit
+ buffer status from a network interface.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+/**
+ Call undi to get the status of the interrupts, get the list of recycled transmit
+ buffers that completed transmitting. The recycled transmit buffer address will
+ be saved into Snp->RecycledTxBuf. This function will also update the MediaPresent
+ field of EFI_SIMPLE_NETWORK_MODE if UNDI support it.
+
+ @param[in] Snp Pointer to snp driver structure.
+ @param[out] InterruptStatusPtr A non null pointer to contain the interrupt
+ status.
+ @param[in] GetTransmittedBuf Set to TRUE to retrieve the recycled transmit
+ buffer address.
+
+ @retval EFI_SUCCESS The status of the network interface was retrieved.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+
+**/
+EFI_STATUS
+PxeGetStatus (
+ IN SNP_DRIVER *Snp,
+ OUT UINT32 *InterruptStatusPtr,
+ IN BOOLEAN GetTransmittedBuf
+ )
+{
+ PXE_DB_GET_STATUS *Db;
+ UINT16 InterruptFlags;
+ UINT32 Index;
+ UINT64 *Tmp;
+
+ Tmp = NULL;
+ Db = Snp->Db;
+ Snp->Cdb.OpCode = PXE_OPCODE_GET_STATUS;
+
+ Snp->Cdb.OpFlags = 0;
+
+ if (GetTransmittedBuf) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS;
+ ZeroMem (Db->TxBuffer, sizeof (Db->TxBuffer));
+ }
+
+ if (InterruptStatusPtr != NULL) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_GET_INTERRUPT_STATUS;
+ }
+
+ if (Snp->MediaStatusSupported) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_GET_MEDIA_STATUS;
+ }
+
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_GET_STATUS);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nSnp->undi.get_status() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_NET,
+ "\nSnp->undi.get_status() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // report the values back..
+ //
+ if (InterruptStatusPtr != NULL) {
+ InterruptFlags = (UINT16) (Snp->Cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_INTERRUPT_MASK);
+
+ *InterruptStatusPtr = 0;
+
+ if ((InterruptFlags & PXE_STATFLAGS_GET_STATUS_RECEIVE) == PXE_STATFLAGS_GET_STATUS_RECEIVE) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ }
+
+ if ((InterruptFlags & PXE_STATFLAGS_GET_STATUS_TRANSMIT) == PXE_STATFLAGS_GET_STATUS_TRANSMIT) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ }
+
+ if ((InterruptFlags & PXE_STATFLAGS_GET_STATUS_COMMAND) == PXE_STATFLAGS_GET_STATUS_COMMAND) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT;
+ }
+
+ if ((InterruptFlags & PXE_STATFLAGS_GET_STATUS_SOFTWARE) == PXE_STATFLAGS_GET_STATUS_SOFTWARE) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT;
+ }
+
+ }
+
+ if (GetTransmittedBuf) {
+ if ((Snp->Cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN) == 0) {
+ //
+ // UNDI has written some transmitted buffer addresses into the DB. Store them into Snp->RecycledTxBuf.
+ //
+ for (Index = 0; Index < MAX_XMIT_BUFFERS; Index++) {
+ if (Db->TxBuffer[Index] != 0) {
+ if (Snp->RecycledTxBufCount == Snp->MaxRecycledTxBuf) {
+ //
+ // Snp->RecycledTxBuf is full, reallocate a new one.
+ //
+ if ((Snp->MaxRecycledTxBuf + SNP_TX_BUFFER_INCREASEMENT) >= SNP_MAX_TX_BUFFER_NUM) {
+ return EFI_DEVICE_ERROR;
+ }
+ Tmp = AllocatePool (sizeof (UINT64) * (Snp->MaxRecycledTxBuf + SNP_TX_BUFFER_INCREASEMENT));
+ if (Tmp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ CopyMem (Tmp, Snp->RecycledTxBuf, sizeof (UINT64) * Snp->RecycledTxBufCount);
+ FreePool (Snp->RecycledTxBuf);
+ Snp->RecycledTxBuf = Tmp;
+ Snp->MaxRecycledTxBuf += SNP_TX_BUFFER_INCREASEMENT;
+ }
+ Snp->RecycledTxBuf[Snp->RecycledTxBufCount] = Db->TxBuffer[Index];
+ Snp->RecycledTxBufCount++;
+ }
+ }
+ }
+ }
+
+ //
+ // Update MediaPresent field of EFI_SIMPLE_NETWORK_MODE if the UNDI support
+ // returning media status from GET_STATUS command
+ //
+ if (Snp->MediaStatusSupported) {
+ Snp->Snp.Mode->MediaPresent =
+ (BOOLEAN) (((Snp->Cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_NO_MEDIA) != 0) ? FALSE : TRUE);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the current interrupt status and recycled transmit buffer status from a
+ network interface.
+
+ This function gets the current interrupt and recycled transmit buffer status
+ from the network interface. The interrupt status is returned as a bit mask in
+ InterruptStatus. If InterruptStatus is NULL, the interrupt status will not be
+ read. If TxBuf is not NULL, a recycled transmit buffer address will be retrieved.
+ If a recycled transmit buffer address is returned in TxBuf, then the buffer has
+ been successfully transmitted, and the status for that buffer is cleared. If
+ the status of the network interface is successfully collected, EFI_SUCCESS
+ will be returned. If the driver has not been initialized, EFI_DEVICE_ERROR will
+ be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param InterruptStatus A pointer to the bit mask of the currently active
+ interrupts (see "Related Definitions"). If this is NULL,
+ the interrupt status will not be read from the device.
+ If this is not NULL, the interrupt status will be read
+ from the device. When the interrupt status is read, it
+ will also be cleared. Clearing the transmit interrupt does
+ not empty the recycled transmit buffer array.
+ @param TxBuf Recycled transmit buffer address. The network interface
+ will not transmit if its internal recycled transmit
+ buffer array is full. Reading the transmit buffer does
+ not clear the transmit interrupt. If this is NULL, then
+ the transmit buffer status will not be read. If there
+ are no transmit buffers to recycle and TxBuf is not NULL,
+ TxBuf will be set to NULL.
+
+ @retval EFI_SUCCESS The status of the network interface was retrieved.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32GetStatus (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINT32 *InterruptStatus, OPTIONAL
+ OUT VOID **TxBuf OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (InterruptStatus == NULL && TxBuf == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (Snp->RecycledTxBufCount == 0 && TxBuf != NULL) {
+ Status = PxeGetStatus (Snp, InterruptStatus, TRUE);
+ } else {
+ Status = PxeGetStatus (Snp, InterruptStatus, FALSE);
+ }
+
+ if (TxBuf != NULL) {
+ //
+ // Get a recycled buf from Snp->RecycledTxBuf
+ //
+ if (Snp->RecycledTxBufCount == 0) {
+ *TxBuf = NULL;
+ } else {
+ Snp->RecycledTxBufCount--;
+ *TxBuf = (VOID *) (UINTN) Snp->RecycledTxBuf[Snp->RecycledTxBufCount];
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Initialize.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Initialize.c
new file mode 100644
index 0000000000..63bdf92f55
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Initialize.c
@@ -0,0 +1,283 @@
+/** @file
+ Implementation of initializing a network adapter.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Snp.h"
+
+/**
+ Call UNDI to initialize the interface.
+
+ @param Snp Pointer to snp driver structure.
+ @param CableDetectFlag Do/don't detect the cable (depending on what
+ undi supports).
+
+ @retval EFI_SUCCESS UNDI is initialized successfully.
+ @retval EFI_DEVICE_ERROR UNDI could not be initialized.
+ @retval Other Other errors as indicated.
+
+**/
+EFI_STATUS
+PxeInit (
+ SNP_DRIVER *Snp,
+ UINT16 CableDetectFlag
+ )
+{
+ PXE_CPB_INITIALIZE *Cpb;
+ VOID *Addr;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ Cpb = Snp->Cpb;
+ if (Snp->TxRxBufferSize != 0) {
+ Status = Snp->PciIo->AllocateBuffer (
+ Snp->PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ SNP_MEM_PAGES (Snp->TxRxBufferSize),
+ &Addr,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->PxeInit() AllocateBuffer %xh (%r)\n",
+ Status,
+ Status)
+ );
+
+ return Status;
+ }
+
+ ASSERT (Addr);
+
+ Snp->TxRxBuffer = Addr;
+ }
+
+ Cpb->MemoryAddr = (UINT64)(UINTN) Snp->TxRxBuffer;
+
+ Cpb->MemoryLength = Snp->TxRxBufferSize;
+
+ //
+ // let UNDI decide/detect these values
+ //
+ Cpb->LinkSpeed = 0;
+ Cpb->TxBufCnt = 0;
+ Cpb->TxBufSize = 0;
+ Cpb->RxBufCnt = 0;
+ Cpb->RxBufSize = 0;
+
+ Cpb->DuplexMode = PXE_DUPLEX_DEFAULT;
+
+ Cpb->LoopBackMode = LOOPBACK_NORMAL;
+
+ Snp->Cdb.OpCode = PXE_OPCODE_INITIALIZE;
+ Snp->Cdb.OpFlags = CableDetectFlag;
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_INITIALIZE);
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_INITIALIZE);
+
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Snp->Cpb;
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Snp->Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nSnp->undi.initialize() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ //
+ // There are two fields need to be checked here:
+ // First is the upper two bits (14 & 15) in the CDB.StatFlags field. Until these bits change to report
+ // PXE_STATFLAGS_COMMAND_COMPLETE or PXE_STATFLAGS_COMMAND_FAILED, the command has not been executed by the UNDI.
+ // Second is the CDB.StatCode field. After command execution completes, either successfully or not,
+ // the CDB.StatCode field contains the result of the command execution.
+ //
+ if ((((Snp->Cdb.StatFlags) & PXE_STATFLAGS_STATUS_MASK) == PXE_STATFLAGS_COMMAND_COMPLETE) &&
+ (Snp->Cdb.StatCode == PXE_STATCODE_SUCCESS)) {
+ //
+ // If cable detect feature is enabled in CDB.OpFlags, check the CDB.StatFlags to see if there is an
+ // active connection to this network device. If the no media StatFlag is set, the UNDI and network
+ // device are still initialized.
+ //
+ if (CableDetectFlag == PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) {
+ if(((Snp->Cdb.StatFlags) & PXE_STATFLAGS_INITIALIZED_NO_MEDIA) != PXE_STATFLAGS_INITIALIZED_NO_MEDIA) {
+ Snp->Mode.MediaPresent = TRUE;
+ } else {
+ Snp->Mode.MediaPresent = FALSE;
+ }
+ }
+
+ Snp->Mode.State = EfiSimpleNetworkInitialized;
+ Status = EFI_SUCCESS;
+ } else {
+ DEBUG (
+ (EFI_D_WARN,
+ "\nSnp->undi.initialize() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ if (Snp->TxRxBuffer != NULL) {
+ Snp->PciIo->FreeBuffer (
+ Snp->PciIo,
+ SNP_MEM_PAGES (Snp->TxRxBufferSize),
+ (VOID *) Snp->TxRxBuffer
+ );
+ }
+
+ Snp->TxRxBuffer = NULL;
+
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+
+/**
+ Resets a network adapter and allocates the transmit and receive buffers
+ required by the network interface; optionally, also requests allocation of
+ additional transmit and receive buffers.
+
+ This function allocates the transmit and receive buffers required by the network
+ interface. If this allocation fails, then EFI_OUT_OF_RESOURCES is returned.
+ If the allocation succeeds and the network interface is successfully initialized,
+ then EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+ @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer space
+ that the driver should allocate for the network interface.
+ Some network interfaces will not be able to use the
+ extra buffer, and the caller will not know if it is
+ actually being used.
+ @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space
+ that the driver should allocate for the network interface.
+ Some network interfaces will not be able to use the
+ extra buffer, and the caller will not know if it is
+ actually being used.
+
+ @retval EFI_SUCCESS The network interface was initialized.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and
+ receive buffers.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Initialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+ )
+{
+ EFI_STATUS EfiStatus;
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Snp == NULL) {
+ EfiStatus = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkStarted:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ EfiStatus = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ EfiStatus = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ EfiStatus = gBS->CreateEvent (
+ EVT_NOTIFY_WAIT,
+ TPL_NOTIFY,
+ &SnpWaitForPacketNotify,
+ Snp,
+ &Snp->Snp.WaitForPacket
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ Snp->Snp.WaitForPacket = NULL;
+ EfiStatus = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ //
+ //
+ Snp->Mode.MCastFilterCount = 0;
+ Snp->Mode.ReceiveFilterSetting = 0;
+ ZeroMem (Snp->Mode.MCastFilter, sizeof Snp->Mode.MCastFilter);
+ CopyMem (
+ &Snp->Mode.CurrentAddress,
+ &Snp->Mode.PermanentAddress,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+
+ //
+ // Compute tx/rx buffer sizes based on UNDI init info and parameters.
+ //
+ Snp->TxRxBufferSize = (UINT32) (Snp->InitInfo.MemoryRequired + ExtraRxBufferSize + ExtraTxBufferSize);
+
+ //
+ // If UNDI support cable detect for INITIALIZE command, try it first.
+ //
+ if (Snp->CableDetectSupported) {
+ if (PxeInit (Snp, PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) == EFI_SUCCESS) {
+ goto ON_EXIT;
+ }
+ }
+
+ Snp->Mode.MediaPresent = FALSE;
+
+ EfiStatus = PxeInit (Snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE);
+
+ if (EFI_ERROR (EfiStatus)) {
+ gBS->CloseEvent (Snp->Snp.WaitForPacket);
+ goto ON_EXIT;
+ }
+
+ //
+ // Try to update the MediaPresent field of EFI_SIMPLE_NETWORK_MODE if the UNDI support it.
+ //
+ if (Snp->MediaStatusSupported) {
+ PxeGetStatus (Snp, NULL, FALSE);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return EfiStatus;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Mcast_ip_to_mac.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Mcast_ip_to_mac.c
new file mode 100644
index 0000000000..23dba90194
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Mcast_ip_to_mac.c
@@ -0,0 +1,179 @@
+/** @file
+ Implementation of converting an multicast IP address to multicast HW MAC
+ address.
+
+Copyright (c) 2004 - 2007, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+/**
+ Call undi to convert an multicast IP address to a MAC address.
+
+ @param Snp Pointer to snp driver structure.
+ @param IPv6 Flag to indicate if this is an ipv6 address.
+ @param IP Multicast IP address.
+ @param MAC Pointer to hold the return MAC address.
+
+ @retval EFI_SUCCESS The multicast IP address was mapped to the
+ multicast HW MAC address.
+ @retval EFI_INVALID_PARAMETER Invalid UNDI command.
+ @retval EFI_UNSUPPORTED Command is not supported by UNDI.
+ @retval EFI_DEVICE_ERROR Fail to execute UNDI command.
+
+**/
+EFI_STATUS
+PxeIp2Mac (
+ IN SNP_DRIVER *Snp,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ IN OUT EFI_MAC_ADDRESS *MAC
+ )
+{
+ PXE_CPB_MCAST_IP_TO_MAC *Cpb;
+ PXE_DB_MCAST_IP_TO_MAC *Db;
+
+ Cpb = Snp->Cpb;
+ Db = Snp->Db;
+ Snp->Cdb.OpCode = PXE_OPCODE_MCAST_IP_TO_MAC;
+ Snp->Cdb.OpFlags = (UINT16) (IPv6 ? PXE_OPFLAGS_MCAST_IPV6_TO_MAC : PXE_OPFLAGS_MCAST_IPV4_TO_MAC);
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_MCAST_IP_TO_MAC);
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_MCAST_IP_TO_MAC);
+
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ CopyMem (&Cpb->IP, IP, sizeof (PXE_IP_ADDR));
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nSnp->undi.mcast_ip_to_mac() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_INVALID_CPB:
+ return EFI_INVALID_PARAMETER;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ DEBUG (
+ (EFI_D_NET,
+ "\nSnp->undi.mcast_ip_to_mac() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+ return EFI_UNSUPPORTED;
+
+ default:
+ //
+ // UNDI command failed. Return EFI_DEVICE_ERROR
+ // to caller.
+ //
+ DEBUG (
+ (EFI_D_NET,
+ "\nSnp->undi.mcast_ip_to_mac() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (MAC, &Db->MAC, sizeof (PXE_MAC_ADDR));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Converts a multicast IP address to a multicast HW MAC address.
+
+ This function converts a multicast IP address to a multicast HW MAC address
+ for all packet transactions. If the mapping is accepted, then EFI_SUCCESS will
+ be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460].
+ Set to FALSE if the multicast IP address is IPv4 [RFC 791].
+ @param IP The multicast IP address that is to be converted to a multicast
+ HW MAC address.
+ @param MAC The multicast HW MAC address that is to be generated from IP.
+
+ @retval EFI_SUCCESS The multicast IP address was mapped to the
+ multicast HW MAC address.
+ @retval EFI_NOT_STARTED The Simple Network Protocol interface has not
+ been started by calling Start().
+ @retval EFI_INVALID_PARAMETER IP is NULL.
+ @retval EFI_INVALID_PARAMETER MAC is NULL.
+ @retval EFI_INVALID_PARAMETER IP does not point to a valid IPv4 or IPv6
+ multicast address.
+ @retval EFI_DEVICE_ERROR The Simple Network Protocol interface has not
+ been initialized by calling Initialize().
+ @retval EFI_UNSUPPORTED IPv6 is TRUE and the implementation does not
+ support IPv6 multicast to MAC address conversion.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32McastIpToMac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ OUT EFI_MAC_ADDRESS *MAC
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Get pointer to SNP driver instance for *this.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IP == NULL || MAC == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = PxeIp2Mac (Snp, IPv6, IP, MAC);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Nvdata.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Nvdata.c
new file mode 100644
index 0000000000..443979087e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Nvdata.c
@@ -0,0 +1,223 @@
+/** @file
+ Implementation of reading and writing operations on the NVRAM device
+ attached to a network interface.
+
+Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ This routine calls Undi to read the desired number of eeprom bytes.
+
+ @param Snp pointer to the snp driver structure
+ @param Offset eeprom register value relative to the base address
+ @param BufferSize number of bytes to read
+ @param Buffer pointer where to read into
+
+ @retval EFI_SUCCESS The NVRAM access was performed.
+ @retval EFI_INVALID_PARAMETER Invalid UNDI command.
+ @retval EFI_UNSUPPORTED Command is not supported by UNDI.
+ @retval EFI_DEVICE_ERROR Fail to execute UNDI command.
+
+**/
+EFI_STATUS
+PxeNvDataRead (
+ IN SNP_DRIVER *Snp,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ )
+{
+ PXE_DB_NVDATA *Db;
+
+ Db = Snp->Db;
+ Snp->Cdb.OpCode = PXE_OPCODE_NVDATA;
+
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NVDATA_READ;
+
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_NVDATA);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.nvdata () "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.nvdata() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_UNSUPPORTED;
+
+ default:
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.nvdata() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ ASSERT (Offset < sizeof (Db->Data));
+
+ CopyMem (Buffer, &Db->Data.Byte[Offset], BufferSize);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Performs read and write operations on the NVRAM device attached to a network
+ interface.
+
+ This function performs read and write operations on the NVRAM device attached
+ to a network interface. If ReadWrite is TRUE, a read operation is performed.
+ If ReadWrite is FALSE, a write operation is performed. Offset specifies the
+ byte offset at which to start either operation. Offset must be a multiple of
+ NvRamAccessSize , and it must have a value between zero and NvRamSize.
+ BufferSize specifies the length of the read or write operation. BufferSize must
+ also be a multiple of NvRamAccessSize, and Offset + BufferSize must not exceed
+ NvRamSize.
+ If any of the above conditions is not met, then EFI_INVALID_PARAMETER will be
+ returned.
+ If all the conditions are met and the operation is "read," the NVRAM device
+ attached to the network interface will be read into Buffer and EFI_SUCCESS
+ will be returned. If this is a write operation, the contents of Buffer will be
+ used to update the contents of the NVRAM device attached to the network
+ interface and EFI_SUCCESS will be returned.
+
+ It does the basic checking on the input parameters and retrieves snp structure
+ and then calls the read_nvdata() call which does the actual reading
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param ReadWrite TRUE for read operations, FALSE for write operations.
+ @param Offset Byte offset in the NVRAM device at which to start the read or
+ write operation. This must be a multiple of NvRamAccessSize
+ and less than NvRamSize. (See EFI_SIMPLE_NETWORK_MODE)
+ @param BufferSize The number of bytes to read or write from the NVRAM device.
+ This must also be a multiple of NvramAccessSize.
+ @param Buffer A pointer to the data buffer.
+
+ @retval EFI_SUCCESS The NVRAM access was performed.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * The This parameter is NULL
+ * The This parameter does not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure
+ * The Offset parameter is not a multiple of
+ EFI_SIMPLE_NETWORK_MODE.NvRamAccessSize
+ * The Offset parameter is not less than
+ EFI_SIMPLE_NETWORK_MODE.NvRamSize
+ * The BufferSize parameter is not a multiple of
+ EFI_SIMPLE_NETWORK_MODE.NvRamAccessSize
+ * The Buffer parameter is NULL
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32NvData (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Get pointer to SNP driver instance for *this.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // Return error if non-volatile memory variables are not valid.
+ //
+ if (Snp->Mode.NvRamSize == 0 || Snp->Mode.NvRamAccessSize == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+ //
+ // Check for invalid parameter combinations.
+ //
+ if ((BufferSize == 0) ||
+ (Buffer == NULL) ||
+ (Offset >= Snp->Mode.NvRamSize) ||
+ (Offset + BufferSize > Snp->Mode.NvRamSize) ||
+ (BufferSize % Snp->Mode.NvRamAccessSize != 0) ||
+ (Offset % Snp->Mode.NvRamAccessSize != 0)
+ ) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // check the implementation flags of undi if we can write the nvdata!
+ //
+ if (!ReadWrite) {
+ Status = EFI_UNSUPPORTED;
+ } else {
+ Status = PxeNvDataRead (Snp, Offset, BufferSize, Buffer);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Receive.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Receive.c
new file mode 100644
index 0000000000..ace5630066
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Receive.c
@@ -0,0 +1,257 @@
+/** @file
+ Implementation of receiving a packet from a network interface.
+
+Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Snp.h"
+
+/**
+ Call UNDI to receive a packet and fills in the data in the input pointers.
+
+ @param Snp Pointer to snp driver structure
+ @param Buffer Pointer to the memory for the received data
+ @param BufferSize Pointer to the length of the buffer on entry and contains
+ the length of the received data on return
+ @param HeaderSize Pointer to the header portion of the data received.
+ @param SrcAddr Pointer to contain the source ethernet address on return
+ @param DestAddr Pointer to contain the destination ethernet address on
+ return
+ @param Protocol Pointer to contain the protocol type from the ethernet
+ header on return
+
+
+ @retval EFI_SUCCESS The received data was stored in Buffer, and
+ BufferSize has been updated to the number of
+ bytes received.
+ @retval EFI_DEVICE_ERROR Fail to execute UNDI command.
+ @retval EFI_NOT_READY No packets have been received on the network
+ interface.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the received
+ packets. BufferSize has been updated to the
+ required size.
+
+**/
+EFI_STATUS
+PxeReceive (
+ SNP_DRIVER *Snp,
+ VOID *Buffer,
+ UINTN *BufferSize,
+ UINTN *HeaderSize,
+ EFI_MAC_ADDRESS *SrcAddr,
+ EFI_MAC_ADDRESS *DestAddr,
+ UINT16 *Protocol
+ )
+{
+ PXE_CPB_RECEIVE *Cpb;
+ PXE_DB_RECEIVE *Db;
+ UINTN BuffSize;
+
+ Cpb = Snp->Cpb;
+ Db = Snp->Db;
+ BuffSize = *BufferSize;
+
+ Cpb->BufferAddr = (UINT64)(UINTN) Buffer;
+ Cpb->BufferLen = (UINT32) *BufferSize;
+
+ Cpb->reserved = 0;
+
+ Snp->Cdb.OpCode = PXE_OPCODE_RECEIVE;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_RECEIVE);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_RECEIVE);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive () "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_NO_DATA:
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.receive () %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_NOT_READY;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ *BufferSize = Db->FrameLen;
+
+ if (HeaderSize != NULL) {
+ *HeaderSize = Db->MediaHeaderLen;
+ }
+
+ if (SrcAddr != NULL) {
+ CopyMem (SrcAddr, &Db->SrcAddr, Snp->Mode.HwAddressSize);
+ }
+
+ if (DestAddr != NULL) {
+ CopyMem (DestAddr, &Db->DestAddr, Snp->Mode.HwAddressSize);
+ }
+
+ if (Protocol != NULL) {
+ //
+ // We need to do the byte swapping
+ //
+ *Protocol = (UINT16) PXE_SWAP_UINT16 (Db->Protocol);
+ }
+
+ //
+ // We have received a packet from network interface, which implies that the
+ // network cable should be present. While, some UNDI driver may not report
+ // correct media status during Snp->Initialize(). So, we need ensure
+ // MediaPresent in SNP mode data is set to correct value.
+ //
+ if (Snp->Mode.MediaPresentSupported && !Snp->Mode.MediaPresent) {
+ Snp->Mode.MediaPresent = TRUE;
+ }
+
+ return (*BufferSize <= BuffSize) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL;
+}
+
+/**
+ Receives a packet from a network interface.
+
+ This function retrieves one packet from the receive queue of a network interface.
+ If there are no packets on the receive queue, then EFI_NOT_READY will be
+ returned. If there is a packet on the receive queue, and the size of the packet
+ is smaller than BufferSize, then the contents of the packet will be placed in
+ Buffer, and BufferSize will be updated with the actual size of the packet.
+ In addition, if SrcAddr, DestAddr, and Protocol are not NULL, then these values
+ will be extracted from the media header and returned. EFI_SUCCESS will be
+ returned if a packet was successfully received.
+ If BufferSize is smaller than the received packet, then the size of the receive
+ packet will be placed in BufferSize and EFI_BUFFER_TOO_SMALL will be returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param HeaderSize The size, in bytes, of the media header received on the network
+ interface. If this parameter is NULL, then the media header size
+ will not be returned.
+ @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
+ bytes, of the packet that was received on the network interface.
+ @param Buffer A pointer to the data buffer to receive both the media
+ header and the data.
+ @param SrcAddr The source HW MAC address. If this parameter is NULL, the HW
+ MAC source address will not be extracted from the media header.
+ @param DestAddr The destination HW MAC address. If this parameter is NULL,
+ the HW MAC destination address will not be extracted from
+ the media header.
+ @param Protocol The media header type. If this parameter is NULL, then the
+ protocol will not be extracted from the media header. See
+ RFC 1700 section "Ether Types" for examples.
+
+ @retval EFI_SUCCESS The received data was stored in Buffer, and
+ BufferSize has been updated to the number of
+ bytes received.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_NOT_READY No packets have been received on the network interface.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the received packets.
+ BufferSize has been updated to the required size.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * The This parameter is NULL
+ * The This parameter does not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ * The BufferSize parameter is NULL
+ * The Buffer parameter is NULL
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Receive (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINTN *HeaderSize OPTIONAL,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer,
+ OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
+ OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL,
+ OUT UINT16 *Protocol OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if ((BufferSize == NULL) || (Buffer == NULL)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (Snp->Mode.ReceiveFilterSetting == 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = PxeReceive (
+ Snp,
+ Buffer,
+ BufferSize,
+ HeaderSize,
+ SrcAddr,
+ DestAddr,
+ Protocol
+ );
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Receive_filters.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Receive_filters.c
new file mode 100644
index 0000000000..c37018c826
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Receive_filters.c
@@ -0,0 +1,484 @@
+/** @file
+ Implementation of managing the multicast receive filters of a network
+ interface.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under the
+terms and conditions of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+
+#include "Snp.h"
+
+/**
+ Call undi to enable the receive filters.
+
+ @param Snp Pointer to snp driver structure.
+ @param EnableFlags Bit mask for enabling the receive filters.
+ @param MCastAddressCount Multicast address count for a new multicast address
+ list.
+ @param MCastAddressList List of new multicast addresses.
+
+ @retval EFI_SUCCESS The multicast receive filter list was updated.
+ @retval EFI_INVALID_PARAMETER Invalid UNDI command.
+ @retval EFI_UNSUPPORTED Command is not supported by UNDI.
+ @retval EFI_DEVICE_ERROR Fail to execute UNDI command.
+
+**/
+EFI_STATUS
+PxeRecvFilterEnable (
+ SNP_DRIVER *Snp,
+ UINT32 EnableFlags,
+ UINTN MCastAddressCount,
+ EFI_MAC_ADDRESS *MCastAddressList
+ )
+{
+ Snp->Cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_RECEIVE_FILTER_ENABLE;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+ }
+
+ if (MCastAddressCount != 0) {
+ Snp->Cdb.CPBsize = (UINT16) (MCastAddressCount * sizeof (EFI_MAC_ADDRESS));
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN)Snp->Cpb;
+ CopyMem (Snp->Cpb, MCastAddressList, Snp->Cdb.CPBsize);
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive_filters() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_INVALID_CDB:
+ case PXE_STATCODE_INVALID_CPB:
+ case PXE_STATCODE_INVALID_PARAMETER:
+ return EFI_INVALID_PARAMETER;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Call undi to disable the receive filters.
+
+ @param Snp Pointer to snp driver structure
+ @param DisableFlags Bit mask for disabling the receive filters
+ @param ResetMCastList Boolean flag to reset/delete the multicast filter
+ list.
+
+ @retval EFI_SUCCESS The multicast receive filter list was updated.
+ @retval EFI_DEVICE_ERROR Fail to execute UNDI command.
+
+**/
+EFI_STATUS
+PxeRecvFilterDisable (
+ SNP_DRIVER *Snp,
+ UINT32 DisableFlags,
+ BOOLEAN ResetMCastList
+ )
+{
+ Snp->Cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ Snp->Cdb.OpFlags = (UINT16) ((DisableFlags != 0) ? PXE_OPFLAGS_RECEIVE_FILTER_DISABLE : PXE_OPFLAGS_NOT_USED);
+
+ if (ResetMCastList) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
+ Snp->Cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive_filters() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Call undi to read the receive filters.
+
+ @param Snp Pointer to snp driver structure.
+
+ @retval EFI_SUCCESS The receive filter was read.
+ @retval EFI_DEVICE_ERROR Fail to execute UNDI command.
+
+**/
+EFI_STATUS
+PxeRecvFilterRead (
+ SNP_DRIVER *Snp
+ )
+{
+ Snp->Cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_RECEIVE_FILTER_READ;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = (UINT16) (Snp->Mode.MaxMCastFilterCount * sizeof (EFI_MAC_ADDRESS));
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ if (Snp->Cdb.DBsize == 0) {
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) NULL;
+ } else {
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Snp->Db;
+ ZeroMem (Snp->Db, Snp->Cdb.DBsize);
+ }
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive_filters() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Convert UNDI32 StatFlags to EFI SNP filter flags.
+ //
+ Snp->Mode.ReceiveFilterSetting = 0;
+
+ if ((Snp->Cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_UNICAST) != 0) {
+ Snp->Mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+ }
+
+ if ((Snp->Cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_BROADCAST) != 0) {
+ Snp->Mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+ }
+
+ if ((Snp->Cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS) != 0) {
+ Snp->Mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ }
+
+ if ((Snp->Cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) {
+ Snp->Mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+ }
+
+ if ((Snp->Cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {
+ Snp->Mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+ }
+
+ CopyMem (Snp->Mode.MCastFilter, Snp->Db, Snp->Cdb.DBsize);
+
+ //
+ // Count number of active entries in multicast filter list.
+ //
+ {
+ EFI_MAC_ADDRESS ZeroMacAddr;
+
+ SetMem (&ZeroMacAddr, sizeof ZeroMacAddr, 0);
+
+ for (Snp->Mode.MCastFilterCount = 0;
+ Snp->Mode.MCastFilterCount < Snp->Mode.MaxMCastFilterCount;
+ Snp->Mode.MCastFilterCount++
+ ) {
+ if (CompareMem (
+ &Snp->Mode.MCastFilter[Snp->Mode.MCastFilterCount],
+ &ZeroMacAddr,
+ sizeof ZeroMacAddr
+ ) == 0) {
+ break;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Manages the multicast receive filters of a network interface.
+
+ This function is used enable and disable the hardware and software receive
+ filters for the underlying network device.
+ The receive filter change is broken down into three steps:
+ * The filter mask bits that are set (ON) in the Enable parameter are added to
+ the current receive filter settings.
+ * The filter mask bits that are set (ON) in the Disable parameter are subtracted
+ from the updated receive filter settings.
+ * If the resulting receive filter setting is not supported by the hardware a
+ more liberal setting is selected.
+ If the same bits are set in the Enable and Disable parameters, then the bits
+ in the Disable parameter takes precedence.
+ If the ResetMCastFilter parameter is TRUE, then the multicast address list
+ filter is disabled (irregardless of what other multicast bits are set in the
+ Enable and Disable parameters). The SNP->Mode->MCastFilterCount field is set
+ to zero. The Snp->Mode->MCastFilter contents are undefined.
+ After enabling or disabling receive filter settings, software should verify
+ the new settings by checking the Snp->Mode->ReceiveFilterSettings,
+ Snp->Mode->MCastFilterCount and Snp->Mode->MCastFilter fields.
+ Note: Some network drivers and/or devices will automatically promote receive
+ filter settings if the requested setting can not be honored. For example, if
+ a request for four multicast addresses is made and the underlying hardware
+ only supports two multicast addresses the driver might set the promiscuous
+ or promiscuous multicast receive filters instead. The receiving software is
+ responsible for discarding any extra packets that get through the hardware
+ receive filters.
+ Note: Note: To disable all receive filter hardware, the network driver must
+ be Shutdown() and Stopped(). Calling ReceiveFilters() with Disable set to
+ Snp->Mode->ReceiveFilterSettings will make it so no more packets are
+ returned by the Receive() function, but the receive hardware may still be
+ moving packets into system memory before inspecting and discarding them.
+ Unexpected system errors, reboots and hangs can occur if an OS is loaded
+ and the network devices are not Shutdown() and Stopped().
+ If ResetMCastFilter is TRUE, then the multicast receive filter list on the
+ network interface will be reset to the default multicast receive filter list.
+ If ResetMCastFilter is FALSE, and this network interface allows the multicast
+ receive filter list to be modified, then the MCastFilterCnt and MCastFilter
+ are used to update the current multicast receive filter list. The modified
+ receive filter list settings can be found in the MCastFilter field of
+ EFI_SIMPLE_NETWORK_MODE. If the network interface does not allow the multicast
+ receive filter list to be modified, then EFI_INVALID_PARAMETER will be returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+ If the receive filter mask and multicast receive filter list have been
+ successfully updated on the network interface, EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param Enable A bit mask of receive filters to enable on the network
+ interface.
+ @param Disable A bit mask of receive filters to disable on the network
+ interface. For backward compatibility with EFI 1.1
+ platforms, the EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit
+ must be set when the ResetMCastFilter parameter is TRUE.
+ @param ResetMCastFilter Set to TRUE to reset the contents of the multicast
+ receive filters on the network interface to their
+ default values.
+ @param MCastFilterCnt Number of multicast HW MAC addresses in the new MCastFilter
+ list. This value must be less than or equal to the
+ MCastFilterCnt field of EFI_SIMPLE_NETWORK_MODE.
+ This field is optional if ResetMCastFilter is TRUE.
+ @param MCastFilter A pointer to a list of new multicast receive filter HW
+ MAC addresses. This list will replace any existing
+ multicast HW MAC address list. This field is optional
+ if ResetMCastFilter is TRUE.
+
+ @retval EFI_SUCCESS The multicast receive filter list was updated.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * This is NULL
+ * There are bits set in Enable that are not set
+ in Snp->Mode->ReceiveFilterMask
+ * There are bits set in Disable that are not set
+ in Snp->Mode->ReceiveFilterMask
+ * Multicast is being enabled (the
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit is
+ set in Enable, it is not set in Disable, and
+ ResetMCastFilter is FALSE) and MCastFilterCount
+ is zero
+ * Multicast is being enabled and MCastFilterCount
+ is greater than Snp->Mode->MaxMCastFilterCount
+ * Multicast is being enabled and MCastFilter is NULL
+ * Multicast is being enabled and one or more of
+ the addresses in the MCastFilter list are not
+ valid multicast MAC addresses
+ @retval EFI_DEVICE_ERROR One or more of the following conditions is TRUE:
+ * The network interface has been started but has
+ not been initialized
+ * An unexpected error was returned by the
+ underlying network driver or device
+ @retval EFI_UNSUPPORTED This function is not supported by the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32ReceiveFilters (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINT32 Enable,
+ IN UINT32 Disable,
+ IN BOOLEAN ResetMCastFilter,
+ IN UINTN MCastFilterCnt, OPTIONAL
+ IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // check if we are asked to enable or disable something that the UNDI
+ // does not even support!
+ //
+ if (((Enable &~Snp->Mode.ReceiveFilterMask) != 0) ||
+ ((Disable &~Snp->Mode.ReceiveFilterMask) != 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (ResetMCastFilter) {
+
+ Disable |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST & Snp->Mode.ReceiveFilterMask;
+ MCastFilterCnt = 0;
+ MCastFilter = NULL;
+ } else {
+ if (MCastFilterCnt != 0) {
+ if ((MCastFilterCnt > Snp->Mode.MaxMCastFilterCount) ||
+ (MCastFilter == NULL)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if (Enable == 0 && Disable == 0 && !ResetMCastFilter && MCastFilterCnt == 0) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0 && MCastFilterCnt == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if ((Enable != 0) || (MCastFilterCnt != 0)) {
+ Status = PxeRecvFilterEnable (
+ Snp,
+ Enable,
+ MCastFilterCnt,
+ MCastFilter
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ if ((Disable != 0) || ResetMCastFilter) {
+ Status = PxeRecvFilterDisable (Snp, Disable, ResetMCastFilter);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = PxeRecvFilterRead (Snp);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Reset.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Reset.c
new file mode 100644
index 0000000000..2381264c11
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Reset.c
@@ -0,0 +1,136 @@
+/** @file
+ Implementation of resetting a network adapter.
+
+Copyright (c) 2004 - 2007, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under the
+terms and conditions of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to reset the NIC.
+
+ @param Snp Pointer to the snp driver structure.
+
+ @return EFI_SUCCESSFUL The NIC was reset.
+ @retval EFI_DEVICE_ERROR The NIC cannot be reset.
+
+**/
+EFI_STATUS
+PxeReset (
+ SNP_DRIVER *Snp
+ )
+{
+ Snp->Cdb.OpCode = PXE_OPCODE_RESET;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.reset() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_WARN,
+ "\nsnp->undi32.reset() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ //
+ // UNDI could not be reset. Return UNDI error.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Resets a network adapter and reinitializes it with the parameters that were
+ provided in the previous call to Initialize().
+
+ This function resets a network adapter and reinitializes it with the parameters
+ that were provided in the previous call to Initialize(). The transmit and
+ receive queues are emptied and all pending interrupts are cleared.
+ Receive filters, the station address, the statistics, and the multicast-IP-to-HW
+ MAC addresses are not reset by this call. If the network interface was
+ successfully reset, then EFI_SUCCESS will be returned. If the driver has not
+ been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param ExtendedVerification Indicates that the driver may perform a more
+ exhaustive verification operation of the device
+ during reset.
+
+ @retval EFI_SUCCESS The network interface was reset.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Reset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Resolve Warning 4 unreferenced parameter problem
+ //
+ ExtendedVerification = 0;
+ DEBUG ((EFI_D_WARN, "ExtendedVerification = %d is not implemented!\n", ExtendedVerification));
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = PxeReset (Snp);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Shutdown.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Shutdown.c
new file mode 100644
index 0000000000..1f51992778
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Shutdown.c
@@ -0,0 +1,152 @@
+/** @file
+ Implementation of shuting down a network adapter.
+
+Copyright (c) 2004 - 2007, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to shut down the interface.
+
+ @param Snp Pointer to snp driver structure.
+
+ @retval EFI_SUCCESS UNDI is shut down successfully.
+ @retval EFI_DEVICE_ERROR UNDI could not be shut down.
+
+**/
+EFI_STATUS
+PxeShutdown (
+ IN SNP_DRIVER *Snp
+ )
+{
+ Snp->Cdb.OpCode = PXE_OPCODE_SHUTDOWN;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.shutdown() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI could not be shutdown. Return UNDI error.
+ //
+ DEBUG ((EFI_D_WARN, "\nsnp->undi.shutdown() %xh:%xh\n", Snp->Cdb.StatFlags, Snp->Cdb.StatCode));
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Free allocated memory.
+ //
+ if (Snp->TxRxBuffer != NULL) {
+ Snp->PciIo->FreeBuffer (
+ Snp->PciIo,
+ SNP_MEM_PAGES (Snp->TxRxBufferSize),
+ (VOID *) Snp->TxRxBuffer
+ );
+ }
+
+ Snp->TxRxBuffer = NULL;
+ Snp->TxRxBufferSize = 0;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Resets a network adapter and leaves it in a state that is safe for another
+ driver to initialize.
+
+ This function releases the memory buffers assigned in the Initialize() call.
+ Pending transmits and receives are lost, and interrupts are cleared and disabled.
+ After this call, only the Initialize() and Stop() calls may be used. If the
+ network interface was successfully shutdown, then EFI_SUCCESS will be returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The network interface was shutdown.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Shutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // Get pointer to SNP driver instance for *This.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = PxeShutdown (Snp);
+
+ Snp->Mode.State = EfiSimpleNetworkStarted;
+ Snp->Mode.ReceiveFilterSetting = 0;
+
+ Snp->Mode.MCastFilterCount = 0;
+ Snp->Mode.ReceiveFilterSetting = 0;
+ ZeroMem (Snp->Mode.MCastFilter, sizeof Snp->Mode.MCastFilter);
+ CopyMem (
+ &Snp->Mode.CurrentAddress,
+ &Snp->Mode.PermanentAddress,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+
+ gBS->CloseEvent (Snp->Snp.WaitForPacket);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.c
new file mode 100644
index 0000000000..9f61aee057
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.c
@@ -0,0 +1,868 @@
+/** @file
+ Implementation of driver entry point and driver binding protocol.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+/**
+ One notified function to stop UNDI device when gBS->ExitBootServices() called.
+
+ @param Event Pointer to this event
+ @param Context Event handler private data
+
+**/
+VOID
+EFIAPI
+SnpNotifyExitBootServices (
+ EFI_EVENT Event,
+ VOID *Context
+ )
+{
+ SNP_DRIVER *Snp;
+
+ Snp = (SNP_DRIVER *)Context;
+
+ //
+ // Shutdown and stop UNDI driver
+ //
+ PxeShutdown (Snp);
+ PxeStop (Snp);
+}
+
+/**
+ Send command to UNDI. It does nothing currently.
+
+ @param Cdb command to be sent to UNDI.
+
+ @retval EFI_INVALID_PARAMETER The command is 0.
+ @retval EFI_UNSUPPORTED Default return status because it's not
+ supported currently.
+
+**/
+EFI_STATUS
+EFIAPI
+IssueHwUndiCommand (
+ UINT64 Cdb
+ )
+{
+ DEBUG ((EFI_D_ERROR, "\nIssueHwUndiCommand() - This should not be called!"));
+
+ if (Cdb == 0) {
+ return EFI_INVALID_PARAMETER;
+
+ }
+ //
+ // %%TBD - For now, nothing is done.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Compute 8-bit checksum of a buffer.
+
+ @param Buffer Pointer to buffer.
+ @param Length Length of buffer in bytes.
+
+ @return 8-bit checksum of all bytes in buffer, or zero if ptr is NULL or len
+ is zero.
+
+**/
+UINT8
+Calc8BitCksum (
+ VOID *Buffer,
+ UINTN Length
+ )
+{
+ UINT8 *Ptr;
+ UINT8 Cksum;
+
+ Ptr = Buffer;
+ Cksum = 0;
+
+ if (Ptr == NULL || Length == 0) {
+ return 0;
+ }
+
+ while (Length-- != 0) {
+ Cksum = (UINT8) (Cksum + *Ptr++);
+ }
+
+ return Cksum;
+}
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param 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
+SimpleNetworkDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiProtocol;
+ PXE_UNDI *Pxe;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &NiiProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ALREADY_STARTED) {
+ DEBUG ((EFI_D_INFO, "Support(): Already Started. on handle %p\n", Controller));
+ }
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Support(): UNDI3.1 found on handle %p\n", Controller));
+
+ //
+ // check the version, we don't want to connect to the undi16
+ //
+ if (NiiProtocol->Type != EfiNetworkInterfaceUndi) {
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+ //
+ // Check to see if !PXE structure is valid. Paragraph alignment of !PXE structure is required.
+ //
+ if ((NiiProtocol->Id & 0x0F) != 0) {
+ DEBUG ((EFI_D_NET, "\n!PXE structure is not paragraph aligned.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ Pxe = (PXE_UNDI *) (UINTN) (NiiProtocol->Id);
+
+ //
+ // Verify !PXE revisions.
+ //
+ if (Pxe->hw.Signature != PXE_ROMID_SIGNATURE) {
+ DEBUG ((EFI_D_NET, "\n!PXE signature is not valid.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ if (Pxe->hw.Rev < PXE_ROMID_REV) {
+ DEBUG ((EFI_D_NET, "\n!PXE.Rev is not supported.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ if (Pxe->hw.MajorVer < PXE_ROMID_MAJORVER) {
+
+ DEBUG ((EFI_D_NET, "\n!PXE.MajorVer is not supported.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+
+ } else if (Pxe->hw.MajorVer == PXE_ROMID_MAJORVER && Pxe->hw.MinorVer < PXE_ROMID_MINORVER) {
+ DEBUG ((EFI_D_NET, "\n!PXE.MinorVer is not supported."));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+ //
+ // Do S/W UNDI specific checks.
+ //
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) == 0) {
+ if (Pxe->sw.EntryPoint < Pxe->sw.Len) {
+ DEBUG ((EFI_D_NET, "\n!PXE S/W entry point is not valid."));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ if (Pxe->sw.BusCnt == 0) {
+ DEBUG ((EFI_D_NET, "\n!PXE.BusCnt is zero."));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+ DEBUG ((EFI_D_INFO, "Support(): supported on %p\n", Controller));
+
+Done:
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to.
+ @param 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 other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+ EFI_DEVICE_PATH_PROTOCOL *NiiDevicePath;
+ EFI_STATUS Status;
+ PXE_UNDI *Pxe;
+ SNP_DRIVER *Snp;
+ VOID *Address;
+ EFI_HANDLE Handle;
+ UINT8 BarIndex;
+ PXE_STATFLAGS InitStatFlags;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *BarDesc;
+ BOOLEAN FoundIoBar;
+ BOOLEAN FoundMemoryBar;
+
+ DEBUG ((EFI_D_NET, "\nSnpNotifyNetworkInterfaceIdentifier() "));
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &NiiDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->LocateDevicePath (
+ &gEfiPciIoProtocolGuid,
+ &NiiDevicePath,
+ &Handle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Get the NII interface.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Nii,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Start(): UNDI3.1 found\n"));
+
+ Pxe = (PXE_UNDI *) (UINTN) (Nii->Id);
+
+ if (Calc8BitCksum (Pxe, Pxe->hw.Len) != 0) {
+ DEBUG ((EFI_D_NET, "\n!PXE checksum is not correct.\n"));
+ goto NiiError;
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {
+ //
+ // We can get any packets.
+ //
+ } else if ((Pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {
+ //
+ // We need to be able to get broadcast packets for DHCP.
+ // If we do not have promiscuous support, we must at least have
+ // broadcast support or we cannot do DHCP!
+ //
+ } else {
+ DEBUG ((EFI_D_NET, "\nUNDI does not have promiscuous or broadcast support."));
+ goto NiiError;
+ }
+ //
+ // OK, we like this UNDI, and we know snp is not already there on this handle
+ // Allocate and initialize a new simple network protocol structure.
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ SNP_MEM_PAGES (sizeof (SNP_DRIVER)),
+ &Address,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nCould not allocate SNP_DRIVER structure.\n"));
+ goto NiiError;
+ }
+
+ Snp = (SNP_DRIVER *) (UINTN) Address;
+
+ ZeroMem (Snp, sizeof (SNP_DRIVER));
+
+ Snp->PciIo = PciIo;
+ Snp->Signature = SNP_DRIVER_SIGNATURE;
+
+ EfiInitializeLock (&Snp->Lock, TPL_NOTIFY);
+
+ Snp->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ Snp->Snp.Start = SnpUndi32Start;
+ Snp->Snp.Stop = SnpUndi32Stop;
+ Snp->Snp.Initialize = SnpUndi32Initialize;
+ Snp->Snp.Reset = SnpUndi32Reset;
+ Snp->Snp.Shutdown = SnpUndi32Shutdown;
+ Snp->Snp.ReceiveFilters = SnpUndi32ReceiveFilters;
+ Snp->Snp.StationAddress = SnpUndi32StationAddress;
+ Snp->Snp.Statistics = SnpUndi32Statistics;
+ Snp->Snp.MCastIpToMac = SnpUndi32McastIpToMac;
+ Snp->Snp.NvData = SnpUndi32NvData;
+ Snp->Snp.GetStatus = SnpUndi32GetStatus;
+ Snp->Snp.Transmit = SnpUndi32Transmit;
+ Snp->Snp.Receive = SnpUndi32Receive;
+ Snp->Snp.WaitForPacket = NULL;
+
+ Snp->Snp.Mode = &Snp->Mode;
+
+ Snp->TxRxBufferSize = 0;
+ Snp->TxRxBuffer = NULL;
+
+ Snp->RecycledTxBuf = AllocatePool (sizeof (UINT64) * SNP_TX_BUFFER_INCREASEMENT);
+ if (Snp->RecycledTxBuf == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error_DeleteSNP;
+ }
+ Snp->MaxRecycledTxBuf = SNP_TX_BUFFER_INCREASEMENT;
+ Snp->RecycledTxBufCount = 0;
+
+ if (Nii->Revision >= EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION) {
+ Snp->IfNum = Nii->IfNum;
+
+ } else {
+ Snp->IfNum = (UINT8) (Nii->IfNum & 0xFF);
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) != 0) {
+ Snp->IsSwUndi = FALSE;
+ Snp->IssueUndi32Command = &IssueHwUndiCommand;
+ } else {
+ Snp->IsSwUndi = TRUE;
+
+ if ((Pxe->sw.Implementation & PXE_ROMID_IMP_SW_VIRT_ADDR) != 0) {
+ Snp->IssueUndi32Command = (ISSUE_UNDI32_COMMAND) (UINTN) Pxe->sw.EntryPoint;
+ } else {
+ Snp->IssueUndi32Command = (ISSUE_UNDI32_COMMAND) (UINTN) ((UINT8) (UINTN) Pxe + Pxe->sw.EntryPoint);
+ }
+ }
+ //
+ // Allocate a global CPB and DB buffer for this UNDI interface.
+ // we do this because:
+ //
+ // -UNDI 3.0 wants all the addresses passed to it (even the cpb and db) to be
+ // within 2GB limit, create them here and map them so that when undi calls
+ // v2p callback to check if the physical address is < 2gb, we will pass.
+ //
+ // -This is not a requirement for 3.1 or later UNDIs but the code looks
+ // simpler if we use the same cpb, db variables for both old and new undi
+ // interfaces from all the SNP interface calls (we don't map the buffers
+ // for the newer undi interfaces though)
+ // .
+ // -it is OK to allocate one global set of CPB, DB pair for each UNDI
+ // interface as EFI does not multi-task and so SNP will not be re-entered!
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ SNP_MEM_PAGES (4096),
+ &Address,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nCould not allocate CPB and DB structures.\n"));
+ goto Error_DeleteSNP;
+ }
+
+ Snp->Cpb = (VOID *) (UINTN) Address;
+ Snp->Db = (VOID *) ((UINTN) Address + 2048);
+
+ //
+ // Find the correct BAR to do IO.
+ //
+ // Enumerate through the PCI BARs for the device to determine which one is
+ // the IO BAR. Save the index of the BAR into the adapter info structure.
+ // for regular 32bit BARs, 0 is memory mapped, 1 is io mapped
+ //
+ Snp->MemoryBarIndex = 0;
+ Snp->IoBarIndex = 1;
+ FoundMemoryBar = FALSE;
+ FoundIoBar = FALSE;
+ for (BarIndex = 0; BarIndex < PCI_MAX_BAR; BarIndex++) {
+ Status = PciIo->GetBarAttributes (
+ PciIo,
+ BarIndex,
+ NULL,
+ (VOID**) &BarDesc
+ );
+ if (Status == EFI_UNSUPPORTED) {
+ continue;
+ } else if (EFI_ERROR (Status)) {
+ goto Error_DeleteSNP;
+ }
+
+ if ((!FoundMemoryBar) && (BarDesc->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM)) {
+ Snp->MemoryBarIndex = BarIndex;
+ FoundMemoryBar = TRUE;
+ } else if ((!FoundIoBar) && (BarDesc->ResType == ACPI_ADDRESS_SPACE_TYPE_IO)) {
+ Snp->IoBarIndex = BarIndex;
+ FoundIoBar = TRUE;
+ }
+
+ FreePool (BarDesc);
+
+ if (FoundMemoryBar && FoundIoBar) {
+ break;
+ }
+ }
+
+ Status = PxeStart (Snp);
+
+ if (Status != EFI_SUCCESS) {
+ goto Error_DeleteSNP;
+ }
+
+ Snp->Cdb.OpCode = PXE_OPCODE_GET_INIT_INFO;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_DBADDR_NOT_USED;
+
+ Snp->Cdb.DBsize = (UINT16) sizeof (Snp->InitInfo);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) (&Snp->InitInfo);
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nSnp->undi.get_init_info() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ //
+ // Save the INIT Stat Code...
+ //
+ InitStatFlags = Snp->Cdb.StatFlags;
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nSnp->undi.init_info() %xh:%xh\n", Snp->Cdb.StatFlags, Snp->Cdb.StatCode));
+ PxeStop (Snp);
+ goto Error_DeleteSNP;
+ }
+
+ //
+ // Initialize simple network protocol mode structure
+ //
+ Snp->Mode.State = EfiSimpleNetworkStopped;
+ Snp->Mode.HwAddressSize = Snp->InitInfo.HWaddrLen;
+ Snp->Mode.MediaHeaderSize = Snp->InitInfo.MediaHeaderLen;
+ Snp->Mode.MaxPacketSize = Snp->InitInfo.FrameDataLen;
+ Snp->Mode.NvRamAccessSize = Snp->InitInfo.NvWidth;
+ Snp->Mode.NvRamSize = Snp->InitInfo.NvCount * Snp->Mode.NvRamAccessSize;
+ Snp->Mode.IfType = Snp->InitInfo.IFtype;
+ Snp->Mode.MaxMCastFilterCount = Snp->InitInfo.MCastFilterCnt;
+ Snp->Mode.MCastFilterCount = 0;
+
+ switch (InitStatFlags & PXE_STATFLAGS_CABLE_DETECT_MASK) {
+ case PXE_STATFLAGS_CABLE_DETECT_SUPPORTED:
+ Snp->CableDetectSupported = TRUE;
+ break;
+
+ case PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED:
+ default:
+ Snp->CableDetectSupported = FALSE;
+ }
+
+ switch (InitStatFlags & PXE_STATFLAGS_GET_STATUS_NO_MEDIA_MASK) {
+ case PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED:
+ Snp->MediaStatusSupported = TRUE;
+ break;
+
+ case PXE_STATFLAGS_GET_STATUS_NO_MEDIA_NOT_SUPPORTED:
+ default:
+ Snp->MediaStatusSupported = FALSE;
+ }
+
+ if (Snp->CableDetectSupported || Snp->MediaStatusSupported) {
+ Snp->Mode.MediaPresentSupported = TRUE;
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE) != 0) {
+ Snp->Mode.MacAddressChangeable = TRUE;
+ } else {
+ Snp->Mode.MacAddressChangeable = FALSE;
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_MULTI_FRAME_SUPPORTED) != 0) {
+ Snp->Mode.MultipleTxSupported = TRUE;
+ } else {
+ Snp->Mode.MultipleTxSupported = FALSE;
+ }
+
+ Snp->Mode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) {
+ Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {
+ Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {
+ Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED) != 0) {
+ Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+
+ }
+
+ if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) {
+ Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+
+ }
+
+ Snp->Mode.ReceiveFilterSetting = 0;
+
+ //
+ // need to get the station address to save in the mode structure. we need to
+ // initialize the UNDI first for this.
+ //
+ Snp->TxRxBufferSize = Snp->InitInfo.MemoryRequired;
+ Status = PxeInit (Snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE);
+
+ if (EFI_ERROR (Status)) {
+ PxeStop (Snp);
+ goto Error_DeleteSNP;
+ }
+
+ Status = PxeGetStnAddr (Snp);
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "\nSnp->undi.get_station_addr() failed.\n"));
+ PxeShutdown (Snp);
+ PxeStop (Snp);
+ goto Error_DeleteSNP;
+ }
+
+ Snp->Mode.MediaPresent = FALSE;
+
+ //
+ // We should not leave UNDI started and initialized here. this DriverStart()
+ // routine must only find and attach the SNP interface to UNDI layer that it
+ // finds on the given handle!
+ // The UNDI layer will be started when upper layers call Snp->start.
+ // How ever, this DriverStart() must fill up the snp mode structure which
+ // contains the MAC address of the NIC. For this reason we started and
+ // initialized UNDI here, now we are done, do a shutdown and stop of the
+ // UNDI interface!
+ //
+ PxeShutdown (Snp);
+ PxeStop (Snp);
+
+ //
+ // Create EXIT_BOOT_SERIVES Event
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ SnpNotifyExitBootServices,
+ Snp,
+ &gEfiEventExitBootServicesGuid,
+ &Snp->ExitBootServicesEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_DeleteSNP;
+ }
+
+ //
+ // add SNP to the undi handle
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &(Snp->Snp)
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ PciIo->FreeBuffer (
+ PciIo,
+ SNP_MEM_PAGES (4096),
+ Snp->Cpb
+ );
+
+Error_DeleteSNP:
+
+ if (Snp->RecycledTxBuf != NULL) {
+ FreePool (Snp->RecycledTxBuf);
+ }
+
+ PciIo->FreeBuffer (
+ PciIo,
+ SNP_MEM_PAGES (sizeof (SNP_DRIVER)),
+ Snp
+ );
+NiiError:
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // If we got here that means we are in error state.
+ //
+ if (!EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param 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
+SimpleNetworkDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol;
+ SNP_DRIVER *Snp;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ //
+ // Get our context back.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &SnpProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (SnpProtocol);
+
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ &Snp->Snp
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Close EXIT_BOOT_SERIVES Event
+ //
+ gBS->CloseEvent (Snp->ExitBootServicesEvent);
+
+ Status = gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ Status = gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ PxeShutdown (Snp);
+ PxeStop (Snp);
+
+ FreePool (Snp->RecycledTxBuf);
+
+ PciIo = Snp->PciIo;
+ PciIo->FreeBuffer (
+ PciIo,
+ SNP_MEM_PAGES (4096),
+ Snp->Cpb
+ );
+
+ PciIo->FreeBuffer (
+ PciIo,
+ SNP_MEM_PAGES (sizeof (SNP_DRIVER)),
+ Snp
+ );
+
+ return Status;
+}
+
+//
+// Simple Network Protocol Driver Global Variables
+//
+EFI_DRIVER_BINDING_PROTOCOL gSimpleNetworkDriverBinding = {
+ SimpleNetworkDriverSupported,
+ SimpleNetworkDriverStart,
+ SimpleNetworkDriverStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ The SNP driver entry point.
+
+ @param ImageHandle The driver image handle.
+ @param SystemTable The system table.
+
+ @retval EFI_SUCEESS Initialization routine has found UNDI hardware,
+ loaded it's ROM, and installed a notify event for
+ the Network Indentifier Interface Protocol
+ successfully.
+ @retval Other Return value from HandleProtocol for
+ DeviceIoProtocol or LoadedImageProtocol
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeSnpNiiDriver (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gSimpleNetworkDriverBinding,
+ ImageHandle,
+ &gSimpleNetworkComponentName,
+ &gSimpleNetworkComponentName2
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.h b/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.h
new file mode 100644
index 0000000000..9d5ae98e51
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.h
@@ -0,0 +1,1039 @@
+/** @file
+ Declaration of strctures and functions for SnpDxe driver.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#ifndef _SNP_H_
+#define _SNP_H_
+
+
+#include <Uefi.h>
+
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/DevicePath.h>
+
+#include <Guid/EventGroup.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Acpi.h>
+
+#define FOUR_GIGABYTES (UINT64) 0x100000000ULL
+
+
+#define SNP_DRIVER_SIGNATURE SIGNATURE_32 ('s', 'n', 'd', 's')
+#define MAX_MAP_LENGTH 100
+
+#define PCI_BAR_IO_MASK 0x00000003
+#define PCI_BAR_IO_MODE 0x00000001
+
+#define PCI_BAR_MEM_MASK 0x0000000F
+#define PCI_BAR_MEM_MODE 0x00000000
+#define PCI_BAR_MEM_64BIT 0x00000004
+
+#define SNP_TX_BUFFER_INCREASEMENT MAX_XMIT_BUFFERS
+#define SNP_MAX_TX_BUFFER_NUM 65536
+
+typedef
+EFI_STATUS
+(EFIAPI *ISSUE_UNDI32_COMMAND) (
+ UINT64 Cdb
+ );
+
+typedef struct {
+ UINT32 Signature;
+ EFI_LOCK Lock;
+
+ EFI_SIMPLE_NETWORK_PROTOCOL Snp;
+ EFI_SIMPLE_NETWORK_MODE Mode;
+
+ EFI_HANDLE DeviceHandle;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ //
+ // Local instance data needed by SNP driver
+ //
+ // Pointer to S/W UNDI API entry point
+ // This will be NULL for H/W UNDI
+ //
+ ISSUE_UNDI32_COMMAND IssueUndi32Command;
+
+ BOOLEAN IsSwUndi;
+
+ //
+ // undi interface number, if one undi manages more nics
+ //
+ PXE_IFNUM IfNum;
+
+ //
+ // Allocated tx/rx buffer that was passed to UNDI Initialize.
+ //
+ UINT32 TxRxBufferSize;
+ VOID *TxRxBuffer;
+ //
+ // mappable buffers for receive and fill header for undi3.0
+ // these will be used if the user buffers are above 4GB limit (instead of
+ // mapping the user buffers)
+ //
+ UINT8 *ReceiveBufffer;
+ VOID *ReceiveBufferUnmap;
+ UINT8 *FillHeaderBuffer;
+ VOID *FillHeaderBufferUnmap;
+
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT8 IoBarIndex;
+ UINT8 MemoryBarIndex;
+
+ //
+ // Buffers for command descriptor block, command parameter block
+ // and data block.
+ //
+ PXE_CDB Cdb;
+ VOID *Cpb;
+ VOID *CpbUnmap;
+ VOID *Db;
+
+ //
+ // UNDI structure, we need to remember the init info for a long time!
+ //
+ PXE_DB_GET_INIT_INFO InitInfo;
+
+ VOID *SnpDriverUnmap;
+ //
+ // when ever we map an address, we must remember it's address and the un-map
+ // cookie so that we can unmap later
+ //
+ struct MAP_LIST {
+ EFI_PHYSICAL_ADDRESS VirtualAddress;
+ VOID *MapCookie;
+ } MapList[MAX_MAP_LENGTH];
+
+ EFI_EVENT ExitBootServicesEvent;
+
+ //
+ // Whether UNDI support reporting media status from GET_STATUS command,
+ // i.e. PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED or
+ // PXE_STATFLAGS_GET_STATUS_NO_MEDIA_NOT_SUPPORTED
+ //
+ BOOLEAN MediaStatusSupported;
+
+ //
+ // Whether UNDI support cable detect for INITIALIZE command,
+ // i.e. PXE_STATFLAGS_CABLE_DETECT_SUPPORTED or
+ // PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED
+ //
+ BOOLEAN CableDetectSupported;
+
+ //
+ // Array of the recycled transmit buffer address from UNDI.
+ //
+ UINT64 *RecycledTxBuf;
+ //
+ // The maximum number of recycled buffer pointers in RecycledTxBuf.
+ //
+ UINT32 MaxRecycledTxBuf;
+ //
+ // Current number of recycled buffer pointers in RecycledTxBuf.
+ //
+ UINT32 RecycledTxBufCount;
+} SNP_DRIVER;
+
+#define EFI_SIMPLE_NETWORK_DEV_FROM_THIS(a) CR (a, SNP_DRIVER, Snp, SNP_DRIVER_SIGNATURE)
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gSimpleNetworkDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gSimpleNetworkComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gSimpleNetworkComponentName2;
+
+/**
+ this routine calls undi to start the interface and changes the snp state.
+
+ @param Snp pointer to snp driver structure
+
+ @retval EFI_DEVICE_ERROR UNDI could not be started
+ @retval EFI_SUCCESS UNDI is started successfully
+
+**/
+EFI_STATUS
+PxeStart (
+ IN SNP_DRIVER *Snp
+ );
+
+/**
+ this routine calls undi to stop the interface and changes the snp state.
+
+ @param Snp pointer to snp driver structure
+
+ @retval EFI_INVALID_PARAMETER invalid parameter
+ @retval EFI_NOT_STARTED SNP is not started
+ @retval EFI_DEVICE_ERROR SNP is not initialized
+ @retval EFI_UNSUPPORTED operation unsupported
+
+**/
+EFI_STATUS
+PxeStop (
+ SNP_DRIVER *Snp
+ );
+
+/**
+ this routine calls undi to initialize the interface.
+
+ @param Snp pointer to snp driver structure
+ @param CableDetectFlag Do/don't detect the cable (depending on what undi supports)
+
+ @retval EFI_SUCCESS UNDI is initialized successfully
+ @retval EFI_DEVICE_ERROR UNDI could not be initialized
+ @retval Other other errors
+
+**/
+EFI_STATUS
+PxeInit (
+ SNP_DRIVER *Snp,
+ UINT16 CableDetectFlag
+ );
+
+/**
+ this routine calls undi to shut down the interface.
+
+ @param Snp pointer to snp driver structure
+
+ @retval EFI_SUCCESS UNDI is shut down successfully
+ @retval EFI_DEVICE_ERROR UNDI could not be shut down
+
+**/
+EFI_STATUS
+PxeShutdown (
+ IN SNP_DRIVER *Snp
+ );
+
+/**
+ this routine calls undi to read the MAC address of the NIC and updates the
+ mode structure with the address.
+
+ @param Snp pointer to snp driver structure.
+
+ @retval EFI_SUCCESS the MAC address of the NIC is read successfully.
+ @retval EFI_DEVICE_ERROR failed to read the MAC address of the NIC.
+
+**/
+EFI_STATUS
+PxeGetStnAddr (
+ SNP_DRIVER *Snp
+ );
+
+/**
+ Call undi to get the status of the interrupts, get the list of recycled transmit
+ buffers that completed transmitting. The recycled transmit buffer address will
+ be saved into Snp->RecycledTxBuf. This function will also update the MediaPresent
+ field of EFI_SIMPLE_NETWORK_MODE if UNDI support it.
+
+ @param[in] Snp Pointer to snp driver structure.
+ @param[out] InterruptStatusPtr A non null pointer to contain the interrupt
+ status.
+ @param[in] GetTransmittedBuf Set to TRUE to retrieve the recycled transmit
+ buffer address.
+
+ @retval EFI_SUCCESS The status of the network interface was retrieved.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+
+**/
+EFI_STATUS
+PxeGetStatus (
+ IN SNP_DRIVER *Snp,
+ OUT UINT32 *InterruptStatusPtr,
+ IN BOOLEAN GetTransmittedBuf
+ );
+
+/**
+ This is a callback routine supplied to UNDI3.1 at undi_start time.
+ UNDI call this routine when it wants to have exclusive access to a critical
+ section of the code/data.
+ New callbacks for 3.1:
+ there won't be a virtual2physical callback for UNDI 3.1 because undi3.1 uses
+ the MemMap call to map the required address by itself!
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store Undi interface context (Undi does not read or write
+ this variable)
+ @param Enable non-zero indicates acquire
+ zero indicates release
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackBlock (
+ IN UINT64 UniqueId,
+ IN UINT32 Enable
+ );
+
+/**
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine with the number of micro seconds when it wants to
+ pause.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to
+ store Undi interface context (Undi does not read or write
+ this variable)
+ @param MicroSeconds number of micro seconds to pause, ususlly multiple of 10.
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackDelay (
+ IN UINT64 UniqueId,
+ IN UINT64 MicroSeconds
+ );
+
+/**
+ This is a callback routine supplied to UNDI at undi_start time.
+ This is the IO routine for UNDI3.1 to start CPB.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this
+ to store Undi interface context (Undi does not read or
+ write this variable)
+ @param ReadOrWrite indicates read or write, IO or Memory.
+ @param NumBytes number of bytes to read or write.
+ @param MemOrPortAddr IO or memory address to read from or write to.
+ @param BufferPtr memory location to read into or that contains the bytes
+ to write.
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackMemio (
+ IN UINT64 UniqueId,
+ IN UINT8 ReadOrWrite,
+ IN UINT8 NumBytes,
+ IN UINT64 MemOrPortAddr,
+ IN OUT UINT64 BufferPtr
+ );
+
+/**
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it has to map a CPU address to a device
+ address.
+
+ @param UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store
+ Undi interface context (Undi does not read or write this variable)
+ @param CpuAddr - Virtual address to be mapped!
+ @param NumBytes - size of memory to be mapped
+ @param Direction - direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways
+ @param DeviceAddrPtr - pointer to return the mapped device address
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackMap (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN OUT UINT64 DeviceAddrPtr
+ );
+
+/**
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it wants to unmap an address that was previously
+ mapped using map callback.
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to store.
+ Undi interface context (Undi does not read or write this variable)
+ @param CpuAddr Virtual address that was mapped!
+ @param NumBytes size of memory mapped
+ @param Direction direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways
+ @param DeviceAddr the mapped device address
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackUnmap (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr
+ );
+
+/**
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it wants synchronize the virtual buffer contents
+ with the mapped buffer contents. The virtual and mapped buffers need not
+ correspond to the same physical memory (especially if the virtual address is
+ > 4GB). Depending on the direction for which the buffer is mapped, undi will
+ need to synchronize their contents whenever it writes to/reads from the buffer
+ using either the cpu address or the device address.
+
+ EFI does not provide a sync call, since virt=physical, we sould just do
+ the synchronization ourself here!
+
+ @param UniqueId This was supplied to UNDI at Undi_Start, SNP uses this to store
+ Undi interface context (Undi does not read or write this variable)
+ @param CpuAddr Virtual address that was mapped!
+ @param NumBytes size of memory mapped.
+ @param Direction direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways.
+ @param DeviceAddr the mapped device address.
+
+**/
+VOID
+EFIAPI
+SnpUndi32CallbackSync (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr
+ );
+
+/**
+ Changes the state of a network interface from "stopped" to "started".
+
+ This function starts a network interface. If the network interface successfully
+ starts, then EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The network interface was started.
+ @retval EFI_ALREADY_STARTED The network interface is already in the started state.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Start (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+/**
+ Changes the state of a network interface from "started" to "stopped".
+
+ This function stops a network interface. This call is only valid if the network
+ interface is in the started state. If the network interface was successfully
+ stopped, then EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+
+ @retval EFI_SUCCESS The network interface was stopped.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Stop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+/**
+ Resets a network adapter and allocates the transmit and receive buffers
+ required by the network interface; optionally, also requests allocation of
+ additional transmit and receive buffers.
+
+ This function allocates the transmit and receive buffers required by the network
+ interface. If this allocation fails, then EFI_OUT_OF_RESOURCES is returned.
+ If the allocation succeeds and the network interface is successfully initialized,
+ then EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+ @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer space
+ that the driver should allocate for the network interface.
+ Some network interfaces will not be able to use the
+ extra buffer, and the caller will not know if it is
+ actually being used.
+ @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space
+ that the driver should allocate for the network interface.
+ Some network interfaces will not be able to use the
+ extra buffer, and the caller will not know if it is
+ actually being used.
+
+ @retval EFI_SUCCESS The network interface was initialized.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and
+ receive buffers.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Initialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+ );
+
+/**
+ Resets a network adapter and reinitializes it with the parameters that were
+ provided in the previous call to Initialize().
+
+ This function resets a network adapter and reinitializes it with the parameters
+ that were provided in the previous call to Initialize(). The transmit and
+ receive queues are emptied and all pending interrupts are cleared.
+ Receive filters, the station address, the statistics, and the multicast-IP-to-HW
+ MAC addresses are not reset by this call. If the network interface was
+ successfully reset, then EFI_SUCCESS will be returned. If the driver has not
+ been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param ExtendedVerification Indicates that the driver may perform a more
+ exhaustive verification operation of the device
+ during reset.
+
+ @retval EFI_SUCCESS The network interface was reset.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Reset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+/**
+ Resets a network adapter and leaves it in a state that is safe for another
+ driver to initialize.
+
+ This function releases the memory buffers assigned in the Initialize() call.
+ Pending transmits and receives are lost, and interrupts are cleared and disabled.
+ After this call, only the Initialize() and Stop() calls may be used. If the
+ network interface was successfully shutdown, then EFI_SUCCESS will be returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The network interface was shutdown.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Shutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+/**
+ Manages the multicast receive filters of a network interface.
+
+ This function is used enable and disable the hardware and software receive
+ filters for the underlying network device.
+ The receive filter change is broken down into three steps:
+ * The filter mask bits that are set (ON) in the Enable parameter are added to
+ the current receive filter settings.
+ * The filter mask bits that are set (ON) in the Disable parameter are subtracted
+ from the updated receive filter settings.
+ * If the resulting receive filter setting is not supported by the hardware a
+ more liberal setting is selected.
+ If the same bits are set in the Enable and Disable parameters, then the bits
+ in the Disable parameter takes precedence.
+ If the ResetMCastFilter parameter is TRUE, then the multicast address list
+ filter is disabled (irregardless of what other multicast bits are set in the
+ Enable and Disable parameters). The SNP->Mode->MCastFilterCount field is set
+ to zero. The Snp->Mode->MCastFilter contents are undefined.
+ After enabling or disabling receive filter settings, software should verify
+ the new settings by checking the Snp->Mode->ReceiveFilterSettings,
+ Snp->Mode->MCastFilterCount and Snp->Mode->MCastFilter fields.
+ Note: Some network drivers and/or devices will automatically promote receive
+ filter settings if the requested setting can not be honored. For example, if
+ a request for four multicast addresses is made and the underlying hardware
+ only supports two multicast addresses the driver might set the promiscuous
+ or promiscuous multicast receive filters instead. The receiving software is
+ responsible for discarding any extra packets that get through the hardware
+ receive filters.
+ Note: Note: To disable all receive filter hardware, the network driver must
+ be Shutdown() and Stopped(). Calling ReceiveFilters() with Disable set to
+ Snp->Mode->ReceiveFilterSettings will make it so no more packets are
+ returned by the Receive() function, but the receive hardware may still be
+ moving packets into system memory before inspecting and discarding them.
+ Unexpected system errors, reboots and hangs can occur if an OS is loaded
+ and the network devices are not Shutdown() and Stopped().
+ If ResetMCastFilter is TRUE, then the multicast receive filter list on the
+ network interface will be reset to the default multicast receive filter list.
+ If ResetMCastFilter is FALSE, and this network interface allows the multicast
+ receive filter list to be modified, then the MCastFilterCnt and MCastFilter
+ are used to update the current multicast receive filter list. The modified
+ receive filter list settings can be found in the MCastFilter field of
+ EFI_SIMPLE_NETWORK_MODE. If the network interface does not allow the multicast
+ receive filter list to be modified, then EFI_INVALID_PARAMETER will be returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+ If the receive filter mask and multicast receive filter list have been
+ successfully updated on the network interface, EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param Enable A bit mask of receive filters to enable on the network
+ interface.
+ @param Disable A bit mask of receive filters to disable on the network
+ interface. For backward compatibility with EFI 1.1
+ platforms, the EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit
+ must be set when the ResetMCastFilter parameter is TRUE.
+ @param ResetMCastFilter Set to TRUE to reset the contents of the multicast
+ receive filters on the network interface to their
+ default values.
+ @param MCastFilterCnt Number of multicast HW MAC addresses in the new MCastFilter
+ list. This value must be less than or equal to the
+ MCastFilterCnt field of EFI_SIMPLE_NETWORK_MODE.
+ This field is optional if ResetMCastFilter is TRUE.
+ @param MCastFilter A pointer to a list of new multicast receive filter HW
+ MAC addresses. This list will replace any existing
+ multicast HW MAC address list. This field is optional
+ if ResetMCastFilter is TRUE.
+
+ @retval EFI_SUCCESS The multicast receive filter list was updated.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * This is NULL
+ * There are bits set in Enable that are not set
+ in Snp->Mode->ReceiveFilterMask
+ * There are bits set in Disable that are not set
+ in Snp->Mode->ReceiveFilterMask
+ * Multicast is being enabled (the
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit is
+ set in Enable, it is not set in Disable, and
+ ResetMCastFilter is FALSE) and MCastFilterCount
+ is zero
+ * Multicast is being enabled and MCastFilterCount
+ is greater than Snp->Mode->MaxMCastFilterCount
+ * Multicast is being enabled and MCastFilter is NULL
+ * Multicast is being enabled and one or more of
+ the addresses in the MCastFilter list are not
+ valid multicast MAC addresses
+ @retval EFI_DEVICE_ERROR One or more of the following conditions is TRUE:
+ * The network interface has been started but has
+ not been initialized
+ * An unexpected error was returned by the
+ underlying network driver or device
+ @retval EFI_UNSUPPORTED This function is not supported by the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32ReceiveFilters (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINT32 Enable,
+ IN UINT32 Disable,
+ IN BOOLEAN ResetMCastFilter,
+ IN UINTN MCastFilterCnt, OPTIONAL
+ IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL
+ );
+
+/**
+ Modifies or resets the current station address, if supported.
+
+ This function modifies or resets the current station address of a network
+ interface, if supported. If Reset is TRUE, then the current station address is
+ set to the network interface's permanent address. If Reset is FALSE, and the
+ network interface allows its station address to be modified, then the current
+ station address is changed to the address specified by New. If the network
+ interface does not allow its station address to be modified, then
+ EFI_INVALID_PARAMETER will be returned. If the station address is successfully
+ updated on the network interface, EFI_SUCCESS will be returned. If the driver
+ has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param Reset Flag used to reset the station address to the network interface's
+ permanent address.
+ @param New New station address to be used for the network interface.
+
+
+ @retval EFI_SUCCESS The network interface's station address was updated.
+ @retval EFI_NOT_STARTED The Simple Network Protocol interface has not been
+ started by calling Start().
+ @retval EFI_INVALID_PARAMETER The New station address was not accepted by the NIC.
+ @retval EFI_INVALID_PARAMETER Reset is FALSE and New is NULL.
+ @retval EFI_DEVICE_ERROR The Simple Network Protocol interface has not
+ been initialized by calling Initialize().
+ @retval EFI_DEVICE_ERROR An error occurred attempting to set the new
+ station address.
+ @retval EFI_UNSUPPORTED The NIC does not support changing the network
+ interface's station address.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32StationAddress (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN EFI_MAC_ADDRESS *New OPTIONAL
+ );
+
+/**
+ Resets or collects the statistics on a network interface.
+
+ This function resets or collects the statistics on a network interface. If the
+ size of the statistics table specified by StatisticsSize is not big enough for
+ all the statistics that are collected by the network interface, then a partial
+ buffer of statistics is returned in StatisticsTable, StatisticsSize is set to
+ the size required to collect all the available statistics, and
+ EFI_BUFFER_TOO_SMALL is returned.
+ If StatisticsSize is big enough for all the statistics, then StatisticsTable
+ will be filled, StatisticsSize will be set to the size of the returned
+ StatisticsTable structure, and EFI_SUCCESS is returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+ If Reset is FALSE, and both StatisticsSize and StatisticsTable are NULL, then
+ no operations will be performed, and EFI_SUCCESS will be returned.
+ If Reset is TRUE, then all of the supported statistics counters on this network
+ interface will be reset to zero.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param Reset Set to TRUE to reset the statistics for the network interface.
+ @param StatisticsSize On input the size, in bytes, of StatisticsTable. On output
+ the size, in bytes, of the resulting table of statistics.
+ @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+ contains the statistics. Type EFI_NETWORK_STATISTICS is
+ defined in "Related Definitions" below.
+
+ @retval EFI_SUCCESS The requested operation succeeded.
+ @retval EFI_NOT_STARTED The Simple Network Protocol interface has not been
+ started by calling Start().
+ @retval EFI_BUFFER_TOO_SMALL StatisticsSize is not NULL and StatisticsTable is
+ NULL. The current buffer size that is needed to
+ hold all the statistics is returned in StatisticsSize.
+ @retval EFI_BUFFER_TOO_SMALL StatisticsSize is not NULL and StatisticsTable is
+ not NULL. The current buffer size that is needed
+ to hold all the statistics is returned in
+ StatisticsSize. A partial set of statistics is
+ returned in StatisticsTable.
+ @retval EFI_INVALID_PARAMETER StatisticsSize is NULL and StatisticsTable is not
+ NULL.
+ @retval EFI_DEVICE_ERROR The Simple Network Protocol interface has not
+ been initialized by calling Initialize().
+ @retval EFI_DEVICE_ERROR An error was encountered collecting statistics
+ from the NIC.
+ @retval EFI_UNSUPPORTED The NIC does not support collecting statistics
+ from the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Statistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN OUT UINTN *StatisticsSize, OPTIONAL
+ IN OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
+ );
+
+/**
+ Converts a multicast IP address to a multicast HW MAC address.
+
+ This function converts a multicast IP address to a multicast HW MAC address
+ for all packet transactions. If the mapping is accepted, then EFI_SUCCESS will
+ be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460].
+ Set to FALSE if the multicast IP address is IPv4 [RFC 791].
+ @param IP The multicast IP address that is to be converted to a multicast
+ HW MAC address.
+ @param MAC The multicast HW MAC address that is to be generated from IP.
+
+ @retval EFI_SUCCESS The multicast IP address was mapped to the
+ multicast HW MAC address.
+ @retval EFI_NOT_STARTED The Simple Network Protocol interface has not
+ been started by calling Start().
+ @retval EFI_INVALID_PARAMETER IP is NULL.
+ @retval EFI_INVALID_PARAMETER MAC is NULL.
+ @retval EFI_INVALID_PARAMETER IP does not point to a valid IPv4 or IPv6
+ multicast address.
+ @retval EFI_DEVICE_ERROR The Simple Network Protocol interface has not
+ been initialized by calling Initialize().
+ @retval EFI_UNSUPPORTED IPv6 is TRUE and the implementation does not
+ support IPv6 multicast to MAC address conversion.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32McastIpToMac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ OUT EFI_MAC_ADDRESS *MAC
+ );
+
+/**
+ Performs read and write operations on the NVRAM device attached to a network
+ interface.
+
+ This function performs read and write operations on the NVRAM device attached
+ to a network interface. If ReadWrite is TRUE, a read operation is performed.
+ If ReadWrite is FALSE, a write operation is performed. Offset specifies the
+ byte offset at which to start either operation. Offset must be a multiple of
+ NvRamAccessSize , and it must have a value between zero and NvRamSize.
+ BufferSize specifies the length of the read or write operation. BufferSize must
+ also be a multiple of NvRamAccessSize, and Offset + BufferSize must not exceed
+ NvRamSize.
+ If any of the above conditions is not met, then EFI_INVALID_PARAMETER will be
+ returned.
+ If all the conditions are met and the operation is "read," the NVRAM device
+ attached to the network interface will be read into Buffer and EFI_SUCCESS
+ will be returned. If this is a write operation, the contents of Buffer will be
+ used to update the contents of the NVRAM device attached to the network
+ interface and EFI_SUCCESS will be returned.
+
+ It does the basic checking on the input parameters and retrieves snp structure
+ and then calls the read_nvdata() call which does the actual reading
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param ReadWrite TRUE for read operations, FALSE for write operations.
+ @param Offset Byte offset in the NVRAM device at which to start the read or
+ write operation. This must be a multiple of NvRamAccessSize
+ and less than NvRamSize. (See EFI_SIMPLE_NETWORK_MODE)
+ @param BufferSize The number of bytes to read or write from the NVRAM device.
+ This must also be a multiple of NvramAccessSize.
+ @param Buffer A pointer to the data buffer.
+
+ @retval EFI_SUCCESS The NVRAM access was performed.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * The This parameter is NULL
+ * The This parameter does not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure
+ * The Offset parameter is not a multiple of
+ EFI_SIMPLE_NETWORK_MODE.NvRamAccessSize
+ * The Offset parameter is not less than
+ EFI_SIMPLE_NETWORK_MODE.NvRamSize
+ * The BufferSize parameter is not a multiple of
+ EFI_SIMPLE_NETWORK_MODE.NvRamAccessSize
+ * The Buffer parameter is NULL
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32NvData (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ );
+
+/**
+ Reads the current interrupt status and recycled transmit buffer status from a
+ network interface.
+
+ This function gets the current interrupt and recycled transmit buffer status
+ from the network interface. The interrupt status is returned as a bit mask in
+ InterruptStatus. If InterruptStatus is NULL, the interrupt status will not be
+ read. If TxBuf is not NULL, a recycled transmit buffer address will be retrieved.
+ If a recycled transmit buffer address is returned in TxBuf, then the buffer has
+ been successfully transmitted, and the status for that buffer is cleared. If
+ the status of the network interface is successfully collected, EFI_SUCCESS
+ will be returned. If the driver has not been initialized, EFI_DEVICE_ERROR will
+ be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param InterruptStatus A pointer to the bit mask of the currently active
+ interrupts (see "Related Definitions"). If this is NULL,
+ the interrupt status will not be read from the device.
+ If this is not NULL, the interrupt status will be read
+ from the device. When the interrupt status is read, it
+ will also be cleared. Clearing the transmit interrupt does
+ not empty the recycled transmit buffer array.
+ @param TxBuf Recycled transmit buffer address. The network interface
+ will not transmit if its internal recycled transmit
+ buffer array is full. Reading the transmit buffer does
+ not clear the transmit interrupt. If this is NULL, then
+ the transmit buffer status will not be read. If there
+ are no transmit buffers to recycle and TxBuf is not NULL,
+ TxBuf will be set to NULL.
+
+ @retval EFI_SUCCESS The status of the network interface was retrieved.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32GetStatus (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINT32 *InterruptStatus, OPTIONAL
+ OUT VOID **TxBuf OPTIONAL
+ );
+
+/**
+ Places a packet in the transmit queue of a network interface.
+
+ This function places the packet specified by Header and Buffer on the transmit
+ queue. If HeaderSize is nonzero and HeaderSize is not equal to
+ This->Mode->MediaHeaderSize, then EFI_INVALID_PARAMETER will be returned. If
+ BufferSize is less than This->Mode->MediaHeaderSize, then EFI_BUFFER_TOO_SMALL
+ will be returned. If Buffer is NULL, then EFI_INVALID_PARAMETER will be
+ returned. If HeaderSize is nonzero and DestAddr or Protocol is NULL, then
+ EFI_INVALID_PARAMETER will be returned. If the transmit engine of the network
+ interface is busy, then EFI_NOT_READY will be returned. If this packet can be
+ accepted by the transmit engine of the network interface, the packet contents
+ specified by Buffer will be placed on the transmit queue of the network
+ interface, and EFI_SUCCESS will be returned. GetStatus() can be used to
+ determine when the packet has actually been transmitted. The contents of the
+ Buffer must not be modified until the packet has actually been transmitted.
+ The Transmit() function performs nonblocking I/O. A caller who wants to perform
+ blocking I/O, should call Transmit(), and then GetStatus() until the
+ transmitted buffer shows up in the recycled transmit buffer.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param HeaderSize The size, in bytes, of the media header to be filled in by the
+ Transmit() function. If HeaderSize is nonzero, then it must
+ be equal to This->Mode->MediaHeaderSize and the DestAddr and
+ Protocol parameters must not be NULL.
+ @param BufferSize The size, in bytes, of the entire packet (media header and
+ data) to be transmitted through the network interface.
+ @param Buffer A pointer to the packet (media header followed by data) to be
+ transmitted. This parameter cannot be NULL. If HeaderSize is
+ zero, then the media header in Buffer must already be filled
+ in by the caller. If HeaderSize is nonzero, then the media
+ header will be filled in by the Transmit() function.
+ @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this
+ parameter is ignored. If HeaderSize is nonzero and SrcAddr
+ is NULL, then This->Mode->CurrentAddress is used for the
+ source HW MAC address.
+ @param DestAddr The destination HW MAC address. If HeaderSize is zero, then
+ this parameter is ignored.
+ @param Protocol The type of header to build. If HeaderSize is zero, then this
+ parameter is ignored. See RFC 1700, section "Ether Types,"
+ for examples.
+
+ @retval EFI_SUCCESS The packet was placed on the transmit queue.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_NOT_READY The network interface is too busy to accept this
+ transmit request.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported
+ value.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Transmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN HeaderSize,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN EFI_MAC_ADDRESS *SrcAddr, OPTIONAL
+ IN EFI_MAC_ADDRESS *DestAddr, OPTIONAL
+ IN UINT16 *Protocol OPTIONAL
+ );
+
+/**
+ Receives a packet from a network interface.
+
+ This function retrieves one packet from the receive queue of a network interface.
+ If there are no packets on the receive queue, then EFI_NOT_READY will be
+ returned. If there is a packet on the receive queue, and the size of the packet
+ is smaller than BufferSize, then the contents of the packet will be placed in
+ Buffer, and BufferSize will be updated with the actual size of the packet.
+ In addition, if SrcAddr, DestAddr, and Protocol are not NULL, then these values
+ will be extracted from the media header and returned. EFI_SUCCESS will be
+ returned if a packet was successfully received.
+ If BufferSize is smaller than the received packet, then the size of the receive
+ packet will be placed in BufferSize and EFI_BUFFER_TOO_SMALL will be returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param HeaderSize The size, in bytes, of the media header received on the network
+ interface. If this parameter is NULL, then the media header size
+ will not be returned.
+ @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
+ bytes, of the packet that was received on the network interface.
+ @param Buffer A pointer to the data buffer to receive both the media
+ header and the data.
+ @param SrcAddr The source HW MAC address. If this parameter is NULL, the HW
+ MAC source address will not be extracted from the media header.
+ @param DestAddr The destination HW MAC address. If this parameter is NULL,
+ the HW MAC destination address will not be extracted from
+ the media header.
+ @param Protocol The media header type. If this parameter is NULL, then the
+ protocol will not be extracted from the media header. See
+ RFC 1700 section "Ether Types" for examples.
+
+ @retval EFI_SUCCESS The received data was stored in Buffer, and
+ BufferSize has been updated to the number of
+ bytes received.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_NOT_READY No packets have been received on the network interface.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the received packets.
+ BufferSize has been updated to the required size.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ * The This parameter is NULL
+ * The This parameter does not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ * The BufferSize parameter is NULL
+ * The Buffer parameter is NULL
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Receive (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINTN *HeaderSize OPTIONAL,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer,
+ OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
+ OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL,
+ OUT UINT16 *Protocol OPTIONAL
+ );
+
+/**
+ Nofication call back function for WaitForPacket event.
+
+ @param Event EFI Event.
+ @param SnpPtr Pointer to SNP_DRIVER structure.
+
+**/
+VOID
+EFIAPI
+SnpWaitForPacketNotify (
+ EFI_EVENT Event,
+ VOID *SnpPtr
+ );
+
+#define SNP_MEM_PAGES(x) (((x) - 1) / 4096 + 1)
+
+
+#endif /* _SNP_H_ */
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
new file mode 100644
index 0000000000..90fd8d5475
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
@@ -0,0 +1,83 @@
+## @file
+# This module produces EFI SNP Protocol.
+#
+# This module produces Simple Network Protocol upon EFI Network Interface
+# Identifier Protocol, to provide a packet level interface to a network adapter.
+#
+# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials are licensed
+# and made available under the terms and conditions of the BSD License which
+# accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SnpDxe
+ MODULE_UNI_FILE = SnpDxe.uni
+ FILE_GUID = A2f436EA-A127-4EF8-957C-8048606FF670
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = InitializeSnpNiiDriver
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = mSimpleNetworkDriverBinding
+# COMPONENT_NAME = gSimpleNetworkComponentName
+# COMPONENT_NAME2 = gSimpleNetworkComponentName2
+#
+
+[Sources]
+ Receive.c
+ Snp.h
+ Nvdata.c
+ Get_status.c
+ Start.c
+ Snp.c
+ Stop.c
+ Statistics.c
+ Reset.c
+ Shutdown.c
+ Mcast_ip_to_mac.c
+ Transmit.c
+ WaitForPacket.c
+ Receive_filters.c
+ Initialize.c
+ ComponentName.c
+ Callback.c
+ Station_address.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+ NetLib
+
+[Guids]
+ gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event
+
+[Protocols]
+ gEfiSimpleNetworkProtocolGuid ## BY_START
+ gEfiDevicePathProtocolGuid ## TO_START
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## TO_START
+ gEfiPciIoProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ SnpDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.uni b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.uni
new file mode 100644
index 0000000000..cd1e9ba521
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// This module produces EFI SNP Protocol.
+//
+// This module produces Simple Network Protocol upon EFI Network Interface
+// Identifier Protocol, to provide a packet level interface to a network adapter.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials are licensed
+// and made available under the terms and conditions of the BSD License which
+// accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI SNP Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces Simple Network Protocol upon EFI Network Interface Identifier Protocol to provide a packet level interface to a network adapter."
+
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni
new file mode 100644
index 0000000000..5476a45f4e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// SnpDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials are licensed
+// and made available under the terms and conditions of the BSD License which
+// accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"SNP DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Start.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Start.c
new file mode 100644
index 0000000000..210f081078
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Start.c
@@ -0,0 +1,168 @@
+/** @file
+ Implementation of starting a network adapter.
+
+Copyright (c) 2004 - 2007, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to start the interface and changes the snp state.
+
+ @param Snp pointer to snp driver structure.
+
+ @retval EFI_SUCCESS UNDI is started successfully.
+ @retval EFI_DEVICE_ERROR UNDI could not be started.
+
+**/
+EFI_STATUS
+PxeStart (
+ IN SNP_DRIVER *Snp
+ )
+{
+ PXE_CPB_START_31 *Cpb31;
+
+ Cpb31 = Snp->Cpb;
+ //
+ // Initialize UNDI Start CDB for H/W UNDI
+ //
+ Snp->Cdb.OpCode = PXE_OPCODE_START;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Make changes to H/W UNDI Start CDB if this is
+ // a S/W UNDI.
+ //
+ if (Snp->IsSwUndi) {
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_START_31);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb31;
+
+ Cpb31->Delay = (UINT64)(UINTN) &SnpUndi32CallbackDelay;
+ Cpb31->Block = (UINT64)(UINTN) &SnpUndi32CallbackBlock;
+
+ //
+ // Virtual == Physical. This can be set to zero.
+ //
+ Cpb31->Virt2Phys = (UINT64)(UINTN) 0;
+ Cpb31->Mem_IO = (UINT64)(UINTN) &SnpUndi32CallbackMemio;
+
+ Cpb31->Map_Mem = (UINT64)(UINTN) &SnpUndi32CallbackMap;
+ Cpb31->UnMap_Mem = (UINT64)(UINTN) &SnpUndi32CallbackUnmap;
+ Cpb31->Sync_Mem = (UINT64)(UINTN) &SnpUndi32CallbackSync;
+
+ Cpb31->Unique_ID = (UINT64)(UINTN) Snp;
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.start() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI could not be started. Return UNDI error.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.start() %xh:%xh\n",
+ Snp->Cdb.StatCode,
+ Snp->Cdb.StatFlags)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set simple network state to Started and return success.
+ //
+ Snp->Mode.State = EfiSimpleNetworkStarted;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Change the state of a network interface from "stopped" to "started."
+
+ This function starts a network interface. If the network interface successfully
+ starts, then EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The network interface was started.
+ @retval EFI_ALREADY_STARTED The network interface is already in the started state.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a valid
+ EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Start (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkStopped:
+ break;
+
+ case EfiSimpleNetworkStarted:
+ case EfiSimpleNetworkInitialized:
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = PxeStart (Snp);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // clear the map_list in SNP structure
+ //
+ for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {
+ Snp->MapList[Index].VirtualAddress = 0;
+ Snp->MapList[Index].MapCookie = 0;
+ }
+
+ Snp->Mode.MCastFilterCount = 0;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Station_address.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Station_address.c
new file mode 100644
index 0000000000..0917baf674
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Station_address.c
@@ -0,0 +1,249 @@
+/** @file
+ Implementation of reading the MAC address of a network adapter.
+
+Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to read the MAC address of the NIC and update the mode structure
+ with the address.
+
+ @param Snp Pointer to snp driver structure.
+
+ @retval EFI_SUCCESS The MAC address of the NIC is read successfully.
+ @retval EFI_DEVICE_ERROR Failed to read the MAC address of the NIC.
+
+**/
+EFI_STATUS
+PxeGetStnAddr (
+ SNP_DRIVER *Snp
+ )
+{
+ PXE_DB_STATION_ADDRESS *Db;
+
+ Db = Snp->Db;
+ Snp->Cdb.OpCode = PXE_OPCODE_STATION_ADDRESS;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_READ;
+
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_STATION_ADDRESS);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.station_addr() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set new station address in SNP->Mode structure and return success.
+ //
+ CopyMem (
+ &(Snp->Mode.CurrentAddress),
+ &Db->StationAddr,
+ Snp->Mode.HwAddressSize
+ );
+
+ CopyMem (
+ &Snp->Mode.BroadcastAddress,
+ &Db->BroadcastAddr,
+ Snp->Mode.HwAddressSize
+ );
+
+ CopyMem (
+ &Snp->Mode.PermanentAddress,
+ &Db->PermanentAddr,
+ Snp->Mode.HwAddressSize
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call UNDI to set a new MAC address for the NIC.
+
+ @param Snp Pointer to Snp driver structure.
+ @param NewMacAddr Pointer to a MAC address to be set for the NIC, if this is
+ NULL then this routine resets the mac address to the NIC's
+ original address.
+
+
+**/
+EFI_STATUS
+PxeSetStnAddr (
+ SNP_DRIVER *Snp,
+ EFI_MAC_ADDRESS *NewMacAddr
+ )
+{
+ PXE_CPB_STATION_ADDRESS *Cpb;
+ PXE_DB_STATION_ADDRESS *Db;
+
+ Cpb = Snp->Cpb;
+ Db = Snp->Db;
+ Snp->Cdb.OpCode = PXE_OPCODE_STATION_ADDRESS;
+
+ if (NewMacAddr == NULL) {
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_RESET;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ } else {
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_WRITE;
+ //
+ // Supplying a new address in the CPB will make undi change the mac address to the new one.
+ //
+ CopyMem (&Cpb->StationAddr, NewMacAddr, Snp->Mode.HwAddressSize);
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_STATION_ADDRESS);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+ }
+
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_STATION_ADDRESS);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) Db;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.station_addr() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // read the changed address and save it in SNP->Mode structure
+ //
+ PxeGetStnAddr (Snp);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Modifies or resets the current station address, if supported.
+
+ This function modifies or resets the current station address of a network
+ interface, if supported. If Reset is TRUE, then the current station address is
+ set to the network interface's permanent address. If Reset is FALSE, and the
+ network interface allows its station address to be modified, then the current
+ station address is changed to the address specified by New. If the network
+ interface does not allow its station address to be modified, then
+ EFI_INVALID_PARAMETER will be returned. If the station address is successfully
+ updated on the network interface, EFI_SUCCESS will be returned. If the driver
+ has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param Reset Flag used to reset the station address to the network interface's
+ permanent address.
+ @param New New station address to be used for the network interface.
+
+
+ @retval EFI_SUCCESS The network interface's station address was updated.
+ @retval EFI_NOT_STARTED The Simple Network Protocol interface has not been
+ started by calling Start().
+ @retval EFI_INVALID_PARAMETER The New station address was not accepted by the NIC.
+ @retval EFI_INVALID_PARAMETER Reset is FALSE and New is NULL.
+ @retval EFI_DEVICE_ERROR The Simple Network Protocol interface has not
+ been initialized by calling Initialize().
+ @retval EFI_DEVICE_ERROR An error occurred attempting to set the new
+ station address.
+ @retval EFI_UNSUPPORTED The NIC does not support changing the network
+ interface's station address.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32StationAddress (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN EFI_MAC_ADDRESS *New OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // Check for invalid parameter combinations.
+ //
+ if ((This == NULL) ||
+ (!Reset && (New == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (Reset) {
+ Status = PxeSetStnAddr (Snp, NULL);
+ } else {
+ Status = PxeSetStnAddr (Snp, New);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Statistics.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Statistics.c
new file mode 100644
index 0000000000..3e564991e3
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Statistics.c
@@ -0,0 +1,230 @@
+/** @file
+ Implementation of collecting the statistics on a network interface.
+
+Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Snp.h"
+
+
+/**
+ Resets or collects the statistics on a network interface.
+
+ This function resets or collects the statistics on a network interface. If the
+ size of the statistics table specified by StatisticsSize is not big enough for
+ all the statistics that are collected by the network interface, then a partial
+ buffer of statistics is returned in StatisticsTable, StatisticsSize is set to
+ the size required to collect all the available statistics, and
+ EFI_BUFFER_TOO_SMALL is returned.
+ If StatisticsSize is big enough for all the statistics, then StatisticsTable
+ will be filled, StatisticsSize will be set to the size of the returned
+ StatisticsTable structure, and EFI_SUCCESS is returned.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+ If Reset is FALSE, and both StatisticsSize and StatisticsTable are NULL, then
+ no operations will be performed, and EFI_SUCCESS will be returned.
+ If Reset is TRUE, then all of the supported statistics counters on this network
+ interface will be reset to zero.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param Reset Set to TRUE to reset the statistics for the network interface.
+ @param StatisticsSize On input the size, in bytes, of StatisticsTable. On output
+ the size, in bytes, of the resulting table of statistics.
+ @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+ contains the statistics. Type EFI_NETWORK_STATISTICS is
+ defined in "Related Definitions" below.
+
+ @retval EFI_SUCCESS The requested operation succeeded.
+ @retval EFI_NOT_STARTED The Simple Network Protocol interface has not been
+ started by calling Start().
+ @retval EFI_BUFFER_TOO_SMALL StatisticsSize is not NULL and StatisticsTable is
+ NULL. The current buffer size that is needed to
+ hold all the statistics is returned in StatisticsSize.
+ @retval EFI_BUFFER_TOO_SMALL StatisticsSize is not NULL and StatisticsTable is
+ not NULL. The current buffer size that is needed
+ to hold all the statistics is returned in
+ StatisticsSize. A partial set of statistics is
+ returned in StatisticsTable.
+ @retval EFI_INVALID_PARAMETER StatisticsSize is NULL and StatisticsTable is not
+ NULL.
+ @retval EFI_DEVICE_ERROR The Simple Network Protocol interface has not
+ been initialized by calling Initialize().
+ @retval EFI_DEVICE_ERROR An error was encountered collecting statistics
+ from the NIC.
+ @retval EFI_UNSUPPORTED The NIC does not support collecting statistics
+ from the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Statistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN OUT UINTN *StatisticsSize, OPTIONAL
+ IN OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ PXE_DB_STATISTICS *Db;
+ UINT64 *Stp;
+ UINT64 Mask;
+ UINTN Size;
+ UINTN Index;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Get pointer to SNP driver instance for *This.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // if we are not resetting the counters, we have to have a valid stat table
+ // with >0 size. if no reset, no table and no size, return success.
+ //
+ if (!Reset && StatisticsSize == NULL) {
+ Status = (StatisticsTable != NULL) ? EFI_INVALID_PARAMETER : EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+ //
+ // Initialize UNDI Statistics CDB
+ //
+ Snp->Cdb.OpCode = PXE_OPCODE_STATISTICS;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ if (Reset) {
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_STATISTICS_RESET;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Db = Snp->Db;
+ } else {
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_STATISTICS_READ;
+ Snp->Cdb.DBsize = (UINT16) sizeof (PXE_DB_STATISTICS);
+ Snp->Cdb.DBaddr = (UINT64)(UINTN) (Db = Snp->Db);
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.statistics() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.statistics() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.statistics() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (Reset) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ if (StatisticsTable == NULL) {
+ *StatisticsSize = sizeof (EFI_NETWORK_STATISTICS);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ //
+ // Convert the UNDI statistics information to SNP statistics
+ // information.
+ //
+ ZeroMem (StatisticsTable, *StatisticsSize);
+ Stp = (UINT64 *) StatisticsTable;
+ Size = 0;
+
+ for (Index = 0, Mask = 1; Index < 64; Index++, Mask = LShiftU64 (Mask, 1), Stp++) {
+ //
+ // There must be room for a full UINT64. Partial
+ // numbers will not be stored.
+ //
+ if ((Index + 1) * sizeof (UINT64) > *StatisticsSize) {
+ break;
+ }
+
+ if ((Db->Supported & Mask) != 0) {
+ *Stp = Db->Data[Index];
+ Size = Index + 1;
+ } else {
+ SetMem (Stp, sizeof (UINT64), 0xFF);
+ }
+ }
+ //
+ // Compute size up to last supported statistic.
+ //
+ while (++Index < 64) {
+ if ((Db->Supported & (Mask = LShiftU64 (Mask, 1))) != 0) {
+ Size = Index;
+ }
+ }
+
+ Size *= sizeof (UINT64);
+
+ if (*StatisticsSize >= Size) {
+ *StatisticsSize = Size;
+ Status = EFI_SUCCESS;
+ } else {
+ *StatisticsSize = Size;
+ Status = EFI_BUFFER_TOO_SMALL;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Stop.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Stop.c
new file mode 100644
index 0000000000..a52d3b1163
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Stop.c
@@ -0,0 +1,126 @@
+/** @file
+ Implementation of stopping a network interface.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to stop the interface and changes the snp state.
+
+ @param Snp Pointer to snp driver structure
+
+ @retval EFI_SUCCESS The network interface was stopped.
+ @retval EFI_DEVICE_ERROR SNP is not initialized.
+
+**/
+EFI_STATUS
+PxeStop (
+ SNP_DRIVER *Snp
+ )
+{
+ Snp->Cdb.OpCode = PXE_OPCODE_STOP;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.stop() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
+
+ if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_WARN,
+ "\nsnp->undi.stop() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set simple network state to Started and return success.
+ //
+ Snp->Mode.State = EfiSimpleNetworkStopped;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Changes the state of a network interface from "started" to "stopped."
+
+ This function stops a network interface. This call is only valid if the network
+ interface is in the started state. If the network interface was successfully
+ stopped, then EFI_SUCCESS will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL
+ instance.
+
+
+ @retval EFI_SUCCESS The network interface was stopped.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_INVALID_PARAMETER This parameter was NULL or did not point to a
+ valid EFI_SIMPLE_NETWORK_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network
+ interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network
+ interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Stop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkStarted:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = PxeStop (Snp);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/Transmit.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/Transmit.c
new file mode 100644
index 0000000000..73461bce42
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Transmit.c
@@ -0,0 +1,355 @@
+/** @file
+ Implementation of transmitting a packet.
+
+Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to create the meadia header for the given data buffer.
+
+ @param Snp Pointer to SNP driver structure.
+ @param MacHeaderPtr Address where the media header will be filled in.
+ @param HeaderSize Size of the memory at MacHeaderPtr.
+ @param Buffer Data buffer pointer.
+ @param BufferSize Size of data in the Buffer
+ @param DestAddr Address of the destination mac address buffer.
+ @param SrcAddr Address of the source mac address buffer.
+ @param ProtocolPtr Address of the protocol type.
+
+ @retval EFI_SUCCESS Successfully completed the undi call.
+ @retval Other Error return from undi call.
+
+**/
+EFI_STATUS
+PxeFillHeader (
+ SNP_DRIVER *Snp,
+ VOID *MacHeaderPtr,
+ UINTN HeaderSize,
+ VOID *Buffer,
+ UINTN BufferSize,
+ EFI_MAC_ADDRESS *DestAddr,
+ EFI_MAC_ADDRESS *SrcAddr,
+ UINT16 *ProtocolPtr
+ )
+{
+ PXE_CPB_FILL_HEADER_FRAGMENTED *Cpb;
+
+ Cpb = Snp->Cpb;
+ if (SrcAddr != NULL) {
+ CopyMem (
+ (VOID *) Cpb->SrcAddr,
+ (VOID *) SrcAddr,
+ Snp->Mode.HwAddressSize
+ );
+ } else {
+ CopyMem (
+ (VOID *) Cpb->SrcAddr,
+ (VOID *) &(Snp->Mode.CurrentAddress),
+ Snp->Mode.HwAddressSize
+ );
+ }
+
+ CopyMem (
+ (VOID *) Cpb->DestAddr,
+ (VOID *) DestAddr,
+ Snp->Mode.HwAddressSize
+ );
+
+ //
+ // we need to do the byte swapping
+ //
+ Cpb->Protocol = (UINT16) PXE_SWAP_UINT16 (*ProtocolPtr);
+
+ Cpb->PacketLen = (UINT32) (BufferSize);
+ Cpb->MediaHeaderLen = (UINT16) HeaderSize;
+
+ Cpb->FragCnt = 2;
+ Cpb->reserved = 0;
+
+ Cpb->FragDesc[0].FragAddr = (UINT64)(UINTN) MacHeaderPtr;
+ Cpb->FragDesc[0].FragLen = (UINT32) HeaderSize;
+ Cpb->FragDesc[1].FragAddr = (UINT64)(UINTN) Buffer;
+ Cpb->FragDesc[1].FragLen = (UINT32) BufferSize;
+
+ Cpb->FragDesc[0].reserved = Cpb->FragDesc[1].reserved = 0;
+
+ Snp->Cdb.OpCode = PXE_OPCODE_FILL_HEADER;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_FILL_HEADER_FRAGMENTED;
+
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nSnp->undi.fill_header() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64) (UINTN) &Snp->Cdb);
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ return EFI_SUCCESS;
+
+ case PXE_STATCODE_INVALID_PARAMETER:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->undi.fill_header() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_INVALID_PARAMETER;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->undi.fill_header() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+
+/**
+ This routine calls undi to transmit the given data buffer
+
+ @param Snp pointer to SNP driver structure
+ @param Buffer data buffer pointer
+ @param BufferSize Size of data in the Buffer
+
+ @retval EFI_SUCCESS if successfully completed the undi call
+ @retval Other error return from undi call.
+
+**/
+EFI_STATUS
+PxeTransmit (
+ SNP_DRIVER *Snp,
+ VOID *Buffer,
+ UINTN BufferSize
+ )
+{
+ PXE_CPB_TRANSMIT *Cpb;
+ EFI_STATUS Status;
+
+ Cpb = Snp->Cpb;
+ Cpb->FrameAddr = (UINT64) (UINTN) Buffer;
+ Cpb->DataLen = (UINT32) BufferSize;
+
+ Cpb->MediaheaderLen = 0;
+ Cpb->reserved = 0;
+
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_TRANSMIT_WHOLE;
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_TRANSMIT);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+
+ Snp->Cdb.OpCode = PXE_OPCODE_TRANSMIT;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nSnp->undi.transmit() "));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.OpCode == %x", Snp->Cdb.OpCode));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.CPBaddr == %LX", Snp->Cdb.CPBaddr));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.DBaddr == %LX", Snp->Cdb.DBaddr));
+ DEBUG ((EFI_D_NET, "\nCpb->FrameAddr == %LX\n", Cpb->FrameAddr));
+
+ (*Snp->IssueUndi32Command) ((UINT64) (UINTN) &Snp->Cdb);
+
+ DEBUG ((EFI_D_NET, "\nexit Snp->undi.transmit() "));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.StatCode == %r", Snp->Cdb.StatCode));
+
+ //
+ // we will unmap the buffers in get_status call, not here
+ //
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ return EFI_SUCCESS;
+
+ case PXE_STATCODE_BUFFER_FULL:
+ case PXE_STATCODE_QUEUE_FULL:
+ case PXE_STATCODE_BUSY:
+ Status = EFI_NOT_READY;
+ break;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->undi.transmit() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return Status;
+}
+
+/**
+ Places a packet in the transmit queue of a network interface.
+
+ This function places the packet specified by Header and Buffer on the transmit
+ queue. If HeaderSize is nonzero and HeaderSize is not equal to
+ This->Mode->MediaHeaderSize, then EFI_INVALID_PARAMETER will be returned. If
+ BufferSize is less than This->Mode->MediaHeaderSize, then EFI_BUFFER_TOO_SMALL
+ will be returned. If Buffer is NULL, then EFI_INVALID_PARAMETER will be
+ returned. If HeaderSize is nonzero and DestAddr or Protocol is NULL, then
+ EFI_INVALID_PARAMETER will be returned. If the transmit engine of the network
+ interface is busy, then EFI_NOT_READY will be returned. If this packet can be
+ accepted by the transmit engine of the network interface, the packet contents
+ specified by Buffer will be placed on the transmit queue of the network
+ interface, and EFI_SUCCESS will be returned. GetStatus() can be used to
+ determine when the packet has actually been transmitted. The contents of the
+ Buffer must not be modified until the packet has actually been transmitted.
+ The Transmit() function performs nonblocking I/O. A caller who wants to perform
+ blocking I/O, should call Transmit(), and then GetStatus() until the
+ transmitted buffer shows up in the recycled transmit buffer.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param HeaderSize The size, in bytes, of the media header to be filled in by the
+ Transmit() function. If HeaderSize is nonzero, then it must
+ be equal to This->Mode->MediaHeaderSize and the DestAddr and
+ Protocol parameters must not be NULL.
+ @param BufferSize The size, in bytes, of the entire packet (media header and
+ data) to be transmitted through the network interface.
+ @param Buffer A pointer to the packet (media header followed by data) to be
+ transmitted. This parameter cannot be NULL. If HeaderSize is
+ zero, then the media header in Buffer must already be filled
+ in by the caller. If HeaderSize is nonzero, then the media
+ header will be filled in by the Transmit() function.
+ @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this
+ parameter is ignored. If HeaderSize is nonzero and SrcAddr
+ is NULL, then This->Mode->CurrentAddress is used for the
+ source HW MAC address.
+ @param DestAddr The destination HW MAC address. If HeaderSize is zero, then
+ this parameter is ignored.
+ @param Protocol The type of header to build. If HeaderSize is zero, then this
+ parameter is ignored. See RFC 1700, section "Ether Types,"
+ for examples.
+
+ @retval EFI_SUCCESS The packet was placed on the transmit queue.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_NOT_READY The network interface is too busy to accept this
+ transmit request.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported
+ value.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Transmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN HeaderSize,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN EFI_MAC_ADDRESS *SrcAddr, OPTIONAL
+ IN EFI_MAC_ADDRESS *DestAddr, OPTIONAL
+ IN UINT16 *Protocol OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (Buffer == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (BufferSize < Snp->Mode.MediaHeaderSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+
+ //
+ // if the HeaderSize is non-zero, we need to fill up the header and for that
+ // we need the destination address and the protocol
+ //
+ if (HeaderSize != 0) {
+ if (HeaderSize != Snp->Mode.MediaHeaderSize || DestAddr == 0 || Protocol == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = PxeFillHeader (
+ Snp,
+ Buffer,
+ HeaderSize,
+ (UINT8 *) Buffer + HeaderSize,
+ BufferSize - HeaderSize,
+ DestAddr,
+ SrcAddr,
+ Protocol
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = PxeTransmit (Snp, Buffer, BufferSize);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c b/Core/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c
new file mode 100644
index 0000000000..23d7455fd1
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c
@@ -0,0 +1,92 @@
+/** @file
+ Event handler to check for available packet.
+
+Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed
+and made available under the terms and conditions of the BSD License which
+accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Nofication call back function for WaitForPacket event.
+
+ @param Event EFI Event.
+ @param SnpPtr Pointer to SNP_DRIVER structure.
+
+**/
+VOID
+EFIAPI
+SnpWaitForPacketNotify (
+ EFI_EVENT Event,
+ VOID *SnpPtr
+ )
+{
+ PXE_DB_GET_STATUS PxeDbGetStatus;
+
+ //
+ // Do nothing if either parameter is a NULL pointer.
+ //
+ if (Event == NULL || SnpPtr == NULL) {
+ return ;
+ }
+ //
+ // Do nothing if the SNP interface is not initialized.
+ //
+ switch (((SNP_DRIVER *) SnpPtr)->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ case EfiSimpleNetworkStarted:
+ default:
+ return ;
+ }
+ //
+ // Fill in CDB for UNDI GetStatus().
+ //
+ ((SNP_DRIVER *) SnpPtr)->Cdb.OpCode = PXE_OPCODE_GET_STATUS;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.OpFlags = 0;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.DBsize = (UINT16) (sizeof (UINT32) * 2);
+ ((SNP_DRIVER *) SnpPtr)->Cdb.DBaddr = (UINT64)(UINTN) (((SNP_DRIVER *) SnpPtr)->Db);
+ ((SNP_DRIVER *) SnpPtr)->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.IFnum = ((SNP_DRIVER *) SnpPtr)->IfNum;
+ ((SNP_DRIVER *) SnpPtr)->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Clear contents of DB buffer.
+ //
+ ZeroMem (((SNP_DRIVER *) SnpPtr)->Db, sizeof (UINT32) * 2);
+
+ //
+ // Issue UNDI command and check result.
+ //
+ (*((SNP_DRIVER *) SnpPtr)->IssueUndi32Command) ((UINT64)(UINTN) &((SNP_DRIVER *) SnpPtr)->Cdb);
+
+ if (((SNP_DRIVER *) SnpPtr)->Cdb.StatCode != EFI_SUCCESS) {
+ return ;
+ }
+ //
+ // We might have a packet. Check the receive length and signal
+ // the event if the length is not zero.
+ //
+ CopyMem (
+ &PxeDbGetStatus,
+ ((SNP_DRIVER *) SnpPtr)->Db,
+ sizeof (UINT32) * 2
+ );
+
+ if (PxeDbGetStatus.RxFrameLen != 0) {
+ gBS->SignalEvent (Event);
+ }
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..bb3a39c5b1
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c
@@ -0,0 +1,433 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Tcp4Dxe driver.
+
+Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+/**
+ 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
+TcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+TcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+///
+/// EFI Component Name Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName = {
+ TcpComponentNameGetDriverName,
+ TcpComponentNameGetControllerName,
+ "eng"
+};
+
+///
+/// EFI Component Name 2 Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTcp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = {
+ {
+ "eng;en",
+ L"Tcp Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gTcpControllerNameTable = NULL;
+
+/**
+ 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
+TcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mTcpDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gTcp4ComponentName)
+ );
+}
+
+/**
+ Update the component name for the Tcp4 child handle.
+
+ @param Tcp4[in] A pointer to the EFI_TCP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_TCP4_PROTOCOL *Tcp4
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ EFI_TCP4_CONFIG_DATA Tcp4ConfigData;
+
+ if (Tcp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer as:
+ // TCPv4 (SrcPort=59, DestPort=60, ActiveFlag=TRUE)
+ //
+ ZeroMem (&Tcp4ConfigData, sizeof (Tcp4ConfigData));
+ Status = Tcp4->GetModeData (Tcp4, NULL, &Tcp4ConfigData, NULL, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"TCPv4 (SrcPort=%d, DestPort=%d, ActiveFlag=%s)",
+ Tcp4ConfigData.AccessPoint.StationPort,
+ Tcp4ConfigData.AccessPoint.RemotePort,
+ (Tcp4ConfigData.AccessPoint.ActiveFlag ? L"TRUE" : L"FALSE")
+ );
+ } else if (Status == EFI_NOT_STARTED) {
+ UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"TCPv4 (Not started)"
+ );
+ } else {
+ return Status;
+ }
+
+ if (gTcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gTcpControllerNameTable);
+ gTcpControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gTcp4ComponentName.SupportedLanguages,
+ &gTcpControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gTcp4ComponentName2.SupportedLanguages,
+ &gTcpControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+TcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_TCP4_PROTOCOL *Tcp4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiIp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **)&Tcp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Tcp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gTcpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gTcp4ComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c
new file mode 100644
index 0000000000..0476077c20
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c
@@ -0,0 +1,1236 @@
+/** @file
+ Implementation of the Socket.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "SockImpl.h"
+
+/**
+ Get the length of the data that can be retrieved from the socket
+ receive buffer.
+
+ @param SockBuffer Pointer to the socket receive buffer.
+ @param IsUrg Pointer to a BOOLEAN variable. If TRUE the data is
+ OOB.
+ @param BufLen The maximum length of the data buffer to store the
+ received data in socket layer.
+
+ @return The length of the data can be retreived.
+
+**/
+UINT32
+SockTcpDataToRcv (
+ IN SOCK_BUFFER *SockBuffer,
+ OUT BOOLEAN *IsUrg,
+ IN UINT32 BufLen
+ );
+
+/**
+ Process the send token.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockProcessSndToken (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Supporting function for both SockImpl and SockInterface.
+
+ @param Event The Event this notify function registered to, ignored.
+
+**/
+VOID
+EFIAPI
+SockFreeFoo (
+ IN EFI_EVENT Event
+ )
+{
+ return ;
+}
+
+
+/**
+ Get the length of the data that can be retrieved from the socket
+ receive buffer.
+
+ @param SockBuffer Pointer to the socket receive buffer.
+ @param IsUrg Pointer to a BOOLEAN variable. If TRUE the data is
+ OOB.
+ @param BufLen The maximum length of the data buffer to store the
+ received data in socket layer.
+
+ @return The length of the data can be retreived.
+
+**/
+UINT32
+SockTcpDataToRcv (
+ IN SOCK_BUFFER *SockBuffer,
+ OUT BOOLEAN *IsUrg,
+ IN UINT32 BufLen
+ )
+{
+ NET_BUF *RcvBufEntry;
+ UINT32 DataLen;
+ TCP_RSV_DATA *TcpRsvData;
+ BOOLEAN Urg;
+ ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0));
+
+ RcvBufEntry = SockBufFirst (SockBuffer);
+ ASSERT (RcvBufEntry != NULL);
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {
+
+ DataLen = MIN (TcpRsvData->UrgLen, BufLen);
+
+ if (DataLen < TcpRsvData->UrgLen) {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;
+ } else {
+ TcpRsvData->UrgLen = 0;
+ }
+
+ return DataLen;
+
+ }
+
+ DataLen = RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+
+ while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ Urg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg != Urg) {
+ break;
+ }
+
+ if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {
+
+ if (TcpRsvData->UrgLen + DataLen < BufLen) {
+ TcpRsvData->UrgLen = 0;
+ } else {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen);
+ }
+
+ return MIN (TcpRsvData->UrgLen + DataLen, BufLen);
+
+ }
+
+ DataLen += RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+ }
+
+ DataLen = MIN (BufLen, DataLen);
+ return DataLen;
+}
+
+
+/**
+ Copy data from socket buffer to application provided receive buffer.
+
+ @param Sock Pointer to the socket.
+ @param TcpRxData Pointer to the application provided receive buffer.
+ @param RcvdBytes The maximum length of the data can be copied.
+ @param IsOOB If TRUE the data is OOB, FALSE the data is normal.
+
+**/
+VOID
+SockSetTcpRxData (
+ IN SOCKET *Sock,
+ IN VOID *TcpRxData,
+ IN UINT32 RcvdBytes,
+ IN BOOLEAN IsOOB
+ )
+{
+ UINT32 Index;
+ UINT32 CopyBytes;
+ UINT32 OffSet;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ EFI_TCP4_FRAGMENT_DATA *Fragment;
+
+ RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData;
+
+ OffSet = 0;
+
+ ASSERT (RxData->DataLength >= RcvdBytes);
+
+ RxData->DataLength = RcvdBytes;
+ RxData->UrgentFlag = IsOOB;
+
+ for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {
+
+ Fragment = &RxData->FragmentTable[Index];
+ CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes);
+
+ NetbufQueCopy (
+ Sock->RcvBuffer.DataQueue,
+ OffSet,
+ CopyBytes,
+ Fragment->FragmentBuffer
+ );
+
+ Fragment->FragmentLength = CopyBytes;
+ RcvdBytes -= CopyBytes;
+ OffSet += CopyBytes;
+ }
+}
+
+
+/**
+ Get received data from the socket layer to the receive token.
+
+ @param Sock Pointer to the socket.
+ @param RcvToken Pointer to the application provided receive token.
+
+ @return The length of data received in this token.
+
+**/
+UINT32
+SockProcessRcvToken (
+ IN SOCKET *Sock,
+ IN OUT SOCK_IO_TOKEN *RcvToken
+ )
+{
+ UINT32 TokenRcvdBytes;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ BOOLEAN IsUrg;
+
+ ASSERT (Sock != NULL);
+
+ ASSERT (SockStream == Sock->Type);
+
+ RxData = RcvToken->Packet.RxData;
+
+ TokenRcvdBytes = SockTcpDataToRcv (
+ &Sock->RcvBuffer,
+ &IsUrg,
+ (UINT32) RxData->DataLength
+ );
+
+ //
+ // Copy data from RcvBuffer of socket to user
+ // provided RxData and set the fields in TCP RxData
+ //
+ SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg);
+
+ NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes);
+ SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS);
+
+ return TokenRcvdBytes;
+}
+
+
+/**
+ Process the TCP send data, buffer the tcp txdata and append
+ the buffer to socket send buffer,then try to send it.
+
+ @param Sock Pointer to the socket.
+ @param TcpTxData Pointer to the application provided send buffer.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+EFI_STATUS
+SockProcessTcpSndData (
+ IN SOCKET *Sock,
+ IN VOID *TcpTxData
+ )
+{
+ NET_BUF *SndData;
+ EFI_STATUS Status;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData;
+
+ //
+ // transform this TxData into a NET_BUFFER
+ // and insert it into Sock->SndBuffer
+ //
+ SndData = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ (UINT32) TxData->FragmentCount,
+ 0,
+ 0,
+ SockFreeFoo,
+ NULL
+ );
+
+ if (NULL == SndData) {
+ DEBUG ((EFI_D_ERROR, "SockKProcessSndData: Failed to"
+ " call NetBufferFromExt\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData);
+
+ //
+ // notify the low layer protocol to handle this send token
+ //
+ if (TxData->Urgent) {
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (TxData->Push) {
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // low layer protocol should really handle the sending
+ // process when catching SOCK_SND request
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Flush the tokens in the specific token list.
+
+ @param Sock Pointer to the socket.
+ @param PendingTokenList Pointer to the token list to be flushed.
+
+**/
+VOID
+SockFlushPendingToken (
+ IN SOCKET *Sock,
+ IN LIST_ENTRY *PendingTokenList
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *Token;
+
+ ASSERT ((Sock != NULL) && (PendingTokenList != NULL));
+
+ while (!IsListEmpty (PendingTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ PendingTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ Token = SockToken->Token;
+ SIGNAL_TOKEN (Token, Sock->SockError);
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ }
+}
+
+
+/**
+ Wake up the connection token while the connection is successfully established,
+ then try to process any pending send token.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeConnToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ ASSERT (Sock->ConnectionToken != NULL);
+
+ SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS);
+ Sock->ConnectionToken = NULL;
+
+ //
+ // check to see if some pending send token existed?
+ //
+ SockProcessSndToken (Sock);
+ return ;
+}
+
+
+/**
+ Wake up the listen token while the connection is established successfully.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeListenToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ SOCKET *Parent;
+ SOCK_TOKEN *SockToken;
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+
+ Parent = Sock->Parent;
+
+ ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));
+
+ if (!IsListEmpty (&Parent->ListenTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ &Parent->ListenTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token;
+ ListenToken->NewChildHandle = Sock->SockHandle;
+
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);
+
+ RemoveEntryList (&SockToken->TokenList);
+ FreePool (SockToken);
+
+ RemoveEntryList (&Sock->ConnectionList);
+
+ Parent->ConnCnt--;
+ DEBUG ((EFI_D_NET, "SockWakeListenToken: accept a socket, now conncnt is %d", Parent->ConnCnt));
+
+ Sock->Parent = NULL;
+ }
+}
+
+
+/**
+ Wake up the receive token while some data is received.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeRcvToken (
+ IN SOCKET *Sock
+ )
+{
+ UINT32 RcvdBytes;
+ UINT32 TokenRcvdBytes;
+ SOCK_TOKEN *SockToken;
+ SOCK_IO_TOKEN *RcvToken;
+
+ ASSERT (Sock->RcvBuffer.DataQueue != NULL);
+
+ RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;
+
+ ASSERT (RcvdBytes > 0);
+
+ while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &Sock->RcvTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ RcvToken = (SOCK_IO_TOKEN *) SockToken->Token;
+ TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken);
+
+ if (0 == TokenRcvdBytes) {
+ return ;
+ }
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ RcvdBytes -= TokenRcvdBytes;
+ }
+}
+
+
+/**
+ Process the send token.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockProcessSndToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ UINT32 FreeSpace;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+ SOCK_IO_TOKEN *SndToken;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+
+ ASSERT ((Sock != NULL) && (SockStream == Sock->Type));
+
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);
+
+ //
+ // to determine if process a send token using
+ // socket layer flow control policy
+ //
+ while ((FreeSpace >= Sock->SndBuffer.LowWater) &&
+ !IsListEmpty (&Sock->SndTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &(Sock->SndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ //
+ // process this token
+ //
+ RemoveEntryList (&(SockToken->TokenList));
+ InsertTailList (
+ &(Sock->ProcessingSndTokenList),
+ &(SockToken->TokenList)
+ );
+
+ //
+ // Proceess it in the light of SockType
+ //
+ SndToken = (SOCK_IO_TOKEN *) SockToken->Token;
+ TxData = SndToken->Packet.TxData;
+
+ DataLen = (UINT32) TxData->DataLength;
+ Status = SockProcessTcpSndData (Sock, TxData);
+
+ if (EFI_ERROR (Status)) {
+ goto OnError;
+ }
+
+ if (DataLen >= FreeSpace) {
+ FreeSpace = 0;
+
+ } else {
+ FreeSpace -= DataLen;
+
+ }
+ }
+
+ return ;
+
+OnError:
+
+ RemoveEntryList (&SockToken->TokenList);
+ SIGNAL_TOKEN (SockToken->Token, Status);
+ FreePool (SockToken);
+}
+
+
+/**
+ Create a socket with initial data SockInitData.
+
+ @param SockInitData Pointer to the initial data of the socket.
+
+ @return Pointer to the newly created socket, return NULL when exception occured.
+
+**/
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ SOCKET *Parent;
+ EFI_STATUS Status;
+
+ ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL));
+ ASSERT (SockInitData->Type == SockStream);
+ ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN));
+
+ Parent = SockInitData->Parent;
+
+ if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: Socket parent has "
+ "reached its connection limit with %d ConnCnt and %d BackLog\n",
+ Parent->ConnCnt,
+ Parent->BackLog)
+ );
+
+ return NULL;
+ }
+
+ Sock = AllocateZeroPool (sizeof (SOCKET));
+ if (NULL == Sock) {
+
+ DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n"));
+ return NULL;
+ }
+
+ InitializeListHead (&Sock->Link);
+ InitializeListHead (&Sock->ConnectionList);
+ InitializeListHead (&Sock->ListenTokenList);
+ InitializeListHead (&Sock->RcvTokenList);
+ InitializeListHead (&Sock->SndTokenList);
+ InitializeListHead (&Sock->ProcessingSndTokenList);
+
+ EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK);
+
+ Sock->SndBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->SndBuffer.DataQueue) {
+ DEBUG ((EFI_D_ERROR, "SockCreate: No resource to allocate"
+ " SndBuffer for new socket\n"));
+
+ goto OnError;
+ }
+
+ Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->RcvBuffer.DataQueue) {
+ DEBUG ((EFI_D_ERROR, "SockCreate: No resource to allocate "
+ "RcvBuffer for new socket\n"));
+
+ goto OnError;
+ }
+
+ Sock->Signature = SOCK_SIGNATURE;
+
+ Sock->Parent = Parent;
+ Sock->BackLog = SockInitData->BackLog;
+ Sock->ProtoHandler = SockInitData->ProtoHandler;
+ Sock->SndBuffer.HighWater = SockInitData->SndBufferSize;
+ Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize;
+ Sock->Type = SockInitData->Type;
+ Sock->DriverBinding = SockInitData->DriverBinding;
+ Sock->State = SockInitData->State;
+ Sock->CreateCallback = SockInitData->CreateCallback;
+ Sock->DestroyCallback = SockInitData->DestroyCallback;
+ Sock->Context = SockInitData->Context;
+
+ Sock->SockError = EFI_ABORTED;
+ Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+ Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+
+ //
+ // Install protocol on Sock->SockHandle
+ //
+ CopyMem (
+ &(Sock->NetProtocol.TcpProtocol),
+ SockInitData->Protocol,
+ sizeof (EFI_TCP4_PROTOCOL)
+ );
+
+ //
+ // copy the protodata into socket
+ //
+ CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ &(Sock->NetProtocol.TcpProtocol),
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "SockCreate: Install TCP protocol in "
+ "socket failed with %r\n", Status));
+
+ goto OnError;
+ }
+
+ if (Parent != NULL) {
+ ASSERT (Parent->BackLog > 0);
+ ASSERT (SOCK_IS_LISTENING (Parent));
+
+ //
+ // need to add it into Parent->ConnectionList
+ // if the Parent->ConnCnt < Parent->BackLog
+ //
+ Parent->ConnCnt++;
+
+ DEBUG (
+ (EFI_D_NET,
+ "SockCreate: Create a new socket and add to parent, now conncnt is %d\n",
+ Parent->ConnCnt)
+ );
+
+ InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList);
+ }
+
+ if (Sock->CreateCallback != NULL) {
+ Status = Sock->CreateCallback (Sock, Sock->Context);
+ if (EFI_ERROR (Status)) {
+ goto OnError;
+ }
+ }
+
+ return Sock;
+
+OnError:
+
+ if (Sock->SockHandle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ &(Sock->NetProtocol.TcpProtocol),
+ NULL
+ );
+ }
+
+ if (NULL != Sock->SndBuffer.DataQueue) {
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+ }
+
+ if (NULL != Sock->RcvBuffer.DataQueue) {
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ }
+
+ FreePool (Sock);
+
+ return NULL;
+}
+
+
+/**
+ Destroy a socket.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockDestroy (
+ IN OUT SOCKET *Sock
+ )
+{
+ ASSERT (SockStream == Sock->Type);
+
+ //
+ // Flush the completion token buffered
+ // by sock and rcv, snd buffer
+ //
+ if (!SOCK_IS_UNCONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+ }
+ //
+ // Destroy the RcvBuffer Queue and SendBuffer Queue
+ //
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+
+ //
+ // Remove it from parent connection list if needed
+ //
+ if (Sock->Parent != NULL) {
+
+ RemoveEntryList (&(Sock->ConnectionList));
+ (Sock->Parent->ConnCnt)--;
+
+ DEBUG (
+ (EFI_D_NET,
+ "SockDestroy: Delete a unaccepted socket from parent"
+ "now conncnt is %d\n",
+ Sock->Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+
+ FreePool (Sock);
+ return ;
+}
+
+
+/**
+ Flush the sndBuffer and rcvBuffer of socket.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockConnFlush (
+ IN OUT SOCKET *Sock
+ )
+{
+ SOCKET *Child;
+
+ ASSERT (Sock != NULL);
+
+ //
+ // Clear the flag in this socket
+ //
+ Sock->Flag = 0;
+
+ //
+ // Flush the SndBuffer and RcvBuffer of Sock
+ //
+ NetbufQueFlush (Sock->SndBuffer.DataQueue);
+ NetbufQueFlush (Sock->RcvBuffer.DataQueue);
+
+ //
+ // Signal the pending token
+ //
+ if (Sock->ConnectionToken != NULL) {
+ SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError);
+ Sock->ConnectionToken = NULL;
+ }
+
+ if (Sock->CloseToken != NULL) {
+ SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError);
+ Sock->CloseToken = NULL;
+ }
+
+ SockFlushPendingToken (Sock, &(Sock->ListenTokenList));
+ SockFlushPendingToken (Sock, &(Sock->RcvTokenList));
+ SockFlushPendingToken (Sock, &(Sock->SndTokenList));
+ SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList));
+
+ //
+ // Destroy the pending connection, if it is a listening socket
+ //
+ if (SOCK_IS_LISTENING (Sock)) {
+ while (!IsListEmpty (&Sock->ConnectionList)) {
+ Child = NET_LIST_HEAD (
+ &Sock->ConnectionList,
+ SOCKET,
+ ConnectionList
+ );
+
+ SockDestroyChild (Child);
+ }
+
+ Sock->ConnCnt = 0;
+ }
+
+ return ;
+}
+
+
+/**
+ Set the state of the socket.
+
+ @param Sock Pointer to the socket.
+ @param State The new socket state to be set.
+
+**/
+VOID
+SockSetState (
+ IN OUT SOCKET *Sock,
+ IN UINT8 State
+ )
+{
+ Sock->State = State;
+}
+
+
+/**
+ Clone a new socket including its associated protocol control block.
+
+ @param Sock Pointer to the socket to be cloned.
+
+ @return Pointer to the newly cloned socket. If NULL, error condition occurred.
+
+**/
+SOCKET *
+SockClone (
+ IN SOCKET *Sock
+ )
+{
+ SOCKET *ClonedSock;
+ SOCK_INIT_DATA InitData;
+
+ InitData.BackLog = Sock->BackLog;
+ InitData.Parent = Sock;
+ InitData.State = Sock->State;
+ InitData.ProtoHandler = Sock->ProtoHandler;
+ InitData.Type = Sock->Type;
+ InitData.RcvBufferSize = Sock->RcvBuffer.HighWater;
+ InitData.SndBufferSize = Sock->SndBuffer.HighWater;
+ InitData.DriverBinding = Sock->DriverBinding;
+ InitData.Protocol = &(Sock->NetProtocol);
+ InitData.CreateCallback = Sock->CreateCallback;
+ InitData.DestroyCallback = Sock->DestroyCallback;
+ InitData.Context = Sock->Context;
+ InitData.ProtoData = Sock->ProtoReserved;
+ InitData.DataSize = sizeof (Sock->ProtoReserved);
+
+ ClonedSock = SockCreate (&InitData);
+
+ if (NULL == ClonedSock) {
+ DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n"));
+ return NULL;
+ }
+
+ SockSetState (ClonedSock, SO_CONNECTING);
+ ClonedSock->ConfigureState = Sock->ConfigureState;
+
+ return ClonedSock;
+}
+
+
+/**
+ Called by the low layer protocol to indicate the socket a connection is
+ established.
+
+ This function just changes the socket's state to SO_CONNECTED
+ and signals the token used for connection establishment.
+
+ @param Sock Pointer to the socket associated with the
+ established connection.
+**/
+VOID
+SockConnEstablished (
+ IN SOCKET *Sock
+ )
+{
+
+ ASSERT (SO_CONNECTING == Sock->State);
+
+ SockSetState (Sock, SO_CONNECTED);
+
+ if (NULL == Sock->Parent) {
+ SockWakeConnToken (Sock);
+ } else {
+ SockWakeListenToken (Sock);
+ }
+
+ return ;
+}
+
+
+/**
+ Called by the low layer protocol to indicate the connection is closed.
+
+ This function flushes the socket, sets the state to SO_CLOSED and signals
+ the close token.
+
+ @param Sock Pointer to the socket associated with the closed
+ connection.
+
+**/
+VOID
+SockConnClosed (
+ IN OUT SOCKET *Sock
+ )
+{
+ if (Sock->CloseToken != NULL) {
+ SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS);
+ Sock->CloseToken = NULL;
+ }
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ if (Sock->Parent != NULL) {
+ SockDestroyChild (Sock);
+ }
+
+}
+
+
+/**
+ Called by low layer protocol to indicate that some data is sent or processed.
+
+ This function trims the sent data in the socket send buffer, signals the data
+ token if proper.
+
+ @param Sock Pointer to the socket.
+ @param Count The length of the data processed or sent, in bytes.
+
+**/
+VOID
+SockDataSent (
+ IN SOCKET *Sock,
+ IN UINT32 Count
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *SndToken;
+
+ ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList));
+ ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize);
+
+ NetbufQueTrim (Sock->SndBuffer.DataQueue, Count);
+
+ //
+ // To check if we can signal some snd token in this socket
+ //
+ while (Count > 0) {
+ SockToken = NET_LIST_HEAD (
+ &(Sock->ProcessingSndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ SndToken = SockToken->Token;
+
+ if (SockToken->RemainDataLen <= Count) {
+
+ RemoveEntryList (&(SockToken->TokenList));
+ SIGNAL_TOKEN (SndToken, EFI_SUCCESS);
+ Count -= SockToken->RemainDataLen;
+ FreePool (SockToken);
+ } else {
+
+ SockToken->RemainDataLen -= Count;
+ Count = 0;
+ }
+ }
+
+ //
+ // to judge if we can process some send token in
+ // Sock->SndTokenList, if so process those send token
+ //
+ SockProcessSndToken (Sock);
+ return ;
+}
+
+
+/**
+ Called by the low layer protocol to copy some data in socket send
+ buffer starting from the specific offset to a buffer provided by
+ the caller.
+
+ @param Sock Pointer to the socket.
+ @param Offset The start point of the data to be copied.
+ @param Len The length of the data to be copied.
+ @param Dest Pointer to the destination to copy the data.
+
+ @return The data size copied.
+
+**/
+UINT32
+SockGetDataToSend (
+ IN SOCKET *Sock,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT8 *Dest
+ )
+{
+ ASSERT ((Sock != NULL) && SockStream == Sock->Type);
+
+ return NetbufQueCopy (
+ Sock->SndBuffer.DataQueue,
+ Offset,
+ Len,
+ Dest
+ );
+}
+
+
+/**
+ Called by the low layer protocol to deliver received data to socket layer.
+
+ This function will append the data to the socket receive buffer, set ther
+ urgent data length and then check if any receive token can be signaled.
+
+ @param Sock Pointer to the socket.
+ @param NetBuffer Pointer to the buffer that contains the received
+ data.
+ @param UrgLen The length of the urgent data in the received data.
+
+**/
+VOID
+SockDataRcvd (
+ IN SOCKET *Sock,
+ IN OUT NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ )
+{
+ ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) &&
+ UrgLen <= NetBuffer->TotalSize);
+
+ NET_GET_REF (NetBuffer);
+
+ ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;
+
+ NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);
+
+ SockWakeRcvToken (Sock);
+ return ;
+}
+
+
+/**
+ Get the length of the free space of the specific socket buffer.
+
+ @param Sock Pointer to the socket.
+ @param Which Flag to indicate which socket buffer to check,
+ either send buffer or receive buffer.
+
+ @return The length of the free space, in bytes.
+
+**/
+UINT32
+SockGetFreeSpace (
+ IN SOCKET *Sock,
+ IN UINT32 Which
+ )
+{
+ UINT32 BufferCC;
+ SOCK_BUFFER *SockBuffer;
+
+ ASSERT ((Sock != NULL) && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which)));
+
+ if (SOCK_SND_BUF == Which) {
+ SockBuffer = &(Sock->SndBuffer);
+ } else {
+ SockBuffer = &(Sock->RcvBuffer);
+ }
+
+ BufferCC = (SockBuffer->DataQueue)->BufSize;
+
+ if (BufferCC >= SockBuffer->HighWater) {
+
+ return 0;
+ }
+
+ return SockBuffer->HighWater - BufferCC;
+}
+
+
+/**
+ Signal the receive token with the specific error or
+ set socket error code after error is received.
+
+ @param Sock Pointer to the socket.
+ @param Error The error code received.
+
+**/
+VOID
+SockRcvdErr (
+ IN OUT SOCKET *Sock,
+ IN EFI_STATUS Error
+ )
+{
+ SOCK_TOKEN *SockToken;
+
+ if (!IsListEmpty (&Sock->RcvTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &Sock->RcvTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ RemoveEntryList (&SockToken->TokenList);
+
+ SIGNAL_TOKEN (SockToken->Token, Error);
+
+ FreePool (SockToken);
+ } else {
+
+ SOCK_ERROR (Sock, Error);
+ }
+}
+
+
+/**
+ Called by the low layer protocol to indicate that there will be no more data
+ from the communication peer.
+
+ This function set the socket's state to SO_NO_MORE_DATA and signal all queued
+ IO tokens with the error status EFI_CONNECTION_FIN.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockNoMoreData (
+ IN OUT SOCKET *Sock
+ )
+{
+ EFI_STATUS Err;
+
+ SOCK_NO_MORE_DATA (Sock);
+
+ if (!IsListEmpty (&Sock->RcvTokenList)) {
+
+ ASSERT (0 == GET_RCV_DATASIZE (Sock));
+
+ Err = Sock->SockError;
+
+ SOCK_ERROR (Sock, EFI_CONNECTION_FIN);
+
+ SockFlushPendingToken (Sock, &Sock->RcvTokenList);
+
+ SOCK_ERROR (Sock, Err);
+
+ }
+
+}
+
+
+/**
+ Get the first buffer block in the specific socket buffer.
+
+ @param Sockbuf Pointer to the socket buffer.
+
+ @return Pointer to the first buffer in the queue. NULL if the queue is empty.
+
+**/
+NET_BUF *
+SockBufFirst (
+ IN SOCK_BUFFER *Sockbuf
+ )
+{
+ LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if (IsListEmpty (NetbufList)) {
+ return NULL;
+ }
+
+ return NET_LIST_HEAD (NetbufList, NET_BUF, List);
+}
+
+
+/**
+ Get the next buffer block in the specific socket buffer.
+
+ @param Sockbuf Pointer to the socket buffer.
+ @param SockEntry Pointer to the buffer block prior to the required
+ one.
+
+ @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is
+ the tail or head entry.
+
+**/
+NET_BUF *
+SockBufNext (
+ IN SOCK_BUFFER *Sockbuf,
+ IN NET_BUF *SockEntry
+ )
+{
+ LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if ((SockEntry->List.ForwardLink == NetbufList) ||
+ (SockEntry->List.BackLink == &SockEntry->List) ||
+ (SockEntry->List.ForwardLink == &SockEntry->List)) {
+
+ return NULL;
+ }
+
+ return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List);
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h
new file mode 100644
index 0000000000..bfd750a8ce
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h
@@ -0,0 +1,131 @@
+/** @file
+ Socket implementation header file.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SOCK_IMPL_H_
+#define _SOCK_IMPL_H_
+
+#include "Socket.h"
+#include "Tcp4Main.h"
+
+/**
+ Signal a event with the given status.
+
+ @param Token The token's event is to be signaled.
+ @param TokenStatus The status to be sent with the event.
+
+**/
+#define SIGNAL_TOKEN(Token, TokenStatus) \
+ do { \
+ (Token)->Status = (TokenStatus); \
+ gBS->SignalEvent ((Token)->Event); \
+ } while (0)
+
+
+/**
+ Supporting function for both SockImpl and SockInterface.
+
+ @param Event The Event this notify function registered to, ignored.
+
+**/
+VOID
+EFIAPI
+SockFreeFoo (
+ IN EFI_EVENT Event
+ );
+
+/**
+ Process the TCP send data, buffer the tcp txdata and append
+ the buffer to socket send buffer,then try to send it.
+
+ @param Sock Pointer to the socket.
+ @param TcpTxData Pointer to the tcp txdata.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+EFI_STATUS
+SockProcessTcpSndData (
+ IN SOCKET *Sock,
+ IN VOID *TcpTxData
+ );
+
+/**
+ Copy data from socket buffer to application provided receive buffer.
+
+ @param Sock Pointer to the socket.
+ @param TcpRxData Pointer to the application provided receive buffer.
+ @param RcvdBytes The maximum length of the data can be copied.
+ @param IsOOB If TRUE the data is OOB, else the data is normal.
+
+**/
+VOID
+SockSetTcpRxData (
+ IN SOCKET *Sock,
+ IN VOID *TcpRxData,
+ IN UINT32 RcvdBytes,
+ IN BOOLEAN IsOOB
+ );
+
+/**
+ Get received data from the socket layer to the receive token.
+
+ @param Sock Pointer to the socket.
+ @param RcvToken Pointer to the application provided receive token.
+
+ @return The length of data received in this token.
+
+**/
+UINT32
+SockProcessRcvToken (
+ IN SOCKET *Sock,
+ IN OUT SOCK_IO_TOKEN *RcvToken
+ );
+
+/**
+ Flush the socket.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockConnFlush (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Create a socket with initial data SockInitData.
+
+ @param SockInitData Pointer to the initial data of the socket.
+
+ @return Pointer to the newly created socket.
+
+**/
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ );
+
+/**
+ Destroy a socket.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockDestroy (
+ IN OUT SOCKET *Sock
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c
new file mode 100644
index 0000000000..f8b535cf6e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c
@@ -0,0 +1,1031 @@
+/** @file
+ Interface function of the Socket.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "SockImpl.h"
+
+
+/**
+ Check whether the Event is in the List.
+
+ @param List Pointer to the token list to be searched.
+ @param Event The event to be checked.
+
+ @retval TRUE The specific Event exists in the List.
+ @retval FALSE The specific Event is not in the List.
+
+**/
+BOOLEAN
+SockTokenExistedInList (
+ IN LIST_ENTRY *List,
+ IN EFI_EVENT Event
+ )
+{
+ LIST_ENTRY *ListEntry;
+ SOCK_TOKEN *SockToken;
+
+ NET_LIST_FOR_EACH (ListEntry, List) {
+ SockToken = NET_LIST_USER_STRUCT (
+ ListEntry,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ if (Event == SockToken->Token->Event) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Call SockTokenExistedInList() to check whether the Event is
+ in the related socket's lists.
+
+ @param Sock Pointer to the instance's socket.
+ @param Event The event to be checked.
+
+ @retval TRUE The Event exists in related socket's lists.
+ @retval FALSE The Event is not in related socket's lists.
+
+**/
+BOOLEAN
+SockTokenExisted (
+ IN SOCKET *Sock,
+ IN EFI_EVENT Event
+ )
+{
+
+ if (SockTokenExistedInList (&Sock->SndTokenList, Event) ||
+ SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) ||
+ SockTokenExistedInList (&Sock->RcvTokenList, Event) ||
+ SockTokenExistedInList (&Sock->ListenTokenList, Event)) {
+
+ return TRUE;
+ }
+
+ if ((Sock->ConnectionToken != NULL) &&
+ (Sock->ConnectionToken->Event == Event)) {
+
+ return TRUE;
+ }
+
+ if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Buffer a token into the specific list of socket Sock.
+
+ @param Sock Pointer to the instance's socket.
+ @param List Pointer to the list to store the token.
+ @param Token Pointer to the token to be buffered.
+ @param DataLen The data length of the buffer contained in Token.
+
+ @return Pointer to the token that wraps Token. If NULL, error condition occurred.
+
+**/
+SOCK_TOKEN *
+SockBufferToken (
+ IN SOCKET *Sock,
+ IN LIST_ENTRY *List,
+ IN VOID *Token,
+ IN UINT32 DataLen
+ )
+{
+ SOCK_TOKEN *SockToken;
+
+ SockToken = AllocatePool (sizeof (SOCK_TOKEN));
+ if (NULL == SockToken) {
+
+ DEBUG ((EFI_D_ERROR, "SockBufferIOToken: No Memory "
+ "to allocate SockToken\n"));
+
+ return NULL;
+ }
+
+ SockToken->Sock = Sock;
+ SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token;
+ SockToken->RemainDataLen = DataLen;
+ InsertTailList (List, &SockToken->TokenList);
+
+ return SockToken;
+}
+
+
+/**
+ Destroy the socket Sock and its associated protocol control block.
+
+ @param Sock The socket to be destroyed.
+
+ @retval EFI_SUCCESS The socket Sock is destroyed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockDestroyChild (
+ IN SOCKET *Sock
+ )
+{
+ EFI_STATUS Status;
+ TCP4_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+ VOID *SockProtocol;
+
+ ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));
+
+ if (Sock->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ Sock->InDestroy = TRUE;
+
+ ProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockDestroyChild: Get the lock to "
+ "access socket failed with %r\n", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Close the IP protocol.
+ //
+ gBS->CloseProtocol (
+ Tcb->IpInfo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ ProtoData->TcpService->IpIo->Image,
+ Sock->SockHandle
+ );
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ //
+ // Retrieve the protocol installed on this sock
+ //
+ Status = gBS->OpenProtocol (
+ Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockDestroyChild: Open protocol installed "
+ "on socket failed with %r\n", Status));
+ }
+
+ //
+ // Uninstall the protocol installed on this sock
+ // in the light of Sock->SockType
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+
+ //
+ // force protocol layer to detach the PCB
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL);
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockDestroyChild: Protocol detach socket"
+ " failed with %r\n", Status));
+
+ Sock->InDestroy = FALSE;
+ } else if (SOCK_IS_CONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ Sock->ConfigureState = SO_UNCONFIGURED;
+ }
+
+ EfiReleaseLock (&(Sock->Lock));
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SockDestroy (Sock);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create a socket and its associated protocol control block
+ with the intial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param SockInitData Inital data to setting the socket.
+
+ @return Pointer to the newly created socket. If NULL, error condition occured.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ VOID *SockProtocol;
+ EFI_STATUS Status;
+
+ //
+ // create a new socket
+ //
+ Sock = SockCreate (SockInitData);
+ if (NULL == Sock) {
+
+ DEBUG ((EFI_D_ERROR, "SockCreateChild: No resource to "
+ "create a new socket\n"));
+
+ return NULL;
+ }
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockCreateChild: Get the lock to "
+ "access socket failed with %r\n", Status));
+
+ goto ERROR;
+ }
+ //
+ // inform the protocol layer to attach the socket
+ // with a new protocol control block
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);
+ EfiReleaseLock (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockCreateChild: Protocol failed to"
+ " attach a socket with %r\n", Status));
+
+ goto ERROR;
+ }
+
+ return Sock;
+
+ERROR:
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ gBS->OpenProtocol (
+ Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ //
+ // Uninstall the protocol installed on this sock
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+ SockDestroy (Sock);
+ return NULL;
+}
+
+
+/**
+ Configure the specific socket Sock using configuration data ConfigData.
+
+ @param Sock Pointer to the socket to be configured.
+ @param ConfigData Pointer to the configuration data.
+
+ @retval EFI_SUCCESS The socket is configured successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket or the
+ socket is already configured.
+
+**/
+EFI_STATUS
+SockConfigure (
+ IN SOCKET *Sock,
+ IN VOID *ConfigData
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockConfigure: Get the access for "
+ "socket failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ ASSERT (Sock->State == SO_CLOSED);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData);
+
+OnExit:
+ EfiReleaseLock (&(Sock->Lock));
+
+ return Status;
+}
+
+
+/**
+ Initiate a connection establishment process.
+
+ @param Sock Pointer to the socket to initiate the initate the
+ connection.
+ @param Token Pointer to the token used for the connection
+ operation.
+
+ @retval EFI_SUCCESS The connection is initialized successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be an active one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockConnect (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockConnect: Get the access for "
+ "socket failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto OnExit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto OnExit;
+ }
+
+ if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token;
+ SockSetState (Sock, SO_CONNECTING);
+ Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL);
+
+OnExit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Issue a listen token to get an existed connected network instance
+ or wait for a connection if there is none.
+
+ @param Sock Pointer to the socket to accept connections.
+ @param Token The token to accept a connection.
+
+ @retval EFI_SUCCESS Either a connection is accpeted or the Token is
+ buffered for further acception.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be a passive one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit.
+
+**/
+EFI_STATUS
+SockAccept (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+ LIST_ENTRY *ListEntry;
+ EFI_STATUS Status;
+ SOCKET *Socket;
+ EFI_EVENT Event;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockAccept: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!SOCK_IS_LISTENING (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token;
+
+ //
+ // Check if a connection has already in this Sock->ConnectionList
+ //
+ NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) {
+
+ Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList);
+
+ if (SOCK_IS_CONNECTED (Socket)) {
+ ListenToken->NewChildHandle = Socket->SockHandle;
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);
+
+ RemoveEntryList (ListEntry);
+
+ ASSERT (Socket->Parent != NULL);
+
+ Socket->Parent->ConnCnt--;
+
+ DEBUG (
+ (EFI_D_NET,
+ "SockAccept: Accept a socket, now conncount is %d",
+ Socket->Parent->ConnCnt)
+ );
+ Socket->Parent = NULL;
+
+ goto Exit;
+ }
+ }
+
+ //
+ // Buffer this token for latter incoming connection request
+ //
+ if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) {
+
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+
+ return Status;
+}
+
+
+/**
+ Issue a token with data to the socket to send out.
+
+ @param Sock Pointer to the socket to process the token with
+ data.
+ @param Token The token with data that needs to send out.
+
+ @retval EFI_SUCCESS The token is processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockSend (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ SOCK_IO_TOKEN *SndToken;
+ EFI_EVENT Event;
+ UINT32 FreeSpace;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockSend: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ SndToken = (SOCK_IO_TOKEN *) Token;
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData;
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ //
+ // check if a token is already in the token buffer
+ //
+ Event = SndToken->Token.Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ DataLen = (UINT32) TxData->DataLength;
+
+ //
+ // process this sending token now or buffer it only?
+ //
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);
+
+ if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) {
+
+ SockToken = SockBufferToken (
+ Sock,
+ &Sock->SndTokenList,
+ SndToken,
+ DataLen
+ );
+
+ if (NULL == SockToken) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+
+ SockToken = SockBufferToken (
+ Sock,
+ &Sock->ProcessingSndTokenList,
+ SndToken,
+ DataLen
+ );
+
+ if (NULL == SockToken) {
+ DEBUG ((EFI_D_ERROR, "SockSend: Failed to buffer IO token into"
+ " socket processing SndToken List\n", Status));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ Status = SockProcessTcpSndData (Sock, TxData);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "SockSend: Failed to process "
+ "Snd Data\n", Status));
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ }
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Issue a token to get data from the socket.
+
+ @param Sock Pointer to the socket to get data from.
+ @param Token The token to store the received data from the
+ socket.
+
+ @retval EFI_SUCCESS The token is processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockRcv (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ SOCK_IO_TOKEN *RcvToken;
+ UINT32 RcvdBytes;
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockRcv: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ RcvToken = (SOCK_IO_TOKEN *) Token;
+
+ //
+ // check if a token is already in the token buffer of this socket
+ //
+ Event = RcvToken->Token.Event;
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ RcvToken = (SOCK_IO_TOKEN *) Token;
+ RcvdBytes = GET_RCV_DATASIZE (Sock);
+
+ //
+ // check whether an error has happened before
+ //
+ if (EFI_ABORTED != Sock->SockError) {
+
+ SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError);
+ Sock->SockError = EFI_ABORTED;
+ goto Exit;
+ }
+
+ //
+ // check whether can not receive and there is no any
+ // data buffered in Sock->RcvBuffer
+ //
+ if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) {
+
+ Status = EFI_CONNECTION_FIN;
+ goto Exit;
+ }
+
+ if (RcvdBytes != 0) {
+ SockProcessRcvToken (Sock, RcvToken);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL);
+ } else {
+
+ if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Reset the socket and its associated protocol control block.
+
+ @param Sock Pointer to the socket to be flushed.
+
+ @retval EFI_SUCCESS The socket is flushed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockFlush (
+ IN SOCKET *Sock
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockFlush: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (!SOCK_IS_CONFIGURED (Sock)) {
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL);
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockFlush: Protocol failed handling"
+ " SOCK_FLUSH with %r", Status));
+
+ goto Exit;
+ }
+
+ SOCK_ERROR (Sock, EFI_ABORTED);
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Close or abort the socket associated connection.
+
+ @param Sock Pointer to the socket of the connection to close or
+ abort.
+ @param Token The token for close operation.
+ @param OnAbort TRUE for aborting the connection, FALSE to close it.
+
+ @retval EFI_SUCCESS The close or abort operation is initialized
+ successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockClose (
+ IN SOCKET *Sock,
+ IN VOID *Token,
+ IN BOOLEAN OnAbort
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "SockClose: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (SOCK_IS_DISCONNECTING (Sock)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Sock->CloseToken = Token;
+ SockSetState (Sock, SO_DISCONNECTING);
+
+ if (OnAbort) {
+ Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL);
+ } else {
+ Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL);
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Get the mode data of the low layer protocol.
+
+ @param Sock Pointer to the socket to get mode data from.
+ @param Mode Pointer to the data to store the low layer mode
+ information.
+
+ @retval EFI_SUCCESS The mode data is got successfully.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGetMode (
+ IN SOCKET *Sock,
+ IN OUT VOID *Mode
+ )
+{
+ return Sock->ProtoHandler (Sock, SOCK_MODE, Mode);
+}
+
+
+/**
+ Configure the low level protocol to join a multicast group for
+ this socket's connection.
+
+ @param Sock Pointer to the socket of the connection to join the
+ specific multicast group.
+ @param GroupInfo Pointer to the multicast group info.
+
+ @retval EFI_SUCCESS The configuration is done successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGroup (
+ IN SOCKET *Sock,
+ IN VOID *GroupInfo
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockGroup: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo);
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Add or remove route information in IP route table associated
+ with this socket.
+
+ @param Sock Pointer to the socket associated with the IP route
+ table to operate on.
+ @param RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The route table is updated successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockRoute (
+ IN SOCKET *Sock,
+ IN VOID *RouteInfo
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "SockRoute: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo);
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h
new file mode 100644
index 0000000000..8c25c63b11
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h
@@ -0,0 +1,986 @@
+/** @file
+ Socket header file.
+
+Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <Uefi.h>
+
+#include <Protocol/Ip4.h>
+#include <Protocol/Tcp4.h>
+#include <Protocol/Udp4.h>
+
+#include <Library/NetLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/DpcLib.h>
+#include <Library/PrintLib.h>
+
+#define SOCK_SND_BUF 0
+#define SOCK_RCV_BUF 1
+
+#define SOCK_BUFF_LOW_WATER (2 * 1024)
+#define SOCK_RCV_BUFF_SIZE (8 * 1024)
+#define SOCK_SND_BUFF_SIZE (8 * 1024)
+#define SOCK_BACKLOG 5
+
+#define PROTO_RESERVED_LEN 20
+
+#define SO_NO_MORE_DATA 0x0001
+
+//
+//
+//
+// When a socket is created it enters into SO_UNCONFIGURED,
+// no actions can be taken on this socket, only after calling
+// SockConfigure. The state transition diagram of socket is
+// as following:
+//
+// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING
+// ^ | |
+// | ---> SO_LISTENING |
+// | |
+// |------------------SO_DISCONNECTING<-- SO_CONNECTED
+//
+// A passive socket can only go into SO_LISTENING and
+// SO_UNCONFIGURED state. SO_XXXING state is a middle state
+// when a socket is undergoing a protocol procedure such
+// as requesting a TCP connection.
+//
+//
+//
+
+///
+/// Socket state
+///
+#define SO_CLOSED 0
+#define SO_LISTENING 1
+#define SO_CONNECTING 2
+#define SO_CONNECTED 3
+#define SO_DISCONNECTING 4
+
+///
+/// Socket configure state
+///
+#define SO_UNCONFIGURED 0
+#define SO_CONFIGURED_ACTIVE 1
+#define SO_CONFIGURED_PASSIVE 2
+#define SO_NO_MAPPING 3
+
+/**
+ Set socket SO_NO_MORE_DATA flag.
+
+ @param Sock Pointer to the socket
+
+**/
+#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA)
+
+/**
+ Check whether the socket is unconfigured.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is unconfigued
+ @retval False The socket is not unconfigued
+
+**/
+#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED)
+
+/**
+ Check whether the socket is configured.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is configued
+ @retval False The socket is not configued
+
+**/
+#define SOCK_IS_CONFIGURED(Sock) \
+ (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \
+ ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE))
+
+/**
+ Check whether the socket is configured to active mode.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is configued to active mode
+ @retval False The socket is not configued to active mode
+
+**/
+#define SOCK_IS_CONFIGURED_ACTIVE(Sock) \
+ ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE)
+
+/**
+ Check whether the socket is configured to passive mode.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is configued to passive mode
+ @retval False The socket is not configued to passive mode
+
+**/
+#define SOCK_IS_CONNECTED_PASSIVE(Sock) \
+ ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)
+
+/**
+ Check whether the socket is mapped.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is no mapping
+ @retval False The socket is mapped
+
+**/
+#define SOCK_IS_NO_MAPPING(Sock) \
+ ((Sock)->ConfigureState == SO_NO_MAPPING)
+
+/**
+ Check whether the socket is closed.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is closed
+ @retval False The socket is not closed
+
+**/
+#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED)
+
+/**
+ Check whether the socket is listening.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is listening
+ @retval False The socket is not listening
+
+**/
+#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING)
+
+/**
+ Check whether the socket is connecting.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is connecting
+ @retval False The socket is not connecting
+
+**/
+#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING)
+
+/**
+ Check whether the socket has connected.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket has connected
+ @retval False The socket has not connected
+
+**/
+#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED)
+
+/**
+ Check whether the socket is disconnecting.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is disconnecting
+ @retval False The socket is not disconnecting
+
+**/
+#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING)
+
+/**
+ Check whether the socket is no more data.
+
+ @param Sock Pointer to the socket
+
+ @retval True The socket is no more data
+ @retval False The socket still has data
+
+**/
+#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA))
+
+/**
+ Set the size of the receive buffer.
+
+ @param Sock Pointer to the socket
+ @param Size The size to set
+
+**/
+#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size))
+
+/**
+ Get the size of the receive buffer.
+
+ @param Sock Pointer to the socket
+
+ @return The receive buffer size
+
+**/
+#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater)
+
+/**
+ Get the size of the receive data.
+
+ @param Sock Pointer to the socket
+
+ @return The received data size
+
+**/
+#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize)
+
+/**
+ Set the size of the send buffer.
+
+ @param Sock Pointer to the socket
+ @param Size The size to set
+
+**/
+#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size))
+
+/**
+ Get the size of the send buffer.
+
+ @param Sock Pointer to the socket
+
+ @return The send buffer size
+
+**/
+#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater)
+
+/**
+ Get the size of the send data.
+
+ @param Sock Pointer to the socket
+
+ @return The send data size
+
+**/
+#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize)
+
+/**
+ Set the backlog value of the socket.
+
+ @param Sock Pointer to the socket
+ @param Value The value to set
+
+**/
+#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value))
+
+/**
+ Get the backlog value of the socket.
+
+ @param Sock Pointer to the socket
+
+ @return The backlog value
+
+**/
+#define GET_BACKLOG(Sock) ((Sock)->BackLog)
+
+/**
+ Set the socket with error state.
+
+ @param Sock Pointer to the socket
+ @param Error The error state
+
+**/
+#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error))
+
+#define SND_BUF_HDR_LEN(Sock) \
+ ((SockBufFirst (&((Sock)->SndBuffer)))->TotalSize)
+
+#define RCV_BUF_HDR_LEN(Sock) \
+ ((SockBufFirst (&((Sock)->RcvBuffer)))->TotalSize)
+
+#define SOCK_SIGNATURE SIGNATURE_32 ('S', 'O', 'C', 'K')
+
+#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE)
+
+#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock)
+
+#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) \
+ ((Type *) (((SOCK_TOKEN *) (SockToken))->Token))
+
+typedef struct _SOCKET SOCKET;
+
+///
+/// Socket completion token
+///
+typedef struct _SOCK_COMPLETION_TOKEN {
+ EFI_EVENT Event; ///< The event to be issued
+ EFI_STATUS Status; ///< The status to be issued
+} SOCK_COMPLETION_TOKEN;
+
+typedef union {
+ VOID *RxData;
+ VOID *TxData;
+} SOCK_IO_DATA;
+
+///
+/// The application token with data packet
+///
+typedef struct _SOCK_IO_TOKEN {
+ SOCK_COMPLETION_TOKEN Token;
+ SOCK_IO_DATA Packet;
+} SOCK_IO_TOKEN;
+
+///
+/// The request issued from socket layer to protocol layer.
+///
+#define SOCK_ATTACH 0 ///< Attach current socket to a new PCB
+#define SOCK_DETACH 1 ///< Detach current socket from the PCB
+#define SOCK_CONFIGURE 2 ///< Configure attached PCB
+#define SOCK_FLUSH 3 ///< Flush attached PCB
+#define SOCK_SND 4 ///< Need protocol to send something
+#define SOCK_SNDPUSH 5 ///< Need protocol to send pushed data
+#define SOCK_SNDURG 6 ///< Need protocol to send urgent data
+#define SOCK_CONSUMED 7 ///< Application has retrieved data from socket
+#define SOCK_CONNECT 8 ///< Need to connect to a peer
+#define SOCK_CLOSE 9 ///< Need to close the protocol process
+#define SOCK_ABORT 10 ///< Need to reset the protocol process
+#define SOCK_POLL 11 ///< Need to poll to the protocol layer
+#define SOCK_ROUTE 12 ///< Need to add a route information
+#define SOCK_MODE 13 ///< Need to get the mode data of the protocol
+#define SOCK_GROUP 14 ///< Need to join a mcast group
+
+///
+/// The socket type.
+///
+typedef enum {
+ SockDgram, ///< This socket providing datagram service
+ SockStream ///< This socket providing stream service
+} SOCK_TYPE;
+
+///
+/// The buffer structure of rcvd data and send data used by socket.
+///
+typedef struct _SOCK_BUFFER {
+ UINT32 HighWater; ///< The buffersize upper limit of sock_buffer
+ UINT32 LowWater; ///< The low warter mark of sock_buffer
+ NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data
+} SOCK_BUFFER;
+
+/**
+ The handler of protocol for request from socket.
+
+ @param Socket The socket issuing the request to protocol
+ @param Request The request issued by socket
+ @param RequestData The request related data
+
+ @retval EFI_SUCCESS The socket request is completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+typedef
+EFI_STATUS
+(*SOCK_PROTO_HANDLER) (
+ IN SOCKET *Socket,
+ IN UINT8 Request,
+ IN VOID *RequestData
+ );
+
+
+//
+// Socket provided oprerations for low layer protocol
+//
+
+//
+// Socket provided operations for user interface
+//
+
+/**
+ Set the state of the socket.
+
+ @param Sock Pointer to the socket.
+ @param State The new socket state to be set.
+
+**/
+VOID
+SockSetState (
+ IN OUT SOCKET *Sock,
+ IN UINT8 State
+ );
+
+/**
+ Called by the low layer protocol to indicate the socket a connection is
+ established.
+
+ This function just changes the socket's state to SO_CONNECTED
+ and signals the token used for connection establishment.
+
+ @param Sock Pointer to the socket associated with the
+ established connection.
+
+**/
+VOID
+SockConnEstablished (
+ IN SOCKET *Sock
+ );
+
+/**
+ Called by the low layer protocol to indicate the connection is closed.
+
+ This function flushes the socket, sets the state to SO_CLOSED and signals
+ the close token.
+
+ @param Sock Pointer to the socket associated with the closed
+ connection.
+
+**/
+VOID
+SockConnClosed (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Called by low layer protocol to indicate that some data is sent or processed.
+
+ This function trims the sent data in the socket send buffer, signals the data
+ token if proper.
+
+ @param Sock Pointer to the socket.
+ @param Count The length of the data processed or sent, in bytes.
+
+**/
+VOID
+SockDataSent (
+ IN SOCKET *Sock,
+ IN UINT32 Count
+ );
+
+/**
+ Called by the low layer protocol to copy some data in socket send
+ buffer starting from the specific offset to a buffer provided by
+ the caller.
+
+ @param Sock Pointer to the socket.
+ @param Offset The start point of the data to be copied.
+ @param Len The length of the data to be copied.
+ @param Dest Pointer to the destination to copy the data.
+
+ @return The data size copied.
+
+**/
+UINT32
+SockGetDataToSend (
+ IN SOCKET *Sock,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT8 *Dest
+ );
+
+/**
+ Called by the low layer protocol to indicate that there
+ will be no more data from the communication peer.
+
+ This function set the socket's state to SO_NO_MORE_DATA and
+ signal all queued IO tokens with the error status EFI_CONNECTION_FIN.
+
+ @param Sock Pointer to the socket.
+
+**/
+VOID
+SockNoMoreData (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Called by the low layer protocol to deliver received data to socket layer.
+
+ This function will append the data to the socket receive buffer, set ther
+ urgent data length and then check if any receive token can be signaled.
+
+ @param Sock Pointer to the socket.
+ @param NetBuffer Pointer to the buffer that contains the received
+ data.
+ @param UrgLen The length of the urgent data in the received data.
+
+**/
+VOID
+SockDataRcvd (
+ IN SOCKET *Sock,
+ IN OUT NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ );
+
+/**
+ Get the length of the free space of the specific socket buffer.
+
+ @param Sock Pointer to the socket.
+ @param Which Flag to indicate which socket buffer to check,
+ either send buffer or receive buffer.
+
+ @return The length of the free space, in bytes.
+
+**/
+UINT32
+SockGetFreeSpace (
+ IN SOCKET *Sock,
+ IN UINT32 Which
+ );
+
+/**
+ Clone a new socket including its associated protocol control block.
+
+ @param Sock Pointer to the socket to be cloned.
+
+ @return Pointer to the newly cloned socket. If NULL, error condition occurred.
+
+**/
+SOCKET *
+SockClone (
+ IN SOCKET *Sock
+ );
+
+/**
+ Signal the receive token with the specific error or
+ set socket error code after error is received.
+
+ @param Sock Pointer to the socket.
+ @param Error The error code received.
+
+**/
+VOID
+SockRcvdErr (
+ IN OUT SOCKET *Sock,
+ IN EFI_STATUS Error
+ );
+
+///
+/// Proto type of the create callback
+///
+typedef
+EFI_STATUS
+(*SOCK_CREATE_CALLBACK) (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+///
+/// Proto type of the destroy callback
+///
+typedef
+VOID
+(*SOCK_DESTROY_CALLBACK) (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+///
+/// The initialize data for create a new socket.
+///
+typedef struct _SOCK_INIT_DATA {
+ SOCK_TYPE Type;
+ UINT8 State;
+
+ SOCKET *Parent; ///< The parent of this socket
+ UINT32 BackLog; ///< The connection limit for listening socket
+ UINT32 SndBufferSize; ///< The high warter mark of send buffer
+ UINT32 RcvBufferSize; ///< The high warter mark of receive buffer
+ VOID *Protocol; ///< The pointer to protocol function template
+ ///< wanted to install on socket
+
+ //
+ // Callbacks after socket is created and before socket is to be destroyed.
+ //
+ SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created
+ SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied
+ VOID *Context; ///< The context of the callback
+
+ //
+ // Opaque protocol data.
+ //
+ VOID *ProtoData;
+ UINT32 DataSize;
+
+ SOCK_PROTO_HANDLER ProtoHandler; ///< The handler of protocol for socket request
+
+ EFI_HANDLE DriverBinding; ///< The driver binding handle
+} SOCK_INIT_DATA;
+
+///
+/// The union type of TCP and UDP protocol.
+///
+typedef union _NET_PROTOCOL {
+ EFI_TCP4_PROTOCOL TcpProtocol; ///< Tcp protocol
+ EFI_UDP4_PROTOCOL UdpProtocol; ///< Udp protocol
+} NET_PROTOCOL;
+
+///
+/// The socket structure representing a network service access point
+///
+struct _SOCKET {
+
+ //
+ // Socket description information
+ //
+ UINT32 Signature; ///< Signature of the socket
+ EFI_HANDLE SockHandle; ///< The virtual handle of the socket
+ EFI_HANDLE DriverBinding; ///< Socket's driver binding protocol
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ LIST_ENTRY Link;
+ UINT8 ConfigureState;
+ SOCK_TYPE Type;
+ UINT8 State;
+ UINT16 Flag;
+ EFI_LOCK Lock; ///< The lock of socket
+ SOCK_BUFFER SndBuffer; ///< Send buffer of application's data
+ SOCK_BUFFER RcvBuffer; ///< Receive buffer of received data
+ EFI_STATUS SockError; ///< The error returned by low layer protocol
+ BOOLEAN InDestroy;
+
+ //
+ // Fields used to manage the connection request
+ //
+ UINT32 BackLog; ///< the limit of connection to this socket
+ UINT32 ConnCnt; ///< the current count of connections to it
+ SOCKET *Parent; ///< listening parent that accept the connection
+ LIST_ENTRY ConnectionList; ///< the connections maintained by this socket
+
+ //
+ // The queue to buffer application's asynchronous token
+ //
+ LIST_ENTRY ListenTokenList;
+ LIST_ENTRY RcvTokenList;
+ LIST_ENTRY SndTokenList;
+ LIST_ENTRY ProcessingSndTokenList;
+
+ SOCK_COMPLETION_TOKEN *ConnectionToken; ///< app's token to signal if connected
+ SOCK_COMPLETION_TOKEN *CloseToken; ///< app's token to signal if closed
+
+ //
+ // Interface for low level protocol
+ //
+ SOCK_PROTO_HANDLER ProtoHandler; ///< The request handler of protocol
+ UINT8 ProtoReserved[PROTO_RESERVED_LEN]; ///< Data fields reserved for protocol
+ NET_PROTOCOL NetProtocol; ///< TCP or UDP protocol socket used
+
+ //
+ // Callbacks after socket is created and before socket is to be destroyed.
+ //
+ SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created
+ SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied
+ VOID *Context; ///< The context of the callback
+};
+
+///
+/// The token structure buffered in socket layer.
+///
+typedef struct _SOCK_TOKEN {
+ LIST_ENTRY TokenList; ///< The entry to add in the token list
+ SOCK_COMPLETION_TOKEN *Token; ///< The application's token
+ UINT32 RemainDataLen; ///< Unprocessed data length
+ SOCKET *Sock; ///< The poninter to the socket this token
+ ///< belongs to
+} SOCK_TOKEN;
+
+///
+/// Reserved data to access the NET_BUF delivered by UDP driver.
+///
+typedef struct _UDP_RSV_DATA {
+ EFI_TIME TimeStamp;
+ EFI_UDP4_SESSION_DATA Session;
+} UDP_RSV_DATA;
+
+///
+/// Reserved data to access the NET_BUF delivered by TCP driver.
+///
+typedef struct _TCP_RSV_DATA {
+ UINT32 UrgLen;
+} TCP_RSV_DATA;
+
+/**
+ Create a socket and its associated protocol control block
+ with the intial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param SockInitData Inital data to setting the socket.
+
+ @return Pointer to the newly created socket. If NULL, error condition occured.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData
+ );
+
+/**
+ Destroy the socket Sock and its associated protocol control block.
+
+ @param Sock The socket to be destroyed.
+
+ @retval EFI_SUCCESS The socket Sock is destroyed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockDestroyChild (
+ IN SOCKET *Sock
+ );
+
+/**
+ Configure the specific socket Sock using configuration data ConfigData.
+
+ @param Sock Pointer to the socket to be configured.
+ @param ConfigData Pointer to the configuration data.
+
+ @retval EFI_SUCCESS The socket is configured successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket or the
+ socket is already configured.
+
+**/
+EFI_STATUS
+SockConfigure (
+ IN SOCKET *Sock,
+ IN VOID *ConfigData
+ );
+
+/**
+ Initiate a connection establishment process.
+
+ @param Sock Pointer to the socket to initiate the initate the
+ connection.
+ @param Token Pointer to the token used for the connection
+ operation.
+
+ @retval EFI_SUCCESS The connection is initialized successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be an active one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockConnect (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Issue a listen token to get an existed connected network instance
+ or wait for a connection if there is none.
+
+ @param Sock Pointer to the socket to accept connections.
+ @param Token The token to accept a connection.
+
+ @retval EFI_SUCCESS Either a connection is accpeted or the Token is
+ buffered for further acception.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be a passive one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit.
+
+**/
+EFI_STATUS
+SockAccept (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Issue a token with data to the socket to send out.
+
+ @param Sock Pointer to the socket to process the token with
+ data.
+ @param Token The token with data that needs to send out.
+
+ @retval EFI_SUCCESS The token is processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockSend (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Issue a token to get data from the socket.
+
+ @param Sock Pointer to the socket to get data from.
+ @param Token The token to store the received data from the
+ socket.
+
+ @retval EFI_SUCCESS The token is processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockRcv (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Reset the socket and its associated protocol control block.
+
+ @param Sock Pointer to the socket to be flushed.
+
+ @retval EFI_SUCCESS The socket is flushed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockFlush (
+ IN SOCKET *Sock
+ );
+
+/**
+ Close or abort the socket associated connection.
+
+ @param Sock Pointer to the socket of the connection to close or
+ abort.
+ @param Token The token for close operation.
+ @param OnAbort TRUE for aborting the connection, FALSE to close it.
+
+ @retval EFI_SUCCESS The close or abort operation is initialized
+ successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockClose (
+ IN SOCKET *Sock,
+ IN VOID *Token,
+ IN BOOLEAN OnAbort
+ );
+
+/**
+ Get the mode data of the low layer protocol.
+
+ @param Sock Pointer to the socket to get mode data from.
+ @param Mode Pointer to the data to store the low layer mode
+ information.
+
+ @retval EFI_SUCCESS The mode data is got successfully.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGetMode (
+ IN SOCKET *Sock,
+ IN OUT VOID *Mode
+ );
+
+/**
+ Configure the low level protocol to join a multicast group for
+ this socket's connection.
+
+ @param Sock Pointer to the socket of the connection to join the
+ specific multicast group.
+ @param GroupInfo Pointer to the multicast group info.
+
+ @retval EFI_SUCCESS The configuration is done successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGroup (
+ IN SOCKET *Sock,
+ IN VOID *GroupInfo
+ );
+
+/**
+ Add or remove route information in IP route table associated
+ with this socket.
+
+ @param Sock Pointer to the socket associated with the IP route
+ table to operate on.
+ @param RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The route table is updated successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockRoute (
+ IN SOCKET *Sock,
+ IN VOID *RouteInfo
+ );
+
+//
+// Supporting function to operate on socket buffer
+//
+
+/**
+ Get the first buffer block in the specific socket buffer.
+
+ @param Sockbuf Pointer to the socket buffer.
+
+ @return Pointer to the first buffer in the queue. NULL if the queue is empty.
+
+**/
+NET_BUF *
+SockBufFirst (
+ IN SOCK_BUFFER *Sockbuf
+ );
+
+/**
+ Get the next buffer block in the specific socket buffer.
+
+ @param Sockbuf Pointer to the socket buffer.
+ @param SockEntry Pointer to the buffer block prior to the required
+ one.
+
+ @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is
+ the tail or head entry.
+
+**/
+NET_BUF *
+SockBufNext (
+ IN SOCK_BUFFER *Sockbuf,
+ IN NET_BUF *SockEntry
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
new file mode 100644
index 0000000000..702cae8d65
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
@@ -0,0 +1,717 @@
+/** @file
+ Tcp request dispatcher implementation.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+#define TCP_COMP_VAL(Min, Max, Default, Val) \
+ ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default))
+
+/**
+ Add or remove a route entry in the IP route table associated with this TCP instance.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param RouteInfo Pointer to the route info to be processed.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration(DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table
+ (when RouteInfo->DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table
+ (when RouteInfo->DeleteRoute is FALSE).
+**/
+EFI_STATUS
+Tcp4Route (
+ IN TCP_CB *Tcb,
+ IN TCP4_ROUTE_INFO *RouteInfo
+ )
+{
+ EFI_IP4_PROTOCOL *Ip4;
+
+ Ip4 = Tcb->IpInfo->Ip.Ip4;
+
+ ASSERT (Ip4 != NULL);
+
+ return Ip4->Routes (
+ Ip4,
+ RouteInfo->DeleteRoute,
+ RouteInfo->SubnetAddress,
+ RouteInfo->SubnetMask,
+ RouteInfo->GatewayAddress
+ );
+
+}
+
+
+/**
+ Get the operational settings of this TCP instance.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Mode Pointer to the buffer to store the operational
+ settings.
+
+ @retval EFI_SUCCESS The mode data is read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+
+**/
+EFI_STATUS
+Tcp4GetMode (
+ IN TCP_CB *Tcb,
+ IN OUT TCP4_MODE_DATA *Mode
+ )
+{
+ SOCKET *Sock;
+ EFI_TCP4_CONFIG_DATA *ConfigData;
+ EFI_TCP4_ACCESS_POINT *AccessPoint;
+ EFI_TCP4_OPTION *Option;
+ EFI_IP4_PROTOCOL *Ip;
+
+ Sock = Tcb->Sk;
+
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->Tcp4State != NULL) {
+ *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;
+ }
+
+ if (Mode->Tcp4ConfigData != NULL) {
+
+ ConfigData = Mode->Tcp4ConfigData;
+ AccessPoint = &(ConfigData->AccessPoint);
+ Option = ConfigData->ControlOption;
+
+ ConfigData->TypeOfService = Tcb->Tos;
+ ConfigData->TimeToLive = Tcb->Ttl;
+
+ AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;
+
+ IP4_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);
+ IP4_COPY_ADDRESS (&AccessPoint->SubnetMask, &Tcb->SubnetMask);
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
+
+ IP4_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
+
+ if (Option != NULL) {
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
+
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
+ Option->DataRetries = Tcb->MaxRexmit;
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
+
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
+
+ Option->EnableSelectiveAck = FALSE;
+ Option->EnablePathMtuDiscovery = FALSE;
+ }
+ }
+
+ Ip = Tcb->IpInfo->Ip.Ip4;
+ ASSERT (Ip != NULL);
+
+ return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);
+}
+
+
+/**
+ If AP->StationPort isn't zero, check whether the access point
+ is registered, else generate a random station port for this
+ access point.
+
+ @param AP Pointer to the access point.
+
+ @retval EFI_SUCCESS The check is passed or the port is assigned.
+ @retval EFI_INVALID_PARAMETER The non-zero station port is already used.
+ @retval EFI_OUT_OF_RESOURCES No port can be allocated.
+
+**/
+EFI_STATUS
+Tcp4Bind (
+ IN EFI_TCP4_ACCESS_POINT *AP
+ )
+{
+ BOOLEAN Cycle;
+
+ if (0 != AP->StationPort) {
+ //
+ // check if a same endpoint is bound
+ //
+ if (TcpFindTcbByPeer (&AP->StationAddress, AP->StationPort)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // generate a random port
+ //
+ Cycle = FALSE;
+
+ if (TCP4_PORT_USER_RESERVED == mTcp4RandomPort) {
+ mTcp4RandomPort = TCP4_PORT_KNOWN;
+ }
+
+ mTcp4RandomPort++;
+
+ while (TcpFindTcbByPeer (&AP->StationAddress, mTcp4RandomPort)) {
+
+ mTcp4RandomPort++;
+
+ if (mTcp4RandomPort <= TCP4_PORT_KNOWN) {
+
+ if (Cycle) {
+ DEBUG ((EFI_D_ERROR, "Tcp4Bind: no port can be allocated "
+ "for this pcb\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ mTcp4RandomPort = TCP4_PORT_KNOWN + 1;
+
+ Cycle = TRUE;
+ }
+
+ }
+
+ AP->StationPort = mTcp4RandomPort;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Flush the Tcb add its associated protocols.
+
+ @param Tcb Pointer to the TCP_CB to be flushed.
+
+**/
+VOID
+Tcp4FlushPcb (
+ IN TCP_CB *Tcb
+ )
+{
+ SOCKET *Sock;
+
+ IpIoConfigIp (Tcb->IpInfo, NULL);
+
+ Sock = Tcb->Sk;
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ RemoveEntryList (&Tcb->List);
+
+ //
+ // Uninstall the device path protocol.
+ //
+ if (Sock->DevicePath != NULL) {
+ gBS->UninstallProtocolInterface (
+ Sock->SockHandle,
+ &gEfiDevicePathProtocolGuid,
+ Sock->DevicePath
+ );
+ FreePool (Sock->DevicePath);
+ }
+ }
+
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+ Tcb->State = TCP_CLOSED;
+}
+
+/**
+ Attach a Pcb to the socket.
+
+ @param Sk Pointer to the socket of this TCP instance.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+EFI_STATUS
+Tcp4AttachPcb (
+ IN SOCKET *Sk
+ )
+{
+ TCP_CB *Tcb;
+ TCP4_PROTO_DATA *ProtoData;
+ IP_IO *IpIo;
+ EFI_STATUS Status;
+ VOID *Ip;
+
+ Tcb = AllocateZeroPool (sizeof (TCP_CB));
+
+ if (Tcb == NULL) {
+
+ DEBUG ((EFI_D_ERROR, "Tcp4ConfigurePcb: failed to allocate a TCB\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved;
+ IpIo = ProtoData->TcpService->IpIo;
+
+ //
+ // Create an IpInfo for this Tcb.
+ //
+ Tcb->IpInfo = IpIoAddIp (IpIo);
+ if (Tcb->IpInfo == NULL) {
+
+ FreePool (Tcb);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Open the new created IP instance BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Tcb->IpInfo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &Ip,
+ IpIo->Image,
+ Sk->SockHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ IpIoRemoveIp (IpIo, Tcb->IpInfo);
+ return Status;
+ }
+
+ InitializeListHead (&Tcb->List);
+ InitializeListHead (&Tcb->SndQue);
+ InitializeListHead (&Tcb->RcvQue);
+
+ Tcb->State = TCP_CLOSED;
+ Tcb->Sk = Sk;
+ ProtoData->TcpPcb = Tcb;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detach the Pcb of the socket.
+
+ @param Sk Pointer to the socket of this TCP instance.
+
+**/
+VOID
+Tcp4DetachPcb (
+ IN SOCKET *Sk
+ )
+{
+ TCP4_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+
+ ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ Tcp4FlushPcb (Tcb);
+
+ IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);
+
+ FreePool (Tcb);
+
+ ProtoData->TcpPcb = NULL;
+}
+
+
+/**
+ Configure the Pcb using CfgData.
+
+ @param Sk Pointer to the socket of this TCP instance.
+ @param CfgData Pointer to the TCP configuration data.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_INVALID_PARAMETER A same access point has been configured in
+ another TCP instance.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+EFI_STATUS
+Tcp4ConfigurePcb (
+ IN SOCKET *Sk,
+ IN EFI_TCP4_CONFIG_DATA *CfgData
+ )
+{
+ EFI_IP4_CONFIG_DATA IpCfgData;
+ EFI_STATUS Status;
+ EFI_TCP4_OPTION *Option;
+ TCP4_PROTO_DATA *TcpProto;
+ TCP_CB *Tcb;
+
+ ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));
+
+ TcpProto = (TCP4_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = TcpProto->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ //
+ // Add Ip for send pkt to the peer
+ //
+ CopyMem (&IpCfgData, &mIp4IoDefaultIpConfigData, sizeof (IpCfgData));
+ IpCfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ IpCfgData.UseDefaultAddress = CfgData->AccessPoint.UseDefaultAddress;
+ IpCfgData.StationAddress = CfgData->AccessPoint.StationAddress;
+ IpCfgData.SubnetMask = CfgData->AccessPoint.SubnetMask;
+ IpCfgData.ReceiveTimeout = (UINT32) (-1);
+
+ //
+ // Configure the IP instance this Tcb consumes.
+ //
+ Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ //
+ // Get the default address info if the instance is configured to use default address.
+ //
+ if (CfgData->AccessPoint.UseDefaultAddress) {
+ CfgData->AccessPoint.StationAddress = IpCfgData.StationAddress;
+ CfgData->AccessPoint.SubnetMask = IpCfgData.SubnetMask;
+ }
+
+ //
+ // check if we can bind this endpoint in CfgData
+ //
+ Status = Tcp4Bind (&(CfgData->AccessPoint));
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Tcp4ConfigurePcb: Bind endpoint failed "
+ "with %r\n", Status));
+
+ goto OnExit;
+ }
+
+ //
+ // Initalize the operating information in this Tcb
+ //
+ ASSERT (Tcb->State == TCP_CLOSED &&
+ IsListEmpty (&Tcb->SndQue) &&
+ IsListEmpty (&Tcb->RcvQue));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+ Tcb->State = TCP_CLOSED;
+
+ Tcb->SndMss = 536;
+ Tcb->RcvMss = TcpGetRcvMss (Sk);
+
+ Tcb->SRtt = 0;
+ Tcb->Rto = 3 * TCP_TICK_HZ;
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->Ssthresh = 0xffffffff;
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN;
+ Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD;
+ Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE;
+ Tcb->MaxRexmit = TCP_MAX_LOSS;
+ Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME;
+ Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME;
+ Tcb->ConnectTimeout = TCP_CONNECT_TIME;
+
+ //
+ // initialize Tcb in the light of CfgData
+ //
+ Tcb->Ttl = CfgData->TimeToLive;
+ Tcb->Tos = CfgData->TypeOfService;
+
+ Tcb->UseDefaultAddr = CfgData->AccessPoint.UseDefaultAddress;
+
+ CopyMem (&Tcb->LocalEnd.Ip, &CfgData->AccessPoint.StationAddress, sizeof (IP4_ADDR));
+ Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort);
+ IP4_COPY_ADDRESS (&Tcb->SubnetMask, &CfgData->AccessPoint.SubnetMask);
+
+ if (CfgData->AccessPoint.ActiveFlag) {
+ CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
+ Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort);
+ } else {
+ Tcb->RemoteEnd.Ip = 0;
+ Tcb->RemoteEnd.Port = 0;
+ }
+
+ Option = CfgData->ControlOption;
+
+ if (Option != NULL) {
+ SET_RCV_BUFFSIZE (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_RCV_BUF_SIZE_MIN,
+ TCP_RCV_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ Option->ReceiveBufferSize
+ )
+ )
+ );
+ SET_SND_BUFFSIZE (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_SND_BUF_SIZE_MIN,
+ TCP_SND_BUF_SIZE,
+ TCP_SND_BUF_SIZE,
+ Option->SendBufferSize
+ )
+ )
+ );
+
+ SET_BACKLOG (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_BACKLOG_MIN,
+ TCP_BACKLOG,
+ TCP_BACKLOG,
+ Option->MaxSynBackLog
+ )
+ )
+ );
+
+ Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (
+ TCP_MAX_LOSS_MIN,
+ TCP_MAX_LOSS,
+ TCP_MAX_LOSS,
+ Option->DataRetries
+ );
+ Tcb->FinWait2Timeout = TCP_COMP_VAL (
+ TCP_FIN_WAIT2_TIME,
+ TCP_FIN_WAIT2_TIME_MAX,
+ TCP_FIN_WAIT2_TIME,
+ (UINT32) (Option->FinTimeout * TCP_TICK_HZ)
+ );
+
+ if (Option->TimeWaitTimeout != 0) {
+ Tcb->TimeWaitTimeout = TCP_COMP_VAL (
+ TCP_TIME_WAIT_TIME,
+ TCP_TIME_WAIT_TIME_MAX,
+ TCP_TIME_WAIT_TIME,
+ (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ)
+ );
+ } else {
+ Tcb->TimeWaitTimeout = 0;
+ }
+
+ if (Option->KeepAliveProbes != 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+
+ Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (
+ TCP_MAX_KEEPALIVE_MIN,
+ TCP_MAX_KEEPALIVE,
+ TCP_MAX_KEEPALIVE,
+ Option->KeepAliveProbes
+ );
+ Tcb->KeepAliveIdle = TCP_COMP_VAL (
+ TCP_KEEPALIVE_IDLE_MIN,
+ TCP_KEEPALIVE_IDLE_MAX,
+ TCP_KEEPALIVE_IDLE_MIN,
+ (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ)
+ );
+ Tcb->KeepAlivePeriod = TCP_COMP_VAL (
+ TCP_KEEPALIVE_PERIOD_MIN,
+ TCP_KEEPALIVE_PERIOD,
+ TCP_KEEPALIVE_PERIOD,
+ (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ)
+ );
+ }
+
+ Tcb->ConnectTimeout = TCP_COMP_VAL (
+ TCP_CONNECT_TIME_MIN,
+ TCP_CONNECT_TIME,
+ TCP_CONNECT_TIME,
+ (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ)
+ );
+
+ if (!Option->EnableNagle) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);
+ }
+
+ if (!Option->EnableTimeStamp) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);
+ }
+
+ if (!Option->EnableWindowScaling) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);
+ }
+ }
+
+ //
+ // The socket is bound, the <SrcIp, SrcPort, DstIp, DstPort> is
+ // determined, construct the IP device path and install it.
+ //
+ Status = TcpInstallDevicePath (Sk);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ //
+ // update state of Tcb and socket
+ //
+ if (!CfgData->AccessPoint.ActiveFlag) {
+
+ TcpSetState (Tcb, TCP_LISTEN);
+ SockSetState (Sk, SO_LISTENING);
+
+ Sk->ConfigureState = SO_CONFIGURED_PASSIVE;
+ } else {
+
+ Sk->ConfigureState = SO_CONFIGURED_ACTIVE;
+ }
+
+ TcpInsertTcb (Tcb);
+
+OnExit:
+
+ return Status;
+}
+
+
+/**
+ The procotol handler provided to the socket layer, used to
+ dispatch the socket level requests by calling the corresponding
+ TCP layer functions.
+
+ @param Sock Pointer to the socket of this TCP instance.
+ @param Request The code of this operation request.
+ @param Data Pointer to the operation specific data passed in
+ together with the operation request.
+
+ @retval EFI_SUCCESS The socket request is completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+EFI_STATUS
+Tcp4Dispatcher (
+ IN SOCKET *Sock,
+ IN UINT8 Request,
+ IN VOID *Data OPTIONAL
+ )
+{
+ TCP_CB *Tcb;
+ TCP4_PROTO_DATA *ProtoData;
+ EFI_IP4_PROTOCOL *Ip;
+
+ ProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ switch (Request) {
+ case SOCK_POLL:
+ Ip = ProtoData->TcpService->IpIo->Ip.Ip4;
+ Ip->Poll (Ip);
+ break;
+
+ case SOCK_CONSUMED:
+ //
+ // After user received data from socket buffer, socket will
+ // notify TCP using this message to give it a chance to send out
+ // window update information
+ //
+ ASSERT (Tcb != NULL);
+ TcpOnAppConsume (Tcb);
+ break;
+
+ case SOCK_SND:
+
+ ASSERT (Tcb != NULL);
+ TcpOnAppSend (Tcb);
+ break;
+
+ case SOCK_CLOSE:
+
+ TcpOnAppClose (Tcb);
+
+ break;
+
+ case SOCK_ABORT:
+
+ TcpOnAppAbort (Tcb);
+
+ break;
+
+ case SOCK_SNDPUSH:
+ Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
+
+ break;
+
+ case SOCK_SNDURG:
+ Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
+
+ break;
+
+ case SOCK_CONNECT:
+
+ TcpOnAppConnect (Tcb);
+
+ break;
+
+ case SOCK_ATTACH:
+
+ return Tcp4AttachPcb (Sock);
+
+ case SOCK_FLUSH:
+
+ Tcp4FlushPcb (Tcb);
+
+ break;
+
+ case SOCK_DETACH:
+
+ Tcp4DetachPcb (Sock);
+
+ break;
+
+ case SOCK_CONFIGURE:
+
+ return Tcp4ConfigurePcb (
+ Sock,
+ (EFI_TCP4_CONFIG_DATA *) Data
+ );
+
+ case SOCK_MODE:
+
+ ASSERT ((Data != NULL) && (Tcb != NULL));
+
+ return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);
+
+ case SOCK_ROUTE:
+
+ ASSERT ((Data != NULL) && (Tcb != NULL));
+
+ return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);
+
+ default:
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
new file mode 100644
index 0000000000..7d84767f5e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
@@ -0,0 +1,782 @@
+/** @file
+ Tcp driver function.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+
+UINT16 mTcp4RandomPort;
+extern EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gTcp4ComponentName2;
+extern EFI_UNICODE_STRING_TABLE *gTcpControllerNameTable;
+
+TCP4_HEARTBEAT_TIMER mTcp4Timer = {
+ NULL,
+ 0
+};
+
+EFI_TCP4_PROTOCOL mTcp4ProtocolTemplate = {
+ Tcp4GetModeData,
+ Tcp4Configure,
+ Tcp4Routes,
+ Tcp4Connect,
+ Tcp4Accept,
+ Tcp4Transmit,
+ Tcp4Receive,
+ Tcp4Close,
+ Tcp4Cancel,
+ Tcp4Poll
+};
+
+SOCK_INIT_DATA mTcp4DefaultSockData = {
+ SockStream,
+ 0,
+ NULL,
+ TCP_BACKLOG,
+ TCP_SND_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ &mTcp4ProtocolTemplate,
+ Tcp4CreateSocketCallback,
+ Tcp4DestroySocketCallback,
+ NULL,
+ NULL,
+ 0,
+ Tcp4Dispatcher,
+ NULL,
+};
+
+EFI_DRIVER_BINDING_PROTOCOL mTcp4DriverBinding = {
+ Tcp4DriverBindingSupported,
+ Tcp4DriverBindingStart,
+ Tcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mTcp4ServiceBinding = {
+ Tcp4ServiceBindingCreateChild,
+ Tcp4ServiceBindingDestroyChild
+};
+
+
+/**
+ Create and start the heartbeat timer for TCP driver.
+
+ @retval EFI_SUCCESS The timer is successfully created and started.
+ @retval other The timer is not created.
+
+**/
+EFI_STATUS
+Tcp4CreateTimer (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mTcp4Timer.RefCnt == 0) {
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpTicking,
+ NULL,
+ &mTcp4Timer.TimerEvent
+ );
+ if (!EFI_ERROR (Status)) {
+
+ Status = gBS->SetTimer (
+ mTcp4Timer.TimerEvent,
+ TimerPeriodic,
+ (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)
+ );
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+
+ mTcp4Timer.RefCnt++;
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop and destroy the heartbeat timer for TCP driver.
+
+**/
+VOID
+Tcp4DestroyTimer (
+ VOID
+ )
+{
+ ASSERT (mTcp4Timer.RefCnt > 0);
+
+ mTcp4Timer.RefCnt--;
+
+ if (mTcp4Timer.RefCnt > 0) {
+ return;
+ }
+
+ gBS->SetTimer (mTcp4Timer.TimerEvent, TimerCancel, 0);
+ gBS->CloseEvent (mTcp4Timer.TimerEvent);
+ mTcp4Timer.TimerEvent = NULL;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ SOCKET *Sock;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = NET_LIST_USER_STRUCT_S (Entry, SOCKET, Link, SOCK_SIGNATURE);
+ ServiceBinding = ((TCP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
+ NumberOfChildren = ((TCP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
+ ChildHandleBuffer = ((TCP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
+
+ if (!NetIsInHandleBuffer (Sock->SockHandle, NumberOfChildren, ChildHandleBuffer)) {
+ return EFI_SUCCESS;
+ }
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);
+}
+
+/**
+ The entry point for Tcp4 driver, used to install Tcp4 driver on the ImageHandle.
+
+ @param ImageHandle The firmware allocated handle for this
+ driver image.
+ @param SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS Driver loaded.
+ @retval other Driver not loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Seed;
+
+ //
+ // Install the TCP4 Driver Binding Protocol
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &mTcp4DriverBinding,
+ ImageHandle,
+ &gTcp4ComponentName,
+ &gTcp4ComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+ //
+ // Initialize ISS and random port.
+ //
+ Seed = NetRandomInitSeed ();
+ mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss;
+ mTcp4RandomPort = (UINT16) (TCP4_PORT_KNOWN +
+ (UINT16) (NET_RANDOM(Seed) % TCP4_PORT_KNOWN));
+
+ return Status;
+}
+
+
+/**
+ Tests to see if this driver supports a given controller.
+
+ If a child device is provided, it further tests to see if this driver supports
+ creating a handle for the specified child device.
+
+ @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is optional for bus drivers.
+
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver
+ specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by
+ the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a
+ different driver or an application that requires
+ exclusive access.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver
+ specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the Tcp4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test for the Ip4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ The Start() function is designed to be invoked from the EFI boot service
+ ConnectController(). As a result, much of the error checking on the parameters
+ to Start() has been moved into this common boot service. It is legal to call
+ Start() from other locations, but the following calling restrictions must be
+ followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally
+ aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified
+ by This must have been called with the same calling parameters, and Supported()
+ must have returned EFI_SUCCESS.
+
+ @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is
+ optional for bus drivers.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_ALREADY_STARTED The device could not be started due to a device error.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
+ of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ IP_IO_OPEN_DATA OpenData;
+
+ TcpServiceData = AllocateZeroPool (sizeof (TCP4_SERVICE_DATA));
+
+ if (NULL == TcpServiceData) {
+ DEBUG ((EFI_D_ERROR, "Tcp4DriverBindingStart: Have no enough"
+ " resource to create a Tcp Servcie Data\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create a new IP IO to Consume it
+ //
+ TcpServiceData->IpIo = IpIoCreate (
+ This->DriverBindingHandle,
+ ControllerHandle,
+ IP_VERSION_4
+ );
+ if (NULL == TcpServiceData->IpIo) {
+
+ DEBUG ((EFI_D_ERROR, "Tcp4DriverBindingStart: Have no enough"
+ " resource to create an Ip Io\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Configure and start IpIo.
+ //
+ ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));
+
+ CopyMem (
+ &OpenData.IpConfigData.Ip4CfgData,
+ &mIp4IoDefaultIpConfigData,
+ sizeof (EFI_IP4_CONFIG_DATA)
+ );
+
+ OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+
+ OpenData.PktRcvdNotify = Tcp4RxCallback;
+ Status = IpIoOpen (TcpServiceData->IpIo, &OpenData);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create the timer event used by TCP driver
+ //
+ Status = Tcp4CreateTimer ();
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "Tcp4DriverBindingStart: Create TcpTimer"
+ " Event failed with %r\n", Status));
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Tcp4ServiceBinding Protocol on the
+ // controller handle
+ //
+ TcpServiceData->Tcp4ServiceBinding = mTcp4ServiceBinding;
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ &TcpServiceData->Tcp4ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "Tcp4DriverBindingStart: Install Tcp4 Service Binding"
+ " Protocol failed for %r\n", Status));
+
+ Tcp4DestroyTimer ();
+ goto ON_ERROR;
+ }
+
+ //
+ // Initialize member in TcpServiceData
+ //
+ TcpServiceData->ControllerHandle = ControllerHandle;
+ TcpServiceData->Signature = TCP4_DRIVER_SIGNATURE;
+ TcpServiceData->DriverBindingHandle = This->DriverBindingHandle;
+
+ InitializeListHead (&TcpServiceData->SocketList);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (TcpServiceData->IpIo != NULL) {
+ IpIoDestroy (TcpServiceData->IpIo);
+ TcpServiceData->IpIo = NULL;
+ }
+
+ FreePool (TcpServiceData);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ The Stop() function is designed to be invoked from the EFI boot service
+ DisconnectController(). As a result, much of the error checking on the parameters
+ to Stop() has been moved into this common boot service. It is legal to call Stop()
+ from other locations, but the following calling restrictions must be followed
+ or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call
+ to this same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this
+ driver's Start() function, and the Start() function must have called OpenProtocol()
+ on ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param ChildHandleBuffer An array of child handles to be freed. May be NULL if
+ NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ LIST_ENTRY *List;
+ TCP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
+
+ // Find the NicHandle where Tcp4 ServiceBinding Protocol is installed.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Retrieve the TCP driver Data Structure
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "Tcp4DriverBindingStop: Locate Tcp4 Service "
+ " Binding Protocol failed with %r\n", Status));
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ TcpServiceData = TCP4_FROM_THIS (ServiceBinding);
+
+ if (NumberOfChildren != 0) {
+ List = &TcpServiceData->SocketList;
+ Context.ServiceBinding = ServiceBinding;
+ Context.NumberOfChildren = NumberOfChildren;
+ Context.ChildHandleBuffer = ChildHandleBuffer;
+ Status = NetDestroyLinkList (
+ List,
+ Tcp4DestroyChildEntryInHandleBuffer,
+ &Context,
+ NULL
+ );
+ } else if (IsListEmpty (&TcpServiceData->SocketList)) {
+ //
+ // Uninstall TCP servicebinding protocol
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ ServiceBinding,
+ NULL
+ );
+
+ //
+ // Destroy the IpIO consumed by TCP driver
+ //
+ IpIoDestroy (TcpServiceData->IpIo);
+ TcpServiceData->IpIo = NULL;
+
+ //
+ // Destroy the heartbeat timer.
+ //
+ Tcp4DestroyTimer ();
+
+ if (gTcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gTcpControllerNameTable);
+ gTcpControllerNameTable = NULL;
+ }
+
+ //
+ // Release the TCP service data
+ //
+ FreePool (TcpServiceData);
+
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Open Ip4 and device path protocols for a created socket, and insert it in
+ socket list.
+
+ @param This Pointer to the socket just created
+ @param Context Context of the socket
+
+ @retval EFI_SUCCESS This protocol is installed successfully.
+ @retval other Some error occured.
+
+**/
+EFI_STATUS
+Tcp4CreateSocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ EFI_IP4_PROTOCOL *Ip4;
+
+ TcpServiceData = ((TCP4_PROTO_DATA *) This->ProtoReserved)->TcpService;
+
+ //
+ // Open the default Ip4 protocol of IP_IO BY_DRIVER.
+ //
+ Status = gBS->OpenProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open the device path on the handle where service binding resides on.
+ //
+ Status = gBS->OpenProtocol (
+ TcpServiceData->ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &This->ParentDevicePath,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle
+ );
+ } else {
+ //
+ // Insert this socket into the SocketList.
+ //
+ InsertTailList (&TcpServiceData->SocketList, &This->Link);
+ }
+
+ return Status;
+}
+
+/**
+ Close Ip4 and device path protocols for a socket, and remove it from socket list.
+
+ @param This Pointer to the socket to be removed
+ @param Context Context of the socket
+
+**/
+VOID
+Tcp4DestroySocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ )
+{
+ TCP4_SERVICE_DATA *TcpServiceData;
+
+ TcpServiceData = ((TCP4_PROTO_DATA *) This->ProtoReserved)->TcpService;
+
+ //
+ // Remove this node from the list.
+ //
+ RemoveEntryList (&This->Link);
+
+ //
+ // Close the Ip4 protocol.
+ //
+ gBS->CloseProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle
+ );
+}
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle. If ChildHandle
+ is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing
+ ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL, then
+ a new handle is created. If it is a pointer to an existing UEFI
+ handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ SOCKET *Sock;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ TCP4_PROTO_DATA TcpProto;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Status = EFI_SUCCESS;
+ TcpServiceData = TCP4_FROM_THIS (This);
+ TcpProto.TcpService = TcpServiceData;
+ TcpProto.TcpPcb = NULL;
+
+ //
+ // Create a tcp instance with defualt Tcp default
+ // sock init data and TcpProto
+ //
+ mTcp4DefaultSockData.ProtoData = &TcpProto;
+ mTcp4DefaultSockData.DataSize = sizeof (TCP4_PROTO_DATA);
+ mTcp4DefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;
+
+ Sock = SockCreateChild (&mTcp4DefaultSockData);
+ if (NULL == Sock) {
+ DEBUG ((EFI_D_ERROR, "Tcp4DriverBindingCreateChild: "
+ "No resource to create a Tcp Child\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ *ChildHandle = Sock->SockHandle;
+ }
+
+ mTcp4DefaultSockData.ProtoData = NULL;
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is
+ being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // retrieve the Tcp4 protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **) &Tcp4,
+ mTcp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_UNSUPPORTED;
+ } else {
+ //
+ // destroy this sock and related Tcp protocol control
+ // block
+ //
+ Sock = SOCK_FROM_THIS (Tcp4);
+
+ SockDestroyChild (Sock);
+ }
+
+ return Status;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h
new file mode 100644
index 0000000000..edaa1a4a81
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h
@@ -0,0 +1,342 @@
+/** @file
+ Tcp driver function header.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP4_DRIVER_H_
+#define _TCP4_DRIVER_H_
+
+#include <Protocol/ServiceBinding.h>
+#include <Library/IpIoLib.h>
+
+#define TCP4_DRIVER_SIGNATURE SIGNATURE_32 ('T', 'C', 'P', '4')
+
+#define TCP4_PORT_KNOWN 1024
+#define TCP4_PORT_USER_RESERVED 65535
+
+#define TCP4_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ TCP4_SERVICE_DATA, \
+ Tcp4ServiceBinding, \
+ TCP4_DRIVER_SIGNATURE \
+ )
+
+///
+/// TCP heartbeat tick timer.
+///
+typedef struct _TCP4_HEARTBEAT_TIMER {
+ EFI_EVENT TimerEvent; ///< The event assoiated with the timer
+ INTN RefCnt; ///< Number of reference
+} TCP4_HEARTBEAT_TIMER;
+
+///
+/// TCP service data
+///
+typedef struct _TCP4_SERVICE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE ControllerHandle;
+ IP_IO *IpIo; // IP Io consumed by TCP4
+ EFI_SERVICE_BINDING_PROTOCOL Tcp4ServiceBinding;
+ EFI_HANDLE DriverBindingHandle;
+ LIST_ENTRY SocketList;
+} TCP4_SERVICE_DATA;
+
+///
+/// TCP protocol data
+///
+typedef struct _TCP4_PROTO_DATA {
+ TCP4_SERVICE_DATA *TcpService;
+ TCP_CB *TcpPcb;
+} TCP4_PROTO_DATA;
+
+
+/**
+ Packet receive callback function provided to IP_IO, used to call
+ the proper function to handle the packet received by IP.
+
+ @param Status Status of the received packet.
+ @param IcmpErr ICMP error number.
+ @param NetSession Pointer to the net session of this packet.
+ @param Pkt Pointer to the recieved packet.
+ @param Context Pointer to the context configured in IpIoOpen(), not used
+ now.
+
+ @return None
+
+**/
+VOID
+EFIAPI
+Tcp4RxCallback (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpErr,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Pkt,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Send the segment to IP via IpIo function.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the TCP segment to be sent.
+ @param Src Source address of the TCP segment.
+ @param Dest Destination address of the TCP segment.
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 The segment was failed to send.
+
+**/
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dest
+ );
+
+/**
+ The procotol handler provided to the socket layer, used to
+ dispatch the socket level requests by calling the corresponding
+ TCP layer functions.
+
+ @param Sock Pointer to the socket of this TCP instance.
+ @param Request The code of this operation request.
+ @param Data Pointer to the operation specific data passed in
+ together with the operation request.
+
+ @retval EFI_SUCCESS The socket request is completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+EFI_STATUS
+Tcp4Dispatcher (
+ IN SOCKET *Sock,
+ IN UINT8 Request,
+ IN VOID *Data OPTIONAL
+ );
+
+
+/**
+ The entry point for Tcp4 driver, used to install Tcp4 driver on the ImageHandle.
+
+ @param ImageHandle The firmware allocated handle for this
+ driver image.
+ @param SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS Driver loaded.
+ @retval other Driver not loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+
+/**
+ Tests to see if this driver supports a given controller.
+
+ If a child device is provided, it further tests to see if this driver supports
+ creating a handle for the specified child device.
+
+ @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is optional for bus drivers.
+
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver
+ specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by
+ the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a
+ different driver or an application that requires
+ exclusive access.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver
+ specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle.
+
+ The Start() function is designed to be invoked from the EFI boot service
+ ConnectController(). As a result, much of the error checking on the parameters
+ to Start() has been moved into this common boot service. It is legal to call
+ Start() from other locations, but the following calling restrictions must be
+ followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally
+ aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified
+ by This must have been called with the same calling parameters, and Supported()
+ must have returned EFI_SUCCESS.
+
+ @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param RemainingDevicePath A pointer to the remaining portion of a device path.
+ This parameter is ignored by device drivers, and is
+ optional for bus drivers.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_ALREADY_STARTED The device could not be started due to a device error.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
+ of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ The Stop() function is designed to be invoked from the EFI boot service
+ DisconnectController(). As a result, much of the error checking on the parameters
+ to Stop() has been moved into this common boot service. It is legal to call Stop()
+ from other locations, but the following calling restrictions must be followed
+ or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call
+ to this same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this
+ driver's Start() function, and the Start() function must have called OpenProtocol()
+ on ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param ChildHandleBuffer An array of child handles to be freed. May be NULL if
+ NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Open Ip4 and device path protocols for a created socket, and insert it in
+ socket list.
+
+ @param This Pointer to the socket just created
+ @param Context Context of the socket
+
+ @retval EFI_SUCCESS This protocol is installed successfully.
+ @retval other Some error occured.
+
+**/
+EFI_STATUS
+Tcp4CreateSocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+/**
+ Close Ip4 and device path protocols for a socket, and remove it from socket list.
+
+ @param This Pointer to the socket to be removed
+ @param Context Context of the socket
+
+**/
+VOID
+Tcp4DestroySocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle. If ChildHandle
+ is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing
+ ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL, then
+ a new handle is created. If it is a pointer to an existing UEFI
+ handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is
+ being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
new file mode 100644
index 0000000000..ba3c377b5f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
@@ -0,0 +1,84 @@
+## @file
+# This module produces EFI TCPv4 Protocol and EFI TCPv4 Service Binding Protocol.
+#
+# This module produces EFI TCPv4(Transmission Control Protocol version 4) Protocol
+# upon EFI IPv4 Protocol, to provide basic TCPv4 I/O services.
+#
+# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Tcp4Dxe
+ MODULE_UNI_FILE = Tcp4Dxe.uni
+ FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af4d
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Tcp4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = mTcp4DriverBinding
+# COMPONENT_NAME = gTcp4ComponentName
+# COMPONENT_NAME2 = gTcp4ComponentName2
+#
+
+[Sources]
+ SockImpl.c
+ SockInterface.c
+ Tcp4Proto.h
+ Tcp4Main.h
+ SockImpl.h
+ Tcp4Output.c
+ Tcp4Timer.c
+ Tcp4Option.h
+ Tcp4Dispatcher.c
+ Tcp4Input.c
+ Tcp4Misc.c
+ Tcp4Main.c
+ Socket.h
+ ComponentName.c
+ Tcp4Driver.h
+ Tcp4Io.c
+ Tcp4Driver.c
+ Tcp4Func.h
+ Tcp4Option.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ DebugLib
+ NetLib
+ IpIoLib
+ DevicePathLib
+ DpcLib
+
+[Protocols]
+ gEfiTcp4ServiceBindingProtocolGuid ## BY_START
+ gEfiIp4ServiceBindingProtocolGuid ## TO_START
+ gEfiTcp4ProtocolGuid ## BY_START
+ gEfiIp4ProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Tcp4DxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.uni b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.uni
new file mode 100644
index 0000000000..b6d30d9851
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// This module produces EFI TCPv4 Protocol and EFI TCPv4 Service Binding Protocol.
+//
+// This module produces EFI TCPv4(Transmission Control Protocol version 4) Protocol
+// upon EFI IPv4 Protocol, to provide basic TCPv4 I/O services.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI TCPv4 Protocol and EFI TCPv4 Service Binding Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI TCPv4(Transmission Control Protocol version 4) Protocol upon EFI IPv4 Protocol to provide basic TCPv4 I/O services."
+
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni
new file mode 100644
index 0000000000..a3e36b362c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Tcp4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"TCP v4 DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h
new file mode 100644
index 0000000000..53b7aac8ae
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h
@@ -0,0 +1,781 @@
+/** @file
+ Tcp function header file.
+
+Copyright (c) 2005 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP4_FUNC_H_
+#define _TCP4_FUNC_H_
+
+//
+// Declaration of all the functions in TCP
+// protocol. It is intended to keep tcp.h
+// clear.
+//
+
+//
+// Functions in tcp.c
+//
+
+/**
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @param Addr Pointer to the IP address needs to match.
+ @param Port The port number needs to match.
+
+ @return The Tcb which matches the <Addr Port> paire exists or not.
+
+**/
+BOOLEAN
+TcpFindTcbByPeer (
+ IN EFI_IPv4_ADDRESS *Addr,
+ IN TCP_PORTNO Port
+ );
+
+/**
+ Locate the TCP_CB related to the socket pair.
+
+ @param LocalPort The local port number.
+ @param LocalIp The local IP address.
+ @param RemotePort The remote port number.
+ @param RemoteIp The remote IP address.
+ @param Syn Whether to search the listen sockets, if TRUE, the
+ listen sockets are searched.
+
+ @return Pointer to the related TCP_CB, if NULL no match is found.
+
+**/
+TCP_CB *
+TcpLocateTcb (
+ IN TCP_PORTNO LocalPort,
+ IN UINT32 LocalIp,
+ IN TCP_PORTNO RemotePort,
+ IN UINT32 RemoteIp,
+ IN BOOLEAN Syn
+ );
+
+/**
+ Insert a Tcb into the proper queue.
+
+ @param Tcb Pointer to the TCP_CB to be inserted.
+
+ @retval 0 The Tcb is inserted successfully.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpInsertTcb (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Clone a TCP_CB from Tcb.
+
+ @param Tcb Pointer to the TCP_CB to be cloned.
+
+ @return Pointer to the new cloned TCP_CB, if NULL error condition occurred.
+
+**/
+TCP_CB *
+TcpCloneTcb (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Compute an ISS to be used by a new connection.
+
+ @return The result ISS.
+
+**/
+TCP_SEQNO
+TcpGetIss (
+ VOID
+ );
+
+/**
+ Initialize the Tcb local related members.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpInitTcbLocal (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Initialize the peer related members.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Pointer to the segment that contains the peer's
+ intial info.
+ @param Opt Pointer to the options announced by the peer.
+
+**/
+VOID
+TcpInitTcbPeer (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg,
+ IN TCP_OPTION *Opt
+ );
+
+/**
+ Get the local mss.
+
+ @param Sock Pointer to the socket to get mss
+
+ @return The mss size.
+
+**/
+UINT16
+TcpGetRcvMss (
+ IN SOCKET *Sock
+ );
+
+/**
+ Set the Tcb's state.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param State The state to be set.
+
+**/
+VOID
+TcpSetState (
+ IN OUT TCP_CB *Tcb,
+ IN UINT8 State
+ );
+
+//
+// Functions in Tcp4Output.c
+//
+/**
+ Send the segment to IP via IpIo function.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the TCP segment to be sent.
+ @param Src Source address of the TCP segment.
+ @param Dest Destination address of the TCP segment.
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 The segment was failed to send.
+
+**/
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dest
+ );
+
+/**
+ Check whether to send data/SYN/FIN and piggy back an ACK.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Force Whether to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The number of bytes sent.
+
+**/
+INTN
+TcpToSendData (
+ IN OUT TCP_CB *Tcb,
+ IN INTN Force
+ );
+
+/**
+ Check whether to send an ACK or delayed ACK.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpToSendAck (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Send an ACK immediately.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSendAck (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Send a zero probe segment. It can be used by keepalive and zero window probe.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The zero probe segment was sent out successfully.
+ @retval other Error condition occurred.
+
+**/
+INTN
+TcpSendZeroProbe (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Process the data and FIN flag, check whether to deliver
+ data to the socket layer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 No error occurred to deliver data.
+ @retval -1 Error condition occurred. Proper response is to reset the
+ connection.
+
+**/
+INTN
+TcpDeliverData (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Send a RESET segment in response to the segment received.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.
+ @param Head TCP header of the segment that triggers the reset.
+ @param Len Length of the segment that triggers the reset.
+ @param Local Local IP address.
+ @param Remote Remote peer's IP address.
+
+ @retval 0 A reset is sent or no need to send it.
+ @retval -1 No reset is sent.
+
+**/
+INTN
+TcpSendReset (
+ IN TCP_CB *Tcb,
+ IN TCP_HEAD *Head,
+ IN INT32 Len,
+ IN UINT32 Local,
+ IN UINT32 Remote
+ );
+
+/**
+ Compute the sequence space left in the old receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence space left in the old receive window.
+
+**/
+UINT32
+TcpRcvWinOld (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Compute the current receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The size of the current receive window, in bytes.
+
+**/
+UINT32
+TcpRcvWinNow (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Retransmit the segment from sequence Seq.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment to be retransmitted.
+
+ @retval 0 Retransmission succeeded.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpRetransmit (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq
+ );
+
+/**
+ Compute how much data to send.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Force Whether to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The length of the data can be sent, if 0, no data can be sent.
+
+**/
+UINT32
+TcpDataToSend (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ );
+
+/**
+ Verify that the segment is in good shape.
+
+ @param Nbuf Buffer that contains the segment to be checked.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpVerifySegment (
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Verify that all the segments in SndQue are in good shape.
+
+ @param Head Pointer to the head node of the SndQue.
+
+ @retval 0 At least one segment is broken.
+ @retval 1 All segments in the specific queue are in good shape.
+
+**/
+INTN
+TcpCheckSndQue (
+ IN LIST_ENTRY *Head
+ );
+
+/**
+ Get a segment from the Tcb's SndQue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ );
+
+/**
+ Get a segment from the Tcb's socket buffer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSock (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ );
+
+/**
+ Get a segment starting from sequence Seq of a maximum
+ length of Len.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegment (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ );
+
+/**
+ Get the maximum SndNxt.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence number of the maximum SndNxt.
+
+**/
+TCP_SEQNO
+TcpGetMaxSndNxt (
+ IN TCP_CB *Tcb
+ );
+
+//
+// Functions from Tcp4Input.c
+//
+/**
+ Process the received ICMP error messages for TCP.
+
+ @param Nbuf Buffer that contains part of the TCP segment without IP header
+ truncated from the ICMP error packet.
+ @param IcmpErr The ICMP error code interpreted from ICMP error packet.
+ @param Src Source address of the ICMP error message.
+ @param Dst Destination address of the ICMP error message.
+
+**/
+VOID
+TcpIcmpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT8 IcmpErr,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ );
+
+/**
+ Process the received TCP segments.
+
+ @param Nbuf Buffer that contains received TCP segment without IP header.
+ @param Src Source address of the segment, or the peer's IP address.
+ @param Dst Destination address of the segment, or the local end's IP
+ address.
+
+ @retval 0 Segment is processed successfully. It is either accepted or
+ discarded. But no connection is reset by the segment.
+ @retval -1 A connection is reset by the segment.
+
+**/
+INTN
+TcpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ );
+
+/**
+ Check whether the sequence number of the incoming segment is acceptable.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Pointer to the incoming segment.
+
+ @retval 1 The sequence number is acceptable.
+ @retval 0 The sequence number is not acceptable.
+
+**/
+INTN
+TcpSeqAcceptable (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ );
+
+/**
+ NewReno fast recovery, RFC3782.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Segment that triggers the fast recovery.
+
+**/
+VOID
+TcpFastRecover (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ );
+
+/**
+ NewReno fast loss recovery, RFC3792.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Segment that triggers the fast loss recovery.
+
+**/
+VOID
+TcpFastLossRecover (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ );
+
+/**
+ Compute the RTT as specified in RFC2988.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Measure Currently measured RTT in heart beats.
+
+**/
+VOID
+TcpComputeRtt (
+ IN OUT TCP_CB *Tcb,
+ IN UINT32 Measure
+ );
+
+/**
+ Trim off the data outside the tcb's receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the NET_BUF containing the received tcp segment.
+
+**/
+VOID
+TcpTrimInWnd (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Store the data into the reassemble queue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer containing the data to be queued.
+
+**/
+VOID
+TcpQueueData (
+ IN OUT TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Ajust the send queue or the retransmit queue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Ack The acknowledge seuqence number of the received segment.
+
+**/
+VOID
+TcpAdjustSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Ack
+ );
+
+//
+// Functions from Tcp4Misc.c
+//
+/**
+ Compute the TCP segment's checksum.
+
+ @param Nbuf Pointer to the buffer that contains the TCP
+ segment.
+ @param HeadSum The checksum value of the fixed part of pseudo
+ header.
+
+ @return The checksum value.
+
+**/
+UINT16
+TcpChecksum (
+ IN NET_BUF *Nbuf,
+ IN UINT16 HeadSum
+ );
+
+/**
+ Translate the information from the head of the received TCP
+ segment Nbuf contains and fill it into a TCP_SEG structure.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer contains the TCP segment.
+
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.
+
+**/
+TCP_SEG *
+TcpFormatNetbuf (
+ IN TCP_CB *Tcb,
+ IN OUT NET_BUF *Nbuf
+ );
+
+/**
+ Initialize an active connection.
+
+ @param Tcb Pointer to the TCP_CB that wants to initiate a
+ connection.
+
+**/
+VOID
+TcpOnAppConnect (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Application has consumed some data, check whether
+ to send a window updata ack or a delayed ack.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppConsume (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Initiate the connection close procedure, called when
+ applications want to close the connection.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppClose (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Check whether the application's newly delivered data can be sent out.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 Whether the data is sent out or is buffered for
+ further sending.
+ @retval -1 The Tcb is not in a state that data is permitted to
+ be sent out.
+
+**/
+INTN
+TcpOnAppSend (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Abort the connection by sending a reset segment, called
+ when the application wants to abort the connection.
+
+ @param Tcb Pointer to the TCP_CB of the TCP instance.
+
+**/
+VOID
+TcpOnAppAbort (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Reset the connection related with Tcb.
+
+ @param Tcb Pointer to the TCP_CB of the connection to be
+ reset.
+
+**/
+VOID
+TcpResetConnection (
+ IN TCP_CB *Tcb
+ );
+
+//
+// Functions in Tcp4Timer.c
+//
+/**
+ Close the TCP connection.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClose (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Heart beat timer handler, queues the DPC at TPL_CALLBACK.
+
+ @param Event Timer event signaled, ignored.
+ @param Context Context of the timer event, ignored.
+
+**/
+VOID
+EFIAPI
+TcpTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Enable a TCP timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Timer The index of the timer to be enabled.
+ @param TimeOut The timeout value of this timer.
+
+**/
+VOID
+TcpSetTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer,
+ IN UINT32 TimeOut
+ );
+
+/**
+ Clear one TCP timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Timer The index of the timer to be cleared.
+
+**/
+VOID
+TcpClearTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer
+ );
+
+/**
+ Clear all TCP timers.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClearAllTimer (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Enable the window prober timer and set the timeout value.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetProbeTimer (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Enable the keepalive timer and set the timeout value.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetKeepaliveTimer (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Backoff the RTO.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpBackoffRto (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Install the device path protocol on the TCP instance.
+
+ @param Sock Pointer to the socket representing the TCP instance.
+
+ @retval EFI_SUCCESS The device path protocol is installed.
+ @retval other Failed to install the device path protocol.
+
+**/
+EFI_STATUS
+TcpInstallDevicePath (
+ IN SOCKET *Sock
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c
new file mode 100644
index 0000000000..1000538f87
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c
@@ -0,0 +1,1478 @@
+/** @file
+ TCP input process routines.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Tcp4Main.h"
+
+
+/**
+ Check whether the sequence number of the incoming segment is acceptable.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Pointer to the incoming segment.
+
+ @retval 1 The sequence number is acceptable.
+ @retval 0 The sequence number is not acceptable.
+
+**/
+INTN
+TcpSeqAcceptable (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ return (TCP_SEQ_LEQ (Tcb->RcvNxt, Seg->End) &&
+ TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));
+}
+
+
+/**
+ NewReno fast recovery, RFC3782.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Segment that triggers the fast recovery.
+
+**/
+VOID
+TcpFastRecover (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ UINT32 FlightSize;
+ UINT32 Acked;
+
+ //
+ // Step 1: Three duplicate ACKs and not in fast recovery
+ //
+ if (Tcb->CongestState != TCP_CONGEST_RECOVER) {
+
+ //
+ // Step 1A: Invoking fast retransmission.
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+
+ Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));
+ Tcb->Recover = Tcb->SndNxt;
+
+ Tcb->CongestState = TCP_CONGEST_RECOVER;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+
+ //
+ // Step 2: Entering fast retransmission
+ //
+ TcpRetransmit (Tcb, Tcb->SndUna);
+ Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;
+
+ DEBUG ((EFI_D_NET, "TcpFastRecover: enter fast retransmission"
+ " for TCB %p, recover point is %d\n", Tcb, Tcb->Recover));
+ return;
+ }
+
+ //
+ // During fast recovery, execute Step 3, 4, 5 of RFC3782
+ //
+ if (Seg->Ack == Tcb->SndUna) {
+
+ //
+ // Step 3: Fast Recovery,
+ // If this is a duplicated ACK, increse Cwnd by SMSS.
+ //
+
+ // Step 4 is skipped here only to be executed later
+ // by TcpToSendData
+ //
+ Tcb->CWnd += Tcb->SndMss;
+ DEBUG ((EFI_D_NET, "TcpFastRecover: received another"
+ " duplicated ACK (%d) for TCB %p\n", Seg->Ack, Tcb));
+
+ } else {
+
+ //
+ // New data is ACKed, check whether it is a
+ // full ACK or partial ACK
+ //
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {
+
+ //
+ // Step 5 - Full ACK:
+ // deflate the congestion window, and exit fast recovery
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+
+ Tcb->CWnd = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+ DEBUG ((EFI_D_NET, "TcpFastRecover: received a full ACK(%d)"
+ " for TCB %p, exit fast recovery\n", Seg->Ack, Tcb));
+
+ } else {
+
+ //
+ // Step 5 - Partial ACK:
+ // fast retransmit the first unacknowledge field
+ // , then deflate the CWnd
+ //
+ TcpRetransmit (Tcb, Seg->Ack);
+ Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);
+
+ //
+ // Deflate the CWnd by the amount of new data
+ // ACKed by SEG.ACK. If more than one SMSS data
+ // is ACKed, add back SMSS byte to CWnd after
+ //
+ if (Acked >= Tcb->SndMss) {
+ Acked -= Tcb->SndMss;
+
+ }
+
+ Tcb->CWnd -= Acked;
+
+ DEBUG ((EFI_D_NET, "TcpFastRecover: received a partial"
+ " ACK(%d) for TCB %p\n", Seg->Ack, Tcb));
+
+ }
+ }
+}
+
+
+/**
+ NewReno fast loss recovery, RFC3792.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Segment that triggers the fast loss recovery.
+
+**/
+VOID
+TcpFastLossRecover (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ //
+ // New data is ACKed, check whether it is a
+ // full ACK or partial ACK
+ //
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {
+
+ //
+ // Full ACK: exit the loss recovery.
+ //
+ Tcb->LossTimes = 0;
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ DEBUG ((EFI_D_NET, "TcpFastLossRecover: received a "
+ "full ACK(%d) for TCB %p\n", Seg->Ack, Tcb));
+
+ } else {
+
+ //
+ // Partial ACK:
+ // fast retransmit the first unacknowledge field.
+ //
+ TcpRetransmit (Tcb, Seg->Ack);
+ DEBUG ((EFI_D_NET, "TcpFastLossRecover: received a "
+ "partial ACK(%d) for TCB %p\n", Seg->Ack, Tcb));
+ }
+ }
+}
+
+
+/**
+ Compute the RTT as specified in RFC2988.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Measure Currently measured RTT in heart beats.
+
+**/
+VOID
+TcpComputeRtt (
+ IN OUT TCP_CB *Tcb,
+ IN UINT32 Measure
+ )
+{
+ INT32 Var;
+
+ //
+ // Step 2.3: Compute the RTO for subsequent RTT measurement.
+ //
+ if (Tcb->SRtt != 0) {
+
+ Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);
+
+ if (Var < 0) {
+ Var = -Var;
+ }
+
+ Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;
+ Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure;
+
+ } else {
+ //
+ // Step 2.2: compute the first RTT measure
+ //
+ Tcb->SRtt = Measure << TCP_RTT_SHIFT;
+ Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);
+ }
+
+ Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;
+
+ //
+ // Step 2.4: Limit the RTO to at least 1 second
+ // Step 2.5: Limit the RTO to a maxium value that
+ // is at least 60 second
+ //
+ if (Tcb->Rto < TCP_RTO_MIN) {
+ Tcb->Rto = TCP_RTO_MIN;
+
+ } else if (Tcb->Rto > TCP_RTO_MAX) {
+ Tcb->Rto = TCP_RTO_MAX;
+
+ }
+
+ DEBUG ((EFI_D_NET, "TcpComputeRtt: new RTT for TCB %p"
+ " computed SRTT: %d RTTVAR: %d RTO: %d\n",
+ Tcb, Tcb->SRtt, Tcb->RttVar, Tcb->Rto));
+
+}
+
+
+/**
+ Trim the data, SYN and FIN to fit into the window defined by Left and Right.
+
+ @param Nbuf Buffer that contains received TCP segment without IP header.
+ @param Left The sequence number of the window's left edge.
+ @param Right The sequence number of the window's right edge.
+
+**/
+VOID
+TcpTrimSegment (
+ IN NET_BUF *Nbuf,
+ IN TCP_SEQNO Left,
+ IN TCP_SEQNO Right
+ )
+{
+ TCP_SEG *Seg;
+ TCP_SEQNO Urg;
+ UINT32 Drop;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // If the segment is completely out of window,
+ // truncate every thing, include SYN and FIN.
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+
+ Seg->Seq = Seg->End;
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);
+ return;
+ }
+
+ //
+ // Adjust the buffer header
+ //
+ if (TCP_SEQ_LT (Seg->Seq, Left)) {
+
+ Drop = TCP_SUB_SEQ (Left, Seg->Seq);
+ Urg = Seg->Seq + Seg->Urg;
+ Seg->Seq = Left;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ Drop--;
+ }
+
+ //
+ // Adjust the urgent point
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {
+
+ if (TCP_SEQ_LT (Urg, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
+ } else {
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);
+ }
+ }
+
+ if (Drop != 0) {
+ NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);
+ }
+ }
+
+ //
+ // Adjust the buffer tail
+ //
+ if (TCP_SEQ_GT (Seg->End, Right)) {
+
+ Drop = TCP_SUB_SEQ (Seg->End, Right);
+ Seg->End = Right;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+ Drop--;
+ }
+
+ if (Drop != 0) {
+ NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);
+ }
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+}
+
+
+/**
+ Trim off the data outside the tcb's receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the NET_BUF containing the received tcp segment.
+
+**/
+VOID
+TcpTrimInWnd (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);
+}
+
+
+/**
+ Process the data and FIN flag, check whether to deliver
+ data to the socket layer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 No error occurred to deliver data.
+ @retval -1 Error condition occurred. Proper response is to reset the
+ connection.
+
+**/
+INTN
+TcpDeliverData (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ TCP_SEQNO Seq;
+ TCP_SEG *Seg;
+ UINT32 Urgent;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ //
+ // make sure there is some data queued,
+ // and TCP is in a proper state
+ //
+ if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {
+
+ return 0;
+ }
+
+ //
+ // Deliver data to the socket layer
+ //
+ Entry = Tcb->RcvQue.ForwardLink;
+ Seq = Tcb->RcvNxt;
+
+ while (Entry != &Tcb->RcvQue) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+ ASSERT (Nbuf->Tcp == NULL);
+
+ if (TCP_SEQ_GT (Seg->Seq, Seq)) {
+ break;
+ }
+
+ Entry = Entry->ForwardLink;
+ Seq = Seg->End;
+ Tcb->RcvNxt = Seq;
+
+ RemoveEntryList (&Nbuf->List);
+
+ //
+ // RFC793 Eighth step: process FIN in sequence
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+
+ //
+ // The peer sends to us junky data after FIN,
+ // reset the connection.
+ //
+ if (!IsListEmpty (&Tcb->RcvQue)) {
+ DEBUG ((EFI_D_ERROR, "TcpDeliverData: data received after"
+ " FIN from peer of TCB %p, reset connection\n", Tcb));
+
+ NetbufFree (Nbuf);
+ return -1;
+ }
+
+ DEBUG ((EFI_D_NET, "TcpDeliverData: processing FIN "
+ "from peer of TCB %p\n", Tcb));
+
+ switch (Tcb->State) {
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+
+ TcpSetState (Tcb, TCP_CLOSE_WAIT);
+ break;
+
+ case TCP_FIN_WAIT_1:
+
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_CLOSING);
+ break;
+ }
+
+ //
+ // fall through
+ //
+ case TCP_FIN_WAIT_2:
+
+ TcpSetState (Tcb, TCP_TIME_WAIT);
+ TcpClearAllTimer (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ DEBUG ((EFI_D_WARN, "Connection closed immediately "
+ "because app disables TIME_WAIT timer for %p\n", Tcb));
+
+ TcpSendAck (Tcb);
+ TcpClose (Tcb);
+ }
+ break;
+
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ //
+ // The peer sends to us junk FIN byte. Discard
+ // the buffer then reset the connection
+ //
+ NetbufFree (Nbuf);
+ return -1;
+ default:
+ break;
+ }
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ Seg->End--;
+ }
+
+ //
+ // Don't delay the ack if PUSH flag is on.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+
+ if (Nbuf->TotalSize != 0) {
+ Urgent = 0;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)) {
+
+ if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {
+ Urgent = Nbuf->TotalSize;
+ } else {
+ Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;
+ }
+ }
+
+ SockDataRcvd (Tcb->Sk, Nbuf, Urgent);
+ }
+
+ if (TCP_FIN_RCVD (Tcb->State)) {
+
+ SockNoMoreData (Tcb->Sk);
+ }
+
+ NetbufFree (Nbuf);
+ }
+
+ return 0;
+}
+
+
+/**
+ Store the data into the reassemble queue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer containing the data to be queued.
+
+**/
+VOID
+TcpQueueData (
+ IN OUT TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_SEG *Seg;
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Prev;
+ LIST_ENTRY *Cur;
+ NET_BUF *Node;
+
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
+
+ NET_GET_REF (Nbuf);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Head = &Tcb->RcvQue;
+
+ //
+ // Fast path to process normal case. That is,
+ // no out-of-order segments are received.
+ //
+ if (IsListEmpty (Head)) {
+
+ InsertTailList (Head, &Nbuf->List);
+ return;
+ }
+
+ //
+ // Find the point to insert the buffer
+ //
+ for (Prev = Head, Cur = Head->ForwardLink;
+ Cur != Head;
+ Prev = Cur, Cur = Cur->ForwardLink) {
+
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {
+ break;
+ }
+ }
+
+ //
+ // Check whether the current segment overlaps with the
+ // previous segment.
+ //
+ if (Prev != Head) {
+ Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
+
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {
+
+ if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {
+
+ NetbufFree (Nbuf);
+ return;
+ }
+
+ TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);
+ }
+ }
+
+ InsertHeadList (Prev, &Nbuf->List);
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ //
+ // Check the segments after the insert point.
+ //
+ while (Cur != Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {
+
+ Cur = Cur->ForwardLink;
+
+ RemoveEntryList (&Node->List);
+ NetbufFree (Node);
+ continue;
+ }
+
+ if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {
+
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {
+
+ RemoveEntryList (&Nbuf->List);
+ NetbufFree (Nbuf);
+ return ;
+ }
+
+ TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);
+ break;
+ }
+
+ Cur = Cur->ForwardLink;
+ }
+}
+
+
+/**
+ Ajust the send queue or the retransmit queue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Ack The acknowledge seuqence number of the received segment.
+
+**/
+VOID
+TcpAdjustSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Ack
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Cur;
+ NET_BUF *Node;
+ TCP_SEG *Seg;
+
+ Head = &Tcb->SndQue;
+ Cur = Head->ForwardLink;
+
+ while (Cur != Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Node);
+
+ if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {
+ break;
+ }
+
+ //
+ // Remove completely ACKed segments
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Ack)) {
+ Cur = Cur->ForwardLink;
+
+ RemoveEntryList (&Node->List);
+ NetbufFree (Node);
+ continue;
+ }
+
+ TcpTrimSegment (Node, Ack, Seg->End);
+ break;
+ }
+}
+
+
+/**
+ Process the received TCP segments.
+
+ @param Nbuf Buffer that contains received TCP segment without IP header.
+ @param Src Source address of the segment, or the peer's IP address.
+ @param Dst Destination address of the segment, or the local end's IP
+ address.
+
+ @retval 0 Segment is processed successfully. It is either accepted or
+ discarded. But no connection is reset by the segment.
+ @retval -1 A connection is reset by the segment.
+
+**/
+INTN
+TcpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ )
+{
+ TCP_CB *Tcb;
+ TCP_CB *Parent;
+ TCP_OPTION Option;
+ TCP_HEAD *Head;
+ INT32 Len;
+ TCP_SEG *Seg;
+ TCP_SEQNO Right;
+ TCP_SEQNO Urg;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Parent = NULL;
+ Tcb = NULL;
+
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Head != NULL);
+
+ if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {
+ DEBUG ((EFI_D_NET, "TcpInput: received a malformed packet\n"));
+ goto DISCARD;
+ }
+
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);
+
+ if ((Head->HeadLen < 5) || (Len < 0) ||
+ (TcpChecksum (Nbuf, NetPseudoHeadChecksum (Src, Dst, 6, 0)) != 0)) {
+
+ DEBUG ((EFI_D_NET, "TcpInput: received a malformed packet\n"));
+ goto DISCARD;
+ }
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {
+ Len++;
+ }
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {
+ Len++;
+ }
+
+ Tcb = TcpLocateTcb (
+ Head->DstPort,
+ Dst,
+ Head->SrcPort,
+ Src,
+ (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)
+ );
+
+ if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {
+ DEBUG ((EFI_D_NET, "TcpInput: send reset because no TCB found\n"));
+
+ Tcb = NULL;
+ goto SEND_RESET;
+ }
+
+ Seg = TcpFormatNetbuf (Tcb, Nbuf);
+
+ //
+ // RFC1122 recommended reaction to illegal option
+ // (in fact, an illegal option length) is reset.
+ //
+ if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {
+ DEBUG ((EFI_D_ERROR, "TcpInput: reset the peer because"
+ " of malformed option for TCB %p\n", Tcb));
+
+ goto SEND_RESET;
+ }
+
+ //
+ // From now on, the segment is headless
+ //
+ NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ //
+ // Process the segment in LISTEN state.
+ //
+ if (Tcb->State == TCP_LISTEN) {
+ //
+ // First step: Check RST
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+ DEBUG ((EFI_D_WARN, "TcpInput: discard a reset segment "
+ "for TCB %p in listening\n", Tcb));
+
+ goto DISCARD;
+ }
+
+ //
+ // Second step: Check ACK.
+ // Any ACK sent to TCP in LISTEN is reseted.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ DEBUG ((EFI_D_WARN, "TcpInput: send reset because of"
+ " segment with ACK for TCB %p in listening\n", Tcb));
+
+ goto SEND_RESET;
+ }
+
+ //
+ // Third step: Check SYN
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ //
+ // create a child TCB to handle the data
+ //
+ Parent = Tcb;
+
+ Tcb = TcpCloneTcb (Parent);
+ if (Tcb == NULL) {
+ DEBUG ((EFI_D_ERROR, "TcpInput: discard a segment because"
+ " failed to clone a child for TCB %p\n", Tcb));
+
+ goto DISCARD;
+ }
+
+ DEBUG ((EFI_D_NET, "TcpInput: create a child for TCB %p"
+ " in listening\n", Tcb));
+
+ //
+ // init the TCB structure
+ //
+ Tcb->LocalEnd.Ip = Dst;
+ Tcb->LocalEnd.Port = Head->DstPort;
+ Tcb->RemoteEnd.Ip = Src;
+ Tcb->RemoteEnd.Port = Head->SrcPort;
+
+ TcpInitTcbLocal (Tcb);
+ TcpInitTcbPeer (Tcb, Seg, &Option);
+
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ goto StepSix;
+ }
+
+ goto DISCARD;
+
+ } else if (Tcb->State == TCP_SYN_SENT) {
+ //
+ // First step: Check ACK bit
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: send reset because of "
+ "wrong ACK received for TCB %p in SYN_SENT\n", Tcb));
+
+ goto SEND_RESET;
+ }
+
+ //
+ // Second step: Check RST bit
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset by"
+ " peer for TCB %p in SYN_SENT\n", Tcb));
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+ goto DROP_CONNECTION;
+ } else {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: discard a reset segment "
+ "because of no ACK for TCB %p in SYN_SENT\n", Tcb));
+
+ goto DISCARD;
+ }
+ }
+
+ //
+ // Third step: Check security and precedence. Skipped
+ //
+
+ //
+ // Fourth step: Check SYN. Pay attention to simultaneous open
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ TcpInitTcbPeer (Tcb, Seg, &Option);
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+
+ Tcb->SndUna = Seg->Ack;
+ }
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+
+ if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {
+
+ TcpSetState (Tcb, TCP_ESTABLISHED);
+
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);
+ TcpDeliverData (Tcb);
+
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ }
+
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ DEBUG ((EFI_D_NET, "TcpInput: connection established"
+ " for TCB %p in SYN_SENT\n", Tcb));
+
+ goto StepSix;
+ } else {
+ //
+ // Received a SYN segment without ACK, simultaneous open.
+ //
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+
+ ASSERT (Tcb->SndNxt == Tcb->Iss + 1);
+ TcpAdjustSndQue (Tcb, Tcb->SndNxt);
+
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ DEBUG ((EFI_D_WARN, "TcpInput: simultaneous open "
+ "for TCB %p in SYN_SENT\n", Tcb));
+
+ goto StepSix;
+ }
+ }
+
+ goto DISCARD;
+ }
+
+ //
+ // Process segment in SYN_RCVD or TCP_CONNECTED states
+ //
+
+ //
+ // Clear probe timer since the RecvWindow is opened.
+ //
+ if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) {
+ TcpClearTimer (Tcb, TCP_TIMER_PROBE);
+ Tcb->ProbeTimerOn = FALSE;
+ }
+
+ //
+ // First step: Check whether SEG.SEQ is acceptable
+ //
+ if (TcpSeqAcceptable (Tcb, Seg) == 0) {
+ DEBUG ((EFI_D_WARN, "TcpInput: sequence acceptance"
+ " test failed for segment of TCB %p\n", Tcb));
+
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+ TcpSendAck (Tcb);
+ }
+
+ goto DISCARD;
+ }
+
+ if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&
+ (Tcb->RcvWl2 == Seg->End) &&
+ !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+
+ //
+ // Second step: Check the RST
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb));
+
+ if (Tcb->State == TCP_SYN_RCVD) {
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);
+
+ //
+ // This TCB comes from either a LISTEN TCB,
+ // or active open TCB with simultanous open.
+ // Do NOT signal user CONNECTION refused
+ // if it comes from a LISTEN TCB.
+ //
+ } else if ((Tcb->State == TCP_ESTABLISHED) ||
+ (Tcb->State == TCP_FIN_WAIT_1) ||
+ (Tcb->State == TCP_FIN_WAIT_2) ||
+ (Tcb->State == TCP_CLOSE_WAIT)) {
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+
+ } else {
+
+ }
+
+ goto DROP_CONNECTION;
+ }
+
+ //
+ // Trim the data and flags.
+ //
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ //
+ // Third step: Check security and precedence, Ignored
+ //
+
+ //
+ // Fourth step: Check the SYN bit.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset "
+ "because received extra SYN for TCB %p\n", Tcb));
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+ goto RESET_THEN_DROP;
+ }
+
+ //
+ // Fifth step: Check the ACK
+ //
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ DEBUG ((EFI_D_WARN, "TcpInput: segment discard because"
+ " of no ACK for connected TCB %p\n", Tcb));
+
+ goto DISCARD;
+
+ }
+
+ if (Tcb->State == TCP_SYN_RCVD) {
+
+ if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) &&
+ TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);
+ Tcb->SndWl1 = Seg->Seq;
+ Tcb->SndWl2 = Seg->Ack;
+ TcpSetState (Tcb, TCP_ESTABLISHED);
+
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);
+ TcpDeliverData (Tcb);
+
+ DEBUG ((EFI_D_NET, "TcpInput: connection established "
+ " for TCB %p in SYN_RCVD\n", Tcb));
+
+ //
+ // Continue the process as ESTABLISHED state
+ //
+ } else {
+ DEBUG ((EFI_D_WARN, "TcpInput: send reset because of"
+ " wrong ACK for TCB %p in SYN_RCVD\n", Tcb));
+
+ goto SEND_RESET;
+ }
+ }
+
+ if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: ignore the out-of-data"
+ " ACK for connected TCB %p\n", Tcb));
+
+ goto StepSix;
+
+ } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: discard segment for "
+ "future ACK for connected TCB %p\n", Tcb));
+
+ TcpSendAck (Tcb);
+ goto DISCARD;
+ }
+
+ //
+ // From now on: SND.UNA <= SEG.ACK <= SND.NXT.
+ //
+ if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {
+ //
+ // update TsRecent as specified in page 16 RFC1323.
+ // RcvWl2 equals to the variable "LastAckSent"
+ // defined there.
+ //
+ if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) &&
+ TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {
+
+ Tcb->TsRecent = Option.TSVal;
+ Tcb->TsRecentAge = mTcpTick;
+ }
+
+ TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));
+
+ } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);
+
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ }
+
+ if (Seg->Ack == Tcb->SndNxt) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+ } else {
+
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+ }
+
+ //
+ // Count duplicate acks.
+ //
+ if ((Seg->Ack == Tcb->SndUna) &&
+ (Tcb->SndUna != Tcb->SndNxt) &&
+ (Seg->Wnd == Tcb->SndWnd) &&
+ (0 == Len)) {
+
+ Tcb->DupAck++;
+ } else {
+
+ Tcb->DupAck = 0;
+ }
+
+ //
+ // Congestion avoidance, fast recovery and fast retransmission.
+ //
+ if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||
+ (Tcb->CongestState == TCP_CONGEST_LOSS)) {
+
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ if (Tcb->CWnd < Tcb->Ssthresh) {
+
+ Tcb->CWnd += Tcb->SndMss;
+ } else {
+
+ Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);
+ }
+
+ Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);
+ }
+
+ if (Tcb->CongestState == TCP_CONGEST_LOSS) {
+ TcpFastLossRecover (Tcb, Seg);
+ }
+ } else {
+
+ TcpFastRecover (Tcb, Seg);
+ }
+
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ TcpAdjustSndQue (Tcb, Seg->Ack);
+ Tcb->SndUna = Seg->Ack;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&
+ TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)) {
+
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
+ }
+ }
+
+ //
+ // Update window info
+ //
+ if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||
+ ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))) {
+
+ Right = Seg->Ack + Seg->Wnd;
+
+ if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {
+
+ if ((Tcb->SndWl1 == Seg->Seq) &&
+ (Tcb->SndWl2 == Seg->Ack) &&
+ (Len == 0)) {
+
+ goto NO_UPDATE;
+ }
+
+ DEBUG ((EFI_D_WARN, "TcpInput: peer shrinks the"
+ " window for connected TCB %p\n", Tcb));
+
+ if ((Tcb->CongestState == TCP_CONGEST_RECOVER) &&
+ (TCP_SEQ_LT (Right, Tcb->Recover))) {
+
+ Tcb->Recover = Right;
+ }
+
+ if ((Tcb->CongestState == TCP_CONGEST_LOSS) &&
+ (TCP_SEQ_LT (Right, Tcb->LossRecover))) {
+
+ Tcb->LossRecover = Right;
+ }
+
+ if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {
+
+ Tcb->SndNxt = Right;
+
+ if (Right == Tcb->SndUna) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+ TcpSetProbeTimer (Tcb);
+ }
+ }
+ }
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);
+ Tcb->SndWl1 = Seg->Seq;
+ Tcb->SndWl2 = Seg->Ack;
+ }
+
+NO_UPDATE:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) &&
+ (Tcb->SndUna == Tcb->SndNxt)) {
+
+ DEBUG ((EFI_D_NET, "TcpInput: local FIN is ACKed by"
+ " peer for connected TCB %p\n", Tcb));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);
+ }
+
+ //
+ // Transit the state if proper.
+ //
+ switch (Tcb->State) {
+ case TCP_FIN_WAIT_1:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_FIN_WAIT_2);
+
+ TcpClearAllTimer (Tcb);
+ TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);
+ }
+
+ case TCP_FIN_WAIT_2:
+
+ break;
+
+ case TCP_CLOSE_WAIT:
+ break;
+
+ case TCP_CLOSING:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_TIME_WAIT);
+
+ TcpClearAllTimer (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ DEBUG ((EFI_D_WARN, "Connection closed immediately "
+ "because app disables TIME_WAIT timer for %p\n", Tcb));
+
+ TcpClose (Tcb);
+ }
+ }
+ break;
+
+ case TCP_LAST_ACK:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_CLOSED);
+ }
+
+ break;
+
+ case TCP_TIME_WAIT:
+
+ TcpSendAck (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ DEBUG ((EFI_D_WARN, "Connection closed immediately "
+ "because app disables TIME_WAIT timer for %p\n", Tcb));
+
+ TcpClose (Tcb);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ //
+ // Sixth step: Check the URG bit.update the Urg point
+ // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.
+ //
+StepSix:
+
+ Tcb->Idle = 0;
+ TcpSetKeepaliveTimer (Tcb);
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) &&
+ !TCP_FIN_RCVD (Tcb->State))
+ {
+
+ DEBUG ((EFI_D_NET, "TcpInput: received urgent data "
+ "from peer for connected TCB %p\n", Tcb));
+
+ Urg = Seg->Seq + Seg->Urg;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&
+ TCP_SEQ_GT (Urg, Tcb->RcvUp)) {
+
+ Tcb->RcvUp = Urg;
+ } else {
+
+ Tcb->RcvUp = Urg;
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);
+ }
+ }
+
+ //
+ // Seventh step: Process the segment data
+ //
+ if (Seg->End != Seg->Seq) {
+
+ if (TCP_FIN_RCVD (Tcb->State)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset because"
+ " data is lost for connected TCB %p\n", Tcb));
+
+ goto RESET_THEN_DROP;
+ }
+
+ if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset because"
+ " data is lost for connected TCB %p\n", Tcb));
+
+ goto RESET_THEN_DROP;
+ }
+
+ TcpQueueData (Tcb, Nbuf);
+ if (TcpDeliverData (Tcb) == -1) {
+ goto RESET_THEN_DROP;
+ }
+
+ if (!IsListEmpty (&Tcb->RcvQue)) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+ }
+
+ //
+ // Eighth step: check the FIN.
+ // This step is moved to TcpDeliverData. FIN will be
+ // processed in sequence there. Check the comments in
+ // the beginning of the file header for information.
+ //
+
+ //
+ // Tcb is a new child of the listening Parent,
+ // commit it.
+ //
+ if (Parent != NULL) {
+ Tcb->Parent = Parent;
+ TcpInsertTcb (Tcb);
+ }
+
+ if ((Tcb->State != TCP_CLOSED) &&
+ (TcpToSendData (Tcb, 0) == 0) &&
+ (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))) {
+
+ TcpToSendAck (Tcb);
+ }
+
+ NetbufFree (Nbuf);
+ return 0;
+
+RESET_THEN_DROP:
+ TcpSendReset (Tcb, Head, Len, Dst, Src);
+
+DROP_CONNECTION:
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ NetbufFree (Nbuf);
+ TcpClose (Tcb);
+
+ return -1;
+
+SEND_RESET:
+
+ TcpSendReset (Tcb, Head, Len, Dst, Src);
+
+DISCARD:
+
+ //
+ // Tcb is a child of Parent, and it doesn't survive
+ //
+ DEBUG ((EFI_D_WARN, "Tcp4Input: Discard a packet\n"));
+ NetbufFree (Nbuf);
+
+ if ((Parent != NULL) && (Tcb != NULL)) {
+
+ ASSERT (Tcb->Sk != NULL);
+ TcpClose (Tcb);
+ }
+
+ return 0;
+}
+
+
+/**
+ Process the received ICMP error messages for TCP.
+
+ @param Nbuf Buffer that contains part of the TCP segment without IP header
+ truncated from the ICMP error packet.
+ @param IcmpErr The ICMP error code interpreted from ICMP error packet.
+ @param Src Source address of the ICMP error message.
+ @param Dst Destination address of the ICMP error message.
+
+**/
+VOID
+TcpIcmpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT8 IcmpErr,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ )
+{
+ TCP_HEAD *Head;
+ TCP_CB *Tcb;
+ TCP_SEQNO Seq;
+ EFI_STATUS IcmpErrStatus;
+ BOOLEAN IcmpErrIsHard;
+ BOOLEAN IcmpErrNotify;
+
+ if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {
+ goto CLEAN_EXIT;
+ }
+
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Head != NULL);
+ Tcb = TcpLocateTcb (
+ Head->DstPort,
+ Dst,
+ Head->SrcPort,
+ Src,
+ FALSE
+ );
+ if (Tcb == NULL || Tcb->State == TCP_CLOSED) {
+
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Validate the sequence number.
+ //
+ Seq = NTOHL (Head->Seq);
+ if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {
+
+ goto CLEAN_EXIT;
+ }
+
+ IcmpErrStatus = IpIoGetIcmpErrStatus (
+ IcmpErr,
+ IP_VERSION_4,
+ &IcmpErrIsHard,
+ &IcmpErrNotify
+ );
+
+ if (IcmpErrNotify) {
+
+ SOCK_ERROR (Tcb->Sk, IcmpErrStatus);
+ }
+
+ if (IcmpErrIsHard) {
+
+ TcpClose (Tcb);
+ }
+
+CLEAN_EXIT:
+ NetbufFree (Nbuf);
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
new file mode 100644
index 0000000000..b98d1e783a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
@@ -0,0 +1,112 @@
+/** @file
+ I/O interfaces between TCP and IpIo.
+
+Copyright (c) 2005 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+
+/**
+ Packet receive callback function provided to IP_IO, used to call
+ the proper function to handle the packet received by IP.
+
+ @param Status Status of the received packet.
+ @param IcmpErr ICMP error number.
+ @param NetSession Pointer to the net session of this packet.
+ @param Pkt Pointer to the recieved packet.
+ @param Context Pointer to the context configured in IpIoOpen(), not used
+ now.
+
+**/
+VOID
+EFIAPI
+Tcp4RxCallback (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpErr,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Pkt,
+ IN VOID *Context OPTIONAL
+ )
+{
+ if (EFI_SUCCESS == Status) {
+ TcpInput (Pkt, NetSession->Source.Addr[0], NetSession->Dest.Addr[0]);
+ } else {
+ TcpIcmpInput (Pkt, IcmpErr, NetSession->Source.Addr[0], NetSession->Dest.Addr[0]);
+ }
+}
+
+
+/**
+ Send the segment to IP via IpIo function.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the TCP segment to be sent.
+ @param Src Source address of the TCP segment.
+ @param Dest Destination address of the TCP segment.
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 The segment was failed to send.
+
+**/
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dest
+ )
+{
+ EFI_STATUS Status;
+ IP_IO *IpIo;
+ IP_IO_OVERRIDE Override;
+ SOCKET *Sock;
+ VOID *IpSender;
+ TCP4_PROTO_DATA *TcpProto;
+ EFI_IP_ADDRESS Source;
+ EFI_IP_ADDRESS Destination;
+
+ Source.Addr[0] = Src;
+ Destination.Addr[0] = Dest;
+
+ if (NULL == Tcb) {
+
+ IpIo = NULL;
+ IpSender = IpIoFindSender (&IpIo, IP_VERSION_4, &Source);
+
+ if (IpSender == NULL) {
+ DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n"));
+ return -1;
+ }
+ } else {
+
+ Sock = Tcb->Sk;
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ IpIo = TcpProto->TcpService->IpIo;
+ IpSender = Tcb->IpInfo;
+ }
+
+ Override.Ip4OverrideData.TypeOfService = 0;
+ Override.Ip4OverrideData.TimeToLive = 255;
+ Override.Ip4OverrideData.DoNotFragment = FALSE;
+ Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_TCP;
+ ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Override.Ip4OverrideData.SourceAddress, &Src, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, &Destination, &Override);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
new file mode 100644
index 0000000000..2679f1b8a0
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
@@ -0,0 +1,674 @@
+/** @file
+ Implementation of TCP4 protocol services.
+
+Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Tcp4Main.h"
+
+
+/**
+ Check the integrity of the data buffer.
+
+ @param DataLen The total length of the data buffer.
+ @param FragmentCount The fragment count of the fragment table.
+ @param FragmentTable Pointer to the fragment table of the data
+ buffer.
+
+ @retval EFI_SUCCESS The integrity check is passed.
+ @retval EFI_INVALID_PARAMETER The integrity check is failed.
+
+**/
+EFI_STATUS
+Tcp4ChkDataBuf (
+ IN UINT32 DataLen,
+ IN UINT32 FragmentCount,
+ IN EFI_TCP4_FRAGMENT_DATA *FragmentTable
+ )
+{
+ UINT32 Index;
+
+ UINT32 Len;
+
+ for (Index = 0, Len = 0; Index < FragmentCount; Index++) {
+ Len = Len + (UINT32) FragmentTable[Index].FragmentLength;
+ }
+
+ if (DataLen != Len) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the current operational status.
+
+ The GetModeData() function copies the current operational settings of this
+ EFI TCPv4 Protocol instance into user-supplied buffers. This function can
+ also be used to retrieve the operational setting of underlying drivers
+ such as IPv4, MNP, or SNP.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Tcp4State Pointer to the buffer to receive the current TCP
+ state.
+ @param Tcp4ConfigData Pointer to the buffer to receive the current TCP
+ configuration.
+ @param Ip4ModeData Pointer to the buffer to receive the current IPv4
+ configuration data used by the TCPv4 instance.
+ @param MnpConfigData Pointer to the buffer to receive the current MNP
+ configuration data indirectly used by the TCPv4
+ Instance.
+ @param SnpModeData Pointer to the buffer to receive the current SNP
+ configuration data indirectly used by the TCPv4
+ Instance.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4GetModeData (
+ IN EFI_TCP4_PROTOCOL *This,
+ OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL,
+ OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ TCP4_MODE_DATA TcpMode;
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ TcpMode.Tcp4State = Tcp4State;
+ TcpMode.Tcp4ConfigData = Tcp4ConfigData;
+ TcpMode.Ip4ModeData = Ip4ModeData;
+ TcpMode.MnpConfigData = MnpConfigData;
+ TcpMode.SnpModeData = SnpModeData;
+
+ return SockGetMode (Sock, &TcpMode);
+}
+
+
+/**
+ Initialize or brutally reset the operational parameters for
+ this EFI TCPv4 instance.
+
+ The Configure() function does the following:
+ * Initialize this EFI TCPv4 instance, i.e., initialize the communication end
+ setting, specify active open or passive open for an instance.
+ * Reset this TCPv4 instance brutally, i.e., cancel all pending asynchronous
+ tokens, flush transmission and receiving buffer directly without informing
+ the communication peer.
+ No other TCPv4 Protocol operation can be executed by this instance
+ until it is configured properly. For an active TCP4 instance, after a proper
+ configuration it may call Connect() to initiates the three-way handshake.
+ For a passive TCP4 instance, its state will transit to Tcp4StateListen after
+ configuration, and Accept() may be called to listen the incoming TCP connection
+ request. If TcpConfigData is set to NULL, the instance is reset. Resetting
+ process will be done brutally, the state machine will be set to Tcp4StateClosed
+ directly, the receive queue and transmit queue will be flushed, and no traffic is
+ allowed through this instance.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param TcpConfigData Pointer to the configure data to configure the
+ instance.
+
+ @retval EFI_SUCCESS The operational settings are set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Configure (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONFIG_DATA *TcpConfigData OPTIONAL
+ )
+{
+ EFI_TCP4_OPTION *Option;
+ SOCKET *Sock;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+ IP4_ADDR SubnetMask;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Tcp protocol related parameter check will be conducted here
+ //
+ if (NULL != TcpConfigData) {
+
+ CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
+ if (IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TcpConfigData->AccessPoint.ActiveFlag &&
+ (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!TcpConfigData->AccessPoint.UseDefaultAddress) {
+
+ CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));
+ CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR));
+ if (!IP4_IS_VALID_NETMASK (NTOHL (SubnetMask)) ||
+ (SubnetMask != 0 && !NetIp4IsUnicast (NTOHL (Ip), NTOHL (SubnetMask)))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Option = TcpConfigData->ControlOption;
+ if ((NULL != Option) &&
+ (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ if (NULL == TcpConfigData) {
+ return SockFlush (Sock);
+ }
+
+ Status = SockConfigure (Sock, TcpConfigData);
+
+ if (EFI_NO_MAPPING == Status) {
+ Sock->ConfigureState = SO_NO_MAPPING;
+ }
+
+ return Status;
+}
+
+
+/**
+ Add or delete routing entries.
+
+ The Routes() function adds or deletes a route from the instance's routing table.
+ The most specific route is selected by comparing the SubnetAddress with the
+ destination IP address's arithmetical AND to the SubnetMask.
+ The default route is added with both SubnetAddress and SubnetMask set to 0.0.0.0.
+ The default route matches all destination IP addresses if there is no more specific route.
+ Direct route is added with GatewayAddress set to 0.0.0.0. Packets are sent to
+ the destination host if its address can be found in the Address Resolution Protocol (ARP)
+ cache or it is on the local subnet. If the instance is configured to use default
+ address, a direct route to the local network will be added automatically.
+ Each TCP instance has its own independent routing table. Instance that uses the
+ default IP address will have a copy of the EFI_IP4_CONFIG_PROTOCOL's routing table.
+ The copy will be updated automatically whenever the IP driver reconfigures its
+ instance. As a result, the previous modification to the instance's local copy
+ will be lost. The priority of checking the route table is specific with IP
+ implementation and every IP implementation must comply with RFC 1122.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param DeleteRoute If TRUE, delete the specified route from routing
+ table; if FALSE, add the specified route to
+ routing table.
+ DestinationAddress and SubnetMask are used as
+ the keywords to search route entry.
+ @param SubnetAddress The destination network.
+ @param SubnetMask The subnet mask for the destination network.
+ @param GatewayAddress The gateway address for this route.
+ It must be on the same subnet with the station
+ address unless a direct route is specified.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the
+ entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Routes (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ SOCKET *Sock;
+ TCP4_ROUTE_INFO RouteInfo;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ RouteInfo.DeleteRoute = DeleteRoute;
+ RouteInfo.SubnetAddress = SubnetAddress;
+ RouteInfo.SubnetMask = SubnetMask;
+ RouteInfo.GatewayAddress = GatewayAddress;
+
+ return SockRoute (Sock, &RouteInfo);
+}
+
+
+/**
+ Initiate a nonblocking TCP connection request for an active TCP instance.
+
+ The Connect() function will initiate an active open to the remote peer configured
+ in current TCP instance if it is configured active. If the connection succeeds
+ or fails due to any error, the ConnectionToken->CompletionToken.Event will be
+ signaled and ConnectionToken->CompletionToken.Status will be updated accordingly.
+ This function can only be called for the TCP instance in Tcp4StateClosed state.
+ The instance will transfer into Tcp4StateSynSent if the function returns EFI_SUCCESS.
+ If TCP three way handshake succeeds, its state will become Tcp4StateEstablished,
+ otherwise, the state will return to Tcp4StateClosed.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param ConnectionToken Pointer to the connection token to return when
+ the TCP three way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request is successfully initiated
+ and the state of this TCPv4 instance has
+ been changed to Tcp4StateSynSent.
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one
+ or it is not in Tcp4StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resource to
+ initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Connect (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This ||
+ NULL == ConnectionToken ||
+ NULL == ConnectionToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockConnect (Sock, ConnectionToken);
+}
+
+
+/**
+ Listen on the passive instance to accept an incoming connection request.
+
+ The Accept() function initiates an asynchronous accept request to wait for an
+ incoming connection on the passive TCP instance. If a remote peer successfully
+ establishes a connection with this instance, a new TCP instance will be created
+ and its handle will be returned in ListenToken->NewChildHandle. The newly created
+ instance is configured by inheriting the passive instance's configuration and is
+ ready for use upon return. The instance is in the Tcp4StateEstablished state.
+ The ListenToken->CompletionToken.Event will be signaled when a new connection
+ is accepted, user aborts the listen or connection is reset. This function only
+ can be called when current TCP instance is in Tcp4StateListen state.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param ListenToken Pointer to the listen token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The listen token has been queued successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not
+ in Tcp4StateListen state or a same listen token
+ has already existed in the listen token queue of
+ this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish
+ the operation.
+ @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above category error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Accept (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This ||
+ NULL == ListenToken ||
+ NULL == ListenToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockAccept (Sock, ListenToken);
+}
+
+
+/**
+ Queues outgoing data into the transmit queue.
+
+ The Transmit() function queues a sending request to this TCPv4 instance along
+ with the user data. The status of the token is updated and the event in the token
+ will be signaled once the data is sent out or some error occurs.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param Token Pointer to the completion token to queue to the
+ transmit queue
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A transmit completion token with the same
+ Token-> CompletionToken.Event was already in the
+ transmission queue.
+ * The current instance is in Tcp4StateClosed state
+ * The current instance is a passive one and
+ it is in Tcp4StateListen state.
+ * User has called Close() to disconnect this
+ connection.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of
+ resource shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or
+ address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Transmit (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.TxData ||
+ 0 == Token->Packet.TxData->FragmentCount ||
+ 0 == Token->Packet.TxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Tcp4ChkDataBuf (
+ (UINT32) Token->Packet.TxData->DataLength,
+ (UINT32) Token->Packet.TxData->FragmentCount,
+ Token->Packet.TxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockSend (Sock, Token);
+
+}
+
+
+/**
+ Place an asynchronous receive request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous. The caller must allocate the
+ Token->CompletionToken.Event and the FragmentBuffer used to receive data. He also
+ must fill the DataLength which represents the whole length of all FragmentBuffer.
+ When the receive operation completes, the EFI TCPv4 Protocol driver updates the
+ Token->CompletionToken.Status and Token->Packet.RxData fields and the
+ Token->CompletionToken.Event is signaled. If got data the data and its length
+ will be copy into the FragmentTable, in the same time the full length of received
+ data will be recorded in the DataLength fields. Providing a proper notification
+ function and context for the event will enable the user to receive the notification
+ and receiving status. That notification function is guaranteed to not be re-entered.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI TCPv4 Protocol instance has been reset
+ to startup defaults.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A receive completion token with the same
+ Token->CompletionToken.Event was already in
+ the receive queue.
+ * The current instance is in Tcp4StateClosed state.
+ * The current instance is a passive one and it
+ is in Tcp4StateListen state.
+ * User has called Close() to disconnect this
+ connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection
+ and there is no any buffered data in the receive
+ buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Receive (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.RxData ||
+ 0 == Token->Packet.RxData->FragmentCount ||
+ 0 == Token->Packet.RxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Tcp4ChkDataBuf (
+ (UINT32) Token->Packet.RxData->DataLength,
+ (UINT32) Token->Packet.RxData->FragmentCount,
+ Token->Packet.RxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockRcv (Sock, Token);
+
+}
+
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection.
+
+ Initiate an asynchronous close token to TCP driver. After Close() is called,
+ any buffered transmission data will be sent by TCP driver and the current
+ instance will have a graceful close working flow described as RFC 793 if
+ AbortOnClose is set to FALSE, otherwise, a rest packet will be sent by TCP
+ driver to fast disconnect this connection. When the close operation completes
+ successfully the TCP instance is in Tcp4StateClosed state, all pending
+ asynchronous operation is signaled and any buffers used for TCP network traffic
+ is flushed.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param CloseToken Pointer to the close token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:
+ * Configure() has been called with TcpConfigData
+ set to NULL and this function has not returned.
+ * Previous Close() call on this instance has not
+ finished.
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the
+ operation.
+ @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above
+ category error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Close (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This ||
+ NULL == CloseToken ||
+ NULL == CloseToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);
+}
+
+
+/**
+ Abort an asynchronous connection, listen, transmission or receive request.
+
+ The Cancel() function aborts a pending connection, listen, transmit or receive
+ request. If Token is not NULL and the token is in the connection, listen,
+ transmission or receive queue when it is being cancelled, its Token->Status
+ will be set to EFI_ABORTED and then Token->Event will be signaled. If the token
+ is not in one of the queues, which usually means that the asynchronous operation
+ has completed, EFI_NOT_FOUND is returned. If Token is NULL all asynchronous token
+ issued by Connect(), Accept(), Transmit() and Receive()will be aborted.
+ NOTE: It has not been implemented currently.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token Pointer to a token that has been issued by
+ Connect(), Accept(), Transmit() or Receive(). If
+ NULL, all pending tokens issued by above four
+ functions will be aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn's been configured.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP,RARP, etc.) hasn's finished yet.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn's found in the
+ transmission or receive queue. It has either
+ completed or wasn's issued by Transmit() and Receive().
+ @retval EFI_UNSUPPORTED The operation is not supported in current
+ implementation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ The Poll() function increases the rate that data is moved between the network
+ and application and can be called when the TCP instance is created successfully.
+ Its use is optional. In some implementations, the periodical timer in the MNP
+ driver may not poll the underlying communications device fast enough to avoid
+ drop packets. Drivers and applications that are experiencing packet loss should
+ try calling the Poll() function in a high frequency.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or
+ receive queue. Consider increasing the polling
+ rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Poll (
+ IN EFI_TCP4_PROTOCOL *This
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
new file mode 100644
index 0000000000..cd904584bf
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
@@ -0,0 +1,494 @@
+/** @file
+ TCP4 protocol services header file.
+
+Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP4_MAIN_H_
+#define _TCP4_MAIN_H_
+
+#include "Socket.h"
+
+#include "Tcp4Proto.h"
+#include "Tcp4Func.h"
+#include "Tcp4Driver.h"
+
+
+extern UINT16 mTcp4RandomPort;
+extern CHAR16 *mTcpStateName[];
+
+//
+// Driver Produced Protocol Prototypes
+//
+
+//
+// Function prototype for the Tcp4 socket request handler
+//
+/**
+ The procotol handler provided to the socket layer, used to
+ dispatch the socket level requests by calling the corresponding
+ TCP layer functions.
+
+ @param Sock Pointer to the socket of this TCP instance.
+ @param Request The code of this operation request.
+ @param Data Pointer to the operation specific data passed in
+ together with the operation request.
+
+ @retval EFI_SUCCESS The socket request is completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+EFI_STATUS
+Tcp4Dispatcher (
+ IN SOCKET *Sock,
+ IN UINT8 Request,
+ IN VOID *Data OPTIONAL
+ );
+
+///
+/// TCP mode data
+///
+typedef struct _TCP4_MODE_DATA {
+ EFI_TCP4_CONNECTION_STATE *Tcp4State;
+ EFI_TCP4_CONFIG_DATA *Tcp4ConfigData;
+ EFI_IP4_MODE_DATA *Ip4ModeData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE *SnpModeData;
+} TCP4_MODE_DATA;
+
+///
+/// TCP route infomation data
+///
+typedef struct _TCP4_ROUTE_INFO {
+ BOOLEAN DeleteRoute;
+ EFI_IPv4_ADDRESS *SubnetAddress;
+ EFI_IPv4_ADDRESS *SubnetMask;
+ EFI_IPv4_ADDRESS *GatewayAddress;
+} TCP4_ROUTE_INFO;
+
+typedef struct {
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+} TCP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
+
+/**
+ Get the current operational status of a TCP instance.
+
+ The GetModeData() function copies the current operational settings of this
+ EFI TCPv4 Protocol instance into user-supplied buffers. This function can
+ also be used to retrieve the operational setting of underlying drivers
+ such as IPv4, MNP, or SNP.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Tcp4State Pointer to the buffer to receive the current TCP
+ state.
+ @param Tcp4ConfigData Pointer to the buffer to receive the current TCP
+ configuration.
+ @param Ip4ModeData Pointer to the buffer to receive the current IPv4
+ configuration data used by the TCPv4 instance.
+ @param MnpConfigData Pointer to the buffer to receive the current MNP
+ configuration data indirectly used by the TCPv4
+ Instance.
+ @param SnpModeData Pointer to the buffer to receive the current SNP
+ configuration data indirectly used by the TCPv4
+ Instance.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4GetModeData (
+ IN EFI_TCP4_PROTOCOL *This,
+ OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL,
+ OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+
+/**
+ Initialize or brutally reset the operational parameters for
+ this EFI TCPv4 instance.
+
+ The Configure() function does the following:
+ * Initialize this EFI TCPv4 instance, i.e., initialize the communication end
+ setting, specify active open or passive open for an instance.
+ * Reset this TCPv4 instance brutally, i.e., cancel all pending asynchronous
+ tokens, flush transmission and receiving buffer directly without informing
+ the communication peer.
+ No other TCPv4 Protocol operation can be executed by this instance
+ until it is configured properly. For an active TCP4 instance, after a proper
+ configuration it may call Connect() to initiates the three-way handshake.
+ For a passive TCP4 instance, its state will transit to Tcp4StateListen after
+ configuration, and Accept() may be called to listen the incoming TCP connection
+ request. If TcpConfigData is set to NULL, the instance is reset. Resetting
+ process will be done brutally, the state machine will be set to Tcp4StateClosed
+ directly, the receive queue and transmit queue will be flushed, and no traffic is
+ allowed through this instance.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param TcpConfigData Pointer to the configure data to configure the
+ instance.
+
+ @retval EFI_SUCCESS The operational settings are set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Configure (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONFIG_DATA *TcpConfigData OPTIONAL
+ );
+
+/**
+ Add or delete routing entries.
+
+ The Routes() function adds or deletes a route from the instance's routing table.
+ The most specific route is selected by comparing the SubnetAddress with the
+ destination IP address's arithmetical AND to the SubnetMask.
+ The default route is added with both SubnetAddress and SubnetMask set to 0.0.0.0.
+ The default route matches all destination IP addresses if there is no more specific route.
+ Direct route is added with GatewayAddress set to 0.0.0.0. Packets are sent to
+ the destination host if its address can be found in the Address Resolution Protocol (ARP)
+ cache or it is on the local subnet. If the instance is configured to use default
+ address, a direct route to the local network will be added automatically.
+ Each TCP instance has its own independent routing table. Instance that uses the
+ default IP address will have a copy of the EFI_IP4_CONFIG_PROTOCOL's routing table.
+ The copy will be updated automatically whenever the IP driver reconfigures its
+ instance. As a result, the previous modification to the instance's local copy
+ will be lost. The priority of checking the route table is specific with IP
+ implementation and every IP implementation must comply with RFC 1122.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param DeleteRoute If TRUE, delete the specified route from routing
+ table; if FALSE, add the specified route to
+ routing table.
+ DestinationAddress and SubnetMask are used as
+ the keywords to search route entry.
+ @param SubnetAddress The destination network.
+ @param SubnetMask The subnet mask for the destination network.
+ @param GatewayAddress The gateway address for this route.
+ It must be on the same subnet with the station
+ address unless a direct route is specified.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the
+ entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Routes (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ );
+
+/**
+ Initiate a nonblocking TCP connection request for an active TCP instance.
+
+ The Connect() function will initiate an active open to the remote peer configured
+ in current TCP instance if it is configured active. If the connection succeeds
+ or fails due to any error, the ConnectionToken->CompletionToken.Event will be
+ signaled and ConnectionToken->CompletionToken.Status will be updated accordingly.
+ This function can only be called for the TCP instance in Tcp4StateClosed state.
+ The instance will transfer into Tcp4StateSynSent if the function returns EFI_SUCCESS.
+ If TCP three way handshake succeeds, its state will become Tcp4StateEstablished,
+ otherwise, the state will return to Tcp4StateClosed.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param ConnectionToken Pointer to the connection token to return when
+ the TCP three way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request is successfully initiated
+ and the state of this TCPv4 instance has
+ been changed to Tcp4StateSynSent.
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one
+ or it is not in Tcp4StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resource to
+ initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Connect (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken
+ );
+
+/**
+ Listen on the passive instance to accept an incoming connection request.
+
+ The Accept() function initiates an asynchronous accept request to wait for an
+ incoming connection on the passive TCP instance. If a remote peer successfully
+ establishes a connection with this instance, a new TCP instance will be created
+ and its handle will be returned in ListenToken->NewChildHandle. The newly created
+ instance is configured by inheriting the passive instance's configuration and is
+ ready for use upon return. The instance is in the Tcp4StateEstablished state.
+ The ListenToken->CompletionToken.Event will be signaled when a new connection
+ is accepted, user aborts the listen or connection is reset. This function only
+ can be called when current TCP instance is in Tcp4StateListen state.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param ListenToken Pointer to the listen token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The listen token has been queued successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not
+ in Tcp4StateListen state or a same listen token
+ has already existed in the listen token queue of
+ this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish
+ the operation.
+ @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above category error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Accept (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken
+ );
+
+/**
+ Queues outgoing data into the transmit queue.
+
+ The Transmit() function queues a sending request to this TCPv4 instance along
+ with the user data. The status of the token is updated and the event in the token
+ will be signaled once the data is sent out or some error occurs.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param Token Pointer to the completion token to queue to the
+ transmit queue
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A transmit completion token with the same
+ Token-> CompletionToken.Event was already in the
+ transmission queue.
+ * The current instance is in Tcp4StateClosed state
+ * The current instance is a passive one and
+ it is in Tcp4StateListen state.
+ * User has called Close() to disconnect this
+ connection.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of
+ resource shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or
+ address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Transmit (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ );
+
+/**
+ Place an asynchronous receive request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous. The caller must allocate the
+ Token->CompletionToken.Event and the FragmentBuffer used to receive data. He also
+ must fill the DataLength which represents the whole length of all FragmentBuffer.
+ When the receive operation completes, the EFI TCPv4 Protocol driver updates the
+ Token->CompletionToken.Status and Token->Packet.RxData fields and the
+ Token->CompletionToken.Event is signaled. If got data the data and its length
+ will be copy into the FragmentTable, in the same time the full length of received
+ data will be recorded in the DataLength fields. Providing a proper notification
+ function and context for the event will enable the user to receive the notification
+ and receiving status. That notification function is guaranteed to not be re-entered.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI TCPv4 Protocol instance has been reset
+ to startup defaults.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A receive completion token with the same
+ Token->CompletionToken.Event was already in
+ the receive queue.
+ * The current instance is in Tcp4StateClosed state.
+ * The current instance is a passive one and it
+ is in Tcp4StateListen state.
+ * User has called Close() to disconnect this
+ connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection
+ and there is no any buffered data in the receive
+ buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Receive (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ );
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection.
+
+ Initiate an asynchronous close token to TCP driver. After Close() is called,
+ any buffered transmission data will be sent by TCP driver and the current
+ instance will have a graceful close working flow described as RFC 793 if
+ AbortOnClose is set to FALSE, otherwise, a rest packet will be sent by TCP
+ driver to fast disconnect this connection. When the close operation completes
+ successfully the TCP instance is in Tcp4StateClosed state, all pending
+ asynchronous operation is signaled and any buffers used for TCP network traffic
+ is flushed.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param CloseToken Pointer to the close token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:
+ * Configure() has been called with TcpConfigData
+ set to NULL and this function has not returned.
+ * Previous Close() call on this instance has not
+ finished.
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the
+ operation.
+ @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above
+ category error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Close (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken
+ );
+
+/**
+ Abort an asynchronous connection, listen, transmission or receive request.
+
+ The Cancel() function aborts a pending connection, listen, transmit or receive
+ request. If Token is not NULL and the token is in the connection, listen,
+ transmission or receive queue when it is being cancelled, its Token->Status
+ will be set to EFI_ABORTED and then Token->Event will be signaled. If the token
+ is not in one of the queues, which usually means that the asynchronous operation
+ has completed, EFI_NOT_FOUND is returned. If Token is NULL all asynchronous token
+ issued by Connect(), Accept(), Transmit() and Receive()will be aborted.
+ NOTE: It has not been implemented currently.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token Pointer to a token that has been issued by
+ Connect(), Accept(), Transmit() or Receive(). If
+ NULL, all pending tokens issued by above four
+ functions will be aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn's been configured.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP,RARP, etc.) hasn's finished yet.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn's found in the
+ transmission or receive queue. It has either
+ completed or wasn's issued by Transmit() and Receive().
+ @retval EFI_UNSUPPORTED The operation is not supported in current
+ implementation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ The Poll() function increases the rate that data is moved between the network
+ and application and can be called when the TCP instance is created successfully.
+ Its use is optional. In some implementations, the periodical timer in the MNP
+ driver may not poll the underlying communications device fast enough to avoid
+ drop packets. Drivers and applications that are experiencing packet loss should
+ try calling the Poll() function in a high frequency.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or
+ receive queue. Consider increasing the polling
+ rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Poll (
+ IN EFI_TCP4_PROTOCOL *This
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
new file mode 100644
index 0000000000..1a7c41aa0b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
@@ -0,0 +1,939 @@
+/** @file
+ Misc support routines for tcp.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Tcp4Main.h"
+
+#include <Library/DevicePathLib.h>
+
+LIST_ENTRY mTcpRunQue = {
+ &mTcpRunQue,
+ &mTcpRunQue
+};
+
+LIST_ENTRY mTcpListenQue = {
+ &mTcpListenQue,
+ &mTcpListenQue
+};
+
+TCP_SEQNO mTcpGlobalIss = 0x4d7e980b;
+
+CHAR16 *mTcpStateName[] = {
+ L"TCP_CLOSED",
+ L"TCP_LISTEN",
+ L"TCP_SYN_SENT",
+ L"TCP_SYN_RCVD",
+ L"TCP_ESTABLISHED",
+ L"TCP_FIN_WAIT_1",
+ L"TCP_FIN_WAIT_2",
+ L"TCP_CLOSING",
+ L"TCP_TIME_WAIT",
+ L"TCP_CLOSE_WAIT",
+ L"TCP_LAST_ACK"
+};
+
+
+/**
+ Initialize the Tcb local related members.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpInitTcbLocal (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ //
+ // Compute the checksum of the fixed parts of pseudo header
+ //
+ Tcb->HeadSum = NetPseudoHeadChecksum (
+ Tcb->LocalEnd.Ip,
+ Tcb->RemoteEnd.Ip,
+ 0x06,
+ 0
+ );
+
+ Tcb->Iss = TcpGetIss ();
+ Tcb->SndUna = Tcb->Iss;
+ Tcb->SndNxt = Tcb->Iss;
+
+ Tcb->SndWl2 = Tcb->Iss;
+ Tcb->SndWnd = 536;
+
+ Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);
+
+ //
+ // First window size is never scaled
+ //
+ Tcb->RcvWndScale = 0;
+
+ Tcb->ProbeTimerOn = FALSE;
+}
+
+
+/**
+ Initialize the peer related members.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Pointer to the segment that contains the peer's
+ intial info.
+ @param Opt Pointer to the options announced by the peer.
+
+**/
+VOID
+TcpInitTcbPeer (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg,
+ IN TCP_OPTION *Opt
+ )
+{
+ UINT16 RcvMss;
+
+ ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));
+ ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = Tcb->SndWnd;
+ Tcb->SndWl1 = Seg->Seq;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ Tcb->SndWl2 = Seg->Ack;
+ } else {
+ Tcb->SndWl2 = Tcb->Iss + 1;
+ }
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {
+ Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);
+
+ RcvMss = TcpGetRcvMss (Tcb->Sk);
+ if (Tcb->SndMss > RcvMss) {
+ Tcb->SndMss = RcvMss;
+ }
+
+ } else {
+ //
+ // One end doesn't support MSS option, use default.
+ //
+ Tcb->RcvMss = 536;
+ }
+
+ Tcb->CWnd = Tcb->SndMss;
+
+ Tcb->Irs = Seg->Seq;
+ Tcb->RcvNxt = Tcb->Irs + 1;
+
+ Tcb->RcvWl2 = Tcb->RcvNxt;
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) &&
+ !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {
+
+ Tcb->SndWndScale = Opt->WndScale;
+
+ Tcb->RcvWndScale = TcpComputeScale (Tcb);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);
+
+ } else {
+ //
+ // One end doesn't support window scale option. use zero.
+ //
+ Tcb->RcvWndScale = 0;
+ }
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) &&
+ !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);
+
+ Tcb->TsRecent = Opt->TSVal;
+
+ //
+ // Compute the effective SndMss per RFC1122
+ // section 4.2.2.6. If timestamp option is
+ // enabled, it will always occupy 12 bytes.
+ //
+ Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;
+ }
+}
+
+
+/**
+ Locate a listen TCB that matchs the Local and Remote.
+
+ @param Local Pointer to the local (IP, Port).
+ @param Remote Pointer to the remote (IP, Port).
+
+ @return Pointer to the TCP_CB with the least number of wildcard,
+ if NULL no match is found.
+
+**/
+TCP_CB *
+TcpLocateListenTcb (
+ IN TCP_PEER *Local,
+ IN TCP_PEER *Remote
+ )
+{
+ LIST_ENTRY *Entry;
+ TCP_CB *Node;
+ TCP_CB *Match;
+ INTN Last;
+ INTN Cur;
+
+ Last = 4;
+ Match = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((Local->Port != Node->LocalEnd.Port) ||
+ !TCP_PEER_MATCH (Remote, &Node->RemoteEnd) ||
+ !TCP_PEER_MATCH (Local, &Node->LocalEnd)) {
+
+ continue;
+ }
+
+ //
+ // Compute the number of wildcard
+ //
+ Cur = 0;
+ if (Node->RemoteEnd.Ip == 0) {
+ Cur++;
+ }
+
+ if (Node->RemoteEnd.Port == 0) {
+ Cur++;
+ }
+
+ if (Node->LocalEnd.Ip == 0) {
+ Cur++;
+ }
+
+ if (Cur < Last) {
+ if (Cur == 0) {
+ return Node;
+ }
+
+ Last = Cur;
+ Match = Node;
+ }
+ }
+
+ return Match;
+}
+
+
+/**
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @param Addr Pointer to the IP address needs to match.
+ @param Port The port number needs to match.
+
+ @return The Tcb which matches the <Addr Port> paire exists or not.
+
+**/
+BOOLEAN
+TcpFindTcbByPeer (
+ IN EFI_IPv4_ADDRESS *Addr,
+ IN TCP_PORTNO Port
+ )
+{
+ TCP_PORTNO LocalPort;
+ LIST_ENTRY *Entry;
+ TCP_CB *Tcb;
+
+ ASSERT ((Addr != NULL) && (Port != 0));
+
+ LocalPort = HTONS (Port);
+
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (EFI_IP4_EQUAL (Addr, &Tcb->LocalEnd.Ip) &&
+ (LocalPort == Tcb->LocalEnd.Port)) {
+
+ return TRUE;
+ }
+ }
+
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (EFI_IP4_EQUAL (Addr, &Tcb->LocalEnd.Ip) &&
+ (LocalPort == Tcb->LocalEnd.Port)) {
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Locate the TCP_CB related to the socket pair.
+
+ @param LocalPort The local port number.
+ @param LocalIp The local IP address.
+ @param RemotePort The remote port number.
+ @param RemoteIp The remote IP address.
+ @param Syn Whether to search the listen sockets, if TRUE, the
+ listen sockets are searched.
+
+ @return Pointer to the related TCP_CB, if NULL no match is found.
+
+**/
+TCP_CB *
+TcpLocateTcb (
+ IN TCP_PORTNO LocalPort,
+ IN UINT32 LocalIp,
+ IN TCP_PORTNO RemotePort,
+ IN UINT32 RemoteIp,
+ IN BOOLEAN Syn
+ )
+{
+ TCP_PEER Local;
+ TCP_PEER Remote;
+ LIST_ENTRY *Entry;
+ TCP_CB *Tcb;
+
+ Local.Port = LocalPort;
+ Local.Ip = LocalIp;
+
+ Remote.Port = RemotePort;
+ Remote.Ip = RemoteIp;
+
+ //
+ // First check for exact match.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd) &&
+ TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd)) {
+
+ RemoveEntryList (&Tcb->List);
+ InsertHeadList (&mTcpRunQue, &Tcb->List);
+
+ return Tcb;
+ }
+ }
+
+ //
+ // Only check listen queue when SYN flag is on
+ //
+ if (Syn) {
+ return TcpLocateListenTcb (&Local, &Remote);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Insert a Tcb into the proper queue.
+
+ @param Tcb Pointer to the TCP_CB to be inserted.
+
+ @retval 0 The Tcb is inserted successfully.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpInsertTcb (
+ IN TCP_CB *Tcb
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Head;
+ TCP_CB *Node;
+
+ ASSERT (
+ (Tcb != NULL) &&
+ ((Tcb->State == TCP_LISTEN) ||
+ (Tcb->State == TCP_SYN_SENT) ||
+ (Tcb->State == TCP_SYN_RCVD) ||
+ (Tcb->State == TCP_CLOSED))
+ );
+
+ if (Tcb->LocalEnd.Port == 0) {
+ return -1;
+ }
+
+ Head = &mTcpRunQue;
+
+ if (Tcb->State == TCP_LISTEN) {
+ Head = &mTcpListenQue;
+ }
+
+ //
+ // Check that Tcb isn't already on the list.
+ //
+ NET_LIST_FOR_EACH (Entry, Head) {
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd) &&
+ TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd)) {
+
+ return -1;
+ }
+ }
+
+ InsertHeadList (Head, &Tcb->List);
+
+ return 0;
+}
+
+
+/**
+ Clone a TCB_CB from Tcb.
+
+ @param Tcb Pointer to the TCP_CB to be cloned.
+
+ @return Pointer to the new cloned TCP_CB, if NULL error condition occurred.
+
+**/
+TCP_CB *
+TcpCloneTcb (
+ IN TCP_CB *Tcb
+ )
+{
+ TCP_CB *Clone;
+
+ Clone = AllocatePool (sizeof (TCP_CB));
+
+ if (Clone == NULL) {
+ return NULL;
+
+ }
+
+ CopyMem (Clone, Tcb, sizeof (TCP_CB));
+
+ //
+ // Increate the reference count of the shared IpInfo.
+ //
+ NET_GET_REF (Tcb->IpInfo);
+
+ InitializeListHead (&Clone->List);
+ InitializeListHead (&Clone->SndQue);
+ InitializeListHead (&Clone->RcvQue);
+
+ Clone->Sk = SockClone (Tcb->Sk);
+ if (Clone->Sk == NULL) {
+ DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));
+ FreePool (Clone);
+ return NULL;
+ }
+
+ ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;
+
+ return Clone;
+}
+
+
+/**
+ Compute an ISS to be used by a new connection.
+
+ @return The result ISS.
+
+**/
+TCP_SEQNO
+TcpGetIss (
+ VOID
+ )
+{
+ mTcpGlobalIss += 2048;
+ return mTcpGlobalIss;
+}
+
+
+/**
+ Get the local mss.
+
+ @param Sock Pointer to the socket to get mss
+
+ @return The mss size.
+
+**/
+UINT16
+TcpGetRcvMss (
+ IN SOCKET *Sock
+ )
+{
+ EFI_IP4_MODE_DATA Ip4Mode;
+ TCP4_PROTO_DATA *TcpProto;
+ EFI_IP4_PROTOCOL *Ip;
+
+ ASSERT (Sock != NULL);
+
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ Ip = TcpProto->TcpService->IpIo->Ip.Ip4;
+ ASSERT (Ip != NULL);
+
+ Ip->GetModeData (Ip, &Ip4Mode, NULL, NULL);
+
+ return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));
+}
+
+
+/**
+ Set the Tcb's state.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param State The state to be set.
+
+**/
+VOID
+TcpSetState (
+ IN OUT TCP_CB *Tcb,
+ IN UINT8 State
+ )
+{
+ ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));
+ ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));
+
+ DEBUG (
+ (EFI_D_NET,
+ "Tcb (%p) state %s --> %s\n",
+ Tcb,
+ mTcpStateName[Tcb->State],
+ mTcpStateName[State])
+ );
+
+ Tcb->State = State;
+
+ switch (State) {
+ case TCP_ESTABLISHED:
+
+ SockConnEstablished (Tcb->Sk);
+
+ if (Tcb->Parent != NULL) {
+ //
+ // A new connection is accepted by a listening socket, install
+ // the device path.
+ //
+ TcpInstallDevicePath (Tcb->Sk);
+ }
+
+ break;
+
+ case TCP_CLOSED:
+
+ SockConnClosed (Tcb->Sk);
+
+ break;
+ default:
+ break;
+ }
+}
+
+
+/**
+ Compute the TCP segment's checksum.
+
+ @param Nbuf Pointer to the buffer that contains the TCP
+ segment.
+ @param HeadSum The checksum value of the fixed part of pseudo
+ header.
+
+ @return The checksum value.
+
+**/
+UINT16
+TcpChecksum (
+ IN NET_BUF *Nbuf,
+ IN UINT16 HeadSum
+ )
+{
+ UINT16 Checksum;
+
+ Checksum = NetbufChecksum (Nbuf);
+ Checksum = NetAddChecksum (Checksum, HeadSum);
+
+ Checksum = NetAddChecksum (
+ Checksum,
+ HTONS ((UINT16) Nbuf->TotalSize)
+ );
+
+ return (UINT16) ~Checksum;
+}
+
+/**
+ Translate the information from the head of the received TCP
+ segment Nbuf contains and fill it into a TCP_SEG structure.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer contains the TCP segment.
+
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.
+
+**/
+TCP_SEG *
+TcpFormatNetbuf (
+ IN TCP_CB *Tcb,
+ IN OUT NET_BUF *Nbuf
+ )
+{
+ TCP_SEG *Seg;
+ TCP_HEAD *Head;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Head != NULL);
+ Nbuf->Tcp = Head;
+
+ Seg->Seq = NTOHL (Head->Seq);
+ Seg->Ack = NTOHL (Head->Ack);
+ Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));
+
+ Seg->Urg = NTOHS (Head->Urg);
+ Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale);
+ Seg->Flag = Head->Flag;
+
+ //
+ // SYN and FIN flag occupy one sequence space each.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ //
+ // RFC requires that initial window not be scaled
+ //
+ Seg->Wnd = NTOHS (Head->Wnd);
+ Seg->End++;
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ Seg->End++;
+ }
+
+ return Seg;
+}
+
+
+/**
+ Reset the connection related with Tcb.
+
+ @param Tcb Pointer to the TCP_CB of the connection to be
+ reset.
+
+**/
+VOID
+TcpResetConnection (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_HEAD *Nhead;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return ;
+ }
+
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_TAIL
+ );
+
+ ASSERT (Nhead != NULL);
+
+ Nbuf->Tcp = Nhead;
+
+ Nhead->Flag = TCP_FLG_RST;
+ Nhead->Seq = HTONL (Tcb->SndNxt);
+ Nhead->Ack = HTONL (Tcb->RcvNxt);
+ Nhead->SrcPort = Tcb->LocalEnd.Port;
+ Nhead->DstPort = Tcb->RemoteEnd.Port;
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);
+ Nhead->Res = 0;
+ Nhead->Wnd = HTONS (0xFFFF);
+ Nhead->Checksum = 0;
+ Nhead->Urg = 0;
+ Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);
+
+ TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);
+
+ NetbufFree (Nbuf);
+}
+
+
+/**
+ Initialize an active connection.
+
+ @param Tcb Pointer to the TCP_CB that wants to initiate a
+ connection.
+
+**/
+VOID
+TcpOnAppConnect (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ TcpInitTcbLocal (Tcb);
+ TcpSetState (Tcb, TCP_SYN_SENT);
+
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);
+ TcpToSendData (Tcb, 1);
+}
+
+
+/**
+ Initiate the connection close procedure, called when
+ applications want to close the connection.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppClose (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ ASSERT (Tcb != NULL);
+
+ if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {
+
+ DEBUG ((EFI_D_WARN, "TcpOnAppClose: connection reset "
+ "because data is lost for TCB %p\n", Tcb));
+
+ TcpResetConnection (Tcb);
+ TcpClose (Tcb);
+ return;
+ }
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ case TCP_LISTEN:
+ case TCP_SYN_SENT:
+ TcpSetState (Tcb, TCP_CLOSED);
+ break;
+
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+ TcpSetState (Tcb, TCP_FIN_WAIT_1);
+ break;
+
+ case TCP_CLOSE_WAIT:
+ TcpSetState (Tcb, TCP_LAST_ACK);
+ break;
+ default:
+ break;
+ }
+
+ TcpToSendData (Tcb, 1);
+}
+
+
+/**
+ Check whether the application's newly delivered data can be sent out.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 Whether the data is sent out or is buffered for
+ further sending.
+ @retval -1 The Tcb is not in a state that data is permitted to
+ be sent out.
+
+**/
+INTN
+TcpOnAppSend (
+ IN OUT TCP_CB *Tcb
+ )
+{
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ return -1;
+
+ case TCP_LISTEN:
+ return -1;
+
+ case TCP_SYN_SENT:
+ case TCP_SYN_RCVD:
+ return 0;
+
+ case TCP_ESTABLISHED:
+ case TCP_CLOSE_WAIT:
+ TcpToSendData (Tcb, 0);
+ return 0;
+
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ return -1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ Application has consumed some data, check whether
+ to send a window updata ack or a delayed ack.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppConsume (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT32 TcpOld;
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ return;
+
+ case TCP_LISTEN:
+ return;
+
+ case TCP_SYN_SENT:
+ case TCP_SYN_RCVD:
+ return;
+
+ case TCP_ESTABLISHED:
+ TcpOld = TcpRcvWinOld (Tcb);
+ if (TcpRcvWinNow (Tcb) > TcpOld) {
+
+ if (TcpOld < Tcb->RcvMss) {
+
+ DEBUG ((EFI_D_NET, "TcpOnAppConsume: send a window"
+ " update for a window closed Tcb %p\n", Tcb));
+
+ TcpSendAck (Tcb);
+ } else if (Tcb->DelayedAck == 0) {
+
+ DEBUG ((EFI_D_NET, "TcpOnAppConsume: scheduled a delayed"
+ " ACK to update window for Tcb %p\n", Tcb));
+
+ Tcb->DelayedAck = 1;
+ }
+ }
+
+ break;
+
+ case TCP_CLOSE_WAIT:
+ return;
+
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ return;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ Abort the connection by sending a reset segment, called
+ when the application wants to abort the connection.
+
+ @param Tcb Pointer to the TCP_CB of the TCP instance.
+
+**/
+VOID
+TcpOnAppAbort (
+ IN TCP_CB *Tcb
+ )
+{
+ DEBUG ((EFI_D_WARN, "TcpOnAppAbort: connection reset "
+ "issued by application for TCB %p\n", Tcb));
+
+ switch (Tcb->State) {
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSE_WAIT:
+ TcpResetConnection (Tcb);
+ break;
+ default:
+ break;
+ }
+
+ TcpSetState (Tcb, TCP_CLOSED);
+}
+
+/**
+ Install the device path protocol on the TCP instance.
+
+ @param Sock Pointer to the socket representing the TCP instance.
+
+ @retval EFI_SUCCESS The device path protocol is installed.
+ @retval other Failed to install the device path protocol.
+
+**/
+EFI_STATUS
+TcpInstallDevicePath (
+ IN SOCKET *Sock
+ )
+{
+ TCP4_PROTO_DATA *TcpProto;
+ TCP4_SERVICE_DATA *TcpService;
+ TCP_CB *Tcb;
+ IPv4_DEVICE_PATH Ip4DPathNode;
+ EFI_STATUS Status;
+ TCP_PORTNO LocalPort;
+ TCP_PORTNO RemotePort;
+
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ TcpService = TcpProto->TcpService;
+ Tcb = TcpProto->TcpPcb;
+
+ LocalPort = NTOHS (Tcb->LocalEnd.Port);
+ RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ NetLibCreateIPv4DPathNode (
+ &Ip4DPathNode,
+ TcpService->ControllerHandle,
+ Tcb->LocalEnd.Ip,
+ LocalPort,
+ Tcb->RemoteEnd.Ip,
+ RemotePort,
+ EFI_IP_PROTO_TCP,
+ Tcb->UseDefaultAddr
+ );
+
+ IP4_COPY_ADDRESS (&Ip4DPathNode.SubnetMask, &Tcb->SubnetMask);
+
+ Sock->DevicePath = AppendDevicePathNode (
+ Sock->ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode
+ );
+ if (Sock->DevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->InstallProtocolInterface (
+ &Sock->SockHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ Sock->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Sock->DevicePath);
+ }
+
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c
new file mode 100644
index 0000000000..0da249c3cc
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c
@@ -0,0 +1,380 @@
+/** @file
+ Routines to process TCP option.
+
+Copyright (c) 2005 - 2006, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+/**
+ Get a UINT16 value from buffer.
+
+ @param Buf Pointer to input buffer.
+
+ @return The UINT16 value get from buffer.
+
+**/
+UINT16
+TcpGetUint16 (
+ IN UINT8 *Buf
+ )
+{
+ UINT16 Value;
+ CopyMem (&Value, Buf, sizeof (UINT16));
+ return NTOHS (Value);
+}
+
+/**
+ Get a UINT32 value from buffer.
+
+ @param Buf Pointer to input buffer.
+
+ @return The UINT32 value get from buffer.
+
+**/
+UINT32
+TcpGetUint32 (
+ IN UINT8 *Buf
+ )
+{
+ UINT32 Value;
+ CopyMem (&Value, Buf, sizeof (UINT32));
+ return NTOHL (Value);
+}
+
+/**
+ Put a UINT32 value in buffer.
+
+ @param Buf Pointer to the buffer.
+ @param Data The UINT32 Date to put in buffer
+
+**/
+VOID
+TcpPutUint32 (
+ OUT UINT8 *Buf,
+ IN UINT32 Data
+ )
+{
+ Data = HTONL (Data);
+ CopyMem (Buf, &Data, sizeof (UINT32));
+}
+
+
+/**
+ Compute the window scale value according to the given buffer size.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The scale value.
+
+**/
+UINT8
+TcpComputeScale (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT8 Scale;
+ UINT32 BufSize;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+
+ Scale = 0;
+ while ((Scale < TCP_OPTION_MAX_WS) &&
+ ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {
+
+ Scale++;
+ }
+
+ return Scale;
+}
+
+
+/**
+ Build the TCP option in three-way handshake.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpSynBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT8 *Data;
+ UINT16 Len;
+
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
+
+ Len = 0;
+
+ //
+ // Add timestamp option if not disabled by application
+ // and it is the first SYN segment or the peer has sent
+ // us its timestamp.
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_TS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data != NULL);
+ Len += TCP_OPTION_TS_ALIGNED_LEN;
+
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
+ TcpPutUint32 (Data + 4, mTcpTick);
+ TcpPutUint32 (Data + 8, 0);
+ }
+
+ //
+ // Build window scale option, only when are configured
+ // to send WS option, and either we are doing active
+ // open or we have received WS option from peer.
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_WS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data != NULL);
+
+ Len += TCP_OPTION_WS_ALIGNED_LEN;
+ TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));
+ }
+
+ //
+ // Build MSS option
+ //
+ Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);
+ ASSERT (Data != NULL);
+
+ Len += TCP_OPTION_MSS_LEN;
+ TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);
+
+ return Len;
+}
+
+
+/**
+ Build the TCP option in synchronized states.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT8 *Data;
+ UINT16 Len;
+
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
+ Len = 0;
+
+ //
+ // Build Timestamp option
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&
+ !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_TS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data != NULL);
+ Len += TCP_OPTION_TS_ALIGNED_LEN;
+
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
+ TcpPutUint32 (Data + 4, mTcpTick);
+ TcpPutUint32 (Data + 8, Tcb->TsRecent);
+ }
+
+ return Len;
+}
+
+
+/**
+ Parse the supported options.
+
+ @param Tcp Pointer to the TCP_CB of this TCP instance.
+ @param Option Pointer to the TCP_OPTION used to store the successfully pasrsed
+ options.
+
+ @retval 0 The options are successfully pasrsed.
+ @retval -1 Ilegal option was found.
+
+**/
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN OUT TCP_OPTION *Option
+ )
+{
+ UINT8 *Head;
+ UINT8 TotalLen;
+ UINT8 Cur;
+ UINT8 Type;
+ UINT8 Len;
+
+ ASSERT ((Tcp != NULL) && (Option != NULL));
+
+ Option->Flag = 0;
+
+ TotalLen = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD));
+ if (TotalLen <= 0) {
+ return 0;
+ }
+
+ Head = (UINT8 *) (Tcp + 1);
+
+ //
+ // Fast process of timestamp option
+ //
+ if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) &&
+ (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {
+
+ Option->TSVal = TcpGetUint32 (Head + 4);
+ Option->TSEcr = TcpGetUint32 (Head + 8);
+ Option->Flag = TCP_OPTION_RCVD_TS;
+
+ return 0;
+ }
+
+ //
+ // Slow path to process the options.
+ //
+ Cur = 0;
+
+ while (Cur < TotalLen) {
+ Type = Head[Cur];
+
+ switch (Type) {
+ case TCP_OPTION_MSS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_MSS_LEN) ||
+ (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {
+
+ return -1;
+ }
+
+ Option->Mss = TcpGetUint16 (&Head[Cur + 2]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);
+
+ Cur += TCP_OPTION_MSS_LEN;
+ break;
+
+ case TCP_OPTION_WS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_WS_LEN) ||
+ (TotalLen - Cur < TCP_OPTION_WS_LEN)) {
+
+ return -1;
+ }
+
+ Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);
+
+ Cur += TCP_OPTION_WS_LEN;
+ break;
+
+ case TCP_OPTION_TS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_TS_LEN) ||
+ (TotalLen - Cur < TCP_OPTION_TS_LEN)) {
+
+ return -1;
+ }
+
+ Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);
+ Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);
+
+ Cur += TCP_OPTION_TS_LEN;
+ break;
+
+ case TCP_OPTION_NOP:
+ Cur++;
+ break;
+
+ case TCP_OPTION_EOP:
+ Cur = TotalLen;
+ break;
+
+ default:
+ Len = Head[Cur + 1];
+
+ if ((TotalLen - Cur) < Len || Len < 2) {
+ return -1;
+ }
+
+ Cur = (UINT8) (Cur + Len);
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+
+/**
+ Check the segment against PAWS.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param TSVal The timestamp value.
+
+ @retval 1 The segment passed the PAWS check.
+ @retval 0 The segment failed to pass the PAWS check.
+
+**/
+UINT32
+TcpPawsOK (
+ IN TCP_CB *Tcb,
+ IN UINT32 TSVal
+ )
+{
+ //
+ // PAWS as defined in RFC1323, buggy...
+ //
+ if (TCP_TIME_LT (TSVal, Tcb->TsRecent) &&
+ TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)) {
+
+ return 0;
+
+ }
+
+ return 1;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h
new file mode 100644
index 0000000000..9676df015d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h
@@ -0,0 +1,145 @@
+/** @file
+ Tcp option's routine header file.
+
+Copyright (c) 2005 - 2009, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP4_OPTION_H_
+#define _TCP4_OPTION_H_
+
+///
+/// The structure to store the parse option value.
+/// ParseOption only parse the options, don't process them.
+///
+typedef struct _TCP_OPTION {
+ UINT8 Flag; ///< Flag such as TCP_OPTION_RCVD_MSS
+ UINT8 WndScale; ///< The WndScale received
+ UINT16 Mss; ///< The Mss received
+ UINT32 TSVal; ///< The TSVal field in a timestamp option
+ UINT32 TSEcr; ///< The TSEcr field in a timestamp option
+} TCP_OPTION;
+
+//
+// supported TCP option type and their length
+//
+#define TCP_OPTION_EOP 0 ///< End Of oPtion
+#define TCP_OPTION_NOP 1 ///< No-Option.
+#define TCP_OPTION_MSS 2 ///< Maximum Segment Size
+#define TCP_OPTION_WS 3 ///< Window scale
+#define TCP_OPTION_TS 8 ///< Timestamp
+#define TCP_OPTION_MSS_LEN 4 ///< Length of MSS option
+#define TCP_OPTION_WS_LEN 3 ///< Length of window scale option
+#define TCP_OPTION_TS_LEN 10 ///< Length of timestamp option
+#define TCP_OPTION_WS_ALIGNED_LEN 4 ///< Length of window scale option, aligned
+#define TCP_OPTION_TS_ALIGNED_LEN 12 ///< Length of timestamp option, aligned
+
+//
+// recommend format of timestamp window scale
+// option for fast process.
+//
+#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \
+ (TCP_OPTION_NOP << 16) | \
+ (TCP_OPTION_TS << 8) | \
+ (TCP_OPTION_TS_LEN))
+
+#define TCP_OPTION_WS_FAST ((TCP_OPTION_NOP << 24) | \
+ (TCP_OPTION_WS << 16) | \
+ (TCP_OPTION_WS_LEN << 8))
+
+#define TCP_OPTION_MSS_FAST ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16))
+
+//
+// Other misc definations
+//
+#define TCP_OPTION_RCVD_MSS 0x01
+#define TCP_OPTION_RCVD_WS 0x02
+#define TCP_OPTION_RCVD_TS 0x04
+#define TCP_OPTION_MAX_WS 14 ///< Maxium window scale value
+#define TCP_OPTION_MAX_WIN 0xffff ///< Max window size in TCP header
+
+
+/**
+ Compute the window scale value according to the given buffer size.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The scale value.
+
+**/
+UINT8
+TcpComputeScale (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Build the TCP option in three-way handshake.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpSynBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Build the TCP option in synchronized states.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Parse the supported options.
+
+ @param Tcp Pointer to the TCP_CB of this TCP instance.
+ @param Option Pointer to the TCP_OPTION used to store the successfully pasrsed
+ options.
+
+ @retval 0 The options are successfully pasrsed.
+ @retval -1 Ilegal option was found.
+
+**/
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN OUT TCP_OPTION *Option
+ );
+
+/**
+ Check the segment against PAWS.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param TSVal The timestamp value.
+
+ @retval 1 The segment passed the PAWS check.
+ @retval 0 The segment failed to pass the PAWS check.
+
+**/
+UINT32
+TcpPawsOK (
+ IN TCP_CB *Tcb,
+ IN UINT32 TSVal
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
new file mode 100644
index 0000000000..0eec8f07b9
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
@@ -0,0 +1,1212 @@
+/** @file
+ TCP output process routines.
+
+Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+UINT8 mTcpOutFlag[] = {
+ 0, // TCP_CLOSED
+ 0, // TCP_LISTEN
+ TCP_FLG_SYN, // TCP_SYN_SENT
+ TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD
+ TCP_FLG_ACK, // TCP_ESTABLISHED
+ TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1
+ TCP_FLG_ACK, // TCP_FIN_WAIT_2
+ TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING
+ TCP_FLG_ACK, // TCP_TIME_WAIT
+ TCP_FLG_ACK, // TCP_CLOSE_WAIT
+ TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK
+};
+
+
+/**
+ Compute the sequence space left in the old receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence space left in the old receive window.
+
+**/
+UINT32
+TcpRcvWinOld (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT32 OldWin;
+
+ OldWin = 0;
+
+ if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {
+
+ OldWin = TCP_SUB_SEQ (
+ Tcb->RcvWl2 + Tcb->RcvWnd,
+ Tcb->RcvNxt
+ );
+ }
+
+ return OldWin;
+}
+
+
+/**
+ Compute the current receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The size of the current receive window, in bytes.
+
+**/
+UINT32
+TcpRcvWinNow (
+ IN TCP_CB *Tcb
+ )
+{
+ SOCKET *Sk;
+ UINT32 Win;
+ UINT32 Increase;
+ UINT32 OldWin;
+
+ Sk = Tcb->Sk;
+ ASSERT (Sk != NULL);
+
+ OldWin = TcpRcvWinOld (Tcb);
+
+ Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF);
+
+ Increase = 0;
+ if (Win > OldWin) {
+ Increase = Win - OldWin;
+ }
+
+ //
+ // Receiver's SWS: don't advertise a bigger window
+ // unless it can be increased by at least one Mss or
+ // half of the receive buffer.
+ //
+ if ((Increase > Tcb->SndMss) ||
+ (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {
+
+ return Win;
+ }
+
+ return OldWin;
+}
+
+
+/**
+ Compute the value to fill in the window size field of the outgoing segment.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Syn The flag to indicate whether the outgoing segment is a SYN
+ segment.
+
+ @return The value of the local receive window size used to fill the outing segment.
+
+**/
+UINT16
+TcpComputeWnd (
+ IN OUT TCP_CB *Tcb,
+ IN BOOLEAN Syn
+ )
+{
+ UINT32 Wnd;
+
+ //
+ // RFC requires that initial window not be scaled
+ //
+ if (Syn) {
+
+ Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);
+ } else {
+
+ Wnd = TcpRcvWinNow (Tcb);
+
+ Tcb->RcvWnd = Wnd;
+ }
+
+ Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);
+ return NTOHS ((UINT16) Wnd);
+}
+
+
+/**
+ Get the maximum SndNxt.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence number of the maximum SndNxt.
+
+**/
+TCP_SEQNO
+TcpGetMaxSndNxt (
+ IN TCP_CB *Tcb
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+
+ if (IsListEmpty (&Tcb->SndQue)) {
+ return Tcb->SndNxt;
+ }
+
+ Entry = Tcb->SndQue.BackLink;
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));
+ return TCPSEG_NETBUF (Nbuf)->End;
+}
+
+
+/**
+ Compute how much data to send.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Force Whether to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The length of the data can be sent, if 0, no data can be sent.
+
+**/
+UINT32
+TcpDataToSend (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ )
+{
+ SOCKET *Sk;
+ UINT32 Win;
+ UINT32 Len;
+ UINT32 Left;
+ UINT32 Limit;
+
+ Sk = Tcb->Sk;
+ ASSERT (Sk != NULL);
+
+ //
+ // TCP should NOT send data beyond the send window
+ // and congestion window. The right edge of send
+ // window is defined as SND.WL2 + SND.WND. The right
+ // edge of congestion window is defined as SND.UNA +
+ // CWND.
+ //
+ Win = 0;
+ Limit = Tcb->SndWl2 + Tcb->SndWnd;
+
+ if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {
+
+ Limit = Tcb->SndUna + Tcb->CWnd;
+ }
+
+ if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {
+ Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);
+ }
+
+ //
+ // The data to send contains two parts: the data on the
+ // socket send queue, and the data on the TCB's send
+ // buffer. The later can be non-zero if the peer shrinks
+ // its advertised window.
+ //
+ Left = GET_SND_DATASIZE (Sk) +
+ TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);
+
+ Len = MIN (Win, Left);
+
+ if (Len > Tcb->SndMss) {
+ Len = Tcb->SndMss;
+ }
+
+ if ((Force != 0)|| (Len == 0 && Left == 0)) {
+ return Len;
+ }
+
+ if (Len == 0 && Left != 0) {
+ goto SetPersistTimer;
+ }
+
+ //
+ // Sender's SWS avoidance: Don't send a small segment unless
+ // a)A full-sized segment can be sent,
+ // b)at least one-half of the maximum sized windows that
+ // the other end has ever advertised.
+ // c)It can send everything it has and either it isn't
+ // expecting an ACK or the Nagle algorithm is disabled.
+ //
+ if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {
+
+ return Len;
+ }
+
+ if ((Len == Left) &&
+ ((Tcb->SndNxt == Tcb->SndUna) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))) {
+
+ return Len;
+ }
+
+ //
+ // RFC1122 suggests to set a timer when SWSA forbids TCP
+ // sending more data, and combine it with probe timer.
+ //
+SetPersistTimer:
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpDataToSend: enter persistent state for TCB %p\n",
+ Tcb)
+ );
+
+ if (!Tcb->ProbeTimerOn) {
+ TcpSetProbeTimer (Tcb);
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Build the TCP header of the TCP segment and transmit the segment by IP.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer containing the segment to be sent out.
+
+ @retval 0 The segment is sent out successfully.
+ @retval other Error condition occurred.
+
+**/
+INTN
+TcpTransmitSegment (
+ IN OUT TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT16 Len;
+ TCP_HEAD *Head;
+ TCP_SEG *Seg;
+ BOOLEAN Syn;
+ UINT32 DataLen;
+
+ ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));
+
+ DataLen = Nbuf->TotalSize;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);
+
+ if (Syn) {
+
+ Len = TcpSynBuildOption (Tcb, Nbuf);
+ } else {
+
+ Len = TcpBuildOption (Tcb, Nbuf);
+ }
+
+ ASSERT ((Len % 4 == 0) && (Len <= 40));
+
+ Len += sizeof (TCP_HEAD);
+
+ Head = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Head != NULL);
+
+ Nbuf->Tcp = Head;
+
+ Head->SrcPort = Tcb->LocalEnd.Port;
+ Head->DstPort = Tcb->RemoteEnd.Port;
+ Head->Seq = NTOHL (Seg->Seq);
+ Head->Ack = NTOHL (Tcb->RcvNxt);
+ Head->HeadLen = (UINT8) (Len >> 2);
+ Head->Res = 0;
+ Head->Wnd = TcpComputeWnd (Tcb, Syn);
+ Head->Checksum = 0;
+
+ //
+ // Check whether to set the PSH flag.
+ //
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);
+
+ if (DataLen != 0) {
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&
+ TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
+
+ } else if ((Seg->End == Tcb->SndNxt) &&
+ (GET_SND_DATASIZE (Tcb->Sk) == 0)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
+ }
+ }
+
+ //
+ // Check whether to set the URG flag and the urgent pointer.
+ //
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);
+
+ if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {
+
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);
+ } else {
+
+ Seg->Urg = (UINT16) MIN (
+ TCP_SUB_SEQ (Tcb->SndUp,
+ Seg->Seq),
+ 0xffff
+ );
+ }
+ }
+
+ Head->Flag = Seg->Flag;
+ Head->Urg = NTOHS (Seg->Urg);
+ Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);
+
+ //
+ // update the TCP session's control information
+ //
+ Tcb->RcvWl2 = Tcb->RcvNxt;
+ if (Syn) {
+ Tcb->RcvWnd = NTOHS (Head->Wnd);
+ }
+
+ //
+ // clear delayedack flag
+ //
+ Tcb->DelayedAck = 0;
+
+ return TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);
+}
+
+
+/**
+ Get a segment from the Tcb's SndQue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Cur;
+ NET_BUF *Node;
+ TCP_SEG *Seg;
+ NET_BUF *Nbuf;
+ TCP_SEQNO End;
+ UINT8 *Data;
+ UINT8 Flag;
+ INT32 Offset;
+ INT32 CopyLen;
+
+ ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));
+
+ //
+ // Find the segment that contains the Seq.
+ //
+ Head = &Tcb->SndQue;
+
+ Node = NULL;
+ Seg = NULL;
+
+ NET_LIST_FOR_EACH (Cur, Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Node);
+
+ if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {
+
+ break;
+ }
+ }
+
+ ASSERT (Cur != Head);
+ ASSERT (Node != NULL);
+ ASSERT (Seg != NULL);
+
+ //
+ // Return the buffer if it can be returned without
+ // adjustment:
+ //
+ if ((Seg->Seq == Seq) &&
+ TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&
+ !NET_BUF_SHARED (Node)) {
+
+ NET_GET_REF (Node);
+ return Node;
+ }
+
+ //
+ // Create a new buffer and copy data there.
+ //
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ Flag = Seg->Flag;
+ End = Seg->End;
+
+ if (TCP_SEQ_LT (Seq + Len, Seg->End)) {
+ End = Seq + Len;
+ }
+
+ CopyLen = TCP_SUB_SEQ (End, Seq);
+ Offset = TCP_SUB_SEQ (Seq, Seg->Seq);
+
+ //
+ // If SYN is set and out of the range, clear the flag.
+ // Because the sequence of the first byte is SEG.SEQ+1,
+ // adjust Offset by -1. If SYN is in the range, copy
+ // one byte less.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ if (TCP_SEQ_LT (Seg->Seq, Seq)) {
+
+ TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);
+ Offset--;
+ } else {
+
+ CopyLen--;
+ }
+ }
+
+ //
+ // If FIN is set and in the range, copy one byte less,
+ // and if it is out of the range, clear the flag.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+
+ if (Seg->End == End) {
+
+ CopyLen--;
+ } else {
+
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
+ }
+ }
+
+ ASSERT (CopyLen >= 0);
+
+ //
+ // copy data to the segment
+ //
+ if (CopyLen != 0) {
+ Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);
+ ASSERT (Data != NULL);
+
+ if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {
+ goto OnError;
+ }
+ }
+
+ CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));
+
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;
+ TCPSEG_NETBUF (Nbuf)->End = End;
+ TCPSEG_NETBUF (Nbuf)->Flag = Flag;
+
+ return Nbuf;
+
+OnError:
+ NetbufFree (Nbuf);
+ return NULL;
+}
+
+
+/**
+ Get a segment from the Tcb's socket buffer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSock (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+ UINT8 *Data;
+ UINT32 DataGet;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ DEBUG ((EFI_D_ERROR, "TcpGetSegmentSock: failed to allocate "
+ "a netbuf for TCB %p\n",Tcb));
+
+ return NULL;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ DataGet = 0;
+
+ if (Len != 0) {
+ //
+ // copy data to the segment.
+ //
+ Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);
+ ASSERT (Data != NULL);
+
+ DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);
+ }
+
+ NET_GET_REF (Nbuf);
+
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;
+ TCPSEG_NETBUF (Nbuf)->End = Seq + Len;
+
+ InsertTailList (&(Tcb->SndQue), &(Nbuf->List));
+
+ if (DataGet != 0) {
+
+ SockDataSent (Tcb->Sk, DataGet);
+ }
+
+ return Nbuf;
+}
+
+
+/**
+ Get a segment starting from sequence Seq of a maximum
+ length of Len.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegment (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+
+ ASSERT (Tcb != NULL);
+
+ //
+ // Compare the SndNxt with the max sequence number sent.
+ //
+ if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ } else {
+
+ Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+ return Nbuf;
+}
+
+
+/**
+ Retransmit the segment from sequence Seq.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment to be retransmitted.
+
+ @retval 0 Retransmission succeeded.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpRetransmit (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq
+ )
+{
+ NET_BUF *Nbuf;
+ UINT32 Len;
+
+ //
+ // Compute the maxium length of retransmission. It is
+ // limited by three factors:
+ // 1. Less than SndMss
+ // 2. must in the current send window
+ // 3. will not change the boundaries of queued segments.
+ //
+ if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {
+ DEBUG ((EFI_D_WARN, "TcpRetransmit: retransmission cancelled "
+ "because send window too small for TCB %p\n", Tcb));
+
+ return 0;
+ }
+
+ Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
+ Len = MIN (Len, Tcb->SndMss);
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ goto OnError;
+ }
+
+ //
+ // The retransmitted buffer may be on the SndQue,
+ // trim TCP head because all the buffer on SndQue
+ // are headless.
+ //
+ ASSERT (Nbuf->Tcp != NULL);
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ NetbufFree (Nbuf);
+ return 0;
+
+OnError:
+ if (Nbuf != NULL) {
+ NetbufFree (Nbuf);
+ }
+
+ return -1;
+}
+
+
+/**
+ Check whether to send data/SYN/FIN and piggy back an ACK.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Force Whether to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The number of bytes sent.
+
+**/
+INTN
+TcpToSendData (
+ IN OUT TCP_CB *Tcb,
+ IN INTN Force
+ )
+{
+ UINT32 Len;
+ INTN Sent;
+ UINT8 Flag;
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+ TCP_SEQNO Seq;
+ TCP_SEQNO End;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));
+
+ Sent = 0;
+
+ if ((Tcb->State == TCP_CLOSED) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {
+
+ return 0;
+ }
+
+SEND_AGAIN:
+ //
+ // compute how much data can be sent
+ //
+ Len = TcpDataToSend (Tcb, Force);
+ Seq = Tcb->SndNxt;
+
+ ASSERT ((Tcb->State) < (ARRAY_SIZE (mTcpOutFlag)));
+ Flag = mTcpOutFlag[Tcb->State];
+
+ if ((Flag & TCP_FLG_SYN) != 0) {
+
+ Seq = Tcb->Iss;
+ Len = 0;
+ }
+
+ //
+ // only send a segment without data if SYN or
+ // FIN is set.
+ //
+ if ((Len == 0) &&
+ ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {
+ return Sent;
+ }
+
+ Nbuf = TcpGetSegment (Tcb, Seq, Len);
+
+ if (Nbuf == NULL) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpToSendData: failed to get a segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto OnError;
+ }
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // Set the TcpSeg in Nbuf.
+ //
+ Len = Nbuf->TotalSize;
+ End = Seq + Len;
+ if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {
+ End++;
+ }
+
+ if ((Flag & TCP_FLG_FIN) != 0) {
+ //
+ // Send FIN if all data is sent, and FIN is
+ // in the window
+ //
+ if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&
+ (GET_SND_DATASIZE (Tcb->Sk) == 0) &&
+ TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)) {
+
+ DEBUG (
+ (EFI_D_NET,
+ "TcpToSendData: send FIN "
+ "to peer for TCB %p in state %s\n",
+ Tcb,
+ mTcpStateName[Tcb->State])
+ );
+
+ End++;
+ } else {
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
+ }
+ }
+
+ Seg->Seq = Seq;
+ Seg->End = End;
+ Seg->Flag = Flag;
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+ ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);
+
+ //
+ // don't send an empty segment here.
+ //
+ if (Seg->End == Seg->Seq) {
+ DEBUG ((EFI_D_WARN, "TcpToSendData: created a empty"
+ " segment for TCB %p, free it now\n", Tcb));
+
+ NetbufFree (Nbuf);
+ return Sent;
+ }
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ if ((Flag & TCP_FLG_FIN) != 0) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
+ }
+
+ goto OnError;
+ }
+
+ Sent += TCP_SUB_SEQ (End, Seq);
+
+ //
+ // All the buffer in the SndQue is headless
+ //
+ ASSERT (Nbuf->Tcp != NULL);
+
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ NetbufFree (Nbuf);
+
+ //
+ // update status in TCB
+ //
+ Tcb->DelayedAck = 0;
+
+ if ((Flag & TCP_FLG_FIN) != 0) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
+ }
+
+ if (TCP_SEQ_GT (End, Tcb->SndNxt)) {
+ Tcb->SndNxt = End;
+ }
+
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+ }
+
+ //
+ // Enable RTT measurement only if not in retransmit.
+ // Karn's algorithm reqires not to update RTT when in loss.
+ //
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
+ !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ DEBUG ((EFI_D_NET, "TcpToSendData: set RTT measure "
+ "sequence %d for TCB %p\n", Seq, Tcb));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ Tcb->RttSeq = Seq;
+ Tcb->RttMeasure = 0;
+ }
+
+ if (Len == Tcb->SndMss) {
+ goto SEND_AGAIN;
+ }
+
+ return Sent;
+
+OnError:
+ if (Nbuf != NULL) {
+ NetbufFree (Nbuf);
+ }
+
+ return Sent;
+}
+
+
+/**
+ Send an ACK immediately.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSendAck (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Seg->Seq = Tcb->SndNxt;
+ Seg->End = Tcb->SndNxt;
+ Seg->Flag = TCP_FLG_ACK;
+
+ if (TcpTransmitSegment (Tcb, Nbuf) == 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ Tcb->DelayedAck = 0;
+ }
+
+ NetbufFree (Nbuf);
+}
+
+
+/**
+ Send a zero probe segment. It can be used by keepalive and zero window probe.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The zero probe segment was sent out successfully.
+ @retval other Error condition occurred.
+
+**/
+INTN
+TcpSendZeroProbe (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+ INTN Result;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ //
+ // SndNxt-1 is out of window. The peer should respond
+ // with an ACK.
+ //
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Seg->Seq = Tcb->SndNxt - 1;
+ Seg->End = Tcb->SndNxt - 1;
+ Seg->Flag = TCP_FLG_ACK;
+
+ Result = TcpTransmitSegment (Tcb, Nbuf);
+ NetbufFree (Nbuf);
+
+ return Result;
+}
+
+
+/**
+ Check whether to send an ACK or delayed ACK.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpToSendAck (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ UINT32 TcpNow;
+
+ TcpNow = TcpRcvWinNow (Tcb);
+ //
+ // Generally, TCP should send a delayed ACK unless:
+ // 1. ACK at least every other FULL sized segment received,
+ // 2. Packets received out of order
+ // 3. Receiving window is open
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||
+ (Tcb->DelayedAck >= 1) ||
+ (TcpNow > TcpRcvWinOld (Tcb))) {
+ TcpSendAck (Tcb);
+ return;
+ }
+
+ DEBUG ((EFI_D_NET, "TcpToSendAck: scheduled a delayed"
+ " ACK for TCB %p\n", Tcb));
+
+ //
+ // schedule a delayed ACK
+ //
+ Tcb->DelayedAck++;
+}
+
+
+/**
+ Send a RESET segment in response to the segment received.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.
+ @param Head TCP header of the segment that triggers the reset.
+ @param Len Length of the segment that triggers the reset.
+ @param Local Local IP address.
+ @param Remote Remote peer's IP address.
+
+ @retval 0 A reset is sent or no need to send it.
+ @retval -1 No reset is sent.
+
+**/
+INTN
+TcpSendReset (
+ IN TCP_CB *Tcb,
+ IN TCP_HEAD *Head,
+ IN INT32 Len,
+ IN UINT32 Local,
+ IN UINT32 Remote
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_HEAD *Nhead;
+ UINT16 HeadSum;
+
+ //
+ // Don't respond to a Reset with reset
+ //
+ if ((Head->Flag & TCP_FLG_RST) != 0) {
+ return 0;
+ }
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_TAIL
+ );
+
+ ASSERT (Nhead != NULL);
+
+ Nbuf->Tcp = Nhead;
+ Nhead->Flag = TCP_FLG_RST;
+
+ //
+ // Derive Seq/ACK from the segment if no TCB
+ // associated with it, otherwise from the Tcb
+ //
+ if (Tcb == NULL) {
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {
+ Nhead->Seq = Head->Ack;
+ Nhead->Ack = 0;
+ } else {
+ Nhead->Seq = 0;
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
+ Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);
+ }
+ } else {
+
+ Nhead->Seq = HTONL (Tcb->SndNxt);
+ Nhead->Ack = HTONL (Tcb->RcvNxt);
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
+ }
+
+ Nhead->SrcPort = Head->DstPort;
+ Nhead->DstPort = Head->SrcPort;
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);
+ Nhead->Res = 0;
+ Nhead->Wnd = HTONS (0xFFFF);
+ Nhead->Checksum = 0;
+ Nhead->Urg = 0;
+
+ HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);
+ Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);
+
+ TcpSendIpPacket (Tcb, Nbuf, Local, Remote);
+
+ NetbufFree (Nbuf);
+ return 0;
+}
+
+
+/**
+ Verify that the segment is in good shape.
+
+ @param Nbuf Buffer that contains the segment to be checked.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpVerifySegment (
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_HEAD *Head;
+ TCP_SEG *Seg;
+ UINT32 Len;
+
+ if (Nbuf == NULL) {
+ return 1;
+ }
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Len = Nbuf->TotalSize;
+ Head = Nbuf->Tcp;
+
+ if (Head != NULL) {
+ if (Head->Flag != Seg->Flag) {
+ return 0;
+ }
+
+ Len -= (Head->HeadLen << 2);
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ Len++;
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ Len++;
+ }
+
+ if (Seg->Seq + Len != Seg->End) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ Verify that all the segments in SndQue are in good shape.
+
+ @param Head Pointer to the head node of the SndQue.
+
+ @retval 0 At least one segment is broken.
+ @retval 1 All segments in the specific queue are in good shape.
+
+**/
+INTN
+TcpCheckSndQue (
+ IN LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ TCP_SEQNO Seq;
+
+ if (IsListEmpty (Head)) {
+ return 1;
+ }
+ //
+ // Initialize the Seq
+ //
+ Entry = Head->ForwardLink;
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Seq = TCPSEG_NETBUF (Nbuf)->Seq;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ return 0;
+ }
+
+ //
+ // All the node in the SndQue should has:
+ // SEG.SEQ = LAST_SEG.END
+ //
+ if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {
+ return 0;
+ }
+
+ Seq = TCPSEG_NETBUF (Nbuf)->End;
+ }
+
+ return 1;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h
new file mode 100644
index 0000000000..01d6034b13
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h
@@ -0,0 +1,351 @@
+/** @file
+ Tcp Protocol header file.
+
+Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP4_PROTO_H_
+#define _TCP4_PROTO_H_
+
+typedef struct _TCP_CB TCP_CB;
+
+#include "Tcp4Driver.h"
+#include "Socket.h"
+#include "Tcp4Option.h"
+
+
+
+///
+/// Tcp states, Don't change their order, it is used as
+/// index to mTcpOutFlag and other macros
+///
+#define TCP_CLOSED 0
+#define TCP_LISTEN 1
+#define TCP_SYN_SENT 2
+#define TCP_SYN_RCVD 3
+#define TCP_ESTABLISHED 4
+#define TCP_FIN_WAIT_1 5
+#define TCP_FIN_WAIT_2 6
+#define TCP_CLOSING 7
+#define TCP_TIME_WAIT 8
+#define TCP_CLOSE_WAIT 9
+#define TCP_LAST_ACK 10
+
+
+///
+/// Flags in the TCP header
+///
+#define TCP_FLG_FIN 0x01
+#define TCP_FLG_SYN 0x02
+#define TCP_FLG_RST 0x04
+#define TCP_FLG_PSH 0x08
+#define TCP_FLG_ACK 0x10
+#define TCP_FLG_URG 0x20
+
+ //
+ // mask for all the flags
+ //
+#define TCP_FLG_FLAG 0x3F
+
+
+#define TCP_CONNECT_REFUSED (-1) ///< TCP error status
+#define TCP_CONNECT_RESET (-2) ///< TCP error status
+#define TCP_CONNECT_CLOSED (-3) ///< TCP error status
+
+//
+// Current congestion status as suggested by RFC3782.
+//
+#define TCP_CONGEST_RECOVER 1 ///< During the NewReno fast recovery
+#define TCP_CONGEST_LOSS 2 ///< Retxmit because of retxmit time out
+#define TCP_CONGEST_OPEN 3 ///< TCP is opening its congestion window
+
+//
+// TCP control flags
+//
+#define TCP_CTRL_NO_NAGLE 0x0001 ///< Disable Nagle algorithm
+#define TCP_CTRL_NO_KEEPALIVE 0x0002 ///< Disable keepalive timer
+#define TCP_CTRL_NO_WS 0x0004 ///< Disable window scale option
+#define TCP_CTRL_RCVD_WS 0x0008 ///< Received a wnd scale option in syn
+#define TCP_CTRL_NO_TS 0x0010 ///< Disable Timestamp option
+#define TCP_CTRL_RCVD_TS 0x0020 ///< Received a Timestamp option in syn
+#define TCP_CTRL_SND_TS 0x0040 ///< Send Timestamp option to remote
+#define TCP_CTRL_SND_URG 0x0080 ///< In urgent send mode
+#define TCP_CTRL_RCVD_URG 0x0100 ///< In urgent receive mode
+#define TCP_CTRL_SND_PSH 0x0200 ///< In PUSH send mode
+#define TCP_CTRL_FIN_SENT 0x0400 ///< FIN is sent
+#define TCP_CTRL_FIN_ACKED 0x0800 ///< FIN is ACKed.
+#define TCP_CTRL_TIMER_ON 0x1000 ///< At least one of the timer is on
+#define TCP_CTRL_RTT_ON 0x2000 ///< The RTT measurement is on
+#define TCP_CTRL_ACK_NOW 0x4000 ///< Send the ACK now, don't delay
+
+//
+// Timer related values
+//
+#define TCP_TIMER_CONNECT 0 ///< Connection establishment timer
+#define TCP_TIMER_REXMIT 1 ///< Retransmit timer
+#define TCP_TIMER_PROBE 2 ///< Window probe timer
+#define TCP_TIMER_KEEPALIVE 3 ///< Keepalive timer
+#define TCP_TIMER_FINWAIT2 4 ///< FIN_WAIT_2 timer
+#define TCP_TIMER_2MSL 5 ///< TIME_WAIT tiemr
+#define TCP_TIMER_NUMBER 6 ///< The total number of TCP timer.
+#define TCP_TICK 200 ///< Every TCP tick is 200ms
+#define TCP_TICK_HZ 5 ///< The frequence of TCP tick
+#define TCP_RTT_SHIFT 3 ///< SRTT & RTTVAR scaled by 8
+#define TCP_RTO_MIN TCP_TICK_HZ ///< The minium value of RTO
+#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maxium value of RTO
+#define TCP_FOLD_RTT 4 ///< Timeout threshod to fold RTT
+
+//
+// Default values for some timers
+//
+#define TCP_MAX_LOSS 12 ///< Default max times to retxmit
+#define TCP_KEEPALIVE_IDLE_MIN (TCP_TICK_HZ * 60 * 60 * 2) ///< First keep alive
+#define TCP_KEEPALIVE_PERIOD (TCP_TICK_HZ * 60)
+#define TCP_MAX_KEEPALIVE 8
+#define TCP_FIN_WAIT2_TIME (2 * TCP_TICK_HZ)
+#define TCP_TIME_WAIT_TIME (2 * TCP_TICK_HZ)
+#define TCP_PAWS_24DAY (24 * 24 * 60 * 60 * TCP_TICK_HZ)
+#define TCP_CONNECT_TIME (75 * TCP_TICK_HZ)
+
+//
+// The header space to be reserved before TCP data to accomodate :
+// 60byte IP head + 60byte TCP head + link layer head
+//
+#define TCP_MAX_HEAD 192
+
+//
+// Value ranges for some control option
+//
+#define TCP_RCV_BUF_SIZE (2 * 1024 * 1024)
+#define TCP_RCV_BUF_SIZE_MIN (8 * 1024)
+#define TCP_SND_BUF_SIZE (2 * 1024 * 1024)
+#define TCP_SND_BUF_SIZE_MIN (8 * 1024)
+#define TCP_BACKLOG 10
+#define TCP_BACKLOG_MIN 5
+#define TCP_MAX_LOSS_MIN 6
+#define TCP_CONNECT_TIME_MIN (60 * TCP_TICK_HZ)
+#define TCP_MAX_KEEPALIVE_MIN 4
+#define TCP_KEEPALIVE_IDLE_MAX (TCP_TICK_HZ * 60 * 60 * 4)
+#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30)
+#define TCP_FIN_WAIT2_TIME_MAX (4 * TCP_TICK_HZ)
+#define TCP_TIME_WAIT_TIME_MAX (60 * TCP_TICK_HZ)
+
+///
+/// TCP segmentation data
+///
+typedef struct _TCP_SEG {
+ TCP_SEQNO Seq; ///< Starting sequence number
+ TCP_SEQNO End; ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN
+ TCP_SEQNO Ack; ///< ACK field in the segment
+ UINT8 Flag; ///< TCP header flags
+ UINT16 Urg; ///< Valid if URG flag is set.
+ UINT32 Wnd; ///< TCP window size field
+} TCP_SEG;
+
+///
+/// Network endpoint, IP+Port structure
+///
+typedef struct _TCP_PEER {
+ UINT32 Ip; ///< IP address, network byte order
+ TCP_PORTNO Port; ///< Port number, network byte order
+} TCP_PEER;
+
+///
+/// TCP control block, it includes various states
+///
+struct _TCP_CB {
+ LIST_ENTRY List; ///< Back and forward link entry
+ TCP_CB *Parent; ///< The parent TCP_CB structure
+
+ SOCKET *Sk; ///< The socket it controled.
+ TCP_PEER LocalEnd; ///< Local endpoint
+ TCP_PEER RemoteEnd;///< Remote endpoint
+
+ LIST_ENTRY SndQue; ///< Retxmission queue
+ LIST_ENTRY RcvQue; ///< Reassemble queue
+ UINT32 CtrlFlag; ///< Control flags, such as NO_NAGLE
+ INT32 Error; ///< Soft error status, such as TCP_CONNECT_RESET
+
+ //
+ // RFC793 and RFC1122 defined variables
+ //
+ UINT8 State; ///< TCP state, such as SYN_SENT, LISTEN
+ UINT8 DelayedAck; ///< Number of delayed ACKs
+ UINT16 HeadSum; ///< Checksum of the fixed parts of pesudo
+ ///< header: Src IP, Dst IP, 0, Protocol,
+ ///< not include the TCP length.
+
+ TCP_SEQNO Iss; ///< Initial Sending Sequence
+ TCP_SEQNO SndUna; ///< First unacknowledged data
+ TCP_SEQNO SndNxt; ///< Next data sequence to send.
+ TCP_SEQNO SndPsh; ///< Send PUSH point
+ TCP_SEQNO SndUp; ///< Send urgent point
+ UINT32 SndWnd; ///< Window advertised by the remote peer
+ UINT32 SndWndMax; ///< Max send window advertised by the peer
+ TCP_SEQNO SndWl1; ///< Seq number used for last window update
+ TCP_SEQNO SndWl2; ///< Ack no of last window update
+ UINT16 SndMss; ///< Max send segment size
+ TCP_SEQNO RcvNxt; ///< Next sequence no to receive
+ UINT32 RcvWnd; ///< Window advertised by the local peer
+ TCP_SEQNO RcvWl2; ///< The RcvNxt (or ACK) of last window update.
+ ///< It is necessary because of delayed ACK
+
+ TCP_SEQNO RcvUp; ///< Urgent point;
+ TCP_SEQNO Irs; ///< Initial Receiving Sequence
+ UINT16 RcvMss; ///< Max receive segment size
+ UINT16 EnabledTimer; ///< Which timer is currently enabled
+ UINT32 Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire
+ INT32 NextExpire; ///< Count down offset for the nearest timer
+ UINT32 Idle; ///< How long the connection is in idle
+ UINT32 ProbeTime; ///< The time out value for current window prober
+ BOOLEAN ProbeTimerOn;///< If TRUE, the probe time is on.
+
+ //
+ // RFC1323 defined variables, about window scale,
+ // timestamp and PAWS
+ //
+ UINT8 SndWndScale; ///< Wndscale received from the peer
+ UINT8 RcvWndScale; ///< Wndscale used to scale local buffer
+ UINT32 TsRecent; ///< TsRecent to echo to the remote peer
+ UINT32 TsRecentAge; ///< When this TsRecent is updated
+
+ //
+ // RFC2988 defined variables. about RTT measurement
+ //
+ TCP_SEQNO RttSeq; ///< The seq of measured segment now
+ UINT32 RttMeasure; ///< Currently measured RTT in heart beats
+ UINT32 SRtt; ///< Smoothed RTT, scaled by 8
+ UINT32 RttVar; ///< RTT variance, scaled by 8
+ UINT32 Rto; ///< Current RTO, not scaled
+
+ //
+ // RFC2581, and 3782 variables.
+ // Congestion control + NewReno fast recovery.
+ //
+ UINT32 CWnd; ///< Sender's congestion window
+ UINT32 Ssthresh; ///< Slow start threshold.
+ TCP_SEQNO Recover; ///< Recover point for NewReno
+ UINT16 DupAck; ///< Number of duplicate ACKs
+ UINT8 CongestState; ///< The current congestion state(RFC3782)
+ UINT8 LossTimes; ///< Number of retxmit timeouts in a row
+ TCP_SEQNO LossRecover; ///< Recover point for retxmit
+
+ //
+ // configuration parameters, for EFI_TCP4_PROTOCOL specification
+ //
+ UINT32 KeepAliveIdle; ///< Idle time before sending first probe
+ UINT32 KeepAlivePeriod; ///< Interval for subsequent keep alive probe
+ UINT8 MaxKeepAlive; ///< Maxium keep alive probe times.
+ UINT8 KeepAliveProbes; ///< The number of keep alive probe.
+ UINT16 MaxRexmit; ///< The maxium number of retxmit before abort
+ UINT32 FinWait2Timeout; ///< The FIN_WAIT_2 time out
+ UINT32 TimeWaitTimeout; ///< The TIME_WAIT time out
+ UINT32 ConnectTimeout; ///< The connect establishment time out
+
+ //
+ // configuration for tcp provided by user
+ //
+ BOOLEAN UseDefaultAddr;
+ UINT8 Tos;
+ UINT8 Ttl;
+ EFI_IPv4_ADDRESS SubnetMask;
+
+ IP_IO_IP_INFO *IpInfo; ///<pointer reference to Ip used to send pkt
+};
+
+extern LIST_ENTRY mTcpRunQue;
+extern LIST_ENTRY mTcpListenQue;
+extern TCP_SEQNO mTcpGlobalIss;
+extern UINT32 mTcpTick;
+
+///
+/// TCP_CONNECTED: both ends have synchronized their ISN.
+///
+#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD)
+
+#define TCP_FIN_RCVD(State) \
+ (((State) == TCP_CLOSE_WAIT) || \
+ ((State) == TCP_LAST_ACK) || \
+ ((State) == TCP_CLOSING) || \
+ ((State) == TCP_TIME_WAIT))
+
+#define TCP_LOCAL_CLOSED(State) \
+ (((State) == TCP_FIN_WAIT_1) || \
+ ((State) == TCP_FIN_WAIT_2) || \
+ ((State) == TCP_CLOSING) || \
+ ((State) == TCP_TIME_WAIT) || \
+ ((State) == TCP_LAST_ACK))
+
+//
+// Get the TCP_SEG point from a net buffer's ProtoData
+//
+#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData))
+
+//
+// macros to compare sequence no
+//
+#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0)
+#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0)
+#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0)
+#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0)
+
+//
+// TCP_SEQ_BETWEEN return whether b <= m <= e
+//
+#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b))
+
+//
+// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2
+//
+#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2)))
+
+//
+// Check whether Flag is on
+//
+#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0))
+
+//
+// Set and Clear operation on a Flag
+//
+#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag))
+#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag))
+
+//
+// Test whether two peers are equal
+//
+#define TCP_PEER_EQUAL(Pa, Pb) \
+ (((Pa)->Ip == (Pb)->Ip) && ((Pa)->Port == (Pb)->Port))
+
+//
+// Test whether Pa matches Pb, or Pa is more specific
+// than pb. Zero means wildcard.
+//
+#define TCP_PEER_MATCH(Pa, Pb) \
+ ((((Pb)->Ip == 0) || ((Pb)->Ip == (Pa)->Ip)) && \
+ (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)))
+
+#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer)))
+#define TCP_SET_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) | (1 << (Timer))))
+#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer)))))
+
+#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0)
+#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0)
+#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb)))
+
+#define TCP_MAX_WIN 0xFFFFU
+
+typedef
+VOID
+(*TCP_TIMER_HANDLER) (
+ IN OUT TCP_CB *Tcb
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c
new file mode 100644
index 0000000000..a8e4a933cf
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c
@@ -0,0 +1,584 @@
+/** @file
+ TCP timer related functions.
+
+Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php<BR>
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Tcp4Main.h"
+
+UINT32 mTcpTick = 1000;
+
+/**
+ Connect timeout handler.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpConnectTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for TCP retransmission timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpRexmitTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for window probe timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpProbeTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for keepalive timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpKeepaliveTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for FIN_WAIT_2 timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpFinwait2Timeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for 2MSL timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+Tcp2MSLTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {
+ TcpConnectTimeout,
+ TcpRexmitTimeout,
+ TcpProbeTimeout,
+ TcpKeepaliveTimeout,
+ TcpFinwait2Timeout,
+ Tcp2MSLTimeout,
+};
+
+/**
+ Close the TCP connection.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClose (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+
+ TcpSetState (Tcb, TCP_CLOSED);
+}
+
+
+/**
+ Connect timeout handler.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpConnectTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ if (!TCP_CONNECTED (Tcb->State)) {
+ DEBUG ((EFI_D_ERROR, "TcpConnectTimeout: connection closed "
+ "because conenction timer timeout for TCB %p\n", Tcb));
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ if (TCP_SYN_RCVD == Tcb->State) {
+ DEBUG ((EFI_D_WARN, "TcpConnectTimeout: send reset because "
+ "connection timer timeout for TCB %p\n", Tcb));
+
+ TcpResetConnection (Tcb);
+
+ }
+
+ TcpClose (Tcb);
+ }
+}
+
+
+/**
+ Timeout handler for TCP retransmission timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpRexmitTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ UINT32 FlightSize;
+
+ DEBUG ((EFI_D_WARN, "TcpRexmitTimeout: transmission "
+ "timeout for TCB %p\n", Tcb));
+
+ //
+ // Set the congestion window. FlightSize is the
+ // amount of data that has been sent but not
+ // yet ACKed.
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+ Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->LossRecover = Tcb->SndNxt;
+
+ Tcb->LossTimes++;
+ if ((Tcb->LossTimes > Tcb->MaxRexmit) &&
+ !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
+
+ DEBUG ((EFI_D_ERROR, "TcpRexmitTimeout: connection closed "
+ "because too many timeouts for TCB %p\n", Tcb));
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ TcpClose (Tcb);
+ return ;
+ }
+
+ TcpBackoffRto (Tcb);
+ TcpRetransmit (Tcb, Tcb->SndUna);
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+
+ Tcb->CongestState = TCP_CONGEST_LOSS;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+}
+
+
+/**
+ Timeout handler for window probe timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpProbeTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ //
+ // This is the timer for sender's SWSA. RFC1122 requires
+ // a timer set for sender's SWSA, and suggest combine it
+ // with window probe timer. If data is sent, don't set
+ // the probe timer, since retransmit timer is on.
+ //
+ if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
+
+ ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);
+ Tcb->ProbeTimerOn = FALSE;
+ return ;
+ }
+
+ TcpSendZeroProbe (Tcb);
+ TcpSetProbeTimer (Tcb);
+}
+
+
+/**
+ Timeout handler for keepalive timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpKeepaliveTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ Tcb->KeepAliveProbes++;
+
+ //
+ // Too many Keep-alive probes, drop the connection
+ //
+ if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ TcpClose (Tcb);
+ return ;
+ }
+
+ TcpSendZeroProbe (Tcb);
+ TcpSetKeepaliveTimer (Tcb);
+}
+
+
+/**
+ Timeout handler for FIN_WAIT_2 timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpFinwait2Timeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ DEBUG ((EFI_D_WARN, "TcpFinwait2Timeout: connection closed "
+ "because FIN_WAIT2 timer timeouts for TCB %p\n", Tcb));
+
+ TcpClose (Tcb);
+}
+
+
+/**
+ Timeout handler for 2MSL timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+Tcp2MSLTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ DEBUG ((EFI_D_WARN, "Tcp2MSLTimeout: connection closed "
+ "because TIME_WAIT timer timeouts for TCB %p\n", Tcb));
+
+ TcpClose (Tcb);
+}
+
+
+/**
+ Update the timer status and the next expire time according to the timers
+ to expire in a specific future time slot.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpUpdateTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ UINT16 Index;
+
+ //
+ // Don't use a too large value to init NextExpire
+ // since mTcpTick wraps around as sequence no does.
+ //
+ Tcb->NextExpire = 65535;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
+
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
+ TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)) {
+
+ Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
+ }
+ }
+}
+
+
+/**
+ Enable a TCP timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Timer The index of the timer to be enabled.
+ @param TimeOut The timeout value of this timer.
+
+**/
+VOID
+TcpSetTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer,
+ IN UINT32 TimeOut
+ )
+{
+ TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
+ Tcb->Timer[Timer] = mTcpTick + TimeOut;
+
+ TcpUpdateTimer (Tcb);
+}
+
+
+/**
+ Clear one TCP timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Timer The index of the timer to be cleared.
+
+**/
+VOID
+TcpClearTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer
+ )
+{
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
+ TcpUpdateTimer (Tcb);
+}
+
+
+/**
+ Clear all TCP timers.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClearAllTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ Tcb->EnabledTimer = 0;
+ TcpUpdateTimer (Tcb);
+}
+
+
+/**
+ Enable the window prober timer and set the timeout value.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetProbeTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ if (!Tcb->ProbeTimerOn) {
+ Tcb->ProbeTime = Tcb->Rto;
+ Tcb->ProbeTimerOn = TRUE;
+
+ } else {
+ Tcb->ProbeTime <<= 1;
+ }
+
+ if (Tcb->ProbeTime < TCP_RTO_MIN) {
+
+ Tcb->ProbeTime = TCP_RTO_MIN;
+ } else if (Tcb->ProbeTime > TCP_RTO_MAX) {
+
+ Tcb->ProbeTime = TCP_RTO_MAX;
+ }
+
+ TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
+}
+
+
+/**
+ Enable the keepalive timer and set the timeout value.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetKeepaliveTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
+ return ;
+
+ }
+
+ //
+ // Set the timer to KeepAliveIdle if either
+ // 1. the keepalive timer is off
+ // 2. The keepalive timer is on, but the idle
+ // is less than KeepAliveIdle, that means the
+ // connection is alive since our last probe.
+ //
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
+ (Tcb->Idle < Tcb->KeepAliveIdle)) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
+ Tcb->KeepAliveProbes = 0;
+
+ } else {
+
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
+ }
+}
+
+
+/**
+ Backoff the RTO.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpBackoffRto (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ //
+ // Fold the RTT estimate if too many times, the estimate
+ // may be wrong, fold it. So the next time a valid
+ // measurement is sampled, we can start fresh.
+ //
+ if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {
+ Tcb->RttVar += Tcb->SRtt >> 2;
+ Tcb->SRtt = 0;
+ }
+
+ Tcb->Rto <<= 1;
+
+ if (Tcb->Rto < TCP_RTO_MIN) {
+
+ Tcb->Rto = TCP_RTO_MIN;
+ } else if (Tcb->Rto > TCP_RTO_MAX) {
+
+ Tcb->Rto = TCP_RTO_MAX;
+ }
+}
+
+
+/**
+ Heart beat timer handler.
+
+ @param Context Context of the timer event, ignored.
+
+**/
+VOID
+EFIAPI
+TcpTickingDpc (
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ TCP_CB *Tcb;
+ INT16 Index;
+
+ mTcpTick++;
+ mTcpGlobalIss += 100;
+
+ //
+ // Don't use LIST_FOR_EACH, which isn't delete safe.
+ //
+ for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
+
+ Next = Entry->ForwardLink;
+
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (Tcb->State == TCP_CLOSED) {
+ continue;
+ }
+ //
+ // The connection is doing RTT measurement.
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+ Tcb->RttMeasure++;
+ }
+
+ Tcb->Idle++;
+
+ if (Tcb->DelayedAck != 0) {
+ TcpSendAck (Tcb);
+ }
+
+ //
+ // No timer is active or no timer expired
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) ||
+ ((--Tcb->NextExpire) > 0)) {
+
+ continue;
+ }
+
+ //
+ // Call the timeout handler for each expired timer.
+ //
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
+ TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
+ //
+ // disable the timer before calling the handler
+ // in case the handler enables it again.
+ //
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
+ mTcpTimerHandler[Index](Tcb);
+
+ //
+ // The Tcb may have been deleted by the timer, or
+ // no other timer is set.
+ //
+ if ((Next->BackLink != Entry) ||
+ (Tcb->EnabledTimer == 0)) {
+ break;
+ }
+ }
+ }
+
+ //
+ // If the Tcb still exist or some timer is set, update the timer
+ //
+ if (Index == TCP_TIMER_NUMBER) {
+ TcpUpdateTimer (Tcb);
+ }
+ }
+}
+
+/**
+ Heart beat timer handler, queues the DPC at TPL_CALLBACK.
+
+ @param Event Timer event signaled, ignored.
+ @param Context Context of the timer event, ignored.
+
+**/
+VOID
+EFIAPI
+TcpTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..cf311055e2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c
@@ -0,0 +1,435 @@
+/** @file
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Udp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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
+UdpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+UdpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUdp4ComponentName = {
+ UdpComponentNameGetDriverName,
+ UdpComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UdpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UdpComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdpDriverNameTable[] = {
+ {
+ "eng;en",
+ L"UDP Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gUdpControllerNameTable = NULL;
+
+/**
+ 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
+UdpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUdpDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gUdp4ComponentName)
+ );
+}
+
+/**
+ Update the component name for the Udp4 child handle.
+
+ @param Udp4[in] A pointer to the EFI_UDP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ EFI_UDP4_PROTOCOL *Udp4
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[64];
+ EFI_UDP4_CONFIG_DATA Udp4ConfigData;
+
+ if (Udp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer as:
+ // UDPv4 (SrcPort=59, DestPort=60)
+ //
+ Status = Udp4->GetModeData (Udp4, &Udp4ConfigData, NULL, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"UDPv4 (SrcPort=%d, DestPort=%d)",
+ Udp4ConfigData.StationPort,
+ Udp4ConfigData.RemotePort
+ );
+ } else if (Status == EFI_NOT_STARTED) {
+ UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"UDPv4 (Not started)"
+ );
+ } else {
+ return Status;
+ }
+
+ if (gUdpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gUdpControllerNameTable);
+ gUdpControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gUdp4ComponentName.SupportedLanguages,
+ &gUdpControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gUdp4ComponentName2.SupportedLanguages,
+ &gUdpControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+UdpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_UDP4_PROTOCOL *Udp4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiIp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **)&Udp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Udp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gUdpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gUdp4ComponentName)
+ );
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
new file mode 100644
index 0000000000..4d179d1b28
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
@@ -0,0 +1,590 @@
+/** @file
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Udp4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gUdp4DriverBinding = {
+ Udp4DriverBindingSupported,
+ Udp4DriverBindingStart,
+ Udp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mUdp4ServiceBinding = {
+ Udp4ServiceBindingCreateChild,
+ Udp4ServiceBindingDestroyChild
+};
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NET_LIST_USER_STRUCT_S (Entry, UDP4_INSTANCE_DATA, Link, UDP4_INSTANCE_DATA_SIGNATURE);
+ ServiceBinding = ((UDP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
+ NumberOfChildren = ((UDP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
+ ChildHandleBuffer = ((UDP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
+
+ if (!NetIsInHandleBuffer (Instance->ChildHandle, NumberOfChildren, ChildHandleBuffer)) {
+ return EFI_SUCCESS;
+ }
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle);
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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
+Udp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the Udp4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test for the Ip4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_SERVICE_DATA *Udp4Service;
+
+ //
+ // Allocate Private Context Data Structure.
+ //
+ Udp4Service = AllocatePool (sizeof (UDP4_SERVICE_DATA));
+ if (Udp4Service == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Udp4CreateService (Udp4Service, This->DriverBindingHandle, ControllerHandle);
+ if (EFI_ERROR (Status)) {
+ FreePool (Udp4Service);
+ return Status;
+ }
+
+ //
+ // Install the Udp4ServiceBindingProtocol on the ControllerHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Udp4Service->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Udp4CleanService (Udp4Service);
+ FreePool (Udp4Service);
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @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
+Udp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UDP4_SERVICE_DATA *Udp4Service;
+ UDP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
+ LIST_ENTRY *List;
+
+ //
+ // Find the NicHandle where UDP4 ServiceBinding Protocol is installed.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Retrieve the UDP4 ServiceBinding Protocol.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (ServiceBinding);
+ if (NumberOfChildren != 0) {
+ //
+ // NumberOfChildren is not zero, destroy the children instances in ChildHandleBuffer.
+ //
+ List = &Udp4Service->ChildrenList;
+ Context.ServiceBinding = ServiceBinding;
+ Context.NumberOfChildren = NumberOfChildren;
+ Context.ChildHandleBuffer = ChildHandleBuffer;
+ Status = NetDestroyLinkList (
+ List,
+ Udp4DestroyChildEntryInHandleBuffer,
+ &Context,
+ NULL
+ );
+ } else {
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Udp4Service->ServiceBinding,
+ NULL
+ );
+
+ Udp4CleanService (Udp4Service);
+
+ if (gUdpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gUdpControllerNameTable);
+ gUdpControllerNameTable = NULL;
+ }
+ FreePool (Udp4Service);
+ }
+
+ return Status;
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ UDP4_SERVICE_DATA *Udp4Service;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ VOID *Ip4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate the instance private data structure.
+ //
+ Instance = AllocateZeroPool (sizeof (UDP4_INSTANCE_DATA));
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Udp4InitInstance (Udp4Service, Instance);
+
+ //
+ // Add an IpInfo for this instance.
+ //
+ Instance->IpInfo = IpIoAddIp (Udp4Service->IpIo);
+ if (Instance->IpInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Udp4Protocol for this instance.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ &Instance->Udp4Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Instance->ChildHandle = *ChildHandle;
+
+ //
+ // Open the default Ip4 protocol in the IP_IO BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Udp4Service->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ gUdp4DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open this instance's Ip4 protocol in the IpInfo BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Instance->IpInfo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ gUdp4DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Link this instance into the service context data and increase the ChildrenNumber.
+ //
+ InsertTailList (&Udp4Service->ChildrenList, &Instance->Link);
+ Udp4Service->ChildrenNumber++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Instance->ChildHandle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ &Instance->Udp4Proto,
+ NULL
+ );
+ }
+
+ if (Instance->IpInfo != NULL) {
+ IpIoRemoveIp (Udp4Service->IpIo, Instance->IpInfo);
+ }
+
+ Udp4CleanInstance (Instance);
+
+ FreePool (Instance);
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ UDP4_SERVICE_DATA *Udp4Service;
+ EFI_UDP4_PROTOCOL *Udp4Proto;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Try to get the Udp4 protocol from the ChildHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4Proto,
+ gUdp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (Udp4Proto);
+
+ if (Instance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Use the Destroyed flag to avoid the re-entering of the following code.
+ //
+ Instance->InDestroy = TRUE;
+
+ //
+ // Close the Ip4 protocol.
+ //
+ gBS->CloseProtocol (
+ Udp4Service->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ gUdp4DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle
+ );
+ //
+ // Close the Ip4 protocol on this instance's IpInfo.
+ //
+ gBS->CloseProtocol (
+ Instance->IpInfo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ gUdp4DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle
+ );
+
+ //
+ // Uninstall the Udp4Protocol previously installed on the ChildHandle.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID *) &Instance->Udp4Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Instance->InDestroy = FALSE;
+ return Status;
+ }
+
+ //
+ // Reset the configuration in case the instance's consumer forgets to do this.
+ //
+ Udp4Proto->Configure (Udp4Proto, NULL);
+
+ //
+ // Remove the IpInfo this instance consumes.
+ //
+ IpIoRemoveIp (Udp4Service->IpIo, Instance->IpInfo);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Remove this instance from the service context data's ChildrenList.
+ //
+ RemoveEntryList (&Instance->Link);
+ Udp4Service->ChildrenNumber--;
+
+ //
+ // Clean the instance.
+ //
+ Udp4CleanInstance (Instance);
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for Udp4 driver which installs the driver binding
+ and component name protocol on its ImageHandle.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install the Udp4DriverBinding and Udp4ComponentName protocols.
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gUdp4DriverBinding,
+ ImageHandle,
+ &gUdp4ComponentName,
+ &gUdp4ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Initialize the UDP random port.
+ //
+ mUdp4RandomPort = (UINT16) (((UINT16) NetRandomInitSeed ()) % UDP4_PORT_KNOWN + UDP4_PORT_KNOWN);
+ }
+
+ return Status;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h
new file mode 100644
index 0000000000..88421aa816
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h
@@ -0,0 +1,154 @@
+/** @file
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _UDP4_DRIVER_H_
+#define _UDP4_DRIVER_H_
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @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
+Udp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @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_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @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
+Udp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
new file mode 100644
index 0000000000..e1b4898c31
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
@@ -0,0 +1,70 @@
+## @file
+# This module produces EFI UDP Protocol and EFI UDPv4 Service Binding Protocol.
+#
+# This module produces EFI UDP(User Datagram Protocol) Protocol upon EFI IPv4
+# Protocol, to provide basic UDPv4 I/O services.
+#
+# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Udp4Dxe
+ MODULE_UNI_FILE = Udp4Dxe.uni
+ FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af2b
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Udp4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gUdp4DriverBinding
+# COMPONENT_NAME = gUdp4ComponentName
+# COMPONENT_NAME2 = gUdp4ComponentName2
+#
+
+[Sources]
+ Udp4Impl.h
+ Udp4Main.c
+ ComponentName.c
+ Udp4Impl.c
+ Udp4Driver.h
+ Udp4Driver.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DebugLib
+ IpIoLib
+ NetLib
+ DpcLib
+
+[Protocols]
+ gEfiUdp4ServiceBindingProtocolGuid ## BY_START
+ gEfiIp4ServiceBindingProtocolGuid ## TO_START
+ gEfiUdp4ProtocolGuid ## BY_START
+ gEfiIp4ProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Udp4DxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.uni b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.uni
new file mode 100644
index 0000000000..87c235d528
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// This module produces EFI UDP Protocol and EFI UDPv4 Service Binding Protocol.
+//
+// This module produces EFI UDP(User Datagram Protocol) Protocol upon EFI IPv4
+// Protocol, to provide basic UDPv4 I/O services.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI UDP Protocol and EFI UDPv4 Service Binding Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI UDP(User Datagram Protocol) Protocol upon EFI IPv4 Protocol to provide basic UDPv4 I/O services."
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni
new file mode 100644
index 0000000000..249c4407ef
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Udp4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UDP v4 DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
new file mode 100644
index 0000000000..df41433a1a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
@@ -0,0 +1,1914 @@
+/** @file
+ The implementation of the Udp4 protocol.
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "Udp4Impl.h"
+
+UINT16 mUdp4RandomPort;
+
+/**
+ This function checks and timeouts the I/O datagrams holding by the corresponding
+ service context.
+
+ @param[in] Event The event this function registered to.
+ @param[in] Context The context data registered during the creation of
+ the Event.
+
+**/
+VOID
+EFIAPI
+Udp4CheckTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ This function finds the udp instance by the specified <Address, Port> pair.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in] Address Pointer to the specified IPv4 address.
+ @param[in] Port The udp port number.
+
+ @retval TRUE The specified <Address, Port> pair is found.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp4FindInstanceByPort (
+ IN LIST_ENTRY *InstanceList,
+ IN EFI_IPv4_ADDRESS *Address,
+ IN UINT16 Port
+ );
+
+/**
+ This function is the packet transmitting notify function registered to the IpIo
+ interface. It's called to signal the udp TxToken when IpIo layer completes the
+ transmitting of the udp datagram.
+
+ @param[in] Status The completion status of the output udp datagram.
+ @param[in] Context Pointer to the context data.
+ @param[in] Sender Specify a pointer of EFI_IP4_PROTOCOL for sending.
+ @param[in] NotifyData Pointer to the notify data.
+
+**/
+VOID
+EFIAPI
+Udp4DgramSent (
+ IN EFI_STATUS Status,
+ IN VOID *Context,
+ IN IP_IO_IP_PROTOCOL Sender,
+ IN VOID *NotifyData
+ );
+
+/**
+ This function processes the received datagram passed up by the IpIo layer.
+
+ @param[in] Status The status of this udp datagram.
+ @param[in] IcmpError The IcmpError code, only available when Status is
+ EFI_ICMP_ERROR.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA.
+ @param[in] Packet Pointer to the NET_BUF containing the received udp
+ datagram.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp4DgramRcvd (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet,
+ IN VOID *Context
+ );
+
+/**
+ This function cancels the token specified by Arg in the Map. This is a callback
+ used by Udp4InstanceCancelToken().
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the token to be cancelled, if NULL,
+ the token specified by Item is cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token
+ is not the same as that in the Item if Arg is not
+ NULL.
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
+ cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4CancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ );
+
+/**
+ This function matches the received udp datagram with the Instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted
+ from the received udp datagram.
+
+ @retval TRUE The udp datagram matches the receiving requirments of the
+ udp Instance.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp4MatchDgram (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_SESSION_DATA *Udp4Session
+ );
+
+/**
+ This function removes the Wrap specified by Context and release relevant resources.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp4RecycleRxDataWrap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ This function wraps the Packet and the RxData.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this
+ datagram.
+
+ @return Pointer to the structure wrapping the RxData and the Packet.
+
+**/
+UDP4_RXDATA_WRAP *
+Udp4WrapRxData (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ );
+
+/**
+ This function enqueues the received datagram into the instances' receiving queues.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this
+ datagram.
+
+ @return The times this datagram is enqueued.
+
+**/
+UINTN
+Udp4EnqueueDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ );
+
+/**
+ This function delivers the datagrams enqueued in the instances.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+
+**/
+VOID
+Udp4DeliverDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ );
+
+/**
+ This function demultiplexes the received udp datagram to the appropriate instances.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted from
+ the received datagram.
+ @param[in] Packet Pointer to the buffer containing the received udp
+ datagram.
+
+**/
+VOID
+Udp4Demultiplex (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ );
+
+/**
+ This function handles the received Icmp Error message and demultiplexes it to the
+ instance.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] IcmpError The icmp error code.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted
+ from the received Icmp Error packet.
+ @param[in] Packet Pointer to the Icmp Error packet.
+
+**/
+VOID
+Udp4IcmpHandler (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ );
+
+/**
+ This function builds and sends out a icmp port unreachable message.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet
+ causes this icmp error message.
+ @param[in] Udp4Header Pointer to the udp header of the datagram causes
+ this icmp error message.
+
+**/
+VOID
+Udp4SendPortUnreach (
+ IN IP_IO *IpIo,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN VOID *Udp4Header
+ );
+
+
+/**
+ Create the Udp service context data.
+
+ @param[in, out] Udp4Service Pointer to the UDP4_SERVICE_DATA.
+ @param[in] ImageHandle The image handle of this udp4 driver.
+ @param[in] ControllerHandle The controller handle this udp4 driver binds on.
+
+ @retval EFI_SUCCESS The udp4 service context data is created and
+ initialized.
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Udp4CreateService (
+ IN OUT UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_OPEN_DATA OpenData;
+ EFI_IP4_CONFIG_DATA *Ip4ConfigData;
+
+ ZeroMem (Udp4Service, sizeof (UDP4_SERVICE_DATA));
+
+ Udp4Service->Signature = UDP4_SERVICE_DATA_SIGNATURE;
+ Udp4Service->ServiceBinding = mUdp4ServiceBinding;
+ Udp4Service->ImageHandle = ImageHandle;
+ Udp4Service->ControllerHandle = ControllerHandle;
+ Udp4Service->ChildrenNumber = 0;
+
+ InitializeListHead (&Udp4Service->ChildrenList);
+
+ //
+ // Create the IpIo for this service context.
+ //
+ Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_4);
+ if (Udp4Service->IpIo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Set the OpenData used to open the IpIo.
+ //
+ Ip4ConfigData = &OpenData.IpConfigData.Ip4CfgData;
+ CopyMem (Ip4ConfigData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));
+ Ip4ConfigData->AcceptBroadcast = TRUE;
+ OpenData.RcvdContext = (VOID *) Udp4Service;
+ OpenData.SndContext = NULL;
+ OpenData.PktRcvdNotify = Udp4DgramRcvd;
+ OpenData.PktSentNotify = Udp4DgramSent;
+
+ //
+ // Configure and start the IpIo.
+ //
+ Status = IpIoOpen (Udp4Service->IpIo, &OpenData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create the event for Udp timeout checking.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Udp4CheckTimeout,
+ Udp4Service,
+ &Udp4Service->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Start the timeout timer event.
+ //
+ Status = gBS->SetTimer (
+ Udp4Service->TimeoutEvent,
+ TimerPeriodic,
+ UDP4_TIMEOUT_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Udp4Service->TimeoutEvent != NULL) {
+ gBS->CloseEvent (Udp4Service->TimeoutEvent);
+ }
+
+ IpIoDestroy (Udp4Service->IpIo);
+
+ return Status;
+}
+
+
+/**
+ Clean the Udp service context data.
+
+ @param[in] Udp4Service Pointer to the UDP4_SERVICE_DATA.
+
+**/
+VOID
+Udp4CleanService (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ )
+{
+ //
+ // Cancel the TimeoutEvent timer.
+ //
+ gBS->SetTimer (Udp4Service->TimeoutEvent, TimerCancel, 0);
+
+ //
+ // Close the TimeoutEvent timer.
+ //
+ gBS->CloseEvent (Udp4Service->TimeoutEvent);
+
+ //
+ // Destroy the IpIo.
+ //
+ IpIoDestroy (Udp4Service->IpIo);
+}
+
+
+/**
+ This function checks and timeouts the I/O datagrams holding by the corresponding
+ service context.
+
+ @param[in] Event The event this function registered to.
+ @param[in] Context The context data registered during the creation of
+ the Event.
+
+**/
+VOID
+EFIAPI
+Udp4CheckTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UDP4_SERVICE_DATA *Udp4Service;
+ LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+ LIST_ENTRY *WrapEntry;
+ LIST_ENTRY *NextEntry;
+ UDP4_RXDATA_WRAP *Wrap;
+
+ Udp4Service = (UDP4_SERVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (Udp4Service, UDP4_SERVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate all the instances belonging to this service context.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+ NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {
+ //
+ // Skip this instance if it's not configured or no receive timeout.
+ //
+ continue;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {
+ //
+ // Iterate all the rxdatas belonging to this udp instance.
+ //
+ Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP4_RXDATA_WRAP, Link);
+
+ //
+ // TimeoutTick unit is microsecond, MNP_TIMEOUT_CHECK_INTERVAL unit is 100ns.
+ //
+ if (Wrap->TimeoutTick < (UDP4_TIMEOUT_INTERVAL / 10)) {
+ //
+ // Remove this RxData if it timeouts.
+ //
+ Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap);
+ } else {
+ Wrap->TimeoutTick -= (UDP4_TIMEOUT_INTERVAL / 10);
+ }
+ }
+ }
+}
+
+
+/**
+ This function intializes the new created udp instance.
+
+ @param[in] Udp4Service Pointer to the UDP4_SERVICE_DATA.
+ @param[in, out] Instance Pointer to the un-initialized UDP4_INSTANCE_DATA.
+
+**/
+VOID
+Udp4InitInstance (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN OUT UDP4_INSTANCE_DATA *Instance
+ )
+{
+ //
+ // Set the signature.
+ //
+ Instance->Signature = UDP4_INSTANCE_DATA_SIGNATURE;
+
+ //
+ // Init the lists.
+ //
+ InitializeListHead (&Instance->Link);
+ InitializeListHead (&Instance->RcvdDgramQue);
+ InitializeListHead (&Instance->DeliveredDgramQue);
+
+ //
+ // Init the NET_MAPs.
+ //
+ NetMapInit (&Instance->TxTokens);
+ NetMapInit (&Instance->RxTokens);
+ NetMapInit (&Instance->McastIps);
+
+ //
+ // Save the pointer to the UDP4_SERVICE_DATA, and initialize other members.
+ //
+ Instance->Udp4Service = Udp4Service;
+ CopyMem (&Instance->Udp4Proto, &mUdp4Protocol, sizeof (Instance->Udp4Proto));
+ Instance->IcmpError = EFI_SUCCESS;
+ Instance->Configured = FALSE;
+ Instance->IsNoMapping = FALSE;
+ Instance->InDestroy = FALSE;
+}
+
+
+/**
+ This function cleans the udp instance.
+
+ @param[in] Instance Pointer to the UDP4_INSTANCE_DATA to clean.
+
+**/
+VOID
+Udp4CleanInstance (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ NetMapClean (&Instance->McastIps);
+ NetMapClean (&Instance->RxTokens);
+ NetMapClean (&Instance->TxTokens);
+}
+
+
+/**
+ This function finds the udp instance by the specified <Address, Port> pair.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in] Address Pointer to the specified IPv4 address.
+ @param[in] Port The udp port number.
+
+ @retval TRUE The specified <Address, Port> pair is found.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp4FindInstanceByPort (
+ IN LIST_ENTRY *InstanceList,
+ IN EFI_IPv4_ADDRESS *Address,
+ IN UINT16 Port
+ )
+{
+ LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+
+ NET_LIST_FOR_EACH (Entry, InstanceList) {
+ //
+ // Iterate all the udp instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+ ConfigData = &Instance->ConfigData;
+
+ if (!Instance->Configured || ConfigData->AcceptAnyPort) {
+ //
+ // If the instance is not configured or the configdata of the instance indicates
+ // this instance accepts any port, skip it.
+ //
+ continue;
+ }
+
+ if (EFI_IP4_EQUAL (&ConfigData->StationAddress, Address) &&
+ (ConfigData->StationPort == Port)) {
+ //
+ // if both the address and the port are the same, return TRUE.
+ //
+ return TRUE;
+ }
+ }
+
+ //
+ // return FALSE when matching fails.
+ //
+ return FALSE;
+}
+
+
+/**
+ This function tries to bind the udp instance according to the configured port
+ allocation strategy.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in, out] ConfigData Pointer to the ConfigData of the instance to be
+ bound. ConfigData->StationPort will be assigned
+ with an available port value on success.
+
+ @retval EFI_SUCCESS The bound operation is completed successfully.
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is
+ already used by other instance.
+ @retval EFI_OUT_OF_RESOURCES No available port resources.
+
+**/
+EFI_STATUS
+Udp4Bind (
+ IN LIST_ENTRY *InstanceList,
+ IN OUT EFI_UDP4_CONFIG_DATA *ConfigData
+ )
+{
+ EFI_IPv4_ADDRESS *StationAddress;
+ UINT16 StartPort;
+
+ if (ConfigData->AcceptAnyPort) {
+ return EFI_SUCCESS;
+ }
+
+ StationAddress = &ConfigData->StationAddress;
+
+ if (ConfigData->StationPort != 0) {
+
+ if (!ConfigData->AllowDuplicatePort &&
+ Udp4FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)) {
+ //
+ // Do not allow duplicate port and the port is already used by other instance.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+ } else {
+ //
+ // select a random port for this instance;
+ //
+
+ if (ConfigData->AllowDuplicatePort) {
+ //
+ // Just pick up the random port if the instance allows duplicate port.
+ //
+ ConfigData->StationPort = mUdp4RandomPort;
+ } else {
+
+ StartPort = mUdp4RandomPort;
+
+ while (Udp4FindInstanceByPort(InstanceList, StationAddress, mUdp4RandomPort)) {
+
+ mUdp4RandomPort++;
+ if (mUdp4RandomPort == 0) {
+ mUdp4RandomPort = UDP4_PORT_KNOWN;
+ }
+
+ if (mUdp4RandomPort == StartPort) {
+ //
+ // No available port.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ ConfigData->StationPort = mUdp4RandomPort;
+ }
+
+ mUdp4RandomPort++;
+ if (mUdp4RandomPort == 0) {
+ mUdp4RandomPort = UDP4_PORT_KNOWN;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function is used to check whether the NewConfigData has any un-reconfigurable
+ parameters changed compared to the OldConfigData.
+
+ @param[in] OldConfigData Pointer to the current ConfigData the udp instance
+ uses.
+ @param[in] NewConfigData Pointer to the new ConfigData.
+
+ @retval TRUE The instance is reconfigurable.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp4IsReconfigurable (
+ IN EFI_UDP4_CONFIG_DATA *OldConfigData,
+ IN EFI_UDP4_CONFIG_DATA *NewConfigData
+ )
+{
+ if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||
+ (NewConfigData->AcceptBroadcast != OldConfigData->AcceptBroadcast) ||
+ (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||
+ (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)
+ ) {
+ //
+ // The receiving filter parameters cannot be changed.
+ //
+ return FALSE;
+ }
+
+ if ((!NewConfigData->AcceptAnyPort) &&
+ (NewConfigData->StationPort != OldConfigData->StationPort)
+ ) {
+ //
+ // The port is not changeable.
+ //
+ return FALSE;
+ }
+
+ if (!NewConfigData->AcceptPromiscuous) {
+
+ if (NewConfigData->UseDefaultAddress != OldConfigData->UseDefaultAddress) {
+ //
+ // The NewConfigData differs to the old one on the UseDefaultAddress.
+ //
+ return FALSE;
+ }
+
+ if (!NewConfigData->UseDefaultAddress &&
+ (!EFI_IP4_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress) ||
+ !EFI_IP4_EQUAL (&NewConfigData->SubnetMask, &OldConfigData->SubnetMask))
+ ) {
+ //
+ // If the instance doesn't use the default address, and the new address or
+ // new subnet mask is different from the old values.
+ //
+ return FALSE;
+ }
+ }
+
+ if (!EFI_IP4_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) {
+ //
+ // The remoteaddress is not the same.
+ //
+ return FALSE;
+ }
+
+ if (!EFI_IP4_EQUAL (&NewConfigData->RemoteAddress, &mZeroIp4Addr) &&
+ NewConfigData->RemotePort != OldConfigData->RemotePort
+ ) {
+ //
+ // The RemotePort differs if it's designated in the configdata.
+ //
+ return FALSE;
+ }
+
+ //
+ // All checks pass, return TRUE.
+ //
+ return TRUE;
+}
+
+
+/**
+ This function builds the Ip4 configdata from the Udp4ConfigData.
+
+ @param[in] Udp4ConfigData Pointer to the EFI_UDP4_CONFIG_DATA.
+ @param[in, out] Ip4ConfigData Pointer to the EFI_IP4_CONFIG_DATA.
+
+**/
+VOID
+Udp4BuildIp4ConfigData (
+ IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData,
+ IN OUT EFI_IP4_CONFIG_DATA *Ip4ConfigData
+ )
+{
+ CopyMem (Ip4ConfigData, &mIp4IoDefaultIpConfigData, sizeof (*Ip4ConfigData));
+
+ Ip4ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP;
+ Ip4ConfigData->AcceptBroadcast = Udp4ConfigData->AcceptBroadcast;
+ Ip4ConfigData->AcceptPromiscuous = Udp4ConfigData->AcceptPromiscuous;
+ Ip4ConfigData->UseDefaultAddress = Udp4ConfigData->UseDefaultAddress;
+ CopyMem (&Ip4ConfigData->StationAddress, &Udp4ConfigData->StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Ip4ConfigData->SubnetMask, &Udp4ConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // use the -1 magic number to disable the receiving process of the ip instance.
+ //
+ Ip4ConfigData->ReceiveTimeout = (UINT32) (-1);
+}
+
+
+/**
+ This function validates the TxToken, it returns the error code according to the spec.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] TxToken Pointer to the token to be checked.
+
+ @retval EFI_SUCCESS The TxToken is valid.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is
+ NULL. Token is NULL. Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentLength fields is zero. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentBuffer fields is NULL.
+ Token.Packet.TxData. GatewayAddress is not a
+ unicast IPv4 address if it is not NULL. One or
+ more IPv4 addresses in Token.Packet.TxData.
+ UdpSessionData are not valid unicast IPv4
+ addresses if the UdpSessionData is not NULL.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size.
+
+**/
+EFI_STATUS
+Udp4ValidateTxToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *TxToken
+ )
+{
+ EFI_UDP4_TRANSMIT_DATA *TxData;
+ UINT32 Index;
+ UINT32 TotalLen;
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+ EFI_UDP4_SESSION_DATA *UdpSessionData;
+ IP4_ADDR SourceAddress;
+ IP4_ADDR GatewayAddress;
+
+ if (TxToken->Event == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TxData = TxToken->Packet.TxData;
+
+ if ((TxData == NULL) || (TxData->FragmentCount == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLen = 0;
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||
+ (TxData->FragmentTable[Index].FragmentLength == 0)) {
+ //
+ // if the FragmentBuffer is NULL or the FragmentLeng is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLen += TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ if (TotalLen != TxData->DataLength) {
+ //
+ // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the
+ // DataLength.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TxData->GatewayAddress != NULL) {
+ CopyMem (&GatewayAddress, TxData->GatewayAddress, sizeof (IP4_ADDR));
+
+ if (!Instance->ConfigData.UseDefaultAddress &&
+ (EFI_NTOHL(Instance->ConfigData.SubnetMask) != 0) &&
+ !NetIp4IsUnicast (NTOHL (GatewayAddress), EFI_NTOHL(Instance->ConfigData.SubnetMask))) {
+ //
+ // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ ConfigData = &Instance->ConfigData;
+ UdpSessionData = TxData->UdpSessionData;
+
+ if (UdpSessionData != NULL) {
+
+ CopyMem (&SourceAddress, &UdpSessionData->SourceAddress, sizeof (IP4_ADDR));
+
+ if ((SourceAddress != 0) &&
+ !Instance->ConfigData.UseDefaultAddress &&
+ (EFI_NTOHL(Instance->ConfigData.SubnetMask) != 0) &&
+ !NetIp4IsUnicast (HTONL (SourceAddress), EFI_NTOHL(Instance->ConfigData.SubnetMask))) {
+ //
+ // Check whether SourceAddress is a valid IPv4 address in case it's not zero.
+ // The configured station address is used if SourceAddress is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {
+ //
+ // Ambiguous, no avalaible DestinationPort for this token.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EFI_IP4_EQUAL (&UdpSessionData->DestinationAddress, &mZeroIp4Addr)) {
+ //
+ // The DestinationAddress specified in the UdpSessionData is 0.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ } else if (EFI_IP4_EQUAL (&ConfigData->RemoteAddress, &mZeroIp4Addr)) {
+ //
+ // the configured RemoteAddress is all zero, and the user doens't override the
+ // destination address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TxData->DataLength > UDP4_MAX_DATA_SIZE) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function checks whether the specified Token duplicates with the one in the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to
+ the Token.
+ @param[in] Context Pointer to the Token to be checked.
+
+ @retval EFI_SUCCESS The Token specified by Context differs from the
+ one in the Item.
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+ EFI_UDP4_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_UDP4_COMPLETION_TOKEN*) Context;
+ TokenInItem = (EFI_UDP4_COMPLETION_TOKEN*) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ //
+ // The Token duplicates with the TokenInItem in case either the two pointers are the
+ // same or the Events of these two tokens are the same.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function calculates the checksum for the Packet, utilizing the pre-calculated
+ pseudo HeadSum to reduce some overhead.
+
+ @param[in] Packet Pointer to the NET_BUF contains the udp datagram.
+ @param[in] HeadSum Checksum of the pseudo header execpt the length
+ field.
+
+ @retval The 16-bit checksum of this udp datagram.
+
+**/
+UINT16
+Udp4Checksum (
+ IN NET_BUF *Packet,
+ IN UINT16 HeadSum
+ )
+{
+ UINT16 Checksum;
+
+ Checksum = NetbufChecksum (Packet);
+ Checksum = NetAddChecksum (Checksum, HeadSum);
+
+ Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));
+
+ return (UINT16) ~Checksum;
+}
+
+
+/**
+ This function removes the specified Token from the TokenMap.
+
+ @param[in, out] TokenMap Pointer to the NET_MAP containing the tokens.
+ @param[in] Token Pointer to the Token to be removed.
+
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.
+
+**/
+EFI_STATUS
+Udp4RemoveToken (
+ IN OUT NET_MAP *TokenMap,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Find the Token first.
+ //
+ Item = NetMapFindKey (TokenMap, (VOID *) Token);
+
+ if (Item != NULL) {
+ //
+ // Remove the token if it's found in the map.
+ //
+ NetMapRemoveItem (TokenMap, Item, NULL);
+
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ This function is the packet transmitting notify function registered to the IpIo
+ interface. It's called to signal the udp TxToken when IpIo layer completes the
+ transmitting of the udp datagram.
+
+ @param[in] Status The completion status of the output udp datagram.
+ @param[in] Context Pointer to the context data.
+ @param[in] Sender Specify a pointer of EFI_IP4_PROTOCOL for sending.
+ @param[in] NotifyData Pointer to the notify data.
+
+**/
+VOID
+EFIAPI
+Udp4DgramSent (
+ IN EFI_STATUS Status,
+ IN VOID *Context,
+ IN IP_IO_IP_PROTOCOL Sender,
+ IN VOID *NotifyData
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+
+ Instance = (UDP4_INSTANCE_DATA *) Context;
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NotifyData;
+
+ if (Udp4RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {
+ //
+ // The token may be cancelled. Only signal it if the remove operation succeeds.
+ //
+ Token->Status = Status;
+ gBS->SignalEvent (Token->Event);
+ DispatchDpc ();
+ }
+}
+
+
+/**
+ This function processes the received datagram passed up by the IpIo layer.
+
+ @param[in] Status The status of this udp datagram.
+ @param[in] IcmpError The IcmpError code, only available when Status is
+ EFI_ICMP_ERROR.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA.
+ @param[in] Packet Pointer to the NET_BUF containing the received udp
+ datagram.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp4DgramRcvd (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet,
+ IN VOID *Context
+ )
+{
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
+
+ //
+ // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.
+ //
+ if (Status == EFI_SUCCESS) {
+ //
+ // Demultiplex the received datagram.
+ //
+ Udp4Demultiplex ((UDP4_SERVICE_DATA *) Context, NetSession, Packet);
+ } else {
+ //
+ // Handle the ICMP_ERROR packet.
+ //
+ Udp4IcmpHandler ((UDP4_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);
+ }
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the rx token's events
+ // which are signaled with received data.
+ //
+ DispatchDpc ();
+}
+
+
+/**
+ This function removes the multicast group specified by Arg from the Map.
+
+ @param[in, out] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's the pointer to a
+ multicast IPv4 Address.
+
+ @retval EFI_SUCCESS The multicast address is removed.
+ @retval EFI_ABORTED The specified multicast address is removed and the
+ Arg is not NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4LeaveGroup (
+ IN OUT NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ EFI_IPv4_ADDRESS *McastIp;
+
+ McastIp = Arg;
+
+ if ((McastIp != NULL) && (!EFI_IP4_EQUAL (McastIp, &(Item->Key)))) {
+ //
+ // McastIp is not NULL and the multicast address contained in the Item
+ // is not the same as McastIp.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove this Item.
+ //
+ NetMapRemoveItem (Map, Item, NULL);
+
+ if (McastIp != NULL) {
+ //
+ // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function cancels the token specified by Arg in the Map. This is a callback
+ used by Udp4InstanceCancelToken().
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the token to be cancelled, if NULL,
+ the token specified by Item is cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token
+ is not the same as that in the Item if Arg is not
+ NULL.
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
+ cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4CancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN *TokenToCancel;
+ NET_BUF *Packet;
+ IP_IO *IpIo;
+
+ if ((Arg != NULL) && (Item->Key != Arg)) {
+ return EFI_SUCCESS;
+ }
+
+ if (Item->Value != NULL) {
+ //
+ // If the token is a transmit token, the corresponding Packet is recorded in
+ // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken
+ // will invoke Udp4DgramSent, the token will be signaled and this Item will
+ // be removed from the Map there.
+ //
+ Packet = (NET_BUF *) (Item->Value);
+ IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));
+
+ IpIoCancelTxToken (IpIo, Packet);
+ } else {
+ //
+ // The token is a receive token. Abort it and remove it from the Map.
+ //
+ TokenToCancel = (EFI_UDP4_COMPLETION_TOKEN *) Item->Key;
+ NetMapRemoveItem (Map, Item, NULL);
+
+ TokenToCancel->Status = EFI_ABORTED;
+ gBS->SignalEvent (TokenToCancel->Event);
+ }
+
+ if (Arg != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function removes all the Wrap datas in the RcvdDgramQue.
+
+ @param[in] Instance Pointer to the udp instance context data.
+
+**/
+VOID
+Udp4FlushRcvdDgram (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ UDP4_RXDATA_WRAP *Wrap;
+
+ while (!IsListEmpty (&Instance->RcvdDgramQue)) {
+ //
+ // Iterate all the Wraps in the RcvdDgramQue.
+ //
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link);
+
+ //
+ // The Wrap will be removed from the RcvdDgramQue by this function call.
+ //
+ Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap);
+ }
+}
+
+
+
+/**
+ Cancel Udp4 tokens from the Udp4 instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Token Pointer to the token to be canceled, if NULL, all
+ tokens in this instance will be cancelled.
+
+ @retval EFI_SUCCESS The Token is cancelled.
+ @retval EFI_NOT_FOUND The Token is not found.
+
+**/
+EFI_STATUS
+Udp4InstanceCancelToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Cancel this token from the TxTokens map.
+ //
+ Status = NetMapIterate (&Instance->TxTokens, Udp4CancelTokens, Token);
+
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ //
+ // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from
+ // the TxTokens, just return success.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to cancel this token from the RxTokens map in condition either the Token
+ // is NULL or the specified Token is not in TxTokens.
+ //
+ Status = NetMapIterate (&Instance->RxTokens, Udp4CancelTokens, Token);
+
+ if ((Token != NULL) && (Status == EFI_SUCCESS)) {
+ //
+ // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the
+ // TxTokens nor the RxTokens, or say, it's not found.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT ((Token != NULL) || ((0 == NetMapGetCount (&Instance->TxTokens))
+ && (0 == NetMapGetCount (&Instance->RxTokens))));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function matches the received udp datagram with the Instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted
+ from the received udp datagram.
+
+ @retval TRUE The udp datagram matches the receiving requirments of the
+ udp Instance.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp4MatchDgram (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_SESSION_DATA *Udp4Session
+ )
+{
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+ IP4_ADDR Destination;
+
+ ConfigData = &Instance->ConfigData;
+
+ if (ConfigData->AcceptPromiscuous) {
+ //
+ // Always matches if this instance is in the promiscuous state.
+ //
+ return TRUE;
+ }
+
+ if ((!ConfigData->AcceptAnyPort && (Udp4Session->DestinationPort != ConfigData->StationPort)) ||
+ ((ConfigData->RemotePort != 0) && (Udp4Session->SourcePort != ConfigData->RemotePort))
+ ) {
+ //
+ // The local port or the remote port doesn't match.
+ //
+ return FALSE;
+ }
+
+ if (!EFI_IP4_EQUAL (&ConfigData->RemoteAddress, &mZeroIp4Addr) &&
+ !EFI_IP4_EQUAL (&ConfigData->RemoteAddress, &Udp4Session->SourceAddress)
+ ) {
+ //
+ // This datagram doesn't come from the instance's specified sender.
+ //
+ return FALSE;
+ }
+
+ if (EFI_IP4_EQUAL (&ConfigData->StationAddress, &mZeroIp4Addr) ||
+ EFI_IP4_EQUAL (&Udp4Session->DestinationAddress, &ConfigData->StationAddress)
+ ) {
+ //
+ // The instance is configured to receive datagrams destined to any station IP or
+ // the destination address of this datagram matches the configured station IP.
+ //
+ return TRUE;
+ }
+
+ CopyMem (&Destination, &Udp4Session->DestinationAddress, sizeof (IP4_ADDR));
+
+ if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) {
+ //
+ // The instance is configured to receive broadcast and this is a broadcast packet.
+ //
+ return TRUE;
+ }
+
+ if (IP4_IS_MULTICAST (NTOHL (Destination)) &&
+ NetMapFindKey (&Instance->McastIps, (VOID *) (UINTN) Destination) != NULL
+ ) {
+ //
+ // It's a multicast packet and the multicast address is accepted by this instance.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This function removes the Wrap specified by Context and release relevant resources.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp4RecycleRxDataWrap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UDP4_RXDATA_WRAP *Wrap;
+
+ Wrap = (UDP4_RXDATA_WRAP *) Context;
+
+ //
+ // Remove the Wrap from the list it belongs to.
+ //
+ RemoveEntryList (&Wrap->Link);
+
+ //
+ // Free the Packet associated with this Wrap.
+ //
+ NetbufFree (Wrap->Packet);
+
+ //
+ // Close the event.
+ //
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);
+
+ FreePool (Wrap);
+}
+
+
+/**
+ This function wraps the Packet and the RxData.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this
+ datagram.
+
+ @return Pointer to the structure wrapping the RxData and the Packet.
+
+**/
+UDP4_RXDATA_WRAP *
+Udp4WrapRxData (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ )
+{
+ EFI_STATUS Status;
+ UDP4_RXDATA_WRAP *Wrap;
+
+ //
+ // Allocate buffer for the Wrap.
+ //
+ Wrap = AllocatePool (sizeof (UDP4_RXDATA_WRAP) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA));
+ if (Wrap == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Wrap->Link);
+
+ CopyMem (&Wrap->RxData, RxData, sizeof (Wrap->RxData));
+
+ //
+ // Create the Recycle event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Udp4RecycleRxDataWrap,
+ Wrap,
+ &Wrap->RxData.RecycleSignal
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Wrap);
+ return NULL;
+ }
+
+ Wrap->Packet = Packet;
+ Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;
+
+ return Wrap;
+}
+
+
+/**
+ This function enqueues the received datagram into the instances' receiving queues.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this
+ datagram.
+
+ @return The times this datagram is enqueued.
+
+**/
+UINTN
+Udp4EnqueueDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ )
+{
+ LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+ UDP4_RXDATA_WRAP *Wrap;
+ UINTN Enqueued;
+
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ if (Udp4MatchDgram (Instance, &RxData->UdpSession)) {
+ //
+ // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.
+ //
+ Wrap = Udp4WrapRxData (Instance, Packet, RxData);
+ if (Wrap == NULL) {
+ continue;
+ }
+
+ NET_GET_REF (Packet);
+
+ InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link);
+
+ Enqueued++;
+ }
+ }
+
+ return Enqueued;
+}
+
+
+/**
+ This function delivers the received datagrams for the specified instance.
+
+ @param[in] Instance Pointer to the instance context data.
+
+**/
+VOID
+Udp4InstanceDeliverDgram (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ UDP4_RXDATA_WRAP *Wrap;
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+ NET_BUF *Dup;
+ EFI_UDP4_RECEIVE_DATA *RxData;
+ EFI_TPL OldTpl;
+
+ if (!IsListEmpty (&Instance->RcvdDgramQue) &&
+ !NetMapIsEmpty (&Instance->RxTokens)) {
+
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link);
+
+ if (NET_BUF_SHARED (Wrap->Packet)) {
+ //
+ // Duplicate the Packet if it is shared between instances.
+ //
+ Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);
+ if (Dup == NULL) {
+ return;
+ }
+
+ NetbufFree (Wrap->Packet);
+
+ Wrap->Packet = Dup;
+ }
+
+ NetListRemoveHead (&Instance->RcvdDgramQue);
+
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
+
+ //
+ // Build the FragmentTable and set the FragmentCount in RxData.
+ //
+ RxData = &Wrap->RxData;
+ RxData->FragmentCount = Wrap->Packet->BlockOpNum;
+
+ NetbufBuildExt (
+ Wrap->Packet,
+ (NET_FRAGMENT *) RxData->FragmentTable,
+ &RxData->FragmentCount
+ );
+
+ Token->Status = EFI_SUCCESS;
+ Token->Packet.RxData = &Wrap->RxData;
+
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link);
+ gBS->RestoreTPL (OldTpl);
+
+ gBS->SignalEvent (Token->Event);
+ }
+}
+
+
+/**
+ This function delivers the datagrams enqueued in the instances.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+
+**/
+VOID
+Udp4DeliverDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ )
+{
+ LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ //
+ // Deliver the datagrams of this instance.
+ //
+ Udp4InstanceDeliverDgram (Instance);
+ }
+}
+
+
+/**
+ This function demultiplexes the received udp datagram to the appropriate instances.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted from
+ the received datagram.
+ @param[in] Packet Pointer to the buffer containing the received udp
+ datagram.
+
+**/
+VOID
+Udp4Demultiplex (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP_HEADER *Udp4Header;
+ UINT16 HeadSum;
+ EFI_UDP4_RECEIVE_DATA RxData;
+ EFI_UDP4_SESSION_DATA *Udp4Session;
+ UINTN Enqueued;
+
+ if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) {
+ NetbufFree (Packet);
+ return;
+ }
+
+ //
+ // Get the datagram header from the packet buffer.
+ //
+ Udp4Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Udp4Header != NULL);
+
+ if (Udp4Header->Checksum != 0) {
+ //
+ // check the checksum.
+ //
+ HeadSum = NetPseudoHeadChecksum (
+ NetSession->Source.Addr[0],
+ NetSession->Dest.Addr[0],
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ if (Udp4Checksum (Packet, HeadSum) != 0) {
+ //
+ // Wrong checksum.
+ //
+ NetbufFree (Packet);
+ return;
+ }
+ }
+
+ Udp4Session = &RxData.UdpSession;
+ Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort);
+ Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort);
+
+ CopyMem (&Udp4Session->SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Udp4Session->DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Trim the UDP header.
+ //
+ NetbufTrim (Packet, UDP4_HEADER_SIZE, TRUE);
+
+ RxData.DataLength = (UINT32) Packet->TotalSize;
+
+ //
+ // Try to enqueue this datagram into the instances.
+ //
+ Enqueued = Udp4EnqueueDgram (Udp4Service, Packet, &RxData);
+
+ if (Enqueued == 0) {
+ //
+ // Send the port unreachable ICMP packet before we free this NET_BUF
+ //
+ Udp4SendPortUnreach (Udp4Service->IpIo, NetSession, Udp4Header);
+ }
+
+ //
+ // Try to free the packet before deliver it.
+ //
+ NetbufFree (Packet);
+
+ if (Enqueued > 0) {
+ //
+ // Deliver the datagram.
+ //
+ Udp4DeliverDgram (Udp4Service);
+ }
+}
+
+
+/**
+ This function builds and sends out a icmp port unreachable message.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet
+ causes this icmp error message.
+ @param[in] Udp4Header Pointer to the udp header of the datagram causes
+ this icmp error message.
+
+**/
+VOID
+Udp4SendPortUnreach (
+ IN IP_IO *IpIo,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN VOID *Udp4Header
+ )
+{
+ NET_BUF *Packet;
+ UINT32 Len;
+ IP4_ICMP_ERROR_HEAD *IcmpErrHdr;
+ EFI_IP4_HEADER *IpHdr;
+ UINT8 *Ptr;
+ IP_IO_OVERRIDE Override;
+ IP_IO_IP_INFO *IpSender;
+
+ IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest);
+ if (IpSender == NULL) {
+ //
+ // No appropriate sender, since we cannot send out the ICMP message through
+ // the default zero station address IP instance, abort.
+ //
+ return;
+ }
+
+ IpHdr = NetSession->IpHdr.Ip4Hdr;
+
+ //
+ // Calculate the required length of the icmp error message.
+ //
+ Len = sizeof (IP4_ICMP_ERROR_HEAD) + (EFI_IP4_HEADER_LEN (IpHdr) -
+ sizeof (IP4_HEAD)) + ICMP_ERROR_PACKET_LENGTH;
+
+ //
+ // Allocate buffer for the icmp error message.
+ //
+ Packet = NetbufAlloc (Len);
+ if (Packet == NULL) {
+ return;
+ }
+
+ //
+ // Allocate space for the IP4_ICMP_ERROR_HEAD.
+ //
+ IcmpErrHdr = (IP4_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);
+ ASSERT (IcmpErrHdr != NULL);
+
+ //
+ // Set the required fields for the icmp port unreachable message.
+ //
+ IcmpErrHdr->Head.Type = ICMP_TYPE_UNREACH;
+ IcmpErrHdr->Head.Code = ICMP_CODE_UNREACH_PORT;
+ IcmpErrHdr->Head.Checksum = 0;
+ IcmpErrHdr->Fourth = 0;
+
+ //
+ // Copy the IP header of the datagram tragged the error.
+ //
+ CopyMem (&IcmpErrHdr->IpHead, IpHdr, EFI_IP4_HEADER_LEN (IpHdr));
+
+ //
+ // Copy the UDP header.
+ //
+ Ptr = (UINT8 *) &IcmpErrHdr->IpHead + EFI_IP4_HEADER_LEN (IpHdr);
+ CopyMem (Ptr, Udp4Header, ICMP_ERROR_PACKET_LENGTH);
+
+ //
+ // Calculate the checksum.
+ //
+ IcmpErrHdr->Head.Checksum = (UINT16) ~(NetbufChecksum (Packet));
+
+ //
+ // Fill the override data.
+ //
+ Override.Ip4OverrideData.DoNotFragment = FALSE;
+ Override.Ip4OverrideData.TypeOfService = 0;
+ Override.Ip4OverrideData.TimeToLive = 255;
+ Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_ICMP;
+
+ CopyMem (&Override.Ip4OverrideData.SourceAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Send out this icmp packet.
+ //
+ IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override);
+
+ NetbufFree (Packet);
+}
+
+
+/**
+ This function handles the received Icmp Error message and demultiplexes it to the
+ instance.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] IcmpError The icmp error code.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted
+ from the received Icmp Error packet.
+ @param[in] Packet Pointer to the Icmp Error packet.
+
+**/
+VOID
+Udp4IcmpHandler (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP_HEADER *Udp4Header;
+ EFI_UDP4_SESSION_DATA Udp4Session;
+ LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+
+ if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) {
+ NetbufFree (Packet);
+ return;
+ }
+
+ Udp4Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Udp4Header != NULL);
+
+ CopyMem (&Udp4Session.SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Udp4Session.DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));
+
+ Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort);
+ Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort);
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate all the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ if (Udp4MatchDgram (Instance, &Udp4Session)) {
+ //
+ // Translate the Icmp Error code according to the udp spec.
+ //
+ Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_4, NULL, NULL);
+
+ if (IcmpError > ICMP_ERR_UNREACH_PORT) {
+ Instance->IcmpError = EFI_ICMP_ERROR;
+ }
+
+ //
+ // Notify the instance with the received Icmp Error.
+ //
+ Udp4ReportIcmpError (Instance);
+
+ break;
+ }
+ }
+
+ NetbufFree (Packet);
+}
+
+
+/**
+ This function reports the received ICMP error.
+
+ @param[in] Instance Pointer to the udp instance context data.
+
+**/
+VOID
+Udp4ReportIcmpError (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+
+ if (NetMapIsEmpty (&Instance->RxTokens)) {
+ //
+ // There are no receive tokens to deliver the ICMP error.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (Instance->IcmpError)) {
+ //
+ // Try to get a RxToken from the RxTokens map.
+ //
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
+
+ if (Token != NULL) {
+ //
+ // Report the error through the Token.
+ //
+ Token->Status = Instance->IcmpError;
+ gBS->SignalEvent (Token->Event);
+
+ //
+ // Clear the IcmpError.
+ //
+ Instance->IcmpError = EFI_SUCCESS;
+ }
+ }
+}
+
+
+/**
+ This function is a dummy ext-free function for the NET_BUF created for the output
+ udp datagram.
+
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp4NetVectorExtFree (
+ VOID *Context
+ )
+{
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h
new file mode 100644
index 0000000000..31edfbda43
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h
@@ -0,0 +1,695 @@
+/** @file
+ EFI UDPv4 protocol implementation.
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _UDP4_IMPL_H_
+#define _UDP4_IMPL_H_
+
+#include <Uefi.h>
+
+#include <Protocol/Ip4.h>
+#include <Protocol/Udp4.h>
+
+#include <Library/IpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/DpcLib.h>
+#include <Library/PrintLib.h>
+
+#include "Udp4Driver.h"
+
+
+extern EFI_COMPONENT_NAME_PROTOCOL gUdp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gUdp4ComponentName2;
+extern EFI_UNICODE_STRING_TABLE *gUdpControllerNameTable;
+extern EFI_SERVICE_BINDING_PROTOCOL mUdp4ServiceBinding;
+extern EFI_UDP4_PROTOCOL mUdp4Protocol;
+extern UINT16 mUdp4RandomPort;
+
+#define ICMP_ERROR_PACKET_LENGTH 8
+
+#define UDP4_TIMEOUT_INTERVAL (50 * TICKS_PER_MS) // 50 milliseconds
+
+#define UDP4_HEADER_SIZE sizeof (EFI_UDP_HEADER)
+#define UDP4_MAX_DATA_SIZE 65507
+
+#define UDP4_PORT_KNOWN 1024
+
+#define UDP4_SERVICE_DATA_SIGNATURE SIGNATURE_32('U', 'd', 'p', '4')
+
+#define UDP4_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ UDP4_SERVICE_DATA, \
+ ServiceBinding, \
+ UDP4_SERVICE_DATA_SIGNATURE \
+ )
+
+typedef struct _UDP4_SERVICE_DATA_ {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE ControllerHandle;
+ LIST_ENTRY ChildrenList;
+ UINTN ChildrenNumber;
+ IP_IO *IpIo;
+
+ EFI_EVENT TimeoutEvent;
+} UDP4_SERVICE_DATA;
+
+#define UDP4_INSTANCE_DATA_SIGNATURE SIGNATURE_32('U', 'd', 'p', 'I')
+
+#define UDP4_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ UDP4_INSTANCE_DATA, \
+ Udp4Proto, \
+ UDP4_INSTANCE_DATA_SIGNATURE \
+ )
+
+typedef struct _UDP4_INSTANCE_DATA_ {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ UDP4_SERVICE_DATA *Udp4Service;
+ EFI_UDP4_PROTOCOL Udp4Proto;
+ EFI_UDP4_CONFIG_DATA ConfigData;
+ EFI_HANDLE ChildHandle;
+ BOOLEAN Configured;
+ BOOLEAN IsNoMapping;
+
+ NET_MAP TxTokens;
+ NET_MAP RxTokens;
+
+ NET_MAP McastIps;
+
+ LIST_ENTRY RcvdDgramQue;
+ LIST_ENTRY DeliveredDgramQue;
+
+ UINT16 HeadSum;
+
+ EFI_STATUS IcmpError;
+
+ IP_IO_IP_INFO *IpInfo;
+
+ BOOLEAN InDestroy;
+} UDP4_INSTANCE_DATA;
+
+typedef struct _UDP4_RXDATA_WRAP_ {
+ LIST_ENTRY Link;
+ NET_BUF *Packet;
+ UINT32 TimeoutTick;
+ EFI_UDP4_RECEIVE_DATA RxData;
+} UDP4_RXDATA_WRAP;
+
+typedef struct {
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+} UDP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
+
+/**
+ Reads the current operational settings.
+
+ The GetModeData() function copies the current operational settings of this EFI
+ UDPv4 Protocol instance into user-supplied buffers. This function is used
+ optionally to retrieve the operational mode data of underlying networks or
+ drivers.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[out] Udp4ConfigData Pointer to the buffer to receive the current configuration data.
+ @param[out] Ip4ModeData Pointer to the EFI IPv4 Protocol mode data structure.
+ @param[out] MnpConfigData Pointer to the managed network configuration data structure.
+ @param[out] SnpModeData Pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED When Udp4ConfigData is queried, no configuration data is
+ available because this instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4GetModeData (
+ IN EFI_UDP4_PROTOCOL *This,
+ OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ Initializes, changes, or resets the operational parameters for this instance of the EFI UDPv4
+ Protocol.
+
+ The Configure() function is used to do the following:
+ * Initialize and start this instance of the EFI UDPv4 Protocol.
+ * Change the filtering rules and operational parameters.
+ * Reset this instance of the EFI UDPv4 Protocol.
+ Until these parameters are initialized, no network traffic can be sent or
+ received by this instance. This instance can be also reset by calling Configure()
+ with UdpConfigData set to NULL. Once reset, the receiving queue and transmitting
+ queue are flushed and no traffic is allowed through this instance.
+ With different parameters in UdpConfigData, Configure() can be used to bind
+ this instance to specified port.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] UdpConfigData Pointer to the buffer to receive the current configuration data.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already started/configured
+ and must be stopped/reset before it can be reconfigured.
+ @retval EFI_ACCESS_DENIED UdpConfigData. AllowDuplicatePort is FALSE
+ and UdpConfigData.StationPort is already used by
+ other instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate memory for this
+ EFI UDPv4 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and this instance
+ was not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Configure (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL
+ );
+
+/**
+ Joins and leaves multicast groups.
+
+ The Groups() function is used to enable and disable the multicast group
+ filtering. If the JoinFlag is FALSE and the MulticastAddress is NULL, then all
+ currently joined groups are left.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join a multicast group. Set to FALSE to leave one
+ or all multicast groups.
+ @param[in] MulticastAddress Pointer to multicast group address to join or leave.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - JoinFlag is TRUE and MulticastAddress is NULL.
+ - JoinFlag is TRUE and *MulticastAddress is not
+ a valid multicast address.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when
+ JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is
+ FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Groups (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL
+ );
+
+/**
+ Adds and deletes routing table entries.
+
+ The Routes() function adds a route to or deletes a route from the routing table.
+ Routes are determined by comparing the SubnetAddress with the destination IP
+ address and arithmetically AND-ing it with the SubnetMask. The gateway address
+ must be on the same subnet as the configured station address.
+ The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0.
+ The default route matches all destination IP addresses that do not match any
+ other routes.
+ A zero GatewayAddress is a nonroute. Packets are sent to the destination IP
+ address if it can be found in the Address Resolution Protocol (ARP) cache or
+ on the local subnet. One automatic nonroute entry will be inserted into the
+ routing table for outgoing packets that are addressed to a local subnet
+ (gateway address of 0.0.0.0).
+ Each instance of the EFI UDPv4 Protocol has its own independent routing table.
+ Instances of the EFI UDPv4 Protocol that use the default IP address will also
+ have copies of the routing table provided by the EFI_IP4_CONFIG_PROTOCOL. These
+ copies will be updated automatically whenever the IP driver reconfigures its
+ instances; as a result, the previous modification to these copies will be lost.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table.
+ Set to FALSE to add this route to the routing table.
+ @param[in] SubnetAddress The destination network address that needs to be routed.
+ @param[in] SubnetMask The subnet mask of SubnetAddress.
+ @param[in] GatewayAddress The gateway IP address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ - RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Routes (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ );
+
+/**
+ Queues outgoing data packets into the transmit queue.
+
+ The Transmit() function places a sending request to this instance of the EFI
+ UDPv4 Protocol, alongside the transmit data that was filled by the user. Whenever
+ the packet in the token is sent out or some errors occur, the Token.Event will
+ be signaled and Token.Status is updated. Providing a proper notification function
+ and context for the event will enable the user to receive the notification and
+ transmitting status.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token that will be placed into the
+ transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same
+ Token.Event was already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because the
+ transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_NOT_FOUND There is no route to the destination network or address.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP packet
+ size. Or the length of the IP header + UDP header + data
+ length is greater than MTU if DoNotFragment is TRUE.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Transmit (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Places an asynchronous receive request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous.
+ The caller must fill in the Token.Event field in the completion token, and this
+ field cannot be NULL. When the receive operation completes, the EFI UDPv4 Protocol
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event
+ is signaled. Providing a proper notification function and context for the event
+ will enable the user to receive the notification and receiving status. That
+ notification function is guaranteed to not be re-entered.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with
+ the receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, RARP, etc.)
+ is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system
+ resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_ACCESS_DENIED A receive completion token with the same Token.Event was already in
+ the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Receive (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Aborts an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token.Status will be set to EFI_ABORTED and then Token.Event will be
+ signaled. If the token is not in one of the queues, which usually means that
+ the asynchronous operation has completed, this function will not signal the
+ token and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_UDP4_PROTOCOL.Transmit() or
+ EFI_UDP4_PROTOCOL.Receive().If NULL, all pending
+ tokens are aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and Token.Event
+ was signaled. When Token is NULL, all pending requests are
+ aborted and their events are signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was
+ not found in the transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Cancel (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications to increase
+ the rate that data packets are moved between the communications device and the
+ transmit and receive queues.
+ In some systems, the periodic timer event in the managed network driver may not
+ poll the underlying communications device fast enough to transmit and/or receive
+ all data packets without missing incoming packets or dropping outgoing packets.
+ Drivers and applications that are experiencing packet loss should try calling
+ the Poll() function more often.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Poll (
+ IN EFI_UDP4_PROTOCOL *This
+ );
+
+/**
+ Create the Udp service context data.
+
+ @param[in, out] Udp4Service Pointer to the UDP4_SERVICE_DATA.
+ @param[in] ImageHandle The image handle of this udp4 driver.
+ @param[in] ControllerHandle The controller handle this udp4 driver binds on.
+
+ @retval EFI_SUCCESS The udp4 service context data is created and
+ initialized.
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Udp4CreateService (
+ IN OUT UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+/**
+ Clean the Udp service context data.
+
+ @param[in] Udp4Service Pointer to the UDP4_SERVICE_DATA.
+
+**/
+VOID
+Udp4CleanService (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ );
+
+/**
+ This function intializes the new created udp instance.
+
+ @param[in] Udp4Service Pointer to the UDP4_SERVICE_DATA.
+ @param[in, out] Instance Pointer to the un-initialized UDP4_INSTANCE_DATA.
+
+**/
+VOID
+Udp4InitInstance (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN OUT UDP4_INSTANCE_DATA *Instance
+ );
+
+/**
+ This function cleans the udp instance.
+
+ @param[in] Instance Pointer to the UDP4_INSTANCE_DATA to clean.
+
+**/
+VOID
+Udp4CleanInstance (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+/**
+ This function tries to bind the udp instance according to the configured port
+ allocation strategy.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in, out] ConfigData Pointer to the ConfigData of the instance to be
+ bound. ConfigData->StationPort will be assigned
+ with an available port value on success.
+
+ @retval EFI_SUCCESS The bound operation is completed successfully.
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is
+ already used by other instance.
+ @retval EFI_OUT_OF_RESOURCES No available port resources.
+
+**/
+EFI_STATUS
+Udp4Bind (
+ IN LIST_ENTRY *InstanceList,
+ IN OUT EFI_UDP4_CONFIG_DATA *ConfigData
+ );
+
+/**
+ This function is used to check whether the NewConfigData has any un-reconfigurable
+ parameters changed compared to the OldConfigData.
+
+ @param[in] OldConfigData Pointer to the current ConfigData the udp instance
+ uses.
+ @param[in] NewConfigData Pointer to the new ConfigData.
+
+ @retval TRUE The instance is reconfigurable.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp4IsReconfigurable (
+ IN EFI_UDP4_CONFIG_DATA *OldConfigData,
+ IN EFI_UDP4_CONFIG_DATA *NewConfigData
+ );
+
+/**
+ This function builds the Ip4 configdata from the Udp4ConfigData.
+
+ @param[in] Udp4ConfigData Pointer to the EFI_UDP4_CONFIG_DATA.
+ @param[in, out] Ip4ConfigData Pointer to the EFI_IP4_CONFIG_DATA.
+
+**/
+VOID
+Udp4BuildIp4ConfigData (
+ IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData,
+ IN OUT EFI_IP4_CONFIG_DATA *Ip4ConfigData
+ );
+
+/**
+ This function validates the TxToken, it returns the error code according to the spec.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] TxToken Pointer to the token to be checked.
+
+ @retval EFI_SUCCESS The TxToken is valid.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is
+ NULL. Token is NULL. Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentLength fields is zero. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentBuffer fields is NULL.
+ Token.Packet.TxData. GatewayAddress is not a
+ unicast IPv4 address if it is not NULL. One or
+ more IPv4 addresses in Token.Packet.TxData.
+ UdpSessionData are not valid unicast IPv4
+ addresses if the UdpSessionData is not NULL.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size.
+
+**/
+EFI_STATUS
+Udp4ValidateTxToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *TxToken
+ );
+
+/**
+ This function checks whether the specified Token duplicates with the one in the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to
+ the Token.
+ @param[in] Context Pointer to the Token to be checked.
+
+ @retval EFI_SUCCESS The Token specified by Context differs from the
+ one in the Item.
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ );
+
+/**
+ This function calculates the checksum for the Packet, utilizing the pre-calculated
+ pseudo HeadSum to reduce some overhead.
+
+ @param[in] Packet Pointer to the NET_BUF contains the udp datagram.
+ @param[in] HeadSum Checksum of the pseudo header execpt the length
+ field.
+
+ @retval The 16-bit checksum of this udp datagram.
+
+**/
+UINT16
+Udp4Checksum (
+ IN NET_BUF *Packet,
+ IN UINT16 HeadSum
+ );
+
+/**
+ This function removes the specified Token from the TokenMap.
+
+ @param[in, out] TokenMap Pointer to the NET_MAP containing the tokens.
+ @param[in] Token Pointer to the Token to be removed.
+
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.
+
+**/
+EFI_STATUS
+Udp4RemoveToken (
+ IN OUT NET_MAP *TokenMap,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ );
+
+/**
+ This function removes the multicast group specified by Arg from the Map.
+
+ @param[in, out] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's the pointer to a
+ multicast IPv4 Address.
+
+ @retval EFI_SUCCESS The multicast address is removed.
+ @retval EFI_ABORTED The specified multicast address is removed and the
+ Arg is not NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4LeaveGroup (
+ IN OUT NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ );
+
+/**
+ This function removes all the Wrap datas in the RcvdDgramQue.
+
+ @param[in] Instance Pointer to the udp instance context data.
+
+**/
+VOID
+Udp4FlushRcvdDgram (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+/**
+ Cancel Udp4 tokens from the Udp4 instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Token Pointer to the token to be canceled, if NULL, all
+ tokens in this instance will be cancelled.
+
+ @retval EFI_SUCCESS The Token is cancelled.
+ @retval EFI_NOT_FOUND The Token is not found.
+
+**/
+EFI_STATUS
+Udp4InstanceCancelToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ This function delivers the received datagrams for the specified instance.
+
+ @param[in] Instance Pointer to the instance context data.
+
+**/
+VOID
+Udp4InstanceDeliverDgram (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+/**
+ This function reports the received ICMP error.
+
+ @param[in] Instance Pointer to the udp instance context data.
+
+**/
+VOID
+Udp4ReportIcmpError (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+/**
+ This function is a dummy ext-free function for the NET_BUF created for the output
+ udp datagram.
+
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp4NetVectorExtFree (
+ VOID *Context
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
new file mode 100644
index 0000000000..d719883925
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
@@ -0,0 +1,908 @@
+/** @file
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Udp4Impl.h"
+
+EFI_UDP4_PROTOCOL mUdp4Protocol = {
+ Udp4GetModeData,
+ Udp4Configure,
+ Udp4Groups,
+ Udp4Routes,
+ Udp4Transmit,
+ Udp4Receive,
+ Udp4Cancel,
+ Udp4Poll
+};
+
+
+/**
+ Reads the current operational settings.
+
+ The GetModeData() function copies the current operational settings of this EFI
+ UDPv4 Protocol instance into user-supplied buffers. This function is used
+ optionally to retrieve the operational mode data of underlying networks or
+ drivers.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[out] Udp4ConfigData Pointer to the buffer to receive the current configuration data.
+ @param[out] Ip4ModeData Pointer to the EFI IPv4 Protocol mode data structure.
+ @param[out] MnpConfigData Pointer to the managed network configuration data structure.
+ @param[out] SnpModeData Pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED When Udp4ConfigData is queried, no configuration data is
+ available because this instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4GetModeData (
+ IN EFI_UDP4_PROTOCOL *This,
+ OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured && (Udp4ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Udp4ConfigData != NULL) {
+ //
+ // Set the Udp4ConfigData.
+ //
+ CopyMem (Udp4ConfigData, &Instance->ConfigData, sizeof (*Udp4ConfigData));
+ }
+
+ Ip = Instance->IpInfo->Ip.Ip4;
+
+ //
+ // Get the underlying Ip4ModeData, MnpConfigData and SnpModeData.
+ //
+ Status = Ip->GetModeData (Ip, Ip4ModeData, MnpConfigData, SnpModeData);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Initializes, changes, or resets the operational parameters for this instance of the EFI UDPv4
+ Protocol.
+
+ The Configure() function is used to do the following:
+ * Initialize and start this instance of the EFI UDPv4 Protocol.
+ * Change the filtering rules and operational parameters.
+ * Reset this instance of the EFI UDPv4 Protocol.
+ Until these parameters are initialized, no network traffic can be sent or
+ received by this instance. This instance can be also reset by calling Configure()
+ with UdpConfigData set to NULL. Once reset, the receiving queue and transmitting
+ queue are flushed and no traffic is allowed through this instance.
+ With different parameters in UdpConfigData, Configure() can be used to bind
+ this instance to specified port.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] UdpConfigData Pointer to the buffer to receive the current configuration data.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already started/configured
+ and must be stopped/reset before it can be reconfigured.
+ @retval EFI_ACCESS_DENIED UdpConfigData. AllowDuplicatePort is FALSE
+ and UdpConfigData.StationPort is already used by
+ other instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate memory for this
+ EFI UDPv4 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and this instance
+ was not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Configure (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ UDP4_SERVICE_DATA *Udp4Service;
+ EFI_TPL OldTpl;
+ IP4_ADDR StationAddress;
+ IP4_ADDR SubnetMask;
+ IP4_ADDR RemoteAddress;
+ EFI_IP4_CONFIG_DATA Ip4ConfigData;
+ IP4_ADDR LocalAddr;
+ IP4_ADDR RemoteAddr;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured && (UdpConfigData == NULL)) {
+ return EFI_SUCCESS;
+ }
+
+ Udp4Service = Instance->Udp4Service;
+ Status = EFI_SUCCESS;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (UdpConfigData != NULL) {
+
+ CopyMem (&StationAddress, &UdpConfigData->StationAddress, sizeof (IP4_ADDR));
+ CopyMem (&SubnetMask, &UdpConfigData->SubnetMask, sizeof (IP4_ADDR));
+ CopyMem (&RemoteAddress, &UdpConfigData->RemoteAddress, sizeof (IP4_ADDR));
+
+ StationAddress = NTOHL (StationAddress);
+ SubnetMask = NTOHL (SubnetMask);
+ RemoteAddress = NTOHL (RemoteAddress);
+
+
+ if (!UdpConfigData->UseDefaultAddress &&
+ (!IP4_IS_VALID_NETMASK (SubnetMask) ||
+ !((StationAddress == 0) || (SubnetMask != 0 && NetIp4IsUnicast (StationAddress, SubnetMask))) ||
+ IP4_IS_LOCAL_BROADCAST (RemoteAddress))) {
+ //
+ // Don't use default address, and subnet mask is invalid or StationAddress is not
+ // a valid unicast IPv4 address or RemoteAddress is not a valid unicast IPv4 address
+ // if it is not 0.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (Instance->Configured) {
+ //
+ // The instance is already configured, try to do the re-configuration.
+ //
+ if (!Udp4IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {
+ //
+ // If the new configuration data wants to change some unreconfigurable
+ // settings, return EFI_ALREADY_STARTED.
+ //
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Save the reconfigurable parameters.
+ //
+ Instance->ConfigData.TypeOfService = UdpConfigData->TypeOfService;
+ Instance->ConfigData.TimeToLive = UdpConfigData->TimeToLive;
+ Instance->ConfigData.DoNotFragment = UdpConfigData->DoNotFragment;
+ Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout;
+ Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;
+ } else {
+ //
+ // Construct the Ip configuration data from the UdpConfigData.
+ //
+ Udp4BuildIp4ConfigData (UdpConfigData, &Ip4ConfigData);
+
+ //
+ // Configure the Ip instance wrapped in the IpInfo.
+ //
+ Status = IpIoConfigIp (Instance->IpInfo, &Ip4ConfigData);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NO_MAPPING) {
+ Instance->IsNoMapping = TRUE;
+ }
+
+ goto ON_EXIT;
+ }
+
+ Instance->IsNoMapping = FALSE;
+
+ //
+ // Save the configuration data.
+ //
+ CopyMem (&Instance->ConfigData, UdpConfigData, sizeof (Instance->ConfigData));
+ IP4_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip4ConfigData.StationAddress);
+ IP4_COPY_ADDRESS (&Instance->ConfigData.SubnetMask, &Ip4ConfigData.SubnetMask);
+
+ //
+ // Try to allocate the required port resource.
+ //
+ Status = Udp4Bind (&Udp4Service->ChildrenList, &Instance->ConfigData);
+ if (EFI_ERROR (Status)) {
+ //
+ // Reset the ip instance if bind fails.
+ //
+ IpIoConfigIp (Instance->IpInfo, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // Pre calculate the checksum for the pseudo head, ignore the UDP length first.
+ //
+ CopyMem (&LocalAddr, &Instance->ConfigData.StationAddress, sizeof (IP4_ADDR));
+ CopyMem (&RemoteAddr, &Instance->ConfigData.RemoteAddress, sizeof (IP4_ADDR));
+ Instance->HeadSum = NetPseudoHeadChecksum (
+ LocalAddr,
+ RemoteAddr,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ Instance->Configured = TRUE;
+ }
+ } else {
+ //
+ // UdpConfigData is NULL, reset the instance.
+ //
+ Instance->Configured = FALSE;
+ Instance->IsNoMapping = FALSE;
+
+ //
+ // Reset the Ip instance wrapped in the IpInfo.
+ //
+ IpIoConfigIp (Instance->IpInfo, NULL);
+
+ //
+ // Cancel all the user tokens.
+ //
+ Instance->Udp4Proto.Cancel (&Instance->Udp4Proto, NULL);
+
+ //
+ // Remove the buffered RxData for this instance.
+ //
+ Udp4FlushRcvdDgram (Instance);
+
+ ASSERT (IsListEmpty (&Instance->DeliveredDgramQue));
+ }
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Joins and leaves multicast groups.
+
+ The Groups() function is used to enable and disable the multicast group
+ filtering. If the JoinFlag is FALSE and the MulticastAddress is NULL, then all
+ currently joined groups are left.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join a multicast group. Set to FALSE to leave one
+ or all multicast groups.
+ @param[in] MulticastAddress Pointer to multicast group address to join or leave.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - JoinFlag is TRUE and MulticastAddress is NULL.
+ - JoinFlag is TRUE and *MulticastAddress is not
+ a valid multicast address.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when
+ JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is
+ FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Groups (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+ EFI_TPL OldTpl;
+ IP4_ADDR McastIp;
+
+ if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ McastIp = 0;
+ if (JoinFlag) {
+ CopyMem (&McastIp, MulticastAddress, sizeof (IP4_ADDR));
+
+ if (!IP4_IS_MULTICAST (NTOHL (McastIp))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Ip = Instance->IpInfo->Ip.Ip4;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Invoke the Ip instance the Udp4 instance consumes to do the group operation.
+ //
+ Status = Ip->Groups (Ip, JoinFlag, MulticastAddress);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Keep a local copy of the configured multicast IPs because IpIo receives
+ // datagrams from the 0 station address IP instance and then UDP delivers to
+ // the matched instance. This copy of multicast IPs is used to avoid receive
+ // the mutlicast datagrams destined to multicast IPs the other instances configured.
+ //
+ if (JoinFlag) {
+
+ NetMapInsertTail (&Instance->McastIps, (VOID *) (UINTN) McastIp, NULL);
+ } else {
+
+ NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress);
+ }
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Adds and deletes routing table entries.
+
+ The Routes() function adds a route to or deletes a route from the routing table.
+ Routes are determined by comparing the SubnetAddress with the destination IP
+ address and arithmetically AND-ing it with the SubnetMask. The gateway address
+ must be on the same subnet as the configured station address.
+ The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0.
+ The default route matches all destination IP addresses that do not match any
+ other routes.
+ A zero GatewayAddress is a nonroute. Packets are sent to the destination IP
+ address if it can be found in the Address Resolution Protocol (ARP) cache or
+ on the local subnet. One automatic nonroute entry will be inserted into the
+ routing table for outgoing packets that are addressed to a local subnet
+ (gateway address of 0.0.0.0).
+ Each instance of the EFI UDPv4 Protocol has its own independent routing table.
+ Instances of the EFI UDPv4 Protocol that use the default IP address will also
+ have copies of the routing table provided by the EFI_IP4_CONFIG_PROTOCOL. These
+ copies will be updated automatically whenever the IP driver reconfigures its
+ instances; as a result, the previous modification to these copies will be lost.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table.
+ Set to FALSE to add this route to the routing table.
+ @param[in] SubnetAddress The destination network address that needs to be routed.
+ @param[in] SubnetMask The subnet mask of SubnetAddress.
+ @param[in] GatewayAddress The gateway IP address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ - RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Routes (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Ip = Instance->IpInfo->Ip.Ip4;
+
+ //
+ // Invoke the Ip instance the Udp4 instance consumes to do the actual operation.
+ //
+ return Ip->Routes (Ip, DeleteRoute, SubnetAddress, SubnetMask, GatewayAddress);
+}
+
+
+/**
+ Queues outgoing data packets into the transmit queue.
+
+ The Transmit() function places a sending request to this instance of the EFI
+ UDPv4 Protocol, alongside the transmit data that was filled by the user. Whenever
+ the packet in the token is sent out or some errors occur, the Token.Event will
+ be signaled and Token.Status is updated. Providing a proper notification function
+ and context for the event will enable the user to receive the notification and
+ transmitting status.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token that will be placed into the
+ transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same
+ Token.Event was already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because the
+ transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_NOT_FOUND There is no route to the destination network or address.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP packet
+ size. Or the length of the IP header + UDP header + data
+ length is greater than MTU if DoNotFragment is TRUE.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Transmit (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ NET_BUF *Packet;
+ EFI_UDP_HEADER *Udp4Header;
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+ IP4_ADDR Source;
+ IP4_ADDR Destination;
+ EFI_UDP4_TRANSMIT_DATA *TxData;
+ EFI_UDP4_SESSION_DATA *UdpSessionData;
+ UDP4_SERVICE_DATA *Udp4Service;
+ IP_IO_OVERRIDE Override;
+ UINT16 HeadSum;
+ EFI_IP_ADDRESS IpDestAddr;
+
+ if ((This == NULL) || (Token == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Validate the Token, if the token is invalid return the error code.
+ //
+ Status = Udp4ValidateTxToken (Instance, Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token)) ||
+ EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))) {
+ //
+ // Try to find a duplicate token in the two token maps, if found, return
+ // EFI_ACCESS_DENIED.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ TxData = Token->Packet.TxData;
+
+ //
+ // Create a net buffer to hold the user buffer and the udp header.
+ //
+ Packet = NetbufFromExt (
+ (NET_FRAGMENT *)TxData->FragmentTable,
+ TxData->FragmentCount,
+ UDP4_HEADER_SIZE,
+ 0,
+ Udp4NetVectorExtFree,
+ NULL
+ );
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Store the IpIo in ProtoData.
+ //
+ Udp4Service = Instance->Udp4Service;
+ *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp4Service->IpIo);
+
+ Udp4Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP4_HEADER_SIZE, TRUE);
+ ASSERT (Udp4Header != NULL);
+
+ ConfigData = &Instance->ConfigData;
+
+ //
+ // Fill the udp header.
+ //
+ Udp4Header->SrcPort = HTONS (ConfigData->StationPort);
+ Udp4Header->DstPort = HTONS (ConfigData->RemotePort);
+ Udp4Header->Length = HTONS ((UINT16) Packet->TotalSize);
+ Udp4Header->Checksum = 0;
+
+ UdpSessionData = TxData->UdpSessionData;
+ IP4_COPY_ADDRESS (&Override.Ip4OverrideData.SourceAddress, &ConfigData->StationAddress);
+
+ if (UdpSessionData != NULL) {
+ //
+ // Set the SourceAddress, SrcPort and Destination according to the specified
+ // UdpSessionData.
+ //
+ if (!EFI_IP4_EQUAL (&UdpSessionData->SourceAddress, &mZeroIp4Addr)) {
+ IP4_COPY_ADDRESS (&Override.Ip4OverrideData.SourceAddress, &UdpSessionData->SourceAddress);
+ }
+
+ if (UdpSessionData->SourcePort != 0) {
+ Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);
+ }
+
+ if (UdpSessionData->DestinationPort != 0) {
+ Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);
+ }
+
+ CopyMem (&Source, &Override.Ip4OverrideData.SourceAddress, sizeof (IP4_ADDR));
+ CopyMem (&Destination, &UdpSessionData->DestinationAddress, sizeof (IP4_ADDR));
+
+ //
+ // calculate the pseudo head checksum using the overridden parameters.
+ //
+ HeadSum = NetPseudoHeadChecksum (
+ Source,
+ Destination,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+ } else {
+ //
+ // UdpSessionData is NULL, use the address and port information previously configured.
+ //
+ CopyMem (&Destination, &ConfigData->RemoteAddress, sizeof (IP4_ADDR));
+
+ HeadSum = Instance->HeadSum;
+ }
+
+ //
+ // calculate the checksum.
+ //
+ Udp4Header->Checksum = Udp4Checksum (Packet, HeadSum);
+ if (Udp4Header->Checksum == 0) {
+ //
+ // If the calculated checksum is 0, fill the Checksum field with all ones.
+ //
+ Udp4Header->Checksum = 0xffff;
+ }
+
+ //
+ // Fill the IpIo Override data.
+ //
+ if (TxData->GatewayAddress != NULL) {
+ IP4_COPY_ADDRESS (&Override.Ip4OverrideData.GatewayAddress, TxData->GatewayAddress);
+ } else {
+ ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_UDP;
+ Override.Ip4OverrideData.TypeOfService = ConfigData->TypeOfService;
+ Override.Ip4OverrideData.TimeToLive = ConfigData->TimeToLive;
+ Override.Ip4OverrideData.DoNotFragment = ConfigData->DoNotFragment;
+
+ //
+ // Save the token into the TxToken map.
+ //
+ Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);
+ if (EFI_ERROR (Status)) {
+ goto FREE_PACKET;
+ }
+
+ //
+ // Send out this datagram through IpIo.
+ //
+ IpDestAddr.Addr[0] = Destination;
+ Status = IpIoSend (
+ Udp4Service->IpIo,
+ Packet,
+ Instance->IpInfo,
+ Instance,
+ Token,
+ &IpDestAddr,
+ &Override
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Remove this token from the TxTokens.
+ //
+ Udp4RemoveToken (&Instance->TxTokens, Token);
+ }
+
+FREE_PACKET:
+
+ NetbufFree (Packet);
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Places an asynchronous receive request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous.
+ The caller must fill in the Token.Event field in the completion token, and this
+ field cannot be NULL. When the receive operation completes, the EFI UDPv4 Protocol
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event
+ is signaled. Providing a proper notification function and context for the event
+ will enable the user to receive the notification and receiving status. That
+ notification function is guaranteed to not be re-entered.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with
+ the receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, RARP, etc.)
+ is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system
+ resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_ACCESS_DENIED A receive completion token with the same Token.Event was already in
+ the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Receive (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))||
+ EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token))) {
+ //
+ // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or
+ // RxTokens map.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ Token->Packet.RxData = NULL;
+
+ //
+ // Save the token into the RxTokens map.
+ //
+ Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ //
+ // If there is an icmp error, report it.
+ //
+ Udp4ReportIcmpError (Instance);
+
+ //
+ // Try to deliver the received datagrams.
+ //
+ Udp4InstanceDeliverDgram (Instance);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.
+ //
+ DispatchDpc ();
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Aborts an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token.Status will be set to EFI_ABORTED and then Token.Event will be
+ signaled. If the token is not in one of the queues, which usually means that
+ the asynchronous operation has completed, this function will not signal the
+ token and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_UDP4_PROTOCOL.Transmit() or
+ EFI_UDP4_PROTOCOL.Receive().If NULL, all pending
+ tokens are aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and Token.Event
+ was signaled. When Token is NULL, all pending requests are
+ aborted and their events are signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was
+ not found in the transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Cancel (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Cancle the tokens specified by Token for this instance.
+ //
+ Status = Udp4InstanceCancelToken (Instance, Token);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the cancelled token's events.
+ //
+ DispatchDpc ();
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications to increase
+ the rate that data packets are moved between the communications device and the
+ transmit and receive queues.
+ In some systems, the periodic timer event in the managed network driver may not
+ poll the underlying communications device fast enough to transmit and/or receive
+ all data packets without missing incoming packets or dropping outgoing packets.
+ Drivers and applications that are experiencing packet loss should try calling
+ the Poll() function more often.
+
+ @param[in] This Pointer to the EFI_UDP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Poll (
+ IN EFI_UDP4_PROTOCOL *This
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+ Ip = Instance->IpInfo->Ip.Ip4;
+
+ //
+ // Invode the Ip instance consumed by the udp instance to do the poll operation.
+ //
+ return Ip->Poll (Ip);
+}
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/ComponentName.c
new file mode 100644
index 0000000000..f4ef59ce5e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/ComponentName.c
@@ -0,0 +1,365 @@
+/** @file
+
+Copyright (c) 2007 - 2012, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "PxeBcImpl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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
+PxeBcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL * This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+PxeBcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = {
+ PxeBcComponentNameGetDriverName,
+ PxeBcComponentNameGetControllerName,
+ "eng"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = {
+ {
+ "eng;en",
+ L"UEFI PXE Base Code Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcControllerNameTable[] = {
+ {
+ "eng;en",
+ L"PXE Controller"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ 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
+PxeBcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL * This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mPxeBcDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gPxeBcComponentName)
+ );
+}
+
+/**
+ 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] ControllerHandle 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 NULL.
+
+ @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
+PxeBcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+
+ if (ControllerHandle == NULL || ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+ }
+ }
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mPxeBcControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gPxeBcComponentName)
+ );
+}
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.c b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.c
new file mode 100644
index 0000000000..f0720e5a8c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.c
@@ -0,0 +1,1996 @@
+/** @file
+ Support for PxeBc dhcp functions.
+
+Copyright (c) 2013, Red Hat, Inc.
+Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "PxeBcImpl.h"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
+ DHCP4_TAG_BOOTFILE_LEN,
+ DHCP4_TAG_VENDOR,
+ DHCP4_TAG_OVERLOAD,
+ DHCP4_TAG_MSG_TYPE,
+ DHCP4_TAG_SERVER_ID,
+ DHCP4_TAG_VENDOR_CLASS_ID,
+ DHCP4_TAG_BOOTFILE
+};
+
+
+/**
+ This function initialize the DHCP4 message instance.
+
+ This function will pad each item of dhcp4 message packet.
+
+ @param Seed Pointer to the message instance of the DHCP4 packet.
+ @param Udp4 Pointer to the EFI_UDP4_PROTOCOL instance.
+
+**/
+VOID
+PxeBcInitSeedPacket (
+ IN EFI_DHCP4_PACKET *Seed,
+ IN EFI_UDP4_PROTOCOL *Udp4
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE Mode;
+ EFI_DHCP4_HEADER *Header;
+
+ Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);
+
+ Seed->Size = sizeof (EFI_DHCP4_PACKET);
+ Seed->Length = sizeof (Seed->Dhcp4);
+
+ Header = &Seed->Dhcp4.Header;
+
+ ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));
+ Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST;
+ Header->HwType = Mode.IfType;
+ Header->HwAddrLen = (UINT8) Mode.HwAddressSize;
+ CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);
+
+ Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC;
+ Seed->Dhcp4.Option[0] = DHCP4_TAG_EOP;
+}
+
+
+/**
+ Copy the DCHP4 packet from srouce to destination.
+
+ @param[in] Dst Pointer to the cache buffer for DHCPv4 packet.
+ @param[in] Src Pointer to the DHCPv4 packet to be cached.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCopyEfiDhcp4Packet (
+ IN EFI_DHCP4_PACKET *Dst,
+ IN EFI_DHCP4_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+ Dst->Length = Src->Length;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Copy the dhcp4 packet to the PxeBc private data and parse the dhcp4 packet.
+
+ @param Private Pointer to PxeBc private data.
+ @param OfferIndex Index of cached packets as complements of pxe mode data,
+ the index is maximum offer number.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCopyProxyOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 OfferIndex
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PACKET *Offer;
+ EFI_STATUS Status;
+
+ ASSERT (OfferIndex < Private->NumOffers);
+ ASSERT (OfferIndex < PXEBC_MAX_OFFER_NUM);
+
+ Mode = Private->PxeBc.Mode;
+ Offer = &Private->Dhcp4Offers[OfferIndex].Packet.Offer;
+
+ Status = PxeBcCopyEfiDhcp4Packet (&Private->ProxyOffer.Packet.Offer, Offer);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ CopyMem (&Mode->ProxyOffer, &Offer->Dhcp4, Offer->Length);
+ Mode->ProxyOfferReceived = TRUE;
+
+ PxeBcParseCachedDhcpPacket (&Private->ProxyOffer);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the cached dhcp packet.
+
+ @param CachedPacket Pointer to cached dhcp packet.
+
+ @retval TRUE Succeed to parse and validation.
+ @retval FALSE Fail to parse or validation.
+
+**/
+BOOLEAN
+PxeBcParseCachedDhcpPacket (
+ IN PXEBC_CACHED_DHCP4_PACKET *CachedPacket
+ )
+{
+ EFI_DHCP4_PACKET *Offer;
+ EFI_DHCP4_PACKET_OPTION **Options;
+ EFI_DHCP4_PACKET_OPTION *Option;
+ UINT8 OfferType;
+ UINTN Index;
+ UINT8 *Ptr8;
+
+ CachedPacket->IsPxeOffer = FALSE;
+ ZeroMem (CachedPacket->Dhcp4Option, sizeof (CachedPacket->Dhcp4Option));
+ ZeroMem (&CachedPacket->PxeVendorOption, sizeof (CachedPacket->PxeVendorOption));
+
+ Offer = &CachedPacket->Packet.Offer;
+ Options = CachedPacket->Dhcp4Option;
+
+ //
+ // Parse interested dhcp options and store their pointers in CachedPacket->Dhcp4Option.
+ // First, try to parse DHCPv4 options from the DHCP optional parameters field.
+ //
+ for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+ Options[Index] = PxeBcParseExtendOptions (
+ Offer->Dhcp4.Option,
+ GET_OPTION_BUFFER_LEN (Offer),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+ //
+ // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132.
+ // If yes, try to parse options from the BootFileName field, then ServerName field.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];
+ if (Option != NULL) {
+ if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {
+ for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+ if (Options[Index] == NULL) {
+ Options[Index] = PxeBcParseExtendOptions (
+ (UINT8 *) Offer->Dhcp4.Header.BootFileName,
+ sizeof (Offer->Dhcp4.Header.BootFileName),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+ }
+ }
+ if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
+ for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+ if (Options[Index] == NULL) {
+ Options[Index] = PxeBcParseExtendOptions (
+ (UINT8 *) Offer->Dhcp4.Header.ServerName,
+ sizeof (Offer->Dhcp4.Header.ServerName),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+ }
+ }
+ }
+
+ //
+ // Check whether is an offer with PXEClient or not.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];
+ if ((Option != NULL) && (Option->Length >= 9) &&
+ (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
+
+ CachedPacket->IsPxeOffer = TRUE;
+ }
+
+ //
+ // Parse pxe vendor options and store their content/pointers in CachedPacket->PxeVendorOption.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];
+ if (CachedPacket->IsPxeOffer && (Option != NULL)) {
+
+ if (!PxeBcParseVendorOptions (Option, &CachedPacket->PxeVendorOption)) {
+ return FALSE;
+ }
+ }
+
+
+ //
+ // Parse PXE boot file name:
+ // According to PXE spec, boot file name should be read from DHCP option 67 (bootfile name) if present.
+ // Otherwise, read from boot file field in DHCP header.
+ //
+ if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ //
+ // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
+ // terminated string. So force to append null terminated character at the end of string.
+ //
+ Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
+ Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;
+ if (*(Ptr8 - 1) != '\0') {
+ *Ptr8 = '\0';
+ }
+ } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) {
+ //
+ // If the bootfile is not present and bootfilename is present in dhcp packet, just parse it.
+ // And do not count dhcp option header, or else will destroy the serverhostname.
+ //
+ // Make sure "BootFileName" is not overloaded.
+ //
+ if (Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD] == NULL ||
+ (Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD]->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) == 0) {
+ Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) (&Offer->Dhcp4.Header.BootFileName[0] -
+ OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
+ }
+ }
+
+ //
+ // Determine offer type of the dhcp packet.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];
+ if ((Option == NULL) || (Option->Data[0] == 0)) {
+ //
+ // It's a bootp offer
+ //
+ Option = CachedPacket->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];
+ if (Option == NULL) {
+ //
+ // bootp offer without bootfilename, discard it.
+ //
+ return FALSE;
+ }
+
+ OfferType = DHCP4_PACKET_TYPE_BOOTP;
+
+ } else {
+
+ if (IS_VALID_DISCOVER_VENDOR_OPTION (CachedPacket->PxeVendorOption.BitMap)) {
+ //
+ // It's a pxe10 offer with PXEClient and discover vendor option.
+ //
+ OfferType = DHCP4_PACKET_TYPE_PXE10;
+ } else if (IS_VALID_MTFTP_VENDOR_OPTION (CachedPacket->PxeVendorOption.BitMap)) {
+ //
+ // It's a wfm11a offer with PXEClient and mtftp vendor option, and
+ // return false since mtftp not supported currently.
+ //
+ return FALSE;
+ } else {
+ //
+ // If the binl offer with only PXEClient.
+ //
+ OfferType = (UINT8) ((CachedPacket->IsPxeOffer) ? DHCP4_PACKET_TYPE_BINL : DHCP4_PACKET_TYPE_DHCP_ONLY);
+ }
+ }
+
+ CachedPacket->OfferType = OfferType;
+
+ return TRUE;
+}
+
+
+/**
+ Offer dhcp service with a BINL dhcp offer.
+
+ @param Private Pointer to PxeBc private data.
+ @param Index Index of cached packets as complements of pxe mode data,
+ the index is maximum offer number.
+
+ @retval TRUE Offer the service successfully under priority BINL.
+ @retval FALSE Boot Service failed, parse cached dhcp packet failed or this
+ BINL ack cannot find options set or bootfile name specified.
+
+**/
+BOOLEAN
+PxeBcTryBinl (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 Index
+ )
+{
+ EFI_DHCP4_PACKET *Offer;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_STATUS Status;
+ PXEBC_CACHED_DHCP4_PACKET *CachedPacket;
+ EFI_DHCP4_PACKET *Reply;
+
+ ASSERT (Index < PXEBC_MAX_OFFER_NUM);
+ ASSERT (Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_BINL);
+
+ Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
+
+ //
+ // Use siaddr(next server) in DHCPOFFER packet header, if zero, use option 54(server identifier)
+ // in DHCPOFFER packet.
+ // (It does not comply with PXE Spec, Ver2.1)
+ //
+ if (EFI_IP4_EQUAL (&Offer->Dhcp4.Header.ServerAddr.Addr, &mZeroIp4Addr)) {
+ CopyMem (
+ &ServerIp.Addr[0],
+ Private->Dhcp4Offers[Index].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ } else {
+ CopyMem (
+ &ServerIp.Addr[0],
+ &Offer->Dhcp4.Header.ServerAddr,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+ if (ServerIp.Addr[0] == 0) {
+ return FALSE;
+ }
+
+ CachedPacket = &Private->ProxyOffer;
+ Reply = &CachedPacket->Packet.Offer;
+
+ Status = PxeBcDiscvBootService (
+ Private,
+ 0,
+ NULL,
+ FALSE,
+ &ServerIp,
+ 0,
+ NULL,
+ FALSE,
+ Reply
+ );
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ if (!PxeBcParseCachedDhcpPacket (CachedPacket)) {
+ return FALSE;
+ }
+
+ if ((CachedPacket->OfferType != DHCP4_PACKET_TYPE_PXE10) &&
+ (CachedPacket->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL)) {
+ //
+ // This BINL ack doesn't have discovery options set or bootfile name
+ // specified.
+ //
+ return FALSE;
+ }
+
+ Private->PxeBc.Mode->ProxyOfferReceived = TRUE;
+ CopyMem (&Private->PxeBc.Mode->ProxyOffer, &Reply->Dhcp4, Reply->Length);
+
+ return TRUE;
+}
+
+
+/**
+ Offer dhcp service for each proxy with a BINL dhcp offer.
+
+ @param Private Pointer to PxeBc private data
+ @param OfferIndex Pointer to the index of cached packets as complements of
+ pxe mode data, the index is maximum offer number.
+
+ @return If there is no service needed offer return FALSE, otherwise TRUE.
+
+**/
+BOOLEAN
+PxeBcTryBinlProxy (
+ IN PXEBC_PRIVATE_DATA *Private,
+ OUT UINT32 *OfferIndex
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL]; Index++) {
+
+ *OfferIndex = Private->BinlIndex[Index];
+ //
+ // Try this BINL proxy offer
+ //
+ if (PxeBcTryBinl (Private, *OfferIndex)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This function is to check the selected proxy offer (include BINL dhcp offer and
+ DHCP_ONLY offer ) and set the flag and copy the DHCP packets to the Pxe base code
+ mode structure.
+
+ @param Private Pointer to PxeBc private data.
+
+ @retval EFI_SUCCESS Operational successful.
+ @retval EFI_NO_RESPONSE Offer dhcp service failed.
+ @retval EFI_BUFFER_TOO_SMALL Failed to copy the packet to Pxe base code mode.
+
+**/
+EFI_STATUS
+PxeBcCheckSelectedOffer (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ PXEBC_CACHED_DHCP4_PACKET *SelectedOffer;
+ EFI_DHCP4_PACKET_OPTION **Options;
+ UINT32 Index;
+ EFI_DHCP4_PACKET *Offer;
+ UINT32 ProxyOfferIndex;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PACKET *Ack;
+
+ ASSERT (Private->SelectedOffer != 0);
+
+ Status = EFI_SUCCESS;
+ SelectedOffer = &Private->Dhcp4Offers[Private->SelectedOffer - 1];
+ Options = SelectedOffer->Dhcp4Option;
+
+ if (SelectedOffer->OfferType == DHCP4_PACKET_TYPE_BINL) {
+ //
+ // The addresses are acquired from a BINL dhcp offer, try BINL to get
+ // the bootfile name
+ //
+ if (!PxeBcTryBinl (Private, Private->SelectedOffer - 1)) {
+ Status = EFI_NO_RESPONSE;
+ }
+ } else if (SelectedOffer->OfferType == DHCP4_PACKET_TYPE_DHCP_ONLY) {
+ //
+ // The selected offer to finish the D.O.R.A. is a DHCP only offer, we need
+ // try proxy offers if there are some, othewise the bootfile name must be
+ // set in this DHCP only offer.
+ //
+ if (Private->GotProxyOffer) {
+ //
+ // Get rid of the compiler warning.
+ //
+ ProxyOfferIndex = 0;
+ if (Private->SortOffers) {
+ //
+ // The offers are sorted before selecting, the proxy offer type must be
+ // already determined.
+ //
+ ASSERT (Private->ProxyIndex[Private->ProxyOfferType] > 0);
+
+ if (Private->ProxyOfferType == DHCP4_PACKET_TYPE_BINL) {
+ //
+ // We buffer all received BINL proxy offers, try them all one by one
+ //
+ if (!PxeBcTryBinlProxy (Private, &ProxyOfferIndex)) {
+ Status = EFI_NO_RESPONSE;
+ }
+ } else {
+ //
+ // For other types, only one proxy offer is buffered.
+ //
+ ProxyOfferIndex = Private->ProxyIndex[Private->ProxyOfferType] - 1;
+ }
+ } else {
+ //
+ // The proxy offer type is not determined, choose proxy offer in the
+ // received order.
+ //
+ Status = EFI_NO_RESPONSE;
+
+ ASSERT (Private->NumOffers < PXEBC_MAX_OFFER_NUM);
+ for (Index = 0; Index < Private->NumOffers; Index++) {
+
+ Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
+ if (!IS_PROXY_DHCP_OFFER (Offer)) {
+ //
+ // Skip non proxy dhcp offers.
+ //
+ continue;
+ }
+
+ if (Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_BINL) {
+ //
+ // Try BINL
+ //
+ if (!PxeBcTryBinl (Private, Index)) {
+ //
+ // Failed, skip to the next offer
+ //
+ continue;
+ }
+ }
+
+ Private->ProxyOfferType = Private->Dhcp4Offers[Index].OfferType;
+ ProxyOfferIndex = Index;
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ if (!EFI_ERROR (Status) && (Private->ProxyOfferType != DHCP4_PACKET_TYPE_BINL)) {
+ //
+ // Copy the proxy offer to Mode and set the flag
+ //
+ Status = PxeBcCopyProxyOffer (Private, ProxyOfferIndex);
+ }
+ } else {
+ //
+ // No proxy offer is received, the bootfile name MUST be set.
+ //
+ ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Everything is OK, set the flag and copy the DHCP packets.
+ //
+ Mode = Private->PxeBc.Mode;
+ Offer = &SelectedOffer->Packet.Offer;
+
+ //
+ // The discover packet is already copied, just set flag here.
+ //
+ Mode->DhcpDiscoverValid = TRUE;
+
+ Ack = &Private->Dhcp4Ack.Packet.Ack;
+ if (SelectedOffer->OfferType == DHCP4_PACKET_TYPE_BOOTP) {
+ //
+ // Other type of ACK is already cached. Bootp is special that we should
+ // use the bootp reply as the ACK and put it into the DHCP_ONLY buffer.
+ //
+ Status = PxeBcCopyEfiDhcp4Packet (&Private->Dhcp4Ack.Packet.Ack, Offer);
+ }
+
+ PxeBcParseCachedDhcpPacket (&Private->Dhcp4Ack);
+
+ Mode->DhcpAckReceived = TRUE;
+
+ //
+ // Copy the dhcp ack.
+ //
+ CopyMem (&Mode->DhcpAck, &Ack->Dhcp4, Ack->Length);
+ }
+
+ return Status;
+}
+
+
+/**
+ Cache the Dhcp4 packet offer, Parse and validate each option of the packet.
+
+ @param Private Pointer to PxeBc private data.
+ @param RcvdOffer Pointer to the received Dhcp proxy offer packet.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+PxeBcCacheDhcpOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *RcvdOffer
+ )
+{
+ PXEBC_CACHED_DHCP4_PACKET *CachedOffer;
+ EFI_DHCP4_PACKET *Offer;
+ UINT8 OfferType;
+ EFI_STATUS Status;
+
+ CachedOffer = &Private->Dhcp4Offers[Private->NumOffers];
+ Offer = &CachedOffer->Packet.Offer;
+
+ //
+ // Cache the orignal dhcp packet
+ //
+ Status = PxeBcCopyEfiDhcp4Packet (Offer, RcvdOffer);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Parse and validate the options (including dhcp option and vendor option)
+ //
+ if (!PxeBcParseCachedDhcpPacket (CachedOffer)) {
+ return EFI_ABORTED;
+ }
+
+ OfferType = CachedOffer->OfferType;
+ if (OfferType >= DHCP4_PACKET_TYPE_MAX) {
+ return EFI_ABORTED;
+ }
+
+ if (OfferType == DHCP4_PACKET_TYPE_BOOTP) {
+
+ if (Private->BootpIndex != 0) {
+ //
+ // Only cache the first bootp offer, discard others.
+ //
+ return EFI_ABORTED;
+ } else {
+ //
+ // Take as a dhcp only offer, but record index specifically.
+ //
+ Private->BootpIndex = Private->NumOffers + 1;
+ }
+ } else {
+
+ if (IS_PROXY_DHCP_OFFER (Offer)) {
+ //
+ // It's a proxy dhcp offer with no your address, including pxe10, wfm11a or binl offer.
+ //
+ Private->GotProxyOffer = TRUE;
+
+ if (OfferType == DHCP4_PACKET_TYPE_BINL) {
+ //
+ // Cache all binl offers.
+ //
+ Private->BinlIndex[Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL]] = Private->NumOffers;
+ Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL]++;
+ } else if (Private->ProxyIndex[OfferType] != 0) {
+ //
+ // Only cache the first pxe10/wfm11a offers each, discard the others.
+ //
+ return EFI_ABORTED;
+ } else {
+ //
+ // Record index of the proxy dhcp offer with type other than binl.
+ //
+ Private->ProxyIndex[OfferType] = Private->NumOffers + 1;
+ }
+ } else {
+ //
+ // It's a dhcp offer with your address.
+ //
+ ASSERT (Private->ServerCount[OfferType] < PXEBC_MAX_OFFER_NUM);
+ Private->OfferIndex[OfferType][Private->ServerCount[OfferType]] = Private->NumOffers;
+ Private->ServerCount[OfferType]++;
+ }
+ }
+
+ //
+ // Count the accepted offers.
+ //
+ Private->NumOffers++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Switch the Ip4 policy to static.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The policy is already configured to static.
+ @retval Others Other error as indicated..
+
+**/
+EFI_STATUS
+PxeBcSetIp4Policy (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ EFI_IP4_CONFIG2_POLICY Policy;
+ UINTN DataSize;
+
+ Ip4Config2 = Private->Ip4Config2;
+ DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypePolicy,
+ &DataSize,
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Policy != Ip4Config2PolicyStatic) {
+ Policy = Ip4Config2PolicyStatic;
+ Status= Ip4Config2->SetData (
+ Ip4Config2,
+ Ip4Config2DataTypePolicy,
+ sizeof (EFI_IP4_CONFIG2_POLICY),
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Select the specified proxy offer, such as BINL, DHCP_ONLY and so on.
+ If the proxy does not exist, try offers with bootfile.
+
+ @param Private Pointer to PxeBc private data.
+
+**/
+VOID
+PxeBcSelectOffer (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ UINT32 Index;
+ UINT32 OfferIndex;
+ EFI_DHCP4_PACKET *Offer;
+
+ Private->SelectedOffer = 0;
+
+ if (Private->SortOffers) {
+ //
+ // Select offer according to the priority
+ //
+ if (Private->ServerCount[DHCP4_PACKET_TYPE_PXE10] > 0) {
+ //
+ // DHCP with PXE10
+ //
+ Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_PXE10][0] + 1;
+
+ } else if (Private->ServerCount[DHCP4_PACKET_TYPE_WFM11A] > 0) {
+ //
+ // DHCP with WfM
+ //
+ Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_WFM11A][0] + 1;
+
+ } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_PXE10] > 0) &&
+ (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
+ ) {
+ //
+ // DHCP only and proxy DHCP with PXE10
+ //
+ Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
+ Private->ProxyOfferType = DHCP4_PACKET_TYPE_PXE10;
+
+ } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_WFM11A] > 0) &&
+ (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
+ ) {
+ //
+ // DHCP only and proxy DHCP with WfM
+ //
+ Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
+ Private->ProxyOfferType = DHCP4_PACKET_TYPE_WFM11A;
+
+ } else if (Private->ServerCount[DHCP4_PACKET_TYPE_BINL] > 0) {
+ //
+ // DHCP with BINL
+ //
+ Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_BINL][0] + 1;
+
+ } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL] > 0) &&
+ (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
+ ) {
+ //
+ // DHCP only and proxy DHCP with BINL
+ //
+ Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
+ Private->ProxyOfferType = DHCP4_PACKET_TYPE_BINL;
+
+ } else {
+ //
+ // Try offers with bootfile
+ //
+ for (Index = 0; Index < Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY]; Index++) {
+ //
+ // Select the first DHCP only offer with bootfile
+ //
+ OfferIndex = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][Index];
+ if (Private->Dhcp4Offers[OfferIndex].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ Private->SelectedOffer = OfferIndex + 1;
+ break;
+ }
+ }
+
+ if (Private->SelectedOffer == 0) {
+ //
+ // Select the Bootp reply with bootfile if any
+ //
+ Private->SelectedOffer = Private->BootpIndex;
+ }
+ }
+ } else {
+ //
+ // Try the offers in the received order.
+ //
+ for (Index = 0; Index < Private->NumOffers; Index++) {
+
+ Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
+
+ if (IS_PROXY_DHCP_OFFER (Offer)) {
+ //
+ // Skip proxy offers
+ //
+ continue;
+ }
+
+ if ((Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_DHCP_ONLY) &&
+ ((!Private->GotProxyOffer) && (Private->Dhcp4Offers[Index].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL))) {
+ //
+ // DHCP only offer but no proxy offer received and no bootfile option in this offer
+ //
+ continue;
+ }
+
+ Private->SelectedOffer = Index + 1;
+ break;
+ }
+ }
+}
+
+
+/**
+ Callback routine.
+
+ EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
+ to intercept events that occurred in the configuration process. This structure
+ provides advanced control of each state transition of the DHCP process. The
+ returned status code determines the behavior of the EFI DHCPv4 Protocol driver.
+ There are three possible returned values, which are described in the following
+ table.
+
+ @param This Pointer to the EFI DHCPv4 Protocol instance that is used to
+ configure this callback function.
+ @param Context Pointer to the context that is initialized by
+ EFI_DHCP4_PROTOCOL.Configure().
+ @param CurrentState The current operational state of the EFI DHCPv4 Protocol
+ driver.
+ @param Dhcp4Event The event that occurs in the current state, which usually means a
+ state transition.
+ @param Packet The DHCP packet that is going to be sent or already received.
+ @param NewPacket The packet that is used to replace the above Packet.
+
+ @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
+ @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
+ driver will continue to wait for more DHCPOFFER packets until the retry
+ timeout expires.
+ @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process and
+ return to the Dhcp4Init or Dhcp4InitReboot state.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDhcpCallBack (
+ IN EFI_DHCP4_PROTOCOL * This,
+ IN VOID *Context,
+ IN EFI_DHCP4_STATE CurrentState,
+ IN EFI_DHCP4_EVENT Dhcp4Event,
+ IN EFI_DHCP4_PACKET * Packet OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+ EFI_DHCP4_PACKET_OPTION *MaxMsgSize;
+ UINT16 Value;
+ EFI_STATUS Status;
+ BOOLEAN Received;
+ EFI_DHCP4_HEADER *DhcpHeader;
+
+ if ((Dhcp4Event != Dhcp4RcvdOffer) &&
+ (Dhcp4Event != Dhcp4SelectOffer) &&
+ (Dhcp4Event != Dhcp4SendDiscover) &&
+ (Dhcp4Event != Dhcp4RcvdAck) &&
+ (Dhcp4Event != Dhcp4SendRequest)) {
+ return EFI_SUCCESS;
+ }
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = Private->PxeBc.Mode;
+ Callback = Private->PxeBcCallback;
+
+ //
+ // Override the Maximum DHCP Message Size.
+ //
+ MaxMsgSize = PxeBcParseExtendOptions (
+ Packet->Dhcp4.Option,
+ GET_OPTION_BUFFER_LEN (Packet),
+ DHCP4_TAG_MAXMSG
+ );
+ if (MaxMsgSize != NULL) {
+ Value = HTONS (PXEBC_DHCP4_MAX_PACKET_SIZE);
+ CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
+ }
+
+ if ((Dhcp4Event != Dhcp4SelectOffer) && (Callback != NULL)) {
+ Received = (BOOLEAN) ((Dhcp4Event == Dhcp4RcvdOffer) || (Dhcp4Event == Dhcp4RcvdAck));
+ Status = Callback->Callback (
+ Callback,
+ Private->Function,
+ Received,
+ Packet->Length,
+ (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4
+ );
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ return EFI_ABORTED;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ switch (Dhcp4Event) {
+
+ case Dhcp4SendDiscover:
+ case Dhcp4SendRequest:
+ if (Packet->Length > PXEBC_DHCP4_MAX_PACKET_SIZE) {
+ //
+ // If the to be sent packet exceeds the maximum length, abort the DHCP process.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+
+ if (Mode->SendGUID) {
+ //
+ // send the system GUID instead of the MAC address as the hardware address
+ // in the DHCP packet header.
+ //
+ DhcpHeader = &Packet->Dhcp4.Header;
+
+ if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) DhcpHeader->ClientHwAddr))) {
+ //
+ // GUID not yet set - send all 0xff's to show programable (via SetVariable)
+ // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
+ // GUID not yet set - send all 0's to show not programable
+ //
+ ZeroMem (DhcpHeader->ClientHwAddr, sizeof (EFI_GUID));
+ }
+
+ DhcpHeader->HwAddrLen = (UINT8) sizeof (EFI_GUID);
+ }
+
+ if (Dhcp4Event == Dhcp4SendDiscover) {
+ //
+ // Cache the dhcp discover packet, of which some information will be used later.
+ //
+ CopyMem (Mode->DhcpDiscover.Raw, &Packet->Dhcp4, Packet->Length);
+ }
+
+ break;
+
+ case Dhcp4RcvdOffer:
+ Status = EFI_NOT_READY;
+ if (Packet->Length > PXEBC_DHCP4_MAX_PACKET_SIZE) {
+ //
+ // Ignore the incoming Offers which exceed the maximum length.
+ //
+ break;
+ }
+
+ if (Private->NumOffers < PXEBC_MAX_OFFER_NUM) {
+ //
+ // Cache the dhcp offers in Private->Dhcp4Offers[]
+ // If error happens, just ignore this packet and continue to wait more offer.
+ //
+ PxeBcCacheDhcpOffer (Private, Packet);
+ }
+
+ break;
+
+ case Dhcp4SelectOffer:
+ //
+ // Select an offer, if succeeded, Private->SelectedOffer points to
+ // the index of the selected one.
+ //
+ PxeBcSelectOffer (Private);
+
+ if (Private->SelectedOffer == 0) {
+ Status = EFI_ABORTED;
+ } else {
+ *NewPacket = &Private->Dhcp4Offers[Private->SelectedOffer - 1].Packet.Offer;
+ }
+
+ break;
+
+ case Dhcp4RcvdAck:
+ //
+ // Cache Ack
+ //
+ ASSERT (Private->SelectedOffer != 0);
+
+ Status = PxeBcCopyEfiDhcp4Packet (&Private->Dhcp4Ack.Packet.Ack, Packet);
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return Status;
+}
+
+
+/**
+ Initialize the DHCP options and build the option list.
+
+ @param Private Pointer to PxeBc private data.
+ @param OptList Pointer to a DHCP option list.
+
+ @param IsDhcpDiscover Discover dhcp option or not.
+
+ @return The index item number of the option list.
+
+**/
+UINT32
+PxeBcBuildDhcpOptions (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET_OPTION **OptList,
+ IN BOOLEAN IsDhcpDiscover
+ )
+{
+ UINT32 Index;
+ PXEBC_DHCP4_OPTION_ENTRY OptEnt;
+ UINT16 Value;
+
+ Index = 0;
+ OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Private->OptionBuffer;
+
+ if (!IsDhcpDiscover) {
+ //
+ // Append message type.
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_MSG_TYPE;
+ OptList[Index]->Length = 1;
+ OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;
+ OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append max message size.
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_MAXMSG;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);
+ OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;
+ Value = NTOHS (PXEBC_DHCP4_MAX_PACKET_SIZE);
+ CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+ }
+ //
+ // Parameter request list option.
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_PARA_LIST;
+ OptList[Index]->Length = 35;
+ OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+ OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK;
+ OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET;
+ OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER;
+ OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER;
+ OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER;
+ OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER;
+ OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME;
+ OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN;
+ OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME;
+ OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH;
+ OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH;
+ OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU;
+ OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL;
+ OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST;
+ OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN;
+ OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER;
+ OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER;
+ OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR;
+ OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP;
+ OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE;
+ OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID;
+ OptEnt.Para->ParaList[21] = DHCP4_TAG_T1;
+ OptEnt.Para->ParaList[22] = DHCP4_TAG_T2;
+ OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID;
+ OptEnt.Para->ParaList[24] = DHCP4_TAG_TFTP;
+ OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE;
+ OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID;
+ OptEnt.Para->ParaList[27] = 0x80;
+ OptEnt.Para->ParaList[28] = 0x81;
+ OptEnt.Para->ParaList[29] = 0x82;
+ OptEnt.Para->ParaList[30] = 0x83;
+ OptEnt.Para->ParaList[31] = 0x84;
+ OptEnt.Para->ParaList[32] = 0x85;
+ OptEnt.Para->ParaList[33] = 0x86;
+ OptEnt.Para->ParaList[34] = 0x87;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append UUID/Guid-based client identifier option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_UUID;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);
+ OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;
+ OptEnt.Uuid->Type = 0;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
+ //
+ // GUID not yet set - send all 0xff's to show programable (via SetVariable)
+ // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
+ // GUID not yet set - send all 0's to show not programable
+ //
+ ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
+ }
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_UNDI;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);
+ OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
+ if (Private->Nii != NULL) {
+ OptEnt.Undi->Type = Private->Nii->Type;
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
+ } else {
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
+ }
+
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client system architecture option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_ARCH;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);
+ OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
+ Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client system architecture option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_VENDOR_CLASS_ID;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);
+ OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;
+ CopyMem (OptEnt.Clid, DEFAULT_CLASS_ID_DATA, sizeof (PXEBC_DHCP4_OPTION_CLID));
+ CvtNum (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, OptEnt.Clid->ArchitectureType, sizeof (OptEnt.Clid->ArchitectureType));
+
+ if (Private->Nii != NULL) {
+ //
+ // If NII protocol exists, update DHCP option data
+ //
+ CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
+ CvtNum (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
+ CvtNum (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
+ }
+
+ Index++;
+
+ return Index;
+}
+
+
+/**
+ Discover the boot of service and initialize the vendor option if exists.
+
+ @param Private Pointer to PxeBc private data.
+ @param Type PxeBc option boot item type
+ @param Layer PxeBc option boot item layer
+ @param UseBis Use BIS or not
+ @param DestIp Ip address for server
+ @param IpCount The total count of the server ip address
+ @param SrvList Server list
+ @param IsDiscv Discover the vendor or not
+ @param Reply The dhcp4 packet of Pxe reply
+
+ @retval EFI_SUCCESS Operation succeeds.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory pool failed.
+ @retval EFI_NOT_FOUND There is no vendor option exists.
+ @retval EFI_TIMEOUT Send Pxe Discover time out.
+
+**/
+EFI_STATUS
+PxeBcDiscvBootService (
+ IN PXEBC_PRIVATE_DATA * Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS * DestIp,
+ IN UINT16 IpCount,
+ IN EFI_PXE_BASE_CODE_SRVLIST * SrvList,
+ IN BOOLEAN IsDiscv,
+ OUT EFI_DHCP4_PACKET * Reply OPTIONAL
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT Sport;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token;
+ BOOLEAN IsBCast;
+ EFI_STATUS Status;
+ UINT16 RepIndex;
+ UINT16 SrvIndex;
+ UINT16 TryIndex;
+ EFI_DHCP4_LISTEN_POINT ListenPoint;
+ EFI_DHCP4_PACKET *Response;
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_MAX_OPTION_NUM];
+ UINT32 OptCount;
+ EFI_DHCP4_PACKET_OPTION *PxeOpt;
+ PXEBC_OPTION_BOOT_ITEM *PxeBootItem;
+ UINT8 VendorOptLen;
+ EFI_DHCP4_HEADER *DhcpHeader;
+ UINT32 Xid;
+
+ Mode = Private->PxeBc.Mode;
+ Dhcp4 = Private->Dhcp4;
+ Status = EFI_SUCCESS;
+
+ ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
+
+ if (DestIp == NULL) {
+ Sport = PXEBC_DHCP4_S_PORT;
+ IsBCast = TRUE;
+ } else {
+ Sport = PXEBC_BS_DISCOVER_PORT;
+ IsBCast = FALSE;
+ }
+
+ if (!UseBis && Layer != NULL) {
+ *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
+ }
+
+ OptCount = PxeBcBuildDhcpOptions (Private, OptList, FALSE);
+
+ if (IsDiscv) {
+ ASSERT (Layer != NULL);
+ //
+ // Add vendor option of PXE_BOOT_ITEM
+ //
+ VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);
+ OptList[OptCount] = AllocatePool (VendorOptLen);
+ if (OptList[OptCount] == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ OptList[OptCount]->OpCode = DHCP4_TAG_VENDOR;
+ OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2);
+ PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;
+ PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM;
+ PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);
+ PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;
+ PxeBootItem->Type = HTONS (Type);
+ PxeBootItem->Layer = HTONS (*Layer);
+ PxeOpt->Data[PxeOpt->Length] = DHCP4_TAG_EOP;
+
+ OptCount++;
+ }
+
+ Status = Dhcp4->Build (Dhcp4, &Private->SeedPacket, 0, NULL, OptCount, OptList, &Token.Packet);
+
+ if (IsDiscv) {
+ FreePool (OptList[OptCount - 1]);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpHeader = &Token.Packet->Dhcp4.Header;
+ if (Mode->SendGUID) {
+ if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) DhcpHeader->ClientHwAddr))) {
+ //
+ // GUID not yet set - send all 0's to show not programable
+ //
+ ZeroMem (DhcpHeader->ClientHwAddr, sizeof (EFI_GUID));
+ }
+
+ DhcpHeader->HwAddrLen = (UINT8) sizeof (EFI_GUID);
+ }
+
+ Xid = NET_RANDOM (NetRandomInitSeed ());
+ Token.Packet->Dhcp4.Header.Xid = HTONL(Xid);
+ Token.Packet->Dhcp4.Header.Reserved = HTONS((UINT16) ((IsBCast) ? 0x8000 : 0));
+ CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+
+ Token.RemotePort = Sport;
+
+ if (IsBCast) {
+ SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
+ } else {
+ CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
+
+ if (!IsBCast) {
+ Token.ListenPointCount = 1;
+ Token.ListenPoints = &ListenPoint;
+ Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT;
+ CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));
+ CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));
+ }
+ //
+ // Send Pxe Discover
+ //
+ for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {
+
+ Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);
+ Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));
+
+ Status = Dhcp4->TransmitReceive (Dhcp4, &Token);
+
+ if (Token.Status != EFI_TIMEOUT) {
+ break;
+ }
+ }
+
+ if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {
+ //
+ // No server response our PXE request
+ //
+ Status = EFI_TIMEOUT;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Find Pxe Reply
+ //
+ RepIndex = 0;
+ SrvIndex = 0;
+ Response = Token.ResponseList;
+
+ while (RepIndex < Token.ResponseCount) {
+ if (Response->Length > PXEBC_DHCP4_MAX_PACKET_SIZE) {
+ SrvIndex = 0;
+ RepIndex++;
+ Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
+ continue;
+ }
+
+ while (SrvIndex < IpCount) {
+
+ if (SrvList[SrvIndex].AcceptAnyResponse) {
+ break;
+ }
+
+ if ((SrvList[SrvIndex].Type == Type) && EFI_IP4_EQUAL (&(Response->Dhcp4.Header.ServerAddr), &(Private->ServerIp))) {
+ break;
+ }
+
+ SrvIndex++;
+ }
+
+ if ((IpCount != SrvIndex) || (IpCount == 0)) {
+ break;
+ }
+
+ SrvIndex = 0;
+ RepIndex++;
+ Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
+ }
+
+ if (RepIndex < Token.ResponseCount) {
+
+ if (Reply != NULL) {
+ Status = PxeBcCopyEfiDhcp4Packet (Reply, Response);
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ if (IsDiscv) {
+ CopyMem (&(Mode->PxeDiscover), &(Token.Packet->Dhcp4), Token.Packet->Length);
+ Mode->PxeDiscoverValid = TRUE;
+
+ CopyMem (Mode->PxeReply.Raw, &Response->Dhcp4, Response->Length);
+ Mode->PxeReplyReceived = TRUE;
+ }
+ } else {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ON_EXIT:
+ //
+ // free the responselist
+ //
+ if (Token.ResponseList != NULL) {
+ FreePool (Token.ResponseList);
+ }
+ //
+ // Free the dhcp packet
+ //
+ if (Token.Packet != NULL) {
+ FreePool (Token.Packet);
+ }
+
+ return Status;
+}
+
+
+/**
+ Parse interested dhcp options.
+
+ @param Buffer Pointer to the dhcp options packet.
+ @param Length The length of the dhcp options.
+ @param OptTag The option OpCode.
+
+ @return NULL if the buffer length is 0 and OpCode is not
+ DHCP4_TAG_EOP, or the pointer to the buffer.
+
+**/
+EFI_DHCP4_PACKET_OPTION *
+PxeBcParseExtendOptions (
+ IN UINT8 *Buffer,
+ IN UINT32 Length,
+ IN UINT8 OptTag
+ )
+{
+ EFI_DHCP4_PACKET_OPTION *Option;
+ UINT32 Offset;
+
+ Option = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+ Offset = 0;
+
+ while (Offset < Length && Option->OpCode != DHCP4_TAG_EOP) {
+
+ if (Option->OpCode == OptTag) {
+
+ return Option;
+ }
+
+ if (Option->OpCode == DHCP4_TAG_PAD) {
+ Offset++;
+ } else {
+ Offset += Option->Length + 2;
+ }
+
+ Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
+ }
+
+ return NULL;
+}
+
+
+/**
+ This function is to parse and check vendor options.
+
+ @param Dhcp4Option Pointer to dhcp options
+ @param VendorOption Pointer to vendor options
+
+ @return TRUE if valid for vendor options, or FALSE.
+
+**/
+BOOLEAN
+PxeBcParseVendorOptions (
+ IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option,
+ IN PXEBC_VENDOR_OPTION *VendorOption
+ )
+{
+ UINT32 *BitMap;
+ UINT8 VendorOptionLen;
+ EFI_DHCP4_PACKET_OPTION *PxeOption;
+ UINT8 Offset;
+
+ BitMap = VendorOption->BitMap;
+ VendorOptionLen = Dhcp4Option->Length;
+ PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];
+ Offset = 0;
+
+ while ((Offset < VendorOptionLen) && (PxeOption->OpCode != DHCP4_TAG_EOP)) {
+ //
+ // Parse every Vendor Option and set its BitMap
+ //
+ switch (PxeOption->OpCode) {
+
+ case PXEBC_VENDOR_TAG_MTFTP_IP:
+
+ CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_CPORT:
+
+ CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_SPORT:
+
+ CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:
+
+ VendorOption->MtftpTimeout = *PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_DELAY:
+
+ VendorOption->MtftpDelay = *PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_DISCOVER_CTRL:
+
+ VendorOption->DiscoverCtrl = *PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_DISCOVER_MCAST:
+
+ CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+ break;
+
+ case PXEBC_VENDOR_TAG_BOOT_SERVERS:
+
+ VendorOption->BootSvrLen = PxeOption->Length;
+ VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_BOOT_MENU:
+
+ VendorOption->BootMenuLen = PxeOption->Length;
+ VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_MENU_PROMPT:
+
+ VendorOption->MenuPromptLen = PxeOption->Length;
+ VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_MCAST_ALLOC:
+
+ CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));
+ CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));
+ break;
+
+ case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:
+
+ VendorOption->CredTypeLen = PxeOption->Length;
+ VendorOption->CredType = (UINT32 *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_BOOT_ITEM:
+
+ CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));
+ CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));
+ break;
+ }
+
+ SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);
+
+ if (PxeOption->OpCode == DHCP4_TAG_PAD) {
+ Offset++;
+ } else {
+ Offset = (UINT8) (Offset + PxeOption->Length + 2);
+ }
+
+ PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);
+ }
+
+ //
+ // FixMe, return falas if invalid of any vendor option
+ //
+
+ return TRUE;
+}
+
+
+/**
+ This function display boot item detail.
+
+ If the length of the boot item string over 70 Char, just display 70 Char.
+
+ @param Str Pointer to a string (boot item string).
+ @param Len The length of string.
+
+**/
+VOID
+PxeBcDisplayBootItem (
+ IN UINT8 *Str,
+ IN UINT8 Len
+ )
+{
+ UINT8 Tmp;
+
+ Len = (UINT8) MIN (70, Len);
+ Tmp = Str[Len];
+ Str[Len] = 0;
+ AsciiPrint ("%a \n", Str);
+ Str[Len] = Tmp;
+}
+
+
+/**
+ Choose the boot prompt.
+
+ @param Private Pointer to PxeBc private data.
+
+ @retval EFI_SUCCESS Select boot prompt done.
+ @retval EFI_TIMEOUT Select boot prompt time out.
+ @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
+ @retval EFI_ABORTED User cancel the operation.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootPrompt (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ PXEBC_CACHED_DHCP4_PACKET *Packet;
+ PXEBC_VENDOR_OPTION *VendorOpt;
+ EFI_EVENT TimeoutEvent;
+ EFI_EVENT DescendEvent;
+ EFI_INPUT_KEY InputKey;
+ EFI_STATUS Status;
+ UINT8 Timeout;
+ UINT8 *Prompt;
+ UINT8 PromptLen;
+ INT32 SecCol;
+ INT32 SecRow;
+
+ TimeoutEvent = NULL;
+ DescendEvent = NULL;
+
+ if (Private->PxeBc.Mode->ProxyOfferReceived) {
+
+ Packet = &Private->ProxyOffer;
+ } else {
+
+ Packet = &Private->Dhcp4Ack;
+ }
+
+ if (Packet->OfferType != DHCP4_PACKET_TYPE_PXE10) {
+ return EFI_NOT_FOUND;
+ }
+
+ VendorOpt = &Packet->PxeVendorOption;
+ //
+ // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options (Full
+ // List), we must not consider a boot prompt or boot menu if all of the
+ // following hold:
+ // - the PXE_DISCOVERY_CONTROL PXE tag is present inside the Vendor Options
+ // (=43) DHCP tag, and
+ // - the PXE_DISCOVERY_CONTROL PXE tag has bit 3 set, and
+ // - a boot file name has been presented with DHCP option 67.
+ //
+ if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
+ Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ return EFI_ABORTED;
+ }
+
+ if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
+ return EFI_SUCCESS;
+ }
+
+ Timeout = VendorOpt->MenuPrompt->Timeout;
+ Prompt = VendorOpt->MenuPrompt->Prompt;
+ PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
+
+ if (Timeout == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (Timeout == 255) {
+ return EFI_TIMEOUT;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ MultU64x32 (Timeout, TICKS_PER_SECOND)
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &DescendEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ DescendEvent,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ SecCol = gST->ConOut->Mode->CursorColumn;
+ SecRow = gST->ConOut->Mode->CursorRow;
+
+ PxeBcDisplayBootItem (Prompt, PromptLen);
+
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
+ AsciiPrint ("(%d) ", Timeout--);
+
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+
+ if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
+ AsciiPrint ("(%d) ", Timeout--);
+ }
+
+ if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
+
+ gBS->Stall (10 * TICKS_PER_MS);
+ continue;
+ }
+
+ if (InputKey.ScanCode == 0) {
+
+ switch (InputKey.UnicodeChar) {
+ case CTRL ('c'):
+ Status = EFI_ABORTED;
+ break;
+
+ case CTRL ('m'):
+ case 'm':
+ case 'M':
+ Status = EFI_TIMEOUT;
+ break;
+
+ default:
+ continue;
+ }
+ } else {
+
+ switch (InputKey.ScanCode) {
+ case SCAN_F8:
+ Status = EFI_TIMEOUT;
+ break;
+
+ case SCAN_ESC:
+ Status = EFI_ABORTED;
+ break;
+
+ default:
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
+
+ON_EXIT:
+
+ if (DescendEvent != NULL) {
+ gBS->CloseEvent (DescendEvent);
+ }
+
+ if (TimeoutEvent != NULL) {
+ gBS->CloseEvent (TimeoutEvent);
+ }
+
+ return Status;
+}
+
+
+/**
+ Select the boot menu.
+
+ @param Private Pointer to PxeBc private data.
+ @param Type The type of the menu.
+ @param UseDefaultItem Use default item or not.
+
+ @retval EFI_ABORTED User cancel operation.
+ @retval EFI_SUCCESS Select the boot menu success.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootMenu (
+ IN PXEBC_PRIVATE_DATA *Private,
+ OUT UINT16 *Type,
+ IN BOOLEAN UseDefaultItem
+ )
+{
+ PXEBC_CACHED_DHCP4_PACKET *Packet;
+ PXEBC_VENDOR_OPTION *VendorOpt;
+ EFI_INPUT_KEY InputKey;
+ UINT8 MenuSize;
+ UINT8 MenuNum;
+ INT32 TopRow;
+ UINT16 Select;
+ UINT16 LastSelect;
+ UINT8 Index;
+ BOOLEAN Finish;
+ CHAR8 Blank[70];
+ PXEBC_BOOT_MENU_ENTRY *MenuItem;
+ PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MAX_MENU_NUM];
+
+ Finish = FALSE;
+ Select = 1;
+ Index = 0;
+ *Type = 0;
+
+ if (Private->PxeBc.Mode->ProxyOfferReceived) {
+
+ Packet = &Private->ProxyOffer;
+ } else {
+
+ Packet = &Private->Dhcp4Ack;
+ }
+
+ ASSERT (Packet->OfferType == DHCP4_PACKET_TYPE_PXE10);
+
+ VendorOpt = &Packet->PxeVendorOption;
+
+ if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
+ return EFI_SUCCESS;
+ }
+
+ SetMem (Blank, sizeof(Blank), ' ');
+
+ MenuSize = VendorOpt->BootMenuLen;
+ MenuItem = VendorOpt->BootMenu;
+
+ if (MenuSize == 0) {
+ return EFI_NOT_READY;
+ }
+
+ while (MenuSize > 0) {
+ MenuArray[Index++] = MenuItem;
+ MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
+ MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
+ if (Index >= PXEBC_MAX_MENU_NUM) {
+ break;
+ }
+ }
+
+ if (UseDefaultItem) {
+ *Type = MenuArray[0]->Type;
+ *Type = NTOHS (*Type);
+ return EFI_SUCCESS;
+ }
+
+ MenuNum = Index;
+
+ for (Index = 0; Index < MenuNum; Index++) {
+ PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
+ }
+
+ TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
+
+ do {
+ ASSERT (Select < PXEBC_MAX_MENU_NUM);
+ //
+ // highlight selected row
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
+ Blank[MenuArray[Select]->DescLen] = 0;
+ AsciiPrint ("%a\r", Blank);
+ PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
+ LastSelect = Select;
+
+ while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
+ gBS->Stall (10 * TICKS_PER_MS);
+ }
+
+ if (InputKey.ScanCode != 0) {
+ switch (InputKey.UnicodeChar) {
+ case CTRL ('c'):
+ InputKey.ScanCode = SCAN_ESC;
+ break;
+
+ case CTRL ('j'): /* linefeed */
+ case CTRL ('m'): /* return */
+ Finish = TRUE;
+ break;
+
+ case CTRL ('i'): /* tab */
+ case ' ':
+ case 'd':
+ case 'D':
+ InputKey.ScanCode = SCAN_DOWN;
+ break;
+
+ case CTRL ('h'): /* backspace */
+ case 'u':
+ case 'U':
+ InputKey.ScanCode = SCAN_UP;
+ break;
+
+ default:
+ InputKey.ScanCode = 0;
+ }
+ }
+
+ switch (InputKey.ScanCode) {
+ case SCAN_LEFT:
+ case SCAN_UP:
+ if (Select > 0) {
+ --Select;
+ }
+
+ break;
+
+ case SCAN_DOWN:
+ case SCAN_RIGHT:
+ if (++Select == MenuNum) {
+ --Select;
+ }
+
+ break;
+
+ case SCAN_PAGE_UP:
+ case SCAN_HOME:
+ Select = 0;
+ break;
+
+ case SCAN_PAGE_DOWN:
+ case SCAN_END:
+ Select = (UINT16) (MenuNum - 1);
+ break;
+
+ case SCAN_ESC:
+ return EFI_ABORTED;
+ }
+
+ /* unhighlight last selected row */
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
+ Blank[MenuArray[LastSelect]->DescLen] = 0;
+ AsciiPrint ("%a\r", Blank);
+ PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
+ } while (!Finish);
+
+ ASSERT (Select < PXEBC_MAX_MENU_NUM);
+
+ //
+ // Swap the byte order
+ //
+ CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
+ *Type = NTOHS (*Type);
+
+ return EFI_SUCCESS;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.h b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.h
new file mode 100644
index 0000000000..d19d2a318f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.h
@@ -0,0 +1,502 @@
+/** @file
+ Dhcp and Discover routines for PxeBc.
+
+Copyright (c) 2013, Red Hat, Inc.
+Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_DHCP_H__
+#define __EFI_PXEBC_DHCP_H__
+
+#define PXEBC_DHCP4_MAX_OPTION_NUM 16
+#define PXEBC_DHCP4_MAX_OPTION_SIZE 312
+#define PXEBC_DHCP4_MAX_PACKET_SIZE (sizeof (EFI_PXE_BASE_CODE_PACKET))
+
+#define PXEBC_DHCP4_S_PORT 67
+#define PXEBC_DHCP4_C_PORT 68
+#define PXEBC_BS_DOWNLOAD_PORT 69
+#define PXEBC_BS_DISCOVER_PORT 4011
+
+#define PXEBC_DHCP4_OPCODE_REQUEST 1
+#define PXEBC_DHCP4_OPCODE_REPLY 2
+#define PXEBC_DHCP4_MSG_TYPE_REQUEST 3
+#define PXEBC_DHCP4_MAGIC 0x63538263 // network byte order
+
+//
+// Sub-Options in Dhcp Vendor Option
+//
+#define PXEBC_VENDOR_TAG_MTFTP_IP 1
+#define PXEBC_VENDOR_TAG_MTFTP_CPORT 2
+#define PXEBC_VENDOR_TAG_MTFTP_SPORT 3
+#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT 4
+#define PXEBC_VENDOR_TAG_MTFTP_DELAY 5
+#define PXEBC_VENDOR_TAG_DISCOVER_CTRL 6
+#define PXEBC_VENDOR_TAG_DISCOVER_MCAST 7
+#define PXEBC_VENDOR_TAG_BOOT_SERVERS 8
+#define PXEBC_VENDOR_TAG_BOOT_MENU 9
+#define PXEBC_VENDOR_TAG_MENU_PROMPT 10
+#define PXEBC_VENDOR_TAG_MCAST_ALLOC 11
+#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES 12
+#define PXEBC_VENDOR_TAG_BOOT_ITEM 71
+
+#define PXEBC_DHCP4_DISCOVER_INIT_TIMEOUT 4
+#define PXEBC_DHCP4_DISCOVER_RETRIES 4
+
+#define PXEBC_MAX_MENU_NUM 24
+#define PXEBC_MAX_OFFER_NUM 16
+
+#define PXEBC_BOOT_REQUEST_TIMEOUT 1
+#define PXEBC_BOOT_REQUEST_RETRIES 4
+
+#define PXEBC_DHCP4_OVERLOAD_FILE 1
+#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME 2
+
+//
+// The array index of the DHCP4 option tag interested
+//
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0
+#define PXEBC_DHCP4_TAG_INDEX_VENDOR 1
+#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD 2
+#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE 3
+#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID 4
+#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID 5
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE 6
+#define PXEBC_DHCP4_TAG_INDEX_MAX 7
+
+//
+// The type of DHCP OFFER, arranged by priority, PXE10 has the highest priority.
+//
+#define DHCP4_PACKET_TYPE_PXE10 0
+#define DHCP4_PACKET_TYPE_WFM11A 1
+#define DHCP4_PACKET_TYPE_BINL 2
+#define DHCP4_PACKET_TYPE_DHCP_ONLY 3
+#define DHCP4_PACKET_TYPE_BOOTP 4
+#define DHCP4_PACKET_TYPE_MAX 5
+
+#define BIT(x) (1 << x)
+#define CTRL(x) (0x1F & (x))
+
+//
+// WfM11a options
+//
+#define MTFTP_VENDOR_OPTION_BIT_MAP (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))
+//
+// Discoverty options
+//
+#define DISCOVER_VENDOR_OPTION_BIT_MAP (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \
+ BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \
+ BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \
+ BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \
+ BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))
+
+#define IS_VALID_BOOT_PROMPT(x) \
+ ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))
+
+#define IS_VALID_BOOT_MENU(x) \
+ ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))
+
+#define IS_VALID_MTFTP_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) == MTFTP_VENDOR_OPTION_BIT_MAP)
+
+#define IS_VALID_DISCOVER_VENDOR_OPTION(x) (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)
+
+#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))
+
+#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \
+ == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))
+
+#define IS_DISABLE_BCAST_DISCOVER(x) (((x) & BIT (0)) == BIT (0))
+#define IS_DISABLE_MCAST_DISCOVER(x) (((x) & BIT (1)) == BIT (1))
+#define IS_ENABLE_USE_SERVER_LIST(x) (((x) & BIT (2)) == BIT (2))
+#define IS_DISABLE_PROMPT_MENU(x) (((x) & BIT (3)) == BIT (3))
+
+#define SET_VENDOR_OPTION_BIT_MAP(x, y) (((x)[(y) / 32]) = (UINT32) ((x)[(y) / 32]) | BIT ((y) % 32))
+
+#pragma pack(1)
+typedef struct {
+ UINT8 ParaList[135];
+} PXEBC_DHCP4_OPTION_PARA;
+
+typedef struct {
+ UINT16 Size;
+} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 MajorVer;
+ UINT8 MinorVer;
+} PXEBC_DHCP4_OPTION_UNDI;
+
+typedef struct {
+ UINT8 Type;
+} PXEBC_DHCP4_OPTION_MESG;
+
+typedef struct {
+ UINT16 Type;
+} PXEBC_DHCP4_OPTION_ARCH;
+
+#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:xxxxx:UNDI:003000"
+#define DEFAULT_UNDI_TYPE 1
+#define DEFAULT_UNDI_MAJOR 3
+#define DEFAULT_UNDI_MINOR 0
+
+typedef struct {
+ UINT8 ClassIdentifier[10];
+ UINT8 ArchitecturePrefix[5];
+ UINT8 ArchitectureType[5];
+ UINT8 Lit3[1];
+ UINT8 InterfaceName[4];
+ UINT8 Lit4[1];
+ UINT8 UndiMajor[3];
+ UINT8 UndiMinor[3];
+} PXEBC_DHCP4_OPTION_CLID;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 Guid[16];
+} PXEBC_DHCP4_OPTION_UUID;
+
+typedef struct {
+ UINT16 Type;
+ UINT16 Layer;
+} PXEBC_OPTION_BOOT_ITEM;
+
+#pragma pack()
+
+typedef union {
+ PXEBC_DHCP4_OPTION_PARA *Para;
+ PXEBC_DHCP4_OPTION_UNDI *Undi;
+ PXEBC_DHCP4_OPTION_ARCH *Arch;
+ PXEBC_DHCP4_OPTION_CLID *Clid;
+ PXEBC_DHCP4_OPTION_UUID *Uuid;
+ PXEBC_DHCP4_OPTION_MESG *Mesg;
+ PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize;
+} PXEBC_DHCP4_OPTION_ENTRY;
+
+typedef struct {
+ UINT16 Type;
+ UINT8 IpCnt;
+ EFI_IPv4_ADDRESS IpAddr[1];
+} PXEBC_BOOT_SVR_ENTRY;
+
+typedef struct {
+ UINT16 Type;
+ UINT8 DescLen;
+ UINT8 DescStr[1];
+} PXEBC_BOOT_MENU_ENTRY;
+
+typedef struct {
+ UINT8 Timeout;
+ UINT8 Prompt[1];
+} PXEBC_MENU_PROMPT;
+
+typedef struct {
+ UINT32 BitMap[8];
+ EFI_IPv4_ADDRESS MtftpIp;
+ UINT16 MtftpCPort;
+ UINT16 MtftpSPort;
+ UINT8 MtftpTimeout;
+ UINT8 MtftpDelay;
+ UINT8 DiscoverCtrl;
+ EFI_IPv4_ADDRESS DiscoverMcastIp;
+ EFI_IPv4_ADDRESS McastIpBase;
+ UINT16 McastIpBlock;
+ UINT16 McastIpRange;
+ UINT16 BootSrvType;
+ UINT16 BootSrvLayer;
+ PXEBC_BOOT_SVR_ENTRY *BootSvr;
+ UINT8 BootSvrLen;
+ PXEBC_BOOT_MENU_ENTRY *BootMenu;
+ UINT8 BootMenuLen;
+ PXEBC_MENU_PROMPT *MenuPrompt;
+ UINT8 MenuPromptLen;
+ UINT32 *CredType;
+ UINT8 CredTypeLen;
+} PXEBC_VENDOR_OPTION;
+
+#define PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP4_PACKET, Dhcp4) + PXEBC_DHCP4_MAX_PACKET_SIZE)
+
+typedef union {
+ EFI_DHCP4_PACKET Offer;
+ EFI_DHCP4_PACKET Ack;
+ UINT8 Buffer[PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE];
+} PXEBC_DHCP4_PACKET;
+
+typedef struct {
+ PXEBC_DHCP4_PACKET Packet;
+ BOOLEAN IsPxeOffer;
+ UINT8 OfferType;
+ EFI_DHCP4_PACKET_OPTION *Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_MAX];
+ PXEBC_VENDOR_OPTION PxeVendorOption;
+} PXEBC_CACHED_DHCP4_PACKET;
+
+#define GET_NEXT_DHCP_OPTION(Opt) \
+ (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)
+
+#define GET_OPTION_BUFFER_LEN(Pkt) ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)
+#define IS_PROXY_DHCP_OFFER(Offer) EFI_IP4_EQUAL (&((Offer)->Dhcp4.Header.YourAddr), &mZeroIp4Addr)
+
+#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \
+ (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))
+
+
+/**
+ This function initialize the DHCP4 message instance.
+
+ This function will pad each item of dhcp4 message packet.
+
+ @param Seed Pointer to the message instance of the DHCP4 packet.
+ @param Udp4 Pointer to the EFI_UDP4_PROTOCOL instance.
+
+**/
+VOID
+PxeBcInitSeedPacket (
+ IN EFI_DHCP4_PACKET *Seed,
+ IN EFI_UDP4_PROTOCOL *Udp4
+ );
+
+
+/**
+ Parse the cached dhcp packet.
+
+ @param CachedPacket Pointer to cached dhcp packet.
+
+ @retval TRUE Succeed to parse and validation.
+ @retval FALSE Fail to parse or validation.
+
+**/
+BOOLEAN
+PxeBcParseCachedDhcpPacket (
+ IN PXEBC_CACHED_DHCP4_PACKET *CachedPacket
+ );
+
+/**
+ This function is to check the selected proxy offer (include BINL dhcp offer and
+ DHCP_ONLY offer ) and set the flag and copy the DHCP packets to the Pxe base code
+ mode structure.
+
+ @param Private Pointer to PxeBc private data.
+
+ @retval EFI_SUCCESS Operational successful.
+ @retval EFI_NO_RESPONSE Offer dhcp service failed.
+ @retval EFI_BUFFER_TOO_SMALL Failed to copy the packet to Pxe base code mode.
+
+**/
+EFI_STATUS
+PxeBcCheckSelectedOffer (
+ IN PXEBC_PRIVATE_DATA *Private
+ );
+
+
+/**
+ Callback routine.
+
+ EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
+ to intercept events that occurred in the configuration process. This structure
+ provides advanced control of each state transition of the DHCP process. The
+ returned status code determines the behavior of the EFI DHCPv4 Protocol driver.
+ There are three possible returned values, which are described in the following
+ table.
+
+ @param This Pointer to the EFI DHCPv4 Protocol instance that is used to
+ configure this callback function.
+ @param Context Pointer to the context that is initialized by
+ EFI_DHCP4_PROTOCOL.Configure().
+ @param CurrentState The current operational state of the EFI DHCPv4 Protocol
+ driver.
+ @param Dhcp4Event The event that occurs in the current state, which usually means a
+ state transition.
+ @param Packet The DHCP packet that is going to be sent or already received.
+ @param NewPacket The packet that is used to replace the above Packet.
+
+ @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
+ @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
+ driver will continue to wait for more DHCPOFFER packets until the retry
+ timeout expires.
+ @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process and
+ return to the Dhcp4Init or Dhcp4InitReboot state.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDhcpCallBack (
+ IN EFI_DHCP4_PROTOCOL * This,
+ IN VOID *Context,
+ IN EFI_DHCP4_STATE CurrentState,
+ IN EFI_DHCP4_EVENT Dhcp4Event,
+ IN EFI_DHCP4_PACKET * Packet OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ );
+
+/**
+ Switch the Ip4 policy to static.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The policy is already configured to static.
+ @retval Others Other error as indicated..
+
+**/
+EFI_STATUS
+PxeBcSetIp4Policy (
+ IN PXEBC_PRIVATE_DATA *Private
+ );
+
+/**
+ Discover the boot of service and initialize the vendor option if exists.
+
+ @param Private Pointer to PxeBc private data.
+ @param Type PxeBc option boot item type
+ @param Layer PxeBc option boot item layer
+ @param UseBis Use BIS or not
+ @param DestIp Ip address for server
+ @param IpCount The total count of the server ip address
+ @param SrvList Server list
+ @param IsDiscv Discover the vendor or not
+ @param Reply The dhcp4 packet of Pxe reply
+
+ @retval EFI_SUCCESS Operation succeeds.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory pool failed.
+ @retval EFI_NOT_FOUND There is no vendor option exists.
+ @retval EFI_TIMEOUT Send Pxe Discover time out.
+
+**/
+EFI_STATUS
+PxeBcDiscvBootService (
+ IN PXEBC_PRIVATE_DATA * Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS * DestIp,
+ IN UINT16 IpCount,
+ IN EFI_PXE_BASE_CODE_SRVLIST * SrvList,
+ IN BOOLEAN IsDiscv,
+ OUT EFI_DHCP4_PACKET * Reply OPTIONAL
+ );
+
+
+/**
+ Initialize the DHCP options and build the option list.
+
+ @param Private Pointer to PxeBc private data.
+ @param OptList Pointer to a DHCP option list.
+
+ @param IsDhcpDiscover Discover dhcp option or not.
+
+ @return The index item number of the option list.
+
+**/
+UINT32
+PxeBcBuildDhcpOptions (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET_OPTION **OptList,
+ IN BOOLEAN IsDhcpDiscover
+ );
+
+
+/**
+ Create the boot options.
+
+ @param OptList Pointer to the list of the options
+ @param Type the type of option
+ @param Layer the layer of the boot options
+ @param OptLen length of opotion
+
+**/
+VOID
+PxeBcCreateBootOptions (
+ IN EFI_DHCP4_PACKET_OPTION *OptList,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ OUT UINT32 *OptLen
+ );
+
+
+/**
+ Parse interested dhcp options.
+
+ @param Buffer Pointer to the dhcp options packet.
+ @param Length The length of the dhcp options.
+ @param OptTag The option OpCode.
+
+ @return NULL if the buffer length is 0 and OpCode is not
+ DHCP4_TAG_EOP, or the pointer to the buffer.
+
+**/
+EFI_DHCP4_PACKET_OPTION *
+PxeBcParseExtendOptions (
+ IN UINT8 *Buffer,
+ IN UINT32 Length,
+ IN UINT8 OptTag
+ );
+
+
+/**
+ This function is to parse and check vendor options.
+
+ @param Dhcp4Option Pointer to dhcp options
+ @param VendorOption Pointer to vendor options
+
+ @return TRUE if valid for vendor options, or FALSE.
+
+**/
+BOOLEAN
+PxeBcParseVendorOptions (
+ IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option,
+ IN PXEBC_VENDOR_OPTION *VendorOption
+ );
+
+
+/**
+ Choose the boot prompt.
+
+ @param Private Pointer to PxeBc private data.
+
+ @retval EFI_SUCCESS Select boot prompt done.
+ @retval EFI_TIMEOUT Select boot prompt time out.
+ @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
+ @retval EFI_ABORTED User cancel the operation.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootPrompt (
+ IN PXEBC_PRIVATE_DATA *Private
+ );
+
+
+/**
+ Select the boot menu.
+
+ @param Private Pointer to PxeBc private data.
+ @param Type The type of the menu.
+ @param UseDefaultItem Use default item or not.
+
+ @retval EFI_ABORTED User cancel operation.
+ @retval EFI_SUCCESS Select the boot menu success.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootMenu (
+ IN PXEBC_PRIVATE_DATA *Private,
+ OUT UINT16 *Type,
+ IN BOOLEAN UseDefaultItem
+ );
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.c b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.c
new file mode 100644
index 0000000000..76c140d8e3
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.c
@@ -0,0 +1,665 @@
+/** @file
+ The driver binding for UEFI PXEBC protocol.
+
+Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "PxeBcImpl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding = {
+ PxeBcDriverBindingSupported,
+ PxeBcDriverBindingStart,
+ PxeBcDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ @param ImageHandle The firmware allocated handle for the UEFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gPxeBcDriverBinding,
+ ImageHandle,
+ &gPxeBcComponentName,
+ &gPxeBcComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+ PxeBc requires DHCP4 and MTFTP4 protocols.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test
+ @param 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
+PxeBcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ }
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ UINTN Index;
+ EFI_STATUS Status;
+ EFI_IP4_MODE_DATA Ip4ModeData;
+
+ Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Private->Signature = PXEBC_PRIVATE_DATA_SIGNATURE;
+ Private->Controller = ControllerHandle;
+ Private->Image = This->DriverBindingHandle;
+ CopyMem (&Private->PxeBc, &mPxeBcProtocolTemplate, sizeof (Private->PxeBc));
+ Private->PxeBc.Mode = &Private->Mode;
+ CopyMem (&Private->LoadFile, &mLoadFileProtocolTemplate, sizeof (Private->LoadFile));
+
+ Private->ProxyOffer.Packet.Offer.Size = PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE;
+ Private->Dhcp4Ack.Packet.Ack.Size = PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE;
+ Private->PxeReply.Packet.Ack.Size = PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE;
+
+ for (Index = 0; Index < PXEBC_MAX_OFFER_NUM; Index++) {
+ Private->Dhcp4Offers[Index].Packet.Offer.Size = PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE;
+ }
+
+ //
+ // Get the NII interface if it exists.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Private->Nii,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Private->Nii = NULL;
+ }
+
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Private->ArpChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->ArpChild,
+ &gEfiArpProtocolGuid,
+ (VOID **) &Private->Arp,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &Private->Dhcp4Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Private->Dhcp4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &Private->Ip4Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Ip4Child,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Private->Ip4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Get max packet size from Ip4 to calculate block size for Tftp later.
+ //
+ Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize;
+
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ &Private->Mtftp4Child
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Mtftp4Child,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **) &Private->Mtftp4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Private->Udp4ReadChild
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // The UDP instance for EfiPxeBcUdpRead
+ //
+ Status = gBS->OpenProtocol (
+ Private->Udp4ReadChild,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Private->Udp4Read,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // The UDP instance for EfiPxeBcUdpWrite
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Private->Udp4WriteChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Udp4WriteChild,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Private->Udp4Write,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ ZeroMem (&Private->Udp4CfgData, sizeof (EFI_UDP4_CONFIG_DATA));
+ Private->Udp4CfgData.AcceptBroadcast = FALSE;
+ Private->Udp4CfgData.AcceptPromiscuous = FALSE;
+ Private->Udp4CfgData.AcceptAnyPort = TRUE;
+ Private->Udp4CfgData.AllowDuplicatePort = TRUE;
+ Private->Udp4CfgData.TypeOfService = DEFAULT_ToS;
+ Private->Udp4CfgData.TimeToLive = DEFAULT_TTL;
+ Private->Udp4CfgData.DoNotFragment = FALSE;
+ Private->Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Private->Udp4CfgData.UseDefaultAddress = FALSE;
+
+ PxeBcInitSeedPacket (&Private->SeedPacket, Private->Udp4Read);
+ Private->MacLen = Private->SeedPacket.Dhcp4.Header.HwAddrLen;
+ CopyMem (&Private->Mac, &Private->SeedPacket.Dhcp4.Header.ClientHwAddr[0], Private->MacLen);
+
+
+ ZeroMem (&Private->Ip4ConfigData, sizeof (EFI_IP4_CONFIG_DATA));
+ Private->Ip4ConfigData.DefaultProtocol = EFI_IP_PROTO_ICMP;
+ Private->Ip4ConfigData.AcceptIcmpErrors = TRUE;
+ Private->Ip4ConfigData.TypeOfService = DEFAULT_ToS;
+ Private->Ip4ConfigData.TimeToLive = DEFAULT_TTL;
+ Private->Ip4ConfigData.DoNotFragment = FALSE;
+ Private->Ip4ConfigData.RawData = FALSE;
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ &Private->PxeBc,
+ &gEfiLoadFileProtocolGuid,
+ &Private->LoadFile,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ //
+ // Locate Ip4->Ip4Config2 and store it for set IPv4 Policy.
+ //
+ Status = gBS->HandleProtocol (
+ ControllerHandle,
+ &gEfiIp4Config2ProtocolGuid,
+ (VOID **) &Private->Ip4Config2
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Private->Udp4WriteChild != NULL) {
+ gBS->CloseProtocol (
+ Private->Udp4WriteChild,
+ &gEfiUdp4ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ Private->Udp4WriteChild
+ );
+ }
+
+ if (Private->Udp4ReadChild != NULL) {
+ gBS->CloseProtocol (
+ Private->Udp4ReadChild,
+ &gEfiUdp4ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ Private->Udp4ReadChild
+ );
+ }
+
+ if (Private->Mtftp4Child != NULL) {
+ gBS->CloseProtocol (
+ Private->Mtftp4Child,
+ &gEfiMtftp4ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ Private->Mtftp4Child
+ );
+ }
+
+ if (Private->Ip4Child != NULL) {
+ gBS->CloseProtocol (
+ Private->Ip4Child,
+ &gEfiIp4ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ Private->Ip4Child
+ );
+ }
+
+ if (Private->Dhcp4Child != NULL) {
+ gBS->CloseProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Private->Dhcp4Child
+ );
+ }
+
+ if (Private->ArpChild != NULL) {
+ gBS->CloseProtocol (
+ Private->ArpChild,
+ &gEfiArpProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ Private->ArpChild
+ );
+ }
+
+ FreePool (Private);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param 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
+PxeBcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+ }
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Stop functionality of PXE Base Code protocol
+ //
+ Status = PxeBc->Stop (PxeBc);
+ if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) {
+ return Status;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ &Private->PxeBc,
+ &gEfiLoadFileProtocolGuid,
+ &Private->LoadFile,
+ NULL
+ );
+
+ if (!EFI_ERROR (Status)) {
+
+ gBS->CloseProtocol (
+ Private->Udp4WriteChild,
+ &gEfiUdp4ProtocolGuid,
+ This->DriverBindingHandle,
+ NicHandle
+ );
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ Private->Udp4WriteChild
+ );
+
+ gBS->CloseProtocol (
+ Private->Udp4ReadChild,
+ &gEfiUdp4ProtocolGuid,
+ This->DriverBindingHandle,
+ NicHandle
+ );
+ NetLibDestroyServiceChild (
+ NicHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ Private->Udp4ReadChild
+ );
+
+ gBS->CloseProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ This->DriverBindingHandle,
+ NicHandle
+ );
+ NetLibDestroyServiceChild (
+ NicHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Private->Dhcp4Child
+ );
+
+ gBS->CloseProtocol (
+ Private->Mtftp4Child,
+ &gEfiMtftp4ProtocolGuid,
+ This->DriverBindingHandle,
+ NicHandle
+ );
+ NetLibDestroyServiceChild (
+ NicHandle,
+ This->DriverBindingHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ Private->Mtftp4Child
+ );
+
+ gBS->CloseProtocol (
+ Private->Ip4Child,
+ &gEfiIp4ProtocolGuid,
+ This->DriverBindingHandle,
+ NicHandle
+ );
+ NetLibDestroyServiceChild (
+ NicHandle,
+ This->DriverBindingHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ Private->Ip4Child
+ );
+
+ gBS->CloseProtocol (
+ Private->ArpChild,
+ &gEfiArpProtocolGuid,
+ This->DriverBindingHandle,
+ NicHandle
+ );
+ NetLibDestroyServiceChild (
+ NicHandle,
+ This->DriverBindingHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ Private->ArpChild
+ );
+
+ FreePool (Private);
+ }
+
+ return Status;
+}
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.h b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.h
new file mode 100644
index 0000000000..f436c895b4
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDriver.h
@@ -0,0 +1,102 @@
+/** @file
+
+Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_DRIVER_H__
+#define __EFI_PXEBC_DRIVER_H__
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+ PxeBc requires DHCP4 and MTFTP4 protocols.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test
+ @param 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
+PxeBcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param 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
+PxeBcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName;
+extern EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding;
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.c b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.c
new file mode 100644
index 0000000000..259568edae
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.c
@@ -0,0 +1,2971 @@
+/** @file
+ Interface routines for PxeBc.
+
+Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "PxeBcImpl.h"
+
+UINT32 mPxeDhcpTimeout[4] = { 4, 8, 16, 32 };
+
+/**
+ Get and record the arp cache.
+
+ @param This Pointer to EFI_PXE_BC_PROTOCOL
+
+ @retval EFI_SUCCESS Arp cache updated successfully
+ @retval others If error occurs when getting arp cache
+
+**/
+EFI_STATUS
+UpdateArpCache (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ UINT32 EntryLength;
+ UINT32 EntryCount;
+ EFI_ARP_FIND_DATA *Entries;
+ UINT32 Index;
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ Status = Private->Arp->Find (
+ Private->Arp,
+ TRUE,
+ NULL,
+ &EntryLength,
+ &EntryCount,
+ &Entries,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Mode->ArpCacheEntries = MIN (
+ EntryCount,
+ EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES
+ );
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index ++) {
+ CopyMem (
+ &Mode->ArpCache[Index].IpAddr,
+ Entries + 1,
+ Entries->SwAddressLength
+ );
+ CopyMem (
+ &Mode->ArpCache[Index].MacAddr,
+ (UINT8 *) (Entries + 1) + Entries->SwAddressLength,
+ Entries->HwAddressLength
+ );
+ //
+ // Slip to the next FindData.
+ //
+ Entries = (EFI_ARP_FIND_DATA *) ((UINT8 *) Entries + EntryLength);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Timeout routine to update arp cache.
+
+ @param Event Pointer to EFI_PXE_BC_PROTOCOL
+ @param Context Context of the timer event
+
+**/
+VOID
+EFIAPI
+ArpCacheUpdateTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UpdateArpCache ((EFI_PXE_BASE_CODE_PROTOCOL *) Context);
+}
+
+/**
+ Do arp resolution from arp cache in PxeBcMode.
+
+ @param PxeBcMode The PXE BC mode to look into.
+ @param Ip4Addr The Ip4 address for resolution.
+ @param MacAddress The resoluted MAC address if the resolution is successful.
+ The value is undefined if resolution fails.
+
+ @retval TRUE The resolution is successful.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+FindInArpCache (
+ IN EFI_PXE_BASE_CODE_MODE *PxeBcMode,
+ IN EFI_IPv4_ADDRESS *Ip4Addr,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < PxeBcMode->ArpCacheEntries; Index ++) {
+ if (EFI_IP4_EQUAL (&PxeBcMode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {
+ CopyMem (
+ MacAddress,
+ &PxeBcMode->ArpCache[Index].MacAddr,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Notify function for the ICMP receive token, used to process
+ the received ICMP packets.
+
+ @param Context The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+IcmpErrorListenHandlerDpc (
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_RECEIVE_DATA *RxData;
+ EFI_IP4_PROTOCOL *Ip4;
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ UINTN Index;
+ UINT32 CopiedLen;
+ UINT8 *CopiedPointer;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = &Private->Mode;
+ Status = Private->IcmpErrorRcvToken.Status;
+ RxData = Private->IcmpErrorRcvToken.Packet.RxData;
+ Ip4 = Private->Ip4;
+
+ if (Status == EFI_ABORTED) {
+ //
+ // The reception is actively aborted by the consumer, directly return.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (Status) || (RxData == NULL)) {
+ //
+ // Only process the normal packets and the icmp error packets, if RxData is NULL
+ // with Status == EFI_SUCCESS or EFI_ICMP_ERROR, just resume the receive although
+ // this should be a bug of the low layer (IP).
+ //
+ goto Resume;
+ }
+
+ if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&
+ (NTOHL (Mode->SubnetMask.Addr[0]) != 0) &&
+ IP4_NET_EQUAL (NTOHL(Mode->StationIp.Addr[0]), EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0])) &&
+ !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0]))) {
+ //
+ // The source address is not zero and it's not a unicast IP address, discard it.
+ //
+ goto CleanUp;
+ }
+
+ if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {
+ //
+ // The dest address is not equal to Station Ip address, discard it.
+ //
+ goto CleanUp;
+ }
+
+ //
+ // Constructor ICMP error packet
+ //
+ CopiedLen = 0;
+ CopiedPointer = (UINT8 *) &Mode->IcmpError;
+
+ for (Index = 0; Index < RxData->FragmentCount; Index ++) {
+ CopiedLen += RxData->FragmentTable[Index].FragmentLength;
+ if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {
+ CopyMem (
+ CopiedPointer,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ RxData->FragmentTable[Index].FragmentLength
+ );
+ } else {
+ CopyMem (
+ CopiedPointer,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+ );
+ }
+ CopiedPointer += CopiedLen;
+ }
+
+ goto Resume;
+
+CleanUp:
+ gBS->SignalEvent (RxData->RecycleSignal);
+
+Resume:
+ Ip4->Receive (Ip4, &(Private->IcmpErrorRcvToken));
+}
+
+/**
+ Request IcmpErrorListenHandlerDpc as a DPC at TPL_CALLBACK
+
+ @param Event The event signaled.
+ @param Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IcmpErrorListenHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request IpIoListenHandlerDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, IcmpErrorListenHandlerDpc, Context);
+}
+
+/**
+ Enables the use of the PXE Base Code Protocol functions.
+
+ This function enables the use of the PXE Base Code Protocol functions. If the
+ Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then
+ EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted
+ addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted
+ addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported
+ field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will
+ be returned. If there is not enough memory or other resources to start the PXE
+ Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the
+ PXE Base Code Protocol will be started, and all of the fields of the EFI_PXE_BASE_CODE_MODE
+ structure will be initialized as follows:
+ StartedSet to TRUE.
+ Ipv6SupportedUnchanged.
+ Ipv6AvailableUnchanged.
+ UsingIpv6Set to UseIpv6.
+ BisSupportedUnchanged.
+ BisDetectedUnchanged.
+ AutoArpSet to TRUE.
+ SendGUIDSet to FALSE.
+ TTLSet to DEFAULT_TTL.
+ ToSSet to DEFAULT_ToS.
+ DhcpCompletedSet to FALSE.
+ ProxyOfferReceivedSet to FALSE.
+ StationIpSet to an address of all zeros.
+ SubnetMaskSet to a subnet mask of all zeros.
+ DhcpDiscoverZero-filled.
+ DhcpAckZero-filled.
+ ProxyOfferZero-filled.
+ PxeDiscoverValidSet to FALSE.
+ PxeDiscoverZero-filled.
+ PxeReplyValidSet to FALSE.
+ PxeReplyZero-filled.
+ PxeBisReplyValidSet to FALSE.
+ PxeBisReplyZero-filled.
+ IpFilterSet the Filters field to 0 and the IpCnt field to 0.
+ ArpCacheEntriesSet to 0.
+ ArpCacheZero-filled.
+ RouteTableEntriesSet to 0.
+ RouteTableZero-filled.
+ IcmpErrorReceivedSet to FALSE.
+ IcmpErrorZero-filled.
+ TftpErroReceivedSet to FALSE.
+ TftpErrorZero-filled.
+ MakeCallbacksSet to TRUE if the PXE Base Code Callback Protocol is available.
+ Set to FALSE if the PXE Base Code Callback Protocol is not available.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param UseIpv6 Specifies the type of IP addresses that are to be used during the session
+ that is being started. Set to TRUE for IPv6 addresses, and FALSE for
+ IPv4 addresses.
+
+ @retval EFI_SUCCESS The PXE Base Code Protocol was started.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this oper
+ @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the
+ EFI_PXE_BASE_CODE_MODE structure is FALSE.
+ @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state.
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the
+ PXE Base Code Protocol.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcStart (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN UseIpv6
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (Mode->Started) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (UseIpv6) {
+ //
+ // IPv6 is not supported now.
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Configure the udp4 instance to let it receive data
+ //
+ Status = Private->Udp4Read->Configure (
+ Private->Udp4Read,
+ &Private->Udp4CfgData
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+
+ //
+ // Configure block size for TFTP as a default value to handle all link layers.
+ //
+ Private->BlockSize = MIN (Private->Ip4MaxPacketSize, PXEBC_DEFAULT_PACKET_SIZE) -
+ PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE;
+ //
+ // If PcdTftpBlockSize is set to non-zero, override the default value.
+ //
+ if (PcdGet64 (PcdTftpBlockSize) != 0) {
+ Private->BlockSize = (UINTN) PcdGet64 (PcdTftpBlockSize);
+ }
+
+ Private->AddressIsOk = FALSE;
+
+ ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));
+
+ Mode->Started = TRUE;
+ Mode->TTL = DEFAULT_TTL;
+ Mode->ToS = DEFAULT_ToS;
+ Mode->AutoArp = TRUE;
+
+ //
+ // Create the event for Arp Cache checking.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ ArpCacheUpdateTimeout,
+ This,
+ &Private->GetArpCacheEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Start the timeout timer event.
+ //
+ Status = gBS->SetTimer (
+ Private->GetArpCacheEvent,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Create ICMP error receiving event
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IcmpErrorListenHandler,
+ Private,
+ &(Private->IcmpErrorRcvToken.Event)
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ //DHCP4 service allows only one of its children to be configured in
+ //the active state, If the DHCP4 D.O.R.A started by IP4 auto
+ //configuration and has not been completed, the Dhcp4 state machine
+ //will not be in the right state for the PXE to start a new round D.O.R.A.
+ //so we need to switch it's policy to static.
+ //
+ Status = PxeBcSetIp4Policy (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4ConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // start to listen incoming packet
+ //
+ Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpErrorRcvToken);
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ON_EXIT:
+ Private->Ip4->Configure (Private->Ip4, NULL);
+
+ if (Private->IcmpErrorRcvToken.Event != NULL) {
+ gBS->CloseEvent (Private->IcmpErrorRcvToken.Event);
+ }
+
+ if (Private->GetArpCacheEvent != NULL) {
+ gBS->SetTimer (Private->GetArpCacheEvent, TimerCancel, 0);
+ gBS->CloseEvent (Private->GetArpCacheEvent);
+ }
+
+ Mode->Started = FALSE;
+ Mode->TTL = 0;
+ Mode->ToS = 0;
+ Mode->AutoArp = FALSE;
+
+ return Status;
+}
+
+
+/**
+ Disables the use of the PXE Base Code Protocol functions.
+
+ This function stops all activity on the network device. All the resources allocated
+ in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is
+ set to FALSE and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE
+ structure is already FALSE, then EFI_NOT_STARTED will be returned.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The PXE Base Code Protocol was stopped.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state.
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcStop (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ Private->Ip4->Cancel (Private->Ip4, NULL);
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's
+ // events.
+ //
+ DispatchDpc ();
+
+ Private->Ip4->Configure (Private->Ip4, NULL);
+
+ //
+ // Close the ICMP error receiving event.
+ //
+ gBS->CloseEvent (Private->IcmpErrorRcvToken.Event);
+
+ //
+ // Cancel the TimeoutEvent timer.
+ //
+ gBS->SetTimer (Private->GetArpCacheEvent, TimerCancel, 0);
+
+ //
+ // Close the TimeoutEvent event.
+ //
+ gBS->CloseEvent (Private->GetArpCacheEvent);
+
+ Mode->Started = FALSE;
+
+ Private->CurrentUdpSrcPort = 0;
+ Private->Udp4Write->Configure (Private->Udp4Write, NULL);
+ Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+ Private->Dhcp4->Stop (Private->Dhcp4);
+ Private->Dhcp4->Configure (Private->Dhcp4, NULL);
+
+ Private->FileSize = 0;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6
+ S.A.R.R (solicit / advertise / request / reply) sequence.
+
+ This function attempts to complete the DHCP sequence. If this sequence is completed,
+ then EFI_SUCCESS is returned, and the DhcpCompleted, ProxyOfferReceived, StationIp,
+ SubnetMask, DhcpDiscover, DhcpAck, and ProxyOffer fields of the EFI_PXE_BASE_CODE_MODE
+ structure are filled in.
+ If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before
+ they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will
+ be tried in the order in which they are received. Please see the Preboot Execution
+ Environment (PXE) Specification for additional details on the implementation of DHCP.
+ This function can take at least 31 seconds to timeout and return control to the
+ caller. If the DHCP sequence does not complete, then EFI_TIMEOUT will be returned.
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then the DHCP sequence will be stopped and EFI_ABORTED will be returned.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param SortOffers TRUE if the offers received should be sorted. Set to FALSE to try the
+ offers in the order that they are received.
+
+ @retval EFI_SUCCESS Valid DHCP has completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol.
+ @retval EFI_ABORTED The callback function aborted the DHCP Protocol.
+ @retval EFI_TIMEOUT The DHCP Protocol timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session.
+ @retval EFI_NO_RESPONSE Valid PXE offer was not received.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcDhcp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN SortOffers
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_DHCP4_CONFIG_DATA Dhcp4CfgData;
+ EFI_DHCP4_MODE_DATA Dhcp4Mode;
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_MAX_OPTION_NUM];
+ UINT32 OptCount;
+ EFI_STATUS Status;
+ EFI_ARP_CONFIG_DATA ArpConfigData;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Dhcp4 = Private->Dhcp4;
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP;
+ Private->SortOffers = SortOffers;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ Mode->IcmpErrorReceived = FALSE;
+
+ //
+ // Stop Udp4Read instance
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+ //
+ // Initialize the DHCP options and build the option list
+ //
+ OptCount = PxeBcBuildDhcpOptions (Private, OptList, TRUE);
+
+ //
+ // Set the DHCP4 config data.
+ // The four discovery timeouts are 4, 8, 16, 32 seconds respectively.
+ //
+ ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dhcp4CfgData.OptionCount = OptCount;
+ Dhcp4CfgData.OptionList = OptList;
+ Dhcp4CfgData.Dhcp4Callback = PxeBcDhcpCallBack;
+ Dhcp4CfgData.CallbackContext = Private;
+ Dhcp4CfgData.DiscoverTryCount = 4;
+ Dhcp4CfgData.DiscoverTimeout = mPxeDhcpTimeout;
+
+ Status = Dhcp4->Configure (Dhcp4, &Dhcp4CfgData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Zero those arrays to record the varies numbers of DHCP OFFERS.
+ //
+ Private->GotProxyOffer = FALSE;
+ Private->NumOffers = 0;
+ Private->BootpIndex = 0;
+ ZeroMem (Private->ServerCount, sizeof (Private->ServerCount));
+ ZeroMem (Private->ProxyIndex, sizeof (Private->ProxyIndex));
+
+ Status = Dhcp4->Start (Dhcp4, NULL);
+ if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ }
+ goto ON_EXIT;
+ }
+
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ ASSERT (Dhcp4Mode.State == Dhcp4Bound);
+
+ CopyMem (&Private->StationIp, &Dhcp4Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->SubnetMask, &Dhcp4Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->GatewayIp, &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ CopyMem (&Mode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Mode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Check the selected offer to see whether BINL is required, if no or BINL is
+ // finished, set the various Mode members.
+ //
+ Status = PxeBcCheckSelectedOffer (Private);
+
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ Dhcp4->Stop (Dhcp4);
+ Dhcp4->Configure (Dhcp4, NULL);
+ } else {
+ //
+ // Remove the previously configured option list and callback function
+ //
+ ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dhcp4->Configure (Dhcp4, &Dhcp4CfgData);
+
+ Private->AddressIsOk = TRUE;
+
+ if (!Mode->UsingIpv6) {
+ //
+ // If in IPv4 mode, configure the corresponding ARP with this new
+ // station IP address.
+ //
+ ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));
+
+ ArpConfigData.SwAddressType = 0x0800;
+ ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);
+ ArpConfigData.StationAddress = &Private->StationIp.v4;
+
+ Private->Arp->Configure (Private->Arp, NULL);
+ Private->Arp->Configure (Private->Arp, &ArpConfigData);
+
+ //
+ // Updated the route table. Fill the first entry.
+ //
+ Mode->RouteTableEntries = 1;
+ Mode->RouteTable[0].IpAddr.Addr[0] = Private->StationIp.Addr[0] & Private->SubnetMask.Addr[0];
+ Mode->RouteTable[0].SubnetMask.Addr[0] = Private->SubnetMask.Addr[0];
+ Mode->RouteTable[0].GwAddr.Addr[0] = 0;
+
+ //
+ // Create the default route entry if there is a default router.
+ //
+ if (Private->GatewayIp.Addr[0] != 0) {
+ Mode->RouteTableEntries = 2;
+ Mode->RouteTable[1].IpAddr.Addr[0] = 0;
+ Mode->RouteTable[1].SubnetMask.Addr[0] = 0;
+ Mode->RouteTable[1].GwAddr.Addr[0] = Private->GatewayIp.Addr[0];
+ }
+
+ //
+ // Flush new station IP address into Udp4CfgData and Ip4ConfigData
+ //
+ CopyMem (&Private->Udp4CfgData.StationAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Udp4CfgData.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Ip4ConfigData.StationAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Ip4ConfigData.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.
+ //
+ Private->Ip4->Cancel (Private->Ip4, &Private->IcmpErrorRcvToken);
+ Private->Ip4->Configure (Private->Ip4, NULL);
+
+ Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4ConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpErrorRcvToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
+
+ //
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ //
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ This->SetIpFilter (This, &IpFilter);
+
+ return Status;
+}
+
+
+/**
+ Attempts to complete the PXE Boot Server and/or boot image discovery sequence.
+
+ This function attempts to complete the PXE Boot Server and/or boot image discovery
+ sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the
+ PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the
+ EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the
+ PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure
+ will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.
+ In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],
+ has two uses: It is the Boot Server IP address list used for unicast discovery
+ (if the UseUCast field is TRUE), and it is the list used for Boot Server verification
+ (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure
+ is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot
+ Server reply of that type will be accepted. If the AcceptAnyResponse field is
+ FALSE, only responses from Boot Servers with matching IP addresses will be accepted.
+ This function can take at least 10 seconds to timeout and return control to the
+ caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be
+ returned. Please see the Preboot Execution Environment (PXE) Specification for
+ additional details on the implementation of the Discovery sequence.
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then the Discovery sequence is stopped and EFI_ABORTED will be returned.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param Type The type of bootstrap to perform.
+ @param Layer Pointer to the boot server layer number to discover, which must be
+ PXE_BOOT_LAYER_INITIAL when a new server type is being
+ discovered.
+ @param UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise.
+ @param Info Pointer to a data structure that contains additional information on the
+ type of discovery operation that is to be performed.
+
+ @retval EFI_SUCCESS The Discovery sequence has been completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery.
+ @retval EFI_ABORTED The callback function aborted the Discovery sequence.
+ @retval EFI_TIMEOUT The Discovery sequence timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery
+ session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcDiscover (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;
+ EFI_PXE_BASE_CODE_DISCOVER_INFO *CreatedInfo;
+ EFI_PXE_BASE_CODE_SRVLIST *SrvList;
+ EFI_PXE_BASE_CODE_SRVLIST DefaultSrvList;
+ PXEBC_CACHED_DHCP4_PACKET *Packet;
+ PXEBC_VENDOR_OPTION *VendorOpt;
+ UINT16 Index;
+ EFI_STATUS Status;
+ PXEBC_BOOT_SVR_ENTRY *BootSvrEntry;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ BootSvrEntry = NULL;
+ SrvList = NULL;
+ CreatedInfo = NULL;
+ Status = EFI_DEVICE_ERROR;
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;
+
+ if (!Private->AddressIsOk) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ //
+ // Stop Udp4Read instance
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+ Mode->IcmpErrorReceived = FALSE;
+
+ //
+ // If layer isn't EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL,
+ // use the previous setting;
+ // If info isn't offered,
+ // use the cached DhcpAck and ProxyOffer packets.
+ //
+ ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO));
+ if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {
+
+ if (!Mode->PxeDiscoverValid || !Mode->PxeReplyReceived || (!Mode->PxeBisReplyReceived && UseBis)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ DefaultInfo.IpCnt = 1;
+ DefaultInfo.UseUCast = TRUE;
+
+ DefaultSrvList.Type = Type;
+ DefaultSrvList.AcceptAnyResponse = FALSE;
+ DefaultSrvList.IpAddr.Addr[0] = Private->ServerIp.Addr[0];
+
+ SrvList = &DefaultSrvList;
+ Info = &DefaultInfo;
+ } else if (Info == NULL) {
+ //
+ // Create info by the cached packet before
+ //
+ Packet = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer : &Private->Dhcp4Ack;
+ VendorOpt = &Packet->PxeVendorOption;
+
+ if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
+ //
+ // Address is not acquired or no discovery options.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ DefaultInfo.UseMCast = (BOOLEAN)!IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
+ DefaultInfo.UseBCast = (BOOLEAN)!IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
+ DefaultInfo.MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
+ DefaultInfo.UseUCast = DefaultInfo.MustUseList;
+
+ if (DefaultInfo.UseMCast) {
+ //
+ // Get the multicast discover ip address from vendor option.
+ //
+ CopyMem (
+ &DefaultInfo.ServerMCastIp.Addr,
+ &VendorOpt->DiscoverMcastIp,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ DefaultInfo.IpCnt = 0;
+ Info = &DefaultInfo;
+ SrvList = Info->SrvList;
+
+ if (DefaultInfo.MustUseList) {
+ BootSvrEntry = VendorOpt->BootSvr;
+ Status = EFI_INVALID_PARAMETER;
+
+ while (((UINT8) (BootSvrEntry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
+
+ if (BootSvrEntry->Type == HTONS (Type)) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+
+ BootSvrEntry = GET_NEXT_BOOT_SVR_ENTRY (BootSvrEntry);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ DefaultInfo.IpCnt = BootSvrEntry->IpCnt;
+
+ if (DefaultInfo.IpCnt >= 1) {
+ CreatedInfo = AllocatePool (sizeof (DefaultInfo) + (DefaultInfo.IpCnt - 1) * sizeof (*SrvList));
+ if (CreatedInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+
+ }
+
+ CopyMem (CreatedInfo, &DefaultInfo, sizeof (DefaultInfo));
+ Info = CreatedInfo;
+ SrvList = Info->SrvList;
+ }
+
+ for (Index = 0; Index < DefaultInfo.IpCnt; Index++) {
+ CopyMem (&SrvList[Index].IpAddr, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
+ SrvList[Index].AcceptAnyResponse = FALSE;
+ SrvList[Index].Type = BootSvrEntry->Type;
+ }
+ }
+
+ } else {
+
+ SrvList = Info->SrvList;
+
+ if (!SrvList[0].AcceptAnyResponse) {
+
+ for (Index = 1; Index < Info->IpCnt; Index++) {
+ if (SrvList[Index].AcceptAnyResponse) {
+ break;
+ }
+ }
+
+ if (Index != Info->IpCnt) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) || (Info->MustUseList && Info->IpCnt == 0)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Execute discover by UniCast/BroadCast/MultiCast
+ //
+ if (Info->UseUCast) {
+
+ for (Index = 0; Index < Info->IpCnt; Index++) {
+
+ if (BootSvrEntry == NULL) {
+ Private->ServerIp.Addr[0] = SrvList[Index].IpAddr.Addr[0];
+ } else {
+ CopyMem (
+ &Private->ServerIp,
+ &BootSvrEntry->IpAddr[Index],
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ Status = PxeBcDiscvBootService (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ &SrvList[Index].IpAddr,
+ 0,
+ NULL,
+ TRUE,
+ &Private->PxeReply.Packet.Ack
+ );
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ } else if (Info->UseMCast) {
+
+ Status = PxeBcDiscvBootService (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ &Info->ServerMCastIp,
+ 0,
+ NULL,
+ TRUE,
+ &Private->PxeReply.Packet.Ack
+ );
+
+ } else if (Info->UseBCast) {
+
+ Status = PxeBcDiscvBootService (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ NULL,
+ Info->IpCnt,
+ SrvList,
+ TRUE,
+ &Private->PxeReply.Packet.Ack
+ );
+ }
+
+ if (EFI_ERROR (Status) || !Mode->PxeReplyReceived || (!Mode->PxeBisReplyReceived && UseBis)) {
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+ goto ON_EXIT;
+ } else {
+ PxeBcParseCachedDhcpPacket (&Private->PxeReply);
+ }
+
+ if (Mode->PxeBisReplyReceived) {
+ CopyMem (
+ &Private->ServerIp,
+ &Mode->PxeReply.Dhcpv4.BootpSiAddr,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ if (CreatedInfo != NULL) {
+ FreePool (CreatedInfo);
+ }
+
+ON_EXIT:
+
+ Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
+
+ //
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ //
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ This->SetIpFilter (This, &IpFilter);
+
+ return Status;
+}
+
+
+/**
+ Used to perform TFTP and MTFTP services.
+
+ This function is used to perform TFTP and MTFTP services. This includes the
+ TFTP operations to get the size of a file, read a directory, read a file, and
+ write a file. It also includes the MTFTP operations to get the size of a file,
+ read a directory, and read a file. The type of operation is specified by Operation.
+ If the callback function that is invoked during the TFTP/MTFTP operation does
+ not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will
+ be returned.
+ For read operations, the return data will be placed in the buffer specified by
+ BufferPtr. If BufferSize is too small to contain the entire downloaded file,
+ then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero
+ or the size of the requested file (the size of the requested file is only returned
+ if the TFTP server supports TFTP options). If BufferSize is large enough for the
+ read operation, then BufferSize will be set to the size of the downloaded file,
+ and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services
+ should use the get-file-size operations to determine the size of the downloaded
+ file prior to using the read-file operations-especially when downloading large
+ (greater than 64 MB) files-instead of making two calls to the read-file operation.
+ Following this recommendation will save time if the file is larger than expected
+ and the TFTP server does not support TFTP option extensions. Without TFTP option
+ extension support, the client has to download the entire file, counting and discarding
+ the received packets, to determine the file size.
+ For write operations, the data to be sent is in the buffer specified by BufferPtr.
+ BufferSize specifies the number of bytes to send. If the write operation completes
+ successfully, then EFI_SUCCESS will be returned.
+ For TFTP "get file size" operations, the size of the requested file or directory
+ is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server
+ does not support options, the file will be downloaded into a bit bucket and the
+ length of the downloaded file will be returned. For MTFTP "get file size" operations,
+ if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED
+ will be returned.
+ This function can take up to 10 seconds to timeout and return control to the caller.
+ If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then the TFTP sequence is stopped and EFI_ABORTED will be returned.
+ The format of the data returned from a TFTP read directory operation is a null-terminated
+ filename followed by a null-terminated information string, of the form
+ "size year-month-day hour:minute:second" (i.e. %d %d-%d-%d %d:%d:%f - note that
+ the seconds field can be a decimal number), where the date and time are UTC. For
+ an MTFTP read directory command, there is additionally a null-terminated multicast
+ IP address preceding the filename of the form %d.%d.%d.%d for IP v4. The final
+ entry is itself null-terminated, so that the final information string is terminated
+ with two null octets.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param Operation The type of operation to perform.
+ @param BufferPtr A pointer to the data buffer.
+ @param Overwrite Only used on write file operations. TRUE if a file on a remote server can
+ be overwritten.
+ @param BufferSize For get-file-size operations, *BufferSize returns the size of the
+ requested file.
+ @param BlockSize The requested block size to be used during a TFTP transfer.
+ @param ServerIp The TFTP / MTFTP server IP address.
+ @param Filename A Null-terminated ASCII string that specifies a directory name or a file
+ name.
+ @param Info Pointer to the MTFTP information.
+ @param DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.
+
+ @retval EFI_SUCCESS The TFTP/MTFTP operation was completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation.
+ @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation.
+ @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session.
+ @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcMtftp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
+ IN OUT VOID *BufferPtr,
+ IN BOOLEAN Overwrite,
+ IN OUT UINT64 *BufferSize,
+ IN UINTN *BlockSize OPTIONAL,
+ IN EFI_IP_ADDRESS *ServerIp,
+ IN UINT8 *Filename,
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_MTFTP4_CONFIG_DATA Mtftp4Config;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_MAC_ADDRESS TempMacAddr;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+
+ if ((This == NULL) ||
+ (Filename == NULL) ||
+ (BufferSize == NULL) ||
+ ((ServerIp == NULL) ||
+ (IP4_IS_UNSPECIFIED (NTOHL (ServerIp->Addr[0])) ||
+ IP4_IS_LOCAL_BROADCAST (NTOHL (ServerIp->Addr[0])))) ||
+ ((BufferPtr == NULL) && DontUseBuffer) ||
+ ((BlockSize != NULL) && (*BlockSize < 512))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_DEVICE_ERROR;
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = &Private->Mode;
+
+ if (!Mode->AutoArp) {
+ //
+ // If AutoArp is set false, check arp cache
+ //
+ UpdateArpCache (This);
+ if (!FindInArpCache (Mode, &ServerIp->v4, &TempMacAddr)) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Stop Udp4Read instance
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+ Mode->TftpErrorReceived = FALSE;
+ Mode->IcmpErrorReceived = FALSE;
+
+ Mtftp4Config.UseDefaultSetting = FALSE;
+ Mtftp4Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT;
+ Mtftp4Config.TryCount = PXEBC_MTFTP_RETRIES;
+
+ CopyMem (
+ &Mtftp4Config.StationIp,
+ &Private->StationIp,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ CopyMem (
+ &Mtftp4Config.SubnetMask,
+ &Private->SubnetMask,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ CopyMem (
+ &Mtftp4Config.GatewayIp,
+ &Private->GatewayIp,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ CopyMem (
+ &Mtftp4Config.ServerIp,
+ ServerIp,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ switch (Operation) {
+
+ case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:
+
+ Status = PxeBcTftpGetFileSize (
+ Private,
+ &Mtftp4Config,
+ Filename,
+ BlockSize,
+ BufferSize
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_READ_FILE:
+
+ Status = PxeBcTftpReadFile (
+ Private,
+ &Mtftp4Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:
+
+ Status = PxeBcTftpWriteFile (
+ Private,
+ &Mtftp4Config,
+ Filename,
+ Overwrite,
+ BlockSize,
+ BufferPtr,
+ BufferSize
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:
+
+ Status = PxeBcTftpReadDirectory (
+ Private,
+ &Mtftp4Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:
+ case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:
+ case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ default:
+
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+ Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
+ //
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ //
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ This->SetIpFilter (This, &IpFilter);
+
+ return Status;
+}
+
+
+/**
+ Writes a UDP packet to the network interface.
+
+ This function writes a UDP packet specified by the (optional HeaderPtr and)
+ BufferPtr parameters to the network interface. The UDP header is automatically
+ built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,
+ SrcIp, and SrcPort to build this header. If the packet is successfully built and
+ transmitted through the network interface, then EFI_SUCCESS will be returned.
+ If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will
+ be returned. If an ICMP error occurs during the transmission of the packet, then
+ the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and
+ EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param OpFlags The UDP operation flags.
+ @param DestIp The destination IP address.
+ @param DestPort The destination UDP port number.
+ @param GatewayIp The gateway IP address.
+ @param SrcIp The source IP address.
+ @param SrcPort The source UDP port number.
+ @param HeaderSize An optional field which may be set to the length of a header at
+ HeaderPtr to be prefixed to the data at BufferPtr.
+ @param HeaderPtr If HeaderSize is not NULL, a pointer to a header to be prefixed to the
+ data at BufferPtr.
+ @param BufferSize A pointer to the size of the data at BufferPtr.
+ @param BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS The UDP Write operation was completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted.
+ @retval EFI_ABORTED The callback function aborted the UDP Write operation.
+ @retval EFI_TIMEOUT The UDP Write operation timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcUdpWrite (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort,
+ IN EFI_IP_ADDRESS *GatewayIp OPTIONAL,
+ IN EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_UDP4_PROTOCOL *Udp4;
+ EFI_UDP4_COMPLETION_TOKEN Token;
+ EFI_UDP4_TRANSMIT_DATA *Udp4TxData;
+ UINT32 FragCount;
+ UINT32 DataLength;
+ EFI_UDP4_SESSION_DATA Udp4Session;
+ EFI_STATUS Status;
+ BOOLEAN IsDone;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_MAC_ADDRESS TempMacAddr;
+
+ IsDone = FALSE;
+
+ if ((This == NULL) || (DestIp == NULL) || (DestPort == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((GatewayIp != NULL) && (IP4_IS_UNSPECIFIED (NTOHL (GatewayIp->Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (GatewayIp->Addr[0])))) {
+ //
+ // Gateway is provided but it's not a unicast IP address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((HeaderSize != NULL) && ((*HeaderSize == 0) || (HeaderPtr == NULL))) {
+ //
+ // The HeaderSize ptr isn't NULL and: 1. the value is zero; or 2. the HeaderPtr
+ // is NULL.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((BufferSize == NULL) || ((*BufferSize != 0) && (BufferPtr == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Udp4 = Private->Udp4Write;
+ Mode = &Private->Mode;
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!Private->AddressIsOk && (SrcIp == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Mode->AutoArp) {
+ //
+ // If AutoArp is set false, check arp cache
+ //
+ UpdateArpCache (This);
+ if (!FindInArpCache (Mode, &DestIp->v4, &TempMacAddr)) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ Mode->IcmpErrorReceived = FALSE;
+
+ if ((Private->CurrentUdpSrcPort == 0) ||
+ ((SrcPort != NULL) && (*SrcPort != Private->CurrentUdpSrcPort))) {
+ //
+ // Port is changed, (re)configure the Udp4Write instance
+ //
+ if (SrcPort != NULL) {
+ Private->CurrentUdpSrcPort = *SrcPort;
+ }
+ }
+
+ Status = PxeBcConfigureUdpWriteInstance (
+ Udp4,
+ &Private->StationIp.v4,
+ &Private->SubnetMask.v4,
+ &Private->GatewayIp.v4,
+ &Private->CurrentUdpSrcPort,
+ Private->Mode.TTL,
+ Private->Mode.ToS
+ );
+ if (EFI_ERROR (Status)) {
+ Private->CurrentUdpSrcPort = 0;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));
+ ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));
+
+ CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
+ Udp4Session.DestinationPort = *DestPort;
+ if (SrcIp != NULL) {
+ CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));
+ }
+ if (SrcPort != NULL) {
+ Udp4Session.SourcePort = *SrcPort;
+ }
+
+ FragCount = (HeaderSize != NULL) ? 2 : 1;
+ Udp4TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA));
+ if (Udp4TxData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Udp4TxData->FragmentCount = FragCount;
+ Udp4TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+ Udp4TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+ DataLength = (UINT32) *BufferSize;
+
+ if (FragCount == 2) {
+
+ Udp4TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+ Udp4TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+ DataLength += (UINT32) *HeaderSize;
+ }
+
+ if (GatewayIp != NULL) {
+ Udp4TxData->GatewayAddress = (EFI_IPv4_ADDRESS *) GatewayIp;
+ }
+ Udp4TxData->UdpSessionData = &Udp4Session;
+ Udp4TxData->DataLength = DataLength;
+ Token.Packet.TxData = Udp4TxData;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Udp4->Transmit (Udp4, &Token);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ }
+ goto ON_EXIT;
+ }
+
+ while (!IsDone) {
+
+ Udp4->Poll (Udp4);
+ }
+
+ Status = Token.Status;
+
+ON_EXIT:
+
+ if (Token.Event != NULL) {
+ gBS->CloseEvent (Token.Event);
+ }
+
+ FreePool (Udp4TxData);
+
+ //
+ // Reset the instance.
+ //
+ Udp4->Configure (Udp4, NULL);
+ return Status;
+}
+
+/**
+ Decide whether the incoming UDP packet is acceptable per IP filter settings
+ in provided PxeBcMode.
+
+ @param PxeBcMode Pointer to EFI_PXE_BASE_CODE_MODE.
+ @param Session Received UDP session.
+
+ @retval TRUE The UDP package matches IP filters.
+ @retval FALSE The UDP package doesn't matches IP filters.
+
+**/
+BOOLEAN
+CheckIpByFilter (
+ IN EFI_PXE_BASE_CODE_MODE *PxeBcMode,
+ IN EFI_UDP4_SESSION_DATA *Session
+ )
+{
+ UINTN Index;
+ EFI_IPv4_ADDRESS Ip4Address;
+ EFI_IPv4_ADDRESS DestIp4Address;
+
+ if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {
+ return TRUE;
+ }
+
+ CopyMem (&DestIp4Address, &Session->DestinationAddress, sizeof (DestIp4Address));
+ if (((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) &&
+ IP4_IS_MULTICAST (EFI_NTOHL (DestIp4Address))
+ ) {
+ return TRUE;
+ }
+
+ if (((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) &&
+ IP4_IS_LOCAL_BROADCAST (EFI_NTOHL (DestIp4Address))
+ ) {
+ return TRUE;
+ }
+
+ CopyMem (&Ip4Address, &PxeBcMode->StationIp.v4, sizeof (Ip4Address));
+ if (((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) &&
+ EFI_IP4_EQUAL (&Ip4Address, &DestIp4Address)
+ ) {
+ return TRUE;
+ }
+
+ ASSERT (PxeBcMode->IpFilter.IpCnt < EFI_PXE_BASE_CODE_MAX_IPCNT);
+
+ for (Index = 0; Index < PxeBcMode->IpFilter.IpCnt; Index++) {
+ CopyMem (
+ &Ip4Address,
+ &PxeBcMode->IpFilter.IpList[Index].v4,
+ sizeof (Ip4Address)
+ );
+ if (EFI_IP4_EQUAL (&Ip4Address, &DestIp4Address)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Reads a UDP packet from the network interface.
+
+ This function reads a UDP packet from a network interface. The data contents
+ are returned in (the optional HeaderPtr and) BufferPtr, and the size of the
+ buffer received is returned in BufferSize . If the input BufferSize is smaller
+ than the UDP packet received (less optional HeaderSize), it will be set to the
+ required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the
+ contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is
+ successfully received, then EFI_SUCCESS will be returned, and the information
+ from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if
+ they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,
+ SrcIp, and SrcPort input values, different types of UDP packet receive filtering
+ will be performed. The following tables summarize these receive filter operations.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param OpFlags The UDP operation flags.
+ @param DestIp The destination IP address.
+ @param DestPort The destination UDP port number.
+ @param SrcIp The source IP address.
+ @param SrcPort The source UDP port number.
+ @param HeaderSize An optional field which may be set to the length of a header at
+ HeaderPtr to be prefixed to the data at BufferPtr.
+ @param HeaderPtr If HeaderSize is not NULL, a pointer to a header to be prefixed to the
+ data at BufferPtr.
+ @param BufferSize A pointer to the size of the data at BufferPtr.
+ @param BufferPtr A pointer to the data to be read.
+
+ @retval EFI_SUCCESS The UDP Read operation was completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold.
+ @retval EFI_ABORTED The callback function aborted the UDP Read operation.
+ @retval EFI_TIMEOUT The UDP Read operation timed out.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcUdpRead (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_UDP4_PROTOCOL *Udp4;
+ EFI_UDP4_COMPLETION_TOKEN Token;
+ EFI_UDP4_RECEIVE_DATA *RxData;
+ EFI_UDP4_SESSION_DATA *Session;
+ EFI_STATUS Status;
+ BOOLEAN IsDone;
+ BOOLEAN Matched;
+ UINTN CopiedLen;
+ UINTN HeaderLen;
+ UINTN HeaderCopiedLen;
+ UINTN BufferCopiedLen;
+ UINT32 FragmentLength;
+ UINTN FragmentIndex;
+ UINT8 *FragmentBuffer;
+
+ if (This == NULL || DestIp == NULL || DestPort == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) == 0 && (DestPort == NULL)) ||
+ ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) == 0 && (SrcIp == NULL)) ||
+ ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) == 0 && (SrcPort == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((HeaderSize != NULL) && (*HeaderSize == 0)) || ((HeaderSize != NULL) && (HeaderPtr == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((BufferSize == NULL) || (BufferPtr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Udp4 = Private->Udp4Read;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ Mode->IcmpErrorReceived = FALSE;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+TRY_AGAIN:
+
+ IsDone = FALSE;
+ Status = Udp4->Receive (Udp4, &Token);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ }
+ goto ON_EXIT;
+ }
+
+ Udp4->Poll (Udp4);
+
+ if (!IsDone) {
+ Status = EFI_TIMEOUT;
+ } else {
+
+ //
+ // check whether this packet matches the filters
+ //
+ if (EFI_ERROR (Token.Status)){
+ goto ON_EXIT;
+ }
+
+ RxData = Token.Packet.RxData;
+ Session = &RxData->UdpSession;
+
+ Matched = TRUE;
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) != 0) {
+ Matched = FALSE;
+ //
+ // Check UDP package by IP filter settings
+ //
+ if (CheckIpByFilter (Mode, Session)) {
+ Matched = TRUE;
+ }
+ }
+
+ if (Matched) {
+ Matched = FALSE;
+
+ //
+ // Match the destination ip of the received udp dgram
+ //
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {
+ Matched = TRUE;
+
+ if (DestIp != NULL) {
+ CopyMem (DestIp, &Session->DestinationAddress, sizeof (EFI_IPv4_ADDRESS));
+ }
+ } else {
+ if (DestIp != NULL) {
+ if (EFI_IP4_EQUAL (DestIp, &Session->DestinationAddress)) {
+ Matched = TRUE;
+ }
+ } else {
+ if (EFI_IP4_EQUAL (&Private->StationIp, &Session->DestinationAddress)) {
+ Matched = TRUE;
+ }
+ }
+ }
+ }
+
+ if (Matched) {
+ //
+ // Match the destination port of the received udp dgram
+ //
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {
+
+ if (DestPort != NULL) {
+ *DestPort = Session->DestinationPort;
+ }
+ } else {
+
+ if (*DestPort != Session->DestinationPort) {
+ Matched = FALSE;
+ }
+ }
+ }
+
+ if (Matched) {
+ //
+ // Match the source ip of the received udp dgram
+ //
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {
+
+ if (SrcIp != NULL) {
+ CopyMem (SrcIp, &Session->SourceAddress, sizeof (EFI_IPv4_ADDRESS));
+ }
+ } else {
+
+ if (!EFI_IP4_EQUAL (SrcIp, &Session->SourceAddress)) {
+ Matched = FALSE;
+ }
+ }
+ }
+
+ if (Matched) {
+ //
+ // Match the source port of the received udp dgram
+ //
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {
+
+ if (SrcPort != NULL) {
+ *SrcPort = Session->SourcePort;
+ }
+ } else {
+
+ if (*SrcPort != Session->SourcePort) {
+ Matched = FALSE;
+ }
+ }
+ }
+
+ if (Matched) {
+ ASSERT (RxData != NULL);
+
+ HeaderLen = 0;
+ if (HeaderSize != NULL) {
+ HeaderLen = MIN (*HeaderSize, RxData->DataLength);
+ }
+
+ if (RxData->DataLength - HeaderLen > *BufferSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ *HeaderSize = HeaderLen;
+ *BufferSize = RxData->DataLength - HeaderLen;
+
+ HeaderCopiedLen = 0;
+ BufferCopiedLen = 0;
+ for (FragmentIndex = 0; FragmentIndex < RxData->FragmentCount; FragmentIndex++) {
+ FragmentLength = RxData->FragmentTable[FragmentIndex].FragmentLength;
+ FragmentBuffer = RxData->FragmentTable[FragmentIndex].FragmentBuffer;
+ if (HeaderCopiedLen + FragmentLength < HeaderLen) {
+ //
+ // Copy the header part of received data.
+ //
+ CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength);
+ HeaderCopiedLen += FragmentLength;
+ } else if (HeaderCopiedLen < HeaderLen) {
+ //
+ // Copy the header part of received data.
+ //
+ CopiedLen = HeaderLen - HeaderCopiedLen;
+ CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen);
+ HeaderCopiedLen += CopiedLen;
+
+ //
+ // Copy the other part of received data.
+ //
+ CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen);
+ BufferCopiedLen += (FragmentLength - CopiedLen);
+ } else {
+ //
+ // Copy the other part of received data.
+ //
+ CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength);
+ BufferCopiedLen += FragmentLength;
+ }
+ }
+ }
+ } else {
+
+ Status = EFI_TIMEOUT;
+ }
+
+ //
+ // Recycle the RxData
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+
+ if (!Matched) {
+ goto TRY_AGAIN;
+ }
+ }
+
+ON_EXIT:
+
+ Udp4->Cancel (Udp4, &Token);
+
+ gBS->CloseEvent (Token.Event);
+
+ return Status;
+}
+
+/**
+ Updates the IP receive filters of a network device and enables software filtering.
+
+ The NewFilter field is used to modify the network device's current IP receive
+ filter settings and to enable a software filter. This function updates the IpFilter
+ field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.
+ The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().
+ The current hardware filter remains in effect no matter what the settings of OpFlags
+ are, so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those
+ packets whose reception is enabled in hardware-physical NIC address (unicast),
+ broadcast address, logical address or addresses (multicast), or all (promiscuous).
+ UdpRead() does not modify the IP filter settings.
+ Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive
+ filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ If an application or driver wishes to preserve the IP receive filter settings,
+ it will have to preserve the IP receive filter settings before these calls, and
+ use SetIpFilter() to restore them after the calls. If incompatible filtering is
+ requested (for example, PROMISCUOUS with anything else) or if the device does not
+ support a requested filter setting and it cannot be accommodated in software
+ (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.
+ The IPlist field is used to enable IPs other than the StationIP. They may be
+ multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,
+ then both the StationIP and the IPs from the IPlist will be used.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param NewFilter Pointer to the new set of IP receive filters.
+
+ @retval EFI_SUCCESS The IP receive filter settings were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetIpFilter (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter
+ )
+{
+ EFI_STATUS Status;
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ UINTN Index;
+ EFI_UDP4_CONFIG_DATA *Udp4Cfg;
+ BOOLEAN PromiscuousNeed;
+ BOOLEAN AcceptPromiscuous;
+ BOOLEAN AcceptBroadcast;
+ BOOLEAN MultiCastUpdate;
+
+ if (This == NULL) {
+ DEBUG ((EFI_D_ERROR, "This == NULL.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (NewFilter == NULL) {
+ DEBUG ((EFI_D_ERROR, "NewFilter == NULL.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewFilter->IpCnt > EFI_PXE_BASE_CODE_MAX_IPCNT) {
+ DEBUG ((EFI_D_ERROR, "NewFilter->IpCnt > %d.\n", EFI_PXE_BASE_CODE_MAX_IPCNT));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Mode->Started) {
+ DEBUG ((EFI_D_ERROR, "BC was not started.\n"));
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->UsingIpv6) {
+ DEBUG ((EFI_D_ERROR, "This driver is PXE for IPv4 Only.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PromiscuousNeed = FALSE;
+
+ for (Index = 0; Index < NewFilter->IpCnt; ++Index) {
+ if (IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {
+ //
+ // The IP is a broadcast address.
+ //
+ DEBUG ((EFI_D_ERROR, "There is broadcast address in NewFilter.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+ if ((EFI_NTOHL(Mode->StationIp) != 0) &&
+ (EFI_NTOHL(Mode->SubnetMask) != 0) &&
+ IP4_NET_EQUAL(EFI_NTOHL(Mode->StationIp), EFI_NTOHL(NewFilter->IpList[Index].v4), EFI_NTOHL(Mode->SubnetMask)) &&
+ NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), EFI_NTOHL(Mode->SubnetMask)) &&
+ ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0)) {
+ //
+ // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IP4 address is in IpList,
+ // promiscuous mode is needed.
+ //
+ PromiscuousNeed = TRUE;
+ }
+ }
+
+ AcceptPromiscuous = FALSE;
+ AcceptBroadcast = FALSE;
+ MultiCastUpdate = FALSE;
+
+ if (PromiscuousNeed ||
+ ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) ||
+ ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0)
+ ) {
+ //
+ // Configure the udp4 filter to receive all packages.
+ //
+ AcceptPromiscuous = TRUE;
+ } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {
+ //
+ // Configure the udp4 filter to receive all broadcast packages.
+ //
+ AcceptBroadcast = TRUE;
+ }
+
+ //
+ // In multicast condition when Promiscuous FALSE and IpCnt no-zero.
+ // Here check if there is any update of the multicast ip address. If yes,
+ // we need leave the old multicast group (by Config UDP instance to NULL),
+ // and join the new multicast group.
+ //
+ if (!AcceptPromiscuous) {
+ if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {
+ if (Mode->IpFilter.IpCnt != NewFilter->IpCnt) {
+ MultiCastUpdate = TRUE;
+ } else if (CompareMem (Mode->IpFilter.IpList, NewFilter->IpList, NewFilter->IpCnt * sizeof (EFI_IP_ADDRESS)) != 0 ) {
+ MultiCastUpdate = TRUE;
+ }
+ }
+ }
+
+ //
+ // Check whether we need reconfigure the UDP instance.
+ //
+ Udp4Cfg = &Private->Udp4CfgData;
+ if ((AcceptPromiscuous != Udp4Cfg->AcceptPromiscuous) ||
+ (AcceptBroadcast != Udp4Cfg->AcceptBroadcast) || MultiCastUpdate) {
+ //
+ // Clear the UDP instance configuration, all joined groups will be left
+ // during the operation.
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+ //
+ // Configure the UDP instance with the new configuration.
+ //
+ Udp4Cfg->AcceptPromiscuous = AcceptPromiscuous;
+ Udp4Cfg->AcceptBroadcast = AcceptBroadcast;
+ Status = Private->Udp4Read->Configure (Private->Udp4Read, Udp4Cfg);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // In not Promiscuous mode, need to join the new multicast group.
+ //
+ if (!AcceptPromiscuous) {
+ for (Index = 0; Index < NewFilter->IpCnt; ++Index) {
+ if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {
+ //
+ // Join the mutilcast group.
+ //
+ Status = Private->Udp4Read->Groups (Private->Udp4Read, TRUE, &NewFilter->IpList[Index].v4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+ }
+ }
+
+
+ //
+ // Save the new filter.
+ //
+ CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Uses the ARP protocol to resolve a MAC address.
+
+ This function uses the ARP protocol to resolve a MAC address. The UsingIpv6 field
+ of the EFI_PXE_BASE_CODE_MODE structure is used to determine if IPv4 or IPv6
+ addresses are being used. The IP address specified by IpAddr is used to resolve
+ a MAC address. If the ARP protocol succeeds in resolving the specified address,
+ then the ArpCacheEntries and ArpCache fields of the EFI_PXE_BASE_CODE_MODE structure
+ are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved
+ MAC address is placed there as well. If the PXE Base Code protocol is in the
+ stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters
+ a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is
+ returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then EFI_ABORTED is returned.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param IpAddr Pointer to the IP address that is used to resolve a MAC address.
+ @param MacAddr If not NULL, a pointer to the MAC address that was resolved with the
+ ARP protocol.
+
+ @retval EFI_SUCCESS The IP or MAC address was resolved.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_ICMP_ERROR Something error occur with the ICMP packet message.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcArp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_IP_ADDRESS * IpAddr,
+ IN EFI_MAC_ADDRESS * MacAddr OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS TempMacAddr;
+
+ if (This == NULL || IpAddr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!Private->AddressIsOk || Mode->UsingIpv6) {
+ //
+ // We can't resolve the IP address if we don't have a local address now.
+ // Don't have ARP for IPv6.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Mode->IcmpErrorReceived = FALSE;
+
+ if (!Mode->AutoArp) {
+ //
+ // If AutoArp is set false, check arp cache
+ //
+ UpdateArpCache (This);
+ if (!FindInArpCache (Mode, &IpAddr->v4, &TempMacAddr)) {
+ return EFI_DEVICE_ERROR;
+ }
+ } else {
+ Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, NULL, &TempMacAddr);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ }
+ return Status;
+ }
+ }
+
+ if (MacAddr != NULL) {
+ CopyMem (MacAddr, &TempMacAddr, sizeof (EFI_MAC_ADDRESS));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Updates the parameters that affect the operation of the PXE Base Code Protocol.
+
+ This function sets parameters that affect the operation of the PXE Base Code Protocol.
+ The parameter specified by NewAutoArp is used to control the generation of ARP
+ protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated
+ as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP
+ Protocol packets will be generated. In this case, the only mappings that are
+ available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.
+ If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol
+ service, then the service will fail. This function updates the AutoArp field of
+ the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.
+ The SetParameters() call must be invoked after a Callback Protocol is installed
+ to enable the use of callbacks.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the
+ current value of AutoARP.
+ @param NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the
+ current value of SendGUID.
+ @param NewTTL If not NULL, a pointer to be used in place of the current value of TTL,
+ the "time to live" field of the IP header.
+ @param NewToS If not NULL, a pointer to be used in place of the current value of ToS,
+ the "type of service" field of the IP header.
+ @param NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the
+ current value of the MakeCallback field of the Mode structure.
+
+ @retval EFI_SUCCESS The new parameters values were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetParameters (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN *NewAutoArp OPTIONAL,
+ IN BOOLEAN *NewSendGUID OPTIONAL,
+ IN UINT8 *NewTTL OPTIONAL,
+ IN UINT8 *NewToS OPTIONAL,
+ IN BOOLEAN *NewMakeCallback // OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (This == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (NewSendGUID != NULL && *NewSendGUID) {
+ //
+ // FixMe, cann't locate SendGuid
+ //
+ }
+
+ if (NewMakeCallback != NULL && *NewMakeCallback) {
+
+ Status = gBS->HandleProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID **) &Private->PxeBcCallback
+ );
+ if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+
+ if (!Mode->Started) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (NewMakeCallback != NULL) {
+
+ if (*NewMakeCallback) {
+ //
+ // Update the Callback protocol.
+ //
+ Status = gBS->HandleProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID **) &Private->PxeBcCallback
+ );
+
+ if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ } else {
+ Private->PxeBcCallback = NULL;
+ }
+
+ Mode->MakeCallbacks = *NewMakeCallback;
+ }
+
+ if (NewAutoArp != NULL) {
+ Mode->AutoArp = *NewAutoArp;
+ }
+
+ if (NewSendGUID != NULL) {
+ Mode->SendGUID = *NewSendGUID;
+ }
+
+ if (NewTTL != NULL) {
+ Mode->TTL = *NewTTL;
+ }
+
+ if (NewToS != NULL) {
+ Mode->ToS = *NewToS;
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Updates the station IP address and/or subnet mask values of a network device.
+
+ This function updates the station IP address and/or subnet mask values of a network
+ device. The NewStationIp field is used to modify the network device's current IP address.
+ If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,
+ this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure
+ with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet
+ mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.
+ Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE
+ structure with NewSubnetMask.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param NewStationIp Pointer to the new IP address to be used by the network device.
+ @param NewSubnetMask Pointer to the new subnet mask to be used by the network device.
+
+ @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetStationIP (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_IP_ADDRESS * NewStationIp OPTIONAL,
+ IN EFI_IP_ADDRESS * NewSubnetMask OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_ARP_CONFIG_DATA ArpConfigData;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewSubnetMask != NULL && !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewStationIp != NULL) {
+ if (IP4_IS_UNSPECIFIED(NTOHL (NewStationIp->Addr[0])) ||
+ IP4_IS_LOCAL_BROADCAST(NTOHL (NewStationIp->Addr[0])) ||
+ (NewSubnetMask != NULL && NewSubnetMask->Addr[0] != 0 && !NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), NTOHL (NewSubnetMask->Addr[0])))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (NewStationIp != NULL) {
+ CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
+ }
+
+ if (NewSubnetMask != NULL) {
+ CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));
+ }
+
+ Private->AddressIsOk = TRUE;
+
+ if (!Mode->UsingIpv6) {
+ //
+ // If in IPv4 mode, configure the corresponding ARP with this new
+ // station IP address.
+ //
+ ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));
+
+ ArpConfigData.SwAddressType = 0x0800;
+ ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);
+ ArpConfigData.StationAddress = &Private->StationIp.v4;
+
+ Private->Arp->Configure (Private->Arp, NULL);
+ Private->Arp->Configure (Private->Arp, &ArpConfigData);
+
+ //
+ // Update the route table.
+ //
+ Mode->RouteTableEntries = 1;
+ Mode->RouteTable[0].IpAddr.Addr[0] = Private->StationIp.Addr[0] & Private->SubnetMask.Addr[0];
+ Mode->RouteTable[0].SubnetMask.Addr[0] = Private->SubnetMask.Addr[0];
+ Mode->RouteTable[0].GwAddr.Addr[0] = 0;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Updates the contents of the cached DHCP and Discover packets.
+
+ The pointers to the new packets are used to update the contents of the cached
+ packets in the EFI_PXE_BASE_CODE_MODE structure.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param NewDhcpDiscoverValid Pointer to a value that will replace the current
+ DhcpDiscoverValid field.
+ @param NewDhcpAckReceived Pointer to a value that will replace the current
+ DhcpAckReceived field.
+ @param NewProxyOfferReceived Pointer to a value that will replace the current
+ ProxyOfferReceived field.
+ @param NewPxeDiscoverValid Pointer to a value that will replace the current
+ ProxyOfferReceived field.
+ @param NewPxeReplyReceived Pointer to a value that will replace the current
+ PxeReplyReceived field.
+ @param NewPxeBisReplyReceived Pointer to a value that will replace the current
+ PxeBisReplyReceived field.
+ @param NewDhcpDiscover Pointer to the new cached DHCP Discover packet contents.
+ @param NewDhcpAck Pointer to the new cached DHCP Ack packet contents.
+ @param NewProxyOffer Pointer to the new cached Proxy Offer packet contents.
+ @param NewPxeDiscover Pointer to the new cached PXE Discover packet contents.
+ @param NewPxeReply Pointer to the new cached PXE Reply packet contents.
+ @param NewPxeBisReply Pointer to the new cached PXE BIS Reply packet contents.
+
+ @retval EFI_SUCCESS The cached packet contents were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER This is NULL or not point to a valid EFI_PXE_BASE_CODE_PROTOCOL structure.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetPackets (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN BOOLEAN * NewDhcpDiscoverValid OPTIONAL,
+ IN BOOLEAN * NewDhcpAckReceived OPTIONAL,
+ IN BOOLEAN * NewProxyOfferReceived OPTIONAL,
+ IN BOOLEAN * NewPxeDiscoverValid OPTIONAL,
+ IN BOOLEAN * NewPxeReplyReceived OPTIONAL,
+ IN BOOLEAN * NewPxeBisReplyReceived OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (NewDhcpDiscoverValid != NULL) {
+ Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
+ }
+
+ if (NewDhcpAckReceived != NULL) {
+ Mode->DhcpAckReceived = *NewDhcpAckReceived;
+ }
+
+ if (NewProxyOfferReceived != NULL) {
+ Mode->ProxyOfferReceived = *NewProxyOfferReceived;
+ }
+
+ if (NewPxeDiscoverValid != NULL) {
+ Mode->PxeDiscoverValid = *NewPxeDiscoverValid;
+ }
+
+ if (NewPxeReplyReceived != NULL) {
+ Mode->PxeReplyReceived = *NewPxeReplyReceived;
+ }
+
+ if (NewPxeBisReplyReceived != NULL) {
+ Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
+ }
+
+ if (NewDhcpDiscover != NULL) {
+ CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewDhcpAck != NULL) {
+ CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewProxyOffer != NULL) {
+ CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewPxeDiscover != NULL) {
+ CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewPxeReply != NULL) {
+ CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewPxeBisReply != NULL) {
+ CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_PXE_BASE_CODE_PROTOCOL mPxeBcProtocolTemplate = {
+ EFI_PXE_BASE_CODE_PROTOCOL_REVISION,
+ EfiPxeBcStart,
+ EfiPxeBcStop,
+ EfiPxeBcDhcp,
+ EfiPxeBcDiscover,
+ EfiPxeBcMtftp,
+ EfiPxeBcUdpWrite,
+ EfiPxeBcUdpRead,
+ EfiPxeBcSetIpFilter,
+ EfiPxeBcArp,
+ EfiPxeBcSetParameters,
+ EfiPxeBcSetStationIP,
+ EfiPxeBcSetPackets,
+ NULL
+};
+
+/**
+ Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has
+ received, or is waiting to receive a packet.
+
+ This function is invoked when the PXE Base Code Protocol is about to transmit, has received,
+ or is waiting to receive a packet. Parameters Function and Received specify the type of event.
+ Parameters PacketLen and Packet specify the packet that generated the event. If these fields
+ are zero and NULL respectively, then this is a status update callback. If the operation specified
+ by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation
+ specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to
+ the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.
+ The SetParameters() function must be called after a Callback Protocol is installed to enable the
+ use of callbacks.
+
+ @param This Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.
+ @param Function The PXE Base Code Protocol function that is waiting for an event.
+ @param Received TRUE if the callback is being invoked due to a receive event. FALSE if
+ the callback is being invoked due to a transmit event.
+ @param PacketLength The length, in bytes, of Packet. This field will have a value of zero if
+ this is a wait for receive event.
+ @param PacketPtr If Received is TRUE, a pointer to the packet that was just received;
+ otherwise a pointer to the packet that is about to be transmitted.
+
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE if Function specifies a continue operation
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT if Function specifies an abort operation
+
+**/
+EFI_PXE_BASE_CODE_CALLBACK_STATUS
+EFIAPI
+EfiPxeLoadFileCallback (
+ IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL * This,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,
+ IN BOOLEAN Received,
+ IN UINT32 PacketLength,
+ IN EFI_PXE_BASE_CODE_PACKET * PacketPtr OPTIONAL
+ )
+{
+ EFI_INPUT_KEY Key;
+ EFI_STATUS Status;
+
+ //
+ // Catch Ctrl-C or ESC to abort.
+ //
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+
+ if (!EFI_ERROR (Status)) {
+
+ if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {
+
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
+ }
+ }
+ //
+ // No print if receive packet
+ //
+ if (Received) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ //
+ // Print only for three functions
+ //
+ switch (Function) {
+
+ case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
+ //
+ // Print only for open MTFTP packets, not every MTFTP packets
+ //
+ if (PacketLength != 0 && PacketPtr != NULL) {
+ if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ }
+ break;
+
+ case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
+ case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
+ break;
+
+ default:
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+
+ if (PacketLength != 0 && PacketPtr != NULL) {
+ //
+ // Print '.' when transmit a packet
+ //
+ AsciiPrint (".");
+
+ }
+
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+}
+
+EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL mPxeBcCallBackTemplate = {
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
+ EfiPxeLoadFileCallback
+};
+
+
+/**
+ Find the boot file.
+
+ @param Private Pointer to PxeBc private data.
+ @param BufferSize Pointer to buffer size.
+ @param Buffer Pointer to buffer.
+
+ @retval EFI_SUCCESS Discover the boot file successfully.
+ @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out.
+ @retval EFI_ABORTED PXE bootstrap server, so local boot need abort.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is too small to load the boot file.
+
+**/
+EFI_STATUS
+DiscoverBootFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN OUT UINT64 *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ UINT16 Type;
+ UINT16 Layer;
+ BOOLEAN UseBis;
+ PXEBC_CACHED_DHCP4_PACKET *Packet;
+ UINT16 Value;
+
+ PxeBc = &Private->PxeBc;
+ Mode = PxeBc->Mode;
+ Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
+ Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
+
+ //
+ // do DHCP.
+ //
+ Status = PxeBc->Dhcp (PxeBc, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Select a boot server
+ //
+ Status = PxeBcSelectBootPrompt (Private);
+
+ if (Status == EFI_SUCCESS) {
+ Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
+ } else if (Status == EFI_TIMEOUT) {
+ Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
+ }
+
+ if (!EFI_ERROR (Status)) {
+
+ if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
+ //
+ // Local boot(PXE bootstrap server) need abort
+ //
+ return EFI_ABORTED;
+ }
+
+ UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
+ Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ *BufferSize = 0;
+
+ //
+ // Get bootfile name and (m)tftp server ip addresss
+ //
+ if (Mode->PxeReplyReceived) {
+ Packet = &Private->PxeReply;
+ } else if (Mode->ProxyOfferReceived) {
+ Packet = &Private->ProxyOffer;
+ } else {
+ Packet = &Private->Dhcp4Ack;
+ }
+
+ //
+ // Use siaddr(next server) in DHCPOFFER packet header, if zero, use option 54(server identifier)
+ // in DHCPOFFER packet.
+ // (It does not comply with PXE Spec, Ver2.1)
+ //
+ if (EFI_IP4_EQUAL (&Packet->Packet.Offer.Dhcp4.Header.ServerAddr, &mZeroIp4Addr)) {
+ CopyMem (
+ &Private->ServerIp,
+ Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ } else {
+ CopyMem (
+ &Private->ServerIp,
+ &Packet->Packet.Offer.Dhcp4.Header.ServerAddr,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+ if (Private->ServerIp.Addr[0] == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ASSERT (Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
+
+ //
+ // bootlfile name
+ //
+ Private->BootFileName = (CHAR8 *) (Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data);
+
+ if (Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
+ //
+ // Already have the bootfile length option, compute the file size
+ //
+ CopyMem (&Value, Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
+ Value = NTOHS (Value);
+ *BufferSize = 512 * Value;
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ //
+ // Get the bootfile size from tftp
+ //
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+ Buffer,
+ FALSE,
+ BufferSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ (UINT8 *) Private->BootFileName,
+ NULL,
+ FALSE
+ );
+ }
+
+ Private->FileSize = (UINTN) *BufferSize;
+
+ return Status;
+}
+
+/**
+ Causes the driver to load a specified file.
+
+ @param This Protocol instance pointer.
+ @param FilePath The device specific path of the file to load.
+ @param BootPolicy If TRUE, indicates that the request originates from the
+ boot manager is attempting to load FilePath as a boot
+ selection. If FALSE, then FilePath must match as exact file
+ to be loaded.
+ @param BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then no the size of the requested file is returned in
+ BufferSize.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+ BufferSize is NULL.
+ @retval EFI_NO_MEDIA No medium was present to load the file.
+ @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
+ @retval EFI_NO_RESPONSE The remote system did not respond.
+ @retval EFI_NOT_FOUND The file was not found.
+ @retval EFI_ABORTED The file load process was manually cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeLoadFile (
+ IN EFI_LOAD_FILE_PROTOCOL * This,
+ IN EFI_DEVICE_PATH_PROTOCOL * FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ BOOLEAN NewMakeCallback;
+ EFI_STATUS Status;
+ UINT64 TmpBufSize;
+ BOOLEAN MediaPresent;
+
+ if (FilePath == NULL || !IsDevicePathEnd (FilePath)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_LOADFILE (This);
+ PxeBc = &Private->PxeBc;
+ NewMakeCallback = FALSE;
+ Status = EFI_DEVICE_ERROR;
+
+ if (This == NULL || BufferSize == NULL) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Only support BootPolicy
+ //
+ if (!BootPolicy) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Check media status before PXE start
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Private->Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ Status = PxeBc->Start (PxeBc, FALSE);
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ return Status;
+ }
+
+ Status = gBS->HandleProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID **) &Private->PxeBcCallback
+ );
+ if (Status == EFI_UNSUPPORTED) {
+
+ CopyMem (&Private->LoadFileCallback, &mPxeBcCallBackTemplate, sizeof (Private->LoadFileCallback));
+
+ Status = gBS->InstallProtocolInterface (
+ &Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->LoadFileCallback
+ );
+
+ NewMakeCallback = (BOOLEAN) (Status == EFI_SUCCESS);
+
+ Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
+ if (EFI_ERROR (Status)) {
+ PxeBc->Stop (PxeBc);
+ return Status;
+ }
+ }
+
+ if (Private->FileSize == 0) {
+ TmpBufSize = 0;
+ Status = DiscoverBootFile (Private, &TmpBufSize, Buffer);
+
+ if (sizeof (UINTN) < sizeof (UINT64) && (TmpBufSize > 0xFFFFFFFF)) {
+ Status = EFI_DEVICE_ERROR;
+ } else if (TmpBufSize > 0 && *BufferSize >= (UINTN) TmpBufSize && Buffer != NULL) {
+ *BufferSize = (UINTN) TmpBufSize;
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ Buffer,
+ FALSE,
+ &TmpBufSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ (UINT8 *) Private->BootFileName,
+ NULL,
+ FALSE
+ );
+ } else if (TmpBufSize > 0) {
+ *BufferSize = (UINTN) TmpBufSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ }
+ } else if (Buffer == NULL || Private->FileSize > *BufferSize) {
+ *BufferSize = Private->FileSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ //
+ // Download the file.
+ //
+ TmpBufSize = (UINT64) (*BufferSize);
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ Buffer,
+ FALSE,
+ &TmpBufSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ (UINT8 *) Private->BootFileName,
+ NULL,
+ FALSE
+ );
+ }
+ //
+ // If we added a callback protocol, now is the time to remove it.
+ //
+ if (NewMakeCallback) {
+
+ NewMakeCallback = FALSE;
+
+ PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
+
+ gBS->UninstallProtocolInterface (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ &Private->LoadFileCallback
+ );
+ }
+
+ //
+ // Check download status
+ //
+ if (Status == EFI_SUCCESS) {
+ //
+ // The DHCP4 can have only one configured child instance so we need to stop
+ // reset the DHCP4 child before we return. Otherwise the other programs which
+ // also need to use DHCP4 will be impacted.
+ // The functionality of PXE Base Code protocol will not be stopped,
+ // when downloading is successfully.
+ //
+ Private->Dhcp4->Stop (Private->Dhcp4);
+ Private->Dhcp4->Configure (Private->Dhcp4, NULL);
+ return EFI_SUCCESS;
+
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ if (Buffer != NULL) {
+ AsciiPrint ("PXE-E05: Download buffer is smaller than requested file.\n");
+ } else {
+ //
+ // The functionality of PXE Base Code protocol will not be stopped.
+ //
+ return Status;
+ }
+
+ } else if (Status == EFI_DEVICE_ERROR) {
+ AsciiPrint ("PXE-E07: Network device error.\n");
+
+ } else if (Status == EFI_OUT_OF_RESOURCES) {
+ AsciiPrint ("PXE-E09: Could not allocate I/O buffers.\n");
+
+ } else if (Status == EFI_NO_MEDIA) {
+ AsciiPrint ("PXE-E12: Could not detect network connection.\n");
+
+ } else if (Status == EFI_NO_RESPONSE) {
+ AsciiPrint ("PXE-E16: No offer received.\n");
+
+ } else if (Status == EFI_TIMEOUT) {
+ AsciiPrint ("PXE-E18: Server response timeout.\n");
+
+ } else if (Status == EFI_ABORTED) {
+ AsciiPrint ("PXE-E21: Remote boot cancelled.\n");
+
+ } else if (Status == EFI_ICMP_ERROR) {
+ AsciiPrint ("PXE-E22: Client received ICMP error from server.\n");
+
+ } else if (Status == EFI_TFTP_ERROR) {
+ AsciiPrint ("PXE-E23: Client received TFTP error from server.\n");
+
+ } else {
+ AsciiPrint ("PXE-E99: Unexpected network error.\n");
+ }
+
+ PxeBc->Stop (PxeBc);
+
+ return Status;
+}
+
+EFI_LOAD_FILE_PROTOCOL mLoadFileProtocolTemplate = { EfiPxeLoadFile };
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.h b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.h
new file mode 100644
index 0000000000..834c0d0089
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.h
@@ -0,0 +1,188 @@
+/** @file
+
+Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_IMPL_H__
+#define __EFI_PXEBC_IMPL_H__
+
+
+typedef struct _PXEBC_PRIVATE_DATA PXEBC_PRIVATE_DATA;
+
+#include <Uefi.h>
+
+#include <Guid/SmBios.h>
+#include <IndustryStandard/SmBios.h>
+#include <IndustryStandard/Dhcp.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/Mtftp4.h>
+#include <Protocol/Udp4.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/PxeBaseCodeCallBack.h>
+#include <Protocol/Arp.h>
+#include <Protocol/Ip4.h>
+#include <Protocol/Ip4Config2.h>
+
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/DpcLib.h>
+#include <Library/PcdLib.h>
+
+#include "PxeBcDriver.h"
+#include "PxeBcDhcp.h"
+#include "PxeBcMtftp.h"
+#include "PxeBcSupport.h"
+
+#define PXEBC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'P')
+#define PXEBC_MTFTP_TIMEOUT 4
+#define PXEBC_MTFTP_RETRIES 6
+#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE 8
+#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE 4
+#define PXEBC_DEFAULT_PACKET_SIZE 1480
+#define PXEBC_DEFAULT_LIFETIME 50000 // 50ms, unit is microsecond
+
+struct _PXEBC_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+ EFI_HANDLE ArpChild;
+ EFI_HANDLE Dhcp4Child;
+ EFI_HANDLE Ip4Child;
+ EFI_HANDLE Mtftp4Child;
+ EFI_HANDLE Udp4ReadChild;
+ EFI_HANDLE Udp4WriteChild;
+
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+
+ EFI_PXE_BASE_CODE_PROTOCOL PxeBc;
+ EFI_LOAD_FILE_PROTOCOL LoadFile;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL LoadFileCallback;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *PxeBcCallback;
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_IP4_PROTOCOL *Ip4;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ EFI_IP4_CONFIG_DATA Ip4ConfigData;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_UDP4_PROTOCOL *Udp4Read;
+ EFI_UDP4_PROTOCOL *Udp4Write;
+ UINT16 CurrentUdpSrcPort;
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+
+
+ EFI_PXE_BASE_CODE_MODE Mode;
+ EFI_PXE_BASE_CODE_FUNCTION Function;
+
+ CHAR8 *BootFileName;
+
+ EFI_IP_ADDRESS StationIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS GatewayIp;
+ EFI_IP_ADDRESS ServerIp;
+ BOOLEAN AddressIsOk;
+ UINT32 Ip4MaxPacketSize;
+ UINTN BlockSize;
+ UINTN FileSize;
+
+ UINT8 OptionBuffer[PXEBC_DHCP4_MAX_OPTION_SIZE];
+ EFI_DHCP4_PACKET SeedPacket;
+ EFI_MAC_ADDRESS Mac;
+ UINT8 MacLen;
+
+ BOOLEAN SortOffers;
+ BOOLEAN GotProxyOffer;
+ UINT32 NumOffers;
+ UINT32 SelectedOffer;
+ UINT32 ProxyOfferType;
+
+ //
+ // Cached packets as complements of pxe mode data
+ //
+ PXEBC_CACHED_DHCP4_PACKET ProxyOffer;
+ PXEBC_CACHED_DHCP4_PACKET Dhcp4Ack;
+ PXEBC_CACHED_DHCP4_PACKET PxeReply;
+ PXEBC_CACHED_DHCP4_PACKET Dhcp4Offers[PXEBC_MAX_OFFER_NUM];
+
+ //
+ // Arrays for different types of offers:
+ // ServerCount records the count of the servers we got the offers,
+ // OfferIndex records the index of the offer sent by the server indexed by ServerCount.
+ //
+ UINT32 ServerCount[DHCP4_PACKET_TYPE_MAX];
+ UINT32 OfferIndex[DHCP4_PACKET_TYPE_MAX][PXEBC_MAX_OFFER_NUM];
+ UINT32 BootpIndex;
+ UINT32 ProxyIndex[DHCP4_PACKET_TYPE_MAX];
+ UINT32 BinlIndex[PXEBC_MAX_OFFER_NUM];
+
+ EFI_EVENT GetArpCacheEvent;
+ //
+ // token and event used to get ICMP error data from IP
+ //
+ EFI_IP4_COMPLETION_TOKEN IcmpErrorRcvToken;
+};
+
+#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a) CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE)
+
+#define PXEBC_PRIVATE_DATA_FROM_LOADFILE(a) CR (a, PXEBC_PRIVATE_DATA, LoadFile, PXEBC_PRIVATE_DATA_SIGNATURE)
+
+#define PXEBC_PRIVATE_DATA_FROM_PXEBCCALLBACK(a) CR (a, PXEBC_PRIVATE_DATA, PxeBcCallback, PXEBC_PRIVATE_DATA_SIGNATURE)
+
+extern EFI_PXE_BASE_CODE_PROTOCOL mPxeBcProtocolTemplate;
+extern EFI_LOAD_FILE_PROTOCOL mLoadFileProtocolTemplate;
+
+/**
+ Causes the driver to load a specified file.
+
+ @param This Protocol instance pointer.
+ @param FilePath The device specific path of the file to load.
+ @param BootPolicy If TRUE, indicates that the request originates from the
+ boot manager is attempting to load FilePath as a boot
+ selection. If FALSE, then FilePath must match as exact file
+ to be loaded.
+ @param BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then no the size of the requested file is returned in
+ BufferSize.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+ BufferSize is NULL.
+ @retval EFI_NO_MEDIA No medium was present to load the file.
+ @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
+ @retval EFI_NO_RESPONSE The remote system did not respond.
+ @retval EFI_NOT_FOUND The file was not found.
+ @retval EFI_ABORTED The file load process was manually cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeLoadFile (
+ IN EFI_LOAD_FILE_PROTOCOL * This,
+ IN EFI_DEVICE_PATH_PROTOCOL * FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.c b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.c
new file mode 100644
index 0000000000..8017b73dd7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.c
@@ -0,0 +1,454 @@
+/** @file
+ PxeBc MTFTP functions.
+
+Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = {
+ "blksize",
+ "timeout",
+ "tsize",
+ "multicast"
+};
+
+
+/**
+ This is a callback function when packets received/transmitted in Mtftp driver.
+
+ A callback function that is provided by the caller to intercept
+ the EFI_MTFTP4_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the
+ EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept
+ EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to
+ EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().
+
+ @param This Pointer to Mtftp protocol instance
+ @param Token Pointer to Mtftp token
+ @param PacketLen Length of Mtftp packet
+ @param Packet Pointer to Mtftp packet
+
+ @retval EFI_SUCCESS Operation sucess
+ @retval EFI_ABORTED Abort transfer process
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcCheckPacket (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+ EFI_STATUS Status;
+
+ Private = (PXEBC_PRIVATE_DATA *) Token->Context;
+ Callback = Private->PxeBcCallback;
+ Status = EFI_SUCCESS;
+
+ if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) {
+ Private->Mode.TftpErrorReceived = TRUE;
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+ AsciiStrnCpyS (Private->Mode.TftpError.ErrorString, PXE_MTFTP_ERROR_STRING_LENGTH, (CHAR8 *) Packet->Error.ErrorMessage, PXE_MTFTP_ERROR_STRING_LENGTH - 1);
+ Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0';
+ }
+
+ if (Callback != NULL) {
+
+ Status = Callback->Callback (
+ Callback,
+ Private->Function,
+ TRUE,
+ PacketLen,
+ (EFI_PXE_BASE_CODE_PACKET *) Packet
+ );
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+
+ Status = EFI_ABORTED;
+ } else {
+
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to get size of a file by Tftp.
+
+ @param Private Pointer to PxeBc private data
+ @param Config Pointer to Mtftp configuration data
+ @param Filename Pointer to file name
+ @param BlockSize Pointer to block size
+ @param BufferSize Pointer to buffer size
+
+ @retval EFI_SUCCESS Get the size of file success
+ @retval EFI_NOT_FOUND Parse the tftp ptions failed.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Other Has not get the size of the file.
+
+**/
+EFI_STATUS
+PxeBcTftpGetFileSize (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_OPTION ReqOpt[2];
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_OPTION *Option;
+ UINT32 PktLen;
+ UINT8 OptBuf[128];
+ UINT32 OptCnt;
+ EFI_STATUS Status;
+
+ *BufferSize = 0;
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ Packet = NULL;
+ Option = NULL;
+ PktLen = 0;
+ OptCnt = 1;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+
+ return Status;
+ }
+
+ ReqOpt[0].OptionStr = (UINT8*)mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];
+ UtoA10 (0, (CHAR8 *) OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+ ReqOpt[0].ValueStr = OptBuf;
+
+ if (BlockSize != NULL) {
+ ReqOpt[1].OptionStr = (UINT8*)mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[1].ValueStr = ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1;
+ UtoA10 (*BlockSize, (CHAR8 *) ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1));
+ OptCnt++;
+ }
+
+ Status = Mtftp4->GetInfo (
+ Mtftp4,
+ NULL,
+ Filename,
+ NULL,
+ (UINT8) OptCnt,
+ ReqOpt,
+ &PktLen,
+ &Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_TFTP_ERROR) {
+ Private->Mode.TftpErrorReceived = TRUE;
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+ AsciiStrnCpyS (
+ Private->Mode.TftpError.ErrorString,
+ PXE_MTFTP_ERROR_STRING_LENGTH,
+ (CHAR8 *) Packet->Error.ErrorMessage,
+ PXE_MTFTP_ERROR_STRING_LENGTH - 1
+ );
+ Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0';
+ }
+ goto ON_ERROR;
+ }
+
+ OptCnt = 0;
+
+ Status = Mtftp4->ParseOptions (
+ Mtftp4,
+ PktLen,
+ Packet,
+ (UINT32 *) &OptCnt,
+ &Option
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ goto ON_ERROR;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ while (OptCnt != 0) {
+
+ if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {
+
+ *BufferSize = AtoU64 (Option[OptCnt - 1].ValueStr);
+ Status = EFI_SUCCESS;
+ }
+
+ OptCnt--;
+ }
+
+ FreePool (Option);
+
+ON_ERROR:
+
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to get data of a file by Tftp.
+
+ @param Private Pointer to PxeBc private data
+ @param Config Pointer to Mtftp configuration data
+ @param Filename Pointer to file name
+ @param BlockSize Pointer to block size
+ @param BufferPtr Pointer to buffer
+ @param BufferSize Pointer to buffer size
+ @param DontUseBuffer Indicate whether with a receive buffer
+
+ @retval EFI_SUCCESS Read the data success from the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_TOKEN Token;
+ EFI_MTFTP4_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+
+ ReqOpt[0].OptionStr = (UINT8*) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ UtoA10 (*BlockSize, (CHAR8 *) ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.Context = Private;
+
+ if (DontUseBuffer) {
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ } else {
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ }
+
+ Token.CheckPacket = PxeBcCheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp4->ReadFile (Mtftp4, &Token);
+
+ *BufferSize = Token.BufferSize;
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is put data of a file by Tftp.
+
+ @param Private Pointer to PxeBc private data
+ @param Config Pointer to Mtftp configuration data
+ @param Filename Pointer to file name
+ @param Overwrite Indicate whether with overwrite attribute
+ @param BlockSize Pointer to block size
+ @param BufferPtr Pointer to buffer
+ @param BufferSize Pointer to buffer size
+
+ @retval EFI_SUCCESS Write the data success into the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpWriteFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN BOOLEAN Overwrite,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_TOKEN Token;
+ EFI_MTFTP4_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+
+ ReqOpt[0].OptionStr = (UINT8*) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ UtoA10 (*BlockSize, (CHAR8 *) ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ Token.CheckPacket = PxeBcCheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp4->WriteFile (Mtftp4, &Token);
+ *BufferSize = Token.BufferSize;
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to get data(file) from a directory(may be a server) by Tftp.
+
+ @param Private Pointer to PxeBc private data.
+ @param Config Pointer to Mtftp configuration data.
+ @param Filename Pointer to file name.
+ @param BlockSize Pointer to block size.
+ @param BufferPtr Pointer to buffer.
+ @param BufferSize Pointer to buffer size.
+ @param DontUseBuffer Indicate whether with a receive buffer.
+
+ @retval EFI_SUCCES Get the data from the file included in directory success.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Operation failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadDirectory (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_TOKEN Token;
+ EFI_MTFTP4_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+
+ ReqOpt[0].OptionStr = (UINT8*) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ UtoA10 (*BlockSize, (CHAR8 *) ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.Context = Private;
+
+ if (DontUseBuffer) {
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ } else {
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ }
+
+ Token.CheckPacket = PxeBcCheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp4->ReadDirectory (Mtftp4, &Token);
+
+ *BufferSize = Token.BufferSize;
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.h b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.h
new file mode 100644
index 0000000000..241b079d72
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcMtftp.h
@@ -0,0 +1,137 @@
+/** @file
+ Mtftp routines for PxeBc.
+
+Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_MTFTP_H__
+#define __EFI_PXEBC_MTFTP_H__
+
+#define PXE_MTFTP_OPTION_BLKSIZE_INDEX 0
+#define PXE_MTFTP_OPTION_TIMEOUT_INDEX 1
+#define PXE_MTFTP_OPTION_TSIZE_INDEX 2
+#define PXE_MTFTP_OPTION_MULTICAST_INDEX 3
+#define PXE_MTFTP_OPTION_MAXIMUM_INDEX 4
+
+#define PXE_MTFTP_ERROR_STRING_LENGTH 127
+#define PXE_MTFTP_OPTBUF_MAXNUM_INDEX 128
+
+
+/**
+ This function is to get size of a file by Tftp.
+
+ @param Private Pointer to PxeBc private data
+ @param Config Pointer to Mtftp configuration data
+ @param Filename Pointer to file name
+ @param BlockSize Pointer to block size
+ @param BufferSize Pointer to buffer size
+
+ @retval EFI_SUCCESS Get the size of file success
+ @retval EFI_NOT_FOUND Parse the tftp ptions failed.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Other Has not get the size of the file.
+
+**/
+EFI_STATUS
+PxeBcTftpGetFileSize (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN OUT UINT64 *BufferSize
+ );
+
+
+/**
+ This function is to get data of a file by Tftp.
+
+ @param Private Pointer to PxeBc private data
+ @param Config Pointer to Mtftp configuration data
+ @param Filename Pointer to file name
+ @param BlockSize Pointer to block size
+ @param BufferPtr Pointer to buffer
+ @param BufferSize Pointer to buffer size
+ @param DontUseBuffer Indicate whether with a receive buffer
+
+ @retval EFI_SUCCESS Read the data success from the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ );
+
+
+/**
+ This function is put data of a file by Tftp.
+
+ @param Private Pointer to PxeBc private data
+ @param Config Pointer to Mtftp configuration data
+ @param Filename Pointer to file name
+ @param Overwrite Indicate whether with overwrite attribute
+ @param BlockSize Pointer to block size
+ @param BufferPtr Pointer to buffer
+ @param BufferSize Pointer to buffer size
+
+ @retval EFI_SUCCESS Write the data success into the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpWriteFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN BOOLEAN Overwrite,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize
+ );
+
+
+/**
+ This function is to get data(file) from a directory(may be a server) by Tftp.
+
+ @param Private Pointer to PxeBc private data.
+ @param Config Pointer to Mtftp configuration data.
+ @param Filename Pointer to file name.
+ @param BlockSize Pointer to block size.
+ @param BufferPtr Pointer to buffer.
+ @param BufferSize Pointer to buffer size.
+ @param DontUseBuffer Indicate whether with a receive buffer.
+
+ @retval EFI_SUCCES Get the data from the file included in directory success.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Operation failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadDirectory (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ );
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.c b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.c
new file mode 100644
index 0000000000..077905671e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,201 @@
+/** @file
+ Support routines for PxeBc.
+
+Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "PxeBcImpl.h"
+
+
+/**
+ The common notify function associated with various PxeBc events.
+
+ @param Event The event signaled.
+ @param Context The context.
+
+**/
+VOID
+EFIAPI
+PxeBcCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+
+/**
+ This function initialize(or configure) the Udp4Write instance.
+
+ @param Udp4 Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param StationIp Pointer to the station ip address.
+ @param SubnetMask Pointer to the subnetmask of the station ip address.
+ @param Gateway Pointer to the gateway ip address.
+ @param SrcPort Pointer to the srouce port of the station.
+ @param Ttl The time to live field of the IP header.
+ @param ToS The type of service field of the IP header.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already started/configured
+ and must be stopped/reset before it can be reconfigured.
+ @retval EFI_ACCESS_DENIED UdpConfigData. AllowDuplicatePort is FALSE
+ and UdpConfigData.StationPort is already used by
+ other instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate memory for this
+ EFI UDPv4 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and this instance
+ was not opened.
+ @retval Others Please examine the function Udp4->Routes(Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway) returns.
+
+**/
+EFI_STATUS
+PxeBcConfigureUdpWriteInstance (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_IPv4_ADDRESS *StationIp,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *Gateway,
+ IN OUT UINT16 *SrcPort,
+ IN UINT8 Ttl,
+ IN UINT8 ToS
+ )
+{
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));
+
+ Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.TypeOfService = ToS;
+ Udp4CfgData.TimeToLive = Ttl;
+ Udp4CfgData.AllowDuplicatePort = TRUE;
+
+ CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));
+ CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));
+
+ Udp4CfgData.StationPort = *SrcPort;
+
+ //
+ // Reset the instance.
+ //
+ Udp4->Configure (Udp4, NULL);
+
+ Status = Udp4->Configure (Udp4, &Udp4CfgData);
+ if (!EFI_ERROR (Status) && (Gateway->Addr[0] != 0)) {
+ //
+ // basic configuration OK, need to add the default route entry
+ //
+ Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);
+ if (EFI_ERROR (Status)) {
+ //
+ // roll back
+ //
+ Udp4->Configure (Udp4, NULL);
+ }
+ }
+
+ if (!EFI_ERROR (Status) && (*SrcPort == 0)) {
+ Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);
+ *SrcPort = Udp4CfgData.StationPort;
+ }
+
+ return Status;
+}
+
+
+/**
+ Convert number to ASCII value.
+
+ @param Number Numeric value to convert to decimal ASCII value.
+ @param Buffer Buffer to place ASCII version of the Number.
+ @param Length Length of Buffer.
+
+**/
+VOID
+CvtNum (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN UINTN Length
+ )
+{
+ UINTN Remainder;
+
+ for (; Length > 0; Length--) {
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length - 1] = (UINT8) ('0' + Remainder);
+ }
+}
+
+
+/**
+ Convert unsigned int number to decimal number.
+
+ @param Number The unsigned int number will be converted.
+ @param Buffer Pointer to the buffer to store the decimal number after transform.
+ @param[in] BufferSize The maxsize of the buffer.
+
+ @return the length of the number after transform.
+
+**/
+UINTN
+UtoA10 (
+ IN UINTN Number,
+ IN CHAR8 *Buffer,
+ IN UINTN BufferSize
+ )
+{
+ UINTN Index;
+ CHAR8 TempStr[64];
+
+ Index = 63;
+ TempStr[Index] = 0;
+
+ do {
+ Index--;
+ TempStr[Index] = (CHAR8) ('0' + (Number % 10));
+ Number = Number / 10;
+ } while (Number != 0);
+
+ AsciiStrCpyS (Buffer, BufferSize, &TempStr[Index]);
+
+ return AsciiStrLen (Buffer);
+}
+
+
+/**
+ Convert ASCII numeric string to a UINTN value.
+
+ @param Buffer Pointer to the 8-byte unsigned int value.
+
+ @return UINTN value of the ASCII string.
+
+**/
+UINT64
+AtoU64 (
+ IN UINT8 *Buffer
+ )
+{
+ UINT64 Value;
+ UINT8 Character;
+
+ Value = 0;
+ while ((Character = *Buffer++) != '\0') {
+ Value = MultU64x32 (Value, 10) + (Character - '0');
+ }
+
+ return Value;
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.h b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.h
new file mode 100644
index 0000000000..16ac05ec82
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.h
@@ -0,0 +1,120 @@
+/** @file
+ Support routines for PxeBc.
+Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_SUPPORT_H__
+#define __EFI_PXEBC_SUPPORT_H__
+
+
+/**
+ The common notify function associated with various PxeBc events.
+
+ @param Event The event signaled.
+ @param Context The context.
+
+**/
+VOID
+EFIAPI
+PxeBcCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ This function initialize(or configure) the Udp4Write instance.
+
+ @param Udp4 Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param StationIp Pointer to the station ip address.
+ @param SubnetMask Pointer to the subnetmask of the station ip address.
+ @param Gateway Pointer to the gateway ip address.
+ @param SrcPort Pointer to the srouce port of the station.
+ @param Ttl The time to live field of the IP header.
+ @param ToS The type of service field of the IP header.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP,
+ RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already started/configured
+ and must be stopped/reset before it can be reconfigured.
+ @retval EFI_ACCESS_DENIED UdpConfigData. AllowDuplicatePort is FALSE
+ and UdpConfigData.StationPort is already used by
+ other instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate memory for this
+ EFI UDPv4 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and this instance
+ was not opened.
+ @retval Others Please examine the function Udp4->Routes(Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway) returns.
+
+**/
+EFI_STATUS
+PxeBcConfigureUdpWriteInstance (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_IPv4_ADDRESS *StationIp,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *Gateway,
+ IN OUT UINT16 *SrcPort,
+ IN UINT8 Ttl,
+ IN UINT8 ToS
+ );
+/**
+ Convert number to ASCII value.
+
+ @param Number Numeric value to convert to decimal ASCII value.
+ @param Buffer Buffer to place ASCII version of the Number.
+ @param Length Length of Buffer.
+
+**/
+VOID
+CvtNum (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN UINTN Length
+ );
+
+
+/**
+ Convert unsigned int number to decimal number.
+
+ @param Number The unsigned int number will be converted.
+ @param Buffer Pointer to the buffer to store the decimal number after transform.
+ @param[in] BufferSize The maxsize of the buffer.
+
+ @return the length of the number after transform.
+
+**/
+UINTN
+UtoA10 (
+ IN UINTN Number,
+ IN CHAR8 *Buffer,
+ IN UINTN BufferSize
+
+ );
+
+
+/**
+ Convert ASCII numeric string to a UINTN value.
+
+ @param Buffer Pointer to the 8-byte unsigned int value.
+
+ @return UINTN value of the ASCII string.
+
+**/
+UINT64
+AtoU64 (
+ IN UINT8 *Buffer
+ );
+
+
+#endif
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxe.uni b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxe.uni
new file mode 100644
index 0000000000..1770b15b6f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxe.uni
@@ -0,0 +1,25 @@
+// /** @file
+// This module produces EFI Preboot Execution Environment (PXE) Base Code Protocol.
+//
+// This module produces EFI PXE Base Code Protocol upon EFI MMP Protocl and IPv4
+// network stack, used to control PXE-compatible devices. It produces EFI Load File
+// Protocol to provide one clean way to otain control from the boot manager if the
+// boot patch is from the remote device.
+//
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI Preboot Execution Environment (PXE) Base Code Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI PXE Base Code Protocol upon the EFI MMP Protocol and the IPv4 network stack, and is used to control PXE-compatible devices. It produces the EFI Load File Protocol to provide a clean method to obtain control from the boot manager if the boot patch is from the remote device."
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni
new file mode 100644
index 0000000000..eb59002e3a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// UefiPxe4BcDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI PXE Base Code DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
new file mode 100644
index 0000000000..fe8f210467
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
@@ -0,0 +1,93 @@
+## @file
+# This module produces EFI Preboot Execution Environment (PXE) Base Code Protocol.
+#
+# This module produces EFI PXE Base Code Protocol upon EFI MMP Protocl and IPv4
+# network stack, used to control PXE-compatible devices. It produces EFI Load File
+# Protocol to provide one clean way to otain control from the boot manager if the
+# boot patch is from the remote device.
+#
+# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = UefiPxe4BcDxe
+ MODULE_UNI_FILE = UefiPxe4BcDxe.uni
+ FILE_GUID = 3B1DEAB5-C75D-442e-9238-8E2FFB62B0BB
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = PxeBcDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+# DRIVER_BINDING = gPxeBcDriverBinding
+# COMPONENT_NAME = gPxeBcComponentName
+# COMPONENT_NAME2 = gPxeBcComponentName2
+#
+
+[Sources]
+ PxeBcMtftp.c
+ PxeBcSupport.h
+ PxeBcSupport.c
+ PxeBcDriver.c
+ PxeBcDhcp.h
+ ComponentName.c
+ PxeBcImpl.c
+ PxeBcImpl.h
+ PxeBcDhcp.c
+ PxeBcMtftp.h
+ PxeBcDriver.h
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ MemoryAllocationLib
+ DebugLib
+ NetLib
+ DpcLib
+ PcdLib
+
+[Protocols]
+ gEfiPxeBaseCodeCallbackProtocolGuid ## SOMETIMES_PRODUCES
+ gEfiPxeBaseCodeProtocolGuid ## BY_START
+ gEfiLoadFileProtocolGuid ## BY_START
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES
+ gEfiArpServiceBindingProtocolGuid ## TO_START
+ gEfiArpProtocolGuid ## TO_START
+ gEfiMtftp4ServiceBindingProtocolGuid ## TO_START
+ gEfiMtftp4ProtocolGuid ## TO_START
+ gEfiUdp4ServiceBindingProtocolGuid ## TO_START
+ gEfiUdp4ProtocolGuid ## TO_START
+ gEfiDhcp4ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp4ProtocolGuid ## TO_START
+ gEfiIp4ServiceBindingProtocolGuid ## TO_START
+ gEfiIp4ProtocolGuid ## TO_START
+ gEfiIp4Config2ProtocolGuid ## TO_START
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize ## SOMETIMES_CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ UefiPxe4BcDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/ComponentName.c b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/ComponentName.c
new file mode 100644
index 0000000000..a5a2f77ce2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/ComponentName.c
@@ -0,0 +1,171 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for VlanConfigDxe driver.
+
+Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "VlanConfigImpl.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gVlanConfigComponentName = {
+ VlanConfigComponentNameGetDriverName,
+ VlanConfigComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gVlanConfigComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) VlanConfigComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) VlanConfigComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mVlanConfigDriverNameTable[] = {
+ {
+ "eng;en",
+ L"VLAN Configuration Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+//
+// EFI Component Name Functions
+//
+
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language[in] 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 DriverName[out] 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
+VlanConfigComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mVlanConfigDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gVlanConfigComponentName)
+ );
+}
+
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param ControllerHandle[in] 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 ChildHandle[in] 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 Language[in] 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 ControllerName[out] 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 NULL.
+ @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
+VlanConfigComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfig.vfr b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfig.vfr
new file mode 100644
index 0000000000..da3fe015c2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfig.vfr
@@ -0,0 +1,79 @@
+///** @file
+// VLAN configuration formset.
+//
+// Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution. The full
+// text of the license may be found at<BR>
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+//**/
+
+#include "VlanConfigNvData.h"
+
+formset
+ guid = VLAN_CONFIG_FORM_SET_GUID,
+ title = STRING_TOKEN(STR_VLAN_FORM_SET_TITLE),
+ help = STRING_TOKEN(STR_VLAN_FORM_SET_TITLE_HELP),
+ classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID,
+
+ varstore VLAN_CONFIGURATION,
+ varid = VLAN_CONFIGURATION_VARSTORE_ID,
+ name = VlanNvData,
+ guid = VLAN_CONFIG_FORM_SET_GUID;
+
+ form formid = VLAN_HEAD_FORM_ID,
+ title = STRING_TOKEN(STR_VLAN_FORM_TITLE);
+
+ goto VLAN_CONFIGURATION_FORM_ID,
+ prompt = STRING_TOKEN (STR_GET_CURRENT_SETTING),
+ help = STRING_TOKEN (STR_GET_CURRENT_SETTING_HELP),
+ flags = INTERACTIVE,
+ key = VLAN_UPDATE_QUESTION_ID;
+
+ endform;
+
+ form formid = VLAN_CONFIGURATION_FORM_ID,
+ title = STRING_TOKEN(STR_VLAN_FORM_TITLE);
+
+ subtitle text = STRING_TOKEN(STR_VLAN_CREATE_VLAN);
+
+ numeric varid = VlanNvData.VlanId,
+ prompt = STRING_TOKEN(STR_VLAN_VID_PROMPT),
+ help = STRING_TOKEN(STR_VLAN_VID_HELP),
+ minimum = 0,
+ maximum = 4094,
+ endnumeric;
+
+ numeric varid = VlanNvData.Priority,
+ prompt = STRING_TOKEN(STR_VLAN_PRIORITY_PROMPT),
+ help = STRING_TOKEN(STR_VLAN_PRIORITY_HELP),
+ minimum = 0,
+ maximum = 7,
+ endnumeric;
+
+ text
+ help = STRING_TOKEN(STR_VLAN_ADD_VLAN_HELP),
+ text = STRING_TOKEN(STR_VLAN_ADD_VLAN_PROMPT),
+ flags = INTERACTIVE,
+ key = VLAN_ADD_QUESTION_ID;
+
+ subtitle text = STRING_TOKEN(STR_VLAN_NULL_STRING);
+ subtitle text = STRING_TOKEN(STR_VLAN_VLAN_LIST);
+
+ label LABEL_VLAN_LIST;
+ label LABEL_END;
+
+ text
+ help = STRING_TOKEN(STR_VLAN_REMOVE_VLAN_HELP),
+ text = STRING_TOKEN(STR_VLAN_REMOVE_VLAN_PROMPT),
+ flags = INTERACTIVE,
+ key = VLAN_REMOVE_QUESTION_ID;
+
+ endform;
+
+endformset;
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDriver.c b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDriver.c
new file mode 100644
index 0000000000..7dfea2e32a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDriver.c
@@ -0,0 +1,306 @@
+/** @file
+ The driver binding for VLAN configuration module.
+
+Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "VlanConfigImpl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gVlanConfigDriverBinding = {
+ VlanConfigDriverBindingSupported,
+ VlanConfigDriverBindingStart,
+ VlanConfigDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ The entry point for IP4 config driver which install the driver
+ binding and component name protocol on its image.
+
+ @param[in] ImageHandle The image handle of the driver.
+ @param[in] SystemTable The system table.
+
+ @retval EFI_SUCCES All the related protocols are installed on the driver.
+ @retval Others Failed to install protocols.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gVlanConfigDriverBinding,
+ ImageHandle,
+ &gVlanConfigComponentName,
+ &gVlanConfigComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @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_SUCCES 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
+VlanConfigDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **) &VlanConfig,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Close the VlanConfig protocol opened for supported test
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @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_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ VLAN_CONFIG_PRIVATE_DATA *PrivateData;
+
+ //
+ // Check for multiple start
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &PrivateData,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Open VlanConfig protocol by driver
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **) &VlanConfig,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get parent device path
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Create a private data for this network device
+ //
+ PrivateData = AllocateCopyPool (sizeof (VLAN_CONFIG_PRIVATE_DATA), &mVlanConfigPrivateDateTemplate);
+ if (PrivateData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ErrorExit;
+ }
+
+ PrivateData->ImageHandle = This->DriverBindingHandle;
+ PrivateData->ControllerHandle = ControllerHandle;
+ PrivateData->VlanConfig = VlanConfig;
+ PrivateData->ParentDevicePath = DevicePath;
+
+ //
+ // Install VLAN configuration form
+ //
+ Status = InstallVlanConfigForm (PrivateData);
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Install private GUID
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiCallerIdGuid,
+ PrivateData,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
+ return Status;
+
+ErrorExit:
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ if (PrivateData != NULL) {
+ UninstallVlanConfigForm (PrivateData);
+ FreePool (PrivateData);
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @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_SUCCES This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ VLAN_CONFIG_PRIVATE_DATA *PrivateData;
+
+ //
+ // Retrieve the PrivateData from ControllerHandle
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &PrivateData,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ASSERT (PrivateData->Signature == VLAN_CONFIG_PRIVATE_DATA_SIGNATURE);
+
+ if (NumberOfChildren != 0) {
+ if (NumberOfChildren != 1 || ChildHandleBuffer[0] != PrivateData->DriverHandle) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return UninstallVlanConfigForm (PrivateData);
+ }
+
+ //
+ // Uninstall the private GUID
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ PrivateData,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ return Status;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.inf b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.inf
new file mode 100644
index 0000000000..a36c57871e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.inf
@@ -0,0 +1,72 @@
+## @file
+# This module provides one way to configurate VALN setting.
+#
+# This module produces EFI HII Configuration Access Protocol to provide one way to
+# configurate VALN setting
+#
+# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VlanConfigDxe
+ MODULE_UNI_FILE = VlanConfigDxe.uni
+ FILE_GUID = E4F61863-FE2C-4b56-A8F4-08519BC439DF
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = VlanConfigDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ ComponentName.c
+ VlanConfigDriver.c
+ VlanConfigImpl.c
+ VlanConfigImpl.h
+ VlanConfig.vfr
+ VlanConfigStrings.uni
+ VlanConfigNvData.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ HiiLib
+
+[Guids]
+ gEfiIfrTianoGuid ## SOMETIMES_PRODUCES ## UNDEFINED
+ ## SOMETIMES_CONSUMES ## UNDEFINED # HiiIsConfigHdrMatch mVlanStorageName
+ ## SOMETIMES_PRODUCES ## UNDEFINED # HiiConstructConfigHdr mVlanStorageName
+ ## SOMETIMES_PRODUCES ## UNDEFINED # HiiGetBrowserData mVlanStorageName
+ ## SOMETIMES_CONSUMES ## UNDEFINED # HiiSetBrowserData mVlanStorageName
+ ## SOMETIMES_CONSUMES ## HII
+ gVlanConfigFormSetGuid
+
+[Protocols]
+ gEfiHiiConfigAccessProtocolGuid ## BY_START
+ gEfiHiiConfigRoutingProtocolGuid ## CONSUMES
+ gEfiVlanConfigProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ VlanConfigDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.uni b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.uni
new file mode 100644
index 0000000000..dabec1f1c5
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// This module provides one way to configurate VALN setting.
+//
+// This module produces EFI HII Configuration Access Protocol to provide one way to
+// configurate VALN setting
+//
+// Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides a way to configure VLAN settings"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI HII Configuration Access Protocol to provide a way to configure VLAN settings."
+
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni
new file mode 100644
index 0000000000..1c15b7cedb
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// VlanConfigDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"VLAN Configuration DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c
new file mode 100644
index 0000000000..f035706218
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c
@@ -0,0 +1,671 @@
+/** @file
+ HII Config Access protocol implementation of VLAN configuration module.
+
+Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "VlanConfigImpl.h"
+
+CHAR16 mVlanStorageName[] = L"VlanNvData";
+EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting = NULL;
+
+VLAN_CONFIG_PRIVATE_DATA mVlanConfigPrivateDateTemplate = {
+ VLAN_CONFIG_PRIVATE_DATA_SIGNATURE,
+ {
+ VlanExtractConfig,
+ VlanRouteConfig,
+ VlanCallback
+ }
+};
+
+VENDOR_DEVICE_PATH mHiiVendorDevicePathNode = {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ VLAN_CONFIG_FORM_SET_GUID
+};
+
+/**
+ This function allows a caller to extract the current configuration for one
+ or more named elements from the target driver.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Request A null-terminated Unicode string in
+ <ConfigRequest> format.
+ @param[out] Progress On return, points to a character in the Request
+ string. Points to the string's null terminator if
+ request was successful. Points to the most recent
+ '&' before the first failing name/value pair (or
+ the beginning of the string if the failure is in
+ the first name/value pair) if the request was not
+ successful.
+ @param[out] Results A null-terminated Unicode string in
+ <ConfigAltResp> format which has all values filled
+ in for the names in the Request string. String to
+ be allocated by the called function.
+
+ @retval EFI_SUCCESS The Results is filled with the requested values.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results.
+ @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name.
+ @retval EFI_NOT_FOUND Routing data doesn't match any storage in this
+ driver.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ VLAN_CONFIGURATION Configuration;
+ VLAN_CONFIG_PRIVATE_DATA *PrivateData;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ UINTN Size;
+
+ if (Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Request;
+ if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gVlanConfigFormSetGuid, mVlanStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ConfigRequestHdr = NULL;
+ ConfigRequest = NULL;
+ AllocatedRequest = FALSE;
+ Size = 0;
+
+ //
+ // Retrieve the pointer to the UEFI HII Config Routing Protocol
+ //
+ if (mHiiConfigRouting == NULL) {
+ gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **) &mHiiConfigRouting);
+ }
+ ASSERT (mHiiConfigRouting != NULL);
+
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ PrivateData = VLAN_CONFIG_PRIVATE_DATA_FROM_THIS (This);
+ ZeroMem (&Configuration, sizeof (VLAN_CONFIGURATION));
+ BufferSize = sizeof (Configuration);
+ ConfigRequest = Request;
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
+ //
+ // Request has no request element, construct full request string.
+ // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
+ // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
+ //
+ ConfigRequestHdr = HiiConstructConfigHdr (&gVlanConfigFormSetGuid, mVlanStorageName, PrivateData->DriverHandle);
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
+ ConfigRequest = AllocateZeroPool (Size);
+ ASSERT (ConfigRequest != NULL);
+ AllocatedRequest = TRUE;
+ UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
+ FreePool (ConfigRequestHdr);
+ }
+
+ Status = mHiiConfigRouting->BlockToConfig (
+ mHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) &Configuration,
+ BufferSize,
+ Results,
+ Progress
+ );
+ //
+ // Free the allocated config request string.
+ //
+ if (AllocatedRequest) {
+ FreePool (ConfigRequest);
+ ConfigRequest = NULL;
+ }
+ //
+ // Set Progress string to the original request string.
+ //
+ if (Request == NULL) {
+ *Progress = NULL;
+ } else if (StrStr (Request, L"OFFSET") == NULL) {
+ *Progress = Request + StrLen (Request);
+ }
+
+ return Status;
+}
+
+
+/**
+ This function processes the results of changes in configuration.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Configuration A null-terminated Unicode string in <ConfigResp>
+ format.
+ @param[out] Progress A pointer to a string filled in with the offset of
+ the most recent '&' before the first failing
+ name/value pair (or the beginning of the string if
+ the failure is in the first name/value pair) or
+ the terminating NULL if all was successful.
+
+ @retval EFI_SUCCESS The Results is processed successfully.
+ @retval EFI_INVALID_PARAMETER Configuration is NULL.
+ @retval EFI_NOT_FOUND Routing data doesn't match any storage in this
+ driver.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ if (Configuration == NULL || Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Configuration;
+ if (!HiiIsConfigHdrMatch (Configuration, &gVlanConfigFormSetGuid, mVlanStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ *Progress = Configuration + StrLen (Configuration);
+ return EFI_SUCCESS;
+}
+
+/**
+ This function processes the results of changes in configuration.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Action Specifies the type of action taken by the browser.
+ @param[in] QuestionId A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect.
+ @param[in] Type The type of value for the question.
+ @param[in] Value A pointer to the data being sent to the original
+ exporting driver.
+ @param[out] ActionRequest On return, points to the action requested by the
+ callback function.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
+ variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be saved.
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the
+ callback.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ VLAN_CONFIG_PRIVATE_DATA *PrivateData;
+ VLAN_CONFIGURATION *Configuration;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ UINTN Index;
+ EFI_HANDLE VlanHandle;
+
+ PrivateData = VLAN_CONFIG_PRIVATE_DATA_FROM_THIS (This);
+
+ if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)) {
+ return EFI_SUCCESS;
+ }
+
+ if ((Action != EFI_BROWSER_ACTION_CHANGED) && (Action != EFI_BROWSER_ACTION_CHANGING)) {
+ //
+ // All other action return unsupported.
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Get Browser data
+ //
+ Configuration = AllocateZeroPool (sizeof (VLAN_CONFIGURATION));
+ ASSERT (Configuration != NULL);
+ HiiGetBrowserData (&gVlanConfigFormSetGuid, mVlanStorageName, sizeof (VLAN_CONFIGURATION), (UINT8 *) Configuration);
+
+ VlanConfig = PrivateData->VlanConfig;
+
+ if (Action == EFI_BROWSER_ACTION_CHANGED) {
+ switch (QuestionId) {
+ case VLAN_ADD_QUESTION_ID:
+ //
+ // Add a VLAN
+ //
+ VlanConfig->Set (VlanConfig, Configuration->VlanId, Configuration->Priority);
+ VlanUpdateForm (PrivateData);
+
+ //
+ // Connect the newly created VLAN device
+ //
+ VlanHandle = NetLibGetVlanHandle (PrivateData->ControllerHandle, Configuration->VlanId);
+ if (VlanHandle == NULL) {
+ //
+ // There may be no child handle created for VLAN ID 0, connect the parent handle
+ //
+ VlanHandle = PrivateData->ControllerHandle;
+ }
+ gBS->ConnectController (VlanHandle, NULL, NULL, TRUE);
+
+ //
+ // Clear UI data
+ //
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
+ Configuration->VlanId = 0;
+ Configuration->Priority = 0;
+ break;
+
+ case VLAN_REMOVE_QUESTION_ID:
+ //
+ // Remove VLAN
+ //
+ ASSERT (PrivateData->NumberOfVlan <= MAX_VLAN_NUMBER);
+ for (Index = 0; Index < PrivateData->NumberOfVlan; Index++) {
+ if (Configuration->VlanList[Index] != 0) {
+ //
+ // Checkbox is selected, need remove this VLAN
+ //
+ VlanConfig->Remove (VlanConfig, PrivateData->VlanId[Index]);
+ }
+ }
+
+ VlanUpdateForm (PrivateData);
+ if (PrivateData->NumberOfVlan == 0) {
+ //
+ // No VLAN device now, connect the physical NIC handle.
+ // Note: PrivateData->NumberOfVlan has been updated by VlanUpdateForm()
+ //
+ gBS->ConnectController (PrivateData->ControllerHandle, NULL, NULL, TRUE);
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
+ ZeroMem (Configuration->VlanList, MAX_VLAN_NUMBER);
+ break;
+
+ default:
+ break;
+ }
+ } else if (Action == EFI_BROWSER_ACTION_CHANGING) {
+ switch (QuestionId) {
+ case VLAN_UPDATE_QUESTION_ID:
+ //
+ // Update current VLAN list into Form.
+ //
+ VlanUpdateForm (PrivateData);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ HiiSetBrowserData (&gVlanConfigFormSetGuid, mVlanStorageName, sizeof (VLAN_CONFIGURATION), (UINT8 *) Configuration, NULL);
+ FreePool (Configuration);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function update VLAN list in the VLAN configuration Form.
+
+ @param[in, out] PrivateData Points to VLAN configuration private data.
+
+**/
+VOID
+VlanUpdateForm (
+ IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData
+ )
+{
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ UINT16 NumberOfVlan;
+ UINTN Index;
+ EFI_VLAN_FIND_DATA *VlanData;
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ CHAR16 *String;
+ CHAR16 VlanStr[30];
+ CHAR16 VlanIdStr[6];
+ UINTN DigitalCount;
+ EFI_STRING_ID StringId;
+
+ //
+ // Find current VLAN configuration
+ //
+ VlanData = NULL;
+ NumberOfVlan = 0;
+ VlanConfig = PrivateData->VlanConfig;
+ VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);
+
+ //
+ // Update VLAN configuration in PrivateData
+ //
+ if (NumberOfVlan > MAX_VLAN_NUMBER) {
+ NumberOfVlan = MAX_VLAN_NUMBER;
+ }
+ PrivateData->NumberOfVlan = NumberOfVlan;
+
+ //
+ // Init OpCode Handle
+ //
+ StartOpCodeHandle = HiiAllocateOpCodeHandle ();
+ ASSERT (StartOpCodeHandle != NULL);
+
+ EndOpCodeHandle = HiiAllocateOpCodeHandle ();
+ ASSERT (EndOpCodeHandle != NULL);
+
+ //
+ // Create Hii Extend Label OpCode as the start opcode
+ //
+ StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ StartOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ StartLabel->Number = LABEL_VLAN_LIST;
+
+ //
+ // Create Hii Extend Label OpCode as the end opcode
+ //
+ EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ EndOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ EndLabel->Number = LABEL_END;
+
+ ZeroMem (PrivateData->VlanId, MAX_VLAN_NUMBER);
+ for (Index = 0; Index < NumberOfVlan; Index++) {
+ String = VlanStr;
+
+ StrCpyS (String, (sizeof (VlanStr) /sizeof (CHAR16)), L" VLAN ID:");
+ String += 10;
+ //
+ // Pad VlanId string up to 4 characters with space
+ //
+ UnicodeValueToStringS (VlanIdStr, sizeof (VlanIdStr), 0, VlanData[Index].VlanId, 5);
+ DigitalCount = StrnLenS (VlanIdStr, ARRAY_SIZE (VlanIdStr));
+ SetMem16 (String, (4 - DigitalCount) * sizeof (CHAR16), L' ');
+ StrCpyS (String + 4 - DigitalCount, (sizeof (VlanStr) /sizeof (CHAR16)) - 10 - (4 - DigitalCount), VlanIdStr);
+ String += 4;
+
+ StrCpyS (String, (sizeof (VlanStr) /sizeof (CHAR16)) - 10 - (4 - DigitalCount) - 4, L", Priority:");
+ String += 11;
+ UnicodeValueToStringS (
+ String,
+ sizeof (VlanStr) - ((UINTN)String - (UINTN)VlanStr),
+ 0,
+ VlanData[Index].Priority,
+ 4
+ );
+ String += StrnLenS (String, ARRAY_SIZE (VlanStr) - ((UINTN)String - (UINTN)VlanStr) / sizeof (CHAR16));
+ *String = 0;
+
+ StringId = HiiSetString (PrivateData->HiiHandle, 0, VlanStr, NULL);
+ ASSERT (StringId != 0);
+
+ HiiCreateCheckBoxOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (VLAN_LIST_VAR_OFFSET + Index),
+ VLAN_CONFIGURATION_VARSTORE_ID,
+ (UINT16) (VLAN_LIST_VAR_OFFSET + Index),
+ StringId,
+ STRING_TOKEN (STR_VLAN_VLAN_LIST_HELP),
+ 0,
+ 0,
+ NULL
+ );
+
+ //
+ // Save VLAN id to private data
+ //
+ PrivateData->VlanId[Index] = VlanData[Index].VlanId;
+ }
+
+ HiiUpdateForm (
+ PrivateData->HiiHandle, // HII handle
+ &gVlanConfigFormSetGuid, // Formset GUID
+ VLAN_CONFIGURATION_FORM_ID, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+
+ if (VlanData != NULL) {
+ FreePool (VlanData);
+ }
+}
+
+
+/**
+ This function publish the VLAN configuration Form for a network device. The
+ HII Config Access protocol will be installed on a child handle of the network
+ device.
+
+ @param[in, out] PrivateData Points to VLAN configuration private data.
+
+ @retval EFI_SUCCESS HII Form is installed for this network device.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource for HII Form installation.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+InstallVlanConfigForm (
+ IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData
+ )
+{
+ EFI_STATUS Status;
+ EFI_HII_HANDLE HiiHandle;
+ EFI_HANDLE DriverHandle;
+ CHAR16 Str[26 + sizeof (EFI_MAC_ADDRESS) * 2 + 1];
+ CHAR16 *MacString;
+ EFI_DEVICE_PATH_PROTOCOL *ChildDevicePath;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+
+ //
+ // Create child handle and install HII Config Access Protocol
+ //
+ ChildDevicePath = AppendDevicePathNode (
+ PrivateData->ParentDevicePath,
+ (CONST EFI_DEVICE_PATH_PROTOCOL *) &mHiiVendorDevicePathNode
+ );
+ if (ChildDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ PrivateData->ChildDevicePath = ChildDevicePath;
+
+ DriverHandle = NULL;
+ ConfigAccess = &PrivateData->ConfigAccess;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ ChildDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ ConfigAccess,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ PrivateData->DriverHandle = DriverHandle;
+
+ //
+ // Establish the parent-child relationship between the new created
+ // child handle and the ControllerHandle.
+ //
+ Status = gBS->OpenProtocol (
+ PrivateData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **)&VlanConfig,
+ PrivateData->ImageHandle,
+ PrivateData->DriverHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Publish the HII package list
+ //
+ HiiHandle = HiiAddPackages (
+ &gVlanConfigFormSetGuid,
+ DriverHandle,
+ VlanConfigDxeStrings,
+ VlanConfigBin,
+ NULL
+ );
+ if (HiiHandle == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ PrivateData->HiiHandle = HiiHandle;
+
+ //
+ // Update formset title help string.
+ //
+ MacString = NULL;
+ Status = NetLibGetMacString (PrivateData->ControllerHandle, PrivateData->ImageHandle, &MacString);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ PrivateData->MacString = MacString;
+
+ StrCpyS (Str, sizeof (Str) / sizeof (CHAR16), L"VLAN Configuration (MAC:");
+ StrCatS (Str, sizeof (Str) / sizeof (CHAR16), MacString);
+ StrCatS (Str, sizeof (Str) / sizeof (CHAR16), L")");
+ HiiSetString (
+ HiiHandle,
+ STRING_TOKEN (STR_VLAN_FORM_SET_TITLE_HELP),
+ Str,
+ NULL
+ );
+
+ //
+ // Update form title help string.
+ //
+ HiiSetString (
+ HiiHandle,
+ STRING_TOKEN (STR_VLAN_FORM_HELP),
+ Str,
+ NULL
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function remove the VLAN configuration Form for a network device. The
+ child handle for HII Config Access protocol will be destroyed.
+
+ @param[in, out] PrivateData Points to VLAN configuration private data.
+
+ @retval EFI_SUCCESS HII Form has been uninstalled successfully.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+UninstallVlanConfigForm (
+ IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData
+ )
+{
+ EFI_STATUS Status;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+
+ //
+ // End the parent-child relationship.
+ //
+ Status = gBS->CloseProtocol (
+ PrivateData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ PrivateData->ImageHandle,
+ PrivateData->DriverHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Uninstall HII Config Access Protocol
+ //
+ if (PrivateData->DriverHandle != NULL) {
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ PrivateData->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ PrivateData->ChildDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &PrivateData->ConfigAccess,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->OpenProtocol (
+ PrivateData->ControllerHandle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **)&VlanConfig,
+ PrivateData->ImageHandle,
+ PrivateData->DriverHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ return Status;
+ }
+ PrivateData->DriverHandle = NULL;
+
+ if (PrivateData->ChildDevicePath != NULL) {
+ FreePool (PrivateData->ChildDevicePath);
+ PrivateData->ChildDevicePath = NULL;
+ }
+ }
+
+ //
+ // Free MAC string
+ //
+ if (PrivateData->MacString != NULL) {
+ FreePool (PrivateData->MacString);
+ PrivateData->MacString = NULL;
+ }
+
+ //
+ // Uninstall HII package list
+ //
+ if (PrivateData->HiiHandle != NULL) {
+ HiiRemovePackages (PrivateData->HiiHandle);
+ PrivateData->HiiHandle = NULL;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.h b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.h
new file mode 100644
index 0000000000..2aa43b4230
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.h
@@ -0,0 +1,388 @@
+/** @file
+ Header file for driver binding protocol and HII config access protocol.
+
+Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __VLAN_CONFIG_IMPL_H__
+#define __VLAN_CONFIG_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/HiiConfigAccess.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Protocol/VlanConfig.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/HiiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+
+#include <Guid/MdeModuleHii.h>
+
+#include "VlanConfigNvData.h"
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gVlanConfigComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gVlanConfigComponentName;
+
+//
+// Tool generated IFR binary data and String package data
+//
+extern UINT8 VlanConfigBin[];
+extern UINT8 VlanConfigDxeStrings[];
+
+#define VLAN_LIST_VAR_OFFSET ((UINT16) OFFSET_OF (VLAN_CONFIGURATION, VlanList))
+
+typedef struct {
+ UINTN Signature;
+
+ EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
+ EFI_HII_HANDLE HiiHandle;
+ EFI_HANDLE DriverHandle;
+ EFI_DEVICE_PATH_PROTOCOL *ChildDevicePath;
+
+ EFI_HANDLE ControllerHandle;
+ EFI_HANDLE ImageHandle;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ CHAR16 *MacString;
+
+ UINT16 NumberOfVlan;
+ UINT16 VlanId[MAX_VLAN_NUMBER];
+} VLAN_CONFIG_PRIVATE_DATA;
+
+#define VLAN_CONFIG_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('V', 'C', 'P', 'D')
+#define VLAN_CONFIG_PRIVATE_DATA_FROM_THIS(a) CR (a, VLAN_CONFIG_PRIVATE_DATA, ConfigAccess, VLAN_CONFIG_PRIVATE_DATA_SIGNATURE)
+
+extern VLAN_CONFIG_PRIVATE_DATA mVlanConfigPrivateDateTemplate;
+
+
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language[in] 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 DriverName[out] 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
+VlanConfigComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param ControllerHandle[in] 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 ChildHandle[in] 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 Language[in] 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 ControllerName[out] 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 NULL.
+ @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
+VlanConfigComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @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_SUCCES 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
+VlanConfigDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle.
+
+ @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_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @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_SUCCES This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ This function update VLAN list in the VLAN configuration Form.
+
+ @param[in, out] PrivateData Points to VLAN configuration private data.
+
+**/
+VOID
+VlanUpdateForm (
+ IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData
+ );
+
+/**
+ This function publish the VLAN configuration Form for a network device. The
+ HII Config Access protocol will be installed on a child handle of the network
+ device.
+
+ @param[in, out] PrivateData Points to VLAN configuration private data.
+
+ @retval EFI_SUCCESS HII Form is installed for this network device.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource for HII Form installation.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+InstallVlanConfigForm (
+ IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData
+ );
+
+/**
+ This function remove the VLAN configuration Form for a network device. The
+ child handle for HII Config Access protocol will be destroyed.
+
+ @param[in, out] PrivateData Points to VLAN configuration private data.
+
+ @retval EFI_SUCCESS HII Form has been uninstalled successfully.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+UninstallVlanConfigForm (
+ IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData
+ );
+
+/**
+ This function allows a caller to extract the current configuration for one
+ or more named elements from the target driver.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Request A null-terminated Unicode string in
+ <ConfigRequest> format.
+ @param[out] Progress On return, points to a character in the Request
+ string. Points to the string's null terminator if
+ request was successful. Points to the most recent
+ '&' before the first failing name/value pair (or
+ the beginning of the string if the failure is in
+ the first name/value pair) if the request was not
+ successful.
+ @param[out] Results A null-terminated Unicode string in
+ <ConfigAltResp> format which has all values filled
+ in for the names in the Request string. String to
+ be allocated by the called function.
+
+ @retval EFI_SUCCESS The Results is filled with the requested values.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results.
+ @retval EFI_INVALID_PARAMETER Request is NULL, illegal syntax, or unknown name.
+ @retval EFI_NOT_FOUND Routing data doesn't match any storage in this
+ driver.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ );
+
+/**
+ This function processes the results of changes in configuration.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Configuration A null-terminated Unicode string in <ConfigResp>
+ format.
+ @param[out] Progress A pointer to a string filled in with the offset of
+ the most recent '&' before the first failing
+ name/value pair (or the beginning of the string if
+ the failure is in the first name/value pair) or
+ the terminating NULL if all was successful.
+
+ @retval EFI_SUCCESS The Results is processed successfully.
+ @retval EFI_INVALID_PARAMETER Configuration is NULL.
+ @retval EFI_NOT_FOUND Routing data doesn't match any storage in this
+ driver.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ );
+
+/**
+ This function processes the results of changes in configuration.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Action Specifies the type of action taken by the browser.
+ @param[in] QuestionId A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect.
+ @param[in] Type The type of value for the question.
+ @param[in] Value A pointer to the data being sent to the original
+ exporting driver.
+ @param[out] ActionRequest On return, points to the action requested by the
+ callback function.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
+ variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be saved.
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the
+ callback.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigNvData.h b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigNvData.h
new file mode 100644
index 0000000000..7dd2fc605b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigNvData.h
@@ -0,0 +1,47 @@
+/** @file
+ Header file for NV data structure definition.
+
+Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The full
+text of the license may be found at<BR>
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __VLAN_CONFIG_NV_DATA_H__
+#define __VLAN_CONFIG_NV_DATA_H__
+
+#include <Guid/HiiPlatformSetupFormset.h>
+#include <Guid/VlanConfigHii.h>
+
+#define VLAN_CONFIGURATION_VARSTORE_ID 0x0001
+#define VLAN_CONFIGURATION_FORM_ID 0x0001
+#define VLAN_HEAD_FORM_ID 0x0002
+
+#define VLAN_ADD_QUESTION_ID 0x1000
+#define VLAN_REMOVE_QUESTION_ID 0x2000
+#define VLAN_UPDATE_QUESTION_ID 0x3000
+
+#define LABEL_VLAN_LIST 0x0001
+#define LABEL_END 0xffff
+
+//
+// The maximum number of VLAN that will be displayed on the menu
+//
+#define MAX_VLAN_NUMBER 100
+
+//
+// Nv Data structure referenced by IFR
+//
+typedef struct {
+ UINT16 VlanId;
+ UINT8 Priority;
+ UINT8 VlanList[MAX_VLAN_NUMBER];
+} VLAN_CONFIGURATION;
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigStrings.uni b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigStrings.uni
new file mode 100644
index 0000000000..d7a1769ff4
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigStrings.uni
@@ -0,0 +1,38 @@
+///** @file
+// String definitions for VLAN Configuration Form.
+//
+// Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution. The full
+// text of the license may be found at<BR>
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+//**/
+
+/=#
+
+#langdef en-US "English"
+
+#string STR_VLAN_FORM_SET_TITLE #language en-US "VLAN Configuration"
+#string STR_VLAN_FORM_SET_TITLE_HELP #language en-US "VLAN configuration for this network device"
+#string STR_VLAN_FORM_TITLE #language en-US "VLAN Configuration"
+#string STR_VLAN_FORM_HELP #language en-US "VLAN Configuration Help"
+#string STR_VLAN_CREATE_VLAN #language en-US "Create new VLAN"
+
+#string STR_VLAN_VID_PROMPT #language en-US " VLAN ID"
+#string STR_VLAN_VID_HELP #language en-US "VLAN ID of new VLAN or existing VLAN, valid value is 0~4094"
+#string STR_VLAN_PRIORITY_PROMPT #language en-US " Priority"
+#string STR_VLAN_PRIORITY_HELP #language en-US "802.1Q Priority, valid value is 0~7"
+#string STR_VLAN_ADD_VLAN_PROMPT #language en-US "Add VLAN"
+#string STR_VLAN_ADD_VLAN_HELP #language en-US "Create a new VLAN or update existing VLAN"
+#string STR_VLAN_VLAN_LIST #language en-US "Configured VLAN List"
+#string STR_VLAN_VLAN_LIST_HELP #language en-US "Select for remove"
+#string STR_VLAN_REMOVE_VLAN_PROMPT #language en-US "Remove VLAN"
+#string STR_VLAN_REMOVE_VLAN_HELP #language en-US "Remove selected VLANs"
+#string STR_VLAN_NULL_STRING #language en-US ""
+#string STR_GET_CURRENT_SETTING #language en-US "Enter Configuration Menu"
+#string STR_GET_CURRENT_SETTING_HELP #language en-US "Press ENTER to enter configuration menu for VLAN configuration." \ No newline at end of file