pxe_loadfile.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 1,697 行 · 第 1/3 页

C
1,697
字号
/*++

Copyright (c) 2004 - 2005, 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;


STATIC
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
  )
/*++

Routine Description:

  PxeBc callback routine for status updates and aborts.

Arguments:

  This - Pointer to PxeBcCallback interface
  Function - PxeBc function ID#
  Received - Receive/transmit flag
  PacketLength - Length of received packet (0 == idle callback)
  PacketPtr - Pointer to received packet (NULL == idle callback)

Returns:

  EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE - 
  EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT -
  
--*/
{
  STATIC UINTN  Propeller;

  EFI_INPUT_KEY Key;
  UINTN         Row;
  UINTN         Col;

  Propeller = 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.
    //
    Aprint (".");
  } 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;

    Aprint ("%c", "/-\\|"[Propeller]);
    gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);

    Propeller = (Propeller + 1) & 3;
  }

  return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
}

STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  _bc_callback = {
  EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION,
  &bc_callback
};

STATIC
VOID
PrintIpv4 (
  UINT8 *Ptr
  )
/*++

Routine Description:

  Display an IPv4 address in dot notation.

Arguments:

  Ptr - Pointer to IPv4 address.

Returns:

  None
  
--*/
{
  if (Ptr != NULL) {
    Aprint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]);
  }
}

STATIC
VOID
ShowMyInfo (
  IN PXE_BASECODE_DEVICE *Private
  )
/*++

Routine Description:

  Display client and server IP information.

Arguments:

  Private - Pointer to PxeBc interface

Returns:

  None
  
--*/
{
  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
  //
  Aprint ("\rCLIENT IP: ");
  PrintIpv4 (PxeBcMode->StationIp.v4.Addr);

  //
  // Display subnet mask
  //
  Aprint ("  MASK: ");
  PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr);

  //
  // Display DHCP and proxyDHCP IP addresses
  //
  if (PxeBcMode->ProxyOfferReceived) {
    Aprint ("\nDHCP IP: ");
    PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);

    Aprint ("  PROXY IP: ");
    PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
  } else {
    Aprint ("  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) {
      Aprint ("\r\nGATEWAY IP:");
    }

    Aprint (" ");
    PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr);
    Aprint (" ");
  }

  Aprint ("\n");
}

STATIC
EFI_STATUS
DoPrompt (
  PXE_BASECODE_DEVICE *Private,
  PXE_OP_BOOT_PROMPT  *BootPromptPtr
  )
/*++

Routine Description:

  Display prompt and wait for input.

Arguments:

  Private - Pointer to PxeBc interface
  BootPromptPtr - Pointer to PXE boot prompt option

Returns:

  AUTO_SELECT - 
  DO_MENU -
  NO_MENU - 
  LOCAL_BOOT - 
  
--*/
{
  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 (
                  EFI_EVENT_TIMER,
                  EFI_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 (
                  EFI_EVENT_TIMER,
                  EFI_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;

  Aprint ("%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);
  Aprint ("(%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);
      Aprint ("(%d) ", SecsLeft);
    }

    if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
      UINT8       Buffer[512];
      UINTN       BufferSize;
      EFI_STATUS  Status;

      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);
  Aprint ("     ");

  return Status;
}

STATIC
VOID
PrintMenuItem (
  PXE_BOOT_MENU_ENTRY *MenuItemPtr
  )
/*++

Routine Description:

  Display one menu item.

Arguments:

  MenuItemPtr - Pointer to PXE menu item option.

Returns:

  None

--*/
{
  UINT8 Length;
  UINT8 SaveChar;

  Length                    = (UINT8) EFI_MIN (70, MenuItemPtr->DataLen);
  SaveChar                  = MenuItemPtr->Data[Length];

  MenuItemPtr->Data[Length] = 0;
  Aprint ("     %a\n", MenuItemPtr->Data);
  MenuItemPtr->Data[Length] = SaveChar;
}

STATIC
EFI_STATUS
DoMenu (
  PXE_BASECODE_DEVICE *Private,
  DHCP_RECEIVE_BUFFER *RxBufferPtr
  )
/*++

Routine Description:

  Display and process menu.

Arguments:

  Private - Pointer to PxeBc interface
  RxBufferPtr - Pointer to receive buffer

Returns:

  NO_MENU - 
  LOCAL_BOOT - 
  
--*/
{
  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 ((EFI_D_WARN, "\nDoMenu()  Enter."));

  /* see if we have a menu/prompt */
  if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) {
    DEBUG (
      (EFI_D_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 ((EFI_D_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 ((EFI_D_WARN, "\nDoMenu()  DoPrompt() returned LOCAL_BOOT.  "));

    return Status;
  }

  Ptr.BytePtr             = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?