OpenCorePkg/Library/OcConsoleLib/GopPassThrough.c

374 lines
12 KiB
C

/** @file
Copyright (C) 2020, vit9696. All rights reserved.
Copyright (C) 2021, PMheart. All rights reserved.
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "OcConsoleLibInternal.h"
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/OcMiscLib.h>
STATIC
EFI_STATUS
EFIAPI
OcGopDrawQueryMode (
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
IN UINT32 ModeNumber,
OUT UINTN *SizeOfInfo,
OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
)
{
if (ModeNumber != 0) {
DEBUG ((DEBUG_VERBOSE, "OCC: OcGopDrawQueryMode invalid ModeNumber - %u\n", ModeNumber));
return EFI_INVALID_PARAMETER;
}
if ((SizeOfInfo == NULL) || (Info == NULL)) {
DEBUG ((DEBUG_VERBOSE, "OCC: OcGopDrawQueryMode got invalid parameter SizeOfInfo or Info!\n"));
return EFI_INVALID_PARAMETER;
}
*SizeOfInfo = This->Mode->SizeOfInfo;
*Info = AllocateCopyPool (This->Mode->SizeOfInfo, This->Mode->Info);
if (*Info == NULL) {
DEBUG ((DEBUG_VERBOSE, "OCC: OcGopDrawQueryMode failed to allocate memory for GOP Info!\n"));
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
OcGopDrawSetMode (
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
IN UINT32 ModeNumber
)
{
if (ModeNumber != 0) {
DEBUG ((DEBUG_VERBOSE, "OCC: OcGopDrawSetMode unsupported ModeNumber - %u\n", ModeNumber));
return EFI_UNSUPPORTED;
}
//
// Assuming 0 is the only mode that is accepted, which is already set.
//
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
OcGopDrawBlt (
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL,
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
IN UINTN SourceX,
IN UINTN SourceY,
IN UINTN DestinationX,
IN UINTN DestinationY,
IN UINTN Width,
IN UINTN Height,
IN UINTN Delta OPTIONAL
)
{
OC_GOP_PROTOCOL *OcGopDraw;
OcGopDraw = BASE_CR (This, OC_GOP_PROTOCOL, GraphicsOutput);
return OcGopDraw->Uga->Blt (
OcGopDraw->Uga,
(EFI_UGA_PIXEL *)BltBuffer,
(EFI_UGA_BLT_OPERATION)BltOperation,
SourceX,
SourceY,
DestinationX,
DestinationY,
Width,
Height,
Delta
);
}
EFI_STATUS
OcProvideGopPassThrough (
IN BOOLEAN ForAll
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_UGA_DRAW_PROTOCOL *UgaDraw;
OC_GOP_PROTOCOL *OcGopDraw;
APPLE_FRAMEBUFFER_INFO_PROTOCOL *FramebufferInfo;
EFI_PHYSICAL_ADDRESS FramebufferBase;
UINT32 FramebufferSize;
UINT32 ScreenRowBytes;
UINT32 ScreenWidth;
UINT32 ScreenHeight;
UINT32 ScreenDepth;
UINT32 HorizontalResolution;
UINT32 VerticalResolution;
EFI_GRAPHICS_PIXEL_FORMAT PixelFormat;
UINT32 ColorDepth;
UINT32 RefreshRate;
BOOLEAN HasAppleFramebuffer;
//
// We should not proxy UGA when there is no AppleFramebuffer,
// but on systems where there is nothing, it is the only option.
// REF: https://github.com/acidanthera/bugtracker/issues/1498
//
Status = gBS->LocateProtocol (
&gAppleFramebufferInfoProtocolGuid,
NULL,
(VOID *)&FramebufferInfo
);
HasAppleFramebuffer = !EFI_ERROR (Status);
DEBUG_CODE_BEGIN ();
HandleCount = (UINT32)OcCountProtocolInstances (&gEfiGraphicsOutputProtocolGuid);
DEBUG ((DEBUG_INFO, "OCC: Found %u handles with GOP draw\n", HandleCount));
HandleCount = (UINT32)OcCountProtocolInstances (&gAppleFramebufferInfoProtocolGuid);
DEBUG ((DEBUG_INFO, "OCC: Found %u handles with Apple Framebuffer info\n", HandleCount));
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCC: Failed to locate AppleFramebufferInfo protocol - %r\n", Status));
} else {
Status = FramebufferInfo->GetInfo (
FramebufferInfo,
&FramebufferBase,
&FramebufferSize,
&ScreenRowBytes,
&ScreenWidth,
&ScreenHeight,
&ScreenDepth
);
if (!EFI_ERROR (Status)) {
DEBUG ((
DEBUG_INFO,
"OCC: AppleFramebufferInfo - Got Base %Lx, Size %u, RowBytes %u, Width %u, Height %u, Depth %u\n",
FramebufferBase,
FramebufferSize,
ScreenRowBytes,
ScreenWidth,
ScreenHeight,
ScreenDepth
));
} else {
DEBUG ((DEBUG_INFO, "OCC: AppleFramebufferInfo failed to retrieve info - %r\n", Status));
}
}
DEBUG_CODE_END ();
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiUgaDrawProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCC: Failed to find handles with UGA - %r\n", Status));
return Status;
}
DEBUG ((DEBUG_INFO, "OCC: Found %u handles with UGA for GOP check\n", (UINT32)HandleCount));
for (Index = 0; Index < HandleCount; ++Index) {
DEBUG ((DEBUG_INFO, "OCC: Trying handle %u - %p\n", (UINT32)Index, HandleBuffer[Index]));
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiUgaDrawProtocolGuid,
(VOID **)&UgaDraw
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCC: No UGA protocol - %r\n", Status));
continue;
}
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiGraphicsOutputProtocolGuid,
(VOID **)&GraphicsOutput
);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCC: Skipping GOP proxying as it is already present on handle %u - %p\n", (UINT32)Index, HandleBuffer[Index]));
continue;
}
FramebufferBase = 0;
FramebufferSize = 0;
ScreenRowBytes = 0;
PixelFormat = PixelBltOnly;
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gAppleFramebufferInfoProtocolGuid,
(VOID **)&FramebufferInfo
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_INFO,
"OCC: Failed to retrieve AppleFramebufferInfo protocol on handle %u - %p (%r)\n",
(UINT32)Index,
HandleBuffer[Index],
Status
));
if (HasAppleFramebuffer || !ForAll) {
continue;
}
} else {
DEBUG ((
DEBUG_INFO,
"OCC: Got AppleFramebufferInfo protocol on handle %u - %p\n",
(UINT32)Index,
HandleBuffer[Index]
));
Status = FramebufferInfo->GetInfo (
FramebufferInfo,
&FramebufferBase,
&FramebufferSize,
&ScreenRowBytes,
&ScreenWidth,
&ScreenHeight,
&ScreenDepth
);
if (!EFI_ERROR (Status)) {
PixelFormat = PixelRedGreenBlueReserved8BitPerColor; ///< or PixelBlueGreenRedReserved8BitPerColor?
DEBUG ((
DEBUG_INFO,
"OCC: AppleFramebufferInfo - Got Base %Lx, Size %u, RowBytes %u, Width %u, Height %u, Depth %u on handle %u - %p\n",
FramebufferBase,
FramebufferSize,
ScreenRowBytes,
ScreenWidth,
ScreenHeight,
ScreenDepth,
(UINT32)Index,
HandleBuffer[Index]
));
} else {
DEBUG ((
DEBUG_INFO,
"OCC: Failed to get info from AppleFramebufferInfo protocol on handle %u - %p (%r)\n",
(UINT32)Index,
HandleBuffer[Index],
Status
));
if (HasAppleFramebuffer) {
continue;
}
}
}
Status = UgaDraw->GetMode (
UgaDraw,
&HorizontalResolution,
&VerticalResolution,
&ColorDepth,
&RefreshRate
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCC: UGA->GetMode returns error - %r\n", Status));
continue;
}
OcGopDraw = AllocateZeroPool (sizeof (*OcGopDraw));
if (OcGopDraw == NULL) {
DEBUG ((DEBUG_INFO, "OCC: Failed to allocate GOP protocol\n"));
continue;
}
OcGopDraw->Uga = UgaDraw;
OcGopDraw->GraphicsOutput.QueryMode = OcGopDrawQueryMode;
OcGopDraw->GraphicsOutput.SetMode = OcGopDrawSetMode;
OcGopDraw->GraphicsOutput.Blt = OcGopDrawBlt;
OcGopDraw->GraphicsOutput.Mode = AllocateZeroPool (sizeof (*OcGopDraw->GraphicsOutput.Mode));
if (OcGopDraw->GraphicsOutput.Mode == NULL) {
FreePool (OcGopDraw);
continue;
}
//
// Only Mode 0 is supported, so there is only one mode supported in total.
//
OcGopDraw->GraphicsOutput.Mode->MaxMode = 1;
//
// Again, only Mode 0 is supported.
//
OcGopDraw->GraphicsOutput.Mode->Mode = 0;
OcGopDraw->GraphicsOutput.Mode->Info = AllocateZeroPool (sizeof (*OcGopDraw->GraphicsOutput.Mode->Info));
if (OcGopDraw->GraphicsOutput.Mode->Info == NULL) {
FreePool (OcGopDraw->GraphicsOutput.Mode);
FreePool (OcGopDraw);
continue;
}
OcGopDraw->GraphicsOutput.Mode->Info->Version = 0;
OcGopDraw->GraphicsOutput.Mode->Info->HorizontalResolution = HorizontalResolution;
OcGopDraw->GraphicsOutput.Mode->Info->VerticalResolution = VerticalResolution;
OcGopDraw->GraphicsOutput.Mode->Info->PixelFormat = PixelFormat;
//
// No pixel mask is needed (i.e. all zero) in PixelInformation,
// plus AllocateZeroPool already assigns zero for it.
// Skip.
// ------------------------------------------------------------------------------
// ScreenRowBytes is PixelsPerScanLine * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
// so here to divide it back.
//
OcGopDraw->GraphicsOutput.Mode->Info->PixelsPerScanLine = ScreenRowBytes / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
OcGopDraw->GraphicsOutput.Mode->SizeOfInfo = sizeof (*OcGopDraw->GraphicsOutput.Mode->Info);
OcGopDraw->GraphicsOutput.Mode->FrameBufferBase = FramebufferBase;
OcGopDraw->GraphicsOutput.Mode->FrameBufferSize = FramebufferSize;
Status = gBS->InstallMultipleProtocolInterfaces (
&HandleBuffer[Index],
&gEfiGraphicsOutputProtocolGuid,
&OcGopDraw->GraphicsOutput,
NULL
);
if (EFI_ERROR (Status)) {
FreePool (OcGopDraw->GraphicsOutput.Mode->Info);
FreePool (OcGopDraw->GraphicsOutput.Mode);
FreePool (OcGopDraw);
}
DEBUG ((
DEBUG_INFO,
"OCC: Installed GOP protocol - %r (Handle %u - %p, Resolution %ux%u, FramebufferBase %Lx, PixelFormat %d)\n",
Status,
(UINT32)Index,
HandleBuffer[Index],
HorizontalResolution,
VerticalResolution,
FramebufferBase,
PixelFormat
));
}
FreePool (HandleBuffer);
return Status;
}