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