| /** @file | |
| Support functions implementation for UefiPxeBc Driver. | |
| Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "PxeBcImpl.h" | |
| /** | |
| Flush the previous configuration 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 | |
| PxeBcFlushStationIp ( | |
| PXEBC_PRIVATE_DATA *Private, | |
| EFI_IP_ADDRESS *StationIp OPTIONAL, | |
| EFI_IP_ADDRESS *SubnetMask OPTIONAL | |
| ) | |
| { | |
| EFI_PXE_BASE_CODE_MODE *Mode; | |
| EFI_STATUS Status; | |
| EFI_ARP_CONFIG_DATA ArpConfigData; | |
| Mode = Private->PxeBc.Mode; | |
| Status = EFI_SUCCESS; | |
| ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA)); | |
| if (Mode->UsingIpv6 && (StationIp != NULL)) { | |
| // | |
| // Overwrite Udp6CfgData/Ip6CfgData StationAddress. | |
| // | |
| 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); | |
| } else { | |
| if (StationIp != NULL) { | |
| // | |
| // Reconfigure the ARP instance with station Ip address. | |
| // | |
| ArpConfigData.SwAddressType = 0x0800; | |
| ArpConfigData.SwAddressLength = (UINT8)sizeof (EFI_IPv4_ADDRESS); | |
| ArpConfigData.StationAddress = StationIp; | |
| Private->Arp->Configure (Private->Arp, NULL); | |
| Private->Arp->Configure (Private->Arp, &ArpConfigData); | |
| // | |
| // Overwrite Udp4CfgData/Ip4CfgData StationAddress. | |
| // | |
| CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); | |
| CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); | |
| } | |
| if (SubnetMask != NULL) { | |
| // | |
| // Overwrite Udp4CfgData/Ip4CfgData SubnetMask. | |
| // | |
| CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); | |
| CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); | |
| } | |
| if ((StationIp != NULL) && (SubnetMask != NULL)) { | |
| // | |
| // Updated the route table. | |
| // | |
| Mode->RouteTableEntries = 1; | |
| Mode->RouteTable[0].IpAddr.Addr[0] = StationIp->Addr[0] & SubnetMask->Addr[0]; | |
| Mode->RouteTable[0].SubnetMask.Addr[0] = SubnetMask->Addr[0]; | |
| Mode->RouteTable[0].GwAddr.Addr[0] = 0; | |
| } | |
| if ((StationIp != NULL) || (SubnetMask != NULL)) { | |
| // | |
| // 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); | |
| } | |
| } | |
| 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 resolved 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. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| if ((EFI_IP4 (RxData->Header->SourceAddress) != 0) && | |
| (NTOHL (Mode->SubnetMask.Addr[0]) != 0) && | |
| IP4_NET_EQUAL (NTOHL (Mode->StationIp.Addr[0]), EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0])) && | |
| !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0]))) | |
| { | |
| // | |
| // The source address of the received packet should be a valid unicast address. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) { | |
| // | |
| // The destination address of the received packet should be equal to the host address. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| // | |
| // The protocol has been configured to only receive ICMP packet. | |
| // | |
| ASSERT (RxData->Header->Protocol == EFI_IP_PROTO_ICMP); | |
| 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. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| // | |
| // 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_RECYCLE: | |
| gBS->SignalEvent (RxData->RecycleSignal); | |
| 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. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) { | |
| // | |
| // The source address of the received packet should be a valid unicast address. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| 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. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| // | |
| // The protocol has been configured to only receive ICMP packet. | |
| // | |
| ASSERT (RxData->Header->NextHeader == IP6_ICMP); | |
| Type = *((UINT8 *)RxData->FragmentTable[0].FragmentBuffer); | |
| if ((Type != ICMP_V6_DEST_UNREACHABLE) && | |
| (Type != ICMP_V6_PACKET_TOO_BIG) && | |
| (Type != ICMP_V6_TIME_EXCEEDED) && | |
| (Type != ICMP_V6_PARAMETER_PROBLEM)) | |
| { | |
| // | |
| // The type of the receveid packet should be an ICMP6 error message. | |
| // | |
| goto ON_RECYCLE; | |
| } | |
| // | |
| // 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_RECYCLE: | |
| gBS->SignalEvent (RxData->RecycleSignal); | |
| 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. | |
| @param[in] Ttl The time to live field of the IP header. | |
| @param[in] ToS The type of service field of the IP header. | |
| @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, | |
| IN UINT8 Ttl, | |
| IN UINT8 ToS | |
| ) | |
| { | |
| EFI_UDP4_CONFIG_DATA Udp4CfgData; | |
| EFI_STATUS Status; | |
| ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData)); | |
| Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; | |
| Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; | |
| Udp4CfgData.TypeOfService = ToS; | |
| Udp4CfgData.TimeToLive = 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] Mode 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 until 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 until 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; | |
| for ( ; Length > 0; Length--) { | |
| Remainder = Number % 10; | |
| Number /= 10; | |
| Buffer[Length - 1] = (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. | |
| @param[in] BufferSize The maxsize of the buffer. | |
| @return Length The actual length of the ASCII string. | |
| **/ | |
| UINTN | |
| PxeBcUintnToAscDec ( | |
| IN UINTN Number, | |
| IN UINT8 *Buffer, | |
| IN UINTN BufferSize | |
| ) | |
| { | |
| 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); | |
| AsciiStrCpyS ((CHAR8 *)Buffer, BufferSize, &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; | |
| } | |
| /** | |
| Calculate the elapsed time. | |
| @param[in] Private The pointer to PXE private data | |
| **/ | |
| VOID | |
| CalcElapsedTime ( | |
| IN PXEBC_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_TIME Time; | |
| UINT64 CurrentStamp; | |
| UINT64 ElapsedTimeValue; | |
| // | |
| // Generate a time stamp of the centiseconds from 1900/1/1, assume 30day/month. | |
| // | |
| ZeroMem (&Time, sizeof (EFI_TIME)); | |
| gRT->GetTime (&Time, NULL); | |
| CurrentStamp = MultU64x32 ( | |
| ((((UINT32)(Time.Year - 1900) * 360 + (Time.Month - 1) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) * 60 + Time.Second, | |
| 100 | |
| ) + | |
| DivU64x32 ( | |
| Time.Nanosecond, | |
| 10000000 | |
| ); | |
| // | |
| // Sentinel value of 0 means that this is the first DHCP packet that we are | |
| // sending and that we need to initialize the value. First DHCP Solicit | |
| // gets 0 elapsed-time. Otherwise, calculate based on StartTime. | |
| // | |
| if (Private->ElapsedTime == 0) { | |
| Private->ElapsedTime = CurrentStamp; | |
| } else { | |
| ElapsedTimeValue = CurrentStamp - Private->ElapsedTime; | |
| // | |
| // If elapsed time cannot fit in two bytes, set it to 0xffff. | |
| // | |
| if (ElapsedTimeValue > 0xffff) { | |
| ElapsedTimeValue = 0xffff; | |
| } | |
| // | |
| // Save the elapsed time | |
| // | |
| Private->ElapsedTime = ElapsedTimeValue; | |
| } | |
| } |