/*++ Copyright (c) 2006 - 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; 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. // 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_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) { AsciiPrint ("%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 // 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"); } 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 ( 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; 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; } 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; AsciiPrint (" %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]; 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 ((EFI_D_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 ( (EFI_D_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 ((EFI_D_WARN, "\nDoMenu() Return w/ EFI_SUCCESS. ")); return EFI_SUCCESS; } STATIC UINT16 GetValue ( DHCPV4_OP_STRUCT *OpPtr ) /*++ Routine Description: Get value 8- or 16-bit value from DHCP option. Arguments: OpPtr - Pointer to DHCP option Returns: Value from DHCP option --*/ { if (OpPtr->Header.Length == 1) { return OpPtr->Data[0]; } else { return NTOHS (OpPtr->Data); } } STATIC UINT8 * _PxeBcFindOpt ( UINT8 *BufferPtr, UINTN BufferLen, UINT8 OpCode ) /*++ Routine Description: Locate opcode in buffer. Arguments: BufferPtr - Pointer to buffer BufferLen - Length of buffer OpCode - Option number Returns: Pointer to opcode, may be NULL --*/ { 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; } STATIC UINT8 * PxeBcFindDhcpOpt ( EFI_PXE_BASE_CODE_PACKET *PacketPtr, UINT8 OpCode ) /*++ Routine Description: Find option in packet Arguments: PacketPtr - Pointer to packet OpCode - option number Returns: Pointer to option in packet --*/ { 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; } STATIC EFI_STATUS DownloadFile ( IN PXE_BASECODE_DEVICE *Private, IN OUT UINT64 *BufferSize, IN VOID *Buffer ) /*++ Routine Description: Download file into buffer Arguments: Private - Pointer to PxeBc interface BufferSize - pointer to size of download buffer Buffer - Pointer to buffer Returns: EFI_BUFFER_TOO_SMALL - EFI_NOT_FOUND - EFI_PROTOCOL_ERROR - --*/ { 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 ((EFI_D_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 ((EFI_D_WARN, "\nDownloadFile() Exit #2")); return EFI_BUFFER_TOO_SMALL; } if (EFI_ERROR (Status)) { DEBUG ((EFI_D_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. // CredentialBuffer = AllocatePool ((UINTN) CredentialLen); if (CredentialBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } // // 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)) { 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; } FreePool (CredentialBuffer); } return Status; } STATIC EFI_STATUS LoadfileStart ( IN PXE_BASECODE_DEVICE *Private, IN OUT UINT64 *BufferSize, IN VOID *Buffer ) /*++ Routine Description: Start PXE DHCP. Get DHCP and proxyDHCP information. Display remote boot menu and prompt. Select item from menu. Arguments: Private - Pointer to PxeBc interface BufferSize - Pointer to download buffer size Buffer - Pointer to download buffer Returns: EFI_SUCCESS - EFI_NOT_READY - --*/ { EFI_PXE_BASE_CODE_MODE *PxeBcMode; EFI_SIMPLE_NETWORK_PROTOCOL *Snp; EFI_SIMPLE_NETWORK_MODE *SnpMode; EFI_STATUS Status; VOID *RxBuf; DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_WARN, "\nLoadfileStart() Exit DoMenu() == %Xh", Status)); return Status; } if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { DEBUG ((EFI_D_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 ( (EFI_D_WARN, "\nLoadfileStart() Exit. DownloadFile() = %Xh", Status) ); return Status; } 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 ) /*++ Routine Description: Loadfile interface for PxeBc interface Arguments: This - Pointer to Loadfile interface FilePath - Not used and not checked BootPolicy - Must be TRUE BufferSize - Pointer to buffer size Buffer - Pointer to download buffer or NULL Returns: EFI_INVALID_PARAMETER - EFI_UNSUPPORTED - EFI_SUCCESS - EFI_BUFFER_TOO_SMALL - --*/ { LOADFILE_DEVICE *LoadfilePtr; UINT64 TmpBufSz; INT32 OrigMode; INT32 OrigAttribute; BOOLEAN RemoveCallback; BOOLEAN NewMakeCallback; EFI_STATUS Status; EFI_STATUS TempStatus; // // // OrigMode = gST->ConOut->Mode->Mode; OrigAttribute = gST->ConOut->Mode->Attribute; RemoveCallback = FALSE; AsciiPrint ("Running LoadFile()\n"); // // If either if these parameters are NULL, we cannot continue. // if (This == NULL || BufferSize == NULL) { DEBUG ((EFI_D_WARN, "\nLoadFile() This or BufferSize == NULL")); return EFI_INVALID_PARAMETER; } // // We only support BootPolicy == TRUE // if (!BootPolicy) { DEBUG ((EFI_D_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 ( (EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_WARN, "\nBC.Loadfile() Status == %xh\n", Status)); if (Status == EFI_SUCCESS) { /* 0 */ return EFI_SUCCESS; } else if (Status == EFI_BUFFER_TOO_SMALL) { /* 5 */ // // 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) { /* 7 */ AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n"); } else if (Status == EFI_OUT_OF_RESOURCES) { /* 9 */ AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n"); } else if (Status == EFI_NO_MEDIA) { /* 12 */ AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n"); } else if (Status == EFI_NO_RESPONSE) { /* 16 */ AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n"); } else if (Status == EFI_TIMEOUT) { /* 18 */ AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n"); } else if (Status == EFI_ABORTED) { /* 21 */ AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n"); } else if (Status == EFI_ICMP_ERROR) { /* 22 */ AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n"); if ((LoadfilePtr->Private->EfiBc.Mode != NULL) && 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) { /* 23 */ AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n"); if ((LoadfilePtr->Private->EfiBc.Mode != NULL) && (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; }