mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-11-04 01:03:23 +00:00 
			
		
		
		
	2. Update IP6 driver to use previous configured prefix length if a pre-exist IP6 address with unspecified prefix length. 3. Add NULL check for Dhcp protocol pointer before it decline the address in Ip6ConfigSetStatefulAddrCallback() function. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Fu, Siyuan <siyuan.fu@intel.com> Reviewed-By: Ye, Ting (ting.ye@intel.com) Reviewed-By: Wu, Jiaxin <jiaxin.wu@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16131 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1260 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1260 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Boot functions implementation 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.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#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,
 | 
						|
                  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;
 | 
						|
}
 | 
						|
 |