1 /** @file 2 Support functions implementation for UEFI HTTP boot driver. 3 4 Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> 5 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> 6 This program and the accompanying materials are licensed and made available under 7 the terms and conditions of the BSD License that accompanies this distribution. 8 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 "HttpBootDxe.h" 17 18 19 /** 20 Get the Nic handle using any child handle in the IPv4 stack. 21 22 @param[in] ControllerHandle Pointer to child handle over IPv4. 23 24 @return NicHandle The pointer to the Nic handle. 25 @return NULL Can't find the Nic handle. 26 27 **/ 28 EFI_HANDLE 29 HttpBootGetNicByIp4Children ( 30 IN EFI_HANDLE ControllerHandle 31 ) 32 { 33 EFI_HANDLE NicHandle; 34 35 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid); 36 if (NicHandle == NULL) { 37 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); 38 if (NicHandle == NULL) { 39 return NULL; 40 } 41 } 42 43 return NicHandle; 44 } 45 46 /** 47 Get the Nic handle using any child handle in the IPv6 stack. 48 49 @param[in] ControllerHandle Pointer to child handle over IPv6. 50 51 @return NicHandle The pointer to the Nic handle. 52 @return NULL Can't find the Nic handle. 53 54 **/ 55 EFI_HANDLE 56 HttpBootGetNicByIp6Children ( 57 IN EFI_HANDLE ControllerHandle 58 ) 59 { 60 EFI_HANDLE NicHandle; 61 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid); 62 if (NicHandle == NULL) { 63 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); 64 if (NicHandle == NULL) { 65 return NULL; 66 } 67 } 68 69 return NicHandle; 70 } 71 72 /** 73 This function is to convert UINTN to ASCII string with the required formatting. 74 75 @param[in] Number Numeric value to be converted. 76 @param[in] Buffer The pointer to the buffer for ASCII string. 77 @param[in] Length The length of the required format. 78 79 **/ 80 VOID 81 HttpBootUintnToAscDecWithFormat ( 82 IN UINTN Number, 83 IN UINT8 *Buffer, 84 IN INTN Length 85 ) 86 { 87 UINTN Remainder; 88 89 for (; Length > 0; Length--) { 90 Remainder = Number % 10; 91 Number /= 10; 92 Buffer[Length - 1] = (UINT8) ('0' + Remainder); 93 } 94 } 95 96 /** 97 This function is to display the IPv4 address. 98 99 @param[in] Ip The pointer to the IPv4 address. 100 101 **/ 102 VOID 103 HttpBootShowIp4Addr ( 104 IN EFI_IPv4_ADDRESS *Ip 105 ) 106 { 107 UINTN Index; 108 109 for (Index = 0; Index < 4; Index++) { 110 AsciiPrint ("%d", Ip->Addr[Index]); 111 if (Index < 3) { 112 AsciiPrint ("."); 113 } 114 } 115 } 116 117 /** 118 This function is to display the IPv6 address. 119 120 @param[in] Ip The pointer to the IPv6 address. 121 122 **/ 123 VOID 124 HttpBootShowIp6Addr ( 125 IN EFI_IPv6_ADDRESS *Ip 126 ) 127 { 128 UINTN Index; 129 130 for (Index = 0; Index < 16; Index++) { 131 132 if (Ip->Addr[Index] != 0) { 133 AsciiPrint ("%x", Ip->Addr[Index]); 134 } 135 Index++; 136 if (Index > 15) { 137 return; 138 } 139 if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) { 140 AsciiPrint ("0"); 141 } 142 AsciiPrint ("%x", Ip->Addr[Index]); 143 if (Index < 15) { 144 AsciiPrint (":"); 145 } 146 } 147 } 148 149 /** 150 This function is to display the HTTP error status. 151 152 @param[in] StatusCode The status code value in HTTP message. 153 154 **/ 155 VOID 156 HttpBootPrintErrorMessage ( 157 EFI_HTTP_STATUS_CODE StatusCode 158 ) 159 { 160 AsciiPrint ("\n"); 161 162 switch (StatusCode) { 163 case HTTP_STATUS_300_MULTIPLE_CHIOCES: 164 AsciiPrint ("\n Redirection: 300 Multiple Choices"); 165 break; 166 167 case HTTP_STATUS_301_MOVED_PERMANENTLY: 168 AsciiPrint ("\n Redirection: 301 Moved Permanently"); 169 break; 170 171 case HTTP_STATUS_302_FOUND: 172 AsciiPrint ("\n Redirection: 302 Found"); 173 break; 174 175 case HTTP_STATUS_303_SEE_OTHER: 176 AsciiPrint ("\n Redirection: 303 See Other"); 177 break; 178 179 case HTTP_STATUS_304_NOT_MODIFIED: 180 AsciiPrint ("\n Redirection: 304 Not Modified"); 181 break; 182 183 case HTTP_STATUS_305_USE_PROXY: 184 AsciiPrint ("\n Redirection: 305 Use Proxy"); 185 break; 186 187 case HTTP_STATUS_307_TEMPORARY_REDIRECT: 188 AsciiPrint ("\n Redirection: 307 Temporary Redirect"); 189 break; 190 191 case HTTP_STATUS_400_BAD_REQUEST: 192 AsciiPrint ("\n Client Error: 400 Bad Request"); 193 break; 194 195 case HTTP_STATUS_401_UNAUTHORIZED: 196 AsciiPrint ("\n Client Error: 401 Unauthorized"); 197 break; 198 199 case HTTP_STATUS_402_PAYMENT_REQUIRED: 200 AsciiPrint ("\n Client Error: 402 Payment Required"); 201 break; 202 203 case HTTP_STATUS_403_FORBIDDEN: 204 AsciiPrint ("\n Client Error: 403 Forbidden"); 205 break; 206 207 case HTTP_STATUS_404_NOT_FOUND: 208 AsciiPrint ("\n Client Error: 404 Not Found"); 209 break; 210 211 case HTTP_STATUS_405_METHOD_NOT_ALLOWED: 212 AsciiPrint ("\n Client Error: 405 Method Not Allowed"); 213 break; 214 215 case HTTP_STATUS_406_NOT_ACCEPTABLE: 216 AsciiPrint ("\n Client Error: 406 Not Acceptable"); 217 break; 218 219 case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED: 220 AsciiPrint ("\n Client Error: 407 Proxy Authentication Required"); 221 break; 222 223 case HTTP_STATUS_408_REQUEST_TIME_OUT: 224 AsciiPrint ("\n Client Error: 408 Request Timeout"); 225 break; 226 227 case HTTP_STATUS_409_CONFLICT: 228 AsciiPrint ("\n Client Error: 409 Conflict"); 229 break; 230 231 case HTTP_STATUS_410_GONE: 232 AsciiPrint ("\n Client Error: 410 Gone"); 233 break; 234 235 case HTTP_STATUS_411_LENGTH_REQUIRED: 236 AsciiPrint ("\n Client Error: 411 Length Required"); 237 break; 238 239 case HTTP_STATUS_412_PRECONDITION_FAILED: 240 AsciiPrint ("\n Client Error: 412 Precondition Failed"); 241 break; 242 243 case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE: 244 AsciiPrint ("\n Client Error: 413 Request Entity Too Large"); 245 break; 246 247 case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE: 248 AsciiPrint ("\n Client Error: 414 Request URI Too Long"); 249 break; 250 251 case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE: 252 AsciiPrint ("\n Client Error: 415 Unsupported Media Type"); 253 break; 254 255 case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED: 256 AsciiPrint ("\n Client Error: 416 Requested Range Not Satisfiable"); 257 break; 258 259 case HTTP_STATUS_417_EXPECTATION_FAILED: 260 AsciiPrint ("\n Client Error: 417 Expectation Failed"); 261 break; 262 263 case HTTP_STATUS_500_INTERNAL_SERVER_ERROR: 264 AsciiPrint ("\n Server Error: 500 Internal Server Error"); 265 break; 266 267 case HTTP_STATUS_501_NOT_IMPLEMENTED: 268 AsciiPrint ("\n Server Error: 501 Not Implemented"); 269 break; 270 271 case HTTP_STATUS_502_BAD_GATEWAY: 272 AsciiPrint ("\n Server Error: 502 Bad Gateway"); 273 break; 274 275 case HTTP_STATUS_503_SERVICE_UNAVAILABLE: 276 AsciiPrint ("\n Server Error: 503 Service Unavailable"); 277 break; 278 279 case HTTP_STATUS_504_GATEWAY_TIME_OUT: 280 AsciiPrint ("\n Server Error: 504 Gateway Timeout"); 281 break; 282 283 case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED: 284 AsciiPrint ("\n Server Error: 505 HTTP Version Not Supported"); 285 break; 286 287 default: ; 288 289 } 290 } 291 292 /** 293 Notify the callback function when an event is triggered. 294 295 @param[in] Event The triggered event. 296 @param[in] Context The opaque parameter to the function. 297 298 **/ 299 VOID 300 EFIAPI 301 HttpBootCommonNotify ( 302 IN EFI_EVENT Event, 303 IN VOID *Context 304 ) 305 { 306 *((BOOLEAN *) Context) = TRUE; 307 } 308 309 /** 310 Retrieve the host address using the EFI_DNS6_PROTOCOL. 311 312 @param[in] Private The pointer to the driver's private data. 313 @param[in] HostName Pointer to buffer containing hostname. 314 @param[out] IpAddress On output, pointer to buffer containing IPv6 address. 315 316 @retval EFI_SUCCESS Operation succeeded. 317 @retval EFI_DEVICE_ERROR An unexpected network error occurred. 318 @retval Others Other errors as indicated. 319 **/ 320 EFI_STATUS 321 HttpBootDns ( 322 IN HTTP_BOOT_PRIVATE_DATA *Private, 323 IN CHAR16 *HostName, 324 OUT EFI_IPv6_ADDRESS *IpAddress 325 ) 326 { 327 EFI_STATUS Status; 328 EFI_DNS6_PROTOCOL *Dns6; 329 EFI_DNS6_CONFIG_DATA Dns6ConfigData; 330 EFI_DNS6_COMPLETION_TOKEN Token; 331 EFI_HANDLE Dns6Handle; 332 EFI_IP6_CONFIG_PROTOCOL *Ip6Config; 333 EFI_IPv6_ADDRESS *DnsServerList; 334 UINTN DnsServerListCount; 335 UINTN DataSize; 336 BOOLEAN IsDone; 337 338 DnsServerList = NULL; 339 DnsServerListCount = 0; 340 Dns6 = NULL; 341 Dns6Handle = NULL; 342 ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); 343 344 // 345 // Get DNS server list from EFI IPv6 Configuration protocol. 346 // 347 Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config); 348 if (!EFI_ERROR (Status)) { 349 // 350 // Get the required size. 351 // 352 DataSize = 0; 353 Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL); 354 if (Status == EFI_BUFFER_TOO_SMALL) { 355 DnsServerList = AllocatePool (DataSize); 356 if (DnsServerList == NULL) { 357 return EFI_OUT_OF_RESOURCES; 358 } 359 360 Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList); 361 if (EFI_ERROR (Status)) { 362 FreePool (DnsServerList); 363 DnsServerList = NULL; 364 } else { 365 DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS); 366 } 367 } 368 } 369 // 370 // Create a DNSv6 child instance and get the protocol. 371 // 372 Status = NetLibCreateServiceChild ( 373 Private->Controller, 374 Private->Ip6Nic->ImageHandle, 375 &gEfiDns6ServiceBindingProtocolGuid, 376 &Dns6Handle 377 ); 378 if (EFI_ERROR (Status)) { 379 goto Exit; 380 } 381 382 Status = gBS->OpenProtocol ( 383 Dns6Handle, 384 &gEfiDns6ProtocolGuid, 385 (VOID **) &Dns6, 386 Private->Ip6Nic->ImageHandle, 387 Private->Controller, 388 EFI_OPEN_PROTOCOL_BY_DRIVER 389 ); 390 if (EFI_ERROR (Status)) { 391 goto Exit; 392 } 393 394 // 395 // Configure DNS6 instance for the DNS server address and protocol. 396 // 397 ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); 398 Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount; 399 Dns6ConfigData.DnsServerList = DnsServerList; 400 Dns6ConfigData.EnableDnsCache = TRUE; 401 Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; 402 IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6); 403 Status = Dns6->Configure ( 404 Dns6, 405 &Dns6ConfigData 406 ); 407 if (EFI_ERROR (Status)) { 408 goto Exit; 409 } 410 411 Token.Status = EFI_NOT_READY; 412 IsDone = FALSE; 413 // 414 // Create event to set the IsDone flag when name resolution is finished. 415 // 416 Status = gBS->CreateEvent ( 417 EVT_NOTIFY_SIGNAL, 418 TPL_NOTIFY, 419 HttpBootCommonNotify, 420 &IsDone, 421 &Token.Event 422 ); 423 if (EFI_ERROR (Status)) { 424 goto Exit; 425 } 426 427 // 428 // Start asynchronous name resolution. 429 // 430 Status = Dns6->HostNameToIp (Dns6, HostName, &Token); 431 if (EFI_ERROR (Status)) { 432 goto Exit; 433 } 434 435 while (!IsDone) { 436 Dns6->Poll (Dns6); 437 } 438 439 // 440 // Name resolution is done, check result. 441 // 442 Status = Token.Status; 443 if (!EFI_ERROR (Status)) { 444 if (Token.RspData.H2AData == NULL) { 445 Status = EFI_DEVICE_ERROR; 446 goto Exit; 447 } 448 if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { 449 Status = EFI_DEVICE_ERROR; 450 goto Exit; 451 } 452 // 453 // We just return the first IPv6 address from DNS protocol. 454 // 455 IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); 456 Status = EFI_SUCCESS; 457 } 458 Exit: 459 460 if (Token.Event != NULL) { 461 gBS->CloseEvent (Token.Event); 462 } 463 if (Token.RspData.H2AData != NULL) { 464 if (Token.RspData.H2AData->IpList != NULL) { 465 FreePool (Token.RspData.H2AData->IpList); 466 } 467 FreePool (Token.RspData.H2AData); 468 } 469 470 if (Dns6 != NULL) { 471 Dns6->Configure (Dns6, NULL); 472 473 gBS->CloseProtocol ( 474 Dns6Handle, 475 &gEfiDns6ProtocolGuid, 476 Private->Ip6Nic->ImageHandle, 477 Private->Controller 478 ); 479 } 480 481 if (Dns6Handle != NULL) { 482 NetLibDestroyServiceChild ( 483 Private->Controller, 484 Private->Ip6Nic->ImageHandle, 485 &gEfiDns6ServiceBindingProtocolGuid, 486 Dns6Handle 487 ); 488 } 489 490 if (DnsServerList != NULL) { 491 FreePool (DnsServerList); 492 } 493 494 return Status; 495 } 496 /** 497 Create a HTTP_IO_HEADER to hold the HTTP header items. 498 499 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder. 500 501 @return A pointer of the HTTP header holder or NULL if failed. 502 503 **/ 504 HTTP_IO_HEADER * 505 HttpBootCreateHeader ( 506 UINTN MaxHeaderCount 507 ) 508 { 509 HTTP_IO_HEADER *HttpIoHeader; 510 511 if (MaxHeaderCount == 0) { 512 return NULL; 513 } 514 515 HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER)); 516 if (HttpIoHeader == NULL) { 517 return NULL; 518 } 519 520 HttpIoHeader->MaxHeaderCount = MaxHeaderCount; 521 HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1); 522 523 return HttpIoHeader; 524 } 525 526 /** 527 Destroy the HTTP_IO_HEADER and release the resouces. 528 529 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed. 530 531 **/ 532 VOID 533 HttpBootFreeHeader ( 534 IN HTTP_IO_HEADER *HttpIoHeader 535 ) 536 { 537 UINTN Index; 538 539 if (HttpIoHeader != NULL) { 540 if (HttpIoHeader->HeaderCount != 0) { 541 for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) { 542 FreePool (HttpIoHeader->Headers[Index].FieldName); 543 FreePool (HttpIoHeader->Headers[Index].FieldValue); 544 } 545 } 546 FreePool (HttpIoHeader); 547 } 548 } 549 550 /** 551 Set or update a HTTP header with the field name and corresponding value. 552 553 @param[in] HttpIoHeader Point to the HTTP header holder. 554 @param[in] FieldName Null terminated string which describes a field name. 555 @param[in] FieldValue Null terminated string which describes the corresponding field value. 556 557 @retval EFI_SUCCESS The HTTP header has been set or updated. 558 @retval EFI_INVALID_PARAMETER Any input parameter is invalid. 559 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation. 560 @retval Other Unexpected error happened. 561 562 **/ 563 EFI_STATUS 564 HttpBootSetHeader ( 565 IN HTTP_IO_HEADER *HttpIoHeader, 566 IN CHAR8 *FieldName, 567 IN CHAR8 *FieldValue 568 ) 569 { 570 EFI_HTTP_HEADER *Header; 571 UINTN StrSize; 572 CHAR8 *NewFieldValue; 573 574 if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) { 575 return EFI_INVALID_PARAMETER; 576 } 577 578 Header = HttpFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName); 579 if (Header == NULL) { 580 // 581 // Add a new header. 582 // 583 if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) { 584 return EFI_OUT_OF_RESOURCES; 585 } 586 Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount]; 587 588 StrSize = AsciiStrSize (FieldName); 589 Header->FieldName = AllocatePool (StrSize); 590 if (Header->FieldName == NULL) { 591 return EFI_OUT_OF_RESOURCES; 592 } 593 CopyMem (Header->FieldName, FieldName, StrSize); 594 Header->FieldName[StrSize -1] = '\0'; 595 596 StrSize = AsciiStrSize (FieldValue); 597 Header->FieldValue = AllocatePool (StrSize); 598 if (Header->FieldValue == NULL) { 599 FreePool (Header->FieldName); 600 return EFI_OUT_OF_RESOURCES; 601 } 602 CopyMem (Header->FieldValue, FieldValue, StrSize); 603 Header->FieldValue[StrSize -1] = '\0'; 604 605 HttpIoHeader->HeaderCount++; 606 } else { 607 // 608 // Update an existing one. 609 // 610 StrSize = AsciiStrSize (FieldValue); 611 NewFieldValue = AllocatePool (StrSize); 612 if (NewFieldValue == NULL) { 613 return EFI_OUT_OF_RESOURCES; 614 } 615 CopyMem (NewFieldValue, FieldValue, StrSize); 616 NewFieldValue[StrSize -1] = '\0'; 617 618 if (Header->FieldValue != NULL) { 619 FreePool (Header->FieldValue); 620 } 621 Header->FieldValue = NewFieldValue; 622 } 623 624 return EFI_SUCCESS; 625 } 626 627 /** 628 Create a HTTP_IO to access the HTTP service. It will create and configure 629 a HTTP child handle. 630 631 @param[in] Image The handle of the driver image. 632 @param[in] Controller The handle of the controller. 633 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. 634 @param[in] ConfigData The HTTP_IO configuration data. 635 @param[out] HttpIo The HTTP_IO. 636 637 @retval EFI_SUCCESS The HTTP_IO is created and configured. 638 @retval EFI_INVALID_PARAMETER One or more parameters are invalid. 639 @retval EFI_UNSUPPORTED One or more of the control options are not 640 supported in the implementation. 641 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. 642 @retval Others Failed to create the HTTP_IO or configure it. 643 644 **/ 645 EFI_STATUS 646 HttpIoCreateIo ( 647 IN EFI_HANDLE Image, 648 IN EFI_HANDLE Controller, 649 IN UINT8 IpVersion, 650 IN HTTP_IO_CONFIG_DATA *ConfigData, 651 OUT HTTP_IO *HttpIo 652 ) 653 { 654 EFI_STATUS Status; 655 EFI_HTTP_CONFIG_DATA HttpConfigData; 656 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint; 657 EFI_HTTPv6_ACCESS_POINT Http6AccessPoint; 658 EFI_HTTP_PROTOCOL *Http; 659 EFI_EVENT Event; 660 661 if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) { 662 return EFI_INVALID_PARAMETER; 663 } 664 665 if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) { 666 return EFI_UNSUPPORTED; 667 } 668 669 ZeroMem (HttpIo, sizeof (HTTP_IO)); 670 671 // 672 // Create the HTTP child instance and get the HTTP protocol. 673 // 674 Status = NetLibCreateServiceChild ( 675 Controller, 676 Image, 677 &gEfiHttpServiceBindingProtocolGuid, 678 &HttpIo->Handle 679 ); 680 if (EFI_ERROR (Status)) { 681 return Status; 682 } 683 684 Status = gBS->OpenProtocol ( 685 HttpIo->Handle, 686 &gEfiHttpProtocolGuid, 687 (VOID **) &Http, 688 Image, 689 Controller, 690 EFI_OPEN_PROTOCOL_BY_DRIVER 691 ); 692 if (EFI_ERROR (Status) || (Http == NULL)) { 693 goto ON_ERROR; 694 } 695 696 // 697 // Init the configuration data and configure the HTTP child. 698 // 699 HttpIo->Image = Image; 700 HttpIo->Controller = Controller; 701 HttpIo->IpVersion = IpVersion; 702 HttpIo->Http = Http; 703 704 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA)); 705 HttpConfigData.HttpVersion = HttpVersion11; 706 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut; 707 if (HttpIo->IpVersion == IP_VERSION_4) { 708 HttpConfigData.LocalAddressIsIPv6 = FALSE; 709 710 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress; 711 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort; 712 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp); 713 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask); 714 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; 715 } else { 716 HttpConfigData.LocalAddressIsIPv6 = TRUE; 717 Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort; 718 IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp); 719 HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint; 720 } 721 722 Status = Http->Configure (Http, &HttpConfigData); 723 if (EFI_ERROR (Status)) { 724 goto ON_ERROR; 725 } 726 727 // 728 // Create events for variuos asynchronous operations. 729 // 730 Status = gBS->CreateEvent ( 731 EVT_NOTIFY_SIGNAL, 732 TPL_NOTIFY, 733 HttpBootCommonNotify, 734 &HttpIo->IsTxDone, 735 &Event 736 ); 737 if (EFI_ERROR (Status)) { 738 goto ON_ERROR; 739 } 740 HttpIo->ReqToken.Event = Event; 741 HttpIo->ReqToken.Message = &HttpIo->ReqMessage; 742 743 Status = gBS->CreateEvent ( 744 EVT_NOTIFY_SIGNAL, 745 TPL_NOTIFY, 746 HttpBootCommonNotify, 747 &HttpIo->IsRxDone, 748 &Event 749 ); 750 if (EFI_ERROR (Status)) { 751 goto ON_ERROR; 752 } 753 HttpIo->RspToken.Event = Event; 754 HttpIo->RspToken.Message = &HttpIo->RspMessage; 755 756 // 757 // Create TimeoutEvent for response 758 // 759 Status = gBS->CreateEvent ( 760 EVT_TIMER, 761 TPL_CALLBACK, 762 NULL, 763 NULL, 764 &Event 765 ); 766 if (EFI_ERROR (Status)) { 767 goto ON_ERROR; 768 } 769 HttpIo->TimeoutEvent = Event; 770 771 return EFI_SUCCESS; 772 773 ON_ERROR: 774 HttpIoDestroyIo (HttpIo); 775 776 return Status; 777 } 778 779 /** 780 Destroy the HTTP_IO and release the resouces. 781 782 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed. 783 784 **/ 785 VOID 786 HttpIoDestroyIo ( 787 IN HTTP_IO *HttpIo 788 ) 789 { 790 EFI_HTTP_PROTOCOL *Http; 791 EFI_EVENT Event; 792 793 if (HttpIo == NULL) { 794 return; 795 } 796 797 Event = HttpIo->ReqToken.Event; 798 if (Event != NULL) { 799 gBS->CloseEvent (Event); 800 } 801 802 Event = HttpIo->RspToken.Event; 803 if (Event != NULL) { 804 gBS->CloseEvent (Event); 805 } 806 807 Event = HttpIo->TimeoutEvent; 808 if (Event != NULL) { 809 gBS->CloseEvent (Event); 810 } 811 812 Http = HttpIo->Http; 813 if (Http != NULL) { 814 Http->Configure (Http, NULL); 815 gBS->CloseProtocol ( 816 HttpIo->Handle, 817 &gEfiHttpProtocolGuid, 818 HttpIo->Image, 819 HttpIo->Controller 820 ); 821 } 822 823 NetLibDestroyServiceChild ( 824 HttpIo->Controller, 825 HttpIo->Image, 826 &gEfiHttpServiceBindingProtocolGuid, 827 HttpIo->Handle 828 ); 829 } 830 831 /** 832 Synchronously send a HTTP REQUEST message to the server. 833 834 @param[in] HttpIo The HttpIo wrapping the HTTP service. 835 @param[in] Request A pointer to storage such data as URL and HTTP method. 836 @param[in] HeaderCount Number of HTTP header structures in Headers list. 837 @param[in] Headers Array containing list of HTTP headers. 838 @param[in] BodyLength Length in bytes of the HTTP body. 839 @param[in] Body Body associated with the HTTP request. 840 841 @retval EFI_SUCCESS The HTTP request is trasmitted. 842 @retval EFI_INVALID_PARAMETER One or more parameters are invalid. 843 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. 844 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. 845 @retval Others Other errors as indicated. 846 847 **/ 848 EFI_STATUS 849 HttpIoSendRequest ( 850 IN HTTP_IO *HttpIo, 851 IN EFI_HTTP_REQUEST_DATA *Request, 852 IN UINTN HeaderCount, 853 IN EFI_HTTP_HEADER *Headers, 854 IN UINTN BodyLength, 855 IN VOID *Body 856 ) 857 { 858 EFI_STATUS Status; 859 EFI_HTTP_PROTOCOL *Http; 860 861 if (HttpIo == NULL || HttpIo->Http == NULL) { 862 return EFI_INVALID_PARAMETER; 863 } 864 865 HttpIo->ReqToken.Status = EFI_NOT_READY; 866 HttpIo->ReqToken.Message->Data.Request = Request; 867 HttpIo->ReqToken.Message->HeaderCount = HeaderCount; 868 HttpIo->ReqToken.Message->Headers = Headers; 869 HttpIo->ReqToken.Message->BodyLength = BodyLength; 870 HttpIo->ReqToken.Message->Body = Body; 871 872 // 873 // Queue the request token to HTTP instances. 874 // 875 Http = HttpIo->Http; 876 HttpIo->IsTxDone = FALSE; 877 Status = Http->Request ( 878 Http, 879 &HttpIo->ReqToken 880 ); 881 if (EFI_ERROR (Status)) { 882 return Status; 883 } 884 885 // 886 // Poll the network until transmit finish. 887 // 888 while (!HttpIo->IsTxDone) { 889 Http->Poll (Http); 890 } 891 892 return HttpIo->ReqToken.Status; 893 } 894 895 /** 896 Synchronously receive a HTTP RESPONSE message from the server. 897 898 @param[in] HttpIo The HttpIo wrapping the HTTP service. 899 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header). 900 FALSE to continue receive the previous response message. 901 @param[out] ResponseData Point to a wrapper of the received response data. 902 903 @retval EFI_SUCCESS The HTTP response is received. 904 @retval EFI_INVALID_PARAMETER One or more parameters are invalid. 905 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. 906 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. 907 @retval Others Other errors as indicated. 908 909 **/ 910 EFI_STATUS 911 HttpIoRecvResponse ( 912 IN HTTP_IO *HttpIo, 913 IN BOOLEAN RecvMsgHeader, 914 OUT HTTP_IO_RESPONSE_DATA *ResponseData 915 ) 916 { 917 EFI_STATUS Status; 918 EFI_HTTP_PROTOCOL *Http; 919 920 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) { 921 return EFI_INVALID_PARAMETER; 922 } 923 924 // 925 // Start the timer, and wait Timeout seconds to receive the header packet. 926 // 927 Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HTTP_BOOT_RESPONSE_TIMEOUT * TICKS_PER_MS); 928 if (EFI_ERROR (Status)) { 929 return Status; 930 } 931 932 // 933 // Queue the response token to HTTP instances. 934 // 935 HttpIo->RspToken.Status = EFI_NOT_READY; 936 if (RecvMsgHeader) { 937 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response; 938 } else { 939 HttpIo->RspToken.Message->Data.Response = NULL; 940 } 941 HttpIo->RspToken.Message->HeaderCount = 0; 942 HttpIo->RspToken.Message->Headers = NULL; 943 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength; 944 HttpIo->RspToken.Message->Body = ResponseData->Body; 945 946 Http = HttpIo->Http; 947 HttpIo->IsRxDone = FALSE; 948 Status = Http->Response ( 949 Http, 950 &HttpIo->RspToken 951 ); 952 953 if (EFI_ERROR (Status)) { 954 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0); 955 return Status; 956 } 957 958 // 959 // Poll the network until receive finish. 960 // 961 while (!HttpIo->IsRxDone && ((HttpIo->TimeoutEvent == NULL) || EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent)))) { 962 Http->Poll (Http); 963 } 964 965 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0); 966 967 if (!HttpIo->IsRxDone) { 968 // 969 // Timeout occurs, cancel the response token. 970 // 971 Http->Cancel (Http, &HttpIo->RspToken); 972 973 Status = EFI_TIMEOUT; 974 975 return Status; 976 } else { 977 HttpIo->IsRxDone = FALSE; 978 } 979 980 // 981 // Store the received data into the wrapper. 982 // 983 ResponseData->Status = HttpIo->RspToken.Status; 984 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount; 985 ResponseData->Headers = HttpIo->RspToken.Message->Headers; 986 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength; 987 988 return Status; 989 } 990 991 /** 992 Get the URI address string from the input device path. 993 994 Caller need to free the buffer in the UriAddress pointer. 995 996 @param[in] FilePath Pointer to the device path which contains a URI device path node. 997 @param[out] UriAddress The URI address string extract from the device path. 998 999 @retval EFI_SUCCESS The URI string is returned. 1000 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. 1001 1002 **/ 1003 EFI_STATUS 1004 HttpBootParseFilePath ( 1005 IN EFI_DEVICE_PATH_PROTOCOL *FilePath, 1006 OUT CHAR8 **UriAddress 1007 ) 1008 { 1009 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; 1010 URI_DEVICE_PATH *UriDevicePath; 1011 CHAR8 *Uri; 1012 UINTN UriStrLength; 1013 1014 if (FilePath == NULL) { 1015 return EFI_INVALID_PARAMETER; 1016 } 1017 1018 *UriAddress = NULL; 1019 1020 // 1021 // Extract the URI address from the FilePath 1022 // 1023 TempDevicePath = FilePath; 1024 while (!IsDevicePathEnd (TempDevicePath)) { 1025 if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) && 1026 (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) { 1027 UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath; 1028 // 1029 // UEFI Spec doesn't require the URI to be a NULL-terminated string 1030 // So we allocate a new buffer and always append a '\0' to it. 1031 // 1032 UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); 1033 if (UriStrLength == 0) { 1034 // 1035 // return a NULL UriAddress if it's a empty URI device path node. 1036 // 1037 break; 1038 } 1039 Uri = AllocatePool (UriStrLength + 1); 1040 if (Uri == NULL) { 1041 return EFI_OUT_OF_RESOURCES; 1042 } 1043 CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)); 1044 Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0'; 1045 1046 *UriAddress = Uri; 1047 } 1048 TempDevicePath = NextDevicePathNode (TempDevicePath); 1049 } 1050 1051 return EFI_SUCCESS; 1052 } 1053 1054 /** 1055 This function returns the image type according to server replied HTTP message 1056 and also the image's URI info. 1057 1058 @param[in] Uri The pointer to the image's URI string. 1059 @param[in] UriParser URI Parse result returned by NetHttpParseUrl(). 1060 @param[in] HeaderCount Number of HTTP header structures in Headers list. 1061 @param[in] Headers Array containing list of HTTP headers. 1062 @param[out] ImageType The image type of the downloaded file. 1063 1064 @retval EFI_SUCCESS The image type is returned in ImageType. 1065 @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL. 1066 @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL. 1067 @retval EFI_NOT_FOUND Failed to identify the image type. 1068 @retval Others Unexpect error happened. 1069 1070 **/ 1071 EFI_STATUS 1072 HttpBootCheckImageType ( 1073 IN CHAR8 *Uri, 1074 IN VOID *UriParser, 1075 IN UINTN HeaderCount, 1076 IN EFI_HTTP_HEADER *Headers, 1077 OUT HTTP_BOOT_IMAGE_TYPE *ImageType 1078 ) 1079 { 1080 EFI_STATUS Status; 1081 EFI_HTTP_HEADER *Header; 1082 CHAR8 *FilePath; 1083 CHAR8 *FilePost; 1084 1085 if (Uri == NULL || UriParser == NULL || ImageType == NULL) { 1086 return EFI_INVALID_PARAMETER; 1087 } 1088 1089 if (HeaderCount != 0 && Headers == NULL) { 1090 return EFI_INVALID_PARAMETER; 1091 } 1092 1093 // 1094 // Determine the image type by the HTTP Content-Type header field first. 1095 // "application/efi" -> EFI Image 1096 // 1097 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE); 1098 if (Header != NULL) { 1099 if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) { 1100 *ImageType = ImageTypeEfi; 1101 return EFI_SUCCESS; 1102 } 1103 } 1104 1105 // 1106 // Determine the image type by file extension: 1107 // *.efi -> EFI Image 1108 // *.iso -> CD/DVD Image 1109 // *.img -> Virtual Disk Image 1110 // 1111 Status = HttpUrlGetPath ( 1112 Uri, 1113 UriParser, 1114 &FilePath 1115 ); 1116 if (EFI_ERROR (Status)) { 1117 return Status; 1118 } 1119 1120 FilePost = FilePath + AsciiStrLen (FilePath) - 4; 1121 if (AsciiStrCmp (FilePost, ".efi") == 0) { 1122 *ImageType = ImageTypeEfi; 1123 } else if (AsciiStrCmp (FilePost, ".iso") == 0) { 1124 *ImageType = ImageTypeVirtualCd; 1125 } else if (AsciiStrCmp (FilePost, ".img") == 0) { 1126 *ImageType = ImageTypeVirtualDisk; 1127 } else { 1128 *ImageType = ImageTypeMax; 1129 } 1130 1131 FreePool (FilePath); 1132 1133 return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND; 1134 } 1135 1136 /** 1137 This function register the RAM disk info to the system. 1138 1139 @param[in] Private The pointer to the driver's private data. 1140 @param[in] BufferSize The size of Buffer in bytes. 1141 @param[in] Buffer The base address of the RAM disk. 1142 @param[in] ImageType The image type of the file in Buffer. 1143 1144 @retval EFI_SUCCESS The RAM disk has been registered. 1145 @retval EFI_NOT_FOUND No RAM disk protocol instances were found. 1146 @retval EFI_UNSUPPORTED The ImageType is not supported. 1147 @retval Others Unexpected error happened. 1148 1149 **/ 1150 EFI_STATUS 1151 HttpBootRegisterRamDisk ( 1152 IN HTTP_BOOT_PRIVATE_DATA *Private, 1153 IN UINTN BufferSize, 1154 IN VOID *Buffer, 1155 IN HTTP_BOOT_IMAGE_TYPE ImageType 1156 ) 1157 { 1158 EFI_RAM_DISK_PROTOCOL *RamDisk; 1159 EFI_STATUS Status; 1160 EFI_DEVICE_PATH_PROTOCOL *DevicePath; 1161 EFI_GUID *RamDiskType; 1162 1163 ASSERT (Private != NULL); 1164 ASSERT (Buffer != NULL); 1165 ASSERT (BufferSize != 0); 1166 1167 Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk); 1168 if (EFI_ERROR (Status)) { 1169 DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status)); 1170 return Status; 1171 } 1172 1173 if (ImageType == ImageTypeVirtualCd) { 1174 RamDiskType = &gEfiVirtualCdGuid; 1175 } else if (ImageType == ImageTypeVirtualDisk) { 1176 RamDiskType = &gEfiVirtualDiskGuid; 1177 } else { 1178 return EFI_UNSUPPORTED; 1179 } 1180 1181 Status = RamDisk->Register ( 1182 (UINTN)Buffer, 1183 (UINT64)BufferSize, 1184 RamDiskType, 1185 Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath, 1186 &DevicePath 1187 ); 1188 if (EFI_ERROR (Status)) { 1189 DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status)); 1190 } 1191 1192 return Status; 1193 } 1194 1195