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