summaryrefslogtreecommitdiff
path: root/Platform/BroxtonPlatformPkg
diff options
context:
space:
mode:
authorWei, David <david.wei@intel.com>2017-01-09 10:05:42 +0800
committerGuo Mang <mang.guo@intel.com>2017-01-12 17:25:58 +0800
commit21eb63bbdd278d149720fb25bff15c22df0c254d (patch)
tree3e91007f0d3f599a168b8deb6a2f0383e0cd3553 /Platform/BroxtonPlatformPkg
parent35468e2aad64ab8a062d696349220e5581fa2f91 (diff)
downloadedk2-platforms-21eb63bbdd278d149720fb25bff15c22df0c254d.tar.xz
Fix build error.
Reviewed-by: zwei4 <david.wei@intel.com> Thanks, David Wei -----Original Message----- From: Lu, ShifeiX A Sent: Monday, January 09, 2017 9:39 AM To: edk2-devel@lists.01.org Cc: Wei, David <david.wei@intel.com> Subject: [Patch][edk2-platforms/devel-MinnowBoard3] Fix build error. Fix build error for latest VS2015 compiler with IA32 tip. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: lushifex <shifeix.a.lu@intel.com>
Diffstat (limited to 'Platform/BroxtonPlatformPkg')
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c358
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c1259
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h100
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c1672
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h409
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c2096
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h303
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c1825
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h181
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c2411
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h225
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c1113
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h137
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c1513
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h515
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf109
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.unibin0 -> 2416 bytes
-rw-r--r--Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.unibin0 -> 1332 bytes
-rw-r--r--Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc4
-rw-r--r--Platform/BroxtonPlatformPkg/PlatformPkg.fdf4
20 files changed, 14230 insertions, 4 deletions
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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+/**
+ 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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+ 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 <F8> or <Ctrl> + <M> 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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+//
+// This 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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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.<BR>
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+//
+// 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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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.<BR>
+ Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+ 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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_IMPL_H__
+#define __EFI_PXEBC_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Guid/SmBios.h>
+#include <IndustryStandard/SmBios.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Arp.h>
+#include <Protocol/Ip4.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Mtftp4.h>
+#include <Protocol/Mtftp6.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/PxeBaseCodeCallBack.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/AdapterInformation.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/DpcLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+
+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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+ 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.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[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
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni
Binary files 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
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni
Binary files differ
diff --git a/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc b/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc
index 0d2d0db683..53658cf795 100644
--- a/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc
+++ b/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc
@@ -1,7 +1,7 @@
## @file
# Platform Components Description.
#
-# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
@@ -325,7 +325,7 @@
NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
!endif
!if $(NETWORK_IP6_ENABLE) == TRUE
- NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
+ $(PLATFORM_PACKAGE_COMMON)/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
!else
MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf {
<LibraryClasses>
diff --git a/Platform/BroxtonPlatformPkg/PlatformPkg.fdf b/Platform/BroxtonPlatformPkg/PlatformPkg.fdf
index 93f04229c5..0d9a087f3c 100644
--- a/Platform/BroxtonPlatformPkg/PlatformPkg.fdf
+++ b/Platform/BroxtonPlatformPkg/PlatformPkg.fdf
@@ -1,7 +1,7 @@
## @file
# FDF file of Platform.
#
-# Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2008 - 2017, Intel Corporation. All rights reserved.<BR>
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
@@ -681,7 +681,7 @@ APRIORI DXE {
!endif
!if $(NETWORK_IP6_ENABLE) == TRUE
INF NetworkPkg/TcpDxe/TcpDxe.inf
- INF NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
+ INF $(PLATFORM_PACKAGE_COMMON)/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
!else
INF MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
INF MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf