NetworkPkg/HttpBootDxe: Add HTTP Boot Callback protocol support.
[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[in] Callback Callback function which will be invoked when specified
675 HTTP_IO_CALLBACK_EVENT happened.
676 @param[in] Context The Context data which will be passed to the Callback function.
677 @param[out] HttpIo The HTTP_IO.
678
679 @retval EFI_SUCCESS The HTTP_IO is created and configured.
680 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
681 @retval EFI_UNSUPPORTED One or more of the control options are not
682 supported in the implementation.
683 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
684 @retval Others Failed to create the HTTP_IO or configure it.
685
686 **/
687 EFI_STATUS
688 HttpIoCreateIo (
689 IN EFI_HANDLE Image,
690 IN EFI_HANDLE Controller,
691 IN UINT8 IpVersion,
692 IN HTTP_IO_CONFIG_DATA *ConfigData,
693 IN HTTP_IO_CALLBACK Callback,
694 IN VOID *Context,
695 OUT HTTP_IO *HttpIo
696 )
697 {
698 EFI_STATUS Status;
699 EFI_HTTP_CONFIG_DATA HttpConfigData;
700 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
701 EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;
702 EFI_HTTP_PROTOCOL *Http;
703 EFI_EVENT Event;
704
705 if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {
706 return EFI_INVALID_PARAMETER;
707 }
708
709 if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
710 return EFI_UNSUPPORTED;
711 }
712
713 ZeroMem (HttpIo, sizeof (HTTP_IO));
714
715 //
716 // Create the HTTP child instance and get the HTTP protocol.
717 //
718 Status = NetLibCreateServiceChild (
719 Controller,
720 Image,
721 &gEfiHttpServiceBindingProtocolGuid,
722 &HttpIo->Handle
723 );
724 if (EFI_ERROR (Status)) {
725 return Status;
726 }
727
728 Status = gBS->OpenProtocol (
729 HttpIo->Handle,
730 &gEfiHttpProtocolGuid,
731 (VOID **) &Http,
732 Image,
733 Controller,
734 EFI_OPEN_PROTOCOL_BY_DRIVER
735 );
736 if (EFI_ERROR (Status) || (Http == NULL)) {
737 goto ON_ERROR;
738 }
739
740 //
741 // Init the configuration data and configure the HTTP child.
742 //
743 HttpIo->Image = Image;
744 HttpIo->Controller = Controller;
745 HttpIo->IpVersion = IpVersion;
746 HttpIo->Http = Http;
747 HttpIo->Callback = Callback;
748 HttpIo->Context = Context;
749
750 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
751 HttpConfigData.HttpVersion = HttpVersion11;
752 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
753 if (HttpIo->IpVersion == IP_VERSION_4) {
754 HttpConfigData.LocalAddressIsIPv6 = FALSE;
755
756 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
757 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
758 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
759 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
760 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
761 } else {
762 HttpConfigData.LocalAddressIsIPv6 = TRUE;
763 Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
764 IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
765 HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
766 }
767
768 Status = Http->Configure (Http, &HttpConfigData);
769 if (EFI_ERROR (Status)) {
770 goto ON_ERROR;
771 }
772
773 //
774 // Create events for variuos asynchronous operations.
775 //
776 Status = gBS->CreateEvent (
777 EVT_NOTIFY_SIGNAL,
778 TPL_NOTIFY,
779 HttpIoNotify,
780 &HttpIo->IsTxDone,
781 &Event
782 );
783 if (EFI_ERROR (Status)) {
784 goto ON_ERROR;
785 }
786 HttpIo->ReqToken.Event = Event;
787 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
788
789 Status = gBS->CreateEvent (
790 EVT_NOTIFY_SIGNAL,
791 TPL_NOTIFY,
792 HttpIoNotify,
793 &HttpIo->IsRxDone,
794 &Event
795 );
796 if (EFI_ERROR (Status)) {
797 goto ON_ERROR;
798 }
799 HttpIo->RspToken.Event = Event;
800 HttpIo->RspToken.Message = &HttpIo->RspMessage;
801
802 //
803 // Create TimeoutEvent for response
804 //
805 Status = gBS->CreateEvent (
806 EVT_TIMER,
807 TPL_CALLBACK,
808 NULL,
809 NULL,
810 &Event
811 );
812 if (EFI_ERROR (Status)) {
813 goto ON_ERROR;
814 }
815 HttpIo->TimeoutEvent = Event;
816
817 return EFI_SUCCESS;
818
819 ON_ERROR:
820 HttpIoDestroyIo (HttpIo);
821
822 return Status;
823 }
824
825 /**
826 Destroy the HTTP_IO and release the resouces.
827
828 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
829
830 **/
831 VOID
832 HttpIoDestroyIo (
833 IN HTTP_IO *HttpIo
834 )
835 {
836 EFI_HTTP_PROTOCOL *Http;
837 EFI_EVENT Event;
838
839 if (HttpIo == NULL) {
840 return;
841 }
842
843 Event = HttpIo->ReqToken.Event;
844 if (Event != NULL) {
845 gBS->CloseEvent (Event);
846 }
847
848 Event = HttpIo->RspToken.Event;
849 if (Event != NULL) {
850 gBS->CloseEvent (Event);
851 }
852
853 Event = HttpIo->TimeoutEvent;
854 if (Event != NULL) {
855 gBS->CloseEvent (Event);
856 }
857
858 Http = HttpIo->Http;
859 if (Http != NULL) {
860 Http->Configure (Http, NULL);
861 gBS->CloseProtocol (
862 HttpIo->Handle,
863 &gEfiHttpProtocolGuid,
864 HttpIo->Image,
865 HttpIo->Controller
866 );
867 }
868
869 NetLibDestroyServiceChild (
870 HttpIo->Controller,
871 HttpIo->Image,
872 &gEfiHttpServiceBindingProtocolGuid,
873 HttpIo->Handle
874 );
875 }
876
877 /**
878 Synchronously send a HTTP REQUEST message to the server.
879
880 @param[in] HttpIo The HttpIo wrapping the HTTP service.
881 @param[in] Request A pointer to storage such data as URL and HTTP method.
882 @param[in] HeaderCount Number of HTTP header structures in Headers list.
883 @param[in] Headers Array containing list of HTTP headers.
884 @param[in] BodyLength Length in bytes of the HTTP body.
885 @param[in] Body Body associated with the HTTP request.
886
887 @retval EFI_SUCCESS The HTTP request is trasmitted.
888 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
889 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
890 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
891 @retval Others Other errors as indicated.
892
893 **/
894 EFI_STATUS
895 HttpIoSendRequest (
896 IN HTTP_IO *HttpIo,
897 IN EFI_HTTP_REQUEST_DATA *Request,
898 IN UINTN HeaderCount,
899 IN EFI_HTTP_HEADER *Headers,
900 IN UINTN BodyLength,
901 IN VOID *Body
902 )
903 {
904 EFI_STATUS Status;
905 EFI_HTTP_PROTOCOL *Http;
906
907 if (HttpIo == NULL || HttpIo->Http == NULL) {
908 return EFI_INVALID_PARAMETER;
909 }
910
911 HttpIo->ReqToken.Status = EFI_NOT_READY;
912 HttpIo->ReqToken.Message->Data.Request = Request;
913 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
914 HttpIo->ReqToken.Message->Headers = Headers;
915 HttpIo->ReqToken.Message->BodyLength = BodyLength;
916 HttpIo->ReqToken.Message->Body = Body;
917
918 if (HttpIo->Callback != NULL) {
919 Status = HttpIo->Callback (
920 HttpIoRequest,
921 HttpIo->ReqToken.Message,
922 HttpIo->Context
923 );
924 if (EFI_ERROR (Status)) {
925 return Status;
926 }
927 }
928
929 //
930 // Queue the request token to HTTP instances.
931 //
932 Http = HttpIo->Http;
933 HttpIo->IsTxDone = FALSE;
934 Status = Http->Request (
935 Http,
936 &HttpIo->ReqToken
937 );
938 if (EFI_ERROR (Status)) {
939 return Status;
940 }
941
942 //
943 // Poll the network until transmit finish.
944 //
945 while (!HttpIo->IsTxDone) {
946 Http->Poll (Http);
947 }
948
949 return HttpIo->ReqToken.Status;
950 }
951
952 /**
953 Synchronously receive a HTTP RESPONSE message from the server.
954
955 @param[in] HttpIo The HttpIo wrapping the HTTP service.
956 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
957 FALSE to continue receive the previous response message.
958 @param[out] ResponseData Point to a wrapper of the received response data.
959
960 @retval EFI_SUCCESS The HTTP response is received.
961 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
962 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
963 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
964 @retval Others Other errors as indicated.
965
966 **/
967 EFI_STATUS
968 HttpIoRecvResponse (
969 IN HTTP_IO *HttpIo,
970 IN BOOLEAN RecvMsgHeader,
971 OUT HTTP_IO_RESPONSE_DATA *ResponseData
972 )
973 {
974 EFI_STATUS Status;
975 EFI_HTTP_PROTOCOL *Http;
976
977 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
978 return EFI_INVALID_PARAMETER;
979 }
980
981 //
982 // Start the timer, and wait Timeout seconds to receive the header packet.
983 //
984 Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HTTP_BOOT_RESPONSE_TIMEOUT * TICKS_PER_MS);
985 if (EFI_ERROR (Status)) {
986 return Status;
987 }
988
989 //
990 // Queue the response token to HTTP instances.
991 //
992 HttpIo->RspToken.Status = EFI_NOT_READY;
993 if (RecvMsgHeader) {
994 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
995 } else {
996 HttpIo->RspToken.Message->Data.Response = NULL;
997 }
998 HttpIo->RspToken.Message->HeaderCount = 0;
999 HttpIo->RspToken.Message->Headers = NULL;
1000 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
1001 HttpIo->RspToken.Message->Body = ResponseData->Body;
1002
1003 Http = HttpIo->Http;
1004 HttpIo->IsRxDone = FALSE;
1005 Status = Http->Response (
1006 Http,
1007 &HttpIo->RspToken
1008 );
1009
1010 if (EFI_ERROR (Status)) {
1011 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
1012 return Status;
1013 }
1014
1015 //
1016 // Poll the network until receive finish.
1017 //
1018 while (!HttpIo->IsRxDone && ((HttpIo->TimeoutEvent == NULL) || EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent)))) {
1019 Http->Poll (Http);
1020 }
1021
1022 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
1023
1024 if (!HttpIo->IsRxDone) {
1025 //
1026 // Timeout occurs, cancel the response token.
1027 //
1028 Http->Cancel (Http, &HttpIo->RspToken);
1029
1030 Status = EFI_TIMEOUT;
1031
1032 return Status;
1033 } else {
1034 HttpIo->IsRxDone = FALSE;
1035 }
1036
1037 if (!EFI_ERROR (HttpIo->RspToken.Status) && HttpIo->Callback != NULL) {
1038 Status = HttpIo->Callback (
1039 HttpIoResponse,
1040 HttpIo->RspToken.Message,
1041 HttpIo->Context
1042 );
1043 if (EFI_ERROR (Status)) {
1044 return Status;
1045 }
1046 }
1047
1048 //
1049 // Store the received data into the wrapper.
1050 //
1051 ResponseData->Status = HttpIo->RspToken.Status;
1052 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
1053 ResponseData->Headers = HttpIo->RspToken.Message->Headers;
1054 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
1055
1056 return Status;
1057 }
1058
1059 /**
1060 This function checks the HTTP(S) URI scheme.
1061
1062 @param[in] Uri The pointer to the URI string.
1063
1064 @retval EFI_SUCCESS The URI scheme is valid.
1065 @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS.
1066 @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP.
1067
1068 **/
1069 EFI_STATUS
1070 HttpBootCheckUriScheme (
1071 IN CHAR8 *Uri
1072 )
1073 {
1074 UINTN Index;
1075 EFI_STATUS Status;
1076
1077 Status = EFI_SUCCESS;
1078
1079 //
1080 // Convert the scheme to all lower case.
1081 //
1082 for (Index = 0; Index < AsciiStrLen (Uri); Index++) {
1083 if (Uri[Index] == ':') {
1084 break;
1085 }
1086 if (Uri[Index] >= 'A' && Uri[Index] <= 'Z') {
1087 Uri[Index] -= (CHAR8)('A' - 'a');
1088 }
1089 }
1090
1091 //
1092 // Return EFI_INVALID_PARAMETER if the URI is not HTTP or HTTPS.
1093 //
1094 if ((AsciiStrnCmp (Uri, "http://", 7) != 0) && (AsciiStrnCmp (Uri, "https://", 8) != 0)) {
1095 DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: Invalid Uri.\n"));
1096 return EFI_INVALID_PARAMETER;
1097 }
1098
1099 //
1100 // HTTP is disabled, return EFI_ACCESS_DENIED if the URI is HTTP.
1101 //
1102 if (!PcdGetBool (PcdAllowHttpConnections) && (AsciiStrnCmp (Uri, "http://", 7) == 0)) {
1103 DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: HTTP is disabled.\n"));
1104 return EFI_ACCESS_DENIED;
1105 }
1106
1107 return Status;
1108 }
1109
1110 /**
1111 Get the URI address string from the input device path.
1112
1113 Caller need to free the buffer in the UriAddress pointer.
1114
1115 @param[in] FilePath Pointer to the device path which contains a URI device path node.
1116 @param[out] UriAddress The URI address string extract from the device path.
1117
1118 @retval EFI_SUCCESS The URI string is returned.
1119 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1120
1121 **/
1122 EFI_STATUS
1123 HttpBootParseFilePath (
1124 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
1125 OUT CHAR8 **UriAddress
1126 )
1127 {
1128 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1129 URI_DEVICE_PATH *UriDevicePath;
1130 CHAR8 *Uri;
1131 UINTN UriStrLength;
1132
1133 if (FilePath == NULL) {
1134 return EFI_INVALID_PARAMETER;
1135 }
1136
1137 *UriAddress = NULL;
1138
1139 //
1140 // Extract the URI address from the FilePath
1141 //
1142 TempDevicePath = FilePath;
1143 while (!IsDevicePathEnd (TempDevicePath)) {
1144 if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
1145 (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) {
1146 UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath;
1147 //
1148 // UEFI Spec doesn't require the URI to be a NULL-terminated string
1149 // So we allocate a new buffer and always append a '\0' to it.
1150 //
1151 UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);
1152 if (UriStrLength == 0) {
1153 //
1154 // return a NULL UriAddress if it's a empty URI device path node.
1155 //
1156 break;
1157 }
1158 Uri = AllocatePool (UriStrLength + 1);
1159 if (Uri == NULL) {
1160 return EFI_OUT_OF_RESOURCES;
1161 }
1162 CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL));
1163 Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0';
1164
1165 *UriAddress = Uri;
1166 }
1167 TempDevicePath = NextDevicePathNode (TempDevicePath);
1168 }
1169
1170 return EFI_SUCCESS;
1171 }
1172
1173 /**
1174 This function returns the image type according to server replied HTTP message
1175 and also the image's URI info.
1176
1177 @param[in] Uri The pointer to the image's URI string.
1178 @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
1179 @param[in] HeaderCount Number of HTTP header structures in Headers list.
1180 @param[in] Headers Array containing list of HTTP headers.
1181 @param[out] ImageType The image type of the downloaded file.
1182
1183 @retval EFI_SUCCESS The image type is returned in ImageType.
1184 @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
1185 @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
1186 @retval EFI_NOT_FOUND Failed to identify the image type.
1187 @retval Others Unexpect error happened.
1188
1189 **/
1190 EFI_STATUS
1191 HttpBootCheckImageType (
1192 IN CHAR8 *Uri,
1193 IN VOID *UriParser,
1194 IN UINTN HeaderCount,
1195 IN EFI_HTTP_HEADER *Headers,
1196 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
1197 )
1198 {
1199 EFI_STATUS Status;
1200 EFI_HTTP_HEADER *Header;
1201 CHAR8 *FilePath;
1202 CHAR8 *FilePost;
1203
1204 if (Uri == NULL || UriParser == NULL || ImageType == NULL) {
1205 return EFI_INVALID_PARAMETER;
1206 }
1207
1208 if (HeaderCount != 0 && Headers == NULL) {
1209 return EFI_INVALID_PARAMETER;
1210 }
1211
1212 //
1213 // Determine the image type by the HTTP Content-Type header field first.
1214 // "application/efi" -> EFI Image
1215 // "application/vnd.efi-iso" -> CD/DVD Image
1216 // "application/vnd.efi-img" -> Virtual Disk Image
1217 //
1218 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE);
1219 if (Header != NULL) {
1220 if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) {
1221 *ImageType = ImageTypeEfi;
1222 return EFI_SUCCESS;
1223 } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_ISO) == 0) {
1224 *ImageType = ImageTypeVirtualCd;
1225 return EFI_SUCCESS;
1226 } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_IMG) == 0) {
1227 *ImageType = ImageTypeVirtualDisk;
1228 return EFI_SUCCESS;
1229 }
1230 }
1231
1232 //
1233 // Determine the image type by file extension:
1234 // *.efi -> EFI Image
1235 // *.iso -> CD/DVD Image
1236 // *.img -> Virtual Disk Image
1237 //
1238 Status = HttpUrlGetPath (
1239 Uri,
1240 UriParser,
1241 &FilePath
1242 );
1243 if (EFI_ERROR (Status)) {
1244 return Status;
1245 }
1246
1247 FilePost = FilePath + AsciiStrLen (FilePath) - 4;
1248 if (AsciiStrCmp (FilePost, ".efi") == 0) {
1249 *ImageType = ImageTypeEfi;
1250 } else if (AsciiStrCmp (FilePost, ".iso") == 0) {
1251 *ImageType = ImageTypeVirtualCd;
1252 } else if (AsciiStrCmp (FilePost, ".img") == 0) {
1253 *ImageType = ImageTypeVirtualDisk;
1254 } else {
1255 *ImageType = ImageTypeMax;
1256 }
1257
1258 FreePool (FilePath);
1259
1260 return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND;
1261 }
1262
1263 /**
1264 This function register the RAM disk info to the system.
1265
1266 @param[in] Private The pointer to the driver's private data.
1267 @param[in] BufferSize The size of Buffer in bytes.
1268 @param[in] Buffer The base address of the RAM disk.
1269 @param[in] ImageType The image type of the file in Buffer.
1270
1271 @retval EFI_SUCCESS The RAM disk has been registered.
1272 @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
1273 @retval EFI_UNSUPPORTED The ImageType is not supported.
1274 @retval Others Unexpected error happened.
1275
1276 **/
1277 EFI_STATUS
1278 HttpBootRegisterRamDisk (
1279 IN HTTP_BOOT_PRIVATE_DATA *Private,
1280 IN UINTN BufferSize,
1281 IN VOID *Buffer,
1282 IN HTTP_BOOT_IMAGE_TYPE ImageType
1283 )
1284 {
1285 EFI_RAM_DISK_PROTOCOL *RamDisk;
1286 EFI_STATUS Status;
1287 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1288 EFI_GUID *RamDiskType;
1289
1290 ASSERT (Private != NULL);
1291 ASSERT (Buffer != NULL);
1292 ASSERT (BufferSize != 0);
1293
1294 Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk);
1295 if (EFI_ERROR (Status)) {
1296 DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status));
1297 return Status;
1298 }
1299
1300 if (ImageType == ImageTypeVirtualCd) {
1301 RamDiskType = &gEfiVirtualCdGuid;
1302 } else if (ImageType == ImageTypeVirtualDisk) {
1303 RamDiskType = &gEfiVirtualDiskGuid;
1304 } else {
1305 return EFI_UNSUPPORTED;
1306 }
1307
1308 Status = RamDisk->Register (
1309 (UINTN)Buffer,
1310 (UINT64)BufferSize,
1311 RamDiskType,
1312 Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath,
1313 &DevicePath
1314 );
1315 if (EFI_ERROR (Status)) {
1316 DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status));
1317 }
1318
1319 return Status;
1320 }
1321