VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/HttpBootDxe/HttpBootClient.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette