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, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials are licensed and made available under
      6 the terms and conditions of the BSD License that accompanies this distribution.
      7 The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php.
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "HttpBootDxe.h"
     16 
     17 /**
     18   Enable the use of UEFI HTTP boot function.
     19 
     20   @param[in]    Private            The pointer to the driver's private data.
     21   @param[in]    UsingIpv6          Specifies the type of IP addresses that are to be
     22                                    used during the session that is being started.
     23                                    Set to TRUE for IPv6, and FALSE for IPv4.
     24 
     25   @retval EFI_SUCCESS              HTTP boot was successfully enabled.
     26   @retval EFI_INVALID_PARAMETER    Private is NULL.
     27   @retval EFI_ALREADY_STARTED      The driver is already in started state.
     28 
     29 **/
     30 EFI_STATUS
     31 HttpBootStart (
     32   IN HTTP_BOOT_PRIVATE_DATA           *Private,
     33   IN BOOLEAN                          UsingIpv6
     34   )
     35 {
     36   UINTN                Index;
     37   EFI_STATUS           Status;
     38 
     39   if (Private == NULL) {
     40     return EFI_INVALID_PARAMETER;
     41   }
     42 
     43   if (Private->Started) {
     44     return EFI_ALREADY_STARTED;
     45   }
     46 
     47   //
     48   // Detect whether using ipv6 or not, and set it to the private data.
     49   //
     50   if (UsingIpv6 && Private->Ip6Nic != NULL) {
     51     Private->UsingIpv6 = TRUE;
     52   } else if (!UsingIpv6 && Private->Ip4Nic != NULL) {
     53     Private->UsingIpv6 = FALSE;
     54   } else {
     55     return EFI_UNSUPPORTED;
     56   }
     57 
     58   //
     59   // Init the content of cached DHCP offer list.
     60   //
     61   ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
     62   if (!Private->UsingIpv6) {
     63     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
     64       Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_BOOT_DHCP4_PACKET_MAX_SIZE;
     65     }
     66   } else {
     67     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
     68       Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_BOOT_DHCP6_PACKET_MAX_SIZE;
     69     }
     70   }
     71 
     72   if (Private->UsingIpv6) {
     73     //
     74     // Set Ip6 policy to Automatic to start the Ip6 router discovery.
     75     //
     76     Status = HttpBootSetIp6Policy (Private);
     77     if (EFI_ERROR (Status)) {
     78       return Status;
     79     }
     80   }
     81   Private->Started = TRUE;
     82 
     83   return EFI_SUCCESS;
     84 }
     85 
     86 /**
     87   Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
     88 
     89   @param[in]    Private            The pointer to the driver's private data.
     90 
     91   @retval EFI_SUCCESS              Boot info was successfully retrieved.
     92   @retval EFI_INVALID_PARAMETER    Private is NULL.
     93   @retval EFI_NOT_STARTED          The driver is in stopped state.
     94   @retval EFI_DEVICE_ERROR         An unexpected network error occurred.
     95   @retval Others                   Other errors as indicated.
     96 
     97 **/
     98 EFI_STATUS
     99 HttpBootDhcp (
    100   IN HTTP_BOOT_PRIVATE_DATA           *Private
    101   )
    102 {
    103   EFI_STATUS                Status;
    104 
    105   if (Private == NULL) {
    106     return EFI_INVALID_PARAMETER;
    107   }
    108 
    109   if (!Private->Started) {
    110     return EFI_NOT_STARTED;
    111   }
    112 
    113   Status = EFI_DEVICE_ERROR;
    114 
    115   if (!Private->UsingIpv6) {
    116     //
    117     // Start D.O.R.A process to get a IPv4 address and other boot information.
    118     //
    119     Status = HttpBootDhcp4Dora (Private);
    120   } else {
    121      //
    122     // Start S.A.R.R process to get a IPv6 address and other boot information.
    123     //
    124     Status = HttpBootDhcp6Sarr (Private);
    125   }
    126 
    127   return Status;
    128 }
    129 
    130 /**
    131   Attempt to download the boot file through HTTP message exchange.
    132 
    133   @param[in]          Private         The pointer to the driver's private data.
    134   @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return
    135                                       code of EFI_SUCCESS, the amount of data transferred to
    136                                       Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
    137                                       the size of Buffer required to retrieve the requested file.
    138   @param[in]          Buffer          The memory buffer to transfer the file to. If Buffer is NULL,
    139                                       then the size of the requested file is returned in
    140                                       BufferSize.
    141 
    142   @retval EFI_SUCCESS                 Boot file was loaded successfully.
    143   @retval EFI_INVALID_PARAMETER       Private is NULL.
    144   @retval EFI_NOT_STARTED             The driver is in stopped state.
    145   @retval EFI_BUFFER_TOO_SMALL        The BufferSize is too small to read the boot file. BufferSize has
    146                                       been updated with the size needed to complete the request.
    147   @retval EFI_DEVICE_ERROR            An unexpected network error occurred.
    148   @retval Others                      Other errors as indicated.
    149 
    150 **/
    151 EFI_STATUS
    152 HttpBootLoadFile (
    153   IN     HTTP_BOOT_PRIVATE_DATA       *Private,
    154   IN OUT UINTN                        *BufferSize,
    155   IN     VOID                         *Buffer       OPTIONAL
    156   )
    157 {
    158   EFI_STATUS             Status;
    159 
    160   if (Private == NULL) {
    161     return EFI_INVALID_PARAMETER;
    162   }
    163 
    164   if (!Private->Started) {
    165     return EFI_NOT_STARTED;
    166   }
    167 
    168   Status = EFI_DEVICE_ERROR;
    169 
    170   if (Private->BootFileUri == NULL) {
    171     //
    172     // Parse the cached offer to get the boot file URL first.
    173     //
    174     Status = HttpBootDiscoverBootInfo (Private);
    175     if (EFI_ERROR (Status)) {
    176       return Status;
    177     }
    178   }
    179 
    180   if (!Private->HttpCreated) {
    181     //
    182     // Create HTTP child.
    183     //
    184     Status = HttpBootCreateHttpIo (Private);
    185     if (EFI_ERROR (Status)) {
    186       return Status;
    187     }
    188   }
    189 
    190   if (Private->BootFileSize == 0) {
    191     //
    192     // Discover the information about the bootfile if we haven't.
    193     //
    194 
    195     //
    196     // Try to use HTTP HEAD method.
    197     //
    198     Status = HttpBootGetBootFile (
    199                Private,
    200                TRUE,
    201                &Private->BootFileSize,
    202                NULL
    203                );
    204     if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
    205       //
    206       // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
    207       //
    208       ASSERT (Private->BootFileSize == 0);
    209       Status = HttpBootGetBootFile (
    210                  Private,
    211                  FALSE,
    212                  &Private->BootFileSize,
    213                  NULL
    214                  );
    215       if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
    216         return Status;
    217       }
    218     }
    219   }
    220 
    221   if (*BufferSize < Private->BootFileSize) {
    222     *BufferSize = Private->BootFileSize;
    223     return EFI_BUFFER_TOO_SMALL;
    224   }
    225 
    226   //
    227   // Load the boot file into Buffer
    228   //
    229   return  HttpBootGetBootFile (
    230             Private,
    231             FALSE,
    232             BufferSize,
    233             Buffer
    234             );
    235 }
    236 
    237 /**
    238   Disable the use of UEFI HTTP boot function.
    239 
    240   @param[in]    Private            The pointer to the driver's private data.
    241 
    242   @retval EFI_SUCCESS              HTTP boot was successfully disabled.
    243   @retval EFI_NOT_STARTED          The driver is already in stopped state.
    244   @retval EFI_INVALID_PARAMETER    Private is NULL.
    245   @retval Others                   Unexpected error when stop the function.
    246 
    247 **/
    248 EFI_STATUS
    249 HttpBootStop (
    250   IN HTTP_BOOT_PRIVATE_DATA           *Private
    251   )
    252 {
    253   UINTN            Index;
    254 
    255   if (Private == NULL) {
    256     return EFI_INVALID_PARAMETER;
    257   }
    258 
    259   if (!Private->Started) {
    260     return EFI_NOT_STARTED;
    261   }
    262 
    263   if (Private->HttpCreated) {
    264     HttpIoDestroyIo (&Private->HttpIo);
    265     Private->HttpCreated = FALSE;
    266   }
    267 
    268   Private->Started = FALSE;
    269   ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
    270   ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
    271   ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
    272   Private->Port = 0;
    273   Private->BootFileUri = NULL;
    274   Private->BootFileUriParser = NULL;
    275   Private->BootFileSize = 0;
    276   Private->SelectIndex = 0;
    277   Private->SelectProxyType = HttpOfferTypeMax;
    278 
    279   if (!Private->UsingIpv6) {
    280     //
    281     // Stop and release the DHCP4 child.
    282     //
    283     Private->Dhcp4->Stop (Private->Dhcp4);
    284     Private->Dhcp4->Configure (Private->Dhcp4, NULL);
    285 
    286     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
    287       if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
    288         HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
    289       }
    290     }
    291   } else {
    292     //
    293     // Stop and release the DHCP6 child.
    294     //
    295     Private->Dhcp6->Stop (Private->Dhcp6);
    296     Private->Dhcp6->Configure (Private->Dhcp6, NULL);
    297 
    298     for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
    299       if (Private->OfferBuffer[Index].Dhcp6.UriParser) {
    300         HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser);
    301       }
    302     }
    303   }
    304 
    305   ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
    306   Private->OfferNum = 0;
    307   ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
    308   ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
    309 
    310   return EFI_SUCCESS;
    311 }
    312 
    313 /**
    314   Causes the driver to load a specified file.
    315 
    316   @param  This       Protocol instance pointer.
    317   @param  FilePath   The device specific path of the file to load.
    318   @param  BootPolicy If TRUE, indicates that the request originates from the
    319                      boot manager is attempting to load FilePath as a boot
    320                      selection. If FALSE, then FilePath must match as exact file
    321                      to be loaded.
    322   @param  BufferSize On input the size of Buffer in bytes. On output with a return
    323                      code of EFI_SUCCESS, the amount of data transferred to
    324                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
    325                      the size of Buffer required to retrieve the requested file.
    326   @param  Buffer     The memory buffer to transfer the file to. IF Buffer is NULL,
    327                      then the size of the requested file is returned in
    328                      BufferSize.
    329 
    330   @retval EFI_SUCCESS           The file was loaded.
    331   @retval EFI_UNSUPPORTED       The device does not support the provided BootPolicy
    332   @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
    333                                 BufferSize is NULL.
    334   @retval EFI_NO_MEDIA          No medium was present to load the file.
    335   @retval EFI_DEVICE_ERROR      The file was not loaded due to a device error.
    336   @retval EFI_NO_RESPONSE       The remote system did not respond.
    337   @retval EFI_NOT_FOUND         The file was not found.
    338   @retval EFI_ABORTED           The file load process was manually cancelled.
    339   @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to read the current directory entry.
    340                                 BufferSize has been updated with the size needed to complete
    341                                 the request.
    342 
    343 **/
    344 EFI_STATUS
    345 EFIAPI
    346 HttpBootDxeLoadFile (
    347   IN EFI_LOAD_FILE_PROTOCOL           *This,
    348   IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
    349   IN BOOLEAN                          BootPolicy,
    350   IN OUT UINTN                        *BufferSize,
    351   IN VOID                             *Buffer OPTIONAL
    352   )
    353 {
    354   HTTP_BOOT_PRIVATE_DATA        *Private;
    355   HTTP_BOOT_VIRTUAL_NIC         *VirtualNic;
    356   BOOLEAN                       MediaPresent;
    357   BOOLEAN                       UsingIpv6;
    358   EFI_STATUS                    Status;
    359 
    360   if (This == NULL || BufferSize == NULL) {
    361     return EFI_INVALID_PARAMETER;
    362   }
    363 
    364   //
    365   // Only support BootPolicy
    366   //
    367   if (!BootPolicy) {
    368     return EFI_UNSUPPORTED;
    369   }
    370 
    371   VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);
    372   Private = VirtualNic->Private;
    373   UsingIpv6 = FALSE;
    374 
    375   //
    376   // Check media status before HTTP boot start
    377   //
    378   MediaPresent = TRUE;
    379   NetLibDetectMedia (Private->Controller, &MediaPresent);
    380   if (!MediaPresent) {
    381     return EFI_NO_MEDIA;
    382   }
    383 
    384   //
    385   // Check whether the virtual nic is using IPv6 or not.
    386   //
    387   if (VirtualNic == Private->Ip6Nic) {
    388     UsingIpv6 = TRUE;
    389   }
    390 
    391   //
    392   // Initialize HTTP boot and load the boot file.
    393   //
    394   Status = HttpBootStart (Private, UsingIpv6);
    395   if (Status == EFI_ALREADY_STARTED && UsingIpv6 != Private->UsingIpv6) {
    396     //
    397     // Http boot Driver has already been started but not on the required IP version, restart it.
    398     //
    399     Status = HttpBootStop (Private);
    400     if (!EFI_ERROR (Status)) {
    401       Status = HttpBootStart (Private, UsingIpv6);
    402     }
    403   }
    404   if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
    405     Status = HttpBootLoadFile (Private, BufferSize, Buffer);
    406   }
    407 
    408   if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
    409     HttpBootStop (Private);
    410   } else {
    411     if (!Private->UsingIpv6) {
    412       //
    413       // Stop and release the DHCP4 child.
    414       //
    415       Private->Dhcp4->Stop (Private->Dhcp4);
    416       Private->Dhcp4->Configure (Private->Dhcp4, NULL);
    417     } else {
    418       //
    419       // Stop and release the DHCP6 child.
    420       //
    421       Private->Dhcp6->Stop (Private->Dhcp6);
    422       Private->Dhcp6->Configure (Private->Dhcp6, NULL);
    423     }
    424   }
    425 
    426   return Status;
    427 }
    428 
    429 ///
    430 /// Load File Protocol instance
    431 ///
    432 GLOBAL_REMOVE_IF_UNREFERENCED
    433 EFI_LOAD_FILE_PROTOCOL  gHttpBootDxeLoadFile = {
    434   HttpBootDxeLoadFile
    435 };
    436