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