summaryrefslogtreecommitdiff
path: root/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c')
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c1588
1 files changed, 1588 insertions, 0 deletions
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
new file mode 100644
index 000000000..7ad070c5e
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,1588 @@
+/** @file
+ Support functions implementation for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 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 "PxeBcImpl.h"
+
+
+
+/**
+ This function returns SMBIOS string given the string number.
+
+ @param[in] Smbios The pointer to the SMBIOS structure
+ @param[in] StringNumber String number to return. 0 is used to skip all
+ strings and point to the next SMBIOS structure.
+
+ @return String The pointer to the next SMBIOS structure if
+ StringNumber == 0.
+
+**/
+CHAR8 *
+PxeBcGetSmbiosString (
+ IN SMBIOS_STRUCTURE_POINTER *Smbios,
+ IN UINT16 StringNumber
+ )
+{
+ UINT16 Index;
+ CHAR8 *String;
+
+ //
+ // Skip over formatted section.
+ //
+ String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);
+
+ //
+ // Look through unformated section.
+ //
+ for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {
+ if (StringNumber == Index) {
+ return String;
+ }
+
+ //
+ // Skip zero string.
+ //
+ while (*String != 0) {
+ String++;
+ }
+ String++;
+
+ if (*String == 0) {
+ //
+ // If double NULL then we are done.
+ // Return pointer to next structure in Smbios.
+ // if you pass in a 0 you will always get here
+ //
+ Smbios->Raw = (UINT8 *)++String;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ This function obtains the system guid and the serial number from the smbios table.
+
+ @param[out] SystemGuid The pointer of the returned system guid.
+
+ @retval EFI_SUCCESS Successfully obtained the system guid.
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.
+
+**/
+EFI_STATUS
+PxeBcGetSystemGuid (
+ OUT EFI_GUID *SystemGuid
+ )
+{
+ EFI_STATUS Status;
+ SMBIOS_TABLE_ENTRY_POINT *SmbiosTable;
+ SMBIOS_STRUCTURE_POINTER Smbios;
+ SMBIOS_STRUCTURE_POINTER SmbiosEnd;
+ UINT16 Index;
+
+ SmbiosTable = NULL;
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);
+
+ if (EFI_ERROR (Status) || SmbiosTable == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);
+
+ for (Index = 0; Index < SmbiosTable->TableLength; Index++) {
+ if (Smbios.Hdr->Type == 1) {
+ if (Smbios.Hdr->Length < 0x19) {
+ //
+ // Older version did not support Guid and Serial number
+ //
+ continue;
+ }
+ //
+ // SMBIOS tables are byte packed so we need to do a byte copy to
+ // prevend alignment faults on Itanium-based platform.
+ //
+ CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));
+ PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Make Smbios point to the next record
+ //
+ PxeBcGetSmbiosString (&Smbios, 0);
+
+ if (Smbios.Raw >= SmbiosEnd.Raw) {
+ //
+ // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.
+ // given this we must double check against the length of the structure.
+ //
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Flush the previous configration using the new station Ip address.
+
+ @param[in] Private The pointer to the PxeBc private data.
+ @param[in] StationIp The pointer to the station Ip address.
+ @param[in] SubnetMask The pointer to the subnet mask address for v4.
+
+ @retval EFI_SUCCESS Successfully flushed the previous configuration.
+ @retval Others Failed to flush using the new station Ip.
+
+**/
+EFI_STATUS
+PxeBcFlushStaionIp (
+ PXEBC_PRIVATE_DATA *Private,
+ EFI_IP_ADDRESS *StationIp,
+ EFI_IP_ADDRESS *SubnetMask OPTIONAL
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+
+ ASSERT (StationIp != NULL);
+
+ Mode = Private->PxeBc.Mode;
+ Status = EFI_SUCCESS;
+
+ if (Mode->UsingIpv6) {
+
+ CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address.
+ //
+ Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);
+ Private->Ip6->Configure (Private->Ip6, NULL);
+
+ Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ } else {
+ ASSERT (SubnetMask != NULL);
+ CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Ip4CfgData.SubnetMask, 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->IcmpToken);
+ Private->Ip4->Configure (Private->Ip4, NULL);
+
+ Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Event The triggered event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+PxeBcCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+
+/**
+ Do arp resolution from arp cache in PxeBcMode.
+
+ @param Mode The pointer to EFI_PXE_BASE_CODE_MODE.
+ @param Ip4Addr The Ip4 address for resolution.
+ @param MacAddress The resoluted MAC address if the resolution is successful.
+ The value is undefined if the resolution fails.
+
+ @retval TRUE Found an matched entry.
+ @retval FALSE Did not find a matched entry.
+
+**/
+BOOLEAN
+PxeBcCheckArpCache (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_IPv4_ADDRESS *Ip4Addr,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ )
+{
+ UINT32 Index;
+
+ ASSERT (!Mode->UsingIpv6);
+
+ //
+ // Check whether the current Arp cache in mode data contains this information or not.
+ //
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {
+ if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {
+ CopyMem (
+ MacAddress,
+ &Mode->ArpCache[Index].MacAddr,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Update the arp cache periodically.
+
+ @param Event The pointer to EFI_PXE_BC_PROTOCOL.
+ @param Context Context of the timer event.
+
+**/
+VOID
+EFIAPI
+PxeBcArpCacheUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_ARP_FIND_DATA *ArpEntry;
+ UINT32 EntryLength;
+ UINT32 EntryCount;
+ UINT32 Index;
+ EFI_STATUS Status;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = Private->PxeBc.Mode;
+
+ ASSERT (!Mode->UsingIpv6);
+
+ //
+ // Get the current Arp cache from Arp driver.
+ //
+ Status = Private->Arp->Find (
+ Private->Arp,
+ TRUE,
+ NULL,
+ &EntryLength,
+ &EntryCount,
+ &ArpEntry,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ //
+ // Update the Arp cache in mode data.
+ //
+ Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES);
+
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {
+ CopyMem (
+ &Mode->ArpCache[Index].IpAddr,
+ ArpEntry + 1,
+ ArpEntry->SwAddressLength
+ );
+ CopyMem (
+ &Mode->ArpCache[Index].MacAddr,
+ (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength,
+ ArpEntry->HwAddressLength
+ );
+ ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength);
+ }
+}
+
+
+/**
+ Notify function to handle the received ICMP message in DPC.
+
+ @param Context The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorDpcHandle (
+ 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;
+ UINT8 Type;
+ UINTN Index;
+ UINT32 CopiedLen;
+ UINT8 *IcmpError;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = &Private->Mode;
+ Status = Private->IcmpToken.Status;
+ RxData = Private->IcmpToken.Packet.RxData;
+ Ip4 = Private->Ip4;
+
+ ASSERT (!Mode->UsingIpv6);
+
+ if (Status == EFI_ABORTED) {
+ //
+ // It's triggered by user cancellation.
+ //
+ return;
+ }
+
+ if (RxData == NULL) {
+ goto ON_EXIT;
+ }
+
+ if (Status != EFI_ICMP_ERROR) {
+ //
+ // The return status should be recognized as EFI_ICMP_ERROR.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&
+ !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {
+ //
+ // The source address of the received packet should be a valid unicast address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {
+ //
+ // The destination address of the received packet should be equal to the host address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {
+ //
+ // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);
+
+ if (Type != ICMP_DEST_UNREACHABLE &&
+ Type != ICMP_SOURCE_QUENCH &&
+ Type != ICMP_REDIRECT &&
+ Type != ICMP_TIME_EXCEEDED &&
+ Type != ICMP_PARAMETER_PROBLEM) {
+ //
+ // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ //
+ // Copy the right ICMP error message into mode data.
+ //
+ CopiedLen = 0;
+ IcmpError = (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 (
+ IcmpError,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ RxData->FragmentTable[Index].FragmentLength
+ );
+ } else {
+ CopyMem (
+ IcmpError,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+ );
+ }
+ IcmpError += CopiedLen;
+ }
+
+ON_EXIT:
+ Private->IcmpToken.Status = EFI_NOT_READY;
+ Ip4->Receive (Ip4, &Private->IcmpToken);
+}
+
+
+/**
+ Callback function to update the latest ICMP6 error message.
+
+ @param Event The event signalled.
+ @param Context The context passed in using the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context);
+}
+
+
+/**
+ Notify function to handle the received ICMP6 message in DPC.
+
+ @param Context The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorDpcHandle (
+ IN VOID *Context
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_IP6_RECEIVE_DATA *RxData;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT8 Type;
+ UINT32 CopiedLen;
+ UINT8 *Icmp6Error;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = &Private->Mode;
+ Status = Private->Icmp6Token.Status;
+ RxData = Private->Icmp6Token.Packet.RxData;
+ Ip6 = Private->Ip6;
+
+ ASSERT (Mode->UsingIpv6);
+
+ if (Status == EFI_ABORTED) {
+ //
+ // It's triggered by user cancellation.
+ //
+ return;
+ }
+
+ if (RxData == NULL) {
+ goto ON_EXIT;
+ }
+
+ if (Status != EFI_ICMP_ERROR) {
+ //
+ // The return status should be recognized as EFI_ICMP_ERROR.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {
+ //
+ // The source address of the received packet should be a valid unicast address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&
+ !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) {
+ //
+ // The destination address of the received packet should be equal to the host address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (RxData->Header->NextHeader != IP6_ICMP) {
+ //
+ // The nextheader in the header of the receveid packet should be IP6_ICMP.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);
+
+ if (Type != ICMP_V6_DEST_UNREACHABLE &&
+ Type != ICMP_V6_PACKET_TOO_BIG &&
+ Type != ICMP_V6_PACKET_TOO_BIG &&
+ Type != ICMP_V6_PARAMETER_PROBLEM) {
+ //
+ // The type of the receveid packet should be an ICMP6 error message.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ //
+ // Copy the right ICMP6 error message into mode data.
+ //
+ CopiedLen = 0;
+ Icmp6Error = (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 (
+ Icmp6Error,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ RxData->FragmentTable[Index].FragmentLength
+ );
+ } else {
+ CopyMem (
+ Icmp6Error,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+ );
+ }
+ Icmp6Error += CopiedLen;
+ }
+
+ON_EXIT:
+ Private->Icmp6Token.Status = EFI_NOT_READY;
+ Ip6->Receive (Ip6, &Private->Icmp6Token);
+}
+
+
+/**
+ Callback function to update the latest ICMP6 error message.
+
+ @param Event The event signalled.
+ @param Context The context passed in using the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context);
+}
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.
+ @param[in] StationIp The pointer to the station address.
+ @param[in] SubnetMask The pointer to the subnet mask.
+ @param[in] Gateway The pointer to the gateway address.
+ @param[in, out] SrcPort The pointer to the source port.
+ @param[in] DoNotFragment If TRUE, fragment is not enabled.
+ Otherwise, fragment is enabled.
+
+ @retval EFI_SUCCESS Successfully configured this instance.
+ @retval Others Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp4Write (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_IPv4_ADDRESS *StationIp,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *Gateway,
+ IN OUT UINT16 *SrcPort,
+ IN BOOLEAN DoNotFragment
+ )
+{
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));
+
+ Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.TypeOfService = DEFAULT_ToS;
+ Udp4CfgData.TimeToLive = DEFAULT_TTL;
+ Udp4CfgData.AllowDuplicatePort = TRUE;
+ Udp4CfgData.DoNotFragment = DoNotFragment;
+
+ CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));
+ CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));
+
+ Udp4CfgData.StationPort = *SrcPort;
+
+ //
+ // Reset the UDPv4 instance.
+ //
+ Udp4->Configure (Udp4, NULL);
+
+ Status = Udp4->Configure (Udp4, &Udp4CfgData);
+ if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) {
+ //
+ // The basic configuration is OK, need to add the default route entry
+ //
+ Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);
+ if (EFI_ERROR (Status)) {
+ Udp4->Configure (Udp4, NULL);
+ }
+ }
+
+ if (!EFI_ERROR (Status) && *SrcPort == 0) {
+ Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);
+ *SrcPort = Udp4CfgData.StationPort;
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to configure a UDPv6 instance for UdpWrite.
+
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.
+ @param[in] StationIp The pointer to the station address.
+ @param[in, out] SrcPort The pointer to the source port.
+
+ @retval EFI_SUCCESS Successfully configured this instance.
+ @retval Others Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp6Write (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_IPv6_ADDRESS *StationIp,
+ IN OUT UINT16 *SrcPort
+ )
+{
+ EFI_UDP6_CONFIG_DATA CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+ CfgData.HopLimit = PXEBC_DEFAULT_HOPLIMIT;
+ CfgData.AllowDuplicatePort = TRUE;
+ CfgData.StationPort = *SrcPort;
+
+ CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Reset the UDPv6 instance.
+ //
+ Udp6->Configure (Udp6, NULL);
+
+ Status = Udp6->Configure (Udp6, &CfgData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!EFI_ERROR (Status) && *SrcPort == 0) {
+ Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL);
+ *SrcPort = CfgData.StationPort;
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.
+ @param[in] Session The pointer to the UDP4 session data.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] Gateway The pointer to the gateway address.
+ @param[in] 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[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS Successfully send out data using Udp4Write.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Write (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_UDP4_SESSION_DATA *Session,
+ IN EFI_EVENT TimeoutEvent,
+ IN EFI_IPv4_ADDRESS *Gateway OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN Token;
+ EFI_UDP4_TRANSMIT_DATA *TxData;
+ UINT32 TxLength;
+ UINT32 FragCount;
+ UINT32 DataLength;
+ BOOLEAN IsDone;
+ EFI_STATUS Status;
+
+ //
+ // Arrange one fragment buffer for data, and another fragment buffer for header if has.
+ //
+ FragCount = (HeaderSize != NULL) ? 2 : 1;
+ TxLength = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA);
+ TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength);
+ if (TxData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TxData->FragmentCount = FragCount;
+ TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+ TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+ DataLength = (UINT32) *BufferSize;
+
+ if (HeaderSize != NULL) {
+ TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+ TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+ DataLength += (UINT32) *HeaderSize;
+ }
+
+ if (Gateway != NULL) {
+ TxData->GatewayAddress = Gateway;
+ }
+
+ TxData->UdpSessionData = Session;
+ TxData->DataLength = DataLength;
+ Token.Packet.TxData = TxData;
+ Token.Status = EFI_NOT_READY;
+ IsDone = FALSE;
+
+ 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)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!IsDone &&
+ Token.Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ Udp4->Poll (Udp4);
+ }
+
+ Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;
+
+ON_EXIT:
+ if (Token.Event != NULL) {
+ gBS->CloseEvent (Token.Event);
+ }
+ FreePool (TxData);
+
+ return Status;
+}
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.
+ @param[in] Session The pointer to the UDP6 session data.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] 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[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS Successfully sent out data using Udp6Write.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Write (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_UDP6_SESSION_DATA *Session,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ EFI_UDP6_COMPLETION_TOKEN Token;
+ EFI_UDP6_TRANSMIT_DATA *TxData;
+ UINT32 TxLength;
+ UINT32 FragCount;
+ UINT32 DataLength;
+ BOOLEAN IsDone;
+ EFI_STATUS Status;
+
+ //
+ // Arrange one fragment buffer for data, and another fragment buffer for header if has.
+ //
+ FragCount = (HeaderSize != NULL) ? 2 : 1;
+ TxLength = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA);
+ TxData = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength);
+ if (TxData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TxData->FragmentCount = FragCount;
+ TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+ TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+ DataLength = (UINT32) *BufferSize;
+
+ if (HeaderSize != NULL) {
+ TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+ TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+ DataLength += (UINT32) *HeaderSize;
+ }
+
+ TxData->UdpSessionData = Session;
+ TxData->DataLength = DataLength;
+ Token.Packet.TxData = TxData;
+ Token.Status = EFI_NOT_READY;
+ IsDone = FALSE;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Udp6->Transmit (Udp6, &Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!IsDone &&
+ Token.Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ Udp6->Poll (Udp6);
+ }
+
+ Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;
+
+ON_EXIT:
+ if (Token.Event != NULL) {
+ gBS->CloseEvent (Token.Event);
+ }
+ FreePool (TxData);
+
+ return Status;
+}
+
+
+/**
+ Check the received packet using the Ip filter.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the Ip filter successfully.
+ @retval FALSE Failed to pass the Ip filter.
+
+**/
+BOOLEAN
+PxeBcCheckByIpFilter (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN UINT16 OpFlags
+ )
+{
+ EFI_IP_ADDRESS DestinationIp;
+ UINTN Index;
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) {
+ return TRUE;
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {
+ return TRUE;
+ }
+
+ //
+ // Convert the destination address in session data to host order.
+ //
+ if (Mode->UsingIpv6) {
+ CopyMem (
+ &DestinationIp,
+ &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ NTOHLLL (&DestinationIp.v6);
+ } else {
+ ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (
+ &DestinationIp,
+ &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ EFI_NTOHL (DestinationIp);
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 &&
+ (IP4_IS_MULTICAST (DestinationIp.Addr[0]) ||
+ IP6_IS_MULTICAST (&DestinationIp))) {
+ return TRUE;
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 &&
+ IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) {
+ ASSERT (!Mode->UsingIpv6);
+ return TRUE;
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
+ (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) ||
+ EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) {
+ //
+ // Matched if the dest address is equal to the station address.
+ //
+ return TRUE;
+ }
+
+ for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) {
+ ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);
+ if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) ||
+ EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) {
+ //
+ // Matched if the dest address is equal to any of address in the filter list.
+ //
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Filter the received packet using the destination Ip.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] DestIp The pointer to the destination Ip address.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestIp (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT EFI_IP_ADDRESS *DestIp,
+ IN UINT16 OpFlags
+ )
+{
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {
+ //
+ // Copy the destination address from the received packet if accept any.
+ //
+ if (DestIp != NULL) {
+ if (Mode->UsingIpv6) {
+ CopyMem (
+ DestIp,
+ &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ } else {
+ ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (
+ DestIp,
+ &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ }
+ return TRUE;
+ } else if (DestIp != NULL &&
+ (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||
+ EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) {
+ //
+ // The destination address in the received packet is matched if present.
+ //
+ return TRUE;
+ } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||
+ EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) {
+ //
+ // The destination address in the received packet is equal to the host address.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Check the received packet using the destination port.
+
+ @param[in] PxeBcMode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] DestPort The pointer to the destination port.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestPort (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT UINT16 *DestPort,
+ IN UINT16 OpFlags
+ )
+{
+ UINT16 Port;
+
+ if (Mode->UsingIpv6) {
+ Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;
+ } else {
+ Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;
+ }
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {
+ //
+ // Return the destination port in the received packet if accept any.
+ //
+ if (DestPort != NULL) {
+ *DestPort = Port;
+ }
+ return TRUE;
+ } else if (DestPort != NULL && *DestPort == Port) {
+ //
+ // The destination port in the received packet is matched if present.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Filter the received packet using the source Ip.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] SrcIp The pointer to the source Ip address.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcIp (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT EFI_IP_ADDRESS *SrcIp,
+ IN UINT16 OpFlags
+ )
+{
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {
+ //
+ // Copy the source address from the received packet if accept any.
+ //
+ if (SrcIp != NULL) {
+ if (Mode->UsingIpv6) {
+ CopyMem (
+ SrcIp,
+ &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ } else {
+ ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (
+ SrcIp,
+ &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ }
+ return TRUE;
+ } else if (SrcIp != NULL &&
+ (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) ||
+ EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) {
+ //
+ // The source address in the received packet is matched if present.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Filter the received packet using the source port.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] SrcPort The pointer to the source port.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcPort (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT UINT16 *SrcPort,
+ IN UINT16 OpFlags
+ )
+{
+ UINT16 Port;
+
+ if (Mode->UsingIpv6) {
+ Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;
+ } else {
+ Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;
+ }
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {
+ //
+ // Return the source port in the received packet if accept any.
+ //
+ if (SrcPort != NULL) {
+ *SrcPort = Port;
+ }
+ return TRUE;
+ } else if (SrcPort != NULL && *SrcPort == Port) {
+ //
+ // The source port in the received packet is matched if present.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This function is to receive packet using Udp4Read.
+
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.
+ @param[in] Token The pointer to EFI_UDP4_COMPLETION_TOKEN.
+ @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] IsDone The pointer to the IsDone flag.
+ @param[out] IsMatched The pointer to the IsMatched flag.
+ @param[in, out] DestIp The pointer to the destination address.
+ @param[in, out] DestPort The pointer to the destination port.
+ @param[in, out] SrcIp The pointer to the source address.
+ @param[in, out] SrcPort The pointer to the source port.
+
+ @retval EFI_SUCCESS Successfully read the data using Udp4.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Read (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token,
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINT16 OpFlags,
+ IN BOOLEAN *IsDone,
+ OUT BOOLEAN *IsMatched,
+ 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
+ )
+{
+ EFI_UDP4_RECEIVE_DATA *RxData;
+ EFI_UDP4_SESSION_DATA *Session;
+ EFI_STATUS Status;
+
+ Token->Status = EFI_NOT_READY;
+ *IsDone = FALSE;
+
+ Status = Udp4->Receive (Udp4, Token);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!(*IsDone) &&
+ Token->Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ //
+ // Poll the token utill reply/ICMPv6 error message received or timeout.
+ //
+ Udp4->Poll (Udp4);
+ if (Token->Status == EFI_ICMP_ERROR ||
+ Token->Status == EFI_NETWORK_UNREACHABLE ||
+ Token->Status == EFI_HOST_UNREACHABLE ||
+ Token->Status == EFI_PROTOCOL_UNREACHABLE ||
+ Token->Status == EFI_PORT_UNREACHABLE) {
+ break;
+ }
+ }
+
+ Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // check whether this packet matches the filters
+ //
+ RxData = Token->Packet.RxData;
+ Session = &RxData->UdpSession;
+
+ *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);
+ }
+
+ if (!(*IsMatched)) {
+ //
+ // Recycle the receiving buffer if not matched.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to receive packets using Udp6Read.
+
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.
+ @param[in] Token The pointer to EFI_UDP6_COMPLETION_TOKEN.
+ @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] IsDone The pointer to the IsDone flag.
+ @param[out] IsMatched The pointer to the IsMatched flag.
+ @param[in, out] DestIp The pointer to the destination address.
+ @param[in, out] DestPort The pointer to the destination port.
+ @param[in, out] SrcIp The pointer to the source address.
+ @param[in, out] SrcPort The pointer to the source port.
+
+ @retval EFI_SUCCESS Successfully read data using Udp6.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Read (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token,
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINT16 OpFlags,
+ IN BOOLEAN *IsDone,
+ OUT BOOLEAN *IsMatched,
+ 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
+ )
+{
+ EFI_UDP6_RECEIVE_DATA *RxData;
+ EFI_UDP6_SESSION_DATA *Session;
+ EFI_STATUS Status;
+
+ Token->Status = EFI_NOT_READY;
+ *IsDone = FALSE;
+
+ Status = Udp6->Receive (Udp6, Token);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!(*IsDone) &&
+ Token->Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ //
+ // Poll the token utill reply/ICMPv6 error message received or timeout.
+ //
+ Udp6->Poll (Udp6);
+ if (Token->Status == EFI_ICMP_ERROR ||
+ Token->Status == EFI_NETWORK_UNREACHABLE ||
+ Token->Status == EFI_HOST_UNREACHABLE ||
+ Token->Status == EFI_PROTOCOL_UNREACHABLE ||
+ Token->Status == EFI_PORT_UNREACHABLE) {
+ break;
+ }
+ }
+
+ Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // check whether this packet matches the filters
+ //
+ RxData = Token->Packet.RxData;
+ Session = &RxData->UdpSession;
+
+ *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);
+ }
+
+ if (!(*IsMatched)) {
+ //
+ // Recycle the receiving buffer if not matched.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to display the IPv4 address.
+
+ @param[in] Ip The pointer to the IPv4 address.
+
+**/
+VOID
+PxeBcShowIp4Addr (
+ IN EFI_IPv4_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < 4; Index++) {
+ AsciiPrint ("%d", Ip->Addr[Index]);
+ if (Index < 3) {
+ AsciiPrint (".");
+ }
+ }
+}
+
+
+/**
+ This function is to display the IPv6 address.
+
+ @param[in] Ip The pointer to the IPv6 address.
+
+**/
+VOID
+PxeBcShowIp6Addr (
+ IN EFI_IPv6_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < 16; Index++) {
+
+ if (Ip->Addr[Index] != 0) {
+ AsciiPrint ("%x", Ip->Addr[Index]);
+ }
+ Index++;
+ if (Index > 15) {
+ return;
+ }
+ if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {
+ AsciiPrint ("0");
+ }
+ AsciiPrint ("%x", Ip->Addr[Index]);
+ if (Index < 15) {
+ AsciiPrint (":");
+ }
+ }
+}
+
+
+/**
+ This function is to convert UINTN to ASCII string with the required formatting.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer The pointer to the buffer for ASCII string.
+ @param[in] Length The length of the required format.
+
+**/
+VOID
+PxeBcUintnToAscDecWithFormat (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN INTN Length
+ )
+{
+ UINTN Remainder;
+
+ while (Length > 0) {
+ Length--;
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length] = (UINT8) ('0' + Remainder);
+ }
+}
+
+
+/**
+ This function is to convert a UINTN to a ASCII string, and return the
+ actual length of the buffer.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer The pointer to the buffer for ASCII string.
+
+ @return Length The actual length of the ASCII string.
+
+**/
+UINTN
+PxeBcUintnToAscDec (
+ IN UINTN Number,
+ IN UINT8 *Buffer
+ )
+{
+ UINTN Index;
+ UINTN Length;
+ CHAR8 TempStr[64];
+
+ Index = 63;
+ TempStr[Index] = 0;
+
+ do {
+ Index--;
+ TempStr[Index] = (CHAR8) ('0' + (Number % 10));
+ Number = (UINTN) (Number / 10);
+ } while (Number != 0);
+
+ AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]);
+
+ Length = AsciiStrLen ((CHAR8 *) Buffer);
+
+ return Length;
+}
+
+
+/**
+ This function is to convert unicode hex number to a UINT8.
+
+ @param[out] Digit The converted UINT8 for output.
+ @param[in] Char The unicode hex number to be converted.
+
+ @retval EFI_SUCCESS Successfully converted the unicode hex.
+ @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex.
+
+**/
+EFI_STATUS
+PxeBcUniHexToUint8 (
+ OUT UINT8 *Digit,
+ IN CHAR16 Char
+ )
+{
+ if ((Char >= L'0') && (Char <= L'9')) {
+ *Digit = (UINT8) (Char - L'0');
+ return EFI_SUCCESS;
+ }
+
+ if ((Char >= L'A') && (Char <= L'F')) {
+ *Digit = (UINT8) (Char - L'A' + 0x0A);
+ return EFI_SUCCESS;
+ }
+
+ if ((Char >= L'a') && (Char <= L'f')) {
+ *Digit = (UINT8) (Char - L'a' + 0x0A);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_INVALID_PARAMETER;
+}