]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpBootDxe/HttpBootSupport.c
8fe81eb8d964dc21d141ba67400046d41aaa0f2d
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootSupport.c
1 /** @file
2 Support functions implementation for UEFI HTTP boot driver.
3
4 Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6 This program and the accompanying materials are licensed and made available under
7 the terms and conditions of the BSD License that accompanies this distribution.
8 The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "HttpBootDxe.h"
17
18
19 /**
20 Get the Nic handle using any child handle in the IPv4 stack.
21
22 @param[in] ControllerHandle Pointer to child handle over IPv4.
23
24 @return NicHandle The pointer to the Nic handle.
25 @return NULL Can't find the Nic handle.
26
27 **/
28 EFI_HANDLE
29 HttpBootGetNicByIp4Children (
30 IN EFI_HANDLE ControllerHandle
31 )
32 {
33 EFI_HANDLE NicHandle;
34
35 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
36 if (NicHandle == NULL) {
37 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
38 if (NicHandle == NULL) {
39 return NULL;
40 }
41 }
42
43 return NicHandle;
44 }
45
46 /**
47 Get the Nic handle using any child handle in the IPv6 stack.
48
49 @param[in] ControllerHandle Pointer to child handle over IPv6.
50
51 @return NicHandle The pointer to the Nic handle.
52 @return NULL Can't find the Nic handle.
53
54 **/
55 EFI_HANDLE
56 HttpBootGetNicByIp6Children (
57 IN EFI_HANDLE ControllerHandle
58 )
59 {
60 EFI_HANDLE NicHandle;
61 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
62 if (NicHandle == NULL) {
63 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
64 if (NicHandle == NULL) {
65 return NULL;
66 }
67 }
68
69 return NicHandle;
70 }
71
72 /**
73 This function is to convert UINTN to ASCII string with the required formatting.
74
75 @param[in] Number Numeric value to be converted.
76 @param[in] Buffer The pointer to the buffer for ASCII string.
77 @param[in] Length The length of the required format.
78
79 **/
80 VOID
81 HttpBootUintnToAscDecWithFormat (
82 IN UINTN Number,
83 IN UINT8 *Buffer,
84 IN INTN Length
85 )
86 {
87 UINTN Remainder;
88
89 for (; Length > 0; Length--) {
90 Remainder = Number % 10;
91 Number /= 10;
92 Buffer[Length - 1] = (UINT8) ('0' + Remainder);
93 }
94 }
95
96 /**
97 This function is to display the IPv4 address.
98
99 @param[in] Ip The pointer to the IPv4 address.
100
101 **/
102 VOID
103 HttpBootShowIp4Addr (
104 IN EFI_IPv4_ADDRESS *Ip
105 )
106 {
107 UINTN Index;
108
109 for (Index = 0; Index < 4; Index++) {
110 AsciiPrint ("%d", Ip->Addr[Index]);
111 if (Index < 3) {
112 AsciiPrint (".");
113 }
114 }
115 }
116
117 /**
118 This function is to display the IPv6 address.
119
120 @param[in] Ip The pointer to the IPv6 address.
121
122 **/
123 VOID
124 HttpBootShowIp6Addr (
125 IN EFI_IPv6_ADDRESS *Ip
126 )
127 {
128 UINTN Index;
129
130 for (Index = 0; Index < 16; Index++) {
131
132 if (Ip->Addr[Index] != 0) {
133 AsciiPrint ("%x", Ip->Addr[Index]);
134 }
135 Index++;
136 if (Index > 15) {
137 return;
138 }
139 if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {
140 AsciiPrint ("0");
141 }
142 AsciiPrint ("%x", Ip->Addr[Index]);
143 if (Index < 15) {
144 AsciiPrint (":");
145 }
146 }
147 }
148
149 /**
150 This function is to display the HTTP error status.
151
152 @param[in] StatusCode The status code value in HTTP message.
153
154 **/
155 VOID
156 HttpBootPrintErrorMessage (
157 EFI_HTTP_STATUS_CODE StatusCode
158 )
159 {
160 AsciiPrint ("\n");
161
162 switch (StatusCode) {
163 case HTTP_STATUS_300_MULTIPLE_CHIOCES:
164 AsciiPrint ("\n Redirection: 300 Multiple Choices");
165 break;
166
167 case HTTP_STATUS_301_MOVED_PERMANENTLY:
168 AsciiPrint ("\n Redirection: 301 Moved Permanently");
169 break;
170
171 case HTTP_STATUS_302_FOUND:
172 AsciiPrint ("\n Redirection: 302 Found");
173 break;
174
175 case HTTP_STATUS_303_SEE_OTHER:
176 AsciiPrint ("\n Redirection: 303 See Other");
177 break;
178
179 case HTTP_STATUS_304_NOT_MODIFIED:
180 AsciiPrint ("\n Redirection: 304 Not Modified");
181 break;
182
183 case HTTP_STATUS_305_USE_PROXY:
184 AsciiPrint ("\n Redirection: 305 Use Proxy");
185 break;
186
187 case HTTP_STATUS_307_TEMPORARY_REDIRECT:
188 AsciiPrint ("\n Redirection: 307 Temporary Redirect");
189 break;
190
191 case HTTP_STATUS_308_PERMANENT_REDIRECT:
192 AsciiPrint ("\n Redirection: 308 Permanent Redirect");
193 break;
194
195 case HTTP_STATUS_400_BAD_REQUEST:
196 AsciiPrint ("\n Client Error: 400 Bad Request");
197 break;
198
199 case HTTP_STATUS_401_UNAUTHORIZED:
200 AsciiPrint ("\n Client Error: 401 Unauthorized");
201 break;
202
203 case HTTP_STATUS_402_PAYMENT_REQUIRED:
204 AsciiPrint ("\n Client Error: 402 Payment Required");
205 break;
206
207 case HTTP_STATUS_403_FORBIDDEN:
208 AsciiPrint ("\n Client Error: 403 Forbidden");
209 break;
210
211 case HTTP_STATUS_404_NOT_FOUND:
212 AsciiPrint ("\n Client Error: 404 Not Found");
213 break;
214
215 case HTTP_STATUS_405_METHOD_NOT_ALLOWED:
216 AsciiPrint ("\n Client Error: 405 Method Not Allowed");
217 break;
218
219 case HTTP_STATUS_406_NOT_ACCEPTABLE:
220 AsciiPrint ("\n Client Error: 406 Not Acceptable");
221 break;
222
223 case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED:
224 AsciiPrint ("\n Client Error: 407 Proxy Authentication Required");
225 break;
226
227 case HTTP_STATUS_408_REQUEST_TIME_OUT:
228 AsciiPrint ("\n Client Error: 408 Request Timeout");
229 break;
230
231 case HTTP_STATUS_409_CONFLICT:
232 AsciiPrint ("\n Client Error: 409 Conflict");
233 break;
234
235 case HTTP_STATUS_410_GONE:
236 AsciiPrint ("\n Client Error: 410 Gone");
237 break;
238
239 case HTTP_STATUS_411_LENGTH_REQUIRED:
240 AsciiPrint ("\n Client Error: 411 Length Required");
241 break;
242
243 case HTTP_STATUS_412_PRECONDITION_FAILED:
244 AsciiPrint ("\n Client Error: 412 Precondition Failed");
245 break;
246
247 case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE:
248 AsciiPrint ("\n Client Error: 413 Request Entity Too Large");
249 break;
250
251 case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE:
252 AsciiPrint ("\n Client Error: 414 Request URI Too Long");
253 break;
254
255 case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE:
256 AsciiPrint ("\n Client Error: 415 Unsupported Media Type");
257 break;
258
259 case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED:
260 AsciiPrint ("\n Client Error: 416 Requested Range Not Satisfiable");
261 break;
262
263 case HTTP_STATUS_417_EXPECTATION_FAILED:
264 AsciiPrint ("\n Client Error: 417 Expectation Failed");
265 break;
266
267 case HTTP_STATUS_500_INTERNAL_SERVER_ERROR:
268 AsciiPrint ("\n Server Error: 500 Internal Server Error");
269 break;
270
271 case HTTP_STATUS_501_NOT_IMPLEMENTED:
272 AsciiPrint ("\n Server Error: 501 Not Implemented");
273 break;
274
275 case HTTP_STATUS_502_BAD_GATEWAY:
276 AsciiPrint ("\n Server Error: 502 Bad Gateway");
277 break;
278
279 case HTTP_STATUS_503_SERVICE_UNAVAILABLE:
280 AsciiPrint ("\n Server Error: 503 Service Unavailable");
281 break;
282
283 case HTTP_STATUS_504_GATEWAY_TIME_OUT:
284 AsciiPrint ("\n Server Error: 504 Gateway Timeout");
285 break;
286
287 case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED:
288 AsciiPrint ("\n Server Error: 505 HTTP Version Not Supported");
289 break;
290
291 default: ;
292
293 }
294 }
295
296 /**
297 Notify the callback function when an event is triggered.
298
299 @param[in] Event The triggered event.
300 @param[in] Context The opaque parameter to the function.
301
302 **/
303 VOID
304 EFIAPI
305 HttpBootCommonNotify (
306 IN EFI_EVENT Event,
307 IN VOID *Context
308 )
309 {
310 *((BOOLEAN *) Context) = TRUE;
311 }
312
313 /**
314 Retrieve the host address using the EFI_DNS6_PROTOCOL.
315
316 @param[in] Private The pointer to the driver's private data.
317 @param[in] HostName Pointer to buffer containing hostname.
318 @param[out] IpAddress On output, pointer to buffer containing IPv6 address.
319
320 @retval EFI_SUCCESS Operation succeeded.
321 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
322 @retval Others Other errors as indicated.
323 **/
324 EFI_STATUS
325 HttpBootDns (
326 IN HTTP_BOOT_PRIVATE_DATA *Private,
327 IN CHAR16 *HostName,
328 OUT EFI_IPv6_ADDRESS *IpAddress
329 )
330 {
331 EFI_STATUS Status;
332 EFI_DNS6_PROTOCOL *Dns6;
333 EFI_DNS6_CONFIG_DATA Dns6ConfigData;
334 EFI_DNS6_COMPLETION_TOKEN Token;
335 EFI_HANDLE Dns6Handle;
336 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
337 EFI_IPv6_ADDRESS *DnsServerList;
338 UINTN DnsServerListCount;
339 UINTN DataSize;
340 BOOLEAN IsDone;
341
342 DnsServerList = NULL;
343 DnsServerListCount = 0;
344 Dns6 = NULL;
345 Dns6Handle = NULL;
346 ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
347
348 //
349 // Get DNS server list from EFI IPv6 Configuration protocol.
350 //
351 Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config);
352 if (!EFI_ERROR (Status)) {
353 //
354 // Get the required size.
355 //
356 DataSize = 0;
357 Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL);
358 if (Status == EFI_BUFFER_TOO_SMALL) {
359 DnsServerList = AllocatePool (DataSize);
360 if (DnsServerList == NULL) {
361 return EFI_OUT_OF_RESOURCES;
362 }
363
364 Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList);
365 if (EFI_ERROR (Status)) {
366 FreePool (DnsServerList);
367 DnsServerList = NULL;
368 } else {
369 DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS);
370 }
371 }
372 }
373 //
374 // Create a DNSv6 child instance and get the protocol.
375 //
376 Status = NetLibCreateServiceChild (
377 Private->Controller,
378 Private->Ip6Nic->ImageHandle,
379 &gEfiDns6ServiceBindingProtocolGuid,
380 &Dns6Handle
381 );
382 if (EFI_ERROR (Status)) {
383 goto Exit;
384 }
385
386 Status = gBS->OpenProtocol (
387 Dns6Handle,
388 &gEfiDns6ProtocolGuid,
389 (VOID **) &Dns6,
390 Private->Ip6Nic->ImageHandle,
391 Private->Controller,
392 EFI_OPEN_PROTOCOL_BY_DRIVER
393 );
394 if (EFI_ERROR (Status)) {
395 goto Exit;
396 }
397
398 //
399 // Configure DNS6 instance for the DNS server address and protocol.
400 //
401 ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
402 Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount;
403 Dns6ConfigData.DnsServerList = DnsServerList;
404 Dns6ConfigData.EnableDnsCache = TRUE;
405 Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP;
406 IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6);
407 Status = Dns6->Configure (
408 Dns6,
409 &Dns6ConfigData
410 );
411 if (EFI_ERROR (Status)) {
412 goto Exit;
413 }
414
415 Token.Status = EFI_NOT_READY;
416 IsDone = FALSE;
417 //
418 // Create event to set the IsDone flag when name resolution is finished.
419 //
420 Status = gBS->CreateEvent (
421 EVT_NOTIFY_SIGNAL,
422 TPL_NOTIFY,
423 HttpBootCommonNotify,
424 &IsDone,
425 &Token.Event
426 );
427 if (EFI_ERROR (Status)) {
428 goto Exit;
429 }
430
431 //
432 // Start asynchronous name resolution.
433 //
434 Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
435 if (EFI_ERROR (Status)) {
436 goto Exit;
437 }
438
439 while (!IsDone) {
440 Dns6->Poll (Dns6);
441 }
442
443 //
444 // Name resolution is done, check result.
445 //
446 Status = Token.Status;
447 if (!EFI_ERROR (Status)) {
448 if (Token.RspData.H2AData == NULL) {
449 Status = EFI_DEVICE_ERROR;
450 goto Exit;
451 }
452 if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
453 Status = EFI_DEVICE_ERROR;
454 goto Exit;
455 }
456 //
457 // We just return the first IPv6 address from DNS protocol.
458 //
459 IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
460 Status = EFI_SUCCESS;
461 }
462 Exit:
463
464 if (Token.Event != NULL) {
465 gBS->CloseEvent (Token.Event);
466 }
467 if (Token.RspData.H2AData != NULL) {
468 if (Token.RspData.H2AData->IpList != NULL) {
469 FreePool (Token.RspData.H2AData->IpList);
470 }
471 FreePool (Token.RspData.H2AData);
472 }
473
474 if (Dns6 != NULL) {
475 Dns6->Configure (Dns6, NULL);
476
477 gBS->CloseProtocol (
478 Dns6Handle,
479 &gEfiDns6ProtocolGuid,
480 Private->Ip6Nic->ImageHandle,
481 Private->Controller
482 );
483 }
484
485 if (Dns6Handle != NULL) {
486 NetLibDestroyServiceChild (
487 Private->Controller,
488 Private->Ip6Nic->ImageHandle,
489 &gEfiDns6ServiceBindingProtocolGuid,
490 Dns6Handle
491 );
492 }
493
494 if (DnsServerList != NULL) {
495 FreePool (DnsServerList);
496 }
497
498 return Status;
499 }
500 /**
501 Create a HTTP_IO_HEADER to hold the HTTP header items.
502
503 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder.
504
505 @return A pointer of the HTTP header holder or NULL if failed.
506
507 **/
508 HTTP_IO_HEADER *
509 HttpBootCreateHeader (
510 UINTN MaxHeaderCount
511 )
512 {
513 HTTP_IO_HEADER *HttpIoHeader;
514
515 if (MaxHeaderCount == 0) {
516 return NULL;
517 }
518
519 HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));
520 if (HttpIoHeader == NULL) {
521 return NULL;
522 }
523
524 HttpIoHeader->MaxHeaderCount = MaxHeaderCount;
525 HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1);
526
527 return HttpIoHeader;
528 }
529
530 /**
531 Destroy the HTTP_IO_HEADER and release the resouces.
532
533 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.
534
535 **/
536 VOID
537 HttpBootFreeHeader (
538 IN HTTP_IO_HEADER *HttpIoHeader
539 )
540 {
541 UINTN Index;
542
543 if (HttpIoHeader != NULL) {
544 if (HttpIoHeader->HeaderCount != 0) {
545 for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {
546 FreePool (HttpIoHeader->Headers[Index].FieldName);
547 FreePool (HttpIoHeader->Headers[Index].FieldValue);
548 }
549 }
550 FreePool (HttpIoHeader);
551 }
552 }
553
554 /**
555 Set or update a HTTP header with the field name and corresponding value.
556
557 @param[in] HttpIoHeader Point to the HTTP header holder.
558 @param[in] FieldName Null terminated string which describes a field name.
559 @param[in] FieldValue Null terminated string which describes the corresponding field value.
560
561 @retval EFI_SUCCESS The HTTP header has been set or updated.
562 @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
563 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.
564 @retval Other Unexpected error happened.
565
566 **/
567 EFI_STATUS
568 HttpBootSetHeader (
569 IN HTTP_IO_HEADER *HttpIoHeader,
570 IN CHAR8 *FieldName,
571 IN CHAR8 *FieldValue
572 )
573 {
574 EFI_HTTP_HEADER *Header;
575 UINTN StrSize;
576 CHAR8 *NewFieldValue;
577
578 if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) {
579 return EFI_INVALID_PARAMETER;
580 }
581
582 Header = HttpFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);
583 if (Header == NULL) {
584 //
585 // Add a new header.
586 //
587 if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {
588 return EFI_OUT_OF_RESOURCES;
589 }
590 Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];
591
592 StrSize = AsciiStrSize (FieldName);
593 Header->FieldName = AllocatePool (StrSize);
594 if (Header->FieldName == NULL) {
595 return EFI_OUT_OF_RESOURCES;
596 }
597 CopyMem (Header->FieldName, FieldName, StrSize);
598 Header->FieldName[StrSize -1] = '\0';
599
600 StrSize = AsciiStrSize (FieldValue);
601 Header->FieldValue = AllocatePool (StrSize);
602 if (Header->FieldValue == NULL) {
603 FreePool (Header->FieldName);
604 return EFI_OUT_OF_RESOURCES;
605 }
606 CopyMem (Header->FieldValue, FieldValue, StrSize);
607 Header->FieldValue[StrSize -1] = '\0';
608
609 HttpIoHeader->HeaderCount++;
610 } else {
611 //
612 // Update an existing one.
613 //
614 StrSize = AsciiStrSize (FieldValue);
615 NewFieldValue = AllocatePool (StrSize);
616 if (NewFieldValue == NULL) {
617 return EFI_OUT_OF_RESOURCES;
618 }
619 CopyMem (NewFieldValue, FieldValue, StrSize);
620 NewFieldValue[StrSize -1] = '\0';
621
622 if (Header->FieldValue != NULL) {
623 FreePool (Header->FieldValue);
624 }
625 Header->FieldValue = NewFieldValue;
626 }
627
628 return EFI_SUCCESS;
629 }
630
631 /**
632 Notify the callback function when an event is triggered.
633
634 @param[in] Context The opaque parameter to the function.
635
636 **/
637 VOID
638 EFIAPI
639 HttpIoNotifyDpc (
640 IN VOID *Context
641 )
642 {
643 *((BOOLEAN *) Context) = TRUE;
644 }
645
646 /**
647 Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
648
649 @param[in] Event The event signaled.
650 @param[in] Context The opaque parameter to the function.
651
652 **/
653 VOID
654 EFIAPI
655 HttpIoNotify (
656 IN EFI_EVENT Event,
657 IN VOID *Context
658 )
659 {
660 //
661 // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
662 //
663 QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context);
664 }
665
666 /**
667 Create a HTTP_IO to access the HTTP service. It will create and configure
668 a HTTP child handle.
669
670 @param[in] Image The handle of the driver image.
671 @param[in] Controller The handle of the controller.
672 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
673 @param[in] ConfigData The HTTP_IO configuration data.
674 @param[out] HttpIo The HTTP_IO.
675
676 @retval EFI_SUCCESS The HTTP_IO is created and configured.
677 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
678 @retval EFI_UNSUPPORTED One or more of the control options are not
679 supported in the implementation.
680 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
681 @retval Others Failed to create the HTTP_IO or configure it.
682
683 **/
684 EFI_STATUS
685 HttpIoCreateIo (
686 IN EFI_HANDLE Image,
687 IN EFI_HANDLE Controller,
688 IN UINT8 IpVersion,
689 IN HTTP_IO_CONFIG_DATA *ConfigData,
690 OUT HTTP_IO *HttpIo
691 )
692 {
693 EFI_STATUS Status;
694 EFI_HTTP_CONFIG_DATA HttpConfigData;
695 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
696 EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;
697 EFI_HTTP_PROTOCOL *Http;
698 EFI_EVENT Event;
699
700 if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {
701 return EFI_INVALID_PARAMETER;
702 }
703
704 if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
705 return EFI_UNSUPPORTED;
706 }
707
708 ZeroMem (HttpIo, sizeof (HTTP_IO));
709
710 //
711 // Create the HTTP child instance and get the HTTP protocol.
712 //
713 Status = NetLibCreateServiceChild (
714 Controller,
715 Image,
716 &gEfiHttpServiceBindingProtocolGuid,
717 &HttpIo->Handle
718 );
719 if (EFI_ERROR (Status)) {
720 return Status;
721 }
722
723 Status = gBS->OpenProtocol (
724 HttpIo->Handle,
725 &gEfiHttpProtocolGuid,
726 (VOID **) &Http,
727 Image,
728 Controller,
729 EFI_OPEN_PROTOCOL_BY_DRIVER
730 );
731 if (EFI_ERROR (Status) || (Http == NULL)) {
732 goto ON_ERROR;
733 }
734
735 //
736 // Init the configuration data and configure the HTTP child.
737 //
738 HttpIo->Image = Image;
739 HttpIo->Controller = Controller;
740 HttpIo->IpVersion = IpVersion;
741 HttpIo->Http = Http;
742
743 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
744 HttpConfigData.HttpVersion = HttpVersion11;
745 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
746 if (HttpIo->IpVersion == IP_VERSION_4) {
747 HttpConfigData.LocalAddressIsIPv6 = FALSE;
748
749 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
750 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
751 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
752 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
753 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
754 } else {
755 HttpConfigData.LocalAddressIsIPv6 = TRUE;
756 Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
757 IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
758 HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
759 }
760
761 Status = Http->Configure (Http, &HttpConfigData);
762 if (EFI_ERROR (Status)) {
763 goto ON_ERROR;
764 }
765
766 //
767 // Create events for variuos asynchronous operations.
768 //
769 Status = gBS->CreateEvent (
770 EVT_NOTIFY_SIGNAL,
771 TPL_NOTIFY,
772 HttpIoNotify,
773 &HttpIo->IsTxDone,
774 &Event
775 );
776 if (EFI_ERROR (Status)) {
777 goto ON_ERROR;
778 }
779 HttpIo->ReqToken.Event = Event;
780 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
781
782 Status = gBS->CreateEvent (
783 EVT_NOTIFY_SIGNAL,
784 TPL_NOTIFY,
785 HttpIoNotify,
786 &HttpIo->IsRxDone,
787 &Event
788 );
789 if (EFI_ERROR (Status)) {
790 goto ON_ERROR;
791 }
792 HttpIo->RspToken.Event = Event;
793 HttpIo->RspToken.Message = &HttpIo->RspMessage;
794
795 //
796 // Create TimeoutEvent for response
797 //
798 Status = gBS->CreateEvent (
799 EVT_TIMER,
800 TPL_CALLBACK,
801 NULL,
802 NULL,
803 &Event
804 );
805 if (EFI_ERROR (Status)) {
806 goto ON_ERROR;
807 }
808 HttpIo->TimeoutEvent = Event;
809
810 return EFI_SUCCESS;
811
812 ON_ERROR:
813 HttpIoDestroyIo (HttpIo);
814
815 return Status;
816 }
817
818 /**
819 Destroy the HTTP_IO and release the resouces.
820
821 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
822
823 **/
824 VOID
825 HttpIoDestroyIo (
826 IN HTTP_IO *HttpIo
827 )
828 {
829 EFI_HTTP_PROTOCOL *Http;
830 EFI_EVENT Event;
831
832 if (HttpIo == NULL) {
833 return;
834 }
835
836 Event = HttpIo->ReqToken.Event;
837 if (Event != NULL) {
838 gBS->CloseEvent (Event);
839 }
840
841 Event = HttpIo->RspToken.Event;
842 if (Event != NULL) {
843 gBS->CloseEvent (Event);
844 }
845
846 Event = HttpIo->TimeoutEvent;
847 if (Event != NULL) {
848 gBS->CloseEvent (Event);
849 }
850
851 Http = HttpIo->Http;
852 if (Http != NULL) {
853 Http->Configure (Http, NULL);
854 gBS->CloseProtocol (
855 HttpIo->Handle,
856 &gEfiHttpProtocolGuid,
857 HttpIo->Image,
858 HttpIo->Controller
859 );
860 }
861
862 NetLibDestroyServiceChild (
863 HttpIo->Controller,
864 HttpIo->Image,
865 &gEfiHttpServiceBindingProtocolGuid,
866 HttpIo->Handle
867 );
868 }
869
870 /**
871 Synchronously send a HTTP REQUEST message to the server.
872
873 @param[in] HttpIo The HttpIo wrapping the HTTP service.
874 @param[in] Request A pointer to storage such data as URL and HTTP method.
875 @param[in] HeaderCount Number of HTTP header structures in Headers list.
876 @param[in] Headers Array containing list of HTTP headers.
877 @param[in] BodyLength Length in bytes of the HTTP body.
878 @param[in] Body Body associated with the HTTP request.
879
880 @retval EFI_SUCCESS The HTTP request is trasmitted.
881 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
882 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
883 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
884 @retval Others Other errors as indicated.
885
886 **/
887 EFI_STATUS
888 HttpIoSendRequest (
889 IN HTTP_IO *HttpIo,
890 IN EFI_HTTP_REQUEST_DATA *Request,
891 IN UINTN HeaderCount,
892 IN EFI_HTTP_HEADER *Headers,
893 IN UINTN BodyLength,
894 IN VOID *Body
895 )
896 {
897 EFI_STATUS Status;
898 EFI_HTTP_PROTOCOL *Http;
899
900 if (HttpIo == NULL || HttpIo->Http == NULL) {
901 return EFI_INVALID_PARAMETER;
902 }
903
904 HttpIo->ReqToken.Status = EFI_NOT_READY;
905 HttpIo->ReqToken.Message->Data.Request = Request;
906 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
907 HttpIo->ReqToken.Message->Headers = Headers;
908 HttpIo->ReqToken.Message->BodyLength = BodyLength;
909 HttpIo->ReqToken.Message->Body = Body;
910
911 //
912 // Queue the request token to HTTP instances.
913 //
914 Http = HttpIo->Http;
915 HttpIo->IsTxDone = FALSE;
916 Status = Http->Request (
917 Http,
918 &HttpIo->ReqToken
919 );
920 if (EFI_ERROR (Status)) {
921 return Status;
922 }
923
924 //
925 // Poll the network until transmit finish.
926 //
927 while (!HttpIo->IsTxDone) {
928 Http->Poll (Http);
929 }
930
931 return HttpIo->ReqToken.Status;
932 }
933
934 /**
935 Synchronously receive a HTTP RESPONSE message from the server.
936
937 @param[in] HttpIo The HttpIo wrapping the HTTP service.
938 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
939 FALSE to continue receive the previous response message.
940 @param[out] ResponseData Point to a wrapper of the received response data.
941
942 @retval EFI_SUCCESS The HTTP response is received.
943 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
944 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
945 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
946 @retval Others Other errors as indicated.
947
948 **/
949 EFI_STATUS
950 HttpIoRecvResponse (
951 IN HTTP_IO *HttpIo,
952 IN BOOLEAN RecvMsgHeader,
953 OUT HTTP_IO_RESPONSE_DATA *ResponseData
954 )
955 {
956 EFI_STATUS Status;
957 EFI_HTTP_PROTOCOL *Http;
958
959 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
960 return EFI_INVALID_PARAMETER;
961 }
962
963 //
964 // Start the timer, and wait Timeout seconds to receive the header packet.
965 //
966 Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HTTP_BOOT_RESPONSE_TIMEOUT * TICKS_PER_MS);
967 if (EFI_ERROR (Status)) {
968 return Status;
969 }
970
971 //
972 // Queue the response token to HTTP instances.
973 //
974 HttpIo->RspToken.Status = EFI_NOT_READY;
975 if (RecvMsgHeader) {
976 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
977 } else {
978 HttpIo->RspToken.Message->Data.Response = NULL;
979 }
980 HttpIo->RspToken.Message->HeaderCount = 0;
981 HttpIo->RspToken.Message->Headers = NULL;
982 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
983 HttpIo->RspToken.Message->Body = ResponseData->Body;
984
985 Http = HttpIo->Http;
986 HttpIo->IsRxDone = FALSE;
987 Status = Http->Response (
988 Http,
989 &HttpIo->RspToken
990 );
991
992 if (EFI_ERROR (Status)) {
993 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
994 return Status;
995 }
996
997 //
998 // Poll the network until receive finish.
999 //
1000 while (!HttpIo->IsRxDone && ((HttpIo->TimeoutEvent == NULL) || EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent)))) {
1001 Http->Poll (Http);
1002 }
1003
1004 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
1005
1006 if (!HttpIo->IsRxDone) {
1007 //
1008 // Timeout occurs, cancel the response token.
1009 //
1010 Http->Cancel (Http, &HttpIo->RspToken);
1011
1012 Status = EFI_TIMEOUT;
1013
1014 return Status;
1015 } else {
1016 HttpIo->IsRxDone = FALSE;
1017 }
1018
1019 //
1020 // Store the received data into the wrapper.
1021 //
1022 ResponseData->Status = HttpIo->RspToken.Status;
1023 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
1024 ResponseData->Headers = HttpIo->RspToken.Message->Headers;
1025 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
1026
1027 return Status;
1028 }
1029
1030 /**
1031 This function checks the HTTP(S) URI scheme.
1032
1033 @param[in] Uri The pointer to the URI string.
1034
1035 @retval EFI_SUCCESS The URI scheme is valid.
1036 @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS.
1037 @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP.
1038
1039 **/
1040 EFI_STATUS
1041 HttpBootCheckUriScheme (
1042 IN CHAR8 *Uri
1043 )
1044 {
1045 UINTN Index;
1046 EFI_STATUS Status;
1047
1048 Status = EFI_SUCCESS;
1049
1050 //
1051 // Convert the scheme to all lower case.
1052 //
1053 for (Index = 0; Index < AsciiStrLen (Uri); Index++) {
1054 if (Uri[Index] == ':') {
1055 break;
1056 }
1057 if (Uri[Index] >= 'A' && Uri[Index] <= 'Z') {
1058 Uri[Index] -= (CHAR8)('A' - 'a');
1059 }
1060 }
1061
1062 //
1063 // Return EFI_INVALID_PARAMETER if the URI is not HTTP or HTTPS.
1064 //
1065 if ((AsciiStrnCmp (Uri, "http://", 7) != 0) && (AsciiStrnCmp (Uri, "https://", 8) != 0)) {
1066 DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: Invalid Uri.\n"));
1067 return EFI_INVALID_PARAMETER;
1068 }
1069
1070 //
1071 // HTTP is disabled, return EFI_ACCESS_DENIED if the URI is HTTP.
1072 //
1073 if (!PcdGetBool (PcdAllowHttpConnections) && (AsciiStrnCmp (Uri, "http://", 7) == 0)) {
1074 DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: HTTP is disabled.\n"));
1075 return EFI_ACCESS_DENIED;
1076 }
1077
1078 return Status;
1079 }
1080
1081 /**
1082 Get the URI address string from the input device path.
1083
1084 Caller need to free the buffer in the UriAddress pointer.
1085
1086 @param[in] FilePath Pointer to the device path which contains a URI device path node.
1087 @param[out] UriAddress The URI address string extract from the device path.
1088
1089 @retval EFI_SUCCESS The URI string is returned.
1090 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1091
1092 **/
1093 EFI_STATUS
1094 HttpBootParseFilePath (
1095 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
1096 OUT CHAR8 **UriAddress
1097 )
1098 {
1099 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1100 URI_DEVICE_PATH *UriDevicePath;
1101 CHAR8 *Uri;
1102 UINTN UriStrLength;
1103
1104 if (FilePath == NULL) {
1105 return EFI_INVALID_PARAMETER;
1106 }
1107
1108 *UriAddress = NULL;
1109
1110 //
1111 // Extract the URI address from the FilePath
1112 //
1113 TempDevicePath = FilePath;
1114 while (!IsDevicePathEnd (TempDevicePath)) {
1115 if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
1116 (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) {
1117 UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath;
1118 //
1119 // UEFI Spec doesn't require the URI to be a NULL-terminated string
1120 // So we allocate a new buffer and always append a '\0' to it.
1121 //
1122 UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);
1123 if (UriStrLength == 0) {
1124 //
1125 // return a NULL UriAddress if it's a empty URI device path node.
1126 //
1127 break;
1128 }
1129 Uri = AllocatePool (UriStrLength + 1);
1130 if (Uri == NULL) {
1131 return EFI_OUT_OF_RESOURCES;
1132 }
1133 CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL));
1134 Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0';
1135
1136 *UriAddress = Uri;
1137 }
1138 TempDevicePath = NextDevicePathNode (TempDevicePath);
1139 }
1140
1141 return EFI_SUCCESS;
1142 }
1143
1144 /**
1145 This function returns the image type according to server replied HTTP message
1146 and also the image's URI info.
1147
1148 @param[in] Uri The pointer to the image's URI string.
1149 @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
1150 @param[in] HeaderCount Number of HTTP header structures in Headers list.
1151 @param[in] Headers Array containing list of HTTP headers.
1152 @param[out] ImageType The image type of the downloaded file.
1153
1154 @retval EFI_SUCCESS The image type is returned in ImageType.
1155 @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
1156 @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
1157 @retval EFI_NOT_FOUND Failed to identify the image type.
1158 @retval Others Unexpect error happened.
1159
1160 **/
1161 EFI_STATUS
1162 HttpBootCheckImageType (
1163 IN CHAR8 *Uri,
1164 IN VOID *UriParser,
1165 IN UINTN HeaderCount,
1166 IN EFI_HTTP_HEADER *Headers,
1167 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
1168 )
1169 {
1170 EFI_STATUS Status;
1171 EFI_HTTP_HEADER *Header;
1172 CHAR8 *FilePath;
1173 CHAR8 *FilePost;
1174
1175 if (Uri == NULL || UriParser == NULL || ImageType == NULL) {
1176 return EFI_INVALID_PARAMETER;
1177 }
1178
1179 if (HeaderCount != 0 && Headers == NULL) {
1180 return EFI_INVALID_PARAMETER;
1181 }
1182
1183 //
1184 // Determine the image type by the HTTP Content-Type header field first.
1185 // "application/efi" -> EFI Image
1186 // "application/vnd.efi-iso" -> CD/DVD Image
1187 // "application/vnd.efi-img" -> Virtual Disk Image
1188 //
1189 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE);
1190 if (Header != NULL) {
1191 if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) {
1192 *ImageType = ImageTypeEfi;
1193 return EFI_SUCCESS;
1194 } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_ISO) == 0) {
1195 *ImageType = ImageTypeVirtualCd;
1196 return EFI_SUCCESS;
1197 } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_IMG) == 0) {
1198 *ImageType = ImageTypeVirtualDisk;
1199 return EFI_SUCCESS;
1200 }
1201 }
1202
1203 //
1204 // Determine the image type by file extension:
1205 // *.efi -> EFI Image
1206 // *.iso -> CD/DVD Image
1207 // *.img -> Virtual Disk Image
1208 //
1209 Status = HttpUrlGetPath (
1210 Uri,
1211 UriParser,
1212 &FilePath
1213 );
1214 if (EFI_ERROR (Status)) {
1215 return Status;
1216 }
1217
1218 FilePost = FilePath + AsciiStrLen (FilePath) - 4;
1219 if (AsciiStrCmp (FilePost, ".efi") == 0) {
1220 *ImageType = ImageTypeEfi;
1221 } else if (AsciiStrCmp (FilePost, ".iso") == 0) {
1222 *ImageType = ImageTypeVirtualCd;
1223 } else if (AsciiStrCmp (FilePost, ".img") == 0) {
1224 *ImageType = ImageTypeVirtualDisk;
1225 } else {
1226 *ImageType = ImageTypeMax;
1227 }
1228
1229 FreePool (FilePath);
1230
1231 return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND;
1232 }
1233
1234 /**
1235 This function register the RAM disk info to the system.
1236
1237 @param[in] Private The pointer to the driver's private data.
1238 @param[in] BufferSize The size of Buffer in bytes.
1239 @param[in] Buffer The base address of the RAM disk.
1240 @param[in] ImageType The image type of the file in Buffer.
1241
1242 @retval EFI_SUCCESS The RAM disk has been registered.
1243 @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
1244 @retval EFI_UNSUPPORTED The ImageType is not supported.
1245 @retval Others Unexpected error happened.
1246
1247 **/
1248 EFI_STATUS
1249 HttpBootRegisterRamDisk (
1250 IN HTTP_BOOT_PRIVATE_DATA *Private,
1251 IN UINTN BufferSize,
1252 IN VOID *Buffer,
1253 IN HTTP_BOOT_IMAGE_TYPE ImageType
1254 )
1255 {
1256 EFI_RAM_DISK_PROTOCOL *RamDisk;
1257 EFI_STATUS Status;
1258 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1259 EFI_GUID *RamDiskType;
1260
1261 ASSERT (Private != NULL);
1262 ASSERT (Buffer != NULL);
1263 ASSERT (BufferSize != 0);
1264
1265 Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk);
1266 if (EFI_ERROR (Status)) {
1267 DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status));
1268 return Status;
1269 }
1270
1271 if (ImageType == ImageTypeVirtualCd) {
1272 RamDiskType = &gEfiVirtualCdGuid;
1273 } else if (ImageType == ImageTypeVirtualDisk) {
1274 RamDiskType = &gEfiVirtualDiskGuid;
1275 } else {
1276 return EFI_UNSUPPORTED;
1277 }
1278
1279 Status = RamDisk->Register (
1280 (UINTN)Buffer,
1281 (UINT64)BufferSize,
1282 RamDiskType,
1283 Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath,
1284 &DevicePath
1285 );
1286 if (EFI_ERROR (Status)) {
1287 DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status));
1288 }
1289
1290 return Status;
1291 }
1292