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