]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - NetworkPkg/HttpBootDxe/HttpBootClient.c
Maintainers.txt: update mailing list information
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootClient.c
... / ...
CommitLineData
1/** @file\r
2 Implementation of the boot file download function.\r
3\r
4Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
5(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
6SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include "HttpBootDxe.h"\r
11\r
12/**\r
13 Update the device path node to include the boot resource information.\r
14\r
15 @param[in] Private The pointer to the driver's private data.\r
16\r
17 @retval EFI_SUCCESS Device patch successfully updated.\r
18 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
19 @retval Others Unexpected error happened.\r
20\r
21**/\r
22EFI_STATUS\r
23HttpBootUpdateDevicePath (\r
24 IN HTTP_BOOT_PRIVATE_DATA *Private\r
25 )\r
26{\r
27 EFI_DEV_PATH *Node;\r
28 EFI_DEVICE_PATH_PROTOCOL *TmpIpDevicePath;\r
29 EFI_DEVICE_PATH_PROTOCOL *TmpDnsDevicePath;\r
30 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
31 UINTN Length;\r
32 EFI_STATUS Status;\r
33\r
34 TmpIpDevicePath = NULL;\r
35 TmpDnsDevicePath = NULL;\r
36\r
37 //\r
38 // Update the IP node with DHCP assigned information.\r
39 //\r
40 if (!Private->UsingIpv6) {\r
41 Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));\r
42 if (Node == NULL) {\r
43 return EFI_OUT_OF_RESOURCES;\r
44 }\r
45 Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;\r
46 Node->Ipv4.Header.SubType = MSG_IPv4_DP;\r
47 SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));\r
48 CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
49 Node->Ipv4.RemotePort = Private->Port;\r
50 Node->Ipv4.Protocol = EFI_IP_PROTO_TCP;\r
51 Node->Ipv4.StaticIpAddress = FALSE;\r
52 CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));\r
53 CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
54 } else {\r
55 Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));\r
56 if (Node == NULL) {\r
57 return EFI_OUT_OF_RESOURCES;\r
58 }\r
59 Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;\r
60 Node->Ipv6.Header.SubType = MSG_IPv6_DP;\r
61 SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));\r
62 Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;\r
63 Node->Ipv6.RemotePort = Private->Port;\r
64 Node->Ipv6.Protocol = EFI_IP_PROTO_TCP;\r
65 Node->Ipv6.IpAddressOrigin = 0;\r
66 CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
67 CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
68 CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
69 }\r
70\r
71 TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
72 FreePool (Node);\r
73 if (TmpIpDevicePath == NULL) {\r
74 return EFI_OUT_OF_RESOURCES;\r
75 }\r
76\r
77 //\r
78 // Update the DNS node with DNS server IP list if existed.\r
79 //\r
80 if (Private->DnsServerIp != NULL) {\r
81 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6) + Private->DnsServerCount * sizeof (EFI_IP_ADDRESS);\r
82 Node = AllocatePool (Length);\r
83 if (Node == NULL) {\r
84 FreePool (TmpIpDevicePath);\r
85 return EFI_OUT_OF_RESOURCES;\r
86 }\r
87 Node->DevPath.Type = MESSAGING_DEVICE_PATH;\r
88 Node->DevPath.SubType = MSG_DNS_DP;\r
89 SetDevicePathNodeLength (Node, Length);\r
90 Node->Dns.IsIPv6 = Private->UsingIpv6 ? 0x01 : 0x00;\r
91 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));\r
92\r
93 TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
94 FreePool (Node);\r
95 FreePool (TmpIpDevicePath);\r
96 TmpIpDevicePath = NULL;\r
97 if (TmpDnsDevicePath == NULL) {\r
98 return EFI_OUT_OF_RESOURCES;\r
99 }\r
100 }\r
101\r
102 //\r
103 // Update the URI node with the boot file URI.\r
104 //\r
105 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);\r
106 Node = AllocatePool (Length);\r
107 if (Node == NULL) {\r
108 if (TmpIpDevicePath != NULL) {\r
109 FreePool (TmpIpDevicePath);\r
110 }\r
111 if (TmpDnsDevicePath != NULL) {\r
112 FreePool (TmpDnsDevicePath);\r
113 }\r
114 return EFI_OUT_OF_RESOURCES;\r
115 }\r
116 Node->DevPath.Type = MESSAGING_DEVICE_PATH;\r
117 Node->DevPath.SubType = MSG_URI_DP;\r
118 SetDevicePathNodeLength (Node, Length);\r
119 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));\r
120\r
121 if (TmpDnsDevicePath != NULL) {\r
122 NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
123 FreePool (TmpDnsDevicePath);\r
124 } else {\r
125 ASSERT (TmpIpDevicePath != NULL);\r
126 NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
127 FreePool (TmpIpDevicePath);\r
128 }\r
129 FreePool (Node);\r
130 if (NewDevicePath == NULL) {\r
131 return EFI_OUT_OF_RESOURCES;\r
132 }\r
133\r
134 if (!Private->UsingIpv6) {\r
135 //\r
136 // Reinstall the device path protocol of the child handle.\r
137 //\r
138 Status = gBS->ReinstallProtocolInterface (\r
139 Private->Ip4Nic->Controller,\r
140 &gEfiDevicePathProtocolGuid,\r
141 Private->Ip4Nic->DevicePath,\r
142 NewDevicePath\r
143 );\r
144 if (EFI_ERROR (Status)) {\r
145 return Status;\r
146 }\r
147\r
148 FreePool (Private->Ip4Nic->DevicePath);\r
149 Private->Ip4Nic->DevicePath = NewDevicePath;\r
150 } else {\r
151 //\r
152 // Reinstall the device path protocol of the child handle.\r
153 //\r
154 Status = gBS->ReinstallProtocolInterface (\r
155 Private->Ip6Nic->Controller,\r
156 &gEfiDevicePathProtocolGuid,\r
157 Private->Ip6Nic->DevicePath,\r
158 NewDevicePath\r
159 );\r
160 if (EFI_ERROR (Status)) {\r
161 return Status;\r
162 }\r
163 FreePool (Private->Ip6Nic->DevicePath);\r
164 Private->Ip6Nic->DevicePath = NewDevicePath;\r
165 }\r
166\r
167 return EFI_SUCCESS;\r
168}\r
169\r
170/**\r
171 Parse the boot file URI information from the selected Dhcp4 offer packet.\r
172\r
173 @param[in] Private The pointer to the driver's private data.\r
174\r
175 @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
176 @retval Others Failed to parse out the boot information.\r
177\r
178**/\r
179EFI_STATUS\r
180HttpBootDhcp4ExtractUriInfo (\r
181 IN HTTP_BOOT_PRIVATE_DATA *Private\r
182 )\r
183{\r
184 HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer;\r
185 HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer;\r
186 UINT32 SelectIndex;\r
187 UINT32 ProxyIndex;\r
188 UINT32 DnsServerIndex;\r
189 EFI_DHCP4_PACKET_OPTION *Option;\r
190 EFI_STATUS Status;\r
191\r
192 ASSERT (Private != NULL);\r
193 ASSERT (Private->SelectIndex != 0);\r
194 SelectIndex = Private->SelectIndex - 1;\r
195 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);\r
196\r
197 DnsServerIndex = 0;\r
198\r
199 Status = EFI_SUCCESS;\r
200\r
201 //\r
202 // SelectOffer contains the IP address configuration and name server configuration.\r
203 // HttpOffer contains the boot file URL.\r
204 //\r
205 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;\r
206 if (Private->FilePathUri == NULL) {\r
207 //\r
208 // In Corporate environment, we need a HttpOffer.\r
209 //\r
210 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||\r
211 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||\r
212 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {\r
213 HttpOffer = SelectOffer;\r
214 } else {\r
215 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
216 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
217 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;\r
218 }\r
219 Private->BootFileUriParser = HttpOffer->UriParser;\r
220 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
221 } else {\r
222 //\r
223 // In Home environment the BootFileUri comes from the FilePath.\r
224 //\r
225 Private->BootFileUriParser = Private->FilePathUriParser;\r
226 Private->BootFileUri = Private->FilePathUri;\r
227 }\r
228\r
229 //\r
230 // Check the URI scheme.\r
231 //\r
232 Status = HttpBootCheckUriScheme (Private->BootFileUri);\r
233 if (EFI_ERROR (Status)) {\r
234 DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status));\r
235 if (Status == EFI_INVALID_PARAMETER) {\r
236 AsciiPrint ("\n Error: Invalid URI address.\n");\r
237 } else if (Status == EFI_ACCESS_DENIED) {\r
238 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");\r
239 }\r
240 return Status;\r
241 }\r
242\r
243 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||\r
244 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||\r
245 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {\r
246 Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];\r
247 ASSERT (Option != NULL);\r
248\r
249 //\r
250 // Record the Dns Server address list.\r
251 //\r
252 Private->DnsServerCount = (Option->Length) / sizeof (EFI_IPv4_ADDRESS);\r
253\r
254 Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));\r
255 if (Private->DnsServerIp == NULL) {\r
256 return EFI_OUT_OF_RESOURCES;\r
257 }\r
258\r
259 for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {\r
260 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS));\r
261 }\r
262\r
263 //\r
264 // Configure the default DNS server if server assigned.\r
265 //\r
266 Status = HttpBootRegisterIp4Dns (\r
267 Private,\r
268 Option->Length,\r
269 Option->Data\r
270 );\r
271 if (EFI_ERROR (Status)) {\r
272 FreePool (Private->DnsServerIp);\r
273 Private->DnsServerIp = NULL;\r
274 return Status;\r
275 }\r
276 }\r
277\r
278 //\r
279 // Extract the port from URL, and use default HTTP port 80 if not provided.\r
280 //\r
281 Status = HttpUrlGetPort (\r
282 Private->BootFileUri,\r
283 Private->BootFileUriParser,\r
284 &Private->Port\r
285 );\r
286 if (EFI_ERROR (Status) || Private->Port == 0) {\r
287 Private->Port = 80;\r
288 }\r
289\r
290 //\r
291 // All boot informations are valid here.\r
292 //\r
293\r
294 //\r
295 // Update the device path to include the boot resource information.\r
296 //\r
297 Status = HttpBootUpdateDevicePath (Private);\r
298 if (EFI_ERROR (Status) && Private->DnsServerIp != NULL) {\r
299 FreePool (Private->DnsServerIp);\r
300 Private->DnsServerIp = NULL;\r
301 }\r
302\r
303 return Status;\r
304}\r
305\r
306/**\r
307 Parse the boot file URI information from the selected Dhcp6 offer packet.\r
308\r
309 @param[in] Private The pointer to the driver's private data.\r
310\r
311 @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
312 @retval Others Failed to parse out the boot information.\r
313\r
314**/\r
315EFI_STATUS\r
316HttpBootDhcp6ExtractUriInfo (\r
317 IN HTTP_BOOT_PRIVATE_DATA *Private\r
318 )\r
319{\r
320 HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer;\r
321 HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer;\r
322 UINT32 SelectIndex;\r
323 UINT32 ProxyIndex;\r
324 UINT32 DnsServerIndex;\r
325 EFI_DHCP6_PACKET_OPTION *Option;\r
326 EFI_IPv6_ADDRESS IpAddr;\r
327 CHAR8 *HostName;\r
328 UINTN HostNameSize;\r
329 CHAR16 *HostNameStr;\r
330 EFI_STATUS Status;\r
331\r
332 ASSERT (Private != NULL);\r
333 ASSERT (Private->SelectIndex != 0);\r
334 SelectIndex = Private->SelectIndex - 1;\r
335 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);\r
336\r
337 DnsServerIndex = 0;\r
338\r
339 Status = EFI_SUCCESS;\r
340 HostName = NULL;\r
341 //\r
342 // SelectOffer contains the IP address configuration and name server configuration.\r
343 // HttpOffer contains the boot file URL.\r
344 //\r
345 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
346 if (Private->FilePathUri == NULL) {\r
347 //\r
348 // In Corporate environment, we need a HttpOffer.\r
349 //\r
350 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||\r
351 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||\r
352 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {\r
353 HttpOffer = SelectOffer;\r
354 } else {\r
355 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
356 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
357 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;\r
358 }\r
359 Private->BootFileUriParser = HttpOffer->UriParser;\r
360 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;\r
361 } else {\r
362 //\r
363 // In Home environment the BootFileUri comes from the FilePath.\r
364 //\r
365 Private->BootFileUriParser = Private->FilePathUriParser;\r
366 Private->BootFileUri = Private->FilePathUri;\r
367 }\r
368\r
369 //\r
370 // Check the URI scheme.\r
371 //\r
372 Status = HttpBootCheckUriScheme (Private->BootFileUri);\r
373 if (EFI_ERROR (Status)) {\r
374 DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status));\r
375 if (Status == EFI_INVALID_PARAMETER) {\r
376 AsciiPrint ("\n Error: Invalid URI address.\n");\r
377 } else if (Status == EFI_ACCESS_DENIED) {\r
378 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");\r
379 }\r
380 return Status;\r
381 }\r
382\r
383 //\r
384 // Set the Local station address to IP layer.\r
385 //\r
386 Status = HttpBootSetIp6Address (Private);\r
387 if (EFI_ERROR (Status)) {\r
388 return Status;\r
389 }\r
390\r
391 //\r
392 // Register the IPv6 gateway address to the network device.\r
393 //\r
394 Status = HttpBootSetIp6Gateway (Private);\r
395 if (EFI_ERROR (Status)) {\r
396 return Status;\r
397 }\r
398\r
399 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||\r
400 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||\r
401 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {\r
402 Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];\r
403 ASSERT (Option != NULL);\r
404\r
405 //\r
406 // Record the Dns Server address list.\r
407 //\r
408 Private->DnsServerCount = HTONS (Option->OpLen) / sizeof (EFI_IPv6_ADDRESS);\r
409\r
410 Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));\r
411 if (Private->DnsServerIp == NULL) {\r
412 return EFI_OUT_OF_RESOURCES;\r
413 }\r
414\r
415 for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {\r
416 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS));\r
417 }\r
418\r
419 //\r
420 // Configure the default DNS server if server assigned.\r
421 //\r
422 Status = HttpBootSetIp6Dns (\r
423 Private,\r
424 HTONS (Option->OpLen),\r
425 Option->Data\r
426 );\r
427 if (EFI_ERROR (Status)) {\r
428 goto Error;\r
429 }\r
430 }\r
431\r
432 //\r
433 // Extract the HTTP server Ip from URL. This is used to Check route table\r
434 // whether can send message to HTTP Server Ip through the GateWay.\r
435 //\r
436 Status = HttpUrlGetIp6 (\r
437 Private->BootFileUri,\r
438 Private->BootFileUriParser,\r
439 &IpAddr\r
440 );\r
441\r
442 if (EFI_ERROR (Status)) {\r
443 //\r
444 // The Http server address is expressed by Name Ip, so perform DNS resolution\r
445 //\r
446 Status = HttpUrlGetHostName (\r
447 Private->BootFileUri,\r
448 Private->BootFileUriParser,\r
449 &HostName\r
450 );\r
451 if (EFI_ERROR (Status)) {\r
452 goto Error;\r
453 }\r
454\r
455 HostNameSize = AsciiStrSize (HostName);\r
456 HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));\r
457 if (HostNameStr == NULL) {\r
458 Status = EFI_OUT_OF_RESOURCES;\r
459 goto Error;\r
460 }\r
461\r
462 AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);\r
463\r
464 if (HostName != NULL) {\r
465 FreePool (HostName);\r
466 }\r
467\r
468 Status = HttpBootDns (Private, HostNameStr, &IpAddr);\r
469 FreePool (HostNameStr);\r
470 if (EFI_ERROR (Status)) {\r
471 AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n");\r
472 goto Error;\r
473 }\r
474 }\r
475\r
476 CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));\r
477\r
478 //\r
479 // Extract the port from URL, and use default HTTP port 80 if not provided.\r
480 //\r
481 Status = HttpUrlGetPort (\r
482 Private->BootFileUri,\r
483 Private->BootFileUriParser,\r
484 &Private->Port\r
485 );\r
486 if (EFI_ERROR (Status) || Private->Port == 0) {\r
487 Private->Port = 80;\r
488 }\r
489\r
490 //\r
491 // All boot informations are valid here.\r
492 //\r
493\r
494 //\r
495 // Update the device path to include the boot resource information.\r
496 //\r
497 Status = HttpBootUpdateDevicePath (Private);\r
498 if (EFI_ERROR (Status)) {\r
499 goto Error;\r
500 }\r
501\r
502 return Status;\r
503\r
504Error:\r
505 if (Private->DnsServerIp != NULL) {\r
506 FreePool (Private->DnsServerIp);\r
507 Private->DnsServerIp = NULL;\r
508 }\r
509\r
510 return Status;\r
511}\r
512\r
513\r
514/**\r
515 Discover all the boot information for boot file.\r
516\r
517 @param[in, out] Private The pointer to the driver's private data.\r
518\r
519 @retval EFI_SUCCESS Successfully obtained all the boot information .\r
520 @retval Others Failed to retrieve the boot information.\r
521\r
522**/\r
523EFI_STATUS\r
524HttpBootDiscoverBootInfo (\r
525 IN OUT HTTP_BOOT_PRIVATE_DATA *Private\r
526 )\r
527{\r
528 EFI_STATUS Status;\r
529\r
530 //\r
531 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
532 // other Http boot information.\r
533 //\r
534 Status = HttpBootDhcp (Private);\r
535 if (EFI_ERROR (Status)) {\r
536 return Status;\r
537 }\r
538\r
539 if (!Private->UsingIpv6) {\r
540 Status = HttpBootDhcp4ExtractUriInfo (Private);\r
541 } else {\r
542 Status = HttpBootDhcp6ExtractUriInfo (Private);\r
543 }\r
544\r
545 return Status;\r
546}\r
547\r
548/**\r
549 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.\r
550\r
551 @param[in] EventType Indicate the Event type that occurs in the current callback.\r
552 @param[in] Message HTTP message which will be send to, or just received from HTTP server.\r
553 @param[in] Context The Callback Context pointer.\r
554\r
555 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.\r
556 @retval Others Tells the HttpIo to abort the current HTTP process.\r
557**/\r
558EFI_STATUS\r
559EFIAPI\r
560HttpBootHttpIoCallback (\r
561 IN HTTP_IO_CALLBACK_EVENT EventType,\r
562 IN EFI_HTTP_MESSAGE *Message,\r
563 IN VOID *Context\r
564 )\r
565{\r
566 HTTP_BOOT_PRIVATE_DATA *Private;\r
567 EFI_STATUS Status;\r
568 Private = (HTTP_BOOT_PRIVATE_DATA *) Context;\r
569 if (Private->HttpBootCallback != NULL) {\r
570 Status = Private->HttpBootCallback->Callback (\r
571 Private->HttpBootCallback,\r
572 EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse,\r
573 EventType == HttpIoRequest ? FALSE : TRUE,\r
574 sizeof (EFI_HTTP_MESSAGE),\r
575 (VOID *) Message\r
576 );\r
577 return Status;\r
578 }\r
579 return EFI_SUCCESS;\r
580}\r
581\r
582/**\r
583 Create a HttpIo instance for the file download.\r
584\r
585 @param[in] Private The pointer to the driver's private data.\r
586\r
587 @retval EFI_SUCCESS Successfully created.\r
588 @retval Others Failed to create HttpIo.\r
589\r
590**/\r
591EFI_STATUS\r
592HttpBootCreateHttpIo (\r
593 IN HTTP_BOOT_PRIVATE_DATA *Private\r
594 )\r
595{\r
596 HTTP_IO_CONFIG_DATA ConfigData;\r
597 EFI_STATUS Status;\r
598 EFI_HANDLE ImageHandle;\r
599\r
600 ASSERT (Private != NULL);\r
601\r
602 ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));\r
603 if (!Private->UsingIpv6) {\r
604 ConfigData.Config4.HttpVersion = HttpVersion11;\r
605 ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
606 IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);\r
607 IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);\r
608 ImageHandle = Private->Ip4Nic->ImageHandle;\r
609 } else {\r
610 ConfigData.Config6.HttpVersion = HttpVersion11;\r
611 ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
612 IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);\r
613 ImageHandle = Private->Ip6Nic->ImageHandle;\r
614 }\r
615\r
616 Status = HttpIoCreateIo (\r
617 ImageHandle,\r
618 Private->Controller,\r
619 Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,\r
620 &ConfigData,\r
621 HttpBootHttpIoCallback,\r
622 (VOID *) Private,\r
623 &Private->HttpIo\r
624 );\r
625 if (EFI_ERROR (Status)) {\r
626 return Status;\r
627 }\r
628\r
629 Private->HttpCreated = TRUE;\r
630 return EFI_SUCCESS;\r
631}\r
632\r
633/**\r
634 Release all the resource of a cache item.\r
635\r
636 @param[in] Cache The pointer to the cache item.\r
637\r
638**/\r
639VOID\r
640HttpBootFreeCache (\r
641 IN HTTP_BOOT_CACHE_CONTENT *Cache\r
642 )\r
643{\r
644 UINTN Index;\r
645 LIST_ENTRY *Entry;\r
646 LIST_ENTRY *NextEntry;\r
647 HTTP_BOOT_ENTITY_DATA *EntityData;\r
648\r
649 if (Cache != NULL) {\r
650 //\r
651 // Free the request data\r
652 //\r
653 if (Cache->RequestData != NULL) {\r
654 if (Cache->RequestData->Url != NULL) {\r
655 FreePool (Cache->RequestData->Url);\r
656 }\r
657 FreePool (Cache->RequestData);\r
658 }\r
659\r
660 //\r
661 // Free the response header\r
662 //\r
663 if (Cache->ResponseData != NULL) {\r
664 if (Cache->ResponseData->Headers != NULL) {\r
665 for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {\r
666 FreePool (Cache->ResponseData->Headers[Index].FieldName);\r
667 FreePool (Cache->ResponseData->Headers[Index].FieldValue);\r
668 }\r
669 FreePool (Cache->ResponseData->Headers);\r
670 }\r
671 }\r
672\r
673 //\r
674 // Free the response body\r
675 //\r
676 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {\r
677 EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);\r
678 if (EntityData->Block != NULL) {\r
679 FreePool (EntityData->Block);\r
680 }\r
681 RemoveEntryList (&EntityData->Link);\r
682 FreePool (EntityData);\r
683 }\r
684\r
685 FreePool (Cache);\r
686 }\r
687}\r
688\r
689/**\r
690 Clean up all cached data.\r
691\r
692 @param[in] Private The pointer to the driver's private data.\r
693\r
694**/\r
695VOID\r
696HttpBootFreeCacheList (\r
697 IN HTTP_BOOT_PRIVATE_DATA *Private\r
698 )\r
699{\r
700 LIST_ENTRY *Entry;\r
701 LIST_ENTRY *NextEntry;\r
702 HTTP_BOOT_CACHE_CONTENT *Cache;\r
703\r
704 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {\r
705 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
706 RemoveEntryList (&Cache->Link);\r
707 HttpBootFreeCache (Cache);\r
708 }\r
709}\r
710\r
711/**\r
712 Get the file content from cached data.\r
713\r
714 @param[in] Private The pointer to the driver's private data.\r
715 @param[in] Uri Uri of the file to be retrieved from cache.\r
716 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
717 code of EFI_SUCCESS, the amount of data transferred to\r
718 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
719 the size of Buffer required to retrieve the requested file.\r
720 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
721 then the size of the requested file is returned in\r
722 BufferSize.\r
723 @param[out] ImageType The image type of the downloaded file.\r
724\r
725 @retval EFI_SUCCESS Successfully created.\r
726 @retval Others Failed to create HttpIo.\r
727\r
728**/\r
729EFI_STATUS\r
730HttpBootGetFileFromCache (\r
731 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
732 IN CHAR16 *Uri,\r
733 IN OUT UINTN *BufferSize,\r
734 OUT UINT8 *Buffer,\r
735 OUT HTTP_BOOT_IMAGE_TYPE *ImageType\r
736 )\r
737{\r
738 LIST_ENTRY *Entry;\r
739 LIST_ENTRY *Entry2;\r
740 HTTP_BOOT_CACHE_CONTENT *Cache;\r
741 HTTP_BOOT_ENTITY_DATA *EntityData;\r
742 UINTN CopyedSize;\r
743\r
744 if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) {\r
745 return EFI_INVALID_PARAMETER;\r
746 }\r
747\r
748 NET_LIST_FOR_EACH (Entry, &Private->CacheList) {\r
749 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
750 //\r
751 // Compare the URI to see whether we already have a cache for this file.\r
752 //\r
753 if ((Cache->RequestData != NULL) &&\r
754 (Cache->RequestData->Url != NULL) &&\r
755 (StrCmp (Uri, Cache->RequestData->Url) == 0)) {\r
756 //\r
757 // Hit in cache, record image type.\r
758 //\r
759 *ImageType = Cache->ImageType;\r
760\r
761 //\r
762 // Check buffer size.\r
763 //\r
764 if (*BufferSize < Cache->EntityLength) {\r
765 *BufferSize = Cache->EntityLength;\r
766 return EFI_BUFFER_TOO_SMALL;\r
767 }\r
768\r
769 //\r
770 // Fill data to buffer.\r
771 //\r
772 CopyedSize = 0;\r
773 NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {\r
774 EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);\r
775 if (*BufferSize > CopyedSize) {\r
776 CopyMem (\r
777 Buffer + CopyedSize,\r
778 EntityData->DataStart,\r
779 MIN (EntityData->DataLength, *BufferSize - CopyedSize)\r
780 );\r
781 CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);\r
782 }\r
783 }\r
784 *BufferSize = CopyedSize;\r
785 return EFI_SUCCESS;\r
786 }\r
787 }\r
788\r
789 return EFI_NOT_FOUND;\r
790}\r
791\r
792/**\r
793 A callback function to intercept events during message parser.\r
794\r
795 This function will be invoked during HttpParseMessageBody() with various events type. An error\r
796 return status of the callback function will cause the HttpParseMessageBody() aborted.\r
797\r
798 @param[in] EventType Event type of this callback call.\r
799 @param[in] Data A pointer to data buffer.\r
800 @param[in] Length Length in bytes of the Data.\r
801 @param[in] Context Callback context set by HttpInitMsgParser().\r
802\r
803 @retval EFI_SUCCESS Continue to parser the message body.\r
804 @retval Others Abort the parse.\r
805\r
806**/\r
807EFI_STATUS\r
808EFIAPI\r
809HttpBootGetBootFileCallback (\r
810 IN HTTP_BODY_PARSE_EVENT EventType,\r
811 IN CHAR8 *Data,\r
812 IN UINTN Length,\r
813 IN VOID *Context\r
814 )\r
815{\r
816 HTTP_BOOT_CALLBACK_DATA *CallbackData;\r
817 HTTP_BOOT_ENTITY_DATA *NewEntityData;\r
818 EFI_STATUS Status;\r
819 EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;\r
820\r
821 //\r
822 // We only care about the entity data.\r
823 //\r
824 if (EventType != BodyParseEventOnData) {\r
825 return EFI_SUCCESS;\r
826 }\r
827\r
828 CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;\r
829 HttpBootCallback = CallbackData->Private->HttpBootCallback;\r
830 if (HttpBootCallback != NULL) {\r
831 Status = HttpBootCallback->Callback (\r
832 HttpBootCallback,\r
833 HttpBootHttpEntityBody,\r
834 TRUE,\r
835 (UINT32)Length,\r
836 Data\r
837 );\r
838 if (EFI_ERROR (Status)) {\r
839 return Status;\r
840 }\r
841 }\r
842 //\r
843 // Copy data if caller has provided a buffer.\r
844 //\r
845 if (CallbackData->BufferSize > CallbackData->CopyedSize) {\r
846 CopyMem (\r
847 CallbackData->Buffer + CallbackData->CopyedSize,\r
848 Data,\r
849 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)\r
850 );\r
851 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);\r
852 }\r
853\r
854 //\r
855 // The caller doesn't provide a buffer, save the block into cache list.\r
856 //\r
857 if (CallbackData->Cache != NULL) {\r
858 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));\r
859 if (NewEntityData == NULL) {\r
860 return EFI_OUT_OF_RESOURCES;\r
861 }\r
862 if (CallbackData->NewBlock) {\r
863 NewEntityData->Block = CallbackData->Block;\r
864 CallbackData->Block = NULL;\r
865 }\r
866 NewEntityData->DataLength = Length;\r
867 NewEntityData->DataStart = (UINT8*) Data;\r
868 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);\r
869 }\r
870 return EFI_SUCCESS;\r
871}\r
872\r
873/**\r
874 This function download the boot file by using UEFI HTTP protocol.\r
875\r
876 @param[in] Private The pointer to the driver's private data.\r
877 @param[in] HeaderOnly Only request the response header, it could save a lot of time if\r
878 the caller only want to know the size of the requested file.\r
879 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
880 code of EFI_SUCCESS, the amount of data transferred to\r
881 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
882 the size of Buffer required to retrieve the requested file.\r
883 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
884 then the size of the requested file is returned in\r
885 BufferSize.\r
886 @param[out] ImageType The image type of the downloaded file.\r
887\r
888 @retval EFI_SUCCESS The file was loaded.\r
889 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.\r
890 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources\r
891 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.\r
892 BufferSize has been updated with the size needed to complete\r
893 the request.\r
894 @retval Others Unexpected error happened.\r
895\r
896**/\r
897EFI_STATUS\r
898HttpBootGetBootFile (\r
899 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
900 IN BOOLEAN HeaderOnly,\r
901 IN OUT UINTN *BufferSize,\r
902 OUT UINT8 *Buffer,\r
903 OUT HTTP_BOOT_IMAGE_TYPE *ImageType\r
904 )\r
905{\r
906 EFI_STATUS Status;\r
907 EFI_HTTP_STATUS_CODE StatusCode;\r
908 CHAR8 *HostName;\r
909 EFI_HTTP_REQUEST_DATA *RequestData;\r
910 HTTP_IO_RESPONSE_DATA *ResponseData;\r
911 HTTP_IO_RESPONSE_DATA ResponseBody;\r
912 HTTP_IO *HttpIo;\r
913 HTTP_IO_HEADER *HttpIoHeader;\r
914 VOID *Parser;\r
915 HTTP_BOOT_CALLBACK_DATA Context;\r
916 UINTN ContentLength;\r
917 HTTP_BOOT_CACHE_CONTENT *Cache;\r
918 UINT8 *Block;\r
919 UINTN UrlSize;\r
920 CHAR16 *Url;\r
921 BOOLEAN IdentityMode;\r
922 UINTN ReceivedSize;\r
923\r
924 ASSERT (Private != NULL);\r
925 ASSERT (Private->HttpCreated);\r
926\r
927 if (BufferSize == NULL || ImageType == NULL) {\r
928 return EFI_INVALID_PARAMETER;\r
929 }\r
930\r
931 if (*BufferSize != 0 && Buffer == NULL) {\r
932 return EFI_INVALID_PARAMETER;\r
933 }\r
934\r
935 //\r
936 // First, check whether we already cached the requested Uri.\r
937 //\r
938 UrlSize = AsciiStrSize (Private->BootFileUri);\r
939 Url = AllocatePool (UrlSize * sizeof (CHAR16));\r
940 if (Url == NULL) {\r
941 return EFI_OUT_OF_RESOURCES;\r
942 }\r
943 AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);\r
944 if (!HeaderOnly && Buffer != NULL) {\r
945 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);\r
946 if (Status != EFI_NOT_FOUND) {\r
947 FreePool (Url);\r
948 return Status;\r
949 }\r
950 }\r
951\r
952 //\r
953 // Not found in cache, try to download it through HTTP.\r
954 //\r
955\r
956 //\r
957 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.\r
958 //\r
959 Cache = NULL;\r
960 if ((!HeaderOnly) && (*BufferSize == 0)) {\r
961 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));\r
962 if (Cache == NULL) {\r
963 Status = EFI_OUT_OF_RESOURCES;\r
964 goto ERROR_1;\r
965 }\r
966 Cache->ImageType = ImageTypeMax;\r
967 InitializeListHead (&Cache->EntityDataList);\r
968 }\r
969\r
970 //\r
971 // 2. Send HTTP request message.\r
972 //\r
973\r
974 //\r
975 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:\r
976 // Host\r
977 // Accept\r
978 // User-Agent\r
979 //\r
980 HttpIoHeader = HttpBootCreateHeader (3);\r
981 if (HttpIoHeader == NULL) {\r
982 Status = EFI_OUT_OF_RESOURCES;\r
983 goto ERROR_2;\r
984 }\r
985\r
986 //\r
987 // Add HTTP header field 1: Host\r
988 //\r
989 HostName = NULL;\r
990 Status = HttpUrlGetHostName (\r
991 Private->BootFileUri,\r
992 Private->BootFileUriParser,\r
993 &HostName\r
994 );\r
995 if (EFI_ERROR (Status)) {\r
996 goto ERROR_3;\r
997 }\r
998 Status = HttpBootSetHeader (\r
999 HttpIoHeader,\r
1000 HTTP_HEADER_HOST,\r
1001 HostName\r
1002 );\r
1003 FreePool (HostName);\r
1004 if (EFI_ERROR (Status)) {\r
1005 goto ERROR_3;\r
1006 }\r
1007\r
1008 //\r
1009 // Add HTTP header field 2: Accept\r
1010 //\r
1011 Status = HttpBootSetHeader (\r
1012 HttpIoHeader,\r
1013 HTTP_HEADER_ACCEPT,\r
1014 "*/*"\r
1015 );\r
1016 if (EFI_ERROR (Status)) {\r
1017 goto ERROR_3;\r
1018 }\r
1019\r
1020 //\r
1021 // Add HTTP header field 3: User-Agent\r
1022 //\r
1023 Status = HttpBootSetHeader (\r
1024 HttpIoHeader,\r
1025 HTTP_HEADER_USER_AGENT,\r
1026 HTTP_USER_AGENT_EFI_HTTP_BOOT\r
1027 );\r
1028 if (EFI_ERROR (Status)) {\r
1029 goto ERROR_3;\r
1030 }\r
1031\r
1032 //\r
1033 // 2.2 Build the rest of HTTP request info.\r
1034 //\r
1035 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));\r
1036 if (RequestData == NULL) {\r
1037 Status = EFI_OUT_OF_RESOURCES;\r
1038 goto ERROR_3;\r
1039 }\r
1040 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;\r
1041 RequestData->Url = Url;\r
1042\r
1043 //\r
1044 // 2.3 Record the request info in a temp cache item.\r
1045 //\r
1046 if (Cache != NULL) {\r
1047 Cache->RequestData = RequestData;\r
1048 }\r
1049\r
1050 //\r
1051 // 2.4 Send out the request to HTTP server.\r
1052 //\r
1053 HttpIo = &Private->HttpIo;\r
1054 Status = HttpIoSendRequest (\r
1055 HttpIo,\r
1056 RequestData,\r
1057 HttpIoHeader->HeaderCount,\r
1058 HttpIoHeader->Headers,\r
1059 0,\r
1060 NULL\r
1061 );\r
1062 if (EFI_ERROR (Status)) {\r
1063 goto ERROR_4;\r
1064 }\r
1065\r
1066 //\r
1067 // 3. Receive HTTP response message.\r
1068 //\r
1069\r
1070 //\r
1071 // 3.1 First step, use zero BodyLength to only receive the response headers.\r
1072 //\r
1073 ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));\r
1074 if (ResponseData == NULL) {\r
1075 Status = EFI_OUT_OF_RESOURCES;\r
1076 goto ERROR_4;\r
1077 }\r
1078 Status = HttpIoRecvResponse (\r
1079 &Private->HttpIo,\r
1080 TRUE,\r
1081 ResponseData\r
1082 );\r
1083 if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {\r
1084 if (EFI_ERROR (ResponseData->Status)) {\r
1085 StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;\r
1086 HttpBootPrintErrorMessage (StatusCode);\r
1087 Status = ResponseData->Status;\r
1088 }\r
1089 goto ERROR_5;\r
1090 }\r
1091\r
1092 //\r
1093 // Check the image type according to server's response.\r
1094 //\r
1095 Status = HttpBootCheckImageType (\r
1096 Private->BootFileUri,\r
1097 Private->BootFileUriParser,\r
1098 ResponseData->HeaderCount,\r
1099 ResponseData->Headers,\r
1100 ImageType\r
1101 );\r
1102 if (EFI_ERROR (Status)) {\r
1103 goto ERROR_5;\r
1104 }\r
1105\r
1106 //\r
1107 // 3.2 Cache the response header.\r
1108 //\r
1109 if (Cache != NULL) {\r
1110 Cache->ResponseData = ResponseData;\r
1111 Cache->ImageType = *ImageType;\r
1112 }\r
1113\r
1114 //\r
1115 // 3.3 Init a message-body parser from the header information.\r
1116 //\r
1117 Parser = NULL;\r
1118 Context.NewBlock = FALSE;\r
1119 Context.Block = NULL;\r
1120 Context.CopyedSize = 0;\r
1121 Context.Buffer = Buffer;\r
1122 Context.BufferSize = *BufferSize;\r
1123 Context.Cache = Cache;\r
1124 Context.Private = Private;\r
1125 Status = HttpInitMsgParser (\r
1126 HeaderOnly ? HttpMethodHead : HttpMethodGet,\r
1127 ResponseData->Response.StatusCode,\r
1128 ResponseData->HeaderCount,\r
1129 ResponseData->Headers,\r
1130 HttpBootGetBootFileCallback,\r
1131 (VOID*) &Context,\r
1132 &Parser\r
1133 );\r
1134 if (EFI_ERROR (Status)) {\r
1135 goto ERROR_6;\r
1136 }\r
1137\r
1138 //\r
1139 // 3.4 Continue to receive and parse message-body if needed.\r
1140 //\r
1141 Block = NULL;\r
1142 if (!HeaderOnly) {\r
1143 //\r
1144 // 3.4.1, check whether we are in identity transfer-coding.\r
1145 //\r
1146 ContentLength = 0;\r
1147 Status = HttpGetEntityLength (Parser, &ContentLength);\r
1148 if (!EFI_ERROR (Status)) {\r
1149 IdentityMode = TRUE;\r
1150 } else {\r
1151 IdentityMode = FALSE;\r
1152 }\r
1153\r
1154 //\r
1155 // 3.4.2, start the message-body download, the identity and chunked transfer-coding\r
1156 // is handled in different path here.\r
1157 //\r
1158 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));\r
1159 if (IdentityMode) {\r
1160 //\r
1161 // In identity transfer-coding there is no need to parse the message body,\r
1162 // just download the message body to the user provided buffer directly.\r
1163 //\r
1164 ReceivedSize = 0;\r
1165 while (ReceivedSize < ContentLength) {\r
1166 ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize;\r
1167 ResponseBody.BodyLength = *BufferSize - ReceivedSize;\r
1168 Status = HttpIoRecvResponse (\r
1169 &Private->HttpIo,\r
1170 FALSE,\r
1171 &ResponseBody\r
1172 );\r
1173 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {\r
1174 if (EFI_ERROR (ResponseBody.Status)) {\r
1175 Status = ResponseBody.Status;\r
1176 }\r
1177 goto ERROR_6;\r
1178 }\r
1179 ReceivedSize += ResponseBody.BodyLength;\r
1180 if (Private->HttpBootCallback != NULL) {\r
1181 Status = Private->HttpBootCallback->Callback (\r
1182 Private->HttpBootCallback,\r
1183 HttpBootHttpEntityBody,\r
1184 TRUE,\r
1185 (UINT32)ResponseBody.BodyLength,\r
1186 ResponseBody.Body\r
1187 );\r
1188 if (EFI_ERROR (Status)) {\r
1189 goto ERROR_6;\r
1190 }\r
1191 }\r
1192 }\r
1193 } else {\r
1194 //\r
1195 // In "chunked" transfer-coding mode, so we need to parse the received\r
1196 // data to get the real entity content.\r
1197 //\r
1198 Block = NULL;\r
1199 while (!HttpIsMessageComplete (Parser)) {\r
1200 //\r
1201 // Allocate a buffer in Block to hold the message-body.\r
1202 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().\r
1203 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before\r
1204 // every HttpIoRecvResponse().\r
1205 //\r
1206 if (Block == NULL || Context.BufferSize == 0) {\r
1207 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);\r
1208 if (Block == NULL) {\r
1209 Status = EFI_OUT_OF_RESOURCES;\r
1210 goto ERROR_6;\r
1211 }\r
1212 Context.NewBlock = TRUE;\r
1213 Context.Block = Block;\r
1214 } else {\r
1215 Context.NewBlock = FALSE;\r
1216 }\r
1217\r
1218 ResponseBody.Body = (CHAR8*) Block;\r
1219 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;\r
1220 Status = HttpIoRecvResponse (\r
1221 &Private->HttpIo,\r
1222 FALSE,\r
1223 &ResponseBody\r
1224 );\r
1225 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {\r
1226 if (EFI_ERROR (ResponseBody.Status)) {\r
1227 Status = ResponseBody.Status;\r
1228 }\r
1229 goto ERROR_6;\r
1230 }\r
1231\r
1232 //\r
1233 // Parse the new received block of the message-body, the block will be saved in cache.\r
1234 //\r
1235 Status = HttpParseMessageBody (\r
1236 Parser,\r
1237 ResponseBody.BodyLength,\r
1238 ResponseBody.Body\r
1239 );\r
1240 if (EFI_ERROR (Status)) {\r
1241 goto ERROR_6;\r
1242 }\r
1243 }\r
1244 }\r
1245 }\r
1246\r
1247 //\r
1248 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.\r
1249 //\r
1250 Status = HttpGetEntityLength (Parser, &ContentLength);\r
1251 if (EFI_ERROR (Status)) {\r
1252 goto ERROR_6;\r
1253 }\r
1254\r
1255 if (*BufferSize < ContentLength) {\r
1256 Status = EFI_BUFFER_TOO_SMALL;\r
1257 } else {\r
1258 Status = EFI_SUCCESS;\r
1259 }\r
1260 *BufferSize = ContentLength;\r
1261\r
1262 //\r
1263 // 4. Save the cache item to driver's cache list and return.\r
1264 //\r
1265 if (Cache != NULL) {\r
1266 Cache->EntityLength = ContentLength;\r
1267 InsertTailList (&Private->CacheList, &Cache->Link);\r
1268 }\r
1269\r
1270 if (Parser != NULL) {\r
1271 HttpFreeMsgParser (Parser);\r
1272 }\r
1273\r
1274 return Status;\r
1275\r
1276ERROR_6:\r
1277 if (Parser != NULL) {\r
1278 HttpFreeMsgParser (Parser);\r
1279 }\r
1280 if (Context.Block != NULL) {\r
1281 FreePool (Context.Block);\r
1282 }\r
1283 HttpBootFreeCache (Cache);\r
1284\r
1285ERROR_5:\r
1286 if (ResponseData != NULL) {\r
1287 FreePool (ResponseData);\r
1288 }\r
1289ERROR_4:\r
1290 if (RequestData != NULL) {\r
1291 FreePool (RequestData);\r
1292 }\r
1293ERROR_3:\r
1294 HttpBootFreeHeader (HttpIoHeader);\r
1295ERROR_2:\r
1296 if (Cache != NULL) {\r
1297 FreePool (Cache);\r
1298 }\r
1299ERROR_1:\r
1300 if (Url != NULL) {\r
1301 FreePool (Url);\r
1302 }\r
1303\r
1304 return Status;\r
1305}\r
1306\r