git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@6313 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1606 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1606 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
| Copyright (c) 2004 - 2008, Intel Corporation
 | |
| All rights reserved. This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| Module Name:
 | |
|   pxe_loadfile.c
 | |
| 
 | |
| Abstract:
 | |
|   An implementation of the load file protocol for network devices.
 | |
| 
 | |
| 
 | |
| **/
 | |
| 
 | |
| 
 | |
| #include "Bc.h"
 | |
| 
 | |
| #define DO_MENU     (EFI_SUCCESS)
 | |
| #define NO_MENU     (DO_MENU + 1)
 | |
| #define LOCAL_BOOT  (EFI_ABORTED)
 | |
| #define AUTO_SELECT (NO_MENU)
 | |
| 
 | |
| #define NUMBER_ROWS   25  // we set to mode 0
 | |
| #define MAX_MENULIST  23
 | |
| 
 | |
| #define Ctl(x)  (0x1F & (x))
 | |
| 
 | |
| typedef union {
 | |
|   DHCPV4_OP_STRUCT          *OpPtr;
 | |
|   PXE_BOOT_MENU_ENTRY       *CurrentMenuItemPtr;
 | |
|   PXE_OP_DISCOVERY_CONTROL  *DiscCtlOpStr;
 | |
|   PXE_OP_BOOT_MENU          *MenuPtr;
 | |
|   UINT8                     *BytePtr;
 | |
| } UNION_PTR;
 | |
| 
 | |
| UINTN  mPropeller;
 | |
| 
 | |
| /**
 | |
|   PxeBc callback routine for status updates and aborts.
 | |
| 
 | |
|   @param  This                                        Pointer to PxeBcCallback
 | |
|                                                       interface
 | |
|   @param  Function                                    PxeBc function ID#
 | |
|   @param  Received                                    Receive/transmit flag
 | |
|   @param  PacketLength                                Length of received packet (0
 | |
|                                                       == idle callback)
 | |
|   @param  PacketPtr                                   Pointer to received packet
 | |
|                                                       (NULL == idle callback)
 | |
| 
 | |
|   @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
 | |
|                                                       EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
 | |
|                                                       -
 | |
| 
 | |
| **/
 | |
| EFI_PXE_BASE_CODE_CALLBACK_STATUS
 | |
| EFIAPI
 | |
| bc_callback (
 | |
|   IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  * This,
 | |
|   IN EFI_PXE_BASE_CODE_FUNCTION           Function,
 | |
|   IN BOOLEAN                              Received,
 | |
|   IN UINT32                               PacketLength,
 | |
|   IN EFI_PXE_BASE_CODE_PACKET             * PacketPtr OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_INPUT_KEY Key;
 | |
|   UINTN         Row;
 | |
|   UINTN         Col;
 | |
| 
 | |
|   mPropeller = 0;
 | |
|   //
 | |
|   // Resolve Warning 4 unreferenced parameter problem
 | |
|   //
 | |
|   This = This;
 | |
| 
 | |
|   //
 | |
|   // Check for user abort.
 | |
|   //
 | |
|   if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) {
 | |
|     if (!Key.ScanCode) {
 | |
|       if (Key.UnicodeChar == Ctl ('c')) {
 | |
|         return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
 | |
|       }
 | |
|     } else if (Key.ScanCode == SCAN_ESC) {
 | |
|       return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Do nothing if this is a receive.
 | |
|   //
 | |
|   if (Received) {
 | |
|     return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
 | |
|   }
 | |
|   //
 | |
|   // The display code is only for these functions.
 | |
|   //
 | |
|   switch (Function) {
 | |
|   case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
 | |
|     //
 | |
|     // If this is a transmit and not a M/TFTP open request,
 | |
|     // return now.  Do not print a dot for each M/TFTP packet
 | |
|     // that is sent, only for the open packets.
 | |
|     //
 | |
|     if (PacketLength != 0 && PacketPtr != NULL) {
 | |
|       if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
 | |
|         return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     break;
 | |
| 
 | |
|   case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
 | |
|   case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
 | |
|   }
 | |
|   //
 | |
|   // Display routines
 | |
|   //
 | |
|   if (PacketLength != 0 && PacketPtr != NULL) {
 | |
|     //
 | |
|     // Display a '.' when a packet is transmitted.
 | |
|     //
 | |
|     AsciiPrint (".");
 | |
|   } else if (PacketLength == 0 && PacketPtr == NULL) {
 | |
|     //
 | |
|     // Display a propeller when waiting for packets if at
 | |
|     // least 200 ms have passed.
 | |
|     //
 | |
|     Row = gST->ConOut->Mode->CursorRow;
 | |
|     Col = gST->ConOut->Mode->CursorColumn;
 | |
| 
 | |
|     AsciiPrint ("%c", "/-\\|"[mPropeller]);
 | |
|     gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);
 | |
| 
 | |
|     mPropeller = (mPropeller + 1) & 3;
 | |
|   }
 | |
| 
 | |
|   return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
 | |
| }
 | |
| 
 | |
| EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  _bc_callback = {
 | |
|   EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
 | |
|   &bc_callback
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Display an IPv4 address in dot notation.
 | |
| 
 | |
|   @param  Ptr                                         Pointer to IPv4 address.
 | |
| 
 | |
|   @return None
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PrintIpv4 (
 | |
|   UINT8 *Ptr
 | |
|   )
 | |
| {
 | |
|   if (Ptr != NULL) {
 | |
|     AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Display client and server IP information.
 | |
| 
 | |
|   @param  Private                                     Pointer to PxeBc interface
 | |
| 
 | |
|   @return None
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ShowMyInfo (
 | |
|   IN PXE_BASECODE_DEVICE *Private
 | |
|   )
 | |
| {
 | |
|   EFI_PXE_BASE_CODE_MODE  *PxeBcMode;
 | |
|   UINTN                   Index;
 | |
| 
 | |
|   //
 | |
|   // Do nothing if a NULL pointer is passed in.
 | |
|   //
 | |
|   if (Private == NULL) {
 | |
|     return ;
 | |
|   }
 | |
|   //
 | |
|   // Get pointer to PXE BaseCode mode structure
 | |
|   //
 | |
|   PxeBcMode = Private->EfiBc.Mode;
 | |
| 
 | |
|   //
 | |
|   // Display client IP address
 | |
|   //
 | |
|   AsciiPrint ("\rCLIENT IP: ");
 | |
|   PrintIpv4 (PxeBcMode->StationIp.v4.Addr);
 | |
| 
 | |
|   //
 | |
|   // Display subnet mask
 | |
|   //
 | |
|   AsciiPrint ("  MASK: ");
 | |
|   PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr);
 | |
| 
 | |
|   //
 | |
|   // Display DHCP and proxyDHCP IP addresses
 | |
|   //
 | |
|   if (PxeBcMode->ProxyOfferReceived) {
 | |
|     AsciiPrint ("\nDHCP IP: ");
 | |
|     PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
 | |
| 
 | |
|     AsciiPrint ("  PROXY IP: ");
 | |
|     PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
 | |
|   } else {
 | |
|     AsciiPrint ("  DHCP IP: ");
 | |
|     PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
 | |
|   }
 | |
|   //
 | |
|   // Display gateway IP addresses
 | |
|   //
 | |
|   for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
 | |
|     if ((Index % 3) == 0) {
 | |
|       AsciiPrint ("\r\nGATEWAY IP:");
 | |
|     }
 | |
| 
 | |
|     AsciiPrint (" ");
 | |
|     PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr);
 | |
|     AsciiPrint (" ");
 | |
|   }
 | |
| 
 | |
|   AsciiPrint ("\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Display prompt and wait for input.
 | |
| 
 | |
|   @param  Private                                     Pointer to PxeBc interface
 | |
|   @param  BootPromptPtr                               Pointer to PXE boot prompt
 | |
|                                                       option
 | |
| 
 | |
|   @retval AUTO_SELECT                                 DO_MENU -
 | |
|   @retval NO_MENU
 | |
|   @retval LOCAL_BOOT
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DoPrompt (
 | |
|   PXE_BASECODE_DEVICE *Private,
 | |
|   PXE_OP_BOOT_PROMPT  *BootPromptPtr
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   EFI_EVENT   TimeoutEvent;
 | |
|   EFI_EVENT   SecondsEvent;
 | |
|   INT32       SecColumn;
 | |
|   INT32       SecRow;
 | |
|   UINT8       SaveChar;
 | |
|   UINT8       SecsLeft;
 | |
| 
 | |
|   //
 | |
|   // if auto select, just get right to it
 | |
|   //
 | |
|   if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) {
 | |
|     return AUTO_SELECT;
 | |
|   }
 | |
|   //
 | |
|   // if no timeout, go directly to display of menu
 | |
|   //
 | |
|   if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) {
 | |
|     return DO_MENU;
 | |
|   }
 | |
|   //
 | |
|   //
 | |
|   //
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER,
 | |
|                   TPL_CALLBACK,
 | |
|                   NULL,
 | |
|                   NULL,
 | |
|                   &TimeoutEvent
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return DO_MENU;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->SetTimer (
 | |
|                   TimeoutEvent,
 | |
|                   TimerRelative,
 | |
|                   BootPromptPtr->Timeout * 10000000 + 100000
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->CloseEvent (TimeoutEvent);
 | |
|     return DO_MENU;
 | |
|   }
 | |
|   //
 | |
|   //
 | |
|   //
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER,
 | |
|                   TPL_CALLBACK,
 | |
|                   NULL,
 | |
|                   NULL,
 | |
|                   &SecondsEvent
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->CloseEvent (TimeoutEvent);
 | |
|     return DO_MENU;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->SetTimer (
 | |
|                   SecondsEvent,
 | |
|                   TimerPeriodic,
 | |
|                   10000000
 | |
|                   );  /* 1 second */
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->CloseEvent (SecondsEvent);
 | |
|     gBS->CloseEvent (TimeoutEvent);
 | |
|     return DO_MENU;
 | |
|   }
 | |
|   //
 | |
|   // display the prompt
 | |
|   // IMPORTANT!  This prompt is an ASCII character string that may
 | |
|   // not be terminated with a NULL byte.
 | |
|   //
 | |
|   SaveChar  = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1];
 | |
|   BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0;
 | |
| 
 | |
|   AsciiPrint ("%a ", BootPromptPtr->Prompt);
 | |
|   BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar;
 | |
| 
 | |
|   //
 | |
|   // wait until time expires or selection made - menu or local
 | |
|   //
 | |
|   SecColumn = gST->ConOut->Mode->CursorColumn;
 | |
|   SecRow    = gST->ConOut->Mode->CursorRow;
 | |
|   SecsLeft  = BootPromptPtr->Timeout;
 | |
| 
 | |
|   gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
 | |
|   AsciiPrint ("(%d) ", SecsLeft);
 | |
| 
 | |
|   //
 | |
|   // set the default action to be AUTO_SELECT
 | |
|   //
 | |
|   Status = AUTO_SELECT;
 | |
| 
 | |
|   while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
 | |
|     EFI_INPUT_KEY Key;
 | |
| 
 | |
|     if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) {
 | |
|       --SecsLeft;
 | |
|       gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
 | |
|       AsciiPrint ("(%d) ", SecsLeft);
 | |
|     }
 | |
| 
 | |
|     if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
 | |
|       UINT8       Buffer[512];
 | |
|       UINTN       BufferSize;
 | |
| 
 | |
|       BufferSize = sizeof Buffer;
 | |
| 
 | |
|       Status = Private->EfiBc.UdpRead (
 | |
|                                 &Private->EfiBc,
 | |
|                                 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
 | |
|                                 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |
 | |
|                                 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,
 | |
|                                 NULL, /* dest ip */
 | |
|                                 NULL, /* dest port */
 | |
|                                 NULL, /* src ip */
 | |
|                                 NULL, /* src port */
 | |
|                                 NULL, /* hdr size */
 | |
|                                 NULL, /* hdr ptr */
 | |
|                                 &BufferSize,
 | |
|                                 Buffer
 | |
|                                 );
 | |
| 
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (Key.ScanCode == 0) {
 | |
|       switch (Key.UnicodeChar) {
 | |
|       case Ctl ('c'):
 | |
|         Status = LOCAL_BOOT;
 | |
|         break;
 | |
| 
 | |
|       case Ctl ('m'):
 | |
|       case 'm':
 | |
|       case 'M':
 | |
|         Status = DO_MENU;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         continue;
 | |
|       }
 | |
|     } else {
 | |
|       switch (Key.ScanCode) {
 | |
|       case SCAN_F8:
 | |
|         Status = DO_MENU;
 | |
|         break;
 | |
| 
 | |
|       case SCAN_ESC:
 | |
|         Status = LOCAL_BOOT;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   gBS->CloseEvent (SecondsEvent);
 | |
|   gBS->CloseEvent (TimeoutEvent);
 | |
| 
 | |
|   gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
 | |
|   AsciiPrint ("     ");
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Display one menu item.
 | |
| 
 | |
|   @param  MenuItemPtr                                 Pointer to PXE menu item
 | |
|                                                       option.
 | |
| 
 | |
|   @return None
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PrintMenuItem (
 | |
|   PXE_BOOT_MENU_ENTRY *MenuItemPtr
 | |
|   )
 | |
| {
 | |
|   UINT8 Length;
 | |
|   UINT8 SaveChar;
 | |
| 
 | |
|   Length                    = (UINT8) MIN (70, MenuItemPtr->DataLen);
 | |
|   SaveChar                  = MenuItemPtr->Data[Length];
 | |
| 
 | |
|   MenuItemPtr->Data[Length] = 0;
 | |
|   AsciiPrint ("     %a\n", MenuItemPtr->Data);
 | |
|   MenuItemPtr->Data[Length] = SaveChar;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Display and process menu.
 | |
| 
 | |
|   @param  Private                                     Pointer to PxeBc interface
 | |
|   @param  RxBufferPtr                                 Pointer to receive buffer
 | |
| 
 | |
|   @retval NO_MENU
 | |
|   @retval LOCAL_BOOT
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DoMenu (
 | |
|   PXE_BASECODE_DEVICE *Private,
 | |
|   DHCP_RECEIVE_BUFFER *RxBufferPtr
 | |
|   )
 | |
| {
 | |
|   PXE_OP_DISCOVERY_CONTROL  *DiscoveryControlPtr;
 | |
|   PXE_BOOT_MENU_ENTRY       *MenuItemPtrs[MAX_MENULIST];
 | |
|   EFI_STATUS                Status;
 | |
|   UNION_PTR                 Ptr;
 | |
|   UINTN                     SaveNumRte;
 | |
|   UINTN                     TopRow;
 | |
|   UINTN                     MenuLth;
 | |
|   UINTN                     NumMenuItems;
 | |
|   UINTN                     Index;
 | |
|   UINTN                     Longest;
 | |
|   UINTN                     Selected;
 | |
|   UINT16                    Type;
 | |
|   UINT16                    Layer;
 | |
|   BOOLEAN                   Done;
 | |
| 
 | |
|   Selected  = 0;
 | |
|   Layer     = 0;
 | |
| 
 | |
|   DEBUG ((DEBUG_WARN, "\nDoMenu()  Enter."));
 | |
| 
 | |
|   /* see if we have a menu/prompt */
 | |
|   if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) {
 | |
|     DEBUG (
 | |
|       (DEBUG_WARN,
 | |
|       "\nDoMenu()  No menu/prompt info.  OpAdds.Status == %xh  ",
 | |
|       RxBufferPtr->OpAdds.Status)
 | |
|       );
 | |
| 
 | |
|     return NO_MENU;
 | |
|   }
 | |
| 
 | |
|   DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];
 | |
| 
 | |
|   //
 | |
|   // if not USE_BOOTFILE or no bootfile given, must have menu stuff
 | |
|   //
 | |
|   if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
 | |
|     DEBUG ((DEBUG_WARN, "\nDoMenu()  DHCP w/ bootfile.  "));
 | |
|     return NO_MENU;
 | |
|   }
 | |
|   //
 | |
|   // do prompt & menu if necessary
 | |
|   //
 | |
|   Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]);
 | |
| 
 | |
|   if (Status == LOCAL_BOOT) {
 | |
|     DEBUG ((DEBUG_WARN, "\nDoMenu()  DoPrompt() returned LOCAL_BOOT.  "));
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Ptr.BytePtr             = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];
 | |
| 
 | |
|   MenuLth                 = Ptr.MenuPtr->Header.Length;
 | |
|   Ptr.CurrentMenuItemPtr  = Ptr.MenuPtr->MenuItem;
 | |
| 
 | |
|   //
 | |
|   // build menu items array
 | |
|   //
 | |
|   for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) {
 | |
|     UINTN lth;
 | |
| 
 | |
|     lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data);
 | |
| 
 | |
|     MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr;
 | |
| 
 | |
|     if (lth > Longest) {
 | |
|       //
 | |
|       // check if too long
 | |
|       //
 | |
|       if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) {
 | |
|         Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data));
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Index += lth;
 | |
|     Ptr.BytePtr += lth;
 | |
|   }
 | |
| 
 | |
|   if (Status != AUTO_SELECT) {
 | |
|     UINT8 BlankBuf[75];
 | |
| 
 | |
|     SetMem (BlankBuf, sizeof BlankBuf, ' ');
 | |
|     BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0;
 | |
|     AsciiPrint ("\n");
 | |
| 
 | |
|     //
 | |
|     // now put up menu
 | |
|     //
 | |
|     for (Index = 0; Index < NumMenuItems; ++Index) {
 | |
|       PrintMenuItem (MenuItemPtrs[Index]);
 | |
|     }
 | |
| 
 | |
|     TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems;
 | |
| 
 | |
|     //
 | |
|     // now wait for a selection
 | |
|     //
 | |
|     Done = FALSE;
 | |
|     do {
 | |
|       //
 | |
|       // highlight selection
 | |
|       //
 | |
|       EFI_INPUT_KEY Key;
 | |
|       UINTN         NewSelected;
 | |
| 
 | |
|       NewSelected = Selected;
 | |
| 
 | |
|       //
 | |
|       // highlight selected row
 | |
|       //
 | |
|       gST->ConOut->SetAttribute (
 | |
|                     gST->ConOut,
 | |
|                     EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)
 | |
|                     );
 | |
|       gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected);
 | |
| 
 | |
|       AsciiPrint (" --->%a\r", BlankBuf);
 | |
| 
 | |
|       PrintMenuItem (MenuItemPtrs[Selected]);
 | |
|       gST->ConOut->SetAttribute (
 | |
|                     gST->ConOut,
 | |
|                     EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
 | |
|                     );
 | |
|       gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems);
 | |
| 
 | |
|       //
 | |
|       // wait for a keystroke
 | |
|       //
 | |
|       while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
 | |
|         UINT8 TmpBuf[512];
 | |
|         UINTN TmpBufLen;
 | |
| 
 | |
|         TmpBufLen = sizeof TmpBuf;
 | |
| 
 | |
|         Private->EfiBc.UdpRead (
 | |
|                         &Private->EfiBc,
 | |
|                         EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
 | |
|                         EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |
 | |
|                         EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,
 | |
|                         NULL, /* dest ip */
 | |
|                         NULL, /* dest port */
 | |
|                         NULL, /* src ip */
 | |
|                         NULL, /* src port */
 | |
|                         NULL, /* hdr size */
 | |
|                         NULL, /* hdr ptr */
 | |
|                         &TmpBufLen,
 | |
|                         TmpBuf
 | |
|                         );
 | |
|       }
 | |
| 
 | |
|       if (!Key.ScanCode) {
 | |
|         switch (Key.UnicodeChar) {
 | |
|         case Ctl ('c'):
 | |
|           Key.ScanCode = SCAN_ESC;
 | |
|           break;
 | |
| 
 | |
|         case Ctl ('j'): /* linefeed */
 | |
|         case Ctl ('m'): /* return */
 | |
|           Done = TRUE;
 | |
|           break;
 | |
| 
 | |
|         case Ctl ('i'): /* tab */
 | |
|         case ' ':
 | |
|         case 'd':
 | |
|         case 'D':
 | |
|           Key.ScanCode = SCAN_DOWN;
 | |
|           break;
 | |
| 
 | |
|         case Ctl ('h'): /* backspace */
 | |
|         case 'u':
 | |
|         case 'U':
 | |
|           Key.ScanCode = SCAN_UP;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           Key.ScanCode = 0;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       switch (Key.ScanCode) {
 | |
|       case SCAN_LEFT:
 | |
|       case SCAN_UP:
 | |
|         if (NewSelected) {
 | |
|           --NewSelected;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|       case SCAN_DOWN:
 | |
|       case SCAN_RIGHT:
 | |
|         if (++NewSelected == NumMenuItems) {
 | |
|           --NewSelected;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|       case SCAN_PAGE_UP:
 | |
|       case SCAN_HOME:
 | |
|         NewSelected = 0;
 | |
|         break;
 | |
| 
 | |
|       case SCAN_PAGE_DOWN:
 | |
|       case SCAN_END:
 | |
|         NewSelected = NumMenuItems - 1;
 | |
|         break;
 | |
| 
 | |
|       case SCAN_ESC:
 | |
|         return LOCAL_BOOT;
 | |
|       }
 | |
| 
 | |
|       /* unhighlight last selected row */
 | |
|       gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected);
 | |
| 
 | |
|       AsciiPrint ("%a\r", BlankBuf);
 | |
| 
 | |
|       PrintMenuItem (MenuItemPtrs[Selected]);
 | |
| 
 | |
|       Selected = NewSelected;
 | |
|     } while (!Done);
 | |
|   }
 | |
| 
 | |
|   SaveNumRte  = Private->EfiBc.Mode->RouteTableEntries;
 | |
| 
 | |
|   Type        = NTOHS (MenuItemPtrs[Selected]->Type);
 | |
| 
 | |
|   if (Type == 0) {
 | |
|     DEBUG ((DEBUG_WARN, "\nDoMenu()  Local boot selected.  "));
 | |
|     return LOCAL_BOOT;
 | |
|   }
 | |
| 
 | |
|   AsciiPrint ("Discover");
 | |
| 
 | |
|   Status = Private->EfiBc.Discover (
 | |
|                             &Private->EfiBc,
 | |
|                             Type,
 | |
|                             &Layer,
 | |
|                             (BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected),
 | |
|                             0
 | |
|                             );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     AsciiPrint ("\r                    \r");
 | |
| 
 | |
|     DEBUG (
 | |
|       (DEBUG_WARN,
 | |
|       "\nDoMenu()  Return w/ %xh (%r).",
 | |
|       Status,
 | |
|       Status)
 | |
|       );
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   AsciiPrint ("\rBOOT_SERVER_IP: ");
 | |
|   PrintIpv4 ((UINT8 *) &Private->ServerIp);
 | |
| 
 | |
|   for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) {
 | |
|     if ((Index % 3) == 0) {
 | |
|       AsciiPrint ("\r\nGATEWAY IP:");
 | |
|     }
 | |
| 
 | |
|     AsciiPrint (" ");
 | |
|     PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr);
 | |
|     AsciiPrint (" ");
 | |
|   }
 | |
| 
 | |
|   AsciiPrint ("\n");
 | |
| 
 | |
|   DEBUG ((DEBUG_WARN, "\nDoMenu()  Return w/ EFI_SUCCESS.  "));
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Get value 8- or 16-bit value from DHCP option.
 | |
| 
 | |
|   @param  OpPtr                                       Pointer to DHCP option
 | |
| 
 | |
|   @return Value from DHCP option
 | |
| 
 | |
| **/
 | |
| UINT16
 | |
| GetValue (
 | |
|   DHCPV4_OP_STRUCT *OpPtr
 | |
|   )
 | |
| {
 | |
|   if (OpPtr->Header.Length == 1) {
 | |
|     return OpPtr->Data[0];
 | |
|   } else {
 | |
|     return NTOHS (OpPtr->Data);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Locate opcode in buffer.
 | |
| 
 | |
|   @param  BufferPtr                                   Pointer to buffer
 | |
|   @param  BufferLen                                   Length of buffer
 | |
|   @param  OpCode                                      Option number
 | |
| 
 | |
|   @return Pointer to opcode, may be NULL
 | |
| 
 | |
| **/
 | |
| UINT8 *
 | |
| _PxeBcFindOpt (
 | |
|   UINT8 *BufferPtr,
 | |
|   UINTN BufferLen,
 | |
|   UINT8 OpCode
 | |
|   )
 | |
| {
 | |
|   if (BufferPtr == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   while (BufferLen != 0) {
 | |
|     if (*BufferPtr == OpCode) {
 | |
|       return BufferPtr;
 | |
|     }
 | |
| 
 | |
|     switch (*BufferPtr) {
 | |
|     case OP_END:
 | |
|       return NULL;
 | |
| 
 | |
|     case OP_PAD:
 | |
|       ++BufferPtr;
 | |
|       --BufferLen;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) {
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|     BufferLen -= 2 + BufferPtr[1];
 | |
|     BufferPtr += 2 + BufferPtr[1];
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Find option in packet
 | |
| 
 | |
|   @param  PacketPtr                                   Pointer to packet
 | |
|   @param  OpCode                                      option number
 | |
| 
 | |
|   @return Pointer to option in packet
 | |
| 
 | |
| **/
 | |
| UINT8 *
 | |
| PxeBcFindDhcpOpt (
 | |
|   EFI_PXE_BASE_CODE_PACKET  *PacketPtr,
 | |
|   UINT8                     OpCode
 | |
|   )
 | |
| {
 | |
|   UINTN PacketLen;
 | |
|   UINT8 Overload;
 | |
|   UINT8 *OptionBufferPtr;
 | |
| 
 | |
|   //
 | |
|   //
 | |
|   //
 | |
|   PacketLen = 380;
 | |
|   Overload  = 0;
 | |
| 
 | |
|   //
 | |
|   // Figure size of DHCP option space.
 | |
|   //
 | |
|   OptionBufferPtr = _PxeBcFindOpt (
 | |
|                       PacketPtr->Dhcpv4.DhcpOptions,
 | |
|                       380,
 | |
|                       OP_DHCP_MAX_MESSAGE_SZ
 | |
|                       );
 | |
| 
 | |
|   if (OptionBufferPtr != NULL) {
 | |
|     if (OptionBufferPtr[1] == 2) {
 | |
|       UINT16  n;
 | |
| 
 | |
|       CopyMem (&n, &OptionBufferPtr[2], 2);
 | |
|       PacketLen = HTONS (n);
 | |
| 
 | |
|       if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) {
 | |
|         PacketLen = 380;
 | |
|       } else {
 | |
|         PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Look for option overloading.
 | |
|   //
 | |
|   OptionBufferPtr = _PxeBcFindOpt (
 | |
|                       PacketPtr->Dhcpv4.DhcpOptions,
 | |
|                       PacketLen,
 | |
|                       OP_DHCP_OPTION_OVERLOAD
 | |
|                       );
 | |
| 
 | |
|   if (OptionBufferPtr != NULL) {
 | |
|     if (OptionBufferPtr[1] == 1) {
 | |
|       Overload = OptionBufferPtr[2];
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Look for caller's option.
 | |
|   //
 | |
|   OptionBufferPtr = _PxeBcFindOpt (
 | |
|                       PacketPtr->Dhcpv4.DhcpOptions,
 | |
|                       PacketLen,
 | |
|                       OpCode
 | |
|                       );
 | |
| 
 | |
|   if (OptionBufferPtr != NULL) {
 | |
|     return OptionBufferPtr;
 | |
|   }
 | |
| 
 | |
|   if (Overload & OVLD_FILE) {
 | |
|     OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode);
 | |
| 
 | |
|     if (OptionBufferPtr != NULL) {
 | |
|       return OptionBufferPtr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Overload & OVLD_SRVR_NAME) {
 | |
|     OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode);
 | |
| 
 | |
|     if (OptionBufferPtr != NULL) {
 | |
|       return OptionBufferPtr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Download file into buffer
 | |
| 
 | |
|   @param  Private                                     Pointer to PxeBc interface
 | |
|   @param  BufferSize                                  pointer to size of download
 | |
|                                                       buffer
 | |
|   @param  Buffer                                      Pointer to buffer
 | |
| 
 | |
|   @return EFI_BUFFER_TOO_SMALL -
 | |
|   @return EFI_NOT_FOUND -
 | |
|   @return EFI_PROTOCOL_ERROR -
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DownloadFile (
 | |
|   IN PXE_BASECODE_DEVICE  *Private,
 | |
|   IN OUT UINT64           *BufferSize,
 | |
|   IN VOID                 *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_PXE_BASE_CODE_MTFTP_INFO  MtftpInfo;
 | |
|   EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode;
 | |
|   DHCP_RECEIVE_BUFFER           *RxBuf;
 | |
|   EFI_STATUS                    Status;
 | |
|   UINTN                         BlockSize;
 | |
| 
 | |
|   RxBuf     = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer;
 | |
|   BlockSize = 0x8000;
 | |
| 
 | |
|   DEBUG ((EFI_D_WARN, "\nDownloadFile()  Enter."));
 | |
| 
 | |
|   if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) {
 | |
|     if (Private->FileSize != 0) {
 | |
|       *BufferSize = Private->FileSize;
 | |
|       return EFI_BUFFER_TOO_SMALL;
 | |
|     }
 | |
| 
 | |
|     AsciiPrint ("\nTSize");
 | |
| 
 | |
|     OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE;
 | |
|   } else if (RxBuf->OpAdds.Status & WfM11a_TYPE) {
 | |
|     OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE;
 | |
| 
 | |
|     ZeroMem (&MtftpInfo, sizeof MtftpInfo);
 | |
| 
 | |
|     *(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data;
 | |
| 
 | |
|     CopyMem (
 | |
|       &MtftpInfo.CPort,
 | |
|       RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data,
 | |
|       sizeof MtftpInfo.CPort
 | |
|       );
 | |
| 
 | |
|     CopyMem (
 | |
|       &MtftpInfo.SPort,
 | |
|       RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data,
 | |
|       sizeof MtftpInfo.SPort
 | |
|       );
 | |
| 
 | |
|     MtftpInfo.ListenTimeout   = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]);
 | |
| 
 | |
|     MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]);
 | |
| 
 | |
|     AsciiPrint ("\nMTFTP");
 | |
|   } else {
 | |
|     AsciiPrint ("\nTFTP");
 | |
| 
 | |
|     OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE;
 | |
|   }
 | |
| 
 | |
|   Private->FileSize = 0;
 | |
| 
 | |
|   RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0;
 | |
| 
 | |
|   Status = Private->EfiBc.Mtftp (
 | |
|                             &Private->EfiBc,
 | |
|                             OpCode,
 | |
|                             Buffer,
 | |
|                             FALSE,
 | |
|                             BufferSize,
 | |
|                             &BlockSize,
 | |
|                             &Private->ServerIp,
 | |
|                             (UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data,
 | |
|                             &MtftpInfo,
 | |
|                             FALSE
 | |
|                             );
 | |
| 
 | |
|   if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
 | |
|     DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #1 %Xh", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) {
 | |
|     Private->FileSize = 0xFFFFFFFF;
 | |
|   } else {
 | |
|     Private->FileSize = (UINTN) *BufferSize;
 | |
|   }
 | |
| 
 | |
|   if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {
 | |
|     DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #2"));
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #3 %Xh", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) {
 | |
|     UINT64  CredentialLen;
 | |
|     UINT8   CredentialFilename[256];
 | |
|     UINT8   *op;
 | |
|     VOID    *CredentialBuffer;
 | |
| 
 | |
|     //
 | |
|     // Get name of credential file.  It may be in the BOOTP
 | |
|     // bootfile field or a DHCP option.
 | |
|     //
 | |
|     ZeroMem (CredentialFilename, sizeof CredentialFilename);
 | |
| 
 | |
|     op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE);
 | |
| 
 | |
|     if (op != NULL) {
 | |
|       if (op[1] == 0) {
 | |
|         /* No credential filename */
 | |
|         return EFI_NOT_FOUND;
 | |
|       }
 | |
| 
 | |
|       CopyMem (CredentialFilename, &op[2], op[1]);
 | |
|     } else {
 | |
|       if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) {
 | |
|         /* No credential filename */
 | |
|         return EFI_NOT_FOUND;
 | |
|       }
 | |
| 
 | |
|       CopyMem (CredentialFilename, &op[2], 128);
 | |
|     }
 | |
|     //
 | |
|     // Get size of credential file.  It may be available as a
 | |
|     // DHCP option.  If not, use the TFTP get file size.
 | |
|     //
 | |
|     CredentialLen = 0;
 | |
| 
 | |
|     op            = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ);
 | |
| 
 | |
|     if (op != NULL) {
 | |
|       /*
 | |
|        * This is actually the size of the credential file
 | |
|        * buffer.  The actual credential file size will be
 | |
|        * returned when we download the file.
 | |
|        */
 | |
|       if (op[1] == 2) {
 | |
|         UINT16  n;
 | |
| 
 | |
|         CopyMem (&n, &op[2], 2);
 | |
|         CredentialLen = HTONS (n) * 512;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (CredentialLen == 0) {
 | |
|       BlockSize = 8192;
 | |
| 
 | |
|       Status = Private->EfiBc.Mtftp (
 | |
|                                 &Private->EfiBc,
 | |
|                                 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
 | |
|                                 NULL,
 | |
|                                 FALSE,
 | |
|                                 &CredentialLen,
 | |
|                                 &BlockSize,
 | |
|                                 &Private->ServerIp,
 | |
|                                 CredentialFilename,
 | |
|                                 NULL,
 | |
|                                 FALSE
 | |
|                                 );
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         return Status;
 | |
|       }
 | |
| 
 | |
|       if (CredentialLen == 0) {
 | |
|         //
 | |
|         // %%TBD -- EFI error for invalid credential
 | |
|         // file.
 | |
|         //
 | |
|         return EFI_PROTOCOL_ERROR;
 | |
|       }
 | |
|     }
 | |
|     //
 | |
|     // Allocate credential file buffer.
 | |
|     //
 | |
|     Status = gBS->AllocatePool (
 | |
|                     EfiBootServicesData,
 | |
|                     (UINTN) CredentialLen,
 | |
|                     &CredentialBuffer
 | |
|                     );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|     //
 | |
|     // Download credential file.
 | |
|     //
 | |
|     BlockSize = 8192;
 | |
| 
 | |
|     Status = Private->EfiBc.Mtftp (
 | |
|                               &Private->EfiBc,
 | |
|                               EFI_PXE_BASE_CODE_TFTP_READ_FILE,
 | |
|                               CredentialBuffer,
 | |
|                               FALSE,
 | |
|                               &CredentialLen,
 | |
|                               &BlockSize,
 | |
|                               &Private->ServerIp,
 | |
|                               CredentialFilename,
 | |
|                               NULL,
 | |
|                               FALSE
 | |
|                               );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       gBS->FreePool (CredentialBuffer);
 | |
|       return Status;
 | |
|     }
 | |
|     //
 | |
|     // Verify credentials.
 | |
|     //
 | |
|     if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) {
 | |
|       Status = EFI_SUCCESS;
 | |
|     } else {
 | |
|       //
 | |
|       // %%TBD -- An EFI error code for failing credential verification.
 | |
|       //
 | |
|       Status = EFI_PROTOCOL_ERROR;
 | |
|     }
 | |
| 
 | |
|     gBS->FreePool (CredentialBuffer);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Start PXE DHCP.  Get DHCP and proxyDHCP information.
 | |
|   Display remote boot menu and prompt.  Select item from menu.
 | |
| 
 | |
|   @param  Private                                     Pointer to PxeBc interface
 | |
|   @param  BufferSize                                  Pointer to download buffer
 | |
|                                                       size
 | |
|   @param  Buffer                                      Pointer to download buffer
 | |
| 
 | |
|   @retval EFI_SUCCESS
 | |
|   @retval EFI_NOT_READY
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| LoadfileStart (
 | |
|   IN PXE_BASECODE_DEVICE  *Private,
 | |
|   IN OUT UINT64           *BufferSize,
 | |
|   IN VOID                 *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_PXE_BASE_CODE_MODE      *PxeBcMode;
 | |
|   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
 | |
|   EFI_SIMPLE_NETWORK_MODE     *SnpMode;
 | |
|   EFI_STATUS                  Status;
 | |
|   VOID                        *RxBuf;
 | |
| 
 | |
|   DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Enter."));
 | |
| 
 | |
|   //
 | |
|   // Try to start BaseCode, for now only IPv4 is supported
 | |
|   // so don't try to start using IPv6.
 | |
|   //
 | |
|   Status = Private->EfiBc.Start (&Private->EfiBc, FALSE);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     if (Status != EFI_ALREADY_STARTED) {
 | |
|       DEBUG ((DEBUG_NET, "\nLoadfileStart()  Exit  BC.Start() == %xh", Status));
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Get pointers to PXE mode structure, SNP protocol structure
 | |
|   // and SNP mode structure.
 | |
|   //
 | |
|   PxeBcMode = Private->EfiBc.Mode;
 | |
|   Snp       = Private->SimpleNetwork;
 | |
|   SnpMode   = Snp->Mode;
 | |
| 
 | |
|   //
 | |
|   // Display client MAC address, like 16-bit PXE ROMs
 | |
|   //
 | |
|   AsciiPrint ("\nCLIENT MAC ADDR: ");
 | |
| 
 | |
|   {
 | |
|     UINTN Index;
 | |
|     UINTN hlen;
 | |
| 
 | |
|     hlen = SnpMode->HwAddressSize;
 | |
| 
 | |
|     for (Index = 0; Index < hlen; ++Index) {
 | |
|       AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AsciiPrint ("\nDHCP");
 | |
| 
 | |
|   Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  BC.Dhcp() == %Xh", Status));
 | |
|     AsciiPrint ("\r               \r");
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ShowMyInfo (Private);
 | |
| 
 | |
|   RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;
 | |
| #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
 | |
| 
 | |
|   Status = DoMenu (Private, RxBufferPtr);
 | |
| 
 | |
|   if (Status == EFI_SUCCESS) {
 | |
|     //
 | |
|     // did a discovery - take info from discovery packet
 | |
|     //
 | |
|     RxBuf = &PXE_ACK_BUFFER;
 | |
|   } else if (Status == NO_MENU) {
 | |
|     //
 | |
|     // did not do a discovery - take info from rxbuf
 | |
|     //
 | |
|     Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr;
 | |
| 
 | |
|     if (!(Private->ServerIp.Addr[0])) {
 | |
|       *(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data;
 | |
|     }
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  DoMenu() == %Xh", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  Not ready?"));
 | |
|     return EFI_NOT_READY;
 | |
|   }
 | |
|   //
 | |
|   // check for file size option sent
 | |
|   //
 | |
|   if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) {
 | |
|     Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data);
 | |
|   }
 | |
| 
 | |
|   Private->BootServerReceiveBuffer  = RxBufferPtr;
 | |
| 
 | |
|   Status = DownloadFile (Private, BufferSize, Buffer);
 | |
| 
 | |
|   DEBUG (
 | |
|     (DEBUG_WARN,
 | |
|     "\nLoadfileStart()  Exit.  DownloadFile() = %Xh",
 | |
|     Status)
 | |
|     );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Loadfile interface for PxeBc interface
 | |
| 
 | |
|   @param  This                                        Pointer to Loadfile interface
 | |
|   @param  FilePath                                    Not used and not checked
 | |
|   @param  BootPolicy                                  Must be TRUE
 | |
|   @param  BufferSize                                  Pointer to buffer size
 | |
|   @param  Buffer                                      Pointer to download buffer or
 | |
|                                                       NULL
 | |
| 
 | |
|   @return EFI_INVALID_PARAMETER -
 | |
|   @return EFI_UNSUPPORTED -
 | |
|   @return EFI_SUCCESS -
 | |
|   @return EFI_BUFFER_TOO_SMALL -
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| LoadFile (
 | |
|   IN EFI_LOAD_FILE_PROTOCOL           *This,
 | |
|   IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
 | |
|   IN BOOLEAN                          BootPolicy,
 | |
|   IN OUT UINTN                        *BufferSize,
 | |
|   IN OUT VOID                         *Buffer
 | |
|   )
 | |
| {
 | |
|   LOADFILE_DEVICE *LoadfilePtr;
 | |
|   UINT64          TmpBufSz;
 | |
|   INT32           OrigMode;
 | |
|   INT32           OrigAttribute;
 | |
|   BOOLEAN         RemoveCallback;
 | |
|   BOOLEAN         NewMakeCallback;
 | |
|   EFI_STATUS      Status;
 | |
|   EFI_STATUS      TempStatus;
 | |
| 
 | |
|   //
 | |
|   // The following line is only used for passing ICC build.
 | |
|   //
 | |
|   DEBUG ((EFI_D_INFO, "FilePath = %p\n", FilePath));
 | |
| 
 | |
|   //
 | |
|   //
 | |
|   //
 | |
|   OrigMode        = gST->ConOut->Mode->Mode;
 | |
|   OrigAttribute   = gST->ConOut->Mode->Attribute;
 | |
|   RemoveCallback  = FALSE;
 | |
| 
 | |
|   AsciiPrint ("Running LoadFile()\n");
 | |
| 
 | |
|   //
 | |
|   // Resolve Warning 4 unreferenced parameter problem
 | |
|   //
 | |
|   FilePath = NULL;
 | |
| 
 | |
|   //
 | |
|   // If either if these parameters are NULL, we cannot continue.
 | |
|   //
 | |
|   if (This == NULL || BufferSize == NULL) {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadFile()  This or BufferSize == NULL"));
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   //
 | |
|   // We only support BootPolicy == TRUE
 | |
|   //
 | |
|   if (!BootPolicy) {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadFile()  BootPolicy == FALSE"));
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
|   //
 | |
|   // Get pointer to LoadFile protocol structure.
 | |
|   //
 | |
|   LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE);
 | |
| 
 | |
|   if (LoadfilePtr == NULL) {
 | |
|     DEBUG (
 | |
|       (DEBUG_NET,
 | |
|       "\nLoadFile()  Could not get pointer to LoadFile structure")
 | |
|       );
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   //
 | |
|   // Lock interface
 | |
|   //
 | |
|   EfiAcquireLock (&LoadfilePtr->Lock);
 | |
| 
 | |
|   //
 | |
|   // Set console output mode and display attribute
 | |
|   //
 | |
|   if (OrigMode != 0) {
 | |
|     gST->ConOut->SetMode (gST->ConOut, 0);
 | |
|   }
 | |
| 
 | |
|   gST->ConOut->SetAttribute (
 | |
|                 gST->ConOut,
 | |
|                 EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK)
 | |
|                 );
 | |
| 
 | |
|   //
 | |
|   // See if BaseCode already has a Callback protocol attached.
 | |
|   // If there is none, attach our own Callback protocol.
 | |
|   //
 | |
|   Status = gBS->HandleProtocol (
 | |
|                   LoadfilePtr->Private->Handle,
 | |
|                   &gEfiPxeBaseCodeCallbackProtocolGuid,
 | |
|                   (VOID *) &LoadfilePtr->Private->CallbackProtocolPtr
 | |
|                   );
 | |
| 
 | |
|   if (Status == EFI_SUCCESS) {
 | |
|     //
 | |
|     // There is already a callback routine.  Do nothing.
 | |
|     //
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadFile()  BC callback exists."));
 | |
| 
 | |
|   } else if (Status == EFI_UNSUPPORTED) {
 | |
|     //
 | |
|     // No BaseCode Callback protocol found.  Add our own.
 | |
|     //
 | |
|     Status = gBS->InstallProtocolInterface (
 | |
|                     &LoadfilePtr->Private->Handle,
 | |
|                     &gEfiPxeBaseCodeCallbackProtocolGuid,
 | |
|                     EFI_NATIVE_INTERFACE,
 | |
|                     &_bc_callback
 | |
|                     );
 | |
| 
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadFile()  Callback install status == %xh", Status));
 | |
| 
 | |
|     RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS);
 | |
| 
 | |
|     if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) {
 | |
|       NewMakeCallback = TRUE;
 | |
|       LoadfilePtr->Private->EfiBc.SetParameters (
 | |
|                                     &LoadfilePtr->Private->EfiBc,
 | |
|                                     NULL,
 | |
|                                     NULL,
 | |
|                                     NULL,
 | |
|                                     NULL,
 | |
|                                     &NewMakeCallback
 | |
|                                     );
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadFile()  Callback check status == %xh", Status));
 | |
|   }
 | |
|   //
 | |
|   // Check for starting or for continuing after already getting
 | |
|   // the file size.
 | |
|   //
 | |
|   if (LoadfilePtr->Private->FileSize == 0) {
 | |
|     TmpBufSz  = 0;
 | |
|     Status    = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer);
 | |
| 
 | |
|     if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) {
 | |
|       *BufferSize = 0xFFFFFFFF;
 | |
|     } else {
 | |
|       *BufferSize = (UINTN) TmpBufSz;
 | |
|     }
 | |
| 
 | |
|     if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|       //
 | |
|       // This is done so loadfile will work even if the boot manager
 | |
|       // did not make the first call with Buffer == NULL.
 | |
|       //
 | |
|       Buffer = NULL;
 | |
|     }
 | |
|   } else if (Buffer == NULL) {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadfile()  Get buffer size"));
 | |
| 
 | |
|     //
 | |
|     // Continuing from previous LoadFile request.  Make sure there
 | |
|     // is a buffer and that it is big enough.
 | |
|     //
 | |
|     *BufferSize = LoadfilePtr->Private->FileSize;
 | |
|     Status      = EFI_BUFFER_TOO_SMALL;
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_WARN, "\nLoadFile()  Download file"));
 | |
| 
 | |
|     //
 | |
|     // Everything looks good, try to download the file.
 | |
|     //
 | |
|     TmpBufSz  = *BufferSize;
 | |
|     Status    = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer);
 | |
| 
 | |
|     //
 | |
|     // Next call to loadfile will start DHCP process again.
 | |
|     //
 | |
|     LoadfilePtr->Private->FileSize = 0;
 | |
|   }
 | |
|   //
 | |
|   // If we added a callback protocol, now is the time to remove it.
 | |
|   //
 | |
|   if (RemoveCallback) {
 | |
|     NewMakeCallback = FALSE;
 | |
|     TempStatus = LoadfilePtr->Private->EfiBc.SetParameters (
 | |
|                                           &LoadfilePtr->Private->EfiBc,
 | |
|                                           NULL,
 | |
|                                           NULL,
 | |
|                                           NULL,
 | |
|                                           NULL,
 | |
|                                           &NewMakeCallback
 | |
|                                           );
 | |
| 
 | |
|     if (TempStatus == EFI_SUCCESS) {
 | |
|       gBS->UninstallProtocolInterface (
 | |
|             LoadfilePtr->Private->Handle,
 | |
|             &gEfiPxeBaseCodeCallbackProtocolGuid,
 | |
|             &_bc_callback
 | |
|             );
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Restore display mode and attribute
 | |
|   //
 | |
|   if (OrigMode != 0) {
 | |
|     gST->ConOut->SetMode (gST->ConOut, OrigMode);
 | |
|   }
 | |
| 
 | |
|   gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute);
 | |
| 
 | |
|   //
 | |
|   // Unlock interface
 | |
|   //
 | |
|   EfiReleaseLock (&LoadfilePtr->Lock);
 | |
| 
 | |
|   DEBUG ((DEBUG_WARN, "\nBC.Loadfile()  Status == %xh\n", Status));
 | |
| 
 | |
|   if (Status == EFI_SUCCESS) {
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   } else if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|     //
 | |
|     // Error is only displayed when we are actually trying to
 | |
|     // download the boot image.
 | |
|     //
 | |
|     if (Buffer == NULL) {
 | |
|       return EFI_BUFFER_TOO_SMALL;
 | |
|     }
 | |
| 
 | |
|     AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
 | |
| 
 | |
|   } else if (Status == EFI_DEVICE_ERROR) {
 | |
|     AsciiPrint ("\nPXE-E07: Network device error.  Check network connection.\n");
 | |
| 
 | |
|   } else if (Status == EFI_OUT_OF_RESOURCES) {
 | |
|     AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
 | |
| 
 | |
|   } else if (Status == EFI_NO_MEDIA) {
 | |
|     AsciiPrint ("\nPXE-E12: Could not detect network connection.  Check cable.\n");
 | |
| 
 | |
|   } else if (Status == EFI_NO_RESPONSE) {
 | |
|     AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
 | |
| 
 | |
|   } else if (Status == EFI_TIMEOUT) {
 | |
|     AsciiPrint ("\nPXE-E18: Timeout.  Server did not respond.\n");
 | |
| 
 | |
|   } else if (Status == EFI_ABORTED) {
 | |
|     AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
 | |
| 
 | |
|   } else if (Status == EFI_ICMP_ERROR) {
 | |
|     AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
 | |
| 
 | |
|     if (LoadfilePtr->Private->EfiBc.Mode != NULL) {
 | |
|       if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) {
 | |
| 
 | |
|       AsciiPrint (
 | |
|           "PXE-E98: Type: %xh  Code: %xh  ",
 | |
|           LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type,
 | |
|           LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code
 | |
|           );
 | |
| 
 | |
|         switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) {
 | |
|         case 0x03:
 | |
|           switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) {
 | |
|           case 0x00:              /* net unreachable */
 | |
|           AsciiPrint ("Net unreachable");
 | |
|             break;
 | |
| 
 | |
|           case 0x01:              /* host unreachable */
 | |
|           AsciiPrint ("Host unreachable");
 | |
|             break;
 | |
| 
 | |
|           case 0x02:              /* protocol unreachable */
 | |
|           AsciiPrint ("Protocol unreachable");
 | |
|             break;
 | |
| 
 | |
|           case 0x03:              /* port unreachable */
 | |
|           AsciiPrint ("Port unreachable");
 | |
|             break;
 | |
| 
 | |
|           case 0x04:              /* Fragmentation needed */
 | |
|           AsciiPrint ("Fragmentation needed");
 | |
|             break;
 | |
| 
 | |
|           case 0x05:              /* Source route failed */
 | |
|           AsciiPrint ("Source route failed");
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|       AsciiPrint ("\n");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   } else if (Status == EFI_TFTP_ERROR) {
 | |
|     AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
 | |
| 
 | |
|     if (LoadfilePtr->Private->EfiBc.Mode != NULL) {
 | |
|       if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) {
 | |
|       AsciiPrint (
 | |
|           "PXE-E98: Code: %xh  %a\n",
 | |
|           LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode,
 | |
|           LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString
 | |
|           );
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status);
 | |
|   }
 | |
| 
 | |
|   LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc);
 | |
| 
 | |
|   return Status;
 | |
| }
 |