3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 An implementation of the load file protocol for network devices.
24 #define DO_MENU (EFI_SUCCESS)
25 #define NO_MENU (DO_MENU + 1)
26 #define LOCAL_BOOT (EFI_ABORTED)
27 #define AUTO_SELECT (NO_MENU)
29 #define NUMBER_ROWS 25 // we set to mode 0
30 #define MAX_MENULIST 23
32 #define Ctl(x) (0x1F & (x))
35 DHCPV4_OP_STRUCT
*OpPtr
;
36 PXE_BOOT_MENU_ENTRY
*CurrentMenuItemPtr
;
37 PXE_OP_DISCOVERY_CONTROL
*DiscCtlOpStr
;
38 PXE_OP_BOOT_MENU
*MenuPtr
;
45 PxeBc callback routine for status updates and aborts.
47 @param This Pointer to PxeBcCallback
49 @param Function PxeBc function ID#
50 @param Received Receive/transmit flag
51 @param PacketLength Length of received packet (0
53 @param PacketPtr Pointer to received packet
54 (NULL == idle callback)
56 @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
57 EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
61 EFI_PXE_BASE_CODE_CALLBACK_STATUS
64 IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
* This
,
65 IN EFI_PXE_BASE_CODE_FUNCTION Function
,
67 IN UINT32 PacketLength
,
68 IN EFI_PXE_BASE_CODE_PACKET
* PacketPtr OPTIONAL
71 STATIC UINTN Propeller
;
79 // Resolve Warning 4 unreferenced parameter problem
84 // Check for user abort.
86 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_SUCCESS
) {
88 if (Key
.UnicodeChar
== Ctl ('c')) {
89 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
91 } else if (Key
.ScanCode
== SCAN_ESC
) {
92 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
96 // Do nothing if this is a receive.
99 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
102 // The display code is only for these functions.
105 case EFI_PXE_BASE_CODE_FUNCTION_MTFTP
:
107 // If this is a transmit and not a M/TFTP open request,
108 // return now. Do not print a dot for each M/TFTP packet
109 // that is sent, only for the open packets.
111 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
112 if (PacketPtr
->Raw
[0x1C] != 0x00 || PacketPtr
->Raw
[0x1D] != 0x01) {
113 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
119 case EFI_PXE_BASE_CODE_FUNCTION_DHCP
:
120 case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER
:
124 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
129 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
131 // Display a '.' when a packet is transmitted.
134 } else if (PacketLength
== 0 && PacketPtr
== NULL
) {
136 // Display a propeller when waiting for packets if at
137 // least 200 ms have passed.
139 Row
= gST
->ConOut
->Mode
->CursorRow
;
140 Col
= gST
->ConOut
->Mode
->CursorColumn
;
142 AsciiPrint ("%c", "/-\\|"[Propeller
]);
143 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Col
, Row
);
145 Propeller
= (Propeller
+ 1) & 3;
148 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
151 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback
= {
152 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION
,
158 Display an IPv4 address in dot notation.
160 @param Ptr Pointer to IPv4 address.
171 AsciiPrint ("%d.%d.%d.%d", Ptr
[0], Ptr
[1], Ptr
[2], Ptr
[3]);
177 Display client and server IP information.
179 @param Private Pointer to PxeBc interface
186 IN PXE_BASECODE_DEVICE
*Private
189 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
193 // Do nothing if a NULL pointer is passed in.
195 if (Private
== NULL
) {
199 // Get pointer to PXE BaseCode mode structure
201 PxeBcMode
= Private
->EfiBc
.Mode
;
204 // Display client IP address
206 AsciiPrint ("\rCLIENT IP: ");
207 PrintIpv4 (PxeBcMode
->StationIp
.v4
.Addr
);
210 // Display subnet mask
212 AsciiPrint (" MASK: ");
213 PrintIpv4 (PxeBcMode
->SubnetMask
.v4
.Addr
);
216 // Display DHCP and proxyDHCP IP addresses
218 if (PxeBcMode
->ProxyOfferReceived
) {
219 AsciiPrint ("\nDHCP IP: ");
220 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
222 AsciiPrint (" PROXY IP: ");
223 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) PXE_OFFER_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
225 AsciiPrint (" DHCP IP: ");
226 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
229 // Display gateway IP addresses
231 for (Index
= 0; Index
< PxeBcMode
->RouteTableEntries
; ++Index
) {
232 if ((Index
% 3) == 0) {
233 AsciiPrint ("\r\nGATEWAY IP:");
237 PrintIpv4 (PxeBcMode
->RouteTable
[Index
].GwAddr
.v4
.Addr
);
246 Display prompt and wait for input.
248 @param Private Pointer to PxeBc interface
249 @param BootPromptPtr Pointer to PXE boot prompt
252 @retval AUTO_SELECT DO_MENU -
259 PXE_BASECODE_DEVICE
*Private
,
260 PXE_OP_BOOT_PROMPT
*BootPromptPtr
264 EFI_EVENT TimeoutEvent
;
265 EFI_EVENT SecondsEvent
;
272 // if auto select, just get right to it
274 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_AUTO_SELECT
) {
278 // if no timeout, go directly to display of menu
280 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_NO_TIMEOUT
) {
286 Status
= gBS
->CreateEvent (
294 if (EFI_ERROR (Status
)) {
298 Status
= gBS
->SetTimer (
301 BootPromptPtr
->Timeout
* 10000000 + 100000
304 if (EFI_ERROR (Status
)) {
305 gBS
->CloseEvent (TimeoutEvent
);
311 Status
= gBS
->CreateEvent (
319 if (EFI_ERROR (Status
)) {
320 gBS
->CloseEvent (TimeoutEvent
);
324 Status
= gBS
->SetTimer (
330 if (EFI_ERROR (Status
)) {
331 gBS
->CloseEvent (SecondsEvent
);
332 gBS
->CloseEvent (TimeoutEvent
);
336 // display the prompt
337 // IMPORTANT! This prompt is an ASCII character string that may
338 // not be terminated with a NULL byte.
340 SaveChar
= BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1];
341 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = 0;
343 AsciiPrint ("%a ", BootPromptPtr
->Prompt
);
344 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = SaveChar
;
347 // wait until time expires or selection made - menu or local
349 SecColumn
= gST
->ConOut
->Mode
->CursorColumn
;
350 SecRow
= gST
->ConOut
->Mode
->CursorRow
;
351 SecsLeft
= BootPromptPtr
->Timeout
;
353 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
354 AsciiPrint ("(%d) ", SecsLeft
);
357 // set the default action to be AUTO_SELECT
359 Status
= AUTO_SELECT
;
361 while (EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
))) {
364 if (!EFI_ERROR (gBS
->CheckEvent (SecondsEvent
))) {
366 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
367 AsciiPrint ("(%d) ", SecsLeft
);
370 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
374 BufferSize
= sizeof Buffer
;
376 Status
= Private
->EfiBc
.UdpRead (
378 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
379 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
380 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
382 NULL
, /* dest port */
394 if (Key
.ScanCode
== 0) {
395 switch (Key
.UnicodeChar
) {
410 switch (Key
.ScanCode
) {
427 gBS
->CloseEvent (SecondsEvent
);
428 gBS
->CloseEvent (TimeoutEvent
);
430 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
438 Display one menu item.
440 @param MenuItemPtr Pointer to PXE menu item
448 PXE_BOOT_MENU_ENTRY
*MenuItemPtr
454 Length
= (UINT8
) MIN (70, MenuItemPtr
->DataLen
);
455 SaveChar
= MenuItemPtr
->Data
[Length
];
457 MenuItemPtr
->Data
[Length
] = 0;
458 AsciiPrint (" %a\n", MenuItemPtr
->Data
);
459 MenuItemPtr
->Data
[Length
] = SaveChar
;
464 Display and process menu.
466 @param Private Pointer to PxeBc interface
467 @param RxBufferPtr Pointer to receive buffer
475 PXE_BASECODE_DEVICE
*Private
,
476 DHCP_RECEIVE_BUFFER
*RxBufferPtr
479 PXE_OP_DISCOVERY_CONTROL
*DiscoveryControlPtr
;
480 PXE_BOOT_MENU_ENTRY
*MenuItemPtrs
[MAX_MENULIST
];
497 DEBUG ((DEBUG_WARN
, "\nDoMenu() Enter."));
499 /* see if we have a menu/prompt */
500 if (!(RxBufferPtr
->OpAdds
.Status
& DISCOVER_TYPE
)) {
503 "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
504 RxBufferPtr
->OpAdds
.Status
)
510 DiscoveryControlPtr
= (PXE_OP_DISCOVERY_CONTROL
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_DISCOVERY_CONTROL_IX
- 1];
513 // if not USE_BOOTFILE or no bootfile given, must have menu stuff
515 if ((DiscoveryControlPtr
->ControlBits
& USE_BOOTFILE
) && RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
516 DEBUG ((DEBUG_WARN
, "\nDoMenu() DHCP w/ bootfile. "));
520 // do prompt & menu if necessary
522 Status
= DoPrompt (Private
, (PXE_OP_BOOT_PROMPT
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_PROMPT_IX
- 1]);
524 if (Status
== LOCAL_BOOT
) {
525 DEBUG ((DEBUG_WARN
, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
530 Ptr
.BytePtr
= (UINT8
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_MENU_IX
- 1];
532 MenuLth
= Ptr
.MenuPtr
->Header
.Length
;
533 Ptr
.CurrentMenuItemPtr
= Ptr
.MenuPtr
->MenuItem
;
536 // build menu items array
538 for (Longest
= NumMenuItems
= Index
= 0; Index
< MenuLth
&& NumMenuItems
< MAX_MENULIST
;) {
541 lth
= Ptr
.CurrentMenuItemPtr
->DataLen
+ sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
);
543 MenuItemPtrs
[NumMenuItems
++] = Ptr
.CurrentMenuItemPtr
;
549 if ((Longest
= lth
) > 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))) {
550 Longest
= 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
));
558 if (Status
!= AUTO_SELECT
) {
561 SetMem (BlankBuf
, sizeof BlankBuf
, ' ');
562 BlankBuf
[Longest
+ 5 - (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))] = 0;
568 for (Index
= 0; Index
< NumMenuItems
; ++Index
) {
569 PrintMenuItem (MenuItemPtrs
[Index
]);
572 TopRow
= gST
->ConOut
->Mode
->CursorRow
- NumMenuItems
;
575 // now wait for a selection
580 // highlight selection
585 NewSelected
= Selected
;
588 // highlight selected row
590 gST
->ConOut
->SetAttribute (
592 EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
)
594 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ Selected
);
596 AsciiPrint (" --->%a\r", BlankBuf
);
598 PrintMenuItem (MenuItemPtrs
[Selected
]);
599 gST
->ConOut
->SetAttribute (
601 EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
)
603 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ NumMenuItems
);
606 // wait for a keystroke
608 while (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
612 TmpBufLen
= sizeof TmpBuf
;
614 Private
->EfiBc
.UdpRead (
616 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
617 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
618 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
620 NULL
, /* dest port */
631 switch (Key
.UnicodeChar
) {
633 Key
.ScanCode
= SCAN_ESC
;
636 case Ctl ('j'): /* linefeed */
637 case Ctl ('m'): /* return */
641 case Ctl ('i'): /* tab */
645 Key
.ScanCode
= SCAN_DOWN
;
648 case Ctl ('h'): /* backspace */
651 Key
.ScanCode
= SCAN_UP
;
659 switch (Key
.ScanCode
) {
670 if (++NewSelected
== NumMenuItems
) {
683 NewSelected
= NumMenuItems
- 1;
690 /* unhighlight last selected row */
691 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 5, TopRow
+ Selected
);
693 AsciiPrint ("%a\r", BlankBuf
);
695 PrintMenuItem (MenuItemPtrs
[Selected
]);
697 Selected
= NewSelected
;
701 SaveNumRte
= Private
->EfiBc
.Mode
->RouteTableEntries
;
703 Type
= NTOHS (MenuItemPtrs
[Selected
]->Type
);
706 DEBUG ((DEBUG_WARN
, "\nDoMenu() Local boot selected. "));
710 AsciiPrint ("Discover");
712 Status
= Private
->EfiBc
.Discover (
716 (BOOLEAN
) (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
),
720 if (EFI_ERROR (Status
)) {
721 AsciiPrint ("\r \r");
725 "\nDoMenu() Return w/ %xh (%r).",
733 AsciiPrint ("\rBOOT_SERVER_IP: ");
734 PrintIpv4 ((UINT8
*) &Private
->ServerIp
);
736 for (Index
= SaveNumRte
; Index
< Private
->EfiBc
.Mode
->RouteTableEntries
; ++Index
) {
737 if ((Index
% 3) == 0) {
738 AsciiPrint ("\r\nGATEWAY IP:");
742 PrintIpv4 ((UINT8
*) &Private
->EfiBc
.Mode
->RouteTable
[Index
].GwAddr
);
748 DEBUG ((DEBUG_WARN
, "\nDoMenu() Return w/ EFI_SUCCESS. "));
755 Get value 8- or 16-bit value from DHCP option.
757 @param OpPtr Pointer to DHCP option
759 @return Value from DHCP option
764 DHCPV4_OP_STRUCT
*OpPtr
767 if (OpPtr
->Header
.Length
== 1) {
768 return OpPtr
->Data
[0];
770 return NTOHS (OpPtr
->Data
);
776 Locate opcode in buffer.
778 @param BufferPtr Pointer to buffer
779 @param BufferLen Length of buffer
780 @param OpCode Option number
782 @return Pointer to opcode, may be NULL
792 if (BufferPtr
== NULL
) {
796 while (BufferLen
!= 0) {
797 if (*BufferPtr
== OpCode
) {
801 switch (*BufferPtr
) {
811 if ((UINTN
) BufferLen
<= (UINTN
) 2 + BufferPtr
[1]) {
815 BufferLen
-= 2 + BufferPtr
[1];
816 BufferPtr
+= 2 + BufferPtr
[1];
824 Find option in packet
826 @param PacketPtr Pointer to packet
827 @param OpCode option number
829 @return Pointer to option in packet
834 EFI_PXE_BASE_CODE_PACKET
*PacketPtr
,
840 UINT8
*OptionBufferPtr
;
849 // Figure size of DHCP option space.
851 OptionBufferPtr
= _PxeBcFindOpt (
852 PacketPtr
->Dhcpv4
.DhcpOptions
,
854 OP_DHCP_MAX_MESSAGE_SZ
857 if (OptionBufferPtr
!= NULL
) {
858 if (OptionBufferPtr
[1] == 2) {
861 CopyMem (&n
, &OptionBufferPtr
[2], 2);
862 PacketLen
= HTONS (n
);
864 if (PacketLen
< sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET
)) {
867 PacketLen
-= (PacketPtr
->Dhcpv4
.DhcpOptions
- &PacketPtr
->Dhcpv4
.BootpOpcode
) + 28;
872 // Look for option overloading.
874 OptionBufferPtr
= _PxeBcFindOpt (
875 PacketPtr
->Dhcpv4
.DhcpOptions
,
877 OP_DHCP_OPTION_OVERLOAD
880 if (OptionBufferPtr
!= NULL
) {
881 if (OptionBufferPtr
[1] == 1) {
882 Overload
= OptionBufferPtr
[2];
886 // Look for caller's option.
888 OptionBufferPtr
= _PxeBcFindOpt (
889 PacketPtr
->Dhcpv4
.DhcpOptions
,
894 if (OptionBufferPtr
!= NULL
) {
895 return OptionBufferPtr
;
898 if (Overload
& OVLD_FILE
) {
899 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpBootFile
, 128, OpCode
);
901 if (OptionBufferPtr
!= NULL
) {
902 return OptionBufferPtr
;
906 if (Overload
& OVLD_SRVR_NAME
) {
907 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpSrvName
, 64, OpCode
);
909 if (OptionBufferPtr
!= NULL
) {
910 return OptionBufferPtr
;
919 Download file into buffer
921 @param Private Pointer to PxeBc interface
922 @param BufferSize pointer to size of download
924 @param Buffer Pointer to buffer
926 @return EFI_BUFFER_TOO_SMALL -
927 @return EFI_NOT_FOUND -
928 @return EFI_PROTOCOL_ERROR -
933 IN PXE_BASECODE_DEVICE
*Private
,
934 IN OUT UINT64
*BufferSize
,
938 EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo
;
939 EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode
;
940 DHCP_RECEIVE_BUFFER
*RxBuf
;
944 RxBuf
= (DHCP_RECEIVE_BUFFER
*) Private
->BootServerReceiveBuffer
;
947 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Enter."));
949 if (Buffer
== NULL
|| *BufferSize
== 0 || *BufferSize
< Private
->FileSize
) {
950 if (Private
->FileSize
!= 0) {
951 *BufferSize
= Private
->FileSize
;
952 return EFI_BUFFER_TOO_SMALL
;
955 AsciiPrint ("\nTSize");
957 OpCode
= EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
;
958 } else if (RxBuf
->OpAdds
.Status
& WfM11a_TYPE
) {
959 OpCode
= EFI_PXE_BASE_CODE_MTFTP_READ_FILE
;
961 ZeroMem (&MtftpInfo
, sizeof MtftpInfo
);
963 *(IPV4_ADDR
*) &MtftpInfo
.MCastIp
= *(IPV4_ADDR
*) RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_IP
- 1]->Data
;
967 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_CPORT
- 1]->Data
,
968 sizeof MtftpInfo
.CPort
973 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_SPORT
- 1]->Data
,
974 sizeof MtftpInfo
.SPort
977 MtftpInfo
.ListenTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_TMOUT
- 1]);
979 MtftpInfo
.TransmitTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_DELAY
- 1]);
981 AsciiPrint ("\nMTFTP");
983 AsciiPrint ("\nTFTP");
985 OpCode
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
988 Private
->FileSize
= 0;
990 RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
[RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Header
.Length
] = 0;
992 Status
= Private
->EfiBc
.Mtftp (
1000 (UINT8
*) RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
,
1005 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_BUFFER_TOO_SMALL
) {
1006 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #1 %Xh", Status
));
1010 if (sizeof (UINTN
) < sizeof (UINT64
) && *BufferSize
> 0xFFFFFFFF) {
1011 Private
->FileSize
= 0xFFFFFFFF;
1013 Private
->FileSize
= (UINTN
) *BufferSize
;
1016 if (OpCode
== EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) {
1017 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #2"));
1018 return EFI_BUFFER_TOO_SMALL
;
1021 if (EFI_ERROR (Status
)) {
1022 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #3 %Xh", Status
));
1026 if (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
&& Private
->EfiBc
.Mode
->PxeBisReplyReceived
) {
1027 UINT64 CredentialLen
;
1028 UINT8 CredentialFilename
[256];
1030 VOID
*CredentialBuffer
;
1033 // Get name of credential file. It may be in the BOOTP
1034 // bootfile field or a DHCP option.
1036 ZeroMem (CredentialFilename
, sizeof CredentialFilename
);
1038 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_DHCP_BOOTFILE
);
1042 /* No credential filename */
1043 return EFI_NOT_FOUND
;
1046 CopyMem (CredentialFilename
, &op
[2], op
[1]);
1048 if (Private
->EfiBc
.Mode
->PxeBisReply
.Dhcpv4
.BootpBootFile
[0] == 0) {
1049 /* No credential filename */
1050 return EFI_NOT_FOUND
;
1053 CopyMem (CredentialFilename
, &op
[2], 128);
1056 // Get size of credential file. It may be available as a
1057 // DHCP option. If not, use the TFTP get file size.
1061 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_BOOT_FILE_SZ
);
1065 * This is actually the size of the credential file
1066 * buffer. The actual credential file size will be
1067 * returned when we download the file.
1072 CopyMem (&n
, &op
[2], 2);
1073 CredentialLen
= HTONS (n
) * 512;
1077 if (CredentialLen
== 0) {
1080 Status
= Private
->EfiBc
.Mtftp (
1082 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1093 if (EFI_ERROR (Status
)) {
1097 if (CredentialLen
== 0) {
1099 // %%TBD -- EFI error for invalid credential
1102 return EFI_PROTOCOL_ERROR
;
1106 // Allocate credential file buffer.
1108 Status
= gBS
->AllocatePool (
1109 EfiBootServicesData
,
1110 (UINTN
) CredentialLen
,
1114 if (EFI_ERROR (Status
)) {
1118 // Download credential file.
1122 Status
= Private
->EfiBc
.Mtftp (
1124 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1135 if (EFI_ERROR (Status
)) {
1136 gBS
->FreePool (CredentialBuffer
);
1140 // Verify credentials.
1142 if (PxebcBisVerify (Private
, Buffer
, Private
->FileSize
, CredentialBuffer
, (UINTN
) CredentialLen
)) {
1143 Status
= EFI_SUCCESS
;
1146 // %%TBD -- An EFI error code for failing credential verification.
1148 Status
= EFI_PROTOCOL_ERROR
;
1151 gBS
->FreePool (CredentialBuffer
);
1159 Start PXE DHCP. Get DHCP and proxyDHCP information.
1160 Display remote boot menu and prompt. Select item from menu.
1162 @param Private Pointer to PxeBc interface
1163 @param BufferSize Pointer to download buffer
1165 @param Buffer Pointer to download buffer
1168 @retval EFI_NOT_READY
1173 IN PXE_BASECODE_DEVICE
*Private
,
1174 IN OUT UINT64
*BufferSize
,
1178 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1179 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
1180 EFI_SIMPLE_NETWORK_MODE
*SnpMode
;
1184 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Enter."));
1187 // Try to start BaseCode, for now only IPv4 is supported
1188 // so don't try to start using IPv6.
1190 Status
= Private
->EfiBc
.Start (&Private
->EfiBc
, FALSE
);
1192 if (EFI_ERROR (Status
)) {
1193 if (Status
!= EFI_ALREADY_STARTED
) {
1194 DEBUG ((DEBUG_NET
, "\nLoadfileStart() Exit BC.Start() == %xh", Status
));
1199 // Get pointers to PXE mode structure, SNP protocol structure
1200 // and SNP mode structure.
1202 PxeBcMode
= Private
->EfiBc
.Mode
;
1203 Snp
= Private
->SimpleNetwork
;
1204 SnpMode
= Snp
->Mode
;
1207 // Display client MAC address, like 16-bit PXE ROMs
1209 AsciiPrint ("\nCLIENT MAC ADDR: ");
1215 hlen
= SnpMode
->HwAddressSize
;
1217 for (Index
= 0; Index
< hlen
; ++Index
) {
1218 AsciiPrint ("%02x ", SnpMode
->CurrentAddress
.Addr
[Index
]);
1222 AsciiPrint ("\nDHCP");
1224 Status
= Private
->EfiBc
.Dhcp (&Private
->EfiBc
, TRUE
);
1226 if (EFI_ERROR (Status
)) {
1227 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status
));
1228 AsciiPrint ("\r \r");
1232 ShowMyInfo (Private
);
1234 RxBuf
= PxeBcMode
->ProxyOfferReceived
? &PXE_OFFER_BUFFER
: &DHCPV4_ACK_BUFFER
;
1235 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
1237 Status
= DoMenu (Private
, RxBufferPtr
);
1239 if (Status
== EFI_SUCCESS
) {
1241 // did a discovery - take info from discovery packet
1243 RxBuf
= &PXE_ACK_BUFFER
;
1244 } else if (Status
== NO_MENU
) {
1246 // did not do a discovery - take info from rxbuf
1248 Private
->ServerIp
.Addr
[0] = RxBufferPtr
->u
.Dhcpv4
.siaddr
;
1250 if (!(Private
->ServerIp
.Addr
[0])) {
1251 *(IPV4_ADDR
*) &Private
->ServerIp
= *(IPV4_ADDR
*) RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1]->Data
;
1254 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit DoMenu() == %Xh", Status
));
1258 if (!RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
1259 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit Not ready?"));
1260 return EFI_NOT_READY
;
1263 // check for file size option sent
1265 if (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]) {
1266 Private
->FileSize
= 512 * NTOHS (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]->Data
);
1269 Private
->BootServerReceiveBuffer
= RxBufferPtr
;
1271 Status
= DownloadFile (Private
, BufferSize
, Buffer
);
1275 "\nLoadfileStart() Exit. DownloadFile() = %Xh",
1284 Loadfile interface for PxeBc interface
1286 @param This Pointer to Loadfile interface
1287 @param FilePath Not used and not checked
1288 @param BootPolicy Must be TRUE
1289 @param BufferSize Pointer to buffer size
1290 @param Buffer Pointer to download buffer or
1293 @return EFI_INVALID_PARAMETER -
1294 @return EFI_UNSUPPORTED -
1295 @return EFI_SUCCESS -
1296 @return EFI_BUFFER_TOO_SMALL -
1302 IN EFI_LOAD_FILE_PROTOCOL
*This
,
1303 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
1304 IN BOOLEAN BootPolicy
,
1305 IN OUT UINTN
*BufferSize
,
1309 LOADFILE_DEVICE
*LoadfilePtr
;
1312 INT32 OrigAttribute
;
1313 BOOLEAN RemoveCallback
;
1314 BOOLEAN NewMakeCallback
;
1316 EFI_STATUS TempStatus
;
1319 // The following line is only used for passing ICC build.
1321 DEBUG ((EFI_D_INFO
, "FilePath = %p\n", FilePath
));
1326 OrigMode
= gST
->ConOut
->Mode
->Mode
;
1327 OrigAttribute
= gST
->ConOut
->Mode
->Attribute
;
1328 RemoveCallback
= FALSE
;
1330 AsciiPrint ("Running LoadFile()\n");
1333 // Resolve Warning 4 unreferenced parameter problem
1338 // If either if these parameters are NULL, we cannot continue.
1340 if (This
== NULL
|| BufferSize
== NULL
) {
1341 DEBUG ((DEBUG_WARN
, "\nLoadFile() This or BufferSize == NULL"));
1342 return EFI_INVALID_PARAMETER
;
1345 // We only support BootPolicy == TRUE
1348 DEBUG ((DEBUG_WARN
, "\nLoadFile() BootPolicy == FALSE"));
1349 return EFI_UNSUPPORTED
;
1352 // Get pointer to LoadFile protocol structure.
1354 LoadfilePtr
= CR (This
, LOADFILE_DEVICE
, LoadFile
, LOADFILE_DEVICE_SIGNATURE
);
1356 if (LoadfilePtr
== NULL
) {
1359 "\nLoadFile() Could not get pointer to LoadFile structure")
1361 return EFI_INVALID_PARAMETER
;
1366 EfiAcquireLock (&LoadfilePtr
->Lock
);
1369 // Set console output mode and display attribute
1371 if (OrigMode
!= 0) {
1372 gST
->ConOut
->SetMode (gST
->ConOut
, 0);
1375 gST
->ConOut
->SetAttribute (
1377 EFI_TEXT_ATTR (EFI_LIGHTGRAY
,EFI_BLACK
)
1381 // See if BaseCode already has a Callback protocol attached.
1382 // If there is none, attach our own Callback protocol.
1384 Status
= gBS
->HandleProtocol (
1385 LoadfilePtr
->Private
->Handle
,
1386 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1387 (VOID
*) &LoadfilePtr
->Private
->CallbackProtocolPtr
1390 if (Status
== EFI_SUCCESS
) {
1392 // There is already a callback routine. Do nothing.
1394 DEBUG ((DEBUG_WARN
, "\nLoadFile() BC callback exists."));
1396 } else if (Status
== EFI_UNSUPPORTED
) {
1398 // No BaseCode Callback protocol found. Add our own.
1400 Status
= gBS
->InstallProtocolInterface (
1401 &LoadfilePtr
->Private
->Handle
,
1402 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1403 EFI_NATIVE_INTERFACE
,
1407 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback install status == %xh", Status
));
1409 RemoveCallback
= (BOOLEAN
) (Status
== EFI_SUCCESS
);
1411 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
&& LoadfilePtr
->Private
->EfiBc
.Mode
->Started
) {
1412 NewMakeCallback
= TRUE
;
1413 LoadfilePtr
->Private
->EfiBc
.SetParameters (
1414 &LoadfilePtr
->Private
->EfiBc
,
1424 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback check status == %xh", Status
));
1427 // Check for starting or for continuing after already getting
1430 if (LoadfilePtr
->Private
->FileSize
== 0) {
1432 Status
= LoadfileStart (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1434 if (sizeof (UINTN
) < sizeof (UINT64
) && TmpBufSz
> 0xFFFFFFFF) {
1435 *BufferSize
= 0xFFFFFFFF;
1437 *BufferSize
= (UINTN
) TmpBufSz
;
1440 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1442 // This is done so loadfile will work even if the boot manager
1443 // did not make the first call with Buffer == NULL.
1447 } else if (Buffer
== NULL
) {
1448 DEBUG ((DEBUG_WARN
, "\nLoadfile() Get buffer size"));
1451 // Continuing from previous LoadFile request. Make sure there
1452 // is a buffer and that it is big enough.
1454 *BufferSize
= LoadfilePtr
->Private
->FileSize
;
1455 Status
= EFI_BUFFER_TOO_SMALL
;
1457 DEBUG ((DEBUG_WARN
, "\nLoadFile() Download file"));
1460 // Everything looks good, try to download the file.
1462 TmpBufSz
= *BufferSize
;
1463 Status
= DownloadFile (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1466 // Next call to loadfile will start DHCP process again.
1468 LoadfilePtr
->Private
->FileSize
= 0;
1471 // If we added a callback protocol, now is the time to remove it.
1473 if (RemoveCallback
) {
1474 NewMakeCallback
= FALSE
;
1475 TempStatus
= LoadfilePtr
->Private
->EfiBc
.SetParameters (
1476 &LoadfilePtr
->Private
->EfiBc
,
1484 if (TempStatus
== EFI_SUCCESS
) {
1485 gBS
->UninstallProtocolInterface (
1486 LoadfilePtr
->Private
->Handle
,
1487 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1493 // Restore display mode and attribute
1495 if (OrigMode
!= 0) {
1496 gST
->ConOut
->SetMode (gST
->ConOut
, OrigMode
);
1499 gST
->ConOut
->SetAttribute (gST
->ConOut
, OrigAttribute
);
1504 EfiReleaseLock (&LoadfilePtr
->Lock
);
1506 DEBUG ((DEBUG_WARN
, "\nBC.Loadfile() Status == %xh\n", Status
));
1508 if (Status
== EFI_SUCCESS
) {
1511 } else if (Status
== EFI_BUFFER_TOO_SMALL
) {
1513 // Error is only displayed when we are actually trying to
1514 // download the boot image.
1516 if (Buffer
== NULL
) {
1517 return EFI_BUFFER_TOO_SMALL
;
1520 AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
1522 } else if (Status
== EFI_DEVICE_ERROR
) {
1523 AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
1525 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
1526 AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
1528 } else if (Status
== EFI_NO_MEDIA
) {
1529 AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
1531 } else if (Status
== EFI_NO_RESPONSE
) {
1532 AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
1534 } else if (Status
== EFI_TIMEOUT
) {
1535 AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
1537 } else if (Status
== EFI_ABORTED
) {
1538 AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
1540 } else if (Status
== EFI_ICMP_ERROR
) {
1541 AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
1543 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1544 if (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpErrorReceived
) {
1547 "PXE-E98: Type: %xh Code: %xh ",
1548 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
,
1549 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
1552 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
) {
1554 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
) {
1555 case 0x00: /* net unreachable */
1556 AsciiPrint ("Net unreachable");
1559 case 0x01: /* host unreachable */
1560 AsciiPrint ("Host unreachable");
1563 case 0x02: /* protocol unreachable */
1564 AsciiPrint ("Protocol unreachable");
1567 case 0x03: /* port unreachable */
1568 AsciiPrint ("Port unreachable");
1571 case 0x04: /* Fragmentation needed */
1572 AsciiPrint ("Fragmentation needed");
1575 case 0x05: /* Source route failed */
1576 AsciiPrint ("Source route failed");
1587 } else if (Status
== EFI_TFTP_ERROR
) {
1588 AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
1590 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1591 if (LoadfilePtr
->Private
->EfiBc
.Mode
->TftpErrorReceived
) {
1593 "PXE-E98: Code: %xh %a\n",
1594 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorCode
,
1595 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorString
1601 AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status
);
1604 LoadfilePtr
->Private
->EfiBc
.Stop (&LoadfilePtr
->Private
->EfiBc
);