Home | History | Annotate | Download | only in HttpBootDxe
      1 /** @file
      2   The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
      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   Enable the use of UEFI HTTP boot function.
     20 
     21   If the driver has already been started but not satisfy the requirement (IP stack and
     22   specified boot file path), this function will stop the driver and start it again.
     23 
     24   @param[in]    Private            The pointer to the driver's private data.
     25   @param[in]    UsingIpv6          Specifies the type of IP addresses that are to be
     26                                    used during the session that is being started.
     27                                    Set to TRUE for IPv6, and FALSE for IPv4.
     28   @param[in]    FilePath           The device specific path of the file to load.
     29 
     30   @retval EFI_SUCCESS              HTTP boot was successfully enabled.
     31   @retval EFI_INVALID_PARAMETER    Private is NULL or FilePath is NULL.
     32   @retval EFI_INVALID_PARAMETER    The FilePath doesn't contain a valid URI device path node.
     33   @retval EFI_ALREADY_STARTED      The driver is already in started state.
     34   @retval EFI_OUT_OF_RESOURCES     There are not enough resources.
     35 
     36 **/
     37 EFI_STATUS
     38 HttpBootStart (
     39   IN HTTP_BOOT_PRIVATE_DATA           *Private,
     40   IN BOOLEAN                          UsingIpv6,
     41   IN EFI_DEVICE_PATH_PROTOCOL         *FilePath
     42   )
     43 {
     44   UINTN                Index;
     45   EFI_STATUS           Status;
     46   CHAR8                *Uri;
     47 
     48 
     49   if (Private == NULL || FilePath == NULL) {
     50     return EFI_INVALID_PARAMETER;
     51   }
     52 
     53   //
     54   // Check the URI in the input FilePath, in order to see whether it is
     55   // required to boot from a new specified boot file.
     56   //
     57   Status = HttpBootParseFilePath (FilePath, &Uri);
     58   if (EFI_ERROR (Status)) {
     59     return EFI_INVALID_PARAMETER;
     60   }
     61 
     62   //
     63   // Check whether we need to stop and restart the HTTP boot driver.
     64   //
     65   if (Private->Started) {
     66     //
     67     // Restart is needed in 2 cases:
     68     // 1. Http boot driver has already been started but not on the required IP stack.
     69     // 2. The specified boot file URI in FilePath is different with the one we have
     70     // recorded before.
     71     //
     72     if ((UsingIpv6 != Private->UsingIpv6) ||
     73         ((Uri != NULL) && (AsciiStrCmp (Private->BootFileUri, Uri) != 0))) {
     74       //
     75       // Restart is required, first stop then continue this start function.
     76       //
     77       Status = HttpBootStop (Private);
     78       if (EFI_ERROR (Status)) {
     79         return Status;
     80       }
     81     } else {
     82       //
     83       // Restart is not required.
     84       //
     85       if (Uri != NULL) {
     86         FreePool (Uri);
     87       }
     88       return EFI_ALREADY_STARTED;
     89     }
     90   }
     91 
     92   //
     93   // Detect whether using ipv6 or not, and set it to the private data.
     94   //
     95   if (UsingIpv6 && Private->Ip6Nic != NULL) {
     96     Private->UsingIpv6 = TRUE;
     97   } else if (!UsingIpv6 && Private->Ip4Nic != NULL) {
     98     Private->UsingIpv6 = FALSE;
     99   } else {
    100     if (Uri != NULL) {
    101       FreePool (Uri);
    102     }
    103     return EFI_UNSUPPORTED;
    104   }
    105 
    106   //
    107   // Record the specified URI and prepare the URI parser if needed.
    108   //
    109   Private->FilePathUri = Uri;
    110   if (Private->FilePathUri != NULL) {
    111     Status = HttpParseUrl (
    112                Private->FilePathUri,
    113                (UINT32) AsciiStrLen (Private->FilePathUri),
    114                FALSE,
    115                &Private->FilePathUriParser
    116                );
    117     if (EFI_ERROR (Status)) {
    118       FreePool (Private->FilePathUri);
    119       return Status;
    120     }
    121   }
    122 
    123   //
    124   // Init the content of cached DHCP offer list.
    125   //
    126   ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
    127   if (!Private->UsingIpv6) {
    128     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
    129       Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_CACHED_DHCP4_PACKET_MAX_SIZE;
    130     }
    131   } else {
    132     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
    133       Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_CACHED_DHCP6_PACKET_MAX_SIZE;
    134     }
    135   }
    136 
    137   if (Private->UsingIpv6) {
    138     //
    139     // Set Ip6 policy to Automatic to start the Ip6 router discovery.
    140     //
    141     Status = HttpBootSetIp6Policy (Private);
    142     if (EFI_ERROR (Status)) {
    143       return Status;
    144     }
    145   }
    146   Private->Started   = TRUE;
    147 
    148   return EFI_SUCCESS;
    149 }
    150 
    151 /**
    152   Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
    153 
    154   @param[in]    Private            The pointer to the driver's private data.
    155 
    156   @retval EFI_SUCCESS              Boot info was successfully retrieved.
    157   @retval EFI_INVALID_PARAMETER    Private is NULL.
    158   @retval EFI_NOT_STARTED          The driver is in stopped state.
    159   @retval EFI_DEVICE_ERROR         An unexpected network error occurred.
    160   @retval Others                   Other errors as indicated.
    161 
    162 **/
    163 EFI_STATUS
    164 HttpBootDhcp (
    165   IN HTTP_BOOT_PRIVATE_DATA           *Private
    166   )
    167 {
    168   EFI_STATUS                Status;
    169 
    170   if (Private == NULL) {
    171     return EFI_INVALID_PARAMETER;
    172   }
    173 
    174   if (!Private->Started) {
    175     return EFI_NOT_STARTED;
    176   }
    177 
    178   Status = EFI_DEVICE_ERROR;
    179 
    180   if (!Private->UsingIpv6) {
    181     //
    182     // Start D.O.R.A process to get a IPv4 address and other boot information.
    183     //
    184     Status = HttpBootDhcp4Dora (Private);
    185   } else {
    186      //
    187     // Start S.A.R.R process to get a IPv6 address and other boot information.
    188     //
    189     Status = HttpBootDhcp6Sarr (Private);
    190   }
    191 
    192   return Status;
    193 }
    194 
    195 /**
    196   Attempt to download the boot file through HTTP message exchange.
    197 
    198   @param[in]          Private         The pointer to the driver's private data.
    199   @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return
    200                                       code of EFI_SUCCESS, the amount of data transferred to
    201                                       Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
    202                                       the size of Buffer required to retrieve the requested file.
    203   @param[in]          Buffer          The memory buffer to transfer the file to. If Buffer is NULL,
    204                                       then the size of the requested file is returned in
    205                                       BufferSize.
    206   @param[out]         ImageType       The image type of the downloaded file.
    207 
    208   @retval EFI_SUCCESS                 Boot file was loaded successfully.
    209   @retval EFI_INVALID_PARAMETER       Private is NULL, or ImageType is NULL, or BufferSize is NULL.
    210   @retval EFI_INVALID_PARAMETER       *BufferSize is not zero, and Buffer is NULL.
    211   @retval EFI_NOT_STARTED             The driver is in stopped state.
    212   @retval EFI_BUFFER_TOO_SMALL        The BufferSize is too small to read the boot file. BufferSize has
    213                                       been updated with the size needed to complete the request.
    214   @retval EFI_DEVICE_ERROR            An unexpected network error occurred.
    215   @retval Others                      Other errors as indicated.
    216 
    217 **/
    218 EFI_STATUS
    219 HttpBootLoadFile (
    220   IN     HTTP_BOOT_PRIVATE_DATA       *Private,
    221   IN OUT UINTN                        *BufferSize,
    222   IN     VOID                         *Buffer,       OPTIONAL
    223      OUT HTTP_BOOT_IMAGE_TYPE         *ImageType
    224   )
    225 {
    226   EFI_STATUS             Status;
    227 
    228   if (Private == NULL || ImageType == NULL || BufferSize == NULL ) {
    229     return EFI_INVALID_PARAMETER;
    230   }
    231 
    232   if (*BufferSize != 0 && Buffer == NULL) {
    233     return EFI_INVALID_PARAMETER;
    234   }
    235 
    236   if (!Private->Started) {
    237     return EFI_NOT_STARTED;
    238   }
    239 
    240   Status = EFI_DEVICE_ERROR;
    241 
    242   if (Private->BootFileUri == NULL) {
    243     //
    244     // Parse the cached offer to get the boot file URL first.
    245     //
    246     Status = HttpBootDiscoverBootInfo (Private);
    247     if (EFI_ERROR (Status)) {
    248       return Status;
    249     }
    250   }
    251 
    252   if (!Private->HttpCreated) {
    253     //
    254     // Create HTTP child.
    255     //
    256     Status = HttpBootCreateHttpIo (Private);
    257     if (EFI_ERROR (Status)) {
    258       return Status;
    259     }
    260   }
    261 
    262   if (Private->BootFileSize == 0) {
    263     //
    264     // Discover the information about the bootfile if we haven't.
    265     //
    266 
    267     //
    268     // Try to use HTTP HEAD method.
    269     //
    270     Status = HttpBootGetBootFile (
    271                Private,
    272                TRUE,
    273                &Private->BootFileSize,
    274                NULL,
    275                &Private->ImageType
    276                );
    277     if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
    278       //
    279       // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
    280       //
    281       ASSERT (Private->BootFileSize == 0);
    282       Status = HttpBootGetBootFile (
    283                  Private,
    284                  FALSE,
    285                  &Private->BootFileSize,
    286                  NULL,
    287                  &Private->ImageType
    288                  );
    289       if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
    290         return Status;
    291       }
    292     }
    293   }
    294 
    295   if (*BufferSize < Private->BootFileSize) {
    296     *BufferSize = Private->BootFileSize;
    297     *ImageType = Private->ImageType;
    298     return EFI_BUFFER_TOO_SMALL;
    299   }
    300 
    301   //
    302   // Load the boot file into Buffer
    303   //
    304   return  HttpBootGetBootFile (
    305             Private,
    306             FALSE,
    307             BufferSize,
    308             Buffer,
    309             ImageType
    310             );
    311 }
    312 
    313 /**
    314   Disable the use of UEFI HTTP boot function.
    315 
    316   @param[in]    Private            The pointer to the driver's private data.
    317 
    318   @retval EFI_SUCCESS              HTTP boot was successfully disabled.
    319   @retval EFI_NOT_STARTED          The driver is already in stopped state.
    320   @retval EFI_INVALID_PARAMETER    Private is NULL.
    321   @retval Others                   Unexpected error when stop the function.
    322 
    323 **/
    324 EFI_STATUS
    325 HttpBootStop (
    326   IN HTTP_BOOT_PRIVATE_DATA           *Private
    327   )
    328 {
    329   UINTN            Index;
    330 
    331   if (Private == NULL) {
    332     return EFI_INVALID_PARAMETER;
    333   }
    334 
    335   if (!Private->Started) {
    336     return EFI_NOT_STARTED;
    337   }
    338 
    339   if (Private->HttpCreated) {
    340     HttpIoDestroyIo (&Private->HttpIo);
    341     Private->HttpCreated = FALSE;
    342   }
    343 
    344   Private->Started = FALSE;
    345   ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
    346   ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
    347   ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
    348   Private->Port = 0;
    349   Private->BootFileUri = NULL;
    350   Private->BootFileUriParser = NULL;
    351   Private->BootFileSize = 0;
    352   Private->SelectIndex = 0;
    353   Private->SelectProxyType = HttpOfferTypeMax;
    354 
    355   if (!Private->UsingIpv6) {
    356     //
    357     // Stop and release the DHCP4 child.
    358     //
    359     Private->Dhcp4->Stop (Private->Dhcp4);
    360     Private->Dhcp4->Configure (Private->Dhcp4, NULL);
    361 
    362     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
    363       if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
    364         HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
    365       }
    366     }
    367   } else {
    368     //
    369     // Stop and release the DHCP6 child.
    370     //
    371     Private->Dhcp6->Stop (Private->Dhcp6);
    372     Private->Dhcp6->Configure (Private->Dhcp6, NULL);
    373 
    374     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
    375       if (Private->OfferBuffer[Index].Dhcp6.UriParser) {
    376         HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser);
    377       }
    378     }
    379   }
    380 
    381   if (Private->FilePathUri!= NULL) {
    382     FreePool (Private->FilePathUri);
    383     HttpUrlFreeParser (Private->FilePathUriParser);
    384     Private->FilePathUri = NULL;
    385     Private->FilePathUriParser = NULL;
    386   }
    387 
    388   ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
    389   Private->OfferNum = 0;
    390   ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
    391   ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
    392 
    393   HttpBootFreeCacheList (Private);
    394 
    395   return EFI_SUCCESS;
    396 }
    397 
    398 /**
    399   Causes the driver to load a specified file.
    400 
    401   @param  This       Protocol instance pointer.
    402   @param  FilePath   The device specific path of the file to load.
    403   @param  BootPolicy If TRUE, indicates that the request originates from the
    404                      boot manager is attempting to load FilePath as a boot
    405                      selection. If FALSE, then FilePath must match as exact file
    406                      to be loaded.
    407   @param  BufferSize On input the size of Buffer in bytes. On output with a return
    408                      code of EFI_SUCCESS, the amount of data transferred to
    409                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
    410                      the size of Buffer required to retrieve the requested file.
    411   @param  Buffer     The memory buffer to transfer the file to. IF Buffer is NULL,
    412                      then the size of the requested file is returned in
    413                      BufferSize.
    414 
    415   @retval EFI_SUCCESS           The file was loaded.
    416   @retval EFI_UNSUPPORTED       The device does not support the provided BootPolicy
    417   @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
    418                                 BufferSize is NULL.
    419   @retval EFI_NO_MEDIA          No medium was present to load the file.
    420   @retval EFI_DEVICE_ERROR      The file was not loaded due to a device error.
    421   @retval EFI_NO_RESPONSE       The remote system did not respond.
    422   @retval EFI_NOT_FOUND         The file was not found.
    423   @retval EFI_ABORTED           The file load process was manually cancelled.
    424   @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to read the current directory entry.
    425                                 BufferSize has been updated with the size needed to complete
    426                                 the request.
    427 
    428 **/
    429 EFI_STATUS
    430 EFIAPI
    431 HttpBootDxeLoadFile (
    432   IN EFI_LOAD_FILE_PROTOCOL           *This,
    433   IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
    434   IN BOOLEAN                          BootPolicy,
    435   IN OUT UINTN                        *BufferSize,
    436   IN VOID                             *Buffer OPTIONAL
    437   )
    438 {
    439   HTTP_BOOT_PRIVATE_DATA        *Private;
    440   HTTP_BOOT_VIRTUAL_NIC         *VirtualNic;
    441   BOOLEAN                       MediaPresent;
    442   BOOLEAN                       UsingIpv6;
    443   EFI_STATUS                    Status;
    444   HTTP_BOOT_IMAGE_TYPE          ImageType;
    445 
    446   if (This == NULL || BufferSize == NULL || FilePath == NULL) {
    447     return EFI_INVALID_PARAMETER;
    448   }
    449 
    450   //
    451   // Only support BootPolicy
    452   //
    453   if (!BootPolicy) {
    454     return EFI_UNSUPPORTED;
    455   }
    456 
    457   VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);
    458   Private = VirtualNic->Private;
    459 
    460   //
    461   // Check media status before HTTP boot start
    462   //
    463   MediaPresent = TRUE;
    464   NetLibDetectMedia (Private->Controller, &MediaPresent);
    465   if (!MediaPresent) {
    466     return EFI_NO_MEDIA;
    467   }
    468 
    469   //
    470   // Check whether the virtual nic is using IPv6 or not.
    471   //
    472   UsingIpv6 = FALSE;
    473   if (VirtualNic == Private->Ip6Nic) {
    474     UsingIpv6 = TRUE;
    475   }
    476 
    477   //
    478   // Initialize HTTP boot.
    479   //
    480   Status = HttpBootStart (Private, UsingIpv6, FilePath);
    481   if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
    482     return Status;
    483   }
    484 
    485   //
    486   // Load the boot file.
    487   //
    488   ImageType = ImageTypeMax;
    489   Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType);
    490   if (EFI_ERROR (Status)) {
    491     if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) {
    492       Status = EFI_WARN_FILE_SYSTEM;
    493     } else if (Status != EFI_BUFFER_TOO_SMALL) {
    494       HttpBootStop (Private);
    495     }
    496     return Status;
    497   }
    498 
    499   //
    500   // Register the RAM Disk to the system if needed.
    501   //
    502   if (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk) {
    503     Status = HttpBootRegisterRamDisk (Private, *BufferSize, Buffer, ImageType);
    504     if (!EFI_ERROR (Status)) {
    505       Status = EFI_WARN_FILE_SYSTEM;
    506     }
    507   }
    508 
    509   //
    510   // Stop the HTTP Boot service after the boot image is downloaded.
    511   //
    512   HttpBootStop (Private);
    513   return Status;
    514 }
    515 
    516 ///
    517 /// Load File Protocol instance
    518 ///
    519 GLOBAL_REMOVE_IF_UNREFERENCED
    520 EFI_LOAD_FILE_PROTOCOL  gHttpBootDxeLoadFile = {
    521   HttpBootDxeLoadFile
    522 };
    523