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