VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c@ 80721

Last change on this file since 80721 was 80721, checked in by vboxsync, 5 years ago

Devices/EFI/FirmwareNew: Start upgrade process to edk2-stable201908 (compiles on Windows and works to some extent), bugref:4643

  • Property svn:eol-style set to native
File size: 35.3 KB
Line 
1/** @file
2 Boot functions implementation for UefiPxeBc Driver.
3
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9**/
10
11#include "PxeBcImpl.h"
12
13
14/**
15 Display the string of the boot item.
16
17 If the length of the boot item string beyond 70 Char, just display 70 Char.
18
19 @param[in] Str The pointer to the string.
20 @param[in] Len The length of the string.
21
22**/
23VOID
24PxeBcDisplayBootItem (
25 IN UINT8 *Str,
26 IN UINT8 Len
27 )
28{
29 UINT8 Tmp;
30
31 //
32 // Cut off the chars behind 70th.
33 //
34 Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);
35 Tmp = Str[Len];
36 Str[Len] = 0;
37 AsciiPrint ("%a \n", Str);
38
39 //
40 // Restore the original 70th char.
41 //
42 Str[Len] = Tmp;
43}
44
45
46/**
47 Select and maintain the boot prompt if needed.
48
49 @param[in] Private Pointer to PxeBc private data.
50
51 @retval EFI_SUCCESS Selected boot prompt done.
52 @retval EFI_TIMEOUT Selected boot prompt timed out.
53 @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
54 @retval EFI_ABORTED User cancelled the operation.
55 @retval EFI_NOT_READY Reading the input key from the keyboard has not finish.
56
57**/
58EFI_STATUS
59PxeBcSelectBootPrompt (
60 IN PXEBC_PRIVATE_DATA *Private
61 )
62{
63 PXEBC_DHCP_PACKET_CACHE *Cache;
64 PXEBC_VENDOR_OPTION *VendorOpt;
65 EFI_PXE_BASE_CODE_MODE *Mode;
66 EFI_EVENT TimeoutEvent;
67 EFI_EVENT DescendEvent;
68 EFI_INPUT_KEY InputKey;
69 EFI_STATUS Status;
70 UINT32 OfferType;
71 UINT8 Timeout;
72 UINT8 *Prompt;
73 UINT8 PromptLen;
74 INT32 SecCol;
75 INT32 SecRow;
76
77 TimeoutEvent = NULL;
78 DescendEvent = NULL;
79 Mode = Private->PxeBc.Mode;
80 Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
81 OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
82
83 //
84 // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt.
85 //
86 if (OfferType != PxeOfferTypeProxyPxe10 && OfferType != PxeOfferTypeDhcpPxe10) {
87 return EFI_NOT_FOUND;
88 }
89
90 //
91 // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
92 //
93 ASSERT (!Mode->UsingIpv6);
94
95 VendorOpt = &Cache->Dhcp4.VendorOpt;
96 //
97 // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options,
98 // we must not consider a boot prompt or boot menu if all of the following hold:
99 // - the PXE_DISCOVERY_CONTROL tag(6) is present inside the Vendor Options(43), and has bit 3 set
100 // - a boot file name has been presented in the initial DHCP or ProxyDHCP offer packet.
101 //
102 if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
103 Cache->Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
104 return EFI_ABORTED;
105 }
106
107 if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
108 return EFI_TIMEOUT;
109 }
110
111 Timeout = VendorOpt->MenuPrompt->Timeout;
112 Prompt = VendorOpt->MenuPrompt->Prompt;
113 PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
114
115 //
116 // The valid scope of Timeout refers to PXE2.1 spec.
117 //
118 if (Timeout == 0) {
119 return EFI_TIMEOUT;
120 }
121 if (Timeout == 255) {
122 return EFI_SUCCESS;
123 }
124
125 //
126 // Create and start a timer as timeout event.
127 //
128 Status = gBS->CreateEvent (
129 EVT_TIMER,
130 TPL_CALLBACK,
131 NULL,
132 NULL,
133 &TimeoutEvent
134 );
135 if (EFI_ERROR (Status)) {
136 return Status;
137 }
138
139 Status = gBS->SetTimer (
140 TimeoutEvent,
141 TimerRelative,
142 MultU64x32 (Timeout, TICKS_PER_SECOND)
143 );
144 if (EFI_ERROR (Status)) {
145 goto ON_EXIT;
146 }
147
148 //
149 // Create and start a periodic timer as descend event by second.
150 //
151 Status = gBS->CreateEvent (
152 EVT_TIMER,
153 TPL_CALLBACK,
154 NULL,
155 NULL,
156 &DescendEvent
157 );
158 if (EFI_ERROR (Status)) {
159 goto ON_EXIT;
160 }
161
162 Status = gBS->SetTimer (
163 DescendEvent,
164 TimerPeriodic,
165 TICKS_PER_SECOND
166 );
167 if (EFI_ERROR (Status)) {
168 goto ON_EXIT;
169 }
170
171 //
172 // Display the boot item and cursor on the screen.
173 //
174 SecCol = gST->ConOut->Mode->CursorColumn;
175 SecRow = gST->ConOut->Mode->CursorRow;
176
177 PxeBcDisplayBootItem (Prompt, PromptLen);
178
179 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
180 AsciiPrint ("(%d) ", Timeout--);
181
182 Status = EFI_TIMEOUT;
183 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
184 if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
185 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
186 AsciiPrint ("(%d) ", Timeout--);
187 }
188 if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
189 gBS->Stall (10 * TICKS_PER_MS);
190 continue;
191 }
192 //
193 // Parse the input key by user.
194 // If <F8> or <Ctrl> + <M> is pressed, return success to display the boot menu.
195 //
196 if (InputKey.ScanCode == 0) {
197
198 switch (InputKey.UnicodeChar) {
199
200 case CTRL ('c'):
201 Status = EFI_ABORTED;
202 break;
203
204 case CTRL ('m'):
205 case 'm':
206 case 'M':
207 Status = EFI_SUCCESS;
208 break;
209
210 default:
211 continue;
212 }
213
214 } else {
215
216 switch (InputKey.ScanCode) {
217
218 case SCAN_F8:
219 Status = EFI_SUCCESS;
220 break;
221
222 case SCAN_ESC:
223 Status = EFI_ABORTED;
224 break;
225
226 default:
227 continue;
228 }
229 }
230
231 break;
232 }
233
234 //
235 // Reset the cursor on the screen.
236 //
237 gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
238
239ON_EXIT:
240 if (DescendEvent != NULL) {
241 gBS->CloseEvent (DescendEvent);
242 }
243 if (TimeoutEvent != NULL) {
244 gBS->CloseEvent (TimeoutEvent);
245 }
246
247 return Status;
248}
249
250
251/**
252 Select the boot menu by user's input.
253
254 @param[in] Private Pointer to PxeBc private data.
255 @param[out] Type The type of the menu.
256 @param[in] UseDefaultItem Use default item or not.
257
258 @retval EFI_ABORTED User cancel operation.
259 @retval EFI_SUCCESS Select the boot menu success.
260 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
261
262**/
263EFI_STATUS
264PxeBcSelectBootMenu (
265 IN PXEBC_PRIVATE_DATA *Private,
266 OUT UINT16 *Type,
267 IN BOOLEAN UseDefaultItem
268 )
269{
270 EFI_PXE_BASE_CODE_MODE *Mode;
271 PXEBC_DHCP_PACKET_CACHE *Cache;
272 PXEBC_VENDOR_OPTION *VendorOpt;
273 EFI_INPUT_KEY InputKey;
274 UINT32 OfferType;
275 UINT8 MenuSize;
276 UINT8 MenuNum;
277 INT32 TopRow;
278 UINT16 Select;
279 UINT16 LastSelect;
280 UINT8 Index;
281 BOOLEAN Finish;
282 CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];
283 PXEBC_BOOT_MENU_ENTRY *MenuItem;
284 PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];
285
286 Finish = FALSE;
287 Select = 0;
288 Index = 0;
289 *Type = 0;
290 Mode = Private->PxeBc.Mode;
291 Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
292 OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
293
294 //
295 // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec.
296 //
297 ASSERT (!Mode->UsingIpv6);
298 ASSERT (OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeDhcpPxe10);
299
300 VendorOpt = &Cache->Dhcp4.VendorOpt;
301 if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
302 return EFI_SUCCESS;
303 }
304
305 //
306 // Display the boot menu on the screen.
307 //
308 SetMem (Blank, sizeof(Blank), ' ');
309
310 MenuSize = VendorOpt->BootMenuLen;
311 MenuItem = VendorOpt->BootMenu;
312
313 if (MenuSize == 0) {
314 return EFI_DEVICE_ERROR;
315 }
316
317 while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
318 ASSERT (MenuItem != NULL);
319 MenuArray[Index] = MenuItem;
320 MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
321 MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
322 Index++;
323 }
324
325 if (UseDefaultItem) {
326 ASSERT (MenuArray[0] != NULL);
327 CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
328 *Type = NTOHS (*Type);
329 return EFI_SUCCESS;
330 }
331
332 MenuNum = Index;
333
334 for (Index = 0; Index < MenuNum; Index++) {
335 ASSERT (MenuArray[Index] != NULL);
336 PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
337 }
338
339 TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
340
341 //
342 // Select the boot item by user in the boot menu.
343 //
344 do {
345 //
346 // Highlight selected row.
347 //
348 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
349 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
350 ASSERT (Select < PXEBC_MENU_MAX_NUM);
351 ASSERT (MenuArray[Select] != NULL);
352 Blank[MenuArray[Select]->DescLen] = 0;
353 AsciiPrint ("%a\r", Blank);
354 PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
355 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
356 LastSelect = Select;
357
358 while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
359 gBS->Stall (10 * TICKS_PER_MS);
360 }
361
362 if (InputKey.ScanCode == 0) {
363 switch (InputKey.UnicodeChar) {
364 case CTRL ('c'):
365 InputKey.ScanCode = SCAN_ESC;
366 break;
367
368 case CTRL ('j'): /* linefeed */
369 case CTRL ('m'): /* return */
370 Finish = TRUE;
371 break;
372
373 case CTRL ('i'): /* tab */
374 case ' ':
375 case 'd':
376 case 'D':
377 InputKey.ScanCode = SCAN_DOWN;
378 break;
379
380 case CTRL ('h'): /* backspace */
381 case 'u':
382 case 'U':
383 InputKey.ScanCode = SCAN_UP;
384 break;
385
386 default:
387 InputKey.ScanCode = 0;
388 }
389 }
390
391 switch (InputKey.ScanCode) {
392 case SCAN_LEFT:
393 case SCAN_UP:
394 if (Select != 0) {
395 Select--;
396 }
397 break;
398
399 case SCAN_DOWN:
400 case SCAN_RIGHT:
401 if (++Select == MenuNum) {
402 Select--;
403 }
404 break;
405
406 case SCAN_PAGE_UP:
407 case SCAN_HOME:
408 Select = 0;
409 break;
410
411 case SCAN_PAGE_DOWN:
412 case SCAN_END:
413 Select = (UINT16) (MenuNum - 1);
414 break;
415
416 case SCAN_ESC:
417 return EFI_ABORTED;
418 }
419
420 //
421 // Unhighlight the last selected row.
422 //
423 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
424 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
425 ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
426 ASSERT (MenuArray[LastSelect] != NULL);
427 Blank[MenuArray[LastSelect]->DescLen] = 0;
428 AsciiPrint ("%a\r", Blank);
429 PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
430 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
431 } while (!Finish);
432
433 //
434 // Swap the byte order.
435 //
436 ASSERT (Select < PXEBC_MENU_MAX_NUM);
437 ASSERT (MenuArray[Select] != NULL);
438 CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
439 *Type = NTOHS (*Type);
440
441 return EFI_SUCCESS;
442}
443
444
445/**
446 Parse out the boot information from the last Dhcp4 reply packet.
447
448 @param[in, out] Private Pointer to PxeBc private data.
449 @param[out] BufferSize Size of the boot file to be downloaded.
450
451 @retval EFI_SUCCESS Successfully parsed out all the boot information.
452 @retval Others Failed to parse out the boot information.
453
454**/
455EFI_STATUS
456PxeBcDhcp4BootInfo (
457 IN OUT PXEBC_PRIVATE_DATA *Private,
458 OUT UINT64 *BufferSize
459 )
460{
461 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
462 EFI_PXE_BASE_CODE_MODE *Mode;
463 EFI_STATUS Status;
464 PXEBC_DHCP4_PACKET_CACHE *Cache4;
465 UINT16 Value;
466 PXEBC_VENDOR_OPTION *VendorOpt;
467 PXEBC_BOOT_SVR_ENTRY *Entry;
468
469 PxeBc = &Private->PxeBc;
470 Mode = PxeBc->Mode;
471 Status = EFI_SUCCESS;
472 *BufferSize = 0;
473
474 //
475 // Get the last received Dhcp4 reply packet.
476 //
477 if (Mode->PxeReplyReceived) {
478 Cache4 = &Private->PxeReply.Dhcp4;
479 } else if (Mode->ProxyOfferReceived) {
480 Cache4 = &Private->ProxyOffer.Dhcp4;
481 } else {
482 Cache4 = &Private->DhcpAck.Dhcp4;
483 }
484
485 ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
486
487 //
488 // Parse the boot server address.
489 // If prompt/discover is disabled, get the first boot server from the boot servers list.
490 // Otherwise, parse the boot server Ipv4 address from next server address field in DHCP header.
491 // If all these fields are not available, use option 54 instead.
492 //
493 VendorOpt = &Cache4->VendorOpt;
494 if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && IS_VALID_BOOT_SERVERS (VendorOpt->BitMap)) {
495 Entry = VendorOpt->BootSvr;
496 if (VendorOpt->BootSvrLen >= sizeof (PXEBC_BOOT_SVR_ENTRY) && Entry->IpCnt > 0) {
497 CopyMem (
498 &Private->ServerIp,
499 &Entry->IpAddr[0],
500 sizeof (EFI_IPv4_ADDRESS)
501 );
502 }
503 }
504 if (Private->ServerIp.Addr[0] == 0) {
505 //
506 // ServerIp.Addr[0] equals zero means we failed to get IP address from boot server list.
507 // Try to use next server address field.
508 //
509 CopyMem (
510 &Private->ServerIp,
511 &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
512 sizeof (EFI_IPv4_ADDRESS)
513 );
514 }
515 if (Private->ServerIp.Addr[0] == 0) {
516 //
517 // Still failed , use the IP address from option 54.
518 //
519 CopyMem (
520 &Private->ServerIp,
521 Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
522 sizeof (EFI_IPv4_ADDRESS)
523 );
524 }
525
526 //
527 // Parse the boot file name by option.
528 //
529 Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
530
531 if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
532 //
533 // Parse the boot file size by option.
534 //
535 CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
536 Value = NTOHS (Value);
537 //
538 // The field of boot file size is 512 bytes in unit.
539 //
540 *BufferSize = 512 * Value;
541 } else {
542 //
543 // Get the bootfile size by tftp command if no option available.
544 //
545 Status = PxeBc->Mtftp (
546 PxeBc,
547 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
548 NULL,
549 FALSE,
550 BufferSize,
551 &Private->BlockSize,
552 &Private->ServerIp,
553 Private->BootFileName,
554 NULL,
555 FALSE
556 );
557 }
558
559 //
560 // Save the value of boot file size.
561 //
562 Private->BootFileSize = (UINTN) *BufferSize;
563
564 //
565 // Display all the information: boot server address, boot file name and boot file size.
566 //
567 AsciiPrint ("\n Server IP address is ");
568 PxeBcShowIp4Addr (&Private->ServerIp.v4);
569 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
570 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
571
572 return Status;
573}
574
575
576/**
577 Parse out the boot information from the last Dhcp6 reply packet.
578
579 @param[in, out] Private Pointer to PxeBc private data.
580 @param[out] BufferSize Size of the boot file to be downloaded.
581
582 @retval EFI_SUCCESS Successfully parsed out all the boot information.
583 @retval EFI_BUFFER_TOO_SMALL
584 @retval Others Failed to parse out the boot information.
585
586**/
587EFI_STATUS
588PxeBcDhcp6BootInfo (
589 IN OUT PXEBC_PRIVATE_DATA *Private,
590 OUT UINT64 *BufferSize
591 )
592{
593 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
594 EFI_PXE_BASE_CODE_MODE *Mode;
595 EFI_STATUS Status;
596 PXEBC_DHCP6_PACKET_CACHE *Cache6;
597 UINT16 Value;
598
599 PxeBc = &Private->PxeBc;
600 Mode = PxeBc->Mode;
601 Status = EFI_SUCCESS;
602 *BufferSize = 0;
603
604 //
605 // Get the last received Dhcp6 reply packet.
606 //
607 if (Mode->PxeReplyReceived) {
608 Cache6 = &Private->PxeReply.Dhcp6;
609 } else if (Mode->ProxyOfferReceived) {
610 Cache6 = &Private->ProxyOffer.Dhcp6;
611 } else {
612 Cache6 = &Private->DhcpAck.Dhcp6;
613 }
614
615 ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
616
617 //
618 // Set the station address to IP layer.
619 //
620 Status = PxeBcSetIp6Address (Private);
621 if (EFI_ERROR (Status)) {
622 return Status;
623 }
624
625
626 //
627 // Parse (m)tftp server ip address and bootfile name.
628 //
629 Status = PxeBcExtractBootFileUrl (
630 Private,
631 &Private->BootFileName,
632 &Private->ServerIp.v6,
633 (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
634 NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
635 );
636 if (EFI_ERROR (Status)) {
637 return Status;
638 }
639
640 //
641 // Parse the value of boot file size.
642 //
643 if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
644 //
645 // Parse it out if have the boot file parameter option.
646 //
647 Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
648 if (EFI_ERROR (Status)) {
649 return Status;
650 }
651 //
652 // The field of boot file size is 512 bytes in unit.
653 //
654 *BufferSize = 512 * Value;
655 } else {
656 //
657 // Send get file size command by tftp if option unavailable.
658 //
659 Status = PxeBc->Mtftp (
660 PxeBc,
661 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
662 NULL,
663 FALSE,
664 BufferSize,
665 &Private->BlockSize,
666 &Private->ServerIp,
667 Private->BootFileName,
668 NULL,
669 FALSE
670 );
671 }
672
673 //
674 // Save the value of boot file size.
675 //
676 Private->BootFileSize = (UINTN) *BufferSize;
677
678 //
679 // Display all the information: boot server address, boot file name and boot file size.
680 //
681 AsciiPrint ("\n Server IP address is ");
682 PxeBcShowIp6Addr (&Private->ServerIp.v6);
683 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
684 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
685
686 return Status;
687}
688
689
690/**
691 Extract the discover information and boot server entry from the
692 cached packets if unspecified.
693
694 @param[in] Private Pointer to PxeBc private data.
695 @param[in] Type The type of bootstrap to perform.
696 @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
697 @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
698 @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
699
700 @retval EFI_SUCCESS Successfully extracted the information.
701 @retval EFI_DEVICE_ERROR Failed to extract the information.
702
703**/
704EFI_STATUS
705PxeBcExtractDiscoverInfo (
706 IN PXEBC_PRIVATE_DATA *Private,
707 IN UINT16 Type,
708 IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo,
709 OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
710 OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
711 )
712{
713 EFI_PXE_BASE_CODE_MODE *Mode;
714 PXEBC_DHCP4_PACKET_CACHE *Cache4;
715 PXEBC_VENDOR_OPTION *VendorOpt;
716 PXEBC_BOOT_SVR_ENTRY *Entry;
717 BOOLEAN IsFound;
718 EFI_PXE_BASE_CODE_DISCOVER_INFO *Info;
719 UINT16 Index;
720
721 Mode = Private->PxeBc.Mode;
722 Info = *DiscoverInfo;
723
724 if (Mode->UsingIpv6) {
725 Info->IpCnt = 1;
726 Info->UseUCast = TRUE;
727
728 Info->SrvList[0].Type = Type;
729 Info->SrvList[0].AcceptAnyResponse = FALSE;
730
731 //
732 // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
733 //
734 CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
735
736 *SrvList = Info->SrvList;
737 } else {
738 Entry = NULL;
739 IsFound = FALSE;
740 Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
741 VendorOpt = &Cache4->VendorOpt;
742
743 if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
744 //
745 // Address is not acquired or no discovery options.
746 //
747 return EFI_INVALID_PARAMETER;
748 }
749
750 //
751 // Parse the boot server entry from the vendor option in the last cached packet.
752 //
753 Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
754 Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
755 Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
756 Info->UseUCast = (BOOLEAN) IS_VALID_BOOT_SERVERS (VendorOpt->BitMap);
757
758 if (Info->UseMCast) {
759 //
760 // Get the multicast discover ip address from vendor option if has.
761 //
762 CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
763 }
764
765 Info->IpCnt = 0;
766
767 if (Info->UseUCast) {
768 Entry = VendorOpt->BootSvr;
769
770 while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
771 if (Entry->Type == HTONS (Type)) {
772 IsFound = TRUE;
773 break;
774 }
775 Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
776 }
777
778 if (!IsFound) {
779 return EFI_DEVICE_ERROR;
780 }
781
782 Info->IpCnt = Entry->IpCnt;
783 if (Info->IpCnt >= 1) {
784 *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList));
785 if (*DiscoverInfo == NULL) {
786 return EFI_OUT_OF_RESOURCES;
787 }
788 CopyMem (*DiscoverInfo, Info, sizeof (*Info));
789 Info = *DiscoverInfo;
790 }
791
792 for (Index = 0; Index < Info->IpCnt; Index++) {
793 CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
794 Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList;
795 Info->SrvList[Index].Type = NTOHS (Entry->Type);
796 }
797 }
798
799 *BootEntry = Entry;
800 *SrvList = Info->SrvList;
801 }
802
803 return EFI_SUCCESS;
804}
805
806
807/**
808 Build the discover packet and send out for boot server.
809
810 @param[in] Private Pointer to PxeBc private data.
811 @param[in] Type PxeBc option boot item type.
812 @param[in] Layer Pointer to option boot item layer.
813 @param[in] UseBis Use BIS or not.
814 @param[in] DestIp Pointer to the destination address.
815 @param[in] IpCount The count of the server address.
816 @param[in] SrvList Pointer to the server address list.
817
818 @retval EFI_SUCCESS Successfully discovered boot file.
819 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
820 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
821 @retval Others Failed to discover boot file.
822
823**/
824EFI_STATUS
825PxeBcDiscoverBootServer (
826 IN PXEBC_PRIVATE_DATA *Private,
827 IN UINT16 Type,
828 IN UINT16 *Layer,
829 IN BOOLEAN UseBis,
830 IN EFI_IP_ADDRESS *DestIp,
831 IN UINT16 IpCount,
832 IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
833 )
834{
835 if (Private->PxeBc.Mode->UsingIpv6) {
836 return PxeBcDhcp6Discover (
837 Private,
838 Type,
839 Layer,
840 UseBis,
841 DestIp
842 );
843 } else {
844 return PxeBcDhcp4Discover (
845 Private,
846 Type,
847 Layer,
848 UseBis,
849 DestIp,
850 IpCount,
851 SrvList
852 );
853 }
854}
855
856
857/**
858 Discover all the boot information for boot file.
859
860 @param[in, out] Private Pointer to PxeBc private data.
861 @param[out] BufferSize Size of the boot file to be downloaded.
862
863 @retval EFI_SUCCESS Successfully obtained all the boot information .
864 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
865 @retval EFI_ABORTED User cancel current operation.
866 @retval Others Failed to parse out the boot information.
867
868**/
869EFI_STATUS
870PxeBcDiscoverBootFile (
871 IN OUT PXEBC_PRIVATE_DATA *Private,
872 OUT UINT64 *BufferSize
873 )
874{
875 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
876 EFI_PXE_BASE_CODE_MODE *Mode;
877 EFI_STATUS Status;
878 UINT16 Type;
879 UINT16 Layer;
880 BOOLEAN UseBis;
881
882 PxeBc = &Private->PxeBc;
883 Mode = PxeBc->Mode;
884 Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
885 Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
886
887 //
888 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
889 // other pxe boot information.
890 //
891 Status = PxeBc->Dhcp (PxeBc, TRUE);
892 if (EFI_ERROR (Status)) {
893 return Status;
894 }
895
896 //
897 // Select a boot server from boot server list.
898 //
899 Status = PxeBcSelectBootPrompt (Private);
900
901 if (Status == EFI_SUCCESS) {
902 //
903 // Choose by user's input.
904 //
905 Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
906 } else if (Status == EFI_TIMEOUT) {
907 //
908 // Choose by default item.
909 //
910 Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
911 }
912
913 if (!EFI_ERROR (Status)) {
914
915 if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
916 //
917 // Local boot(PXE bootstrap server) need abort
918 //
919 return EFI_ABORTED;
920 }
921
922 //
923 // Start to discover the boot server to get (m)tftp server ip address, bootfile
924 // name and bootfile size.
925 //
926 UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
927 Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
928 if (EFI_ERROR (Status)) {
929 return Status;
930 }
931
932 if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) {
933 //
934 // Some network boot loader only search the packet in Mode.ProxyOffer to get its server
935 // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer.
936 //
937 if (Mode->UsingIpv6) {
938 CopyMem (
939 &Mode->ProxyOffer.Dhcpv6,
940 &Mode->PxeReply.Dhcpv6,
941 Private->PxeReply.Dhcp6.Packet.Ack.Length
942 );
943 } else {
944 CopyMem (
945 &Mode->ProxyOffer.Dhcpv4,
946 &Mode->PxeReply.Dhcpv4,
947 Private->PxeReply.Dhcp4.Packet.Ack.Length
948 );
949 }
950 Mode->ProxyOfferReceived = TRUE;
951 }
952 }
953
954 //
955 // Parse the boot information.
956 //
957 if (Mode->UsingIpv6) {
958 Status = PxeBcDhcp6BootInfo (Private, BufferSize);
959 } else {
960 Status = PxeBcDhcp4BootInfo (Private, BufferSize);
961 }
962
963 return Status;
964}
965
966
967/**
968 Install PxeBaseCodeCallbackProtocol if not installed before.
969
970 @param[in, out] Private Pointer to PxeBc private data.
971 @param[out] NewMakeCallback If TRUE, it is a new callback.
972 Otherwise, it is not new callback.
973 @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.
974 @retval Others Failed to install PxeBaseCodeCallbackProtocol.
975
976**/
977EFI_STATUS
978PxeBcInstallCallback (
979 IN OUT PXEBC_PRIVATE_DATA *Private,
980 OUT BOOLEAN *NewMakeCallback
981 )
982{
983 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
984 EFI_STATUS Status;
985
986 //
987 // Check whether PxeBaseCodeCallbackProtocol already installed.
988 //
989 PxeBc = &Private->PxeBc;
990 Status = gBS->HandleProtocol (
991 Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
992 &gEfiPxeBaseCodeCallbackProtocolGuid,
993 (VOID **) &Private->PxeBcCallback
994 );
995 if (Status == EFI_UNSUPPORTED) {
996
997 CopyMem (
998 &Private->LoadFileCallback,
999 &gPxeBcCallBackTemplate,
1000 sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
1001 );
1002
1003 //
1004 // Install a default callback if user didn't offer one.
1005 //
1006 Status = gBS->InstallProtocolInterface (
1007 Private->Mode.UsingIpv6 ? &Private->Ip6Nic->Controller : &Private->Ip4Nic->Controller,
1008 &gEfiPxeBaseCodeCallbackProtocolGuid,
1009 EFI_NATIVE_INTERFACE,
1010 &Private->LoadFileCallback
1011 );
1012
1013 (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
1014
1015 Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
1016 if (EFI_ERROR (Status)) {
1017 PxeBc->Stop (PxeBc);
1018 return Status;
1019 }
1020 }
1021
1022 return EFI_SUCCESS;
1023}
1024
1025
1026/**
1027 Uninstall PxeBaseCodeCallbackProtocol.
1028
1029 @param[in] Private Pointer to PxeBc private data.
1030 @param[in] NewMakeCallback If TRUE, it is a new callback.
1031 Otherwise, it is not new callback.
1032
1033**/
1034VOID
1035PxeBcUninstallCallback (
1036 IN PXEBC_PRIVATE_DATA *Private,
1037 IN BOOLEAN NewMakeCallback
1038 )
1039{
1040 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1041
1042 PxeBc = &Private->PxeBc;
1043
1044 if (NewMakeCallback) {
1045
1046 NewMakeCallback = FALSE;
1047
1048 PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
1049
1050 gBS->UninstallProtocolInterface (
1051 Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
1052 &gEfiPxeBaseCodeCallbackProtocolGuid,
1053 &Private->LoadFileCallback
1054 );
1055 }
1056}
1057
1058
1059/**
1060 Download one of boot file in the list, and it's special for IPv6.
1061
1062 @param[in] Private Pointer to PxeBc private data.
1063 @param[in, out] BufferSize Size of user buffer for input;
1064 required buffer size for output.
1065 @param[in] Buffer Pointer to user buffer.
1066
1067 @retval EFI_SUCCESS Read one of boot file in the list successfully.
1068 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
1069 @retval EFI_NOT_FOUND There is no proper boot file available.
1070 @retval Others Failed to download boot file in the list.
1071
1072**/
1073EFI_STATUS
1074PxeBcReadBootFileList (
1075 IN PXEBC_PRIVATE_DATA *Private,
1076 IN OUT UINT64 *BufferSize,
1077 IN VOID *Buffer OPTIONAL
1078 )
1079{
1080 EFI_STATUS Status;
1081 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1082
1083 PxeBc = &Private->PxeBc;
1084
1085 //
1086 // Try to download the boot file if everything is ready.
1087 //
1088 if (Buffer != NULL) {
1089 Status = PxeBc->Mtftp (
1090 PxeBc,
1091 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1092 Buffer,
1093 FALSE,
1094 BufferSize,
1095 &Private->BlockSize,
1096 &Private->ServerIp,
1097 Private->BootFileName,
1098 NULL,
1099 FALSE
1100 );
1101
1102
1103 } else {
1104 Status = EFI_BUFFER_TOO_SMALL;
1105 }
1106
1107 return Status;
1108}
1109
1110
1111/**
1112 Load boot file into user buffer.
1113
1114 @param[in] Private Pointer to PxeBc private data.
1115 @param[in, out] BufferSize Size of user buffer for input;
1116 required buffer size for output.
1117 @param[in] Buffer Pointer to user buffer.
1118
1119 @retval EFI_SUCCESS Get all the boot information successfully.
1120 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
1121 @retval EFI_ABORTED User cancelled the current operation.
1122 @retval Others Failed to parse out the boot information.
1123
1124**/
1125EFI_STATUS
1126PxeBcLoadBootFile (
1127 IN PXEBC_PRIVATE_DATA *Private,
1128 IN OUT UINTN *BufferSize,
1129 IN VOID *Buffer OPTIONAL
1130 )
1131{
1132 BOOLEAN NewMakeCallback;
1133 UINT64 RequiredSize;
1134 UINT64 CurrentSize;
1135 EFI_STATUS Status;
1136 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1137 EFI_PXE_BASE_CODE_MODE *PxeBcMode;
1138
1139 NewMakeCallback = FALSE;
1140 PxeBc = &Private->PxeBc;
1141 PxeBcMode = &Private->Mode;
1142 CurrentSize = *BufferSize;
1143 RequiredSize = 0;
1144
1145 //
1146 // Install pxebc callback protocol if hasn't been installed yet.
1147 //
1148 Status = PxeBcInstallCallback (Private, &NewMakeCallback);
1149 if (EFI_ERROR(Status)) {
1150 return Status;
1151 }
1152
1153 if (Private->BootFileSize == 0) {
1154 //
1155 // Discover the boot information about the bootfile if hasn't.
1156 //
1157 Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
1158 if (EFI_ERROR (Status)) {
1159 goto ON_EXIT;
1160 }
1161
1162 if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
1163 //
1164 // It's error if the required buffer size is beyond the system scope.
1165 //
1166 Status = EFI_DEVICE_ERROR;
1167 goto ON_EXIT;
1168 } else if (RequiredSize > 0) {
1169 //
1170 // Get the right buffer size of the bootfile required.
1171 //
1172 if (CurrentSize < RequiredSize || Buffer == NULL) {
1173 //
1174 // It's buffer too small if the size of user buffer is smaller than the required.
1175 //
1176 CurrentSize = RequiredSize;
1177 Status = EFI_BUFFER_TOO_SMALL;
1178 goto ON_EXIT;
1179 }
1180 CurrentSize = RequiredSize;
1181 } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
1182 //
1183 // Try to download another bootfile in list if failed to get the filesize of the last one.
1184 // It's special for the case of IPv6.
1185 //
1186 Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
1187 goto ON_EXIT;
1188 }
1189 } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
1190 //
1191 // It's buffer too small if the size of user buffer is smaller than the required.
1192 //
1193 CurrentSize = Private->BootFileSize;
1194 Status = EFI_BUFFER_TOO_SMALL;
1195 goto ON_EXIT;
1196 }
1197
1198 //
1199 // Begin to download the bootfile if everything is ready.
1200 //
1201 AsciiPrint ("\n Downloading NBP file...\n");
1202 if (PxeBcMode->UsingIpv6) {
1203 Status = PxeBcReadBootFileList (
1204 Private,
1205 &CurrentSize,
1206 Buffer
1207 );
1208 } else {
1209 Status = PxeBc->Mtftp (
1210 PxeBc,
1211 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1212 Buffer,
1213 FALSE,
1214 &CurrentSize,
1215 &Private->BlockSize,
1216 &Private->ServerIp,
1217 Private->BootFileName,
1218 NULL,
1219 FALSE
1220 );
1221 }
1222
1223ON_EXIT:
1224 *BufferSize = (UINTN) CurrentSize;
1225 PxeBcUninstallCallback(Private, NewMakeCallback);
1226
1227 if (Status == EFI_SUCCESS) {
1228 AsciiPrint ("\n NBP file downloaded successfully.\n");
1229 return EFI_SUCCESS;
1230 } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
1231 AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");
1232 } else if (Status == EFI_DEVICE_ERROR) {
1233 AsciiPrint ("\n PXE-E07: Network device error.\n");
1234 } else if (Status == EFI_OUT_OF_RESOURCES) {
1235 AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");
1236 } else if (Status == EFI_NO_MEDIA) {
1237 AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");
1238 } else if (Status == EFI_NO_RESPONSE) {
1239 AsciiPrint ("\n PXE-E16: No valid offer received.\n");
1240 } else if (Status == EFI_TIMEOUT) {
1241 AsciiPrint ("\n PXE-E18: Server response timeout.\n");
1242 } else if (Status == EFI_ABORTED) {
1243 AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");
1244 } else if (Status == EFI_ICMP_ERROR) {
1245 AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");
1246 } else if (Status == EFI_TFTP_ERROR) {
1247 AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");
1248 } else if (Status == EFI_NOT_FOUND) {
1249 AsciiPrint ("\n PXE-E53: No boot filename received.\n");
1250 } else if (Status != EFI_BUFFER_TOO_SMALL) {
1251 AsciiPrint ("\n PXE-E99: Unexpected network error.\n");
1252 }
1253
1254 return Status;
1255}
1256
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette