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
62 EFI_PXE_BASE_CODE_CALLBACK_STATUS
65 IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
* This
,
66 IN EFI_PXE_BASE_CODE_FUNCTION Function
,
68 IN UINT32 PacketLength
,
69 IN EFI_PXE_BASE_CODE_PACKET
* PacketPtr OPTIONAL
72 STATIC UINTN Propeller
;
80 // Resolve Warning 4 unreferenced parameter problem
85 // Check for user abort.
87 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_SUCCESS
) {
89 if (Key
.UnicodeChar
== Ctl ('c')) {
90 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
92 } else if (Key
.ScanCode
== SCAN_ESC
) {
93 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
97 // Do nothing if this is a receive.
100 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
103 // The display code is only for these functions.
106 case EFI_PXE_BASE_CODE_FUNCTION_MTFTP
:
108 // If this is a transmit and not a M/TFTP open request,
109 // return now. Do not print a dot for each M/TFTP packet
110 // that is sent, only for the open packets.
112 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
113 if (PacketPtr
->Raw
[0x1C] != 0x00 || PacketPtr
->Raw
[0x1D] != 0x01) {
114 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
120 case EFI_PXE_BASE_CODE_FUNCTION_DHCP
:
121 case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER
:
125 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
130 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
132 // Display a '.' when a packet is transmitted.
135 } else if (PacketLength
== 0 && PacketPtr
== NULL
) {
137 // Display a propeller when waiting for packets if at
138 // least 200 ms have passed.
140 Row
= gST
->ConOut
->Mode
->CursorRow
;
141 Col
= gST
->ConOut
->Mode
->CursorColumn
;
143 AsciiPrint ("%c", "/-\\|"[Propeller
]);
144 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Col
, Row
);
146 Propeller
= (Propeller
+ 1) & 3;
149 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
152 STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback
= {
153 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION
,
159 Display an IPv4 address in dot notation.
161 @param Ptr Pointer to IPv4 address.
173 AsciiPrint ("%d.%d.%d.%d", Ptr
[0], Ptr
[1], Ptr
[2], Ptr
[3]);
179 Display client and server IP information.
181 @param Private Pointer to PxeBc interface
189 IN PXE_BASECODE_DEVICE
*Private
192 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
196 // Do nothing if a NULL pointer is passed in.
198 if (Private
== NULL
) {
202 // Get pointer to PXE BaseCode mode structure
204 PxeBcMode
= Private
->EfiBc
.Mode
;
207 // Display client IP address
209 AsciiPrint ("\rCLIENT IP: ");
210 PrintIpv4 (PxeBcMode
->StationIp
.v4
.Addr
);
213 // Display subnet mask
215 AsciiPrint (" MASK: ");
216 PrintIpv4 (PxeBcMode
->SubnetMask
.v4
.Addr
);
219 // Display DHCP and proxyDHCP IP addresses
221 if (PxeBcMode
->ProxyOfferReceived
) {
222 AsciiPrint ("\nDHCP IP: ");
223 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
225 AsciiPrint (" PROXY IP: ");
226 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) PXE_OFFER_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
228 AsciiPrint (" DHCP IP: ");
229 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
232 // Display gateway IP addresses
234 for (Index
= 0; Index
< PxeBcMode
->RouteTableEntries
; ++Index
) {
235 if ((Index
% 3) == 0) {
236 AsciiPrint ("\r\nGATEWAY IP:");
240 PrintIpv4 (PxeBcMode
->RouteTable
[Index
].GwAddr
.v4
.Addr
);
249 Display prompt and wait for input.
251 @param Private Pointer to PxeBc interface
252 @param BootPromptPtr Pointer to PXE boot prompt
255 @retval AUTO_SELECT DO_MENU -
263 PXE_BASECODE_DEVICE
*Private
,
264 PXE_OP_BOOT_PROMPT
*BootPromptPtr
268 EFI_EVENT TimeoutEvent
;
269 EFI_EVENT SecondsEvent
;
276 // if auto select, just get right to it
278 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_AUTO_SELECT
) {
282 // if no timeout, go directly to display of menu
284 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_NO_TIMEOUT
) {
290 Status
= gBS
->CreateEvent (
298 if (EFI_ERROR (Status
)) {
302 Status
= gBS
->SetTimer (
305 BootPromptPtr
->Timeout
* 10000000 + 100000
308 if (EFI_ERROR (Status
)) {
309 gBS
->CloseEvent (TimeoutEvent
);
315 Status
= gBS
->CreateEvent (
323 if (EFI_ERROR (Status
)) {
324 gBS
->CloseEvent (TimeoutEvent
);
328 Status
= gBS
->SetTimer (
334 if (EFI_ERROR (Status
)) {
335 gBS
->CloseEvent (SecondsEvent
);
336 gBS
->CloseEvent (TimeoutEvent
);
340 // display the prompt
341 // IMPORTANT! This prompt is an ASCII character string that may
342 // not be terminated with a NULL byte.
344 SaveChar
= BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1];
345 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = 0;
347 AsciiPrint ("%a ", BootPromptPtr
->Prompt
);
348 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = SaveChar
;
351 // wait until time expires or selection made - menu or local
353 SecColumn
= gST
->ConOut
->Mode
->CursorColumn
;
354 SecRow
= gST
->ConOut
->Mode
->CursorRow
;
355 SecsLeft
= BootPromptPtr
->Timeout
;
357 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
358 AsciiPrint ("(%d) ", SecsLeft
);
361 // set the default action to be AUTO_SELECT
363 Status
= AUTO_SELECT
;
365 while (EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
))) {
368 if (!EFI_ERROR (gBS
->CheckEvent (SecondsEvent
))) {
370 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
371 AsciiPrint ("(%d) ", SecsLeft
);
374 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
379 BufferSize
= sizeof Buffer
;
381 Status
= Private
->EfiBc
.UdpRead (
383 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
384 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
385 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
387 NULL
, /* dest port */
399 if (Key
.ScanCode
== 0) {
400 switch (Key
.UnicodeChar
) {
415 switch (Key
.ScanCode
) {
432 gBS
->CloseEvent (SecondsEvent
);
433 gBS
->CloseEvent (TimeoutEvent
);
435 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
443 Display one menu item.
445 @param MenuItemPtr Pointer to PXE menu item
454 PXE_BOOT_MENU_ENTRY
*MenuItemPtr
460 Length
= (UINT8
) MIN (70, MenuItemPtr
->DataLen
);
461 SaveChar
= MenuItemPtr
->Data
[Length
];
463 MenuItemPtr
->Data
[Length
] = 0;
464 AsciiPrint (" %a\n", MenuItemPtr
->Data
);
465 MenuItemPtr
->Data
[Length
] = SaveChar
;
470 Display and process menu.
472 @param Private Pointer to PxeBc interface
473 @param RxBufferPtr Pointer to receive buffer
482 PXE_BASECODE_DEVICE
*Private
,
483 DHCP_RECEIVE_BUFFER
*RxBufferPtr
486 PXE_OP_DISCOVERY_CONTROL
*DiscoveryControlPtr
;
487 PXE_BOOT_MENU_ENTRY
*MenuItemPtrs
[MAX_MENULIST
];
504 DEBUG ((DEBUG_WARN
, "\nDoMenu() Enter."));
506 /* see if we have a menu/prompt */
507 if (!(RxBufferPtr
->OpAdds
.Status
& DISCOVER_TYPE
)) {
510 "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
511 RxBufferPtr
->OpAdds
.Status
)
517 DiscoveryControlPtr
= (PXE_OP_DISCOVERY_CONTROL
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_DISCOVERY_CONTROL_IX
- 1];
520 // if not USE_BOOTFILE or no bootfile given, must have menu stuff
522 if ((DiscoveryControlPtr
->ControlBits
& USE_BOOTFILE
) && RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
523 DEBUG ((DEBUG_WARN
, "\nDoMenu() DHCP w/ bootfile. "));
527 // do prompt & menu if necessary
529 Status
= DoPrompt (Private
, (PXE_OP_BOOT_PROMPT
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_PROMPT_IX
- 1]);
531 if (Status
== LOCAL_BOOT
) {
532 DEBUG ((DEBUG_WARN
, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
537 Ptr
.BytePtr
= (UINT8
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_MENU_IX
- 1];
539 MenuLth
= Ptr
.MenuPtr
->Header
.Length
;
540 Ptr
.CurrentMenuItemPtr
= Ptr
.MenuPtr
->MenuItem
;
543 // build menu items array
545 for (Longest
= NumMenuItems
= Index
= 0; Index
< MenuLth
&& NumMenuItems
< MAX_MENULIST
;) {
548 lth
= Ptr
.CurrentMenuItemPtr
->DataLen
+ sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
);
550 MenuItemPtrs
[NumMenuItems
++] = Ptr
.CurrentMenuItemPtr
;
556 if ((Longest
= lth
) > 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))) {
557 Longest
= 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
));
565 if (Status
!= AUTO_SELECT
) {
568 SetMem (BlankBuf
, sizeof BlankBuf
, ' ');
569 BlankBuf
[Longest
+ 5 - (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))] = 0;
575 for (Index
= 0; Index
< NumMenuItems
; ++Index
) {
576 PrintMenuItem (MenuItemPtrs
[Index
]);
579 TopRow
= gST
->ConOut
->Mode
->CursorRow
- NumMenuItems
;
582 // now wait for a selection
587 // highlight selection
592 NewSelected
= Selected
;
595 // highlight selected row
597 gST
->ConOut
->SetAttribute (
599 EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
)
601 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ Selected
);
603 AsciiPrint (" --->%a\r", BlankBuf
);
605 PrintMenuItem (MenuItemPtrs
[Selected
]);
606 gST
->ConOut
->SetAttribute (
608 EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
)
610 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ NumMenuItems
);
613 // wait for a keystroke
615 while (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
619 TmpBufLen
= sizeof TmpBuf
;
621 Private
->EfiBc
.UdpRead (
623 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
624 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
625 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
627 NULL
, /* dest port */
638 switch (Key
.UnicodeChar
) {
640 Key
.ScanCode
= SCAN_ESC
;
643 case Ctl ('j'): /* linefeed */
644 case Ctl ('m'): /* return */
648 case Ctl ('i'): /* tab */
652 Key
.ScanCode
= SCAN_DOWN
;
655 case Ctl ('h'): /* backspace */
658 Key
.ScanCode
= SCAN_UP
;
666 switch (Key
.ScanCode
) {
677 if (++NewSelected
== NumMenuItems
) {
690 NewSelected
= NumMenuItems
- 1;
697 /* unhighlight last selected row */
698 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 5, TopRow
+ Selected
);
700 AsciiPrint ("%a\r", BlankBuf
);
702 PrintMenuItem (MenuItemPtrs
[Selected
]);
704 Selected
= NewSelected
;
708 SaveNumRte
= Private
->EfiBc
.Mode
->RouteTableEntries
;
710 Type
= NTOHS (MenuItemPtrs
[Selected
]->Type
);
713 DEBUG ((DEBUG_WARN
, "\nDoMenu() Local boot selected. "));
717 AsciiPrint ("Discover");
719 Status
= Private
->EfiBc
.Discover (
723 (BOOLEAN
) (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
),
727 if (EFI_ERROR (Status
)) {
728 AsciiPrint ("\r \r");
732 "\nDoMenu() Return w/ %xh (%r).",
740 AsciiPrint ("\rBOOT_SERVER_IP: ");
741 PrintIpv4 ((UINT8
*) &Private
->ServerIp
);
743 for (Index
= SaveNumRte
; Index
< Private
->EfiBc
.Mode
->RouteTableEntries
; ++Index
) {
744 if ((Index
% 3) == 0) {
745 AsciiPrint ("\r\nGATEWAY IP:");
749 PrintIpv4 ((UINT8
*) &Private
->EfiBc
.Mode
->RouteTable
[Index
].GwAddr
);
755 DEBUG ((DEBUG_WARN
, "\nDoMenu() Return w/ EFI_SUCCESS. "));
762 Get value 8- or 16-bit value from DHCP option.
764 @param OpPtr Pointer to DHCP option
766 @return Value from DHCP option
772 DHCPV4_OP_STRUCT
*OpPtr
775 if (OpPtr
->Header
.Length
== 1) {
776 return OpPtr
->Data
[0];
778 return NTOHS (OpPtr
->Data
);
784 Locate opcode in buffer.
786 @param BufferPtr Pointer to buffer
787 @param BufferLen Length of buffer
788 @param OpCode Option number
790 @return Pointer to opcode, may be NULL
801 if (BufferPtr
== NULL
) {
805 while (BufferLen
!= 0) {
806 if (*BufferPtr
== OpCode
) {
810 switch (*BufferPtr
) {
820 if ((UINTN
) BufferLen
<= (UINTN
) 2 + BufferPtr
[1]) {
824 BufferLen
-= 2 + BufferPtr
[1];
825 BufferPtr
+= 2 + BufferPtr
[1];
833 Find option in packet
835 @param PacketPtr Pointer to packet
836 @param OpCode option number
838 @return Pointer to option in packet
844 EFI_PXE_BASE_CODE_PACKET
*PacketPtr
,
850 UINT8
*OptionBufferPtr
;
859 // Figure size of DHCP option space.
861 OptionBufferPtr
= _PxeBcFindOpt (
862 PacketPtr
->Dhcpv4
.DhcpOptions
,
864 OP_DHCP_MAX_MESSAGE_SZ
867 if (OptionBufferPtr
!= NULL
) {
868 if (OptionBufferPtr
[1] == 2) {
871 CopyMem (&n
, &OptionBufferPtr
[2], 2);
872 PacketLen
= HTONS (n
);
874 if (PacketLen
< sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET
)) {
877 PacketLen
-= (PacketPtr
->Dhcpv4
.DhcpOptions
- &PacketPtr
->Dhcpv4
.BootpOpcode
) + 28;
882 // Look for option overloading.
884 OptionBufferPtr
= _PxeBcFindOpt (
885 PacketPtr
->Dhcpv4
.DhcpOptions
,
887 OP_DHCP_OPTION_OVERLOAD
890 if (OptionBufferPtr
!= NULL
) {
891 if (OptionBufferPtr
[1] == 1) {
892 Overload
= OptionBufferPtr
[2];
896 // Look for caller's option.
898 OptionBufferPtr
= _PxeBcFindOpt (
899 PacketPtr
->Dhcpv4
.DhcpOptions
,
904 if (OptionBufferPtr
!= NULL
) {
905 return OptionBufferPtr
;
908 if (Overload
& OVLD_FILE
) {
909 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpBootFile
, 128, OpCode
);
911 if (OptionBufferPtr
!= NULL
) {
912 return OptionBufferPtr
;
916 if (Overload
& OVLD_SRVR_NAME
) {
917 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpSrvName
, 64, OpCode
);
919 if (OptionBufferPtr
!= NULL
) {
920 return OptionBufferPtr
;
929 Download file into buffer
931 @param Private Pointer to PxeBc interface
932 @param BufferSize pointer to size of download
934 @param Buffer Pointer to buffer
936 @return EFI_BUFFER_TOO_SMALL -
937 @return EFI_NOT_FOUND -
938 @return EFI_PROTOCOL_ERROR -
944 IN PXE_BASECODE_DEVICE
*Private
,
945 IN OUT UINT64
*BufferSize
,
949 EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo
;
950 EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode
;
951 DHCP_RECEIVE_BUFFER
*RxBuf
;
955 RxBuf
= (DHCP_RECEIVE_BUFFER
*) Private
->BootServerReceiveBuffer
;
958 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Enter."));
960 if (Buffer
== NULL
|| *BufferSize
== 0 || *BufferSize
< Private
->FileSize
) {
961 if (Private
->FileSize
!= 0) {
962 *BufferSize
= Private
->FileSize
;
963 return EFI_BUFFER_TOO_SMALL
;
966 AsciiPrint ("\nTSize");
968 OpCode
= EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
;
969 } else if (RxBuf
->OpAdds
.Status
& WfM11a_TYPE
) {
970 OpCode
= EFI_PXE_BASE_CODE_MTFTP_READ_FILE
;
972 ZeroMem (&MtftpInfo
, sizeof MtftpInfo
);
974 *(IPV4_ADDR
*) &MtftpInfo
.MCastIp
= *(IPV4_ADDR
*) RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_IP
- 1]->Data
;
978 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_CPORT
- 1]->Data
,
979 sizeof MtftpInfo
.CPort
984 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_SPORT
- 1]->Data
,
985 sizeof MtftpInfo
.SPort
988 MtftpInfo
.ListenTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_TMOUT
- 1]);
990 MtftpInfo
.TransmitTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_DELAY
- 1]);
992 AsciiPrint ("\nMTFTP");
994 AsciiPrint ("\nTFTP");
996 OpCode
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
999 Private
->FileSize
= 0;
1001 RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
[RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Header
.Length
] = 0;
1003 Status
= Private
->EfiBc
.Mtftp (
1011 (UINT8
*) RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
,
1016 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_BUFFER_TOO_SMALL
) {
1017 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #1 %Xh", Status
));
1021 if (sizeof (UINTN
) < sizeof (UINT64
) && *BufferSize
> 0xFFFFFFFF) {
1022 Private
->FileSize
= 0xFFFFFFFF;
1024 Private
->FileSize
= (UINTN
) *BufferSize
;
1027 if (OpCode
== EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) {
1028 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #2"));
1029 return EFI_BUFFER_TOO_SMALL
;
1032 if (EFI_ERROR (Status
)) {
1033 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #3 %Xh", Status
));
1037 if (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
&& Private
->EfiBc
.Mode
->PxeBisReplyReceived
) {
1038 UINT64 CredentialLen
;
1040 UINT8 CredentialFilename
[256];
1042 VOID
*CredentialBuffer
;
1045 // Get name of credential file. It may be in the BOOTP
1046 // bootfile field or a DHCP option.
1048 ZeroMem (CredentialFilename
, sizeof CredentialFilename
);
1050 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_DHCP_BOOTFILE
);
1054 /* No credential filename */
1055 return EFI_NOT_FOUND
;
1058 CopyMem (CredentialFilename
, &op
[2], op
[1]);
1060 if (Private
->EfiBc
.Mode
->PxeBisReply
.Dhcpv4
.BootpBootFile
[0] == 0) {
1061 /* No credential filename */
1062 return EFI_NOT_FOUND
;
1065 CopyMem (CredentialFilename
, &op
[2], 128);
1068 // Get size of credential file. It may be available as a
1069 // DHCP option. If not, use the TFTP get file size.
1073 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_BOOT_FILE_SZ
);
1077 * This is actually the size of the credential file
1078 * buffer. The actual credential file size will be
1079 * returned when we download the file.
1084 CopyMem (&n
, &op
[2], 2);
1085 CredentialLen
= HTONS (n
) * 512;
1089 if (CredentialLen
== 0) {
1092 Status
= Private
->EfiBc
.Mtftp (
1094 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1105 if (EFI_ERROR (Status
)) {
1109 if (CredentialLen
== 0) {
1111 // %%TBD -- EFI error for invalid credential
1114 return EFI_PROTOCOL_ERROR
;
1118 // Allocate credential file buffer.
1120 Status
= gBS
->AllocatePool (
1121 EfiBootServicesData
,
1122 (UINTN
) CredentialLen
,
1126 if (EFI_ERROR (Status
)) {
1130 // Download credential file.
1134 Status
= Private
->EfiBc
.Mtftp (
1136 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1147 if (EFI_ERROR (Status
)) {
1148 gBS
->FreePool (CredentialBuffer
);
1152 // Verify credentials.
1154 if (PxebcBisVerify (Private
, Buffer
, Private
->FileSize
, CredentialBuffer
, (UINTN
) CredentialLen
)) {
1155 Status
= EFI_SUCCESS
;
1158 // %%TBD -- An EFI error code for failing credential verification.
1160 Status
= EFI_PROTOCOL_ERROR
;
1163 gBS
->FreePool (CredentialBuffer
);
1171 Start PXE DHCP. Get DHCP and proxyDHCP information.
1172 Display remote boot menu and prompt. Select item from menu.
1174 @param Private Pointer to PxeBc interface
1175 @param BufferSize Pointer to download buffer
1177 @param Buffer Pointer to download buffer
1180 @retval EFI_NOT_READY
1186 IN PXE_BASECODE_DEVICE
*Private
,
1187 IN OUT UINT64
*BufferSize
,
1191 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1192 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
1193 EFI_SIMPLE_NETWORK_MODE
*SnpMode
;
1197 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Enter."));
1200 // Try to start BaseCode, for now only IPv4 is supported
1201 // so don't try to start using IPv6.
1203 Status
= Private
->EfiBc
.Start (&Private
->EfiBc
, FALSE
);
1205 if (EFI_ERROR (Status
)) {
1206 if (Status
!= EFI_ALREADY_STARTED
) {
1207 DEBUG ((DEBUG_NET
, "\nLoadfileStart() Exit BC.Start() == %xh", Status
));
1212 // Get pointers to PXE mode structure, SNP protocol structure
1213 // and SNP mode structure.
1215 PxeBcMode
= Private
->EfiBc
.Mode
;
1216 Snp
= Private
->SimpleNetwork
;
1217 SnpMode
= Snp
->Mode
;
1220 // Display client MAC address, like 16-bit PXE ROMs
1222 AsciiPrint ("\nCLIENT MAC ADDR: ");
1228 hlen
= SnpMode
->HwAddressSize
;
1230 for (Index
= 0; Index
< hlen
; ++Index
) {
1231 AsciiPrint ("%02x ", SnpMode
->CurrentAddress
.Addr
[Index
]);
1235 AsciiPrint ("\nDHCP");
1237 Status
= Private
->EfiBc
.Dhcp (&Private
->EfiBc
, TRUE
);
1239 if (EFI_ERROR (Status
)) {
1240 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status
));
1241 AsciiPrint ("\r \r");
1245 ShowMyInfo (Private
);
1247 RxBuf
= PxeBcMode
->ProxyOfferReceived
? &PXE_OFFER_BUFFER
: &DHCPV4_ACK_BUFFER
;
1248 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
1250 Status
= DoMenu (Private
, RxBufferPtr
);
1252 if (Status
== EFI_SUCCESS
) {
1254 // did a discovery - take info from discovery packet
1256 RxBuf
= &PXE_ACK_BUFFER
;
1257 } else if (Status
== NO_MENU
) {
1259 // did not do a discovery - take info from rxbuf
1261 Private
->ServerIp
.Addr
[0] = RxBufferPtr
->u
.Dhcpv4
.siaddr
;
1263 if (!(Private
->ServerIp
.Addr
[0])) {
1264 *(IPV4_ADDR
*) &Private
->ServerIp
= *(IPV4_ADDR
*) RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1]->Data
;
1267 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit DoMenu() == %Xh", Status
));
1271 if (!RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
1272 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit Not ready?"));
1273 return EFI_NOT_READY
;
1276 // check for file size option sent
1278 if (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]) {
1279 Private
->FileSize
= 512 * NTOHS (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]->Data
);
1282 Private
->BootServerReceiveBuffer
= RxBufferPtr
;
1284 Status
= DownloadFile (Private
, BufferSize
, Buffer
);
1288 "\nLoadfileStart() Exit. DownloadFile() = %Xh",
1297 Loadfile interface for PxeBc interface
1299 @param This Pointer to Loadfile interface
1300 @param FilePath Not used and not checked
1301 @param BootPolicy Must be TRUE
1302 @param BufferSize Pointer to buffer size
1303 @param Buffer Pointer to download buffer or
1306 @return EFI_INVALID_PARAMETER -
1307 @return EFI_UNSUPPORTED -
1308 @return EFI_SUCCESS -
1309 @return EFI_BUFFER_TOO_SMALL -
1315 IN EFI_LOAD_FILE_PROTOCOL
*This
,
1316 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
1317 IN BOOLEAN BootPolicy
,
1318 IN OUT UINTN
*BufferSize
,
1322 LOADFILE_DEVICE
*LoadfilePtr
;
1325 INT32 OrigAttribute
;
1326 BOOLEAN RemoveCallback
;
1327 BOOLEAN NewMakeCallback
;
1329 EFI_STATUS TempStatus
;
1333 OrigMode
= gST
->ConOut
->Mode
->Mode
;
1334 OrigAttribute
= gST
->ConOut
->Mode
->Attribute
;
1335 RemoveCallback
= FALSE
;
1337 AsciiPrint ("Running LoadFile()\n");
1340 // Resolve Warning 4 unreferenced parameter problem
1345 // If either if these parameters are NULL, we cannot continue.
1347 if (This
== NULL
|| BufferSize
== NULL
) {
1348 DEBUG ((DEBUG_WARN
, "\nLoadFile() This or BufferSize == NULL"));
1349 return EFI_INVALID_PARAMETER
;
1352 // We only support BootPolicy == TRUE
1355 DEBUG ((DEBUG_WARN
, "\nLoadFile() BootPolicy == FALSE"));
1356 return EFI_UNSUPPORTED
;
1359 // Get pointer to LoadFile protocol structure.
1361 LoadfilePtr
= CR (This
, LOADFILE_DEVICE
, LoadFile
, LOADFILE_DEVICE_SIGNATURE
);
1363 if (LoadfilePtr
== NULL
) {
1366 "\nLoadFile() Could not get pointer to LoadFile structure")
1368 return EFI_INVALID_PARAMETER
;
1373 EfiAcquireLock (&LoadfilePtr
->Lock
);
1376 // Set console output mode and display attribute
1378 if (OrigMode
!= 0) {
1379 gST
->ConOut
->SetMode (gST
->ConOut
, 0);
1382 gST
->ConOut
->SetAttribute (
1384 EFI_TEXT_ATTR (EFI_LIGHTGRAY
,EFI_BLACK
)
1388 // See if BaseCode already has a Callback protocol attached.
1389 // If there is none, attach our own Callback protocol.
1391 Status
= gBS
->HandleProtocol (
1392 LoadfilePtr
->Private
->Handle
,
1393 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1394 (VOID
*) &LoadfilePtr
->Private
->CallbackProtocolPtr
1397 if (Status
== EFI_SUCCESS
) {
1399 // There is already a callback routine. Do nothing.
1401 DEBUG ((DEBUG_WARN
, "\nLoadFile() BC callback exists."));
1403 } else if (Status
== EFI_UNSUPPORTED
) {
1405 // No BaseCode Callback protocol found. Add our own.
1407 Status
= gBS
->InstallProtocolInterface (
1408 &LoadfilePtr
->Private
->Handle
,
1409 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1410 EFI_NATIVE_INTERFACE
,
1414 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback install status == %xh", Status
));
1416 RemoveCallback
= (BOOLEAN
) (Status
== EFI_SUCCESS
);
1418 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
&& LoadfilePtr
->Private
->EfiBc
.Mode
->Started
) {
1419 NewMakeCallback
= TRUE
;
1420 LoadfilePtr
->Private
->EfiBc
.SetParameters (
1421 &LoadfilePtr
->Private
->EfiBc
,
1431 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback check status == %xh", Status
));
1434 // Check for starting or for continuing after already getting
1437 if (LoadfilePtr
->Private
->FileSize
== 0) {
1439 Status
= LoadfileStart (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1441 if (sizeof (UINTN
) < sizeof (UINT64
) && TmpBufSz
> 0xFFFFFFFF) {
1442 *BufferSize
= 0xFFFFFFFF;
1444 *BufferSize
= (UINTN
) TmpBufSz
;
1447 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1449 // This is done so loadfile will work even if the boot manager
1450 // did not make the first call with Buffer == NULL.
1454 } else if (Buffer
== NULL
) {
1455 DEBUG ((DEBUG_WARN
, "\nLoadfile() Get buffer size"));
1458 // Continuing from previous LoadFile request. Make sure there
1459 // is a buffer and that it is big enough.
1461 *BufferSize
= LoadfilePtr
->Private
->FileSize
;
1462 Status
= EFI_BUFFER_TOO_SMALL
;
1464 DEBUG ((DEBUG_WARN
, "\nLoadFile() Download file"));
1467 // Everything looks good, try to download the file.
1469 TmpBufSz
= *BufferSize
;
1470 Status
= DownloadFile (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1473 // Next call to loadfile will start DHCP process again.
1475 LoadfilePtr
->Private
->FileSize
= 0;
1478 // If we added a callback protocol, now is the time to remove it.
1480 if (RemoveCallback
) {
1481 NewMakeCallback
= FALSE
;
1482 TempStatus
= LoadfilePtr
->Private
->EfiBc
.SetParameters (
1483 &LoadfilePtr
->Private
->EfiBc
,
1491 if (TempStatus
== EFI_SUCCESS
) {
1492 gBS
->UninstallProtocolInterface (
1493 LoadfilePtr
->Private
->Handle
,
1494 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1500 // Restore display mode and attribute
1502 if (OrigMode
!= 0) {
1503 gST
->ConOut
->SetMode (gST
->ConOut
, OrigMode
);
1506 gST
->ConOut
->SetAttribute (gST
->ConOut
, OrigAttribute
);
1511 EfiReleaseLock (&LoadfilePtr
->Lock
);
1513 DEBUG ((DEBUG_WARN
, "\nBC.Loadfile() Status == %xh\n", Status
));
1515 if (Status
== EFI_SUCCESS
) {
1518 } else if (Status
== EFI_BUFFER_TOO_SMALL
) {
1520 // Error is only displayed when we are actually trying to
1521 // download the boot image.
1523 if (Buffer
== NULL
) {
1524 return EFI_BUFFER_TOO_SMALL
;
1527 AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
1529 } else if (Status
== EFI_DEVICE_ERROR
) {
1530 AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
1532 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
1533 AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
1535 } else if (Status
== EFI_NO_MEDIA
) {
1536 AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
1538 } else if (Status
== EFI_NO_RESPONSE
) {
1539 AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
1541 } else if (Status
== EFI_TIMEOUT
) {
1542 AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
1544 } else if (Status
== EFI_ABORTED
) {
1545 AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
1547 } else if (Status
== EFI_ICMP_ERROR
) {
1548 AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
1550 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1551 if (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpErrorReceived
) {
1554 "PXE-E98: Type: %xh Code: %xh ",
1555 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
,
1556 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
1559 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
) {
1561 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
) {
1562 case 0x00: /* net unreachable */
1563 AsciiPrint ("Net unreachable");
1566 case 0x01: /* host unreachable */
1567 AsciiPrint ("Host unreachable");
1570 case 0x02: /* protocol unreachable */
1571 AsciiPrint ("Protocol unreachable");
1574 case 0x03: /* port unreachable */
1575 AsciiPrint ("Port unreachable");
1578 case 0x04: /* Fragmentation needed */
1579 AsciiPrint ("Fragmentation needed");
1582 case 0x05: /* Source route failed */
1583 AsciiPrint ("Source route failed");
1594 } else if (Status
== EFI_TFTP_ERROR
) {
1595 AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
1597 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1598 if (LoadfilePtr
->Private
->EfiBc
.Mode
->TftpErrorReceived
) {
1600 "PXE-E98: Code: %xh %a\n",
1601 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorCode
,
1602 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorString
1608 AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status
);
1611 LoadfilePtr
->Private
->EfiBc
.Stop (&LoadfilePtr
->Private
->EfiBc
);