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