REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the NetworkPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
		
			
				
	
	
		
			1273 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1273 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Boot functions implementation for UefiPxeBc Driver.
 | |
| 
 | |
|   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
|   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
 | |
| 
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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 keyboard 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;
 | |
|   }
 | |
| 
 | |
|   if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
 | |
|     //
 | |
|     // This should never happen in a correctly configured DHCP / PXE
 | |
|     // environment. One misconfiguration that can cause it is two DHCP servers
 | |
|     // mistakenly running on the same network segment at the same time, and
 | |
|     // racing each other in answering DHCP requests. Thus, the DHCP packets
 | |
|     // that the edk2 PXE client considers "belonging together" may actually be
 | |
|     // entirely independent, coming from two (competing) DHCP servers.
 | |
|     //
 | |
|     // Try to deal with this gracefully. Note that this check is not
 | |
|     // comprehensive, as we don't try to identify all such errors.
 | |
|     //
 | |
|     return EFI_PROTOCOL_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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;
 | |
|   }
 | |
| 
 | |
|   if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
 | |
|     //
 | |
|     // This should never happen in a correctly configured DHCP / PXE
 | |
|     // environment. One misconfiguration that can cause it is two DHCP servers
 | |
|     // mistakenly running on the same network segment at the same time, and
 | |
|     // racing each other in answering DHCP requests. Thus, the DHCP packets
 | |
|     // that the edk2 PXE client considers "belonging together" may actually be
 | |
|     // entirely independent, coming from two (competing) DHCP servers.
 | |
|     //
 | |
|     // Try to deal with this gracefully. Note that this check is not
 | |
|     // comprehensive, as we don't try to identify all such errors.
 | |
|     //
 | |
|     return EFI_PROTOCOL_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set the station address to IP layer.
 | |
|   //
 | |
|   Status = PxeBcSetIp6Address (Private);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Parse (m)tftp server ip address and bootfile name.
 | |
|   //
 | |
|   Status = PxeBcExtractBootFileUrl (
 | |
|              Private,
 | |
|              &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;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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 successfully.
 | |
|   @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->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->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->Mode.UsingIpv6 ? &Private->Ip6Nic->Controller : &Private->Ip4Nic->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->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->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  NBP file downloaded successfully.\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 valid 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;
 | |
| }
 |