HttpBootImpl.c revision b659408b933f40765960e877de3e1f8ceaab52cb
1/** @file
2  The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
3
4Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials are licensed and made available under
6the terms and conditions of the BSD License that accompanies this distribution.
7The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php.
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
30EFI_STATUS
31HttpBootStart (
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**/
98EFI_STATUS
99HttpBootDhcp (
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**/
151EFI_STATUS
152HttpBootLoadFile (
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**/
248EFI_STATUS
249HttpBootStop (
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**/
344EFI_STATUS
345EFIAPI
346HttpBootDxeLoadFile (
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///
432GLOBAL_REMOVE_IF_UNREFERENCED
433EFI_LOAD_FILE_PROTOCOL  gHttpBootDxeLoadFile = {
434  HttpBootDxeLoadFile
435};
436