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