From 772db4bb33ae66fa20e39f786b5f80d107d450a5 Mon Sep 17 00:00:00 2001 From: vanjeff Date: Mon, 30 Jul 2007 02:37:10 +0000 Subject: Import ArpDxe, Dhcp4Dxe, Ip4Dxe, Mtftp4Dxe, PxeBcDxe and PxeDhcp4Dxe. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c | 2401 ++++++++++++++ MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h | 564 ++++ .../Universal/Network/PxeBcDxe/ComponentName.c | 160 + MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h | 632 ++++ .../Universal/Network/PxeBcDxe/Ebc/PxeArch.c | 46 + .../Universal/Network/PxeBcDxe/Ebc/PxeArch.h | 36 + MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h | 43 + .../Universal/Network/PxeBcDxe/Ia32/PxeArch.h | 26 + MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h | 736 +++++ .../Universal/Network/PxeBcDxe/Ipf/PxeArch.h | 26 + .../Universal/Network/PxeBcDxe/PxeBcDxe.inf | 92 + .../Universal/Network/PxeBcDxe/PxeBcDxe.msa | 106 + .../Universal/Network/PxeBcDxe/Pxe_bc_arp.c | 583 ++++ .../Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c | 3284 ++++++++++++++++++++ .../Universal/Network/PxeBcDxe/Pxe_bc_igmp.c | 421 +++ .../Universal/Network/PxeBcDxe/Pxe_bc_ip.c | 846 +++++ .../Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c | 2193 +++++++++++++ .../Universal/Network/PxeBcDxe/Pxe_bc_udp.c | 517 +++ .../Universal/Network/PxeBcDxe/Pxe_loadfile.c | 1614 ++++++++++ MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h | 154 + .../Universal/Network/PxeBcDxe/X64/PxeArch.h | 26 + 21 files changed, 14506 insertions(+) create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h (limited to 'MdeModulePkg/Universal/Network/PxeBcDxe') diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c new file mode 100644 index 0000000000..5f15c81e48 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c @@ -0,0 +1,2401 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + bc.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// +// +EFI_STATUS +EFIAPI +PxeBcDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +PxeBcDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +PxeBcDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +extern +VOID +InitArpHeader ( + VOID + ); +extern +VOID +OptionsStrucInit ( + VOID + ); + +// +// helper routines +// + +/** + Convert number to ASCII value + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + @param Length Length of Buffer. + + @retval none none + +**/ +VOID +CvtNum ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + while (Length--) { + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (UINT8) ('0' + Remainder); + } +} + + +/** + Convert number to decimal ASCII value at Buffer location + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + + @retval none none + +**/ +VOID +UtoA10 ( + IN UINTN Number, + IN UINT8 *Buffer + ) +{ + INTN Index; + UINT8 BuffArray[31]; + + BuffArray[30] = 0; + CvtNum (Number, BuffArray, 30); + + for (Index = 0; Index < 30; ++Index) { + if (BuffArray[Index] != '0') { + break; + } + } + + CopyMem (Buffer, BuffArray + Index, 31 - Index); +} + + +/** + Convert ASCII numeric string to a UINTN value + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + + @retval Value UINTN value of the ASCII string. + +**/ +UINTN +AtoU ( + IN UINT8 *Buffer + ) +{ + UINTN Value; + INT8 Character; + + Value = 0; + Character = *Buffer++; + do { + Value = Value * 10 + Character - '0'; + Character = *Buffer++; + } while (Character); + + return Value; +} + + +/** + Convert ASCII numeric string to a UINTN value + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + + @retval Value UINTN value of the ASCII string. + +**/ +UINT64 +AtoU64 ( + IN UINT8 *Buffer + ) +{ + UINT64 Value; + UINT8 Character; + + Value = 0; + while ((Character = *Buffer++) != '\0') { + Value = MultU64x32 (Value, 10) + (Character - '0'); + } + + return Value; +} +// +// random number generator +// +#define RANDOM_MULTIPLIER 2053 +#define RANDOM_ADD_IN_VALUE 19 + +VOID +SeedRandom ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 InitialSeed + ) +/*++ + + Routine Description: + Initialize the Seed for the random number generator + + Arguments: + + Returns: + none - + +--*/ +{ + if (Private != NULL) { + Private->RandomSeed = InitialSeed; + } +} + + +/** + Generate and return a pseudo-random number + + + @retval Number UINT16 random number + +**/ +UINT16 +Random ( + IN PXE_BASECODE_DEVICE *Private + ) +{ + UINTN Number; + + if (Private != NULL) { + Number = -(INTN) Private->RandomSeed * RANDOM_MULTIPLIER + RANDOM_ADD_IN_VALUE; + + return Private->RandomSeed = (UINT16) Number; + } else { + return 0; + } +} +// +// calculate the internet checksum (RFC 1071) +// return 16 bit ones complement of ones complement sum of 16 bit words +// + +/** + Calculate the internet checksum (see RFC 1071) + + @param Packet Buffer which contains the data to be checksummed + @param Length Length to be checksummed + + @retval Checksum Returns the 16 bit ones complement of ones + complement sum of 16 bit words + +**/ +UINT16 +IpChecksum ( + IN UINT16 *Packet, + IN UINTN Length + ) +{ + UINT32 Sum; + UINT8 Odd; + + Sum = 0; + Odd = (UINT8) (Length & 1); + Length >>= 1; + while (Length--) { + Sum += *Packet++; + } + + if (Odd) { + Sum += *(UINT8 *) Packet; + } + + Sum = (Sum & 0xffff) + (Sum >> 16); + // + // in case above carried + // + Sum += Sum >> 16; + + return (UINT16) (~ (UINT16) Sum); +} + + +/** + Calculate the internet checksum (see RFC 1071) + on a non contiguous header and data + + @param Header Buffer which contains the data to be checksummed + @param HeaderLen Length to be checksummed + @param Message Buffer which contains the data to be checksummed + @param MessageLen Length to be checksummed + + @retval Checksum Returns the 16 bit ones complement of ones + complement sum of 16 bit words + +**/ +UINT16 +IpChecksum2 ( + IN UINT16 *Header, + IN UINTN HeaderLen, + IN UINT16 *Message, + IN UINTN MessageLen + ) +{ + UINT32 Sum; + + Sum = (UINT16)~IpChecksum (Header, HeaderLen) + (UINT16)~IpChecksum (Message, MessageLen); + + // + // in case above carried + // + Sum += Sum >> 16; + + return (UINT16) (~ (UINT16) Sum); +} + + +/** + Adjust the internet checksum (see RFC 1071) on a single word update. + + @param OldChkSum Checksum previously calculated + @param OldWord Value + @param NewWord New Value + + @retval Checksum Returns the 16 bit ones complement of ones + complement sum of 16 bit words + +**/ +UINT16 +UpdateChecksum ( + IN UINT16 OldChksum, + IN UINT16 OldWord, + IN UINT16 NewWord + ) +{ + UINT32 sum; + + sum = ~OldChksum + NewWord - OldWord; + // + // in case above carried + // + sum += sum >> 16; + return (UINT16) (~ (UINT16) sum); +} + + +/** + See if a callback is in play + + @param Private Pointer to Pxe BaseCode Protocol + + @retval 0 Callbacks are active on the handle + @retval 1 Callbacks are not active on the handle + +**/ +STATIC +BOOLEAN +SetMakeCallback ( + IN PXE_BASECODE_DEVICE *Private + ) +{ + Private->EfiBc.Mode->MakeCallbacks = (BOOLEAN) (gBS->HandleProtocol ( + Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID *) &Private->CallbackProtocolPtr + ) == EFI_SUCCESS); + + DEBUG ( + (DEBUG_INFO, + "\nMode->MakeCallbacks == %d ", + Private->EfiBc.Mode->MakeCallbacks) + ); + + DEBUG ( + (DEBUG_INFO, + "\nPrivate->CallbackProtocolPtr == %xh ", + Private->CallbackProtocolPtr) + ); + + if (Private->CallbackProtocolPtr != NULL) { + DEBUG ( + (DEBUG_INFO, + "\nCallbackProtocolPtr->Revision = %xh ", + Private->CallbackProtocolPtr->Revision) + ); + + DEBUG ( + (DEBUG_INFO, + "\nCallbackProtocolPtr->Callback = %xh ", + Private->CallbackProtocolPtr->Callback) + ); + } + + return Private->EfiBc.Mode->MakeCallbacks; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Routine which does an SNP->Receive over a timeout period and doing callbacks + + @param Private Pointer to Pxe BaseCode Protocol + @param Function What PXE function to callback + @param TimeoutEvent Timer event that will trigger when we have waited + too long for an incoming packet + @param HeaderSizePtr Pointer to the size of the Header size + @param BufferSizePtr Pointer to the size of the Buffer size + @param ProtocolPtr The protocol to sniff for (namely, UDP/TCP/etc) + + @retval 0 Something was returned + @retval !0 Like there was nothing to receive + (EFI_TIMEOUT/NOT_READY) + +**/ +EFI_STATUS +WaitForReceive ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN EFI_EVENT TimeoutEvent, + IN OUT UINTN *HeaderSizePtr, + IN OUT UINTN *BufferSizePtr, + IN OUT UINT16 *ProtocolPtr + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_PXE_CALLBACK CallbackPtr; + EFI_STATUS StatCode; + EFI_EVENT CallbackEvent; + + // + // Initialize pointer to SNP interface + // + SnpPtr = Private->SimpleNetwork; + + // + // Initialize pointer to PxeBc callback routine - if any + // + CallbackPtr = (Private->EfiBc.Mode->MakeCallbacks) ? Private->CallbackProtocolPtr->Callback : NULL; + + // + // Create callback event and set timer + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &CallbackEvent + ); + + if (EFI_ERROR (StatCode)) { + return EFI_DEVICE_ERROR; + } + + // + // every 100 milliseconds + // + StatCode = gBS->SetTimer ( + CallbackEvent, + TimerPeriodic, + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (CallbackEvent); + return EFI_DEVICE_ERROR; + } + // + // Loop until a packet is received or a receive error is detected or + // a callback abort is detected or a timeout event occurs. + // + for (;;) + { + // + // Poll for received packet. + // + *BufferSizePtr = BUFFER_ALLOCATE_SIZE; + + StatCode = SnpPtr->Receive ( + SnpPtr, + HeaderSizePtr, + BufferSizePtr, + Private->ReceiveBufferPtr, + 0, + 0, + ProtocolPtr + ); + + if (!EFI_ERROR (StatCode)) { + // + // Packet was received. Make received callback then return. + // + if (CallbackPtr != NULL) { + StatCode = CallbackPtr ( + Private->CallbackProtocolPtr, + Function, + TRUE, + (UINT32) *BufferSizePtr, + (EFI_PXE_BASE_CODE_PACKET *) Private->ReceiveBufferPtr + ); + + if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + StatCode = EFI_ABORTED; + } else { + StatCode = EFI_SUCCESS; + } + } + + break; + } + + if (StatCode != EFI_NOT_READY) { + break; + } + // + // Check for callback event. + // + if (!EFI_ERROR (gBS->CheckEvent (CallbackEvent))) { + // + // Make periodic callback if callback pointer is initialized. + // + if (CallbackPtr != NULL) { + StatCode = CallbackPtr ( + Private->CallbackProtocolPtr, + Function, + FALSE, + 0, + NULL + ); + + // + // Abort if directed to by callback routine. + // + if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + StatCode = EFI_ABORTED; + break; + } + } + } + // + // Check for timeout event. + // + if (TimeoutEvent == 0) { + StatCode = EFI_TIMEOUT; + break; + } + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + StatCode = EFI_TIMEOUT; + break; + } + // + // Check IGMP timer events. + // + IgmpCheckTimers (Private); + } + + gBS->CloseEvent (CallbackEvent); + + return StatCode; +} + + +/** + Routine which does an SNP->Transmit of a buffer + + @param Private Pointer to Pxe BaseCode Protocol + @param HeaderPtr Pointer to the buffer + @param PacketPtr Pointer to the packet to send + @param PacketLen The length of the entire packet to send + @param HardwareAddr Pointer to the MAC address of the destination + @param MediaProtocol What type of frame to create (RFC 1700) - IE. + Ethernet + @param Function What PXE function to callback + + @retval 0 Something was sent + @retval !0 An error was encountered during sending of a packet + +**/ +EFI_STATUS +SendPacket ( + PXE_BASECODE_DEVICE *Private, + VOID *HeaderPtr, + VOID *PacketPtr, + INTN PacketLen, + VOID *HardwareAddr, + UINT16 MediaProtocol, + IN EFI_PXE_BASE_CODE_FUNCTION Function + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + EFI_PXE_CALLBACK CallbackPtr; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + UINT32 IntStatus; + VOID *TxBuf; + + // + // + // + CallbackPtr = Private->EfiBc.Mode->MakeCallbacks ? Private->CallbackProtocolPtr->Callback : 0; + + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // clear prior interrupt status + // + StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, 0); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #1 %xh (%r)", + StatCode, + StatCode) + ); + return StatCode; + } + + Private->DidTransmit = FALSE; + + if (CallbackPtr != NULL) { + if (CallbackPtr ( + Private->CallbackProtocolPtr, + Function, + FALSE, + (UINT32) PacketLen, + PacketPtr + ) != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #2 %xh (%r)", + EFI_ABORTED, + EFI_ABORTED) + ); + return EFI_ABORTED; + } + } + // + // put packet in transmit queue + // headersize should be zero if not filled in + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not create transmit timeout event. %r\n", + StatCode) + ); + return EFI_DEVICE_ERROR; + } + + // + // 5 milliseconds + // + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + 50000 + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not set transmit timeout event timer. %r\n", + StatCode) + ); + gBS->CloseEvent (TimeoutEvent); + return EFI_DEVICE_ERROR; + } + + for (;;) { + StatCode = SnpPtr->Transmit ( + SnpPtr, + (UINTN) SnpPtr->Mode->MediaHeaderSize, + (UINTN) (PacketLen + SnpPtr->Mode->MediaHeaderSize), + HeaderPtr, + &SnpModePtr->CurrentAddress, + (EFI_MAC_ADDRESS *) HardwareAddr, + &MediaProtocol + ); + + if (StatCode != EFI_NOT_READY) { + break; + } + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + StatCode = EFI_TIMEOUT; + break; + } + } + + gBS->CloseEvent (TimeoutEvent); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #3 %xh (%r)", + StatCode, + StatCode) + ); + return StatCode; + } + // + // remove transmit buffer from snp's unused queue + // done this way in case someday things are buffered and we don't get it back + // immediately + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not create transmit status timeout event. %r\n", + StatCode) + ); + return EFI_DEVICE_ERROR; + } + + // + // 5 milliseconds + // + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + 50000 + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not set transmit status timeout event timer. %r\n", + StatCode) + ); + gBS->CloseEvent (TimeoutEvent); + return EFI_DEVICE_ERROR; + } + + for (;;) { + StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, &TxBuf); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #4 %xh (%r)", + StatCode, + StatCode) + ); + break; + } + + if (IntStatus & EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT) { + Private->DidTransmit = TRUE; + } + + if (TxBuf != NULL) { + break; + } + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + StatCode = EFI_TIMEOUT; + break; + } + } + + gBS->CloseEvent (TimeoutEvent); + + return StatCode; +} +// +// +// + +/** + + +**/ +EFI_BIS_PROTOCOL * +PxebcBisStart ( + IN PXE_BASECODE_DEVICE *Private, + OUT BIS_APPLICATION_HANDLE *BisAppHandle, + OUT OPTIONAL EFI_BIS_DATA **BisDataSigInfo + ) +{ + EFI_STATUS EfiStatus; + EFI_HANDLE BisHandleBuffer; + UINTN BisHandleCount; + EFI_BIS_PROTOCOL *BisPtr; + EFI_BIS_VERSION BisInterfaceVersion; + BOOLEAN BisCheckFlag; + + BisHandleCount = sizeof (EFI_HANDLE); + BisCheckFlag = FALSE; + + // + // Locate BIS protocol handle (if present). + // If BIS protocol handle is not found, return NULL. + // + DEBUG ((DEBUG_INFO, "\ngBS->LocateHandle() ")); + + EfiStatus = gBS->LocateHandle ( + ByProtocol, + &gEfiBisProtocolGuid, + NULL, + &BisHandleCount, + &BisHandleBuffer + ); + + if (EFI_ERROR (EfiStatus)) { + // + // Any error means that there is no BIS. + // Note - It could mean that there are more than + // one BIS protocols installed, but that scenario + // is not yet supported. + // + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n gBS->LocateHandle() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + return NULL; + } + + if (BisHandleCount != sizeof BisHandleBuffer) { + // + // This really should never happen, but I am paranoid. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart() BisHandleCount != %d\n", + sizeof BisHandleBuffer) + ); + + return NULL; + } + + DEBUG ((DEBUG_INFO, "BIS handle found.")); + + // + // Locate BIS protocol interface. + // If the BIS protocol interface cannot be found, return NULL. + // + DEBUG ((DEBUG_INFO, "\ngBS->HandleProtocol() ")); + + EfiStatus = gBS->HandleProtocol ( + BisHandleBuffer, + &gEfiBisProtocolGuid, + (VOID **) &BisPtr + ); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n gBS->HandleProtocol() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + return NULL; + } + + if (BisPtr == NULL) { + // + // This really should never happen. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart()""\n gBS->HandleProtocoL() ""BIS protocol interface pointer is NULL!\n") + ); + + return NULL; + } + + DEBUG ((DEBUG_INFO, "BIS protocol interface found.")); + + // + // Check that all of the BIS API function pointers are not NULL. + // + if (BisPtr->Initialize == NULL || + BisPtr->Shutdown == NULL || + BisPtr->Free == NULL || + BisPtr->GetBootObjectAuthorizationCertificate == NULL || + BisPtr->GetBootObjectAuthorizationCheckFlag == NULL || + BisPtr->GetBootObjectAuthorizationUpdateToken == NULL || + BisPtr->GetSignatureInfo == NULL || + BisPtr->UpdateBootObjectAuthorization == NULL || + BisPtr->VerifyBootObject == NULL || + BisPtr->VerifyObjectWithCredential == NULL + ) { + DEBUG ( + ( + DEBUG_NET, + "\nPxebcBisStart()""\n BIS protocol interface is invalid." + "\n At least one BIS protocol function pointer is NULL.\n" + ) + ); + + return NULL; + } + // + // Initialize BIS. + // If BIS does not initialize, return NULL. + // + DEBUG ((DEBUG_INFO, "\nBisPtr->Initialize() ")); + + BisInterfaceVersion.Major = BIS_VERSION_1; + + EfiStatus = BisPtr->Initialize ( + BisPtr, + BisAppHandle, + &BisInterfaceVersion, + NULL + ); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BisPtr->Initialize() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + return NULL; + } + + DEBUG ( + (DEBUG_INFO, + " BIS version: %d.%d", + BisInterfaceVersion.Major, + BisInterfaceVersion.Minor) + ); + + // + // If the requested BIS API version is not supported, + // shutdown BIS and return NULL. + // + if (BisInterfaceVersion.Major != BIS_VERSION_1) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BIS version %d.%d not supported by PXE BaseCode.\n", + BisInterfaceVersion.Major, + BisInterfaceVersion.Minor) + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + // + // Get BIS check flag. + // If the BIS check flag cannot be read, shutdown BIS and return NULL. + // + DEBUG ((DEBUG_INFO, "\nBisPtr->GetBootObjectAuthorizationCheckFlag() ")); + + EfiStatus = BisPtr->GetBootObjectAuthorizationCheckFlag (*BisAppHandle, &BisCheckFlag); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BisPtr->GetBootObjectAuthorizationCheckFlag() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + // + // If the BIS check flag is FALSE, shutdown BIS and return NULL. + // + if (!BisCheckFlag) { + DEBUG ((DEBUG_INFO, "\nBIS check flag is FALSE.\n")); + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } else { + DEBUG ((DEBUG_INFO, "\nBIS check flag is TRUE.")); + } + // + // Early out if caller does not want signature information. + // + if (BisDataSigInfo == NULL) { + return BisPtr; + } + // + // Get BIS signature information. + // If the signature information cannot be read or is invalid, + // shutdown BIS and return NULL. + // + DEBUG ((DEBUG_INFO, "\nBisPtr->GetSignatureInfo() ")); + + EfiStatus = BisPtr->GetSignatureInfo (*BisAppHandle, BisDataSigInfo); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BisPtr_GetSignatureInfo() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + + if (*BisDataSigInfo == NULL) { + // + // This should never happen. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart()""\n BisPtr->GetSignatureInfo() Data pointer is NULL!\n") + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + + if ((*BisDataSigInfo)->Length < sizeof (EFI_BIS_SIGNATURE_INFO) || + (*BisDataSigInfo)->Length % sizeof (EFI_BIS_SIGNATURE_INFO) || + (*BisDataSigInfo)->Length > sizeof (EFI_BIS_SIGNATURE_INFO) * 63 + ) { + // + // This should never happen. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart()""\n BisPtr->GetSignatureInfo() Invalid BIS siginfo length.\n") + ); + + BisPtr->Free (*BisAppHandle, *BisDataSigInfo); + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + + return BisPtr; +} + + +/** + + +**/ +VOID +PxebcBisStop ( + EFI_BIS_PROTOCOL *BisPtr, + BIS_APPLICATION_HANDLE BisAppHandle, + EFI_BIS_DATA *BisDataSigInfo + ) +{ + if (BisPtr == NULL) { + return ; + } + // + // Free BIS allocated resources and shutdown BIS. + // Return TRUE - BIS support is officially detected. + // + if (BisDataSigInfo != NULL) { + BisPtr->Free (BisAppHandle, BisDataSigInfo); + } + + BisPtr->Shutdown (BisAppHandle); +} + + +/** + + @return TRUE := verified + @return FALSE := not verified + +**/ +BOOLEAN +PxebcBisVerify ( + PXE_BASECODE_DEVICE *Private, + VOID *FileBuffer, + UINTN FileLength, + VOID *CredentialBuffer, + UINTN CredentialLength + ) +{ + EFI_BIS_PROTOCOL *BisPtr; + BIS_APPLICATION_HANDLE BisAppHandle; + EFI_BIS_DATA FileData; + EFI_BIS_DATA CredentialData; + EFI_STATUS EfiStatus; + BOOLEAN IsVerified; + + if (Private == NULL || FileBuffer == NULL || FileLength == 0 || CredentialBuffer == NULL || CredentialLength == 0) { + return FALSE; + } + + BisPtr = PxebcBisStart (Private, &BisAppHandle, NULL); + + if (BisPtr == NULL) { + return FALSE; + } + + FileData.Length = (UINT32) FileLength; + FileData.Data = FileBuffer; + CredentialData.Length = (UINT32) CredentialLength; + CredentialData.Data = CredentialBuffer; + + EfiStatus = BisPtr->VerifyBootObject ( + BisAppHandle, + &CredentialData, + &FileData, + &IsVerified + ); + + PxebcBisStop (BisPtr, BisAppHandle, NULL); + + return (BOOLEAN) ((EFI_ERROR (EfiStatus)) ? FALSE : (IsVerified ? TRUE : FALSE)); +} + + +/** + + @return TRUE := BIS present + @return FALSE := BIS not present + +**/ +BOOLEAN +PxebcBisDetect ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_BIS_PROTOCOL *BisPtr; + BIS_APPLICATION_HANDLE BisAppHandle; + EFI_BIS_DATA *BisDataSigInfo; + + BisPtr = PxebcBisStart (Private, &BisAppHandle, &BisDataSigInfo); + + if (BisPtr == NULL) { + return FALSE; + } + + PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo); + + return TRUE; +} + +static VOID *BCNotifyReg; + + +/** + Start and initialize the BaseCode protocol, Simple Network protocol and UNDI. + + @param Private Pointer to Pxe BaseCode Protocol + @param UseIPv6 Do we want to support IPv6? + + @return EFI_SUCCESS + @return EFI_INVALID_PARAMETER + @return EFI_UNSUPPORTED + @return EFI_ALREADY_STARTED + @return EFI_OUT_OF_RESOURCES + @return Status is also returned from SNP.Start() and SNP.Initialize(). + +**/ +EFI_STATUS +EFIAPI +BcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIPv6 + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + EFI_STATUS Status; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + // + // Make sure BaseCode is not already started. + // + if (This->Mode->Started) { + DEBUG ((DEBUG_WARN, "\nBcStart() BC is already started.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_ALREADY_STARTED; + } + +#if !SUPPORT_IPV6 + // + // Fail if IPv6 is requested and not supported. + // + if (UseIPv6) { + DEBUG ((DEBUG_WARN, "\nBcStart() IPv6 is not supported.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_UNSUPPORTED; + } +#endif + // + // Setup shortcuts to SNP protocol and data structure. + // + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // Start and initialize SNP. + // + if (SnpModePtr->State == EfiSimpleNetworkStopped) { + StatCode = (*SnpPtr->Start) (SnpPtr); + + if (SnpModePtr->State != EfiSimpleNetworkStarted) { + DEBUG ((DEBUG_WARN, "\nBcStart() Could not start SNP.\n")); + EfiReleaseLock (&Private->Lock); + return StatCode; + } + } + // + // acquire memory for mode and transmit/receive buffers + // + if (SnpModePtr->State == EfiSimpleNetworkStarted) { + StatCode = (*SnpPtr->Initialize) (SnpPtr, 0, 0); + + if (SnpModePtr->State != EfiSimpleNetworkInitialized) { + DEBUG ((DEBUG_WARN, "\nBcStart() Could not initialize SNP.")); + EfiReleaseLock (&Private->Lock); + return StatCode; + } + } + // + // Dump debug info. + // + DEBUG ((DEBUG_INFO, "\nBC Start()")); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->State %Xh", + SnpModePtr->State) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->HwAddressSize %Xh", + SnpModePtr->HwAddressSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MediaHeaderSize %Xh", + SnpModePtr->MediaHeaderSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MaxPacketSize %Xh", + SnpModePtr->MaxPacketSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MacAddressChangeable %Xh", + SnpModePtr->MacAddressChangeable) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MultipleTxSupported %Xh", + SnpModePtr->MultipleTxSupported) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->CurrentAddress %Xh", + *((UINTN *)&SnpModePtr->CurrentAddress)) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->BroadcastAddress %Xh", + *((UINTN *)&SnpModePtr->BroadcastAddress)) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->PermanentAddress %Xh", + *((UINTN *)&SnpModePtr->PermanentAddress)) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->NvRamSize %Xh", + SnpModePtr->NvRamSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->NvRamAccessSize %Xh", + SnpModePtr->NvRamAccessSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->ReceiveFilterMask %Xh", + SnpModePtr->ReceiveFilterMask) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->ReceiveFilterSetting %Xh", + SnpModePtr->ReceiveFilterSetting) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MCastFilterCount %Xh", + SnpModePtr->MCastFilterCount) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MCastFilter %Xh", + SnpModePtr->MCastFilter) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->IfType %Xh", + SnpModePtr->IfType) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MediaPresentSupported %Xh", + SnpModePtr->MediaPresentSupported) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MediaPresent %Xh", + SnpModePtr->MediaPresent) + ); + + // + // If media check is supported and there is no media, + // return error to caller. + // + if (SnpModePtr->MediaPresentSupported && !SnpModePtr->MediaPresent) { + DEBUG ((DEBUG_WARN, "\nBcStart() Media not present.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_NO_MEDIA; + } + // + // Allocate Tx/Rx buffers + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + BUFFER_ALLOCATE_SIZE, + &Private->TransmitBufferPtr + ); + + if (!EFI_ERROR (Status)) { + ZeroMem (Private->TransmitBufferPtr, BUFFER_ALLOCATE_SIZE); + } else { + DEBUG ((DEBUG_NET, "\nBcStart() Could not alloc TxBuf.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + BUFFER_ALLOCATE_SIZE, + &Private->ReceiveBufferPtr + ); + + if (!EFI_ERROR (Status)) { + ZeroMem (Private->ReceiveBufferPtr, BUFFER_ALLOCATE_SIZE); + } else { + DEBUG ((DEBUG_NET, "\nBcStart() Could not alloc RxBuf.\n")); + gBS->FreePool (Private->TransmitBufferPtr); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + 256, + &Private->TftpErrorBuffer + ); + + if (EFI_ERROR (Status)) { + gBS->FreePool (Private->ReceiveBufferPtr); + gBS->FreePool (Private->TransmitBufferPtr); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePool (EfiBootServicesData, 256, &Private->TftpAckBuffer); + + if (EFI_ERROR (Status)) { + gBS->FreePool (Private->TftpErrorBuffer); + gBS->FreePool (Private->ReceiveBufferPtr); + gBS->FreePool (Private->TransmitBufferPtr); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + // + // Initialize private BaseCode instance data + // + do { + Private->RandomPort = (UINT16) (Private->RandomPort + PXE_RND_PORT_LOW + Random (Private)); + } while (Private->RandomPort < PXE_RND_PORT_LOW); + + Private->Igmpv1TimeoutEvent = NULL; + Private->UseIgmpv1Reporting = TRUE; + Private->IpLength = IP_ADDRESS_LENGTH (Private->EfiBc.Mode); + + // + // Initialize Mode structure + // + ZeroMem (Private->EfiBc.Mode, sizeof (EFI_PXE_BASE_CODE_MODE)); + // + // check for callback protocol and set boolean + // + SetMakeCallback (Private); + Private->EfiBc.Mode->Started = TRUE; + Private->EfiBc.Mode->TTL = DEFAULT_TTL; + Private->EfiBc.Mode->ToS = DEFAULT_ToS; + Private->EfiBc.Mode->UsingIpv6 = UseIPv6; + + // + // Set to PXE_TRUE by the BC constructor if this BC implementation + // supports IPv6. + // + Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6; + + Private->EfiBc.Mode->Ipv6Available = FALSE; + // + // Set to TRUE by the BC constructor if this BC implementation + // supports BIS. + // + Private->EfiBc.Mode->BisSupported = TRUE; + Private->EfiBc.Mode->BisDetected = PxebcBisDetect (Private); + + // + // This field is set to PXE_TRUE by the BC Start() function. When this + // field is PXE_TRUE, ARP packets are sent as needed to get IP and MAC + // addresses. This can cause unexpected delays in the DHCP(), Discover() + // and MTFTP() functions. Setting this to PXE_FALSE will cause these + // functions to fail if the required IP/MAC information is not in the + // ARP cache. The value of this field can be changed by an application + // at any time. + // + Private->EfiBc.Mode->AutoArp = TRUE; + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return EFI_SUCCESS; +} + + +/** + Stop the BaseCode protocol, Simple Network protocol and UNDI. + + @param Private Pointer to Pxe BaseCode Protocol + + @retval 0 Successfully stopped + @retval !0 Failed + +**/ +EFI_STATUS +EFIAPI +BcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +{ + // + // Lock the instance data + // + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + PxebcMode = Private->EfiBc.Mode; + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // Issue BC command + // + StatCode = EFI_NOT_STARTED; + + if (SnpModePtr->State == EfiSimpleNetworkInitialized) { + StatCode = (*SnpPtr->Shutdown) (SnpPtr); + } + + if (SnpModePtr->State == EfiSimpleNetworkStarted) { + StatCode = (*SnpPtr->Stop) (SnpPtr); + } + + if (Private->TransmitBufferPtr != NULL) { + gBS->FreePool (Private->TransmitBufferPtr); + Private->TransmitBufferPtr = NULL; + } + + if (Private->ReceiveBufferPtr != NULL) { + gBS->FreePool (Private->ReceiveBufferPtr); + Private->ReceiveBufferPtr = NULL; + } + + if (Private->ArpBuffer != NULL) { + gBS->FreePool (Private->ArpBuffer); + Private->ArpBuffer = NULL; + } + + if (Private->TftpErrorBuffer != NULL) { + gBS->FreePool (Private->TftpErrorBuffer); + Private->TftpErrorBuffer = NULL; + } + + if (Private->TftpAckBuffer != NULL) { + gBS->FreePool (Private->TftpAckBuffer); + Private->TftpAckBuffer = NULL; + } + + if (Private->Igmpv1TimeoutEvent != NULL) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + } + + Private->FileSize = 0; + + if (!Private->EfiBc.Mode->Started) { + StatCode = EFI_NOT_STARTED; + } else { + Private->EfiBc.Mode->Started = FALSE; + } + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +const IPV4_ADDR AllSystemsGroup = { 224, 0, 0, 1 }; + + +/** + Set up the IP filter + + @param Private Pointer to Pxe BaseCode Protocol + @param Filter Pointer to the filter + + @retval 0 Successfully set the filter + @retval !0 Failed + +**/ +EFI_STATUS +IpFilter ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_IP_FILTER *Filter + ) +{ + EFI_STATUS StatCode; + EFI_MAC_ADDRESS MACadds[PXE_IP_FILTER_SIZE]; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + UINT32 Enable; + UINT32 Disable; + UINTN Index; + UINTN Index2; + + PxebcMode = Private->EfiBc.Mode; + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // validate input parameters + // must have a filter + // must not have any extra filter bits set + // + if (Filter == NULL || + (Filter->Filters &~FILTER_BITS) + // + // must not have a count which is too large or with no IP list + // + || + (Filter->IpCnt && (!Filter->IpList || Filter->IpCnt > PXE_IP_FILTER_SIZE)) + // + // must not have incompatible filters - promiscuous incompatible with anything else + // + || + ( + (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) && + ((Filter->Filters &~EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) || Filter->IpCnt) + ) + ) { + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #1")); + return EFI_INVALID_PARAMETER; + } + // + // promiscuous multicast incompatible with multicast in IP list + // + if (Filter->IpCnt && (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST)) { + for (Index = 0; Index < Filter->IpCnt; ++Index) { + if (IS_MULTICAST (&Filter->IpList[Index])) { + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #2")); + return EFI_INVALID_PARAMETER; + } + } + } + // + // leave groups for all those multicast which are no longer enabled + // + for (Index = 0; Index < PxebcMode->IpFilter.IpCnt; ++Index) { + if (!IS_MULTICAST (&PxebcMode->IpFilter.IpList[Index])) { + continue; + } + + for (Index2 = 0; Index2 < Filter->IpCnt; ++Index2) { + if (!CompareMem (&PxebcMode->IpFilter.IpList[Index], &Filter->IpList[Index2], IP_ADDRESS_LENGTH (PxebcMode))) { + // + // still enabled + // + break; + } + } + // + // if we didn't find it, remove from group + // + if (Index2 == Filter->IpCnt) { + IgmpLeaveGroup (Private, &PxebcMode->IpFilter.IpList[Index]); + } + } + // + // set enable bits, convert multicast ip adds, join groups + // allways leave receive broadcast enabled at hardware layer + // + Index2 = 0; + + if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) { + Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } else { + if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) { + Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } else { + Enable = EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + + for (Index = 0; Index < Filter->IpCnt; ++Index) { + PxebcMode->IpFilter.IpList[Index] = Filter->IpList[Index]; + + if (IS_MULTICAST (&Filter->IpList[Index])) { + EFI_IP_ADDRESS *TmpIp; + + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; + + // + // if this is the first group, add the all systems group to mcast list + // + if (!Index2) + { + TmpIp = (EFI_IP_ADDRESS *) &AllSystemsGroup; + --Index; + } else { + TmpIp = (EFI_IP_ADDRESS *) &Filter->IpList[Index]; + } + // + // get MAC address of IP + // + StatCode = (*SnpPtr->MCastIpToMac) (SnpPtr, PxebcMode->UsingIpv6, TmpIp, &MACadds[Index2++]); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_INFO, + "\nIpFilter() Exit #2 %Xh (%r)", + StatCode, + StatCode) + ); + return StatCode; + } + } else { + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + } + } + } + + if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) { + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + } + } + // + // if nothing changed, just return + // + DEBUG ( + (DEBUG_INFO, + "\nsnp->ReceiveFilterSetting == %Xh Filter->IpCnt == %Xh", + SnpModePtr->ReceiveFilterSetting, + Filter->IpCnt) + ); + + if (SnpModePtr->ReceiveFilterSetting == Enable && !Filter->IpCnt) { + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #4")); + return EFI_SUCCESS; + } + // + // disable those currently set but not set in new filter + // + Disable = SnpModePtr->ReceiveFilterSetting &~Enable; + + StatCode = SnpPtr->ReceiveFilters (SnpPtr, Enable, Disable, FALSE, Index2, MACadds); + + PxebcMode->IpFilter.IpCnt = Filter->IpCnt; + + // + // join groups for all multicast in list + // + for (Index = 0; Index < Filter->IpCnt; ++Index) { + if (IS_MULTICAST (&Filter->IpList[Index])) { + IgmpJoinGroup (Private, &Filter->IpList[Index]); + } + } + + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #5 %Xh (%r)", StatCode, StatCode)); + + return StatCode; +} + + +/** + Call the IP filter + + @param Private Pointer to Pxe BaseCode Protocol + @param Filter Pointer to the filter + + @retval 0 Successfully set the filter + @retval !0 Failed + +**/ +EFI_STATUS +EFIAPI +BcIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *Filter + ) +{ + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + UINTN Index; + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < Filter->IpCnt; ++Index) { + if ((Filter->IpList[Index].Addr[0]) == BROADCAST_IPv4) { + // + // The IP is a broadcast address. + // + return EFI_INVALID_PARAMETER; + } + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + if (Filter == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Issue BC command + // + StatCode = IpFilter (Private, Filter); + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + + +/** + Set the Base Code behavior parameters + + @param This Pointer to Pxe BaseCode Protocol + @param AutoArpPtr Boolean to do ARP stuff + @param SendGuidPtr Boolean whether or not to send GUID info + @param TimeToLivePtr Value for Total time to live + @param TypeOfServicePtr Value for Type of Service + @param MakeCallbackPtr Boolean to determine if we make callbacks + + @retval 0 Successfully set the parameters + @retval !0 Failed + +**/ +EFI_STATUS +EFIAPI +BcSetParameters ( + EFI_PXE_BASE_CODE_PROTOCOL *This, + BOOLEAN *AutoArpPtr, + BOOLEAN *SendGuidPtr, + UINT8 *TimeToLivePtr, + UINT8 *TypeOfServicePtr, + BOOLEAN *MakeCallbackPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_GUID TmpGuid; + UINT8 *SerialNumberPtr; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + DEBUG ((DEBUG_INFO, "\nSetParameters() Entry. ")); + + PxebcMode = Private->EfiBc.Mode; + StatCode = EFI_SUCCESS; + + if (SendGuidPtr != NULL) { + if (*SendGuidPtr) { + if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (&TmpGuid, &SerialNumberPtr) != EFI_SUCCESS) { + return EFI_INVALID_PARAMETER; + } + } + } + + if (MakeCallbackPtr != NULL) { + if (*MakeCallbackPtr) { + if (!SetMakeCallback (Private)) { + return EFI_INVALID_PARAMETER; + } + } + + PxebcMode->MakeCallbacks = *MakeCallbackPtr; + } + + if (AutoArpPtr != NULL) { + PxebcMode->AutoArp = *AutoArpPtr; + } + + if (SendGuidPtr != NULL) { + PxebcMode->SendGUID = *SendGuidPtr; + } + + if (TimeToLivePtr != NULL) { + PxebcMode->TTL = *TimeToLivePtr; + } + + if (TypeOfServicePtr != NULL) { + PxebcMode->ToS = *TypeOfServicePtr; + } + // + // Unlock the instance data + // + DEBUG ((DEBUG_INFO, "\nSetparameters() Exit = %xh ", StatCode)); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} +// +// ////////////////////////////////////////////////////////// +// +// BC Set Station IP Routine +// + +/** + Set the station IP address + + @param This Pointer to Pxe BaseCode Protocol + @param StationIpPtr Pointer to the requested IP address to set in base + code + @param SubnetMaskPtr Pointer to the requested subnet mask for the base + code + + @retval EFI_SUCCESS Successfully set the parameters + @retval EFI_NOT_STARTED BC has not started + +**/ +EFI_STATUS +EFIAPI +BcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *StationIpPtr, + IN EFI_IP_ADDRESS *SubnetMaskPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + UINT32 SubnetMask; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + StatCode = EFI_NOT_STARTED; + goto RELEASE_LOCK; + } + + PxebcMode = Private->EfiBc.Mode; + + if (!Private->GoodStationIp && ((StationIpPtr == NULL) || (SubnetMaskPtr == NULL))) { + // + // It's not allowed to only set one of the two addresses while there isn't a previous + // GOOD address configuration. + // + StatCode = EFI_INVALID_PARAMETER; + goto RELEASE_LOCK; + } + + if (SubnetMaskPtr != NULL) { + SubnetMask = SubnetMaskPtr->Addr[0]; + + if (SubnetMask & (SubnetMask + 1)) { + // + // the subnet mask is valid if it's with leading continuous 1 bits. + // + StatCode = EFI_INVALID_PARAMETER; + goto RELEASE_LOCK; + } + } else { + SubnetMaskPtr = &PxebcMode->SubnetMask; + SubnetMask = SubnetMaskPtr->Addr[0]; + } + + if (StationIpPtr == NULL) { + StationIpPtr = &PxebcMode->StationIp; + } + + if (!IS_INADDR_UNICAST (StationIpPtr) || + ((StationIpPtr->Addr[0] | SubnetMask) == BROADCAST_IPv4)) { + // + // The station IP is not a unicast address. + // + StatCode = EFI_INVALID_PARAMETER; + goto RELEASE_LOCK; + } + + PxebcMode->StationIp = *StationIpPtr; + PxebcMode->SubnetMask = *SubnetMaskPtr; + Private->GoodStationIp = TRUE; + +RELEASE_LOCK: + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + + return StatCode; +} + +EFI_DRIVER_BINDING_PROTOCOL mPxeBcDriverBinding = { + PxeBcDriverSupported, + PxeBcDriverStart, + PxeBcDriverStop, + 0xa, + NULL, + NULL +}; + + +/** + Test to see if this driver supports Controller. Any Controller + than contains a Snp protocol can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &SnpPtr, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Start the Base code driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + PXE_BASECODE_DEVICE *Private; + LOADFILE_DEVICE *pLF; + + // + // Allocate structures needed by BaseCode and LoadFile protocols. + // + Private = AllocateZeroPool (sizeof (PXE_BASECODE_DEVICE)); + + if (Private == NULL ) { + DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc PXE_BASECODE_DEVICE structure.\n")); + return EFI_OUT_OF_RESOURCES; + } + + pLF = AllocateZeroPool (sizeof (LOADFILE_DEVICE)); + if (pLF == NULL) { + DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc LOADFILE_DEVICE structure.\n")); + FreePool (Private); + return EFI_OUT_OF_RESOURCES; + } + + Private->EfiBc.Mode = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_MODE)); + if (Private->EfiBc.Mode == NULL) { + DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc Mode structure.\n")); + FreePool (Private); + FreePool (pLF); + return EFI_OUT_OF_RESOURCES; + } + // + // Lock access, just in case + // + EfiInitializeLock (&Private->Lock, TPL_CALLBACK); + EfiAcquireLock (&Private->Lock); + + EfiInitializeLock (&pLF->Lock, TPL_CALLBACK); + EfiAcquireLock (&pLF->Lock); + + // + // Initialize PXE structure + // + // + // First initialize the internal 'private' data that the application + // does not see. + // + Private->Signature = PXE_BASECODE_DEVICE_SIGNATURE; + Private->Handle = Controller; + + // + // Get the NII interface + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->NiiPtr, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + (VOID **) &Private->NiiPtr, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto PxeBcError; + } + } + // + // Get the Snp interface + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Private->SimpleNetwork, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto PxeBcError; + } + + // + // Next, initialize the external 'public' data that + // the application does see. + // + Private->EfiBc.Revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION; + Private->EfiBc.Start = BcStart; + Private->EfiBc.Stop = BcStop; + Private->EfiBc.Dhcp = BcDhcp; + Private->EfiBc.Discover = BcDiscover; + Private->EfiBc.Mtftp = BcMtftp; + Private->EfiBc.UdpWrite = BcUdpWrite; + Private->EfiBc.UdpRead = BcUdpRead; + Private->EfiBc.Arp = BcArp; + Private->EfiBc.SetIpFilter = BcIpFilter; + Private->EfiBc.SetParameters = BcSetParameters; + Private->EfiBc.SetStationIp = BcSetStationIP; + Private->EfiBc.SetPackets = BcSetPackets; + + // + // Initialize BaseCode Mode structure + // + Private->EfiBc.Mode->Started = FALSE; + Private->EfiBc.Mode->TTL = DEFAULT_TTL; + Private->EfiBc.Mode->ToS = DEFAULT_ToS; + Private->EfiBc.Mode->UsingIpv6 = FALSE; + Private->EfiBc.Mode->AutoArp = TRUE; + + // + // Set to PXE_TRUE by the BC constructor if this BC + // implementation supports IPv6. + // + Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6; + +#if SUPPORT_IPV6 + Private->EfiBc.Mode->Ipv6Available = Private->NiiPtr->Ipv6Supported; +#else + Private->EfiBc.Mode->Ipv6Available = FALSE; +#endif + // + // Set to TRUE by the BC constructor if this BC + // implementation supports BIS. + // + Private->EfiBc.Mode->BisSupported = TRUE; + Private->EfiBc.Mode->BisDetected = PxebcBisDetect (Private); + + // + // Initialize LoadFile structure. + // + pLF->Signature = LOADFILE_DEVICE_SIGNATURE; + pLF->LoadFile.LoadFile = LoadFile; + pLF->Private = Private; + + // + // Install protocol interfaces. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiPxeBaseCodeProtocolGuid, + &Private->EfiBc, + &gEfiLoadFileProtocolGuid, + &pLF->LoadFile, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + goto PxeBcError; + } + // + // Release locks. + // + EfiReleaseLock (&pLF->Lock); + EfiReleaseLock (&Private->Lock); + return Status; + +PxeBcError: ; + gBS->FreePool (Private->EfiBc.Mode); + gBS->FreePool (Private); + gBS->FreePool (pLF); + return Status; +} + + +/** + Stop the Base code driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param NumberOfChildren Not used + @param ChildHandleBuffer Not used + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_LOAD_FILE_PROTOCOL *LfProtocol; + LOADFILE_DEVICE *LoadDevice; + + // + // Get our context back. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiLoadFileProtocolGuid, + (VOID **) &LfProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + LoadDevice = EFI_LOAD_FILE_DEV_FROM_THIS (LfProtocol); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiLoadFileProtocolGuid, + &LoadDevice->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &LoadDevice->Private->EfiBc, + NULL + ); + + if (!EFI_ERROR (Status)) { + + Status = gBS->CloseProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->FreePool (LoadDevice->Private->EfiBc.Mode); + gBS->FreePool (LoadDevice->Private); + gBS->FreePool (LoadDevice); + } + + return Status; +} + + +/** + Initialize the base code drivers and install the driver binding + + Standard EFI Image Entry + + @retval EFI_SUCCESS This driver was successfully bound + +**/ +EFI_STATUS +EFIAPI +InitializeBCDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Initialize EFI library + // + Status = EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &mPxeBcDriverBinding, + NULL, + COMPONENT_NAME, + NULL, + NULL + ); + + InitArpHeader (); + OptionsStrucInit (); + + return EFI_SUCCESS; +} + +/* eof - bc.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h new file mode 100644 index 0000000000..71d1518656 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h @@ -0,0 +1,564 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + bc.h + +Abstract: + + +**/ + +#ifndef _BC_H +#define _BC_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CALLBACK_INTERVAL 100 // ten times a second +#define FILTER_BITS (EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | \ + EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST | \ + EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS | \ + EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST \ + ) + +#define WAIT_TX_TIMEOUT 1000 + +#define SUPPORT_IPV6 0 + +#define PXE_BASECODE_DEVICE_SIGNATURE 'pxed' + +// +// Determine the classes of IPv4 address +// +#define IS_CLASSA_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0x80) == 0x00) +#define IS_CLASSB_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0xc0) == 0x80) +#define IS_CLASSC_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0xe0) == 0xc0) +#define IS_INADDR_UNICAST(x) ((IS_CLASSA_IPADDR(x) || IS_CLASSB_IPADDR(x) || IS_CLASSC_IPADDR(x)) && (((EFI_IP_ADDRESS*)x)->Addr[0] != 0) ) + +// +// Definitions for internet group management protocol version 2 message +// structure +// Per RFC 2236, November 1997 +// +#pragma pack(1) + +typedef struct { + UINT8 Type; + UINT8 MaxRespTime; // in tenths of a second + UINT16 Checksum; // ones complement of ones complement sum of + // 16 bit words of message + UINT32 GroupAddress; // for general query, all systems group, + // for group specific, the group +} IGMPV2_MESSAGE; + +#define IGMP_TYPE_QUERY 0x11 +#define IGMP_TYPE_REPORT 0x16 +#define IGMP_TYPE_V1REPORT 0x12 +#define IGMP_TYPE_LEAVE_GROUP 0x17 + +#define IGMP_DEFAULT_MAX_RESPONSE_TIME 10 // 10 second default +#pragma pack() + +#define MAX_MCAST_GROUPS 8 // most we allow ourselves to join at once +#define MAX_OFFERS 16 + +typedef struct { + UINTN Signature; + EFI_LOCK Lock; + BOOLEAN ShowErrorMessages; + EFI_TCP_PROTOCOL Tcp; + EFI_PXE_BASE_CODE_PROTOCOL EfiBc; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *CallbackProtocolPtr; + EFI_HANDLE Handle; + + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiPtr; + EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork; + UINT8 *TransmitBufferPtr; + UINT8 *ReceiveBufferPtr; + EFI_PXE_BASE_CODE_FUNCTION Function; + + UINTN OldestArpEntry; + UINTN MCastGroupCount; + EFI_EVENT Igmpv1TimeoutEvent; + BOOLEAN UseIgmpv1Reporting; + EFI_EVENT IgmpGroupEvent[MAX_MCAST_GROUPS]; + UINT16 RandomPort; + +#if SUPPORT_IPV6 + // + // TBD + // +#else + UINT32 MCastGroup[MAX_MCAST_GROUPS]; +#endif + + BOOLEAN GoodStationIp; + BOOLEAN DidTransmit; + UINTN IpLength; + VOID *DhcpPacketBuffer; + UINTN FileSize; + VOID *BootServerReceiveBuffer; + EFI_IP_ADDRESS ServerIp; + + // + // work area + // for dhcp + // + VOID *ReceiveBuffers; + VOID *TransmitBuffer; + UINTN NumOffersReceived; + UINT16 TotalSeconds; + + // + // arrays for different types of offers + // + UINT8 ServerCount[4]; + UINT8 OfferCount[4][MAX_OFFERS]; + UINT8 GotBootp; + UINT8 GotProxy[4]; + UINT8 BinlProxies[MAX_OFFERS]; + + UINT8 *ArpBuffer; + UINT8 *TftpAckBuffer; + UINT8 *TftpErrorBuffer; + IGMPV2_MESSAGE IgmpMessage; + BOOLEAN BigBlkNumFlag; + UINT8 Timeout; + UINT16 RandomSeed; +} PXE_BASECODE_DEVICE; + +// +// type index +// +#define DHCP_ONLY_IX 0 +#define PXE10_IX 1 +#define WfM11a_IX 2 +#define BINL_IX 3 + +#define PXE_RND_PORT_LOW 2070 + +// +// +// +#define LOADFILE_DEVICE_SIGNATURE 'pxel' + +typedef struct { + UINTN Signature; + EFI_LOCK Lock; + EFI_LOAD_FILE_PROTOCOL LoadFile; + PXE_BASECODE_DEVICE *Private; +} LOADFILE_DEVICE; + +#define EFI_BASE_CODE_DEV_FROM_THIS(a) CR (a, PXE_BASECODE_DEVICE, efi_bc, PXE_BASECODE_DEVICE_SIGNATURE); + +#define EFI_BASE_CODE_DEV_FROM_TCP(a) CR (a, PXE_BASECODE_DEVICE, Tcp, PXE_BASECODE_DEVICE_SIGNATURE); + +#define EFI_LOAD_FILE_DEV_FROM_THIS(a) CR (a, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE) + +EFI_BIS_PROTOCOL * +PxebcBisStart ( + PXE_BASECODE_DEVICE *Private, + BIS_APPLICATION_HANDLE *BisAppHandle, + EFI_BIS_DATA **BisDataSigInfo + ) +; + +VOID +PxebcBisStop ( + EFI_BIS_PROTOCOL *Bis, + BIS_APPLICATION_HANDLE BisAppHandle, + EFI_BIS_DATA *BisDataSigInfo + ) +; + +BOOLEAN +PxebcBisVerify ( + PXE_BASECODE_DEVICE *Private, + VOID *FileBuffer, + UINTN FileBufferLength, + VOID *CredentialBuffer, + UINTN CredentialBufferLength + ) +; + +BOOLEAN +PxebcBisDetect ( + PXE_BASECODE_DEVICE *Private + ) +; + +// +// Global Variables +// +extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName; + +// +// ////////////////////////////////////////////////////////// +// +// prototypes +// + +/** + Initialize the base code drivers and install the driver binding + + Standard EFI Image Entry + + @retval EFI_SUCCESS This driver was successfully bound + +**/ +EFI_STATUS +EFIAPI +InitializeBCDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +; + +EFI_STATUS +EFIAPI +BcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIpv6 + ) +; + +EFI_STATUS +EFIAPI +BcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +; + +EFI_STATUS +EFIAPI +BcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +; + +EFI_STATUS +EFIAPI +BcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO * Info OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSize, + IN UINTN *BlockSize OPTIONAL, + IN EFI_IP_ADDRESS * ServerIp, + IN UINT8 *Filename, + IN EFI_PXE_BASE_CODE_MTFTP_INFO * Info OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +; + +EFI_STATUS +EFIAPI +BcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL + IN EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcTcpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN UINT16 *UrgentPointer, + IN UINT32 *SequenceNumber, + IN UINT32 *AckNumber, + IN UINT16 *HlenResCode, + IN UINT16 *Window, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_TCP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL + IN EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_TCP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcTcpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_TCP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_TCP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_IP_ADDRESS * IpAddr, + IN EFI_MAC_ADDRESS * MacAddr OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter + ) +; + +EFI_STATUS +EFIAPI +BcSetParameters ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN BOOLEAN *NewAutoArp, OPTIONAL + IN BOOLEAN *NewSendGUID, OPTIONAL + IN UINT8 *NewTTL, OPTIONAL + IN UINT8 *NewToS, OPTIONAL + IN BOOLEAN *NewMakeCallback OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_IP_ADDRESS * NewStationIp, OPTIONAL + IN EFI_IP_ADDRESS * NewSubnetMask OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + BOOLEAN *NewDhcpDiscoverValid, OPTIONAL + BOOLEAN *NewDhcpAckReceived, OPTIONAL + BOOLEAN *NewProxyOfferReceived, OPTIONAL + BOOLEAN *NewPxeDiscoverValid, OPTIONAL + BOOLEAN *NewPxeReplyReceived, OPTIONAL + BOOLEAN *NewPxeBisReplyReceived, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +LoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +; + +EFI_STATUS +PxeBcLibGetSmbiosSystemGuidAndSerialNumber ( + IN EFI_GUID *SystemGuid, + OUT CHAR8 **SystemSerialNumber + ) +; + +#ifdef EFI_SIZE_REDUCTION_APPLIED + #define COMPONENT_NAME_CODE(code) + #define COMPONENT_NAME NULL +#else + #define COMPONENT_NAME_CODE(code) code + #define COMPONENT_NAME &gPxeBcComponentName +#endif + + +// +// Define SMBIOS tables. +// +#pragma pack(1) +typedef struct { + UINT8 AnchorString[4]; + UINT8 EntryPointStructureChecksum; + UINT8 EntryPointLength; + UINT8 MajorVersion; + UINT8 MinorVersion; + UINT16 MaxStructureSize; + UINT8 EntryPointRevision; + UINT8 FormattedArea[5]; + UINT8 IntermediateAnchorString[5]; + UINT8 IntermediateChecksum; + UINT16 TableLength; + UINT32 TableAddress; + UINT16 NumberOfSmbiosStructures; + UINT8 SmbiosBcdRevision; +} SMBIOS_STRUCTURE_TABLE; + +// +// Please note that SMBIOS structures can be odd byte aligned since the +// unformated section of each record is a set of arbitrary size strings. +// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 Handle[2]; +} SMBIOS_HEADER; + +typedef UINT8 SMBIOS_STRING; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Vendor; + SMBIOS_STRING BiosVersion; + UINT8 BiosSegment[2]; + SMBIOS_STRING BiosReleaseDate; + UINT8 BiosSize; + UINT8 BiosCharacteristics[8]; +} SMBIOS_TYPE0; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Manufacturer; + SMBIOS_STRING ProductName; + SMBIOS_STRING Version; + SMBIOS_STRING SerialNumber; + + // + // always byte copy this data to prevent alignment faults! + // + EFI_GUID Uuid; + + UINT8 WakeUpType; +} SMBIOS_TYPE1; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Manufacturer; + SMBIOS_STRING ProductName; + SMBIOS_STRING Version; + SMBIOS_STRING SerialNumber; +} SMBIOS_TYPE2; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Manufacturer; + UINT8 Type; + SMBIOS_STRING Version; + SMBIOS_STRING SerialNumber; + SMBIOS_STRING AssetTag; + UINT8 BootupState; + UINT8 PowerSupplyState; + UINT8 ThermalState; + UINT8 SecurityStatus; + UINT8 OemDefined[4]; +} SMBIOS_TYPE3; + +typedef struct { + SMBIOS_HEADER Hdr; + UINT8 Socket; + UINT8 ProcessorType; + UINT8 ProcessorFamily; + SMBIOS_STRING ProcessorManufacture; + UINT8 ProcessorId[8]; + SMBIOS_STRING ProcessorVersion; + UINT8 Voltage; + UINT8 ExternalClock[2]; + UINT8 MaxSpeed[2]; + UINT8 CurrentSpeed[2]; + UINT8 Status; + UINT8 ProcessorUpgrade; + UINT8 L1CacheHandle[2]; + UINT8 L2CacheHandle[2]; + UINT8 L3CacheHandle[2]; +} SMBIOS_TYPE4; + +typedef union { + SMBIOS_HEADER *Hdr; + SMBIOS_TYPE0 *Type0; + SMBIOS_TYPE1 *Type1; + SMBIOS_TYPE2 *Type2; + SMBIOS_TYPE3 *Type3; + SMBIOS_TYPE4 *Type4; + UINT8 *Raw; +} SMBIOS_STRUCTURE_POINTER; +#pragma pack() + +#include "ip.h" +#include "dhcp.h" +#include "tftp.h" + +#endif /* _BC_H */ + +/* EOF - bc.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c b/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c new file mode 100644 index 0000000000..18dca38408 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c @@ -0,0 +1,160 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + ComponentName.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = { + PxeBcComponentNameGetDriverName, + PxeBcComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = { + { + "eng", + L"PXE Base Code Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCESS - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gPxeBcComponentName.SupportedLanguages, + mPxeBcDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h new file mode 100644 index 0000000000..36f71f775a --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h @@ -0,0 +1,632 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _DHCP_H +#define _DHCP_H + +// +// Definitions for DHCP version 4 UDP packet. +// The field names in this structure are defined and described in RFC 2131. +// +#pragma pack(1) + +typedef struct { + UINT8 op; +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + + UINT8 htype; + UINT8 hlen; + UINT8 hops; + UINT32 xid; + UINT16 secs; + UINT16 flags; +#define DHCP_BROADCAST_FLAG 0x8000 + + UINT32 ciaddr; + UINT32 yiaddr; + UINT32 siaddr; + UINT32 giaddr; + UINT8 chaddr[16]; + UINT8 sname[64]; + UINT8 file[128]; + UINT8 options[312]; +#define OP_PAD 0 +#define OP_END 255 +#define OP_SUBNET_MASK 1 +#define OP_TIME_OFFSET 2 +#define OP_ROUTER_LIST 3 +#define OP_TIME_SERVERS 4 +#define OP_NAME_SERVERS 5 +#define OP_DNS_SERVERS 6 +#define OP_LOG_SERVERS 7 +#define OP_COOKIE_SERVERS 8 +#define OP_LPR_SREVERS 9 +#define OP_IMPRESS_SERVERS 10 +#define OP_RES_LOC_SERVERS 11 +#define OP_HOST_NAME 12 +#define OP_BOOT_FILE_SZ 13 +#define OP_DUMP_FILE 14 +#define OP_DOMAIN_NAME 15 +#define OP_SWAP_SERVER 16 +#define OP_ROOT_PATH 17 +#define OP_EXTENSION_PATH 18 +#define OP_IP_FORWARDING 19 +#define OP_NON_LOCAL_SRC_RTE 20 +#define OP_POLICY_FILTER 21 +#define OP_MAX_DATAGRAM_SZ 22 +#define OP_DEFAULT_TTL 23 +#define OP_MTU_AGING_TIMEOUT 24 +#define OP_MTU_SIZES 25 +#define OP_MTU_TO_USE 26 +#define OP_ALL_SUBNETS_LOCAL 27 +#define OP_BROADCAST_ADD 28 +#define OP_PERFORM_MASK_DISCOVERY 29 +#define OP_RESPOND_TO_MASK_REQ 30 +#define OP_PERFORM_ROUTER_DISCOVERY 31 +#define OP_ROUTER_SOLICIT_ADDRESS 32 +#define OP_STATIC_ROUTER_LIST 33 +#define OP_USE_ARP_TRAILERS 34 +#define OP_ARP_CACHE_TIMEOUT 35 +#define OP_ETHERNET_ENCAPSULATION 36 +#define OP_TCP_DEFAULT_TTL 37 +#define OP_TCP_KEEP_ALIVE_INT 38 +#define OP_KEEP_ALIVE_GARBAGE 39 +#define OP_NIS_DOMAIN_NAME 40 +#define OP_NIS_SERVERS 41 +#define OP_NTP_SERVERS 42 +#define OP_VENDOR_SPECIFIC 43 +#define VEND_PXE_MTFTP_IP 1 +#define VEND_PXE_MTFTP_CPORT 2 +#define VEND_PXE_MTFTP_SPORT 3 +#define VEND_PXE_MTFTP_TMOUT 4 +#define VEND_PXE_MTFTP_DELAY 5 +#define VEND_PXE_DISCOVERY_CONTROL 6 +#define PXE_DISABLE_BROADCAST_DISCOVERY (1 << 0) +#define PXE_DISABLE_MULTICAST_DISCOVERY (1 << 1) +#define PXE_ACCEPT_ONLY_PXE_BOOT_SERVERS (1 << 2) +#define PXE_DO_NOT_PROMPT (1 << 3) +#define VEND_PXE_DISCOVERY_MCAST_ADDR 7 +#define VEND_PXE_BOOT_SERVERS 8 +#define VEND_PXE_BOOT_MENU 9 +#define VEND_PXE_BOOT_PROMPT 10 +#define VEND_PXE_MCAST_ADDRS_ALLOC 11 +#define VEND_PXE_CREDENTIAL_TYPES 12 +#define VEND_PXE_BOOT_ITEM 71 +#define OP_NBNS_SERVERS 44 +#define OP_NBDD_SERVERS 45 +#define OP_NETBIOS_NODE_TYPE 46 +#define OP_NETBIOS_SCOPE 47 +#define OP_XWINDOW_SYSTEM_FONT_SERVERS 48 +#define OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49 +#define OP_DHCP_REQ_IP_ADD 50 +#define OP_DHCP_LEASE_TIME 51 +#define OP_DHCP_OPTION_OVERLOAD 52 +#define OVLD_FILE 1 +#define OVLD_SRVR_NAME 2 +#define OP_DHCP_MESSAGE_TYPE 53 +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 +#define OP_DHCP_SERVER_IP 54 +#define OP_DHCP_PARM_REQ_LIST 55 +#define OP_DHCP_ERROR_MESSAGE 56 +#define OP_DHCP_MAX_MESSAGE_SZ 57 +#define OP_DHCP_RENEWAL_TIME 58 +#define OP_DHCP_REBINDING_TIME 59 +#define OP_DHCP_CLASS_IDENTIFIER 60 +#define OP_DHCP_CLIENT_IDENTIFIER 61 +#define OP_NISPLUS_DOMAIN_NAME 64 +#define OP_NISPLUS_SERVERS 65 +#define OP_DHCP_TFTP_SERVER_NAME 66 +#define OP_DHCP_BOOTFILE 67 +#define OP_MOBILE_IP_HOME_AGENTS 68 +#define OP_SMPT_SERVERS 69 +#define OP_POP3_SERVERS 70 +#define OP_NNTP_SERVERS 71 +#define OP_WWW_SERVERS 72 +#define OP_FINGER_SERVERS 73 +#define OP_IRC_SERVERS 74 +#define OP_STREET_TALK_SERVERS 75 +#define OP_STREET_TALK_DIR_ASSIST_SERVERS 76 +#define OP_NDS_SERVERS 85 +#define OP_NDS_TREE_NAME 86 +#define OP_NDS_CONTEXT 87 +#define OP_DHCP_SYSTEM_ARCH 93 +#define OP_DHCP_NETWORK_ARCH 94 +#define OP_DHCP_PLATFORM_ID 97 +} DHCPV4_STRUCT; + +// +// DHCPv4 option header +// +typedef struct { + UINT8 OpCode; + UINT8 Length; + // + // followed by Data[] + // +} DHCPV4_OP_HEADER; + +// +// Generic DHCPv4 option (header followed by data) +// +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Data[1]; +} DHCPV4_OP_STRUCT; + +// +// Maximum DHCP packet size on ethernet +// +#define MAX_DHCP_MSG_SZ (MAX_ENET_DATA_SIZE - sizeof (IPV4_HEADER) - sizeof (UDPV4_HEADER)) + +// +// Macros used in pxe_bc_dhcp.c and pxe_loadfile.c +// +#define DHCPV4_TRANSMIT_BUFFER (*(DHCPV4_STRUCT *) (Private->TransmitBuffer)) +#define DHCPV4_OPTIONS_BUFFER (*(struct optionsstr *) DHCPV4_TRANSMIT_BUFFER.options) + +#define DHCPV4_ACK_INDEX 0 +#define PXE_BINL_INDEX 1 +#define PXE_OFFER_INDEX 1 +#define PXE_ACK_INDEX 2 +#define PXE_BIS_INDEX 3 + +#define DHCPV4_ACK_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[DHCPV4_ACK_INDEX] +#define PXE_BINL_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_BINL_INDEX] +#define PXE_OFFER_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_OFFER_INDEX] +#define PXE_ACK_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_ACK_INDEX] +#define PXE_BIS_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_BIS_INDEX] + +#define DHCPV4_ACK_PACKET DHCPV4_ACK_BUFFER.u.Dhcpv4 +#define PXE_BINL_PACKET PXE_BINL_BUFFER.u.Dhcpv4 +#define PXE_OFFER_PACKET PXE_OFFER_BUFFER.u.Dhcpv4 +#define PXE_ACK_PACKET PXE_ACK_BUFFER.u.Dhcpv4 +#define PXE_BIS_PACKET PXE_BIS_BUFFER.u.Dhcpv4 + +// +// network structure definitions +// +// +// some option definitions +// +#define DHCPV4_OPTION_LENGTH(type) (sizeof (type) - sizeof (DHCPV4_OP_HEADER)) + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Type; +} DHCPV4_OP_MESSAGE_TYPE; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Overload; +} DHCPV4_OP_OVERLOAD; + +// +// boot server list structure +// one or more contained in a pxe boot servers structure +// +typedef struct { + UINT8 IpCount; + EFI_IPv4_ADDRESS IpList[1]; // IP count of IPs +} PXEV4_SERVER_LIST; + +typedef struct { + UINT8 IpCount; + EFI_IPv6_ADDRESS IpList[1]; // IP count of IPs +} PXEV6_SERVER_LIST; + +typedef union { + PXEV4_SERVER_LIST Ipv4List; + PXEV6_SERVER_LIST Ipv6List; +} PXE_SERVER_LISTS; + +typedef struct { + UINT16 Type; + PXE_SERVER_LISTS u; +} PXE_SERVER_LIST; + +// +// pxe boot servers structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + PXE_SERVER_LIST ServerList[1]; // one or more +} PXE_OP_SERVER_LIST; + +// +// pxe boot item structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + UINT16 Type; + UINT16 Layer; +} PXE_OP_BOOT_ITEM; + +// +// pxe boot menu item structure +// +typedef struct { + UINT16 Type; + UINT8 DataLen; + UINT8 Data[1]; +} PXE_BOOT_MENU_ENTRY; + +// +// pxe boot menu structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + PXE_BOOT_MENU_ENTRY MenuItem[1]; +} PXE_OP_BOOT_MENU; + +// +// pxe boot prompt structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Timeout; + UINT8 Prompt[1]; +} PXE_OP_BOOT_PROMPT; + +#define PXE_BOOT_PROMPT_AUTO_SELECT 0 +#define PXE_BOOT_PROMPT_NO_TIMEOUT 255 + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Class[1]; +} DHCPV4_OP_CLASS; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 File[1]; +} DHCPV4_OP_BOOTFILE; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 VendorOptions[1]; +} DHCPV4_OP_VENDOR_OPTIONS; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 MaxSize[2]; +} DHCPV4_OP_MAX_MESSAGE_SIZE; + +typedef struct { + UINT8 _OP_SUBNET_MASK; /* 1 */ + UINT8 _OP_TIME_OFFSET; /* 2 */ + UINT8 _OP_ROUTER_LIST; /* 3 */ + UINT8 _OP_TIME_SERVERS; /* 4 */ + UINT8 _OP_NAME_SERVERS; /* 5 */ + UINT8 _OP_DNS_SERVERS; /* 6 */ + UINT8 _OP_HOST_NAME; /* 12 */ + UINT8 _OP_BOOT_FILE_SZ; /* 13 */ + UINT8 _OP_DOMAIN_NAME; /* 15 */ + UINT8 _OP_ROOT_PATH; /* 17 */ + UINT8 _OP_EXTENSION_PATH; /* 18 */ + UINT8 _OP_MAX_DATAGRAM_SZ; /* 22 */ + UINT8 _OP_DEFAULT_TTL; /* 23 */ + UINT8 _OP_BROADCAST_ADD; /* 28 */ + UINT8 _OP_NIS_DOMAIN_NAME; /* 40 */ + UINT8 _OP_NIS_SERVERS; /* 41 */ + UINT8 _OP_NTP_SERVERS; /* 42 */ + UINT8 _OP_VENDOR_SPECIFIC; /* 43 */ + UINT8 _OP_DHCP_REQ_IP_ADD; /* 50 */ + UINT8 _OP_DHCP_LEASE_TIME; /* 51 */ + UINT8 _OP_DHCP_SERVER_IP; /* 54 */ + UINT8 _OP_DHCP_RENEWAL_TIME; /* 58 */ + UINT8 _OP_DHCP_REBINDING_TIME; /* 59 */ + UINT8 _OP_DHCP_CLASS_IDENTIFIER; /* 60 */ + UINT8 _OP_DHCP_TFTP_SERVER_NAME; /* 66 */ + UINT8 _OP_DHCP_BOOTFILE; /* 67 */ + UINT8 _OP_DHCP_PLATFORM_ID; /* 97 */ + UINT8 VendorOption128; // vendor option 128 + UINT8 VendorOption129; // vendor option 129 + UINT8 VendorOption130; // vendor option 130 + UINT8 VendorOption131; // vendor option 131 + UINT8 VendorOption132; // vendor option 132 + UINT8 VendorOption133; // vendor option 133 + UINT8 VendorOption134; // vendor option 134 + UINT8 VendorOption135; // vendor option 135 +} DHCPV4_REQUESTED_OPTIONS_DATA; + +typedef struct { + DHCPV4_OP_HEADER Header; + DHCPV4_REQUESTED_OPTIONS_DATA Data; +} DHCPV4_OP_REQUESTED_OPTIONS; + +typedef struct opipstr { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_IP_ADDRESS; + +// +// ip list structure - e.g. router list +// +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS IpList[1]; +} DHCPV4_OP_IP_LIST; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Type; + UINT8 Guid[sizeof (EFI_GUID)]; +} DHCPV4_OP_CLIENT_ID; + +// +// special options start - someday obsolete ??? +// +#define DHCPV4_OP_PLATFORM_ID DHCPV4_OP_CLIENT_ID + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Type; // SNP = 2 + UINT8 MajorVersion; + UINT8 MinorVersion; +} DHCPV4_OP_NETWORK_INTERFACE; + +#define UNDI_TYPE 1 +#define SNP_TYPE 2 + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT16 Type; +} DHCPV4_OP_ARCHITECTURE_TYPE; +// +// special options end - someday obsolete ??? +// +typedef struct { + UINT8 ClassIdentifier[10]; // PXEClient: + UINT8 Lit2[5]; // Arch: + UINT8 ArchitectureType[5]; // 00000 - 65536 + UINT8 Lit3[1]; // : + UINT8 InterfaceName[4]; // e.g. UNDI + UINT8 Lit4[1]; // : + UINT8 UndiMajor[3]; // 000 - 255 + UINT8 UndiMinor[3]; // 000 - 255 +} DHCPV4_CLASS_ID_DATA; + +typedef struct { + DHCPV4_OP_HEADER Header; + DHCPV4_CLASS_ID_DATA Data; +} DHCPV4_OP_CLASS_ID; + +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_REQUESTED_IP; + +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_SERVER_IP; + +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_SUBNET_MASK; + +typedef struct { // oppxedisctlstr { + DHCPV4_OP_HEADER Header; + UINT8 ControlBits; +} PXE_OP_DISCOVERY_CONTROL; + +#define DISABLE_BCAST (1 << 0) +#define DISABLE_MCAST (1 << 1) +#define USE_ACCEPT_LIST (1 << 2) +#define USE_BOOTFILE (1 << 3) + +#pragma pack() +// +// definitions of indices to populate option interest array +// +#define VEND_PXE_MTFTP_IP_IX 1 // multicast IP address of bootfile for MTFTP listen +#define VEND_PXE_MTFTP_CPORT_IX 2 // UDP Port to monitor for MTFTP responses - Intel order +#define VEND_PXE_MTFTP_SPORT_IX 3 // Server UDP Port for MTFTP open - Intel order +#define VEND_PXE_MTFTP_TMOUT_IX 4 // Listen timeout - secs +#define VEND_PXE_MTFTP_DELAY_IX 5 // Transmission timeout - secs +#define VEND_PXE_DISCOVERY_CONTROL_IX 6 // bit field +#define VEND_PXE_DISCOVERY_MCAST_ADDR_IX 7 // boot server discovery multicast address +#define VEND_PXE_BOOT_SERVERS_IX 8 // list of boot servers of form tp(2) cnt(1) ips[cnt] +#define VEND_PXE_BOOT_MENU_IX 9 +#define VEND_PXE_BOOT_PROMPT_IX 10 +#define VEND_PXE_MCAST_ADDRS_ALLOC_IX 0 // not used by PXE client +#define VEND_PXE_CREDENTIAL_TYPES_IX 11 +#define VEND_13_IX 0 // not used by PXE client +#define VEND_14_IX 0 // not used by PXE client +#define VEND_15_IX 0 // not used by PXE client +#define VEND_16_IX 0 // not used by PXE client +#define VEND_17_IX 0 // not used by PXE client +#define VEND_18_IX 0 // not used by PXE client +#define VEND_19_IX 0 // not used by PXE client +#define VEND_20_IX 0 // not used by PXE client +#define VEND_21_IX 0 // not used by PXE client +#define VEND_22_IX 0 // not used by PXE client +#define VEND_23_IX 0 // not used by PXE client +#define VEND_24_IX 0 // not used by PXE client +#define VEND_25_IX 0 // not used by PXE client +#define VEND_26_IX 0 // not used by PXE client +#define VEND_27_IX 0 // not used by PXE client +#define VEND_28_IX 0 // not used by PXE client +#define VEND_29_IX 0 // not used by PXE client +#define VEND_30_IX 0 // not used by PXE client +#define VEND_31_IX 0 // not used by PXE client +#define VEND_32_IX 0 // not used by PXE client +#define VEND_33_IX 0 // not used by PXE client +#define VEND_34_IX 0 // not used by PXE client +#define VEND_35_IX 0 // not used by PXE client +#define VEND_36_IX 0 // not used by PXE client +#define VEND_37_IX 0 // not used by PXE client +#define VEND_38_IX 0 // not used by PXE client +#define VEND_39_IX 0 // not used by PXE client +#define VEND_40_IX 0 // not used by PXE client +#define VEND_41_IX 0 // not used by PXE client +#define VEND_42_IX 0 // not used by PXE client +#define VEND_43_IX 0 // not used by PXE client +#define VEND_44_IX 0 // not used by PXE client +#define VEND_45_IX 0 // not used by PXE client +#define VEND_46_IX 0 // not used by PXE client +#define VEND_47_IX 0 // not used by PXE client +#define VEND_48_IX 0 // not used by PXE client +#define VEND_49_IX 0 // not used by PXE client +#define VEND_50_IX 0 // not used by PXE client +#define VEND_51_IX 0 // not used by PXE client +#define VEND_52_IX 0 // not used by PXE client +#define VEND_53_IX 0 // not used by PXE client +#define VEND_54_IX 0 // not used by PXE client +#define VEND_55_IX 0 // not used by PXE client +#define VEND_56_IX 0 // not used by PXE client +#define VEND_57_IX 0 // not used by PXE client +#define VEND_58_IX 0 // not used by PXE client +#define VEND_59_IX 0 // not used by PXE client +#define VEND_60_IX 0 // not used by PXE client +#define VEND_61_IX 0 // not used by PXE client +#define VEND_62_IX 0 // not used by PXE client +#define VEND_63_IX 0 // not used by PXE client +#define VEND_64_IX 0 // not used by PXE client +#define VEND_65_IX 0 // not used by PXE client +#define VEND_66_IX 0 // not used by PXE client +#define VEND_67_IX 0 // not used by PXE client +#define VEND_68_IX 0 // not used by PXE client +#define VEND_69_IX 0 // not used by PXE client +#define VEND_70_IX 0 // not used by PXE client +#define VEND_PXE_BOOT_ITEM_IX 12 + +#define MAX_OUR_PXE_OPT VEND_PXE_BOOT_ITEM // largest PXE option in which we are interested +#define MAX_OUR_PXE_IX VEND_PXE_BOOT_ITEM_IX // largest PXE option index +// +// define various types by options that are sent +// +#define WfM11a_OPTS ((1<> 8) & 0x00ff)) + +#define HTONL(v) \ + (UINT32) ((((v) << 24) & 0xff000000) + (((v) << 8) & 0x00ff0000) + (((v) >> 8) & 0x0000ff00) + (((v) >> 24) & 0x000000ff)) + +#define HTONLL(v) swap64 (v) + +#define U8PTR(na) ((UINT8 *) &(na)) + +#define NTOHS(ns) ((UINT16) (((*U8PTR (ns)) << 8) +*(U8PTR (ns) + 1))) + +#define NTOHL(ns) \ + ((UINT32) (((*U8PTR (ns)) << 24) + ((*(U8PTR (ns) + 1)) << 16) + ((*(U8PTR (ns) + 2)) << 8) +*(U8PTR (ns) + 3))) + +#endif /* _HTON_H_ */ + +/* EOF - hton.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h new file mode 100644 index 0000000000..cc878d8dd7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h @@ -0,0 +1,26 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeArch.h + +Abstract: + Defines PXE Arch type + + +**/ + +#ifndef _EFI_PXE_ARCH_H_ +#define _EFI_PXE_ARCH_H_ + +#define SYS_ARCH 0x6 + +#endif diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h new file mode 100644 index 0000000000..a482eb9cd9 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h @@ -0,0 +1,736 @@ +/** @file + +Copyright (c) 2004 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _IP_H_ +#define _IP_H_ + +#include "hton.h" + +// +// portability macros +// +#define UDP_FILTER_MASK (EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER \ + ) + +#define PXE_BOOT_LAYER_MASK 0x7FFF +#define PXE_BOOT_LAYER_INITIAL 0x0000 +#define PXE_BOOT_LAYER_CREDENTIAL_FLAG 0x8000 +#define MAX_BOOT_SERVERS 32 + +// +// macro to evaluate IP address as TRUE if it is a multicast IP address +// +#define IS_MULTICAST(ptr) ((*((UINT8 *) ptr) & 0xf0) == 0xe0) + +// +// length macros +// +#define IP_ADDRESS_LENGTH(qp) (((qp)->UsingIpv6) ? sizeof (EFI_IPv6_ADDRESS) : sizeof (EFI_IPv4_ADDRESS)) + +#define MAX_FRAME_DATA_SIZE 1488 +#define ALLOCATE_SIZE(X) (((X) + 7) & 0xfff8) +#define MODE_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_MODE)) +#define BUFFER_ALLOCATE_SIZE (8192 + 512) +#define ROUTER_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY) * PXE_ROUTER_TABLE_SIZE)) +#define ARP_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_PXE_BASE_CODE_ARP_ENTRY) * PXE_ARP_CACHE_SIZE)) +#define FILTER_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_IP_ADDRESS) * PXE_IP_FILTER_SIZE)) +#define PXE_ARP_CACHE_SIZE 8 +#define PXE_ROUTER_TABLE_SIZE 8 +#define PXE_IP_FILTER_SIZE 8 +#define ICMP_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) +#define TFTP_ERR_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_TFTP_ERROR)) + +// +// DHCP discover/request packets are sent to this UDP port. ProxyDHCP +// servers listen on this port for DHCP discover packets that have a +// class identifier (option 60) with 'PXEClient' in the first 9 bytes. +// Bootservers also listen on this port for PXE broadcast discover +// requests from PXE clients. +// +#define DHCP_SERVER_PORT 67 + +// +// When DHCP, proxyDHCP and Bootservers respond to DHCP and PXE broadcast +// discover requests by broadcasting the reply packet, the packet is +// broadcast to this port. +// +#define DHCP_CLIENT_PORT 68 + +// +// TFTP servers listen for TFTP open requests on this port. +// +#define TFTP_OPEN_PORT 69 + +// +// proxyDHCP and Bootservers listen on this port for a PXE unicast and/or +// multicast discover requests from PXE clients. A PXE discover request +// looks like a DHCP discover or DHCP request packet. +// +#define PXE_DISCOVERY_PORT 4011 + +// +// This port is used by the PXE client/server protocol tests. +// +#define PXE_PORT_PXETEST_PORT 0x8080 + +// +// Definitions for Ethertype protocol numbers and interface types +// Per RFC 1700, +// +#define PXE_PROTOCOL_ETHERNET_IP 0x0800 +#define PXE_PROTOCOL_ETHERNET_ARP 0x0806 +#define PXE_PROTOCOL_ETHERNET_RARP 0x8035 + +#define PXE_IFTYPE_ETHERNET 0x01 +#define PXE_IFTYPE_TOKENRING 0x04 +#define PXE_IFTYPE_FIBRE_CHANNEL 0x12 + +// +// Definitions for internet protocol version 4 header +// Per RFC 791, September 1981. +// +#define IPVER4 4 + +#pragma pack(1) // make network structures packed byte alignment +typedef union { + UINT8 B[4]; + UINT32 L; +} IPV4_ADDR; + +#define IPV4_HEADER_LENGTH(IpHeaderPtr) (((IpHeaderPtr)->VersionIhl & 0xf) << 2) + +#define SET_IPV4_VER_HDL(IpHeaderPtr, IpHeaderLen) { \ + (IpHeaderPtr)->VersionIhl = (UINT8) ((IPVER4 << 4) | ((IpHeaderLen) >> 2)); \ + } + +typedef struct { + UINT8 VersionIhl; + UINT8 TypeOfService; + UINT16 TotalLength; + UINT16 Id; + UINT16 FragmentFields; + UINT8 TimeToLive; + UINT8 Protocol; + UINT16 HeaderChecksum; + IPV4_ADDR SrcAddr; + IPV4_ADDR DestAddr; + // + // options are not implemented + // +} IPV4_HEADER; + +#define IP_FRAG_RSVD 0x8000 // reserved bit - must be zero +#define IP_NO_FRAG 0x4000 // do not fragment bit +#define IP_MORE_FRAG 0x2000 // not last fragment +#define IP_FRAG_OFF_MSK 0x1fff // fragment offset in 8 byte chunks +#define DEFAULT_RFC_TTL 64 + +#define PROT_ICMP 1 +#define PROT_IGMP 2 +#define PROT_TCP 6 +#define PROT_UDP 17 + +/* + * Definitions for internet control message protocol version 4 message + * structure. Per RFC 792, September 1981. + */ + +// +// icmp header for all icmp messages +// +typedef struct { + UINT8 Type; // message type + UINT8 Code; // type specific - 0 for types we implement + UINT16 Checksum; // ones complement of ones complement sum of 16 bit words of message +} ICMPV4_HEADER; + +#define ICMP_DEST_UNREACHABLE 3 +#define ICMP_SOURCE_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO 8 +#define ICMP_ECHO_REPLY 0 +#define ICMP_ROUTER_ADV 9 +#define ICMP_ROUTER_SOLICIT 10 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 +#define ICMP_TIMESTAMP 13 +#define ICMP_TIMESTAMP_REPLY 14 +#define ICMP_INFO_REQ 15 +#define ICMP_INFO_REQ_REPLY 16 +#define ICMP_SUBNET_MASK_REQ 17 +#define ICMP_SUBNET_MASK_REPLY 18 +// +// other ICMP message types ignored in this implementation +// +// icmp general messages +// +typedef struct { + ICMPV4_HEADER Header; + // + // generally unused except byte [0] for + // parameter problem message + // + UINT8 GenerallyUnused[4]; + // + // original message ip header of plus 64 + // bits of data + // + IPV4_HEADER IpHeader; +} ICMPV4_GENERAL_MESSAGE; + +// +// icmp req/rply message header +// +typedef struct { + ICMPV4_HEADER Header; + UINT16 Id; + UINT16 SequenceNumber; +} ICMPV4_REQUEST_REPLY_HEADER; + +// +// icmp echo message +// +typedef struct { + ICMPV4_REQUEST_REPLY_HEADER Header; + UINT8 EchoData[1]; // variable length data to be echoed +} ICMPV4_ECHO_MESSAGE; + +// +// icmp timestamp message - times are milliseconds since midnight UT - +// if non std, set high order bit +// +typedef struct { + ICMPV4_REQUEST_REPLY_HEADER Header; + UINT32 OriginalTime; // originating timestamp + UINT32 ReceiveTime; // receiving timestamp + UINT32 TransmitTime; // transmitting timestamp +} ICMPV4_TIMESTAMP_MESSAGE; + +// +// icmp info request structure - fill in source and dest net ip address on reply +// +typedef struct { + ICMPV4_REQUEST_REPLY_HEADER Header; +} ICMPV4_INFO_MESSAGE; + +// +// Definitions for internet control message protocol version 4 message structure +// Router discovery +// Per RFC 1256, September 1991. +// +// +// icmp router advertisement message +// +typedef struct { + ICMPV4_HEADER Header; + UINT8 NumberEntries; // number of address entries + UINT8 EntrySize; // number of 32 bit words per address entry + UINT16 Lifetime; // seconds to consider info valid + UINT32 RouterIp; + UINT32 Preferance; +} ICMPV4_ROUTER_ADVERTISE_MESSAGE; + +// +// icmp router solicitation message +// +typedef struct { + ICMPV4_HEADER Header; + UINT32 Reserved; +} ICMPV4_ROUTER_SOLICIT_MESSAGE; + +#define MAX_SOLICITATION_DELAY 1 // 1 second +#define SOLICITATION_INTERVAL 3 // 3 seconds +#define MAX_SOLICITATIONS 3 // 3 transmissions +#define V1ROUTER_PRESENT_TIMEOUT 400 // 400 second timeout until v2 reports can be sent +#define UNSOLICITED_REPORT_INTERVAL 10 // 10 seconds between unsolicited reports +#define BROADCAST_IPv4 0xffffffff + +// +// Definitions for address resolution protocol message structure +// Per RFC 826, November 1982 +// +typedef struct { + UINT16 HwType; // hardware type - e.g. ethernet (1) + UINT16 ProtType; // protocol type - for ethernet, 0x800 for IP + UINT8 HwAddLen; // byte length of a hardware address (e.g. 6 for ethernet) + UINT8 ProtAddLen; // byte length of a protocol address (e.g. 4 for ipv4) + UINT16 OpCode; + // + // source and dest hw and prot addresses follow - see example below + // +} ARP_HEADER; + +#define ETHERNET_ADD_SPC 1 + +#define ETHER_TYPE_IP 0x800 + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +// +// generic ARP packet +// +typedef struct { + ARP_HEADER ArpHeader; + EFI_MAC_ADDRESS SrcHardwareAddr; + EFI_IP_ADDRESS SrcProtocolAddr; + EFI_MAC_ADDRESS DestHardwareAddr; + EFI_IP_ADDRESS DestProtocolAddr; +} ARP_PACKET; + +#define ENET_HWADDLEN 6 +#define IPV4_PROTADDLEN 4 + +// +// Definitions for user datagram protocol version 4 pseudo header & header +// Per RFC 768, 28 August 1980 +// +typedef struct { + IPV4_ADDR SrcAddr; // source ip address + IPV4_ADDR DestAddr; // dest ip address + UINT8 Zero; // 0 + UINT8 Protocol; // protocol + UINT16 TotalLength; // UDP length - sizeof udpv4hdr + data length +} UDPV4_PSEUDO_HEADER; + +typedef struct { + UINT16 SrcPort; // source port identifier + UINT16 DestPort; // destination port identifier + UINT16 TotalLength; // total length header plus data + // + // ones complement of ones complement sum of 16 bit + // words of pseudo header, UDP header, and data + // zero checksum is transmitted as -0 (ones comp) + // zero transmitted means checksum not computed + // data follows + // + UINT16 Checksum; +} UDPV4_HEADER; + +typedef struct { + UDPV4_PSEUDO_HEADER Udpv4PseudoHeader; + UDPV4_HEADER Udpv4Header; +} UDPV4_HEADERS; + +// +// Definitions for transmission control protocol header +// Per RFC 793, September, 1981 +// +typedef struct { + IPV4_ADDR SrcAddr; // source ip address + IPV4_ADDR DestAddr; // dest ip address + UINT8 Zero; // 0 + UINT8 Protocol; // protocol + UINT16 TotalLength; // TCP length - TCP header length + data length +} TCPV4_PSEUDO_HEADER; + +typedef struct { + UINT16 SrcPort; // source port identifier + UINT16 DestPort; // destination port identifier + UINT32 SeqNumber; // Sequence number + UINT32 AckNumber; // Acknowledgement Number + // + // Nibble of HLEN (length of header in 32-bit multiples) + // 6bits of RESERVED + // Nibble of Code Bits + // + UINT16 HlenResCode; + UINT16 Window; // Software buffer size (sliding window size) in network-standard byte order + // + // ones complement of ones complement sum of 16 bit words of + // pseudo header, TCP header, and data + // zero checksum is transmitted as -0 (ones comp) + // zero transmitted means checksum not computed + // + UINT16 Checksum; + UINT16 UrgentPointer; // pointer to urgent data (allows sender to specify urgent data) +} TCPV4_HEADER; + +typedef struct { + TCPV4_PSEUDO_HEADER Tcpv4PseudoHeader; + TCPV4_HEADER Tcpv4Header; +} TCPV4_HEADERS; + +typedef struct { + UINT8 Kind; // one of the following: + UINT8 Length; // total option length including Kind and Lth + UINT8 Data[1]; // length = Lth - 2 +} TCPV4_OPTION; + +#define TCP_OP_END 0 // only used to pad to end of TCP header +#define TCP_NOP 1 // optional - may be used to pad between options to get alignment +#define TCP_MAX_SEG 2 // maximum receive segment size - only send at initial connection request +#define MAX_MEDIA_HDR_SIZE 64 +#define MIN_ENET_DATA_SIZE 64 +#define MAX_ENET_DATA_SIZE 1500 // temp def - make a network based var +#define MAX_IPV4_PKT_SIZE 65535 // maximum IP packet size +#define MAX_IPV4_DATA_SIZE (MAX_IPV4_PKT_SIZE - sizeof (IPV4_HEADER)) +#define MAX_IPV4_FRAME_DATA_SIZE (MAX_FRAME_DATA_SIZE - sizeof (IPV4_HEADER)) +#define REAS_IPV4_PKT_SIZE 576 // minimum IP packet size all IP host can handle +#define REAS_IPV4_DATA_SIZE (REAS_IPV4_PKT_SIZE - sizeof (IPV4_HEADER)) + +// +// +// +typedef union { + UINT8 Data[MAX_ENET_DATA_SIZE]; + ICMPV4_HEADER IcmpHeader; + IGMPV2_MESSAGE IgmpMessage; + struct { + UDPV4_HEADER UdpHeader; + UINT8 Data[1]; + } Udp; + struct { + TCPV4_HEADER TcpHeader; + UINT8 Data[1]; + } Tcp; +} PROTOCOL_UNION; + +// +// out buffer structure +// +typedef struct { + UINT8 MediaHeader[MAX_MEDIA_HDR_SIZE]; + IPV4_HEADER IpHeader; + // + // following union placement only valid if no option IP header + // + PROTOCOL_UNION u; +} IPV4_BUFFER; + +typedef struct { + IPV4_HEADER IpHeader; + // + // following union placement only valid if no option IP header + // + PROTOCOL_UNION u; +} IPV4_STRUCT; + +#pragma pack() // reset to default + + //////////////////////////////////////////////////////////// +// +// BC IP Filter Routine +// +EFI_STATUS +IpFilter ( + PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_IP_FILTER *Filter + ) +; + +// +// ////////////////////////////////////////////////////////////////////// +// +// Udp Write Routine - called by base code - e.g. TFTP - already locked +// +EFI_STATUS +UdpWrite ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIpPtr, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortptr, + IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL + IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSizePtr, + IN VOID *BufferPtr + ) +; + +// +// ///////////////////////////////////////////////////////////////////// +// +// Udp Read Routine - called by base code - e.g. TFTP - already locked +// +EFI_STATUS +UdpRead ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPorPtrt, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSizePtr, + IN VOID *BufferPtr, + IN EFI_EVENT TimeoutEvent + ) +; + +VOID +IgmpLeaveGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS * + ) +; + +VOID +IgmpJoinGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS * + ) +; + +// +// convert number to zero filled ascii value of length lth +// +VOID +CvtNum ( + UINTN Number, + UINT8 *BufferPtr, + INTN BufferLen + ) +; + +// +// convert number to ascii string at ptr +// +VOID +UtoA10 ( + UINTN Number, + UINT8 *BufferPtr + ) +; + +// +// convert ascii numeric string to UINTN +// +UINTN +AtoU ( + UINT8 *BufferPtr + ) +; + +UINT64 +AtoU64 ( + UINT8 *BufferPtr + ) +; + +// +// calculate the internet checksum (RFC 1071) +// return 16 bit ones complement of ones complement sum of 16 bit words +// +UINT16 +IpChecksum ( + UINT16 *MessagePtr, + UINTN ByteLength + ) +; + +// +// do checksum on non contiguous header and data +// +UINT16 +IpChecksum2 ( + UINT16 *Header, + UINTN HeaderLength, + UINT16 *Message, + UINTN MessageLength + ) +; + +// +// update checksum when only a single word changes +// +UINT16 +UpdateChecksum ( + UINT16 OldChecksum, + UINT16 OldWord, + UINT16 NewWord + ) +; + +VOID +SeedRandom ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 InitialSeed + ) +; + +UINT16 +Random ( + IN PXE_BASECODE_DEVICE *Private + ) +; + +EFI_STATUS +SendPacket ( + PXE_BASECODE_DEVICE *Private, + VOID *HeaderPtr, + VOID *PacketPtr, + INTN PacketLength, + VOID *HardwareAddress, + UINT16 MediaProtocol, + IN EFI_PXE_BASE_CODE_FUNCTION Function + ) +; + +VOID +HandleArpReceive ( + PXE_BASECODE_DEVICE *Private, + ARP_PACKET *ArpPacketPtr, + VOID *HeaderPtr + ) +; + +VOID +HandleIgmp ( + PXE_BASECODE_DEVICE *Private, + IGMPV2_MESSAGE *IgmpMessageptr, + UINTN IgmpMessageLen + ) +; + +VOID +IgmpCheckTimers ( + PXE_BASECODE_DEVICE *Private + ) +; // poll when doing a receive +// return hw add of IP and TRUE if available, otherwise FALSE +// +BOOLEAN +GetHwAddr ( + IN PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ProtocolAddressPtr, + EFI_MAC_ADDRESS *HardwareAddressPtr + ) +; + +EFI_STATUS +DoArp ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddressPtr, + OUT EFI_MAC_ADDRESS *HardwareAddressptr + ) +; + +BOOLEAN +OnSameSubnet ( + UINTN IpAddressLen, + EFI_IP_ADDRESS *Ip1, + EFI_IP_ADDRESS *Ip2, + EFI_IP_ADDRESS *SubnetMask + ) +; + +VOID +IpAddRouter ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *RouterIp + ) +; + +#define Ip4AddRouter(Private, Ipv4Ptr) IpAddRouter (Private, (EFI_IP_ADDRESS *) Ipv4Ptr) + +// +// routine to send ipv4 packet +// ipv4 + upper protocol header for length TotHdrLth in xmtbuf, ipv4 header length IpHdrLth +// routine fills in ipv4hdr Ver_Hdl, TotLth, and Checksum, moves in Data, and gets dest MAC address +// +EFI_STATUS +Ipv4Xmt ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIP, + UINTN IpHeaderLen, + UINTN TotalHeaderLen, + VOID *Data, + UINTN DataLen, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +; + +// +// send ipv4 packet with ipv4 option +// +EFI_STATUS +Ipv4SendWOp ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIP, + UINT8 *MessagePtr, + UINTN MessageLth, + UINT8 Protocol, + UINT8 *Option, + UINTN OptionLen, + UINT32 DestIp, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +; + +// +// send MsgLth message at MsgPtr - higher level protocol header already in xmtbuf, length HdrSize +// +EFI_STATUS +Ip4Send ( + IN PXE_BASECODE_DEVICE *Private, // pointer to instance data + IN UINTN MayFragment, // + IN UINT8 Protocol, // protocol + IN UINT32 SrcIp, // Source IP address + IN UINT32 DestIp, // Destination IP address + IN UINT32 GatewayIp, // used if not NULL and needed + IN UINTN HeaderSize, // protocol header byte length + IN UINT8 *MsgPtr, // pointer to data + IN UINTN MsgLength + ) +; // data byte length +// receive up to MsgLth message into MsgPtr for protocol Prot +// return message length, src/dest ips if select any, and pointer to protocol header +// +EFI_STATUS +IpReceive ( + IN PXE_BASECODE_DEVICE *Private, // pointer to instance data + UINT16 OpFlags, // Flags to determine if filtering on IP addresses + EFI_IP_ADDRESS *SrcIpPtr, // if filtering, O if accept any + EFI_IP_ADDRESS *DstIpPtr, // if filtering, O if accept any + UINT8 Protocol, // protocol + VOID *HeaderPtr, // address of where to put protocol header + UINTN HeaderSize, // protocol header byte length + UINT8 *MsgPtr, // pointer to data buffer + UINTN *MsgLenPtr, // pointer to data buffer length/ O - returned data length + IN EFI_EVENT TimeoutEvent + ) +; + +#if 0 +VOID +WaitForTxComplete ( + IN PXE_BASECODE_DEVICE *Private + ) +; +#endif +// +// routine to cycle waiting for a receive or timeout +// +EFI_STATUS +WaitForReceive ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN EFI_EVENT TimeoutEvent, + IN OUT UINTN *HeaderSizePtr, + IN OUT UINTN *BufferSizePtr, + IN OUT UINT16 *ProtocolPtr + ) +; + +#endif /* _IP_H_ */ + +/* EOF - ip.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h new file mode 100644 index 0000000000..6d06045df0 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h @@ -0,0 +1,26 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeArch.h + +Abstract: + Defines PXE Arch type + + +**/ + +#ifndef _EFI_PXE_ARCH_H_ +#define _EFI_PXE_ARCH_H_ + +#define SYS_ARCH 0x2 + +#endif diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf new file mode 100644 index 0000000000..f5eaa0f622 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf @@ -0,0 +1,92 @@ +#/** @file +# Component name for module BC +# +# Copyright (c) 2007, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PxeBcDxe + FILE_GUID = A3f436EA-A127-4EF8-957C-8048606FF670 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeBCDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Pxe_bc_mtftp.c + Bc.c + Dhcp.h + Ip.h + Pxe_bc_ip.c + Pxe_bc_dhcp.c + Pxe_bc_arp.c + Hton.h + ComponentName.c + Bc.h + Pxe_loadfile.c + Tftp.h + Pxe_bc_igmp.c + Pxe_bc_udp.c + +[Sources.IA32] + Ia32\PxeArch.h + +[Sources.X64] + X64\PxeArch.h + +[Sources.IPF] + Ipf\PxeArch.h + +[Sources.EBC] + Ebc\PxeArch.h + Ebc\PxeArch.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + + +[Guids] + gEfiSmbiosTableGuid # ALWAYS_CONSUMED + + +[Protocols] + gEfiBisProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPxeBaseCodeCallbackProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPxeBaseCodeProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLoadFileProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiNetworkInterfaceIdentifierProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcpProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiNetworkInterfaceIdentifierProtocolGuid_31 # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa new file mode 100644 index 0000000000..1a1f81d1e3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa @@ -0,0 +1,106 @@ + + + BC + DXE_DRIVER + A3f436EA-A127-4EF8-957C-8048606FF670 + 1.0 + Component name for module BC + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of 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. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + BC + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + pxe_bc_udp.c + pxe_bc_igmp.c + tftp.h + pxe_loadfile.c + bc.h + ComponentName.c + BcEntry.c + ipf\PxeArch.h + ebc\PxeArch.h + x64\PxeArch.h + pxe_bc_tcp.c + hton.h + pxe_bc_arp.c + pxe_bc_dhcp.c + pxe_bc_ip.c + ip.h + dhcp.h + bc.c + pxe_bc_mtftp.c + ia32\PxeArch.h + ebc\PxeArch.c + + + + + + + + gEfiNetworkInterfaceIdentifierProtocolGuid + + + gEfiDevicePathProtocolGuid + + + gEfiSimpleNetworkProtocolGuid + + + gEfiLoadFileProtocolGuid + + + gEfiPxeBaseCodeProtocolGuid + + + gEfiPxeBaseCodeCallbackProtocolGuid + + + gEfiBisProtocolGuid + + + + + gEfiSmbiosTableGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + InitializeBCDriver + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c new file mode 100644 index 0000000000..3654363e8e --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c @@ -0,0 +1,583 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_arp.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// Definitions for ARP +// Per RFC 826 +// +STATIC ARP_HEADER ArpHeader; + +#pragma pack(1) +STATIC struct { + UINT8 MediaHeader[14]; + ARP_HEADER ArpHeader; + UINT8 ArpData[64]; +} ArpReplyPacket; +#pragma pack() + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return none + +**/ +VOID +InitArpHeader ( + VOID + ) +{ + ArpHeader.HwType = HTONS (ETHERNET_ADD_SPC); + ArpHeader.ProtType = HTONS (ETHER_TYPE_IP); + ArpHeader.HwAddLen = ENET_HWADDLEN; + ArpHeader.ProtAddLen = IPV4_PROTADDLEN; + ArpHeader.OpCode = HTONS (ARP_REQUEST); + + CopyMem (&ArpReplyPacket.ArpHeader, &ArpHeader, sizeof (ARP_HEADER)); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +HandleArpReceive ( + IN PXE_BASECODE_DEVICE *Private, + IN ARP_PACKET *ArpPacketPtr, + IN VOID *MediaHeader + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_MAC_ADDRESS TmpMacAddr; + UINTN Index; + UINT8 *SrcHwAddr; + UINT8 *SrcPrAddr; + UINT8 *DstHwAddr; + UINT8 *DstPrAddr; + UINT8 *TmpPtr; + + // + // + // + PxeBcMode = Private->EfiBc.Mode; + SnpMode = Private->SimpleNetwork->Mode; + + // + // For now only ethernet addresses are supported. + // This will need to be updated when other media + // layers are supported by PxeBc, Snp and UNDI. + // + if (ArpPacketPtr->ArpHeader.HwType != HTONS (ETHERNET_ADD_SPC)) { + return ; + } + // + // For now only IP protocol addresses are supported. + // This will need to be updated when other protocol + // types are supported by PxeBc, Snp and UNDI. + // + if (ArpPacketPtr->ArpHeader.ProtType != HTONS (ETHER_TYPE_IP)) { + return ; + } + // + // For now only SNP hardware address sizes are supported. + // + if (ArpPacketPtr->ArpHeader.HwAddLen != SnpMode->HwAddressSize) { + return ; + } + // + // For now only PxeBc protocol address sizes are supported. + // + if (ArpPacketPtr->ArpHeader.ProtAddLen != Private->IpLength) { + return ; + } + // + // Ignore out of range opcodes + // + switch (ArpPacketPtr->ArpHeader.OpCode) { + case HTONS (ARP_REPLY): + case HTONS (ARP_REQUEST): + break; + + default: + return ; + } + // + // update entry in our ARP cache if we have it + // + SrcHwAddr = (UINT8 *) &ArpPacketPtr->SrcHardwareAddr; + SrcPrAddr = SrcHwAddr + SnpMode->HwAddressSize; + + for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) { + if (CompareMem ( + &PxeBcMode->ArpCache[Index].IpAddr, + SrcPrAddr, + Private->IpLength + )) { + continue; + } + + CopyMem ( + &PxeBcMode->ArpCache[Index].MacAddr, + SrcHwAddr, + SnpMode->HwAddressSize + ); + + break; + } + // + // Done if ARP packet was not for us. + // + DstHwAddr = SrcPrAddr + Private->IpLength; + DstPrAddr = DstHwAddr + SnpMode->HwAddressSize; + + if (CompareMem (DstPrAddr, &PxeBcMode->StationIp, Private->IpLength)) { + return ; + // + // not for us + // + } + // + // for us - if we did not update entry, add it + // + if (Index == PxeBcMode->ArpCacheEntries) { + // + // if we have a full table, get rid of oldest + // + if (Index == PXE_ARP_CACHE_SIZE) { + Index = Private->OldestArpEntry; + + if (++Private->OldestArpEntry == PXE_ARP_CACHE_SIZE) { + Private->OldestArpEntry = 0; + } + } else { + ++PxeBcMode->ArpCacheEntries; + } + + CopyMem ( + &PxeBcMode->ArpCache[Index].MacAddr, + SrcHwAddr, + SnpMode->HwAddressSize + ); + + CopyMem ( + &PxeBcMode->ArpCache[Index].IpAddr, + SrcPrAddr, + Private->IpLength + ); + } + // + // if this is not a request or we don't yet have an IP, finished + // + if (ArpPacketPtr->ArpHeader.OpCode != HTONS (ARP_REQUEST) || !Private->GoodStationIp) { + return ; + } + // + // Assemble ARP reply. + // + // + // Create media header. [ dest mac | src mac | prot ] + // + CopyMem ( + &ArpReplyPacket.MediaHeader[0], + SrcHwAddr, + SnpMode->HwAddressSize + ); + + CopyMem ( + &ArpReplyPacket.MediaHeader[SnpMode->HwAddressSize], + &SnpMode->CurrentAddress, + SnpMode->HwAddressSize + ); + + CopyMem ( + &ArpReplyPacket.MediaHeader[2 * SnpMode->HwAddressSize], + &((UINT8 *) MediaHeader)[2 * SnpMode->HwAddressSize], + sizeof (UINT16) + ); + + // + // ARP reply header is almost filled in, + // just insert the correct opcode. + // + ArpReplyPacket.ArpHeader.OpCode = HTONS (ARP_REPLY); + + // + // Now fill in ARP data. [ src mac | src prot | dest mac | dest prot ] + // + TmpPtr = ArpReplyPacket.ArpData; + CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize); + + TmpPtr += SnpMode->HwAddressSize; + CopyMem (TmpPtr, &PxeBcMode->StationIp, Private->IpLength); + + TmpPtr += Private->IpLength; + CopyMem (TmpPtr, SrcHwAddr, SnpMode->HwAddressSize); + + TmpPtr += SnpMode->HwAddressSize; + CopyMem (TmpPtr, SrcPrAddr, Private->IpLength); + + // + // Now send out the ARP reply. + // + CopyMem (&TmpMacAddr, SrcHwAddr, sizeof (EFI_MAC_ADDRESS)); + + SendPacket ( + Private, + &ArpReplyPacket.MediaHeader, + &ArpReplyPacket.ArpHeader, + sizeof (ARP_HEADER) + 2 * (Private->IpLength + SnpMode->HwAddressSize), + &TmpMacAddr, + PXE_PROTOCOL_ETHERNET_ARP, + EFI_PXE_BASE_CODE_FUNCTION_ARP + ); + + // + // Give time (100 microseconds) for ARP reply to get onto wire. + // + gBS->Stall (1000); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return TRUE := If IP address was found and MAC address was stored + @return FALSE := If IP address was not found + +**/ +BOOLEAN +GetHwAddr ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddrPtr, + OUT EFI_MAC_ADDRESS *HardwareAddrPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN HardwareAddrLength; + UINTN Index; + + PxeBcMode = Private->EfiBc.Mode; + HardwareAddrLength = Private->SimpleNetwork->Mode->HwAddressSize; + + for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) { + if (!CompareMem ( + ProtocolAddrPtr, + &PxeBcMode->ArpCache[Index].IpAddr, + Private->IpLength + )) { + CopyMem ( + HardwareAddrPtr, + &PxeBcMode->ArpCache[Index].MacAddr, + HardwareAddrLength + ); + + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := ARP request sent + @return other := ARP request could not be sent + +**/ +STATIC +EFI_STATUS +SendRequest ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddrPtr, + IN EFI_MAC_ADDRESS *HardwareAddrPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + ARP_PACKET *ArpPacket; + EFI_STATUS Status; + UINTN HardwareAddrLength; + UINT8 *SrcProtocolAddrPtr; + UINT8 *DestHardwareAddrptr; + UINT8 *DestProtocolAddrPtr; + + // + // + // + PxeBcMode = Private->EfiBc.Mode; + SnpMode = Private->SimpleNetwork->Mode; + HardwareAddrLength = SnpMode->HwAddressSize; + + // + // Allocate ARP buffer + // + if (Private->ArpBuffer == NULL) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + SnpMode->MediaHeaderSize + sizeof (ARP_PACKET), + &Private->ArpBuffer + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + ArpPacket = (VOID *) (Private->ArpBuffer + SnpMode->MediaHeaderSize); + + // + // for now, only handle one kind of hw and pr address + // + ArpPacket->ArpHeader = ArpHeader; + ArpPacket->ArpHeader.HwAddLen = (UINT8) HardwareAddrLength; + ArpPacket->ArpHeader.ProtAddLen = (UINT8) Private->IpLength; + + // + // rest more generic + // + SrcProtocolAddrPtr = (UINT8 *) (&ArpPacket->SrcHardwareAddr) + HardwareAddrLength; + DestHardwareAddrptr = SrcProtocolAddrPtr + Private->IpLength; + DestProtocolAddrPtr = DestHardwareAddrptr + HardwareAddrLength; + + CopyMem (DestProtocolAddrPtr, ProtocolAddrPtr, Private->IpLength); + CopyMem (DestHardwareAddrptr, HardwareAddrPtr, HardwareAddrLength); + CopyMem (SrcProtocolAddrPtr, &PxeBcMode->StationIp, Private->IpLength); + CopyMem ( + &ArpPacket->SrcHardwareAddr, + &SnpMode->CurrentAddress, + HardwareAddrLength + ); + + return SendPacket ( + Private, + Private->ArpBuffer, + ArpPacket, + sizeof (ARP_HEADER) + ((Private->IpLength + HardwareAddrLength) << 1), + &SnpMode->BroadcastAddress, + PXE_PROTOCOL_ETHERNET_ARP, + EFI_PXE_BASE_CODE_FUNCTION_ARP + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// check for address - if not there, send ARP request, wait and check again +// not how it would be done in a full system +// +#define ARP_REQUEST_TIMEOUT_MS 500 // try for half a second + + //////////////////////////////////////////////////////////// +// +// BC Arp Routine +// + +/** + + +**/ +EFI_STATUS +EFIAPI +BcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_IP_ADDRESS * ProtocolAddrPtr, + OUT EFI_MAC_ADDRESS * HardwareAddrPtr OPTIONAL + ) +{ + EFI_MAC_ADDRESS Mac; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + DEBUG ((DEBUG_INFO, "\nBcArp()")); + + // + // Issue BC command + // + if (ProtocolAddrPtr == NULL) { + DEBUG ( + (DEBUG_INFO, + "\nBcArp() Exit #1 %Xh (%r)", + EFI_INVALID_PARAMETER, + EFI_INVALID_PARAMETER) + ); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (HardwareAddrPtr == NULL) { + HardwareAddrPtr = &Mac; + } + + ZeroMem (HardwareAddrPtr, Private->SimpleNetwork->Mode->HwAddressSize); + + if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) { + DEBUG ( + (DEBUG_INFO, + "\nBcArp() Exit #2 %Xh (%r)", + EFI_SUCCESS, + EFI_SUCCESS) + ); + + EfiReleaseLock (&Private->Lock); + return EFI_SUCCESS; + } + + StatCode = DoArp (Private, ProtocolAddrPtr, HardwareAddrPtr); + + DEBUG ((DEBUG_INFO, "\nBcArp() Exit #3 %Xh (%r)", StatCode, StatCode)); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := MAC address found + @return other := MAC address could not be found + +**/ +EFI_STATUS +DoArp ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddrPtr, + OUT EFI_MAC_ADDRESS *HardwareAddrPtr + ) +{ + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + UINTN HeaderSize; + UINTN BufferSize; + UINT16 Protocol; + + DEBUG ((DEBUG_INFO, "\nDoArp()")); + + // + // + // + StatCode = SendRequest (Private, ProtocolAddrPtr, HardwareAddrPtr); + + if (EFI_ERROR (StatCode)) { + DEBUG ((DEBUG_INFO, "\nDoArp() Exit #1 %Xh (%r)", StatCode, StatCode)); + return StatCode; + } + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + ARP_REQUEST_TIMEOUT_MS * 10000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + // + // + // + for (;;) { + StatCode = WaitForReceive ( + Private, + EFI_PXE_BASE_CODE_FUNCTION_ARP, + TimeoutEvent, + &HeaderSize, + &BufferSize, + &Protocol + ); + + if (EFI_ERROR (StatCode)) { + break; + } + + if (Protocol != PXE_PROTOCOL_ETHERNET_ARP) { + continue; + } + + HandleArpReceive ( + Private, + (ARP_PACKET *) (Private->ReceiveBufferPtr + HeaderSize), + Private->ReceiveBufferPtr + ); + + if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) { + break; + } + } + + DEBUG ( + (DEBUG_INFO, + "\nDoArp() Exit #2 %Xh, (%r)", + StatCode, + StatCode) + ); + + gBS->CloseEvent (TimeoutEvent); + + return StatCode; +} + +/* eof - pxe_bc_arp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c new file mode 100644 index 0000000000..7dec5ec2d2 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c @@ -0,0 +1,3284 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_dhcp.c + +Abstract: + DHCP and PXE discovery protocol implementations. + + +**/ + +#include "Bc.h" + +#include "PxeArch.h" + +STATIC EFI_PXE_BASE_CODE_UDP_PORT DhcpServerPort = DHCP_SERVER_PORT; +STATIC EFI_PXE_BASE_CODE_UDP_PORT DHCPClientPort = DHCP_CLIENT_PORT; +STATIC EFI_PXE_BASE_CODE_UDP_PORT PseudoDhcpServerPort = PXE_DISCOVERY_PORT; +#define PSEUDO_DHCP_CLIENT_PORT PseudoDhcpServerPort +STATIC EFI_IP_ADDRESS BroadcastIP = { 0xffffffff }; +STATIC EFI_IP_ADDRESS DefaultSubnetMask = { 0xffffff00 }; + +typedef union { + DHCPV4_OP_STRUCT *OpPtr; + PXE_OP_SERVER_LIST *BootServersStr; + PXE_SERVER_LIST *BootServerList; + PXE_BOOT_MENU_ENTRY *BootMenuItem; + PXE_OP_DISCOVERY_CONTROL *DiscoveryControl; + PXE_OP_BOOT_MENU *BootMenu; + PXE_OP_BOOT_ITEM *BootItem; + DHCPV4_OP_VENDOR_OPTIONS *VendorOptions; + DHCPV4_OP_OVERLOAD *Overload; + DHCPV4_OP_CLASS *PxeClassStr; + DHCPV4_OP_SUBNET_MASK *SubnetMaskStr; + DHCPV4_OP_MESSAGE_TYPE *MessageType; + UINT8 *BytePtr; +} UNION_PTR; + +#pragma pack(1) +// +// option structure for DHCPREQUEST at end of DISCOVER options +// and for DHCPDECLINE +// +STATIC const struct requestopendstr { + DHCPV4_OP_REQUESTED_IP OpReqIP; + DHCPV4_OP_SERVER_IP DhcServerIpPtr; + UINT8 End[1]; +} +RequestOpEndStr = { + { + { + OP_DHCP_REQ_IP_ADD, + DHCPV4_OPTION_LENGTH(DHCPV4_OP_REQUESTED_IP) + } + }, + { + { + OP_DHCP_SERVER_IP, + DHCPV4_OPTION_LENGTH(DHCPV4_OP_SERVER_IP) + } + }, + { + OP_END + } +}; + +#define DHCP_REQ_OPTIONS (*(struct requestopendstr *) DHCPV4_OPTIONS_BUFFER.End) + +PXE_OP_BOOT_ITEM DefaultBootItem = { + { + VEND_PXE_BOOT_ITEM, + DHCPV4_OPTION_LENGTH(PXE_OP_BOOT_ITEM) + }, + 0, + 0 +}; + +// +// PXE discovery control default structure +// +STATIC PXE_OP_DISCOVERY_CONTROL DefaultDisCtl = { + { VEND_PXE_DISCOVERY_CONTROL, DHCPV4_OPTION_LENGTH(PXE_OP_DISCOVERY_CONTROL) }, + 0 +}; + +// +// PXE credentials option structure +// +typedef struct { + UINT8 c[4]; +} PXE_CREDENTIAL; + +typedef struct { + DHCPV4_OP_HEADER Header; + PXE_CREDENTIAL Credentials[1]; +} PXE_OP_CREDENTIAL_TYPES; + +// +// option structure for PXE discover (without credentials) +// +typedef struct { // discoveropendstr { + DHCPV4_OP_HEADER Header; // vendor options + PXE_OP_BOOT_ITEM BootItem; + UINT8 End[1]; // if credentials option, it starts here +} PXE_DISCOVER_OPTIONS; + +#define DISCOVERoptions (*(PXE_DISCOVER_OPTIONS *) DHCPV4_OPTIONS_BUFFER.End) +#define DISCREDoptions (*(PXE_OP_CREDENTIAL_TYPES *) DISCOVERoptions.End) + +// +// common option beginning for all our DHCP messages except +// DHCPDECLINE and DHCPRELEASE +// +STATIC struct optionsstr { + UINT8 DhcpCookie[4]; + DHCPV4_OP_MESSAGE_TYPE DhcpMessageType; + DHCPV4_OP_MAX_MESSAGE_SIZE DhcpMaxMessageSize; + DHCPV4_OP_REQUESTED_OPTIONS DhcpRequestedOptions; + DHCPV4_OP_PLATFORM_ID DhcpPlatformId; + DHCPV4_OP_NETWORK_INTERFACE DhcpNetworkInterface; + DHCPV4_OP_ARCHITECTURE_TYPE DhcpClientArchitecture; + DHCPV4_OP_CLASS_ID DhcpClassIdentifier; + UINT8 End[1]; +} DHCPOpStart; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +OptionsStrucInit ( + VOID + ) +{ + DHCPOpStart.DhcpCookie[0] = 99; + DHCPOpStart.DhcpCookie[1] = 130; + DHCPOpStart.DhcpCookie[2] = 83; + DHCPOpStart.DhcpCookie[3] = 99; + DHCPOpStart.DhcpMessageType.Header.OpCode = OP_DHCP_MESSAGE_TYPE; + DHCPOpStart.DhcpMessageType.Header.Length = 1; + DHCPOpStart.DhcpMessageType.Type = DHCPDISCOVER; + DHCPOpStart.DhcpMaxMessageSize.Header.OpCode = OP_DHCP_MAX_MESSAGE_SZ; + DHCPOpStart.DhcpMaxMessageSize.Header.Length = 2; + DHCPOpStart.DhcpMaxMessageSize.MaxSize[0] = MAX_DHCP_MSG_SZ >> 8; + DHCPOpStart.DhcpMaxMessageSize.MaxSize[1] = MAX_DHCP_MSG_SZ & 0xff; + DHCPOpStart.DhcpRequestedOptions.Header.OpCode = OP_DHCP_PARM_REQ_LIST; + DHCPOpStart.DhcpRequestedOptions.Header.Length = sizeof (DHCPV4_REQUESTED_OPTIONS_DATA); + DHCPOpStart.DhcpRequestedOptions.Data._OP_SUBNET_MASK = OP_SUBNET_MASK; /* 1 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_OFFSET = OP_TIME_OFFSET; /* 2 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_ROUTER_LIST = OP_ROUTER_LIST; /* 3 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_SERVERS = OP_TIME_SERVERS; /* 4 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NAME_SERVERS = OP_NAME_SERVERS; /* 5 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DNS_SERVERS = OP_DNS_SERVERS; /* 6 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_HOST_NAME = OP_HOST_NAME; /* 12 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_BOOT_FILE_SZ = OP_BOOT_FILE_SZ; /* 13 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DOMAIN_NAME = OP_DOMAIN_NAME; /* 15 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_ROOT_PATH = OP_ROOT_PATH; /* 17 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_EXTENSION_PATH = OP_EXTENSION_PATH; /* 18 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_MAX_DATAGRAM_SZ = OP_MAX_DATAGRAM_SZ; /* 22 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DEFAULT_TTL = OP_DEFAULT_TTL; /* 23 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_BROADCAST_ADD = OP_BROADCAST_ADD; /* 28 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_DOMAIN_NAME = OP_NIS_DOMAIN_NAME; /* 40 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_SERVERS = OP_NIS_SERVERS; /* 41 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NTP_SERVERS = OP_NTP_SERVERS; /* 42 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_VENDOR_SPECIFIC = OP_VENDOR_SPECIFIC; /* 43 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REQ_IP_ADD = OP_DHCP_REQ_IP_ADD; /* 50 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_LEASE_TIME = OP_DHCP_LEASE_TIME; /* 51 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_SERVER_IP = OP_DHCP_SERVER_IP; /* 54 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_RENEWAL_TIME = OP_DHCP_RENEWAL_TIME; /* 58 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REBINDING_TIME = OP_DHCP_REBINDING_TIME; /* 59 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_CLASS_IDENTIFIER = OP_DHCP_CLASS_IDENTIFIER; /* 60 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_TFTP_SERVER_NAME = OP_DHCP_TFTP_SERVER_NAME; /* 66 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_BOOTFILE = OP_DHCP_BOOTFILE; /* 67 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_PLATFORM_ID = OP_DHCP_PLATFORM_ID; /* 97 */ + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption128 = 128; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption129 = 129; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption130 = 130; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption131 = 131; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption132 = 132; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption133 = 133, DHCPOpStart.DhcpRequestedOptions.Data.VendorOption134 = 134; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption135 = 135; + DHCPOpStart.DhcpPlatformId.Header.OpCode = OP_DHCP_PLATFORM_ID; + DHCPOpStart.DhcpPlatformId.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_PLATFORM_ID); + DHCPOpStart.DhcpNetworkInterface.Header.OpCode = OP_DHCP_NETWORK_ARCH; + DHCPOpStart.DhcpNetworkInterface.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_NETWORK_INTERFACE); + DHCPOpStart.DhcpNetworkInterface.Type = 0; + DHCPOpStart.DhcpNetworkInterface.MajorVersion = 0; + DHCPOpStart.DhcpNetworkInterface.MinorVersion = 0; + DHCPOpStart.DhcpClientArchitecture.Header.OpCode = OP_DHCP_SYSTEM_ARCH; + DHCPOpStart.DhcpClientArchitecture.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_ARCHITECTURE_TYPE); + DHCPOpStart.DhcpClientArchitecture.Type = HTONS (SYS_ARCH); + DHCPOpStart.DhcpClassIdentifier.Header.OpCode = OP_DHCP_CLASS_IDENTIFIER; + DHCPOpStart.DhcpClassIdentifier.Header.Length = sizeof (DHCPV4_CLASS_ID_DATA); + CopyMem ( + DHCPOpStart.DhcpClassIdentifier.Data.ClassIdentifier, + "PXEClient:", + sizeof ("PXEClient:") + ); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit2, "Arch:", sizeof ("Arch:")); + CopyMem ( + DHCPOpStart.DhcpClassIdentifier.Data.ArchitectureType, + "xxxxx", + sizeof ("xxxxx") + ); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit3, ":", sizeof (":")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.InterfaceName, "XXXX", sizeof ("XXXX")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit4, ":", sizeof (":")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMajor, "yyy", sizeof ("yyy")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMinor, "xxx", sizeof ("xxx")); + DHCPOpStart.End[0] = OP_END; +}; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// DHCPDECLINE option structure +// +struct opdeclinestr { + UINT8 DhcpCookie[4]; + DHCPV4_OP_MESSAGE_TYPE DhcpMessageType; + struct requestopendstr OpDeclineEnd; +}; + +#define DHCPDECLINEoptions (*(struct opdeclinestr *) DHCPV4_TRANSMIT_BUFFER.options) + +// +// DHCPRELEASE option structure +// +struct opreleasestr { + UINT8 DhcpCookie[4]; + DHCPV4_OP_MESSAGE_TYPE DhcpMessageType; + DHCPV4_OP_SERVER_IP DhcServerIpPtr; + UINT8 End[1]; +}; + +#define DHCPRELEASEoptions (*(struct opreleasestr *) DHCPV4_TRANSMIT_BUFFER.options) + +// +// array of PXE vendor options in which we are interested +// value 0 -> not of interest, else value is index into PXE OPTION array +// option values from 1 to MAX_OUR_PXE_OPT +// +STATIC UINT8 ourPXEopts[MAX_OUR_PXE_OPT] = { + VEND_PXE_MTFTP_IP_IX, // multicast IP address of bootfile for MTFTP listen + VEND_PXE_MTFTP_CPORT_IX, // UDP Port to monitor for MTFTP responses - Intel order + VEND_PXE_MTFTP_SPORT_IX, // Server UDP Port for MTFTP open - Intel order + VEND_PXE_MTFTP_TMOUT_IX, // Listen timeout - secs + VEND_PXE_MTFTP_DELAY_IX, // Transmission timeout - secs + VEND_PXE_DISCOVERY_CONTROL_IX, // bit field + VEND_PXE_DISCOVERY_MCAST_ADDR_IX, // boot server discovery multicast address + VEND_PXE_BOOT_SERVERS_IX, // list of boot servers of form tp(2) cnt(1) ips[cnt] + VEND_PXE_BOOT_MENU_IX, + VEND_PXE_BOOT_PROMPT_IX, + VEND_PXE_MCAST_ADDRS_ALLOC_IX, // not used by client + VEND_PXE_CREDENTIAL_TYPES_IX, + VEND_13_IX, // not used by client + VEND_14_IX, // not used by client + VEND_15_IX, // not used by client + VEND_16_IX, // not used by client + VEND_17_IX, // not used by client + VEND_18_IX, // not used by client + VEND_19_IX, // not used by client + VEND_20_IX, // not used by client + VEND_21_IX, // not used by client + VEND_22_IX, // not used by client + VEND_23_IX, // not used by client + VEND_24_IX, // not used by client + VEND_25_IX, // not used by client + VEND_26_IX, // not used by client + VEND_27_IX, // not used by client + VEND_28_IX, // not used by client + VEND_29_IX, // not used by client + VEND_30_IX, // not used by client + VEND_31_IX, // not used by client + VEND_32_IX, // not used by client + VEND_33_IX, // not used by client + VEND_34_IX, // not used by client + VEND_35_IX, // not used by client + VEND_36_IX, // not used by client + VEND_37_IX, // not used by client + VEND_38_IX, // not used by client + VEND_39_IX, // not used by client + VEND_40_IX, // not used by client + VEND_41_IX, // not used by client + VEND_42_IX, // not used by client + VEND_43_IX, // not used by client + VEND_44_IX, // not used by client + VEND_45_IX, // not used by client + VEND_46_IX, // not used by client + VEND_47_IX, // not used by client + VEND_48_IX, // not used by client + VEND_49_IX, // not used by client + VEND_50_IX, // not used by client + VEND_51_IX, // not used by client + VEND_52_IX, // not used by client + VEND_53_IX, // not used by client + VEND_54_IX, // not used by client + VEND_55_IX, // not used by client + VEND_56_IX, // not used by client + VEND_57_IX, // not used by client + VEND_58_IX, // not used by client + VEND_59_IX, // not used by client + VEND_60_IX, // not used by client + VEND_61_IX, // not used by client + VEND_62_IX, // not used by client + VEND_63_IX, // not used by client + VEND_64_IX, // not used by client + VEND_65_IX, // not used by client + VEND_66_IX, // not used by client + VEND_67_IX, // not used by client + VEND_68_IX, // not used by client + VEND_69_IX, // not used by client + VEND_70_IX, // not used by client + VEND_PXE_BOOT_ITEM_IX +}; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// array of options in which we are interested +// value 0 -> not of interest, else value is index into OPTION array +// option values from 1 to MAX_OUR_OPT +// +STATIC UINT8 OurDhcpOptions[MAX_OUR_OPT] = { + OP_SUBNET_MASK_IX, // OP_SUBNET_MASK 1 // data is the subnet mask + OP_TIME_OFFSET_IX, // OP_TIME_OFFSET 2 // data is the time offset of subnet to UTC in seconds + OP_ROUTER_LIST_IX, // OP_ROUTER_LIST 3 // list of routers on subnet + OP_TIME_SERVERS_IX, // OP_TIME_SERVERS 4 // list of time servers available + OP_NAME_SERVERS_IX, // OP_NAME_SERVERS 5 // list of name servers available + OP_DNS_SERVERS_IX, // OP_DNS_SERVERS 6 // list of DNS servers available + OP_LOG_SERVERS_IX, // OP_LOG_SERVERS 7 + OP_COOKIE_SERVERS_IX, // OP_COOKIE_SERVERS 8 + OP_LPR_SREVERS_IX, // OP_LPR_SREVERS 9 + OP_IMPRESS_SERVERS_IX, // OP_IMPRESS_SERVERS 10 + OP_RES_LOC_SERVERS_IX, // OP_RES_LOC_SERVERS 11 + OP_HOST_NAME_IX, // OP_HOST_NAME 12 // client name + OP_BOOT_FILE_SZ_IX, // OP_BOOT_FILE_SZ 13 // number of 512 blocks of boot file + OP_DUMP_FILE_IX, // OP_DUMP_FILE 14 // path name of dump file if client crashes + OP_DOMAIN_NAME_IX, // OP_DOMAIN_NAME 15 // domain name to use + OP_SWAP_SERVER_IX, // OP_SWAP_SERVER 16 + OP_ROOT_PATH_IX, // OP_ROOT_PATH 17 // path name containing root disk + OP_EXTENSION_PATH_IX, // OP_EXTENSION_PATH 18 // name of TFTP downloadable file of form of OP + OP_IP_FORWARDING_IX, // OP_IP_FORWARDING 19 // enable/disable IP packet forwarding + OP_NON_LOCAL_SRC_RTE_IX, // OP_NON_LOCAL_SRC_RTE 20 // enable/disable non local source routing + OP_POLICY_FILTER_IX, // OP_POLICY_FILTER 21 // policy filters for non local source routing + OP_MAX_DATAGRAM_SZ_IX, // OP_MAX_DATAGRAM_SZ 22 // maximum datagram reassembly size + OP_DEFAULT_TTL_IX, // OP_DEFAULT_TTL 23 // default IP time to live + OP_MTU_AGING_TIMEOUT_IX, // OP_MTU_AGING_TIMEOUT 24 + OP_MTU_SIZES_IX, // OP_MTU_SIZES 25 + OP_MTU_TO_USE_IX, // OP_MTU_TO_USE 26 + OP_ALL_SUBNETS_LOCAL_IX, // OP_ALL_SUBNETS_LOCAL 27 + OP_BROADCAST_ADD_IX, // OP_BROADCAST_ADD 28 // broadcast address used on subnet + OP_PERFORM_MASK_DISCOVERY_IX, // OP_PERFORM_MASK_DISCOVERY 29 // perform mask discovery using ICMP + OP_RESPOND_TO_MASK_REQ_IX, // OP_RESPOND_TO_MASK_REQ 30 // respond to subnet mask requests using ICMP + OP_PERFORM_ROUTER_DISCOVERY_IX, // OP_PERFORM_ROUTER_DISCOVERY 31 + OP_ROUTER_SOLICIT_ADDRESS_IX, // OP_ROUTER_SOLICIT_ADDRESS 32 + OP_STATIC_ROUTER_LIST_IX, // OP_STATIC_ROUTER_LIST 33 // list of dest/route pairs + OP_USE_ARP_TRAILERS_IX, // OP_USE_ARP_TRAILERS 34 + OP_ARP_CACHE_TIMEOUT_IX, // OP_ARP_CACHE_TIMEOUT 35 + OP_ETHERNET_ENCAPSULATION_IX, // OP_ETHERNET_ENCAPSULATION 36 // 0 -> RFC 894, 1 -> IEEE 802.3 (RFC 1042) + OP_TCP_DEFAULT_TTL_IX, // OP_TCP_DEFAULT_TTL 37 // default time to live when sending TCP segments + OP_TCP_KEEP_ALIVE_INT_IX, // OP_TCP_KEEP_ALIVE_INT 38 // keep alive interval in seconds + OP_KEEP_ALIVE_GARBAGE_IX, // OP_KEEP_ALIVE_GARBAGE 39 + OP_NIS_DOMAIN_NAME_IX, // OP_NIS_DOMAIN_NAME 40 + OP_NIS_SERVERS_IX, // OP_NIS_SERVERS 41 + OP_NTP_SERVERS_IX, // OP_NTP_SERVERS 42 + OP_VENDOR_SPECIFIC_IX, // OP_VENDOR_SPECIFIC 43 + OP_NBNS_SERVERS_IX, // OP_NBNS_SERVERS 44 + OP_NBDD_SERVERS_IX, // OP_NBDD_SERVERS 45 + OP_NETBIOS_NODE_TYPE_IX, // OP_NETBIOS_NODE_TYPE 46 + OP_NETBIOS_SCOPE_IX, // OP_NETBIOS_SCOPE 47 + OP_XWINDOW_SYSTEM_FONT_SERVERS_IX, // OP_XWINDOW_SYSTEM_FONT_SERVERS 48 + OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS_IX, // OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49 + OP_DHCP_REQ_IP_ADD_IX, // OP_DHCP_REQ_IP_ADD 50 // requested IP address - in DHCPDISCOVER + OP_DHCP_LEASE_TIME_IX, // OP_DHCP_LEASE_TIME 51 // lease time requested/granted + OP_DHCP_OPTION_OVERLOAD_IX, // OP_DHCP_OPTION_OVERLOAD 52 // file/server name/both used to hold options + OP_DHCP_MESSAGE_TYPE_IX, // OP_DHCP_MESSAGE_TYPE 53 // message type + OP_DHCP_SERVER_IP_IX, // OP_DHCP_SERVER_IP 54 // IP of server + OP_DHCP_PARM_REQ_LIST_IX, // OP_DHCP_PARM_REQ_LIST 55 // list of requested parameters + OP_DHCP_ERROR_MESSAGE_IX, // OP_DHCP_ERROR_MESSAGE 56 // in DHCPNAK or DECLINE messages + OP_DHCP_MAX_MESSAGE_SZ_IX, // OP_DHCP_MAX_MESSAGE_SZ 57 // maximum DHCP message size client will accept + OP_DHCP_RENEWAL_TIME_IX, // OP_DHCP_RENEWAL_TIME 58 // time in seconds before transitioning to RENEWING state + OP_DHCP_REBINDING_TIME_IX, // OP_DHCP_REBINDING_TIME 59 // time in seconds before transitioning to REBINDING state + OP_DHCP_CLASS_IDENTIFIER_IX, // OP_DHCP_CLASS_IDENTIFIER 60 + OP_DHCP_CLIENT_IDENTIFIER_IX, // OP_DHCP_CLIENT_IDENTIFIER 61 + OP_RESERVED62_IX, // OP_RESERVED62 + OP_RESERVED63_IX, // OP_RESERVED63 + OP_NISPLUS_DOMAIN_NAME_IX, // OP_NISPLUS_DOMAIN_NAME 64 + OP_NISPLUS_SERVERS_IX, // OP_NISPLUS_SERVERS 65 + OP_DHCP_TFTP_SERVER_NAME_IX, // OP_DHCP_TFTP_SERVER_NAME 66 + OP_DHCP_BOOTFILE_IX // OP_DHCP_BOOTFILE 67 +}; + +#define RxBuf ((DHCP_RECEIVE_BUFFER *) (Private->ReceiveBuffers)) + +#pragma pack() + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @param Smbios Pointer to SMBIOS structure + @param StringNumber String number to return. 0 is used to skip all + strings and point to the next SMBIOS structure. + + @return Pointer to string, or pointer to next SMBIOS strcuture if StringNumber == 0 + +**/ +CHAR8 * +PxeBcLibGetSmbiosString ( + 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 string + // + for (; *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 gets system guid and serial number from the smbios table + + @param SystemGuid The pointer of returned system guid + @param SystemSerialNumber The pointer of returned system serial number + + @retval EFI_SUCCESS Successfully get the system guid and system serial + number + @retval EFI_NOT_FOUND Not find the SMBIOS table + +**/ +EFI_STATUS +PxeBcLibGetSmbiosSystemGuidAndSerialNumber ( + IN EFI_GUID *SystemGuid, + OUT CHAR8 **SystemSerialNumber + ) +{ + EFI_STATUS Status; + SMBIOS_STRUCTURE_TABLE *SmbiosTable; + SMBIOS_STRUCTURE_POINTER Smbios; + SMBIOS_STRUCTURE_POINTER SmbiosEnd; + UINT16 Index; + + Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable); + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + Smbios.Hdr = (SMBIOS_HEADER *) (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)); + *SystemSerialNumber = PxeBcLibGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber); + + return EFI_SUCCESS; + } + // + // Make Smbios point to the next record + // + PxeBcLibGetSmbiosString (&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 lenght of + // the structure. + // + return EFI_SUCCESS; + } + } + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// add router list to list +// +STATIC +VOID +Ip4AddRouterList ( + PXE_BASECODE_DEVICE *Private, + DHCPV4_OP_IP_LIST *IpListPtr + ) +{ + EFI_IP_ADDRESS TmpIp; + INTN Index; + INTN num; + + if (IpListPtr == NULL) { + return ; + } + + for (Index = 0, num = IpListPtr->Header.Length >> 2; Index < num; ++Index) { + CopyMem (&TmpIp, &IpListPtr->IpList[Index], 4); + Ip4AddRouter (Private, &TmpIp); + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send ARP for our IP - fail if someone has it +// +STATIC +BOOLEAN +SetStationIP ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_MAC_ADDRESS DestMac; + EFI_STATUS EfiStatus; + + ZeroMem (&DestMac, sizeof DestMac); + + if (GetHwAddr(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) + || DoArp(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) == EFI_SUCCESS) { + return FALSE; // somebody else has this IP + } + + CopyMem ( + (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->StationIp, + &DHCP_REQ_OPTIONS.OpReqIP.Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + + Private->GoodStationIp = TRUE; + + if (!Private->UseIgmpv1Reporting) { + return TRUE; + } + + if (Private->Igmpv1TimeoutEvent != NULL) { + return TRUE; + } + + EfiStatus = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->Igmpv1TimeoutEvent + ); + + if (EFI_ERROR (EfiStatus)) { + Private->Igmpv1TimeoutEvent = NULL; + return TRUE; + } + + EfiStatus = gBS->SetTimer ( + Private->Igmpv1TimeoutEvent, + TimerRelative, + (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000 + ); /* 400 seconds */ + + if (EFI_ERROR (EfiStatus)) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +AddRouters ( + PXE_BASECODE_DEVICE *Private, + DHCP_RECEIVE_BUFFER *RxBufPtr + ) +{ + Ip4AddRouterList ( + Private, + (DHCPV4_OP_IP_LIST *) RxBufPtr->OpAdds.PktOptAdds[OP_ROUTER_LIST_IX - 1] + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +EFI_STATUS +DoUdpWrite ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ClientIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr + ) +{ + UINTN Len; + + Len = sizeof DHCPV4_TRANSMIT_BUFFER; + + return UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + ClientIpPtr, + ClientPortPtr, + 0, + 0, + &Len, + Private->TransmitBuffer + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// initialize the DHCP structure +// +typedef struct { + UINT8 x[4]; +} C4Str; + +STATIC +VOID +InitDhcpv4TxBuf ( + PXE_BASECODE_DEVICE *Private + ) +{ + UINTN HwAddrLen; + UINT8 *String; + CHAR8 *SystemSerialNumber; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + + PxebcMode = Private->EfiBc.Mode; + + ZeroMem (&DHCPV4_TRANSMIT_BUFFER, sizeof (DHCPV4_STRUCT)); + DHCPV4_TRANSMIT_BUFFER.op = BOOTP_REQUEST; + DHCPV4_TRANSMIT_BUFFER.htype = Private->SimpleNetwork->Mode->IfType; + DHCPV4_TRANSMIT_BUFFER.flags = HTONS (DHCP_BROADCAST_FLAG); + CopyMem (&DHCPV4_OPTIONS_BUFFER, (VOID *) &DHCPOpStart, sizeof (DHCPOpStart)); + + // + // default to hardware address + // + HwAddrLen = Private->SimpleNetwork->Mode->HwAddressSize; + + if (HwAddrLen > sizeof DHCPV4_TRANSMIT_BUFFER.chaddr) { + HwAddrLen = sizeof DHCPV4_TRANSMIT_BUFFER.chaddr; + } + + String = (UINT8 *) &Private->SimpleNetwork->Mode->CurrentAddress; + + if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber ( + (EFI_GUID *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, + &SystemSerialNumber + ) == EFI_SUCCESS) { + if (PxebcMode->SendGUID) { + HwAddrLen = sizeof (EFI_GUID); + String = (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid; + } + } else { + // + // GUID not yet set - send all 0xff's to show programable (via SetVariable) + // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff); + // GUID not yet set - send all 0's to show not programable + // + ZeroMem (DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof (EFI_GUID)); + } + + DHCPV4_TRANSMIT_BUFFER.hlen = (UINT8) HwAddrLen; + CopyMem (DHCPV4_TRANSMIT_BUFFER.chaddr, String, HwAddrLen); + + CvtNum ( + SYS_ARCH, + (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType, + sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType + ); + + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.Type = Private->NiiPtr->Type; + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion = Private->NiiPtr->MajorVer; + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion = Private->NiiPtr->MinorVer; + + *(C4Str *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.InterfaceName = *(C4Str *) Private->NiiPtr->StringId; + + CvtNum ( + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion, + (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor, + sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor + ); + + CvtNum ( + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion, + (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor, + sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +UINT32 +DecodePxeOptions ( + DHCP_RECEIVE_BUFFER *RxBufPtr, + UINT8 *ptr, + INTN Len + ) +{ + UINT8 Op; + UINT8 *EndPtr; + INTN Index; + UNION_PTR LocalPtr; + UINT32 status; + + status = 0; + + for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) { + Op = ptr[0]; + Len = ptr[1]; + + switch (Op) { + case OP_PAD: + Len = -1; + break; + + case OP_END: + return status; + + default: + LocalPtr.BytePtr = ptr; + if (Op <= MAX_OUR_PXE_OPT) { + Index = ourPXEopts[Op - 1]; + if (Index) { + RxBufPtr->OpAdds.PxeOptAdds[Index - 1] = LocalPtr.OpPtr; + status |= 1 << Index; + if (Index == VEND_PXE_BOOT_ITEM && LocalPtr.BootItem->Header.Length == 3) { + RxBufPtr->OpAdds.Status |= USE_THREE_BYTE; + } + } + } + break; + } + } + + return status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +DecodeOptions ( + DHCP_RECEIVE_BUFFER *RxBufPtr, + UINT8 *ptr, + INTN Len + ) +{ + UINT8 Op; + UINT8 *EndPtr; + INTN Index; + UNION_PTR LocalPtr; + + for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) { + Op = ptr[0]; + Len = ptr[1]; + + switch (Op) { + case OP_PAD: + Len = -1; + break; + + case OP_END: + return ; + + default: + LocalPtr.BytePtr = ptr; + if (Op <= MAX_OUR_OPT) { + Index = OurDhcpOptions[Op - 1]; + if (Index) { + RxBufPtr->OpAdds.PktOptAdds[Index - 1] = LocalPtr.OpPtr; + if (Index == OP_VENDOR_SPECIFIC_IX) { + UINT32 status; + status = DecodePxeOptions ( + RxBufPtr, + (UINT8 *) LocalPtr.VendorOptions->VendorOptions, + LocalPtr.VendorOptions->Header.Length + ); + if (status) { + RxBufPtr->OpAdds.Status |= PXE_TYPE; + // + // check for all the MTFTP info options present - any missing is a nogo + // + if ((status & WfM11a_OPTS) == WfM11a_OPTS) { + RxBufPtr->OpAdds.Status |= WfM11a_TYPE; + } + + if (status & DISCOVER_OPTS) { + RxBufPtr->OpAdds.Status |= DISCOVER_TYPE; + } + + if (status & CREDENTIALS_OPT) { + RxBufPtr->OpAdds.Status |= CREDENTIALS_TYPE; + } + } + } + } + } + break; + } + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +Parse ( + DHCP_RECEIVE_BUFFER *RxBufPtr, + INTN Len + ) +{ + UNION_PTR LocalPtr; + + // + // initialize + // + SetMem (&RxBufPtr->OpAdds, sizeof RxBufPtr->OpAdds, 0); + + DecodeOptions ( + RxBufPtr, + RxBufPtr->u.Dhcpv4.options + 4, + Len - (sizeof RxBufPtr->u.Dhcpv4 - sizeof RxBufPtr->u.Dhcpv4.options + 4) + ); + + LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_OPTION_OVERLOAD_IX - 1]; + + if ((LocalPtr.OpPtr) && (LocalPtr.Overload->Overload & OVLD_SRVR_NAME)) { + DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.sname, sizeof RxBufPtr->u.Dhcpv4.sname); + } + + if (LocalPtr.OpPtr && (LocalPtr.Overload->Overload & OVLD_FILE)) { + DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.file, sizeof RxBufPtr->u.Dhcpv4.file); + } else if (!RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && RxBufPtr->u.Dhcpv4.file[0]) { + RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] = (DHCPV4_OP_STRUCT *) (RxBufPtr->u.Dhcpv4.file - sizeof (DHCPV4_OP_HEADER)); + + RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length = (UINT8) AsciiStrLen (RxBufPtr->u.Dhcpv4.file); + } + + LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_CLASS_IDENTIFIER_IX - 1]; + + if ((LocalPtr.OpPtr) && + LocalPtr.PxeClassStr->Header.Length >= 9 && + !CompareMem (LocalPtr.PxeClassStr->Class, "PXEClient", 9) + ) { + RxBufPtr->OpAdds.Status |= PXE_TYPE; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +CopyParseRxBuf ( + PXE_BASECODE_DEVICE *Private, + INTN RxBufIndex, + INTN PacketIndex + ) +{ + DHCP_RECEIVE_BUFFER *RxBufPtr; + + RxBufPtr = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[PacketIndex]; + + CopyMem ( + &RxBufPtr->u.Dhcpv4, + &RxBuf[RxBufIndex].u.Dhcpv4, + sizeof (RxBuf[RxBufIndex].u.Dhcpv4) + ); + + Parse (RxBufPtr, sizeof RxBufPtr->u.ReceiveBuffer); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +CopyProxyRxBuf ( + PXE_BASECODE_DEVICE *Private, + INTN RxBufIndex + ) +{ + Private->EfiBc.Mode->ProxyOfferReceived = TRUE; + CopyParseRxBuf (Private, RxBufIndex, PXE_OFFER_INDEX); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +CopyParse ( + PXE_BASECODE_DEVICE *Private, + EFI_PXE_BASE_CODE_PACKET *PacketPtr, + EFI_PXE_BASE_CODE_PACKET *NewPacketPtr, + INTN Index + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + + DhcpRxBuf = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[Index]; + + CopyMem ( + (EFI_PXE_BASE_CODE_PACKET *) &DhcpRxBuf->u.Dhcpv4, + NewPacketPtr, + sizeof (*NewPacketPtr) + ); + + CopyMem (&*PacketPtr, &*NewPacketPtr, sizeof (*NewPacketPtr)); + + Parse (DhcpRxBuf, sizeof DhcpRxBuf->u.ReceiveBuffer); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +AckEdit ( + DHCP_RECEIVE_BUFFER *DhcpRxBuf + ) +{ + UNION_PTR LocalPtr; + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]; + + // + // check that an ACK + // if a DHCP type, must be DHCPOFFER and must have server id + // + return (BOOLEAN) + ( + (LocalPtr.OpPtr) && + (LocalPtr.MessageType->Type == DHCPACK) && + DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// if a discover type packet, make sure all required fields are present +// +BOOLEAN +DHCPOfferAckEdit ( + DHCP_RECEIVE_BUFFER *DhcpRxBuf + ) +{ + PXE_OP_SERVER_LIST *BootServerOpPtr; + UNION_PTR LocalPtr; + + if ((DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) == 0) { + return TRUE; + } + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1]; + + if (LocalPtr.OpPtr == NULL) { + LocalPtr.OpPtr = (DHCPV4_OP_STRUCT *) &DefaultDisCtl; + DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultDisCtl; + } + // + // make sure all required fields are here + // if mucticast enabled, need multicast address + // + if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST) && + (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1] || !IS_MULTICAST (((DHCPV4_OP_STRUCT *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Data)) + ) { + return FALSE; + // + // missing required field + // + } + // + // if a list, it better be good + // + BootServerOpPtr = (PXE_OP_SERVER_LIST *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1]; + + if (BootServerOpPtr != NULL) { + PXE_SERVER_LIST *BootServerListPtr; + INTN ServerListLen; + INTN ServerEntryLen; + + BootServerListPtr = BootServerOpPtr->ServerList; + ServerListLen = BootServerOpPtr->Header.Length; + + do { + EFI_IPv4_ADDRESS *IpListPtr; + INTN IpCnt; + + IpCnt = BootServerListPtr->u.Ipv4List.IpCount; + + ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS); + + if (ServerListLen < ServerEntryLen) { + // + // missing required field + // + return FALSE; + } + + IpListPtr = BootServerListPtr->u.Ipv4List.IpList; + + while (IpCnt--) { + if (IS_MULTICAST (IpListPtr)) { + // + // missing required field + // + return FALSE; + } else { + ++IpListPtr; + } + } + + BootServerListPtr = (PXE_SERVER_LIST *) IpListPtr; + } while (ServerListLen -= ServerEntryLen); + } + // + // else there must be a list if use list enabled or multicast and + // broadcast disabled + // + else if ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) || + ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST)) + ) { + // + // missing required field + // + return FALSE; + } + // + // if not USE_BOOTFILE or no bootfile given, must have menu stuff + // + if (!(LocalPtr.DiscoveryControl->ControlBits & USE_BOOTFILE) || + !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] + ) { + INTN MenuLth; + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1]; + + if (LocalPtr.OpPtr == NULL || !DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]) { + // + // missing required field + // + return FALSE; + } + // + // make sure menu valid + // + MenuLth = LocalPtr.BootMenu->Header.Length; + LocalPtr.BootMenuItem = LocalPtr.BootMenu->MenuItem; + + do { + INTN MenuItemLen; + + MenuItemLen = LocalPtr.BootMenuItem->DataLen; + + if (MenuItemLen == 0) { + // + // missing required field + // + return FALSE; + } + + MenuItemLen += sizeof (*LocalPtr.BootMenuItem) - sizeof (LocalPtr.BootMenuItem->Data); + + MenuLth -= MenuItemLen; + LocalPtr.BytePtr += MenuItemLen; + } while (MenuLth > 0); + + if (MenuLth != 0) { + // + // missing required field + // + return FALSE; + } + } + + if (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) { + DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultBootItem; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +DHCPAckEdit ( + DHCP_RECEIVE_BUFFER *RxBufPtr + ) +{ + return (BOOLEAN) (DHCPOfferAckEdit (RxBufPtr) ? AckEdit (RxBufPtr) : FALSE); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// get an offer/ack +// +EFI_STATUS +GetOfferAck ( + PXE_BASECODE_DEVICE *Private, + BOOLEAN (*ExtraEdit)(DHCP_RECEIVE_BUFFER *DhcpRxBuf), + UINT16 OpFlags, // for Udp read + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ClientIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr, + DHCP_RECEIVE_BUFFER *DhcpRxBuf, + EFI_EVENT TimeoutEvent + ) +/*++ +Routine description: + Wait for an OFFER/ACK packet. + +Parameters: + Private := Pointer to PxeBc interface + ExtraEdit := Pointer to extra option checking function + OpFlags := UdpRead() option flags + ServerIpPtr := + ServerPortPtr := + ClientIpPtr := + ClientPortPtr := + DhcpRxBuf := + TimeoutEvent := + +Returns: +--*/ +{ + EFI_IP_ADDRESS ServerIp; + EFI_STATUS StatCode; + INTN RxBufLen; + + for (;;) { + // + // Wait until we get a UDP packet. + // + ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS)); + RxBufLen = sizeof RxBuf[0].u.ReceiveBuffer; + + if ((StatCode = UdpRead ( + Private, + OpFlags, + ClientIpPtr, + ClientPortPtr, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + (UINTN *) &RxBufLen, + &DhcpRxBuf->u.Dhcpv4, + TimeoutEvent + )) != EFI_SUCCESS) { + if (StatCode == EFI_TIMEOUT) { + StatCode = EFI_NO_RESPONSE; + } + + break; + } + // + // got a packet - see if a good offer + // + if (DhcpRxBuf->u.Dhcpv4.op != BOOTP_REPLY) { + continue; + } + + if (DhcpRxBuf->u.Dhcpv4.xid != DHCPV4_TRANSMIT_BUFFER.xid) { + continue; + } + + if (*(UINT32 *) DHCPV4_TRANSMIT_BUFFER.options != * (UINT32 *) DhcpRxBuf->u.Dhcpv4.options) { + continue; + } + + if (*(UINT8 *) &DhcpRxBuf->u.Dhcpv4.yiaddr > 223) { + continue; + } + + if (CompareMem ( + DhcpRxBuf->u.Dhcpv4.chaddr, + DHCPV4_TRANSMIT_BUFFER.chaddr, + sizeof DhcpRxBuf->u.Dhcpv4.chaddr + )) { + // + // no good + // + continue; + } + + Parse (DhcpRxBuf, RxBufLen); + + if (!(*ExtraEdit) (DhcpRxBuf)) { + continue; + } + // + // Good DHCP packet. + // + StatCode = EFI_SUCCESS; + break; + } + + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// get DHCPOFFER's +// +EFI_STATUS +GetOffers ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_IP_ADDRESS ClientIp; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + INTN NumOffers; + INTN Index; + + // + // + // + ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS)); + NumOffers = 0; + + for (Index = 0; Index < (sizeof Private->ServerCount) / sizeof Private->ServerCount[0]; ++Index) { + Private->ServerCount[Index] = 0; + Private->GotProxy[Index] = 0; + } + + Private->GotBootp = 0; + // + // these we throw away + // + Private->GotProxy[DHCP_ONLY_IX] = 1; + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + // + // get offers + // + for (;;) { + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + UNION_PTR LocalPtr; + + DhcpRxBuf = &RxBuf[NumOffers]; + + if (( + StatCode = GetOfferAck ( + Private, + DHCPOfferAckEdit, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + &ServerIp, + &DhcpServerPort, + &ClientIp, + &DHCPClientPort, + DhcpRxBuf, + TimeoutEvent + ) +) != EFI_SUCCESS + ) { + break; + } + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]; + + // + // check type of offer + // + if (LocalPtr.OpPtr == NULL) { + // + // bootp - we only need one and make sure has bootfile + // + if (Private->GotBootp || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + continue; + } + + Private->GotBootp = (UINT8) (NumOffers + 1); + } + // + // if a DHCP type, must be DHCPOFFER and must have server id + // + else if (LocalPtr.MessageType->Type != DHCPOFFER || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]) { + continue; + } else { + INTN TypeIx; + + // + // get type - PXE10, WfM11a, or BINL + // + if (DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) { + TypeIx = PXE10_IX; + } else if (DhcpRxBuf->OpAdds.Status & WfM11a_TYPE) { + // + // WfM - make sure it has a bootfile + // + if (!DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + continue; + } + + TypeIx = WfM11a_IX; + } else { + TypeIx = (DhcpRxBuf->OpAdds.Status & PXE_TYPE) ? BINL_IX : DHCP_ONLY_IX; + } + // + // check DHCP or proxy + // + if (DhcpRxBuf->u.Dhcpv4.yiaddr == 0) { + // + // proxy - only need one of each type if not BINL + // and must have at least PXE_TYPE + // + if (TypeIx == BINL_IX) { + Private->BinlProxies[Private->GotProxy[BINL_IX]++] = (UINT8) NumOffers; + } else if (Private->GotProxy[TypeIx]) { + continue; + } else { + Private->GotProxy[TypeIx] = (UINT8) (NumOffers + 1); + } + } else { + Private->OfferCount[TypeIx][Private->ServerCount[TypeIx]++] = (UINT8) NumOffers; + } + } + + if (++NumOffers == MAX_OFFERS) { + break; + } + } + + gBS->CloseEvent (TimeoutEvent); + Private->NumOffersReceived = NumOffers; + + return (Private->NumOffersReceived) ? EFI_SUCCESS : EFI_NO_RESPONSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send DHCPDECLINE +// +STATIC +VOID +DeclineOffer ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + UINT16 SaveSecs; + + PxebcMode = Private->EfiBc.Mode; + SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs; + + DHCPV4_TRANSMIT_BUFFER.secs = 0; + DHCPV4_TRANSMIT_BUFFER.flags = 0; + SetMem ( + DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opdeclinestr), + sizeof (DHCPOpStart) - sizeof (struct opdeclinestr), + OP_PAD + ); + DHCPDECLINEoptions.DhcpMessageType.Type = DHCPDECLINE; + CopyMem (&DHCPDECLINEoptions.OpDeclineEnd, &DHCP_REQ_OPTIONS, sizeof (struct requestopendstr)); + + { + EFI_IP_ADDRESS TmpIp; + + CopyMem (&TmpIp, &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, sizeof TmpIp); + + DoUdpWrite ( + Private, + &TmpIp, + &DhcpServerPort, + &PxebcMode->StationIp, + &DHCPClientPort + ); + } + + InitDhcpv4TxBuf (Private); + DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs; + Private->GoodStationIp = FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send DHCPRELEASE +// +STATIC +BOOLEAN +Release ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + UINT16 SaveSecs; + + PxebcMode = Private->EfiBc.Mode; + SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs; + DHCPV4_TRANSMIT_BUFFER.secs = 0; + + SetMem ( + DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opreleasestr), + sizeof (DHCPOpStart) - sizeof (struct opreleasestr), + OP_PAD + ); + + DHCPRELEASEoptions.DhcpMessageType.Type = DHCPRELEASE; + + CopyMem ( + &DHCPRELEASEoptions.DhcServerIpPtr, + &(DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1], + sizeof DHCPRELEASEoptions.DhcServerIpPtr + ); + + DHCPRELEASEoptions.End[0] = OP_END; + + { + EFI_IP_ADDRESS TmpIp; + + CopyMem (&TmpIp, &DHCPRELEASEoptions.DhcServerIpPtr.Ip, sizeof TmpIp); + + DoUdpWrite ( + Private, + &TmpIp, + &DhcpServerPort, + &PxebcMode->StationIp, + &DHCPClientPort + ); + } + + InitDhcpv4TxBuf (Private); + + DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs; + Private->GoodStationIp = FALSE; + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +GetBINLAck ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return FALSE; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return FALSE; + } + // + // + // + DhcpRxBuf = &PXE_BINL_BUFFER; + + for (;;) { + EFI_PXE_BASE_CODE_UDP_PORT BINLSrvPort; + + BINLSrvPort = 0; + + if (GetOfferAck ( + Private, + AckEdit, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + ServerIpPtr, + &BINLSrvPort, + &Private->EfiBc.Mode->StationIp, + &PSEUDO_DHCP_CLIENT_PORT, + DhcpRxBuf, + TimeoutEvent + ) != EFI_SUCCESS) { + break; + } + // + // make sure from whom we wanted + // + if (!DhcpRxBuf->u.Dhcpv4.yiaddr && !CompareMem ( + &ServerIpPtr->v4, + &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof (ServerIpPtr->v4) + )) { + gBS->CloseEvent (TimeoutEvent); + // + // got an ACK from server + // + return TRUE; + } + } + + gBS->CloseEvent (TimeoutEvent); + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// make sure we can get BINL +// send DHCPREQUEST to PXE server +// +STATIC +BOOLEAN +TryBINL ( + PXE_BASECODE_DEVICE *Private, + INTN OfferIx + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + EFI_IP_ADDRESS ServerIp; + UINT16 SaveSecs; + INTN Index; + + DhcpRxBuf = &RxBuf[OfferIx]; + + // + // use next server address first. + // + ServerIp.Addr[0] = DhcpRxBuf->u.Dhcpv4.siaddr; + if (ServerIp.Addr[0] == 0) { + // + // next server address is NULL, use option 54. + // + CopyMem ( + ((EFI_IPv4_ADDRESS *) &ServerIp), + &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + // + // client IP address - filled in by client if it knows it + // + CopyMem ( + ((EFI_IPv4_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr), + &DHCP_REQ_OPTIONS.OpReqIP.Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + + SetMem (&DHCP_REQ_OPTIONS, sizeof DHCP_REQ_OPTIONS, OP_PAD); + DHCPV4_TRANSMIT_BUFFER.flags = 0; + DHCPV4_OPTIONS_BUFFER.End[0] = OP_END; + AddRouters (Private, DhcpRxBuf); + SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs; + + for (Index = 0; Index < 3; Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Index) { + DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds); + + // + // unicast DHCPREQUEST to PXE server + // + if (DoUdpWrite ( + Private, + &ServerIp, + &PseudoDhcpServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &PSEUDO_DHCP_CLIENT_PORT + ) != EFI_SUCCESS) { + break; + } + + if (!GetBINLAck (Private, &ServerIp)) { + continue; + } + // + // early exit failures + // make sure a good ACK + // + if (!DHCPOfferAckEdit (&PXE_BINL_BUFFER) || ( + !(PXE_BINL_BUFFER.OpAdds.Status & DISCOVER_TYPE) && !PXE_BINL_BUFFER.OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] + ) + ) { + break; + } + + Private->EfiBc.Mode->ProxyOfferReceived = TRUE; + return TRUE; + } + // + // failed - reset seconds field, etc. + // + Private->EfiBc.Mode->RouteTableEntries = 0; + // + // reset + // + DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs; + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +TryFinishBINL ( + PXE_BASECODE_DEVICE *Private, + INTN OfferIx + ) +{ + if (TryBINL (Private, OfferIx)) { + return TRUE; + } + + return Release (Private); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +TryFinishProxyBINL ( + PXE_BASECODE_DEVICE *Private + ) +{ + INTN Index; + + for (Index = 0; Index < Private->GotProxy[BINL_IX]; ++Index) { + if (TryBINL (Private, Private->BinlProxies[Index])) { + return TRUE; + } + } + + return Release (Private); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// try to finish DORA - send DHCP request, wait for ACK, check with ARP +// +STATIC +BOOLEAN +TryFinishDORA ( + PXE_BASECODE_DEVICE *Private, + INTN OfferIx + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + EFI_IP_ADDRESS ClientIp; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS StatCode; + UNION_PTR LocalPtr; + EFI_EVENT TimeoutEvent; + + // + // send DHCP request + // if fail return false + // + DhcpRxBuf = &DHCPV4_ACK_BUFFER; + DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type = DHCPREQUEST; + CopyMem (&DHCP_REQ_OPTIONS, &RequestOpEndStr, sizeof (RequestOpEndStr)); +// DHCP_REQ_OPTIONS = RequestOpEndStr; + DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[OfferIx].u.Dhcpv4.yiaddr; + + CopyMem ( + &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, + &((DHCPV4_OP_SERVER_IP *) RxBuf[OfferIx].OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip + ); + + CopyMem ( + Private->EfiBc.Mode->SubnetMask.Addr, + &DefaultSubnetMask, + 4 + ); + + // + // broadcast DHCPREQUEST + // + if (DoUdpWrite ( + Private, + &BroadcastIP, + &DhcpServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &DHCPClientPort + ) != EFI_SUCCESS) { + return FALSE; + } + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return FALSE; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerPeriodic, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return FALSE; + } + // + // wait for ACK + // + for (;;) { + if (GetOfferAck ( + Private, + DHCPAckEdit, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP, + &ServerIp, + &DhcpServerPort, + &ClientIp, + &DHCPClientPort, + DhcpRxBuf, + TimeoutEvent + ) != EFI_SUCCESS) { + break; + } + // + // check type of response - need DHCPACK + // + if (CompareMem ( + &DHCP_REQ_OPTIONS.OpReqIP.Ip, + &DhcpRxBuf->u.Dhcpv4.yiaddr, + sizeof (EFI_IPv4_ADDRESS) + ) || CompareMem ( + &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, + &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof (EFI_IPv4_ADDRESS) + )) { + continue; + } + // + // got ACK + // check with ARP that IP unused - good return true + // + if (!SetStationIP (Private)) { + // + // fail - send DHCPDECLINE and return false + // + DeclineOffer (Private); + break; + } + + LocalPtr.OpPtr = DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1]; + + if (LocalPtr.OpPtr != NULL) { + CopyMem ( + (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask, + &LocalPtr.SubnetMaskStr->Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + AddRouters (Private, DhcpRxBuf); + gBS->CloseEvent (TimeoutEvent); + return TRUE; + } + + gBS->CloseEvent (TimeoutEvent); + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// try a DHCP server of appropriate type +// +STATIC +BOOLEAN +TryDHCPFinishDORA ( + PXE_BASECODE_DEVICE *Private, + INTN TypeIx + ) +{ + INTN Index; + + // + // go through the DHCP servers of the requested type + // + for (Index = 0; Index < Private->ServerCount[TypeIx]; ++Index) { + if (TryFinishDORA (Private, Index = Private->OfferCount[TypeIx][Index])) { + if (TypeIx == BINL_IX && !TryFinishBINL (Private, Index)) { + continue; + } + + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// try a DHCP only server and a proxy of appropriate type +// +STATIC +BOOLEAN +TryProxyFinishDORA ( + PXE_BASECODE_DEVICE *Private, + INTN TypeIx + ) +{ + INTN Index; + + if (!Private->GotProxy[TypeIx]) { + // + // no proxies of the type wanted + // + return FALSE; + } + // + // go through the DHCP only servers + // + for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) { + if (TryFinishDORA (Private, Private->OfferCount[DHCP_ONLY_IX][Index])) { + if (TypeIx != BINL_IX) { + CopyProxyRxBuf (Private, Private->GotProxy[TypeIx] - 1); + } else if (!TryFinishProxyBINL (Private)) { + // + // if didn't work with this DHCP, won't work with any + // + return FALSE; + } + + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// getting to the bottom of the barrel +// +STATIC +BOOLEAN +TryAnyWithBootfileFinishDORA ( + PXE_BASECODE_DEVICE *Private + ) +{ + // + // try a DHCP only server who has a bootfile + // + UNION_PTR LocalPtr; + INTN Index; + + for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) { + INTN offer; + + offer = Private->OfferCount[DHCP_ONLY_IX][Index]; + + if (RxBuf[offer].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && TryFinishDORA (Private, offer)) { + return TRUE; + } + } + // + // really at bottom - see if be have any bootps + // + if (!Private->GotBootp) { + return FALSE; + } + + DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[Private->GotBootp - 1].u.Dhcpv4.yiaddr; + + if (!SetStationIP (Private)) { + return FALSE; + } + // + // treat BOOTP response as DHCP ACK packet + // + CopyParseRxBuf (Private, Private->GotBootp - 1, DHCPV4_ACK_INDEX); + + LocalPtr.OpPtr = RxBuf[Private->GotBootp - 1].OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1]; + + if (LocalPtr.OpPtr != NULL) { + *(EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask = LocalPtr.SubnetMaskStr->Ip; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* DoDhcpDora() + */ +STATIC +EFI_STATUS +DoDhcpDora ( + PXE_BASECODE_DEVICE *Private, + BOOLEAN SortOffers + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS StatCode; + INTN NumOffers; + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST; + + Filter.IpCnt = 0; + Filter.reserved = 0; + + // + // set filter unicast or broadcast + // + if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + return StatCode; + } + // + // seed random number with hardware address + // + SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress); + + for (Private->Timeout = 1; + Private->Timeout < 17; + Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), Private->Timeout <<= 1 + ) { + INTN Index; + + InitDhcpv4TxBuf (Private); + DHCPV4_TRANSMIT_BUFFER.xid = Random (Private); + DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds); + + // + // broadcast DHCPDISCOVER + // + StatCode = DoUdpWrite ( + Private, + &BroadcastIP, + &DhcpServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &DHCPClientPort + ); + + if (StatCode != EFI_SUCCESS) { + return StatCode; + } + + CopyMem ( + &Private->EfiBc.Mode->DhcpDiscover, + (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + + // + // get DHCPOFFER's + // + if ((StatCode = GetOffers (Private)) != EFI_SUCCESS) { + if (StatCode != EFI_NO_RESPONSE) { + return StatCode; + } + + continue; + } + // + // select offer and reply DHCPREQUEST + // + if (SortOffers) { + if (TryDHCPFinishDORA(Private, PXE10_IX) || // try DHCP with PXE10 + TryDHCPFinishDORA(Private, WfM11a_IX) || // no - try with WfM + TryProxyFinishDORA(Private, PXE10_IX) || // no - try DHCP only and proxy with PXE10 + TryProxyFinishDORA(Private, WfM11a_IX) || // no - try DHCP only and proxy with WfM + TryDHCPFinishDORA(Private, BINL_IX) || // no - try with WfM + TryProxyFinishDORA(Private, BINL_IX) || // no - try DHCP only and proxy with PXE10 + TryAnyWithBootfileFinishDORA(Private)) + { + return EFI_SUCCESS; + } + + continue; + } + // + // FIFO order + // + NumOffers = Private->NumOffersReceived; + + for (Index = 0; Index < NumOffers; ++Index) { + // + // ignore proxies + // + if (!RxBuf[Index].u.Dhcpv4.yiaddr) { + continue; + } + // + // check if a bootp server + // + if (!RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]) { + // + // it is - just check ARP + // + if (!SetStationIP (Private)) { + continue; + } + } + // + // else check if a DHCP only server + // + else if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE))) { + // + // it is a normal DHCP offer (without any PXE options), just finish the D.O.R.A by sending DHCP request. + // + if (!TryFinishDORA (Private, Index)) { + continue; + } + } else if (TryFinishDORA (Private, Index)) { + if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) && !TryFinishBINL (Private, Index)) { + continue; + } + } + + DEBUG ((DEBUG_WARN, "\nDoDhcpDora() Got packets. ")); + return EFI_SUCCESS; + } + // + // now look for DHCP onlys and a Proxy + // + for (Index = 0; Index < NumOffers; ++Index) { + INT8 Index2; + + // + // ignore proxies, bootps, non DHCP onlys, and bootable DHCPS + // + if (!RxBuf[Index].u.Dhcpv4.yiaddr || + !RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1] || + RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE) || + RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] + ) { + continue; + } + // + // found non bootable DHCP only - try to find a proxy + // + for (Index2 = 0; Index2 < NumOffers; ++Index2) { + if (!RxBuf[Index2].u.Dhcpv4.yiaddr) { + if (!TryFinishDORA (Private, Index)) { + // + // DHCP no ACK + // + break; + } + + if (RxBuf[Index2].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) { + CopyProxyRxBuf (Private, Index2); + } else if (!TryFinishBINL (Private, Index2)) { + continue; + } + + DEBUG ((DEBUG_WARN, "\nDoDhcpDora() Got packets. ")); + return EFI_SUCCESS; + } + } + } + } + + return EFI_NO_RESPONSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// determine if the server ip is in the ip list +// +BOOLEAN +InServerList ( + EFI_IP_ADDRESS *ServerIpPtr, + PXE_SERVER_LISTS *ServerListPtr + ) +{ + UINTN Index; + + if (!ServerListPtr || !ServerListPtr->Ipv4List.IpCount) { + return TRUE; + } + + for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) { + if (!CompareMem ( + ServerIpPtr, + &ServerListPtr->Ipv4List.IpList[Index], + sizeof (EFI_IPv4_ADDRESS) + )) { + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +ExtractBootServerList ( + UINT16 Type, + DHCPV4_OP_STRUCT *ptr, + PXE_SERVER_LISTS **ServerListPtr + ) +{ + UNION_PTR LocalPtr; + INTN ServerListLen; + + LocalPtr.OpPtr = ptr; + ServerListLen = LocalPtr.BootServersStr->Header.Length; + + // + // find type + // + LocalPtr.BootServerList = LocalPtr.BootServersStr->ServerList; + + while (ServerListLen) { + INTN ServerEntryLen; + + ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (LocalPtr.BootServerList->u.Ipv4List.IpCount - 1) * + sizeof (EFI_IPv4_ADDRESS); + + if (NTOHS (LocalPtr.BootServerList->Type) == Type) { + *ServerListPtr = &LocalPtr.BootServerList->u; + return TRUE; + } + + (LocalPtr.BytePtr) += ServerEntryLen; + ServerListLen -= ServerEntryLen; + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +FreeMem ( + PXE_BASECODE_DEVICE *Private + ) +{ + if (Private->TransmitBuffer != NULL) { + gBS->FreePool (Private->TransmitBuffer); + Private->TransmitBuffer = NULL; + } + + if (Private->ReceiveBuffers != NULL) { + gBS->FreePool (Private->ReceiveBuffers); + Private->ReceiveBuffers = NULL; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +GetMem ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_STATUS Status; + + if (Private->DhcpPacketBuffer == NULL) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1), + &Private->DhcpPacketBuffer + ); + + if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) { + Private->DhcpPacketBuffer = NULL; + FreeMem (Private); + return FALSE; + } + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_PXE_BASE_CODE_PACKET), + &Private->TransmitBuffer + ); + + if (EFI_ERROR (Status) || Private->TransmitBuffer == NULL) { + gBS->FreePool (Private->DhcpPacketBuffer); + Private->DhcpPacketBuffer = NULL; + Private->TransmitBuffer = NULL; + FreeMem (Private); + return FALSE; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (DHCP_RECEIVE_BUFFER) * (MAX_OFFERS), + &Private->ReceiveBuffers + ); + + if (EFI_ERROR (Status) || Private->ReceiveBuffers == NULL) { + gBS->FreePool (Private->TransmitBuffer); + gBS->FreePool (Private->DhcpPacketBuffer); + Private->DhcpPacketBuffer = NULL; + Private->TransmitBuffer = NULL; + Private->ReceiveBuffers = NULL; + FreeMem (Private); + return FALSE; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +EFIAPI +BcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + PXE_BASECODE_DEVICE *Private; + EFI_STATUS StatCode; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + Filter.IpCnt = 0; + Filter.reserved = 0; + + DEBUG ((DEBUG_INFO, "\nBcDhcp() Enter. ")); + + PxebcMode = Private->EfiBc.Mode; + + if (!GetMem (Private)) { + DEBUG ((DEBUG_ERROR, "\nBcDhcp() GetMem() failed.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + PxebcMode->DhcpDiscoverValid = FALSE; + PxebcMode->DhcpAckReceived = FALSE; + PxebcMode->ProxyOfferReceived = FALSE; + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP; + + // + // Issue BC command + // + if (Private->TotalSeconds == 0) { + // + // put in seconds field of DHCP send packets + // + Private->TotalSeconds = 4; + } + + if ((StatCode = DoDhcpDora (Private, SortOffers)) == EFI_SUCCESS) { + // + // success - copy packets + // + PxebcMode->DhcpDiscoverValid = PxebcMode->DhcpAckReceived = TRUE; + + CopyMem ( + &PxebcMode->DhcpAck, + (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_ACK_PACKET, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + + if (PxebcMode->ProxyOfferReceived) { + CopyMem ( + &PxebcMode->ProxyOffer, + (EFI_PXE_BASE_CODE_PACKET *) &PXE_OFFER_PACKET, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + } + } + // + // set filter back to unicast + // + IpFilter (Private, &Filter); + + FreeMem (Private); + + // + // Unlock the instance data + // + DEBUG ((DEBUG_WARN, "\nBcDhcp() Exit = %xh ", StatCode)); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +VerifyCredentialOption ( + UINT8 *tx, + UINT8 *rx + ) +{ + UINTN n; + + // + // Fail verification if either pointer is NULL. + // + if (tx == NULL || rx == NULL) { + return FALSE; + } + // + // Fail verification if tx[0] is not a credential type option + // or if the length is zero or not a multiple of four. + // + if (tx[0] != VEND_PXE_CREDENTIAL_TYPES || tx[1] == 0 || tx[1] % 4 != 0) { + return FALSE; + } + // + // Fail verification if rx[0] is not a credential type option + // or if the length is not equal to four. + // + if (rx[0] != VEND_PXE_CREDENTIAL_TYPES || rx[1] != 4) { + return FALSE; + } + // + // Look through transmitted credential types for a copy + // of the received credential type. + // + for (n = 0; n < tx[1]; n += 4) { + if (!CompareMem (&tx[n + 2], &rx[2], 4)) { + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +DoDiscover ( + PXE_BASECODE_DEVICE *Private, + UINT16 OpFlags, + IN UINT16 Type, + IN UINT16 *LayerPtr, + IN BOOLEAN UseBis, + EFI_IP_ADDRESS *DestPtr, + PXE_SERVER_LISTS *ServerListPtr + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT ClientPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerPort; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + UINT8 OpLen; + + PxebcMode = Private->EfiBc.Mode; + + if (DestPtr->Addr[0] == 0) { + DEBUG ((DEBUG_WARN, "\nDoDiscover() !DestPtr->Addr[0]")); + return EFI_INVALID_PARAMETER; + } + // + // seed random number with hardware address + // + SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress); + + if (DestPtr->Addr[0] == BroadcastIP.Addr[0]) { + ClientPort = DHCPClientPort; + ServerPort = DhcpServerPort; + } else { + ClientPort = PSEUDO_DHCP_CLIENT_PORT; + ServerPort = PseudoDhcpServerPort; + } + + if (UseBis) { + *LayerPtr |= PXE_BOOT_LAYER_CREDENTIAL_FLAG; + } else { + *LayerPtr &= PXE_BOOT_LAYER_MASK; + } + + for (Private->Timeout = 1; + Private->Timeout < 5; + Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Private->Timeout + ) { + InitDhcpv4TxBuf (Private); + // + // initialize DHCP message structure + // + DHCPV4_TRANSMIT_BUFFER.xid = Random (Private); + DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds); + CopyMem ( + &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &PxebcMode->StationIp, + sizeof DHCPV4_TRANSMIT_BUFFER.ciaddr + ); + + DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type = DHCPREQUEST; + DISCOVERoptions.Header.OpCode = OP_VENDOR_SPECIFIC; + DISCOVERoptions.BootItem.Header.OpCode = VEND_PXE_BOOT_ITEM; + DISCOVERoptions.BootItem.Header.Length = DHCPV4_OPTION_LENGTH (PXE_OP_BOOT_ITEM); + DISCOVERoptions.BootItem.Type = HTONS (Type); + DISCOVERoptions.BootItem.Layer = HTONS (*LayerPtr); + + if (UseBis) { + EFI_BIS_PROTOCOL *BisPtr; + BIS_APPLICATION_HANDLE BisAppHandle; + EFI_BIS_DATA *BisDataSigInfo; + EFI_BIS_SIGNATURE_INFO *BisSigInfo; + UINTN Index; + UINTN Index2; + + BisPtr = PxebcBisStart ( + Private, + &BisAppHandle, + &BisDataSigInfo + ); + + if (BisPtr == NULL) { + // + // %%TBD - In order to get here, BIS must have + // been present when PXEBC.Start() was called. + // BIS had to be shutdown/removed/damaged + // before PXEBC.Discover() was called. + // Do we need to document a specific error + // for this case? + // + return EFI_OUT_OF_RESOURCES; + } + // + // Compute number of credential types. + // + Index2 = BisDataSigInfo->Length / sizeof (EFI_BIS_SIGNATURE_INFO); + + DISCREDoptions.Header.OpCode = VEND_PXE_CREDENTIAL_TYPES; + + DISCREDoptions.Header.Length = (UINT8) (Index2 * sizeof (PXE_CREDENTIAL)); + + OpLen = (UINT8) (DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS) + sizeof (DHCPV4_OP_HEADER) + DISCREDoptions.Header.Length); + + BisSigInfo = (EFI_BIS_SIGNATURE_INFO *) BisDataSigInfo->Data; + + for (Index = 0; Index < Index2; ++Index) { + UINT32 x; + + CopyMem (&x, &BisSigInfo[Index], sizeof x); + x = HTONL (x); + CopyMem (&DISCREDoptions.Credentials[Index], &x, sizeof x); + } + + PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo); + } else { + OpLen = DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS); + } + + DISCOVERoptions.Header.Length = OpLen; + + ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen - 1] = OP_END; + ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen] = OP_END; + + StatCode = DoUdpWrite ( + Private, + DestPtr, + &ServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &ClientPort + ); + + if (StatCode != EFI_SUCCESS) { + return StatCode; + } + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + // + // wait for ACK + // + for (;;) { + DHCP_RECEIVE_BUFFER *RxBufPtr; + UINT16 TmpType; + UINT16 TmpLayer; + + RxBufPtr = UseBis ? &PXE_BIS_BUFFER : &PXE_ACK_BUFFER; + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + if (GetOfferAck ( + Private, + AckEdit, + OpFlags, + (EFI_IP_ADDRESS *) &Private->ServerIp, + 0, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &ClientPort, + RxBufPtr, + TimeoutEvent + ) != EFI_SUCCESS) { + break; + } + // + // check type of response - need PXEClient DHCPACK of proper type with bootfile + // + if (!(RxBufPtr->OpAdds.Status & PXE_TYPE) || + (UseBis && (RxBufPtr->OpAdds.Status & USE_THREE_BYTE)) || + !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] || + !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] || + !InServerList((EFI_IP_ADDRESS *)&((DHCPV4_OP_SERVER_IP *)RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX-1])->Ip, ServerListPtr)) { + + continue; + } + + TmpType = TmpLayer = 0; + + if (RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) { + TmpType = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Type); + + if (RxBufPtr->OpAdds.Status & USE_THREE_BYTE) { + TmpLayer = (UINT16) (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer >> 8); + } else { + TmpLayer = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer); + } + } + + if (TmpType != Type) { + continue; + } + + if (UseBis) { + if (!RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]) { + continue; + } + + if (!VerifyCredentialOption ( + (UINT8 *) &DISCREDoptions.Header, + (UINT8 *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1] + )) { + continue; + } + } + + *LayerPtr = TmpLayer; + + if (UseBis) { + CopyMem ( + &PxebcMode->PxeBisReply, + &RxBufPtr->u.Dhcpv4, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + + PxebcMode->PxeBisReplyReceived = TRUE; + + StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + FALSE, + &Private->ServerIp, + 0 + ); + + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + + PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = TRUE; + + CopyMem ( + &PxebcMode->PxeDiscover, + &*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER, + sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER) + ); + + CopyMem ( + &PxebcMode->PxeReply, + &*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4, + sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4) + ); + + AddRouters (Private, RxBufPtr); + + gBS->CloseEvent (TimeoutEvent); + return EFI_SUCCESS; + } + + gBS->CloseEvent (TimeoutEvent); + } + // + // end for loop + // + return EFI_TIMEOUT; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Parameters: + Private := Pointer to PxeBc interface + Type := + LayerPtr := + UseBis := + DiscoverInfoPtr := + McastServerListPtr := + ServerListPtr := + + +**/ +STATIC +EFI_STATUS +Discover ( + PXE_BASECODE_DEVICE *Private, + IN UINT16 Type, + IN UINT16 *LayerPtr, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO *DiscoverInfoPtr, + PXE_SERVER_LISTS *McastServerListPtr, + PXE_SERVER_LISTS *ServerListPtr + ) +{ + EFI_IP_ADDRESS DestIp; + EFI_STATUS StatCode; + + DEBUG ((DEBUG_INFO, "\nDiscover() Type=%d Layer=%d ", Type, *LayerPtr)); + + if (UseBis) { + DEBUG ((DEBUG_INFO, "BIS ")); + } + // + // get dest IP addr - mcast, bcast, or unicast + // + if (DiscoverInfoPtr->UseMCast) { + DestIp.v4 = DiscoverInfoPtr->ServerMCastIp.v4; + + DEBUG ( + (DEBUG_INFO, + "\nDiscover() MCast %d.%d.%d.%d ", + DestIp.v4.Addr[0], + DestIp.v4.Addr[1], + DestIp.v4.Addr[2], + DestIp.v4.Addr[3]) + ); + + if ((StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + UseBis, + &DestIp, + McastServerListPtr + )) != EFI_TIMEOUT) { + DEBUG ( + (DEBUG_WARN, + "\nDiscover() status == %r (%Xh)", + StatCode, + StatCode) + ); + + return StatCode; + } + } + + if (DiscoverInfoPtr->UseBCast) { + DEBUG ((DEBUG_INFO, "\nDiscver() BCast ")); + + if ((StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + UseBis, + &BroadcastIP, + McastServerListPtr + )) != EFI_TIMEOUT) { + + DEBUG ((DEBUG_WARN, "\nDiscover() status == %r (%Xh)", StatCode, StatCode)); + + return StatCode; + } + } + + if (DiscoverInfoPtr->UseUCast) { + UINTN Index; + + DEBUG ( + (DEBUG_INFO, + "\nDiscover() UCast IP#=%d ", + ServerListPtr->Ipv4List.IpCount) + ); + + for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) { + CopyMem (&DestIp, &ServerListPtr->Ipv4List.IpList[Index], 4); + + DEBUG ( + (DEBUG_INFO, + "\nDiscover() UCast %d.%d.%d.%d ", + DestIp.v4.Addr[0], + DestIp.v4.Addr[1], + DestIp.v4.Addr[2], + DestIp.v4.Addr[3]) + ); + + if ((StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + UseBis, + &DestIp, + 0 + )) != EFI_TIMEOUT) { + DEBUG ( + (DEBUG_WARN, + "\nDiscover() status == %r (%Xh)", + StatCode, + StatCode) + ); + + return StatCode; + } + } + } + + DEBUG ((DEBUG_WARN, "\nDiscover() TIMEOUT")); + + return EFI_TIMEOUT; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* BcDiscover() + */ + +/** + + +**/ +EFI_STATUS +EFIAPI +BcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN UINT16 Type, + IN UINT16 *LayerPtr, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO * DiscoverInfoPtr OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + PXE_SERVER_LISTS DefaultSrvList; + PXE_SERVER_LISTS *ServerListPtr; + PXE_SERVER_LISTS *McastServerListPtr; + UNION_PTR LocalPtr; + UINTN Index; + UINTN Index2; + BOOLEAN AcquiredSrvList; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + ServerListPtr = NULL; + McastServerListPtr = NULL; + AcquiredSrvList = FALSE; + + PxebcMode = Private->EfiBc.Mode; + + if (!GetMem (Private)) { + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + if (UseBis) { + if (!PxebcMode->BisSupported) { + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER; + + if (Private->TotalSeconds == 0) { + // + // put in seconds field of DHCP send packets + // + Private->TotalSeconds = 4; + } + + ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO)); + + // + // if layer number not zero, use previous discover + // + if (*LayerPtr != 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0")); + + if (DiscoverInfoPtr != NULL) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && DiscoverInfoPtr != NULL\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (!PxebcMode->PxeDiscoverValid) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeDiscoverValid == 0\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (!PxebcMode->PxeReplyReceived) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeReplyReceived == 0\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (UseBis && !PxebcMode->PxeBisReplyReceived) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeBisReplyReceived == 0\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + DefaultInfo.UseUCast = TRUE; + DiscoverInfoPtr = &DefaultInfo; + + DefaultSrvList.Ipv4List.IpCount = 1; + CopyMem (&DefaultSrvList.Ipv4List.IpList[0], &Private->ServerIp, 4); + + ServerListPtr = &DefaultSrvList; + } + // + // layer is zero - see if info is supplied or if we need to use info from a cached offer + // + else if (!DiscoverInfoPtr) { + // + // not supplied - generate it + // make sure that there is cached, appropriate information + // if neither DhcpAck packet nor ProxyOffer packet has pxe info, fail + // + DhcpRxBuf = (PxebcMode->ProxyOfferReceived) ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER; + + if (!PxebcMode->DhcpAckReceived || !(DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE)) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() !ack && !proxy")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + DiscoverInfoPtr = &DefaultInfo; + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1]; + + // + // if multicast enabled, need multicast address + // + if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST)) { + DefaultInfo.UseMCast = TRUE; + + CopyMem ( + ((EFI_IPv4_ADDRESS *) &DefaultInfo.ServerMCastIp), + &((DHCPV4_OP_IP_ADDRESS *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + DefaultInfo.UseBCast = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & DISABLE_BCAST) == 0); + + DefaultInfo.MustUseList = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) != 0); + + DefaultInfo.UseUCast = (BOOLEAN) + ( + (DefaultInfo.MustUseList) || + ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST)) + ); + + if ((DefaultInfo.UseUCast | DefaultInfo.MustUseList) && !ExtractBootServerList ( + Type, + DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1], + &ServerListPtr + )) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() type not in list")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + } + // + // Info supplied - make SrvList if required + // if we use ucast discovery or must use list, there better be one + // + else if (DiscoverInfoPtr->UseUCast || DiscoverInfoPtr->MustUseList) { + // + // there better be a list + // + if (DiscoverInfoPtr->IpCnt == 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() no bootserver list")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + // + // get its size + // + for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) { + if (DiscoverInfoPtr->SrvList[Index].Type == Type) { + if (DiscoverInfoPtr->SrvList[Index].AcceptAnyResponse) { + if (Index2 != 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() accept any?")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } else { + Index2 = 1; + DefaultSrvList.Ipv4List.IpCount = 0; + ServerListPtr = &DefaultSrvList; + break; + } + } else { + ++Index2; + } + } + } + + if (Index2 == 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() !Index2?")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (ServerListPtr == NULL) { + ServerListPtr = AllocatePool ( + sizeof (PXEV4_SERVER_LIST) + (Index2 - 1) * sizeof (EFI_IPv4_ADDRESS) + ); + + if (ServerListPtr == NULL) { + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + // + // build an array of IP addresses from the server list + // + AcquiredSrvList = TRUE; + ServerListPtr->Ipv4List.IpCount = (UINT8) Index2; + + for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) { + if (DiscoverInfoPtr->SrvList[Index].Type == Type) { + CopyMem ( + &ServerListPtr->Ipv4List.IpList[Index2++], + &DiscoverInfoPtr->SrvList[Index].IpAddr.v4, + sizeof ServerListPtr->Ipv4List.IpList[0] + ); + } + } + } + } + + if (DiscoverInfoPtr->MustUseList) { + McastServerListPtr = ServerListPtr; + } + + if (!(DiscoverInfoPtr->UseMCast || DiscoverInfoPtr->UseBCast || DiscoverInfoPtr->UseUCast)) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() Nothing to use!\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = PxebcMode->PxeBisReplyReceived = FALSE; + + StatCode = Discover ( + Private, + Type, + LayerPtr, + UseBis, + DiscoverInfoPtr, + McastServerListPtr, + ServerListPtr + ); + + if (AcquiredSrvList) { + gBS->FreePool (ServerListPtr); + } + + FreeMem (Private); + + // + // Unlock the instance data + // + DEBUG ( + (DEBUG_INFO, + "\nBcDiscover() status == %r (%Xh)\n", + StatCode, + StatCode) + ); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +EFIAPI +BcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + BOOLEAN *NewDhcpDiscoverValid, OPTIONAL + BOOLEAN *NewDhcpAckReceived, OPTIONAL + BOOLEAN *NewProxyOfferReceived, OPTIONAL + BOOLEAN *NewPxeDiscoverValid, OPTIONAL + BOOLEAN *NewPxeReplyReceived, OPTIONAL + BOOLEAN *NewPxeBisReplyReceived, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_STATUS Status; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + PxebcMode = Private->EfiBc.Mode; + + if (Private->DhcpPacketBuffer == NULL) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1), + &Private->DhcpPacketBuffer + ); + + if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) { + Private->DhcpPacketBuffer = NULL; + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + } + // + // Issue BC command + // + // + // reset + // + Private->FileSize = 0; + if (NewDhcpDiscoverValid != NULL) { + PxebcMode->DhcpDiscoverValid = *NewDhcpDiscoverValid; + } + + if (NewDhcpAckReceived != NULL) { + PxebcMode->DhcpAckReceived = *NewDhcpAckReceived; + } + + if (NewProxyOfferReceived != NULL) { + PxebcMode->ProxyOfferReceived = *NewProxyOfferReceived; + } + + if (NewPxeDiscoverValid != NULL) { + PxebcMode->PxeDiscoverValid = *NewPxeDiscoverValid; + } + + if (NewPxeReplyReceived != NULL) { + PxebcMode->PxeReplyReceived = *NewPxeReplyReceived; + } + + if (NewPxeBisReplyReceived != NULL) { + PxebcMode->PxeBisReplyReceived = *NewPxeBisReplyReceived; + } + + if (NewDhcpDiscover != NULL) { + CopyMem ( + &PxebcMode->DhcpDiscover, + NewDhcpDiscover, + sizeof *NewDhcpDiscover + ); + } + + if (NewDhcpAck != NULL) { + CopyParse (Private, &PxebcMode->DhcpAck, NewDhcpAck, DHCPV4_ACK_INDEX); + } + + if (NewProxyOffer != NULL) { + CopyParse (Private, &PxebcMode->ProxyOffer, NewProxyOffer, PXE_OFFER_INDEX); + } + + if (NewPxeDiscover != NULL) { + CopyMem ( + &PxebcMode->PxeDiscover, + NewPxeDiscover, + sizeof *NewPxeDiscover + ); + } + + if (NewPxeReply != NULL) { + CopyParse (Private, &PxebcMode->PxeReply, NewPxeReply, PXE_ACK_INDEX); + } + + if (NewPxeBisReply != NULL) { + CopyParse (Private, &PxebcMode->PxeBisReply, NewPxeBisReply, PXE_BIS_INDEX); + } + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return EFI_SUCCESS; +} + +/* eof - pxe_bc_dhcp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c new file mode 100644 index 0000000000..c5088dff32 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c @@ -0,0 +1,421 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of 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. + + +**/ + + +#define RAND_MAX 0x10000 + +#include "Bc.h" + +// +// Definitions for internet group management protocol version 2 message +// structure Per RFC 2236, November 1997 +// +STATIC UINT8 RouterAlertOption[4] = { 0x80 | 20, 4, 0, 0 }; +STATIC IPV4_ADDR AllRoutersGroup = { { 224, 0, 0, 2 } }; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +ClearGroupTimer ( + PXE_BASECODE_DEVICE *Private, + UINTN TimerId + ) +{ + if (Private == NULL) { + return ; + } + + if (TimerId >= Private->MCastGroupCount) { + return ; + } + + if (Private->IgmpGroupEvent[TimerId] == NULL) { + return ; + } + + gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]); + Private->IgmpGroupEvent[TimerId] = NULL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +SetGroupTimer ( + PXE_BASECODE_DEVICE *Private, + UINTN TimerId, + UINTN MaxRespTime + ) +{ + EFI_STATUS EfiStatus; + + if (Private == NULL) { + return ; + } + + if (TimerId >= Private->MCastGroupCount) { + return ; + } + + if (Private->IgmpGroupEvent[TimerId] != NULL) { + gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]); + } + + EfiStatus = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->IgmpGroupEvent[TimerId] + ); + + if (EFI_ERROR (EfiStatus)) { + Private->IgmpGroupEvent[TimerId] = NULL; + return ; + } + + EfiStatus = gBS->SetTimer ( + Private->IgmpGroupEvent[TimerId], + TimerRelative, + MaxRespTime * 1000000 + Random (Private) % RAND_MAX + ); + + if (EFI_ERROR (EfiStatus)) { + gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]); + Private->IgmpGroupEvent[TimerId] = NULL; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +SendIgmpMessage ( + PXE_BASECODE_DEVICE *Private, + UINT8 Type, + INTN GroupId + ) +{ + Private->IgmpMessage.Type = Type; + Private->IgmpMessage.MaxRespTime = 0; + Private->IgmpMessage.Checksum = 0; + Private->IgmpMessage.GroupAddress = Private->MCastGroup[GroupId]; + Private->IgmpMessage.Checksum = IpChecksum ( + (UINT16 *) &Private->IgmpMessage, + sizeof Private->IgmpMessage + ); + + Ipv4SendWOp ( + Private, + 0, + (UINT8 *) &Private->IgmpMessage, + sizeof Private->IgmpMessage, + PROT_IGMP, + RouterAlertOption, + sizeof RouterAlertOption, + ((Type == IGMP_TYPE_LEAVE_GROUP) ? AllRoutersGroup.L : Private->IgmpMessage.GroupAddress), + EFI_PXE_BASE_CODE_FUNCTION_IGMP + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +ReportIgmp ( + PXE_BASECODE_DEVICE *Private, + INTN GroupId + ) +{ + // + // if version 1 querier, send v1 report + // + UINT8 Type; + + if (Private->Igmpv1TimeoutEvent != NULL) { + if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + Private->UseIgmpv1Reporting = TRUE; + } + } + + Type = (UINT8) (Private->UseIgmpv1Reporting ? IGMP_TYPE_V1REPORT : IGMP_TYPE_REPORT); + + SendIgmpMessage (Private, Type, GroupId); + ClearGroupTimer (Private, GroupId); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +IgmpCheckTimers ( + PXE_BASECODE_DEVICE *Private + ) +{ + UINTN GroupId; + + if (Private == NULL) { + return ; + } + + for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) { + if (Private->IgmpGroupEvent[GroupId] == NULL) { + continue; + } + + if (!EFI_ERROR (gBS->CheckEvent (Private->IgmpGroupEvent[GroupId]))) { + // + // send a report + // + ReportIgmp (Private, GroupId); + } + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return 0 := Group not found + @return other := Group ID# + +**/ +STATIC +INTN +FindMulticastGroup ( + PXE_BASECODE_DEVICE *Private, + UINT32 GroupAddress + ) +{ + UINTN GroupId; + + for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) { + if (Private->MCastGroup[GroupId] == GroupAddress) { + return GroupId + 1; + } + } + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +IgmpJoinGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *GroupPtr + ) +{ + UINT32 Grp; + + Grp = *(UINT32 *) GroupPtr; + + // + // see if we already have it or if we can't take anymore + // + if (FindMulticastGroup (Private, Grp) || Private->MCastGroupCount == MAX_MCAST_GROUPS) { + return ; + } + // + // add the group + // + Private->MCastGroup[Private->MCastGroupCount] = Grp; + + ReportIgmp (Private, Private->MCastGroupCount); + // + // send a report + // so it will get sent again per RFC 2236 + // + SetGroupTimer ( + Private, + Private->MCastGroupCount++, + UNSOLICITED_REPORT_INTERVAL * 10 + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +IgmpLeaveGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *GroupPtr + ) +{ + UINT32 Grp; + UINTN GroupId; + + Grp = *(UINT32 *) GroupPtr; + + // + // if not in group, ignore + // + GroupId = FindMulticastGroup (Private, Grp); + + if (GroupId == 0) { + return ; + } + // + // if not v1 querrier, send leave group IGMP message + // + if (Private->Igmpv1TimeoutEvent != NULL) { + if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + Private->UseIgmpv1Reporting = TRUE; + } else { + SendIgmpMessage (Private, IGMP_TYPE_LEAVE_GROUP, GroupId - 1); + } + } + + while (GroupId < Private->MCastGroupCount) { + Private->MCastGroup[GroupId - 1] = Private->MCastGroup[GroupId]; + Private->IgmpGroupEvent[GroupId - 1] = Private->IgmpGroupEvent[GroupId]; + ++GroupId; + } + + --Private->MCastGroupCount; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +HandleIgmp ( + PXE_BASECODE_DEVICE *Private, + IGMPV2_MESSAGE *IgmpMessagePtr, + UINTN IgmpLength + ) +{ + EFI_STATUS EfiStatus; + UINTN GroupId; + INTN MaxRespTime; + + if (Private == NULL) { + return ; + } + + if (Private->MCastGroupCount == 0) { + // + // if we don't belong to any multicast groups, ignore + // + return ; + } + // + // verify checksum + // + if (IpChecksum ((UINT16 *) IgmpMessagePtr, IgmpLength)) { + // + // bad checksum - ignore packet + // + return ; + } + + switch (IgmpMessagePtr->Type) { + case IGMP_TYPE_QUERY: + // + // if a version 1 querier, note the fact and set max resp time + // + MaxRespTime = IgmpMessagePtr->MaxRespTime; + + if (MaxRespTime == 0) { + Private->UseIgmpv1Reporting = TRUE; + + if (Private->Igmpv1TimeoutEvent != NULL) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + } + + EfiStatus = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->Igmpv1TimeoutEvent + ); + + if (EFI_ERROR (EfiStatus)) { + Private->Igmpv1TimeoutEvent = NULL; + } else { + EfiStatus = gBS->SetTimer ( + Private->Igmpv1TimeoutEvent, + TimerRelative, + (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000 + ); + } + + MaxRespTime = IGMP_DEFAULT_MAX_RESPONSE_TIME * 10; + } + // + // if a general query (!GroupAddress), set all our group timers + // + if (!IgmpMessagePtr->GroupAddress) { + for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) { + SetGroupTimer (Private, GroupId, MaxRespTime); + } + } else { + // + // specific query - set only specific group + // + GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress); + + if (GroupId != 0) { + SetGroupTimer (Private, GroupId - 1, MaxRespTime); + } + } + + break; + + // + // if we have a timer running for this group, clear it + // + case IGMP_TYPE_V1REPORT: + case IGMP_TYPE_REPORT: + GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress); + + if (GroupId != 0) { + ClearGroupTimer (Private, GroupId - 1); + } + + break; + } +} + +/* EOF - pxe_bc_igmp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c new file mode 100644 index 0000000000..6330679a8f --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c @@ -0,0 +1,846 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_ip.c + +Abstract: + + +**/ + +#include "Bc.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Check if two IP addresses are on the same subnet. + + @param IpLength Length of IP address in bytes. + @param Ip1 IP address to check. + @param Ip2 IP address to check. + @param SubnetMask Subnet mask to check with. + + @retval TRUE IP addresses are on the same subnet. + @retval FALSE IP addresses are on different subnets. + +**/ +BOOLEAN +OnSameSubnet ( + IN UINTN IpLength, + IN EFI_IP_ADDRESS *Ip1, + IN EFI_IP_ADDRESS *Ip2, + IN EFI_IP_ADDRESS *SubnetMask + ) +{ + if (IpLength == 0 || Ip1 == NULL || Ip2 == NULL || SubnetMask == NULL) { + return FALSE; + } + + while (IpLength-- != 0) { + if ((Ip1->v6.Addr[IpLength] ^ Ip2->v6.Addr[IpLength]) & SubnetMask->v6.Addr[IpLength]) { + return FALSE; + } + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Add router to router table. + + @param Private Pointer PxeBc instance data. + @param RouterIpPtr Pointer to router IP address. + + @return Nothing + +**/ +VOID +IpAddRouter ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *RouterIpPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + if (Private == NULL || RouterIpPtr == NULL) { + return ; + } + + PxeBcMode = Private->EfiBc.Mode; + + // + // if we are filled up or this is not on the same subnet, forget it + // + if ((PxeBcMode->RouteTableEntries == PXE_ROUTER_TABLE_SIZE) || + !OnSameSubnet(Private->IpLength, &PxeBcMode->StationIp, RouterIpPtr, &PxeBcMode->SubnetMask)) { + return ; + } + // + // make sure we don't already have it + // + for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) { + if (!CompareMem ( + &PxeBcMode->RouteTable[Index].GwAddr, + RouterIpPtr, + Private->IpLength + )) { + return ; + } + } + // + // keep it + // + ZeroMem ( + &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries], + sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY) + ); + + CopyMem ( + &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries++].GwAddr, + RouterIpPtr, + Private->IpLength + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// return router ip to use for DestIp (0 if none) +// +STATIC +EFI_IP_ADDRESS * +GetRouterIp ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *DestIpPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + if (Private == NULL || DestIpPtr == NULL) { + return NULL; + } + + PxeBcMode = Private->EfiBc.Mode; + + for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) { + if (OnSameSubnet ( + Private->IpLength, + &PxeBcMode->RouteTable[Index].IpAddr, + DestIpPtr, + &PxeBcMode->RouteTable[Index].SubnetMask + )) { + return &PxeBcMode->RouteTable[Index].GwAddr; + } + } + + return NULL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// routine to send ipv4 packet +// ipv4 header of length HdrLth in TransmitBufferPtr +// routine fills in ipv4hdr Ver_Hdl, TotalLength, and Checksum, moves in Data +// and gets dest MAC address +// +#define IP_TX_BUFFER ((IPV4_BUFFER *) Private->TransmitBufferPtr) +#define IP_TX_HEADER IP_TX_BUFFER->IpHeader + +EFI_STATUS +Ipv4Xmt ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIp, + UINTN IpHeaderLength, + UINTN TotalHeaderLength, + VOID *Data, + UINTN DataLength, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +{ + EFI_MAC_ADDRESS DestMac; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_STATUS StatCode; + UINTN PacketLength; + + Snp = Private->SimpleNetwork; + PxeBcMode = Private->EfiBc.Mode; + StatCode = EFI_SUCCESS; + PacketLength = TotalHeaderLength + DataLength; + + // + // get dest MAC address + // multicast - convert to hw equiv + // unicast on same net, use arp + // on different net, arp for router + // + if (IP_TX_HEADER.DestAddr.L == BROADCAST_IPv4) { + CopyMem (&DestMac, &Snp->Mode->BroadcastAddress, sizeof (DestMac)); + } else if (IS_MULTICAST (&IP_TX_HEADER.DestAddr)) { + StatCode = (*Snp->MCastIpToMac) (Snp, PxeBcMode->UsingIpv6, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, &DestMac); + } else { + UINT32 Ip; + + if (OnSameSubnet ( + Private->IpLength, + &PxeBcMode->StationIp, + (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, + &PxeBcMode->SubnetMask + )) { + Ip = IP_TX_HEADER.DestAddr.L; + } else if (GatewayIp != 0) { + Ip = GatewayIp; + } else { + EFI_IP_ADDRESS *TmpIp; + + TmpIp = GetRouterIp (Private, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr); + + if (TmpIp == NULL) { + DEBUG ( + (DEBUG_WARN, + "\nIpv4Xmit() Exit #1 %xh (%r)", + EFI_NO_RESPONSE, + EFI_NO_RESPONSE) + ); + + return EFI_NO_RESPONSE; + // + // no router + // + } + + Ip = TmpIp->Addr[0]; + } + + if (!GetHwAddr ( + Private, + (EFI_IP_ADDRESS *) &Ip, + (EFI_MAC_ADDRESS *) &DestMac + )) { + if (!PxeBcMode->AutoArp) { + DEBUG ( + (DEBUG_WARN, + "\nIpv4Xmit() Exit #2 %xh (%r)", + EFI_DEVICE_ERROR, + EFI_DEVICE_ERROR) + ); + + return EFI_DEVICE_ERROR; + } else { + StatCode = DoArp ( + Private, + (EFI_IP_ADDRESS *) &Ip, + (EFI_MAC_ADDRESS *) &DestMac + ); + } + } + } + + if (EFI_ERROR (StatCode)) { + DEBUG ((DEBUG_WARN, "\nIpv4Xmit() Exit #3 %xh (%r)", StatCode, StatCode)); + return StatCode; + } + // + // fill in packet info + // + SET_IPV4_VER_HDL (&IP_TX_HEADER, IpHeaderLength); + IP_TX_HEADER.TotalLength = HTONS (PacketLength); + IP_TX_HEADER.HeaderChecksum = IpChecksum ((UINT16 *) &IP_TX_HEADER, IpHeaderLength); + CopyMem (((UINT8 *) &IP_TX_HEADER) + TotalHeaderLength, Data, DataLength); + + // + // send it + // + return SendPacket ( + Private, + (UINT8 *) &IP_TX_HEADER - Snp->Mode->MediaHeaderSize, + &IP_TX_HEADER, + PacketLength, + &DestMac, + PXE_PROTOCOL_ETHERNET_IP, + Function + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send ipv4 packet with option +// +EFI_STATUS +Ipv4SendWOp ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIp, + UINT8 *Msg, + UINTN MessageLength, + UINT8 Prot, + UINT8 *Option, + UINTN OptionLength, + UINT32 DestIp, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN HdrLth; + + PxeBcMode = Private->EfiBc.Mode; + HdrLth = sizeof (IPV4_HEADER) + OptionLength; + + ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER)); + IP_TX_HEADER.TimeToLive = PxeBcMode->TTL; + IP_TX_HEADER.TypeOfService = PxeBcMode->ToS; + IP_TX_HEADER.Protocol = Prot; + IP_TX_HEADER.SrcAddr.L = *(UINT32 *) &PxeBcMode->StationIp; + IP_TX_HEADER.DestAddr.L = DestIp; + IP_TX_HEADER.Id = Random (Private); + CopyMem (IP_TX_BUFFER->u.Data, Option, OptionLength); + return Ipv4Xmt ( + Private, + GatewayIp, + HdrLth, + HdrLth, + Msg, + MessageLength, + Function + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send MessageLength message at MessagePtr - higher level protocol header already in TransmitBufferPtr, length HdrSize +// +EFI_STATUS +Ip4Send ( + PXE_BASECODE_DEVICE *Private, // pointer to instance data + UINTN MayFrag, // + UINT8 Prot, // protocol + UINT32 SrcIp, // Source IP address + UINT32 DestIp, // Destination IP address + UINT32 GatewayIp, // used if not NULL and needed + UINTN HdrSize, // protocol header byte length + UINT8 *MessagePtr, // pointer to data + UINTN MessageLength // data byte length + ) +{ + EFI_STATUS StatCode; + UINTN TotDataLength; + + TotDataLength = HdrSize + MessageLength; + + if (TotDataLength > MAX_IPV4_DATA_SIZE) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #1 %xh (%r)", + EFI_BAD_BUFFER_SIZE, + EFI_BAD_BUFFER_SIZE) + ); + + return EFI_BAD_BUFFER_SIZE; + } + + ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER)); + IP_TX_HEADER.TimeToLive = DEFAULT_TTL; + IP_TX_HEADER.Protocol = Prot; + IP_TX_HEADER.SrcAddr.L = SrcIp; + IP_TX_HEADER.DestAddr.L = DestIp; + IP_TX_HEADER.Id = Random (Private); + + if (!MayFrag) { + *(UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_NO_FRAG >> 8; + } + // + // check for need to fragment + // + if (TotDataLength > MAX_IPV4_FRAME_DATA_SIZE) { + UINTN DataLengthSent; + UINT16 FragmentOffset; + + FragmentOffset = IP_MORE_FRAG; + // + // frag offset field + // + if (!MayFrag) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #2 %xh (%r)", + EFI_BAD_BUFFER_SIZE, + EFI_BAD_BUFFER_SIZE) + ); + + return EFI_BAD_BUFFER_SIZE; + } + // + // send out in fragments - first includes upper level header + // all are max and include more frag bit except last + // + * (UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_MORE_FRAG >> 8; + +#define IPV4_FRAG_SIZE (MAX_IPV4_FRAME_DATA_SIZE & 0xfff8) +#define IPV4_FRAG_OFF_INC (IPV4_FRAG_SIZE >> 3) + + DataLengthSent = IPV4_FRAG_SIZE - HdrSize; + + StatCode = Ipv4Xmt ( + Private, + GatewayIp, + sizeof (IPV4_HEADER), + sizeof (IPV4_HEADER) + HdrSize, + MessagePtr, + DataLengthSent, + Private->Function + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #3 %xh (%r)", + StatCode, + StatCode) + ); + + return StatCode; + } + + MessagePtr += DataLengthSent; + MessageLength -= DataLengthSent; + FragmentOffset += IPV4_FRAG_OFF_INC; + IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset); + + while (MessageLength > IPV4_FRAG_SIZE) { + StatCode = Ipv4Xmt ( + Private, + GatewayIp, + sizeof (IPV4_HEADER), + sizeof (IPV4_HEADER), + MessagePtr, + IPV4_FRAG_SIZE, + Private->Function + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #3 %xh (%r)", + StatCode, + StatCode) + ); + + return StatCode; + } + + MessagePtr += IPV4_FRAG_SIZE; + MessageLength -= IPV4_FRAG_SIZE; + FragmentOffset += IPV4_FRAG_OFF_INC; + IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset); + } + + * (UINT8 *) (&IP_TX_HEADER.FragmentFields) &= ~(IP_MORE_FRAG >> 8); + HdrSize = 0; + } + // + // transmit + // + return Ipv4Xmt ( + Private, + GatewayIp, + sizeof (IPV4_HEADER), + sizeof (IPV4_HEADER) + HdrSize, + MessagePtr, + MessageLength, + Private->Function + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// return true if dst IP in receive header matched with what's enabled +// +STATIC +BOOLEAN +IPgood ( + PXE_BASECODE_DEVICE *Private, + IPV4_HEADER *IpHeader + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + PxeBcMode = Private->EfiBc.Mode; + + if (PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) { + return TRUE; + } + + if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) && + IS_MULTICAST (&IpHeader->DestAddr) + ) { + return TRUE; + } + + if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) && + PxeBcMode->StationIp.Addr[0] == IpHeader->DestAddr.L + ) { + return TRUE; + } + + if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) && IpHeader->DestAddr.L == BROADCAST_IPv4) { + return TRUE; + } + + for (Index = 0; Index < PxeBcMode->IpFilter.IpCnt; ++Index) { + if (IpHeader->DestAddr.L == PxeBcMode->IpFilter.IpList[Index].Addr[0]) { + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// receive up to MessageLength message into MessagePtr for protocol Prot +// return message length, src/dest ips if select any, and pointer to protocol +// header routine will filter based on source and/or dest ip if OpFlags set. +// +EFI_STATUS +IpReceive ( + PXE_BASECODE_DEVICE *Private, + PXE_OPFLAGS OpFlags, + EFI_IP_ADDRESS *SrcIpPtr, + EFI_IP_ADDRESS *DestIpPtr, + UINT8 Prot, + VOID *HeaderPtr, + UINTN HdrSize, + UINT8 *MessagePtr, + UINTN *MessageLengthPtr, + EFI_EVENT TimeoutEvent + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_STATUS StatCode; + UINTN ByteCount; + UINTN FragmentCount; + UINTN ExpectedPacketLength; + UINTN Id; + BOOLEAN GotFirstFragment; + BOOLEAN GotLastFragment; + + DEBUG ( + (DEBUG_NET, + "\nIpReceive() Hdr=%Xh HdrSz=%d Data=%Xh DataSz=%d", + HeaderPtr, + HdrSize, + MessagePtr, + *MessageLengthPtr) + ); + + PxeBcMode = Private->EfiBc.Mode; + PxeBcMode->IcmpErrorReceived = FALSE; + + ExpectedPacketLength = 0; + GotFirstFragment = FALSE; + GotLastFragment = FALSE; + FragmentCount = 0; + ByteCount = 0; + Id = 0; + + for (;;) { + IPV4_HEADER IpHdr; + UINTN FFlds; + UINTN TotalLength; + UINTN FragmentOffset; + UINTN HeaderSize; + UINTN BufferSize; + UINTN IpHeaderLength; + UINTN DataLength; + UINT16 Protocol; + UINT8 *NextHdrPtr; + UINT8 *PacketPtr; + + StatCode = WaitForReceive ( + Private, + Private->Function, + TimeoutEvent, + &HeaderSize, + &BufferSize, + &Protocol + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + PacketPtr = Private->ReceiveBufferPtr + HeaderSize; + + if (Protocol == PXE_PROTOCOL_ETHERNET_ARP) { + HandleArpReceive ( + Private, + (ARP_PACKET *) PacketPtr, + Private->ReceiveBufferPtr + ); + + continue; + } + + if (Protocol != PXE_PROTOCOL_ETHERNET_IP) { + continue; + } + +#define IpRxHeader ((IPV4_HEADER *) PacketPtr) + + // + // filter for version & check sum + // + IpHeaderLength = IPV4_HEADER_LENGTH (IpRxHeader); + + if ((IpRxHeader->VersionIhl >> 4) != IPVER4) { + continue; + } + + if (IpChecksum ((UINT16 *) IpRxHeader, IpHeaderLength)) { + continue; + } + + CopyMem (&IpHdr, IpRxHeader, sizeof (IpHdr)); + TotalLength = NTOHS (IpHdr.TotalLength); + + if (IpHdr.Protocol == PROT_TCP) { + // + // The NextHdrPtr is used to seed the header buffer we are passing back. + // That being the case, we want to see everything in pPkt which contains + // everything but the ethernet (or whatever) frame. IP + TCP in this case. + // + DataLength = TotalLength; + NextHdrPtr = PacketPtr; + } else { + DataLength = TotalLength - IpHeaderLength; + NextHdrPtr = PacketPtr + IpHeaderLength; + } + // + // If this is an ICMP, it might not be for us. + // Double check the state of the IP stack and the + // packet fields before assuming it is an ICMP + // error. ICMP requests are not supported by the + // PxeBc IP stack and should be ignored. + // + if (IpHdr.Protocol == PROT_ICMP) { + ICMPV4_HEADER *Icmpv4; + + Icmpv4 = (ICMPV4_HEADER *) NextHdrPtr; + + // + // For now only obvious ICMP error replies will be accepted by + // this stack. This still makes us vulnerable to DoS attacks. + // But at least we will not be killed by DHCP daemons. + // + switch (Icmpv4->Type) { + case ICMP_REDIRECT: + case ICMP_ECHO: + case ICMP_ROUTER_ADV: + case ICMP_ROUTER_SOLICIT: + case ICMP_TIMESTAMP: + case ICMP_TIMESTAMP_REPLY: + case ICMP_INFO_REQ: + case ICMP_INFO_REQ_REPLY: + case ICMP_SUBNET_MASK_REQ: + case ICMP_SUBNET_MASK_REPLY: + default: + continue; + + // + // %%TBD - This should be implemented. + // + case ICMP_ECHO_REPLY: + continue; + + case ICMP_DEST_UNREACHABLE: + case ICMP_TIME_EXCEEDED: + case ICMP_PARAMETER_PROBLEM: + case ICMP_SOURCE_QUENCH: + PxeBcMode->IcmpErrorReceived = TRUE; + + CopyMem ( + &PxeBcMode->IcmpError, + NextHdrPtr, + sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + + DEBUG ( + (DEBUG_NET, + "\nIpReceive() Exit #1 %Xh (%r)", + EFI_ICMP_ERROR, + EFI_ICMP_ERROR) + ); + } + + return EFI_ICMP_ERROR; + } + + if (IpHdr.Protocol == PROT_IGMP) { + HandleIgmp (Private, (IGMPV2_MESSAGE *) NextHdrPtr, DataLength); + + DEBUG ((DEBUG_NET, "\n IGMP")); + continue; + } + // + // check for protocol + // + if (IpHdr.Protocol != Prot) { + continue; + } + // + // do filtering + // + if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr && SrcIpPtr->Addr[0] != IpHdr.SrcAddr.L) { + DEBUG ((DEBUG_NET, "\n Not expected source IP address.")); + continue; + } + + if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) { + if (!IPgood (Private, &IpHdr)) { + continue; + } + } else if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP)) { + if (DestIpPtr == NULL) { + if (PxeBcMode->StationIp.Addr[0] != IpHdr.DestAddr.L) { + continue; + } + } else if (DestIpPtr->Addr[0] != IpHdr.DestAddr.L) { + continue; + } + } + // + // get some data we need + // + FFlds = NTOHS (IpHdr.FragmentFields); + FragmentOffset = ((FFlds & IP_FRAG_OFF_MSK) << 3); + + /* Keep count of fragments that belong to this session. + * If we get packets with a different IP ID number, + * ignore them. Ignored packets should be handled + * by the upper level protocol. + */ + if (FragmentCount == 0) { + Id = IpHdr.Id; + + if (DestIpPtr != NULL) { + DestIpPtr->Addr[0] = IpHdr.DestAddr.L; + } + + if (SrcIpPtr != NULL) { + SrcIpPtr->Addr[0] = IpHdr.SrcAddr.L; + } + } else { + if (IpHdr.Id != Id) { + continue; + } + } + + ++FragmentCount; + + /* Fragment management. + */ + if (FragmentOffset == 0) { + /* This is the first fragment (may also be the + * only fragment). + */ + GotFirstFragment = TRUE; + + /* If there is a separate protocol header buffer, + * copy the header, adjust the data pointer and + * the data length. + */ + if (HdrSize != 0) { + CopyMem (HeaderPtr, NextHdrPtr, HdrSize); + + NextHdrPtr += HdrSize; + DataLength -= HdrSize; + } + } else { + /* If there is a separate protocol header buffer, + * adjust the fragment offset. + */ + FragmentOffset -= HdrSize; + } + + /* See if this is the last fragment. + */ + if (!(FFlds & IP_MORE_FRAG)) { + // + // This is the last fragment (may also be the only fragment). + // + GotLastFragment = TRUE; + + /* Compute the expected length of the assembled + * packet. This will be used to decide if we + * have gotten all of the fragments. + */ + ExpectedPacketLength = FragmentOffset + DataLength; + } + + DEBUG ( + (DEBUG_NET, + "\n ID = %Xh Off = %d Len = %d", + Id, + FragmentOffset, + DataLength) + ); + + /* Check for receive buffer overflow. + */ + if (FragmentOffset + DataLength > *MessageLengthPtr) { + /* There is not enough space in the receive + * buffer for the fragment. + */ + DEBUG ( + (DEBUG_NET, + "\nIpReceive() Exit #3 %Xh (%r)", + EFI_BUFFER_TOO_SMALL, + EFI_BUFFER_TOO_SMALL) + ); + + return EFI_BUFFER_TOO_SMALL; + } + + /* Copy data into receive buffer. + */ + if (DataLength != 0) { + DEBUG ((DEBUG_NET, " To = %Xh", MessagePtr + FragmentOffset)); + + CopyMem (MessagePtr + FragmentOffset, NextHdrPtr, DataLength); + ByteCount += DataLength; + } + + /* If we have seen the first and last fragments and + * the receive byte count is at least as large as the + * expected byte count, return SUCCESS. + * + * We could be tricked by receiving a fragment twice + * but the upper level protocol should figure this + * out. + */ + if (GotFirstFragment && GotLastFragment && ByteCount >= ExpectedPacketLength) { + *MessageLengthPtr = ExpectedPacketLength; + return EFI_SUCCESS; + } + } +} + +/* eof - pxe_bc_ip.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c new file mode 100644 index 0000000000..43e1bbb429 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c @@ -0,0 +1,2193 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + pxe_bc_mtftp.c + +Abstract: + TFTP and MTFTP (multicast TFTP) implementation. + +Revision History + + +**/ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// The following #define is used to create a version that does not wait to +// open after a listen. This is just for a special regression test of MTFTP +// server to make sure multiple opens are handled correctly. Normally this +// next line should be a comment. +// #define SpecialNowaitVersion // comment out for normal operation +// + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "bc.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +UINT64 +Swap64 ( + UINT64 n + ) +{ + union { + UINT64 n; + UINT8 b[8]; + } u; + + UINT8 t; + + u.n = n; + + t = u.b[0]; + u.b[0] = u.b[7]; + u.b[7] = t; + + t = u.b[1]; + u.b[1] = u.b[6]; + u.b[6] = t; + + t = u.b[2]; + u.b[2] = u.b[5]; + u.b[5] = t; + + t = u.b[3]; + u.b[3] = u.b[4]; + u.b[4] = t; + + return u.n; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := + @return EFI_TFTP_ERROR := + @return other := + +**/ +STATIC +EFI_STATUS +TftpUdpRead ( + PXE_BASECODE_DEVICE *Private, + UINT16 Operation, + VOID *HeaderPtr, + UINTN *BufferSizePtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *OurIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT16 Timeout + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_STATUS Status; + EFI_EVENT TimeoutEvent; + UINTN HeaderSize; + + // + // + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimeoutEvent); + return Status; + } + // + // + // + HeaderSize = Private->BigBlkNumFlag ? sizeof (struct Tftpv4Ack8) : sizeof (struct Tftpv4Ack); + +#define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr) + + Status = UdpRead ( + Private, + Operation, + OurIpPtr, + OurPortPtr, + ServerIpPtr, + ServerPortPtr, + &HeaderSize, + HeaderPtr, + BufferSizePtr, + BufferPtr, + TimeoutEvent + ); + + if (Status != EFI_SUCCESS || ERROR_MESSAGE_PTR->OpCode != HTONS (TFTP_ERROR)) { + gBS->CloseEvent (TimeoutEvent); + return Status; + } + // + // got an error packet + // write one byte error code followed by error message + // + PxeBcMode = Private->EfiBc.Mode; + PxeBcMode->TftpErrorReceived = TRUE; + PxeBcMode->TftpError.ErrorCode = (UINT8) NTOHS (ERROR_MESSAGE_PTR->ErrCode); + HeaderSize = MIN (*BufferSizePtr, sizeof PxeBcMode->TftpError.ErrorString); + CopyMem (PxeBcMode->TftpError.ErrorString, BufferPtr, HeaderSize); + + gBS->CloseEvent (TimeoutEvent); + return EFI_TFTP_ERROR; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +SendError ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr + ) +{ + struct Tftpv4Error *ErrStr; + UINTN Len; + + ErrStr = (VOID *) Private->TftpErrorBuffer; + Len = sizeof *ErrStr; + + ErrStr->OpCode = HTONS (TFTP_ERROR); + ErrStr->ErrCode = HTONS (TFTP_ERR_OPTION); + ErrStr->ErrMsg[0] = 0; + + UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + &Len, + ErrStr + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +SendAckAndGetData ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ReplyIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT16 Timeout, + UINTN *ReplyLenPtr, + UINT8 *PxeBcMode, + UINT64 *BlockNumPtr, + BOOLEAN AckOnly + ) +{ + struct Tftpv4Data DataBuffer; + struct Tftpv4Ack *Ack2Ptr; + struct Tftpv4Ack8 *Ack8Ptr; + EFI_STATUS Status; + UINTN Len; + + Ack2Ptr = (VOID *) Private->TftpAckBuffer; + Ack8Ptr = (VOID *) Private->TftpAckBuffer; + + if (Private->BigBlkNumFlag) { + Len = sizeof (struct Tftpv4Ack8); + + Ack8Ptr->OpCode = HTONS (TFTP_ACK8); + Ack8Ptr->BlockNum = Swap64 (*BlockNumPtr); + + Status = UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + &Len, + Ack8Ptr + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } else { + Len = sizeof (struct Tftpv4Ack); + + Ack2Ptr->OpCode = HTONS (TFTP_ACK); + Ack2Ptr->BlockNum = HTONS ((UINT16) *BlockNumPtr); + + Status = UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + &Len, + Ack2Ptr + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (AckOnly) { + // + // ACK of last packet. This is just a courtesy. + // Do not wait for response. + // + return EFI_SUCCESS; + } + // + // read reply + // + Status = TftpUdpRead ( + Private, + 0, + &DataBuffer, + ReplyLenPtr, + PxeBcMode, + ServerIpPtr, + ServerPortPtr, + ReplyIpPtr, + OurPortPtr, + Timeout + ); + + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + // + // got a good reply (so far) + // check for next data packet + // + if (!Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA)) { + if (Status == EFI_BUFFER_TOO_SMALL) { + SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr); + } + + *BlockNumPtr = NTOHS (DataBuffer.Header.BlockNum); + return Status; + } + + if (Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA8)) { + if (Status == EFI_BUFFER_TOO_SMALL) { + SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr); + } + + *BlockNumPtr = Swap64 (*(UINT64 *) &DataBuffer.Header.BlockNum); + return Status; + } + + return EFI_PROTOCOL_ERROR; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +LockStepReceive ( + PXE_BASECODE_DEVICE *Private, + UINTN PacketSize, + UINT64 *BufferSizePtr, + UINT64 Offset, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ReplyIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT64 LastBlock, + UINT16 Timeout, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_STATUS Status; + UINT64 BlockNum; + UINT64 BufferSize; + UINTN Retries; + UINTN SaveLen; + UINTN ReplyLen; + + ReplyLen = PacketSize; + BlockNum = LastBlock; + + DEBUG ((DEBUG_INFO, "\nLockStepReceive() PacketSize = %d", PacketSize)); + + if (DontUseBuffer) { + BufferSize = PacketSize; + } else { + BufferSize = *BufferSizePtr - Offset; + BufferPtr += Offset; + } + + while (ReplyLen >= 512 && ReplyLen == PacketSize) { + if (BufferSize < PacketSize) { + ReplyLen = (UINTN) ((BufferSize > 0) ? BufferSize : 0); + } + + SaveLen = ReplyLen; + + // + // write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout + // + Retries = NUM_ACK_RETRIES; + + do { + ReplyLen = SaveLen; + + Status = SendAckAndGetData ( + Private, + ServerIpPtr, + ServerPortPtr, + ReplyIpPtr, + OurPortPtr, + Timeout, + (UINTN *) &ReplyLen, + BufferPtr, + &BlockNum, + FALSE + ); + + if (!EFI_ERROR (Status) || Status == EFI_BUFFER_TOO_SMALL) { + if (BlockNum == LastBlock) { + DEBUG ((DEBUG_NET, "\nresend")); + // + // a resend - continue + // + Status = EFI_TIMEOUT; + } else if (Private->BigBlkNumFlag) { + if (BlockNum != ++LastBlock) { + DEBUG ((DEBUG_NET, "\nLockStepReceive() Exit #1a")); + // + // not correct blocknum - error + // + return EFI_PROTOCOL_ERROR; + } + } else { + LastBlock = (LastBlock + 1) & 0xFFFF; + if (BlockNum != LastBlock) { + DEBUG ((DEBUG_NET, "\nLockStepReceive() Exit #1b")); + return EFI_PROTOCOL_ERROR; + // + // not correct blocknum - error + // + } + } + } + } while (Status == EFI_TIMEOUT && --Retries); + + if (EFI_ERROR (Status)) { + if (Status != EFI_BUFFER_TOO_SMALL) { + SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr); + } + + return Status; + } + + if (DontUseBuffer) { + BufferSize += ReplyLen; + } else { + BufferPtr += ReplyLen; + BufferSize -= ReplyLen; + } + } + // + // while (ReplyLen == PacketSize); + // + if (DontUseBuffer) { + if (BufferSizePtr != NULL) { + *BufferSizePtr = (BufferSize - PacketSize); + } + } else { + *BufferSizePtr -= BufferSize; + } + + /* Send ACK of last packet. */ + ReplyLen = 0; + + SendAckAndGetData ( + Private, + ServerIpPtr, + ServerPortPtr, + ReplyIpPtr, + OurPortPtr, + Timeout, + (UINTN *) &ReplyLen, + BufferPtr, + &BlockNum, + TRUE + ); + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// some literals +// +STATIC UINT8 Mode[] = MODE_BINARY; +STATIC UINT8 BlockSizeOp[] = OP_BLKSIZE; +STATIC UINT8 TsizeOp[] = OP_TFRSIZE; +STATIC UINT8 OverwriteOp[] = OP_OVERWRITE; +STATIC UINT8 BigBlkNumOp[] = OP_BIGBLKNUM; +STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return Pointer to value field if option found or NULL if not found. + +**/ +STATIC +UINT8 * +FindOption ( + UINT8 *OptionPtr, + INTN OpLen, + UINT8 *OackPtr, + INTN OackSize + ) +{ + if ((OackSize -= OpLen) <= 0) { + return NULL; + } + + do { + if (!CompareMem (OackPtr, OptionPtr, OpLen)) { + return OackPtr + OpLen; + } + + ++OackPtr; + } while (--OackSize); + + return NULL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#define BKSZOP 1 // block size +#define TSIZEOP 2 // transfer size +#define OVERWRITEOP 4 // overwrite +#define BIGBLKNUMOP 8 // big block numbers +STATIC +EFI_STATUS +TftpRwReq ( + UINT16 Req, + UINT16 Options, + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + VOID *Buffer + ) +{ + union { + UINT8 Data[514]; + struct Tftpv4Req ReqStr; + } *u; + + UINT16 OpFlags; + INTN Len; + INTN TotalLen; + UINT8 *Ptr; + + if (*OurPortPtr == 0) { + OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT; + } else { + OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT; + } + // + // build the basic request - opcode, filename, mode + // + u = Buffer; + u->ReqStr.OpCode = HTONS (Req); + TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + AsciiStrLen (FilenamePtr)); + + CopyMem (u->ReqStr.FileName, FilenamePtr, Len); + Ptr = (UINT8 *) (u->ReqStr.FileName + Len); + + CopyMem (Ptr, Mode, sizeof (Mode)); + Ptr += sizeof (Mode); + + if (Options & BKSZOP) { + CopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp)); + UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp)); + + TotalLen += (Len = 1 + AsciiStrLen (Ptr + sizeof (BlockSizeOp)) + sizeof (BlockSizeOp)); + + Ptr += Len; + } + + if (Options & TSIZEOP) { + CopyMem (Ptr, TsizeOp, sizeof (TsizeOp)); + CopyMem (Ptr + sizeof (TsizeOp), "0", 2); + TotalLen += sizeof (TsizeOp) + 2; + Ptr += sizeof (TsizeOp) + 2; + } + + if (Options & OVERWRITEOP) { + CopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp)); + CopyMem (Ptr + sizeof (OverwriteOp), "1", 2); + TotalLen += sizeof (OverwriteOp) + 2; + Ptr += sizeof (OverwriteOp) + 2; + } + + if (Options & BIGBLKNUMOP) { + CopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp)); + CopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2); + TotalLen += sizeof (BigBlkNumOp) + 2; + Ptr += sizeof (BigBlkNumOp) + 2; + } + // + // send it + // + return UdpWrite ( + Private, + OpFlags, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + (UINTN *) &TotalLen, + u + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpRwReqwResp ( + UINT16 Req, + UINT16 Options, + PXE_BASECODE_DEVICE *Private, + VOID *HeaderPtr, + UINTN *PacketSizePtr, + UINTN *ReplyLenPtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerReplyPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT8 *FilenamePtr, + UINT16 Timeout + ) +{ + EFI_STATUS Status; + UINTN SaveReplyLen; + INTN Retries; + UINT8 Buffer[514]; + + SaveReplyLen = *ReplyLenPtr; + Retries = 3; + Private->BigBlkNumFlag = FALSE; + *OurPortPtr = 0; + // + // generate random + // + do { + if (*OurPortPtr != 0) { + if (++ *OurPortPtr == 0) { + *OurPortPtr = PXE_RND_PORT_LOW; + } + } + // + // send request from our Ip = StationIp + // + if ((Status = TftpRwReq ( + Req, + Options, + Private, + ServerIpPtr, + ServerPortPtr, + OurPortPtr, + FilenamePtr, + PacketSizePtr, + Buffer + )) != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nTftpRwReqwResp() Exit #1 %xh (%r)", + Status, + Status) + ); + + return Status; + } + // + // read reply to our Ip = StationIp + // + *ReplyLenPtr = SaveReplyLen; + + Status = TftpUdpRead ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + HeaderPtr, + ReplyLenPtr, + BufferPtr, + ServerIpPtr, + ServerReplyPortPtr, + 0, + OurPortPtr, + Timeout + ); + } while (Status == EFI_TIMEOUT && --Retries); + + if (!Options || Status != EFI_TFTP_ERROR) { + DEBUG ( + (DEBUG_WARN, + "\nTftpRwReqwResp() Exit #2 %xh (%r)", + Status, + Status) + ); + return Status; + } + + Status = TftpRwReqwResp ( + Req, + 0, + Private, + HeaderPtr, + PacketSizePtr, + ReplyLenPtr, + BufferPtr, + ServerIpPtr, + ServerPortPtr, + ServerReplyPortPtr, + OurPortPtr, + FilenamePtr, + Timeout + ); + + DEBUG ((DEBUG_WARN, "\nTftpRwReqwResp() Exit #3 %xh (%r)", Status, Status)); + + return Status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// mtftp listen +// read on mcast ip, cport, from sport, for data packet +// returns success if gets multicast last packet or all up to last block +// if not missing, then finished +// + +/** + + +**/ +STATIC +EFI_STATUS +MtftpListen ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, + UINT64 *StartBlockPtr, + UINTN *NumMissedPtr, + UINT16 TransTimeout, + UINT16 ListenTimeout, + UINT64 FinalBlock, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_STATUS Status; + struct Tftpv4Ack Header; + UINT64 Offset; + UINT64 BlockNum; + UINT64 LastBlockNum; + UINT64 BufferSize; + UINTN NumMissed; + UINTN PacketSize; + UINTN SaveReplyLen; + UINTN ReplyLen; + UINT16 Timeout; + + LastBlockNum = *StartBlockPtr; + Timeout = ListenTimeout; + *NumMissedPtr = 0; + PacketSize = 0; + BufferSize = *BufferSizePtr; + ReplyLen = MAX_TFTP_PKT_SIZE;; + + // + // receive + // + do { + if ((SaveReplyLen = ReplyLen) > BufferSize) { + SaveReplyLen = (UINTN) BufferSize; + } + + /* %%TBD - add big block number support */ + + // + // get data - loop on resends + // + do { + ReplyLen = SaveReplyLen; + + if ((Status = TftpUdpRead ( + Private, + 0, + &Header, + &ReplyLen, + BufferPtr, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &MtftpInfoPtr->MCastIp, + &MtftpInfoPtr->CPort, + Timeout + )) != EFI_SUCCESS) { + return Status; + } + // + // make sure a data packet + // + if (Header.OpCode != HTONS (TFTP_DATA)) { + return EFI_PROTOCOL_ERROR; + } + } while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum); + + // + // make sure still going up + // + if (LastBlockNum > BlockNum) { + return EFI_PROTOCOL_ERROR; + } + + if (BlockNum - LastBlockNum > 0xFFFFFFFF) { + return EFI_PROTOCOL_ERROR; + } else { + NumMissed = (UINTN) (BlockNum - LastBlockNum - 1); + } + + LastBlockNum = BlockNum; + + // + // if first time through, some reinitialization + // + if (!PacketSize) { + *StartBlockPtr = BlockNum; + PacketSize = ReplyLen; + Timeout = TransTimeout; + } else { + *NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed); + } + // + // if missed packets, update start block, + // etc. and move packet to proper place in buffer + // + if (NumMissed) { + *StartBlockPtr = BlockNum; + if (!DontUseBuffer) { + Offset = NumMissed * PacketSize; + CopyMem (BufferPtr + Offset, BufferPtr, ReplyLen); + BufferPtr += Offset; + BufferSize -= Offset; + } + } + + if (!DontUseBuffer) { + BufferPtr += ReplyLen; + BufferSize -= ReplyLen; + } + } while (ReplyLen == PacketSize && BlockNum != FinalBlock); + + *BufferSizePtr = BufferSize; + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return // mtftp open session + @return // return code EFI_SUCCESS + @return // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done + @return // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest + @return // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all + @retval GOTUNI returns NO_DATA go will go to TFTP session) + +**/ +STATIC +EFI_STATUS +MtftpOpen ( + PXE_BASECODE_DEVICE * Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + UINTN *PacketSizePtr, + EFI_IP_ADDRESS * ServerIpPtr, + UINT8 *FilenamePtr, + EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr, + UINT8 *CompletionStatusPtr, +#define GOTUNI 1 +#define GOTMULTI 2 + IN BOOLEAN DontUseBuffer + ) +{ + EFI_STATUS Status; + EFI_IP_ADDRESS OurReplyIp; + struct Tftpv4Ack Header; + INTN ReplyLen; + INTN Retries; + UINT8 *BufferPtr2; + UINT8 TmpBuf[514]; + + Retries = NUM_MTFTP_OPEN_RETRIES; + BufferPtr2 = BufferPtr; + *PacketSizePtr = (UINTN) (MIN (*BufferSizePtr, MAX_TFTP_PKT_SIZE)); + + do { + // + // send a read request + // + *CompletionStatusPtr = 0; + + if ((Status = TftpRwReq ( + TFTP_RRQ, + 0, + Private, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &MtftpInfoPtr->CPort, + FilenamePtr, + PacketSizePtr, + TmpBuf + )) != EFI_SUCCESS) { + return Status; + } + + for (;;) { + // + // read reply + // + ZeroMem (&OurReplyIp, Private->IpLength); + ReplyLen = *PacketSizePtr; + + if ((Status = TftpUdpRead ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER, + &Header, + (UINTN *) &ReplyLen, + BufferPtr2, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &OurReplyIp, + &MtftpInfoPtr->CPort, + MtftpInfoPtr->TransmitTimeout + )) == EFI_SUCCESS) { + // + // check for first data packet + // + if (Header.OpCode != HTONS (TFTP_DATA)) { + return EFI_PROTOCOL_ERROR; + } + // + // check block num + // + if (Header.BlockNum != HTONS (1)) { + // + // it's not first + // if we are not the primary client, + // we probably got first and now second + // multicast but no unicast, so + // *CompletionStatusPtr = GOTMULTI - if this is + // the second, can just go on to listen + // starting with 2 as the last block + // received + // + if (Header.BlockNum != HTONS (2)) { + // + // not second + // + *CompletionStatusPtr = 0; + } + + return Status; + } + + // + // now actual + // + *PacketSizePtr = ReplyLen; + // + // see if a unicast data packet + // + if (!CompareMem ( + &OurReplyIp, + &Private->EfiBc.Mode->StationIp, + Private->IpLength + )) { + *CompletionStatusPtr |= GOTUNI; + // + // it is + // if already got multicast packet, + // got em both + // + if (*CompletionStatusPtr & GOTMULTI) { + break; + } + } else if (!CompareMem ( + &OurReplyIp, + &MtftpInfoPtr->MCastIp, + Private->IpLength + )) { + // + // otherwise see if a multicast data packet + // + *CompletionStatusPtr |= GOTMULTI; + // + // it is + // got first - bump pointer so that if + // second multi comes along, we're OK + // + if (!DontUseBuffer) { + BufferPtr2 = (UINT8 *) BufferPtr + ReplyLen; + } + // + // if already got unicast packet, + // got em both + // + if (*CompletionStatusPtr & GOTUNI) { + break; + } + } else { + // + // else protocol error + // + return EFI_PROTOCOL_ERROR; + } + } else if (Status == EFI_TIMEOUT) { + // + // bad return code - if timed out, retry + // + break; + } else { + // + // else just bad - failed MTFTP open + // + return Status; + } + } + } while (Status == EFI_TIMEOUT && --Retries); + + if (Status != EFI_SUCCESS) { + // + // open failed + // + return Status; + } + // + // got em both - go into receive mode + // routine to read rest of file after a successful open (TFTP or MTFTP) + // sends ACK and gets next data packet until short packet arrives, + // then sends ACK and (hopefully) times out + // + return LockStepReceive ( + Private, + (UINT16) ReplyLen, + BufferSizePtr, + ReplyLen, + BufferPtr, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &MtftpInfoPtr->MCastIp, + &MtftpInfoPtr->CPort, + 1, + MtftpInfoPtr->TransmitTimeout, + DontUseBuffer + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +MtftpDownload ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS Status; + UINT64 StartBlock; + UINT64 LastBlock; + UINT64 LastStartBlock; + UINT64 BufferSize; + UINTN Offset; + UINTN NumMissed; + UINT16 TransTimeout; + UINT16 ListenTimeout; + UINT8 *BufferPtrLocal; + + TransTimeout = MtftpInfoPtr->TransmitTimeout; + ListenTimeout = MtftpInfoPtr->ListenTimeout; + LastBlock = 0; + LastStartBlock = 0; + Offset = 0; + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST; + Filter.IpCnt = 2; + Filter.IpList[0] = Private->EfiBc.Mode->StationIp; + Filter.IpList[1] = MtftpInfoPtr->MCastIp; + + if ((Status = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + return Status; + } + + for (;;) { + StartBlock = LastStartBlock; + BufferSize = *BufferSizePtr - Offset; + + if (DontUseBuffer) { + // + // overwrie the temp buf + // + BufferPtrLocal = BufferPtr; + } else { + BufferPtrLocal = BufferPtr + Offset; + + } + // + // special !!! do not leave enabled in saved version on Source Safe + // Following code put in in order to create a special version for regression + // test of MTFTP server to make sure it handles mulitple opens correctly. + // This code should NOT be enabled normally. + // +#ifdef SpecialNowaitVersion +#pragma message ("This is special version for MTFTP regression test") + if (StartBlock || !LastBlock) +#endif + if (((Status = MtftpListen ( + Private, + &BufferSize, + BufferPtrLocal, + ServerIpPtr, + MtftpInfoPtr, + &StartBlock, + &NumMissed, + TransTimeout, + ListenTimeout, + LastBlock, + DontUseBuffer + )) != EFI_SUCCESS) && (Status != EFI_TIMEOUT)) { + return Status; + // + // failed + // + } + // + // if none were received, start block is not reset + // + if (StartBlock == LastStartBlock) { + UINT8 CompStat; + + // + // timed out with none received - try MTFTP open + // + if ((Status = MtftpOpen ( + Private, + BufferSizePtr, + BufferPtr, + &Offset, + ServerIpPtr, + FilenamePtr, + MtftpInfoPtr, + &CompStat, + DontUseBuffer + )) != EFI_SUCCESS) { + // + // open failure - try TFTP + // + return Status; + } + // + // return code EFI_SUCCESS + // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done + // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest + // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all + // (do not get = GOTUNI - returns NO_DATA go will go to TFTP session) + // + if (CompStat == (GOTUNI | GOTMULTI)) { + // + // finished - got it all + // + return Status; + } + + if (CompStat) { + // + // offset is two packet lengths + // + Offset <<= 1; + // + // last block received + // + LastStartBlock = 2; + } else { + Offset = 0; + LastStartBlock = 0; + } + + ListenTimeout = TransTimeout; + continue; + } + // + // did we get the last block + // + if (Status == EFI_SUCCESS) { + // + // yes - set the file size if this was first time + // + if (!LastBlock) { + *BufferSizePtr -= BufferSize; + } + // + // if buffer was too small, finished + // + if (!DontUseBuffer) { + return EFI_BUFFER_TOO_SMALL; + } + // + // if we got them all, finished + // + if (!NumMissed && StartBlock == LastStartBlock + 1) { + return Status; + } + // + // did not get them all - set last block + // + LastBlock = (UINT16) (StartBlock - 1); + } + // + // compute listen timeout + // + ListenTimeout = (UINT16) ((NumMissed > MtftpInfoPtr->ListenTimeout) ? 0 : (MtftpInfoPtr->ListenTimeout - NumMissed)); + + // + // reset + // + Offset = 0; + LastStartBlock = 0; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpInfo ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT SrvPort, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT OurPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort; + EFI_STATUS Status; + UINT64 BlockNum; + UINTN Offset; + UINTN ReplyLen; + UINT8 *Ptr; + + union { + struct Tftpv4Oack OAck2Ptr; + struct Tftpv4Ack Ack2Ptr; + struct Tftpv4Data Datastr; + } u; + + OurPort = 0; + ServerReplyPort = 0; + ReplyLen = sizeof (u.Datastr.Data); + + // + // send a write request with the blocksize option - + // sets our IP and port - and receive reply - sets his port + // will retry operation up to 3 times if no response, + // and will retry without options on an error reply + // + if ((Status = TftpRwReqwResp ( + TFTP_RRQ, + /* BIGBLKNUMOP | */BKSZOP | TSIZEOP, + Private, + &u, + PacketSizePtr, + &ReplyLen, + u.Datastr.Data, + ServerIpPtr, + &SrvPort, + &ServerReplyPort, + &OurPort, + FilenamePtr, + REQ_RESP_TIMEOUT + )) != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpInfo() Exit #1")); + return Status; + } + // + // check for good OACK + // + if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) { + // + // now parse it for options + // bigblk# + // + Ptr = FindOption ( + BigBlkNumOp, + sizeof (BigBlkNumOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen + sizeof (u.Ack2Ptr.BlockNum) + ); + + if (Ptr != NULL) { + if (AtoU (Ptr) == 8) { + Private->BigBlkNumFlag = TRUE; + } else { + return EFI_PROTOCOL_ERROR; + } + } + // + // blksize + // + Ptr = FindOption ( + BlockSizeOp, + sizeof (BlockSizeOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen += sizeof (u.Ack2Ptr.BlockNum) + ); + + *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512; + + // + // tsize + // + Ptr = FindOption ( + TsizeOp, + sizeof (TsizeOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen + ); + + if (Ptr != NULL) { + *BufferSizePtr = AtoU64 (Ptr); + + // + // teminate session with error + // + SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort); + + return EFI_SUCCESS; + } + + Offset = 0; + BlockNum = 0; + } else { + // + // if MTFTP get filesize, return unsupported + // + if (SrvPort != TftpRequestPort) { + SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort); + DEBUG ((DEBUG_WARN, "\nTftpInfo() Exit #3")); + return EFI_UNSUPPORTED; + } + + Offset = ReplyLen; + // + // last block received + // + BlockNum = 1; + } + // + // does not support the option - do a download with no buffer + // + *BufferSizePtr = 0; + + Status = LockStepReceive ( + Private, + (UINT16) ReplyLen, + BufferSizePtr, + Offset, + (INT8 *) &u, + ServerIpPtr, + &ServerReplyPort, + &Private->EfiBc.Mode->StationIp, + &OurPort, + BlockNum, + ACK_TIMEOUT, + TRUE + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpInfo() LockStepReceive() == %Xh", Status)); + } + + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpDownload ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + EFI_PXE_BASE_CODE_UDP_PORT SrvPort, + UINT16 Req, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT OurPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort; + EFI_STATUS Status; + UINT64 Offset; + UINT64 BlockNum; + UINTN ReplyLen; + UINT8 *Ptr; + + union { + struct Tftpv4Ack Ack2Ptr; + struct Tftpv4Oack OAck2Ptr; + struct Tftpv4Data Data; + struct Tftpv4Ack8 Ack8Ptr; + struct Tftpv4Data8 Data8; + } U; + + OurPort = 0; + ServerReplyPort = 0; + ReplyLen = (UINTN) ((*BufferSizePtr > 0xFFFF) ? 0xFFFF : *BufferSizePtr); + + // + // send a read request with the blocksize option - sets our IP and port + // - and receive reply - sets his port will retry operation up to 3 + // times if no response, and will retry without options on an error + // reply + // + if ((Status = TftpRwReqwResp ( + Req, + /* BIGBLKNUMOP | */BKSZOP, + Private, + &U, + PacketSizePtr, + &ReplyLen, + BufferPtr, + ServerIpPtr, + &SrvPort, + &ServerReplyPort, + &OurPort, + FilenamePtr, + REQ_RESP_TIMEOUT + )) != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #1 %xh (%r)", Status, Status)); + return Status; + } + // + // check for OACK + // + if (U.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) { + // + // get the OACK + // + CopyMem (U.Data.Data, BufferPtr, ReplyLen); + + Ptr = FindOption ( + BigBlkNumOp, + sizeof (BigBlkNumOp), + U.OAck2Ptr.OpAck[0].Option, + ReplyLen + sizeof (U.Ack2Ptr.BlockNum) + ); + + if (Ptr != NULL) { + if (AtoU (Ptr) == 8) { + Private->BigBlkNumFlag = TRUE; + } else { + return EFI_PROTOCOL_ERROR; + } + } + // + // now parse it for blocksize option + // + Ptr = FindOption ( + BlockSizeOp, + sizeof (BlockSizeOp), + U.OAck2Ptr.OpAck[0].Option, + ReplyLen += sizeof (U.Ack2Ptr.BlockNum) + ); + + ReplyLen = (Ptr != NULL) ? AtoU (Ptr) : 512; + + Offset = 0; + // + // last block received + // + BlockNum = 0; + } else if (U.Ack2Ptr.OpCode != HTONS (TFTP_DATA) || U.Ack2Ptr.BlockNum != HTONS (1)) { + // + // or data + // + DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #2 %xh (%r)", Status, Status)); + + return EFI_PROTOCOL_ERROR; + } else { + // + // got good data packet + // + Offset = ReplyLen; + // + // last block received + // + BlockNum = 1; + } + + if (PacketSizePtr != NULL) { + *PacketSizePtr = ReplyLen; + } + // + // routine to read rest of file after a successful open (TFTP or MTFTP) + // sends ACK and gets next data packet until short packet arrives, then sends + // ACK and (hopefully) times out + // if first packet has been read, BufferPtr and BufferSize must reflect fact + // + Status = LockStepReceive ( + Private, + ReplyLen, + BufferSizePtr, + Offset, + BufferPtr, + ServerIpPtr, + &ServerReplyPort, + &Private->EfiBc.Mode->StationIp, + &OurPort, + BlockNum, + ACK_TIMEOUT, + DontUseBuffer + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #3 %xh (%r)", Status, Status)); + + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = TftpInfo ( + Private, + BufferSizePtr, + ServerIpPtr, + SrvPort, + FilenamePtr, + PacketSizePtr + ); + + if (!EFI_ERROR (Status)) { + Status = EFI_BUFFER_TOO_SMALL; + } + } + } + + return Status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpUpload ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + BOOLEAN Overwrite + ) +{ + struct Tftpv4Ack Header; + EFI_PXE_BASE_CODE_UDP_PORT OurPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort; + EFI_STATUS Status; + UINT64 BlockNum; + UINT64 TransferSize; + UINTN ReplyLen; + UINTN TransferLen; + UINT16 Options; + UINT8 *Ptr; + + union { + struct Tftpv4Oack OAck2Ptr; + struct Tftpv4Ack Ack2Ptr; + struct Tftpv4Data Datastr; + } u; + + OurPort = 0; + ServerReplyPort = 0; + TransferSize = *BufferSizePtr; + ReplyLen = sizeof (u.Datastr.Data); + Options = (UINT16) ((Overwrite) ? OVERWRITEOP | BKSZOP : BKSZOP); + + // + // send a write request with the blocksize option - sets our IP and port - + // and receive reply - sets his port + // will retry operation up to 3 times if no response, and will retry without + // options on an error reply + // + if ((Status = TftpRwReqwResp ( + TFTP_WRQ, + Options, + Private, + &u, + PacketSizePtr, + &ReplyLen, + u.Datastr.Data, + ServerIpPtr, + &TftpRequestPort, + &ServerReplyPort, + &OurPort, + FilenamePtr, + REQ_RESP_TIMEOUT + )) != EFI_SUCCESS) { + return Status; + } + // + // check for OACK + // + if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) { + // + // parse it for blocksize option + // + Ptr = FindOption ( + BlockSizeOp, + sizeof (BlockSizeOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen += sizeof (u.Ack2Ptr.BlockNum) + ); + *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512; + } + // + // or ACK + // + else if (u.Ack2Ptr.OpCode == HTONS (TFTP_ACK)) { + // + // option was not supported + // + *PacketSizePtr = 512; + } else { + return EFI_PROTOCOL_ERROR; + } + // + // loop + // + Header.OpCode = HTONS (TFTP_DATA); + BlockNum = 1; + Header.BlockNum = HTONS (1); + + do { + UINTN HeaderSize; + INTN Retries; + + Retries = NUM_ACK_RETRIES; + HeaderSize = sizeof (Header); + TransferLen = (UINTN) (MIN (*PacketSizePtr, TransferSize)); + + // + // write a data packet and get an ack + // + do { + // + // write + // + if ((Status = UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + &ServerReplyPort, + 0, + 0, + &OurPort, + &HeaderSize, + &Header, + &TransferLen, + BufferPtr + )) != EFI_SUCCESS) { + return Status; + } + // + // read reply + // + ReplyLen = sizeof (u.Datastr.Data); + + if ((Status = TftpUdpRead ( + Private, + 0, + &u, + &ReplyLen, + u.Datastr.Data, + ServerIpPtr, + &ServerReplyPort, + 0, + &OurPort, + ACK_TIMEOUT + )) == EFI_SUCCESS) { + // + // check for ACK for this data packet + // + if (u.Ack2Ptr.OpCode != HTONS (TFTP_ACK)) { + return EFI_PROTOCOL_ERROR; + } + + if (u.Ack2Ptr.BlockNum != Header.BlockNum) { + // + // not for this packet - continue + // + Status = EFI_TIMEOUT; + } + } + } while (Status == EFI_TIMEOUT && --Retries); + + if (Status != EFI_SUCCESS) { + return Status; + } + + BufferPtr = (VOID *) ((UINT8 *) (BufferPtr) + TransferLen); + TransferSize -= TransferLen; + ++BlockNum; + Header.BlockNum = HTONS ((UINT16) BlockNum); + } while (TransferLen == *PacketSizePtr); + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return * EFI_INVALID_PARAMETER + @return * EFI_OUT_OF_RESOURCES + @return * EFI_BAD_BUFFER_SIZE + @return * Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(), + @return * TftpDownload() and TftpUpload(). + +**/ +EFI_STATUS +PxeBcMtftp ( + PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + UINT64 *BufferSizePtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + IN EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, OPTIONAL + IN BOOLEAN Overwrite, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS StatCode; + EFI_STATUS Status; + UINT64 BufferSizeLocal; + UINTN PacketSize; + UINT8 *BufferPtrLocal; + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + Filter.IpCnt = 0; + Filter.reserved = 0; + + /* No error has occurred, yet. */ + Private->EfiBc.Mode->TftpErrorReceived = FALSE; + + /* We must at least have an MTFTP server IP address and + * a pointer to the buffer size. + */ + if (!ServerIpPtr || !BufferSizePtr) { + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #1")); + + return EFI_INVALID_PARAMETER; + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_MTFTP; + + // + // make sure filter set to unicast at start + // + if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + DEBUG ( + (DEBUG_NET, + "\nPxeBcMtftp() Exit IpFilter() == %Xh", + StatCode) + ); + + return StatCode; + } + // + // set unset parms to default values + // + if (!PacketSizePtr) { + *(PacketSizePtr = &PacketSize) = MAX_TFTP_PKT_SIZE; + } + + if (*PacketSizePtr > *BufferSizePtr) { + *PacketSizePtr = (UINTN) *BufferSizePtr; + } + + if (*PacketSizePtr < MIN_TFTP_PKT_SIZE) { + *PacketSizePtr = MIN_TFTP_PKT_SIZE; + return EFI_INVALID_PARAMETER; + } + + if (*PacketSizePtr > BUFFER_ALLOCATE_SIZE) { + *PacketSizePtr = BUFFER_ALLOCATE_SIZE; + } + + if (*PacketSizePtr > MAX_TFTP_PKT_SIZE) { + *PacketSizePtr = MAX_TFTP_PKT_SIZE; + } + + if (Operation == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) { + StatCode = TftpInfo ( + Private, + BufferSizePtr, + ServerIpPtr, + TftpRequestPort, + FilenamePtr, + PacketSizePtr + ); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit TftpInfo() == %Xh", + StatCode) + ); + } + + return StatCode; + } + + if (Operation == EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE) { + if (!MtftpInfoPtr || !MtftpInfoPtr->SPort) { + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #2")); + return EFI_INVALID_PARAMETER; + } else { + StatCode = TftpInfo ( + Private, + BufferSizePtr, + ServerIpPtr, + MtftpInfoPtr->SPort, + FilenamePtr, + PacketSizePtr + ); + + gBS->Stall (10000); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit TftpInfo() == %Xh", + StatCode) + ); + } + + return StatCode; + } + } + + if (!BufferPtr && !DontUseBuffer) { + // + // if dontusebuffer is false and no buffer??? + // + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #3")); + // + // DontUseBuffer can be true only for read_file operation + // + return EFI_INVALID_PARAMETER; + } + + if (DontUseBuffer) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + BUFFER_ALLOCATE_SIZE, + &BufferPtrLocal + ); + + if (EFI_ERROR (Status) || BufferPtrLocal == NULL) { + DEBUG ((DEBUG_NET, "\nPxeBcMtftp() Exit #4")); + return EFI_OUT_OF_RESOURCES; + } + + BufferSizeLocal = BUFFER_ALLOCATE_SIZE; + } else { + if (!*BufferSizePtr && Operation != EFI_PXE_BASE_CODE_TFTP_WRITE_FILE) { + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #5")); + return EFI_BAD_BUFFER_SIZE; + } + + BufferPtrLocal = BufferPtr; + BufferSizeLocal = *BufferSizePtr; + } + + switch (Operation) { + case EFI_PXE_BASE_CODE_MTFTP_READ_FILE: + if (FilenamePtr == NULL || + MtftpInfoPtr == NULL || + MtftpInfoPtr->MCastIp.Addr[0] == 0 || + MtftpInfoPtr->SPort == 0 || + MtftpInfoPtr->CPort == 0 || + MtftpInfoPtr->ListenTimeout == 0 || + MtftpInfoPtr->TransmitTimeout == 0 + ) { + StatCode = EFI_INVALID_PARAMETER; + break; + } + // + // try MTFTP - if fails, drop into TFTP read + // + if ((StatCode = MtftpDownload ( + Private, + &BufferSizeLocal, + BufferPtrLocal, + ServerIpPtr, + FilenamePtr, + MtftpInfoPtr, + DontUseBuffer + )) == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { + if (BufferSizePtr /* %% !DontUseBuffer */ ) { + *BufferSizePtr = BufferSizeLocal; + } + + break; + } + // + // go back to unicast + // + if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + break; + } + + /* fall thru */ + case EFI_PXE_BASE_CODE_TFTP_READ_FILE: + if (FilenamePtr == NULL) { + StatCode = EFI_INVALID_PARAMETER; + break; + } + + StatCode = TftpDownload ( + Private, + &BufferSizeLocal, + BufferPtrLocal, + ServerIpPtr, + FilenamePtr, + PacketSizePtr, + TftpRequestPort, + TFTP_RRQ, + DontUseBuffer + ); + + if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { + if (BufferSizePtr /* !DontUseBuffer */ ) { + *BufferSizePtr = BufferSizeLocal; + } + } + + break; + + case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE: + if (FilenamePtr == NULL || DontUseBuffer) { + // + // not a valid option + // + StatCode = EFI_INVALID_PARAMETER; + break; + } + + StatCode = TftpUpload ( + Private, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + FilenamePtr, + PacketSizePtr, + Overwrite + ); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #6 %xh (%r)", + StatCode, + StatCode) + ); + } + + return StatCode; + + case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY: + if (FilenamePtr == NULL || DontUseBuffer) { + // + // not a valid option + // + StatCode = EFI_INVALID_PARAMETER; + break; + } + + StatCode = TftpDownload ( + Private, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + FilenamePtr, + PacketSizePtr, + TftpRequestPort, + TFTP_DIR, + DontUseBuffer + ); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #7 %xh (%r)", + StatCode, + StatCode) + ); + } + + return StatCode; + + case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY: + if (DontUseBuffer) { + StatCode = EFI_INVALID_PARAMETER; + break; + } + + if (MtftpInfoPtr == NULL || !MtftpInfoPtr->SPort) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #9 %xh (%r)", + EFI_INVALID_PARAMETER, + EFI_INVALID_PARAMETER) + ); + + return EFI_INVALID_PARAMETER; + } + + StatCode = TftpDownload ( + Private, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + (UINT8 *) "/", + PacketSizePtr, + MtftpInfoPtr->SPort, + TFTP_DIR, + DontUseBuffer + ); + + break; + + default: + StatCode = EFI_INVALID_PARAMETER; + } + + if (DontUseBuffer) { + gBS->FreePool (BufferPtrLocal); + } + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #8 %xh (%r)", + StatCode, + StatCode) + ); + } + + gBS->Stall (10000); + + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return * EFI_INVALID_PARAMETER + @return * Status is also returned from PxeBcMtftp(); + +**/ +EFI_STATUS +EFIAPI +BcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSizePtr, + IN UINTN *BlockSizePtr OPTIONAL, + IN EFI_IP_ADDRESS * ServerIpPtr, + IN UINT8 *FilenamePtr, + IN EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + if (!IS_INADDR_UNICAST (ServerIpPtr)) { + // + // The station IP is not a unicast address. + // + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + // + // Issue BC command + // + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + Filter.IpCnt = 0; + Filter.reserved = 0; + + DEBUG ((DEBUG_WARN, "\nBcMtftp() Op=%d Buf=%Xh", Operation, BufferPtr)); + + StatCode = PxeBcMtftp ( + Private, + Operation, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + FilenamePtr, + BlockSizePtr, + MtftpInfoPtr, + Overwrite, + DontUseBuffer + ); + + // + // restore to unicast + // + IpFilter (Private, &Filter); + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* eof - PxeBcMtftp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c new file mode 100644 index 0000000000..fcb55acfa0 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c @@ -0,0 +1,517 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_udp.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// ////////////////////////////////////////////////////////////////////// +// +// Udp Write Routine - called by base code - e.g. TFTP - already locked +// + +/** + + @return EFI_SUCCESS := + @return EFI_INVALID_PARAMETER := + @return other := + +**/ +EFI_STATUS +UdpWrite ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIpPtr, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, + IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL + IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSizeptr, + IN VOID *BufferPtr + ) +{ + UINTN TotalLength; + UINTN HeaderSize; + EFI_PXE_BASE_CODE_UDP_PORT DefaultSrcPort; + + // + // + // + HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0; + DefaultSrcPort = 0; + + // + // check parameters + // + if (BufferSizeptr == NULL || + BufferPtr == NULL || + DestIpPtr == NULL || + DestPortPtr == NULL || + (HeaderSizePtr != NULL && *HeaderSizePtr == 0) || + (HeaderSize != 0 && HeaderPtr == NULL) || + (GatewayIpPtr != NULL && !IS_INADDR_UNICAST(GatewayIpPtr)) || + (OpFlags &~(EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT)) + ) { + DEBUG ( + (DEBUG_WARN, + "\nUdpWrite() Exit #1 %xh (%r)", + EFI_INVALID_PARAMETER, + EFI_INVALID_PARAMETER) + ); + + return EFI_INVALID_PARAMETER; + } + + TotalLength = *BufferSizeptr + HeaderSize + sizeof (UDPV4_HEADER); + + if (TotalLength > 0x0000ffff) { + DEBUG ( + (DEBUG_WARN, + "\nUdpWrite() Exit #2 %xh (%r)", + EFI_BAD_BUFFER_SIZE, + EFI_BAD_BUFFER_SIZE) + ); + + return EFI_BAD_BUFFER_SIZE; + } + + if (SrcIpPtr == NULL) { + SrcIpPtr = &Private->EfiBc.Mode->StationIp; + } + + if (SrcPortPtr == NULL) { + SrcPortPtr = &DefaultSrcPort; + OpFlags |= EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT; + } + + if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) { + *SrcPortPtr = Private->RandomPort; + + if (++Private->RandomPort == 0) { + Private->RandomPort = PXE_RND_PORT_LOW; + } + } + +#define IpTxBuffer ((IPV4_BUFFER *) Private->TransmitBufferPtr) + // + // build pseudo header and udp header in transmit buffer + // +#define Udpv4Base ((UDPV4_HEADERS *) (IpTxBuffer->u.Data - sizeof (UDPV4_PSEUDO_HEADER))) + + Udpv4Base->Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0]; + Udpv4Base->Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0]; + Udpv4Base->Udpv4PseudoHeader.Zero = 0; + Udpv4Base->Udpv4PseudoHeader.Protocol = PROT_UDP; + Udpv4Base->Udpv4PseudoHeader.TotalLength = HTONS (TotalLength); + Udpv4Base->Udpv4Header.SrcPort = HTONS (*SrcPortPtr); + Udpv4Base->Udpv4Header.DestPort = HTONS (*DestPortPtr); + Udpv4Base->Udpv4Header.TotalLength = Udpv4Base->Udpv4PseudoHeader.TotalLength; + Udpv4Base->Udpv4Header.Checksum = 0; + + if (HeaderSize != 0) { + CopyMem (IpTxBuffer->u.Udp.Data, HeaderPtr, HeaderSize); + } + + HeaderSize += sizeof (UDPV4_HEADER); + + Udpv4Base->Udpv4Header.Checksum = IpChecksum2 ( + (UINT16 *) Udpv4Base, + HeaderSize + sizeof (UDPV4_PSEUDO_HEADER), + (UINT16 *) BufferPtr, + (UINT16) *BufferSizeptr + ); + + if (Udpv4Base->Udpv4Header.Checksum == 0) { + Udpv4Base->Udpv4Header.Checksum = 0xffff; + // + // transmit zero checksum as ones complement + // + } + + return Ip4Send ( + Private, + OpFlags, + PROT_UDP, + Udpv4Base->Udpv4PseudoHeader.SrcAddr.L, + Udpv4Base->Udpv4PseudoHeader.DestAddr.L, + (GatewayIpPtr) ? GatewayIpPtr->Addr[0] : 0, + HeaderSize, + BufferPtr, + *BufferSizeptr + ); +} +// +// ////////////////////////////////////////////////////////// +// +// BC Udp Write Routine +// + +/** + + @return EFI_SUCCESS := + @return other := + +**/ +EFI_STATUS +EFIAPI +BcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIpPtr, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, + IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL + IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSizeptr, + IN VOID *BufferPtr + ) +{ + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE; + + // + // Issue BC command + // + StatCode = UdpWrite ( + Private, + OpFlags, + DestIpPtr, + DestPortPtr, + GatewayIpPtr, + SrcIpPtr, + SrcPortPtr, + HeaderSizePtr, + HeaderPtr, + BufferSizeptr, + BufferPtr + ); + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} +// +// ///////////////////////////////////////////////////////////////////// +// +// Udp Read Routine - called by base code - e.g. TFTP - already locked +// + +/** + + @return EFI_SUCCESS := + @return EFI_INVALID_PARAMETER := + @return other := + +**/ +EFI_STATUS +UdpRead ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSizeptr, + IN VOID *BufferPtr, + EFI_EVENT TimeoutEvent + ) +{ + EFI_STATUS StatCode; + EFI_IP_ADDRESS TmpSrcIp; + EFI_IP_ADDRESS TmpDestIp; + UINTN BufferSize; + UINTN HeaderSize; + + // + // combination structure of pseudo header/udp header + // +#pragma pack (1) + struct { + UDPV4_PSEUDO_HEADER Udpv4PseudoHeader; + UDPV4_HEADER Udpv4Header; + UINT8 ProtHdr[64]; + } Hdrs; +#pragma pack () + + HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0; + // + // read [with filtering] + // check parameters + // + if (BufferSizeptr == NULL || + BufferPtr == NULL || + (HeaderSize != 0 && HeaderPtr == NULL) || + (OpFlags &~UDP_FILTER_MASK) + // + // if filtering on a particular IP/Port, need it + // + || + (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr == NULL) || + (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && SrcPortPtr == NULL) || + (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && DestPortPtr == NULL) + ) { + DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #1 Invalid Parameter")); + return EFI_INVALID_PARAMETER; + } + + // + // in case we loop + // + BufferSize = *BufferSizeptr; + // + // we need source and dest IPs for pseudo header + // + if (SrcIpPtr == NULL) { + SrcIpPtr = &TmpSrcIp; + } + + if (DestIpPtr == NULL) { + DestIpPtr = &TmpDestIp; + TmpDestIp = Private->EfiBc.Mode->StationIp; + } + +#if SUPPORT_IPV6 + if (Private->EfiBc.Mode->UsingIpv6) { + // + // %%TBD + // + } +#endif + + for (;;) { + *BufferSizeptr = BufferSize; + + StatCode = IpReceive ( + Private, + OpFlags, + SrcIpPtr, + DestIpPtr, + PROT_UDP, + &Hdrs.Udpv4Header, + HeaderSize + sizeof Hdrs.Udpv4Header, + BufferPtr, + BufferSizeptr, + TimeoutEvent + ); + + if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { + UINT16 SPort; + UINT16 DPort; + + SPort = NTOHS (Hdrs.Udpv4Header.SrcPort); + DPort = NTOHS (Hdrs.Udpv4Header.DestPort); + + // + // do filtering + // + if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && *SrcPortPtr != SPort) { + continue; + } + + if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && *DestPortPtr != DPort) { + continue; + } + // + // check checksum + // + if (StatCode == EFI_SUCCESS && Hdrs.Udpv4Header.Checksum) { + Hdrs.Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0]; + Hdrs.Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0]; + Hdrs.Udpv4PseudoHeader.Zero = 0; + Hdrs.Udpv4PseudoHeader.Protocol = PROT_UDP; + Hdrs.Udpv4PseudoHeader.TotalLength = Hdrs.Udpv4Header.TotalLength; + + if (Hdrs.Udpv4Header.Checksum == 0xffff) { + Hdrs.Udpv4Header.Checksum = 0; + } + + if (IpChecksum2 ( + (UINT16 *) &Hdrs.Udpv4PseudoHeader, + HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader) + sizeof (Hdrs.Udpv4Header), + (UINT16 *) BufferPtr, + *BufferSizeptr + )) { + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Hdrs.Udpv4PseudoHeader == %Xh", + Hdrs.Udpv4PseudoHeader) + ); + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Header size == %d", + HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader)) + ); + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() BufferPtr == %Xh", + BufferPtr) + ); + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Buffer size == %d", + *BufferSizeptr) + ); + DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #2 Device Error")); + return EFI_DEVICE_ERROR; + } + } + // + // all passed + // + if (SrcPortPtr != NULL) { + *SrcPortPtr = SPort; + } + + if (DestPortPtr != NULL) { + *DestPortPtr = DPort; + } + + if (HeaderSize != 0) { + CopyMem (HeaderPtr, Hdrs.ProtHdr, HeaderSize); + } + } + + if ((StatCode != EFI_SUCCESS) && (StatCode != EFI_TIMEOUT)) { + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Exit #3 %Xh %r", + StatCode, + StatCode) + ); + } + + return StatCode; + } +} +// +// ////////////////////////////////////////////////////////// +// +// BC Udp Read Routine +// + +/** + + @return EFI_SUCCESS := + @return other := + +**/ +EFI_STATUS +EFIAPI +BcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_READ; + + // + // Issue BC command + // + StatCode = UdpRead ( + Private, + OpFlags, + DestIp, + DestPort, + SrcIp, + SrcPort, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr, + 0 + ); + + // + // Unlock the instance data and return + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* eof - pxe_bc_udp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c new file mode 100644 index 0000000000..7c5d0bc2fb --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c @@ -0,0 +1,1614 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_loadfile.c + +Abstract: + An implementation of the load file protocol for network devices. + + +**/ + + +#include "Bc.h" + +#define DO_MENU (EFI_SUCCESS) +#define NO_MENU (DO_MENU + 1) +#define LOCAL_BOOT (EFI_ABORTED) +#define AUTO_SELECT (NO_MENU) + +#define NUMBER_ROWS 25 // we set to mode 0 +#define MAX_MENULIST 23 + +#define Ctl(x) (0x1F & (x)) + +typedef union { + DHCPV4_OP_STRUCT *OpPtr; + PXE_BOOT_MENU_ENTRY *CurrentMenuItemPtr; + PXE_OP_DISCOVERY_CONTROL *DiscCtlOpStr; + PXE_OP_BOOT_MENU *MenuPtr; + UINT8 *BytePtr; +} UNION_PTR; + + + +/** + PxeBc callback routine for status updates and aborts. + + @param This Pointer to PxeBcCallback + interface + @param Function PxeBc function ID# + @param Received Receive/transmit flag + @param PacketLength Length of received packet (0 + == idle callback) + @param PacketPtr Pointer to received packet + (NULL == idle callback) + + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE + EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT + - + +**/ +STATIC +EFI_PXE_BASE_CODE_CALLBACK_STATUS +EFIAPI +bc_callback ( + IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL * This, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN BOOLEAN Received, + IN UINT32 PacketLength, + IN EFI_PXE_BASE_CODE_PACKET * PacketPtr OPTIONAL + ) +{ + STATIC UINTN Propeller; + + EFI_INPUT_KEY Key; + UINTN Row; + UINTN Col; + + Propeller = 0; + // + // Resolve Warning 4 unreferenced parameter problem + // + This = This; + + // + // Check for user abort. + // + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) { + if (!Key.ScanCode) { + if (Key.UnicodeChar == Ctl ('c')) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } else if (Key.ScanCode == SCAN_ESC) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } + // + // Do nothing if this is a receive. + // + if (Received) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // The display code is only for these functions. + // + switch (Function) { + case EFI_PXE_BASE_CODE_FUNCTION_MTFTP: + // + // If this is a transmit and not a M/TFTP open request, + // return now. Do not print a dot for each M/TFTP packet + // that is sent, only for the open packets. + // + if (PacketLength != 0 && PacketPtr != NULL) { + if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + } + + break; + + case EFI_PXE_BASE_CODE_FUNCTION_DHCP: + case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER: + break; + + default: + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // Display routines + // + if (PacketLength != 0 && PacketPtr != NULL) { + // + // Display a '.' when a packet is transmitted. + // + AsciiPrint ("."); + } else if (PacketLength == 0 && PacketPtr == NULL) { + // + // Display a propeller when waiting for packets if at + // least 200 ms have passed. + // + Row = gST->ConOut->Mode->CursorRow; + Col = gST->ConOut->Mode->CursorColumn; + + AsciiPrint ("%c", "/-\\|"[Propeller]); + gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row); + + Propeller = (Propeller + 1) & 3; + } + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; +} + +STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback = { + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION, + &bc_callback +}; + + +/** + Display an IPv4 address in dot notation. + + @param Ptr Pointer to IPv4 address. + + @return None + +**/ +STATIC +VOID +PrintIpv4 ( + UINT8 *Ptr + ) +{ + if (Ptr != NULL) { + AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]); + } +} + + +/** + Display client and server IP information. + + @param Private Pointer to PxeBc interface + + @return None + +**/ +STATIC +VOID +ShowMyInfo ( + IN PXE_BASECODE_DEVICE *Private + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + // + // Do nothing if a NULL pointer is passed in. + // + if (Private == NULL) { + return ; + } + // + // Get pointer to PXE BaseCode mode structure + // + PxeBcMode = Private->EfiBc.Mode; + + // + // Display client IP address + // + AsciiPrint ("\rCLIENT IP: "); + PrintIpv4 (PxeBcMode->StationIp.v4.Addr); + + // + // Display subnet mask + // + AsciiPrint (" MASK: "); + PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr); + + // + // Display DHCP and proxyDHCP IP addresses + // + if (PxeBcMode->ProxyOfferReceived) { + AsciiPrint ("\nDHCP IP: "); + PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr); + + AsciiPrint (" PROXY IP: "); + PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr); + } else { + AsciiPrint (" DHCP IP: "); + PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr); + } + // + // Display gateway IP addresses + // + for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) { + if ((Index % 3) == 0) { + AsciiPrint ("\r\nGATEWAY IP:"); + } + + AsciiPrint (" "); + PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr); + AsciiPrint (" "); + } + + AsciiPrint ("\n"); +} + + +/** + Display prompt and wait for input. + + @param Private Pointer to PxeBc interface + @param BootPromptPtr Pointer to PXE boot prompt + option + + @retval AUTO_SELECT DO_MENU - + @retval NO_MENU + @retval LOCAL_BOOT + +**/ +STATIC +EFI_STATUS +DoPrompt ( + PXE_BASECODE_DEVICE *Private, + PXE_OP_BOOT_PROMPT *BootPromptPtr + ) +{ + EFI_STATUS Status; + EFI_EVENT TimeoutEvent; + EFI_EVENT SecondsEvent; + INT32 SecColumn; + INT32 SecRow; + UINT8 SaveChar; + UINT8 SecsLeft; + + // + // if auto select, just get right to it + // + if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) { + return AUTO_SELECT; + } + // + // if no timeout, go directly to display of menu + // + if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) { + return DO_MENU; + } + // + // + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + return DO_MENU; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + BootPromptPtr->Timeout * 10000000 + 100000 + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimeoutEvent); + return DO_MENU; + } + // + // + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &SecondsEvent + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimeoutEvent); + return DO_MENU; + } + + Status = gBS->SetTimer ( + SecondsEvent, + TimerPeriodic, + 10000000 + ); /* 1 second */ + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (SecondsEvent); + gBS->CloseEvent (TimeoutEvent); + return DO_MENU; + } + // + // display the prompt + // IMPORTANT! This prompt is an ASCII character string that may + // not be terminated with a NULL byte. + // + SaveChar = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1]; + BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0; + + AsciiPrint ("%a ", BootPromptPtr->Prompt); + BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar; + + // + // wait until time expires or selection made - menu or local + // + SecColumn = gST->ConOut->Mode->CursorColumn; + SecRow = gST->ConOut->Mode->CursorRow; + SecsLeft = BootPromptPtr->Timeout; + + gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow); + AsciiPrint ("(%d) ", SecsLeft); + + // + // set the default action to be AUTO_SELECT + // + Status = AUTO_SELECT; + + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + EFI_INPUT_KEY Key; + + if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) { + --SecsLeft; + gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow); + AsciiPrint ("(%d) ", SecsLeft); + } + + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) { + UINT8 Buffer[512]; + UINTN BufferSize; + EFI_STATUS Status; + + BufferSize = sizeof Buffer; + + Status = Private->EfiBc.UdpRead ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT, + NULL, /* dest ip */ + NULL, /* dest port */ + NULL, /* src ip */ + NULL, /* src port */ + NULL, /* hdr size */ + NULL, /* hdr ptr */ + &BufferSize, + Buffer + ); + + continue; + } + + if (Key.ScanCode == 0) { + switch (Key.UnicodeChar) { + case Ctl ('c'): + Status = LOCAL_BOOT; + break; + + case Ctl ('m'): + case 'm': + case 'M': + Status = DO_MENU; + break; + + default: + continue; + } + } else { + switch (Key.ScanCode) { + case SCAN_F8: + Status = DO_MENU; + break; + + case SCAN_ESC: + Status = LOCAL_BOOT; + break; + + default: + continue; + } + } + + break; + } + + gBS->CloseEvent (SecondsEvent); + gBS->CloseEvent (TimeoutEvent); + + gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow); + AsciiPrint (" "); + + return Status; +} + + +/** + Display one menu item. + + @param MenuItemPtr Pointer to PXE menu item + option. + + @return None + +**/ +STATIC +VOID +PrintMenuItem ( + PXE_BOOT_MENU_ENTRY *MenuItemPtr + ) +{ + UINT8 Length; + UINT8 SaveChar; + + Length = (UINT8) MIN (70, MenuItemPtr->DataLen); + SaveChar = MenuItemPtr->Data[Length]; + + MenuItemPtr->Data[Length] = 0; + AsciiPrint (" %a\n", MenuItemPtr->Data); + MenuItemPtr->Data[Length] = SaveChar; +} + + +/** + Display and process menu. + + @param Private Pointer to PxeBc interface + @param RxBufferPtr Pointer to receive buffer + + @retval NO_MENU + @retval LOCAL_BOOT + +**/ +STATIC +EFI_STATUS +DoMenu ( + PXE_BASECODE_DEVICE *Private, + DHCP_RECEIVE_BUFFER *RxBufferPtr + ) +{ + PXE_OP_DISCOVERY_CONTROL *DiscoveryControlPtr; + PXE_BOOT_MENU_ENTRY *MenuItemPtrs[MAX_MENULIST]; + EFI_STATUS Status; + UNION_PTR Ptr; + UINTN SaveNumRte; + UINTN TopRow; + UINTN MenuLth; + UINTN NumMenuItems; + UINTN Index; + UINTN Longest; + UINTN Selected; + UINT16 Type; + UINT16 Layer; + BOOLEAN Done; + + Selected = 0; + Layer = 0; + + DEBUG ((DEBUG_WARN, "\nDoMenu() Enter.")); + + /* see if we have a menu/prompt */ + if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) { + DEBUG ( + (DEBUG_WARN, + "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ", + RxBufferPtr->OpAdds.Status) + ); + + return NO_MENU; + } + + DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1]; + + // + // if not USE_BOOTFILE or no bootfile given, must have menu stuff + // + if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + DEBUG ((DEBUG_WARN, "\nDoMenu() DHCP w/ bootfile. ")); + return NO_MENU; + } + // + // do prompt & menu if necessary + // + Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]); + + if (Status == LOCAL_BOOT) { + DEBUG ((DEBUG_WARN, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. ")); + + return Status; + } + + Ptr.BytePtr = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1]; + + MenuLth = Ptr.MenuPtr->Header.Length; + Ptr.CurrentMenuItemPtr = Ptr.MenuPtr->MenuItem; + + // + // build menu items array + // + for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) { + UINTN lth; + + lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data); + + MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr; + + if (lth > Longest) { + // + // check if too long + // + if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) { + Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data)); + } + } + + Index += lth; + Ptr.BytePtr += lth; + } + + if (Status != AUTO_SELECT) { + UINT8 BlankBuf[75]; + + SetMem (BlankBuf, sizeof BlankBuf, ' '); + BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0; + AsciiPrint ("\n"); + + // + // now put up menu + // + for (Index = 0; Index < NumMenuItems; ++Index) { + PrintMenuItem (MenuItemPtrs[Index]); + } + + TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems; + + // + // now wait for a selection + // + Done = FALSE; + do { + // + // highlight selection + // + EFI_INPUT_KEY Key; + UINTN NewSelected; + + NewSelected = Selected; + + // + // highlight selected row + // + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY) + ); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected); + + AsciiPrint (" --->%a\r", BlankBuf); + + PrintMenuItem (MenuItemPtrs[Selected]); + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK) + ); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems); + + // + // wait for a keystroke + // + while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) { + UINT8 TmpBuf[512]; + UINTN TmpBufLen; + + TmpBufLen = sizeof TmpBuf; + + Private->EfiBc.UdpRead ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT, + NULL, /* dest ip */ + NULL, /* dest port */ + NULL, /* src ip */ + NULL, /* src port */ + NULL, /* hdr size */ + NULL, /* hdr ptr */ + &TmpBufLen, + TmpBuf + ); + } + + if (!Key.ScanCode) { + switch (Key.UnicodeChar) { + case Ctl ('c'): + Key.ScanCode = SCAN_ESC; + break; + + case Ctl ('j'): /* linefeed */ + case Ctl ('m'): /* return */ + Done = TRUE; + break; + + case Ctl ('i'): /* tab */ + case ' ': + case 'd': + case 'D': + Key.ScanCode = SCAN_DOWN; + break; + + case Ctl ('h'): /* backspace */ + case 'u': + case 'U': + Key.ScanCode = SCAN_UP; + break; + + default: + Key.ScanCode = 0; + } + } + + switch (Key.ScanCode) { + case SCAN_LEFT: + case SCAN_UP: + if (NewSelected) { + --NewSelected; + } + + break; + + case SCAN_DOWN: + case SCAN_RIGHT: + if (++NewSelected == NumMenuItems) { + --NewSelected; + } + + break; + + case SCAN_PAGE_UP: + case SCAN_HOME: + NewSelected = 0; + break; + + case SCAN_PAGE_DOWN: + case SCAN_END: + NewSelected = NumMenuItems - 1; + break; + + case SCAN_ESC: + return LOCAL_BOOT; + } + + /* unhighlight last selected row */ + gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected); + + AsciiPrint ("%a\r", BlankBuf); + + PrintMenuItem (MenuItemPtrs[Selected]); + + Selected = NewSelected; + } while (!Done); + } + + SaveNumRte = Private->EfiBc.Mode->RouteTableEntries; + + Type = NTOHS (MenuItemPtrs[Selected]->Type); + + if (Type == 0) { + DEBUG ((DEBUG_WARN, "\nDoMenu() Local boot selected. ")); + return LOCAL_BOOT; + } + + AsciiPrint ("Discover"); + + Status = Private->EfiBc.Discover ( + &Private->EfiBc, + Type, + &Layer, + (BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected), + 0 + ); + + if (EFI_ERROR (Status)) { + AsciiPrint ("\r \r"); + + DEBUG ( + (DEBUG_WARN, + "\nDoMenu() Return w/ %xh (%r).", + Status, + Status) + ); + + return Status; + } + + AsciiPrint ("\rBOOT_SERVER_IP: "); + PrintIpv4 ((UINT8 *) &Private->ServerIp); + + for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) { + if ((Index % 3) == 0) { + AsciiPrint ("\r\nGATEWAY IP:"); + } + + AsciiPrint (" "); + PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr); + AsciiPrint (" "); + } + + AsciiPrint ("\n"); + + DEBUG ((DEBUG_WARN, "\nDoMenu() Return w/ EFI_SUCCESS. ")); + + return EFI_SUCCESS; +} + + +/** + Get value 8- or 16-bit value from DHCP option. + + @param OpPtr Pointer to DHCP option + + @return Value from DHCP option + +**/ +STATIC +UINT16 +GetValue ( + DHCPV4_OP_STRUCT *OpPtr + ) +{ + if (OpPtr->Header.Length == 1) { + return OpPtr->Data[0]; + } else { + return NTOHS (OpPtr->Data); + } +} + + +/** + Locate opcode in buffer. + + @param BufferPtr Pointer to buffer + @param BufferLen Length of buffer + @param OpCode Option number + + @return Pointer to opcode, may be NULL + +**/ +STATIC +UINT8 * +_PxeBcFindOpt ( + UINT8 *BufferPtr, + UINTN BufferLen, + UINT8 OpCode + ) +{ + if (BufferPtr == NULL) { + return NULL; + } + + while (BufferLen != 0) { + if (*BufferPtr == OpCode) { + return BufferPtr; + } + + switch (*BufferPtr) { + case OP_END: + return NULL; + + case OP_PAD: + ++BufferPtr; + --BufferLen; + continue; + } + + if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) { + return NULL; + } + + BufferLen -= 2 + BufferPtr[1]; + BufferPtr += 2 + BufferPtr[1]; + } + + return NULL; +} + + +/** + Find option in packet + + @param PacketPtr Pointer to packet + @param OpCode option number + + @return Pointer to option in packet + +**/ +STATIC +UINT8 * +PxeBcFindDhcpOpt ( + EFI_PXE_BASE_CODE_PACKET *PacketPtr, + UINT8 OpCode + ) +{ + UINTN PacketLen; + UINT8 Overload; + UINT8 *OptionBufferPtr; + + // + // + // + PacketLen = 380; + Overload = 0; + + // + // Figure size of DHCP option space. + // + OptionBufferPtr = _PxeBcFindOpt ( + PacketPtr->Dhcpv4.DhcpOptions, + 380, + OP_DHCP_MAX_MESSAGE_SZ + ); + + if (OptionBufferPtr != NULL) { + if (OptionBufferPtr[1] == 2) { + UINT16 n; + + CopyMem (&n, &OptionBufferPtr[2], 2); + PacketLen = HTONS (n); + + if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) { + PacketLen = 380; + } else { + PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28; + } + } + } + // + // Look for option overloading. + // + OptionBufferPtr = _PxeBcFindOpt ( + PacketPtr->Dhcpv4.DhcpOptions, + PacketLen, + OP_DHCP_OPTION_OVERLOAD + ); + + if (OptionBufferPtr != NULL) { + if (OptionBufferPtr[1] == 1) { + Overload = OptionBufferPtr[2]; + } + } + // + // Look for caller's option. + // + OptionBufferPtr = _PxeBcFindOpt ( + PacketPtr->Dhcpv4.DhcpOptions, + PacketLen, + OpCode + ); + + if (OptionBufferPtr != NULL) { + return OptionBufferPtr; + } + + if (Overload & OVLD_FILE) { + OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode); + + if (OptionBufferPtr != NULL) { + return OptionBufferPtr; + } + } + + if (Overload & OVLD_SRVR_NAME) { + OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode); + + if (OptionBufferPtr != NULL) { + return OptionBufferPtr; + } + } + + return NULL; +} + + +/** + Download file into buffer + + @param Private Pointer to PxeBc interface + @param BufferSize pointer to size of download + buffer + @param Buffer Pointer to buffer + + @return EFI_BUFFER_TOO_SMALL - + @return EFI_NOT_FOUND - + @return EFI_PROTOCOL_ERROR - + +**/ +STATIC +EFI_STATUS +DownloadFile ( + IN PXE_BASECODE_DEVICE *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer + ) +{ + EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo; + EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode; + DHCP_RECEIVE_BUFFER *RxBuf; + EFI_STATUS Status; + UINTN BlockSize; + + RxBuf = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer; + BlockSize = 0x8000; + + DEBUG ((EFI_D_WARN, "\nDownloadFile() Enter.")); + + if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) { + if (Private->FileSize != 0) { + *BufferSize = Private->FileSize; + return EFI_BUFFER_TOO_SMALL; + } + + AsciiPrint ("\nTSize"); + + OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE; + } else if (RxBuf->OpAdds.Status & WfM11a_TYPE) { + OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE; + + ZeroMem (&MtftpInfo, sizeof MtftpInfo); + + *(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data; + + CopyMem ( + &MtftpInfo.CPort, + RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data, + sizeof MtftpInfo.CPort + ); + + CopyMem ( + &MtftpInfo.SPort, + RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data, + sizeof MtftpInfo.SPort + ); + + MtftpInfo.ListenTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]); + + MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]); + + AsciiPrint ("\nMTFTP"); + } else { + AsciiPrint ("\nTFTP"); + + OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE; + } + + Private->FileSize = 0; + + RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0; + + Status = Private->EfiBc.Mtftp ( + &Private->EfiBc, + OpCode, + Buffer, + FALSE, + BufferSize, + &BlockSize, + &Private->ServerIp, + (UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data, + &MtftpInfo, + FALSE + ); + + if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #1 %Xh", Status)); + return Status; + } + + if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) { + Private->FileSize = 0xFFFFFFFF; + } else { + Private->FileSize = (UINTN) *BufferSize; + } + + if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) { + DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #2")); + return EFI_BUFFER_TOO_SMALL; + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #3 %Xh", Status)); + return Status; + } + + if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) { + UINT64 CredentialLen; + UINTN BlockSize; + UINT8 CredentialFilename[256]; + UINT8 *op; + VOID *CredentialBuffer; + + // + // Get name of credential file. It may be in the BOOTP + // bootfile field or a DHCP option. + // + ZeroMem (CredentialFilename, sizeof CredentialFilename); + + op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE); + + if (op != NULL) { + if (op[1] == 0) { + /* No credential filename */ + return EFI_NOT_FOUND; + } + + CopyMem (CredentialFilename, &op[2], op[1]); + } else { + if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) { + /* No credential filename */ + return EFI_NOT_FOUND; + } + + CopyMem (CredentialFilename, &op[2], 128); + } + // + // Get size of credential file. It may be available as a + // DHCP option. If not, use the TFTP get file size. + // + CredentialLen = 0; + + op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ); + + if (op != NULL) { + /* + * This is actually the size of the credential file + * buffer. The actual credential file size will be + * returned when we download the file. + */ + if (op[1] == 2) { + UINT16 n; + + CopyMem (&n, &op[2], 2); + CredentialLen = HTONS (n) * 512; + } + } + + if (CredentialLen == 0) { + BlockSize = 8192; + + Status = Private->EfiBc.Mtftp ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + &CredentialLen, + &BlockSize, + &Private->ServerIp, + CredentialFilename, + NULL, + FALSE + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (CredentialLen == 0) { + // + // %%TBD -- EFI error for invalid credential + // file. + // + return EFI_PROTOCOL_ERROR; + } + } + // + // Allocate credential file buffer. + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + (UINTN) CredentialLen, + &CredentialBuffer + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Download credential file. + // + BlockSize = 8192; + + Status = Private->EfiBc.Mtftp ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + CredentialBuffer, + FALSE, + &CredentialLen, + &BlockSize, + &Private->ServerIp, + CredentialFilename, + NULL, + FALSE + ); + + if (EFI_ERROR (Status)) { + gBS->FreePool (CredentialBuffer); + return Status; + } + // + // Verify credentials. + // + if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) { + Status = EFI_SUCCESS; + } else { + // + // %%TBD -- An EFI error code for failing credential verification. + // + Status = EFI_PROTOCOL_ERROR; + } + + gBS->FreePool (CredentialBuffer); + } + + return Status; +} + + +/** + Start PXE DHCP. Get DHCP and proxyDHCP information. + Display remote boot menu and prompt. Select item from menu. + + @param Private Pointer to PxeBc interface + @param BufferSize Pointer to download buffer + size + @param Buffer Pointer to download buffer + + @retval EFI_SUCCESS + @retval EFI_NOT_READY + +**/ +STATIC +EFI_STATUS +LoadfileStart ( + IN PXE_BASECODE_DEVICE *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_STATUS Status; + VOID *RxBuf; + + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Enter.")); + + // + // Try to start BaseCode, for now only IPv4 is supported + // so don't try to start using IPv6. + // + Status = Private->EfiBc.Start (&Private->EfiBc, FALSE); + + if (EFI_ERROR (Status)) { + if (Status != EFI_ALREADY_STARTED) { + DEBUG ((DEBUG_NET, "\nLoadfileStart() Exit BC.Start() == %xh", Status)); + return Status; + } + } + // + // Get pointers to PXE mode structure, SNP protocol structure + // and SNP mode structure. + // + PxeBcMode = Private->EfiBc.Mode; + Snp = Private->SimpleNetwork; + SnpMode = Snp->Mode; + + // + // Display client MAC address, like 16-bit PXE ROMs + // + AsciiPrint ("\nCLIENT MAC ADDR: "); + + { + UINTN Index; + UINTN hlen; + + hlen = SnpMode->HwAddressSize; + + for (Index = 0; Index < hlen; ++Index) { + AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]); + } + } + + AsciiPrint ("\nDHCP"); + + Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status)); + AsciiPrint ("\r \r"); + return Status; + } + + ShowMyInfo (Private); + + RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER; +#define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf) + + Status = DoMenu (Private, RxBufferPtr); + + if (Status == EFI_SUCCESS) { + // + // did a discovery - take info from discovery packet + // + RxBuf = &PXE_ACK_BUFFER; + } else if (Status == NO_MENU) { + // + // did not do a discovery - take info from rxbuf + // + Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr; + + if (!(Private->ServerIp.Addr[0])) { + *(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data; + } + } else { + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit DoMenu() == %Xh", Status)); + return Status; + } + + if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit Not ready?")); + return EFI_NOT_READY; + } + // + // check for file size option sent + // + if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) { + Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data); + } + + Private->BootServerReceiveBuffer = RxBufferPtr; + + Status = DownloadFile (Private, BufferSize, Buffer); + + DEBUG ( + (DEBUG_WARN, + "\nLoadfileStart() Exit. DownloadFile() = %Xh", + Status) + ); + + return Status; +} + + +/** + Loadfile interface for PxeBc interface + + @param This Pointer to Loadfile interface + @param FilePath Not used and not checked + @param BootPolicy Must be TRUE + @param BufferSize Pointer to buffer size + @param Buffer Pointer to download buffer or + NULL + + @return EFI_INVALID_PARAMETER - + @return EFI_UNSUPPORTED - + @return EFI_SUCCESS - + @return EFI_BUFFER_TOO_SMALL - + +**/ +EFI_STATUS +EFIAPI +LoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ) +{ + LOADFILE_DEVICE *LoadfilePtr; + UINT64 TmpBufSz; + INT32 OrigMode; + INT32 OrigAttribute; + BOOLEAN RemoveCallback; + BOOLEAN NewMakeCallback; + EFI_STATUS Status; + EFI_STATUS TempStatus; + // + // + // + OrigMode = gST->ConOut->Mode->Mode; + OrigAttribute = gST->ConOut->Mode->Attribute; + RemoveCallback = FALSE; + + AsciiPrint ("Running LoadFile()\n"); + + // + // Resolve Warning 4 unreferenced parameter problem + // + FilePath = NULL; + + // + // If either if these parameters are NULL, we cannot continue. + // + if (This == NULL || BufferSize == NULL) { + DEBUG ((DEBUG_WARN, "\nLoadFile() This or BufferSize == NULL")); + return EFI_INVALID_PARAMETER; + } + // + // We only support BootPolicy == TRUE + // + if (!BootPolicy) { + DEBUG ((DEBUG_WARN, "\nLoadFile() BootPolicy == FALSE")); + return EFI_UNSUPPORTED; + } + // + // Get pointer to LoadFile protocol structure. + // + LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE); + + if (LoadfilePtr == NULL) { + DEBUG ( + (DEBUG_NET, + "\nLoadFile() Could not get pointer to LoadFile structure") + ); + return EFI_INVALID_PARAMETER; + } + // + // Lock interface + // + EfiAcquireLock (&LoadfilePtr->Lock); + + // + // Set console output mode and display attribute + // + if (OrigMode != 0) { + gST->ConOut->SetMode (gST->ConOut, 0); + } + + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK) + ); + + // + // See if BaseCode already has a Callback protocol attached. + // If there is none, attach our own Callback protocol. + // + Status = gBS->HandleProtocol ( + LoadfilePtr->Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID *) &LoadfilePtr->Private->CallbackProtocolPtr + ); + + if (Status == EFI_SUCCESS) { + // + // There is already a callback routine. Do nothing. + // + DEBUG ((DEBUG_WARN, "\nLoadFile() BC callback exists.")); + + } else if (Status == EFI_UNSUPPORTED) { + // + // No BaseCode Callback protocol found. Add our own. + // + Status = gBS->InstallProtocolInterface ( + &LoadfilePtr->Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + EFI_NATIVE_INTERFACE, + &_bc_callback + ); + + DEBUG ((DEBUG_WARN, "\nLoadFile() Callback install status == %xh", Status)); + + RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS); + + if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) { + NewMakeCallback = TRUE; + LoadfilePtr->Private->EfiBc.SetParameters ( + &LoadfilePtr->Private->EfiBc, + NULL, + NULL, + NULL, + NULL, + &NewMakeCallback + ); + } + + } else { + DEBUG ((DEBUG_WARN, "\nLoadFile() Callback check status == %xh", Status)); + } + // + // Check for starting or for continuing after already getting + // the file size. + // + if (LoadfilePtr->Private->FileSize == 0) { + TmpBufSz = 0; + Status = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer); + + if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) { + *BufferSize = 0xFFFFFFFF; + } else { + *BufferSize = (UINTN) TmpBufSz; + } + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // This is done so loadfile will work even if the boot manager + // did not make the first call with Buffer == NULL. + // + Buffer = NULL; + } + } else if (Buffer == NULL) { + DEBUG ((DEBUG_WARN, "\nLoadfile() Get buffer size")); + + // + // Continuing from previous LoadFile request. Make sure there + // is a buffer and that it is big enough. + // + *BufferSize = LoadfilePtr->Private->FileSize; + Status = EFI_BUFFER_TOO_SMALL; + } else { + DEBUG ((DEBUG_WARN, "\nLoadFile() Download file")); + + // + // Everything looks good, try to download the file. + // + TmpBufSz = *BufferSize; + Status = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer); + + // + // Next call to loadfile will start DHCP process again. + // + LoadfilePtr->Private->FileSize = 0; + } + // + // If we added a callback protocol, now is the time to remove it. + // + if (RemoveCallback) { + NewMakeCallback = FALSE; + TempStatus = LoadfilePtr->Private->EfiBc.SetParameters ( + &LoadfilePtr->Private->EfiBc, + NULL, + NULL, + NULL, + NULL, + &NewMakeCallback + ); + + if (TempStatus == EFI_SUCCESS) { + gBS->UninstallProtocolInterface ( + LoadfilePtr->Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + &_bc_callback + ); + } + } + // + // Restore display mode and attribute + // + if (OrigMode != 0) { + gST->ConOut->SetMode (gST->ConOut, OrigMode); + } + + gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute); + + // + // Unlock interface + // + EfiReleaseLock (&LoadfilePtr->Lock); + + DEBUG ((DEBUG_WARN, "\nBC.Loadfile() Status == %xh\n", Status)); + + if (Status == EFI_SUCCESS) { + return EFI_SUCCESS; + + } else if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Error is only displayed when we are actually trying to + // download the boot image. + // + if (Buffer == NULL) { + return EFI_BUFFER_TOO_SMALL; + } + + AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n"); + + } else if (Status == EFI_DEVICE_ERROR) { + AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n"); + + } else if (Status == EFI_OUT_OF_RESOURCES) { + AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n"); + + } else if (Status == EFI_NO_MEDIA) { + AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n"); + + } else if (Status == EFI_NO_RESPONSE) { + AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n"); + + } else if (Status == EFI_TIMEOUT) { + AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n"); + + } else if (Status == EFI_ABORTED) { + AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n"); + + } else if (Status == EFI_ICMP_ERROR) { + AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n"); + + if (LoadfilePtr->Private->EfiBc.Mode != NULL) { + if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) { + + AsciiPrint ( + "PXE-E98: Type: %xh Code: %xh ", + LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type, + LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code + ); + + switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) { + case 0x03: + switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) { + case 0x00: /* net unreachable */ + AsciiPrint ("Net unreachable"); + break; + + case 0x01: /* host unreachable */ + AsciiPrint ("Host unreachable"); + break; + + case 0x02: /* protocol unreachable */ + AsciiPrint ("Protocol unreachable"); + break; + + case 0x03: /* port unreachable */ + AsciiPrint ("Port unreachable"); + break; + + case 0x04: /* Fragmentation needed */ + AsciiPrint ("Fragmentation needed"); + break; + + case 0x05: /* Source route failed */ + AsciiPrint ("Source route failed"); + break; + } + + break; + } + + AsciiPrint ("\n"); + } + } + + } else if (Status == EFI_TFTP_ERROR) { + AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n"); + + if (LoadfilePtr->Private->EfiBc.Mode != NULL) { + if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) { + AsciiPrint ( + "PXE-E98: Code: %xh %a\n", + LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode, + LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString + ); + } + } + + } else { + AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status); + } + + LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h new file mode 100644 index 0000000000..f81d86c163 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h @@ -0,0 +1,154 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + tftp.h + +Abstract: + + +**/ + +#ifndef __TFTP_H__ +#define __TFTP_H__ + +// +// Definitions for trivial file transfer protocol functionality with IP v4 +// Per RFC 1350, July 1992 and RFC 2347, 8, and 9, May 1998 +// +#pragma pack(1) +// +// max and min packet sizes +// (all data packets in transmission except last) +// +#define MAX_TFTP_PKT_SIZE (BUFFER_ALLOCATE_SIZE - 512) +#define MIN_TFTP_PKT_SIZE 512 + +// +// TFTPv4 OpCodes +// +#define TFTP_RRQ 1 // read request +#define TFTP_WRQ 2 // write request +#define TFTP_DATA 3 // data +#define TFTP_ACK 4 // acknowledgement +#define TFTP_ERROR 5 // error packet +#define TFTP_OACK 6 // option acknowledge +#define TFTP_DIR 7 // read directory request +#define TFTP_DATA8 8 +#define TFTP_ACK8 9 + +// +// request packet (read or write) +// Fields shown (except file name) are not to be referenced directly, +// since their placement is variable within a request packet. +// All are null terminated case insensitive ascii strings. +// +struct Tftpv4Req { + UINT16 OpCode; // TFTP Op code + UINT8 FileName[2]; // file name + UINT8 Mode[2]; // "netascii" or "octet" + struct { // optionally, one or more option requests + UINT8 Option[2]; // option name + UINT8 Value[2]; // value requested + } OpReq[1]; +}; + +// +// modes +// +#define MODE_ASCII "netascii" +#define MODE_BINARY "octet" + +// +// option strings +// +#define OP_BLKSIZE "blksize" // block size option +#define OP_TIMEOUT "timeout" // time to wait before retransmitting +#define OP_TFRSIZE "tsize" // total transfer size option +#define OP_OVERWRITE "overwrite" // overwrite file option +#define OP_BIGBLKNUM "bigblk#" // big block number +// See RFC 2347, 8, and 9 for more information on TFTP options +// option acknowledge packet (optional) +// options not acknowledged are rejected +// +struct Tftpv4Oack { + UINT16 OpCode; // TFTP Op code + struct { // optionally, one or more option acknowledgements + UINT8 Option[2]; // option name (of those requested) + UINT8 Value[2]; // value acknowledged + } OpAck[1]; +}; + +// +// acknowledge packet +// +struct Tftpv4Ack { + UINT16 OpCode; // TFTP Op code + UINT16 BlockNum; +}; + +// +// data packet +// +struct Tftpv4Data { + struct Tftpv4Ack Header; + UINT8 Data[512]; +}; + +// +// big block number ack packet +// +struct Tftpv4Ack8 { + UINT16 OpCode; + UINT64 BlockNum; +}; + +// +// big block number data packet +// +struct Tftpv4Data8 { + struct Tftpv4Ack8 Header; + UINT8 Data[506]; +}; + +// +// error packet +// +struct Tftpv4Error { + UINT16 OpCode; // TFTP Op code + UINT16 ErrCode; // error code + UINT8 ErrMsg[1]; // error message (nul terminated) +}; + +#pragma pack() +// +// error codes +// +#define TFTP_ERR_UNDEF 0 // Not defined, see error message (if any). +#define TFTP_ERR_NOT_FOUND 1 // File not found. +#define TFTP_ERR_ACCESS 2 // Access violation. +#define TFTP_ERR_FULL 3 // Disk full or allocation exceeded. +#define TFTP_ERR_ILLEGAL 4 // Illegal TFTP operation. +#define TFTP_ERR_BAD_ID 5 // Unknown transfer ID. +#define TFTP_ERR_EXISTS 6 // File already exists. +#define TFTP_ERR_NO_USER 7 // No such user. +#define TFTP_ERR_OPTION 8 // Option negotiation termination +// +// some defines +// +#define REQ_RESP_TIMEOUT 5 // Wait five seconds for request response. +#define ACK_TIMEOUT 4 // Wait four seconds for ack response. +#define NUM_ACK_RETRIES 3 +#define NUM_MTFTP_OPEN_RETRIES 3 + +#endif /* __TFTP_H__ */ + +/* EOF - tftp.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h new file mode 100644 index 0000000000..a8fa293a16 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h @@ -0,0 +1,26 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeArch.h + +Abstract: + Defines PXE Arch type + + +**/ + +#ifndef _EFI_PXE_ARCH_H_ +#define _EFI_PXE_ARCH_H_ + +#define SYS_ARCH 0x7 + +#endif -- cgit v1.2.3