• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
HttpBootStart(IN HTTP_BOOT_PRIVATE_DATA * Private,IN BOOLEAN UsingIpv6,IN EFI_DEVICE_PATH_PROTOCOL * FilePath)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
HttpBootDhcp(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootLoadFile(IN HTTP_BOOT_PRIVATE_DATA * Private,IN OUT UINTN * BufferSize,IN VOID * Buffer,OPTIONAL OUT HTTP_BOOT_IMAGE_TYPE * ImageType)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
HttpBootStop(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootDxeLoadFile(IN EFI_LOAD_FILE_PROTOCOL * This,IN EFI_DEVICE_PATH_PROTOCOL * FilePath,IN BOOLEAN BootPolicy,IN OUT UINTN * BufferSize,IN VOID * Buffer OPTIONAL)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