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