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