3 Copyright (c) 2006, 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 Status
= gBS
->AllocatePool (
1172 EfiBootServicesData
,
1173 (UINTN
) CredentialLen
,
1177 if (EFI_ERROR (Status
)) {
1181 // Download credential file.
1185 Status
= Private
->EfiBc
.Mtftp (
1187 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1198 if (EFI_ERROR (Status
)) {
1199 gBS
->FreePool (CredentialBuffer
);
1203 // Verify credentials.
1205 if (PxebcBisVerify (Private
, Buffer
, Private
->FileSize
, CredentialBuffer
, (UINTN
) CredentialLen
)) {
1206 Status
= EFI_SUCCESS
;
1209 // %%TBD -- An EFI error code for failing credential verification.
1211 Status
= EFI_PROTOCOL_ERROR
;
1214 gBS
->FreePool (CredentialBuffer
);
1223 IN PXE_BASECODE_DEVICE
*Private
,
1224 IN OUT UINT64
*BufferSize
,
1229 Routine Description:
1231 Start PXE DHCP. Get DHCP and proxyDHCP information.
1232 Display remote boot menu and prompt. Select item from menu.
1236 Private - Pointer to PxeBc interface
1237 BufferSize - Pointer to download buffer size
1238 Buffer - Pointer to download buffer
1247 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1248 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
1249 EFI_SIMPLE_NETWORK_MODE
*SnpMode
;
1253 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Enter."));
1256 // Try to start BaseCode, for now only IPv4 is supported
1257 // so don't try to start using IPv6.
1259 Status
= Private
->EfiBc
.Start (&Private
->EfiBc
, FALSE
);
1261 if (EFI_ERROR (Status
)) {
1262 if (Status
!= EFI_ALREADY_STARTED
) {
1263 DEBUG ((EFI_D_NET
, "\nLoadfileStart() Exit BC.Start() == %xh", Status
));
1268 // Get pointers to PXE mode structure, SNP protocol structure
1269 // and SNP mode structure.
1271 PxeBcMode
= Private
->EfiBc
.Mode
;
1272 Snp
= Private
->SimpleNetwork
;
1273 SnpMode
= Snp
->Mode
;
1276 // Display client MAC address, like 16-bit PXE ROMs
1278 AsciiPrint ("\nCLIENT MAC ADDR: ");
1284 hlen
= SnpMode
->HwAddressSize
;
1286 for (Index
= 0; Index
< hlen
; ++Index
) {
1287 AsciiPrint ("%02x ", SnpMode
->CurrentAddress
.Addr
[Index
]);
1291 AsciiPrint ("\nDHCP");
1293 Status
= Private
->EfiBc
.Dhcp (&Private
->EfiBc
, TRUE
);
1295 if (EFI_ERROR (Status
)) {
1296 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status
));
1297 AsciiPrint ("\r \r");
1301 ShowMyInfo (Private
);
1303 RxBuf
= PxeBcMode
->ProxyOfferReceived
? &PXE_OFFER_BUFFER
: &DHCPV4_ACK_BUFFER
;
1304 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
1306 Status
= DoMenu (Private
, RxBufferPtr
);
1308 if (Status
== EFI_SUCCESS
) {
1310 // did a discovery - take info from discovery packet
1312 RxBuf
= &PXE_ACK_BUFFER
;
1313 } else if (Status
== NO_MENU
) {
1315 // did not do a discovery - take info from rxbuf
1317 Private
->ServerIp
.Addr
[0] = RxBufferPtr
->u
.Dhcpv4
.siaddr
;
1319 if (!(Private
->ServerIp
.Addr
[0])) {
1320 *(IPV4_ADDR
*) &Private
->ServerIp
= *(IPV4_ADDR
*) RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1]->Data
;
1323 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Exit DoMenu() == %Xh", Status
));
1327 if (!RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
1328 DEBUG ((EFI_D_WARN
, "\nLoadfileStart() Exit Not ready?"));
1329 return EFI_NOT_READY
;
1332 // check for file size option sent
1334 if (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]) {
1335 Private
->FileSize
= 512 * NTOHS (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]->Data
);
1338 Private
->BootServerReceiveBuffer
= RxBufferPtr
;
1340 Status
= DownloadFile (Private
, BufferSize
, Buffer
);
1344 "\nLoadfileStart() Exit. DownloadFile() = %Xh",
1354 IN EFI_LOAD_FILE_PROTOCOL
*This
,
1355 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
1356 IN BOOLEAN BootPolicy
,
1357 IN OUT UINTN
*BufferSize
,
1362 Routine Description:
1364 Loadfile interface for PxeBc interface
1368 This - Pointer to Loadfile interface
1369 FilePath - Not used and not checked
1370 BootPolicy - Must be TRUE
1371 BufferSize - Pointer to buffer size
1372 Buffer - Pointer to download buffer or NULL
1376 EFI_INVALID_PARAMETER -
1379 EFI_BUFFER_TOO_SMALL -
1383 LOADFILE_DEVICE
*LoadfilePtr
;
1386 INT32 OrigAttribute
;
1387 BOOLEAN RemoveCallback
;
1388 BOOLEAN NewMakeCallback
;
1390 EFI_STATUS TempStatus
;
1394 OrigMode
= gST
->ConOut
->Mode
->Mode
;
1395 OrigAttribute
= gST
->ConOut
->Mode
->Attribute
;
1396 RemoveCallback
= FALSE
;
1398 AsciiPrint ("Running LoadFile()\n");
1401 // If either if these parameters are NULL, we cannot continue.
1403 if (This
== NULL
|| BufferSize
== NULL
) {
1404 DEBUG ((EFI_D_WARN
, "\nLoadFile() This or BufferSize == NULL"));
1405 return EFI_INVALID_PARAMETER
;
1408 // We only support BootPolicy == TRUE
1411 DEBUG ((EFI_D_WARN
, "\nLoadFile() BootPolicy == FALSE"));
1412 return EFI_UNSUPPORTED
;
1415 // Get pointer to LoadFile protocol structure.
1417 LoadfilePtr
= CR (This
, LOADFILE_DEVICE
, LoadFile
, LOADFILE_DEVICE_SIGNATURE
);
1419 if (LoadfilePtr
== NULL
) {
1422 "\nLoadFile() Could not get pointer to LoadFile structure")
1424 return EFI_INVALID_PARAMETER
;
1429 EfiAcquireLock (&LoadfilePtr
->Lock
);
1432 // Set console output mode and display attribute
1434 if (OrigMode
!= 0) {
1435 gST
->ConOut
->SetMode (gST
->ConOut
, 0);
1438 gST
->ConOut
->SetAttribute (
1440 EFI_TEXT_ATTR (EFI_LIGHTGRAY
,EFI_BLACK
)
1444 // See if BaseCode already has a Callback protocol attached.
1445 // If there is none, attach our own Callback protocol.
1447 Status
= gBS
->HandleProtocol (
1448 LoadfilePtr
->Private
->Handle
,
1449 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1450 (VOID
*) &LoadfilePtr
->Private
->CallbackProtocolPtr
1453 if (Status
== EFI_SUCCESS
) {
1455 // There is already a callback routine. Do nothing.
1457 DEBUG ((EFI_D_WARN
, "\nLoadFile() BC callback exists."));
1458 } else if (Status
== EFI_UNSUPPORTED
) {
1460 // No BaseCode Callback protocol found. Add our own.
1462 Status
= gBS
->InstallProtocolInterface (
1463 &LoadfilePtr
->Private
->Handle
,
1464 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1465 EFI_NATIVE_INTERFACE
,
1469 DEBUG ((EFI_D_WARN
, "\nLoadFile() Callback install status == %xh", Status
));
1471 RemoveCallback
= (BOOLEAN
) (Status
== EFI_SUCCESS
);
1473 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
&& LoadfilePtr
->Private
->EfiBc
.Mode
->Started
) {
1474 NewMakeCallback
= TRUE
;
1475 LoadfilePtr
->Private
->EfiBc
.SetParameters (
1476 &LoadfilePtr
->Private
->EfiBc
,
1485 DEBUG ((EFI_D_WARN
, "\nLoadFile() Callback check status == %xh", Status
));
1488 // Check for starting or for continuing after already getting
1491 if (LoadfilePtr
->Private
->FileSize
== 0) {
1493 Status
= LoadfileStart (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1495 if (sizeof (UINTN
) < sizeof (UINT64
) && TmpBufSz
> 0xFFFFFFFF) {
1496 *BufferSize
= 0xFFFFFFFF;
1498 *BufferSize
= (UINTN
) TmpBufSz
;
1501 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1503 // This is done so loadfile will work even if the boot manager
1504 // did not make the first call with Buffer == NULL.
1508 } else if (Buffer
== NULL
) {
1509 DEBUG ((EFI_D_WARN
, "\nLoadfile() Get buffer size"));
1512 // Continuing from previous LoadFile request. Make sure there
1513 // is a buffer and that it is big enough.
1515 *BufferSize
= LoadfilePtr
->Private
->FileSize
;
1516 Status
= EFI_BUFFER_TOO_SMALL
;
1518 DEBUG ((EFI_D_WARN
, "\nLoadFile() Download file"));
1521 // Everything looks good, try to download the file.
1523 TmpBufSz
= *BufferSize
;
1524 Status
= DownloadFile (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1527 // Next call to loadfile will start DHCP process again.
1529 LoadfilePtr
->Private
->FileSize
= 0;
1532 // If we added a callback protocol, now is the time to remove it.
1534 if (RemoveCallback
) {
1535 NewMakeCallback
= FALSE
;
1536 TempStatus
= LoadfilePtr
->Private
->EfiBc
.SetParameters (
1537 &LoadfilePtr
->Private
->EfiBc
,
1545 if (TempStatus
== EFI_SUCCESS
) {
1546 gBS
->UninstallProtocolInterface (
1547 LoadfilePtr
->Private
->Handle
,
1548 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1554 // Restore display mode and attribute
1556 if (OrigMode
!= 0) {
1557 gST
->ConOut
->SetMode (gST
->ConOut
, OrigMode
);
1560 gST
->ConOut
->SetAttribute (gST
->ConOut
, OrigAttribute
);
1565 EfiReleaseLock (&LoadfilePtr
->Lock
);
1567 DEBUG ((EFI_D_WARN
, "\nBC.Loadfile() Status == %xh\n", Status
));
1569 if (Status
== EFI_SUCCESS
) {
1572 } else if (Status
== EFI_BUFFER_TOO_SMALL
) {
1575 // Error is only displayed when we are actually trying to
1576 // download the boot image.
1578 if (Buffer
== NULL
) {
1579 return EFI_BUFFER_TOO_SMALL
;
1581 AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
1582 } else if (Status
== EFI_DEVICE_ERROR
) {
1584 AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
1585 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
1587 AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
1588 } else if (Status
== EFI_NO_MEDIA
) {
1590 AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
1591 } else if (Status
== EFI_NO_RESPONSE
) {
1593 AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
1594 } else if (Status
== EFI_TIMEOUT
) {
1596 AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
1597 } else if (Status
== EFI_ABORTED
) {
1599 AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
1600 } else if (Status
== EFI_ICMP_ERROR
) {
1602 AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
1604 if ((LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) && LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpErrorReceived
) {
1606 "PXE-E98: Type: %xh Code: %xh ",
1607 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
,
1608 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
1611 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
) {
1613 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
) {
1614 case 0x00: /* net unreachable */
1615 AsciiPrint ("Net unreachable");
1618 case 0x01: /* host unreachable */
1619 AsciiPrint ("Host unreachable");
1622 case 0x02: /* protocol unreachable */
1623 AsciiPrint ("Protocol unreachable");
1626 case 0x03: /* port unreachable */
1627 AsciiPrint ("Port unreachable");
1630 case 0x04: /* Fragmentation needed */
1631 AsciiPrint ("Fragmentation needed");
1634 case 0x05: /* Source route failed */
1635 AsciiPrint ("Source route failed");
1644 } else if (Status
== EFI_TFTP_ERROR
) {
1646 AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
1648 if ((LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) && (LoadfilePtr
->Private
->EfiBc
.Mode
->TftpErrorReceived
)) {
1650 "PXE-E98: Code: %xh %a\n",
1651 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorCode
,
1652 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorString
1656 AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status
);
1659 LoadfilePtr
->Private
->EfiBc
.Stop (&LoadfilePtr
->Private
->EfiBc
);