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
) {
378 BufferSize
= sizeof Buffer
;
380 Status
= Private
->EfiBc
.UdpRead (
382 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
383 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
384 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
386 NULL
, /* dest port */
398 if (Key
.ScanCode
== 0) {
399 switch (Key
.UnicodeChar
) {
414 switch (Key
.ScanCode
) {
431 gBS
->CloseEvent (SecondsEvent
);
432 gBS
->CloseEvent (TimeoutEvent
);
434 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecColumn
, SecRow
);
442 Display one menu item.
444 @param MenuItemPtr Pointer to PXE menu item
453 PXE_BOOT_MENU_ENTRY
*MenuItemPtr
459 Length
= (UINT8
) MIN (70, MenuItemPtr
->DataLen
);
460 SaveChar
= MenuItemPtr
->Data
[Length
];
462 MenuItemPtr
->Data
[Length
] = 0;
463 AsciiPrint (" %a\n", MenuItemPtr
->Data
);
464 MenuItemPtr
->Data
[Length
] = SaveChar
;
469 Display and process menu.
471 @param Private Pointer to PxeBc interface
472 @param RxBufferPtr Pointer to receive buffer
481 PXE_BASECODE_DEVICE
*Private
,
482 DHCP_RECEIVE_BUFFER
*RxBufferPtr
485 PXE_OP_DISCOVERY_CONTROL
*DiscoveryControlPtr
;
486 PXE_BOOT_MENU_ENTRY
*MenuItemPtrs
[MAX_MENULIST
];
503 DEBUG ((DEBUG_WARN
, "\nDoMenu() Enter."));
505 /* see if we have a menu/prompt */
506 if (!(RxBufferPtr
->OpAdds
.Status
& DISCOVER_TYPE
)) {
509 "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
510 RxBufferPtr
->OpAdds
.Status
)
516 DiscoveryControlPtr
= (PXE_OP_DISCOVERY_CONTROL
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_DISCOVERY_CONTROL_IX
- 1];
519 // if not USE_BOOTFILE or no bootfile given, must have menu stuff
521 if ((DiscoveryControlPtr
->ControlBits
& USE_BOOTFILE
) && RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
522 DEBUG ((DEBUG_WARN
, "\nDoMenu() DHCP w/ bootfile. "));
526 // do prompt & menu if necessary
528 Status
= DoPrompt (Private
, (PXE_OP_BOOT_PROMPT
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_PROMPT_IX
- 1]);
530 if (Status
== LOCAL_BOOT
) {
531 DEBUG ((DEBUG_WARN
, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
536 Ptr
.BytePtr
= (UINT8
*) RxBufferPtr
->OpAdds
.PxeOptAdds
[VEND_PXE_BOOT_MENU_IX
- 1];
538 MenuLth
= Ptr
.MenuPtr
->Header
.Length
;
539 Ptr
.CurrentMenuItemPtr
= Ptr
.MenuPtr
->MenuItem
;
542 // build menu items array
544 for (Longest
= NumMenuItems
= Index
= 0; Index
< MenuLth
&& NumMenuItems
< MAX_MENULIST
;) {
547 lth
= Ptr
.CurrentMenuItemPtr
->DataLen
+ sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
);
549 MenuItemPtrs
[NumMenuItems
++] = Ptr
.CurrentMenuItemPtr
;
555 if ((Longest
= lth
) > 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))) {
556 Longest
= 70 + (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
));
564 if (Status
!= AUTO_SELECT
) {
567 SetMem (BlankBuf
, sizeof BlankBuf
, ' ');
568 BlankBuf
[Longest
+ 5 - (sizeof (*Ptr
.CurrentMenuItemPtr
) - sizeof (Ptr
.CurrentMenuItemPtr
->Data
))] = 0;
574 for (Index
= 0; Index
< NumMenuItems
; ++Index
) {
575 PrintMenuItem (MenuItemPtrs
[Index
]);
578 TopRow
= gST
->ConOut
->Mode
->CursorRow
- NumMenuItems
;
581 // now wait for a selection
586 // highlight selection
591 NewSelected
= Selected
;
594 // highlight selected row
596 gST
->ConOut
->SetAttribute (
598 EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
)
600 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ Selected
);
602 AsciiPrint (" --->%a\r", BlankBuf
);
604 PrintMenuItem (MenuItemPtrs
[Selected
]);
605 gST
->ConOut
->SetAttribute (
607 EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
)
609 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, TopRow
+ NumMenuItems
);
612 // wait for a keystroke
614 while (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
) == EFI_NOT_READY
) {
618 TmpBufLen
= sizeof TmpBuf
;
620 Private
->EfiBc
.UdpRead (
622 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
|
623 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
|
624 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT
,
626 NULL
, /* dest port */
637 switch (Key
.UnicodeChar
) {
639 Key
.ScanCode
= SCAN_ESC
;
642 case Ctl ('j'): /* linefeed */
643 case Ctl ('m'): /* return */
647 case Ctl ('i'): /* tab */
651 Key
.ScanCode
= SCAN_DOWN
;
654 case Ctl ('h'): /* backspace */
657 Key
.ScanCode
= SCAN_UP
;
665 switch (Key
.ScanCode
) {
676 if (++NewSelected
== NumMenuItems
) {
689 NewSelected
= NumMenuItems
- 1;
696 /* unhighlight last selected row */
697 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 5, TopRow
+ Selected
);
699 AsciiPrint ("%a\r", BlankBuf
);
701 PrintMenuItem (MenuItemPtrs
[Selected
]);
703 Selected
= NewSelected
;
707 SaveNumRte
= Private
->EfiBc
.Mode
->RouteTableEntries
;
709 Type
= NTOHS (MenuItemPtrs
[Selected
]->Type
);
712 DEBUG ((DEBUG_WARN
, "\nDoMenu() Local boot selected. "));
716 AsciiPrint ("Discover");
718 Status
= Private
->EfiBc
.Discover (
722 (BOOLEAN
) (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
),
726 if (EFI_ERROR (Status
)) {
727 AsciiPrint ("\r \r");
731 "\nDoMenu() Return w/ %xh (%r).",
739 AsciiPrint ("\rBOOT_SERVER_IP: ");
740 PrintIpv4 ((UINT8
*) &Private
->ServerIp
);
742 for (Index
= SaveNumRte
; Index
< Private
->EfiBc
.Mode
->RouteTableEntries
; ++Index
) {
743 if ((Index
% 3) == 0) {
744 AsciiPrint ("\r\nGATEWAY IP:");
748 PrintIpv4 ((UINT8
*) &Private
->EfiBc
.Mode
->RouteTable
[Index
].GwAddr
);
754 DEBUG ((DEBUG_WARN
, "\nDoMenu() Return w/ EFI_SUCCESS. "));
761 Get value 8- or 16-bit value from DHCP option.
763 @param OpPtr Pointer to DHCP option
765 @return Value from DHCP option
771 DHCPV4_OP_STRUCT
*OpPtr
774 if (OpPtr
->Header
.Length
== 1) {
775 return OpPtr
->Data
[0];
777 return NTOHS (OpPtr
->Data
);
783 Locate opcode in buffer.
785 @param BufferPtr Pointer to buffer
786 @param BufferLen Length of buffer
787 @param OpCode Option number
789 @return Pointer to opcode, may be NULL
800 if (BufferPtr
== NULL
) {
804 while (BufferLen
!= 0) {
805 if (*BufferPtr
== OpCode
) {
809 switch (*BufferPtr
) {
819 if ((UINTN
) BufferLen
<= (UINTN
) 2 + BufferPtr
[1]) {
823 BufferLen
-= 2 + BufferPtr
[1];
824 BufferPtr
+= 2 + BufferPtr
[1];
832 Find option in packet
834 @param PacketPtr Pointer to packet
835 @param OpCode option number
837 @return Pointer to option in packet
843 EFI_PXE_BASE_CODE_PACKET
*PacketPtr
,
849 UINT8
*OptionBufferPtr
;
858 // Figure size of DHCP option space.
860 OptionBufferPtr
= _PxeBcFindOpt (
861 PacketPtr
->Dhcpv4
.DhcpOptions
,
863 OP_DHCP_MAX_MESSAGE_SZ
866 if (OptionBufferPtr
!= NULL
) {
867 if (OptionBufferPtr
[1] == 2) {
870 CopyMem (&n
, &OptionBufferPtr
[2], 2);
871 PacketLen
= HTONS (n
);
873 if (PacketLen
< sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET
)) {
876 PacketLen
-= (PacketPtr
->Dhcpv4
.DhcpOptions
- &PacketPtr
->Dhcpv4
.BootpOpcode
) + 28;
881 // Look for option overloading.
883 OptionBufferPtr
= _PxeBcFindOpt (
884 PacketPtr
->Dhcpv4
.DhcpOptions
,
886 OP_DHCP_OPTION_OVERLOAD
889 if (OptionBufferPtr
!= NULL
) {
890 if (OptionBufferPtr
[1] == 1) {
891 Overload
= OptionBufferPtr
[2];
895 // Look for caller's option.
897 OptionBufferPtr
= _PxeBcFindOpt (
898 PacketPtr
->Dhcpv4
.DhcpOptions
,
903 if (OptionBufferPtr
!= NULL
) {
904 return OptionBufferPtr
;
907 if (Overload
& OVLD_FILE
) {
908 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpBootFile
, 128, OpCode
);
910 if (OptionBufferPtr
!= NULL
) {
911 return OptionBufferPtr
;
915 if (Overload
& OVLD_SRVR_NAME
) {
916 OptionBufferPtr
= _PxeBcFindOpt (PacketPtr
->Dhcpv4
.BootpSrvName
, 64, OpCode
);
918 if (OptionBufferPtr
!= NULL
) {
919 return OptionBufferPtr
;
928 Download file into buffer
930 @param Private Pointer to PxeBc interface
931 @param BufferSize pointer to size of download
933 @param Buffer Pointer to buffer
935 @return EFI_BUFFER_TOO_SMALL -
936 @return EFI_NOT_FOUND -
937 @return EFI_PROTOCOL_ERROR -
943 IN PXE_BASECODE_DEVICE
*Private
,
944 IN OUT UINT64
*BufferSize
,
948 EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo
;
949 EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode
;
950 DHCP_RECEIVE_BUFFER
*RxBuf
;
954 RxBuf
= (DHCP_RECEIVE_BUFFER
*) Private
->BootServerReceiveBuffer
;
957 DEBUG ((EFI_D_WARN
, "\nDownloadFile() Enter."));
959 if (Buffer
== NULL
|| *BufferSize
== 0 || *BufferSize
< Private
->FileSize
) {
960 if (Private
->FileSize
!= 0) {
961 *BufferSize
= Private
->FileSize
;
962 return EFI_BUFFER_TOO_SMALL
;
965 AsciiPrint ("\nTSize");
967 OpCode
= EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
;
968 } else if (RxBuf
->OpAdds
.Status
& WfM11a_TYPE
) {
969 OpCode
= EFI_PXE_BASE_CODE_MTFTP_READ_FILE
;
971 ZeroMem (&MtftpInfo
, sizeof MtftpInfo
);
973 *(IPV4_ADDR
*) &MtftpInfo
.MCastIp
= *(IPV4_ADDR
*) RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_IP
- 1]->Data
;
977 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_CPORT
- 1]->Data
,
978 sizeof MtftpInfo
.CPort
983 RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_SPORT
- 1]->Data
,
984 sizeof MtftpInfo
.SPort
987 MtftpInfo
.ListenTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_TMOUT
- 1]);
989 MtftpInfo
.TransmitTimeout
= GetValue (RxBuf
->OpAdds
.PxeOptAdds
[VEND_PXE_MTFTP_DELAY
- 1]);
991 AsciiPrint ("\nMTFTP");
993 AsciiPrint ("\nTFTP");
995 OpCode
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
998 Private
->FileSize
= 0;
1000 RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
[RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Header
.Length
] = 0;
1002 Status
= Private
->EfiBc
.Mtftp (
1010 (UINT8
*) RxBuf
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]->Data
,
1015 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_BUFFER_TOO_SMALL
) {
1016 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #1 %Xh", Status
));
1020 if (sizeof (UINTN
) < sizeof (UINT64
) && *BufferSize
> 0xFFFFFFFF) {
1021 Private
->FileSize
= 0xFFFFFFFF;
1023 Private
->FileSize
= (UINTN
) *BufferSize
;
1026 if (OpCode
== EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) {
1027 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #2"));
1028 return EFI_BUFFER_TOO_SMALL
;
1031 if (EFI_ERROR (Status
)) {
1032 DEBUG ((DEBUG_WARN
, "\nDownloadFile() Exit #3 %Xh", Status
));
1036 if (Private
->EfiBc
.Mode
->BisSupported
&& Private
->EfiBc
.Mode
->BisDetected
&& Private
->EfiBc
.Mode
->PxeBisReplyReceived
) {
1037 UINT64 CredentialLen
;
1038 UINT8 CredentialFilename
[256];
1040 VOID
*CredentialBuffer
;
1043 // Get name of credential file. It may be in the BOOTP
1044 // bootfile field or a DHCP option.
1046 ZeroMem (CredentialFilename
, sizeof CredentialFilename
);
1048 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_DHCP_BOOTFILE
);
1052 /* No credential filename */
1053 return EFI_NOT_FOUND
;
1056 CopyMem (CredentialFilename
, &op
[2], op
[1]);
1058 if (Private
->EfiBc
.Mode
->PxeBisReply
.Dhcpv4
.BootpBootFile
[0] == 0) {
1059 /* No credential filename */
1060 return EFI_NOT_FOUND
;
1063 CopyMem (CredentialFilename
, &op
[2], 128);
1066 // Get size of credential file. It may be available as a
1067 // DHCP option. If not, use the TFTP get file size.
1071 op
= PxeBcFindDhcpOpt (&Private
->EfiBc
.Mode
->PxeBisReply
, OP_BOOT_FILE_SZ
);
1075 * This is actually the size of the credential file
1076 * buffer. The actual credential file size will be
1077 * returned when we download the file.
1082 CopyMem (&n
, &op
[2], 2);
1083 CredentialLen
= HTONS (n
) * 512;
1087 if (CredentialLen
== 0) {
1090 Status
= Private
->EfiBc
.Mtftp (
1092 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1103 if (EFI_ERROR (Status
)) {
1107 if (CredentialLen
== 0) {
1109 // %%TBD -- EFI error for invalid credential
1112 return EFI_PROTOCOL_ERROR
;
1116 // Allocate credential file buffer.
1118 Status
= gBS
->AllocatePool (
1119 EfiBootServicesData
,
1120 (UINTN
) CredentialLen
,
1124 if (EFI_ERROR (Status
)) {
1128 // Download credential file.
1132 Status
= Private
->EfiBc
.Mtftp (
1134 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1145 if (EFI_ERROR (Status
)) {
1146 gBS
->FreePool (CredentialBuffer
);
1150 // Verify credentials.
1152 if (PxebcBisVerify (Private
, Buffer
, Private
->FileSize
, CredentialBuffer
, (UINTN
) CredentialLen
)) {
1153 Status
= EFI_SUCCESS
;
1156 // %%TBD -- An EFI error code for failing credential verification.
1158 Status
= EFI_PROTOCOL_ERROR
;
1161 gBS
->FreePool (CredentialBuffer
);
1169 Start PXE DHCP. Get DHCP and proxyDHCP information.
1170 Display remote boot menu and prompt. Select item from menu.
1172 @param Private Pointer to PxeBc interface
1173 @param BufferSize Pointer to download buffer
1175 @param Buffer Pointer to download buffer
1178 @retval EFI_NOT_READY
1184 IN PXE_BASECODE_DEVICE
*Private
,
1185 IN OUT UINT64
*BufferSize
,
1189 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1190 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
1191 EFI_SIMPLE_NETWORK_MODE
*SnpMode
;
1195 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Enter."));
1198 // Try to start BaseCode, for now only IPv4 is supported
1199 // so don't try to start using IPv6.
1201 Status
= Private
->EfiBc
.Start (&Private
->EfiBc
, FALSE
);
1203 if (EFI_ERROR (Status
)) {
1204 if (Status
!= EFI_ALREADY_STARTED
) {
1205 DEBUG ((DEBUG_NET
, "\nLoadfileStart() Exit BC.Start() == %xh", Status
));
1210 // Get pointers to PXE mode structure, SNP protocol structure
1211 // and SNP mode structure.
1213 PxeBcMode
= Private
->EfiBc
.Mode
;
1214 Snp
= Private
->SimpleNetwork
;
1215 SnpMode
= Snp
->Mode
;
1218 // Display client MAC address, like 16-bit PXE ROMs
1220 AsciiPrint ("\nCLIENT MAC ADDR: ");
1226 hlen
= SnpMode
->HwAddressSize
;
1228 for (Index
= 0; Index
< hlen
; ++Index
) {
1229 AsciiPrint ("%02x ", SnpMode
->CurrentAddress
.Addr
[Index
]);
1233 AsciiPrint ("\nDHCP");
1235 Status
= Private
->EfiBc
.Dhcp (&Private
->EfiBc
, TRUE
);
1237 if (EFI_ERROR (Status
)) {
1238 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status
));
1239 AsciiPrint ("\r \r");
1243 ShowMyInfo (Private
);
1245 RxBuf
= PxeBcMode
->ProxyOfferReceived
? &PXE_OFFER_BUFFER
: &DHCPV4_ACK_BUFFER
;
1246 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
1248 Status
= DoMenu (Private
, RxBufferPtr
);
1250 if (Status
== EFI_SUCCESS
) {
1252 // did a discovery - take info from discovery packet
1254 RxBuf
= &PXE_ACK_BUFFER
;
1255 } else if (Status
== NO_MENU
) {
1257 // did not do a discovery - take info from rxbuf
1259 Private
->ServerIp
.Addr
[0] = RxBufferPtr
->u
.Dhcpv4
.siaddr
;
1261 if (!(Private
->ServerIp
.Addr
[0])) {
1262 *(IPV4_ADDR
*) &Private
->ServerIp
= *(IPV4_ADDR
*) RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_SERVER_IP_IX
- 1]->Data
;
1265 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit DoMenu() == %Xh", Status
));
1269 if (!RxBufferPtr
->OpAdds
.PktOptAdds
[OP_DHCP_BOOTFILE_IX
- 1]) {
1270 DEBUG ((DEBUG_WARN
, "\nLoadfileStart() Exit Not ready?"));
1271 return EFI_NOT_READY
;
1274 // check for file size option sent
1276 if (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]) {
1277 Private
->FileSize
= 512 * NTOHS (RxBufferPtr
->OpAdds
.PktOptAdds
[OP_BOOT_FILE_SZ_IX
- 1]->Data
);
1280 Private
->BootServerReceiveBuffer
= RxBufferPtr
;
1282 Status
= DownloadFile (Private
, BufferSize
, Buffer
);
1286 "\nLoadfileStart() Exit. DownloadFile() = %Xh",
1295 Loadfile interface for PxeBc interface
1297 @param This Pointer to Loadfile interface
1298 @param FilePath Not used and not checked
1299 @param BootPolicy Must be TRUE
1300 @param BufferSize Pointer to buffer size
1301 @param Buffer Pointer to download buffer or
1304 @return EFI_INVALID_PARAMETER -
1305 @return EFI_UNSUPPORTED -
1306 @return EFI_SUCCESS -
1307 @return EFI_BUFFER_TOO_SMALL -
1313 IN EFI_LOAD_FILE_PROTOCOL
*This
,
1314 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
1315 IN BOOLEAN BootPolicy
,
1316 IN OUT UINTN
*BufferSize
,
1320 LOADFILE_DEVICE
*LoadfilePtr
;
1323 INT32 OrigAttribute
;
1324 BOOLEAN RemoveCallback
;
1325 BOOLEAN NewMakeCallback
;
1327 EFI_STATUS TempStatus
;
1330 // The following line is only used for passing ICC build.
1332 DEBUG ((EFI_D_INFO
, "FilePath = %x\n", FilePath
));
1337 OrigMode
= gST
->ConOut
->Mode
->Mode
;
1338 OrigAttribute
= gST
->ConOut
->Mode
->Attribute
;
1339 RemoveCallback
= FALSE
;
1341 AsciiPrint ("Running LoadFile()\n");
1344 // Resolve Warning 4 unreferenced parameter problem
1349 // If either if these parameters are NULL, we cannot continue.
1351 if (This
== NULL
|| BufferSize
== NULL
) {
1352 DEBUG ((DEBUG_WARN
, "\nLoadFile() This or BufferSize == NULL"));
1353 return EFI_INVALID_PARAMETER
;
1356 // We only support BootPolicy == TRUE
1359 DEBUG ((DEBUG_WARN
, "\nLoadFile() BootPolicy == FALSE"));
1360 return EFI_UNSUPPORTED
;
1363 // Get pointer to LoadFile protocol structure.
1365 LoadfilePtr
= CR (This
, LOADFILE_DEVICE
, LoadFile
, LOADFILE_DEVICE_SIGNATURE
);
1367 if (LoadfilePtr
== NULL
) {
1370 "\nLoadFile() Could not get pointer to LoadFile structure")
1372 return EFI_INVALID_PARAMETER
;
1377 EfiAcquireLock (&LoadfilePtr
->Lock
);
1380 // Set console output mode and display attribute
1382 if (OrigMode
!= 0) {
1383 gST
->ConOut
->SetMode (gST
->ConOut
, 0);
1386 gST
->ConOut
->SetAttribute (
1388 EFI_TEXT_ATTR (EFI_LIGHTGRAY
,EFI_BLACK
)
1392 // See if BaseCode already has a Callback protocol attached.
1393 // If there is none, attach our own Callback protocol.
1395 Status
= gBS
->HandleProtocol (
1396 LoadfilePtr
->Private
->Handle
,
1397 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1398 (VOID
*) &LoadfilePtr
->Private
->CallbackProtocolPtr
1401 if (Status
== EFI_SUCCESS
) {
1403 // There is already a callback routine. Do nothing.
1405 DEBUG ((DEBUG_WARN
, "\nLoadFile() BC callback exists."));
1407 } else if (Status
== EFI_UNSUPPORTED
) {
1409 // No BaseCode Callback protocol found. Add our own.
1411 Status
= gBS
->InstallProtocolInterface (
1412 &LoadfilePtr
->Private
->Handle
,
1413 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1414 EFI_NATIVE_INTERFACE
,
1418 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback install status == %xh", Status
));
1420 RemoveCallback
= (BOOLEAN
) (Status
== EFI_SUCCESS
);
1422 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
&& LoadfilePtr
->Private
->EfiBc
.Mode
->Started
) {
1423 NewMakeCallback
= TRUE
;
1424 LoadfilePtr
->Private
->EfiBc
.SetParameters (
1425 &LoadfilePtr
->Private
->EfiBc
,
1435 DEBUG ((DEBUG_WARN
, "\nLoadFile() Callback check status == %xh", Status
));
1438 // Check for starting or for continuing after already getting
1441 if (LoadfilePtr
->Private
->FileSize
== 0) {
1443 Status
= LoadfileStart (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1445 if (sizeof (UINTN
) < sizeof (UINT64
) && TmpBufSz
> 0xFFFFFFFF) {
1446 *BufferSize
= 0xFFFFFFFF;
1448 *BufferSize
= (UINTN
) TmpBufSz
;
1451 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1453 // This is done so loadfile will work even if the boot manager
1454 // did not make the first call with Buffer == NULL.
1458 } else if (Buffer
== NULL
) {
1459 DEBUG ((DEBUG_WARN
, "\nLoadfile() Get buffer size"));
1462 // Continuing from previous LoadFile request. Make sure there
1463 // is a buffer and that it is big enough.
1465 *BufferSize
= LoadfilePtr
->Private
->FileSize
;
1466 Status
= EFI_BUFFER_TOO_SMALL
;
1468 DEBUG ((DEBUG_WARN
, "\nLoadFile() Download file"));
1471 // Everything looks good, try to download the file.
1473 TmpBufSz
= *BufferSize
;
1474 Status
= DownloadFile (LoadfilePtr
->Private
, &TmpBufSz
, Buffer
);
1477 // Next call to loadfile will start DHCP process again.
1479 LoadfilePtr
->Private
->FileSize
= 0;
1482 // If we added a callback protocol, now is the time to remove it.
1484 if (RemoveCallback
) {
1485 NewMakeCallback
= FALSE
;
1486 TempStatus
= LoadfilePtr
->Private
->EfiBc
.SetParameters (
1487 &LoadfilePtr
->Private
->EfiBc
,
1495 if (TempStatus
== EFI_SUCCESS
) {
1496 gBS
->UninstallProtocolInterface (
1497 LoadfilePtr
->Private
->Handle
,
1498 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1504 // Restore display mode and attribute
1506 if (OrigMode
!= 0) {
1507 gST
->ConOut
->SetMode (gST
->ConOut
, OrigMode
);
1510 gST
->ConOut
->SetAttribute (gST
->ConOut
, OrigAttribute
);
1515 EfiReleaseLock (&LoadfilePtr
->Lock
);
1517 DEBUG ((DEBUG_WARN
, "\nBC.Loadfile() Status == %xh\n", Status
));
1519 if (Status
== EFI_SUCCESS
) {
1522 } else if (Status
== EFI_BUFFER_TOO_SMALL
) {
1524 // Error is only displayed when we are actually trying to
1525 // download the boot image.
1527 if (Buffer
== NULL
) {
1528 return EFI_BUFFER_TOO_SMALL
;
1531 AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
1533 } else if (Status
== EFI_DEVICE_ERROR
) {
1534 AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
1536 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
1537 AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
1539 } else if (Status
== EFI_NO_MEDIA
) {
1540 AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
1542 } else if (Status
== EFI_NO_RESPONSE
) {
1543 AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
1545 } else if (Status
== EFI_TIMEOUT
) {
1546 AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
1548 } else if (Status
== EFI_ABORTED
) {
1549 AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
1551 } else if (Status
== EFI_ICMP_ERROR
) {
1552 AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
1554 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1555 if (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpErrorReceived
) {
1558 "PXE-E98: Type: %xh Code: %xh ",
1559 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
,
1560 LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
1563 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Type
) {
1565 switch (LoadfilePtr
->Private
->EfiBc
.Mode
->IcmpError
.Code
) {
1566 case 0x00: /* net unreachable */
1567 AsciiPrint ("Net unreachable");
1570 case 0x01: /* host unreachable */
1571 AsciiPrint ("Host unreachable");
1574 case 0x02: /* protocol unreachable */
1575 AsciiPrint ("Protocol unreachable");
1578 case 0x03: /* port unreachable */
1579 AsciiPrint ("Port unreachable");
1582 case 0x04: /* Fragmentation needed */
1583 AsciiPrint ("Fragmentation needed");
1586 case 0x05: /* Source route failed */
1587 AsciiPrint ("Source route failed");
1598 } else if (Status
== EFI_TFTP_ERROR
) {
1599 AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
1601 if (LoadfilePtr
->Private
->EfiBc
.Mode
!= NULL
) {
1602 if (LoadfilePtr
->Private
->EfiBc
.Mode
->TftpErrorReceived
) {
1604 "PXE-E98: Code: %xh %a\n",
1605 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorCode
,
1606 LoadfilePtr
->Private
->EfiBc
.Mode
->TftpError
.ErrorString
1612 AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status
);
1615 LoadfilePtr
->Private
->EfiBc
.Stop (&LoadfilePtr
->Private
->EfiBc
);