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