From 2b0b5250bf957f4266a50307f9903e6f5e7e6557 Mon Sep 17 00:00:00 2001 From: "Wei, David" Date: Mon, 9 Jan 2017 10:05:42 +0800 Subject: Fix build error. Fix build error for latest VS2015 compiler with IA32 tip. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: lushifex Reviewed-by: zwei4 --- .../NetworkPkg/UefiPxeBcDxe/ComponentName.c | 358 +++ .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c | 1259 ++++++++++ .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h | 100 + .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c | 1672 ++++++++++++++ .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h | 409 ++++ .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c | 2096 +++++++++++++++++ .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h | 303 +++ .../NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c | 1825 +++++++++++++++ .../NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h | 181 ++ .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c | 2411 ++++++++++++++++++++ .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h | 225 ++ .../NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c | 1113 +++++++++ .../NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h | 137 ++ .../NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c | 1513 ++++++++++++ .../NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h | 515 +++++ .../NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf | 109 + .../NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni | Bin 0 -> 2416 bytes .../NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni | Bin 0 -> 1332 bytes 18 files changed, 14226 insertions(+) create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni (limited to 'Platform/BroxtonPlatformPkg/Common/SampleCode') diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c new file mode 100644 index 0000000000..6e48d4aa18 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c @@ -0,0 +1,358 @@ +/** @file + UEFI Component Name(2) protocol implementation for UefiPxeBc driver. + + Copyright (c) 2009 - 2012, 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. + +**/ + +#include "PxeBcImpl.h" + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = { + PxeBcComponentNameGetDriverName, + PxeBcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = { + { + "eng;en", + L"UEFI PXE Base Code Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcControllerNameTable[] = { + { + "eng;en", + L"PXE Controller" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2( + Language, + This->SupportedLanguages, + mPxeBcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPxeBcComponentName) + ); +} + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + PXEBC_PRIVATE_PROTOCOL *Id; + + if (ControllerHandle == NULL || ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + NicHandle = PxeBcGetNicByIp4Children (ControllerHandle); + if (NicHandle == NULL) { + NicHandle = PxeBcGetNicByIp6Children (ControllerHandle); + if (NicHandle == NULL) { + return EFI_UNSUPPORTED; + } + } + + // + // Try to retrieve the private data by PxeBcPrivate protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mPxeBcControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gPxeBcComponentName) + ); +} diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c new file mode 100644 index 0000000000..9628770413 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c @@ -0,0 +1,1259 @@ +/** @file + Boot functions implementation for UefiPxeBc Driver. + + Copyright (c) 2009 - 2017, 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. + +**/ + +#include "PxeBcImpl.h" + + +/** + Display the string of the boot item. + + If the length of the boot item string beyond 70 Char, just display 70 Char. + + @param[in] Str The pointer to the string. + @param[in] Len The length of the string. + +**/ +VOID +PxeBcDisplayBootItem ( + IN UINT8 *Str, + IN UINT8 Len + ) +{ + UINT8 Tmp; + + // + // Cut off the chars behind 70th. + // + Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len); + Tmp = Str[Len]; + Str[Len] = 0; + AsciiPrint ("%a \n", Str); + + // + // Restore the original 70th char. + // + Str[Len] = Tmp; +} + + +/** + Select and maintain the boot prompt if needed. + + @param[in] Private Pointer to PxeBc private data. + + @retval EFI_SUCCESS Selected boot prompt done. + @retval EFI_TIMEOUT Selected boot prompt timed out. + @retval EFI_NOT_FOUND The proxy offer is not Pxe10. + @retval EFI_ABORTED User cancelled the operation. + @retval EFI_NOT_READY Reading the input key from the keyboard has not finish. + +**/ +EFI_STATUS +PxeBcSelectBootPrompt ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP_PACKET_CACHE *Cache; + PXEBC_VENDOR_OPTION *VendorOpt; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_EVENT TimeoutEvent; + EFI_EVENT DescendEvent; + EFI_INPUT_KEY InputKey; + EFI_STATUS Status; + UINT32 OfferType; + UINT8 Timeout; + UINT8 *Prompt; + UINT8 PromptLen; + INT32 SecCol; + INT32 SecRow; + + TimeoutEvent = NULL; + DescendEvent = NULL; + Mode = Private->PxeBc.Mode; + Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck; + OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType; + + // + // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt. + // + if (OfferType != PxeOfferTypeProxyPxe10 && OfferType != PxeOfferTypeDhcpPxe10) { + return EFI_NOT_FOUND; + } + + // + // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec. + // + ASSERT (!Mode->UsingIpv6); + + VendorOpt = &Cache->Dhcp4.VendorOpt; + // + // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options, + // we must not consider a boot prompt or boot menu if all of the following hold: + // - the PXE_DISCOVERY_CONTROL tag(6) is present inside the Vendor Options(43), and has bit 3 set + // - a boot file name has been presented in the initial DHCP or ProxyDHCP offer packet. + // + if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && + Cache->Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + return EFI_ABORTED; + } + + if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) { + return EFI_TIMEOUT; + } + + Timeout = VendorOpt->MenuPrompt->Timeout; + Prompt = VendorOpt->MenuPrompt->Prompt; + PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1); + + // + // The valid scope of Timeout refers to PXE2.1 spec. + // + if (Timeout == 0) { + return EFI_TIMEOUT; + } + if (Timeout == 255) { + return EFI_SUCCESS; + } + + // + // Create and start a timer as timeout event. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + MultU64x32 (Timeout, TICKS_PER_SECOND) + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create and start a periodic timer as descend event by second. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &DescendEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + DescendEvent, + TimerPeriodic, + TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Display the boot item and cursor on the screen. + // + SecCol = gST->ConOut->Mode->CursorColumn; + SecRow = gST->ConOut->Mode->CursorRow; + + PxeBcDisplayBootItem (Prompt, PromptLen); + + gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow); + AsciiPrint ("(%d) ", Timeout--); + + Status = EFI_TIMEOUT; + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) { + gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow); + AsciiPrint ("(%d) ", Timeout--); + } + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) { + gBS->Stall (10 * TICKS_PER_MS); + continue; + } + // + // Parse the input key by user. + // If or + is pressed, return success to display the boot menu. + // + if (InputKey.ScanCode == 0) { + + switch (InputKey.UnicodeChar) { + + case CTRL ('c'): + Status = EFI_ABORTED; + break; + + case CTRL ('m'): + case 'm': + case 'M': + Status = EFI_SUCCESS; + break; + + default: + continue; + } + + } else { + + switch (InputKey.ScanCode) { + + case SCAN_F8: + Status = EFI_SUCCESS; + break; + + case SCAN_ESC: + Status = EFI_ABORTED; + break; + + default: + continue; + } + } + + break; + } + + // + // Reset the cursor on the screen. + // + gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1); + +ON_EXIT: + if (DescendEvent != NULL) { + gBS->CloseEvent (DescendEvent); + } + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + return Status; +} + + +/** + Select the boot menu by user's input. + + @param[in] Private Pointer to PxeBc private data. + @param[out] Type The type of the menu. + @param[in] UseDefaultItem Use default item or not. + + @retval EFI_ABORTED User cancel operation. + @retval EFI_SUCCESS Select the boot menu success. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcSelectBootMenu ( + IN PXEBC_PRIVATE_DATA *Private, + OUT UINT16 *Type, + IN BOOLEAN UseDefaultItem + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP_PACKET_CACHE *Cache; + PXEBC_VENDOR_OPTION *VendorOpt; + EFI_INPUT_KEY InputKey; + UINT32 OfferType; + UINT8 MenuSize; + UINT8 MenuNum; + INT32 TopRow; + UINT16 Select; + UINT16 LastSelect; + UINT8 Index; + BOOLEAN Finish; + CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE]; + PXEBC_BOOT_MENU_ENTRY *MenuItem; + PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM]; + + Finish = FALSE; + Select = 0; + Index = 0; + *Type = 0; + Mode = Private->PxeBc.Mode; + Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck; + OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType; + + // + // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec. + // + ASSERT (!Mode->UsingIpv6); + ASSERT (OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeDhcpPxe10); + + VendorOpt = &Cache->Dhcp4.VendorOpt; + if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) { + return EFI_SUCCESS; + } + + // + // Display the boot menu on the screen. + // + SetMem (Blank, sizeof(Blank), ' '); + + MenuSize = VendorOpt->BootMenuLen; + MenuItem = VendorOpt->BootMenu; + + if (MenuSize == 0) { + return EFI_DEVICE_ERROR; + } + + while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) { + ASSERT (MenuItem != NULL); + MenuArray[Index] = MenuItem; + MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3)); + MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3); + Index++; + } + + if (UseDefaultItem) { + ASSERT (MenuArray[0] != NULL); + CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16)); + *Type = NTOHS (*Type); + return EFI_SUCCESS; + } + + MenuNum = Index; + + for (Index = 0; Index < MenuNum; Index++) { + ASSERT (MenuArray[Index] != NULL); + PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen); + } + + TopRow = gST->ConOut->Mode->CursorRow - MenuNum; + + // + // Select the boot item by user in the boot menu. + // + do { + // + // Highlight selected row. + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select); + ASSERT (Select < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[Select] != NULL); + Blank[MenuArray[Select]->DescLen] = 0; + AsciiPrint ("%a\r", Blank); + PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum); + LastSelect = Select; + + while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) { + gBS->Stall (10 * TICKS_PER_MS); + } + + if (InputKey.ScanCode == 0) { + switch (InputKey.UnicodeChar) { + case CTRL ('c'): + InputKey.ScanCode = SCAN_ESC; + break; + + case CTRL ('j'): /* linefeed */ + case CTRL ('m'): /* return */ + Finish = TRUE; + break; + + case CTRL ('i'): /* tab */ + case ' ': + case 'd': + case 'D': + InputKey.ScanCode = SCAN_DOWN; + break; + + case CTRL ('h'): /* backspace */ + case 'u': + case 'U': + InputKey.ScanCode = SCAN_UP; + break; + + default: + InputKey.ScanCode = 0; + } + } + + switch (InputKey.ScanCode) { + case SCAN_LEFT: + case SCAN_UP: + if (Select != 0) { + Select--; + } + break; + + case SCAN_DOWN: + case SCAN_RIGHT: + if (++Select == MenuNum) { + Select--; + } + break; + + case SCAN_PAGE_UP: + case SCAN_HOME: + Select = 0; + break; + + case SCAN_PAGE_DOWN: + case SCAN_END: + Select = (UINT16) (MenuNum - 1); + break; + + case SCAN_ESC: + return EFI_ABORTED; + } + + // + // Unhighlight the last selected row. + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect); + ASSERT (LastSelect < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[LastSelect] != NULL); + Blank[MenuArray[LastSelect]->DescLen] = 0; + AsciiPrint ("%a\r", Blank); + PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum); + } while (!Finish); + + // + // Swap the byte order. + // + ASSERT (Select < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[Select] != NULL); + CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16)); + *Type = NTOHS (*Type); + + return EFI_SUCCESS; +} + + +/** + Parse out the boot information from the last Dhcp4 reply packet. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDhcp4BootInfo ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + UINT16 Value; + PXEBC_VENDOR_OPTION *VendorOpt; + PXEBC_BOOT_SVR_ENTRY *Entry; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Status = EFI_SUCCESS; + *BufferSize = 0; + + // + // Get the last received Dhcp4 reply packet. + // + if (Mode->PxeReplyReceived) { + Cache4 = &Private->PxeReply.Dhcp4; + } else if (Mode->ProxyOfferReceived) { + Cache4 = &Private->ProxyOffer.Dhcp4; + } else { + Cache4 = &Private->DhcpAck.Dhcp4; + } + + ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL); + + // + // Parse the boot server address. + // If prompt/discover is disabled, get the first boot server from the boot servers list. + // Otherwise, parse the boot server Ipv4 address from next server address field in DHCP header. + // If all these fields are not available, use option 54 instead. + // + VendorOpt = &Cache4->VendorOpt; + if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && IS_VALID_BOOT_SERVERS (VendorOpt->BitMap)) { + Entry = VendorOpt->BootSvr; + if (VendorOpt->BootSvrLen >= sizeof (PXEBC_BOOT_SVR_ENTRY) && Entry->IpCnt > 0) { + CopyMem ( + &Private->ServerIp, + &Entry->IpAddr[0], + sizeof (EFI_IPv4_ADDRESS) + ); + } + } + if (Private->ServerIp.Addr[0] == 0) { + // + // ServerIp.Addr[0] equals zero means we failed to get IP address from boot server list. + // Try to use next server address field. + // + CopyMem ( + &Private->ServerIp, + &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr, + sizeof (EFI_IPv4_ADDRESS) + ); + } + if (Private->ServerIp.Addr[0] == 0) { + // + // Still failed , use the IP address from option 54. + // + CopyMem ( + &Private->ServerIp, + Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + // + // Parse the boot file name by option. + // + Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data; + + if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) { + // + // Parse the boot file size by option. + // + CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value)); + Value = NTOHS (Value); + // + // The field of boot file size is 512 bytes in unit. + // + *BufferSize = 512 * Value; + } else { + // + // Get the bootfile size by tftp command if no option available. + // + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + + // + // Save the value of boot file size. + // + Private->BootFileSize = (UINTN) *BufferSize; + + // + // Display all the information: boot server address, boot file name and boot file size. + // + AsciiPrint ("\n Server IP address is "); + PxeBcShowIp4Addr (&Private->ServerIp.v4); + AsciiPrint ("\n NBP filename is %a", Private->BootFileName); + AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize); + + return Status; +} + + +/** + Parse out the boot information from the last Dhcp6 reply packet. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval EFI_BUFFER_TOO_SMALL + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDhcp6BootInfo ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + PXEBC_DHCP6_PACKET_CACHE *Cache6; + UINT16 Value; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Status = EFI_SUCCESS; + *BufferSize = 0; + + // + // Get the last received Dhcp6 reply packet. + // + if (Mode->PxeReplyReceived) { + Cache6 = &Private->PxeReply.Dhcp6; + } else if (Mode->ProxyOfferReceived) { + Cache6 = &Private->ProxyOffer.Dhcp6; + } else { + Cache6 = &Private->DhcpAck.Dhcp6; + } + + ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + + // + // Parse (m)tftp server ip address and bootfile name. + // + Status = PxeBcExtractBootFileUrl ( + &Private->BootFileName, + &Private->ServerIp.v6, + (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the station address to IP layer. + // + Status = PxeBcSetIp6Address (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the value of boot file size. + // + if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) { + // + // Parse it out if have the boot file parameter option. + // + Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + // + // The field of boot file size is 512 bytes in unit. + // + *BufferSize = 512 * Value; + } else { + // + // Send get file size command by tftp if option unavailable. + // + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + + // + // Save the value of boot file size. + // + Private->BootFileSize = (UINTN) *BufferSize; + + // + // Display all the information: boot server address, boot file name and boot file size. + // + AsciiPrint ("\n Server IP address is "); + PxeBcShowIp6Addr (&Private->ServerIp.v6); + AsciiPrint ("\n NBP filename is %a", Private->BootFileName); + AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize); + + return Status; +} + + +/** + Extract the discover information and boot server entry from the + cached packets if unspecified. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type The type of bootstrap to perform. + @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO. + @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY. + @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully extracted the information. + @retval EFI_DEVICE_ERROR Failed to extract the information. + +**/ +EFI_STATUS +PxeBcExtractDiscoverInfo ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo, + OUT PXEBC_BOOT_SVR_ENTRY **BootEntry, + OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + PXEBC_VENDOR_OPTION *VendorOpt; + PXEBC_BOOT_SVR_ENTRY *Entry; + BOOLEAN IsFound; + EFI_PXE_BASE_CODE_DISCOVER_INFO *Info; + UINT16 Index; + + Mode = Private->PxeBc.Mode; + Info = *DiscoverInfo; + + if (Mode->UsingIpv6) { + Info->IpCnt = 1; + Info->UseUCast = TRUE; + + Info->SrvList[0].Type = Type; + Info->SrvList[0].AcceptAnyResponse = FALSE; + + // + // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet. + // + CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + *SrvList = Info->SrvList; + } else { + Entry = NULL; + IsFound = FALSE; + Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4; + VendorOpt = &Cache4->VendorOpt; + + if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) { + // + // Address is not acquired or no discovery options. + // + return EFI_INVALID_PARAMETER; + } + + // + // Parse the boot server entry from the vendor option in the last cached packet. + // + Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl); + Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl); + Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl); + Info->UseUCast = (BOOLEAN) IS_VALID_BOOT_SERVERS (VendorOpt->BitMap); + + if (Info->UseMCast) { + // + // Get the multicast discover ip address from vendor option if has. + // + CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS)); + } + + Info->IpCnt = 0; + + if (Info->UseUCast) { + Entry = VendorOpt->BootSvr; + + while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) { + if (Entry->Type == HTONS (Type)) { + IsFound = TRUE; + break; + } + Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry); + } + + if (!IsFound) { + return EFI_DEVICE_ERROR; + } + + Info->IpCnt = Entry->IpCnt; + if (Info->IpCnt >= 1) { + *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList)); + if (*DiscoverInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (*DiscoverInfo, Info, sizeof (*Info)); + Info = *DiscoverInfo; + } + + for (Index = 0; Index < Info->IpCnt; Index++) { + CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS)); + Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList; + Info->SrvList[Index].Type = NTOHS (Entry->Type); + } + } + + *BootEntry = Entry; + *SrvList = Info->SrvList; + } + + return EFI_SUCCESS; +} + + +/** + Build the discover packet and send out for boot server. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the destination address. + @param[in] IpCount The count of the server address. + @param[in] SrvList Pointer to the server address list. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDiscoverBootServer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcDhcp6Discover ( + Private, + Type, + Layer, + UseBis, + DestIp + ); + } else { + return PxeBcDhcp4Discover ( + Private, + Type, + Layer, + UseBis, + DestIp, + IpCount, + SrvList + ); + } +} + + +/** + Discover all the boot information for boot file. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancel current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDiscoverBootFile ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + UINT16 Type; + UINT16 Layer; + BOOLEAN UseBis; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP; + Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL; + + // + // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and + // other pxe boot information. + // + Status = PxeBc->Dhcp (PxeBc, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select a boot server from boot server list. + // + Status = PxeBcSelectBootPrompt (Private); + + if (Status == EFI_SUCCESS) { + // + // Choose by user's input. + // + Status = PxeBcSelectBootMenu (Private, &Type, FALSE); + } else if (Status == EFI_TIMEOUT) { + // + // Choose by default item. + // + Status = PxeBcSelectBootMenu (Private, &Type, TRUE); + } + + if (!EFI_ERROR (Status)) { + + if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) { + // + // Local boot(PXE bootstrap server) need abort + // + return EFI_ABORTED; + } + + // + // Start to discover the boot server to get (m)tftp server ip address, bootfile + // name and bootfile size. + // + UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected); + Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) { + // + // Some network boot loader only search the packet in Mode.ProxyOffer to get its server + // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer. + // + if (Mode->UsingIpv6) { + CopyMem ( + &Mode->ProxyOffer.Dhcpv6, + &Mode->PxeReply.Dhcpv6, + Private->PxeReply.Dhcp6.Packet.Ack.Length + ); + } else { + CopyMem ( + &Mode->ProxyOffer.Dhcpv4, + &Mode->PxeReply.Dhcpv4, + Private->PxeReply.Dhcp4.Packet.Ack.Length + ); + } + Mode->ProxyOfferReceived = TRUE; + } + } + + // + // Parse the boot information. + // + if (Mode->UsingIpv6) { + Status = PxeBcDhcp6BootInfo (Private, BufferSize); + } else { + Status = PxeBcDhcp4BootInfo (Private, BufferSize); + } + + return Status; +} + + +/** + Install PxeBaseCodeCallbackProtocol if not installed before. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] NewMakeCallback If TRUE, it is a new callback. + Otherwise, it is not new callback. + @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully. + @retval Others Failed to install PxeBaseCodeCallbackProtocol. + +**/ +EFI_STATUS +PxeBcInstallCallback ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT BOOLEAN *NewMakeCallback + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_STATUS Status; + + // + // Check whether PxeBaseCodeCallbackProtocol already installed. + // + PxeBc = &Private->PxeBc; + Status = gBS->HandleProtocol ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID **) &Private->PxeBcCallback + ); + if (Status == EFI_UNSUPPORTED) { + + CopyMem ( + &Private->LoadFileCallback, + &gPxeBcCallBackTemplate, + sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL) + ); + + // + // Install a default callback if user didn't offer one. + // + Status = gBS->InstallProtocolInterface ( + &Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->LoadFileCallback + ); + + (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS); + + Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback); + if (EFI_ERROR (Status)) { + PxeBc->Stop (PxeBc); + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Uninstall PxeBaseCodeCallbackProtocol. + + @param[in] Private Pointer to PxeBc private data. + @param[in] NewMakeCallback If TRUE, it is a new callback. + Otherwise, it is not new callback. + +**/ +VOID +PxeBcUninstallCallback ( + IN PXEBC_PRIVATE_DATA *Private, + IN BOOLEAN NewMakeCallback + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + PxeBc = &Private->PxeBc; + + if (NewMakeCallback) { + + NewMakeCallback = FALSE; + + PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback); + + gBS->UninstallProtocolInterface ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + &Private->LoadFileCallback + ); + } +} + + +/** + Download one of boot file in the list, and it's special for IPv6. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Read one of boot file in the list successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_NOT_FOUND There is no proper boot file available. + @retval Others Failed to download boot file in the list. + +**/ +EFI_STATUS +PxeBcReadBootFileList ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + PxeBc = &Private->PxeBc; + + // + // Try to download the boot file if everything is ready. + // + if (Buffer != NULL) { + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + Buffer, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + + + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + return Status; +} + + +/** + Load boot file into user buffer. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Get all the boot information successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancelled the current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcLoadBootFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + BOOLEAN NewMakeCallback; + UINT64 RequiredSize; + UINT64 CurrentSize; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + + NewMakeCallback = FALSE; + PxeBc = &Private->PxeBc; + PxeBcMode = &Private->Mode; + CurrentSize = *BufferSize; + RequiredSize = 0; + + // + // Install pxebc callback protocol if hasn't been installed yet. + // + Status = PxeBcInstallCallback (Private, &NewMakeCallback); + if (EFI_ERROR(Status)) { + return Status; + } + + if (Private->BootFileSize == 0) { + // + // Discover the boot information about the bootfile if hasn't. + // + Status = PxeBcDiscoverBootFile (Private, &RequiredSize); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) { + // + // It's error if the required buffer size is beyond the system scope. + // + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } else if (RequiredSize > 0) { + // + // Get the right buffer size of the bootfile required. + // + if (CurrentSize < RequiredSize || Buffer == NULL) { + // + // It's buffer too small if the size of user buffer is smaller than the required. + // + CurrentSize = RequiredSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + CurrentSize = RequiredSize; + } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) { + // + // Try to download another bootfile in list if failed to get the filesize of the last one. + // It's special for the case of IPv6. + // + Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer); + goto ON_EXIT; + } + } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) { + // + // It's buffer too small if the size of user buffer is smaller than the required. + // + CurrentSize = Private->BootFileSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + // + // Begin to download the bootfile if everything is ready. + // + AsciiPrint ("\n Downloading NBP file...\n"); + if (PxeBcMode->UsingIpv6) { + Status = PxeBcReadBootFileList ( + Private, + &CurrentSize, + Buffer + ); + } else { + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + Buffer, + FALSE, + &CurrentSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + +ON_EXIT: + *BufferSize = (UINTN) CurrentSize; + PxeBcUninstallCallback(Private, NewMakeCallback); + + if (Status == EFI_SUCCESS) { + AsciiPrint ("\n Succeed to download NBP file.\n"); + return EFI_SUCCESS; + } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) { + AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n"); + } else if (Status == EFI_DEVICE_ERROR) { + AsciiPrint ("\n PXE-E07: Network device error.\n"); + } else if (Status == EFI_OUT_OF_RESOURCES) { + AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n"); + } else if (Status == EFI_NO_MEDIA) { + AsciiPrint ("\n PXE-E12: Could not detect network connection.\n"); + } else if (Status == EFI_NO_RESPONSE) { + AsciiPrint ("\n PXE-E16: No offer received.\n"); + } else if (Status == EFI_TIMEOUT) { + AsciiPrint ("\n PXE-E18: Server response timeout.\n"); + } else if (Status == EFI_ABORTED) { + AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n"); + } else if (Status == EFI_ICMP_ERROR) { + AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n"); + } else if (Status == EFI_TFTP_ERROR) { + AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n"); + } else if (Status == EFI_NOT_FOUND) { + AsciiPrint ("\n PXE-E53: No boot filename received.\n"); + } else if (Status != EFI_BUFFER_TOO_SMALL) { + AsciiPrint ("\n PXE-E99: Unexpected network error.\n"); + } + + return Status; +} + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h new file mode 100644 index 0000000000..d998200ce0 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h @@ -0,0 +1,100 @@ +/** @file + Boot functions declaration for UefiPxeBc Driver. + + Copyright (c) 2009 - 2012, 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 __EFI_PXEBC_BOOT_H__ +#define __EFI_PXEBC_BOOT_H__ + +#define PXEBC_DISPLAY_MAX_LINE 70 +#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE 8 +#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE 4 + +#define PXEBC_IS_SIZE_OVERFLOWED(x) ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF)) + + +/** + Extract the discover information and boot server entry from the + cached packets if unspecified. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type The type of bootstrap to perform. + @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO. + @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY. + @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully extracted the information. + @retval EFI_DEVICE_ERROR Failed to extract the information. + +**/ +EFI_STATUS +PxeBcExtractDiscoverInfo ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo, + OUT PXEBC_BOOT_SVR_ENTRY **BootEntry, + OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList + ); + + +/** + Build the discover packet and send out for boot. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to the server address list. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDiscoverBootServer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ); + + +/** + Load boot file into user buffer. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Successfully obtained all the boot information. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancelled the current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcLoadBootFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ); + +#endif diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c new file mode 100644 index 0000000000..587566d4e0 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c @@ -0,0 +1,1672 @@ +/** @file + Functions implementation related with DHCPv4 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2015, 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. + +**/ + +#include "PxeBcImpl.h" + +// +// This is a map from the interested DHCP4 option tags' index to the tag value. +// +UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = { + PXEBC_DHCP4_TAG_BOOTFILE_LEN, + PXEBC_DHCP4_TAG_VENDOR, + PXEBC_DHCP4_TAG_OVERLOAD, + PXEBC_DHCP4_TAG_MSG_TYPE, + PXEBC_DHCP4_TAG_SERVER_ID, + PXEBC_DHCP4_TAG_CLASS_ID, + PXEBC_DHCP4_TAG_BOOTFILE +}; + +// +// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec. +// +UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32}; + + +/** + Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. + + @param[in] Buffer Pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag Tag of the required option. + + @retval NULL Failed to find the required option. + @retval Others The position of the required option. + +**/ +EFI_DHCP4_PACKET_OPTION * +PxeBcParseDhcp4Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT8 OptTag + ) +{ + EFI_DHCP4_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; + Offset = 0; + + while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) { + + if (Option->OpCode == OptTag) { + // + // Found the required option. + // + return Option; + } + + // + // Skip the current option to the next. + // + if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset += Option->Length + 2; + } + + Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + + +/** + Parse the PXE vender options and extract the information from them. + + @param[in] Dhcp4Option Pointer to vendor options in buffer. + @param[in] VendorOption Pointer to structure to store information in vendor options. + +**/ +VOID +PxeBcParseVendorOptions ( + IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option, + IN PXEBC_VENDOR_OPTION *VendorOption + ) +{ + UINT32 *BitMap; + UINT8 VendorOptionLen; + EFI_DHCP4_PACKET_OPTION *PxeOption; + UINT8 Offset; + + BitMap = VendorOption->BitMap; + VendorOptionLen = Dhcp4Option->Length; + PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0]; + Offset = 0; + + ASSERT (PxeOption != NULL); + + while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) { + // + // Parse all the interesting PXE vendor options one by one. + // + switch (PxeOption->OpCode) { + + case PXEBC_VENDOR_TAG_MTFTP_IP: + + CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_CPORT: + + CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_SPORT: + + CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT: + + VendorOption->MtftpTimeout = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MTFTP_DELAY: + + VendorOption->MtftpDelay = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_DISCOVER_CTRL: + + VendorOption->DiscoverCtrl = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_DISCOVER_MCAST: + + CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + break; + + case PXEBC_VENDOR_TAG_BOOT_SERVERS: + + VendorOption->BootSvrLen = PxeOption->Length; + VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_BOOT_MENU: + + VendorOption->BootMenuLen = PxeOption->Length; + VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MENU_PROMPT: + + VendorOption->MenuPromptLen = PxeOption->Length; + VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MCAST_ALLOC: + + CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock)); + CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange)); + break; + + case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES: + + VendorOption->CredTypeLen = PxeOption->Length; + VendorOption->CredType = (UINT32 *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_BOOT_ITEM: + + CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType)); + CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer)); + break; + + default: + // + // Not interesting PXE vendor options. + // + break; + } + + // + // Set the bit map for the special PXE options. + // + SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode); + + // + // Continue to the next option. + // + if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset = (UINT8) (Offset + PxeOption->Length + 2); + } + + PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset); + } +} + + +/** + Build the options buffer for the DHCPv4 request packet. + + @param[in] Private Pointer to PxeBc private data. + @param[out] OptList Pointer to the option pointer array. + @param[in] Buffer Pointer to the buffer to contain the option list. + @param[in] NeedMsgType If TRUE, it is necessary to include the Msg type option. + Otherwise, it is not necessary. + + @return Index The count of the built-in options. + +**/ +UINT32 +PxeBcBuildDhcp4Options ( + IN PXEBC_PRIVATE_DATA *Private, + OUT EFI_DHCP4_PACKET_OPTION **OptList, + IN UINT8 *Buffer, + IN BOOLEAN NeedMsgType + ) +{ + UINT32 Index; + PXEBC_DHCP4_OPTION_ENTRY OptEnt; + UINT16 Value; + + Index = 0; + OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; + + if (NeedMsgType) { + // + // Append message type. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MSG_TYPE; + OptList[Index]->Length = 1; + OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data; + OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append max message size. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MAXMSG; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE); + OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data; + Value = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8); + CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + } + + // + // Append parameter request list option. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_PARA_LIST; + OptList[Index]->Length = 35; + OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data; + OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK; + OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET; + OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER; + OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER; + OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER; + OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER; + OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME; + OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN; + OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME; + OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH; + OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH; + OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU; + OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL; + OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST; + OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN; + OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER; + OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER; + OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR; + OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP; + OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE; + OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID; + OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1; + OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2; + OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID; + OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP; + OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE; + OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID; + OptEnt.Para->ParaList[27] = 0x80; + OptEnt.Para->ParaList[28] = 0x81; + OptEnt.Para->ParaList[29] = 0x82; + OptEnt.Para->ParaList[30] = 0x83; + OptEnt.Para->ParaList[31] = 0x84; + OptEnt.Para->ParaList[32] = 0x85; + OptEnt.Para->ParaList[33] = 0x86; + OptEnt.Para->ParaList[34] = 0x87; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append UUID/Guid-based client identifier option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UUID; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID); + OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data; + OptEnt.Uuid->Type = 0; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); + } + + // + // Append client network device interface option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UNDI; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI); + OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_ARCH; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH); + OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_CLASS_ID; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID); + OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data; + CopyMem ( + OptEnt.Clid, + DEFAULT_CLASS_ID_DATA, + sizeof (PXEBC_DHCP4_OPTION_CLID) + ); + PxeBcUintnToAscDecWithFormat ( + EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.Clid->ArchitectureType, + sizeof (OptEnt.Clid->ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); + PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); + PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); + } + + Index++; + + return Index; +} + + +/** + Create a template DHCPv4 packet as a seed. + + @param[out] Seed Pointer to the seed packet. + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + +**/ +VOID +PxeBcSeedDhcp4Packet ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_UDP4_PROTOCOL *Udp4 + ) +{ + EFI_SIMPLE_NETWORK_MODE Mode; + EFI_DHCP4_HEADER *Header; + + // + // Get IfType and HwAddressSize from SNP mode data. + // + Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode); + + Seed->Size = sizeof (EFI_DHCP4_PACKET); + Seed->Length = sizeof (Seed->Dhcp4); + Header = &Seed->Dhcp4.Header; + ZeroMem (Header, sizeof (EFI_DHCP4_HEADER)); + Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST; + Header->HwType = Mode.IfType; + Header->HwAddrLen = (UINT8) Mode.HwAddressSize; + CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen); + + Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC; + Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP; +} + + +/** + Cache the DHCPv4 packet. + + @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. + @param[in] Src Pointer to the DHCPv4 packet to be cached. + +**/ +VOID +PxeBcCacheDhcp4Packet ( + IN EFI_DHCP4_PACKET *Dst, + IN EFI_DHCP4_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); + Dst->Length = Src->Length; +} + + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp4Packet ( + IN PXEBC_DHCP4_PACKET_CACHE *Cache4 + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_DHCP4_PACKET_OPTION **Options; + EFI_DHCP4_PACKET_OPTION *Option; + PXEBC_OFFER_TYPE OfferType; + UINTN Index; + BOOLEAN IsProxyOffer; + BOOLEAN IsPxeOffer; + UINT8 *Ptr8; + + IsProxyOffer = FALSE; + IsPxeOffer = FALSE; + + ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); + ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt)); + + Offer = &Cache4->Packet.Offer; + Options = Cache4->OptList; + + // + // Parse DHCPv4 options in this offer, and store the pointers. + // First, try to parse DHCPv4 options from the DHCP optional parameters field. + // + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + Options[Index] = PxeBcParseDhcp4Options ( + Offer->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Offer), + mInterestedDhcp4Tags[Index] + ); + } + // + // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. + // If yes, try to parse options from the BootFileName field, then ServerName field. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD]; + if (Option != NULL) { + if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) { + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = PxeBcParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.BootFileName, + sizeof (Offer->Dhcp4.Header.BootFileName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) { + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = PxeBcParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.ServerName, + sizeof (Offer->Dhcp4.Header.ServerName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + } + + // + // The offer with zero "yiaddr" is a proxy offer. + // + if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { + IsProxyOffer = TRUE; + } + + // + // The offer with "PXEClient" is a PXE offer. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID]; + if ((Option != NULL) && (Option->Length >= 9) && + (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { + IsPxeOffer = TRUE; + } + + // + // Parse PXE vendor options in this offer, and store the contents/pointers. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR]; + if (IsPxeOffer && Option != NULL) { + PxeBcParseVendorOptions (Option, &Cache4->VendorOpt); + } + + // + // Parse PXE boot file name: + // According to PXE spec, boot file name should be read from DHCP option 67 (bootfile name) if present. + // Otherwise, read from boot file field in DHCP header. + // + if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + // + // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null + // terminated string. So force to append null terminated character at the end of string. + // + Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; + Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length; + if (*(Ptr8 - 1) != '\0') { + *Ptr8 = '\0'; + } + } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) { + // + // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. + // Do not count dhcp option header here, or else will destroy the serverhostname. + // + Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) + (&Offer->Dhcp4.Header.BootFileName[0] - + OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); + + } + + // + // Determine offer type of the DHCPv4 packet. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE]; + if (Option == NULL || Option->Data[0] == 0) { + // + // It's a Bootp offer. + // + OfferType = PxeOfferTypeBootp; + + Option = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]; + if (Option == NULL) { + // + // If the Bootp offer without bootfilename, discard it. + // + return EFI_DEVICE_ERROR; + } + } else { + + if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { + // + // It's a PXE10 offer with PXEClient and discover vendor option. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10; + } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { + // + // It's a WFM11a offer with PXEClient and mtftp vendor option. + // But multi-cast download is not supported currently, so discard it. + // + return EFI_DEVICE_ERROR; + } else if (IsPxeOffer) { + // + // It's a BINL offer only with PXEClient. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; + } else { + // + // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet. + // + OfferType = PxeOfferTypeDhcpOnly; + } + } + + Cache4->OfferType = OfferType; + + return EFI_SUCCESS; +} + + +/** + Cache the DHCPv4 ack packet, and parse it on demand. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Ack Pointer to the DHCPv4 ack packet. + @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + +**/ +VOID +PxeBcCopyDhcp4Ack ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *Ack, + IN BOOLEAN Verified + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + + Mode = Private->PxeBc.Mode; + + PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack); + + if (Verified) { + // + // Parse the ack packet and store it into mode data if needed. + // + PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4); + CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length); + Mode->DhcpAckReceived = TRUE; + } +} + + +/** + Cache the DHCPv4 proxy offer packet according to the received order. + + @param[in] Private Pointer to PxeBc private data. + @param[in] OfferIndex The received order of offer packets. + +**/ +VOID +PxeBcCopyProxyOffer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 OfferIndex + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PACKET *Offer; + + ASSERT (OfferIndex < Private->OfferNum); + ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); + + Mode = Private->PxeBc.Mode; + Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer; + + // + // Cache the proxy offer packet and parse it. + // + PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer); + PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4); + + // + // Store this packet into mode data. + // + CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length); + Mode->ProxyOfferReceived = TRUE; +} + + +/** + Retry to request bootfile name by the BINL offer. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Index The received order of offer packets. + + @retval EFI_SUCCESS Successfully retried to request bootfile name. + @retval EFI_DEVICE_ERROR Failed to retry bootfile name. + +**/ +EFI_STATUS +PxeBcRetryBinlOffer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS Status; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Reply; + + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl || + Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl); + + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + + // + // Prefer to siaddr in header as next server address. If it's zero, then use option 54. + // + if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) { + CopyMem ( + &ServerIp.Addr[0], + Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, + sizeof (EFI_IPv4_ADDRESS) + ); + } else { + CopyMem ( + &ServerIp.Addr[0], + &Offer->Dhcp4.Header.ServerAddr, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + Private->IsDoDiscover = FALSE; + Cache4 = &Private->ProxyOffer.Dhcp4; + Reply = &Cache4->Packet.Offer; + + // + // Send another request packet for bootfile name. + // + Status = PxeBcDhcp4Discover ( + Private, + 0, + NULL, + FALSE, + &ServerIp, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the reply for the last request packet. + // + Status = PxeBcParseDhcp4Packet (Cache4); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Cache4->OfferType != PxeOfferTypeProxyPxe10 && + Cache4->OfferType != PxeOfferTypeProxyWfm11a && + Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + // + // This BINL ack doesn't have discovery option set or multicast option set + // or bootfile name specified. + // + return EFI_DEVICE_ERROR; + } + + // + // Store the reply into mode data. + // + Private->PxeBc.Mode->ProxyOfferReceived = TRUE; + CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length); + + return EFI_SUCCESS; +} + + +/** + Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. + + @param[in] Private Pointer to PxeBc private data. + @param[in] RcvdOffer Pointer to the received offer packet. + +**/ +VOID +PxeBcCacheDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *RcvdOffer + ) +{ + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + + ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; + Offer = &Cache4->Packet.Offer; + + // + // Cache the content of DHCPv4 packet firstly. + // + PxeBcCacheDhcp4Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv4 packet, and parse the options and offer type. + // + if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) { + return; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache4->OfferType; + ASSERT (OfferType < PxeOfferTypeMax); + + if (OfferType == PxeOfferTypeBootp) { + // + // It's a Bootp offer, only cache the first one, and discard the others. + // + if (Private->OfferCount[OfferType] == 0) { + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return; + } + } else { + ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); + if (IS_PROXY_DHCP_OFFER (Offer)) { + // + // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. + // + Private->IsProxyRecved = TRUE; + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Cache all proxy BINL offers. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } else if (Private->OfferCount[OfferType] > 0) { + // + // Only cache the first PXE10/WFM11a offer, and discard the others. + // + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return ; + } + } else { + // + // It's a DHCPv4 offer with yiaddr, and cache them all. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } + } + + Private->OfferNum++; +} + + +/** + Select an DHCPv4 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to PxeBc private data. + +**/ +VOID +PxeBcSelectDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + UINT32 Index; + UINT32 OfferIndex; + EFI_DHCP4_PACKET *Offer; + + Private->SelectIndex = 0; + + if (Private->IsOfferSorted) { + // + // Select offer by default policy. + // + if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { + // + // 1. DhcpPxe10 offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { + // + // 2. DhcpWfm11a offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { + // + // 3. DhcpOnly offer and ProxyPxe10 offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyPxe10; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { + // + // 4. DhcpOnly offer and ProxyWfm11a offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyWfm11a; + + } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { + // + // 5. DhcpBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { + // + // 6. DhcpOnly offer and ProxyBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyBinl; + + } else { + // + // 7. DhcpOnly offer with bootfilename. + // + for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { + OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; + if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Private->SelectIndex = OfferIndex + 1; + break; + } + } + // + // 8. Bootp offer with bootfilename. + // + OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0]; + if (Private->SelectIndex == 0 && + Private->OfferCount[PxeOfferTypeBootp] > 0 && + Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Private->SelectIndex = OfferIndex + 1; + } + } + } else { + // + // Select offer by received order. + // + for (Index = 0; Index < Private->OfferNum; Index++) { + + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + + if (IS_PROXY_DHCP_OFFER (Offer)) { + // + // Skip proxy offers + // + continue; + } + + if (!Private->IsProxyRecved && + Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly && + Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + // + // Skip if DhcpOnly offer without any other proxy offers or bootfilename. + // + continue; + } + + // + // Record the index of the select offer. + // + Private->SelectIndex = Index + 1; + break; + } + } +} + + +/** + Handle the DHCPv4 offer packet. + + @param[in] Private Pointer to PxeBc private data. + + @retval EFI_SUCCESS Handled the DHCPv4 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + @retval EFI_NOT_FOUND No boot filename received. + +**/ +EFI_STATUS +PxeBcHandleDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET_OPTION **Options; + UINT32 Index; + EFI_DHCP4_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + UINT32 ProxyIndex; + UINT32 SelectIndex; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PACKET *Ack; + + ASSERT (Private->SelectIndex > 0); + SelectIndex = (UINT32) (Private->SelectIndex - 1); + ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[SelectIndex].Dhcp4; + Options = Cache4->OptList; + Status = EFI_SUCCESS; + + if (Cache4->OfferType == PxeOfferTypeDhcpBinl) { + // + // DhcpBinl offer is selected, so need try to request bootfilename by this offer. + // + if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) { + Status = EFI_NO_RESPONSE; + } + } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) { + + if (Private->IsProxyRecved) { + // + // DhcpOnly offer is selected, so need try to request bootfile name. + // + ProxyIndex = 0; + if (Private->IsOfferSorted) { + // + // The proxy offer should be determined if select by default policy. + // IsOfferSorted means all offers are labeled by OfferIndex. + // + ASSERT (Private->SelectProxyType < PxeOfferTypeMax); + ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); + + if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfile name. + // + for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; + if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) { + break; + } + } + if (Index == Private->OfferCount[Private->SelectProxyType]) { + Status = EFI_NO_RESPONSE; + } + } else { + // + // For other proxy offers, only one is buffered. + // + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + } + } else { + // + // The proxy offer should not be determined if select by received order. + // + Status = EFI_NO_RESPONSE; + + for (Index = 0; Index < Private->OfferNum; Index++) { + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType; + if (!IS_PROXY_DHCP_OFFER (Offer)) { + // + // Skip non proxy DHCPv4 offers. + // + continue; + } + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfile name. + // + if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) { + continue; + } + } + + Private->SelectProxyType = OfferType; + ProxyIndex = Index; + Status = EFI_SUCCESS; + break; + } + } + + if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { + // + // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. + // + PxeBcCopyProxyOffer (Private, ProxyIndex); + } + } else { + // + // Othewise, the bootfile name must be included in DhcpOnly offer. + // + if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + Status = EFI_NOT_FOUND; + } + } + } + + if (!EFI_ERROR (Status)) { + // + // All PXE boot information is ready by now. + // + Mode = Private->PxeBc.Mode; + Offer = &Cache4->Packet.Offer; + Ack = &Private->DhcpAck.Dhcp4.Packet.Ack; + if (Cache4->OfferType == PxeOfferTypeBootp) { + // + // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply + // should be taken as ack. + // + Ack = Offer; + } + + PxeBcCopyDhcp4Ack (Private, Ack, TRUE); + Mode->DhcpDiscoverValid = TRUE; + } + + return Status; +} + + +/** + EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This Pointer to the EFI DHCPv4 Protocol. + @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. + @param[in] Dhcp4Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv4 packet that is going to be sent or already received. + @param[out] NewPacket The packet that is used to replace the above Packet. + + @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol + driver will continue to wait for more DHCPOFFER packets until the + retry timeout expires. + @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process + and return to the Dhcp4Init or Dhcp4InitReboot state. + +**/ +EFI_STATUS +EFIAPI +PxeBcDhcp4CallBack ( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_DHCP4_PACKET_OPTION *MaxMsgSize; + UINT16 Value; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp4Event != Dhcp4RcvdOffer) && + (Dhcp4Event != Dhcp4SelectOffer) && + (Dhcp4Event != Dhcp4SendDiscover) && + (Dhcp4Event != Dhcp4RcvdAck)) { + return EFI_SUCCESS; + } + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + Callback = Private->PxeBcCallback; + + // + // Override the Maximum DHCP Message Size. + // + MaxMsgSize = PxeBcParseDhcp4Options ( + Packet->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Packet), + PXEBC_DHCP4_TAG_MAXMSG + ); + if (MaxMsgSize != NULL) { + Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8); + CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); + } + + // + // Callback to user if any packets sent or received. + // + if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) { + Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck); + Status = Callback->Callback ( + Callback, + Private->Function, + Received, + Packet->Length, + (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4 + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + + switch (Dhcp4Event) { + + case Dhcp4SendDiscover: + // + // Cache the DHCPv4 discover packet to mode data directly. + // It need to check SendGuid as well as Dhcp4SendRequest. + // + CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length); + + case Dhcp4SendRequest: + if (Mode->SendGUID) { + // + // Send the system Guid instead of the MAC address as the hardware address if required. + // + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); + } + Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); + } + break; + + case Dhcp4RcvdOffer: + Status = EFI_NOT_READY; + if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { + // + // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + PxeBcCacheDhcp4Offer (Private, Packet); + } + break; + + case Dhcp4SelectOffer: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + PxeBcSelectDhcp4Offer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; + } + break; + + case Dhcp4RcvdAck: + // + // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data + // without verification. + // + ASSERT (Private->SelectIndex != 0); + + PxeBcCopyDhcp4Ack (Private, Packet, FALSE); + break; + + default: + break; + } + + return Status; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp4Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT Sport; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token; + BOOLEAN IsBCast; + EFI_STATUS Status; + UINT16 RepIndex; + UINT16 SrvIndex; + UINT16 TryIndex; + EFI_DHCP4_LISTEN_POINT ListenPoint; + EFI_DHCP4_PACKET *Response; + UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; + UINT32 OptCount; + EFI_DHCP4_PACKET_OPTION *PxeOpt; + PXEBC_OPTION_BOOT_ITEM *PxeBootItem; + UINT8 VendorOptLen; + UINT32 Xid; + + Mode = Private->PxeBc.Mode; + Dhcp4 = Private->Dhcp4; + Status = EFI_SUCCESS; + + ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN)); + + // + // Use broadcast if destination address not specified. + // + if (DestIp == NULL) { + Sport = PXEBC_DHCP4_S_PORT; + IsBCast = TRUE; + } else { + Sport = PXEBC_BS_DISCOVER_PORT; + IsBCast = FALSE; + } + + if (!UseBis && Layer != NULL) { + *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; + } + + // + // Build all the options for the request packet. + // + OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE); + + if (Private->IsDoDiscover) { + // + // Add vendor option of PXE_BOOT_ITEM + // + VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1); + OptList[OptCount] = AllocateZeroPool (VendorOptLen); + if (OptList[OptCount] == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OptList[OptCount]->OpCode = PXEBC_DHCP4_TAG_VENDOR; + OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2); + PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data; + PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM; + PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM); + PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data; + PxeBootItem->Type = HTONS (Type); + PxeOpt->Data[PxeOpt->Length] = PXEBC_DHCP4_TAG_EOP; + + if (Layer != NULL) { + PxeBootItem->Layer = HTONS (*Layer); + } + + OptCount++; + } + + // + // Build the request packet with seed packet and option list. + // + Status = Dhcp4->Build ( + Dhcp4, + &Private->SeedPacket, + 0, + NULL, + OptCount, + OptList, + &Token.Packet + ); + // + // Free the vendor option of PXE_BOOT_ITEM. + // + if (Private->IsDoDiscover) { + FreePool (OptList[OptCount - 1]); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Mode->SendGUID) { + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); + } + Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); + } + + // + // Set fields of the token for the request packet. + // + Xid = NET_RANDOM (NetRandomInitSeed ()); + Token.Packet->Dhcp4.Header.Xid = HTONL (Xid); + Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0)); + CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + + Token.RemotePort = Sport; + + if (IsBCast) { + SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff); + } else { + CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); + } + + CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); + + if (!IsBCast) { + Token.ListenPointCount = 1; + Token.ListenPoints = &ListenPoint; + Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT; + CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS)); + CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS)); + } + + // + // Send out the request packet to discover the bootfile. + // + for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) { + + Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex); + Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1)); + + Status = Dhcp4->TransmitReceive (Dhcp4, &Token); + if (Token.Status != EFI_TIMEOUT) { + break; + } + } + + if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) { + // + // No server response our PXE request + // + Status = EFI_TIMEOUT; + } + + if (!EFI_ERROR (Status)) { + + RepIndex = 0; + SrvIndex = 0; + Response = Token.ResponseList; + // + // Find the right PXE Reply according to server address. + // + while (RepIndex < Token.ResponseCount) { + + while (SrvIndex < IpCount) { + if (SrvList[SrvIndex].AcceptAnyResponse) { + break; + } + if ((SrvList[SrvIndex].Type == Type) && + EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &SrvList[SrvIndex].IpAddr)) { + break; + } + SrvIndex++; + } + + if ((IpCount != SrvIndex) || (IpCount == 0)) { + break; + } + + SrvIndex = 0; + RepIndex++; + + Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size); + } + + if (RepIndex < Token.ResponseCount) { + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + if (Private->IsDoDiscover) { + PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response); + CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length); + } else { + PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response); + } + } else { + // + // Not found the right PXE reply packet. + // + Status = EFI_NOT_FOUND; + } + if (Token.ResponseList != NULL) { + FreePool (Token.ResponseList); + } + } + + FreePool (Token.Packet); + return Status; +} + +/** + Switch the Ip4 policy to static. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The policy is already configured to static. + @retval Others Other error as indicated.. + +**/ +EFI_STATUS +PxeBcSetIp4Policy ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_IP4_CONFIG2_POLICY Policy; + UINTN DataSize; + + Ip4Config2 = Private->Ip4Config2; + DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Policy != Ip4Config2PolicyStatic) { + Policy = Ip4Config2PolicyStatic; + Status= Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +PxeBcDhcp4Dora ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PROTOCOL *Dhcp4 + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeMode; + EFI_DHCP4_CONFIG_DATA Config; + EFI_DHCP4_MODE_DATA Mode; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; + UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; + UINT32 OptCount; + EFI_STATUS Status; + + ASSERT (Dhcp4 != NULL); + + Status = EFI_SUCCESS; + PxeMode = Private->PxeBc.Mode; + + // + // Build option list for the request packet. + // + OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE); + ASSERT (OptCount> 0); + + ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp4Callback = PxeBcDhcp4CallBack; + Config.CallbackContext = Private; + Config.DiscoverTryCount = PXEBC_DHCP_RETRIES; + Config.DiscoverTimeout = mPxeDhcpTimeout; + + // + // Configure the DHCPv4 instance for PXE boot. + // + Status = Dhcp4->Configure (Dhcp4, &Config); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Initialize the record fields for DHCPv4 offer in private data. + // + Private->IsProxyRecved = FALSE; + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. This may + // have already been done, thus do not leave in error if the return + // code is EFI_ALREADY_STARTED. + // + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + if (Status == EFI_ICMP_ERROR) { + PxeMode->IcmpErrorReceived = TRUE; + } + goto ON_EXIT; + } + + // + // Get the acquired IPv4 address and store them. + // + Status = Dhcp4->GetModeData (Dhcp4, &Mode); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.State == Dhcp4Bound); + + CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + Status = PxeBcFlushStationIp (Private, &Private->StationIp, &Private->SubnetMask); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check the selected offer whether BINL retry is needed. + // + Status = PxeBcHandleDhcp4Offer (Private); + + AsciiPrint ("\n Station IP address is "); + + PxeBcShowIp4Addr (&Private->StationIp.v4); + AsciiPrint ("\n"); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dhcp4->Configure (Dhcp4, &Config); + Private->IsAddressOk = TRUE; + } + + return Status; +} diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h new file mode 100644 index 0000000000..248dc60d2c --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h @@ -0,0 +1,409 @@ +/** @file + Functions declaration related with DHCPv4 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2015, 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 __EFI_PXEBC_DHCP4_H__ +#define __EFI_PXEBC_DHCP4_H__ + +#define PXEBC_DHCP4_OPTION_MAX_NUM 16 +#define PXEBC_DHCP4_OPTION_MAX_SIZE 312 +#define PXEBC_DHCP4_PACKET_MAX_SIZE 1472 +#define PXEBC_DHCP4_S_PORT 67 +#define PXEBC_DHCP4_C_PORT 68 +#define PXEBC_BS_DOWNLOAD_PORT 69 +#define PXEBC_BS_DISCOVER_PORT 4011 +#define PXEBC_DHCP4_OPCODE_REQUEST 1 +#define PXEBC_DHCP4_OPCODE_REPLY 2 +#define PXEBC_DHCP4_MSG_TYPE_REQUEST 3 +#define PXEBC_DHCP4_MAGIC 0x63538263 // network byte order + +// +// Dhcp Options +// +#define PXEBC_DHCP4_TAG_PAD 0 // Pad Option +#define PXEBC_DHCP4_TAG_EOP 255 // End Option +#define PXEBC_DHCP4_TAG_NETMASK 1 // Subnet Mask +#define PXEBC_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC +#define PXEBC_DHCP4_TAG_ROUTER 3 // Router option, +#define PXEBC_DHCP4_TAG_TIME_SERVER 4 // Time Server +#define PXEBC_DHCP4_TAG_NAME_SERVER 5 // Name Server +#define PXEBC_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server +#define PXEBC_DHCP4_TAG_HOSTNAME 12 // Host Name +#define PXEBC_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size +#define PXEBC_DHCP4_TAG_DUMP 14 // Merit Dump File +#define PXEBC_DHCP4_TAG_DOMAINNAME 15 // Domain Name +#define PXEBC_DHCP4_TAG_ROOTPATH 17 // Root path +#define PXEBC_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path +#define PXEBC_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size +#define PXEBC_DHCP4_TAG_TTL 23 // Default IP Time-to-live +#define PXEBC_DHCP4_TAG_BROADCAST 28 // Broadcast Address +#define PXEBC_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain +#define PXEBC_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers +#define PXEBC_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers +#define PXEBC_DHCP4_TAG_VENDOR 43 // Vendor Specific Information +#define PXEBC_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address +#define PXEBC_DHCP4_TAG_LEASE 51 // IP Address Lease Time +#define PXEBC_DHCP4_TAG_OVERLOAD 52 // Option Overload +#define PXEBC_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type +#define PXEBC_DHCP4_TAG_SERVER_ID 54 // Server Identifier +#define PXEBC_DHCP4_TAG_PARA_LIST 55 // Parameter Request List +#define PXEBC_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size +#define PXEBC_DHCP4_TAG_T1 58 // Renewal (T1) Time Value +#define PXEBC_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value +#define PXEBC_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier +#define PXEBC_DHCP4_TAG_CLIENT_ID 61 // Client-identifier +#define PXEBC_DHCP4_TAG_TFTP 66 // TFTP server name +#define PXEBC_DHCP4_TAG_BOOTFILE 67 // Bootfile name +#define PXEBC_PXE_DHCP4_TAG_ARCH 93 +#define PXEBC_PXE_DHCP4_TAG_UNDI 94 +#define PXEBC_PXE_DHCP4_TAG_UUID 97 +// +// Sub-Options in Dhcp Vendor Option +// +#define PXEBC_VENDOR_TAG_MTFTP_IP 1 +#define PXEBC_VENDOR_TAG_MTFTP_CPORT 2 +#define PXEBC_VENDOR_TAG_MTFTP_SPORT 3 +#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT 4 +#define PXEBC_VENDOR_TAG_MTFTP_DELAY 5 +#define PXEBC_VENDOR_TAG_DISCOVER_CTRL 6 +#define PXEBC_VENDOR_TAG_DISCOVER_MCAST 7 +#define PXEBC_VENDOR_TAG_BOOT_SERVERS 8 +#define PXEBC_VENDOR_TAG_BOOT_MENU 9 +#define PXEBC_VENDOR_TAG_MENU_PROMPT 10 +#define PXEBC_VENDOR_TAG_MCAST_ALLOC 11 +#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES 12 +#define PXEBC_VENDOR_TAG_BOOT_ITEM 71 + +#define PXEBC_BOOT_REQUEST_TIMEOUT 1 +#define PXEBC_BOOT_REQUEST_RETRIES 4 + +#define PXEBC_DHCP4_OVERLOAD_FILE 1 +#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME 2 + + +// +// The array index of the DHCP4 option tag interested +// +#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0 +#define PXEBC_DHCP4_TAG_INDEX_VENDOR 1 +#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD 2 +#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE 3 +#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID 4 +#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID 5 +#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE 6 +#define PXEBC_DHCP4_TAG_INDEX_MAX 7 + +// +// Dhcp4 and Dhcp6 share this definition, and corresponding +// relatioinship is as follows: +// +// Dhcp4Discover <> Dhcp6Solicit +// Dhcp4Offer <> Dhcp6Advertise +// Dhcp4Request <> Dhcp6Request +// Dhcp4Ack <> DHcp6Reply +// +typedef enum { + PxeOfferTypeDhcpOnly, + PxeOfferTypeDhcpPxe10, + PxeOfferTypeDhcpWfm11a, + PxeOfferTypeDhcpBinl, + PxeOfferTypeProxyPxe10, + PxeOfferTypeProxyWfm11a, + PxeOfferTypeProxyBinl, + PxeOfferTypeBootp, + PxeOfferTypeMax +} PXEBC_OFFER_TYPE; + +#define BIT(x) (1 << x) +#define CTRL(x) (0x1F & (x)) +#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:xxxxx:UNDI:003000" +#define DEFAULT_UNDI_TYPE 1 +#define DEFAULT_UNDI_MAJOR 3 +#define DEFAULT_UNDI_MINOR 0 + +#define MTFTP_VENDOR_OPTION_BIT_MAP \ + (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY)) + +#define DISCOVER_VENDOR_OPTION_BIT_MAP \ + (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \ + BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \ + BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \ + BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \ + BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) + +#define IS_VALID_BOOT_SERVERS(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS)) + +#define IS_VALID_BOOT_PROMPT(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \ + == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) + +#define IS_VALID_BOOT_MENU(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) + +#define IS_VALID_MTFTP_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \ + == MTFTP_VENDOR_OPTION_BIT_MAP) + +#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0) + +#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \ + == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) + +#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \ + (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \ + BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) + +#define SET_VENDOR_OPTION_BIT_MAP(x, y) \ + (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32))) + +#define GET_NEXT_DHCP_OPTION(Opt) \ + (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1) + +#define GET_OPTION_BUFFER_LEN(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4) + +#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \ + (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \ + ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS)) + +#define IS_PROXY_DHCP_OFFER(Offer) \ + EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr) + +#define IS_DISABLE_BCAST_DISCOVER(x) \ + (((x) & BIT (0)) == BIT (0)) + +#define IS_DISABLE_MCAST_DISCOVER(x) \ + (((x) & BIT (1)) == BIT (1)) + +#define IS_ENABLE_USE_SERVER_LIST(x) \ + (((x) & BIT (2)) == BIT (2)) + +#define IS_DISABLE_PROMPT_MENU(x) \ + (((x) & BIT (3)) == BIT (3)) + + +#pragma pack(1) +typedef struct { + UINT8 ParaList[135]; +} PXEBC_DHCP4_OPTION_PARA; + +typedef struct { + UINT16 Size; +} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} PXEBC_DHCP4_OPTION_UNDI; + +typedef struct { + UINT8 Type; +} PXEBC_DHCP4_OPTION_MESG; + +typedef struct { + UINT16 Type; +} PXEBC_DHCP4_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} PXEBC_DHCP4_OPTION_CLID; + +typedef struct { + UINT8 Type; + UINT8 Guid[16]; +} PXEBC_DHCP4_OPTION_UUID; + +typedef struct { + UINT16 Type; + UINT16 Layer; +} PXEBC_OPTION_BOOT_ITEM; + +#pragma pack() + +typedef union { + PXEBC_DHCP4_OPTION_PARA *Para; + PXEBC_DHCP4_OPTION_UNDI *Undi; + PXEBC_DHCP4_OPTION_ARCH *Arch; + PXEBC_DHCP4_OPTION_CLID *Clid; + PXEBC_DHCP4_OPTION_UUID *Uuid; + PXEBC_DHCP4_OPTION_MESG *Mesg; + PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize; +} PXEBC_DHCP4_OPTION_ENTRY; + +#pragma pack(1) +typedef struct { + UINT16 Type; + UINT8 IpCnt; + EFI_IPv4_ADDRESS IpAddr[1]; +} PXEBC_BOOT_SVR_ENTRY; + +typedef struct { + UINT16 Type; + UINT8 DescLen; + UINT8 DescStr[1]; +} PXEBC_BOOT_MENU_ENTRY; + +typedef struct { + UINT8 Timeout; + UINT8 Prompt[1]; +} PXEBC_MENU_PROMPT; +#pragma pack() + +typedef struct { + UINT32 BitMap[8]; + EFI_IPv4_ADDRESS MtftpIp; + UINT16 MtftpCPort; + UINT16 MtftpSPort; + UINT8 MtftpTimeout; + UINT8 MtftpDelay; + UINT8 DiscoverCtrl; + EFI_IPv4_ADDRESS DiscoverMcastIp; + EFI_IPv4_ADDRESS McastIpBase; + UINT16 McastIpBlock; + UINT16 McastIpRange; + UINT16 BootSrvType; + UINT16 BootSrvLayer; + PXEBC_BOOT_SVR_ENTRY *BootSvr; + UINT8 BootSvrLen; + PXEBC_BOOT_MENU_ENTRY *BootMenu; + UINT8 BootMenuLen; + PXEBC_MENU_PROMPT *MenuPrompt; + UINT8 MenuPromptLen; + UINT32 *CredType; + UINT8 CredTypeLen; +} PXEBC_VENDOR_OPTION; + +typedef union { + EFI_DHCP4_PACKET Offer; + EFI_DHCP4_PACKET Ack; + UINT8 Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE]; +} PXEBC_DHCP4_PACKET; + +typedef struct { + PXEBC_DHCP4_PACKET Packet; + PXEBC_OFFER_TYPE OfferType; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX]; + PXEBC_VENDOR_OPTION VendorOpt; +} PXEBC_DHCP4_PACKET_CACHE; + + +/** + Create a template DHCPv4 packet as a seed. + + @param[out] Seed Pointer to the seed packet. + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + +**/ +VOID +PxeBcSeedDhcp4Packet ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_UDP4_PROTOCOL *Udp4 + ); + + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp4Packet ( + IN PXEBC_DHCP4_PACKET_CACHE *Cache4 + ); + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp4Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ); + +/** + Switch the Ip4 policy to static. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The policy is already configured to static. + @retval Others Other error as indicated.. + +**/ +EFI_STATUS +PxeBcSetIp4Policy ( + IN PXEBC_PRIVATE_DATA *Private + ); + + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +PxeBcDhcp4Dora ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PROTOCOL *Dhcp4 + ); + +#endif + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c new file mode 100644 index 0000000000..6ad5f5f1ac --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -0,0 +1,2096 @@ +/** @file + Functions implementation related with DHCPv6 for UefiPxeBc Driver. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2015, 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. + +**/ + +#include "PxeBcImpl.h" + +// +// Well-known multi-cast address defined in section-24.1 of rfc-3315 +// +// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2 +// +EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}}; + +/** + Parse out a DHCPv6 option by OptTag, and find the position in buffer. + + @param[in] Buffer The pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag The required option tag. + + @retval NULL Failed to parse the required option. + @retval Others The postion of the required option in buffer. + +**/ +EFI_DHCP6_PACKET_OPTION * +PxeBcParseDhcp6Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT16 OptTag + ) +{ + EFI_DHCP6_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP6_PACKET_OPTION *) Buffer; + Offset = 0; + + // + // OpLen and OpCode here are both stored in network order. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == OptTag) { + + return Option; + } + + Offset += (NTOHS(Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + + +/** + Build the options buffer for the DHCPv6 request packet. + + @param[in] Private The pointer to PxeBc private data. + @param[out] OptList The pointer to the option pointer array. + @param[in] Buffer The pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +PxeBcBuildDhcp6Options ( + IN PXEBC_PRIVATE_DATA *Private, + OUT EFI_DHCP6_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + PXEBC_DHCP6_OPTION_ENTRY OptEnt; + UINT32 Index; + UINT16 Value; + + Index = 0; + OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer; + + // + // Append client option request option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (4); + OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data; + OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL); + OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI); + OptList[Index]->OpLen = HTONS ((UINT16)3); + OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH)); + OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append vendor class option to store the PXE class identifier. + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS)); + OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data; + OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM); + OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID)); + CopyMem ( + &OptEnt.VendorClass->ClassId, + DEFAULT_CLASS_ID_DATA, + sizeof (PXEBC_CLASS_ID) + ); + PxeBcUintnToAscDecWithFormat ( + EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.VendorClass->ClassId.ArchitectureType, + sizeof (OptEnt.VendorClass->ClassId.ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem ( + OptEnt.VendorClass->ClassId.InterfaceName, + Private->Nii->StringId, + sizeof (OptEnt.VendorClass->ClassId.InterfaceName) + ); + PxeBcUintnToAscDecWithFormat ( + Private->Nii->MajorVer, + OptEnt.VendorClass->ClassId.UndiMajor, + sizeof (OptEnt.VendorClass->ClassId.UndiMajor) + ); + PxeBcUintnToAscDecWithFormat ( + Private->Nii->MinorVer, + OptEnt.VendorClass->ClassId.UndiMinor, + sizeof (OptEnt.VendorClass->ClassId.UndiMinor) + ); + } + + Index++; + + return Index; +} + + +/** + Cache the DHCPv6 packet. + + @param[in] Dst The pointer to the cache buffer for DHCPv6 packet. + @param[in] Src The pointer to the DHCPv6 packet to be cached. + +**/ +VOID +PxeBcCacheDhcp6Packet ( + IN EFI_DHCP6_PACKET *Dst, + IN EFI_DHCP6_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); + Dst->Length = Src->Length; +} + + +/** + Free all the nodes in the list for boot file. + + @param[in] Head The pointer to the head of list. + +**/ +VOID +PxeBcFreeBootFileOption ( + IN LIST_ENTRY *Head + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + PXEBC_DHCP6_OPTION_NODE *Node; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) { + Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link); + RemoveEntryList (Entry); + FreePool (Node); + } +} + + +/** + Parse the Boot File URL option. + + @param[out] FileName The pointer to the boot file name. + @param[in, out] SrvAddr The pointer to the boot server address. + @param[in] BootFile The pointer to the boot file URL option data. + @param[in] Length The length of the boot file URL option data. + + @retval EFI_ABORTED User cancel operation. + @retval EFI_SUCCESS Selected the boot menu successfully. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcExtractBootFileUrl ( + OUT UINT8 **FileName, + IN OUT EFI_IPv6_ADDRESS *SrvAddr, + IN CHAR8 *BootFile, + IN UINT16 Length + ) +{ + UINT16 PrefixLen; + CHAR8 *BootFileNamePtr; + CHAR8 *BootFileName; + UINT16 BootFileNameLen; + CHAR8 *TmpStr; + CHAR8 TmpChar; + CHAR8 *ServerAddressOption; + CHAR8 *ServerAddress; + CHAR8 *ModeStr; + EFI_STATUS Status; + + // + // The format of the Boot File URL option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPT_BOOTFILE_URL | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . bootfile-url (variable length) . + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format + // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME + // As an example where the BOOTFILE_NAME is the EFI loader and + // SERVER_ADDRESS is the ASCII encoding of an IPV6 address. + // + PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX); + + if (Length <= PrefixLen || + CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) { + return EFI_NOT_FOUND; + } + + BootFile = BootFile + PrefixLen; + Length = (UINT16) (Length - PrefixLen); + + TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1); + if (TmpStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TmpStr, BootFile, Length); + TmpStr[Length] = '\0'; + + // + // Get the part of SERVER_ADDRESS string. + // + ServerAddressOption = TmpStr; + if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + ServerAddressOption ++; + ServerAddress = ServerAddressOption; + while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) { + ServerAddress++; + } + + if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + *ServerAddress = '\0'; + + // + // Convert the string of server address to Ipv6 address format and store it. + // + Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr); + if (EFI_ERROR (Status)) { + FreePool (TmpStr); + return Status; + } + + // + // Get the part of BOOTFILE_NAME string. + // + BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1); + if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + ++BootFileNamePtr; + BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1); + if (BootFileNameLen != 0 || FileName != NULL) { + // + // Remove trailing mode=octet if present and ignore. All other modes are + // invalid for netboot6, so reject them. + // + ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet"); + if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') { + *ModeStr = '\0'; + } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Extract boot file name from URL. + // + BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen); + if (BootFileName == NULL) { + FreePool (TmpStr); + return EFI_OUT_OF_RESOURCES; + } + *FileName = (UINT8*) BootFileName; + + // + // Decode percent-encoding in boot file name. + // + while (*BootFileNamePtr != '\0') { + if (*BootFileNamePtr == '%') { + TmpChar = *(BootFileNamePtr+ 3); + *(BootFileNamePtr+ 3) = '\0'; + *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1)); + BootFileName++; + *(BootFileNamePtr+ 3) = TmpChar; + BootFileNamePtr += 3; + } else { + *BootFileName = *BootFileNamePtr; + BootFileName++; + BootFileNamePtr++; + } + } + *BootFileName = '\0'; + } + + FreePool (TmpStr); + + return EFI_SUCCESS; +} + + +/** + Parse the Boot File Parameter option. + + @param[in] BootFilePara The pointer to boot file parameter option data. + @param[out] BootFileSize The pointer to the parsed boot file size. + + @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option. + @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option. + +**/ +EFI_STATUS +PxeBcExtractBootFileParam ( + IN CHAR8 *BootFilePara, + OUT UINT16 *BootFileSize + ) +{ + UINT16 Length; + UINT8 Index; + UINT8 Digit; + UINT32 Size; + + CopyMem (&Length, BootFilePara, sizeof (UINT16)); + Length = NTOHS (Length); + + // + // The BootFile Size should be 1~5 byte ASCII strings + // + if (Length < 1 || Length > 5) { + return EFI_NOT_FOUND; + } + + // + // Extract the value of BootFile Size. + // + BootFilePara = BootFilePara + sizeof (UINT16); + Size = 0; + for (Index = 0; Index < Length; Index++) { + if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) { + return EFI_NOT_FOUND; + } + + Size = (Size + Digit) * 10; + } + + Size = Size / 10; + if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) { + return EFI_NOT_FOUND; + } + + *BootFileSize = (UINT16) Size; + return EFI_SUCCESS; +} + + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet. + +**/ +EFI_STATUS +PxeBcParseDhcp6Packet ( + IN PXEBC_DHCP6_PACKET_CACHE *Cache6 + ) +{ + EFI_DHCP6_PACKET *Offer; + EFI_DHCP6_PACKET_OPTION **Options; + EFI_DHCP6_PACKET_OPTION *Option; + PXEBC_OFFER_TYPE OfferType; + BOOLEAN IsProxyOffer; + BOOLEAN IsPxeOffer; + UINT32 Offset; + UINT32 Length; + UINT32 EnterpriseNum; + + IsProxyOffer = TRUE; + IsPxeOffer = FALSE; + Offer = &Cache6->Packet.Offer; + Options = Cache6->OptList; + + ZeroMem (Cache6->OptList, sizeof (Cache6->OptList)); + + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option); + Offset = 0; + Length = GET_DHCP6_OPTION_SIZE (Offer); + + // + // OpLen and OpCode here are both stored in network order, since they are from original packet. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) { + Options[PXEBC_DHCP6_IDX_IA_NA] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) { + Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) { + Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option; + } + + Offset += (NTOHS (Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset); + } + + // + // The offer with assigned client address is NOT a proxy offer. + // An ia_na option, embeded with valid ia_addr option and a status_code of success. + // + Option = Options[PXEBC_DHCP6_IDX_IA_NA]; + if (Option != NULL) { + Option = PxeBcParseDhcp6Options ( + Option->Data + 12, + NTOHS (Option->OpLen), + PXEBC_DHCP6_OPT_STATUS_CODE + ); + if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) { + IsProxyOffer = FALSE; + } + } + + // + // The offer with "PXEClient" is a pxe offer. + // + Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS]; + EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM); + + if (Option != NULL && + NTOHS(Option->OpLen) >= 13 && + CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 && + CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) { + IsPxeOffer = TRUE; + } + + // + // Determine offer type of the dhcp6 packet. + // + if (IsPxeOffer) { + // + // It's a binl offer only with PXEClient. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; + } else { + // + // It's a dhcp only offer, which is a pure dhcp6 offer packet. + // + OfferType = PxeOfferTypeDhcpOnly; + } + + Cache6->OfferType = OfferType; + + return EFI_SUCCESS; +} + + +/** + Cache the DHCPv6 ack packet, and parse it on demand. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Ack The pointer to the DHCPv6 ack packet. + @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + +**/ +VOID +PxeBcCopyDhcp6Ack ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *Ack, + IN BOOLEAN Verified + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + + Mode = Private->PxeBc.Mode; + + PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack); + + if (Verified) { + // + // Parse the ack packet and store it into mode data if needed. + // + PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6); + CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length); + Mode->DhcpAckReceived = TRUE; + } +} + + +/** + Cache the DHCPv6 proxy offer packet according to the received order. + + @param[in] Private The pointer to PxeBc private data. + @param[in] OfferIndex The received order of offer packets. + +**/ +VOID +PxeBcCopyDhcp6Proxy ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 OfferIndex + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP6_PACKET *Offer; + + ASSERT (OfferIndex < Private->OfferNum); + ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); + + Mode = Private->PxeBc.Mode; + Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer; + + // + // Cache the proxy offer packet and parse it. + // + PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer); + PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6); + + // + // Store this packet into mode data. + // + CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length); + Mode->ProxyOfferReceived = TRUE; +} + +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If it failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +PxeBcDhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + + Option = NULL; + Cursor = Buf; + + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + if (OpCode == HTONS (OptType)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Index PxeBc option boot item type. + + @retval EFI_SUCCESS Successfully discovered the boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover the boot file. + +**/ +EFI_STATUS +PxeBcRequestBootService ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT SrcPort; + EFI_PXE_BASE_CODE_UDP_PORT DestPort; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; + UINTN DiscoverLen; + EFI_DHCP6_PACKET *Request; + UINTN RequestLen; + EFI_DHCP6_PACKET *Reply; + UINT8 *RequestOpt; + UINT8 *DiscoverOpt; + UINTN ReadSize; + UINT16 OpFlags; + UINT16 OpCode; + UINT16 OpLen; + EFI_STATUS Status; + EFI_DHCP6_PACKET *ProxyOffer; + UINT8 *Option; + + PxeBc = &Private->PxeBc; + Request = Private->Dhcp6Request; + ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer; + SrcPort = PXEBC_BS_DISCOVER_PORT; + DestPort = PXEBC_BS_DISCOVER_PORT; + OpFlags = 0; + + if (Request == NULL) { + return EFI_DEVICE_ERROR; + } + + Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET)); + if (Discover == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Build the request packet by the cached request packet before. + // + Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId; + Discover->MessageType = Request->Dhcp6.Header.MessageType; + RequestOpt = Request->Dhcp6.Option; + DiscoverOpt = Discover->DhcpOptions; + DiscoverLen = sizeof (EFI_DHCP6_HEADER); + RequestLen = DiscoverLen; + + // + // Find Server ID Option from ProxyOffer. + // + Option = PxeBcDhcp6SeekOption ( + ProxyOffer->Dhcp6.Option, + ProxyOffer->Length - 4, + PXEBC_DHCP6_OPT_SERVER_ID + ); + if (Option == NULL) { + return EFI_NOT_FOUND; + } + + // + // Add Server ID Option. + // + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen); + CopyMem (DiscoverOpt, Option, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + + while (RequestLen < Request->Length) { + OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode); + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen); + if (OpCode != EFI_DHCP6_IA_TYPE_NA && + OpCode != EFI_DHCP6_IA_TYPE_TA && + OpCode != PXEBC_DHCP6_OPT_SERVER_ID + ) { + // + // Copy all the options except IA option and Server ID + // + CopyMem (DiscoverOpt, RequestOpt, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } + RequestOpt += (OpLen + 4); + RequestLen += (OpLen + 4); + } + + // + // Update Elapsed option in the package + // + Option = PxeBcDhcp6SeekOption ( + Discover->DhcpOptions, + (UINT32)(RequestLen - 4), + PXEBC_DHCP6_OPT_ELAPSED_TIME + ); + if (Option != NULL) { + CalcElapsedTime (Private); + WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime)); + } + + Status = PxeBc->UdpWrite ( + PxeBc, + OpFlags, + &Private->ServerIp, + &DestPort, + NULL, + &Private->StationIp, + &SrcPort, + NULL, + NULL, + &DiscoverLen, + (VOID *) Discover + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer; + ReadSize = (UINTN) Reply->Size; + + // + // Start Udp6Read instance + // + Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PxeBc->UdpRead ( + PxeBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP, + NULL, + &SrcPort, + &Private->ServerIp, + &DestPort, + NULL, + NULL, + &ReadSize, + (VOID *) &Reply->Dhcp6 + ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update length + // + Reply->Length = (UINT32) ReadSize; + + return EFI_SUCCESS; +} + + +/** + Retry to request bootfile name by the BINL offer. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Index The received order of offer packets. + + @retval EFI_SUCCESS Successfully retried a request for the bootfile name. + @retval EFI_DEVICE_ERROR Failed to retry the bootfile name. + +**/ +EFI_STATUS +PxeBcRetryDhcp6Binl ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP6_PACKET_CACHE *Offer; + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_STATUS Status; + + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl || + Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl); + + Mode = Private->PxeBc.Mode; + Private->IsDoDiscover = FALSE; + Offer = &Private->OfferBuffer[Index].Dhcp6; + if (Offer->OfferType == PxeOfferTypeDhcpBinl) { + // + // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead. + // + CopyMem ( + &Private->ServerIp.v6, + &mAllDhcpRelayAndServersAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + // + // Parse out the next server address from the last offer, and store it + // + Status = PxeBcExtractBootFileUrl ( + &Private->BootFileName, + &Private->ServerIp.v6, + (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer. + // + Status = PxeBcRequestBootService (Private, Index); + + if (EFI_ERROR (Status)) { + return Status; + } + + Cache6 = &Private->ProxyOffer.Dhcp6; + Status = PxeBcParseDhcp6Packet (Cache6); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Cache6->OfferType != PxeOfferTypeProxyPxe10 && + Cache6->OfferType != PxeOfferTypeProxyWfm11a && + Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // This BINL ack doesn't have discovery option set or multicast option set + // or bootfile name specified. + // + return EFI_DEVICE_ERROR; + } + + Mode->ProxyOfferReceived = TRUE; + CopyMem ( + &Mode->ProxyOffer.Dhcpv6, + &Cache6->Packet.Offer.Dhcp6, + Cache6->Packet.Offer.Length + ); + + return EFI_SUCCESS; +} + + +/** + Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] RcvdOffer The pointer to the received offer packet. + +**/ +VOID +PxeBcCacheDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *RcvdOffer + ) +{ + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_DHCP6_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + + Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; + Offer = &Cache6->Packet.Offer; + + // + // Cache the content of DHCPv6 packet firstly. + // + PxeBcCacheDhcp6Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv6 packet, and parse the options and offer type. + // + if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) { + return ; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache6->OfferType; + ASSERT (OfferType < PxeOfferTypeMax); + ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); + + if (IS_PROXY_OFFER (OfferType)) { + // + // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. + // + Private->IsProxyRecved = TRUE; + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Cache all proxy BINL offers. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } else if (Private->OfferCount[OfferType] > 0) { + // + // Only cache the first PXE10/WFM11a offer, and discard the others. + // + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return; + } + } else { + // + // It's a DHCPv6 offer with yiaddr, and cache them all. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } + + Private->OfferNum++; +} + + +/** + Select an DHCPv6 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcSelectDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + UINT32 Index; + UINT32 OfferIndex; + PXEBC_OFFER_TYPE OfferType; + + Private->SelectIndex = 0; + + if (Private->IsOfferSorted) { + // + // Select offer by default policy. + // + if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { + // + // 1. DhcpPxe10 offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { + // + // 2. DhcpWfm11a offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { + // + // 3. DhcpOnly offer and ProxyPxe10 offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyPxe10; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { + // + // 4. DhcpOnly offer and ProxyWfm11a offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyWfm11a; + + } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { + // + // 5. DhcpBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { + // + // 6. DhcpOnly offer and ProxyBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyBinl; + + } else { + // + // 7. DhcpOnly offer with bootfilename. + // + for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { + OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; + if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) { + Private->SelectIndex = OfferIndex + 1; + break; + } + } + } + } else { + // + // Select offer by received order. + // + for (Index = 0; Index < Private->OfferNum; Index++) { + + OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType; + + if (IS_PROXY_OFFER (OfferType)) { + // + // Skip proxy offers + // + continue; + } + + if (!Private->IsProxyRecved && + OfferType == PxeOfferTypeDhcpOnly && + Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // Skip if DhcpOnly offer without any other proxy offers or bootfilename. + // + continue; + } + + Private->SelectIndex = Index + 1; + break; + } + } +} + + +/** + Handle the DHCPv6 offer packet. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + +**/ +EFI_STATUS +PxeBcHandleDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_STATUS Status; + PXEBC_OFFER_TYPE OfferType; + UINT32 ProxyIndex; + UINT32 SelectIndex; + UINT32 Index; + + ASSERT (Private->SelectIndex > 0); + SelectIndex = (UINT32) (Private->SelectIndex - 1); + ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); + Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6; + Status = EFI_SUCCESS; + + if (Cache6->OfferType == PxeOfferTypeDhcpBinl) { + // + // DhcpBinl offer is selected, so need try to request bootfilename by this offer. + // + if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) { + Status = EFI_NO_RESPONSE; + } + } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) { + + if (Private->IsProxyRecved) { + // + // DhcpOnly offer is selected, so need try to request bootfilename. + // + ProxyIndex = 0; + if (Private->IsOfferSorted) { + // + // The proxy offer should be determined if select by default policy. + // IsOfferSorted means all offers are labeled by OfferIndex. + // + ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); + + if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfilename. + // + for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { + + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; + if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) { + break; + } + } + if (Index == Private->OfferCount[Private->SelectProxyType]) { + Status = EFI_NO_RESPONSE; + } + } else { + // + // For other proxy offers (pxe10 or wfm11a), only one is buffered. + // + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + } + } else { + // + // The proxy offer should not be determined if select by received order. + // + Status = EFI_NO_RESPONSE; + + for (Index = 0; Index < Private->OfferNum; Index++) { + + OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType; + + if (!IS_PROXY_OFFER (OfferType)) { + // + // Skip non proxy dhcp offers. + // + continue; + } + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfilename. + // + if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) { + continue; + } + } + + Private->SelectProxyType = OfferType; + ProxyIndex = Index; + Status = EFI_SUCCESS; + break; + } + } + + if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { + // + // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. + // + PxeBcCopyDhcp6Proxy (Private, ProxyIndex); + } + } else { + // + // Othewise, the bootfilename must be included in DhcpOnly offer. + // + ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + } + } + + if (!EFI_ERROR (Status)) { + // + // All PXE boot information is ready by now. + // + PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE); + Private->PxeBc.Mode->DhcpDiscoverValid = TRUE; + } + + return Status; +} + + +/** + Unregister the address by Ip6Config protocol. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcUnregisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) { + // + // PXE driver change the policy of IP6 driver, it's a chance to recover. + // Keep the point and there is no enough requirements to do recovery. + // + } +} + +/** + Check whether IP driver could route the message which will be sent to ServerIp address. + + This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid + route is found in IP6 route table, the address will be filed in GatewayAddr and return. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] TimeOutInSecond Timeout value in seconds. + @param[out] GatewayAddr Pointer to store the gateway IP address. + + @retval EFI_SUCCESS Found a valid gateway address successfully. + @retval EFI_TIMEOUT The operation is time out. + @retval Other Unexpect error happened. + +**/ +EFI_STATUS +PxeBcCheckRouteTable ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINTN TimeOutInSecond, + OUT EFI_IPv6_ADDRESS *GatewayAddr + ) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Index; + EFI_EVENT TimeOutEvt; + UINTN RetryCount; + BOOLEAN GatewayIsFound; + + ASSERT (GatewayAddr != NULL); + ASSERT (Private != NULL); + + Ip6 = Private->Ip6; + GatewayIsFound = FALSE; + RetryCount = 0; + TimeOutEvt = NULL; + ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS)); + + while (TRUE) { + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Find out the gateway address which can route the message which send to ServerIp. + // + for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) { + if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) { + IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway); + GatewayIsFound = TRUE; + break; + } + } + + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + if (Ip6ModeData.GroupTable != NULL) { + FreePool (Ip6ModeData.GroupTable); + } + if (Ip6ModeData.RouteTable != NULL) { + FreePool (Ip6ModeData.RouteTable); + } + if (Ip6ModeData.NeighborCache != NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + if (Ip6ModeData.PrefixTable != NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + if (Ip6ModeData.IcmpTypeList != NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + + if (GatewayIsFound || RetryCount == TimeOutInSecond) { + break; + } + + RetryCount++; + + // + // Delay 1 second then recheck it again. + // + if (TimeOutEvt == NULL) { + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + Ip6->Poll (Ip6); + } + } + +ON_EXIT: + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (GatewayIsFound) { + Status = EFI_SUCCESS; + } else if (RetryCount == TimeOutInSecond) { + Status = EFI_TIMEOUT; + } + + return Status; +} + +/** + Register the ready station address and gateway by Ip6Config protocol. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] Address The pointer to the ready address. + + @retval EFI_SUCCESS Registered the address succesfully. + @retval Others Failed to register the address. + +**/ +EFI_STATUS +PxeBcRegisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_IPv6_ADDRESS *Address + ) +{ + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + EFI_IPv6_ADDRESS GatewayAddr; + UINTN DataSize; + EFI_EVENT MappedEvt; + EFI_STATUS Status; + BOOLEAN NoGateway; + EFI_IPv6_ADDRESS *Ip6Addr; + UINTN Index; + + Status = EFI_SUCCESS; + MappedEvt = NULL; + Ip6Addr = NULL; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Ip6Cfg = Private->Ip6Cfg; + Ip6 = Private->Ip6; + NoGateway = FALSE; + + ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS)); + + Status = Ip6->Configure (Ip6, &Private->Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Retrieve the gateway address from IP6 route table. + // + Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); + if (EFI_ERROR (Status)) { + NoGateway = TRUE; + } + + // + // There is no channel between IP6 and PXE driver about address setting, + // so it has to set the new address by Ip6ConfigProtocol manually. + // + Policy = Ip6ConfigPolicyManual; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + goto ON_EXIT; + } + + // + // Create a notify event to set address flag when DAD if IP6 driver succeeded. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &Private->IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Private->IsAddressOk = FALSE; + Status = Ip6Cfg->RegisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS), + &CfgAddr + ); + if (EFI_ERROR(Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } else if (Status == EFI_NOT_READY) { + // + // Poll the network until the asynchronous process is finished. + // + while (!Private->IsAddressOk) { + Ip6->Poll (Ip6); + } + // + // Check whether the IP6 address setting is successed. + // + DataSize = 0; + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Ip6Addr = AllocatePool (DataSize); + if (Ip6Addr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + (VOID*) Ip6Addr + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) { + if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) { + break; + } + } + if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Set the default gateway address back if needed. + // + if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) { + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeGateway, + sizeof (EFI_IPv6_ADDRESS), + &GatewayAddr + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + +ON_EXIT: + if (MappedEvt != NULL) { + Ip6Cfg->UnregisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + gBS->CloseEvent (MappedEvt); + } + if (Ip6Addr != NULL) { + FreePool (Ip6Addr); + } + return Status; +} + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_POLICY Policy; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + UINTN DataSize; + + Ip6Cfg = Private->Ip6Cfg; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &Private->Ip6Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Private->Ip6Policy == Ip6ConfigPolicyManual) { + Policy = Ip6ConfigPolicyAutomatic; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + } + } + + return Status; +} + +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Private->Dhcp6; + + CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + + Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL); + if (EFI_ERROR (Status)) { + PxeBcUnregisterIp6Address (Private); + Dhcp6->Stop (Dhcp6); + return Status; + } + + AsciiPrint ("\n Station IP address is "); + PxeBcShowIp6Addr (&Private->StationIp.v6); + + return EFI_SUCCESS; +} + +/** + EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This The pointer to the EFI DHCPv6 Protocol. + @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver. + @param[in] Dhcp6Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv6 packet that is going to be sent or was already received. + @param[out] NewPacket The packet that is used to replace the Packet above. + + @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol + driver will continue to wait for more packets. + @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process. + +**/ +EFI_STATUS +EFIAPI +PxeBcDhcp6CallBack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_STATE CurrentState, + IN EFI_DHCP6_EVENT Dhcp6Event, + IN EFI_DHCP6_PACKET *Packet, + OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_DHCP6_PACKET *SelectAd; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp6Event != Dhcp6RcvdAdvertise) && + (Dhcp6Event != Dhcp6SelectAdvertise) && + (Dhcp6Event != Dhcp6SendSolicit) && + (Dhcp6Event != Dhcp6SendRequest) && + (Dhcp6Event != Dhcp6RcvdReply)) { + return EFI_SUCCESS; + } + + ASSERT (Packet != NULL); + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + Callback = Private->PxeBcCallback; + + // + // Callback to user when any traffic ocurred if has. + // + if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) { + Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply); + Status = Callback->Callback ( + Callback, + Private->Function, + Received, + Packet->Length, + (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6 + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + + switch (Dhcp6Event) { + + case Dhcp6SendSolicit: + // + // Record the first Solicate msg time + // + if (Private->SolicitTimes == 0) { + CalcElapsedTime (Private); + Private->SolicitTimes++; + } + // + // Cache the dhcp discover packet to mode data directly. + // + CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length); + break; + + case Dhcp6RcvdAdvertise: + Status = EFI_NOT_READY; + if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { + // + // Cache the dhcp offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + PxeBcCacheDhcp6Offer (Private, Packet); + } + break; + + case Dhcp6SendRequest: + // + // Store the request packet as seed packet for discover. + // + if (Private->Dhcp6Request != NULL) { + FreePool (Private->Dhcp6Request); + } + Private->Dhcp6Request = AllocateZeroPool (Packet->Size); + if (Private->Dhcp6Request != NULL) { + CopyMem (Private->Dhcp6Request, Packet, Packet->Size); + } + break; + + case Dhcp6SelectAdvertise: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + PxeBcSelectDhcp6Offer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + ASSERT (NewPacket != NULL); + SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer; + *NewPacket = AllocateZeroPool (SelectAd->Size); + ASSERT (*NewPacket != NULL); + CopyMem (*NewPacket, SelectAd, SelectAd->Size); + } + break; + + case Dhcp6RcvdReply: + // + // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data + // without verification. + // + ASSERT (Private->SelectIndex != 0); + PxeBcCopyDhcp6Ack (Private, Packet, FALSE); + break; + + default: + ASSERT (0); + } + + return Status; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer The pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp The pointer to the server address. + + @retval EFI_SUCCESS Successfully discovered the boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover the boot file. + +**/ +EFI_STATUS +PxeBcDhcp6Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT SrcPort; + EFI_PXE_BASE_CODE_UDP_PORT DestPort; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; + UINTN DiscoverLen; + EFI_DHCP6_PACKET *Request; + UINTN RequestLen; + EFI_DHCP6_PACKET *Reply; + UINT8 *RequestOpt; + UINT8 *DiscoverOpt; + UINTN ReadSize; + UINT16 OpCode; + UINT16 OpLen; + UINT32 Xid; + EFI_STATUS Status; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Request = Private->Dhcp6Request; + SrcPort = PXEBC_BS_DISCOVER_PORT; + DestPort = PXEBC_BS_DISCOVER_PORT; + + if (!UseBis && Layer != NULL) { + *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; + } + + if (Request == NULL) { + return EFI_DEVICE_ERROR; + } + + Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET)); + if (Discover == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Build the discover packet by the cached request packet before. + // + Xid = NET_RANDOM (NetRandomInitSeed ()); + Discover->TransactionId = HTONL (Xid); + Discover->MessageType = Request->Dhcp6.Header.MessageType; + RequestOpt = Request->Dhcp6.Option; + DiscoverOpt = Discover->DhcpOptions; + DiscoverLen = sizeof (EFI_DHCP6_HEADER); + RequestLen = DiscoverLen; + + while (RequestLen < Request->Length) { + OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode); + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen); + if (OpCode != EFI_DHCP6_IA_TYPE_NA && + OpCode != EFI_DHCP6_IA_TYPE_TA) { + // + // Copy all the options except IA option. + // + CopyMem (DiscoverOpt, RequestOpt, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } + RequestOpt += (OpLen + 4); + RequestLen += (OpLen + 4); + } + + Status = PxeBc->UdpWrite ( + PxeBc, + 0, + &Private->ServerIp, + &DestPort, + NULL, + &Private->StationIp, + &SrcPort, + NULL, + NULL, + &DiscoverLen, + (VOID *) Discover + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + if (Private->IsDoDiscover) { + CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen); + Reply = &Private->PxeReply.Dhcp6.Packet.Ack; + } else { + Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer; + } + ReadSize = (UINTN) Reply->Size; + + // + // Start Udp6Read instance + // + Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PxeBc->UdpRead ( + PxeBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP, + NULL, + &SrcPort, + &Private->ServerIp, + &DestPort, + NULL, + NULL, + &ReadSize, + (VOID *) &Reply->Dhcp6 + ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL + + @retval EFI_SUCCESS The S.A.R.R. process successfully finished. + @retval Others Failed to finish the S.A.R.R. process. + +**/ +EFI_STATUS +PxeBcDhcp6Sarr ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeMode; + EFI_DHCP6_CONFIG_DATA Config; + EFI_DHCP6_MODE_DATA Mode; + EFI_DHCP6_RETRANSMISSION *Retransmit; + EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM]; + UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE]; + UINT32 OptCount; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_STATUS TimerStatus; + EFI_EVENT Timer; + UINT64 GetMappingTimeOut; + UINTN DataSize; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + + Status = EFI_SUCCESS; + PxeMode = Private->PxeBc.Mode; + Ip6Cfg = Private->Ip6Cfg; + Timer = NULL; + + // + // Build option list for the request packet. + // + OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer); + ASSERT (OptCount> 0); + + Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + if (Retransmit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp6Callback = PxeBcDhcp6CallBack; + Config.CallbackContext = Private; + Config.IaInfoEvent = NULL; + Config.RapidCommit = FALSE; + Config.ReconfigureAccept = FALSE; + Config.IaDescriptor.IaId = Private->IaId; + Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Config.SolicitRetransmission = Retransmit; + Retransmit->Irt = 4; + Retransmit->Mrc = 4; + Retransmit->Mrt = 32; + Retransmit->Mrd = 60; + + // + // Configure the DHCPv6 instance for PXE boot. + // + Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize the record fields for DHCPv6 offer in private data. + // + Private->IsProxyRecved = FALSE; + Private->OfferNum = 0; + Private->SelectIndex = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + + // + // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. + // + Status = Dhcp6->Start (Dhcp6); + if (Status == EFI_NO_MAPPING) { + // + // IP6 Linklocal address is not available for use, so stop current Dhcp process + // and wait for duplicate address detection to finish. + // + Dhcp6->Stop (Dhcp6); + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY; + Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Timer); + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->Start (Dhcp6); + } + } while (TimerStatus == EFI_NOT_READY); + + gBS->CloseEvent (Timer); + } + if (EFI_ERROR (Status)) { + if (Status == EFI_ICMP_ERROR) { + PxeMode->IcmpErrorReceived = TRUE; + } + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + // + // Get the acquired IPv6 address and store them. + // + Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + ASSERT (Mode.Ia->State == Dhcp6Bound); + // + // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the + // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when + // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as + // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery + // to find a valid router address. + // + CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); + + // + // Check the selected offer whether BINL retry is needed. + // + Status = PxeBcHandleDhcp6Offer (Private); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + return EFI_SUCCESS; +} diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h new file mode 100644 index 0000000000..38bf26564d --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h @@ -0,0 +1,303 @@ +/** @file + Functions declaration related with DHCPv6 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2014, 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 __EFI_PXEBC_DHCP6_H__ +#define __EFI_PXEBC_DHCP6_H__ + +#define PXEBC_DHCP6_OPTION_MAX_NUM 16 +#define PXEBC_DHCP6_OPTION_MAX_SIZE 312 +#define PXEBC_DHCP6_PACKET_MAX_SIZE 1472 +#define PXEBC_IP6_POLICY_MAX 0xff +#define PXEBC_IP6_ROUTE_TABLE_TIMEOUT 10 + +#define PXEBC_DHCP6_S_PORT 547 +#define PXEBC_DHCP6_C_PORT 546 + +#define PXEBC_DHCP6_OPT_CLIENT_ID 1 +#define PXEBC_DHCP6_OPT_SERVER_ID 2 +#define PXEBC_DHCP6_OPT_IA_NA 3 +#define PXEBC_DHCP6_OPT_IA_TA 4 +#define PXEBC_DHCP6_OPT_IAADDR 5 +#define PXEBC_DHCP6_OPT_ORO 6 +#define PXEBC_DHCP6_OPT_PREFERENCE 7 +#define PXEBC_DHCP6_OPT_ELAPSED_TIME 8 +#define PXEBC_DHCP6_OPT_REPLAY_MSG 9 +#define PXEBC_DHCP6_OPT_AUTH 11 +#define PXEBC_DHCP6_OPT_UNICAST 12 +#define PXEBC_DHCP6_OPT_STATUS_CODE 13 +#define PXEBC_DHCP6_OPT_RAPID_COMMIT 14 +#define PXEBC_DHCP6_OPT_USER_CLASS 15 +#define PXEBC_DHCP6_OPT_VENDOR_CLASS 16 +#define PXEBC_DHCP6_OPT_VENDOR_OPTS 17 +#define PXEBC_DHCP6_OPT_INTERFACE_ID 18 +#define PXEBC_DHCP6_OPT_RECONFIG_MSG 19 +#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT 20 +#define PXEBC_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's +#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes. + + +#define PXEBC_DHCP6_IDX_IA_NA 0 +#define PXEBC_DHCP6_IDX_BOOT_FILE_URL 1 +#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM 2 +#define PXEBC_DHCP6_IDX_VENDOR_CLASS 3 +#define PXEBC_DHCP6_IDX_MAX 4 + +#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX "tftp://" +#define PXEBC_TFTP_URL_SEPARATOR '/' +#define PXEBC_ADDR_START_DELIMITER '[' +#define PXEBC_ADDR_END_DELIMITER ']' + +#define GET_NEXT_DHCP6_OPTION(Opt) \ + (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1) + +#define GET_DHCP6_OPTION_SIZE(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER)) + +#define IS_PROXY_OFFER(Type) \ + ((Type) == PxeOfferTypeProxyBinl || \ + (Type) == PxeOfferTypeProxyPxe10 || \ + (Type) == PxeOfferTypeProxyWfm11a) + + +#pragma pack(1) +typedef struct { + UINT16 OpCode[256]; +} PXEBC_DHCP6_OPTION_ORO; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} PXEBC_DHCP6_OPTION_UNDI; + +typedef struct { + UINT16 Type; +} PXEBC_DHCP6_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} PXEBC_CLASS_ID; + +typedef struct { + UINT32 Vendor; + UINT16 ClassLen; + PXEBC_CLASS_ID ClassId; +} PXEBC_DHCP6_OPTION_VENDOR_CLASS; + +#pragma pack() + +typedef union { + PXEBC_DHCP6_OPTION_ORO *Oro; + PXEBC_DHCP6_OPTION_UNDI *Undi; + PXEBC_DHCP6_OPTION_ARCH *Arch; + PXEBC_DHCP6_OPTION_VENDOR_CLASS *VendorClass; +} PXEBC_DHCP6_OPTION_ENTRY; + +typedef struct { + LIST_ENTRY Link; + EFI_DHCP6_PACKET_OPTION *Option; + UINT8 Precedence; +} PXEBC_DHCP6_OPTION_NODE; + +typedef union { + EFI_DHCP6_PACKET Offer; + EFI_DHCP6_PACKET Ack; + UINT8 Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE]; +} PXEBC_DHCP6_PACKET; + +typedef struct { + PXEBC_DHCP6_PACKET Packet; + PXEBC_OFFER_TYPE OfferType; + EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX]; +} PXEBC_DHCP6_PACKET_CACHE; + + +/** + Free all the nodes in the boot file list. + + @param[in] Head The pointer to the head of the list. + +**/ +VOID +PxeBcFreeBootFileOption ( + IN LIST_ENTRY *Head + ); + + +/** + Parse the Boot File URL option. + + @param[out] FileName The pointer to the boot file name. + @param[in, out] SrvAddr The pointer to the boot server address. + @param[in] BootFile The pointer to the boot file URL option data. + @param[in] Length Length of the boot file URL option data. + + @retval EFI_ABORTED User canceled the operation. + @retval EFI_SUCCESS Selected the boot menu successfully. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcExtractBootFileUrl ( + OUT UINT8 **FileName, + IN OUT EFI_IPv6_ADDRESS *SrvAddr, + IN CHAR8 *BootFile, + IN UINT16 Length + ); + + +/** + Parse the Boot File Parameter option. + + @param[in] BootFilePara The pointer to the boot file parameter option data. + @param[out] BootFileSize The pointer to the parsed boot file size. + + @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option. + @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option. + +**/ +EFI_STATUS +PxeBcExtractBootFileParam ( + IN CHAR8 *BootFilePara, + OUT UINT16 *BootFileSize + ); + + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp6Packet ( + IN PXEBC_DHCP6_PACKET_CACHE *Cache6 + ); + + +/** + Register the ready address by Ip6Config protocol. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Address The pointer to the ready address. + + @retval EFI_SUCCESS Registered the address succesfully. + @retval Others Failed to register the address. + +**/ +EFI_STATUS +PxeBcRegisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_IPv6_ADDRESS *Address + ); + + +/** + Unregister the address by Ip6Config protocol. + + @param[in] Private The pointer to the PxeBc private data. + +**/ +VOID +PxeBcUnregisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ); + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer The pointer to the option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp The pointer to the server address. + + @retval EFI_SUCCESS Successfully discovered theboot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp6Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp + ); + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Dhcp6 The pointer to EFI_DHCP6_PROTOCOL. + + @retval EFI_SUCCESS The S.A.R.R. process successfully finished. + @retval Others Failed to finish the S.A.R.R. process. + +**/ +EFI_STATUS +PxeBcDhcp6Sarr ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ); + +#endif + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c new file mode 100644 index 0000000000..a6f66682f3 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c @@ -0,0 +1,1825 @@ +/** @file + Driver Binding functions implementationfor for UefiPxeBc Driver. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2007 - 2015, 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. + +**/ + +#include "PxeBcImpl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gPxeBcIp4DriverBinding = { + PxeBcIp4DriverBindingSupported, + PxeBcIp4DriverBindingStart, + PxeBcIp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gPxeBcIp6DriverBinding = { + PxeBcIp6DriverBindingSupported, + PxeBcIp6DriverBindingStart, + PxeBcIp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + } + } + } + + return NicHandle; +} + + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + } + } + + return NicHandle; +} + + +/** + Destroy the opened instances based on IPv4. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcDestroyIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + ASSERT(Private != NULL); + + if (Private->ArpChild != NULL) { + // + // Close Arp for PxeBc->Arp and destroy the instance. + // + gBS->CloseProtocol ( + Private->ArpChild, + &gEfiArpProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiArpServiceBindingProtocolGuid, + Private->ArpChild + ); + } + + if (Private->Ip4Child != NULL) { + // + // Close Ip4 for background ICMP error message and destroy the instance. + // + gBS->CloseProtocol ( + Private->Ip4Child, + &gEfiIp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp4ServiceBindingProtocolGuid, + Private->Ip4Child + ); + } + + if (Private->Udp4WriteChild != NULL) { + // + // Close Udp4 for PxeBc->UdpWrite and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp4WriteChild, + &gEfiUdp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + Private->Udp4WriteChild + ); + } + + if (Private->Udp4ReadChild != NULL) { + // + // Close Udp4 for PxeBc->UdpRead and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp4ReadChild, + &gEfiUdp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + Private->Udp4ReadChild + ); + } + + if (Private->Mtftp4Child != NULL) { + // + // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance. + // + gBS->CloseProtocol ( + Private->Mtftp4Child, + &gEfiMtftp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + Private->Mtftp4Child + ); + } + + if (Private->Dhcp4Child != NULL) { + // + // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance. + // + gBS->CloseProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Private->Dhcp4Child + ); + } + + if (Private->Ip4Nic != NULL) { + // + // Close PxeBcPrivate from the parent Nic handle and destroy the virtual handle. + // + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + + if (Private->Snp != NULL) { + // + // Close SNP from the child virtual handle + // + gBS->CloseProtocol ( + Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallProtocolInterface ( + Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + Private->Snp + ); + } + FreePool (Private->Ip4Nic); + } + + Private->ArpChild = NULL; + Private->Ip4Child = NULL; + Private->Udp4WriteChild = NULL; + Private->Udp4ReadChild = NULL; + Private->Mtftp4Child = NULL; + Private->Dhcp4Child = NULL; + Private->Ip4Nic = NULL; +} + + +/** + Destroy the opened instances based on IPv6. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcDestroyIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + ASSERT(Private != NULL); + + if (Private->Ip6Child != NULL) { + // + // Close Ip6 for Ip6->Ip6Config and destroy the instance. + // + gBS->CloseProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + Private->Ip6Child + ); + } + + if (Private->Udp6WriteChild != NULL) { + // + // Close Udp6 for PxeBc->UdpWrite and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp6WriteChild, + &gEfiUdp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + Private->Udp6WriteChild + ); + } + + if (Private->Udp6ReadChild != NULL) { + // + // Close Udp6 for PxeBc->UdpRead and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp6ReadChild, + &gEfiUdp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + Private->Udp6ReadChild + ); + } + + if (Private->Mtftp6Child != NULL) { + // + // Close Mtftp6 for PxeBc->Mtftp and destroy the instance. + // + gBS->CloseProtocol ( + Private->Mtftp6Child, + &gEfiMtftp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + Private->Mtftp6Child + ); + } + + if (Private->Dhcp6Child != NULL) { + // + // Close Dhcp6 for PxeBc->Dhcp and destroy the instance. + // + gBS->CloseProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + Private->Dhcp6Child + ); + } + + if (Private->Ip6Nic != NULL) { + // + // Close PxeBcPrivate from the parent Nic handle and destroy the virtual handle. + // + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + if (Private->Snp != NULL) { + // + // Close SNP from the child virtual handle + // + gBS->CloseProtocol ( + Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + gBS->UninstallProtocolInterface ( + Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + Private->Snp + ); + } + FreePool (Private->Ip6Nic); + } + + Private->Ip6Child = NULL; + Private->Udp6WriteChild = NULL; + Private->Udp6ReadChild = NULL; + Private->Mtftp6Child = NULL; + Private->Dhcp6Child = NULL; + Private->Ip6Nic = NULL; + Private->Mode.Ipv6Available = FALSE; +} + +/** + Check whether UNDI protocol supports IPv6. + + @param[in] ControllerHandle Controller handle. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + @param[out] Ipv6Support TRUE if UNDI supports IPv6. + + @retval EFI_SUCCESS Get the result whether UNDI supports IPv6 by NII or AIP protocol successfully. + @retval EFI_NOT_FOUND Don't know whether UNDI supports IPv6 since NII or AIP is not available. + +**/ +EFI_STATUS +PxeBcCheckIpv6Support ( + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private, + OUT BOOLEAN *Ipv6Support + ) +{ + EFI_HANDLE Handle; + EFI_ADAPTER_INFORMATION_PROTOCOL *Aip; + EFI_STATUS Status; + EFI_GUID *InfoTypesBuffer; + UINTN InfoTypeBufferCount; + UINTN TypeIndex; + BOOLEAN Supported; + VOID *InfoBlock; + UINTN InfoBlockSize; + + ASSERT (Private != NULL && Ipv6Support != NULL); + + // + // Check whether the UNDI supports IPv6 by NII protocol. + // + if (Private->Nii != NULL) { + *Ipv6Support = Private->Nii->Ipv6Supported; + return EFI_SUCCESS; + } + + // + // Check whether the UNDI supports IPv6 by AIP protocol. + // + + // + // Get the NIC handle by SNP protocol. + // + Handle = NetLibGetSnpHandle (ControllerHandle, NULL); + if (Handle == NULL) { + return EFI_NOT_FOUND; + } + + Aip = NULL; + Status = gBS->HandleProtocol ( + Handle, + &gEfiAdapterInformationProtocolGuid, + (VOID *) &Aip + ); + if (EFI_ERROR (Status) || Aip == NULL) { + return EFI_NOT_FOUND; + } + + InfoTypesBuffer = NULL; + InfoTypeBufferCount = 0; + Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount); + if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) { + FreePool (InfoTypesBuffer); + return EFI_NOT_FOUND; + } + + Supported = FALSE; + for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) { + if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoUndiIpv6SupportGuid)) { + Supported = TRUE; + break; + } + } + + FreePool (InfoTypesBuffer); + if (!Supported) { + return EFI_NOT_FOUND; + } + + // + // We now have adapter information block. + // + InfoBlock = NULL; + InfoBlockSize = 0; + Status = Aip->GetInformation (Aip, &gEfiAdapterInfoUndiIpv6SupportGuid, &InfoBlock, &InfoBlockSize); + if (EFI_ERROR (Status) || InfoBlock == NULL) { + FreePool (InfoBlock); + return EFI_NOT_FOUND; + } + + *Ipv6Support = ((EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) InfoBlock)->Ipv6Support; + FreePool (InfoBlock); + return EFI_SUCCESS; + +} + +/** + Create the opened instances based on IPv4. + + @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL. + @param[in] ControllerHandle Handle of the child to destroy. + @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The instances based on IPv4 were all created successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +PxeBcCreateIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + IPv4_DEVICE_PATH Ip4Node; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_CONFIG_DATA *Udp4CfgData; + EFI_IP4_CONFIG_DATA *Ip4CfgData; + EFI_IP4_MODE_DATA Ip4ModeData; + PXEBC_PRIVATE_PROTOCOL *Id; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + if (Private->Ip4Nic != NULL) { + // + // Already created before. + // + return EFI_SUCCESS; + } + + // + // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Private->Dhcp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Private->Dhcp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &Private->Mtftp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Mtftp4Child, + &gEfiMtftp4ProtocolGuid, + (VOID **) &Private->Mtftp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Private->Udp4ReadChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp4ReadChild, + &gEfiUdp4ProtocolGuid, + (VOID **) &Private->Udp4Read, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Private->Udp4WriteChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp4WriteChild, + &gEfiUdp4ProtocolGuid, + (VOID **) &Private->Udp4Write, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Arp child and open Arp protocol for PxeBc->Arp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiArpServiceBindingProtocolGuid, + &Private->ArpChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->ArpChild, + &gEfiArpProtocolGuid, + (VOID **) &Private->Arp, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip4 child and open Ip4 protocol for background ICMP packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &Private->Ip4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip4Child, + &gEfiIp4ProtocolGuid, + (VOID **) &Private->Ip4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get max packet size from Ip4 to calculate block size for Tftp later. + // + Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize; + + Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC)); + if (Private->Ip4Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Ip4Nic->Private = Private; + Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE; + + // + // Locate Ip4->Ip4Config2 and store it for set IPv4 Policy. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiIp4Config2ProtocolGuid, + (VOID **) &Private->Ip4Config2 + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create a device path node for Ipv4 virtual nic, and append it. + // + ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH)); + Ip4Node.Header.Type = MESSAGING_DEVICE_PATH; + Ip4Node.Header.SubType = MSG_IPv4_DP; + Ip4Node.StaticIpAddress = FALSE; + + SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node)); + + Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header); + + if (Private->Ip4Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + CopyMem ( + &Private->Ip4Nic->LoadFile, + &gLoadFileProtocolTemplate, + sizeof (EFI_LOAD_FILE_PROTOCOL) + ); + + // + // Create a new handle for IPv4 virtual nic, + // and install PxeBaseCode, LoadFile and DevicePath protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Private->Snp != NULL) { + // + // Install SNP protocol on purpose is for some OS loader backward + // compatibility consideration. + // + Status = gBS->InstallProtocolInterface ( + &Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + EFI_NATIVE_INTERFACE, + Private->Snp + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open SNP on the child handle BY_DRIVER. It will prevent any additionally + // layering to perform the experiment. + // + Status = gBS->OpenProtocol ( + Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Open PxeBaseCodePrivate protocol by child to setup a parent-child relationship between + // real NIC handle and the virtual IPv4 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set default configure data for Udp4Read and Ip4 instance. + // + Mode = Private->PxeBc.Mode; + Udp4CfgData = &Private->Udp4CfgData; + Ip4CfgData = &Private->Ip4CfgData; + + Udp4CfgData->AcceptBroadcast = FALSE; + Udp4CfgData->AcceptAnyPort = TRUE; + Udp4CfgData->AllowDuplicatePort = TRUE; + Udp4CfgData->TypeOfService = Mode->ToS; + Udp4CfgData->TimeToLive = Mode->TTL; + Udp4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + Ip4CfgData->AcceptIcmpErrors = TRUE; + Ip4CfgData->DefaultProtocol = EFI_IP_PROTO_ICMP; + Ip4CfgData->TypeOfService = Mode->ToS; + Ip4CfgData->TimeToLive = Mode->TTL; + Ip4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Ip4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + return EFI_SUCCESS; + +ON_ERROR: + PxeBcDestroyIp4Children (This, Private); + return Status; +} + + +/** + Create the opened instances based on IPv6. + + @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL. + @param[in] ControllerHandle Handle of the child to destroy. + @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The instances based on IPv6 were all created successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +PxeBcCreateIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + IPv6_DEVICE_PATH Ip6Node; + EFI_UDP6_CONFIG_DATA *Udp6CfgData; + EFI_IP6_CONFIG_DATA *Ip6CfgData; + EFI_IP6_MODE_DATA Ip6ModeData; + PXEBC_PRIVATE_PROTOCOL *Id; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + UINTN Index; + + if (Private->Ip6Nic != NULL) { + // + // Already created before. + // + return EFI_SUCCESS; + } + + Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC)); + + if (Private->Ip6Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Ip6Nic->Private = Private; + Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE; + + // + // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Private->Dhcp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Private->Dhcp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Generate a random IAID for the Dhcp6 assigned address. + // + Private->IaId = NET_RANDOM (NetRandomInitSeed ()); + if (Private->Snp != NULL) { + for (Index = 0; Index < Private->Snp->Mode->HwAddressSize; Index++) { + Private->IaId |= (Private->Snp->Mode->CurrentAddress.Addr[Index] << ((Index << 3) & 31)); + } + } + + // + // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + &Private->Mtftp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Mtftp6Child, + &gEfiMtftp6ProtocolGuid, + (VOID **) &Private->Mtftp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Private->Udp6ReadChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp6ReadChild, + &gEfiUdp6ProtocolGuid, + (VOID **) &Private->Udp6Read, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Private->Udp6WriteChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp6WriteChild, + &gEfiUdp6ProtocolGuid, + (VOID **) &Private->Udp6Write, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip6 child and open Ip6 protocol for background ICMP6 packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &Private->Ip6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get max packet size from Ip6 to calculate block size for Tftp later. + // + Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize; + + // + // Locate Ip6->Ip6Config and store it for set IPv6 address. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Private->Ip6Cfg + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create a device path node for Ipv6 virtual nic, and append it. + // + ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH)); + Ip6Node.Header.Type = MESSAGING_DEVICE_PATH; + Ip6Node.Header.SubType = MSG_IPv6_DP; + Ip6Node.PrefixLength = IP6_PREFIX_LENGTH; + + SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node)); + + Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header); + + if (Private->Ip6Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + CopyMem ( + &Private->Ip6Nic->LoadFile, + &gLoadFileProtocolTemplate, + sizeof (EFI_LOAD_FILE_PROTOCOL) + ); + + // + // Create a new handle for IPv6 virtual nic, + // and install PxeBaseCode, LoadFile and DevicePath protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Private->Snp != NULL) { + // + // Install SNP protocol on purpose is for some OS loader backward + // compatibility consideration. + // + Status = gBS->InstallProtocolInterface ( + &Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + EFI_NATIVE_INTERFACE, + Private->Snp + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open SNP on the child handle BY_DRIVER. It will prevent any additionally + // layering to perform the experiment. + // + Status = gBS->OpenProtocol ( + Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Open PxeBaseCodePrivate protocol by child to setup a parent-child relationship between + // real NIC handle and the virtual IPv6 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set IPv6 avaiable flag and set default configure data for + // Udp6Read and Ip6 instance. + // + Status = PxeBcCheckIpv6Support (ControllerHandle, Private, &Private->Mode.Ipv6Available); + if (EFI_ERROR (Status)) { + // + // Fail to get the data whether UNDI supports IPv6. Set default value. + // + Private->Mode.Ipv6Available = TRUE; + } + + if (!Private->Mode.Ipv6Available) { + goto ON_ERROR; + } + + Udp6CfgData = &Private->Udp6CfgData; + Ip6CfgData = &Private->Ip6CfgData; + + Udp6CfgData->AcceptAnyPort = TRUE; + Udp6CfgData->AllowDuplicatePort = TRUE; + Udp6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT; + Udp6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + Ip6CfgData->AcceptIcmpErrors = TRUE; + Ip6CfgData->DefaultProtocol = IP6_ICMP; + Ip6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT; + Ip6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Ip6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + return EFI_SUCCESS; + +ON_ERROR: + PxeBcDestroyIp6Children (This, Private); + return Status; +} + + +/** + The entry point for UefiPxeBc driver that installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The Image handle of the driver. + @param[in] SystemTable The system table. + + @return EFI_SUCCESS + @return Others + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPxeBcIp4DriverBinding, + ImageHandle, + &gPxeBcComponentName, + &gPxeBcComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPxeBcIp6DriverBinding, + NULL, + &gPxeBcComponentName, + &gPxeBcComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gPxeBcIp4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gPxeBcComponentName2, + &gEfiComponentNameProtocolGuid, + &gPxeBcComponentName, + NULL + ); + } + + return Status; +} + +/** + Test to see if this driver supports ControllerHandle. This is the worker function for + PxeBcIp4(6)DriverBindingSupported. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *DhcpServiceBindingGuid; + EFI_GUID *MtftpServiceBindingGuid; + + if (IpVersion == IP_VERSION_4) { + DhcpServiceBindingGuid = &gEfiDhcp4ServiceBindingProtocolGuid; + MtftpServiceBindingGuid = &gEfiMtftp4ServiceBindingProtocolGuid; + } else { + DhcpServiceBindingGuid = &gEfiDhcp6ServiceBindingProtocolGuid; + MtftpServiceBindingGuid = &gEfiMtftp6ServiceBindingProtocolGuid; + } + + // + // Try to open the Mtftp and Dhcp protocol to test whether IP stack is ready. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + DhcpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + ControllerHandle, + MtftpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + } + + // + // It's unsupported case if IP stack are not ready. + // + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle. This is the worker function for + PxeBcIp4(6)DriverBindingStart. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_STATUS Status; + PXEBC_PRIVATE_PROTOCOL *Id; + BOOLEAN FirstStart; + + FirstStart = FALSE; + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Skip the initialization if the driver has been started already. + // + Private = PXEBC_PRIVATE_DATA_FROM_ID (Id); + } else { + FirstStart = TRUE; + // + // If the driver has not been started yet, it should do initialization. + // + Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + &Private->PxeBc, + &gPxeBcProtocolTemplate, + sizeof (EFI_PXE_BASE_CODE_PROTOCOL) + ); + + Private->Signature = PXEBC_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + Private->PxeBc.Mode = &Private->Mode; + Private->Mode.Ipv6Supported = TRUE; + Private->Mode.AutoArp = TRUE; + Private->Mode.TTL = DEFAULT_TTL; + Private->Mode.ToS = DEFAULT_ToS; + + // + // Open device path to prepare for appending virtual NIC node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Install PxeBaseCodePrivate protocol onto the real NIC handler. + // PxeBaseCodePrivate protocol is only used to keep the relationship between + // NIC handle and virtual child handles. + // gEfiCallerIdGuid will be used as its protocol guid. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Try to locate SNP protocol. + // + NetLibGetSnpHandle(ControllerHandle, &Private->Snp); + } + + if (IpVersion == IP_VERSION_4) { + // + // Try to create virtual NIC handle for IPv4. + // + Status = PxeBcCreateIp4Children (This, ControllerHandle, Private); + } else { + // + // Try to create virtual NIC handle for IPv6. + // + Status = PxeBcCreateIp6Children (This, ControllerHandle, Private); + } + if (EFI_ERROR (Status)) { + // + // Failed to start PXE driver if IPv4 and IPv6 stack are both not available. + // + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (FirstStart) { + gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + } + + if (IpVersion == IP_VERSION_4) { + PxeBcDestroyIp4Children (This, Private); + } else { + PxeBcDestroyIp6Children (This, Private); + } + + if (FirstStart && Private != NULL) { + FreePool (Private); + } + + return Status; +} + + +/** + Stop this driver on ControllerHandle. This is the worker function for + PxeBcIp4(6)DriverBindingStop. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver was removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +PxeBcStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, + IN UINT8 IpVersion + ) +{ + PXEBC_PRIVATE_DATA *Private; + PXEBC_VIRTUAL_NIC *VirtualNic; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + EFI_STATUS Status; + EFI_HANDLE NicHandle; + PXEBC_PRIVATE_PROTOCOL *Id; + + Private = NULL; + NicHandle = NULL; + VirtualNic = NULL; + LoadFile = NULL; + Id = NULL; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Get the Nic handle by any pass-over service child handle. + // + if (IpVersion == IP_VERSION_4) { + NicHandle = PxeBcGetNicByIp4Children (ControllerHandle); + } else { + NicHandle = PxeBcGetNicByIp6Children (ControllerHandle); + } + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Try to retrieve the private data by PxeBcPrivate protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private = PXEBC_PRIVATE_DATA_FROM_ID (Id); + + } else { + // + // It's a virtual handle with LoadFileProtocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile); + Private = VirtualNic->Private; + NicHandle = Private->Controller; + } + + // + // Stop functionality of PXE Base Code protocol + // + Status = Private->PxeBc.Stop (&Private->PxeBc); + if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) { + return Status; + } + + + if (Private->Ip4Nic != NULL && IpVersion == IP_VERSION_4) { + PxeBcDestroyIp4Children (This, Private); + } + + if (Private->Ip6Nic != NULL && IpVersion == IP_VERSION_6) { + PxeBcDestroyIp6Children (This, Private); + } + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + } + + return EFI_SUCCESS; +} + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return PxeBcStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return PxeBcStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h new file mode 100644 index 0000000000..f22f0f6bb3 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h @@ -0,0 +1,181 @@ +/** @file + Driver Binding functions declaration for UefiPxeBc Driver. + + Copyright (c) 2007 - 2013, 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 __EFI_PXEBC_DRIVER_H__ +#define __EFI_PXEBC_DRIVER_H__ + +extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); +#endif + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c new file mode 100644 index 0000000000..12e5566a79 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c @@ -0,0 +1,2411 @@ +/** @file + This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL. + + Copyright (c) 2007 - 2015, 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. + +**/ + +#include "PxeBcImpl.h" + + +/** + Enables the use of the PXE Base Code Protocol functions. + + This function enables the use of the PXE Base Code Protocol functions. If the + Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then + EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted + addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted + addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported + field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will + be returned. If there is not enough memory or other resources to start the PXE + Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the + PXE Base Code Protocol will be started. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] UseIpv6 Specifies the type of IP addresses that are to be + used during the session that is being started. + Set to TRUE for IPv6, and FALSE for IPv4. + + @retval EFI_SUCCESS The PXE Base Code Protocol was started. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the + EFI_PXE_BASE_CODE_MODE structure is FALSE. + @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the + PXE Base Code Protocol. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIpv6 + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + UINTN Index; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (Mode->Started) { + return EFI_ALREADY_STARTED; + } + + // + // Detect whether using IPv6 or not, and set it into mode data. + // + if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) { + Mode->UsingIpv6 = TRUE; + } else if (!UseIpv6 && Private->Ip4Nic != NULL) { + Mode->UsingIpv6 = FALSE; + } else { + return EFI_UNSUPPORTED; + } + + if (Mode->UsingIpv6) { + AsciiPrint ("\n>>Start PXE over IPv6"); + // + // Configure udp6 instance to receive data. + // + Status = Private->Udp6Read->Configure ( + Private->Udp6Read, + &Private->Udp6CfgData + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Configure block size for TFTP as a default value to handle all link layers. + // + Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize - + PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE); + + // + // PXE over IPv6 starts here, initialize the fields and list header. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + Private->DhcpAck.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + Private->PxeReply.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + + for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + } + + // + // Create event and set status for token to capture ICMP6 error message. + // + Private->Icmp6Token.Status = EFI_NOT_READY; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcIcmp6ErrorUpdate, + Private, + &Private->Icmp6Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set Ip6 policy to Automatic to start the IP6 router discovery. + // + Status = PxeBcSetIp6Policy (Private); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } else { + AsciiPrint ("\n>>Start PXE over IPv4"); + // + // Configure udp4 instance to receive data. + // + Status = Private->Udp4Read->Configure ( + Private->Udp4Read, + &Private->Udp4CfgData + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Configure block size for TFTP as a default value to handle all link layers. + // + Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize - + PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE); + + // + // PXE over IPv4 starts here, initialize the fields. + // + Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + Private->DhcpAck.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + Private->PxeReply.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + + for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + } + + PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read); + + // + // Create the event for Arp cache update. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + PxeBcArpCacheUpdate, + Private, + &Private->ArpUpdateEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Start a periodic timer by second to update Arp cache. + // + Status = gBS->SetTimer ( + Private->ArpUpdateEvent, + TimerPeriodic, + TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create event and set status for token to capture ICMP error message. + // + Private->Icmp6Token.Status = EFI_NOT_READY; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcIcmpErrorUpdate, + Private, + &Private->IcmpToken.Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + //DHCP4 service allows only one of its children to be configured in + //the active state, If the DHCP4 D.O.R.A started by IP4 auto + //configuration and has not been completed, the Dhcp4 state machine + //will not be in the right state for the PXE to start a new round D.O.R.A. + //so we need to switch it's policy to static. + // + Status = PxeBcSetIp4Policy (Private); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // If PcdTftpBlockSize is set to non-zero, override the default value. + // + if (PcdGet64 (PcdTftpBlockSize) != 0) { + Private->BlockSize = (UINTN) PcdGet64 (PcdTftpBlockSize); + } + + // + // Create event for UdpRead/UdpWrite timeout since they are both blocking API. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->UdpTimeOutEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->IsAddressOk = FALSE; + Mode->Started = TRUE; + + return EFI_SUCCESS; + +ON_ERROR: + if (Mode->UsingIpv6) { + if (Private->Icmp6Token.Event != NULL) { + gBS->CloseEvent (Private->Icmp6Token.Event); + Private->Icmp6Token.Event = NULL; + } + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Private->Ip6->Configure (Private->Ip6, NULL); + } else { + if (Private->ArpUpdateEvent != NULL) { + gBS->CloseEvent (Private->ArpUpdateEvent); + Private->ArpUpdateEvent = NULL; + } + if (Private->IcmpToken.Event != NULL) { + gBS->CloseEvent (Private->IcmpToken.Event); + Private->IcmpToken.Event = NULL; + } + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Private->Ip4->Configure (Private->Ip4, NULL); + } + return Status; +} + + +/** + Disable the use of the PXE Base Code Protocol functions. + + This function stops all activity on the network device. All the resources allocated + in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is + set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE + structure is already FALSE, then EFI_NOT_STARTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + + @retval EFI_SUCCESS The PXE Base Code Protocol was stopped. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval Others + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + BOOLEAN Ipv6Supported; + BOOLEAN Ipv6Available; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Ipv6Supported = Mode->Ipv6Supported; + Ipv6Available = Mode->Ipv6Available; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + // + // Configure all the instances for IPv6 as NULL. + // + ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS)); + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + Private->Udp6Write->Configure (Private->Udp6Write, NULL); + Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL); + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token); + Private->Ip6->Configure (Private->Ip6, NULL); + PxeBcUnregisterIp6Address (Private); + if (Private->Icmp6Token.Event != NULL) { + gBS->CloseEvent (Private->Icmp6Token.Event); + Private->Icmp6Token.Event = NULL; + } + if (Private->Dhcp6Request != NULL) { + FreePool (Private->Dhcp6Request); + Private->Dhcp6Request = NULL; + } + if (Private->BootFileName != NULL) { + FreePool (Private->BootFileName); + Private->BootFileName = NULL; + } + } else { + // + // Configure all the instances for IPv4 as NULL. + // + ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + Private->Udp4Write->Configure (Private->Udp4Write, NULL); + Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL); + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken); + Private->Ip4->Configure (Private->Ip4, NULL); + if (Private->ArpUpdateEvent != NULL) { + gBS->CloseEvent (Private->ArpUpdateEvent); + Private->ArpUpdateEvent = NULL; + } + if (Private->IcmpToken.Event != NULL) { + gBS->CloseEvent (Private->IcmpToken.Event); + Private->IcmpToken.Event = NULL; + } + Private->BootFileName = NULL; + } + + gBS->CloseEvent (Private->UdpTimeOutEvent); + Private->CurSrcPort = 0; + Private->BootFileSize = 0; + Private->SolicitTimes = 0; + Private->ElapsedTime = 0; + ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + // + // Reset the mode data. + // + ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE)); + Mode->Ipv6Available = Ipv6Available; + Mode->Ipv6Supported = Ipv6Supported; + Mode->AutoArp = TRUE; + Mode->TTL = DEFAULT_TTL; + Mode->ToS = DEFAULT_ToS; + + return EFI_SUCCESS; +} + + +/** + Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6 + S.A.R.R (solicit / advertise / request / reply) sequence. + + If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before + they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will + be tried in the order in which they are received. Please see the Preboot Execution + Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI) + Specification for additional details on the implementation of DHCP. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the DHCP sequence will be stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] SortOffers TRUE if the offers received should be sorted. Set to FALSE to + try the offers in the order that they are received. + + @retval EFI_SUCCESS Valid DHCP has completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol. + @retval EFI_ABORTED The callback function aborted the DHCP Protocol. + @retval EFI_TIMEOUT The DHCP Protocol timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session. + @retval EFI_NO_RESPONSE Valid PXE offer was not received. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Mode->IcmpErrorReceived = FALSE; + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP; + Private->IsOfferSorted = SortOffers; + Private->SolicitTimes = 0; + Private->ElapsedTime = 0; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + // + // Start S.A.R.R. process to get a IPv6 address and other boot information. + // + Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6); + } else { + + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + + // + // Start D.O.R.A. process to get a IPv4 address and other boot information. + // + Status = PxeBcDhcp4Dora (Private, Private->Dhcp4); + } + + // + // Reconfigure the UDP instance with the default configuration. + // + if (Mode->UsingIpv6) { + Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + } else { + Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData); + } + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Attempts to complete the PXE Boot Server and/or boot image discovery sequence. + + This function attempts to complete the PXE Boot Server and/or boot image discovery + sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the + PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the + EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the + PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure + will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE. + In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[], + has two uses: It is the Boot Server IP address list used for unicast discovery + (if the UseUCast field is TRUE), and it is the list used for Boot Server verification + (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure + is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot + Server reply of that type will be accepted. If the AcceptAnyResponse field is + FALSE, only responses from Boot Servers with matching IP addresses will be accepted. + This function can take at least 10 seconds to timeout and return control to the + caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be + returned. Please see the Preboot Execution Environment (PXE) Specification for + additional details on the implementation of the Discovery sequence. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the Discovery sequence is stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] Type The type of bootstrap to perform. + @param[in] Layer Pointer to the boot server layer number to discover, which must be + PXE_BOOT_LAYER_INITIAL when a new server type is being + discovered. + @param[in] UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise. + @param[in] Info Pointer to a data structure that contains additional information + on the type of discovery operation that is to be performed. + It is optional. + + @retval EFI_SUCCESS The Discovery sequence has been completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery. + @retval EFI_ABORTED The callback function aborted the Discovery sequence. + @retval EFI_TIMEOUT The Discovery sequence timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery + session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo; + EFI_PXE_BASE_CODE_SRVLIST *SrvList; + PXEBC_BOOT_SVR_ENTRY *BootSvrEntry; + UINT16 Index; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + EFI_PXE_BASE_CODE_DISCOVER_INFO *NewCreatedInfo; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Mode->IcmpErrorReceived = FALSE; + BootSvrEntry = NULL; + SrvList = NULL; + Status = EFI_DEVICE_ERROR; + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER; + NewCreatedInfo = NULL; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + // + // Station address should be ready before do discover. + // + if (!Private->IsAddressOk) { + return EFI_INVALID_PARAMETER; + } + + if (Mode->UsingIpv6) { + + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + } else { + + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + } + + // + // There are 3 methods to get the information for discover. + // + ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO)); + if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) { + // + // 1. Take the previous setting as the discover info. + // + if (!Mode->PxeDiscoverValid || + !Mode->PxeReplyReceived || + (!Mode->PxeBisReplyReceived && UseBis)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Info = &DefaultInfo; + Info->IpCnt = 1; + Info->UseUCast = TRUE; + SrvList = Info->SrvList; + SrvList[0].Type = Type; + SrvList[0].AcceptAnyResponse = FALSE; + + CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + } else if (Info == NULL) { + // + // 2. Extract the discover information from the cached packets if unspecified. + // + NewCreatedInfo = &DefaultInfo; + Status = PxeBcExtractDiscoverInfo (Private, Type, &NewCreatedInfo, &BootSvrEntry, &SrvList); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + ASSERT (NewCreatedInfo != NULL); + Info = NewCreatedInfo; + } else { + // + // 3. Take the pass-in information as the discover info, and validate the server list. + // + SrvList = Info->SrvList; + + if (!SrvList[0].AcceptAnyResponse) { + for (Index = 1; Index < Info->IpCnt; Index++) { + if (SrvList[Index].AcceptAnyResponse) { + break; + } + } + if (Index != Info->IpCnt) { + // + // It's invalid if the first server doesn't accecpt any response + // but any of the other servers does accept any response. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + } + + // + // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast. + // + if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) || + (Info->MustUseList && Info->IpCnt == 0)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Private->IsDoDiscover = TRUE; + + if (Info->UseMCast) { + // + // Do discover by multicast. + // + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + &Info->ServerMCastIp, + Info->IpCnt, + SrvList + ); + + } else if (Info->UseBCast) { + // + // Do discover by broadcast, but only valid for IPv4. + // + ASSERT (!Mode->UsingIpv6); + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + NULL, + Info->IpCnt, + SrvList + ); + + } else if (Info->UseUCast) { + // + // Do discover by unicast. + // + for (Index = 0; Index < Info->IpCnt; Index++) { + if (BootSvrEntry == NULL) { + CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS)); + } else { + ASSERT (!Mode->UsingIpv6); + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS)); + } + + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + &Private->ServerIp, + Info->IpCnt, + SrvList + ); + } + } + + if (!EFI_ERROR (Status)) { + // + // Parse the cached PXE reply packet, and store it into mode data if valid. + // + if (Mode->UsingIpv6) { + Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6); + if (!EFI_ERROR (Status)) { + CopyMem ( + &Mode->PxeReply.Dhcpv6, + &Private->PxeReply.Dhcp6.Packet.Ack.Dhcp6, + Private->PxeReply.Dhcp6.Packet.Ack.Length + ); + Mode->PxeReplyReceived = TRUE; + Mode->PxeDiscoverValid = TRUE; + } + } else { + Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4); + if (!EFI_ERROR (Status)) { + CopyMem ( + &Mode->PxeReply.Dhcpv4, + &Private->PxeReply.Dhcp4.Packet.Ack.Dhcp4, + Private->PxeReply.Dhcp4.Packet.Ack.Length + ); + Mode->PxeReplyReceived = TRUE; + Mode->PxeDiscoverValid = TRUE; + } + } + } + +ON_EXIT: + + if (NewCreatedInfo != NULL && NewCreatedInfo != &DefaultInfo) { + FreePool (NewCreatedInfo); + } + + if (Mode->UsingIpv6) { + Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + } else { + Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData); + } + + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Used to perform TFTP and MTFTP services. + + This function is used to perform TFTP and MTFTP services. This includes the + TFTP operations to get the size of a file, read a directory, read a file, and + write a file. It also includes the MTFTP operations to get the size of a file, + read a directory, and read a file. The type of operation is specified by Operation. + If the callback function that is invoked during the TFTP/MTFTP operation does + not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will + be returned. + For read operations, the return data will be placed in the buffer specified by + BufferPtr. If BufferSize is too small to contain the entire downloaded file, + then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero, + or the size of the requested file. (NOTE: the size of the requested file is only returned + if the TFTP server supports TFTP options). If BufferSize is large enough for the + read operation, then BufferSize will be set to the size of the downloaded file, + and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services + should use the get-file-size operations to determine the size of the downloaded + file prior to using the read-file operations-especially when downloading large + (greater than 64 MB) files-instead of making two calls to the read-file operation. + Following this recommendation will save time if the file is larger than expected + and the TFTP server does not support TFTP option extensions. Without TFTP option + extension support, the client must download the entire file, counting and discarding + the received packets, to determine the file size. + For write operations, the data to be sent is in the buffer specified by BufferPtr. + BufferSize specifies the number of bytes to send. If the write operation completes + successfully, then EFI_SUCCESS will be returned. + For TFTP "get file size" operations, the size of the requested file or directory + is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server + does not support options, the file will be downloaded into a bit bucket and the + length of the downloaded file will be returned. For MTFTP "get file size" operations, + if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED + will be returned. + This function can take up to 10 seconds to timeout and return control to the caller. + If the TFTP sequence does not complete, EFI_TIMEOUT will be returned. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the TFTP sequence is stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] Operation The type of operation to perform. + @param[in, out] BufferPtr A pointer to the data buffer. + @param[in] Overwrite Only used on write file operations. TRUE if a file on a remote + server can be overwritten. + @param[in, out] BufferSize For get-file-size operations, *BufferSize returns the size of the + requested file. + @param[in] BlockSize The requested block size to be used during a TFTP transfer. + @param[in] ServerIp The TFTP / MTFTP server IP address. + @param[in] Filename A Null-terminated ASCII string that specifies a directory name + or a file name. + @param[in] Info Pointer to the MTFTP information. + @param[in] DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation. + + @retval EFI_SUCCESS The TFTP/MTFTP operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation. + @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation. + @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session. + @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr OPTIONAL, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSize, + IN UINTN *BlockSize OPTIONAL, + IN EFI_IP_ADDRESS *ServerIp, + IN UINT8 *Filename, + IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_MTFTP4_CONFIG_DATA Mtftp4Config; + EFI_MTFTP6_CONFIG_DATA Mtftp6Config; + VOID *Config; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + + if ((This == NULL) || + (Filename == NULL) || + (BufferSize == NULL) || + (ServerIp == NULL) || + ((BufferPtr == NULL) && DontUseBuffer) || + ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) || + (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) { + return EFI_INVALID_PARAMETER; + } + + Config = NULL; + Status = EFI_DEVICE_ERROR; + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (Mode->UsingIpv6) { + // + // Set configuration data for Mtftp6 instance. + // + ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA)); + Config = &Mtftp6Config; + Mtftp6Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT; + Mtftp6Config.TryCount = PXEBC_MTFTP_RETRIES; + CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS)); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + } else { + // + // Set configuration data for Mtftp4 instance. + // + ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA)); + Config = &Mtftp4Config; + Mtftp4Config.UseDefaultSetting = FALSE; + Mtftp4Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT; + Mtftp4Config.TryCount = PXEBC_MTFTP_RETRIES; + CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS)); + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + } + + Mode->TftpErrorReceived = FALSE; + Mode->IcmpErrorReceived = FALSE; + + switch (Operation) { + + case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE: + // + // Send TFTP request to get file size. + // + Status = PxeBcTftpGetFileSize ( + Private, + Config, + Filename, + BlockSize, + BufferSize + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_READ_FILE: + // + // Send TFTP request to read file. + // + Status = PxeBcTftpReadFile ( + Private, + Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE: + // + // Send TFTP request to write file. + // + Status = PxeBcTftpWriteFile ( + Private, + Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY: + // + // Send TFTP request to read directory. + // + Status = PxeBcTftpReadDirectory ( + Private, + Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + + break; + + case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE: + case EFI_PXE_BASE_CODE_MTFTP_READ_FILE: + case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY: + Status = EFI_UNSUPPORTED; + + break; + + default: + Status = EFI_INVALID_PARAMETER; + + break; + } + + if (Status == EFI_ICMP_ERROR) { + Mode->IcmpErrorReceived = TRUE; + } + + // + // Reconfigure the UDP instance with the default configuration. + // + if (Mode->UsingIpv6) { + Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + } else { + Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData); + } + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Writes a UDP packet to the network interface. + + This function writes a UDP packet specified by the (optional HeaderPtr and) + BufferPtr parameters to the network interface. The UDP header is automatically + built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp, + SrcIp, and SrcPort to build this header. If the packet is successfully built and + transmitted through the network interface, then EFI_SUCCESS will be returned. + If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will + be returned. If an ICMP error occurs during the transmission of the packet, then + the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and + EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return + EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] OpFlags The UDP operation flags. + @param[in] DestIp The destination IP address. + @param[in] DestPort The destination UDP port number. + @param[in] GatewayIp The gateway IP address. + @param[in] SrcIp The source IP address. + @param[in, out] SrcPort The source UDP port number. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS The UDP Write operation completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted. + @retval EFI_ABORTED The callback function aborted the UDP Write operation. + @retval EFI_TIMEOUT The UDP Write operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp OPTIONAL, + IN EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_SESSION_DATA Udp4Session; + EFI_UDP6_SESSION_DATA Udp6Session; + EFI_STATUS Status; + BOOLEAN DoNotFragment; + + if (This == NULL || DestIp == NULL || DestPort == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) { + DoNotFragment = FALSE; + } else { + DoNotFragment = TRUE; + } + + if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) { + // + // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6. + // + return EFI_INVALID_PARAMETER; + } + + if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (!Private->IsAddressOk && SrcIp == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->CurSrcPort == 0 || + (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) { + // + // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed. + // + if (SrcPort != NULL) { + Private->CurSrcPort = *SrcPort; + } + } + + if (Mode->UsingIpv6) { + Status = PxeBcConfigUdp6Write ( + Private->Udp6Write, + &Private->StationIp.v6, + &Private->CurSrcPort + ); + } else { + // + // Configure the UDPv4 instance with gateway information from DHCP server as default. + // + Status = PxeBcConfigUdp4Write ( + Private->Udp4Write, + &Private->StationIp.v4, + &Private->SubnetMask.v4, + &Private->GatewayIp.v4, + &Private->CurSrcPort, + DoNotFragment + ); + } + + if (EFI_ERROR (Status)) { + Private->CurSrcPort = 0; + return EFI_INVALID_PARAMETER; + } else if (SrcPort != NULL) { + *SrcPort = Private->CurSrcPort; + } + + // + // Start a timer as timeout event for this blocking API. + // + gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT); + + if (Mode->UsingIpv6) { + // + // Construct UDPv6 session data. + // + ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA)); + CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS)); + Udp6Session.DestinationPort = *DestPort; + if (SrcIp != NULL) { + CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS)); + } + if (SrcPort != NULL) { + Udp6Session.SourcePort = *SrcPort; + } + + Status = PxeBcUdp6Write ( + Private->Udp6Write, + &Udp6Session, + Private->UdpTimeOutEvent, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr + ); + } else { + // + // Construct UDPv4 session data. + // + ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA)); + CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); + Udp4Session.DestinationPort = *DestPort; + if (SrcIp != NULL) { + CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS)); + } + if (SrcPort != NULL) { + Udp4Session.SourcePort = *SrcPort; + } + // + // Override the gateway information if user specified. + // + Status = PxeBcUdp4Write ( + Private->Udp4Write, + &Udp4Session, + Private->UdpTimeOutEvent, + (EFI_IPv4_ADDRESS *) GatewayIp, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr + ); + } + + gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0); + + + // + // Reset the UdpWrite instance. + // + if (Mode->UsingIpv6) { + Private->Udp6Write->Configure (Private->Udp6Write, NULL); + } else { + Private->Udp4Write->Configure (Private->Udp4Write, NULL); + } + + return Status; +} + + +/** + Reads a UDP packet from the network interface. ++ + This function reads a UDP packet from a network interface. The data contents + are returned in (the optional HeaderPtr and) BufferPtr, and the size of the + buffer received is returned in BufferSize . If the input BufferSize is smaller + than the UDP packet received (less optional HeaderSize), it will be set to the + required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the + contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is + successfully received, then EFI_SUCCESS will be returned, and the information + from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if + they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort, + SrcIp, and SrcPort input values, different types of UDP packet receive filtering + will be performed. The following tables summarize these receive filter operations. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] OpFlags The UDP operation flags. + @param[in, out] DestIp The destination IP address. + @param[in, out] DestPort The destination UDP port number. + @param[in, out] SrcIp The source IP address. + @param[in, out] SrcPort The source UDP port number. + @param[in] HeaderSize An optional field which may be set to the length of a + header at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in, out] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be read. + + @retval EFI_SUCCESS The UDP Read operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold. + @retval EFI_ABORTED The callback function aborted the UDP Read operation. + @retval EFI_TIMEOUT The UDP Read operation timed out. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_COMPLETION_TOKEN Udp4Token; + EFI_UDP6_COMPLETION_TOKEN Udp6Token; + EFI_UDP4_RECEIVE_DATA *Udp4Rx; + EFI_UDP6_RECEIVE_DATA *Udp6Rx; + EFI_STATUS Status; + BOOLEAN IsDone; + BOOLEAN IsMatched; + UINTN CopiedLen; + UINTN HeaderLen; + UINTN HeaderCopiedLen; + UINTN BufferCopiedLen; + UINT32 FragmentLength; + UINTN FragmentIndex; + UINT8 *FragmentBuffer; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + IsDone = FALSE; + IsMatched = FALSE; + Udp4Rx = NULL; + Udp6Rx = NULL; + + if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) == 0 && DestPort == NULL) || + ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) == 0 && SrcIp == NULL) || + ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) == 0 && SrcPort == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((BufferSize == NULL) || (BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN)); + ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN)); + + if (Mode->UsingIpv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Udp6Token.Event + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Udp4Token.Event + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Start a timer as timeout event for this blocking API. + // + gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT); + Mode->IcmpErrorReceived = FALSE; + + // + // Read packet by Udp4Read/Udp6Read until matched or timeout. + // + while (!IsMatched && !EFI_ERROR (Status)) { + if (Mode->UsingIpv6) { + Status = PxeBcUdp6Read ( + Private->Udp6Read, + &Udp6Token, + Mode, + Private->UdpTimeOutEvent, + OpFlags, + &IsDone, + &IsMatched, + DestIp, + DestPort, + SrcIp, + SrcPort + ); + } else { + Status = PxeBcUdp4Read ( + Private->Udp4Read, + &Udp4Token, + Mode, + Private->UdpTimeOutEvent, + OpFlags, + &IsDone, + &IsMatched, + DestIp, + DestPort, + SrcIp, + SrcPort + ); + } + } + + if (Status == EFI_ICMP_ERROR || + Status == EFI_NETWORK_UNREACHABLE || + Status == EFI_HOST_UNREACHABLE || + Status == EFI_PROTOCOL_UNREACHABLE || + Status == EFI_PORT_UNREACHABLE) { + // + // Get different return status for icmp error from Udp, refers to UEFI spec. + // + Mode->IcmpErrorReceived = TRUE; + } + gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0); + + if (IsMatched) { + // + // Copy the rececived packet to user if matched by filter. + // + if (Mode->UsingIpv6) { + Udp6Rx = Udp6Token.Packet.RxData; + ASSERT (Udp6Rx != NULL); + + HeaderLen = 0; + if (HeaderSize != NULL) { + HeaderLen = MIN (*HeaderSize, Udp6Rx->DataLength); + } + + if (Udp6Rx->DataLength - HeaderLen > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + if (HeaderSize != NULL) { + *HeaderSize = HeaderLen; + } + *BufferSize = Udp6Rx->DataLength - HeaderLen; + + HeaderCopiedLen = 0; + BufferCopiedLen = 0; + for (FragmentIndex = 0; FragmentIndex < Udp6Rx->FragmentCount; FragmentIndex++) { + FragmentLength = Udp6Rx->FragmentTable[FragmentIndex].FragmentLength; + FragmentBuffer = Udp6Rx->FragmentTable[FragmentIndex].FragmentBuffer; + if (HeaderCopiedLen + FragmentLength < HeaderLen) { + // + // Copy the header part of received data. + // + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength); + HeaderCopiedLen += FragmentLength; + } else if (HeaderCopiedLen < HeaderLen) { + // + // Copy the header part of received data. + // + CopiedLen = HeaderLen - HeaderCopiedLen; + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen); + HeaderCopiedLen += CopiedLen; + + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen); + BufferCopiedLen += (FragmentLength - CopiedLen); + } else { + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength); + BufferCopiedLen += FragmentLength; + } + } + } + // + // Recycle the receiving buffer after copy to user. + // + gBS->SignalEvent (Udp6Rx->RecycleSignal); + } else { + Udp4Rx = Udp4Token.Packet.RxData; + ASSERT (Udp4Rx != NULL); + + HeaderLen = 0; + if (HeaderSize != NULL) { + HeaderLen = MIN (*HeaderSize, Udp4Rx->DataLength); + } + + if (Udp4Rx->DataLength - HeaderLen > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + if (HeaderSize != NULL) { + *HeaderSize = HeaderLen; + } + *BufferSize = Udp4Rx->DataLength - HeaderLen; + + HeaderCopiedLen = 0; + BufferCopiedLen = 0; + for (FragmentIndex = 0; FragmentIndex < Udp4Rx->FragmentCount; FragmentIndex++) { + FragmentLength = Udp4Rx->FragmentTable[FragmentIndex].FragmentLength; + FragmentBuffer = Udp4Rx->FragmentTable[FragmentIndex].FragmentBuffer; + if (HeaderCopiedLen + FragmentLength < HeaderLen) { + // + // Copy the header part of received data. + // + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength); + HeaderCopiedLen += FragmentLength; + } else if (HeaderCopiedLen < HeaderLen) { + // + // Copy the header part of received data. + // + CopiedLen = HeaderLen - HeaderCopiedLen; + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen); + HeaderCopiedLen += CopiedLen; + + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen); + BufferCopiedLen += (FragmentLength - CopiedLen); + } else { + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength); + BufferCopiedLen += FragmentLength; + } + } + } + // + // Recycle the receiving buffer after copy to user. + // + gBS->SignalEvent (Udp4Rx->RecycleSignal); + } + } + + if (Mode->UsingIpv6) { + Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token); + gBS->CloseEvent (Udp6Token.Event); + } else { + Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token); + gBS->CloseEvent (Udp4Token.Event); + } + + return Status; +} + + +/** + Updates the IP receive filters of a network device and enables software filtering. + + The NewFilter field is used to modify the network device's current IP receive + filter settings and to enable a software filter. This function updates the IpFilter + field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter. + The software filter is used when the USE_FILTER in OpFlags is set to UdpRead(). + The current hardware filter remains in effect no matter what the settings of OpFlags. + This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those + packets whose reception is enabled in hardware-physical NIC address (unicast), + broadcast address, logical address or addresses (multicast), or all (promiscuous). + UdpRead() does not modify the IP filter settings. + Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive + filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + If an application or driver wishes to preserve the IP receive filter settings, + it will have to preserve the IP receive filter settings before these calls, and + use SetIpFilter() to restore them after the calls. If incompatible filtering is + requested (for example, PROMISCUOUS with anything else), or if the device does not + support a requested filter setting and it cannot be accommodated in software + (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned. + The IPlist field is used to enable IPs other than the StationIP. They may be + multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP, + then both the StationIP and the IPs from the IPlist will be used. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewFilter Pointer to the new set of IP receive filters. + + @retval EFI_SUCCESS The IP receive filter settings were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter + ) +{ + EFI_STATUS Status; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_CONFIG_DATA *Udp4Cfg; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + UINTN Index; + BOOLEAN NeedPromiscuous; + BOOLEAN AcceptPromiscuous; + BOOLEAN AcceptBroadcast; + BOOLEAN MultiCastUpdate; + + if (This == NULL || NewFilter == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + NeedPromiscuous = FALSE; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + for (Index = 0; Index < NewFilter->IpCnt; Index++) { + ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT); + if (!Mode->UsingIpv6 && + IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) { + // + // IPv4 broadcast address should not be in IP filter. + // + return EFI_INVALID_PARAMETER; + } + if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 && + (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) || + NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) { + // + // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address + // is in IpList, promiscuous mode is needed. + // + NeedPromiscuous = TRUE; + } + } + + AcceptPromiscuous = FALSE; + AcceptBroadcast = FALSE; + MultiCastUpdate = FALSE; + + if (NeedPromiscuous || + (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 || + (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) { + // + // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets. + // + AcceptPromiscuous = TRUE; + } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) { + // + // Configure UDPv4 to receive all broadcast packets. + // + AcceptBroadcast = TRUE; + } + + // + // In multicast condition when Promiscuous FALSE and IpCnt no-zero. + // Here check if there is any update of the multicast ip address. If yes, + // we need leave the old multicast group (by Config UDP instance to NULL), + // and join the new multicast group. + // + if (!AcceptPromiscuous) { + if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) { + if (Mode->IpFilter.IpCnt != NewFilter->IpCnt) { + MultiCastUpdate = TRUE; + } else if (CompareMem (Mode->IpFilter.IpList, NewFilter->IpList, NewFilter->IpCnt * sizeof (EFI_IP_ADDRESS)) != 0 ) { + MultiCastUpdate = TRUE; + } + } + } + + if (!Mode->UsingIpv6) { + // + // Check whether we need reconfigure the UDP4 instance. + // + Udp4Cfg = &Private->Udp4CfgData; + if ((AcceptPromiscuous != Udp4Cfg->AcceptPromiscuous) || + (AcceptBroadcast != Udp4Cfg->AcceptBroadcast) || MultiCastUpdate) { + // + // Clear the UDP4 instance configuration, all joined groups will be left + // during the operation. + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + + // + // Configure the UDP instance with the new configuration. + // + Udp4Cfg->AcceptPromiscuous = AcceptPromiscuous; + Udp4Cfg->AcceptBroadcast = AcceptBroadcast; + Status = Private->Udp4Read->Configure (Private->Udp4Read, Udp4Cfg); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // In not Promiscuous mode, need to join the new multicast group. + // + if (!AcceptPromiscuous) { + for (Index = 0; Index < NewFilter->IpCnt; ++Index) { + if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) { + // + // Join the mutilcast group. + // + Status = Private->Udp4Read->Groups (Private->Udp4Read, TRUE, &NewFilter->IpList[Index].v4); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + } + } else { + // + // Check whether we need reconfigure the UDP6 instance. + // + Udp6Cfg = &Private->Udp6CfgData; + if ((AcceptPromiscuous != Udp6Cfg->AcceptPromiscuous) || MultiCastUpdate) { + // + // Clear the UDP6 instance configuration, all joined groups will be left + // during the operation. + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + // + // Configure the UDP instance with the new configuration. + // + Udp6Cfg->AcceptPromiscuous = AcceptPromiscuous; + Status = Private->Udp6Read->Configure (Private->Udp6Read, Udp6Cfg); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // In not Promiscuous mode, need to join the new multicast group. + // + if (!AcceptPromiscuous) { + for (Index = 0; Index < NewFilter->IpCnt; ++Index) { + if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) { + // + // Join the mutilcast group. + // + Status = Private->Udp6Read->Groups (Private->Udp6Read, TRUE, &NewFilter->IpList[Index].v6); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + } + } + + // + // Save the new IP filter into mode data. + // + CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter)); + + return Status; +} + + +/** + Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6. + + This function uses the ARP protocol to resolve a MAC address. The IP address specified + by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving + the specified address, then the ArpCacheEntries and ArpCache fields of the mode data + are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved + MAC address is placed there as well. If the PXE Base Code protocol is in the + stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters + a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is + returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then EFI_ABORTED is returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] IpAddr Pointer to the IP address that is used to resolve a MAC address. + @param[in] MacAddr If not NULL, a pointer to the MAC address that was resolved with the + ARP protocol. + + @retval EFI_SUCCESS The IP or MAC address was resolved. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_ICMP_ERROR An error occur with the ICMP packet message. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_MAC_ADDRESS *MacAddr OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_EVENT ResolvedEvent; + EFI_STATUS Status; + EFI_MAC_ADDRESS TempMac; + EFI_MAC_ADDRESS ZeroMac; + BOOLEAN IsResolved; + + if (This == NULL || IpAddr == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + ResolvedEvent = NULL; + Status = EFI_SUCCESS; + IsResolved = FALSE; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + return EFI_UNSUPPORTED; + } + + // + // Station address should be ready before do arp. + // + if (!Private->IsAddressOk) { + return EFI_INVALID_PARAMETER; + } + + Mode->IcmpErrorReceived = FALSE; + ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS)); + ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS)); + + if (!Mode->AutoArp) { + // + // If AutoArp is FALSE, only search in the current Arp cache. + // + PxeBcArpCacheUpdate (NULL, Private); + if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsResolved, + &ResolvedEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // If AutoArp is TRUE, try to send Arp request on initiative. + // + Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac); + if (EFI_ERROR (Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } + + while (!IsResolved) { + if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) { + break; + } + } + if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) { + Status = EFI_SUCCESS; + } else { + Status = EFI_TIMEOUT; + } + } + + // + // Copy the Mac address to user if needed. + // + if (MacAddr != NULL && !EFI_ERROR (Status)) { + CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS)); + } + +ON_EXIT: + if (ResolvedEvent != NULL) { + gBS->CloseEvent (ResolvedEvent); + } + return Status; +} + + +/** + Updates the parameters that affect the operation of the PXE Base Code Protocol. + + This function sets parameters that affect the operation of the PXE Base Code Protocol. + The parameter specified by NewAutoArp is used to control the generation of ARP + protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated + as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP + Protocol packets will be generated. In this case, the only mappings that are + available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure. + If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol + service, then the service will fail. This function updates the AutoArp field of + the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp. + The SetParameters() call must be invoked after a Callback Protocol is installed + to enable the use of callbacks. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the + current value of AutoARP. + @param[in] NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the + current value of SendGUID. + @param[in] NewTTL If not NULL, a pointer to be used in place of the current value of TTL, + the "time to live" field of the IP header. + @param[in] NewToS If not NULL, a pointer to be used in place of the current value of ToS, + the "type of service" field of the IP header. + @param[in] NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the + current value of the MakeCallback field of the Mode structure. + + @retval EFI_SUCCESS The new parameters values were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetParameters ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewAutoArp OPTIONAL, + IN BOOLEAN *NewSendGUID OPTIONAL, + IN UINT8 *NewTTL OPTIONAL, + IN UINT8 *NewToS OPTIONAL, + IN BOOLEAN *NewMakeCallback OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_GUID SystemGuid; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (NewMakeCallback != NULL) { + if (*NewMakeCallback) { + // + // Update the previous PxeBcCallback protocol. + // + Status = gBS->HandleProtocol ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID **) &Private->PxeBcCallback + ); + + if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) { + return EFI_INVALID_PARAMETER; + } + } else { + Private->PxeBcCallback = NULL; + } + Mode->MakeCallbacks = *NewMakeCallback; + } + + if (NewSendGUID != NULL) { + if (*NewSendGUID && EFI_ERROR (NetLibGetSystemGuid (&SystemGuid))) { + return EFI_INVALID_PARAMETER; + } + Mode->SendGUID = *NewSendGUID; + } + + if (NewAutoArp != NULL) { + Mode->AutoArp = *NewAutoArp; + } + + if (NewTTL != NULL) { + Mode->TTL = *NewTTL; + } + + if (NewToS != NULL) { + Mode->ToS = *NewToS; + } + + return EFI_SUCCESS; +} + + +/** + Updates the station IP address and/or subnet mask values of a network device. + + This function updates the station IP address and/or subnet mask values of a network + device. The NewStationIp field is used to modify the network device's current IP address. + If NewStationIP is NULL, then the current IP address will not be modified. Otherwise, + this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure + with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet + mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified. + Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE + structure with NewSubnetMask. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewStationIp Pointer to the new IP address to be used by the network device. + @param[in] NewSubnetMask Pointer to the new subnet mask to be used by the network device. + + @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *NewStationIp OPTIONAL, + IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL + ) +{ + EFI_STATUS Status; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_ARP_CONFIG_DATA ArpConfigData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (NewStationIp != NULL && + (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) && + !NetIp6IsValidUnicast (&NewStationIp->v6))) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + + if (!Mode->UsingIpv6 && + NewSubnetMask != NULL && + !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6 && NewStationIp != NULL) { + // + // Set the IPv6 address by Ip6Config protocol. + // + Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else if (!Mode->UsingIpv6 && NewStationIp != NULL) { + // + // Configure the corresponding ARP with the IPv4 address. + // + ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA)); + + ArpConfigData.SwAddressType = 0x0800; + ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS); + ArpConfigData.StationAddress = &NewStationIp->v4; + + Private->Arp->Configure (Private->Arp, NULL); + Private->Arp->Configure (Private->Arp, &ArpConfigData); + + if (NewSubnetMask != NULL) { + Mode->RouteTableEntries = 1; + Mode->RouteTable[0].IpAddr.Addr[0] = NewStationIp->Addr[0] & NewSubnetMask->Addr[0]; + Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0]; + Mode->RouteTable[0].GwAddr.Addr[0] = 0; + } + + Private->IsAddressOk = TRUE; + } + + if (NewStationIp != NULL) { + CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS)); + } + + if (!Mode->UsingIpv6 && NewSubnetMask != NULL) { + CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS)); + } + + Status = PxeBcFlushStationIp (Private, NewStationIp, NewSubnetMask); +ON_EXIT: + return Status; +} + + +/** + Updates the contents of the cached DHCP and Discover packets. + + The pointers to the new packets are used to update the contents of the cached + packets in the EFI_PXE_BASE_CODE_MODE structure. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewDhcpDiscoverValid Pointer to a value that will replace the current + DhcpDiscoverValid field. + @param[in] NewDhcpAckReceived Pointer to a value that will replace the current + DhcpAckReceived field. + @param[in] NewProxyOfferReceived Pointer to a value that will replace the current + ProxyOfferReceived field. + @param[in] NewPxeDiscoverValid Pointer to a value that will replace the current + ProxyOfferReceived field. + @param[in] NewPxeReplyReceived Pointer to a value that will replace the current + PxeReplyReceived field. + @param[in] NewPxeBisReplyReceived Pointer to a value that will replace the current + PxeBisReplyReceived field. + @param[in] NewDhcpDiscover Pointer to the new cached DHCP Discover packet contents. + @param[in] NewDhcpAck Pointer to the new cached DHCP Ack packet contents. + @param[in] NewProxyOffer Pointer to the new cached Proxy Offer packet contents. + @param[in] NewPxeDiscover Pointer to the new cached PXE Discover packet contents. + @param[in] NewPxeReply Pointer to the new cached PXE Reply packet contents. + @param[in] NewPxeBisReply Pointer to the new cached PXE BIS Reply packet contents. + + @retval EFI_SUCCESS The cached packet contents were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER This is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewDhcpDiscoverValid OPTIONAL, + IN BOOLEAN *NewDhcpAckReceived OPTIONAL, + IN BOOLEAN *NewProxyOfferReceived OPTIONAL, + IN BOOLEAN *NewPxeDiscoverValid OPTIONAL, + IN BOOLEAN *NewPxeReplyReceived OPTIONAL, + IN BOOLEAN *NewPxeBisReplyReceived OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (NewDhcpDiscoverValid != NULL) { + Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid; + } + + if (NewDhcpAckReceived != NULL) { + Mode->DhcpAckReceived = *NewDhcpAckReceived; + } + + if (NewProxyOfferReceived != NULL) { + Mode->ProxyOfferReceived = *NewProxyOfferReceived; + } + + if (NewPxeDiscoverValid != NULL) { + Mode->PxeDiscoverValid = *NewPxeDiscoverValid; + } + + if (NewPxeReplyReceived != NULL) { + Mode->PxeReplyReceived = *NewPxeReplyReceived; + } + + if (NewPxeBisReplyReceived != NULL) { + Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived; + } + + if (NewDhcpDiscover != NULL) { + CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewDhcpAck != NULL) { + CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewProxyOffer != NULL) { + CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeDiscover != NULL) { + CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeReply != NULL) { + CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeBisReply != NULL) { + CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + return EFI_SUCCESS; +} + +EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate = { + EFI_PXE_BASE_CODE_PROTOCOL_REVISION, + EfiPxeBcStart, + EfiPxeBcStop, + EfiPxeBcDhcp, + EfiPxeBcDiscover, + EfiPxeBcMtftp, + EfiPxeBcUdpWrite, + EfiPxeBcUdpRead, + EfiPxeBcSetIpFilter, + EfiPxeBcArp, + EfiPxeBcSetParameters, + EfiPxeBcSetStationIP, + EfiPxeBcSetPackets, + NULL +}; + + +/** + Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has + received, or is waiting to receive a packet. + + This function is invoked when the PXE Base Code Protocol is about to transmit, has received, + or is waiting to receive a packet. Parameters Function and Received specify the type of event. + Parameters PacketLen and Packet specify the packet that generated the event. If these fields + are zero and NULL respectively, then this is a status update callback. If the operation specified + by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation + specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to + the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms. + The SetParameters() function must be called after a Callback Protocol is installed to enable the + use of callbacks. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance. + @param[in] Function The PXE Base Code Protocol function that is waiting for an event. + @param[in] Received TRUE if the callback is being invoked due to a receive event. FALSE if + the callback is being invoked due to a transmit event. + @param[in] PacketLength The length, in bytes, of Packet. This field will have a value of zero if + this is a wait for receive event. + @param[in] PacketPtr If Received is TRUE, a pointer to the packet that was just received; + otherwise a pointer to the packet that is about to be transmitted. + + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation. + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT If Function specifies an abort operation. + +**/ +EFI_PXE_BASE_CODE_CALLBACK_STATUS +EFIAPI +EfiPxeLoadFileCallback ( + IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN BOOLEAN Received, + IN UINT32 PacketLength, + IN EFI_PXE_BASE_CODE_PACKET *PacketPtr OPTIONAL + ) +{ + EFI_INPUT_KEY Key; + EFI_STATUS Status; + + // + // Catch Ctrl-C or ESC to abort. + // + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + if (!EFI_ERROR (Status)) { + + if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) { + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } + // + // No print if receive packet + // + if (Received) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // Print only for three functions + // + switch (Function) { + + case EFI_PXE_BASE_CODE_FUNCTION_MTFTP: + // + // Print only for open MTFTP packets, not every MTFTP packets + // + if (PacketLength != 0 && PacketPtr != NULL) { + if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + } + break; + + case EFI_PXE_BASE_CODE_FUNCTION_DHCP: + case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER: + break; + + default: + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + + if (PacketLength != 0 && PacketPtr != NULL) { + // + // Print '.' when transmit a packet + // + AsciiPrint ("."); + } + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; +} + +EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = { + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION, + EfiPxeLoadFileCallback +}; + + +/** + Causes the driver to load a specified file. + + @param[in] This Protocol instance pointer. + @param[in] FilePath The device specific path of the file to load. + @param[in] BootPolicy If TRUE, indicates that the request originates from the + boot manager is attempting to load FilePath as a boot + selection. If FALSE, then FilePath must match an exact file + to be loaded. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then no the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_ABORTED The file load process was manually cancelled. + +**/ +EFI_STATUS +EFIAPI +EfiPxeLoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + PXEBC_VIRTUAL_NIC *VirtualNic; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + BOOLEAN UsingIpv6; + EFI_STATUS Status; + BOOLEAN MediaPresent; + + if (FilePath == NULL || !IsDevicePathEnd (FilePath)) { + return EFI_INVALID_PARAMETER; + } + + VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This); + Private = VirtualNic->Private; + PxeBc = &Private->PxeBc; + UsingIpv6 = FALSE; + Status = EFI_DEVICE_ERROR; + + if (This == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + // + // Check media status before PXE start + // + MediaPresent = TRUE; + NetLibDetectMedia (Private->Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Check whether the virtual nic is using IPv6 or not. + // + if (VirtualNic == Private->Ip6Nic) { + UsingIpv6 = TRUE; + } + + // + // Start Pxe Base Code to initialize PXE boot. + // + Status = PxeBc->Start (PxeBc, UsingIpv6); + if (Status == EFI_ALREADY_STARTED && UsingIpv6 != PxeBc->Mode->UsingIpv6) { + // + // PxeBc protocol has already been started but not on the required IP version, restart it. + // + Status = PxeBc->Stop (PxeBc); + if (!EFI_ERROR (Status)) { + Status = PxeBc->Start (PxeBc, UsingIpv6); + } + } + if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) { + Status = PxeBcLoadBootFile (Private, BufferSize, Buffer); + } + + if (Status != EFI_SUCCESS && + Status != EFI_UNSUPPORTED && + Status != EFI_BUFFER_TOO_SMALL) { + // + // There are three cases, which needn't stop pxebc here. + // 1. success to download file. + // 2. success to get file size. + // 3. unsupported. + // + PxeBc->Stop (PxeBc); + } else { + // + // The DHCP4 can have only one configured child instance so we need to stop + // reset the DHCP4 child before we return. Otherwise these programs which + // also need to use DHCP4 will be impacted. + // + if (!PxeBc->Mode->UsingIpv6) { + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + } + } + + return Status; +} + +EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate = { EfiPxeLoadFile }; + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h new file mode 100644 index 0000000000..ac7dc8781a --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h @@ -0,0 +1,225 @@ +/** @file + This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL. + interfaces declaration. + + Copyright (c) 2007 - 2015, 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 __EFI_PXEBC_IMPL_H__ +#define __EFI_PXEBC_IMPL_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PXEBC_PRIVATE_DATA PXEBC_PRIVATE_DATA; +typedef struct _PXEBC_PRIVATE_PROTOCOL PXEBC_PRIVATE_PROTOCOL; +typedef struct _PXEBC_VIRTUAL_NIC PXEBC_VIRTUAL_NIC; + +#include "PxeBcDriver.h" +#include "PxeBcDhcp4.h" +#include "PxeBcDhcp6.h" +#include "PxeBcMtftp.h" +#include "PxeBcBoot.h" +#include "PxeBcSupport.h" + +#define PXEBC_DEFAULT_HOPLIMIT 64 +#define PXEBC_DEFAULT_LIFETIME 50000 // 50 ms, unit is microsecond +#define PXEBC_UDP_TIMEOUT 30000000 // 3 seconds, unit is 100nanosecond +#define PXEBC_DAD_ADDITIONAL_DELAY 30000000 // 3 seconds +#define PXEBC_MTFTP_TIMEOUT 4 +#define PXEBC_MTFTP_RETRIES 6 +#define PXEBC_DHCP_RETRIES 4 // refers to mPxeDhcpTimeout, also by PXE2.1 spec. +#define PXEBC_MENU_MAX_NUM 24 +#define PXEBC_OFFER_MAX_NUM 16 + +#define PXEBC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'P') +#define PXEBC_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'V') +#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a) CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE) +#define PXEBC_PRIVATE_DATA_FROM_ID(a) CR (a, PXEBC_PRIVATE_DATA, Id, PXEBC_PRIVATE_DATA_SIGNATURE) +#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE) + +typedef union { + PXEBC_DHCP4_PACKET_CACHE Dhcp4; + PXEBC_DHCP6_PACKET_CACHE Dhcp6; +} PXEBC_DHCP_PACKET_CACHE; + +struct _PXEBC_PRIVATE_PROTOCOL { + UINT64 Reserved; +}; + +struct _PXEBC_VIRTUAL_NIC { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + PXEBC_PRIVATE_DATA *Private; +}; + +struct _PXEBC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + PXEBC_PRIVATE_PROTOCOL Id; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + PXEBC_VIRTUAL_NIC *Ip4Nic; + PXEBC_VIRTUAL_NIC *Ip6Nic; + + EFI_HANDLE ArpChild; + EFI_HANDLE Ip4Child; + EFI_HANDLE Dhcp4Child; + EFI_HANDLE Mtftp4Child; + EFI_HANDLE Udp4ReadChild; + EFI_HANDLE Udp4WriteChild; + + EFI_ARP_PROTOCOL *Arp; + EFI_IP4_PROTOCOL *Ip4; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_UDP4_PROTOCOL *Udp4Read; + EFI_UDP4_PROTOCOL *Udp4Write; + + EFI_HANDLE Ip6Child; + EFI_HANDLE Dhcp6Child; + EFI_HANDLE Mtftp6Child; + EFI_HANDLE Udp6ReadChild; + EFI_HANDLE Udp6WriteChild; + + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_UDP6_PROTOCOL *Udp6Read; + EFI_UDP6_PROTOCOL *Udp6Write; + + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_PXE_BASE_CODE_PROTOCOL PxeBc; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL LoadFileCallback; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *PxeBcCallback; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + EFI_PXE_BASE_CODE_MODE Mode; + EFI_PXE_BASE_CODE_FUNCTION Function; + UINT32 Ip6Policy; + UINT32 SolicitTimes; + UINT64 ElapsedTime; + + EFI_UDP4_CONFIG_DATA Udp4CfgData; + EFI_UDP6_CONFIG_DATA Udp6CfgData; + EFI_IP4_CONFIG_DATA Ip4CfgData; + EFI_IP6_CONFIG_DATA Ip6CfgData; + + EFI_EVENT UdpTimeOutEvent; + EFI_EVENT ArpUpdateEvent; + EFI_IP4_COMPLETION_TOKEN IcmpToken; + EFI_IP6_COMPLETION_TOKEN Icmp6Token; + + BOOLEAN IsAddressOk; + BOOLEAN IsOfferSorted; + BOOLEAN IsProxyRecved; + BOOLEAN IsDoDiscover; + + EFI_IP_ADDRESS TmpStationIp; + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS ServerIp; + UINT16 CurSrcPort; + UINT32 IaId; + + UINT32 Ip4MaxPacketSize; + UINT32 Ip6MaxPacketSize; + UINT8 *BootFileName; + UINTN BootFileSize; + UINTN BlockSize; + + PXEBC_DHCP_PACKET_CACHE ProxyOffer; + PXEBC_DHCP_PACKET_CACHE DhcpAck; + PXEBC_DHCP_PACKET_CACHE PxeReply; + EFI_DHCP6_PACKET *Dhcp6Request; + EFI_DHCP4_PACKET SeedPacket; + + // + // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer. + // + // It supposed that + // + // OfferNum: 8 + // OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl] + // (OfferBuffer is 0-based.) + // + // And assume that (DhcpPxe10 is the first priority actually.) + // + // SelectIndex: 2 + // SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL + // (SelectIndex is 1-based, and 0 means no one is selected.) + // + // So it should be + // + // DhcpOnly DhcpPxe10 DhcpWfm11a DhcpBinl ProxyPxe10 ProxyWfm11a ProxyBinl Bootp + // OfferCount: [ 2(n), 1(n), 0(n), 1(n), 1(1), 0(1), 3(n), 1(1)] + // + // OfferIndex: {[ 2, 5, 0, 6, 3, 0, *0, 0] + // [ 4, 0, 0, 0, 0, 0, 1, 0] + // [ 0, 0, 0, 0, 0, 0, 7, 0] + // ... ]} + // (OfferIndex is 0-based.) + // + // + UINT32 SelectIndex; + UINT32 SelectProxyType; + PXEBC_DHCP_PACKET_CACHE OfferBuffer[PXEBC_OFFER_MAX_NUM]; + UINT32 OfferNum; + UINT32 OfferCount[PxeOfferTypeMax]; + UINT32 OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM]; +}; + +extern EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate; +extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate; +extern EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate; + +#endif diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c new file mode 100644 index 0000000000..270190d42e --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c @@ -0,0 +1,1113 @@ +/** @file + Functions implementation related with Mtftp for UefiPxeBc Driver. + + Copyright (c) 2007 - 2015, 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. + +**/ + +#include "PxeBcImpl.h" + +CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = { + "blksize", + "timeout", + "tsize", + "multicast" +}; + + +/** + This is a callback function when packets are received or transmitted in Mtftp driver. + + A callback function that is provided by the caller to intercept + the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the + EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to + EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param[in] This Pointer to EFI_MTFTP6_PROTOCOL. + @param[in] Token Pointer to EFI_MTFTP6_TOKEN. + @param[in] PacketLen Length of EFI_MTFTP6_PACKET. + @param[in] Packet Pointer to EFI_MTFTP6_PACKET to be checked. + + @retval EFI_SUCCESS The current operation succeeded. + @retval EFI_ABORTED Abort the current transfer process. + +**/ +EFI_STATUS +EFIAPI +PxeBcMtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Token->Context; + Callback = Private->PxeBcCallback; + Status = EFI_SUCCESS; + + if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + + if (Callback != NULL) { + // + // Callback to user if has when received any tftp packet. + // + Status = Callback->Callback ( + Callback, + Private->Function, + TRUE, + PacketLen, + (EFI_PXE_BASE_CODE_PACKET *) Packet + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + // + // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_ABORTED; + } else { + // + // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + This function is to get the size of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Sucessfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp ptions failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Has not obtained the size of the file. + +**/ +EFI_STATUS +PxeBcMtftp6GetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_OPTION ReqOpt[2]; + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_OPTION *Option; + UINT32 PktLen; + UINT8 OptBuf[128]; + UINT32 OptCnt; + EFI_STATUS Status; + + *BufferSize = 0; + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + Packet = NULL; + Option = NULL; + PktLen = 0; + OptCnt = 1; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build the required options for get info. + // + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; + PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + ReqOpt[0].ValueStr = OptBuf; + + if (BlockSize != NULL) { + ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); + PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1)); + OptCnt++; + } + + Status = Mtftp6->GetInfo ( + Mtftp6, + NULL, + Filename, + NULL, + (UINT8) OptCnt, + ReqOpt, + &PktLen, + &Packet + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_TFTP_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + goto ON_ERROR; + } + + // + // Parse the options in the reply packet. + // + OptCnt = 0; + Status = Mtftp6->ParseOptions ( + Mtftp6, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &Option + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Parse out the value of "tsize" option. + // + Status = EFI_NOT_FOUND; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { + *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); + Status = EFI_SUCCESS; + } + OptCnt--; + } + FreePool (Option); + +ON_ERROR: + if (Packet != NULL) { + FreePool (Packet); + } + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is to get data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether with a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcMtftp6ReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->ReadFile (Mtftp6, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is used to write the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicate whether with overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into a special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcMtftp6WriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->WriteFile (Mtftp6, &Token); + // + // Get the real size of transmitted buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is to read the data (file) from a directory using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully obtained the data from the file included in directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcMtftp6ReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->ReadDirectory (Mtftp6, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This is a callback function when packets are received or transmitted in Mtftp driver. + + A callback function that is provided by the caller to intercept + the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the + EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to + EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param[in] This Pointer to EFI_MTFTP4_PROTOCOL. + @param[in] Token Pointer to EFI_MTFTP4_TOKEN. + @param[in] PacketLen Length of EFI_MTFTP4_PACKET. + @param[in] Packet Pointer to EFI_MTFTP4_PACKET to be checked. + + @retval EFI_SUCCESS The current operation succeeeded. + @retval EFI_ABORTED Abort the current transfer process. + +**/ +EFI_STATUS +EFIAPI +PxeBcMtftp4CheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Token->Context; + Callback = Private->PxeBcCallback; + Status = EFI_SUCCESS; + + if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + + if (Callback != NULL) { + // + // Callback to user if has when received any tftp packet. + // + Status = Callback->Callback ( + Callback, + Private->Function, + TRUE, + PacketLen, + (EFI_PXE_BASE_CODE_PACKET *) Packet + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + // + // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_ABORTED; + } else { + // + // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + This function is to get size of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp options failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcMtftp4GetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_OPTION ReqOpt[2]; + EFI_MTFTP4_PACKET *Packet; + EFI_MTFTP4_OPTION *Option; + UINT32 PktLen; + UINT8 OptBuf[128]; + UINT32 OptCnt; + EFI_STATUS Status; + + *BufferSize = 0; + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + Packet = NULL; + Option = NULL; + PktLen = 0; + OptCnt = 1; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build the required options for get info. + // + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; + PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + ReqOpt[0].ValueStr = OptBuf; + + if (BlockSize != NULL) { + ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); + PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1)); + OptCnt++; + } + + Status = Mtftp4->GetInfo ( + Mtftp4, + NULL, + Filename, + NULL, + (UINT8) OptCnt, + ReqOpt, + &PktLen, + &Packet + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_TFTP_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + goto ON_ERROR; + } + + // + // Parse the options in the reply packet. + // + OptCnt = 0; + Status = Mtftp4->ParseOptions ( + Mtftp4, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &Option + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Parse out the value of "tsize" option. + // + Status = EFI_NOT_FOUND; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { + *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); + Status = EFI_SUCCESS; + } + OptCnt--; + } + FreePool (Option); + +ON_ERROR: + if (Packet != NULL) { + FreePool (Packet); + } + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to read the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcMtftp4ReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->ReadFile (Mtftp4, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to write the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicates whether to use the overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully write the data into the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcMtftp4WriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->WriteFile (Mtftp4, &Token); + // + // Get the real size of transmitted buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to get data (file) from a directory using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in the directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcMtftp4ReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->ReadDirectory (Mtftp4, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is wrapper to get the file size using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to configure data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp options failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcTftpGetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6GetFileSize ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferSize + ); + } else { + return PxeBcMtftp4GetFileSize ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferSize + ); + } +} + + +/** + This function is a wrapper to get file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Sucessfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcTftpReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6ReadFile ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } else { + return PxeBcMtftp4ReadFile ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } +} + + +/** + This function is a wrapper to write file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicate whether with overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into a special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcTftpWriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6WriteFile ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + } else { + return PxeBcMtftp4WriteFile ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + } +} + + +/** + This function is a wrapper to get the data (file) from a directory using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicatse whether to use a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in the directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcTftpReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6ReadDirectory ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } else { + return PxeBcMtftp4ReadDirectory ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } +} + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h new file mode 100644 index 0000000000..f1150762c6 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h @@ -0,0 +1,137 @@ +/** @file + Functions declaration related with Mtftp for UefiPxeBc Driver. + + Copyright (c) 2007 - 2015, 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 __EFI_PXEBC_MTFTP_H__ +#define __EFI_PXEBC_MTFTP_H__ + +#define PXE_MTFTP_OPTION_BLKSIZE_INDEX 0 +#define PXE_MTFTP_OPTION_TIMEOUT_INDEX 1 +#define PXE_MTFTP_OPTION_TSIZE_INDEX 2 +#define PXE_MTFTP_OPTION_MULTICAST_INDEX 3 +#define PXE_MTFTP_OPTION_MAXIMUM_INDEX 4 +#define PXE_MTFTP_OPTBUF_MAXNUM_INDEX 128 + +#define PXE_MTFTP_ERROR_STRING_LENGTH 127 // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR. +#define PXE_MTFTP_DEFAULT_BLOCK_SIZE 512 // refer to rfc-1350. + + +/** + This function is wrapper to get the file size using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to configure data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp ptions failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcTftpGetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ); + + +/** + This function is a wrapper to get a file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcTftpReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ); + + +/** + This function is a wrapper to put file with TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicates whether to use an overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcTftpWriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ); + + +/** + This function is a wrapper to get the data (file) from a directory using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether with a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcTftpReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ); +#endif diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c new file mode 100644 index 0000000000..36b0665a96 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c @@ -0,0 +1,1513 @@ +/** @file + Support functions implementation for UefiPxeBc Driver. + + Copyright (c) 2007 - 2015, 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. + +**/ + +#include "PxeBcImpl.h" + + +/** + Flush the previous configration using the new station Ip address. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] StationIp The pointer to the station Ip address. + @param[in] SubnetMask The pointer to the subnet mask address for v4. + + @retval EFI_SUCCESS Successfully flushed the previous configuration. + @retval Others Failed to flush using the new station Ip. + +**/ +EFI_STATUS +PxeBcFlushStationIp ( + PXEBC_PRIVATE_DATA *Private, + EFI_IP_ADDRESS *StationIp, + EFI_IP_ADDRESS *SubnetMask OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + + ASSERT (StationIp != NULL); + + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + + if (Mode->UsingIpv6) { + + CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + + // + // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address. + // + Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token); + Private->Ip6->Configure (Private->Ip6, NULL); + + Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token); + } else { + ASSERT (SubnetMask != NULL); + CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + // + // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address. + // + Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken); + Private->Ip4->Configure (Private->Ip4, NULL); + + Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken); + } + +ON_EXIT: + return Status; +} + + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +PxeBcCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + + +/** + Do arp resolution from arp cache in PxeBcMode. + + @param Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param Ip4Addr The Ip4 address for resolution. + @param MacAddress The resoluted MAC address if the resolution is successful. + The value is undefined if the resolution fails. + + @retval TRUE Found an matched entry. + @retval FALSE Did not find a matched entry. + +**/ +BOOLEAN +PxeBcCheckArpCache ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_IPv4_ADDRESS *Ip4Addr, + OUT EFI_MAC_ADDRESS *MacAddress + ) +{ + UINT32 Index; + + ASSERT (!Mode->UsingIpv6); + + // + // Check whether the current Arp cache in mode data contains this information or not. + // + for (Index = 0; Index < Mode->ArpCacheEntries; Index++) { + if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) { + CopyMem ( + MacAddress, + &Mode->ArpCache[Index].MacAddr, + sizeof (EFI_MAC_ADDRESS) + ); + return TRUE; + } + } + + return FALSE; +} + + +/** + Update the arp cache periodically. + + @param Event The pointer to EFI_PXE_BC_PROTOCOL. + @param Context Context of the timer event. + +**/ +VOID +EFIAPI +PxeBcArpCacheUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_ARP_FIND_DATA *ArpEntry; + UINT32 EntryLength; + UINT32 EntryCount; + UINT32 Index; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + + ASSERT (!Mode->UsingIpv6); + + // + // Get the current Arp cache from Arp driver. + // + Status = Private->Arp->Find ( + Private->Arp, + TRUE, + NULL, + &EntryLength, + &EntryCount, + &ArpEntry, + TRUE + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Update the Arp cache in mode data. + // + Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES); + + for (Index = 0; Index < Mode->ArpCacheEntries; Index++) { + CopyMem ( + &Mode->ArpCache[Index].IpAddr, + ArpEntry + 1, + ArpEntry->SwAddressLength + ); + CopyMem ( + &Mode->ArpCache[Index].MacAddr, + (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength, + ArpEntry->HwAddressLength + ); + ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength); + } +} + + +/** + Notify function to handle the received ICMP message in DPC. + + @param Context The PXEBC private data. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorDpcHandle ( + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_IP4_RECEIVE_DATA *RxData; + EFI_IP4_PROTOCOL *Ip4; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + UINT8 Type; + UINTN Index; + UINT32 CopiedLen; + UINT8 *IcmpError; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = &Private->Mode; + Status = Private->IcmpToken.Status; + RxData = Private->IcmpToken.Packet.RxData; + Ip4 = Private->Ip4; + + ASSERT (!Mode->UsingIpv6); + + if (Status == EFI_ABORTED) { + // + // It's triggered by user cancellation. + // + return; + } + + if (RxData == NULL) { + goto ON_EXIT; + } + + if (Status != EFI_ICMP_ERROR) { + // + // The return status should be recognized as EFI_ICMP_ERROR. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (EFI_IP4 (RxData->Header->SourceAddress) != 0 && + !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) { + // + // The source address of the received packet should be a valid unicast address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) { + // + // The destination address of the received packet should be equal to the host address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) { + // + // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer); + + if (Type != ICMP_DEST_UNREACHABLE && + Type != ICMP_SOURCE_QUENCH && + Type != ICMP_REDIRECT && + Type != ICMP_TIME_EXCEEDED && + Type != ICMP_PARAMETER_PROBLEM) { + // + // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + // + // Copy the right ICMP error message into mode data. + // + CopiedLen = 0; + IcmpError = (UINT8 *) &Mode->IcmpError; + + for (Index = 0; Index < RxData->FragmentCount; Index++) { + CopiedLen += RxData->FragmentTable[Index].FragmentLength; + if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) { + CopyMem ( + IcmpError, + RxData->FragmentTable[Index].FragmentBuffer, + RxData->FragmentTable[Index].FragmentLength + ); + } else { + CopyMem ( + IcmpError, + RxData->FragmentTable[Index].FragmentBuffer, + CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + } + IcmpError += CopiedLen; + } + +ON_EXIT: + Private->IcmpToken.Status = EFI_NOT_READY; + Ip4->Receive (Ip4, &Private->IcmpToken); +} + + +/** + Callback function to update the latest ICMP6 error message. + + @param Event The event signalled. + @param Context The context passed in using the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context); +} + + +/** + Notify function to handle the received ICMP6 message in DPC. + + @param Context The PXEBC private data. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorDpcHandle ( + IN VOID *Context + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_IP6_RECEIVE_DATA *RxData; + EFI_IP6_PROTOCOL *Ip6; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + UINTN Index; + UINT8 Type; + UINT32 CopiedLen; + UINT8 *Icmp6Error; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = &Private->Mode; + Status = Private->Icmp6Token.Status; + RxData = Private->Icmp6Token.Packet.RxData; + Ip6 = Private->Ip6; + + ASSERT (Mode->UsingIpv6); + + if (Status == EFI_ABORTED) { + // + // It's triggered by user cancellation. + // + return; + } + + if (RxData == NULL) { + goto ON_EXIT; + } + + if (Status != EFI_ICMP_ERROR) { + // + // The return status should be recognized as EFI_ICMP_ERROR. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) { + // + // The source address of the received packet should be a valid unicast address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) && + !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) { + // + // The destination address of the received packet should be equal to the host address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (RxData->Header->NextHeader != IP6_ICMP) { + // + // The nextheader in the header of the receveid packet should be IP6_ICMP. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer); + + if (Type != ICMP_V6_DEST_UNREACHABLE && + Type != ICMP_V6_PACKET_TOO_BIG && + Type != ICMP_V6_PACKET_TOO_BIG && + Type != ICMP_V6_PARAMETER_PROBLEM) { + // + // The type of the receveid packet should be an ICMP6 error message. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + // + // Copy the right ICMP6 error message into mode data. + // + CopiedLen = 0; + Icmp6Error = (UINT8 *) &Mode->IcmpError; + + for (Index = 0; Index < RxData->FragmentCount; Index++) { + CopiedLen += RxData->FragmentTable[Index].FragmentLength; + if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) { + CopyMem ( + Icmp6Error, + RxData->FragmentTable[Index].FragmentBuffer, + RxData->FragmentTable[Index].FragmentLength + ); + } else { + CopyMem ( + Icmp6Error, + RxData->FragmentTable[Index].FragmentBuffer, + CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + } + Icmp6Error += CopiedLen; + } + +ON_EXIT: + Private->Icmp6Token.Status = EFI_NOT_READY; + Ip6->Receive (Ip6, &Private->Icmp6Token); +} + + +/** + Callback function to update the latest ICMP6 error message. + + @param Event The event signalled. + @param Context The context passed in using the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context); +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] StationIp The pointer to the station address. + @param[in] SubnetMask The pointer to the subnet mask. + @param[in] Gateway The pointer to the gateway address. + @param[in, out] SrcPort The pointer to the source port. + @param[in] DoNotFragment If TRUE, fragment is not enabled. + Otherwise, fragment is enabled. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_IPv4_ADDRESS *StationIp, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *Gateway, + IN OUT UINT16 *SrcPort, + IN BOOLEAN DoNotFragment + ) +{ + EFI_UDP4_CONFIG_DATA Udp4CfgData; + EFI_STATUS Status; + + ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData)); + + Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData.TypeOfService = DEFAULT_ToS; + Udp4CfgData.TimeToLive = DEFAULT_TTL; + Udp4CfgData.AllowDuplicatePort = TRUE; + Udp4CfgData.DoNotFragment = DoNotFragment; + + CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp)); + CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask)); + + Udp4CfgData.StationPort = *SrcPort; + + // + // Reset the UDPv4 instance. + // + Udp4->Configure (Udp4, NULL); + + Status = Udp4->Configure (Udp4, &Udp4CfgData); + if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) { + // + // The basic configuration is OK, need to add the default route entry + // + Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway); + if (EFI_ERROR (Status)) { + Udp4->Configure (Udp4, NULL); + } + } + + if (!EFI_ERROR (Status) && *SrcPort == 0) { + Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL); + *SrcPort = Udp4CfgData.StationPort; + } + + return Status; +} + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] StationIp The pointer to the station address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_IPv6_ADDRESS *StationIp, + IN OUT UINT16 *SrcPort + ) +{ + EFI_UDP6_CONFIG_DATA CfgData; + EFI_STATUS Status; + + ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA)); + + CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + CfgData.HopLimit = PXEBC_DEFAULT_HOPLIMIT; + CfgData.AllowDuplicatePort = TRUE; + CfgData.StationPort = *SrcPort; + + CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + + // + // Reset the UDPv6 instance. + // + Udp6->Configure (Udp6, NULL); + + Status = Udp6->Configure (Udp6, &CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!EFI_ERROR (Status) && *SrcPort == 0) { + Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL); + *SrcPort = CfgData.StationPort; + } + + return Status; +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] Session The pointer to the UDP4 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] Gateway The pointer to the gateway address. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully send out data using Udp4Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN EFI_IPv4_ADDRESS *Gateway OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_UDP4_COMPLETION_TOKEN Token; + EFI_UDP4_TRANSMIT_DATA *TxData; + UINT32 TxLength; + UINT32 FragCount; + UINT32 DataLength; + BOOLEAN IsDone; + EFI_STATUS Status; + + // + // Arrange one fragment buffer for data, and another fragment buffer for header if has. + // + FragCount = (HeaderSize != NULL) ? 2 : 1; + TxLength = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA); + TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength); + if (TxData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxData->FragmentCount = FragCount; + TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize; + TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr; + DataLength = (UINT32) *BufferSize; + + if (HeaderSize != NULL) { + TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize; + TxData->FragmentTable[0].FragmentBuffer = HeaderPtr; + DataLength += (UINT32) *HeaderSize; + } + + if (Gateway != NULL) { + TxData->GatewayAddress = Gateway; + } + + TxData->UdpSessionData = Session; + TxData->DataLength = DataLength; + Token.Packet.TxData = TxData; + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Udp4->Transmit (Udp4, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!IsDone && + Token.Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Udp4->Poll (Udp4); + } + + Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status; + +ON_EXIT: + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + FreePool (TxData); + + return Status; +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] Session The pointer to the UDP6 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully sent out data using Udp6Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_UDP6_COMPLETION_TOKEN Token; + EFI_UDP6_TRANSMIT_DATA *TxData; + UINT32 TxLength; + UINT32 FragCount; + UINT32 DataLength; + BOOLEAN IsDone; + EFI_STATUS Status; + + // + // Arrange one fragment buffer for data, and another fragment buffer for header if has. + // + FragCount = (HeaderSize != NULL) ? 2 : 1; + TxLength = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA); + TxData = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength); + if (TxData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxData->FragmentCount = FragCount; + TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize; + TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr; + DataLength = (UINT32) *BufferSize; + + if (HeaderSize != NULL) { + TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize; + TxData->FragmentTable[0].FragmentBuffer = HeaderPtr; + DataLength += (UINT32) *HeaderSize; + } + + TxData->UdpSessionData = Session; + TxData->DataLength = DataLength; + Token.Packet.TxData = TxData; + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Udp6->Transmit (Udp6, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!IsDone && + Token.Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Udp6->Poll (Udp6); + } + + Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status; + +ON_EXIT: + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + FreePool (TxData); + + return Status; +} + + +/** + Check the received packet using the Ip filter. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the Ip filter successfully. + @retval FALSE Failed to pass the Ip filter. + +**/ +BOOLEAN +PxeBcCheckByIpFilter ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN UINT16 OpFlags + ) +{ + EFI_IP_ADDRESS DestinationIp; + UINTN Index; + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) { + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) { + return TRUE; + } + + // + // Convert the destination address in session data to host order. + // + if (Mode->UsingIpv6) { + CopyMem ( + &DestinationIp, + &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + NTOHLLL (&DestinationIp.v6); + } else { + ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + &DestinationIp, + &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + EFI_NTOHL (DestinationIp); + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 && + (IP4_IS_MULTICAST (DestinationIp.Addr[0]) || + IP6_IS_MULTICAST (&DestinationIp))) { + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 && + IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) { + ASSERT (!Mode->UsingIpv6); + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 && + (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) || + EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) { + // + // Matched if the dest address is equal to the station address. + // + return TRUE; + } + + for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) { + ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT); + if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) || + EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) { + // + // Matched if the dest address is equal to any of address in the filter list. + // + return TRUE; + } + } + + return FALSE; +} + + +/** + Filter the received packet using the destination Ip. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] DestIp The pointer to the destination Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *DestIp, + IN UINT16 OpFlags + ) +{ + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) { + // + // Copy the destination address from the received packet if accept any. + // + if (DestIp != NULL) { + if (Mode->UsingIpv6) { + CopyMem ( + DestIp, + &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + DestIp, + &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + } + return TRUE; + } else if (DestIp != NULL && + (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) || + EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) { + // + // The destination address in the received packet is matched if present. + // + return TRUE; + } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) || + EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) { + // + // The destination address in the received packet is equal to the host address. + // + return TRUE; + } + + return FALSE; +} + + +/** + Check the received packet using the destination port. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] DestPort The pointer to the destination port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *DestPort, + IN UINT16 OpFlags + ) +{ + UINT16 Port; + + if (Mode->UsingIpv6) { + Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort; + } else { + Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort; + } + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) { + // + // Return the destination port in the received packet if accept any. + // + if (DestPort != NULL) { + *DestPort = Port; + } + return TRUE; + } else if (DestPort != NULL && *DestPort == Port) { + // + // The destination port in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + Filter the received packet using the source Ip. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] SrcIp The pointer to the source Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *SrcIp, + IN UINT16 OpFlags + ) +{ + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) { + // + // Copy the source address from the received packet if accept any. + // + if (SrcIp != NULL) { + if (Mode->UsingIpv6) { + CopyMem ( + SrcIp, + &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + SrcIp, + &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + } + return TRUE; + } else if (SrcIp != NULL && + (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) || + EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) { + // + // The source address in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + Filter the received packet using the source port. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] SrcPort The pointer to the source port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *SrcPort, + IN UINT16 OpFlags + ) +{ + UINT16 Port; + + if (Mode->UsingIpv6) { + Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort; + } else { + Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort; + } + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) { + // + // Return the source port in the received packet if accept any. + // + if (SrcPort != NULL) { + *SrcPort = Port; + } + return TRUE; + } else if (SrcPort != NULL && *SrcPort == Port) { + // + // The source port in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function is to receive packet using Udp4Read. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] Token The pointer to EFI_UDP4_COMPLETION_TOKEN. + @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone The pointer to the IsDone flag. + @param[out] IsMatched The pointer to the IsMatched flag. + @param[in, out] DestIp The pointer to the destination address. + @param[in, out] DestPort The pointer to the destination port. + @param[in, out] SrcIp The pointer to the source address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully read the data using Udp4. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Read ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ) +{ + EFI_UDP4_RECEIVE_DATA *RxData; + EFI_UDP4_SESSION_DATA *Session; + EFI_STATUS Status; + + Token->Status = EFI_NOT_READY; + *IsDone = FALSE; + + Status = Udp4->Receive (Udp4, Token); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!(*IsDone) && + Token->Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + // + // Poll the token utill reply/ICMPv6 error message received or timeout. + // + Udp4->Poll (Udp4); + if (Token->Status == EFI_ICMP_ERROR || + Token->Status == EFI_NETWORK_UNREACHABLE || + Token->Status == EFI_HOST_UNREACHABLE || + Token->Status == EFI_PROTOCOL_UNREACHABLE || + Token->Status == EFI_PORT_UNREACHABLE) { + break; + } + } + + Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status; + + if (!EFI_ERROR (Status)) { + // + // check whether this packet matches the filters + // + RxData = Token->Packet.RxData; + Session = &RxData->UdpSession; + + *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags); + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags); + } + + if (!(*IsMatched)) { + // + // Recycle the receiving buffer if not matched. + // + gBS->SignalEvent (RxData->RecycleSignal); + } + } + + return Status; +} + + +/** + This function is to receive packets using Udp6Read. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] Token The pointer to EFI_UDP6_COMPLETION_TOKEN. + @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone The pointer to the IsDone flag. + @param[out] IsMatched The pointer to the IsMatched flag. + @param[in, out] DestIp The pointer to the destination address. + @param[in, out] DestPort The pointer to the destination port. + @param[in, out] SrcIp The pointer to the source address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully read data using Udp6. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Read ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ) +{ + EFI_UDP6_RECEIVE_DATA *RxData; + EFI_UDP6_SESSION_DATA *Session; + EFI_STATUS Status; + + Token->Status = EFI_NOT_READY; + *IsDone = FALSE; + + Status = Udp6->Receive (Udp6, Token); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!(*IsDone) && + Token->Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + // + // Poll the token utill reply/ICMPv6 error message received or timeout. + // + Udp6->Poll (Udp6); + if (Token->Status == EFI_ICMP_ERROR || + Token->Status == EFI_NETWORK_UNREACHABLE || + Token->Status == EFI_HOST_UNREACHABLE || + Token->Status == EFI_PROTOCOL_UNREACHABLE || + Token->Status == EFI_PORT_UNREACHABLE) { + break; + } + } + + Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status; + + if (!EFI_ERROR (Status)) { + // + // check whether this packet matches the filters + // + RxData = Token->Packet.RxData; + Session = &RxData->UdpSession; + + *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags); + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags); + } + + if (!(*IsMatched)) { + // + // Recycle the receiving buffer if not matched. + // + gBS->SignalEvent (RxData->RecycleSignal); + } + } + + return Status; +} + + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +PxeBcShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 4; Index++) { + AsciiPrint ("%d", Ip->Addr[Index]); + if (Index < 3) { + AsciiPrint ("."); + } + } +} + + +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +PxeBcShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 16; Index++) { + + if (Ip->Addr[Index] != 0) { + AsciiPrint ("%x", Ip->Addr[Index]); + } + Index++; + if (Index > 15) { + return; + } + if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) { + AsciiPrint ("0"); + } + AsciiPrint ("%x", Ip->Addr[Index]); + if (Index < 15) { + AsciiPrint (":"); + } + } +} + + +/** + This function is to convert UINTN to ASCII string with the required formatting. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +PxeBcUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + while (Length > 0) { + Length--; + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (UINT8) ('0' + Remainder); + } +} + + +/** + This function is to convert a UINTN to a ASCII string, and return the + actual length of the buffer. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] BufferSize The maxsize of the buffer. + + @return Length The actual length of the ASCII string. + +**/ +UINTN +PxeBcUintnToAscDec ( + IN UINTN Number, + IN UINT8 *Buffer, + IN UINTN BufferSize + ) +{ + UINTN Index; + UINTN Length; + CHAR8 TempStr[64]; + + Index = 63; + TempStr[Index] = 0; + + do { + Index--; + TempStr[Index] = (CHAR8) ('0' + (Number % 10)); + Number = (UINTN) (Number / 10); + } while (Number != 0); + + AsciiStrCpyS ((CHAR8 *) Buffer, BufferSize, &TempStr[Index]); + + Length = AsciiStrLen ((CHAR8 *) Buffer); + + return Length; +} + + +/** + This function is to convert unicode hex number to a UINT8. + + @param[out] Digit The converted UINT8 for output. + @param[in] Char The unicode hex number to be converted. + + @retval EFI_SUCCESS Successfully converted the unicode hex. + @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex. + +**/ +EFI_STATUS +PxeBcUniHexToUint8 ( + OUT UINT8 *Digit, + IN CHAR16 Char + ) +{ + if ((Char >= L'0') && (Char <= L'9')) { + *Digit = (UINT8) (Char - L'0'); + return EFI_SUCCESS; + } + + if ((Char >= L'A') && (Char <= L'F')) { + *Digit = (UINT8) (Char - L'A' + 0x0A); + return EFI_SUCCESS; + } + + if ((Char >= L'a') && (Char <= L'f')) { + *Digit = (UINT8) (Char - L'a' + 0x0A); + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} + +/** + Calculate the elapsed time. + + @param[in] Private The pointer to PXE private data + +**/ +VOID +CalcElapsedTime ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_TIME Time; + UINT64 CurrentStamp; + UINT64 ElapsedTimeValue; + + // + // Generate a time stamp of the centiseconds from 1900/1/1, assume 30day/month. + // + ZeroMem (&Time, sizeof (EFI_TIME)); + gRT->GetTime (&Time, NULL); + CurrentStamp = (UINT64) + ( + ((((((Time.Year - 1900) * 360 + + (Time.Month - 1)) * 30 + + (Time.Day - 1)) * 24 + Time.Hour) * 60 + + Time.Minute) * 60 + Time.Second) * 100 + + DivU64x32(Time.Nanosecond, 10000000) + ); + + // + // Sentinel value of 0 means that this is the first DHCP packet that we are + // sending and that we need to initialize the value. First DHCP Solicit + // gets 0 elapsed-time. Otherwise, calculate based on StartTime. + // + if (Private->ElapsedTime == 0) { + Private->ElapsedTime = CurrentStamp; + } else { + ElapsedTimeValue = CurrentStamp - Private->ElapsedTime; + + // + // If elapsed time cannot fit in two bytes, set it to 0xffff. + // + if (ElapsedTimeValue > 0xffff) { + ElapsedTimeValue = 0xffff; + } + // + // Save the elapsed time + // + Private->ElapsedTime = ElapsedTimeValue; + } +} + diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h new file mode 100644 index 0000000000..0a43aeb79b --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h @@ -0,0 +1,515 @@ +/** @file + Support functions declaration for UefiPxeBc Driver. + + Copyright (c) 2007 - 2014, 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 __EFI_PXEBC_SUPPORT_H__ +#define __EFI_PXEBC_SUPPORT_H__ + + +#define ICMP_DEST_UNREACHABLE 3 +#define ICMP_SOURCE_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO_REQUEST 8 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 + + + +/** + Flush the previous configration using the new station Ip address. + + @param[in] Private Pointer to PxeBc private data. + @param[in] StationIp Pointer to the station Ip address. + @param[in] SubnetMask Pointer to the subnet mask address for v4. + + @retval EFI_SUCCESS Successfully flushed the previous config. + @retval Others Failed to flush using the new station Ip. + +**/ +EFI_STATUS +PxeBcFlushStationIp ( + PXEBC_PRIVATE_DATA *Private, + EFI_IP_ADDRESS *StationIp, + EFI_IP_ADDRESS *SubnetMask OPTIONAL + ); + + +/** + Notify callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +PxeBcCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + Perform arp resolution from the arp cache in PxeBcMode. + + @param Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param Ip4Addr The Ip4 address for resolution. + @param MacAddress The resoluted MAC address if the resolution is successful. + The value is undefined if resolution fails. + + @retval TRUE Found a matched entry. + @retval FALSE Did not find a matched entry. + +**/ +BOOLEAN +PxeBcCheckArpCache ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_IPv4_ADDRESS *Ip4Addr, + OUT EFI_MAC_ADDRESS *MacAddress + ); + + +/** + Update arp cache periodically. + + @param Event Pointer to EFI_PXE_BC_PROTOCOL. + @param Context Context of the timer event. + +**/ +VOID +EFIAPI +PxeBcArpCacheUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + xxx + + @param Event The event signaled. + @param Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + xxx + + @param Event The event signaled. + @param Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] StationIp Pointer to the station address. + @param[in] SubnetMask Pointer to the subnet mask. + @param[in] Gateway Pointer to the gateway address. + @param[in, out] SrcPort Pointer to the source port. + @param[in] DoNotFragment The flag of DoNotFragment bit in the IPv4 + packet. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_IPv4_ADDRESS *StationIp, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *Gateway, + IN OUT UINT16 *SrcPort, + IN BOOLEAN DoNotFragment + ); + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] StationIp Pointer to the station address. + @param[in, out] SrcPort Pointer to the source port. + + @retval EFI_SUCCESS Successfuly configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_IPv6_ADDRESS *StationIp, + IN OUT UINT16 *SrcPort + ); + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] Session Pointer to the UDP4 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] Gateway Pointer to the gateway address. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully sent out data with Udp4Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN EFI_IPv4_ADDRESS *Gateway OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] Session Pointer to the UDP6 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully to send out data with Udp6Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + + +/** + Check the received packet with the Ip filter. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the Ip filter. + @retval FALSE Failed to pass the Ip filter. + +**/ +BOOLEAN +PxeBcCheckByIpFilter ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the destination Ip. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] DestIp Pointer to the dest Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *DestIp, + IN UINT16 OpFlags + ); + + +/** + Check the received packet with the destination port. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] DestPort Pointer to the destination port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *DestPort, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the source Ip. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] SrcIp Pointer to the source Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *SrcIp, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the source port. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] SrcPort Pointer to the source port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *SrcPort, + IN UINT16 OpFlags + ); + + +/** + This function is to receive packet with Udp4Read. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] Token Pointer to EFI_UDP4_COMPLETION_TOKEN. + @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone Pointer to IsDone flag. + @param[out] IsMatched Pointer to IsMatched flag. + @param[in, out] DestIp Pointer to destination address. + @param[in, out] DestPort Pointer to destination port. + @param[in, out] SrcIp Pointer to source address. + @param[in, out] SrcPort Pointer to source port. + + @retval EFI_SUCCESS Successfully read data with Udp4. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Read ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ); + + +/** + This function is to receive packet with Udp6Read. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] Token Pointer to EFI_UDP6_COMPLETION_TOKEN. + @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone Pointer to IsDone flag. + @param[out] IsMatched Pointer to IsMatched flag. + @param[in, out] DestIp Pointer to destination address. + @param[in, out] DestPort Pointer to destination port. + @param[in, out] SrcIp Pointer to source address. + @param[in, out] SrcPort Pointer to source port. + + @retval EFI_SUCCESS Successfully read data with Udp6. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Read ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ); + + +/** + This function is to display the IPv4 address. + + @param[in] Ip Pointer to the IPv4 address. + +**/ +VOID +PxeBcShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ); + + +/** + This function is to display the IPv6 address. + + @param[in] Ip Pointer to the IPv6 address. + +**/ +VOID +PxeBcShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ); + + +/** + This function is to convert UINTN to ASCII string with required format. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer Pointer to the buffer for ASCII string. + @param[in] Length Length of the required format. + +**/ +VOID +PxeBcUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ); + + +/** + This function is to convert a UINTN to a ASCII string, and return the + actual length of the buffer. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer Pointer to the buffer for ASCII string. + @param[in] BufferSize The maxsize of the buffer. + + @return Length The actual length of the ASCII string. + +**/ +UINTN +PxeBcUintnToAscDec ( + IN UINTN Number, + IN UINT8 *Buffer, + IN UINTN BufferSize + ); + +/** + This function is to convert unicode hex number to a UINT8. + + @param[out] Digit The converted UINT8 for output. + @param[in] Char The unicode hex number to be converted. + + @retval EFI_SUCCESS Successfully converted the unicode hex. + @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex. + +**/ +EFI_STATUS +PxeBcUniHexToUint8 ( + OUT UINT8 *Digit, + IN CHAR16 Char + ); + +/** + Calculate the elapsed time. + + @param[in] Private The pointer to PXE private data + +**/ +VOID +CalcElapsedTime ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ); + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ); +#endif diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf new file mode 100644 index 0000000000..c3ca218ba3 --- /dev/null +++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf @@ -0,0 +1,109 @@ +## @file +# Access PXE-compatible devices for network access and network booting. +# +# This driver provides PXE Base Code Protocol which is used to accessing +# PXE-compatible device for network access or booting. It could work together +# with an IPv4 stack, an IPv6 stack or both. +# +# +# Copyright (c) 2007 - 2015, 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 = UefiPxeBcDxe + FILE_GUID = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = PxeBcDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = UefiPxeBcDxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + ComponentName.c + PxeBcDriver.c + PxeBcDriver.h + PxeBcImpl.c + PxeBcImpl.h + PxeBcBoot.c + PxeBcBoot.h + PxeBcDhcp6.c + PxeBcDhcp6.h + PxeBcDhcp4.c + PxeBcDhcp4.h + PxeBcMtftp.c + PxeBcMtftp.h + PxeBcSupport.c + PxeBcSupport.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + MemoryAllocationLib + DebugLib + NetLib + DpcLib + DevicePathLib + PcdLib + +[Protocols] + ## TO_START + ## SOMETIMES_CONSUMES + gEfiDevicePathProtocolGuid + gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES + gEfiArpServiceBindingProtocolGuid ## TO_START + gEfiArpProtocolGuid ## TO_START + gEfiIp4ServiceBindingProtocolGuid ## TO_START + gEfiIp4ProtocolGuid ## TO_START + gEfiIp4Config2ProtocolGuid ## TO_START + gEfiIp6ServiceBindingProtocolGuid ## TO_START + gEfiIp6ProtocolGuid ## TO_START + gEfiIp6ConfigProtocolGuid ## TO_START + gEfiUdp4ServiceBindingProtocolGuid ## TO_START + gEfiUdp4ProtocolGuid ## TO_START + gEfiMtftp4ServiceBindingProtocolGuid ## TO_START + gEfiMtftp4ProtocolGuid ## TO_START + gEfiDhcp4ServiceBindingProtocolGuid ## TO_START + gEfiDhcp4ProtocolGuid ## TO_START + gEfiUdp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ProtocolGuid ## TO_START + gEfiMtftp6ServiceBindingProtocolGuid ## TO_START + gEfiMtftp6ProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## TO_START + gEfiDhcp6ProtocolGuid ## TO_START + gEfiPxeBaseCodeCallbackProtocolGuid ## SOMETIMES_PRODUCES + gEfiPxeBaseCodeProtocolGuid ## BY_START + gEfiLoadFileProtocolGuid ## BY_START + gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiAdapterInfoUndiIpv6SupportGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize ## SOMETIMES_CONSUMES +[UserExtensions.TianoCore."ExtraFiles"] + UefiPxeBcDxeExtra.uni diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni new file mode 100644 index 0000000000..37ca0cb036 Binary files /dev/null and b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni differ diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni new file mode 100644 index 0000000000..2877631fc4 Binary files /dev/null and b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni differ -- cgit v1.2.3