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