3 Copyright (c) 2004 - 2008, 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
77 // Resolve Warning 4 unreferenced parameter problem
82 // Check for user abort.
84 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_SUCCESS
) {
86 if (Key
.UnicodeChar
== Ctl ('c')) {
87 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
89 } else if (Key
.ScanCode
== SCAN_ESC
) {
90 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
94 // Do nothing if this is a receive.
97 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
100 // The display code is only for these functions.
103 case EFI_PXE_BASE_CODE_FUNCTION_MTFTP
:
105 // If this is a transmit and not a M/TFTP open request,
106 // return now. Do not print a dot for each M/TFTP packet
107 // that is sent, only for the open packets.
109 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
110 if (PacketPtr
->Raw
[0x1C] != 0x00 || PacketPtr
->Raw
[0x1D] != 0x01) {
111 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
117 case EFI_PXE_BASE_CODE_FUNCTION_DHCP
:
118 case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER
:
122 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
127 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
129 // Display a '.' when a packet is transmitted.
132 } else if (PacketLength
== 0 && PacketPtr
== NULL
) {
134 // Display a propeller when waiting for packets if at
135 // least 200 ms have passed.
137 Row
= gST
->ConOut
->Mode
->CursorRow
;
138 Col
= gST
->ConOut
->Mode
->CursorColumn
;
140 AsciiPrint ("%c", "/-\\|"[mPropeller
]);
141 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Col
, Row
);
143 mPropeller
= (mPropeller
+ 1) & 3;
146 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
149 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback
= {
150 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION
,
156 Display an IPv4 address in dot notation.
158 @param Ptr Pointer to IPv4 address.
169 AsciiPrint ("%d.%d.%d.%d", Ptr
[0], Ptr
[1], Ptr
[2], Ptr
[3]);
175 Display client and server IP information.
177 @param Private Pointer to PxeBc interface
184 IN PXE_BASECODE_DEVICE
*Private
187 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
191 // Do nothing if a NULL pointer is passed in.
193 if (Private
== NULL
) {
197 // Get pointer to PXE BaseCode mode structure
199 PxeBcMode
= Private
->EfiBc
.Mode
;
202 // Display client IP address
204 AsciiPrint ("\rCLIENT IP: ");
205 PrintIpv4 (PxeBcMode
->StationIp
.v4
.Addr
);
208 // Display subnet mask
210 AsciiPrint (" MASK: ");
211 PrintIpv4 (PxeBcMode
->SubnetMask
.v4
.Addr
);
214 // Display DHCP and proxyDHCP IP addresses
216 if (PxeBcMode
->ProxyOfferReceived
) {
217 AsciiPrint ("\nDHCP IP: ");
218 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
220 AsciiPrint (" PROXY IP: ");
221 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) PXE_OFFER_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
223 AsciiPrint (" DHCP IP: ");
224 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
227 // Display gateway IP addresses
229 for (Index
= 0; Index
< PxeBcMode
->RouteTableEntries
; ++Index
) {
230 if ((Index
% 3) == 0) {
231 AsciiPrint ("\r\nGATEWAY IP:");
235 PrintIpv4 (PxeBcMode
->RouteTable
[Index
].GwAddr
.v4
.Addr
);
244 Display prompt and wait for input.
246 @param Private Pointer to PxeBc interface
247 @param BootPromptPtr Pointer to PXE boot prompt
250 @retval AUTO_SELECT DO_MENU -
257 PXE_BASECODE_DEVICE
*Private
,
258 PXE_OP_BOOT_PROMPT
*BootPromptPtr
262 EFI_EVENT TimeoutEvent
;
263 EFI_EVENT SecondsEvent
;
270 // if auto select, just get right to it
272 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_AUTO_SELECT
) {
276 // if no timeout, go directly to display of menu
278 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_NO_TIMEOUT
) {
284 Status
= gBS
->CreateEvent (
292 if (EFI_ERROR (Status
)) {
296 Status
= gBS
->SetTimer (
299 BootPromptPtr
->Timeout
* 10000000 + 100000
302 if (EFI_ERROR (Status
)) {
303 gBS
->CloseEvent (TimeoutEvent
);
309 Status
= gBS
->CreateEvent (
317 if (EFI_ERROR (Status
)) {
318 gBS
->CloseEvent (TimeoutEvent
);
322 Status
= gBS
->SetTimer (
328 if (EFI_ERROR (Status
)) {
329 gBS
->CloseEvent (SecondsEvent
);
330 gBS
->CloseEvent (TimeoutEvent
);
334 // display the prompt
335 // IMPORTANT! This prompt is an ASCII character string that may
336 // not be terminated with a NULL byte.
338 SaveChar
= BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1];
339 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = 0;
341 AsciiPrint ("%a ", BootPromptPtr
->Prompt
);
342 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = SaveChar
;
345 // wait until time expires or selection made - menu or local
347 SecColumn
= gST
->ConOut
->Mode
->CursorColumn
;
348 SecRow
= gST
->ConOut
->Mode
->CursorRow
;
349 SecsLeft
= BootPromptPtr
->Timeout
;
351 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
352 AsciiPrint ("(%d) ", SecsLeft
);
355 // set the default action to be AUTO_SELECT
357 Status
= AUTO_SELECT
;
359 while (EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
))) {
362 if (!EFI_ERROR (gBS
->CheckEvent (SecondsEvent
))) {
364 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
365 AsciiPrint ("(%d) ", SecsLeft
);
368 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
372 BufferSize
= sizeof Buffer
;
374 Status
= Private
->EfiBc
.UdpRead (
376 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
377 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
378 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
380 NULL
, /* dest port */
392 if (Key
.ScanCode
== 0) {
393 switch (Key
.UnicodeChar
) {
408 switch (Key
.ScanCode
) {
425 gBS
->CloseEvent (SecondsEvent
);
426 gBS
->CloseEvent (TimeoutEvent
);
428 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
436 Display one menu item.
438 @param MenuItemPtr Pointer to PXE menu item
446 PXE_BOOT_MENU_ENTRY
*MenuItemPtr
452 Length
= (UINT8
) MIN (70, MenuItemPtr
->DataLen
);
453 SaveChar
= MenuItemPtr
->Data
[Length
];
455 MenuItemPtr
->Data
[Length
] = 0;
456 AsciiPrint (" %a\n", MenuItemPtr
->Data
);
457 MenuItemPtr
->Data
[Length
] = SaveChar
;
462 Display and process menu.
464 @param Private Pointer to PxeBc interface
465 @param RxBufferPtr Pointer to receive buffer
473 PXE_BASECODE_DEVICE
*Private
,
474 DHCP_RECEIVE_BUFFER
*RxBufferPtr
477 PXE_OP_DISCOVERY_CONTROL
*DiscoveryControlPtr
;
478 PXE_BOOT_MENU_ENTRY
*MenuItemPtrs
[MAX_MENULIST
];
495 DEBUG ((DEBUG_WARN
, "\nDoMenu() Enter."));
497 /* see if we have a menu/prompt */
498 if (!(RxBufferPtr
->OpAdds
.Status
& DISCOVER_TYPE
)) {
501 "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
502 RxBufferPtr
->OpAdds
.Status
)
508 DiscoveryControlPtr
= (PXE_OP_DISCOVERY_CONTROL
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_DISCOVERY_CONTROL_IX
- 1];
511 // if not USE_BOOTFILE or no bootfile given, must have menu stuff
513 if ((DiscoveryControlPtr
->ControlBits
& USE_BOOTFILE
) && RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
514 DEBUG ((DEBUG_WARN
, "\nDoMenu() DHCP w/ bootfile. "));
518 // do prompt & menu if necessary
520 Status
= DoPrompt (Private
, (PXE_OP_BOOT_PROMPT
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_PROMPT_IX
- 1]);
522 if (Status
== LOCAL_BOOT
) {
523 DEBUG ((DEBUG_WARN
, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
528 Ptr
.BytePtr
= (UINT8
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_MENU_IX
- 1];
530 MenuLth
= Ptr
.MenuPtr
->Header
.Length
;
531 Ptr
.CurrentMenuItemPtr
= Ptr
.MenuPtr
->MenuItem
;
534 // build menu items array
536 for (Longest
= NumMenuItems
= Index
= 0; Index
< MenuLth
&& NumMenuItems
< MAX_MENULIST
;) {
539 lth
= Ptr
.CurrentMenuItemPtr
->DataLen
+ sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
);
541 MenuItemPtrs
[NumMenuItems
++] = Ptr
.CurrentMenuItemPtr
;
547 if ((Longest
= lth
) > 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))) {
548 Longest
= 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
));
556 if (Status
!= AUTO_SELECT
) {
559 SetMem (BlankBuf
, sizeof BlankBuf
, ' ');
560 BlankBuf
[Longest
+ 5 - (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))] = 0;
566 for (Index
= 0; Index
< NumMenuItems
; ++Index
) {
567 PrintMenuItem (MenuItemPtrs
[Index
]);
570 TopRow
= gST
->ConOut
->Mode
->CursorRow
- NumMenuItems
;
573 // now wait for a selection
578 // highlight selection
583 NewSelected
= Selected
;
586 // highlight selected row
588 gST
->ConOut
->SetAttribute (
590 EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
)
592 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ Selected
);
594 AsciiPrint (" --->%a\r", BlankBuf
);
596 PrintMenuItem (MenuItemPtrs
[Selected
]);
597 gST
->ConOut
->SetAttribute (
599 EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
)
601 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ NumMenuItems
);
604 // wait for a keystroke
606 while (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
610 TmpBufLen
= sizeof TmpBuf
;
612 Private
->EfiBc
.UdpRead (
614 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
615 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
616 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
618 NULL
, /* dest port */
629 switch (Key
.UnicodeChar
) {
631 Key
.ScanCode
= SCAN_ESC
;
634 case Ctl ('j'): /* linefeed */
635 case Ctl ('m'): /* return */
639 case Ctl ('i'): /* tab */
643 Key
.ScanCode
= SCAN_DOWN
;
646 case Ctl ('h'): /* backspace */
649 Key
.ScanCode
= SCAN_UP
;
657 switch (Key
.ScanCode
) {
668 if (++NewSelected
== NumMenuItems
) {
681 NewSelected
= NumMenuItems
- 1;
688 /* unhighlight last selected row */
689 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 5, TopRow
+ Selected
);
691 AsciiPrint ("%a\r", BlankBuf
);
693 PrintMenuItem (MenuItemPtrs
[Selected
]);
695 Selected
= NewSelected
;
699 SaveNumRte
= Private
->EfiBc
.Mode
->RouteTableEntries
;
701 Type
= NTOHS (MenuItemPtrs
[Selected
]->Type
);
704 DEBUG ((DEBUG_WARN
, "\nDoMenu() Local boot selected. "));
708 AsciiPrint ("Discover");
710 Status
= Private
->EfiBc
.Discover (
714 (BOOLEAN
) (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
),
718 if (EFI_ERROR (Status
)) {
719 AsciiPrint ("\r \r");
723 "\nDoMenu() Return w/ %xh (%r).",
731 AsciiPrint ("\rBOOT_SERVER_IP: ");
732 PrintIpv4 ((UINT8
*) &Private
->ServerIp
);
734 for (Index
= SaveNumRte
; Index
< Private
->EfiBc
.Mode
->RouteTableEntries
; ++Index
) {
735 if ((Index
% 3) == 0) {
736 AsciiPrint ("\r\nGATEWAY IP:");
740 PrintIpv4 ((UINT8
*) &Private
->EfiBc
.Mode
->RouteTable
[Index
].GwAddr
);
746 DEBUG ((DEBUG_WARN
, "\nDoMenu() Return w/ EFI_SUCCESS. "));
753 Get value 8- or 16-bit value from DHCP option.
755 @param OpPtr Pointer to DHCP option
757 @return Value from DHCP option
762 DHCPV4_OP_STRUCT
*OpPtr
765 if (OpPtr
->Header
.Length
== 1) {
766 return OpPtr
->Data
[0];
768 return NTOHS (OpPtr
->Data
);
774 Locate opcode in buffer.
776 @param BufferPtr Pointer to buffer
777 @param BufferLen Length of buffer
778 @param OpCode Option number
780 @return Pointer to opcode, may be NULL
790 if (BufferPtr
== NULL
) {
794 while (BufferLen
!= 0) {
795 if (*BufferPtr
== OpCode
) {
799 switch (*BufferPtr
) {
809 if ((UINTN
) BufferLen
<= (UINTN
) 2 + BufferPtr
[1]) {
813 BufferLen
-= 2 + BufferPtr
[1];
814 BufferPtr
+= 2 + BufferPtr
[1];
822 Find option in packet
824 @param PacketPtr Pointer to packet
825 @param OpCode option number
827 @return Pointer to option in packet
832 EFI_PXE_BASE_CODE_PACKET
*PacketPtr
,
838 UINT8
*OptionBufferPtr
;
847 // Figure size of DHCP option space.
849 OptionBufferPtr
= _PxeBcFindOpt (
850 PacketPtr
->Dhcpv4
.DhcpOptions
,
852 OP_DHCP_MAX_MESSAGE_SZ
855 if (OptionBufferPtr
!= NULL
) {
856 if (OptionBufferPtr
[1] == 2) {
859 CopyMem (&n
, &OptionBufferPtr
[2], 2);
860 PacketLen
= HTONS (n
);
862 if (PacketLen
< sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET
)) {
865 PacketLen
-= (PacketPtr
->Dhcpv4
.DhcpOptions
- &PacketPtr
->Dhcpv4
.BootpOpcode
) + 28;
870 // Look for option overloading.
872 OptionBufferPtr
= _PxeBcFindOpt (
873 PacketPtr
->Dhcpv4
.DhcpOptions
,
875 OP_DHCP_OPTION_OVERLOAD
878 if (OptionBufferPtr
!= NULL
) {
879 if (OptionBufferPtr
[1] == 1) {
880 Overload
= OptionBufferPtr
[2];
884 // Look for caller's option.
886 OptionBufferPtr
= _PxeBcFindOpt (
887 PacketPtr
->Dhcpv4
.DhcpOptions
,
892 if (OptionBufferPtr
!= NULL
) {
893 return OptionBufferPtr
;
896 if (Overload
& OVLD_FILE
) {
897 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpBootFile
, 128, OpCode
);
899 if (OptionBufferPtr
!= NULL
) {
900 return OptionBufferPtr
;
904 if (Overload
& OVLD_SRVR_NAME
) {
905 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpSrvName
, 64, OpCode
);
907 if (OptionBufferPtr
!= NULL
) {
908 return OptionBufferPtr
;
917 Download file into buffer
919 @param Private Pointer to PxeBc interface
920 @param BufferSize pointer to size of download
922 @param Buffer Pointer to buffer
924 @return EFI_BUFFER_TOO_SMALL -
925 @return EFI_NOT_FOUND -
926 @return EFI_PROTOCOL_ERROR -
931 IN PXE_BASECODE_DEVICE
*Private
,
932 IN OUT UINT64
*BufferSize
,
936 EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo
;
937 EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode
;
938 DHCP_RECEIVE_BUFFER
*RxBuf
;
942 RxBuf
= (DHCP_RECEIVE_BUFFER
*) Private
->BootServerReceiveBuffer
;
945 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Enter."));
947 if (Buffer
== NULL
|| *BufferSize
== 0 || *BufferSize
< Private
->FileSize
) {
948 if (Private
->FileSize
!= 0) {
949 *BufferSize
= Private
->FileSize
;
950 return EFI_BUFFER_TOO_SMALL
;
953 AsciiPrint ("\nTSize");
955 OpCode
= EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
;
956 } else if (RxBuf
->OpAdds
.Status
& WfM11a_TYPE
) {
957 OpCode
= EFI_PXE_BASE_CODE_MTFTP_READ_FILE
;
959 ZeroMem (&MtftpInfo
, sizeof MtftpInfo
);
961 *(IPV4_ADDR
*) &MtftpInfo
.MCastIp
= *(IPV4_ADDR
*) RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_IP
- 1]->Data
;
965 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_CPORT
- 1]->Data
,
966 sizeof MtftpInfo
.CPort
971 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_SPORT
- 1]->Data
,
972 sizeof MtftpInfo
.SPort
975 MtftpInfo
.ListenTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_TMOUT
- 1]);
977 MtftpInfo
.TransmitTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_DELAY
- 1]);
979 AsciiPrint ("\nMTFTP");
981 AsciiPrint ("\nTFTP");
983 OpCode
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
986 Private
->FileSize
= 0;
988 RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
[RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Header
.Length
] = 0;
990 Status
= Private
->EfiBc
.Mtftp (
998 (UINT8
*) RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
,
1003 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_BUFFER_TOO_SMALL
) {
1004 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #1 %Xh", Status
));
1008 if (sizeof (UINTN
) < sizeof (UINT64
) && *BufferSize
> 0xFFFFFFFF) {
1009 Private
->FileSize
= 0xFFFFFFFF;
1011 Private
->FileSize
= (UINTN
) *BufferSize
;
1014 if (OpCode
== EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) {
1015 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #2"));
1016 return EFI_BUFFER_TOO_SMALL
;
1019 if (EFI_ERROR (Status
)) {
1020 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #3 %Xh", Status
));
1024 if (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
&& Private
->EfiBc
.Mode
->PxeBisReplyReceived
) {
1025 UINT64 CredentialLen
;
1026 UINT8 CredentialFilename
[256];
1028 VOID
*CredentialBuffer
;
1031 // Get name of credential file. It may be in the BOOTP
1032 // bootfile field or a DHCP option.
1034 ZeroMem (CredentialFilename
, sizeof CredentialFilename
);
1036 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_DHCP_BOOTFILE
);
1040 /* No credential filename */
1041 return EFI_NOT_FOUND
;
1044 CopyMem (CredentialFilename
, &op
[2], op
[1]);
1046 if (Private
->EfiBc
.Mode
->PxeBisReply
.Dhcpv4
.BootpBootFile
[0] == 0) {
1047 /* No credential filename */
1048 return EFI_NOT_FOUND
;
1051 CopyMem (CredentialFilename
, &op
[2], 128);
1054 // Get size of credential file. It may be available as a
1055 // DHCP option. If not, use the TFTP get file size.
1059 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_BOOT_FILE_SZ
);
1063 * This is actually the size of the credential file
1064 * buffer. The actual credential file size will be
1065 * returned when we download the file.
1070 CopyMem (&n
, &op
[2], 2);
1071 CredentialLen
= HTONS (n
) * 512;
1075 if (CredentialLen
== 0) {
1078 Status
= Private
->EfiBc
.Mtftp (
1080 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1091 if (EFI_ERROR (Status
)) {
1095 if (CredentialLen
== 0) {
1097 // %%TBD -- EFI error for invalid credential
1100 return EFI_PROTOCOL_ERROR
;
1104 // Allocate credential file buffer.
1106 Status
= gBS
->AllocatePool (
1107 EfiBootServicesData
,
1108 (UINTN
) CredentialLen
,
1112 if (EFI_ERROR (Status
)) {
1116 // Download credential file.
1120 Status
= Private
->EfiBc
.Mtftp (
1122 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1133 if (EFI_ERROR (Status
)) {
1134 gBS
->FreePool (CredentialBuffer
);
1138 // Verify credentials.
1140 if (PxebcBisVerify (Private
, Buffer
, Private
->FileSize
, CredentialBuffer
, (UINTN
) CredentialLen
)) {
1141 Status
= EFI_SUCCESS
;
1144 // %%TBD -- An EFI error code for failing credential verification.
1146 Status
= EFI_PROTOCOL_ERROR
;
1149 gBS
->FreePool (CredentialBuffer
);
1157 Start PXE DHCP. Get DHCP and proxyDHCP information.
1158 Display remote boot menu and prompt. Select item from menu.
1160 @param Private Pointer to PxeBc interface
1161 @param BufferSize Pointer to download buffer
1163 @param Buffer Pointer to download buffer
1166 @retval EFI_NOT_READY
1171 IN PXE_BASECODE_DEVICE
*Private
,
1172 IN OUT UINT64
*BufferSize
,
1176 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1177 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
1178 EFI_SIMPLE_NETWORK_MODE
*SnpMode
;
1182 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Enter."));
1185 // Try to start BaseCode, for now only IPv4 is supported
1186 // so don't try to start using IPv6.
1188 Status
= Private
->EfiBc
.Start (&Private
->EfiBc
, FALSE
);
1190 if (EFI_ERROR (Status
)) {
1191 if (Status
!= EFI_ALREADY_STARTED
) {
1192 DEBUG ((DEBUG_NET
, "\nLoadfileStart() Exit BC.Start() == %xh", Status
));
1197 // Get pointers to PXE mode structure, SNP protocol structure
1198 // and SNP mode structure.
1200 PxeBcMode
= Private
->EfiBc
.Mode
;
1201 Snp
= Private
->SimpleNetwork
;
1202 SnpMode
= Snp
->Mode
;
1205 // Display client MAC address, like 16-bit PXE ROMs
1207 AsciiPrint ("\nCLIENT MAC ADDR: ");
1213 hlen
= SnpMode
->HwAddressSize
;
1215 for (Index
= 0; Index
< hlen
; ++Index
) {
1216 AsciiPrint ("%02x ", SnpMode
->CurrentAddress
.Addr
[Index
]);
1220 AsciiPrint ("\nDHCP");
1222 Status
= Private
->EfiBc
.Dhcp (&Private
->EfiBc
, TRUE
);
1224 if (EFI_ERROR (Status
)) {
1225 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status
));
1226 AsciiPrint ("\r \r");
1230 ShowMyInfo (Private
);
1232 RxBuf
= PxeBcMode
->ProxyOfferReceived
? &PXE_OFFER_BUFFER
: &DHCPV4_ACK_BUFFER
;
1233 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
1235 Status
= DoMenu (Private
, RxBufferPtr
);
1237 if (Status
== EFI_SUCCESS
) {
1239 // did a discovery - take info from discovery packet
1241 RxBuf
= &PXE_ACK_BUFFER
;
1242 } else if (Status
== NO_MENU
) {
1244 // did not do a discovery - take info from rxbuf
1246 Private
->ServerIp
.Addr
[0] = RxBufferPtr
->u
.Dhcpv4
.siaddr
;
1248 if (!(Private
->ServerIp
.Addr
[0])) {
1249 *(IPV4_ADDR
*) &Private
->ServerIp
= *(IPV4_ADDR
*) RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1]->Data
;
1252 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit DoMenu() == %Xh", Status
));
1256 if (!RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
1257 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit Not ready?"));
1258 return EFI_NOT_READY
;
1261 // check for file size option sent
1263 if (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]) {
1264 Private
->FileSize
= 512 * NTOHS (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]->Data
);
1267 Private
->BootServerReceiveBuffer
= RxBufferPtr
;
1269 Status
= DownloadFile (Private
, BufferSize
, Buffer
);
1273 "\nLoadfileStart() Exit. DownloadFile() = %Xh",
1282 Loadfile interface for PxeBc interface
1284 @param This Pointer to Loadfile interface
1285 @param FilePath Not used and not checked
1286 @param BootPolicy Must be TRUE
1287 @param BufferSize Pointer to buffer size
1288 @param Buffer Pointer to download buffer or
1291 @return EFI_INVALID_PARAMETER -
1292 @return EFI_UNSUPPORTED -
1293 @return EFI_SUCCESS -
1294 @return EFI_BUFFER_TOO_SMALL -
1300 IN EFI_LOAD_FILE_PROTOCOL
*This
,
1301 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
1302 IN BOOLEAN BootPolicy
,
1303 IN OUT UINTN
*BufferSize
,
1307 LOADFILE_DEVICE
*LoadfilePtr
;
1310 INT32 OrigAttribute
;
1311 BOOLEAN RemoveCallback
;
1312 BOOLEAN NewMakeCallback
;
1314 EFI_STATUS TempStatus
;
1317 // The following line is only used for passing ICC build.
1319 DEBUG ((EFI_D_INFO
, "FilePath = %p\n", FilePath
));
1324 OrigMode
= gST
->ConOut
->Mode
->Mode
;
1325 OrigAttribute
= gST
->ConOut
->Mode
->Attribute
;
1326 RemoveCallback
= FALSE
;
1328 AsciiPrint ("Running LoadFile()\n");
1331 // Resolve Warning 4 unreferenced parameter problem
1336 // If either if these parameters are NULL, we cannot continue.
1338 if (This
== NULL
|| BufferSize
== NULL
) {
1339 DEBUG ((DEBUG_WARN
, "\nLoadFile() This or BufferSize == NULL"));
1340 return EFI_INVALID_PARAMETER
;
1343 // We only support BootPolicy == TRUE
1346 DEBUG ((DEBUG_WARN
, "\nLoadFile() BootPolicy == FALSE"));
1347 return EFI_UNSUPPORTED
;
1350 // Get pointer to LoadFile protocol structure.
1352 LoadfilePtr
= CR (This
, LOADFILE_DEVICE
, LoadFile
, LOADFILE_DEVICE_SIGNATURE
);
1354 if (LoadfilePtr
== NULL
) {
1357 "\nLoadFile() Could not get pointer to LoadFile structure")
1359 return EFI_INVALID_PARAMETER
;
1364 EfiAcquireLock (&LoadfilePtr
->Lock
);
1367 // Set console output mode and display attribute
1369 if (OrigMode
!= 0) {
1370 gST
->ConOut
->SetMode (gST
->ConOut
, 0);
1373 gST
->ConOut
->SetAttribute (
1375 EFI_TEXT_ATTR (EFI_LIGHTGRAY
,EFI_BLACK
)
1379 // See if BaseCode already has a Callback protocol attached.
1380 // If there is none, attach our own Callback protocol.
1382 Status
= gBS
->HandleProtocol (
1383 LoadfilePtr
->Private
->Handle
,
1384 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1385 (VOID
*) &LoadfilePtr
->Private
->CallbackProtocolPtr
1388 if (Status
== EFI_SUCCESS
) {
1390 // There is already a callback routine. Do nothing.
1392 DEBUG ((DEBUG_WARN
, "\nLoadFile() BC callback exists."));
1394 } else if (Status
== EFI_UNSUPPORTED
) {
1396 // No BaseCode Callback protocol found. Add our own.
1398 Status
= gBS
->InstallProtocolInterface (
1399 &LoadfilePtr
->Private
->Handle
,
1400 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1401 EFI_NATIVE_INTERFACE
,
1405 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback install status == %xh", Status
));
1407 RemoveCallback
= (BOOLEAN
) (Status
== EFI_SUCCESS
);
1409 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
&& LoadfilePtr
->Private
->EfiBc
.Mode
->Started
) {
1410 NewMakeCallback
= TRUE
;
1411 LoadfilePtr
->Private
->EfiBc
.SetParameters (
1412 &LoadfilePtr
->Private
->EfiBc
,
1422 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback check status == %xh", Status
));
1425 // Check for starting or for continuing after already getting
1428 if (LoadfilePtr
->Private
->FileSize
== 0) {
1430 Status
= LoadfileStart (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1432 if (sizeof (UINTN
) < sizeof (UINT64
) && TmpBufSz
> 0xFFFFFFFF) {
1433 *BufferSize
= 0xFFFFFFFF;
1435 *BufferSize
= (UINTN
) TmpBufSz
;
1438 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1440 // This is done so loadfile will work even if the boot manager
1441 // did not make the first call with Buffer == NULL.
1445 } else if (Buffer
== NULL
) {
1446 DEBUG ((DEBUG_WARN
, "\nLoadfile() Get buffer size"));
1449 // Continuing from previous LoadFile request. Make sure there
1450 // is a buffer and that it is big enough.
1452 *BufferSize
= LoadfilePtr
->Private
->FileSize
;
1453 Status
= EFI_BUFFER_TOO_SMALL
;
1455 DEBUG ((DEBUG_WARN
, "\nLoadFile() Download file"));
1458 // Everything looks good, try to download the file.
1460 TmpBufSz
= *BufferSize
;
1461 Status
= DownloadFile (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1464 // Next call to loadfile will start DHCP process again.
1466 LoadfilePtr
->Private
->FileSize
= 0;
1469 // If we added a callback protocol, now is the time to remove it.
1471 if (RemoveCallback
) {
1472 NewMakeCallback
= FALSE
;
1473 TempStatus
= LoadfilePtr
->Private
->EfiBc
.SetParameters (
1474 &LoadfilePtr
->Private
->EfiBc
,
1482 if (TempStatus
== EFI_SUCCESS
) {
1483 gBS
->UninstallProtocolInterface (
1484 LoadfilePtr
->Private
->Handle
,
1485 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1491 // Restore display mode and attribute
1493 if (OrigMode
!= 0) {
1494 gST
->ConOut
->SetMode (gST
->ConOut
, OrigMode
);
1497 gST
->ConOut
->SetAttribute (gST
->ConOut
, OrigAttribute
);
1502 EfiReleaseLock (&LoadfilePtr
->Lock
);
1504 DEBUG ((DEBUG_WARN
, "\nBC.Loadfile() Status == %xh\n", Status
));
1506 if (Status
== EFI_SUCCESS
) {
1509 } else if (Status
== EFI_BUFFER_TOO_SMALL
) {
1511 // Error is only displayed when we are actually trying to
1512 // download the boot image.
1514 if (Buffer
== NULL
) {
1515 return EFI_BUFFER_TOO_SMALL
;
1518 AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
1520 } else if (Status
== EFI_DEVICE_ERROR
) {
1521 AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
1523 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
1524 AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
1526 } else if (Status
== EFI_NO_MEDIA
) {
1527 AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
1529 } else if (Status
== EFI_NO_RESPONSE
) {
1530 AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
1532 } else if (Status
== EFI_TIMEOUT
) {
1533 AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
1535 } else if (Status
== EFI_ABORTED
) {
1536 AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
1538 } else if (Status
== EFI_ICMP_ERROR
) {
1539 AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
1541 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1542 if (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpErrorReceived
) {
1545 "PXE-E98: Type: %xh Code: %xh ",
1546 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
,
1547 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
1550 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
) {
1552 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
) {
1553 case 0x00: /* net unreachable */
1554 AsciiPrint ("Net unreachable");
1557 case 0x01: /* host unreachable */
1558 AsciiPrint ("Host unreachable");
1561 case 0x02: /* protocol unreachable */
1562 AsciiPrint ("Protocol unreachable");
1565 case 0x03: /* port unreachable */
1566 AsciiPrint ("Port unreachable");
1569 case 0x04: /* Fragmentation needed */
1570 AsciiPrint ("Fragmentation needed");
1573 case 0x05: /* Source route failed */
1574 AsciiPrint ("Source route failed");
1585 } else if (Status
== EFI_TFTP_ERROR
) {
1586 AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
1588 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1589 if (LoadfilePtr
->Private
->EfiBc
.Mode
->TftpErrorReceived
) {
1591 "PXE-E98: Code: %xh %a\n",
1592 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorCode
,
1593 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorString
1599 AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status
);
1602 LoadfilePtr
->Private
->EfiBc
.Stop (&LoadfilePtr
->Private
->EfiBc
);