]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpBootDxe/HttpBootImpl.c
NetworkPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootImpl.c
CommitLineData
d933e70a
JW
1/** @file\r
2 The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.\r
3\r
f33d3994 4Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
44a7d08b 5(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
ecf98fbc 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
d933e70a
JW
7\r
8**/\r
9\r
10#include "HttpBootDxe.h"\r
11\r
95b5c32f
FS
12/**\r
13 Install HTTP Boot Callback Protocol if not installed before.\r
14\r
15 @param[in] Private Pointer to HTTP Boot private data.\r
16\r
17 @retval EFI_SUCCESS HTTP Boot Callback Protocol installed succesfully.\r
18 @retval Others Failed to install HTTP Boot Callback Protocol.\r
19\r
20**/\r
21EFI_STATUS\r
22HttpBootInstallCallback (\r
23 IN HTTP_BOOT_PRIVATE_DATA *Private\r
24 )\r
25{\r
26 EFI_STATUS Status;\r
27 EFI_HANDLE ControllerHandle;\r
28\r
29 if (!Private->UsingIpv6) {\r
30 ControllerHandle = Private->Ip4Nic->Controller;\r
31 } else {\r
32 ControllerHandle = Private->Ip6Nic->Controller;\r
33 }\r
34\r
35 //\r
36 // Check whether gEfiHttpBootCallbackProtocolGuid already installed.\r
37 //\r
38 Status = gBS->HandleProtocol (\r
39 ControllerHandle,\r
40 &gEfiHttpBootCallbackProtocolGuid,\r
41 (VOID **) &Private->HttpBootCallback\r
42 );\r
43 if (Status == EFI_UNSUPPORTED) {\r
44\r
45 CopyMem (\r
46 &Private->LoadFileCallback,\r
47 &gHttpBootDxeHttpBootCallback,\r
48 sizeof (EFI_HTTP_BOOT_CALLBACK_PROTOCOL)\r
49 );\r
50\r
51 //\r
52 // Install a default callback if user didn't offer one.\r
53 //\r
54 Status = gBS->InstallProtocolInterface (\r
55 &ControllerHandle,\r
56 &gEfiHttpBootCallbackProtocolGuid,\r
57 EFI_NATIVE_INTERFACE,\r
58 &Private->LoadFileCallback\r
59 );\r
60 if (EFI_ERROR (Status)) {\r
61 return Status;\r
62 }\r
63 Private->HttpBootCallback = &Private->LoadFileCallback;\r
64 }\r
65\r
66 return EFI_SUCCESS;\r
67}\r
68\r
69/**\r
70 Uninstall HTTP Boot Callback Protocol if it's installed by this driver.\r
71\r
72 @param[in] Private Pointer to HTTP Boot private data.\r
73\r
74**/\r
75VOID\r
76HttpBootUninstallCallback (\r
77 IN HTTP_BOOT_PRIVATE_DATA *Private\r
78 )\r
79{\r
80 if (Private->HttpBootCallback == &Private->LoadFileCallback) {\r
81 gBS->UninstallProtocolInterface (\r
82 Private->Controller,\r
83 &gEfiHttpBootCallbackProtocolGuid,\r
84 &Private->HttpBootCallback\r
85 );\r
86 Private->HttpBootCallback = NULL;\r
87 }\r
88}\r
89\r
d933e70a
JW
90/**\r
91 Enable the use of UEFI HTTP boot function.\r
92\r
f75a7f56 93 If the driver has already been started but not satisfy the requirement (IP stack and\r
587d204c
FS
94 specified boot file path), this function will stop the driver and start it again.\r
95\r
d933e70a 96 @param[in] Private The pointer to the driver's private data.\r
b659408b
ZL
97 @param[in] UsingIpv6 Specifies the type of IP addresses that are to be\r
98 used during the session that is being started.\r
99 Set to TRUE for IPv6, and FALSE for IPv4.\r
fa848a40 100 @param[in] FilePath The device specific path of the file to load.\r
d933e70a
JW
101\r
102 @retval EFI_SUCCESS HTTP boot was successfully enabled.\r
587d204c
FS
103 @retval EFI_INVALID_PARAMETER Private is NULL or FilePath is NULL.\r
104 @retval EFI_INVALID_PARAMETER The FilePath doesn't contain a valid URI device path node.\r
d933e70a 105 @retval EFI_ALREADY_STARTED The driver is already in started state.\r
fa848a40 106 @retval EFI_OUT_OF_RESOURCES There are not enough resources.\r
f75a7f56 107\r
d933e70a
JW
108**/\r
109EFI_STATUS\r
110HttpBootStart (\r
b659408b 111 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
fa848a40
FS
112 IN BOOLEAN UsingIpv6,\r
113 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
d933e70a
JW
114 )\r
115{\r
b659408b
ZL
116 UINTN Index;\r
117 EFI_STATUS Status;\r
587d204c 118 CHAR8 *Uri;\r
d933e70a 119\r
0dc59296 120 Uri = NULL;\r
f75a7f56 121\r
fa848a40 122 if (Private == NULL || FilePath == NULL) {\r
d933e70a
JW
123 return EFI_INVALID_PARAMETER;\r
124 }\r
f75a7f56 125\r
587d204c
FS
126 //\r
127 // Check the URI in the input FilePath, in order to see whether it is\r
f75a7f56 128 // required to boot from a new specified boot file.\r
587d204c
FS
129 //\r
130 Status = HttpBootParseFilePath (FilePath, &Uri);\r
131 if (EFI_ERROR (Status)) {\r
132 return EFI_INVALID_PARAMETER;\r
133 }\r
f75a7f56 134\r
587d204c
FS
135 //\r
136 // Check whether we need to stop and restart the HTTP boot driver.\r
137 //\r
d933e70a 138 if (Private->Started) {\r
587d204c
FS
139 //\r
140 // Restart is needed in 2 cases:\r
141 // 1. Http boot driver has already been started but not on the required IP stack.\r
142 // 2. The specified boot file URI in FilePath is different with the one we have\r
143 // recorded before.\r
144 //\r
f75a7f56 145 if ((UsingIpv6 != Private->UsingIpv6) ||\r
587d204c
FS
146 ((Uri != NULL) && (AsciiStrCmp (Private->BootFileUri, Uri) != 0))) {\r
147 //\r
148 // Restart is required, first stop then continue this start function.\r
149 //\r
150 Status = HttpBootStop (Private);\r
151 if (EFI_ERROR (Status)) {\r
0dc59296
JW
152 if (Uri != NULL) {\r
153 FreePool (Uri);\r
154 }\r
587d204c
FS
155 return Status;\r
156 }\r
157 } else {\r
158 //\r
159 // Restart is not required.\r
160 //\r
161 if (Uri != NULL) {\r
162 FreePool (Uri);\r
163 }\r
164 return EFI_ALREADY_STARTED;\r
165 }\r
d933e70a
JW
166 }\r
167\r
b659408b
ZL
168 //\r
169 // Detect whether using ipv6 or not, and set it to the private data.\r
170 //\r
171 if (UsingIpv6 && Private->Ip6Nic != NULL) {\r
172 Private->UsingIpv6 = TRUE;\r
173 } else if (!UsingIpv6 && Private->Ip4Nic != NULL) {\r
174 Private->UsingIpv6 = FALSE;\r
175 } else {\r
587d204c
FS
176 if (Uri != NULL) {\r
177 FreePool (Uri);\r
178 }\r
b659408b
ZL
179 return EFI_UNSUPPORTED;\r
180 }\r
fa848a40
FS
181\r
182 //\r
587d204c 183 // Record the specified URI and prepare the URI parser if needed.\r
fa848a40 184 //\r
587d204c 185 Private->FilePathUri = Uri;\r
fa848a40
FS
186 if (Private->FilePathUri != NULL) {\r
187 Status = HttpParseUrl (\r
188 Private->FilePathUri,\r
189 (UINT32) AsciiStrLen (Private->FilePathUri),\r
190 FALSE,\r
191 &Private->FilePathUriParser\r
192 );\r
193 if (EFI_ERROR (Status)) {\r
587d204c 194 FreePool (Private->FilePathUri);\r
fa848a40
FS
195 return Status;\r
196 }\r
197 }\r
f75a7f56 198\r
b659408b
ZL
199 //\r
200 // Init the content of cached DHCP offer list.\r
201 //\r
202 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));\r
d933e70a 203 if (!Private->UsingIpv6) {\r
d933e70a 204 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {\r
632dcfd6 205 Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_CACHED_DHCP4_PACKET_MAX_SIZE;\r
d933e70a
JW
206 }\r
207 } else {\r
b659408b 208 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {\r
632dcfd6 209 Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_CACHED_DHCP6_PACKET_MAX_SIZE;\r
b659408b 210 }\r
d933e70a
JW
211 }\r
212\r
b659408b
ZL
213 if (Private->UsingIpv6) {\r
214 //\r
215 // Set Ip6 policy to Automatic to start the Ip6 router discovery.\r
216 //\r
217 Status = HttpBootSetIp6Policy (Private);\r
218 if (EFI_ERROR (Status)) {\r
219 return Status;\r
220 }\r
221 }\r
587d204c 222 Private->Started = TRUE;\r
95b5c32f 223 Print (L"\n>>Start HTTP Boot over IPv%d", Private->UsingIpv6 ? 6 : 4);\r
d933e70a
JW
224\r
225 return EFI_SUCCESS;\r
226}\r
227\r
228/**\r
b659408b 229 Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.\r
d933e70a
JW
230\r
231 @param[in] Private The pointer to the driver's private data.\r
232\r
233 @retval EFI_SUCCESS Boot info was successfully retrieved.\r
234 @retval EFI_INVALID_PARAMETER Private is NULL.\r
235 @retval EFI_NOT_STARTED The driver is in stopped state.\r
236 @retval EFI_DEVICE_ERROR An unexpected network error occurred.\r
237 @retval Others Other errors as indicated.\r
f75a7f56 238\r
d933e70a
JW
239**/\r
240EFI_STATUS\r
241HttpBootDhcp (\r
242 IN HTTP_BOOT_PRIVATE_DATA *Private\r
243 )\r
244{\r
245 EFI_STATUS Status;\r
246\r
247 if (Private == NULL) {\r
248 return EFI_INVALID_PARAMETER;\r
249 }\r
f75a7f56 250\r
d933e70a
JW
251 if (!Private->Started) {\r
252 return EFI_NOT_STARTED;\r
253 }\r
254\r
255 Status = EFI_DEVICE_ERROR;\r
256\r
257 if (!Private->UsingIpv6) {\r
b659408b
ZL
258 //\r
259 // Start D.O.R.A process to get a IPv4 address and other boot information.\r
260 //\r
d933e70a
JW
261 Status = HttpBootDhcp4Dora (Private);\r
262 } else {\r
b659408b
ZL
263 //\r
264 // Start S.A.R.R process to get a IPv6 address and other boot information.\r
265 //\r
266 Status = HttpBootDhcp6Sarr (Private);\r
d933e70a
JW
267 }\r
268\r
269 return Status;\r
270}\r
271\r
272/**\r
273 Attempt to download the boot file through HTTP message exchange.\r
274\r
275 @param[in] Private The pointer to the driver's private data.\r
276 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
277 code of EFI_SUCCESS, the amount of data transferred to\r
278 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
279 the size of Buffer required to retrieve the requested file.\r
280 @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL,\r
281 then the size of the requested file is returned in\r
282 BufferSize.\r
587d204c 283 @param[out] ImageType The image type of the downloaded file.\r
d933e70a
JW
284\r
285 @retval EFI_SUCCESS Boot file was loaded successfully.\r
587d204c
FS
286 @retval EFI_INVALID_PARAMETER Private is NULL, or ImageType is NULL, or BufferSize is NULL.\r
287 @retval EFI_INVALID_PARAMETER *BufferSize is not zero, and Buffer is NULL.\r
d933e70a 288 @retval EFI_NOT_STARTED The driver is in stopped state.\r
f75a7f56 289 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has\r
d933e70a
JW
290 been updated with the size needed to complete the request.\r
291 @retval EFI_DEVICE_ERROR An unexpected network error occurred.\r
292 @retval Others Other errors as indicated.\r
f75a7f56 293\r
d933e70a
JW
294**/\r
295EFI_STATUS\r
296HttpBootLoadFile (\r
297 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
298 IN OUT UINTN *BufferSize,\r
587d204c
FS
299 IN VOID *Buffer, OPTIONAL\r
300 OUT HTTP_BOOT_IMAGE_TYPE *ImageType\r
d933e70a
JW
301 )\r
302{\r
303 EFI_STATUS Status;\r
304\r
587d204c
FS
305 if (Private == NULL || ImageType == NULL || BufferSize == NULL ) {\r
306 return EFI_INVALID_PARAMETER;\r
307 }\r
308\r
309 if (*BufferSize != 0 && Buffer == NULL) {\r
d933e70a
JW
310 return EFI_INVALID_PARAMETER;\r
311 }\r
f75a7f56 312\r
d933e70a
JW
313 if (!Private->Started) {\r
314 return EFI_NOT_STARTED;\r
315 }\r
316\r
95b5c32f
FS
317 Status = HttpBootInstallCallback (Private);\r
318 if (EFI_ERROR(Status)) {\r
319 goto ON_EXIT;\r
320 }\r
d933e70a
JW
321\r
322 if (Private->BootFileUri == NULL) {\r
323 //\r
324 // Parse the cached offer to get the boot file URL first.\r
325 //\r
326 Status = HttpBootDiscoverBootInfo (Private);\r
327 if (EFI_ERROR (Status)) {\r
f33d3994 328 AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP server.\n");\r
95b5c32f 329 goto ON_EXIT;\r
d933e70a
JW
330 }\r
331 }\r
332\r
333 if (!Private->HttpCreated) {\r
334 //\r
335 // Create HTTP child.\r
336 //\r
337 Status = HttpBootCreateHttpIo (Private);\r
338 if (EFI_ERROR (Status)) {\r
95b5c32f 339 goto ON_EXIT;\r
d933e70a
JW
340 }\r
341 }\r
342\r
343 if (Private->BootFileSize == 0) {\r
344 //\r
345 // Discover the information about the bootfile if we haven't.\r
346 //\r
347\r
348 //\r
349 // Try to use HTTP HEAD method.\r
350 //\r
351 Status = HttpBootGetBootFile (\r
352 Private,\r
353 TRUE,\r
354 &Private->BootFileSize,\r
587d204c 355 NULL,\r
44a7d08b 356 &Private->ImageType\r
d933e70a
JW
357 );\r
358 if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {\r
359 //\r
360 // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.\r
361 //\r
362 ASSERT (Private->BootFileSize == 0);\r
363 Status = HttpBootGetBootFile (\r
364 Private,\r
365 FALSE,\r
366 &Private->BootFileSize,\r
587d204c 367 NULL,\r
44a7d08b 368 &Private->ImageType\r
d933e70a
JW
369 );\r
370 if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {\r
fca04738 371 AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP server.\n");\r
95b5c32f 372 goto ON_EXIT;\r
d933e70a
JW
373 }\r
374 }\r
375 }\r
376\r
377 if (*BufferSize < Private->BootFileSize) {\r
378 *BufferSize = Private->BootFileSize;\r
44a7d08b 379 *ImageType = Private->ImageType;\r
95b5c32f
FS
380 Status = EFI_BUFFER_TOO_SMALL;\r
381 goto ON_EXIT;\r
d933e70a
JW
382 }\r
383\r
384 //\r
385 // Load the boot file into Buffer\r
386 //\r
95b5c32f
FS
387 Status = HttpBootGetBootFile (\r
388 Private,\r
389 FALSE,\r
390 BufferSize,\r
391 Buffer,\r
392 ImageType\r
393 );\r
f75a7f56 394\r
95b5c32f
FS
395ON_EXIT:\r
396 HttpBootUninstallCallback (Private);\r
f75a7f56 397\r
f33d3994
JW
398 if (EFI_ERROR (Status)) {\r
399 if (Status == EFI_ACCESS_DENIED) {\r
400 AsciiPrint ("\n Error: Could not establish connection with HTTP server.\n");\r
401 } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {\r
402 AsciiPrint ("\n Error: Buffer size is smaller than the requested file.\n");\r
403 } else if (Status == EFI_OUT_OF_RESOURCES) {\r
404 AsciiPrint ("\n Error: Could not allocate I/O buffers.\n");\r
405 } else if (Status == EFI_DEVICE_ERROR) {\r
406 AsciiPrint ("\n Error: Network device error.\n");\r
407 } else if (Status == EFI_TIMEOUT) {\r
408 AsciiPrint ("\n Error: Server response timeout.\n");\r
409 } else if (Status == EFI_ABORTED) {\r
410 AsciiPrint ("\n Error: Remote boot cancelled.\n");\r
411 } else if (Status != EFI_BUFFER_TOO_SMALL) {\r
412 AsciiPrint ("\n Error: Unexpected network error.\n");\r
413 }\r
fca04738 414 }\r
f75a7f56 415\r
95b5c32f 416 return Status;\r
d933e70a
JW
417}\r
418\r
419/**\r
420 Disable the use of UEFI HTTP boot function.\r
421\r
422 @param[in] Private The pointer to the driver's private data.\r
423\r
424 @retval EFI_SUCCESS HTTP boot was successfully disabled.\r
425 @retval EFI_NOT_STARTED The driver is already in stopped state.\r
426 @retval EFI_INVALID_PARAMETER Private is NULL.\r
427 @retval Others Unexpected error when stop the function.\r
f75a7f56 428\r
d933e70a
JW
429**/\r
430EFI_STATUS\r
431HttpBootStop (\r
432 IN HTTP_BOOT_PRIVATE_DATA *Private\r
433 )\r
434{\r
435 UINTN Index;\r
436\r
437 if (Private == NULL) {\r
438 return EFI_INVALID_PARAMETER;\r
439 }\r
f75a7f56 440\r
d933e70a
JW
441 if (!Private->Started) {\r
442 return EFI_NOT_STARTED;\r
443 }\r
f75a7f56 444\r
d933e70a
JW
445 if (Private->HttpCreated) {\r
446 HttpIoDestroyIo (&Private->HttpIo);\r
447 Private->HttpCreated = FALSE;\r
448 }\r
f75a7f56 449\r
d933e70a
JW
450 Private->Started = FALSE;\r
451 ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));\r
452 ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));\r
453 ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));\r
454 Private->Port = 0;\r
455 Private->BootFileUri = NULL;\r
456 Private->BootFileUriParser = NULL;\r
457 Private->BootFileSize = 0;\r
458 Private->SelectIndex = 0;\r
f75a7f56 459 Private->SelectProxyType = HttpOfferTypeMax;\r
d933e70a
JW
460\r
461 if (!Private->UsingIpv6) {\r
462 //\r
463 // Stop and release the DHCP4 child.\r
464 //\r
465 Private->Dhcp4->Stop (Private->Dhcp4);\r
466 Private->Dhcp4->Configure (Private->Dhcp4, NULL);\r
467\r
468 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {\r
469 if (Private->OfferBuffer[Index].Dhcp4.UriParser) {\r
470 HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);\r
471 }\r
472 }\r
473 } else {\r
b659408b
ZL
474 //\r
475 // Stop and release the DHCP6 child.\r
476 //\r
477 Private->Dhcp6->Stop (Private->Dhcp6);\r
478 Private->Dhcp6->Configure (Private->Dhcp6, NULL);\r
f75a7f56 479\r
b659408b
ZL
480 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {\r
481 if (Private->OfferBuffer[Index].Dhcp6.UriParser) {\r
482 HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser);\r
483 }\r
484 }\r
d933e70a 485 }\r
fa848a40 486\r
7b1dbd15
JW
487 if (Private->DnsServerIp != NULL) {\r
488 FreePool (Private->DnsServerIp);\r
489 Private->DnsServerIp = NULL;\r
490 }\r
491\r
fa848a40
FS
492 if (Private->FilePathUri!= NULL) {\r
493 FreePool (Private->FilePathUri);\r
494 HttpUrlFreeParser (Private->FilePathUriParser);\r
495 Private->FilePathUri = NULL;\r
496 Private->FilePathUriParser = NULL;\r
497 }\r
f75a7f56 498\r
d933e70a
JW
499 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));\r
500 Private->OfferNum = 0;\r
501 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
502 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
f75a7f56 503\r
fa848a40 504 HttpBootFreeCacheList (Private);\r
f75a7f56 505\r
d933e70a
JW
506 return EFI_SUCCESS;\r
507}\r
508\r
509/**\r
510 Causes the driver to load a specified file.\r
511\r
512 @param This Protocol instance pointer.\r
513 @param FilePath The device specific path of the file to load.\r
514 @param BootPolicy If TRUE, indicates that the request originates from the\r
515 boot manager is attempting to load FilePath as a boot\r
516 selection. If FALSE, then FilePath must match as exact file\r
517 to be loaded.\r
518 @param BufferSize On input the size of Buffer in bytes. On output with a return\r
519 code of EFI_SUCCESS, the amount of data transferred to\r
520 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
521 the size of Buffer required to retrieve the requested file.\r
522 @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
523 then the size of the requested file is returned in\r
524 BufferSize.\r
525\r
526 @retval EFI_SUCCESS The file was loaded.\r
527 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy\r
528 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or\r
529 BufferSize is NULL.\r
530 @retval EFI_NO_MEDIA No medium was present to load the file.\r
531 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.\r
532 @retval EFI_NO_RESPONSE The remote system did not respond.\r
533 @retval EFI_NOT_FOUND The file was not found.\r
534 @retval EFI_ABORTED The file load process was manually cancelled.\r
535 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.\r
536 BufferSize has been updated with the size needed to complete\r
537 the request.\r
538\r
539**/\r
540EFI_STATUS\r
541EFIAPI\r
542HttpBootDxeLoadFile (\r
543 IN EFI_LOAD_FILE_PROTOCOL *This,\r
544 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
545 IN BOOLEAN BootPolicy,\r
546 IN OUT UINTN *BufferSize,\r
547 IN VOID *Buffer OPTIONAL\r
548 )\r
549{\r
550 HTTP_BOOT_PRIVATE_DATA *Private;\r
b659408b 551 HTTP_BOOT_VIRTUAL_NIC *VirtualNic;\r
152f2d5e 552 EFI_STATUS MediaStatus;\r
b659408b 553 BOOLEAN UsingIpv6;\r
d933e70a 554 EFI_STATUS Status;\r
587d204c 555 HTTP_BOOT_IMAGE_TYPE ImageType;\r
d933e70a 556\r
fa848a40 557 if (This == NULL || BufferSize == NULL || FilePath == NULL) {\r
d933e70a
JW
558 return EFI_INVALID_PARAMETER;\r
559 }\r
560\r
561 //\r
562 // Only support BootPolicy\r
563 //\r
564 if (!BootPolicy) {\r
565 return EFI_UNSUPPORTED;\r
566 }\r
567\r
b659408b
ZL
568 VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);\r
569 Private = VirtualNic->Private;\r
f75a7f56 570\r
d933e70a
JW
571 //\r
572 // Check media status before HTTP boot start\r
573 //\r
152f2d5e 574 MediaStatus = EFI_SUCCESS;\r
575 NetLibDetectMediaWaitTimeout (Private->Controller, HTTP_BOOT_CHECK_MEDIA_WAITING_TIME, &MediaStatus);\r
576 if (MediaStatus != EFI_SUCCESS) {\r
fca04738 577 AsciiPrint ("\n Error: Could not detect network connection.\n");\r
d933e70a
JW
578 return EFI_NO_MEDIA;\r
579 }\r
f75a7f56 580\r
b659408b
ZL
581 //\r
582 // Check whether the virtual nic is using IPv6 or not.\r
583 //\r
fa848a40 584 UsingIpv6 = FALSE;\r
b659408b
ZL
585 if (VirtualNic == Private->Ip6Nic) {\r
586 UsingIpv6 = TRUE;\r
587 }\r
fa848a40 588\r
d933e70a 589 //\r
fa848a40 590 // Initialize HTTP boot.\r
d933e70a 591 //\r
fa848a40 592 Status = HttpBootStart (Private, UsingIpv6, FilePath);\r
587d204c
FS
593 if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {\r
594 return Status;\r
b659408b 595 }\r
f75a7f56 596\r
fa848a40
FS
597 //\r
598 // Load the boot file.\r
599 //\r
587d204c
FS
600 ImageType = ImageTypeMax;\r
601 Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType);\r
602 if (EFI_ERROR (Status)) {\r
603 if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) {\r
604 Status = EFI_WARN_FILE_SYSTEM;\r
605 } else if (Status != EFI_BUFFER_TOO_SMALL) {\r
606 HttpBootStop (Private);\r
607 }\r
608 return Status;\r
d933e70a
JW
609 }\r
610\r
587d204c
FS
611 //\r
612 // Register the RAM Disk to the system if needed.\r
613 //\r
614 if (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk) {\r
615 Status = HttpBootRegisterRamDisk (Private, *BufferSize, Buffer, ImageType);\r
616 if (!EFI_ERROR (Status)) {\r
617 Status = EFI_WARN_FILE_SYSTEM;\r
fca04738
FS
618 } else {\r
619 AsciiPrint ("\n Error: Could not register RAM disk to the system.\n");\r
b659408b 620 }\r
d933e70a 621 }\r
287f05cd
FS
622\r
623 //\r
624 // Stop the HTTP Boot service after the boot image is downloaded.\r
625 //\r
626 HttpBootStop (Private);\r
d933e70a
JW
627 return Status;\r
628}\r
629\r
630///\r
631/// Load File Protocol instance\r
632///\r
f75a7f56 633GLOBAL_REMOVE_IF_UNREFERENCED\r
d933e70a
JW
634EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {\r
635 HttpBootDxeLoadFile\r
636};\r
95b5c32f
FS
637\r
638/**\r
639 Callback function that is invoked when the HTTP Boot driver is about to transmit or has received a\r
640 packet.\r
641\r
642 This function is invoked when the HTTP Boot driver is about to transmit or has received packet.\r
643 Parameters DataType and Received specify the type of event and the format of the buffer pointed\r
644 to by Data. Due to the polling nature of UEFI device drivers, this callback function should not\r
645 execute for more than 5 ms.\r
646 The returned status code determines the behavior of the HTTP Boot driver.\r
647\r
648 @param[in] This Pointer to the EFI_HTTP_BOOT_CALLBACK_PROTOCOL instance.\r
649 @param[in] DataType The event that occurs in the current state.\r
650 @param[in] Received TRUE if the callback is being invoked due to a receive event.\r
651 FALSE if the callback is being invoked due to a transmit event.\r
652 @param[in] DataLength The length in bytes of the buffer pointed to by Data.\r
653 @param[in] Data A pointer to the buffer of data, the data type is specified by\r
654 DataType.\r
f75a7f56 655\r
95b5c32f
FS
656 @retval EFI_SUCCESS Tells the HTTP Boot driver to continue the HTTP Boot process.\r
657 @retval EFI_ABORTED Tells the HTTP Boot driver to abort the current HTTP Boot process.\r
658**/\r
659EFI_STATUS\r
3858c58a 660EFIAPI\r
95b5c32f
FS
661HttpBootCallback (\r
662 IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This,\r
663 IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType,\r
664 IN BOOLEAN Received,\r
665 IN UINT32 DataLength,\r
666 IN VOID *Data OPTIONAL\r
667 )\r
668{\r
669 EFI_HTTP_MESSAGE *HttpMessage;\r
670 EFI_HTTP_HEADER *HttpHeader;\r
671 HTTP_BOOT_PRIVATE_DATA *Private;\r
672 UINT32 Percentage;\r
673\r
674 Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_PROTOCOL(This);\r
675\r
676 switch (DataType) {\r
677 case HttpBootDhcp4:\r
678 case HttpBootDhcp6:\r
679 Print (L".");\r
680 break;\r
681\r
682 case HttpBootHttpRequest:\r
683 if (Data != NULL) {\r
684 HttpMessage = (EFI_HTTP_MESSAGE *) Data;\r
685 if (HttpMessage->Data.Request->Method == HttpMethodGet &&\r
686 HttpMessage->Data.Request->Url != NULL) {\r
687 Print (L"\n URI: %s\n", HttpMessage->Data.Request->Url);\r
688 }\r
689 }\r
690 break;\r
691\r
692 case HttpBootHttpResponse:\r
693 if (Data != NULL) {\r
694 HttpMessage = (EFI_HTTP_MESSAGE *) Data;\r
f75a7f56 695\r
bb4831c0
FS
696 if (HttpMessage->Data.Response != NULL) {\r
697 if (HttpBootIsHttpRedirectStatusCode (HttpMessage->Data.Response->StatusCode)) {\r
698 //\r
699 // Server indicates the resource has been redirected to a different URL\r
700 // according to the section 6.4 of RFC7231 and the RFC 7538.\r
701 // Display the redirect information on the screen.\r
702 //\r
703 HttpHeader = HttpFindHeader (\r
704 HttpMessage->HeaderCount,\r
705 HttpMessage->Headers,\r
706 HTTP_HEADER_LOCATION\r
707 );\r
708 if (HttpHeader != NULL) {\r
709 Print (L"\n HTTP ERROR: Resource Redirected.\n New Location: %a\n", HttpHeader->FieldValue);\r
710 }\r
f75a7f56 711 break;\r
bb4831c0
FS
712 }\r
713 }\r
f75a7f56 714\r
95b5c32f
FS
715 HttpHeader = HttpFindHeader (\r
716 HttpMessage->HeaderCount,\r
717 HttpMessage->Headers,\r
718 HTTP_HEADER_CONTENT_LENGTH\r
719 );\r
720 if (HttpHeader != NULL) {\r
721 Private->FileSize = AsciiStrDecimalToUintn (HttpHeader->FieldValue);\r
722 Private->ReceivedSize = 0;\r
723 Private->Percentage = 0;\r
724 }\r
725 }\r
726 break;\r
727\r
728 case HttpBootHttpEntityBody:\r
729 if (DataLength != 0) {\r
730 if (Private->FileSize != 0) {\r
731 //\r
732 // We already know the file size, print in percentage format.\r
733 //\r
734 if (Private->ReceivedSize == 0) {\r
bb4831c0 735 Print (L" File Size: %lu Bytes\n", Private->FileSize);\r
95b5c32f
FS
736 }\r
737 Private->ReceivedSize += DataLength;\r
738 Percentage = (UINT32) DivU64x64Remainder (MultU64x32 (Private->ReceivedSize, 100), Private->FileSize, NULL);\r
739 if (Private->Percentage != Percentage) {\r
740 Private->Percentage = Percentage;\r
741 Print (L"\r Downloading...%d%%", Percentage);\r
742 }\r
743 } else {\r
744 //\r
745 // In some case we couldn't get the file size from the HTTP header, so we\r
746 // just print the downloaded file size.\r
747 //\r
748 Private->ReceivedSize += DataLength;\r
749 Print (L"\r Downloading...%lu Bytes", Private->ReceivedSize);\r
750 }\r
751 }\r
752 break;\r
753\r
754 default:\r
755 break;\r
756 };\r
757\r
758 return EFI_SUCCESS;\r
759}\r
760\r
761///\r
762/// HTTP Boot Callback Protocol instance\r
763///\r
f75a7f56 764GLOBAL_REMOVE_IF_UNREFERENCED\r
95b5c32f
FS
765EFI_HTTP_BOOT_CALLBACK_PROTOCOL gHttpBootDxeHttpBootCallback = {\r
766 HttpBootCallback\r
767};\r