]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
UefiCpuPkg/PiSmmCpuDxeSmm: patch "gSmiCr3" with PatchInstructionX86()
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcBoot.c
1 /** @file
2 Boot functions implementation for UefiPxeBc Driver.
3
4 Copyright (c) 2009 - 2016, 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 MultU64x32 (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 // Set the station address to IP layer.
625 //
626 Status = PxeBcSetIp6Address (Private);
627 if (EFI_ERROR (Status)) {
628 return Status;
629 }
630
631
632 //
633 // Parse (m)tftp server ip address and bootfile name.
634 //
635 Status = PxeBcExtractBootFileUrl (
636 Private,
637 &Private->BootFileName,
638 &Private->ServerIp.v6,
639 (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
640 NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
641 );
642 if (EFI_ERROR (Status)) {
643 return Status;
644 }
645
646 //
647 // Parse the value of boot file size.
648 //
649 if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
650 //
651 // Parse it out if have the boot file parameter option.
652 //
653 Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
654 if (EFI_ERROR (Status)) {
655 return Status;
656 }
657 //
658 // The field of boot file size is 512 bytes in unit.
659 //
660 *BufferSize = 512 * Value;
661 } else {
662 //
663 // Send get file size command by tftp if option unavailable.
664 //
665 Status = PxeBc->Mtftp (
666 PxeBc,
667 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
668 NULL,
669 FALSE,
670 BufferSize,
671 &Private->BlockSize,
672 &Private->ServerIp,
673 Private->BootFileName,
674 NULL,
675 FALSE
676 );
677 }
678
679 //
680 // Save the value of boot file size.
681 //
682 Private->BootFileSize = (UINTN) *BufferSize;
683
684 //
685 // Display all the information: boot server address, boot file name and boot file size.
686 //
687 AsciiPrint ("\n Server IP address is ");
688 PxeBcShowIp6Addr (&Private->ServerIp.v6);
689 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
690 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
691
692 return Status;
693 }
694
695
696 /**
697 Extract the discover information and boot server entry from the
698 cached packets if unspecified.
699
700 @param[in] Private Pointer to PxeBc private data.
701 @param[in] Type The type of bootstrap to perform.
702 @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
703 @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
704 @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
705
706 @retval EFI_SUCCESS Successfully extracted the information.
707 @retval EFI_DEVICE_ERROR Failed to extract the information.
708
709 **/
710 EFI_STATUS
711 PxeBcExtractDiscoverInfo (
712 IN PXEBC_PRIVATE_DATA *Private,
713 IN UINT16 Type,
714 IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo,
715 OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
716 OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
717 )
718 {
719 EFI_PXE_BASE_CODE_MODE *Mode;
720 PXEBC_DHCP4_PACKET_CACHE *Cache4;
721 PXEBC_VENDOR_OPTION *VendorOpt;
722 PXEBC_BOOT_SVR_ENTRY *Entry;
723 BOOLEAN IsFound;
724 EFI_PXE_BASE_CODE_DISCOVER_INFO *Info;
725 UINT16 Index;
726
727 Mode = Private->PxeBc.Mode;
728 Info = *DiscoverInfo;
729
730 if (Mode->UsingIpv6) {
731 Info->IpCnt = 1;
732 Info->UseUCast = TRUE;
733
734 Info->SrvList[0].Type = Type;
735 Info->SrvList[0].AcceptAnyResponse = FALSE;
736
737 //
738 // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
739 //
740 CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
741
742 *SrvList = Info->SrvList;
743 } else {
744 Entry = NULL;
745 IsFound = FALSE;
746 Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
747 VendorOpt = &Cache4->VendorOpt;
748
749 if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
750 //
751 // Address is not acquired or no discovery options.
752 //
753 return EFI_INVALID_PARAMETER;
754 }
755
756 //
757 // Parse the boot server entry from the vendor option in the last cached packet.
758 //
759 Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
760 Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
761 Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
762 Info->UseUCast = (BOOLEAN) IS_VALID_BOOT_SERVERS (VendorOpt->BitMap);
763
764 if (Info->UseMCast) {
765 //
766 // Get the multicast discover ip address from vendor option if has.
767 //
768 CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
769 }
770
771 Info->IpCnt = 0;
772
773 if (Info->UseUCast) {
774 Entry = VendorOpt->BootSvr;
775
776 while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
777 if (Entry->Type == HTONS (Type)) {
778 IsFound = TRUE;
779 break;
780 }
781 Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
782 }
783
784 if (!IsFound) {
785 return EFI_DEVICE_ERROR;
786 }
787
788 Info->IpCnt = Entry->IpCnt;
789 if (Info->IpCnt >= 1) {
790 *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList));
791 if (*DiscoverInfo == NULL) {
792 return EFI_OUT_OF_RESOURCES;
793 }
794 CopyMem (*DiscoverInfo, Info, sizeof (*Info));
795 Info = *DiscoverInfo;
796 }
797
798 for (Index = 0; Index < Info->IpCnt; Index++) {
799 CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
800 Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList;
801 Info->SrvList[Index].Type = NTOHS (Entry->Type);
802 }
803 }
804
805 *BootEntry = Entry;
806 *SrvList = Info->SrvList;
807 }
808
809 return EFI_SUCCESS;
810 }
811
812
813 /**
814 Build the discover packet and send out for boot server.
815
816 @param[in] Private Pointer to PxeBc private data.
817 @param[in] Type PxeBc option boot item type.
818 @param[in] Layer Pointer to option boot item layer.
819 @param[in] UseBis Use BIS or not.
820 @param[in] DestIp Pointer to the destination address.
821 @param[in] IpCount The count of the server address.
822 @param[in] SrvList Pointer to the server address list.
823
824 @retval EFI_SUCCESS Successfully discovered boot file.
825 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
826 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
827 @retval Others Failed to discover boot file.
828
829 **/
830 EFI_STATUS
831 PxeBcDiscoverBootServer (
832 IN PXEBC_PRIVATE_DATA *Private,
833 IN UINT16 Type,
834 IN UINT16 *Layer,
835 IN BOOLEAN UseBis,
836 IN EFI_IP_ADDRESS *DestIp,
837 IN UINT16 IpCount,
838 IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
839 )
840 {
841 if (Private->PxeBc.Mode->UsingIpv6) {
842 return PxeBcDhcp6Discover (
843 Private,
844 Type,
845 Layer,
846 UseBis,
847 DestIp
848 );
849 } else {
850 return PxeBcDhcp4Discover (
851 Private,
852 Type,
853 Layer,
854 UseBis,
855 DestIp,
856 IpCount,
857 SrvList
858 );
859 }
860 }
861
862
863 /**
864 Discover all the boot information for boot file.
865
866 @param[in, out] Private Pointer to PxeBc private data.
867 @param[out] BufferSize Size of the boot file to be downloaded.
868
869 @retval EFI_SUCCESS Successfully obtained all the boot information .
870 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
871 @retval EFI_ABORTED User cancel current operation.
872 @retval Others Failed to parse out the boot information.
873
874 **/
875 EFI_STATUS
876 PxeBcDiscoverBootFile (
877 IN OUT PXEBC_PRIVATE_DATA *Private,
878 OUT UINT64 *BufferSize
879 )
880 {
881 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
882 EFI_PXE_BASE_CODE_MODE *Mode;
883 EFI_STATUS Status;
884 UINT16 Type;
885 UINT16 Layer;
886 BOOLEAN UseBis;
887
888 PxeBc = &Private->PxeBc;
889 Mode = PxeBc->Mode;
890 Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
891 Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
892
893 //
894 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
895 // other pxe boot information.
896 //
897 Status = PxeBc->Dhcp (PxeBc, TRUE);
898 if (EFI_ERROR (Status)) {
899 return Status;
900 }
901
902 //
903 // Select a boot server from boot server list.
904 //
905 Status = PxeBcSelectBootPrompt (Private);
906
907 if (Status == EFI_SUCCESS) {
908 //
909 // Choose by user's input.
910 //
911 Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
912 } else if (Status == EFI_TIMEOUT) {
913 //
914 // Choose by default item.
915 //
916 Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
917 }
918
919 if (!EFI_ERROR (Status)) {
920
921 if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
922 //
923 // Local boot(PXE bootstrap server) need abort
924 //
925 return EFI_ABORTED;
926 }
927
928 //
929 // Start to discover the boot server to get (m)tftp server ip address, bootfile
930 // name and bootfile size.
931 //
932 UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
933 Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
934 if (EFI_ERROR (Status)) {
935 return Status;
936 }
937
938 if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) {
939 //
940 // Some network boot loader only search the packet in Mode.ProxyOffer to get its server
941 // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer.
942 //
943 if (Mode->UsingIpv6) {
944 CopyMem (
945 &Mode->ProxyOffer.Dhcpv6,
946 &Mode->PxeReply.Dhcpv6,
947 Private->PxeReply.Dhcp6.Packet.Ack.Length
948 );
949 } else {
950 CopyMem (
951 &Mode->ProxyOffer.Dhcpv4,
952 &Mode->PxeReply.Dhcpv4,
953 Private->PxeReply.Dhcp4.Packet.Ack.Length
954 );
955 }
956 Mode->ProxyOfferReceived = TRUE;
957 }
958 }
959
960 //
961 // Parse the boot information.
962 //
963 if (Mode->UsingIpv6) {
964 Status = PxeBcDhcp6BootInfo (Private, BufferSize);
965 } else {
966 Status = PxeBcDhcp4BootInfo (Private, BufferSize);
967 }
968
969 return Status;
970 }
971
972
973 /**
974 Install PxeBaseCodeCallbackProtocol if not installed before.
975
976 @param[in, out] Private Pointer to PxeBc private data.
977 @param[out] NewMakeCallback If TRUE, it is a new callback.
978 Otherwise, it is not new callback.
979 @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.
980 @retval Others Failed to install PxeBaseCodeCallbackProtocol.
981
982 **/
983 EFI_STATUS
984 PxeBcInstallCallback (
985 IN OUT PXEBC_PRIVATE_DATA *Private,
986 OUT BOOLEAN *NewMakeCallback
987 )
988 {
989 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
990 EFI_STATUS Status;
991
992 //
993 // Check whether PxeBaseCodeCallbackProtocol already installed.
994 //
995 PxeBc = &Private->PxeBc;
996 Status = gBS->HandleProtocol (
997 Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
998 &gEfiPxeBaseCodeCallbackProtocolGuid,
999 (VOID **) &Private->PxeBcCallback
1000 );
1001 if (Status == EFI_UNSUPPORTED) {
1002
1003 CopyMem (
1004 &Private->LoadFileCallback,
1005 &gPxeBcCallBackTemplate,
1006 sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
1007 );
1008
1009 //
1010 // Install a default callback if user didn't offer one.
1011 //
1012 Status = gBS->InstallProtocolInterface (
1013 Private->Mode.UsingIpv6 ? &Private->Ip6Nic->Controller : &Private->Ip4Nic->Controller,
1014 &gEfiPxeBaseCodeCallbackProtocolGuid,
1015 EFI_NATIVE_INTERFACE,
1016 &Private->LoadFileCallback
1017 );
1018
1019 (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
1020
1021 Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
1022 if (EFI_ERROR (Status)) {
1023 PxeBc->Stop (PxeBc);
1024 return Status;
1025 }
1026 }
1027
1028 return EFI_SUCCESS;
1029 }
1030
1031
1032 /**
1033 Uninstall PxeBaseCodeCallbackProtocol.
1034
1035 @param[in] Private Pointer to PxeBc private data.
1036 @param[in] NewMakeCallback If TRUE, it is a new callback.
1037 Otherwise, it is not new callback.
1038
1039 **/
1040 VOID
1041 PxeBcUninstallCallback (
1042 IN PXEBC_PRIVATE_DATA *Private,
1043 IN BOOLEAN NewMakeCallback
1044 )
1045 {
1046 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1047
1048 PxeBc = &Private->PxeBc;
1049
1050 if (NewMakeCallback) {
1051
1052 NewMakeCallback = FALSE;
1053
1054 PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
1055
1056 gBS->UninstallProtocolInterface (
1057 Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
1058 &gEfiPxeBaseCodeCallbackProtocolGuid,
1059 &Private->LoadFileCallback
1060 );
1061 }
1062 }
1063
1064
1065 /**
1066 Download one of boot file in the list, and it's special for IPv6.
1067
1068 @param[in] Private Pointer to PxeBc private data.
1069 @param[in, out] BufferSize Size of user buffer for input;
1070 required buffer size for output.
1071 @param[in] Buffer Pointer to user buffer.
1072
1073 @retval EFI_SUCCESS Read one of boot file in the list successfully.
1074 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
1075 @retval EFI_NOT_FOUND There is no proper boot file available.
1076 @retval Others Failed to download boot file in the list.
1077
1078 **/
1079 EFI_STATUS
1080 PxeBcReadBootFileList (
1081 IN PXEBC_PRIVATE_DATA *Private,
1082 IN OUT UINT64 *BufferSize,
1083 IN VOID *Buffer OPTIONAL
1084 )
1085 {
1086 EFI_STATUS Status;
1087 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1088
1089 PxeBc = &Private->PxeBc;
1090
1091 //
1092 // Try to download the boot file if everything is ready.
1093 //
1094 if (Buffer != NULL) {
1095 Status = PxeBc->Mtftp (
1096 PxeBc,
1097 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1098 Buffer,
1099 FALSE,
1100 BufferSize,
1101 &Private->BlockSize,
1102 &Private->ServerIp,
1103 Private->BootFileName,
1104 NULL,
1105 FALSE
1106 );
1107
1108
1109 } else {
1110 Status = EFI_BUFFER_TOO_SMALL;
1111 }
1112
1113 return Status;
1114 }
1115
1116
1117 /**
1118 Load boot file into user buffer.
1119
1120 @param[in] Private Pointer to PxeBc private data.
1121 @param[in, out] BufferSize Size of user buffer for input;
1122 required buffer size for output.
1123 @param[in] Buffer Pointer to user buffer.
1124
1125 @retval EFI_SUCCESS Get all the boot information successfully.
1126 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
1127 @retval EFI_ABORTED User cancelled the current operation.
1128 @retval Others Failed to parse out the boot information.
1129
1130 **/
1131 EFI_STATUS
1132 PxeBcLoadBootFile (
1133 IN PXEBC_PRIVATE_DATA *Private,
1134 IN OUT UINTN *BufferSize,
1135 IN VOID *Buffer OPTIONAL
1136 )
1137 {
1138 BOOLEAN NewMakeCallback;
1139 UINT64 RequiredSize;
1140 UINT64 CurrentSize;
1141 EFI_STATUS Status;
1142 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1143 EFI_PXE_BASE_CODE_MODE *PxeBcMode;
1144
1145 NewMakeCallback = FALSE;
1146 PxeBc = &Private->PxeBc;
1147 PxeBcMode = &Private->Mode;
1148 CurrentSize = *BufferSize;
1149 RequiredSize = 0;
1150
1151 //
1152 // Install pxebc callback protocol if hasn't been installed yet.
1153 //
1154 Status = PxeBcInstallCallback (Private, &NewMakeCallback);
1155 if (EFI_ERROR(Status)) {
1156 return Status;
1157 }
1158
1159 if (Private->BootFileSize == 0) {
1160 //
1161 // Discover the boot information about the bootfile if hasn't.
1162 //
1163 Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
1164 if (EFI_ERROR (Status)) {
1165 goto ON_EXIT;
1166 }
1167
1168 if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
1169 //
1170 // It's error if the required buffer size is beyond the system scope.
1171 //
1172 Status = EFI_DEVICE_ERROR;
1173 goto ON_EXIT;
1174 } else if (RequiredSize > 0) {
1175 //
1176 // Get the right buffer size of the bootfile required.
1177 //
1178 if (CurrentSize < RequiredSize || Buffer == NULL) {
1179 //
1180 // It's buffer too small if the size of user buffer is smaller than the required.
1181 //
1182 CurrentSize = RequiredSize;
1183 Status = EFI_BUFFER_TOO_SMALL;
1184 goto ON_EXIT;
1185 }
1186 CurrentSize = RequiredSize;
1187 } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
1188 //
1189 // Try to download another bootfile in list if failed to get the filesize of the last one.
1190 // It's special for the case of IPv6.
1191 //
1192 Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
1193 goto ON_EXIT;
1194 }
1195 } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
1196 //
1197 // It's buffer too small if the size of user buffer is smaller than the required.
1198 //
1199 CurrentSize = Private->BootFileSize;
1200 Status = EFI_BUFFER_TOO_SMALL;
1201 goto ON_EXIT;
1202 }
1203
1204 //
1205 // Begin to download the bootfile if everything is ready.
1206 //
1207 AsciiPrint ("\n Downloading NBP file...\n");
1208 if (PxeBcMode->UsingIpv6) {
1209 Status = PxeBcReadBootFileList (
1210 Private,
1211 &CurrentSize,
1212 Buffer
1213 );
1214 } else {
1215 Status = PxeBc->Mtftp (
1216 PxeBc,
1217 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1218 Buffer,
1219 FALSE,
1220 &CurrentSize,
1221 &Private->BlockSize,
1222 &Private->ServerIp,
1223 Private->BootFileName,
1224 NULL,
1225 FALSE
1226 );
1227 }
1228
1229 ON_EXIT:
1230 *BufferSize = (UINTN) CurrentSize;
1231 PxeBcUninstallCallback(Private, NewMakeCallback);
1232
1233 if (Status == EFI_SUCCESS) {
1234 AsciiPrint ("\n NBP file downloaded successfully.\n");
1235 return EFI_SUCCESS;
1236 } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
1237 AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");
1238 } else if (Status == EFI_DEVICE_ERROR) {
1239 AsciiPrint ("\n PXE-E07: Network device error.\n");
1240 } else if (Status == EFI_OUT_OF_RESOURCES) {
1241 AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");
1242 } else if (Status == EFI_NO_MEDIA) {
1243 AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");
1244 } else if (Status == EFI_NO_RESPONSE) {
1245 AsciiPrint ("\n PXE-E16: No valid offer received.\n");
1246 } else if (Status == EFI_TIMEOUT) {
1247 AsciiPrint ("\n PXE-E18: Server response timeout.\n");
1248 } else if (Status == EFI_ABORTED) {
1249 AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");
1250 } else if (Status == EFI_ICMP_ERROR) {
1251 AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");
1252 } else if (Status == EFI_TFTP_ERROR) {
1253 AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");
1254 } else if (Status == EFI_NOT_FOUND) {
1255 AsciiPrint ("\n PXE-E53: No boot filename received.\n");
1256 } else if (Status != EFI_BUFFER_TOO_SMALL) {
1257 AsciiPrint ("\n PXE-E99: Unexpected network error.\n");
1258 }
1259
1260 return Status;
1261 }
1262