1 /** @file
2   Console Platform DXE Driver, install Console Device Guids and update Console
3   Environment Variables.
4 
5 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "ConPlatform.h"
11 
12 
13 EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextInDriverBinding = {
14   ConPlatformTextInDriverBindingSupported,
15   ConPlatformTextInDriverBindingStart,
16   ConPlatformTextInDriverBindingStop,
17   0xa,
18   NULL,
19   NULL
20 };
21 
22 EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextOutDriverBinding = {
23   ConPlatformTextOutDriverBindingSupported,
24   ConPlatformTextOutDriverBindingStart,
25   ConPlatformTextOutDriverBindingStop,
26   0xa,
27   NULL,
28   NULL
29 };
30 
31 /**
32   Entrypoint of this module.
33 
34   This function is the entrypoint of this module. It installs Driver Binding
35   Protocols together with Component Name Protocols.
36 
37   @param  ImageHandle       The firmware allocated handle for the EFI image.
38   @param  SystemTable       A pointer to the EFI System Table.
39 
40   @retval EFI_SUCCESS       The entry point is executed successfully.
41 
42 **/
43 EFI_STATUS
44 EFIAPI
InitializeConPlatform(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)45 InitializeConPlatform(
46   IN EFI_HANDLE           ImageHandle,
47   IN EFI_SYSTEM_TABLE     *SystemTable
48   )
49 {
50   EFI_STATUS              Status;
51 
52   Status = EfiLibInstallDriverBindingComponentName2 (
53              ImageHandle,
54              SystemTable,
55              &gConPlatformTextInDriverBinding,
56              ImageHandle,
57              &gConPlatformComponentName,
58              &gConPlatformComponentName2
59              );
60   ASSERT_EFI_ERROR (Status);
61 
62   Status = EfiLibInstallDriverBindingComponentName2 (
63              ImageHandle,
64              SystemTable,
65              &gConPlatformTextOutDriverBinding,
66              NULL,
67              &gConPlatformComponentName,
68              &gConPlatformComponentName2
69              );
70   ASSERT_EFI_ERROR (Status);
71 
72   return EFI_SUCCESS;
73 }
74 
75 
76 /**
77   Test to see if EFI_SIMPLE_TEXT_INPUT_PROTOCOL is supported on ControllerHandle.
78 
79   @param  This                Protocol instance pointer.
80   @param  ControllerHandle    Handle of device to test.
81   @param  RemainingDevicePath Optional parameter use to pick a specific child
82                               device to start.
83 
84   @retval EFI_SUCCESS         This driver supports this device.
85   @retval other               This driver does not support this device.
86 
87 **/
88 EFI_STATUS
89 EFIAPI
ConPlatformTextInDriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)90 ConPlatformTextInDriverBindingSupported (
91   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
92   IN  EFI_HANDLE                   ControllerHandle,
93   IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
94   )
95 {
96   return ConPlatformDriverBindingSupported (
97            This,
98            ControllerHandle,
99            &gEfiSimpleTextInProtocolGuid
100            );
101 }
102 
103 
104 /**
105   Test to see if EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL is supported on ControllerHandle.
106 
107   @param  This                Protocol instance pointer.
108   @param  ControllerHandle    Handle of device to test.
109   @param  RemainingDevicePath Optional parameter use to pick a specific child
110                               device to start.
111 
112   @retval EFI_SUCCESS         This driver supports this device.
113   @retval other               This driver does not support this device.
114 
115 **/
116 EFI_STATUS
117 EFIAPI
ConPlatformTextOutDriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)118 ConPlatformTextOutDriverBindingSupported (
119   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
120   IN  EFI_HANDLE                   ControllerHandle,
121   IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
122   )
123 {
124   return ConPlatformDriverBindingSupported (
125            This,
126            ControllerHandle,
127            &gEfiSimpleTextOutProtocolGuid
128            );
129 }
130 
131 
132 /**
133   Test to see if the specified protocol is supported on ControllerHandle.
134 
135   @param  This                Protocol instance pointer.
136   @param  ControllerHandle    Handle of device to test.
137   @param  ProtocolGuid        The specfic protocol.
138 
139   @retval EFI_SUCCESS         This driver supports this device.
140   @retval other               This driver does not support this device.
141 
142 **/
143 EFI_STATUS
ConPlatformDriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_GUID * ProtocolGuid)144 ConPlatformDriverBindingSupported (
145   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
146   IN  EFI_HANDLE                   ControllerHandle,
147   IN  EFI_GUID                     *ProtocolGuid
148   )
149 {
150   EFI_STATUS  Status;
151   VOID        *Interface;
152 
153   //
154   // Test to see if this is a physical device by checking if
155   // it has a Device Path Protocol.
156   //
157   Status = gBS->OpenProtocol (
158                   ControllerHandle,
159                   &gEfiDevicePathProtocolGuid,
160                   NULL,
161                   This->DriverBindingHandle,
162                   ControllerHandle,
163                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
164                   );
165   if (EFI_ERROR (Status)) {
166     return Status;
167   }
168   //
169   // Test to see if this device supports the specified Protocol.
170   //
171   Status = gBS->OpenProtocol (
172                   ControllerHandle,
173                   ProtocolGuid,
174                   (VOID **) &Interface,
175                   This->DriverBindingHandle,
176                   ControllerHandle,
177                   EFI_OPEN_PROTOCOL_BY_DRIVER
178                   );
179   if (EFI_ERROR (Status)) {
180     return Status;
181   }
182 
183   gBS->CloseProtocol (
184          ControllerHandle,
185          ProtocolGuid,
186          This->DriverBindingHandle,
187          ControllerHandle
188          );
189 
190   return EFI_SUCCESS;
191 }
192 
193 /**
194   Start this driver on the device for console input.
195 
196   Start this driver on ControllerHandle by opening Simple Text Input Protocol,
197   reading Device Path, and installing Console In Devcice GUID on ControllerHandle.
198 
199   Append its device path into the console environment variables ConInDev.
200 
201   @param  This                 Protocol instance pointer.
202   @param  ControllerHandle     Handle of device to bind driver to
203   @param  RemainingDevicePath  Optional parameter use to pick a specific child
204                                device to start.
205 
206   @retval EFI_SUCCESS          This driver is added to ControllerHandle
207   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
208   @retval other                This driver does not support this device.
209 
210 **/
211 EFI_STATUS
212 EFIAPI
ConPlatformTextInDriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)213 ConPlatformTextInDriverBindingStart (
214   IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
215   IN  EFI_HANDLE                    ControllerHandle,
216   IN  EFI_DEVICE_PATH_PROTOCOL      *RemainingDevicePath
217   )
218 {
219   EFI_STATUS                     Status;
220   EFI_DEVICE_PATH_PROTOCOL       *DevicePath;
221   EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;
222   BOOLEAN                        IsInConInVariable;
223 
224   //
225   // Get the Device Path Protocol so the environment variables can be updated
226   //
227   Status = gBS->OpenProtocol (
228                   ControllerHandle,
229                   &gEfiDevicePathProtocolGuid,
230                   (VOID **) &DevicePath,
231                   This->DriverBindingHandle,
232                   ControllerHandle,
233                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
234                   );
235   if (EFI_ERROR (Status)) {
236     return Status;
237   }
238   //
239   // Open the Simple Text Input Protocol BY_DRIVER
240   //
241   Status = gBS->OpenProtocol (
242                   ControllerHandle,
243                   &gEfiSimpleTextInProtocolGuid,
244                   (VOID **) &TextIn,
245                   This->DriverBindingHandle,
246                   ControllerHandle,
247                   EFI_OPEN_PROTOCOL_BY_DRIVER
248                   );
249   if (EFI_ERROR (Status)) {
250     return Status;
251   }
252   //
253   // Check if the device path is in ConIn Variable
254   //
255   IsInConInVariable = FALSE;
256   Status = ConPlatformUpdateDeviceVariable (
257              L"ConIn",
258              DevicePath,
259              Check
260              );
261   if (!EFI_ERROR (Status)) {
262     IsInConInVariable = TRUE;
263   }
264 
265   //
266   // Append the device path to the ConInDev environment variable
267   //
268   ConPlatformUpdateDeviceVariable (
269     L"ConInDev",
270     DevicePath,
271     Append
272     );
273 
274   //
275   // If the device path is an instance in the ConIn environment variable,
276   // then install EfiConsoleInDeviceGuid onto ControllerHandle
277   //
278   if (IsInConInVariable) {
279     gBS->InstallMultipleProtocolInterfaces (
280            &ControllerHandle,
281            &gEfiConsoleInDeviceGuid,
282            NULL,
283            NULL
284            );
285   } else {
286     gBS->CloseProtocol (
287            ControllerHandle,
288            &gEfiSimpleTextInProtocolGuid,
289            This->DriverBindingHandle,
290            ControllerHandle
291            );
292   }
293 
294   return EFI_SUCCESS;
295 }
296 
297 /**
298   Start this driver on the device for console output and standard error output.
299 
300   Start this driver on ControllerHandle by opening Simple Text Output Protocol,
301   reading Device Path, and installing Console Out Devcic GUID, Standard Error
302   Device GUID on ControllerHandle.
303 
304   Append its device path into the console environment variables ConOutDev, ErrOutDev.
305 
306   @param  This                 Protocol instance pointer.
307   @param  ControllerHandle     Handle of device to bind driver to
308   @param  RemainingDevicePath  Optional parameter use to pick a specific child
309                                device to start.
310 
311   @retval EFI_SUCCESS          This driver is added to ControllerHandle
312   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
313   @retval other                This driver does not support this device
314 
315 **/
316 EFI_STATUS
317 EFIAPI
ConPlatformTextOutDriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)318 ConPlatformTextOutDriverBindingStart (
319   IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
320   IN  EFI_HANDLE                    ControllerHandle,
321   IN  EFI_DEVICE_PATH_PROTOCOL      *RemainingDevicePath
322   )
323 {
324   EFI_STATUS                       Status;
325   EFI_DEVICE_PATH_PROTOCOL         *DevicePath;
326   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *TextOut;
327   BOOLEAN                          NeedClose;
328   BOOLEAN                          IsInConOutVariable;
329   BOOLEAN                          IsInErrOutVariable;
330 
331   NeedClose = TRUE;
332 
333   //
334   // Get the Device Path Protocol so the environment variables can be updated
335   //
336   Status = gBS->OpenProtocol (
337                   ControllerHandle,
338                   &gEfiDevicePathProtocolGuid,
339                   (VOID **) &DevicePath,
340                   This->DriverBindingHandle,
341                   ControllerHandle,
342                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
343                   );
344   if (EFI_ERROR (Status)) {
345     return Status;
346   }
347   //
348   // Open the Simple Text Output Protocol BY_DRIVER
349   //
350   Status = gBS->OpenProtocol (
351                   ControllerHandle,
352                   &gEfiSimpleTextOutProtocolGuid,
353                   (VOID **) &TextOut,
354                   This->DriverBindingHandle,
355                   ControllerHandle,
356                   EFI_OPEN_PROTOCOL_BY_DRIVER
357                   );
358   if (EFI_ERROR (Status)) {
359     return Status;
360   }
361   //
362   // Check if the device path is in ConOut & ErrOut Variable
363   //
364   IsInConOutVariable = FALSE;
365   Status = ConPlatformUpdateDeviceVariable (
366              L"ConOut",
367              DevicePath,
368              Check
369              );
370   if (!EFI_ERROR (Status)) {
371     IsInConOutVariable = TRUE;
372   }
373 
374   IsInErrOutVariable = FALSE;
375   Status = ConPlatformUpdateDeviceVariable (
376              L"ErrOut",
377              DevicePath,
378              Check
379              );
380   if (!EFI_ERROR (Status)) {
381     IsInErrOutVariable = TRUE;
382   }
383 
384   //
385   // Append the device path to the ConOutDev and ErrOutDev environment variable.
386   // For GOP device path, append the sibling device path as well.
387   //
388   if (!ConPlatformUpdateGopCandidate (DevicePath)) {
389     ConPlatformUpdateDeviceVariable (
390       L"ConOutDev",
391       DevicePath,
392       Append
393       );
394     //
395     // Then append the device path to the ErrOutDev environment variable
396     //
397     ConPlatformUpdateDeviceVariable (
398       L"ErrOutDev",
399       DevicePath,
400       Append
401       );
402   }
403 
404   //
405   // If the device path is an instance in the ConOut environment variable,
406   // then install EfiConsoleOutDeviceGuid onto ControllerHandle
407   //
408   if (IsInConOutVariable) {
409     NeedClose = FALSE;
410     Status = gBS->InstallMultipleProtocolInterfaces (
411                     &ControllerHandle,
412                     &gEfiConsoleOutDeviceGuid,
413                     NULL,
414                     NULL
415                     );
416   }
417   //
418   // If the device path is an instance in the ErrOut environment variable,
419   // then install EfiStandardErrorDeviceGuid onto ControllerHandle
420   //
421   if (IsInErrOutVariable) {
422     NeedClose = FALSE;
423     gBS->InstallMultipleProtocolInterfaces (
424            &ControllerHandle,
425            &gEfiStandardErrorDeviceGuid,
426            NULL,
427            NULL
428            );
429   }
430 
431   if (NeedClose) {
432     gBS->CloseProtocol (
433            ControllerHandle,
434            &gEfiSimpleTextOutProtocolGuid,
435            This->DriverBindingHandle,
436            ControllerHandle
437            );
438   }
439 
440   return EFI_SUCCESS;
441 }
442 
443 /**
444   Stop this driver on ControllerHandle by removing Console In Devcice GUID
445   and closing the Simple Text Input protocol on ControllerHandle.
446 
447   @param  This              Protocol instance pointer.
448   @param  ControllerHandle  Handle of device to stop driver on
449   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
450                             children is zero stop the entire bus driver.
451   @param  ChildHandleBuffer List of Child Handles to Stop.
452 
453   @retval EFI_SUCCESS       This driver is removed ControllerHandle
454   @retval other             This driver was not removed from this device
455 
456 **/
457 EFI_STATUS
458 EFIAPI
ConPlatformTextInDriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)459 ConPlatformTextInDriverBindingStop (
460   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
461   IN  EFI_HANDLE                   ControllerHandle,
462   IN  UINTN                        NumberOfChildren,
463   IN  EFI_HANDLE                   *ChildHandleBuffer
464   )
465 {
466   EFI_STATUS                Status;
467   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
468 
469   //
470   // Get the Device Path Protocol firstly
471   //
472   Status = gBS->OpenProtocol (
473                   ControllerHandle,
474                   &gEfiDevicePathProtocolGuid,
475                   (VOID **) &DevicePath,
476                   This->DriverBindingHandle,
477                   ControllerHandle,
478                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
479                   );
480   //
481   // If there is device path on ControllerHandle
482   //
483   if (!EFI_ERROR (Status)) {
484     //
485     // Remove DevicePath from ConInDev if exists.
486     //
487     ConPlatformUpdateDeviceVariable (
488       L"ConInDev",
489       DevicePath,
490       Delete
491       );
492   }
493 
494   //
495   // Uninstall the Console Device GUIDs from Controller Handle
496   //
497   ConPlatformUnInstallProtocol (
498     This,
499     ControllerHandle,
500     &gEfiConsoleInDeviceGuid
501     );
502 
503   //
504   // Close the Simple Text Input Protocol
505   //
506   gBS->CloseProtocol (
507          ControllerHandle,
508          &gEfiSimpleTextInProtocolGuid,
509          This->DriverBindingHandle,
510          ControllerHandle
511          );
512 
513   return EFI_SUCCESS;
514 }
515 
516 
517 /**
518   Stop this driver on ControllerHandle by removing Console Out Devcice GUID
519   and closing the Simple Text Output protocol on ControllerHandle.
520 
521   @param  This              Protocol instance pointer.
522   @param  ControllerHandle  Handle of device to stop driver on
523   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
524                             children is zero stop the entire bus driver.
525   @param  ChildHandleBuffer List of Child Handles to Stop.
526 
527   @retval EFI_SUCCESS       This driver is removed ControllerHandle
528   @retval other             This driver was not removed from this device
529 
530 **/
531 EFI_STATUS
532 EFIAPI
ConPlatformTextOutDriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)533 ConPlatformTextOutDriverBindingStop (
534   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
535   IN  EFI_HANDLE                   ControllerHandle,
536   IN  UINTN                        NumberOfChildren,
537   IN  EFI_HANDLE                   *ChildHandleBuffer
538   )
539 {
540   EFI_STATUS                Status;
541   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
542 
543   //
544   // Get the Device Path Protocol firstly
545   //
546   Status = gBS->OpenProtocol (
547                   ControllerHandle,
548                   &gEfiDevicePathProtocolGuid,
549                   (VOID **) &DevicePath,
550                   This->DriverBindingHandle,
551                   ControllerHandle,
552                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
553                   );
554   if (!EFI_ERROR (Status)) {
555     //
556     // Remove DevicePath from ConOutDev and ErrOutDev if exists.
557     //
558     ConPlatformUpdateDeviceVariable (
559       L"ConOutDev",
560       DevicePath,
561       Delete
562       );
563     ConPlatformUpdateDeviceVariable (
564       L"ErrOutDev",
565       DevicePath,
566       Delete
567       );
568   }
569 
570   //
571   // Uninstall the Console Device GUIDs from Controller Handle
572   //
573   ConPlatformUnInstallProtocol (
574     This,
575     ControllerHandle,
576     &gEfiConsoleOutDeviceGuid
577     );
578 
579   ConPlatformUnInstallProtocol (
580     This,
581     ControllerHandle,
582     &gEfiStandardErrorDeviceGuid
583     );
584 
585   //
586   // Close the Simple Text Output Protocol
587   //
588   gBS->CloseProtocol (
589         ControllerHandle,
590         &gEfiSimpleTextOutProtocolGuid,
591         This->DriverBindingHandle,
592         ControllerHandle
593         );
594 
595   return EFI_SUCCESS;
596 }
597 
598 
599 /**
600   Uninstall the specified protocol.
601 
602   @param This            Protocol instance pointer.
603   @param Handle          Handle of device to uninstall protocol on.
604   @param ProtocolGuid    The specified protocol need to be uninstalled.
605 
606 **/
607 VOID
ConPlatformUnInstallProtocol(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Handle,IN EFI_GUID * ProtocolGuid)608 ConPlatformUnInstallProtocol (
609   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
610   IN  EFI_HANDLE                   Handle,
611   IN  EFI_GUID                     *ProtocolGuid
612   )
613 {
614   EFI_STATUS  Status;
615 
616   Status = gBS->OpenProtocol (
617                   Handle,
618                   ProtocolGuid,
619                   NULL,
620                   This->DriverBindingHandle,
621                   Handle,
622                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
623                   );
624 
625   if (!EFI_ERROR (Status)) {
626     gBS->UninstallMultipleProtocolInterfaces (
627            Handle,
628            ProtocolGuid,
629            NULL,
630            NULL
631            );
632   }
633 
634   return ;
635 }
636 
637 /**
638   Get the necessary size of buffer and read the variable.
639 
640   First get the necessary size of buffer. Then read the
641   EFI variable (Name) and return a dynamically allocated
642   buffer. On failure return NULL.
643 
644   @param  Name             String part of EFI variable name
645 
646   @return Dynamically allocated memory that contains a copy of the EFI variable.
647           Caller is repsoncible freeing the buffer. Return NULL means Variable
648           was not read.
649 
650 **/
651 VOID *
ConPlatformGetVariable(IN CHAR16 * Name)652 ConPlatformGetVariable (
653   IN  CHAR16    *Name
654   )
655 {
656   EFI_STATUS  Status;
657   VOID        *Buffer;
658   UINTN       BufferSize;
659 
660   BufferSize  = 0;
661   Buffer      = NULL;
662 
663   //
664   // Test to see if the variable exists.  If it doesn't, return NULL.
665   //
666   Status = gRT->GetVariable (
667                   Name,
668                   &gEfiGlobalVariableGuid,
669                   NULL,
670                   &BufferSize,
671                   Buffer
672                   );
673 
674   if (Status == EFI_BUFFER_TOO_SMALL) {
675     //
676     // Allocate the buffer to return
677     //
678     Buffer = AllocatePool (BufferSize);
679     if (Buffer == NULL) {
680       return NULL;
681     }
682     //
683     // Read variable into the allocated buffer.
684     //
685     Status = gRT->GetVariable (
686                     Name,
687                     &gEfiGlobalVariableGuid,
688                     NULL,
689                     &BufferSize,
690                     Buffer
691                     );
692     if (EFI_ERROR (Status)) {
693       FreePool (Buffer);
694       //
695       // To make sure Buffer is NULL if any error occurs.
696       //
697       Buffer = NULL;
698     }
699   }
700 
701   return Buffer;
702 }
703 
704 /**
705   Function returns TRUE when the two input device paths point to the two
706   GOP child handles that have the same parent.
707 
708   @param Left    A pointer to a device path data structure.
709   @param Right   A pointer to a device path data structure.
710 
711   @retval TRUE  Left and Right share the same parent.
712   @retval FALSE Left and Right don't share the same parent or either of them is not
713                 a GOP device path.
714 **/
715 BOOLEAN
IsGopSibling(IN EFI_DEVICE_PATH_PROTOCOL * Left,IN EFI_DEVICE_PATH_PROTOCOL * Right)716 IsGopSibling (
717   IN EFI_DEVICE_PATH_PROTOCOL  *Left,
718   IN EFI_DEVICE_PATH_PROTOCOL  *Right
719   )
720 {
721   EFI_DEVICE_PATH_PROTOCOL  *NodeLeft;
722   EFI_DEVICE_PATH_PROTOCOL  *NodeRight;
723 
724   for (NodeLeft = Left; !IsDevicePathEndType (NodeLeft); NodeLeft = NextDevicePathNode (NodeLeft)) {
725     if ((DevicePathType (NodeLeft) == ACPI_DEVICE_PATH && DevicePathSubType (NodeLeft) == ACPI_ADR_DP) ||
726         (DevicePathType (NodeLeft) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeLeft) == HW_CONTROLLER_DP &&
727          DevicePathType (NextDevicePathNode (NodeLeft)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeLeft)) == ACPI_ADR_DP)) {
728       break;
729     }
730   }
731 
732   if (IsDevicePathEndType (NodeLeft)) {
733     return FALSE;
734   }
735 
736   for (NodeRight = Right; !IsDevicePathEndType (NodeRight); NodeRight = NextDevicePathNode (NodeRight)) {
737     if ((DevicePathType (NodeRight) == ACPI_DEVICE_PATH && DevicePathSubType (NodeRight) == ACPI_ADR_DP) ||
738         (DevicePathType (NodeRight) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeRight) == HW_CONTROLLER_DP &&
739          DevicePathType (NextDevicePathNode (NodeRight)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeRight)) == ACPI_ADR_DP)) {
740       break;
741     }
742   }
743 
744   if (IsDevicePathEndType (NodeRight)) {
745     return FALSE;
746   }
747 
748   if (((UINTN) NodeLeft - (UINTN) Left) != ((UINTN) NodeRight - (UINTN) Right)) {
749     return FALSE;
750   }
751 
752   return (BOOLEAN) (CompareMem (Left, Right, (UINTN) NodeLeft - (UINTN) Left) == 0);
753 }
754 
755 /**
756   Check whether a USB device match the specified USB Class device path. This
757   function follows "Load Option Processing" behavior in UEFI specification.
758 
759   @param UsbIo       USB I/O protocol associated with the USB device.
760   @param UsbClass    The USB Class device path to match.
761 
762   @retval TRUE       The USB device match the USB Class device path.
763   @retval FALSE      The USB device does not match the USB Class device path.
764 
765 **/
766 BOOLEAN
MatchUsbClass(IN EFI_USB_IO_PROTOCOL * UsbIo,IN USB_CLASS_DEVICE_PATH * UsbClass)767 MatchUsbClass (
768   IN EFI_USB_IO_PROTOCOL        *UsbIo,
769   IN USB_CLASS_DEVICE_PATH      *UsbClass
770   )
771 {
772   EFI_STATUS                    Status;
773   EFI_USB_DEVICE_DESCRIPTOR     DevDesc;
774   EFI_USB_INTERFACE_DESCRIPTOR  IfDesc;
775   UINT8                         DeviceClass;
776   UINT8                         DeviceSubClass;
777   UINT8                         DeviceProtocol;
778 
779   if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
780       (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){
781     return FALSE;
782   }
783 
784   //
785   // Check Vendor Id and Product Id.
786   //
787   Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
788   if (EFI_ERROR (Status)) {
789     return FALSE;
790   }
791 
792   if ((UsbClass->VendorId != 0xffff) &&
793       (UsbClass->VendorId != DevDesc.IdVendor)) {
794     return FALSE;
795   }
796 
797   if ((UsbClass->ProductId != 0xffff) &&
798       (UsbClass->ProductId != DevDesc.IdProduct)) {
799     return FALSE;
800   }
801 
802   DeviceClass    = DevDesc.DeviceClass;
803   DeviceSubClass = DevDesc.DeviceSubClass;
804   DeviceProtocol = DevDesc.DeviceProtocol;
805   if (DeviceClass == 0) {
806     //
807     // If Class in Device Descriptor is set to 0, use the Class, SubClass and
808     // Protocol in Interface Descriptor instead.
809     //
810     Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
811     if (EFI_ERROR (Status)) {
812       return FALSE;
813     }
814 
815     DeviceClass    = IfDesc.InterfaceClass;
816     DeviceSubClass = IfDesc.InterfaceSubClass;
817     DeviceProtocol = IfDesc.InterfaceProtocol;
818   }
819 
820   //
821   // Check Class, SubClass and Protocol.
822   //
823   if ((UsbClass->DeviceClass != 0xff) &&
824       (UsbClass->DeviceClass != DeviceClass)) {
825     return FALSE;
826   }
827 
828   if ((UsbClass->DeviceSubClass != 0xff) &&
829       (UsbClass->DeviceSubClass != DeviceSubClass)) {
830     return FALSE;
831   }
832 
833   if ((UsbClass->DeviceProtocol != 0xff) &&
834       (UsbClass->DeviceProtocol != DeviceProtocol)) {
835     return FALSE;
836   }
837 
838   return TRUE;
839 }
840 
841 /**
842   Check whether a USB device match the specified USB WWID device path. This
843   function follows "Load Option Processing" behavior in UEFI specification.
844 
845   @param UsbIo       USB I/O protocol associated with the USB device.
846   @param UsbWwid     The USB WWID device path to match.
847 
848   @retval TRUE       The USB device match the USB WWID device path.
849   @retval FALSE      The USB device does not match the USB WWID device path.
850 
851 **/
852 BOOLEAN
MatchUsbWwid(IN EFI_USB_IO_PROTOCOL * UsbIo,IN USB_WWID_DEVICE_PATH * UsbWwid)853 MatchUsbWwid (
854   IN EFI_USB_IO_PROTOCOL        *UsbIo,
855   IN USB_WWID_DEVICE_PATH       *UsbWwid
856   )
857 {
858   EFI_STATUS                   Status;
859   EFI_USB_DEVICE_DESCRIPTOR    DevDesc;
860   EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
861   UINT16                       *LangIdTable;
862   UINT16                       TableSize;
863   UINT16                       Index;
864   CHAR16                       *CompareStr;
865   UINTN                        CompareLen;
866   CHAR16                       *SerialNumberStr;
867   UINTN                        Length;
868 
869   if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
870       (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {
871     return FALSE;
872   }
873 
874   //
875   // Check Vendor Id and Product Id.
876   //
877   Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
878   if (EFI_ERROR (Status)) {
879     return FALSE;
880   }
881   if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
882       (DevDesc.IdProduct != UsbWwid->ProductId)) {
883     return FALSE;
884   }
885 
886   //
887   // Check Interface Number.
888   //
889   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
890   if (EFI_ERROR (Status)) {
891     return FALSE;
892   }
893   if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
894     return FALSE;
895   }
896 
897   //
898   // Check Serial Number.
899   //
900   if (DevDesc.StrSerialNumber == 0) {
901     return FALSE;
902   }
903 
904   //
905   // Get all supported languages.
906   //
907   TableSize = 0;
908   LangIdTable = NULL;
909   Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
910   if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
911     return FALSE;
912   }
913 
914   //
915   // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
916   //
917   CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);
918   CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
919   if (CompareStr[CompareLen - 1] == L'\0') {
920     CompareLen--;
921   }
922 
923   //
924   // Compare serial number in each supported language.
925   //
926   for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
927     SerialNumberStr = NULL;
928     Status = UsbIo->UsbGetStringDescriptor (
929                       UsbIo,
930                       LangIdTable[Index],
931                       DevDesc.StrSerialNumber,
932                       &SerialNumberStr
933                       );
934     if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
935       continue;
936     }
937 
938     Length = StrLen (SerialNumberStr);
939     if ((Length >= CompareLen) &&
940         (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {
941       FreePool (SerialNumberStr);
942       return TRUE;
943     }
944 
945     FreePool (SerialNumberStr);
946   }
947 
948   return FALSE;
949 }
950 
951 /**
952   Compare whether a full console device path matches a USB shortform device path.
953 
954   @param[in] FullPath      Full console device path.
955   @param[in] ShortformPath Short-form device path. Short-form device node may in the beginning or in the middle.
956 
957   @retval TRUE  The full console device path matches the short-form device path.
958   @retval FALSE The full console device path doesn't match the short-form device path.
959 **/
960 BOOLEAN
MatchUsbShortformDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * FullPath,IN EFI_DEVICE_PATH_PROTOCOL * ShortformPath)961 MatchUsbShortformDevicePath (
962   IN EFI_DEVICE_PATH_PROTOCOL  *FullPath,
963   IN EFI_DEVICE_PATH_PROTOCOL  *ShortformPath
964   )
965 {
966   EFI_STATUS                Status;
967   EFI_DEVICE_PATH_PROTOCOL  *ShortformNode;
968   UINTN                     ParentDevicePathSize;
969   EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath;
970   EFI_USB_IO_PROTOCOL       *UsbIo;
971   EFI_HANDLE                Handle;
972 
973   for ( ShortformNode = ShortformPath
974       ; !IsDevicePathEnd (ShortformNode)
975       ; ShortformNode = NextDevicePathNode (ShortformNode)
976       ) {
977     if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) &&
978         ((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) ||
979          (DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP))
980         ) {
981       break;
982     }
983   }
984 
985   //
986   // Skip further compare when it's not a shortform device path.
987   //
988   if (IsDevicePathEnd (ShortformNode)) {
989     return FALSE;
990   }
991 
992   //
993   // Compare the parent device path when the ShortformPath doesn't start with short-form node.
994   //
995   ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) ShortformPath;
996   RemainingDevicePath  = FullPath;
997   Status               = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &RemainingDevicePath, &Handle);
998   if (EFI_ERROR (Status)) {
999     return FALSE;
1000   }
1001   if (ParentDevicePathSize != 0) {
1002     if ((ParentDevicePathSize > (UINTN) RemainingDevicePath - (UINTN) FullPath) ||
1003         (CompareMem (FullPath, ShortformPath, ParentDevicePathSize) != 0)) {
1004       return FALSE;
1005     }
1006   }
1007 
1008   //
1009   // Compar the USB layer.
1010   //
1011   Status = gBS->HandleProtocol(
1012                   Handle,
1013                   &gEfiUsbIoProtocolGuid,
1014                   (VOID **) &UsbIo
1015                   );
1016   ASSERT_EFI_ERROR (Status);
1017 
1018   return MatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)ShortformNode) ||
1019          MatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)ShortformNode);
1020 }
1021 
1022 /**
1023   Function compares a device path data structure to that of all the nodes of a
1024   second device path instance.
1025 
1026   @param Multi           A pointer to a multi-instance device path data structure.
1027   @param Single          A pointer to a single-instance device path data structure.
1028   @param NewDevicePath   If Delete is TRUE, this parameter must not be null, and it
1029                          points to the remaining device path data structure.
1030                          (remaining device path = Multi - Single.)
1031   @param Delete          If TRUE, means removing Single from Multi.
1032                          If FALSE, the routine just check whether Single matches
1033                          with any instance in Multi.
1034 
1035   @retval EFI_SUCCESS           If the Single is contained within Multi.
1036   @retval EFI_NOT_FOUND         If the Single is not contained within Multi.
1037   @retval EFI_INVALID_PARAMETER Multi is NULL.
1038   @retval EFI_INVALID_PARAMETER Single is NULL.
1039   @retval EFI_INVALID_PARAMETER NewDevicePath is NULL when Delete is TRUE.
1040 
1041 **/
1042 EFI_STATUS
ConPlatformMatchDevicePaths(IN EFI_DEVICE_PATH_PROTOCOL * Multi,IN EFI_DEVICE_PATH_PROTOCOL * Single,OUT EFI_DEVICE_PATH_PROTOCOL ** NewDevicePath OPTIONAL,IN BOOLEAN Delete)1043 ConPlatformMatchDevicePaths (
1044   IN  EFI_DEVICE_PATH_PROTOCOL  *Multi,
1045   IN  EFI_DEVICE_PATH_PROTOCOL  *Single,
1046   OUT EFI_DEVICE_PATH_PROTOCOL  **NewDevicePath OPTIONAL,
1047   IN  BOOLEAN                   Delete
1048   )
1049 {
1050   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
1051   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath1;
1052   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath2;
1053   EFI_DEVICE_PATH_PROTOCOL  *DevicePathInst;
1054   UINTN                     Size;
1055 
1056   //
1057   // The passed in DevicePath should not be NULL
1058   //
1059   if ((Multi == NULL) || (Single == NULL)) {
1060     return EFI_INVALID_PARAMETER;
1061   }
1062 
1063   //
1064   // If performing Delete operation, the NewDevicePath must not be NULL.
1065   //
1066   if (Delete) {
1067     if (NewDevicePath == NULL) {
1068       return EFI_INVALID_PARAMETER;
1069     }
1070   }
1071 
1072   TempDevicePath1 = NULL;
1073 
1074   DevicePath      = Multi;
1075   DevicePathInst  = GetNextDevicePathInstance (&DevicePath, &Size);
1076 
1077   //
1078   // Search for the match of 'Single' in 'Multi'
1079   //
1080   while (DevicePathInst != NULL) {
1081     if ((CompareMem (Single, DevicePathInst, Size) == 0) ||
1082       IsGopSibling (Single, DevicePathInst) || MatchUsbShortformDevicePath (Single, DevicePathInst)) {
1083       if (!Delete) {
1084         //
1085         // If Delete is FALSE, return EFI_SUCCESS if Single is found in Multi.
1086         //
1087         FreePool (DevicePathInst);
1088         return EFI_SUCCESS;
1089       }
1090     } else {
1091       if (Delete) {
1092         //
1093         // If the node of Multi does not match Single, then added it back to the result.
1094         // That is, the node matching Single will be dropped and deleted from result.
1095         //
1096         TempDevicePath2 = AppendDevicePathInstance (
1097                             TempDevicePath1,
1098                             DevicePathInst
1099                             );
1100         if (TempDevicePath1 != NULL) {
1101           FreePool (TempDevicePath1);
1102         }
1103         TempDevicePath1 = TempDevicePath2;
1104       }
1105     }
1106 
1107     FreePool (DevicePathInst);
1108     DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
1109   }
1110 
1111   if (Delete) {
1112     //
1113     // Return the new device path data structure with specified node deleted.
1114     //
1115     *NewDevicePath = TempDevicePath1;
1116     return EFI_SUCCESS;
1117   }
1118 
1119   return EFI_NOT_FOUND;
1120 }
1121 
1122 /**
1123   Update console environment variables.
1124 
1125   @param  VariableName    Console environment variables, ConOutDev, ConInDev
1126                           ErrOutDev, ConIn ,ConOut or ErrOut.
1127   @param  DevicePath      Console devcie's device path.
1128   @param  Operation       Variable operations, including APPEND, CHECK and DELETE.
1129 
1130   @retval EFI_SUCCESS           Variable operates successfully.
1131   @retval EFI_OUT_OF_RESOURCES  If variable cannot be appended.
1132   @retval other                 Variable updating failed.
1133 
1134 **/
1135 EFI_STATUS
ConPlatformUpdateDeviceVariable(IN CHAR16 * VariableName,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN CONPLATFORM_VAR_OPERATION Operation)1136 ConPlatformUpdateDeviceVariable (
1137   IN  CHAR16                    *VariableName,
1138   IN  EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
1139   IN  CONPLATFORM_VAR_OPERATION Operation
1140   )
1141 {
1142   EFI_STATUS                Status;
1143   EFI_DEVICE_PATH_PROTOCOL  *VariableDevicePath;
1144   EFI_DEVICE_PATH_PROTOCOL  *NewVariableDevicePath;
1145 
1146   VariableDevicePath    = NULL;
1147   NewVariableDevicePath = NULL;
1148 
1149   //
1150   // Get Variable according to variable name.
1151   // The memory for Variable is allocated within ConPlatformGetVarible(),
1152   // it is the caller's responsibility to free the memory before return.
1153   //
1154   VariableDevicePath = ConPlatformGetVariable (VariableName);
1155 
1156   if (Operation != Delete) {
1157     //
1158     // Match specified DevicePath in Console Variable.
1159     //
1160     Status = ConPlatformMatchDevicePaths (
1161                VariableDevicePath,
1162                DevicePath,
1163                NULL,
1164                FALSE
1165                );
1166 
1167     if ((Operation == Check) || (!EFI_ERROR (Status))) {
1168       //
1169       // Branch here includes 2 cases:
1170       // 1. Operation is CHECK, simply return Status.
1171       // 2. Operation is APPEND, and device path already exists in variable, also return.
1172       //
1173       if (VariableDevicePath != NULL) {
1174         FreePool (VariableDevicePath);
1175       }
1176 
1177       return Status;
1178     }
1179     //
1180     // We reach here to append a device path that does not exist in variable.
1181     //
1182     Status = EFI_SUCCESS;
1183     NewVariableDevicePath = AppendDevicePathInstance (
1184                               VariableDevicePath,
1185                               DevicePath
1186                               );
1187     if (NewVariableDevicePath == NULL) {
1188       Status = EFI_OUT_OF_RESOURCES;
1189     }
1190 
1191   } else {
1192     //
1193     // We reach here to remove DevicePath from the environment variable that
1194     // is a multi-instance device path.
1195     //
1196     Status = ConPlatformMatchDevicePaths (
1197                VariableDevicePath,
1198                DevicePath,
1199                &NewVariableDevicePath,
1200                TRUE
1201                );
1202   }
1203 
1204   if (VariableDevicePath != NULL) {
1205     FreePool (VariableDevicePath);
1206   }
1207 
1208   if (EFI_ERROR (Status)) {
1209     return Status;
1210   }
1211 
1212   if (NewVariableDevicePath != NULL) {
1213     //
1214     // Update Console Environment Variable.
1215     //
1216     Status = gRT->SetVariable (
1217                     VariableName,
1218                     &gEfiGlobalVariableGuid,
1219                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1220                     GetDevicePathSize (NewVariableDevicePath),
1221                     NewVariableDevicePath
1222                     );
1223 
1224     FreePool (NewVariableDevicePath);
1225   }
1226 
1227   return Status;
1228 }
1229 
1230 /**
1231   Update ConOutDev and ErrOutDev variables to add the device path of
1232   GOP controller itself and the sibling controllers.
1233 
1234   @param  DevicePath            Pointer to device's device path.
1235 
1236   @retval TRUE                  The devcie is a GOP device.
1237   @retval FALSE                 The devcie is not a GOP device.
1238 
1239 **/
1240 BOOLEAN
ConPlatformUpdateGopCandidate(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)1241 ConPlatformUpdateGopCandidate (
1242   IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath
1243   )
1244 {
1245   EFI_STATUS                           Status;
1246   EFI_HANDLE                           PciHandle;
1247   EFI_HANDLE                           GopHandle;
1248   EFI_DEVICE_PATH_PROTOCOL             *TempDevicePath;
1249 
1250   //
1251   // Check whether it's a GOP device.
1252   //
1253   TempDevicePath = DevicePath;
1254   Status = gBS->LocateDevicePath (&gEfiGraphicsOutputProtocolGuid, &TempDevicePath, &GopHandle);
1255   if (EFI_ERROR (Status)) {
1256     return FALSE;
1257   }
1258   //
1259   // Get the parent PciIo handle in order to find all the children
1260   //
1261   Status = gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &DevicePath, &PciHandle);
1262   if (EFI_ERROR (Status)) {
1263     return FALSE;
1264   }
1265   TempDevicePath = EfiBootManagerGetGopDevicePath (PciHandle);
1266   if (TempDevicePath != NULL) {
1267     ConPlatformUpdateDeviceVariable (L"ConOutDev", TempDevicePath, Append);
1268     ConPlatformUpdateDeviceVariable (L"ErrOutDev", TempDevicePath, Append);
1269   }
1270   return TRUE;
1271 }
1272