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.unibin0 -> 2288 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.unibin0 -> 1322 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c1660
-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.unibin0 -> 2510 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.unibin0 -> 1334 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c1781
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h199
-rw-r--r--Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c1690
-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.h340
-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.unibin0 -> 1776 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.unibin0 -> 1364 bytes
-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.unibin0 -> 2696 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.unibin0 -> 1334 bytes
-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.c1258
-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.unibin0 -> 7248 bytes
-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.h61
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.c669
-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.c534
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.h38
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiImpl.h167
-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.c931
-rw-r--r--Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.h314
-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.c269
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h204
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2.vfr100
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c2031
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h270
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.c1436
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.h51
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c989
-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.unibin0 -> 2842 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.unibin0 -> 1326 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.unibin0 -> 3976 bytes
-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.c1263
-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.c2432
-rw-r--r--Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h403
-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.unibin0 -> 2636 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.unibin0 -> 1320 bytes
-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.c683
-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.unibin0 -> 2290 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.unibin0 -> 1338 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c1107
-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.c266
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Initialize.c251
-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.c864
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.h1007
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf83
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.unibin0 -> 2100 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.unibin0 -> 1322 bytes
-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.c354
-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.c1282
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h130
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c959
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h986
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c727
-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.unibin0 -> 2228 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.unibin0 -> 1330 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h781
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c1468
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c112
-rw-r--r--Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c673
-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.unibin0 -> 2144 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.unibin0 -> 1330 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c1898
-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.c1938
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.h540
-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.c2959
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.h187
-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.c198
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.h116
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxe.unibin0 -> 2834 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.unibin0 -> 1366 bytes
-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.unibin0 -> 1988 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.unibin0 -> 1366 bytes
-rw-r--r--Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c663
-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.unibin0 -> 4240 bytes
192 files changed, 84587 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..81ddd62634
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c
@@ -0,0 +1,817 @@
+/** @file
+ ARP driver functions.
+
+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"
+#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 availabe 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..24b466fdbc
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h
@@ -0,0 +1,340 @@
+/** @file
+ ARP driver header file.
+
+Copyright (c) 2006 - 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 _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 availabe 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..38148cba45
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni
new file mode 100644
index 0000000000..de8045a6d8
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpDxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c
new file mode 100644
index 0000000000..633c3e7d38
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c
@@ -0,0 +1,1660 @@
+/** @file
+ The implementation of the ARP protocol.
+
+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 "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;
+ Head = (ARP_HEAD *) RxData->PacketData;
+
+ //
+ // Convert the byte order of the multi-byte fields.
+ //
+ Head->HwType = NTOHS (Head->HwType);
+ Head->ProtoType = NTOHS (Head->ProtoType);
+ Head->OpCode = NTOHS (Head->OpCode);
+
+ 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 (!NetIp4IsUnicast (NTOHL (Ip), 0)) {
+ //
+ // The station address is not a valid IPv4 unicast 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..9253df2b3f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c
@@ -0,0 +1,738 @@
+/** @file
+
+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
+
+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 availabe 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..b4a63ef173
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h
@@ -0,0 +1,152 @@
+/** @file
+ Header for the DHCP4 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
+
+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 availabe 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..43f1ab374e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni
new file mode 100644
index 0000000000..f06b0d4803
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4DxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
new file mode 100644
index 0000000000..4f491b4bba
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
@@ -0,0 +1,1781 @@
+/** @file
+ This file implement the EFI_DHCP4_PROTOCOL interface.
+
+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
+
+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 ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {
+
+ 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;
+ 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;
+ 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));
+
+ Class = NetGetIpClass (ClientAddr);
+ ASSERT (Class < IP4_ADDR_CLASSE);
+ SubnetMask = gIp4AllMasks[Class << 3];
+ 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;
+ INTN Class;
+ IP4_ADDR SubnetMask;
+
+ 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.
+ //
+ Class = NetGetIpClass (ClientAddr);
+ ASSERT (Class < IP4_ADDR_CLASSE);
+ SubnetMask = gIp4AllMasks[Class << 3];
+ ZeroMem (&Gateway, sizeof (Gateway));
+ if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], SubnetMask)) {
+ 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..44213cf40a
--- /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 - 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_DHCP4_IMPL_H__
+#define __EFI_DHCP4_IMPL_H__
+
+
+
+#include <Uefi.h>
+
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Udp4.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..4bc991557a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
@@ -0,0 +1,1690 @@
+/** @file
+ EFI DHCP 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.
+
+**/
+
+
+#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
+ )
+{
+ INTN Class;
+
+ 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) {
+ Class = NetGetIpClass (DhcpSb->ClientAddr);
+ ASSERT (Class < IP4_ADDR_CLASSE);
+ DhcpSb->Netmask = gIp4AllMasks[Class << 3];
+ }
+
+ 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, DHCP_TAG_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, DHCP_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, DHCP_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, DHCP_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, DHCP_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 != DHCP_TAG_CLIENT_ID)) {
+ continue;
+ }
+
+ Buf = DhcpAppendOption (
+ Buf,
+ Config->OptionList[Index]->OpCode,
+ Config->OptionList[Index]->Length,
+ Config->OptionList[Index]->Data
+ );
+ }
+ }
+
+ *(Buf++) = DHCP_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..d7700bccc4
--- /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 - 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 "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[] = {
+ {DHCP_TAG_NETMASK, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_ROUTER, DHCP_OPTION_IP, 1, -1 , TRUE},
+ {DHCP_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_DUMP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+ {DHCP_TAG_EMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16, 1, -1 , FALSE},
+
+ {DHCP_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+
+ {DHCP_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_NBNS, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NBDD, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_XFONT, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_XDM, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_LEASE, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP_TAG_TYPE, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_T1, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP_TAG_T2, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP_TAG_VENDOR_CLASS, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1 , FALSE},
+
+ {DHCP_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP_TAG_TFTP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1 , FALSE},
+ {DHCP_TAG_SMTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_POP3, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NNTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_WWW, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_FINGER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_IRC, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_STTALK, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_STDA, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP_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 DHCP_TAG_NETMASK:
+ Para->NetMask = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_ROUTER:
+ //
+ // Return the first router to consumer which is the preferred one
+ //
+ Para->Router = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_LEASE:
+ Para->Lease = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_OVERLOAD:
+ Para->Overload = *Data;
+
+ if ((Para->Overload < 1) || (Para->Overload > 3)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP_TAG_TYPE:
+ Para->DhcpType = *Data;
+
+ if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP_TAG_SERVER_ID:
+ Para->ServerId = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_T1:
+ Para->T1 = NetGetUint32 (Data);
+ break;
+
+ case DHCP_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 DHCP_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 DHCP_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 == DHCP_TAG_PAD) {
+ Cur++;
+ continue;
+ } else if (Tag == DHCP_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 == DHCP_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++) = DHCP_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..f532780970
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h
@@ -0,0 +1,340 @@
+/** @file
+ To validate, parse and process the DHCP 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
+
+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)
+///
+
+//
+// RFC1497 vendor extensions
+//
+#define DHCP_TAG_PAD 0 // Pad Option
+#define DHCP_TAG_EOP 255 // End Option
+#define DHCP_TAG_NETMASK 1 // Subnet Mask
+#define DHCP_TAG_TIME_OFFSET 2 // Time Offset from UTC
+#define DHCP_TAG_ROUTER 3 // Router option,
+#define DHCP_TAG_TIME_SERVER 4 // Time Server
+#define DHCP_TAG_NAME_SERVER 5 // Name Server
+#define DHCP_TAG_DNS_SERVER 6 // Domain Name Server
+#define DHCP_TAG_LOG_SERVER 7 // Log Server
+#define DHCP_TAG_COOKIE_SERVER 8 // Cookie Server
+#define DHCP_TAG_LPR_SERVER 9 // LPR Print Server
+#define DHCP_TAG_IMPRESS_SERVER 10 // Impress Server
+#define DHCP_TAG_RL_SERVER 11 // Resource Location Server
+#define DHCP_TAG_HOSTNAME 12 // Host Name
+#define DHCP_TAG_BOOTFILE_LEN 13 // Boot File Size
+#define DHCP_TAG_DUMP 14 // Merit Dump File
+#define DHCP_TAG_DOMAINNAME 15 // Domain Name
+#define DHCP_TAG_SWAP_SERVER 16 // Swap Server
+#define DHCP_TAG_ROOTPATH 17 // Root path
+#define DHCP_TAG_EXTEND_PATH 18 // Extensions Path
+
+//
+// IP Layer Parameters per Host
+//
+#define DHCP_TAG_IPFORWARD 19 // IP Forwarding Enable/Disable
+#define DHCP_TAG_NONLOCAL_SRR 20 // on-Local Source Routing Enable/Disable
+#define DHCP_TAG_POLICY_SRR 21 // Policy Filter
+#define DHCP_TAG_EMTU 22 // Maximum Datagram Reassembly Size
+#define DHCP_TAG_TTL 23 // Default IP Time-to-live
+#define DHCP_TAG_PATHMTU_AGE 24 // Path MTU Aging Timeout
+#define DHCP_TAG_PATHMTU_PLATEAU 25 // Path MTU Plateau Table
+
+//
+// IP Layer Parameters per Interface
+//
+#define DHCP_TAG_IFMTU 26 // Interface MTU
+#define DHCP_TAG_SUBNET_LOCAL 27 // All Subnets are Local
+#define DHCP_TAG_BROADCAST 28 // Broadcast Address
+#define DHCP_TAG_DISCOVER_MASK 29 // Perform Mask Discovery
+#define DHCP_TAG_SUPPLY_MASK 30 // Mask Supplier
+#define DHCP_TAG_DISCOVER_ROUTE 31 // Perform Router Discovery
+#define DHCP_TAG_ROUTER_SOLICIT 32 // Router Solicitation Address
+#define DHCP_TAG_STATIC_ROUTE 33 // Static Route
+
+//
+// Link Layer Parameters per Interface
+//
+#define DHCP_TAG_TRAILER 34 // Trailer Encapsulation
+#define DHCP_TAG_ARPAGE 35 // ARP Cache Timeout
+#define DHCP_TAG_ETHER_ENCAP 36 // Ethernet Encapsulation
+
+//
+// TCP Parameters
+//
+#define DHCP_TAG_TCP_TTL 37 // TCP Default TTL
+#define DHCP_TAG_KEEP_INTERVAL 38 // TCP Keepalive Interval
+#define DHCP_TAG_KEEP_GARBAGE 39 // TCP Keepalive Garbage
+
+//
+// Application and Service Parameters
+//
+#define DHCP_TAG_NIS_DOMAIN 40 // Network Information Service Domain
+#define DHCP_TAG_NIS_SERVER 41 // Network Information Servers
+#define DHCP_TAG_NTP_SERVER 42 // Network Time Protocol Servers
+#define DHCP_TAG_VENDOR 43 // Vendor Specific Information
+#define DHCP_TAG_NBNS 44 // NetBIOS over TCP/IP Name Server
+#define DHCP_TAG_NBDD 45 // NetBIOS Datagram Distribution Server
+#define DHCP_TAG_NBTYPE 46 // NetBIOS over TCP/IP Node Type
+#define DHCP_TAG_NBSCOPE 47 // NetBIOS over TCP/IP Scope
+#define DHCP_TAG_XFONT 48 // X Window System Font Server
+#define DHCP_TAG_XDM 49 // X Window System Display Manager
+#define DHCP_TAG_NISPLUS 64 // Network Information Service+ Domain
+#define DHCP_TAG_NISPLUS_SERVER 65 // Network Information Service+ Servers
+#define DHCP_TAG_MOBILEIP 68 // Mobile IP Home Agent
+#define DHCP_TAG_SMTP 69 // Simple Mail Transport Protocol Server
+#define DHCP_TAG_POP3 70 // Post Office Protocol (POP3) Server
+#define DHCP_TAG_NNTP 71 // Network News Transport Protocol Server
+#define DHCP_TAG_WWW 72 // Default World Wide Web (WWW) Server
+#define DHCP_TAG_FINGER 73 // Default Finger Server
+#define DHCP_TAG_IRC 74 // Default Internet Relay Chat (IRC) Server
+#define DHCP_TAG_STTALK 75 // StreetTalk Server
+#define DHCP_TAG_STDA 76 // StreetTalk Directory Assistance Server
+#define DHCP_TAG_CLASSLESS_ROUTE 121 // Classless Route
+
+//
+// DHCP Extensions
+//
+#define DHCP_TAG_REQUEST_IP 50 // Requested IP Address
+#define DHCP_TAG_LEASE 51 // IP Address Lease Time
+#define DHCP_TAG_OVERLOAD 52 // Option Overload
+#define DHCP_TAG_TFTP 66 // TFTP server name
+#define DHCP_TAG_BOOTFILE 67 // Bootfile name
+#define DHCP_TAG_TYPE 53 // DHCP Message Type
+#define DHCP_TAG_SERVER_ID 54 // Server Identifier
+#define DHCP_TAG_PARA_LIST 55 // Parameter Request List
+#define DHCP_TAG_MESSAGE 56 // Message
+#define DHCP_TAG_MAXMSG 57 // Maximum DHCP Message Size
+#define DHCP_TAG_T1 58 // Renewal (T1) Time Value
+#define DHCP_TAG_T2 59 // Rebinding (T2) Time Value
+#define DHCP_TAG_VENDOR_CLASS 60 // Vendor class identifier
+#define DHCP_TAG_CLIENT_ID 61 // Client-identifier
+
+
+#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; // DHCP_TAG_NETMASK
+ IP4_ADDR Router; // DHCP_TAG_ROUTER, only the first router is used
+
+ //
+ // DHCP specific options
+ //
+ UINT8 DhcpType; // DHCP_TAG_TYPE
+ UINT8 Overload; // DHCP_TAG_OVERLOAD
+ IP4_ADDR ServerId; // DHCP_TAG_SERVER_ID
+ UINT32 Lease; // DHCP_TAG_LEASE
+ UINT32 T1; // DHCP_TAG_T1
+ UINT32 T2; // DHCP_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..82e3e7985c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni
new file mode 100644
index 0000000000..ca47539550
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/DpcDxe/DpcDxeExtra.uni
Binary files differ
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..21266b08c5
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4Dxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni
new file mode 100644
index 0000000000..0753072161
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsi4DxeExtra.uni
Binary files differ
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..7b77fd386b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfig.c
@@ -0,0 +1,1258 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to 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"
+
+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);
+ //
+ // Retrive 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) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {
+ 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) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) {
+ 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) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {
+ 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 (!NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 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);
+ }
+ //
+ // 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..140f2425a2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiConfigDxeStrings.uni
Binary files differ
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..d574ce21dd
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c
@@ -0,0 +1,472 @@
+/** @file
+ iSCSI DHCP related configuration routines.
+
+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"
+
+/**
+ 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_ROOT_PATH) {
+ 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) {
+
+ 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;
+ Data[3] = DHCP4_TAG_ROOT_PATH;
+
+ 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..77754b896a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.h
@@ -0,0 +1,61 @@
+/** @file
+ The header file of IScsiDhcp.
+
+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.
+
+**/
+
+#ifndef _ISCSI_DHCP_H_
+#define _ISCSI_DHCP_H_
+
+#include <Protocol/Dhcp4.h>
+
+#define DHCP4_TAG_PARA_LIST 55
+#define DHCP4_TAG_NETMASK 1
+#define DHCP4_TAG_ROUTER 3
+#define DHCP4_TAG_DNS 6
+#define DHCP4_TAG_SERVER_ID 54
+#define DHCP4_TAG_ROOT_PATH 17
+#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..e55bee8af9
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiDriver.c
@@ -0,0 +1,669 @@
+/** @file
+ The entry point of IScsi 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"
+
+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.
+**/
+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);
+ IScsiCleanDriverData (Private);
+
+ 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..372efdc15e
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c
@@ -0,0 +1,534 @@
+/** @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 accroding 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;
+ UINTN Index;
+ EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
+ EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
+
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **)&AcpiTableProtocol);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+
+ //
+ // Find ACPI table RSD_PTR from system table
+ //
+ for (Index = 0, Rsdp = NULL; Index < gST->NumberOfTableEntries; Index++) {
+ if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid) ||
+ CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid) ||
+ CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpiTableGuid)
+ ) {
+ //
+ // A match was found.
+ //
+ Rsdp = (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *) gST->ConfigurationTable[Index].VendorTable;
+ break;
+ }
+ }
+
+ if (Rsdp == NULL) {
+ return ;
+ } else {
+ Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress;
+ }
+
+
+ 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.
+ //
+ 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..8c761f882c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiImpl.h
@@ -0,0 +1,167 @@
+/** @file
+ The header file of IScsiImpl.c.
+
+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.
+
+**/
+
+#ifndef _ISCSI_IMPL_H_
+#define _ISCSI_IMPL_H_
+
+#include <Uefi.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..ebd9f5b40d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.c
@@ -0,0 +1,931 @@
+/** @file
+ Miscellaneous routines for iSCSI 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"
+
+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.
+**/
+VOID
+IScsiCleanDriverData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ if (Private->DevicePath != NULL) {
+ gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ Private->DevicePath
+ );
+
+ FreePool (Private->DevicePath);
+ }
+
+ if (Private->ExtScsiPassThruHandle != NULL) {
+ gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ }
+
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+
+ FreePool (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
+ )
+{
+ 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..8a95493dcd
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/IScsiDxe/IScsiMisc.h
@@ -0,0 +1,314 @@
+/** @file
+ Miscellaneous definitions for iSCSI 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.
+
+**/
+
+#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.
+**/
+VOID
+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..1ac79e7ca1
--- /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 assocated 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..004a8bc703
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c
@@ -0,0 +1,269 @@
+/** @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
+
+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;
+}
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
new file mode 100644
index 0000000000..d38857c677
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
@@ -0,0 +1,204 @@
+/** @file
+ Common definition for IP4.
+
+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
+
+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
+ );
+
+#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..edbddba021
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.c
@@ -0,0 +1,2031 @@
+/** @file
+ The implementation of EFI IPv4 Configuration II Protocol.
+
+ Copyright (c) 2015, 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.
+
+**/
+
+#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_UNSTARTED;
+ }
+
+ //
+ // Start the dhcp configuration.
+ //
+ if (NewPolicy == Ip4Config2PolicyDhcp) {
+ IpSb->Reconfig = TRUE;
+ 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_NUM - 1; 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);
+
+ 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;
+ UINTN Index1;
+ 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 (!NetIp4IsUnicast (NTOHL (DnsAddress), 0)) {
+ //
+ // The dns server address must be unicast.
+ //
+ FreePool (Tmp);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {
+ if (EFI_IP4_EQUAL (NewDns + NewIndex, NewDns + Index1)) {
+ 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 == DHCP_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 successfull 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 = DHCP_TAG_PARA_LIST;
+ ParaList.Head.Length = 3;
+ ParaList.Head.Data[0] = DHCP_TAG_NETMASK;
+ ParaList.Route = DHCP_TAG_ROUTER;
+ ParaList.Dns = DHCP_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) {
+ return EFI_ABORTED;
+ } else {
+ if (NewPolicy == Ip4Config2PolicyDhcp) {
+ //
+ // The policy is changed from static to dhcp:
+ // 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);
+ } 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);
+ }
+ }
+ }
+
+ 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);
+
+ //
+ // 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;
+
+ StationAddress = EFI_NTOHL (NewAddress.Address);
+ SubnetMask = EFI_NTOHL (NewAddress.SubnetMask);
+
+ IpSb->Reconfig = TRUE;
+ Status = Ip4Config2SetDefaultAddr (IpSb, StationAddress, SubnetMask);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ DataItem->Status = EFI_SUCCESS;
+
+ON_EXIT:
+ if (EFI_ERROR (DataItem->Status)) {
+ if (Ptr != NULL) {
+ FreePool (Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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;
+ }
+
+
+ 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 (!NetIp4IsUnicast (NTOHL (Gateway), 0)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) {
+ if (EFI_IP4_EQUAL (NewGateway + Index1, NewGateway + Index2)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance);
+ 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
+ )
+{
+ if (Instance->Policy != Ip4Config2PolicyStatic) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ 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
+ )
+{
+ IfInfo->Name[0] = L'e';
+ IfInfo->Name[1] = L't';
+ IfInfo->Name[2] = L'h';
+ IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip4Config2Instance.IfIndex);
+ IfInfo->Name[4] = 0;
+
+ 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 = Ip4Config2PolicyDhcp;
+ 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.
+ //
+ Status = Ip4Config2ReadConfigData (IpSb->MacString, Instance);
+ if (Status == EFI_NOT_FOUND) {
+ Status = Ip4Config2WriteConfigData (IpSb->MacString, Instance);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Try to set the configured parameter.
+ //
+ for (Index = Ip4Config2DataTypePolicy; Index < Ip4Config2DataTypeMaximum; Index++) {
+ DataItem = &IpSb->Ip4Config2Instance.DataItem[Index];
+ if (DataItem->Data.Ptr != NULL) {
+ DataItem->SetData (
+ &IpSb->Ip4Config2Instance,
+ DataItem->DataSize,
+ DataItem->Data.Ptr
+ );
+ }
+ }
+
+ 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);
+ }
+
+ 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);
+}
+
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
new file mode 100644
index 0000000000..ab72525646
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Impl.h
@@ -0,0 +1,270 @@
+/** @file
+ Definitions for EFI IPv4 Configuration II Protocol implementation.
+
+ Copyright (c) 2015, 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 DHCP_TAG_PARA_LIST 55
+#define DHCP_TAG_NETMASK 1
+#define DHCP_TAG_ROUTER 3
+#define DHCP_TAG_DNS_SERVER 6
+
+#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits))
+#define SET_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 DHCP_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()
+
+/**
+ 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 successfull 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
+ );
+
+/**
+ 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..381dcb1e97
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Config2Nv.c
@@ -0,0 +1,1436 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to Ip4.
+
+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 "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->StationAddress, &StationAddress.v4);
+ if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (StationAddress.Addr[0]), 0)) {
+ CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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->GatewayAddress, &Gateway.v4);
+ if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 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 (!NetIp4IsUnicast (NTOHL (Ip), 0)) {
+ 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;
+ }
+
+ //
+ // Retrive 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) || !NetIp4IsUnicast (NTOHL (StationAddress.Addr[0]), 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) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 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 (!NetIp4IsUnicast (NTOHL (Ip), 0)) {
+ 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..3dc4d88f13
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c
@@ -0,0 +1,989 @@
+/** @file
+ The driver binding and service binding protocol for IP4 driver.
+
+Copyright (c) 2005 - 2015, 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 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 = 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;
+
+ 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->Timer != NULL) {
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (IpSb->Timer);
+
+ IpSb->Timer = 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;
+
+ //
+ // 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);
+
+ //
+ // Install the Ip4ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding,
+ &gEfiIp4Config2ProtocolGuid,
+ &IpSb->Ip4Config2Instance.Ip4Config2,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_SERVICE;
+ }
+
+ //
+ // 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;
+ IpSb->State = IP4_SERVICE_DESTROY;
+
+ //
+ // 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 availabe 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;
+ INTN State;
+
+ 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->State == IP4_STATE_DESTROY) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ State = IpInstance->State;
+ IpInstance->State = IP4_STATE_DESTROY;
+
+ //
+ // 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)) {
+ 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:
+ IpInstance->State = State;
+ 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..32984bad66
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h
@@ -0,0 +1,190 @@
+/** @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_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 ptototypes for the ServiceBinding Prococol
+//
+/**
+ 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 availabe 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..f561af0c75
--- /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 - 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 = 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..398f730582
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni
new file mode 100644
index 0000000000..7923ef1c7f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni
new file mode 100644
index 0000000000..c940cf688c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4DxeStrings.uni
Binary files differ
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..669e041c10
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c
@@ -0,0 +1,1263 @@
+/** @file
+ Implement IP4 pesudo interface.
+
+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"
+
+//
+// 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;
+ INTN Type;
+ INTN Len;
+ IP4_ADDR Netmask;
+
+ 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);
+
+ Type = NetGetIpClass (IpAddr);
+ ASSERT (Type <= IP4_ADDR_CLASSC);
+ Len = NetGetMaskLength (SubnetMask);
+ ASSERT (Len < IP4_MASK_NUM);
+ Netmask = gIp4AllMasks[MIN (Len, Type << 3)];
+ Interface->NetBrdcast = (IpAddr | ~Netmask);
+
+ //
+ // 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..6dada9605b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h
@@ -0,0 +1,343 @@
+/** @file
+ Definition for IP4 pesudo interface structure.
+
+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_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 fileds
+ // 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..b9a294b199
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c
@@ -0,0 +1,2432 @@
+/** @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"
+
+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->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);
+}
+
+
+/**
+ 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);
+}
+
+
+/**
+ 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. If the default configuration hasn't
+ // been started, start it.
+ //
+ if (IpSb->State == IP4_SERVICE_UNSTARTED) {
+ //
+ // Create the ReconfigEvent to start the new configuration.
+ //
+ if (IpSb->ReconfigEvent == NULL) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip4AutoReconfigCallBack,
+ IpSb,
+ &IpSb->ReconfigEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ Status = Ip4StartAutoConfig (&IpSb->Ip4Config2Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto CLOSE_RECONFIG_EVENT;
+ }
+ }
+
+ 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 CLOSE_RECONFIG_EVENT;
+ }
+ }
+ 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;
+
+CLOSE_RECONFIG_EVENT:
+ if (IpSb->ReconfigEvent != NULL) {
+ gBS->CloseEvent (IpSb->ReconfigEvent);
+ IpSb->ReconfigEvent = NULL;
+ }
+
+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;
+}
+
+
+/**
+ 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_NUM) {
+ 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;
+}
+
+
+/**
+ 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);
+
+ //
+ // Don't change the state if it is DESTROY, 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) {
+ //
+ // 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..e802b9516b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h
@@ -0,0 +1,403 @@
+/** @file
+ Ip4 internal functions and type defintions.
+
+Copyright (c) 2005 - 2015, 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 <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. If (partly) destroyed, it
+// becomes DESTROY.
+//
+#define IP4_STATE_UNCONFIGED 0
+#define IP4_STATE_CONFIGED 1
+#define IP4_STATE_DESTROY 2
+
+//
+// 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;
+
+ 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..ea0023ddcb
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c
@@ -0,0 +1,661 @@
+/** @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
+
+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_NUM; 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_NUM; 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_NUM - 1; 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_NUM - 1; 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..2ddcec8962
--- /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 - 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 "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 availabe 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..126d968200
--- /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 availabe 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..a4f5eef7cf
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni
new file mode 100644
index 0000000000..ab0cf08400
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpDxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h
new file mode 100644
index 0000000000..c66be6487b
--- /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 64
+#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..1cbfc30e5c
--- /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 availabe 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..6e14c1f37f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/MnpDxe/MnpVlan.c
@@ -0,0 +1,683 @@
+/** @file
+ VLAN Config Protocol implementation and VLAN packet process routine.
+
+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 "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);
+}
+
+
+/**
+ 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;
+
+ //
+ // 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;
+ }
+
+ *NumberOfVlan = BufferSize / sizeof (VLAN_TCI);
+ *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..13c4a6c825
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni
new file mode 100644
index 0000000000..8d2508c774
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4DxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c
new file mode 100644
index 0000000000..7ed2e5250f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c
@@ -0,0 +1,1107 @@
+/** @file
+ Interface routine for Mtftp4.
+
+(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"
+
+
+/**
+ 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 (!NetIp4IsUnicast (NTOHL (Ip), 0)) {
+ 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 (!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 (!NetIp4IsUnicast (ServerIp, 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!ConfigData->UseDefaultSetting &&
+ ((!IP4_IS_VALID_NETMASK (Netmask) || !NetIp4IsUnicast (Ip, Netmask)))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Gateway != 0) &&
+ (!IP4_NET_EQUAL (Gateway, Ip, Netmask) || !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..8f2ab9b24a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Get_status.c
@@ -0,0 +1,266 @@
+/** @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.
+
+ @param Snp Pointer to snp driver structure.
+ @param InterruptStatusPtr A non null pointer to contain the interrupt
+ status.
+ @param 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 (
+ SNP_DRIVER *Snp,
+ UINT32 *InterruptStatusPtr,
+ 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;
+
+ //
+ // size DB for return of one buffer
+ //
+ Snp->Cdb.DBsize = (UINT16) ((sizeof (PXE_DB_GET_STATUS) - sizeof (Db->TxBuffer)) + sizeof (Db->TxBuffer[0]));
+
+ 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 != EFI_SUCCESS) {
+ DEBUG (
+ (EFI_D_NET,
+ "\nSnp->undi.get_status() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatFlags)
+ );
+
+ 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..3f40ef3ce7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Initialize.c
@@ -0,0 +1,251 @@
+/** @file
+ Implementation of initializing a network adapter.
+
+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"
+
+/**
+ 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;
+
+ 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);
+
+ if (Snp->Cdb.StatCode == PXE_STATCODE_SUCCESS) {
+ 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 (Snp->Mode.MediaPresentSupported) {
+ if (PxeInit (Snp, PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) == EFI_SUCCESS) {
+ Snp->Mode.MediaPresent = TRUE;
+ 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);
+ }
+
+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..bb98f4c8a7
--- /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 - 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 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 != EFI_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 != EFI_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 != EFI_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..5ff294f8b5
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.c
@@ -0,0 +1,864 @@
+/** @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->Mode.MediaPresentSupported = TRUE;
+ break;
+
+ case PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED:
+ default:
+ Snp->Mode.MediaPresentSupported = 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 ((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..67f65ffc42
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Snp.h
@@ -0,0 +1,1007 @@
+/** @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
+ //
+ BOOLEAN MediaStatusSupported;
+
+ //
+ // 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
+ );
+
+/**
+ 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..5d45e3a7a4
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni
new file mode 100644
index 0000000000..aec08b1811
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/SnpDxeExtra.uni
Binary files differ
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..730e397452
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Stop.c
@@ -0,0 +1,126 @@
+/** @file
+ Implementation of stopping a network interface.
+
+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 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.StatCode,
+ Snp->Cdb.StatFlags)
+ );
+
+ 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..e6c5af77ec
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/SnpDxe/Transmit.c
@@ -0,0 +1,354 @@
+/** @file
+ Implementation of transmitting a packet.
+
+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 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_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..2e31643821
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c
@@ -0,0 +1,1282 @@
+/** @file
+ Implementation of the Socket.
+
+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.
+
+**/
+
+#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 TURE 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_INFO, "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_INFO,
+ "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
+ )
+{
+ VOID *SockProtocol;
+ EFI_GUID *ProtocolGuid;
+ EFI_STATUS Status;
+
+ ASSERT (SockStream == Sock->Type);
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ //
+ // 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_INFO,
+ "SockDestroy: Delete a unaccepted socket from parent"
+ "now conncnt is %d\n",
+ Sock->Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+
+ //
+ // Set the protocol guid and driver binding handle
+ // in the light of Sock->SockType
+ //
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+
+ //
+ // Retrieve the protocol installed on this sock
+ //
+ Status = gBS->OpenProtocol (
+ Sock->SockHandle,
+ ProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG ((EFI_D_ERROR, "SockDestroy: Open protocol installed "
+ "on socket failed with %r\n", Status));
+
+ goto FreeSock;
+ }
+
+ //
+ // Uninstall the protocol installed on this sock
+ // in the light of Sock->SockType
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ ProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+
+FreeSock:
+ 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..52472d4440
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h
@@ -0,0 +1,130 @@
+/** @file
+ Socket implementation header 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<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"
+
+/**
+ 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 TURE 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..feed86c590
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c
@@ -0,0 +1,959 @@
+/** @file
+ Interface function of the Socket.
+
+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.
+
+**/
+
+
+#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;
+
+ ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));
+
+ if (Sock->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ Sock->InDestroy = TRUE;
+
+ 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;
+ }
+
+ //
+ // 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;
+ 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));
+
+ SockDestroy (Sock);
+ return NULL;
+ }
+ //
+ // 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));
+
+ SockDestroy (Sock);
+ Sock = NULL;
+ }
+
+ return 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
+ )
+{
+ 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_INFO,
+ "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) {
+ Status = SockProcessRcvToken (Sock, RcvToken);
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ 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..5b327af721
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
@@ -0,0 +1,727 @@
+/** @file
+ Tcp request dispatcher implementation.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+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.
+
+**/
+
+#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);
+
+ //
+ // Close the IP protocol.
+ //
+ gBS->CloseProtocol (
+ Tcb->IpInfo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ ProtoData->TcpService->IpIo->Image,
+ Sk->SockHandle
+ );
+
+ 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..30e9406161
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
@@ -0,0 +1,782 @@
+/** @file
+ Tcp driver function.
+
+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.
+
+**/
+
+#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 availabe 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..5fe98d4068
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h
@@ -0,0 +1,342 @@
+/** @file
+ Tcp driver function header.
+
+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_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 availabe 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..a1789dcd65
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni
new file mode 100644
index 0000000000..7690fe5a13
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4DxeExtra.uni
Binary files differ
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..209127d18f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c
@@ -0,0 +1,1468 @@
+/** @file
+ TCP input process routines.
+
+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"
+
+
+/**
+ 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->RcvWl2, 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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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);
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);
+
+ if ((Head->HeadLen < 5) || (Len < 0) ||
+ (TcpChecksum (Nbuf, NetPseudoHeadChecksum (Src, Dst, 6, 0)) != 0)) {
+
+ DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated 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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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;
+
+ 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..c7d3d32185
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
@@ -0,0 +1,673 @@
+/** @file
+ Implementation of TCP4 protocol services.
+
+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<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 ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {
+ 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 (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (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..95f47f91bc
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
@@ -0,0 +1,939 @@
+/** @file
+ Misc support routines for tcp.
+
+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"
+
+#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_INFO,
+ "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_INFO, "TcpOnAppConsume: send a window"
+ " update for a window closed Tcb %p\n", Tcb));
+
+ TcpSendAck (Tcb);
+ } else if (Tcb->DelayedAck == 0) {
+
+ DEBUG ((EFI_D_INFO, "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..b44e851b26
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
@@ -0,0 +1,1212 @@
+/** @file
+ TCP output process routines.
+
+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"
+
+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.
+ // Becuase 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) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0])));
+ 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_INFO,
+ "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_INFO, "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_INFO, "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..faf9ca5e28
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
@@ -0,0 +1,590 @@
+/** @file
+
+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 "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 availabe 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..db2127015f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h
@@ -0,0 +1,154 @@
+/** @file
+
+Copyright (c) 2006 - 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 _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 availabe 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..6cb5ff0b73
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni
new file mode 100644
index 0000000000..09a7a35b02
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4DxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
new file mode 100644
index 0000000000..4f077d6b96
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
@@ -0,0 +1,1898 @@
+/** @file
+ The implementation of the Udp4 protocol.
+
+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 "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 apropriate instances.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated 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 (!NetIp4IsUnicast (NTOHL (GatewayAddress), 0)) {
+ //
+ // 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) && !NetIp4IsUnicast (HTONL (SourceAddress), 0)) {
+ //
+ // 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 apropriate instances.
+
+ @param[in] Udp4Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated 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;
+
+ //
+ // 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.
+ //
+ 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 apropriate 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 requried 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;
+
+ 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..5ea22acb14
--- /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 - 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 "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) || NetIp4IsUnicast (StationAddress, SubnetMask)) ||
+ !((RemoteAddress == 0) || NetIp4IsUnicast (RemoteAddress, 0)))) {
+ //
+ // 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..6c06373004
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.c
@@ -0,0 +1,1938 @@
+/** @file
+ Support for PxeBc dhcp functions.
+
+Copyright (c) 2013, Red Hat, Inc.
+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"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
+ PXEBC_DHCP4_TAG_BOOTFILE_LEN,
+ PXEBC_DHCP4_TAG_VENDOR,
+ PXEBC_DHCP4_TAG_OVERLOAD,
+ PXEBC_DHCP4_TAG_MSG_TYPE,
+ PXEBC_DHCP4_TAG_SERVER_ID,
+ PXEBC_DHCP4_TAG_CLASS_ID,
+ PXEBC_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] = PXEBC_DHCP4_TAG_EOP;
+}
+
+
+/**
+ Copy the DCHP4 packet from srouce to destination.
+
+ @param Dst Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param Src Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+**/
+VOID
+PxeBcCopyEfiDhcp4Packet (
+ IN EFI_DHCP4_PACKET *Dst,
+ IN EFI_DHCP4_PACKET *Src
+ )
+{
+ ASSERT (Dst->Size >= Src->Length);
+
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+ Dst->Length = Src->Length;
+}
+
+
+/**
+ 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.
+
+**/
+VOID
+PxeBcCopyProxyOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 OfferIndex
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PACKET *Offer;
+
+ ASSERT (OfferIndex < Private->NumOffers);
+ ASSERT (OfferIndex < PXEBC_MAX_OFFER_NUM);
+
+ Mode = Private->PxeBc.Mode;
+ Offer = &Private->Dhcp4Offers[OfferIndex].Packet.Offer;
+
+ PxeBcCopyEfiDhcp4Packet (&Private->ProxyOffer.Packet.Offer, Offer);
+ CopyMem (&Mode->ProxyOffer, &Offer->Dhcp4, Offer->Length);
+ Mode->ProxyOfferReceived = TRUE;
+
+ PxeBcParseCachedDhcpPacket (&Private->ProxyOffer);
+}
+
+
+/**
+ 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.
+ //
+ 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.
+
+**/
+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
+ //
+ 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.
+ //
+ 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.
+
+**/
+VOID
+PxeBcCacheDhcpOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *RcvdOffer
+ )
+{
+ PXEBC_CACHED_DHCP4_PACKET *CachedOffer;
+ EFI_DHCP4_PACKET *Offer;
+ UINT8 OfferType;
+
+ CachedOffer = &Private->Dhcp4Offers[Private->NumOffers];
+ Offer = &CachedOffer->Packet.Offer;
+
+ //
+ // Cache the orignal dhcp packet
+ //
+ PxeBcCopyEfiDhcp4Packet (Offer, RcvdOffer);
+
+ //
+ // Parse and validate the options (including dhcp option and vendor option)
+ //
+ if (!PxeBcParseCachedDhcpPacket (CachedOffer)) {
+ return ;
+ }
+
+ OfferType = CachedOffer->OfferType;
+ if (OfferType >= DHCP4_PACKET_TYPE_MAX) {
+ return ;
+ }
+
+ if (OfferType == DHCP4_PACKET_TYPE_BOOTP) {
+
+ if (Private->BootpIndex != 0) {
+ //
+ // Only cache the first bootp offer, discard others.
+ //
+ return ;
+ } 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 ;
+ } 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++;
+}
+
+/**
+ 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),
+ PXEBC_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 (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 (Private->NumOffers < PXEBC_MAX_OFFER_NUM) {
+ //
+ // Cache the dhcp offers in Private->Dhcp4Offers[]
+ //
+ 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);
+
+ PxeBcCopyEfiDhcp4Packet (&Private->Dhcp4Ack.Packet.Ack, Packet);
+ 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 = PXEBC_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 = PXEBC_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 = PXEBC_DHCP4_TAG_PARA_LIST;
+ OptList[Index]->Length = 35;
+ OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+ OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK;
+ OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET;
+ OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER;
+ OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER;
+ OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER;
+ OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER;
+ OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME;
+ OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN;
+ OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME;
+ OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH;
+ OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;
+ OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;
+ OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;
+ OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;
+ OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;
+ OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;
+ OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;
+ OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;
+ OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;
+ OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;
+ OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;
+ OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;
+ OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;
+ OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;
+ OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;
+ OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;
+ OptEnt.Para->ParaList[26] = PXEBC_PXE_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 = PXEBC_PXE_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 = PXEBC_PXE_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 = PXEBC_PXE_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 = PXEBC_DHCP4_TAG_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 = PXEBC_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] = PXEBC_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) {
+
+ 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) {
+ PxeBcCopyEfiDhcp4Packet (Reply, Response);
+ }
+
+ 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;
+ }
+
+ //
+ // free the responselist
+ //
+ if (Token.ResponseList != NULL) {
+ FreePool (Token.ResponseList);
+ }
+ }
+ //
+ // Free the dhcp packet
+ //
+ 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
+ PXEBC_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 != PXEBC_DHCP4_TAG_EOP) {
+
+ if (Option->OpCode == OptTag) {
+
+ return Option;
+ }
+
+ if (Option->OpCode == PXEBC_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 != PXEBC_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 == PXEBC_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,
+ 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..1626060ee2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.h
@@ -0,0 +1,540 @@
+/** @file
+ Dhcp and Discover routines for PxeBc.
+
+Copyright (c) 2013, Red Hat, Inc.
+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_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 1472
+
+#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
+//
+// Dhcp Options
+//
+#define PXEBC_DHCP4_TAG_PAD 0 // Pad Option
+#define PXEBC_DHCP4_TAG_EOP 255 // End Option
+#define PXEBC_DHCP4_TAG_NETMASK 1 // Subnet Mask
+#define PXEBC_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC
+#define PXEBC_DHCP4_TAG_ROUTER 3 // Router option,
+#define PXEBC_DHCP4_TAG_TIME_SERVER 4 // Time Server
+#define PXEBC_DHCP4_TAG_NAME_SERVER 5 // Name Server
+#define PXEBC_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server
+#define PXEBC_DHCP4_TAG_HOSTNAME 12 // Host Name
+#define PXEBC_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size
+#define PXEBC_DHCP4_TAG_DUMP 14 // Merit Dump File
+#define PXEBC_DHCP4_TAG_DOMAINNAME 15 // Domain Name
+#define PXEBC_DHCP4_TAG_ROOTPATH 17 // Root path
+#define PXEBC_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path
+#define PXEBC_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size
+#define PXEBC_DHCP4_TAG_TTL 23 // Default IP Time-to-live
+#define PXEBC_DHCP4_TAG_BROADCAST 28 // Broadcast Address
+#define PXEBC_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain
+#define PXEBC_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers
+#define PXEBC_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers
+#define PXEBC_DHCP4_TAG_VENDOR 43 // Vendor Specific Information
+#define PXEBC_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address
+#define PXEBC_DHCP4_TAG_LEASE 51 // IP Address Lease Time
+#define PXEBC_DHCP4_TAG_OVERLOAD 52 // Option Overload
+#define PXEBC_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type
+#define PXEBC_DHCP4_TAG_SERVER_ID 54 // Server Identifier
+#define PXEBC_DHCP4_TAG_PARA_LIST 55 // Parameter Request List
+#define PXEBC_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size
+#define PXEBC_DHCP4_TAG_T1 58 // Renewal (T1) Time Value
+#define PXEBC_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value
+#define PXEBC_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier
+#define PXEBC_DHCP4_TAG_CLIENT_ID 61 // Client-identifier
+#define PXEBC_DHCP4_TAG_TFTP 66 // TFTP server name
+#define PXEBC_DHCP4_TAG_BOOTFILE 67 // Bootfile name
+#define PXEBC_PXE_DHCP4_TAG_ARCH 93
+#define PXEBC_PXE_DHCP4_TAG_UNDI 94
+#define PXEBC_PXE_DHCP4_TAG_UUID 97
+//
+// 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.
+
+**/
+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
+ PXEBC_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..89977e6690
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.c
@@ -0,0 +1,2959 @@
+/** @file
+ Interface 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.
+
+**/
+
+
+#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 &&
+ !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 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 = (UINTN) (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) || !NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 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) && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 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
+ );
+ 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 (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) &&
+ ((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 (NewStationIp != NULL && !NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewSubnetMask != NULL && !IP4_IS_VALID_NETMASK (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..ce8d8add3f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcImpl.h
@@ -0,0 +1,187 @@
+/** @file
+
+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_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 <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..3016da7324
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,198 @@
+/** @file
+ Support 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.
+
+**/
+
+
+#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.
+
+ @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
+ )
+{
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));
+
+ Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.TypeOfService = DEFAULT_ToS;
+ Udp4CfgData.TimeToLive = DEFAULT_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;
+
+ while (Length > 0) {
+ Remainder = Number % 10;
+ Number /= 10;
+ Length--;
+ Buffer[Length] = (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..6f41aa671d
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcSupport.h
@@ -0,0 +1,116 @@
+/** @file
+ Support 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_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.
+
+ @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
+ );
+/**
+ 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..f416ffc2b9
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni
new file mode 100644
index 0000000000..f132359193
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxe4BcDxeExtra.uni
Binary files differ
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..3dc2c6f3bf
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni
new file mode 100644
index 0000000000..ade7f17af8
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxeExtra.uni
Binary files differ
diff --git a/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c
new file mode 100644
index 0000000000..5e0fe42a88
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigImpl.c
@@ -0,0 +1,663 @@
+/** @file
+ HII Config Access protocol implementation of VLAN configuration module.
+
+Copyright (c) 2009 - 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.
+
+**/
+
+#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
+ //
+ DigitalCount = UnicodeValueToString (VlanIdStr, 0, VlanData[Index].VlanId, 5);
+ 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;
+ String += UnicodeValueToString (String, 0, VlanData[Index].Priority, 4);
+ *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..bf95abd8b8
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigStrings.uni
Binary files differ