]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - NetworkPkg/HttpBootDxe/HttpBootClient.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 - 2021, 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\r
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
55 } else {\r
56 Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));\r
57 if (Node == NULL) {\r
58 return EFI_OUT_OF_RESOURCES;\r
59 }\r
60\r
61 Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;\r
62 Node->Ipv6.Header.SubType = MSG_IPv6_DP;\r
63 SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));\r
64 Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;\r
65 Node->Ipv6.RemotePort = Private->Port;\r
66 Node->Ipv6.Protocol = EFI_IP_PROTO_TCP;\r
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
72\r
73 TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)Node);\r
74 FreePool (Node);\r
75 if (TmpIpDevicePath == NULL) {\r
76 return EFI_OUT_OF_RESOURCES;\r
77 }\r
78\r
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
84 Node = AllocatePool (Length);\r
85 if (Node == NULL) {\r
86 FreePool (TmpIpDevicePath);\r
87 return EFI_OUT_OF_RESOURCES;\r
88 }\r
89\r
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
94 CopyMem ((UINT8 *)Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));\r
95\r
96 TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)Node);\r
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
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
109 Node = AllocatePool (Length);\r
110 if (Node == NULL) {\r
111 if (TmpIpDevicePath != NULL) {\r
112 FreePool (TmpIpDevicePath);\r
113 }\r
114\r
115 if (TmpDnsDevicePath != NULL) {\r
116 FreePool (TmpDnsDevicePath);\r
117 }\r
118\r
119 return EFI_OUT_OF_RESOURCES;\r
120 }\r
121\r
122 Node->DevPath.Type = MESSAGING_DEVICE_PATH;\r
123 Node->DevPath.SubType = MSG_URI_DP;\r
124 SetDevicePathNodeLength (Node, Length);\r
125 CopyMem ((UINT8 *)Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));\r
126\r
127 if (TmpDnsDevicePath != NULL) {\r
128 NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)Node);\r
129 FreePool (TmpDnsDevicePath);\r
130 } else {\r
131 ASSERT (TmpIpDevicePath != NULL);\r
132 NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)Node);\r
133 FreePool (TmpIpDevicePath);\r
134 }\r
135\r
136 FreePool (Node);\r
137 if (NewDevicePath == NULL) {\r
138 return EFI_OUT_OF_RESOURCES;\r
139 }\r
140\r
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
154\r
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
170\r
171 FreePool (Private->Ip6Nic->DevicePath);\r
172 Private->Ip6Nic->DevicePath = NewDevicePath;\r
173 }\r
174\r
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
188HttpBootDhcp4ExtractUriInfo (\r
189 IN HTTP_BOOT_PRIVATE_DATA *Private\r
190 )\r
191{\r
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
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
205 DnsServerIndex = 0;\r
206\r
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
214 if (Private->FilePathUri == NULL) {\r
215 //\r
216 // In Corporate environment, we need a HttpOffer.\r
217 //\r
218 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||\r
219 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||\r
220 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns))\r
221 {\r
222 HttpOffer = SelectOffer;\r
223 } else {\r
224 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
225 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
226 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;\r
227 }\r
228\r
229 Private->BootFileUriParser = HttpOffer->UriParser;\r
230 Private->BootFileUri = (CHAR8 *)HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
231 } else {\r
232 //\r
233 // In Home environment the BootFileUri comes from the FilePath.\r
234 //\r
235 Private->BootFileUriParser = Private->FilePathUriParser;\r
236 Private->BootFileUri = Private->FilePathUri;\r
237 }\r
238\r
239 //\r
240 // Check the URI scheme.\r
241 //\r
242 Status = HttpBootCheckUriScheme (Private->BootFileUri);\r
243 if (EFI_ERROR (Status)) {\r
244 DEBUG ((DEBUG_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status));\r
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
250\r
251 return Status;\r
252 }\r
253\r
254 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||\r
255 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||\r
256 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns))\r
257 {\r
258 Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];\r
259 ASSERT (Option != NULL);\r
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
272 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *)Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS));\r
273 }\r
274\r
275 //\r
276 // Configure the default DNS server if server assigned.\r
277 //\r
278 Status = HttpBootRegisterIp4Dns (\r
279 Private,\r
280 Option->Length,\r
281 Option->Data\r
282 );\r
283 if (EFI_ERROR (Status)) {\r
284 FreePool (Private->DnsServerIp);\r
285 Private->DnsServerIp = NULL;\r
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
294 Private->BootFileUri,\r
295 Private->BootFileUriParser,\r
296 &Private->Port\r
297 );\r
298 if (EFI_ERROR (Status) || (Private->Port == 0)) {\r
299 Private->Port = 80;\r
300 }\r
301\r
302 //\r
303 // All boot informations are valid here.\r
304 //\r
305\r
306 //\r
307 // Update the device path to include the boot resource information.\r
308 //\r
309 Status = HttpBootUpdateDevicePath (Private);\r
310 if (EFI_ERROR (Status) && (Private->DnsServerIp != NULL)) {\r
311 FreePool (Private->DnsServerIp);\r
312 Private->DnsServerIp = NULL;\r
313 }\r
314\r
315 return Status;\r
316}\r
317\r
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
329 IN HTTP_BOOT_PRIVATE_DATA *Private\r
330 )\r
331{\r
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
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
349 DnsServerIndex = 0;\r
350\r
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
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
364 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns))\r
365 {\r
366 HttpOffer = SelectOffer;\r
367 } else {\r
368 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
369 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
370 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;\r
371 }\r
372\r
373 Private->BootFileUriParser = HttpOffer->UriParser;\r
374 Private->BootFileUri = (CHAR8 *)HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;\r
375 } else {\r
376 //\r
377 // In Home environment the BootFileUri comes from the FilePath.\r
378 //\r
379 Private->BootFileUriParser = Private->FilePathUriParser;\r
380 Private->BootFileUri = Private->FilePathUri;\r
381 }\r
382\r
383 //\r
384 // Check the URI scheme.\r
385 //\r
386 Status = HttpBootCheckUriScheme (Private->BootFileUri);\r
387 if (EFI_ERROR (Status)) {\r
388 DEBUG ((DEBUG_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status));\r
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
394\r
395 return Status;\r
396 }\r
397\r
398 //\r
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
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
413\r
414 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||\r
415 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||\r
416 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns))\r
417 {\r
418 Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];\r
419 ASSERT (Option != NULL);\r
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
432 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *)Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS));\r
433 }\r
434\r
435 //\r
436 // Configure the default DNS server if server assigned.\r
437 //\r
438 Status = HttpBootSetIp6Dns (\r
439 Private,\r
440 HTONS (Option->OpLen),\r
441 Option->Data\r
442 );\r
443 if (EFI_ERROR (Status)) {\r
444 goto Error;\r
445 }\r
446 }\r
447\r
448 //\r
449 // Extract the HTTP server Ip from URL. This is used to Check route table\r
450 // whether can send message to HTTP Server Ip through the GateWay.\r
451 //\r
452 Status = HttpUrlGetIp6 (\r
453 Private->BootFileUri,\r
454 Private->BootFileUriParser,\r
455 &IpAddr\r
456 );\r
457\r
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
463 Private->BootFileUri,\r
464 Private->BootFileUriParser,\r
465 &HostName\r
466 );\r
467 if (EFI_ERROR (Status)) {\r
468 goto Error;\r
469 }\r
470\r
471 HostNameSize = AsciiStrSize (HostName);\r
472 HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));\r
473 if (HostNameStr == NULL) {\r
474 Status = EFI_OUT_OF_RESOURCES;\r
475 goto Error;\r
476 }\r
477\r
478 AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);\r
479\r
480 if (HostName != NULL) {\r
481 FreePool (HostName);\r
482 }\r
483\r
484 Status = HttpBootDns (Private, HostNameStr, &IpAddr);\r
485 FreePool (HostNameStr);\r
486 if (EFI_ERROR (Status)) {\r
487 AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n");\r
488 goto Error;\r
489 }\r
490 }\r
491\r
492 CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));\r
493\r
494 //\r
495 // Extract the port from URL, and use default HTTP port 80 if not provided.\r
496 //\r
497 Status = HttpUrlGetPort (\r
498 Private->BootFileUri,\r
499 Private->BootFileUriParser,\r
500 &Private->Port\r
501 );\r
502 if (EFI_ERROR (Status) || (Private->Port == 0)) {\r
503 Private->Port = 80;\r
504 }\r
505\r
506 //\r
507 // All boot informations are valid here.\r
508 //\r
509\r
510 //\r
511 // Update the device path to include the boot resource information.\r
512 //\r
513 Status = HttpBootUpdateDevicePath (Private);\r
514 if (EFI_ERROR (Status)) {\r
515 goto Error;\r
516 }\r
517\r
518 return Status;\r
519\r
520Error:\r
521 if (Private->DnsServerIp != NULL) {\r
522 FreePool (Private->DnsServerIp);\r
523 Private->DnsServerIp = NULL;\r
524 }\r
525\r
526 return Status;\r
527}\r
528\r
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
540 IN OUT HTTP_BOOT_PRIVATE_DATA *Private\r
541 )\r
542{\r
543 EFI_STATUS Status;\r
544\r
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
555 Status = HttpBootDhcp4ExtractUriInfo (Private);\r
556 } else {\r
557 Status = HttpBootDhcp6ExtractUriInfo (Private);\r
558 }\r
559\r
560 return Status;\r
561}\r
562\r
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
569\r
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
576 IN HTTP_IO_CALLBACK_EVENT EventType,\r
577 IN EFI_HTTP_MESSAGE *Message,\r
578 IN VOID *Context\r
579 )\r
580{\r
581 HTTP_BOOT_PRIVATE_DATA *Private;\r
582 EFI_STATUS Status;\r
583\r
584 Private = (HTTP_BOOT_PRIVATE_DATA *)Context;\r
585 if (Private->HttpBootCallback != NULL) {\r
586 Status = Private->HttpBootCallback->Callback (\r
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
593 return Status;\r
594 }\r
595\r
596 return EFI_SUCCESS;\r
597}\r
598\r
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
610 IN HTTP_BOOT_PRIVATE_DATA *Private\r
611 )\r
612{\r
613 HTTP_IO_CONFIG_DATA ConfigData;\r
614 EFI_STATUS Status;\r
615 EFI_HANDLE ImageHandle;\r
616 UINT32 TimeoutValue;\r
617\r
618 ASSERT (Private != NULL);\r
619\r
620 //\r
621 // Get HTTP timeout value\r
622 //\r
623 TimeoutValue = PcdGet32 (PcdHttpIoTimeout);\r
624\r
625 ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));\r
626 if (!Private->UsingIpv6) {\r
627 ConfigData.Config4.HttpVersion = HttpVersion11;\r
628 ConfigData.Config4.RequestTimeOut = TimeoutValue;\r
629 IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);\r
630 IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);\r
631 ImageHandle = Private->Ip4Nic->ImageHandle;\r
632 } else {\r
633 ConfigData.Config6.HttpVersion = HttpVersion11;\r
634 ConfigData.Config6.RequestTimeOut = TimeoutValue;\r
635 IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);\r
636 ImageHandle = Private->Ip6Nic->ImageHandle;\r
637 }\r
638\r
639 Status = HttpIoCreateIo (\r
640 ImageHandle,\r
641 Private->Controller,\r
642 Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,\r
643 &ConfigData,\r
644 HttpBootHttpIoCallback,\r
645 (VOID *)Private,\r
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
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
664 IN HTTP_BOOT_CACHE_CONTENT *Cache\r
665 )\r
666{\r
667 UINTN Index;\r
668 LIST_ENTRY *Entry;\r
669 LIST_ENTRY *NextEntry;\r
670 HTTP_BOOT_ENTITY_DATA *EntityData;\r
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
680\r
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
693\r
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
706\r
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
723 IN HTTP_BOOT_PRIVATE_DATA *Private\r
724 )\r
725{\r
726 LIST_ENTRY *Entry;\r
727 LIST_ENTRY *NextEntry;\r
728 HTTP_BOOT_CACHE_CONTENT *Cache;\r
729\r
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
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
749 @param[out] ImageType The image type of the downloaded file.\r
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
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
762 )\r
763{\r
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
769\r
770 if ((Uri == NULL) || (BufferSize == NULL) || (Buffer == NULL) || (ImageType == NULL)) {\r
771 return EFI_INVALID_PARAMETER;\r
772 }\r
773\r
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
781 (StrCmp (Uri, Cache->RequestData->Url) == 0))\r
782 {\r
783 //\r
784 // Hit in cache, record image type.\r
785 //\r
786 *ImageType = Cache->ImageType;\r
787\r
788 //\r
789 // Check buffer size.\r
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
812 return EFI_SUCCESS;\r
813 }\r
814 }\r
815\r
816 return EFI_NOT_FOUND;\r
817}\r
818\r
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
832\r
833**/\r
834EFI_STATUS\r
835EFIAPI\r
836HttpBootGetBootFileCallback (\r
837 IN HTTP_BODY_PARSE_EVENT EventType,\r
838 IN CHAR8 *Data,\r
839 IN UINTN Length,\r
840 IN VOID *Context\r
841 )\r
842{\r
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
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
855 CallbackData = (HTTP_BOOT_CALLBACK_DATA *)Context;\r
856 HttpBootCallback = CallbackData->Private->HttpBootCallback;\r
857 if (HttpBootCallback != NULL) {\r
858 Status = HttpBootCallback->Callback (\r
859 HttpBootCallback,\r
860 HttpBootHttpEntityBody,\r
861 TRUE,\r
862 (UINT32)Length,\r
863 Data\r
864 );\r
865 if (EFI_ERROR (Status)) {\r
866 return Status;\r
867 }\r
868 }\r
869\r
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
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
890\r
891 if (CallbackData->NewBlock) {\r
892 NewEntityData->Block = CallbackData->Block;\r
893 CallbackData->Block = NULL;\r
894 }\r
895\r
896 NewEntityData->DataLength = Length;\r
897 NewEntityData->DataStart = (UINT8 *)Data;\r
898 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);\r
899 }\r
900\r
901 return EFI_SUCCESS;\r
902}\r
903\r
904/**\r
905 This function download the boot file by using UEFI HTTP protocol.\r
906\r
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
917 @param[out] ImageType The image type of the downloaded file.\r
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 EFI_ACCESS_DENIED The server needs to authenticate the client.\r
926 @retval Others Unexpected error happened.\r
927\r
928**/\r
929EFI_STATUS\r
930HttpBootGetBootFile (\r
931 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
932 IN BOOLEAN HeaderOnly,\r
933 IN OUT UINTN *BufferSize,\r
934 OUT UINT8 *Buffer,\r
935 OUT HTTP_BOOT_IMAGE_TYPE *ImageType\r
936 )\r
937{\r
938 EFI_STATUS Status;\r
939 EFI_HTTP_STATUS_CODE StatusCode;\r
940 CHAR8 *HostName;\r
941 EFI_HTTP_REQUEST_DATA *RequestData;\r
942 HTTP_IO_RESPONSE_DATA *ResponseData;\r
943 HTTP_IO_RESPONSE_DATA ResponseBody;\r
944 HTTP_IO *HttpIo;\r
945 HTTP_IO_HEADER *HttpIoHeader;\r
946 VOID *Parser;\r
947 HTTP_BOOT_CALLBACK_DATA Context;\r
948 UINTN ContentLength;\r
949 HTTP_BOOT_CACHE_CONTENT *Cache;\r
950 UINT8 *Block;\r
951 UINTN UrlSize;\r
952 CHAR16 *Url;\r
953 BOOLEAN IdentityMode;\r
954 UINTN ReceivedSize;\r
955 CHAR8 BaseAuthValue[80];\r
956 EFI_HTTP_HEADER *HttpHeader;\r
957 CHAR8 *Data;\r
958\r
959 ASSERT (Private != NULL);\r
960 ASSERT (Private->HttpCreated);\r
961\r
962 if ((BufferSize == NULL) || (ImageType == NULL)) {\r
963 return EFI_INVALID_PARAMETER;\r
964 }\r
965\r
966 if ((*BufferSize != 0) && (Buffer == NULL)) {\r
967 return EFI_INVALID_PARAMETER;\r
968 }\r
969\r
970 //\r
971 // First, check whether we already cached the requested Uri.\r
972 //\r
973 UrlSize = AsciiStrSize (Private->BootFileUri);\r
974 Url = AllocatePool (UrlSize * sizeof (CHAR16));\r
975 if (Url == NULL) {\r
976 return EFI_OUT_OF_RESOURCES;\r
977 }\r
978\r
979 AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);\r
980 if (!HeaderOnly && (Buffer != NULL)) {\r
981 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);\r
982 if (Status != EFI_NOT_FOUND) {\r
983 FreePool (Url);\r
984 return Status;\r
985 }\r
986 }\r
987\r
988 //\r
989 // Not found in cache, try to download it through HTTP.\r
990 //\r
991\r
992 //\r
993 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.\r
994 //\r
995 Cache = NULL;\r
996 if ((!HeaderOnly) && (*BufferSize == 0)) {\r
997 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));\r
998 if (Cache == NULL) {\r
999 Status = EFI_OUT_OF_RESOURCES;\r
1000 goto ERROR_1;\r
1001 }\r
1002\r
1003 Cache->ImageType = ImageTypeMax;\r
1004 InitializeListHead (&Cache->EntityDataList);\r
1005 }\r
1006\r
1007 //\r
1008 // 2. Send HTTP request message.\r
1009 //\r
1010\r
1011 //\r
1012 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:\r
1013 // Host\r
1014 // Accept\r
1015 // User-Agent\r
1016 // [Authorization]\r
1017 //\r
1018 HttpIoHeader = HttpIoCreateHeader ((Private->AuthData != NULL) ? 4 : 3);\r
1019 if (HttpIoHeader == NULL) {\r
1020 Status = EFI_OUT_OF_RESOURCES;\r
1021 goto ERROR_2;\r
1022 }\r
1023\r
1024 //\r
1025 // Add HTTP header field 1: Host\r
1026 //\r
1027 HostName = NULL;\r
1028 Status = HttpUrlGetHostName (\r
1029 Private->BootFileUri,\r
1030 Private->BootFileUriParser,\r
1031 &HostName\r
1032 );\r
1033 if (EFI_ERROR (Status)) {\r
1034 goto ERROR_3;\r
1035 }\r
1036\r
1037 Status = HttpIoSetHeader (\r
1038 HttpIoHeader,\r
1039 HTTP_HEADER_HOST,\r
1040 HostName\r
1041 );\r
1042 FreePool (HostName);\r
1043 if (EFI_ERROR (Status)) {\r
1044 goto ERROR_3;\r
1045 }\r
1046\r
1047 //\r
1048 // Add HTTP header field 2: Accept\r
1049 //\r
1050 Status = HttpIoSetHeader (\r
1051 HttpIoHeader,\r
1052 HTTP_HEADER_ACCEPT,\r
1053 "*/*"\r
1054 );\r
1055 if (EFI_ERROR (Status)) {\r
1056 goto ERROR_3;\r
1057 }\r
1058\r
1059 //\r
1060 // Add HTTP header field 3: User-Agent\r
1061 //\r
1062 Status = HttpIoSetHeader (\r
1063 HttpIoHeader,\r
1064 HTTP_HEADER_USER_AGENT,\r
1065 HTTP_USER_AGENT_EFI_HTTP_BOOT\r
1066 );\r
1067 if (EFI_ERROR (Status)) {\r
1068 goto ERROR_3;\r
1069 }\r
1070\r
1071 //\r
1072 // Add HTTP header field 4: Authorization\r
1073 //\r
1074 if (Private->AuthData != NULL) {\r
1075 ASSERT (HttpIoHeader->MaxHeaderCount == 4);\r
1076\r
1077 if ((Private->AuthScheme != NULL) && (CompareMem (Private->AuthScheme, "Basic", 5) != 0)) {\r
1078 Status = EFI_UNSUPPORTED;\r
1079 goto ERROR_3;\r
1080 }\r
1081\r
1082 AsciiSPrint (\r
1083 BaseAuthValue,\r
1084 sizeof (BaseAuthValue),\r
1085 "%a %a",\r
1086 "Basic",\r
1087 Private->AuthData\r
1088 );\r
1089\r
1090 Status = HttpIoSetHeader (\r
1091 HttpIoHeader,\r
1092 HTTP_HEADER_AUTHORIZATION,\r
1093 BaseAuthValue\r
1094 );\r
1095 if (EFI_ERROR (Status)) {\r
1096 goto ERROR_3;\r
1097 }\r
1098 }\r
1099\r
1100 //\r
1101 // 2.2 Build the rest of HTTP request info.\r
1102 //\r
1103 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));\r
1104 if (RequestData == NULL) {\r
1105 Status = EFI_OUT_OF_RESOURCES;\r
1106 goto ERROR_3;\r
1107 }\r
1108\r
1109 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;\r
1110 RequestData->Url = Url;\r
1111\r
1112 //\r
1113 // 2.3 Record the request info in a temp cache item.\r
1114 //\r
1115 if (Cache != NULL) {\r
1116 Cache->RequestData = RequestData;\r
1117 }\r
1118\r
1119 //\r
1120 // 2.4 Send out the request to HTTP server.\r
1121 //\r
1122 HttpIo = &Private->HttpIo;\r
1123 Status = HttpIoSendRequest (\r
1124 HttpIo,\r
1125 RequestData,\r
1126 HttpIoHeader->HeaderCount,\r
1127 HttpIoHeader->Headers,\r
1128 0,\r
1129 NULL\r
1130 );\r
1131 if (EFI_ERROR (Status)) {\r
1132 goto ERROR_4;\r
1133 }\r
1134\r
1135 //\r
1136 // 3. Receive HTTP response message.\r
1137 //\r
1138\r
1139 //\r
1140 // 3.1 First step, use zero BodyLength to only receive the response headers.\r
1141 //\r
1142 ResponseData = AllocateZeroPool (sizeof (HTTP_IO_RESPONSE_DATA));\r
1143 if (ResponseData == NULL) {\r
1144 Status = EFI_OUT_OF_RESOURCES;\r
1145 goto ERROR_4;\r
1146 }\r
1147\r
1148 Data = NULL;\r
1149 Status = HttpIoRecvResponse (\r
1150 &Private->HttpIo,\r
1151 TRUE,\r
1152 ResponseData\r
1153 );\r
1154 if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {\r
1155 if (EFI_ERROR (ResponseData->Status)) {\r
1156 StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;\r
1157 HttpBootPrintErrorMessage (StatusCode);\r
1158 Status = ResponseData->Status;\r
1159 if ((StatusCode == HTTP_STATUS_401_UNAUTHORIZED) || \\r
1160 (StatusCode == HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED))\r
1161 {\r
1162 if ((Private->AuthData != NULL) || (Private->AuthScheme != NULL)) {\r
1163 if (Private->AuthData != NULL) {\r
1164 FreePool (Private->AuthData);\r
1165 Private->AuthData = NULL;\r
1166 }\r
1167\r
1168 if (Private->AuthScheme != NULL) {\r
1169 FreePool (Private->AuthScheme);\r
1170 Private->AuthScheme = NULL;\r
1171 }\r
1172\r
1173 Status = EFI_ACCESS_DENIED;\r
1174 goto ERROR_4;\r
1175 }\r
1176\r
1177 //\r
1178 // Server indicates the user has to provide a user-id and password as a means of identification.\r
1179 //\r
1180 if (Private->HttpBootCallback != NULL) {\r
1181 Data = AllocateZeroPool (sizeof (CHAR8) * HTTP_BOOT_AUTHENTICATION_INFO_MAX_LEN);\r
1182 if (Data == NULL) {\r
1183 Status = EFI_OUT_OF_RESOURCES;\r
1184 goto ERROR_4;\r
1185 }\r
1186\r
1187 Status = Private->HttpBootCallback->Callback (\r
1188 Private->HttpBootCallback,\r
1189 HttpBootHttpAuthInfo,\r
1190 TRUE,\r
1191 HTTP_BOOT_AUTHENTICATION_INFO_MAX_LEN,\r
1192 Data\r
1193 );\r
1194 if (EFI_ERROR (Status)) {\r
1195 if (Data != NULL) {\r
1196 FreePool (Data);\r
1197 }\r
1198\r
1199 goto ERROR_5;\r
1200 }\r
1201\r
1202 Private->AuthData = (CHAR8 *)Data;\r
1203 }\r
1204\r
1205 HttpHeader = HttpFindHeader (\r
1206 ResponseData->HeaderCount,\r
1207 ResponseData->Headers,\r
1208 HTTP_HEADER_WWW_AUTHENTICATE\r
1209 );\r
1210 if (HttpHeader != NULL) {\r
1211 Private->AuthScheme = AllocateZeroPool (AsciiStrLen (HttpHeader->FieldValue) + 1);\r
1212 if (Private->AuthScheme == NULL) {\r
1213 return EFI_OUT_OF_RESOURCES;\r
1214 }\r
1215\r
1216 CopyMem (Private->AuthScheme, HttpHeader->FieldValue, AsciiStrLen (HttpHeader->FieldValue));\r
1217 }\r
1218\r
1219 Status = EFI_ACCESS_DENIED;\r
1220 }\r
1221 }\r
1222\r
1223 goto ERROR_5;\r
1224 }\r
1225\r
1226 //\r
1227 // Check the image type according to server's response.\r
1228 //\r
1229 Status = HttpBootCheckImageType (\r
1230 Private->BootFileUri,\r
1231 Private->BootFileUriParser,\r
1232 ResponseData->HeaderCount,\r
1233 ResponseData->Headers,\r
1234 ImageType\r
1235 );\r
1236 if (EFI_ERROR (Status)) {\r
1237 goto ERROR_5;\r
1238 }\r
1239\r
1240 //\r
1241 // 3.2 Cache the response header.\r
1242 //\r
1243 if (Cache != NULL) {\r
1244 Cache->ResponseData = ResponseData;\r
1245 Cache->ImageType = *ImageType;\r
1246 }\r
1247\r
1248 //\r
1249 // 3.3 Init a message-body parser from the header information.\r
1250 //\r
1251 Parser = NULL;\r
1252 Context.NewBlock = FALSE;\r
1253 Context.Block = NULL;\r
1254 Context.CopyedSize = 0;\r
1255 Context.Buffer = Buffer;\r
1256 Context.BufferSize = *BufferSize;\r
1257 Context.Cache = Cache;\r
1258 Context.Private = Private;\r
1259 Status = HttpInitMsgParser (\r
1260 HeaderOnly ? HttpMethodHead : HttpMethodGet,\r
1261 ResponseData->Response.StatusCode,\r
1262 ResponseData->HeaderCount,\r
1263 ResponseData->Headers,\r
1264 HttpBootGetBootFileCallback,\r
1265 (VOID *)&Context,\r
1266 &Parser\r
1267 );\r
1268 if (EFI_ERROR (Status)) {\r
1269 goto ERROR_6;\r
1270 }\r
1271\r
1272 //\r
1273 // 3.4 Continue to receive and parse message-body if needed.\r
1274 //\r
1275 Block = NULL;\r
1276 if (!HeaderOnly) {\r
1277 //\r
1278 // 3.4.1, check whether we are in identity transfer-coding.\r
1279 //\r
1280 ContentLength = 0;\r
1281 Status = HttpGetEntityLength (Parser, &ContentLength);\r
1282 if (!EFI_ERROR (Status)) {\r
1283 IdentityMode = TRUE;\r
1284 } else {\r
1285 IdentityMode = FALSE;\r
1286 }\r
1287\r
1288 //\r
1289 // 3.4.2, start the message-body download, the identity and chunked transfer-coding\r
1290 // is handled in different path here.\r
1291 //\r
1292 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));\r
1293 if (IdentityMode) {\r
1294 //\r
1295 // In identity transfer-coding there is no need to parse the message body,\r
1296 // just download the message body to the user provided buffer directly.\r
1297 //\r
1298 ReceivedSize = 0;\r
1299 while (ReceivedSize < ContentLength) {\r
1300 ResponseBody.Body = (CHAR8 *)Buffer + ReceivedSize;\r
1301 ResponseBody.BodyLength = *BufferSize - ReceivedSize;\r
1302 Status = HttpIoRecvResponse (\r
1303 &Private->HttpIo,\r
1304 FALSE,\r
1305 &ResponseBody\r
1306 );\r
1307 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {\r
1308 if (EFI_ERROR (ResponseBody.Status)) {\r
1309 Status = ResponseBody.Status;\r
1310 }\r
1311\r
1312 goto ERROR_6;\r
1313 }\r
1314\r
1315 ReceivedSize += ResponseBody.BodyLength;\r
1316 if (Private->HttpBootCallback != NULL) {\r
1317 Status = Private->HttpBootCallback->Callback (\r
1318 Private->HttpBootCallback,\r
1319 HttpBootHttpEntityBody,\r
1320 TRUE,\r
1321 (UINT32)ResponseBody.BodyLength,\r
1322 ResponseBody.Body\r
1323 );\r
1324 if (EFI_ERROR (Status)) {\r
1325 goto ERROR_6;\r
1326 }\r
1327 }\r
1328 }\r
1329 } else {\r
1330 //\r
1331 // In "chunked" transfer-coding mode, so we need to parse the received\r
1332 // data to get the real entity content.\r
1333 //\r
1334 Block = NULL;\r
1335 while (!HttpIsMessageComplete (Parser)) {\r
1336 //\r
1337 // Allocate a buffer in Block to hold the message-body.\r
1338 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().\r
1339 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before\r
1340 // every HttpIoRecvResponse().\r
1341 //\r
1342 if ((Block == NULL) || (Context.BufferSize == 0)) {\r
1343 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);\r
1344 if (Block == NULL) {\r
1345 Status = EFI_OUT_OF_RESOURCES;\r
1346 goto ERROR_6;\r
1347 }\r
1348\r
1349 Context.NewBlock = TRUE;\r
1350 Context.Block = Block;\r
1351 } else {\r
1352 Context.NewBlock = FALSE;\r
1353 }\r
1354\r
1355 ResponseBody.Body = (CHAR8 *)Block;\r
1356 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;\r
1357 Status = HttpIoRecvResponse (\r
1358 &Private->HttpIo,\r
1359 FALSE,\r
1360 &ResponseBody\r
1361 );\r
1362 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {\r
1363 if (EFI_ERROR (ResponseBody.Status)) {\r
1364 Status = ResponseBody.Status;\r
1365 }\r
1366\r
1367 goto ERROR_6;\r
1368 }\r
1369\r
1370 //\r
1371 // Parse the new received block of the message-body, the block will be saved in cache.\r
1372 //\r
1373 Status = HttpParseMessageBody (\r
1374 Parser,\r
1375 ResponseBody.BodyLength,\r
1376 ResponseBody.Body\r
1377 );\r
1378 if (EFI_ERROR (Status)) {\r
1379 goto ERROR_6;\r
1380 }\r
1381 }\r
1382 }\r
1383 }\r
1384\r
1385 //\r
1386 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.\r
1387 //\r
1388 Status = HttpGetEntityLength (Parser, &ContentLength);\r
1389 if (EFI_ERROR (Status)) {\r
1390 goto ERROR_6;\r
1391 }\r
1392\r
1393 if (*BufferSize < ContentLength) {\r
1394 Status = EFI_BUFFER_TOO_SMALL;\r
1395 } else {\r
1396 Status = EFI_SUCCESS;\r
1397 }\r
1398\r
1399 *BufferSize = ContentLength;\r
1400\r
1401 //\r
1402 // 4. Save the cache item to driver's cache list and return.\r
1403 //\r
1404 if (Cache != NULL) {\r
1405 Cache->EntityLength = ContentLength;\r
1406 InsertTailList (&Private->CacheList, &Cache->Link);\r
1407 }\r
1408\r
1409 if (Parser != NULL) {\r
1410 HttpFreeMsgParser (Parser);\r
1411 }\r
1412\r
1413 return Status;\r
1414\r
1415ERROR_6:\r
1416 if (Parser != NULL) {\r
1417 HttpFreeMsgParser (Parser);\r
1418 }\r
1419\r
1420 if (Context.Block != NULL) {\r
1421 FreePool (Context.Block);\r
1422 }\r
1423\r
1424 HttpBootFreeCache (Cache);\r
1425\r
1426ERROR_5:\r
1427 if (ResponseData != NULL) {\r
1428 FreePool (ResponseData);\r
1429 }\r
1430\r
1431ERROR_4:\r
1432 if (RequestData != NULL) {\r
1433 FreePool (RequestData);\r
1434 }\r
1435\r
1436ERROR_3:\r
1437 HttpIoFreeHeader (HttpIoHeader);\r
1438ERROR_2:\r
1439 if (Cache != NULL) {\r
1440 FreePool (Cache);\r
1441 }\r
1442\r
1443ERROR_1:\r
1444 if (Url != NULL) {\r
1445 FreePool (Url);\r
1446 }\r
1447\r
1448 return Status;\r
1449}\r