| /** @file | |
| Copyright (c) 2004 - 2007, 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; | |
| /** | |
| 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 | |
| - | |
| **/ | |
| 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 | |
| ) | |
| { | |
| 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. | |
| // | |
| 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", "/-\\|"[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_PROTOCOL_REVISION, | |
| &bc_callback | |
| }; | |
| /** | |
| Display an IPv4 address in dot notation. | |
| @param Ptr Pointer to IPv4 address. | |
| @return None | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 - | |
| **/ | |
| STATIC | |
| 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 | |
| **/ | |
| STATIC | |
| 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 = %x\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; | |
| } |