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