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
HttpBootGetNicByIp4Children(IN EFI_HANDLE ControllerHandle)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
HttpBootGetNicByIp6Children(IN EFI_HANDLE ControllerHandle)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
HttpBootUintnToAscDecWithFormat(IN UINTN Number,IN UINT8 * Buffer,IN INTN Length)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
HttpBootShowIp4Addr(IN EFI_IPv4_ADDRESS * Ip)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
HttpBootShowIp6Addr(IN EFI_IPv6_ADDRESS * Ip)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
HttpBootPrintErrorMessage(EFI_HTTP_STATUS_CODE StatusCode)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
HttpBootCommonNotify(IN EFI_EVENT Event,IN VOID * Context)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
HttpBootDns(IN HTTP_BOOT_PRIVATE_DATA * Private,IN CHAR16 * HostName,OUT EFI_IPv6_ADDRESS * IpAddress)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 *
HttpBootCreateHeader(UINTN MaxHeaderCount)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
HttpBootFreeHeader(IN HTTP_IO_HEADER * HttpIoHeader)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
HttpBootSetHeader(IN HTTP_IO_HEADER * HttpIoHeader,IN CHAR8 * FieldName,IN CHAR8 * FieldValue)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
HttpIoCreateIo(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,IN UINT8 IpVersion,IN HTTP_IO_CONFIG_DATA * ConfigData,OUT HTTP_IO * HttpIo)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
HttpIoDestroyIo(IN HTTP_IO * HttpIo)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
HttpIoSendRequest(IN HTTP_IO * HttpIo,IN EFI_HTTP_REQUEST_DATA * Request,IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,IN UINTN BodyLength,IN VOID * Body)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
HttpIoRecvResponse(IN HTTP_IO * HttpIo,IN BOOLEAN RecvMsgHeader,OUT HTTP_IO_RESPONSE_DATA * ResponseData)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 Get the URI address string from the input device path.
993
994 Caller need to free the buffer in the UriAddress pointer.
995
996 @param[in] FilePath Pointer to the device path which contains a URI device path node.
997 @param[out] UriAddress The URI address string extract from the device path.
998
999 @retval EFI_SUCCESS The URI string is returned.
1000 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1001
1002 **/
1003 EFI_STATUS
HttpBootParseFilePath(IN EFI_DEVICE_PATH_PROTOCOL * FilePath,OUT CHAR8 ** UriAddress)1004 HttpBootParseFilePath (
1005 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
1006 OUT CHAR8 **UriAddress
1007 )
1008 {
1009 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1010 URI_DEVICE_PATH *UriDevicePath;
1011 CHAR8 *Uri;
1012 UINTN UriStrLength;
1013
1014 if (FilePath == NULL) {
1015 return EFI_INVALID_PARAMETER;
1016 }
1017
1018 *UriAddress = NULL;
1019
1020 //
1021 // Extract the URI address from the FilePath
1022 //
1023 TempDevicePath = FilePath;
1024 while (!IsDevicePathEnd (TempDevicePath)) {
1025 if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
1026 (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) {
1027 UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath;
1028 //
1029 // UEFI Spec doesn't require the URI to be a NULL-terminated string
1030 // So we allocate a new buffer and always append a '\0' to it.
1031 //
1032 UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);
1033 if (UriStrLength == 0) {
1034 //
1035 // return a NULL UriAddress if it's a empty URI device path node.
1036 //
1037 break;
1038 }
1039 Uri = AllocatePool (UriStrLength + 1);
1040 if (Uri == NULL) {
1041 return EFI_OUT_OF_RESOURCES;
1042 }
1043 CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL));
1044 Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0';
1045
1046 *UriAddress = Uri;
1047 }
1048 TempDevicePath = NextDevicePathNode (TempDevicePath);
1049 }
1050
1051 return EFI_SUCCESS;
1052 }
1053
1054 /**
1055 This function returns the image type according to server replied HTTP message
1056 and also the image's URI info.
1057
1058 @param[in] Uri The pointer to the image's URI string.
1059 @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
1060 @param[in] HeaderCount Number of HTTP header structures in Headers list.
1061 @param[in] Headers Array containing list of HTTP headers.
1062 @param[out] ImageType The image type of the downloaded file.
1063
1064 @retval EFI_SUCCESS The image type is returned in ImageType.
1065 @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
1066 @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
1067 @retval EFI_NOT_FOUND Failed to identify the image type.
1068 @retval Others Unexpect error happened.
1069
1070 **/
1071 EFI_STATUS
HttpBootCheckImageType(IN CHAR8 * Uri,IN VOID * UriParser,IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,OUT HTTP_BOOT_IMAGE_TYPE * ImageType)1072 HttpBootCheckImageType (
1073 IN CHAR8 *Uri,
1074 IN VOID *UriParser,
1075 IN UINTN HeaderCount,
1076 IN EFI_HTTP_HEADER *Headers,
1077 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
1078 )
1079 {
1080 EFI_STATUS Status;
1081 EFI_HTTP_HEADER *Header;
1082 CHAR8 *FilePath;
1083 CHAR8 *FilePost;
1084
1085 if (Uri == NULL || UriParser == NULL || ImageType == NULL) {
1086 return EFI_INVALID_PARAMETER;
1087 }
1088
1089 if (HeaderCount != 0 && Headers == NULL) {
1090 return EFI_INVALID_PARAMETER;
1091 }
1092
1093 //
1094 // Determine the image type by the HTTP Content-Type header field first.
1095 // "application/efi" -> EFI Image
1096 //
1097 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE);
1098 if (Header != NULL) {
1099 if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) {
1100 *ImageType = ImageTypeEfi;
1101 return EFI_SUCCESS;
1102 }
1103 }
1104
1105 //
1106 // Determine the image type by file extension:
1107 // *.efi -> EFI Image
1108 // *.iso -> CD/DVD Image
1109 // *.img -> Virtual Disk Image
1110 //
1111 Status = HttpUrlGetPath (
1112 Uri,
1113 UriParser,
1114 &FilePath
1115 );
1116 if (EFI_ERROR (Status)) {
1117 return Status;
1118 }
1119
1120 FilePost = FilePath + AsciiStrLen (FilePath) - 4;
1121 if (AsciiStrCmp (FilePost, ".efi") == 0) {
1122 *ImageType = ImageTypeEfi;
1123 } else if (AsciiStrCmp (FilePost, ".iso") == 0) {
1124 *ImageType = ImageTypeVirtualCd;
1125 } else if (AsciiStrCmp (FilePost, ".img") == 0) {
1126 *ImageType = ImageTypeVirtualDisk;
1127 } else {
1128 *ImageType = ImageTypeMax;
1129 }
1130
1131 FreePool (FilePath);
1132
1133 return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND;
1134 }
1135
1136 /**
1137 This function register the RAM disk info to the system.
1138
1139 @param[in] Private The pointer to the driver's private data.
1140 @param[in] BufferSize The size of Buffer in bytes.
1141 @param[in] Buffer The base address of the RAM disk.
1142 @param[in] ImageType The image type of the file in Buffer.
1143
1144 @retval EFI_SUCCESS The RAM disk has been registered.
1145 @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
1146 @retval EFI_UNSUPPORTED The ImageType is not supported.
1147 @retval Others Unexpected error happened.
1148
1149 **/
1150 EFI_STATUS
HttpBootRegisterRamDisk(IN HTTP_BOOT_PRIVATE_DATA * Private,IN UINTN BufferSize,IN VOID * Buffer,IN HTTP_BOOT_IMAGE_TYPE ImageType)1151 HttpBootRegisterRamDisk (
1152 IN HTTP_BOOT_PRIVATE_DATA *Private,
1153 IN UINTN BufferSize,
1154 IN VOID *Buffer,
1155 IN HTTP_BOOT_IMAGE_TYPE ImageType
1156 )
1157 {
1158 EFI_RAM_DISK_PROTOCOL *RamDisk;
1159 EFI_STATUS Status;
1160 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1161 EFI_GUID *RamDiskType;
1162
1163 ASSERT (Private != NULL);
1164 ASSERT (Buffer != NULL);
1165 ASSERT (BufferSize != 0);
1166
1167 Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk);
1168 if (EFI_ERROR (Status)) {
1169 DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status));
1170 return Status;
1171 }
1172
1173 if (ImageType == ImageTypeVirtualCd) {
1174 RamDiskType = &gEfiVirtualCdGuid;
1175 } else if (ImageType == ImageTypeVirtualDisk) {
1176 RamDiskType = &gEfiVirtualDiskGuid;
1177 } else {
1178 return EFI_UNSUPPORTED;
1179 }
1180
1181 Status = RamDisk->Register (
1182 (UINTN)Buffer,
1183 (UINT64)BufferSize,
1184 RamDiskType,
1185 Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath,
1186 &DevicePath
1187 );
1188 if (EFI_ERROR (Status)) {
1189 DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status));
1190 }
1191
1192 return Status;
1193 }
1194
1195