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