1 /** @file
2   Support functions implementation for UEFI HTTP boot driver.
3 
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "HttpBootDxe.h"
16 
17 
18 /**
19   Get the Nic handle using any child handle in the IPv4 stack.
20 
21   @param[in]  ControllerHandle    Pointer to child handle over IPv4.
22 
23   @return NicHandle               The pointer to the Nic handle.
24   @return NULL                    Can't find the Nic handle.
25 
26 **/
27 EFI_HANDLE
HttpBootGetNicByIp4Children(IN EFI_HANDLE ControllerHandle)28 HttpBootGetNicByIp4Children (
29   IN EFI_HANDLE                 ControllerHandle
30   )
31 {
32   EFI_HANDLE                    NicHandle;
33 
34   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
35   if (NicHandle == NULL) {
36     NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
37     if (NicHandle == NULL) {
38       return NULL;
39     }
40   }
41 
42   return NicHandle;
43 }
44 
45 /**
46   Get the Nic handle using any child handle in the IPv6 stack.
47 
48   @param[in]  ControllerHandle    Pointer to child handle over IPv6.
49 
50   @return NicHandle               The pointer to the Nic handle.
51   @return NULL                    Can't find the Nic handle.
52 
53 **/
54 EFI_HANDLE
HttpBootGetNicByIp6Children(IN EFI_HANDLE ControllerHandle)55 HttpBootGetNicByIp6Children (
56   IN EFI_HANDLE                 ControllerHandle
57   )
58 {
59   EFI_HANDLE                    NicHandle;
60   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
61   if (NicHandle == NULL) {
62     NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
63     if (NicHandle == NULL) {
64       return NULL;
65     }
66   }
67 
68   return NicHandle;
69 }
70 
71 /**
72   This function is to convert UINTN to ASCII string with the required formatting.
73 
74   @param[in]  Number         Numeric value to be converted.
75   @param[in]  Buffer         The pointer to the buffer for ASCII string.
76   @param[in]  Length         The length of the required format.
77 
78 **/
79 VOID
HttpBootUintnToAscDecWithFormat(IN UINTN Number,IN UINT8 * Buffer,IN INTN Length)80 HttpBootUintnToAscDecWithFormat (
81   IN UINTN                       Number,
82   IN UINT8                       *Buffer,
83   IN INTN                        Length
84   )
85 {
86   UINTN                          Remainder;
87 
88   while (Length > 0) {
89     Length--;
90     Remainder      = Number % 10;
91     Number        /= 10;
92     Buffer[Length] = (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
HttpBootShowIp4Addr(IN EFI_IPv4_ADDRESS * Ip)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
HttpBootShowIp6Addr(IN EFI_IPv6_ADDRESS * Ip)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
HttpBootPrintErrorMessage(EFI_HTTP_STATUS_CODE StatusCode)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_400_BAD_REQUEST:
192     AsciiPrint ("\n  Client Error: 400 Bad Request");
193     break;
194 
195   case HTTP_STATUS_401_UNAUTHORIZED:
196     AsciiPrint ("\n  Client Error: 401 Unauthorized");
197     break;
198 
199   case HTTP_STATUS_402_PAYMENT_REQUIRED:
200     AsciiPrint ("\n  Client Error: 402 Payment Required");
201     break;
202 
203   case HTTP_STATUS_403_FORBIDDEN:
204     AsciiPrint ("\n  Client Error: 403 Forbidden");
205     break;
206 
207   case HTTP_STATUS_404_NOT_FOUND:
208     AsciiPrint ("\n  Client Error: 404 Not Found");
209     break;
210 
211   case HTTP_STATUS_405_METHOD_NOT_ALLOWED:
212     AsciiPrint ("\n  Client Error: 405 Method Not Allowed");
213     break;
214 
215   case HTTP_STATUS_406_NOT_ACCEPTABLE:
216     AsciiPrint ("\n  Client Error: 406 Not Acceptable");
217     break;
218 
219   case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED:
220     AsciiPrint ("\n  Client Error: 407 Proxy Authentication Required");
221     break;
222 
223   case HTTP_STATUS_408_REQUEST_TIME_OUT:
224     AsciiPrint ("\n  Client Error: 408 Request Timeout");
225     break;
226 
227   case HTTP_STATUS_409_CONFLICT:
228     AsciiPrint ("\n  Client Error: 409 Conflict");
229     break;
230 
231   case HTTP_STATUS_410_GONE:
232     AsciiPrint ("\n  Client Error: 410 Gone");
233     break;
234 
235   case HTTP_STATUS_411_LENGTH_REQUIRED:
236     AsciiPrint ("\n  Client Error: 411 Length Required");
237     break;
238 
239   case HTTP_STATUS_412_PRECONDITION_FAILED:
240     AsciiPrint ("\n  Client Error: 412 Precondition Failed");
241     break;
242 
243   case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE:
244     AsciiPrint ("\n  Client Error: 413 Request Entity Too Large");
245     break;
246 
247   case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE:
248     AsciiPrint ("\n  Client Error: 414 Request URI Too Long");
249     break;
250 
251   case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE:
252     AsciiPrint ("\n  Client Error: 415 Unsupported Media Type");
253     break;
254 
255   case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED:
256     AsciiPrint ("\n  Client Error: 416 Requested Range Not Satisfiable");
257     break;
258 
259   case HTTP_STATUS_417_EXPECTATION_FAILED:
260     AsciiPrint ("\n  Client Error: 417 Expectation Failed");
261     break;
262 
263   case HTTP_STATUS_500_INTERNAL_SERVER_ERROR:
264     AsciiPrint ("\n  Server Error: 500 Internal Server Error");
265     break;
266 
267   case HTTP_STATUS_501_NOT_IMPLEMENTED:
268     AsciiPrint ("\n  Server Error: 501 Not Implemented");
269     break;
270 
271   case HTTP_STATUS_502_BAD_GATEWAY:
272     AsciiPrint ("\n  Server Error: 502 Bad Gateway");
273     break;
274 
275   case HTTP_STATUS_503_SERVICE_UNAVAILABLE:
276     AsciiPrint ("\n  Server Error: 503 Service Unavailable");
277     break;
278 
279   case HTTP_STATUS_504_GATEWAY_TIME_OUT:
280     AsciiPrint ("\n  Server Error: 504 Gateway Timeout");
281     break;
282 
283   case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED:
284     AsciiPrint ("\n  Server Error: 505 HTTP Version Not Supported");
285     break;
286 
287   default: ;
288 
289   }
290 }
291 
292 /**
293   Notify the callback function when an event is triggered.
294 
295   @param[in]  Event           The triggered event.
296   @param[in]  Context         The opaque parameter to the function.
297 
298 **/
299 VOID
300 EFIAPI
HttpBootCommonNotify(IN EFI_EVENT Event,IN VOID * Context)301 HttpBootCommonNotify (
302   IN EFI_EVENT           Event,
303   IN VOID                *Context
304   )
305 {
306   *((BOOLEAN *) Context) = TRUE;
307 }
308 
309 /**
310   Retrieve the host address using the EFI_DNS6_PROTOCOL.
311 
312   @param[in]  Private             The pointer to the driver's private data.
313   @param[in]  HostName            Pointer to buffer containing hostname.
314   @param[out] IpAddress           On output, pointer to buffer containing IPv6 address.
315 
316   @retval EFI_SUCCESS             Operation succeeded.
317   @retval EFI_DEVICE_ERROR        An unexpected network error occurred.
318   @retval Others                  Other errors as indicated.
319 **/
320 EFI_STATUS
HttpBootDns(IN HTTP_BOOT_PRIVATE_DATA * Private,IN CHAR16 * HostName,OUT EFI_IPv6_ADDRESS * IpAddress)321 HttpBootDns (
322   IN     HTTP_BOOT_PRIVATE_DATA   *Private,
323   IN     CHAR16                   *HostName,
324      OUT EFI_IPv6_ADDRESS         *IpAddress
325   )
326 {
327   EFI_STATUS                      Status;
328   EFI_DNS6_PROTOCOL               *Dns6;
329   EFI_DNS6_CONFIG_DATA            Dns6ConfigData;
330   EFI_DNS6_COMPLETION_TOKEN       Token;
331   EFI_HANDLE                      Dns6Handle;
332   EFI_IP6_CONFIG_PROTOCOL         *Ip6Config;
333   EFI_IPv6_ADDRESS                *DnsServerList;
334   UINTN                           DnsServerListCount;
335   UINTN                           DataSize;
336   BOOLEAN                         IsDone;
337 
338   DnsServerList       = NULL;
339   DnsServerListCount  = 0;
340   Dns6                = NULL;
341   Dns6Handle          = NULL;
342   ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
343 
344   //
345   // Get DNS server list from EFI IPv6 Configuration protocol.
346   //
347   Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config);
348   if (!EFI_ERROR (Status)) {
349     //
350     // Get the required size.
351     //
352     DataSize = 0;
353     Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL);
354     if (Status == EFI_BUFFER_TOO_SMALL) {
355       DnsServerList = AllocatePool (DataSize);
356       if (DnsServerList == NULL) {
357         return EFI_OUT_OF_RESOURCES;
358       }
359 
360       Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList);
361       if (EFI_ERROR (Status)) {
362         FreePool (DnsServerList);
363         DnsServerList = NULL;
364       } else {
365         DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS);
366       }
367     }
368   }
369   //
370   // Create a DNSv6 child instance and get the protocol.
371   //
372   Status = NetLibCreateServiceChild (
373              Private->Controller,
374              Private->Image,
375              &gEfiDns6ServiceBindingProtocolGuid,
376              &Dns6Handle
377              );
378   if (EFI_ERROR (Status)) {
379     goto Exit;
380   }
381 
382   Status = gBS->OpenProtocol (
383                   Dns6Handle,
384                   &gEfiDns6ProtocolGuid,
385                   (VOID **) &Dns6,
386                   Private->Image,
387                   Private->Controller,
388                   EFI_OPEN_PROTOCOL_BY_DRIVER
389                   );
390   if (EFI_ERROR (Status)) {
391     goto Exit;
392   }
393 
394   //
395   // Configure DNS6 instance for the DNS server address and protocol.
396   //
397   ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
398   Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount;
399   Dns6ConfigData.DnsServerList  = DnsServerList;
400   Dns6ConfigData.EnableDnsCache = TRUE;
401   Dns6ConfigData.Protocol       = EFI_IP_PROTO_UDP;
402   IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6);
403   Status = Dns6->Configure (
404                     Dns6,
405                     &Dns6ConfigData
406                     );
407   if (EFI_ERROR (Status)) {
408     goto Exit;
409   }
410 
411   Token.Status = EFI_NOT_READY;
412   IsDone       = FALSE;
413   //
414   // Create event to set the  IsDone flag when name resolution is finished.
415   //
416   Status = gBS->CreateEvent (
417                   EVT_NOTIFY_SIGNAL,
418                   TPL_NOTIFY,
419                   HttpBootCommonNotify,
420                   &IsDone,
421                   &Token.Event
422                   );
423   if (EFI_ERROR (Status)) {
424     goto Exit;
425   }
426 
427   //
428   // Start asynchronous name resolution.
429   //
430   Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
431   if (EFI_ERROR (Status)) {
432     goto Exit;
433   }
434 
435   while (!IsDone) {
436     Dns6->Poll (Dns6);
437   }
438 
439   //
440   // Name resolution is done, check result.
441   //
442   Status = Token.Status;
443   if (!EFI_ERROR (Status)) {
444     if (Token.RspData.H2AData == NULL) {
445       Status = EFI_DEVICE_ERROR;
446       goto Exit;
447     }
448     if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
449       Status = EFI_DEVICE_ERROR;
450       goto Exit;
451     }
452     //
453     // We just return the first IPv6 address from DNS protocol.
454     //
455     IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
456     Status = EFI_SUCCESS;
457   }
458 Exit:
459 
460   if (Token.Event != NULL) {
461     gBS->CloseEvent (Token.Event);
462   }
463   if (Token.RspData.H2AData != NULL) {
464     if (Token.RspData.H2AData->IpList != NULL) {
465       FreePool (Token.RspData.H2AData->IpList);
466     }
467     FreePool (Token.RspData.H2AData);
468   }
469 
470   if (Dns6 != NULL) {
471     Dns6->Configure (Dns6, NULL);
472 
473     gBS->CloseProtocol (
474            Dns6Handle,
475            &gEfiDns6ProtocolGuid,
476            Private->Image,
477            Private->Controller
478            );
479   }
480 
481   if (Dns6Handle != NULL) {
482     NetLibDestroyServiceChild (
483       Private->Controller,
484       Private->Image,
485       &gEfiDns6ServiceBindingProtocolGuid,
486       Dns6Handle
487       );
488   }
489 
490   if (DnsServerList != NULL) {
491     FreePool (DnsServerList);
492   }
493 
494   return Status;
495 }
496 /**
497   Create a HTTP_IO_HEADER to hold the HTTP header items.
498 
499   @param[in]  MaxHeaderCount         The maximun number of HTTP header in this holder.
500 
501   @return    A pointer of the HTTP header holder or NULL if failed.
502 
503 **/
504 HTTP_IO_HEADER *
HttpBootCreateHeader(UINTN MaxHeaderCount)505 HttpBootCreateHeader (
506   UINTN                     MaxHeaderCount
507   )
508 {
509   HTTP_IO_HEADER        *HttpIoHeader;
510 
511   if (MaxHeaderCount == 0) {
512     return NULL;
513   }
514 
515   HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));
516   if (HttpIoHeader == NULL) {
517     return NULL;
518   }
519 
520   HttpIoHeader->MaxHeaderCount = MaxHeaderCount;
521   HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1);
522 
523   return HttpIoHeader;
524 }
525 
526 /**
527   Destroy the HTTP_IO_HEADER and release the resouces.
528 
529   @param[in]  HttpIoHeader       Point to the HTTP header holder to be destroyed.
530 
531 **/
532 VOID
HttpBootFreeHeader(IN HTTP_IO_HEADER * HttpIoHeader)533 HttpBootFreeHeader (
534   IN  HTTP_IO_HEADER       *HttpIoHeader
535   )
536 {
537   UINTN      Index;
538 
539   if (HttpIoHeader != NULL) {
540     if (HttpIoHeader->HeaderCount != 0) {
541       for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {
542         FreePool (HttpIoHeader->Headers[Index].FieldName);
543         FreePool (HttpIoHeader->Headers[Index].FieldValue);
544       }
545     }
546     FreePool (HttpIoHeader);
547   }
548 }
549 
550 /**
551   Find a specified header field according to the field name.
552 
553   @param[in]   HeaderCount      Number of HTTP header structures in Headers list.
554   @param[in]   Headers          Array containing list of HTTP headers.
555   @param[in]   FieldName        Null terminated string which describes a field name.
556 
557   @return    Pointer to the found header or NULL.
558 
559 **/
560 EFI_HTTP_HEADER *
HttpBootFindHeader(IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,IN CHAR8 * FieldName)561 HttpBootFindHeader (
562   IN  UINTN                HeaderCount,
563   IN  EFI_HTTP_HEADER      *Headers,
564   IN  CHAR8                *FieldName
565   )
566 {
567   UINTN                 Index;
568 
569   if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
570     return NULL;
571   }
572 
573   for (Index = 0; Index < HeaderCount; Index++){
574     //
575     // Field names are case-insensitive (RFC 2616).
576     //
577     if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
578       return &Headers[Index];
579     }
580   }
581   return NULL;
582 }
583 
584 /**
585   Set or update a HTTP header with the field name and corresponding value.
586 
587   @param[in]  HttpIoHeader       Point to the HTTP header holder.
588   @param[in]  FieldName          Null terminated string which describes a field name.
589   @param[in]  FieldValue         Null terminated string which describes the corresponding field value.
590 
591   @retval  EFI_SUCCESS           The HTTP header has been set or updated.
592   @retval  EFI_INVALID_PARAMETER Any input parameter is invalid.
593   @retval  EFI_OUT_OF_RESOURCES  Insufficient resource to complete the operation.
594   @retval  Other                 Unexpected error happened.
595 
596 **/
597 EFI_STATUS
HttpBootSetHeader(IN HTTP_IO_HEADER * HttpIoHeader,IN CHAR8 * FieldName,IN CHAR8 * FieldValue)598 HttpBootSetHeader (
599   IN  HTTP_IO_HEADER       *HttpIoHeader,
600   IN  CHAR8                *FieldName,
601   IN  CHAR8                *FieldValue
602   )
603 {
604   EFI_HTTP_HEADER       *Header;
605   UINTN                 StrSize;
606   CHAR8                 *NewFieldValue;
607 
608   if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) {
609     return EFI_INVALID_PARAMETER;
610   }
611 
612   Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);
613   if (Header == NULL) {
614     //
615     // Add a new header.
616     //
617     if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {
618       return EFI_OUT_OF_RESOURCES;
619     }
620     Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];
621 
622     StrSize = AsciiStrSize (FieldName);
623     Header->FieldName = AllocatePool (StrSize);
624     if (Header->FieldName == NULL) {
625       return EFI_OUT_OF_RESOURCES;
626     }
627     CopyMem (Header->FieldName, FieldName, StrSize);
628     Header->FieldName[StrSize -1] = '\0';
629 
630     StrSize = AsciiStrSize (FieldValue);
631     Header->FieldValue = AllocatePool (StrSize);
632     if (Header->FieldValue == NULL) {
633       FreePool (Header->FieldName);
634       return EFI_OUT_OF_RESOURCES;
635     }
636     CopyMem (Header->FieldValue, FieldValue, StrSize);
637     Header->FieldValue[StrSize -1] = '\0';
638 
639     HttpIoHeader->HeaderCount++;
640   } else {
641     //
642     // Update an existing one.
643     //
644     StrSize = AsciiStrSize (FieldValue);
645     NewFieldValue = AllocatePool (StrSize);
646     if (NewFieldValue == NULL) {
647       return EFI_OUT_OF_RESOURCES;
648     }
649     CopyMem (NewFieldValue, FieldValue, StrSize);
650     NewFieldValue[StrSize -1] = '\0';
651 
652     if (Header->FieldValue != NULL) {
653       FreePool (Header->FieldValue);
654     }
655     Header->FieldValue = NewFieldValue;
656   }
657 
658   return EFI_SUCCESS;
659 }
660 
661 /**
662   Create a HTTP_IO to access the HTTP service. It will create and configure
663   a HTTP child handle.
664 
665   @param[in]  Image          The handle of the driver image.
666   @param[in]  Controller     The handle of the controller.
667   @param[in]  IpVersion      IP_VERSION_4 or IP_VERSION_6.
668   @param[in]  ConfigData     The HTTP_IO configuration data.
669   @param[out] HttpIo         The HTTP_IO.
670 
671   @retval EFI_SUCCESS            The HTTP_IO is created and configured.
672   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
673   @retval EFI_UNSUPPORTED        One or more of the control options are not
674                                  supported in the implementation.
675   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
676   @retval Others                 Failed to create the HTTP_IO or configure it.
677 
678 **/
679 EFI_STATUS
HttpIoCreateIo(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,IN UINT8 IpVersion,IN HTTP_IO_CONFIG_DATA * ConfigData,OUT HTTP_IO * HttpIo)680 HttpIoCreateIo (
681   IN EFI_HANDLE             Image,
682   IN EFI_HANDLE             Controller,
683   IN UINT8                  IpVersion,
684   IN HTTP_IO_CONFIG_DATA    *ConfigData,
685   OUT HTTP_IO               *HttpIo
686   )
687 {
688   EFI_STATUS                Status;
689   EFI_HTTP_CONFIG_DATA      HttpConfigData;
690   EFI_HTTPv4_ACCESS_POINT   Http4AccessPoint;
691   EFI_HTTPv6_ACCESS_POINT   Http6AccessPoint;
692   EFI_HTTP_PROTOCOL         *Http;
693   EFI_EVENT                 Event;
694 
695   if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {
696     return EFI_INVALID_PARAMETER;
697   }
698 
699   if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
700     return EFI_UNSUPPORTED;
701   }
702 
703   ZeroMem (HttpIo, sizeof (HTTP_IO));
704 
705   //
706   // Create the HTTP child instance and get the HTTP protocol.
707   //
708   Status = NetLibCreateServiceChild (
709              Controller,
710              Image,
711              &gEfiHttpServiceBindingProtocolGuid,
712              &HttpIo->Handle
713              );
714   if (EFI_ERROR (Status)) {
715     return Status;
716   }
717 
718   Status = gBS->OpenProtocol (
719                   HttpIo->Handle,
720                   &gEfiHttpProtocolGuid,
721                   (VOID **) &Http,
722                   Image,
723                   Controller,
724                   EFI_OPEN_PROTOCOL_BY_DRIVER
725                   );
726   if (EFI_ERROR (Status) || (Http == NULL)) {
727     goto ON_ERROR;
728   }
729 
730   //
731   // Init the configuration data and configure the HTTP child.
732   //
733   HttpIo->Image       = Image;
734   HttpIo->Controller  = Controller;
735   HttpIo->IpVersion   = IpVersion;
736   HttpIo->Http        = Http;
737 
738   ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
739   HttpConfigData.HttpVersion        = HttpVersion11;
740   HttpConfigData.TimeOutMillisec    = ConfigData->Config4.RequestTimeOut;
741   if (HttpIo->IpVersion == IP_VERSION_4) {
742     HttpConfigData.LocalAddressIsIPv6 = FALSE;
743 
744     Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
745     Http4AccessPoint.LocalPort         = ConfigData->Config4.LocalPort;
746     IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
747     IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
748     HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
749   } else {
750     HttpConfigData.LocalAddressIsIPv6 = TRUE;
751     Http6AccessPoint.LocalPort        = ConfigData->Config6.LocalPort;
752     IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
753     HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
754   }
755 
756   Status = Http->Configure (Http, &HttpConfigData);
757   if (EFI_ERROR (Status)) {
758     goto ON_ERROR;
759   }
760 
761   //
762   // Create events for variuos asynchronous operations.
763   //
764   Status = gBS->CreateEvent (
765                   EVT_NOTIFY_SIGNAL,
766                   TPL_NOTIFY,
767                   HttpBootCommonNotify,
768                   &HttpIo->IsTxDone,
769                   &Event
770                   );
771   if (EFI_ERROR (Status)) {
772     goto ON_ERROR;
773   }
774   HttpIo->ReqToken.Event = Event;
775   HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
776 
777   Status = gBS->CreateEvent (
778                   EVT_NOTIFY_SIGNAL,
779                   TPL_NOTIFY,
780                   HttpBootCommonNotify,
781                   &HttpIo->IsRxDone,
782                   &Event
783                   );
784   if (EFI_ERROR (Status)) {
785     goto ON_ERROR;
786   }
787   HttpIo->RspToken.Event = Event;
788   HttpIo->RspToken.Message = &HttpIo->RspMessage;
789 
790   return EFI_SUCCESS;
791 
792 ON_ERROR:
793   HttpIoDestroyIo (HttpIo);
794 
795   return Status;
796 }
797 
798 /**
799   Destroy the HTTP_IO and release the resouces.
800 
801   @param[in]  HttpIo          The HTTP_IO which wraps the HTTP service to be destroyed.
802 
803 **/
804 VOID
HttpIoDestroyIo(IN HTTP_IO * HttpIo)805 HttpIoDestroyIo (
806   IN HTTP_IO                *HttpIo
807   )
808 {
809   EFI_HTTP_PROTOCOL         *Http;
810   EFI_EVENT                 Event;
811 
812   if (HttpIo == NULL) {
813     return;
814   }
815 
816   Event = HttpIo->ReqToken.Event;
817   if (Event != NULL) {
818     gBS->CloseEvent (Event);
819   }
820 
821   Event = HttpIo->RspToken.Event;
822   if (Event != NULL) {
823     gBS->CloseEvent (Event);
824   }
825 
826   Http = HttpIo->Http;
827   if (Http != NULL) {
828     Http->Configure (Http, NULL);
829     gBS->CloseProtocol (
830            HttpIo->Handle,
831            &gEfiHttpProtocolGuid,
832            HttpIo->Image,
833            HttpIo->Controller
834            );
835   }
836 
837   NetLibDestroyServiceChild (
838     HttpIo->Controller,
839     HttpIo->Image,
840     &gEfiHttpServiceBindingProtocolGuid,
841     HttpIo->Handle
842     );
843 }
844 
845 /**
846   Synchronously send a HTTP REQUEST message to the server.
847 
848   @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
849   @param[in]   Request          A pointer to storage such data as URL and HTTP method.
850   @param[in]   HeaderCount      Number of HTTP header structures in Headers list.
851   @param[in]   Headers          Array containing list of HTTP headers.
852   @param[in]   BodyLength       Length in bytes of the HTTP body.
853   @param[in]   Body             Body associated with the HTTP request.
854 
855   @retval EFI_SUCCESS            The HTTP request is trasmitted.
856   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
857   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
858   @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
859   @retval Others                 Other errors as indicated.
860 
861 **/
862 EFI_STATUS
HttpIoSendRequest(IN HTTP_IO * HttpIo,IN EFI_HTTP_REQUEST_DATA * Request,IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,IN UINTN BodyLength,IN VOID * Body)863 HttpIoSendRequest (
864   IN  HTTP_IO                *HttpIo,
865   IN  EFI_HTTP_REQUEST_DATA  *Request,
866   IN  UINTN                  HeaderCount,
867   IN  EFI_HTTP_HEADER        *Headers,
868   IN  UINTN                  BodyLength,
869   IN  VOID                   *Body
870   )
871 {
872   EFI_STATUS                 Status;
873   EFI_HTTP_PROTOCOL          *Http;
874 
875   if (HttpIo == NULL || HttpIo->Http == NULL) {
876     return EFI_INVALID_PARAMETER;
877   }
878 
879   HttpIo->ReqToken.Status  = EFI_NOT_READY;
880   HttpIo->ReqToken.Message->Data.Request = Request;
881   HttpIo->ReqToken.Message->HeaderCount  = HeaderCount;
882   HttpIo->ReqToken.Message->Headers      = Headers;
883   HttpIo->ReqToken.Message->BodyLength   = BodyLength;
884   HttpIo->ReqToken.Message->Body         = Body;
885 
886   //
887   // Queue the request token to HTTP instances.
888   //
889   Http = HttpIo->Http;
890   HttpIo->IsTxDone = FALSE;
891   Status = Http->Request (
892                    Http,
893                    &HttpIo->ReqToken
894                    );
895   if (EFI_ERROR (Status)) {
896     return Status;
897   }
898 
899   //
900   // Poll the network until transmit finish.
901   //
902   while (!HttpIo->IsTxDone) {
903     Http->Poll (Http);
904   }
905 
906   return HttpIo->ReqToken.Status;
907 }
908 
909 /**
910   Synchronously receive a HTTP RESPONSE message from the server.
911 
912   @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
913   @param[in]   RecvMsgHeader    TRUE to receive a new HTTP response (from message header).
914                                 FALSE to continue receive the previous response message.
915   @param[out]  ResponseData     Point to a wrapper of the received response data.
916 
917   @retval EFI_SUCCESS            The HTTP resopnse is received.
918   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
919   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
920   @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
921   @retval Others                 Other errors as indicated.
922 
923 **/
924 EFI_STATUS
HttpIoRecvResponse(IN HTTP_IO * HttpIo,IN BOOLEAN RecvMsgHeader,OUT HTTP_IO_RESOPNSE_DATA * ResponseData)925 HttpIoRecvResponse (
926   IN      HTTP_IO                  *HttpIo,
927   IN      BOOLEAN                  RecvMsgHeader,
928      OUT  HTTP_IO_RESOPNSE_DATA    *ResponseData
929   )
930 {
931   EFI_STATUS                 Status;
932   EFI_HTTP_PROTOCOL          *Http;
933   EFI_HTTP_STATUS_CODE       StatusCode;
934 
935   if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
936     return EFI_INVALID_PARAMETER;
937   }
938 
939   //
940   // Queue the response token to HTTP instances.
941   //
942   HttpIo->RspToken.Status  = EFI_NOT_READY;
943   if (RecvMsgHeader) {
944     HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
945   } else {
946     HttpIo->RspToken.Message->Data.Response = NULL;
947   }
948   HttpIo->RspToken.Message->HeaderCount   = 0;
949   HttpIo->RspToken.Message->Headers       = NULL;
950   HttpIo->RspToken.Message->BodyLength    = ResponseData->BodyLength;
951   HttpIo->RspToken.Message->Body          = ResponseData->Body;
952 
953   Http = HttpIo->Http;
954   HttpIo->IsRxDone = FALSE;
955   Status = Http->Response (
956                    Http,
957                    &HttpIo->RspToken
958                    );
959 
960   if (EFI_ERROR (Status)) {
961     return Status;
962   }
963 
964   //
965   // Poll the network until receive finish.
966   //
967   while (!HttpIo->IsRxDone) {
968     Http->Poll (Http);
969   }
970 
971   //
972   // Store the received data into the wrapper.
973   //
974   Status = HttpIo->RspToken.Status;
975   if (!EFI_ERROR (Status)) {
976     ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
977     ResponseData->Headers     = HttpIo->RspToken.Message->Headers;
978     ResponseData->BodyLength  = HttpIo->RspToken.Message->BodyLength;
979   }
980 
981   if (RecvMsgHeader) {
982     StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
983     HttpBootPrintErrorMessage (StatusCode);
984   }
985 
986   return Status;
987 }
988