2 Support functions implementation for UEFI HTTP boot driver.
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.
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.
15 #include "HttpBootDxe.h"
19 Get the Nic handle using any child handle in the IPv4 stack.
21 @param[in] ControllerHandle Pointer to child handle over IPv4.
23 @return NicHandle The pointer to the Nic handle.
24 @return NULL Can't find the Nic handle.
28 HttpBootGetNicByIp4Children (
29 IN EFI_HANDLE ControllerHandle
34 NicHandle
= NetLibGetNicHandle (ControllerHandle
, &gEfiHttpProtocolGuid
);
35 if (NicHandle
== NULL
) {
36 NicHandle
= NetLibGetNicHandle (ControllerHandle
, &gEfiDhcp4ProtocolGuid
);
37 if (NicHandle
== NULL
) {
46 Get the Nic handle using any child handle in the IPv6 stack.
48 @param[in] ControllerHandle Pointer to child handle over IPv6.
50 @return NicHandle The pointer to the Nic handle.
51 @return NULL Can't find the Nic handle.
55 HttpBootGetNicByIp6Children (
56 IN EFI_HANDLE ControllerHandle
60 NicHandle
= NetLibGetNicHandle (ControllerHandle
, &gEfiHttpProtocolGuid
);
61 if (NicHandle
== NULL
) {
62 NicHandle
= NetLibGetNicHandle (ControllerHandle
, &gEfiDhcp6ProtocolGuid
);
63 if (NicHandle
== NULL
) {
72 This function is to convert UINTN to ASCII string with the required formatting.
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.
80 HttpBootUintnToAscDecWithFormat (
90 Remainder
= Number
% 10;
92 Buffer
[Length
] = (UINT8
) ('0' + Remainder
);
97 This function is to display the IPv4 address.
99 @param[in] Ip The pointer to the IPv4 address.
103 HttpBootShowIp4Addr (
104 IN EFI_IPv4_ADDRESS
*Ip
109 for (Index
= 0; Index
< 4; Index
++) {
110 AsciiPrint ("%d", Ip
->Addr
[Index
]);
118 This function is to display the IPv6 address.
120 @param[in] Ip The pointer to the IPv6 address.
124 HttpBootShowIp6Addr (
125 IN EFI_IPv6_ADDRESS
*Ip
130 for (Index
= 0; Index
< 16; Index
++) {
132 if (Ip
->Addr
[Index
] != 0) {
133 AsciiPrint ("%x", Ip
->Addr
[Index
]);
139 if (((Ip
->Addr
[Index
] & 0xf0) == 0) && (Ip
->Addr
[Index
- 1] != 0)) {
142 AsciiPrint ("%x", Ip
->Addr
[Index
]);
150 Notify the callback function when an event is triggered.
152 @param[in] Event The triggered event.
153 @param[in] Context The opaque parameter to the function.
158 HttpBootCommonNotify (
163 *((BOOLEAN
*) Context
) = TRUE
;
167 Retrieve the host address using the EFI_DNS6_PROTOCOL.
169 @param[in] Private The pointer to the driver's private data.
170 @param[in] HostName Pointer to buffer containing hostname.
171 @param[out] IpAddress On output, pointer to buffer containing IPv6 address.
173 @retval EFI_SUCCESS Operation succeeded.
174 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
175 @retval Others Other errors as indicated.
179 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
181 OUT EFI_IPv6_ADDRESS
*IpAddress
185 EFI_DNS6_PROTOCOL
*Dns6
;
186 EFI_DNS6_CONFIG_DATA Dns6ConfigData
;
187 EFI_DNS6_COMPLETION_TOKEN Token
;
188 EFI_HANDLE Dns6Handle
;
189 EFI_IP6_CONFIG_PROTOCOL
*Ip6Config
;
190 EFI_IPv6_ADDRESS
*DnsServerList
;
191 UINTN DnsServerListCount
;
195 DnsServerList
= NULL
;
196 DnsServerListCount
= 0;
199 ZeroMem (&Token
, sizeof (EFI_DNS6_COMPLETION_TOKEN
));
202 // Get DNS server list from EFI IPv6 Configuration protocol.
204 Status
= gBS
->HandleProtocol (Private
->Controller
, &gEfiIp6ConfigProtocolGuid
, (VOID
**) &Ip6Config
);
205 if (!EFI_ERROR (Status
)) {
207 // Get the required size.
210 Status
= Ip6Config
->GetData (Ip6Config
, Ip6ConfigDataTypeDnsServer
, &DataSize
, NULL
);
211 if (Status
== EFI_BUFFER_TOO_SMALL
) {
212 DnsServerList
= AllocatePool (DataSize
);
213 if (DnsServerList
== NULL
) {
214 return EFI_OUT_OF_RESOURCES
;
217 Status
= Ip6Config
->GetData (Ip6Config
, Ip6ConfigDataTypeDnsServer
, &DataSize
, DnsServerList
);
218 if (EFI_ERROR (Status
)) {
219 FreePool (DnsServerList
);
220 DnsServerList
= NULL
;
222 DnsServerListCount
= DataSize
/ sizeof (EFI_IPv6_ADDRESS
);
227 // Create a DNSv6 child instance and get the protocol.
229 Status
= NetLibCreateServiceChild (
232 &gEfiDns6ServiceBindingProtocolGuid
,
235 if (EFI_ERROR (Status
)) {
239 Status
= gBS
->OpenProtocol (
241 &gEfiDns6ProtocolGuid
,
245 EFI_OPEN_PROTOCOL_BY_DRIVER
247 if (EFI_ERROR (Status
)) {
252 // Configure DNS6 instance for the DNS server address and protocol.
254 ZeroMem (&Dns6ConfigData
, sizeof (EFI_DNS6_CONFIG_DATA
));
255 Dns6ConfigData
.DnsServerCount
= (UINT32
)DnsServerListCount
;
256 Dns6ConfigData
.DnsServerList
= DnsServerList
;
257 Dns6ConfigData
.EnableDnsCache
= TRUE
;
258 Dns6ConfigData
.Protocol
= EFI_IP_PROTO_UDP
;
259 IP6_COPY_ADDRESS (&Dns6ConfigData
.StationIp
,&Private
->StationIp
.v6
);
260 Status
= Dns6
->Configure (
264 if (EFI_ERROR (Status
)) {
268 Token
.Status
= EFI_NOT_READY
;
271 // Create event to set the IsDone flag when name resolution is finished.
273 Status
= gBS
->CreateEvent (
276 HttpBootCommonNotify
,
280 if (EFI_ERROR (Status
)) {
285 // Start asynchronous name resolution.
287 Status
= Dns6
->HostNameToIp (Dns6
, HostName
, &Token
);
288 if (EFI_ERROR (Status
)) {
297 // Name resolution is done, check result.
299 Status
= Token
.Status
;
300 if (!EFI_ERROR (Status
)) {
301 if (Token
.RspData
.H2AData
== NULL
) {
302 Status
= EFI_DEVICE_ERROR
;
305 if (Token
.RspData
.H2AData
->IpCount
== 0 || Token
.RspData
.H2AData
->IpList
== NULL
) {
306 Status
= EFI_DEVICE_ERROR
;
310 // We just return the first IPv6 address from DNS protocol.
312 IP6_COPY_ADDRESS (IpAddress
, Token
.RspData
.H2AData
->IpList
);
313 Status
= EFI_SUCCESS
;
317 if (Token
.Event
!= NULL
) {
318 gBS
->CloseEvent (Token
.Event
);
320 if (Token
.RspData
.H2AData
!= NULL
) {
321 if (Token
.RspData
.H2AData
->IpList
!= NULL
) {
322 FreePool (Token
.RspData
.H2AData
->IpList
);
324 FreePool (Token
.RspData
.H2AData
);
328 Dns6
->Configure (Dns6
, NULL
);
332 &gEfiDns6ProtocolGuid
,
338 if (Dns6Handle
!= NULL
) {
339 NetLibDestroyServiceChild (
342 &gEfiDns6ServiceBindingProtocolGuid
,
347 if (DnsServerList
!= NULL
) {
348 FreePool (DnsServerList
);
354 Create a HTTP_IO_HEADER to hold the HTTP header items.
356 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder.
358 @return A pointer of the HTTP header holder or NULL if failed.
362 HttpBootCreateHeader (
366 HTTP_IO_HEADER
*HttpIoHeader
;
368 if (MaxHeaderCount
== 0) {
372 HttpIoHeader
= AllocateZeroPool (sizeof (HTTP_IO_HEADER
) + MaxHeaderCount
* sizeof (EFI_HTTP_HEADER
));
373 if (HttpIoHeader
== NULL
) {
377 HttpIoHeader
->MaxHeaderCount
= MaxHeaderCount
;
378 HttpIoHeader
->Headers
= (EFI_HTTP_HEADER
*) (HttpIoHeader
+ 1);
384 Destroy the HTTP_IO_HEADER and release the resouces.
386 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.
391 IN HTTP_IO_HEADER
*HttpIoHeader
396 if (HttpIoHeader
!= NULL
) {
397 if (HttpIoHeader
->HeaderCount
!= 0) {
398 for (Index
= 0; Index
< HttpIoHeader
->HeaderCount
; Index
++) {
399 FreePool (HttpIoHeader
->Headers
[Index
].FieldName
);
400 FreePool (HttpIoHeader
->Headers
[Index
].FieldValue
);
403 FreePool (HttpIoHeader
);
408 Find a specified header field according to the field name.
410 @param[in] HeaderCount Number of HTTP header structures in Headers list.
411 @param[in] Headers Array containing list of HTTP headers.
412 @param[in] FieldName Null terminated string which describes a field name.
414 @return Pointer to the found header or NULL.
419 IN UINTN HeaderCount
,
420 IN EFI_HTTP_HEADER
*Headers
,
426 if (HeaderCount
== 0 || Headers
== NULL
|| FieldName
== NULL
) {
430 for (Index
= 0; Index
< HeaderCount
; Index
++){
432 // Field names are case-insensitive (RFC 2616).
434 if (AsciiStriCmp (Headers
[Index
].FieldName
, FieldName
) == 0) {
435 return &Headers
[Index
];
442 Set or update a HTTP header with the field name and corresponding value.
444 @param[in] HttpIoHeader Point to the HTTP header holder.
445 @param[in] FieldName Null terminated string which describes a field name.
446 @param[in] FieldValue Null terminated string which describes the corresponding field value.
448 @retval EFI_SUCCESS The HTTP header has been set or updated.
449 @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
450 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.
451 @retval Other Unexpected error happened.
456 IN HTTP_IO_HEADER
*HttpIoHeader
,
461 EFI_HTTP_HEADER
*Header
;
463 CHAR8
*NewFieldValue
;
465 if (HttpIoHeader
== NULL
|| FieldName
== NULL
|| FieldValue
== NULL
) {
466 return EFI_INVALID_PARAMETER
;
469 Header
= HttpBootFindHeader (HttpIoHeader
->HeaderCount
, HttpIoHeader
->Headers
, FieldName
);
470 if (Header
== NULL
) {
474 if (HttpIoHeader
->HeaderCount
>= HttpIoHeader
->MaxHeaderCount
) {
475 return EFI_OUT_OF_RESOURCES
;
477 Header
= &HttpIoHeader
->Headers
[HttpIoHeader
->HeaderCount
];
479 StrSize
= AsciiStrSize (FieldName
);
480 Header
->FieldName
= AllocatePool (StrSize
);
481 if (Header
->FieldName
== NULL
) {
482 return EFI_OUT_OF_RESOURCES
;
484 CopyMem (Header
->FieldName
, FieldName
, StrSize
);
485 Header
->FieldName
[StrSize
-1] = '\0';
487 StrSize
= AsciiStrSize (FieldValue
);
488 Header
->FieldValue
= AllocatePool (StrSize
);
489 if (Header
->FieldValue
== NULL
) {
490 FreePool (Header
->FieldName
);
491 return EFI_OUT_OF_RESOURCES
;
493 CopyMem (Header
->FieldValue
, FieldValue
, StrSize
);
494 Header
->FieldValue
[StrSize
-1] = '\0';
496 HttpIoHeader
->HeaderCount
++;
499 // Update an existing one.
501 StrSize
= AsciiStrSize (FieldValue
);
502 NewFieldValue
= AllocatePool (StrSize
);
503 if (NewFieldValue
== NULL
) {
504 return EFI_OUT_OF_RESOURCES
;
506 CopyMem (NewFieldValue
, FieldValue
, StrSize
);
507 NewFieldValue
[StrSize
-1] = '\0';
509 if (Header
->FieldValue
!= NULL
) {
510 FreePool (Header
->FieldValue
);
512 Header
->FieldValue
= NewFieldValue
;
519 Create a HTTP_IO to access the HTTP service. It will create and configure
522 @param[in] Image The handle of the driver image.
523 @param[in] Controller The handle of the controller.
524 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
525 @param[in] ConfigData The HTTP_IO configuration data.
526 @param[out] HttpIo The HTTP_IO.
528 @retval EFI_SUCCESS The HTTP_IO is created and configured.
529 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
530 @retval EFI_UNSUPPORTED One or more of the control options are not
531 supported in the implementation.
532 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
533 @retval Others Failed to create the HTTP_IO or configure it.
539 IN EFI_HANDLE Controller
,
541 IN HTTP_IO_CONFIG_DATA
*ConfigData
,
546 EFI_HTTP_CONFIG_DATA HttpConfigData
;
547 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint
;
548 EFI_HTTPv6_ACCESS_POINT Http6AccessPoint
;
549 EFI_HTTP_PROTOCOL
*Http
;
552 if ((Image
== NULL
) || (Controller
== NULL
) || (ConfigData
== NULL
) || (HttpIo
== NULL
)) {
553 return EFI_INVALID_PARAMETER
;
556 if (IpVersion
!= IP_VERSION_4
&& IpVersion
!= IP_VERSION_6
) {
557 return EFI_UNSUPPORTED
;
560 ZeroMem (HttpIo
, sizeof (HTTP_IO
));
563 // Create the HTTP child instance and get the HTTP protocol.
565 Status
= NetLibCreateServiceChild (
568 &gEfiHttpServiceBindingProtocolGuid
,
571 if (EFI_ERROR (Status
)) {
575 Status
= gBS
->OpenProtocol (
577 &gEfiHttpProtocolGuid
,
581 EFI_OPEN_PROTOCOL_BY_DRIVER
583 if (EFI_ERROR (Status
) || (Http
== NULL
)) {
588 // Init the configuration data and configure the HTTP child.
590 HttpIo
->Image
= Image
;
591 HttpIo
->Controller
= Controller
;
592 HttpIo
->IpVersion
= IpVersion
;
595 ZeroMem (&HttpConfigData
, sizeof (EFI_HTTP_CONFIG_DATA
));
596 HttpConfigData
.HttpVersion
= HttpVersion11
;
597 HttpConfigData
.TimeOutMillisec
= ConfigData
->Config4
.RequestTimeOut
;
598 if (HttpIo
->IpVersion
== IP_VERSION_4
) {
599 HttpConfigData
.LocalAddressIsIPv6
= FALSE
;
601 Http4AccessPoint
.UseDefaultAddress
= ConfigData
->Config4
.UseDefaultAddress
;
602 Http4AccessPoint
.LocalPort
= ConfigData
->Config4
.LocalPort
;
603 IP4_COPY_ADDRESS (&Http4AccessPoint
.LocalAddress
, &ConfigData
->Config4
.LocalIp
);
604 IP4_COPY_ADDRESS (&Http4AccessPoint
.LocalSubnet
, &ConfigData
->Config4
.SubnetMask
);
605 HttpConfigData
.AccessPoint
.IPv4Node
= &Http4AccessPoint
;
607 HttpConfigData
.LocalAddressIsIPv6
= TRUE
;
608 Http6AccessPoint
.LocalPort
= ConfigData
->Config6
.LocalPort
;
609 IP6_COPY_ADDRESS (&Http6AccessPoint
.LocalAddress
, &ConfigData
->Config6
.LocalIp
);
610 HttpConfigData
.AccessPoint
.IPv6Node
= &Http6AccessPoint
;
613 Status
= Http
->Configure (Http
, &HttpConfigData
);
614 if (EFI_ERROR (Status
)) {
619 // Create events for variuos asynchronous operations.
621 Status
= gBS
->CreateEvent (
624 HttpBootCommonNotify
,
628 if (EFI_ERROR (Status
)) {
631 HttpIo
->ReqToken
.Event
= Event
;
632 HttpIo
->ReqToken
.Message
= &HttpIo
->ReqMessage
;
634 Status
= gBS
->CreateEvent (
637 HttpBootCommonNotify
,
641 if (EFI_ERROR (Status
)) {
644 HttpIo
->RspToken
.Event
= Event
;
645 HttpIo
->RspToken
.Message
= &HttpIo
->RspMessage
;
650 HttpIoDestroyIo (HttpIo
);
656 Destroy the HTTP_IO and release the resouces.
658 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
666 EFI_HTTP_PROTOCOL
*Http
;
669 if (HttpIo
== NULL
) {
673 Event
= HttpIo
->ReqToken
.Event
;
675 gBS
->CloseEvent (Event
);
678 Event
= HttpIo
->RspToken
.Event
;
680 gBS
->CloseEvent (Event
);
685 Http
->Configure (Http
, NULL
);
688 &gEfiHttpProtocolGuid
,
694 NetLibDestroyServiceChild (
697 &gEfiHttpServiceBindingProtocolGuid
,
703 Synchronously send a HTTP REQUEST message to the server.
705 @param[in] HttpIo The HttpIo wrapping the HTTP service.
706 @param[in] Request A pointer to storage such data as URL and HTTP method.
707 @param[in] HeaderCount Number of HTTP header structures in Headers list.
708 @param[in] Headers Array containing list of HTTP headers.
709 @param[in] BodyLength Length in bytes of the HTTP body.
710 @param[in] Body Body associated with the HTTP request.
712 @retval EFI_SUCCESS The HTTP request is trasmitted.
713 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
714 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
715 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
716 @retval Others Other errors as indicated.
722 IN EFI_HTTP_REQUEST_DATA
*Request
,
723 IN UINTN HeaderCount
,
724 IN EFI_HTTP_HEADER
*Headers
,
730 EFI_HTTP_PROTOCOL
*Http
;
732 if (HttpIo
== NULL
|| HttpIo
->Http
== NULL
) {
733 return EFI_INVALID_PARAMETER
;
736 HttpIo
->ReqToken
.Status
= EFI_NOT_READY
;
737 HttpIo
->ReqToken
.Message
->Data
.Request
= Request
;
738 HttpIo
->ReqToken
.Message
->HeaderCount
= HeaderCount
;
739 HttpIo
->ReqToken
.Message
->Headers
= Headers
;
740 HttpIo
->ReqToken
.Message
->BodyLength
= BodyLength
;
741 HttpIo
->ReqToken
.Message
->Body
= Body
;
744 // Queue the request token to HTTP instances.
747 HttpIo
->IsTxDone
= FALSE
;
748 Status
= Http
->Request (
752 if (EFI_ERROR (Status
)) {
757 // Poll the network until transmit finish.
759 while (!HttpIo
->IsTxDone
) {
763 return HttpIo
->ReqToken
.Status
;
767 Synchronously receive a HTTP RESPONSE message from the server.
769 @param[in] HttpIo The HttpIo wrapping the HTTP service.
770 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
771 FALSE to continue receive the previous response message.
772 @param[out] ResponseData Point to a wrapper of the received response data.
774 @retval EFI_SUCCESS The HTTP resopnse is received.
775 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
776 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
777 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
778 @retval Others Other errors as indicated.
784 IN BOOLEAN RecvMsgHeader
,
785 OUT HTTP_IO_RESOPNSE_DATA
*ResponseData
789 EFI_HTTP_PROTOCOL
*Http
;
791 if (HttpIo
== NULL
|| HttpIo
->Http
== NULL
|| ResponseData
== NULL
) {
792 return EFI_INVALID_PARAMETER
;
796 // Queue the response token to HTTP instances.
798 HttpIo
->RspToken
.Status
= EFI_NOT_READY
;
800 HttpIo
->RspToken
.Message
->Data
.Response
= &ResponseData
->Response
;
802 HttpIo
->RspToken
.Message
->Data
.Response
= NULL
;
804 HttpIo
->RspToken
.Message
->HeaderCount
= 0;
805 HttpIo
->RspToken
.Message
->Headers
= NULL
;
806 HttpIo
->RspToken
.Message
->BodyLength
= ResponseData
->BodyLength
;
807 HttpIo
->RspToken
.Message
->Body
= ResponseData
->Body
;
810 HttpIo
->IsRxDone
= FALSE
;
811 Status
= Http
->Response (
816 if (EFI_ERROR (Status
)) {
821 // Poll the network until transmit finish.
823 while (!HttpIo
->IsRxDone
) {
828 // Store the received data into the wrapper.
830 Status
= HttpIo
->RspToken
.Status
;
831 if (!EFI_ERROR (Status
)) {
832 ResponseData
->HeaderCount
= HttpIo
->RspToken
.Message
->HeaderCount
;
833 ResponseData
->Headers
= HttpIo
->RspToken
.Message
->Headers
;
834 ResponseData
->BodyLength
= HttpIo
->RspToken
.Message
->BodyLength
;