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