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->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->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->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 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 1263