3 Copyright (c) 2006 - 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.
23 #define DO_MENU (EFI_SUCCESS)
24 #define NO_MENU (DO_MENU + 1)
25 #define LOCAL_BOOT (EFI_ABORTED)
26 #define AUTO_SELECT (NO_MENU)
28 #define NUMBER_ROWS 25 // we set to mode 0
29 #define MAX_MENULIST 23
31 #define Ctl(x) (0x1F & (x))
34 DHCPV4_OP_STRUCT
*OpPtr
;
35 PXE_BOOT_MENU_ENTRY
*CurrentMenuItemPtr
;
36 PXE_OP_DISCOVERY_CONTROL
*DiscCtlOpStr
;
37 PXE_OP_BOOT_MENU
*MenuPtr
;
43 EFI_PXE_BASE_CODE_CALLBACK_STATUS
46 IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
* This
,
47 IN EFI_PXE_BASE_CODE_FUNCTION Function
,
49 IN UINT32 PacketLength
,
50 IN EFI_PXE_BASE_CODE_PACKET
* PacketPtr OPTIONAL
56 PxeBc callback routine for status updates and aborts.
60 This - Pointer to PxeBcCallback interface
61 Function - PxeBc function ID#
62 Received - Receive/transmit flag
63 PacketLength - Length of received packet (0 == idle callback)
64 PacketPtr - Pointer to received packet (NULL == idle callback)
68 EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE -
69 EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT -
73 STATIC UINTN Propeller
;
81 // Resolve Warning 4 unreferenced parameter problem
86 // Check for user abort.
88 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_SUCCESS
) {
90 if (Key
.UnicodeChar
== Ctl ('c')) {
91 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
93 } else if (Key
.ScanCode
== SCAN_ESC
) {
94 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
;
98 // Do nothing if this is a receive.
101 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
104 // The display code is only for these functions.
107 case EFI_PXE_BASE_CODE_FUNCTION_MTFTP
:
109 // If this is a transmit and not a M/TFTP open request,
110 // return now. Do not print a dot for each M/TFTP packet
111 // that is sent, only for the open packets.
113 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
114 if (PacketPtr
->Raw
[0x1C] != 0x00 || PacketPtr
->Raw
[0x1D] != 0x01) {
115 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
121 case EFI_PXE_BASE_CODE_FUNCTION_DHCP
:
122 case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER
:
126 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
131 if (PacketLength
!= 0 && PacketPtr
!= NULL
) {
133 // Display a '.' when a packet is transmitted.
136 } else if (PacketLength
== 0 && PacketPtr
== NULL
) {
138 // Display a propeller when waiting for packets if at
139 // least 200 ms have passed.
141 Row
= gST
->ConOut
->Mode
->CursorRow
;
142 Col
= gST
->ConOut
->Mode
->CursorColumn
;
144 AsciiPrint ("%c", "/-\\|"[Propeller
]);
145 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Col
, Row
);
147 Propeller
= (Propeller
+ 1) & 3;
150 return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
;
153 STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback
= {
154 EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION
,
167 Display an IPv4 address in dot notation.
171 Ptr - Pointer to IPv4 address.
180 AsciiPrint ("%d.%d.%d.%d", Ptr
[0], Ptr
[1], Ptr
[2], Ptr
[3]);
187 IN PXE_BASECODE_DEVICE
*Private
193 Display client and server IP information.
197 Private - Pointer to PxeBc interface
205 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
209 // Do nothing if a NULL pointer is passed in.
211 if (Private
== NULL
) {
215 // Get pointer to PXE BaseCode mode structure
217 PxeBcMode
= Private
->EfiBc
.Mode
;
220 // Display client IP address
222 AsciiPrint ("\rCLIENT IP: ");
223 PrintIpv4 (PxeBcMode
->StationIp
.v4
.Addr
);
226 // Display subnet mask
228 AsciiPrint (" MASK: ");
229 PrintIpv4 (PxeBcMode
->SubnetMask
.v4
.Addr
);
232 // Display DHCP and proxyDHCP IP addresses
234 if (PxeBcMode
->ProxyOfferReceived
) {
235 AsciiPrint ("\nDHCP IP: ");
236 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
238 AsciiPrint (" PROXY IP: ");
239 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) PXE_OFFER_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
241 AsciiPrint (" DHCP IP: ");
242 PrintIpv4 (((DHCPV4_OP_SERVER_IP
*) DHCPV4_ACK_BUFFER
.OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1])->Ip
.Addr
);
245 // Display gateway IP addresses
247 for (Index
= 0; Index
< PxeBcMode
->RouteTableEntries
; ++Index
) {
248 if ((Index
% 3) == 0) {
249 AsciiPrint ("\r\nGATEWAY IP:");
253 PrintIpv4 (PxeBcMode
->RouteTable
[Index
].GwAddr
.v4
.Addr
);
263 PXE_BASECODE_DEVICE
*Private
,
264 PXE_OP_BOOT_PROMPT
*BootPromptPtr
270 Display prompt and wait for input.
274 Private - Pointer to PxeBc interface
275 BootPromptPtr - Pointer to PXE boot prompt option
287 EFI_EVENT TimeoutEvent
;
288 EFI_EVENT SecondsEvent
;
295 // if auto select, just get right to it
297 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_AUTO_SELECT
) {
301 // if no timeout, go directly to display of menu
303 if (BootPromptPtr
->Timeout
== PXE_BOOT_PROMPT_NO_TIMEOUT
) {
309 Status
= gBS
->CreateEvent (
317 if (EFI_ERROR (Status
)) {
321 Status
= gBS
->SetTimer (
324 BootPromptPtr
->Timeout
* 10000000 + 100000
327 if (EFI_ERROR (Status
)) {
328 gBS
->CloseEvent (TimeoutEvent
);
334 Status
= gBS
->CreateEvent (
342 if (EFI_ERROR (Status
)) {
343 gBS
->CloseEvent (TimeoutEvent
);
347 Status
= gBS
->SetTimer (
353 if (EFI_ERROR (Status
)) {
354 gBS
->CloseEvent (SecondsEvent
);
355 gBS
->CloseEvent (TimeoutEvent
);
359 // display the prompt
360 // IMPORTANT! This prompt is an ASCII character string that may
361 // not be terminated with a NULL byte.
363 SaveChar
= BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1];
364 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = 0;
366 AsciiPrint ("%a ", BootPromptPtr
->Prompt
);
367 BootPromptPtr
->Prompt
[BootPromptPtr
->Header
.Length
- 1] = SaveChar
;
370 // wait until time expires or selection made - menu or local
372 SecColumn
= gST
->ConOut
->Mode
->CursorColumn
;
373 SecRow
= gST
->ConOut
->Mode
->CursorRow
;
374 SecsLeft
= BootPromptPtr
->Timeout
;
376 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
377 AsciiPrint ("(%d) ", SecsLeft
);
380 // set the default action to be AUTO_SELECT
382 Status
= AUTO_SELECT
;
384 while (EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
))) {
387 if (!EFI_ERROR (gBS
->CheckEvent (SecondsEvent
))) {
389 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
390 AsciiPrint ("(%d) ", SecsLeft
);
393 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
397 BufferSize
= sizeof Buffer
;
399 Private
->EfiBc
.UdpRead (
401 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
402 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
403 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
405 NULL
, /* dest port */
417 if (Key
.ScanCode
== 0) {
418 switch (Key
.UnicodeChar
) {
433 switch (Key
.ScanCode
) {
450 gBS
->CloseEvent (SecondsEvent
);
451 gBS
->CloseEvent (TimeoutEvent
);
453 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
462 PXE_BOOT_MENU_ENTRY
*MenuItemPtr
468 Display one menu item.
472 MenuItemPtr - Pointer to PXE menu item option.
483 Length
= (UINT8
) EFI_MIN (70, MenuItemPtr
->DataLen
);
484 SaveChar
= MenuItemPtr
->Data
[Length
];
486 MenuItemPtr
->Data
[Length
] = 0;
487 AsciiPrint (" %a\n", MenuItemPtr
->Data
);
488 MenuItemPtr
->Data
[Length
] = SaveChar
;
494 PXE_BASECODE_DEVICE
*Private
,
495 DHCP_RECEIVE_BUFFER
*RxBufferPtr
501 Display and process menu.
505 Private - Pointer to PxeBc interface
506 RxBufferPtr - Pointer to receive buffer
515 PXE_OP_DISCOVERY_CONTROL
*DiscoveryControlPtr
;
516 PXE_BOOT_MENU_ENTRY
*MenuItemPtrs
[MAX_MENULIST
];
533 DEBUG ((EFI_D_WARN
, "\nDoMenu() Enter."));
535 /* see if we have a menu/prompt */
536 if (!(RxBufferPtr
->OpAdds
.Status
& DISCOVER_TYPE
)) {
539 "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
540 RxBufferPtr
->OpAdds
.Status
)
546 DiscoveryControlPtr
= (PXE_OP_DISCOVERY_CONTROL
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_DISCOVERY_CONTROL_IX
- 1];
549 // if not USE_BOOTFILE or no bootfile given, must have menu stuff
551 if ((DiscoveryControlPtr
->ControlBits
& USE_BOOTFILE
) && RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
552 DEBUG ((EFI_D_WARN
, "\nDoMenu() DHCP w/ bootfile. "));
556 // do prompt & menu if necessary
558 Status
= DoPrompt (Private
, (PXE_OP_BOOT_PROMPT
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_PROMPT_IX
- 1]);
560 if (Status
== LOCAL_BOOT
) {
561 DEBUG ((EFI_D_WARN
, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
566 Ptr
.BytePtr
= (UINT8
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_MENU_IX
- 1];
568 MenuLth
= Ptr
.MenuPtr
->Header
.Length
;
569 Ptr
.CurrentMenuItemPtr
= Ptr
.MenuPtr
->MenuItem
;
572 // build menu items array
574 for (Longest
= NumMenuItems
= Index
= 0; Index
< MenuLth
&& NumMenuItems
< MAX_MENULIST
;) {
577 lth
= Ptr
.CurrentMenuItemPtr
->DataLen
+ sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
);
579 MenuItemPtrs
[NumMenuItems
++] = Ptr
.CurrentMenuItemPtr
;
585 if ((Longest
= lth
) > 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))) {
586 Longest
= 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
));
594 if (Status
!= AUTO_SELECT
) {
597 SetMem (BlankBuf
, sizeof BlankBuf
, ' ');
598 BlankBuf
[Longest
+ 5 - (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))] = 0;
604 for (Index
= 0; Index
< NumMenuItems
; ++Index
) {
605 PrintMenuItem (MenuItemPtrs
[Index
]);
608 TopRow
= gST
->ConOut
->Mode
->CursorRow
- NumMenuItems
;
611 // now wait for a selection
616 // highlight selection
621 NewSelected
= Selected
;
624 // highlight selected row
626 gST
->ConOut
->SetAttribute (
628 EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
)
630 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ Selected
);
632 AsciiPrint (" --->%a\r", BlankBuf
);
634 PrintMenuItem (MenuItemPtrs
[Selected
]);
635 gST
->ConOut
->SetAttribute (
637 EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
)
639 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ NumMenuItems
);
642 // wait for a keystroke
644 while (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
648 TmpBufLen
= sizeof TmpBuf
;
650 Private
->EfiBc
.UdpRead (
652 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
653 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
654 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
656 NULL
, /* dest port */
667 switch (Key
.UnicodeChar
) {
669 Key
.ScanCode
= SCAN_ESC
;
672 case Ctl ('j'): /* linefeed */
673 case Ctl ('m'): /* return */
677 case Ctl ('i'): /* tab */
681 Key
.ScanCode
= SCAN_DOWN
;
684 case Ctl ('h'): /* backspace */
687 Key
.ScanCode
= SCAN_UP
;
695 switch (Key
.ScanCode
) {
706 if (++NewSelected
== NumMenuItems
) {
719 NewSelected
= NumMenuItems
- 1;
726 /* unhighlight last selected row */
727 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 5, TopRow
+ Selected
);
729 AsciiPrint ("%a\r", BlankBuf
);
731 PrintMenuItem (MenuItemPtrs
[Selected
]);
733 Selected
= NewSelected
;
737 SaveNumRte
= Private
->EfiBc
.Mode
->RouteTableEntries
;
739 Type
= NTOHS (MenuItemPtrs
[Selected
]->Type
);
742 DEBUG ((EFI_D_WARN
, "\nDoMenu() Local boot selected. "));
746 AsciiPrint ("Discover");
748 Status
= Private
->EfiBc
.Discover (
752 (BOOLEAN
) (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
),
756 if (EFI_ERROR (Status
)) {
757 AsciiPrint ("\r \r");
761 "\nDoMenu() Return w/ %xh (%r).",
769 AsciiPrint ("\rBOOT_SERVER_IP: ");
770 PrintIpv4 ((UINT8
*) &Private
->ServerIp
);
772 for (Index
= SaveNumRte
; Index
< Private
->EfiBc
.Mode
->RouteTableEntries
; ++Index
) {
773 if ((Index
% 3) == 0) {
774 AsciiPrint ("\r\nGATEWAY IP:");
778 PrintIpv4 ((UINT8
*) &Private
->EfiBc
.Mode
->RouteTable
[Index
].GwAddr
);
784 DEBUG ((EFI_D_WARN
, "\nDoMenu() Return w/ EFI_SUCCESS. "));
792 DHCPV4_OP_STRUCT
*OpPtr
798 Get value 8- or 16-bit value from DHCP option.
802 OpPtr - Pointer to DHCP option
806 Value from DHCP option
810 if (OpPtr
->Header
.Length
== 1) {
811 return OpPtr
->Data
[0];
813 return NTOHS (OpPtr
->Data
);
828 Locate opcode in buffer.
832 BufferPtr - Pointer to buffer
833 BufferLen - Length of buffer
834 OpCode - Option number
838 Pointer to opcode, may be NULL
842 if (BufferPtr
== NULL
) {
846 while (BufferLen
!= 0) {
847 if (*BufferPtr
== OpCode
) {
851 switch (*BufferPtr
) {
861 if ((UINTN
) BufferLen
<= (UINTN
) 2 + BufferPtr
[1]) {
865 BufferLen
-= 2 + BufferPtr
[1];
866 BufferPtr
+= 2 + BufferPtr
[1];
875 EFI_PXE_BASE_CODE_PACKET
*PacketPtr
,
882 Find option in packet
886 PacketPtr - Pointer to packet
887 OpCode - option number
891 Pointer to option in packet
897 UINT8
*OptionBufferPtr
;
906 // Figure size of DHCP option space.
908 OptionBufferPtr
= _PxeBcFindOpt (
909 PacketPtr
->Dhcpv4
.DhcpOptions
,
911 OP_DHCP_MAX_MESSAGE_SZ
914 if (OptionBufferPtr
!= NULL
) {
915 if (OptionBufferPtr
[1] == 2) {
918 CopyMem (&n
, &OptionBufferPtr
[2], 2);
919 PacketLen
= HTONS (n
);
921 if (PacketLen
< sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET
)) {
924 PacketLen
-= (PacketPtr
->Dhcpv4
.DhcpOptions
- &PacketPtr
->Dhcpv4
.BootpOpcode
) + 28;
929 // Look for option overloading.
931 OptionBufferPtr
= _PxeBcFindOpt (
932 PacketPtr
->Dhcpv4
.DhcpOptions
,
934 OP_DHCP_OPTION_OVERLOAD
937 if (OptionBufferPtr
!= NULL
) {
938 if (OptionBufferPtr
[1] == 1) {
939 Overload
= OptionBufferPtr
[2];
943 // Look for caller's option.
945 OptionBufferPtr
= _PxeBcFindOpt (
946 PacketPtr
->Dhcpv4
.DhcpOptions
,
951 if (OptionBufferPtr
!= NULL
) {
952 return OptionBufferPtr
;
955 if (Overload
& OVLD_FILE
) {
956 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpBootFile
, 128, OpCode
);
958 if (OptionBufferPtr
!= NULL
) {
959 return OptionBufferPtr
;
963 if (Overload
& OVLD_SRVR_NAME
) {
964 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpSrvName
, 64, OpCode
);
966 if (OptionBufferPtr
!= NULL
) {
967 return OptionBufferPtr
;
977 IN PXE_BASECODE_DEVICE
*Private
,
978 IN OUT UINT64
*BufferSize
,
985 Download file into buffer
989 Private - Pointer to PxeBc interface
990 BufferSize - pointer to size of download buffer
991 Buffer - Pointer to buffer
995 EFI_BUFFER_TOO_SMALL -
1001 EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo
;
1002 EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode
;
1003 DHCP_RECEIVE_BUFFER
*RxBuf
;
1007 RxBuf
= (DHCP_RECEIVE_BUFFER
*) Private
->BootServerReceiveBuffer
;
1010 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Enter."));
1012 if (Buffer
== NULL
|| *BufferSize
== 0 || *BufferSize
< Private
->FileSize
) {
1013 if (Private
->FileSize
!= 0) {
1014 *BufferSize
= Private
->FileSize
;
1015 return EFI_BUFFER_TOO_SMALL
;
1018 AsciiPrint ("\nTSize");
1020 OpCode
= EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
;
1021 } else if (RxBuf
->OpAdds
.Status
& WfM11a_TYPE
) {
1022 OpCode
= EFI_PXE_BASE_CODE_MTFTP_READ_FILE
;
1024 ZeroMem (&MtftpInfo
, sizeof MtftpInfo
);
1026 *(IPV4_ADDR
*) &MtftpInfo
.MCastIp
= *(IPV4_ADDR
*) RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_IP
- 1]->Data
;
1030 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_CPORT
- 1]->Data
,
1031 sizeof MtftpInfo
.CPort
1036 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_SPORT
- 1]->Data
,
1037 sizeof MtftpInfo
.SPort
1040 MtftpInfo
.ListenTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_TMOUT
- 1]);
1042 MtftpInfo
.TransmitTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_DELAY
- 1]);
1044 AsciiPrint ("\nMTFTP");
1046 AsciiPrint ("\nTFTP");
1048 OpCode
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
1051 Private
->FileSize
= 0;
1053 RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
[RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Header
.Length
] = 0;
1055 Status
= Private
->EfiBc
.Mtftp (
1063 (UINT8
*) RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
,
1068 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_BUFFER_TOO_SMALL
) {
1069 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Exit #1 %Xh", Status
));
1073 if (sizeof (UINTN
) < sizeof (UINT64
) && *BufferSize
> 0xFFFFFFFF) {
1074 Private
->FileSize
= 0xFFFFFFFF;
1076 Private
->FileSize
= (UINTN
) *BufferSize
;
1079 if (OpCode
== EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) {
1080 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Exit #2"));
1081 return EFI_BUFFER_TOO_SMALL
;
1084 if (EFI_ERROR (Status
)) {
1085 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Exit #3 %Xh", Status
));
1089 if (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
&& Private
->EfiBc
.Mode
->PxeBisReplyReceived
) {
1090 UINT64 CredentialLen
;
1091 UINT8 CredentialFilename
[256];
1093 VOID
*CredentialBuffer
;
1096 // Get name of credential file. It may be in the BOOTP
1097 // bootfile field or a DHCP option.
1099 ZeroMem (CredentialFilename
, sizeof CredentialFilename
);
1101 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_DHCP_BOOTFILE
);
1105 /* No credential filename */
1106 return EFI_NOT_FOUND
;
1109 CopyMem (CredentialFilename
, &op
[2], op
[1]);
1111 if (Private
->EfiBc
.Mode
->PxeBisReply
.Dhcpv4
.BootpBootFile
[0] == 0) {
1112 /* No credential filename */
1113 return EFI_NOT_FOUND
;
1116 CopyMem (CredentialFilename
, &op
[2], 128);
1119 // Get size of credential file. It may be available as a
1120 // DHCP option. If not, use the TFTP get file size.
1124 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_BOOT_FILE_SZ
);
1128 * This is actually the size of the credential file
1129 * buffer. The actual credential file size will be
1130 * returned when we download the file.
1135 CopyMem (&n
, &op
[2], 2);
1136 CredentialLen
= HTONS (n
) * 512;
1140 if (CredentialLen
== 0) {
1143 Status
= Private
->EfiBc
.Mtftp (
1145 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1156 if (EFI_ERROR (Status
)) {
1160 if (CredentialLen
== 0) {
1162 // %%TBD -- EFI error for invalid credential
1165 return EFI_PROTOCOL_ERROR
;
1169 // Allocate credential file buffer.
1171 CredentialBuffer
= AllocatePool ((UINTN
) CredentialLen
);
1172 if (CredentialBuffer
== NULL
) {
1173 return EFI_OUT_OF_RESOURCES
;
1176 // Download credential file.
1180 Status
= Private
->EfiBc
.Mtftp (
1182 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1193 if (EFI_ERROR (Status
)) {
1194 FreePool (CredentialBuffer
);
1198 // Verify credentials.
1200 if (PxebcBisVerify (Private
, Buffer
, Private
->FileSize
, CredentialBuffer
, (UINTN
) CredentialLen
)) {
1201 Status
= EFI_SUCCESS
;
1204 // %%TBD -- An EFI error code for failing credential verification.
1206 Status
= EFI_PROTOCOL_ERROR
;
1209 FreePool (CredentialBuffer
);
1218 IN PXE_BASECODE_DEVICE
*Private
,
1219 IN OUT UINT64
*BufferSize
,
1224 Routine Description:
1226 Start PXE DHCP. Get DHCP and proxyDHCP information.
1227 Display remote boot menu and prompt. Select item from menu.
1231 Private - Pointer to PxeBc interface
1232 BufferSize - Pointer to download buffer size
1233 Buffer - Pointer to download buffer
1242 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1243 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
1244 EFI_SIMPLE_NETWORK_MODE
*SnpMode
;
1248 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Enter."));
1251 // Try to start BaseCode, for now only IPv4 is supported
1252 // so don't try to start using IPv6.
1254 Status
= Private
->EfiBc
.Start (&Private
->EfiBc
, FALSE
);
1256 if (EFI_ERROR (Status
)) {
1257 if (Status
!= EFI_ALREADY_STARTED
) {
1258 DEBUG ((EFI_D_NET
, "\nLoadfileStart() Exit BC.Start() == %xh", Status
));
1263 // Get pointers to PXE mode structure, SNP protocol structure
1264 // and SNP mode structure.
1266 PxeBcMode
= Private
->EfiBc
.Mode
;
1267 Snp
= Private
->SimpleNetwork
;
1268 SnpMode
= Snp
->Mode
;
1271 // Display client MAC address, like 16-bit PXE ROMs
1273 AsciiPrint ("\nCLIENT MAC ADDR: ");
1279 hlen
= SnpMode
->HwAddressSize
;
1281 for (Index
= 0; Index
< hlen
; ++Index
) {
1282 AsciiPrint ("%02x ", SnpMode
->CurrentAddress
.Addr
[Index
]);
1286 AsciiPrint ("\nDHCP");
1288 Status
= Private
->EfiBc
.Dhcp (&Private
->EfiBc
, TRUE
);
1290 if (EFI_ERROR (Status
)) {
1291 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status
));
1292 AsciiPrint ("\r \r");
1296 ShowMyInfo (Private
);
1298 RxBuf
= PxeBcMode
->ProxyOfferReceived
? &PXE_OFFER_BUFFER
: &DHCPV4_ACK_BUFFER
;
1299 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
1301 Status
= DoMenu (Private
, RxBufferPtr
);
1303 if (Status
== EFI_SUCCESS
) {
1305 // did a discovery - take info from discovery packet
1307 RxBuf
= &PXE_ACK_BUFFER
;
1308 } else if (Status
== NO_MENU
) {
1310 // did not do a discovery - take info from rxbuf
1312 Private
->ServerIp
.Addr
[0] = RxBufferPtr
->u
.Dhcpv4
.siaddr
;
1314 if (!(Private
->ServerIp
.Addr
[0])) {
1315 *(IPV4_ADDR
*) &Private
->ServerIp
= *(IPV4_ADDR
*) RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1]->Data
;
1318 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Exit DoMenu() == %Xh", Status
));
1322 if (!RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
1323 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Exit Not ready?"));
1324 return EFI_NOT_READY
;
1327 // check for file size option sent
1329 if (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]) {
1330 Private
->FileSize
= 512 * NTOHS (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]->Data
);
1333 Private
->BootServerReceiveBuffer
= RxBufferPtr
;
1335 Status
= DownloadFile (Private
, BufferSize
, Buffer
);
1339 "\nLoadfileStart() Exit. DownloadFile() = %Xh",
1349 IN EFI_LOAD_FILE_PROTOCOL
*This
,
1350 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
1351 IN BOOLEAN BootPolicy
,
1352 IN OUT UINTN
*BufferSize
,
1357 Routine Description:
1359 Loadfile interface for PxeBc interface
1363 This - Pointer to Loadfile interface
1364 FilePath - Not used and not checked
1365 BootPolicy - Must be TRUE
1366 BufferSize - Pointer to buffer size
1367 Buffer - Pointer to download buffer or NULL
1371 EFI_INVALID_PARAMETER -
1374 EFI_BUFFER_TOO_SMALL -
1378 LOADFILE_DEVICE
*LoadfilePtr
;
1381 INT32 OrigAttribute
;
1382 BOOLEAN RemoveCallback
;
1383 BOOLEAN NewMakeCallback
;
1385 EFI_STATUS TempStatus
;
1389 OrigMode
= gST
->ConOut
->Mode
->Mode
;
1390 OrigAttribute
= gST
->ConOut
->Mode
->Attribute
;
1391 RemoveCallback
= FALSE
;
1393 AsciiPrint ("Running LoadFile()\n");
1396 // If either if these parameters are NULL, we cannot continue.
1398 if (This
== NULL
|| BufferSize
== NULL
) {
1399 DEBUG ((EFI_D_WARN
, "\nLoadFile() This or BufferSize == NULL"));
1400 return EFI_INVALID_PARAMETER
;
1403 // We only support BootPolicy == TRUE
1406 DEBUG ((EFI_D_WARN
, "\nLoadFile() BootPolicy == FALSE"));
1407 return EFI_UNSUPPORTED
;
1410 // Get pointer to LoadFile protocol structure.
1412 LoadfilePtr
= CR (This
, LOADFILE_DEVICE
, LoadFile
, LOADFILE_DEVICE_SIGNATURE
);
1414 if (LoadfilePtr
== NULL
) {
1417 "\nLoadFile() Could not get pointer to LoadFile structure")
1419 return EFI_INVALID_PARAMETER
;
1424 EfiAcquireLock (&LoadfilePtr
->Lock
);
1427 // Set console output mode and display attribute
1429 if (OrigMode
!= 0) {
1430 gST
->ConOut
->SetMode (gST
->ConOut
, 0);
1433 gST
->ConOut
->SetAttribute (
1435 EFI_TEXT_ATTR (EFI_LIGHTGRAY
,EFI_BLACK
)
1439 // See if BaseCode already has a Callback protocol attached.
1440 // If there is none, attach our own Callback protocol.
1442 Status
= gBS
->HandleProtocol (
1443 LoadfilePtr
->Private
->Handle
,
1444 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1445 (VOID
*) &LoadfilePtr
->Private
->CallbackProtocolPtr
1448 if (Status
== EFI_SUCCESS
) {
1450 // There is already a callback routine. Do nothing.
1452 DEBUG ((EFI_D_WARN
, "\nLoadFile() BC callback exists."));
1453 } else if (Status
== EFI_UNSUPPORTED
) {
1455 // No BaseCode Callback protocol found. Add our own.
1457 Status
= gBS
->InstallProtocolInterface (
1458 &LoadfilePtr
->Private
->Handle
,
1459 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1460 EFI_NATIVE_INTERFACE
,
1464 DEBUG ((EFI_D_WARN
, "\nLoadFile() Callback install status == %xh", Status
));
1466 RemoveCallback
= (BOOLEAN
) (Status
== EFI_SUCCESS
);
1468 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
&& LoadfilePtr
->Private
->EfiBc
.Mode
->Started
) {
1469 NewMakeCallback
= TRUE
;
1470 LoadfilePtr
->Private
->EfiBc
.SetParameters (
1471 &LoadfilePtr
->Private
->EfiBc
,
1480 DEBUG ((EFI_D_WARN
, "\nLoadFile() Callback check status == %xh", Status
));
1483 // Check for starting or for continuing after already getting
1486 if (LoadfilePtr
->Private
->FileSize
== 0) {
1488 Status
= LoadfileStart (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1490 if (sizeof (UINTN
) < sizeof (UINT64
) && TmpBufSz
> 0xFFFFFFFF) {
1491 *BufferSize
= 0xFFFFFFFF;
1493 *BufferSize
= (UINTN
) TmpBufSz
;
1496 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1498 // This is done so loadfile will work even if the boot manager
1499 // did not make the first call with Buffer == NULL.
1503 } else if (Buffer
== NULL
) {
1504 DEBUG ((EFI_D_WARN
, "\nLoadfile() Get buffer size"));
1507 // Continuing from previous LoadFile request. Make sure there
1508 // is a buffer and that it is big enough.
1510 *BufferSize
= LoadfilePtr
->Private
->FileSize
;
1511 Status
= EFI_BUFFER_TOO_SMALL
;
1513 DEBUG ((EFI_D_WARN
, "\nLoadFile() Download file"));
1516 // Everything looks good, try to download the file.
1518 TmpBufSz
= *BufferSize
;
1519 Status
= DownloadFile (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1522 // Next call to loadfile will start DHCP process again.
1524 LoadfilePtr
->Private
->FileSize
= 0;
1527 // If we added a callback protocol, now is the time to remove it.
1529 if (RemoveCallback
) {
1530 NewMakeCallback
= FALSE
;
1531 TempStatus
= LoadfilePtr
->Private
->EfiBc
.SetParameters (
1532 &LoadfilePtr
->Private
->EfiBc
,
1540 if (TempStatus
== EFI_SUCCESS
) {
1541 gBS
->UninstallProtocolInterface (
1542 LoadfilePtr
->Private
->Handle
,
1543 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1549 // Restore display mode and attribute
1551 if (OrigMode
!= 0) {
1552 gST
->ConOut
->SetMode (gST
->ConOut
, OrigMode
);
1555 gST
->ConOut
->SetAttribute (gST
->ConOut
, OrigAttribute
);
1560 EfiReleaseLock (&LoadfilePtr
->Lock
);
1562 DEBUG ((EFI_D_WARN
, "\nBC.Loadfile() Status == %xh\n", Status
));
1564 if (Status
== EFI_SUCCESS
) {
1567 } else if (Status
== EFI_BUFFER_TOO_SMALL
) {
1570 // Error is only displayed when we are actually trying to
1571 // download the boot image.
1573 if (Buffer
== NULL
) {
1574 return EFI_BUFFER_TOO_SMALL
;
1576 AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
1577 } else if (Status
== EFI_DEVICE_ERROR
) {
1579 AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
1580 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
1582 AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
1583 } else if (Status
== EFI_NO_MEDIA
) {
1585 AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
1586 } else if (Status
== EFI_NO_RESPONSE
) {
1588 AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
1589 } else if (Status
== EFI_TIMEOUT
) {
1591 AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
1592 } else if (Status
== EFI_ABORTED
) {
1594 AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
1595 } else if (Status
== EFI_ICMP_ERROR
) {
1597 AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
1599 if ((LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) && LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpErrorReceived
) {
1601 "PXE-E98: Type: %xh Code: %xh ",
1602 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
,
1603 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
1606 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
) {
1608 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
) {
1609 case 0x00: /* net unreachable */
1610 AsciiPrint ("Net unreachable");
1613 case 0x01: /* host unreachable */
1614 AsciiPrint ("Host unreachable");
1617 case 0x02: /* protocol unreachable */
1618 AsciiPrint ("Protocol unreachable");
1621 case 0x03: /* port unreachable */
1622 AsciiPrint ("Port unreachable");
1625 case 0x04: /* Fragmentation needed */
1626 AsciiPrint ("Fragmentation needed");
1629 case 0x05: /* Source route failed */
1630 AsciiPrint ("Source route failed");
1639 } else if (Status
== EFI_TFTP_ERROR
) {
1641 AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
1643 if ((LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) && (LoadfilePtr
->Private
->EfiBc
.Mode
->TftpErrorReceived
)) {
1645 "PXE-E98: Code: %xh %a\n",
1646 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorCode
,
1647 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorString
1651 AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status
);
1654 LoadfilePtr
->Private
->EfiBc
.Stop (&LoadfilePtr
->Private
->EfiBc
);