]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - NetworkPkg/HttpBootDxe/HttpBootImpl.c
NetworkPkg:Fix Http boot download issue.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootImpl.c
... / ...
CommitLineData
1/** @file\r
2 The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.\r
3\r
4Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
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
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
24 @param[in] FilePath The device specific path of the file to load.\r
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
29 @retval EFI_OUT_OF_RESOURCES There are not enough resources.\r
30 \r
31**/\r
32EFI_STATUS\r
33HttpBootStart (\r
34 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
35 IN BOOLEAN UsingIpv6,\r
36 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
37 )\r
38{\r
39 UINTN Index;\r
40 EFI_STATUS Status;\r
41\r
42 if (Private == NULL || FilePath == NULL) {\r
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
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
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
80 \r
81 //\r
82 // Init the content of cached DHCP offer list.\r
83 //\r
84 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));\r
85 if (!Private->UsingIpv6) {\r
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
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
93 }\r
94\r
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
104 Private->Started = TRUE;\r
105\r
106 return EFI_SUCCESS;\r
107}\r
108\r
109/**\r
110 Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.\r
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
139 //\r
140 // Start D.O.R.A process to get a IPv4 address and other boot information.\r
141 //\r
142 Status = HttpBootDhcp4Dora (Private);\r
143 } else {\r
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
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
300 Private->SelectProxyType = HttpOfferTypeMax; \r
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
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
326 }\r
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
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
339 \r
340 HttpBootFreeCacheList (Private);\r
341 \r
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
387 HTTP_BOOT_VIRTUAL_NIC *VirtualNic;\r
388 BOOLEAN MediaPresent;\r
389 BOOLEAN UsingIpv6;\r
390 EFI_STATUS Status;\r
391\r
392 if (This == NULL || BufferSize == NULL || FilePath == NULL) {\r
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
403 VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);\r
404 Private = VirtualNic->Private;\r
405 \r
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
414 \r
415 //\r
416 // Check whether the virtual nic is using IPv6 or not.\r
417 //\r
418 UsingIpv6 = FALSE;\r
419 if (VirtualNic == Private->Ip6Nic) {\r
420 UsingIpv6 = TRUE;\r
421 }\r
422\r
423 //\r
424 // Initialize HTTP boot.\r
425 //\r
426 Status = HttpBootStart (Private, UsingIpv6, FilePath);\r
427 if (Status == EFI_ALREADY_STARTED) {\r
428 //\r
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
433 //\r
434 if ((UsingIpv6 != Private->UsingIpv6) || ((Private->FilePathUri != NULL) && (AsciiStrCmp (Private->BootFileUri, Private->FilePathUri) != 0))) {\r
435 Status = HttpBootStop (Private);\r
436 if (!EFI_ERROR (Status)) {\r
437 Status = HttpBootStart (Private, UsingIpv6, FilePath);\r
438 }\r
439 }\r
440 }\r
441\r
442 //\r
443 // Load the boot file.\r
444 //\r
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
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
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