mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
15dada20e6
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
1539 lines
45 KiB
C
1539 lines
45 KiB
C
/** @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 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
|
|
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 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.
|
|
//
|
|
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 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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|