diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe')
10 files changed, 4294 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/ComponentName.c new file mode 100644 index 00000000..a3a64c94 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/ComponentName.c @@ -0,0 +1,247 @@ +/* $Id: ComponentName.c $ */ +/** @file + * ComponentName.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Copyright (c) 2006, Intel Corporation + 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 "VBoxVga.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gVBoxVgaComponentName = { + VBoxVgaComponentNameGetDriverName, + VBoxVgaComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gVBoxVgaComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) VBoxVgaComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) VBoxVgaComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mVBoxVgaDriverNameTable[] = { + { "eng;en", L"VirtualBox SVGA Driver" }, + { NULL , NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mVBoxVgaControllerNameTable[] = { + { "eng;en", L"VirtualBox SVGA PCI Adapter" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VBoxVgaComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mVBoxVgaDriverNameTable, + DriverName, + (BOOLEAN)(This == &gVBoxVgaComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VBoxVgaComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gVBoxVgaDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the Cirrus Logic 5430's Device structure + // + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mVBoxVgaControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gVBoxVgaComponentName) + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/DriverSupportedEfiVersion.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/DriverSupportedEfiVersion.c new file mode 100644 index 00000000..737e68ff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/DriverSupportedEfiVersion.c @@ -0,0 +1,58 @@ +/* $Id: DriverSupportedEfiVersion.c $ */ +/** @file + * DriverSupportedEfiVersion.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Copyright (c) 2007, Intel Corporation + 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. + + Module Name: DriverSupportEfiVersion.c + +*/ +#include "VBoxVga.h" + +EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gVBoxVgaDriverSupportedEfiVersion = { + sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure. + 0 // Version number to be filled at start up. +}; + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/Edid.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/Edid.c new file mode 100644 index 00000000..4c3c73c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/Edid.c @@ -0,0 +1,676 @@ +/* $Id: Edid.c $ */ +/** @file + * Edid.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Read EDID information and parse EDID information. + + Copyright (c) 2008, Intel Corporation + 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 "VBoxVga.h" +#include "VBoxVgaI2c.h" + +// +// EDID block +// +typedef struct { + UINT8 Header[8]; //EDID header "00 FF FF FF FF FF FF 00" + UINT16 ManufactureName; //EISA 3-character ID + UINT16 ProductCode; //Vendor assigned code + UINT32 SerialNumber; //32-bit serial number + UINT8 WeekOfManufacture; //Week number + UINT8 YearOfManufacture; //Year + UINT8 EdidVersion; //EDID Structure Version + UINT8 EdidRevision; //EDID Structure Revision + UINT8 VideoInputDefinition; + UINT8 MaxHorizontalImageSize; //cm + UINT8 MaxVerticalImageSize; //cm + UINT8 DisplayTransferCharacteristic; + UINT8 FeatureSupport; + UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0 + UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0 + UINT8 RedX; //Red-x Bits 9 - 2 + UINT8 RedY; //Red-y Bits 9 - 2 + UINT8 GreenX; //Green-x Bits 9 - 2 + UINT8 GreenY; //Green-y Bits 9 - 2 + UINT8 BlueX; //Blue-x Bits 9 - 2 + UINT8 BlueY; //Blue-y Bits 9 - 2 + UINT8 WhiteX; //White-x Bits 9 - 2 + UINT8 WhiteY; //White-x Bits 9 - 2 + UINT8 EstablishedTimings[3]; + UINT8 StandardTimingIdentification[16]; + UINT8 DetailedTimingDescriptions[72]; + UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow + UINT8 Checksum; +} EDID_BLOCK; + +#define EDID_BLOCK_SIZE 128 +#define VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17 + +typedef struct { + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT16 RefreshRate; +} EDID_TIMING; + +typedef struct { + UINT32 ValidNumber; + UINT32 Key[VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER]; +} VALID_EDID_TIMING; + +// +// Standard timing defined by VESA EDID +// +EDID_TIMING mVbeEstablishedEdidTiming[] = { + // + // Established Timing I + // + {800, 600, 60}, + {800, 600, 56}, + {640, 480, 75}, + {640, 480, 72}, + {640, 480, 67}, + {640, 480, 60}, + {720, 400, 88}, + {720, 400, 70}, + // + // Established Timing II + // + {1280, 1024, 75}, + {1024, 768, 75}, + {1024, 768, 70}, + {1024, 768, 60}, + {1024, 768, 87}, + {832, 624, 75}, + {800, 600, 75}, + {800, 600, 72}, + // + // Established Timing III + // + {1152, 870, 75} +}; + +/** + Read EDID information from I2C Bus on CirrusLogic. + + @param Private Pointer to VBOX_VGA_PRIVATE_DATA. + @param EdidDataBlock Pointer to EDID data block. + @param EdidSize Returned EDID block size. + + @retval EFI_UNSUPPORTED + @retval EFI_SUCCESS + +**/ +EFI_STATUS +ReadEdidData ( + VBOX_VGA_PRIVATE_DATA *Private, + UINT8 **EdidDataBlock, + UINTN *EdidSize + ) +{ + UINTN Index; + UINT8 EdidData[EDID_BLOCK_SIZE * 2]; + UINT8 *ValidEdid; + UINT64 Signature; + + for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++) { + I2cReadByte (Private->PciIo, 0xa0, (UINT8)Index, &EdidData[Index]); + } + + // + // Search for the EDID signature + // + ValidEdid = &EdidData[0]; + Signature = 0x00ffffffffffff00ull; + for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++, ValidEdid ++) { + if (CompareMem (ValidEdid, &Signature, 8) == 0) { + break; + } + } + + if (Index == 256) { + // + // No EDID signature found + // + return EFI_UNSUPPORTED; + } + + *EdidDataBlock = AllocateCopyPool ( + sizeof (EDID_BLOCK_SIZE), + ValidEdid + ); + if (*EdidDataBlock == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Currently only support EDID 1.x + // + *EdidSize = EDID_BLOCK_SIZE; + + return EFI_SUCCESS; +} + +/** + Generate a search key for a specified timing data. + + @param EdidTiming Pointer to EDID timing + + @return The 32 bit unique key for search. + +**/ +UINT32 +CalculateEdidKey ( + EDID_TIMING *EdidTiming + ) +{ + UINT32 Key; + + // + // Be sure no conflicts for all standard timing defined by VESA. + // + Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution; + return Key; +} + +/** + Search a specified Timing in all the valid EDID timings. + + @param ValidEdidTiming All valid EDID timing information. + @param EdidTiming The Timing to search for. + + @retval TRUE Found. + @retval FALSE Not found. + +**/ +BOOLEAN +SearchEdidTiming ( + VALID_EDID_TIMING *ValidEdidTiming, + EDID_TIMING *EdidTiming + ) +{ + UINT32 Index; + UINT32 Key; + + Key = CalculateEdidKey (EdidTiming); + + for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) { + if (Key == ValidEdidTiming->Key[Index]) { + return TRUE; + } + } + + return FALSE; +} + +/** + Parse the Established Timing and Standard Timing in EDID data block. + + @param EdidBuffer Pointer to EDID data block + @param ValidEdidTiming Valid EDID timing information + + @retval TRUE The EDID data is valid. + @retval FALSE The EDID data is invalid. + +**/ +BOOLEAN +ParseEdidData ( + UINT8 *EdidBuffer, + VALID_EDID_TIMING *ValidEdidTiming + ) +{ + UINT8 CheckSum; + UINT32 Index; + UINT32 ValidNumber; + UINT32 TimingBits; + UINT8 *BufferIndex; + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT8 AspectRatio; + UINT8 RefreshRate; + EDID_TIMING TempTiming; + EDID_BLOCK *EdidDataBlock; + + EdidDataBlock = (EDID_BLOCK *) EdidBuffer; + + // + // Check the checksum of EDID data + // + CheckSum = 0; + for (Index = 0; Index < EDID_BLOCK_SIZE; Index ++) { + CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]); + } + if (CheckSum != 0) { + return FALSE; + } + + ValidNumber = 0; + SetMem (ValidEdidTiming, sizeof (VALID_EDID_TIMING), 0); + + if ((EdidDataBlock->EstablishedTimings[0] != 0) || + (EdidDataBlock->EstablishedTimings[1] != 0) || + (EdidDataBlock->EstablishedTimings[2] != 0) + ) { + // + // Established timing data + // + TimingBits = EdidDataBlock->EstablishedTimings[0] | + (EdidDataBlock->EstablishedTimings[1] << 8) | + ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ; + for (Index = 0; Index < VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) { + if (TimingBits & 0x1) { + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mVbeEstablishedEdidTiming[Index]); + ValidNumber ++; + } + TimingBits = TimingBits >> 1; + } + } else { + // + // If no Established timing data, read the standard timing data + // + BufferIndex = &EdidDataBlock->StandardTimingIdentification[0]; + for (Index = 0; Index < 8; Index ++) { + if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){ + // + // A valid Standard Timing + // + HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248); + AspectRatio = (UINT8) (BufferIndex[1] >> 6); + switch (AspectRatio) { + case 0: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10); + break; + case 1: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + case 2: + VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4); + break; + case 3: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9); + break; + default: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + } + RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60); + TempTiming.HorizontalResolution = HorizontalResolution; + TempTiming.VerticalResolution = VerticalResolution; + TempTiming.RefreshRate = RefreshRate; + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming); + ValidNumber ++; + } + BufferIndex += 2; + } + } + + ValidEdidTiming->ValidNumber = ValidNumber; + return TRUE; +} + +static uint16_t in_word(uint16_t port, uint16_t addr) +{ + ASMOutU16(port, addr); + return ASMInU16(port); +} + +static EFI_STATUS VBoxVgaVideoModeInitExtra(void) +{ + UINT16 w, cur_info_ofs, vmode, xres, yres; + UINTN Index; + VBOX_VGA_VIDEO_MODES *VideoMode; + + // Read and check the VBE Extra Data magic + w = in_word(VBE_EXTRA_PORT, 0); + if (w != VBEHEADER_MAGIC) { + DEBUG((DEBUG_INFO, "%a:%d could not find VBE magic, got %x\n", __FILE__, __LINE__, w)); + return EFI_NOT_FOUND; + } + + cur_info_ofs = sizeof(VBEHeader); + + Index = VBoxVgaVideoModeCount - 16; + VideoMode = &VBoxVgaVideoModes[Index]; + vmode = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, mode)); + while (vmode != VBE_VESA_MODE_END_OF_LIST) + { + xres = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, info.XResolution)); + yres = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, info.YResolution)); + + if (vmode >= VBE_VBOX_MODE_CUSTOM1 && vmode <= VBE_VBOX_MODE_CUSTOM16 && xres && yres && Index < VBoxVgaVideoModeCount) { + VideoMode->Width = xres; + VideoMode->Height = yres; + VideoMode->ColorDepth = 32; + VideoMode->RefreshRate = 60; + VideoMode->MiscSetting = 0x01; + VideoMode++; + Index++; + } + + cur_info_ofs += sizeof(ModeInfoListItem); + vmode = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, mode)); + } + return EFI_SUCCESS; +} + +/** + Construct the valid video modes for VBoxVga. + +**/ +EFI_STATUS +VBoxVgaVideoModeSetup ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 Index; + BOOLEAN EdidFound; + EFI_EDID_OVERRIDE_PROTOCOL *EdidOverride; + UINT32 EdidAttributes; + BOOLEAN EdidOverrideFound; + UINTN EdidOverrideDataSize; + UINT8 *EdidOverrideDataBlock; + UINTN EdidDiscoveredDataSize; + UINT8 *EdidDiscoveredDataBlock; + UINTN EdidActiveDataSize; + UINT8 *EdidActiveDataBlock; + VALID_EDID_TIMING ValidEdidTiming; + UINT32 ValidModeCount; + VBOX_VGA_MODE_DATA *ModeData; + BOOLEAN TimingMatch; + const VBOX_VGA_VIDEO_MODES *VideoMode; + EDID_TIMING TempTiming; + + // + // setup EDID information + // + Private->EdidDiscovered.Edid = NULL; + Private->EdidDiscovered.SizeOfEdid = 0; + Private->EdidActive.Edid = NULL; + Private->EdidActive.SizeOfEdid = 0; + + EdidFound = FALSE; + EdidOverrideFound = FALSE; + EdidAttributes = 0xff; + EdidOverrideDataSize = 0; + EdidOverrideDataBlock = NULL; + EdidActiveDataSize = 0; + EdidActiveDataBlock = NULL; + EdidDiscoveredDataBlock = NULL; + + // + // Find EDID Override protocol firstly, this protocol is installed by platform if needed. + // + Status = gBS->LocateProtocol ( + &gEfiEdidOverrideProtocolGuid, + NULL, + (VOID **) &EdidOverride + ); + if (!EFI_ERROR (Status)) { + // + // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow + // + EdidOverrideDataBlock = AllocatePool (sizeof (EDID_BLOCK_SIZE * 2)); + if (NULL == EdidOverrideDataBlock) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = EdidOverride->GetEdid ( + EdidOverride, + Private->Handle, + &EdidAttributes, + &EdidOverrideDataSize, + (UINT8 **) &EdidOverrideDataBlock + ); + if (!EFI_ERROR (Status) && + EdidAttributes == 0 && + EdidOverrideDataSize != 0) { + // + // Succeeded to get EDID Override Data + // + EdidOverrideFound = TRUE; + } + } + + if (EdidOverrideFound != TRUE || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) { + // + // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned, + // read EDID information through I2C Bus + // + if (ReadEdidData (Private, &EdidDiscoveredDataBlock, &EdidDiscoveredDataSize) == EFI_SUCCESS) { + Private->EdidDiscovered.SizeOfEdid = (UINT32) EdidDiscoveredDataSize; + Private->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool ( + EdidDiscoveredDataSize, + EdidDiscoveredDataBlock + ); + + if (NULL == Private->EdidDiscovered.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + EdidActiveDataSize = Private->EdidDiscovered.SizeOfEdid; + EdidActiveDataBlock = Private->EdidDiscovered.Edid; + + EdidFound = TRUE; + } + } + + if (EdidFound != TRUE && EdidOverrideFound == TRUE) { + EdidActiveDataSize = EdidOverrideDataSize; + EdidActiveDataBlock = EdidOverrideDataBlock; + EdidFound = TRUE; + } + + if (EdidFound == TRUE) { + // + // Parse EDID data structure to retrieve modes supported by monitor + // + if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming) == TRUE) { + // + // Copy EDID Override Data to EDID Active Data + // + Private->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize; + Private->EdidActive.Edid = (UINT8 *) AllocateCopyPool ( + EdidActiveDataSize, + EdidActiveDataBlock + ); + if (NULL == Private->EdidActive.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + } + } else { + Private->EdidActive.SizeOfEdid = 0; + Private->EdidActive.Edid = NULL; + EdidFound = FALSE; + } + + if (EdidFound && 0) { + // + // Initialize the private mode data with the supported modes. + // + ValidModeCount = 0; + Private->ModeData = AllocatePool(sizeof(VBOX_VGA_MODE_DATA) * VBoxVgaVideoModeCount); + ModeData = &Private->ModeData[0]; + VideoMode = &VBoxVgaVideoModes[0]; + for (Index = 0; Index < VBoxVgaVideoModeCount; Index++) { + + TimingMatch = TRUE; + + // + // Check whether match with VBoxVga video mode + // + TempTiming.HorizontalResolution = (UINT16) VideoMode->Width; + TempTiming.VerticalResolution = (UINT16) VideoMode->Height; + TempTiming.RefreshRate = (UINT16) VideoMode->RefreshRate; + if (SearchEdidTiming (&ValidEdidTiming, &TempTiming) != TRUE) { + TimingMatch = FALSE; + } + + // + // Not export Mode 0x0 as GOP mode, this is not defined in spec. + // + if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) { + TimingMatch = FALSE; + } + + // + // Check whether the mode would be exceeding the VRAM size. + // + if (VideoMode->Width * VideoMode->Height * (VideoMode->ColorDepth / 8) > Private->VRAMSize) { + TimingMatch = FALSE; + } + + if (TimingMatch) { + ModeData->ModeNumber = Index; + ModeData->HorizontalResolution = VideoMode->Width; + ModeData->VerticalResolution = VideoMode->Height; + ModeData->ColorDepth = VideoMode->ColorDepth; + ModeData->RefreshRate = VideoMode->RefreshRate; + + ModeData ++; + ValidModeCount ++; + } + + VideoMode ++; + } + } else { + // + // If EDID information wasn't found + // + VBoxVgaVideoModeInitExtra(); + ValidModeCount = 0; + Private->ModeData = AllocatePool(sizeof(VBOX_VGA_MODE_DATA) * VBoxVgaVideoModeCount); + ModeData = &Private->ModeData[0]; + VideoMode = &VBoxVgaVideoModes[0]; + for (Index = 0; Index < VBoxVgaVideoModeCount; Index ++) { + + TimingMatch = TRUE; + + // + // Not export Mode 0x0 as GOP mode, this is not defined in spec. + // + if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) { + TimingMatch = FALSE; + } + + // + // Check whether the mode would be exceeding the VRAM size. + // + if (VideoMode->Width * VideoMode->Height * (VideoMode->ColorDepth / 8) > Private->VRAMSize) { + TimingMatch = FALSE; + } + + if (TimingMatch) { + ModeData->ModeNumber = Index; + ModeData->HorizontalResolution = VideoMode->Width; + ModeData->VerticalResolution = VideoMode->Height; + ModeData->ColorDepth = VideoMode->ColorDepth; + ModeData->RefreshRate = VideoMode->RefreshRate; + + ModeData ++; + ValidModeCount ++; + } + + VideoMode ++; + } + } + + // Sort list of video modes (keeping duplicates) by increasing X, then Y, + // then the mode number. This way the custom modes are not overriding the + // default modes if they are for the same resolution. + ModeData = &Private->ModeData[0]; + for (Index = 0; Index < ValidModeCount - 1; Index ++) { + UINT32 Index2; + VBOX_VGA_MODE_DATA *ModeData2 = ModeData + 1; + for (Index2 = Index + 1; Index2 < ValidModeCount; Index2 ++) { + if ( ModeData->HorizontalResolution > ModeData2->HorizontalResolution + || ( ModeData->HorizontalResolution == ModeData2->HorizontalResolution + && ModeData->VerticalResolution > ModeData2->VerticalResolution) + || ( ModeData->HorizontalResolution == ModeData2->HorizontalResolution + && ModeData->VerticalResolution == ModeData2->VerticalResolution + && ModeData->ModeNumber > ModeData2->ModeNumber)) { + VBOX_VGA_MODE_DATA Tmp; + CopyMem(&Tmp, ModeData, sizeof(Tmp)); + CopyMem(ModeData, ModeData2, sizeof(Tmp)); + CopyMem(ModeData2, &Tmp, sizeof(Tmp)); + DEBUG((DEBUG_INFO, "%a:%d swapped mode entries %d and %d\n", __FILE__, __LINE__, Index, Index2)); + } + ModeData2++; + } + ModeData++; + } + + // dump mode list for debugging purposes + ModeData = &Private->ModeData[0]; + for (Index = 0; Index < ValidModeCount; Index ++) { + DEBUG((DEBUG_INFO, "%a:%d mode %d: %dx%d mode number %d\n", __FILE__, __LINE__, Index, ModeData->HorizontalResolution, ModeData->VerticalResolution, ModeData->ModeNumber)); + ModeData++; + } + + Private->MaxMode = ValidModeCount; + + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + + return EFI_SUCCESS; + +Done: + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + if (Private->EdidDiscovered.Edid != NULL) { + FreePool (Private->EdidDiscovered.Edid); + } + if (Private->EdidDiscovered.Edid != NULL) { + FreePool (Private->EdidActive.Edid); + } + + return EFI_DEVICE_ERROR; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.c new file mode 100644 index 00000000..3d97fa94 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.c @@ -0,0 +1,1171 @@ +/* $Id: VBoxVga.c $ */ +/** @file + * VBoxVga.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Cirrus Logic 5430 Controller Driver. + This driver is a sample implementation of the UGA Draw and Graphics Output + Protocols for the Cirrus Logic 5430 family of PCI video controllers. + This driver is only usable in the EFI pre-boot environment. + This sample is intended to show how the UGA Draw and Graphics output Protocol + is able to function. + The UGA I/O Protocol is not implemented in this sample. + A fully compliant EFI UGA driver requires both + the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's + documentation on UGA for details on how to write a UGA driver that is able + to function both in the EFI pre-boot environment and from the OS runtime. + + Copyright (c) 2006 - 2009, Intel Corporation + 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. + +*/ + +// +// VirtualBox VGA Controller Driver +// +#include "VBoxVga.h" +#include <IndustryStandard/Acpi.h> +#include "iprt/asm.h" + + +#define BOUTB(storage, count, aport, dport) \ + do { \ + for (i = 0 ; i < (count); ++i) \ + if ((dport) == (aport) + 1) \ + ASMOutU16((aport), ((UINT16)storage[i] << 8) | (UINT8)i); \ + else { \ + ASMOutU8((aport), (UINT8)i); \ + ASMOutU8((dport), storage[i]); \ + } \ + } while (0) + + + +EFI_DRIVER_BINDING_PROTOCOL gVBoxVgaDriverBinding = { + VBoxVgaControllerDriverSupported, + VBoxVgaControllerDriverStart, + VBoxVgaControllerDriverStop, + 0x10, + NULL, + NULL +}; + +/// +/// Generic Attribute Controller Register Settings +/// +UINT8 AttributeController[21] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x41, 0x00, 0x0F, 0x00, 0x00 +}; + +/// +/// Generic Graphics Controller Register Settings +/// +UINT8 GraphicsController[9] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xff +}; + +/// +/// Generic Graphics Controller Sequencer Register Settings +/// +UINT8 Seq_Default[5] = { + 0x01, 0x01, 0x0f, 0x00, 0x0a +}; + +#if 0 // CRTC tables not used (and not checked for correctness), as VBE is much simpler +// +// 640 x 480 x 256 color @ 60 Hertz +// +UINT8 Crtc_640_480_256_60[25] = { + /* r0 = */0x5f, /* r1 = */0x4f, /* r2 = */0x50, /* r3 = */0x82, + /* r4 = */0x54, /* r5 = */0x80, /* r6 = */0x0b, /* r7 = */0x3e, + /* r8 = */0x00, /* r9 = */0x40, /* r10 = */0x00, /* r11 = */0x00, + /* r12 = */0x00, /* r13 = */0x00, /* r14 = */0x00, /* r15 = */0x00, + /* r16 = */0xea, /* r17 = */0x0c, /* r18 = */0xdf, /* r19 = */0x28, + /* r20 = */0x4f, /* r21 = */0xe7, /* r22 = */0x04, /* r23 = */0xe3, + /* r24 = */0xff +}; + +// +// 800 x 600 x 256 color @ 60 Hertz +// +UINT8 Crtc_800_600_256_60[25] = { + /* r0 = */0x7f, /* r1 = */0x63, /* r2 = */0x64, /* r3 = */0x82, + /* r4 = */0x6b, /* r5 = */0x80, /* r6 = */0x0b, /* r7 = */0x3e, + /* r8 = */0x00, /* r9 = */0x60, /* r10 = */0x00, /* r11 = */0x00, + /* r12 = */0x00, /* r13 = */0x00, /* r14 = */0x00, /* r15 = */0x00, + /* r16 = */0xea, /* r17 = */0x0c, /* r18 = */0xdf, /* r19 = */0x28, + /* r20 = */0x4f, /* r21 = */0xe7, /* r22 = */0x04, /* r23 = */0xe3, + /* r24 = */0xff + +}; + +// +// 1024 x 768 x 256 color @ 60 Hertz +// +UINT8 Crtc_1024_768_256_60[25] = { + /* r0 = */0xa3, /* r1 = */0x7f, /* r2 = */0x81, /* r3 = */0x90, + /* r4 = */0x88, /* r5 = */0x05, /* r6 = */0x28, /* r7 = */0xfd, + /* r8 = */0x00, /* r9 = */0x60, /* r10 = */0x00, /* r11 = */0x00, + /* r12 = */0x00, /* r13 = */0x00, /* r14 = */0x00, /* r15 = */0x00, + /* r16 = */0x06, /* r17 = */0x0f, /* r18 = */0xff, /* r19 = */0x40, + /* r20 = */0x4f, /* r21 = */0x05, /* r22 = */0x1a, /* r23 = */0xe3, + /* r24 = */0xff +}; +#endif + +/// +/// Table of supported video modes (sorted by increasing horizontal, then by +/// increasing vertical resolution) +/// +VBOX_VGA_VIDEO_MODES VBoxVgaVideoModes[] = +{ + { 640, 480, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // VGA 4:3 + { 800, 600, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // SVGA 4:3 + { 1024, 768, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // XGA 4:3 + { 1152, 864, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // XGA+ 4:3 + { 1280, 720, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HD 16:9 + { 1280, 800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WXGA 16:10 + { 1280, 1024, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // SXGA 5:4 + { 1400, 1050, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // SXGA+ 4:3 + { 1440, 900, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WXGA+ 16:10 + { 1600, 900, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HD+ 16:9 + { 1600, 1200, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // UXGA 4:3 + { 1680, 1050, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WSXGA+ 16:10 + { 1920, 1080, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // FHD 16:9 + { 1920, 1200, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WUXGA 16:10 + { 2048, 1080, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // DCI_2K 19:10 + { 2160, 1440, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // FHD+ 3:2 + { 2304, 1440, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // unnamed 16:10 + { 2560, 1440, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // QHD 16:9 + { 2560, 1600, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WQXGA 16:10 + { 2880, 1800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // QWXGA+ 16:10 + { 3200, 1800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // QHD+ 16:9 + { 3200, 2048, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WQSXGA 16:10 + { 3840, 2160, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // 4K_UHD 16:9 + { 3840, 2400, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WQUXGA 16:10 + { 4096, 2160, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // DCI_4K 19:10 + { 4096, 3072, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HXGA 4:3 + { 5120, 2880, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // UHD+ 16:9 + { 5120, 3200, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WHXGA 16:10 + { 6400, 4096, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WHSXGA 16:10 + { 6400, 4800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HUXGA 4:3 + { 7680, 4320, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // 8K_UHD2 16:9 + { 0, }, // Custom video mode 0, do not delete, must be at the end! + { 0, }, // Custom video mode 1, do not delete, must be at the end! + { 0, }, // Custom video mode 2, do not delete, must be at the end! + { 0, }, // Custom video mode 3, do not delete, must be at the end! + { 0, }, // Custom video mode 4, do not delete, must be at the end! + { 0, }, // Custom video mode 5, do not delete, must be at the end! + { 0, }, // Custom video mode 6, do not delete, must be at the end! + { 0, }, // Custom video mode 7, do not delete, must be at the end! + { 0, }, // Custom video mode 8, do not delete, must be at the end! + { 0, }, // Custom video mode 9, do not delete, must be at the end! + { 0, }, // Custom video mode 10, do not delete, must be at the end! + { 0, }, // Custom video mode 11, do not delete, must be at the end! + { 0, }, // Custom video mode 12, do not delete, must be at the end! + { 0, }, // Custom video mode 13, do not delete, must be at the end! + { 0, }, // Custom video mode 14, do not delete, must be at the end! + { 0, } // Custom video mode 15, do not delete, must be at the end! +}; + +const UINT32 VBoxVgaVideoModeCount = sizeof(VBoxVgaVideoModes) / sizeof(VBoxVgaVideoModes[0]); + +typedef struct _APPLE_FRAMEBUFFERINFO_PROTOCOL APPLE_FRAMEBUFFERINFO_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *APPLE_FRAMEBUFFERINFO_PROTOCOL_GET_INFO) ( + IN APPLE_FRAMEBUFFERINFO_PROTOCOL *This, + OUT UINT32 *BaseAddr, + OUT UINT32 *Something, + OUT UINT32 *RowBytes, + OUT UINT32 *Width, + OUT UINT32 *Height, + OUT UINT32 *Depth); + +struct _APPLE_FRAMEBUFFERINFO_PROTOCOL { + APPLE_FRAMEBUFFERINFO_PROTOCOL_GET_INFO GetInfo; + VBOX_VGA_PRIVATE_DATA *Private; +}; + +EFI_STATUS EFIAPI +GetFrameBufferInfo(IN APPLE_FRAMEBUFFERINFO_PROTOCOL *This, + OUT UINT32 *BaseAddr, + OUT UINT32 *Something, + OUT UINT32 *RowBytes, + OUT UINT32 *Width, + OUT UINT32 *Height, + OUT UINT32 *Depth); + +static APPLE_FRAMEBUFFERINFO_PROTOCOL gAppleFrameBufferInfo = +{ + GetFrameBufferInfo, + NULL +}; + + +/* + * @todo move this function to the library. + */ +UINT32 VBoxVgaGetVmVariable(UINT32 Variable, CHAR8* Buffer, UINT32 Size) +{ + UINT32 VarLen, i; + + ASMOutU32(EFI_INFO_PORT, Variable); + VarLen = ASMInU32(EFI_INFO_PORT); + + for (i = 0; i < VarLen && i < Size; i++) + Buffer[i] = ASMInU8(EFI_INFO_PORT); + + return VarLen; +} + + +/** + VBoxVgaControllerDriverSupported + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: RemainingDevicePath - add argument and description to function comment +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + EFI_DEV_PATH *Node; + + // + // Open the PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + return Status; + } + + // + // Read the PCI Configuration Header from the PCI Device + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + goto Done; + } + + Status = EFI_UNSUPPORTED; + // + // See if the I/O enable is on. Most systems only allow one VGA device to be turned on + // at a time, so see if this is one that is turned on. + // + // if (((Pci.Hdr.Command & 0x01) == 0x01)) { + // + // See if this is a VirtualBox VGA or VMSVGA II PCI controller + // + if ( ((Pci.Hdr.VendorId == VBOX_VENDOR_ID) && (Pci.Hdr.DeviceId == VBOX_VGA_DEVICE_ID)) + || ((Pci.Hdr.VendorId == VMSVGA_VENDOR_ID) && (Pci.Hdr.DeviceId == VMSVGA_II_DEVICE_ID))) { + + Status = EFI_SUCCESS; + if (RemainingDevicePath != NULL) { + Node = (EFI_DEV_PATH *) RemainingDevicePath; + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (!IsDevicePathEnd (Node)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + if (Node->DevPath.Type != ACPI_DEVICE_PATH || + Node->DevPath.SubType != ACPI_ADR_DP || + DevicePathNodeLength(&Node->DevPath) != sizeof(ACPI_ADR_DEVICE_PATH)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + Status = EFI_UNSUPPORTED; + } + } + } + } + +Done: + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + return Status; +} + +/** + VBoxVgaControllerDriverStart + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: RemainingDevicePath - add argument and description to function comment +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + VBOX_VGA_PRIVATE_DATA *Private; + BOOLEAN PciAttributesSaved; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + ACPI_ADR_DEVICE_PATH AcpiDeviceNode; + PCI_TYPE00 Pci; + + PciAttributesSaved = FALSE; + // + // Allocate Private context data for UGA Draw interface. + // + Private = AllocateZeroPool (sizeof (VBOX_VGA_PRIVATE_DATA)); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + gAppleFrameBufferInfo.Private = Private; + // + // Set up context record + // + Private->Signature = VBOX_VGA_PRIVATE_DATA_SIGNATURE; + Private->Handle = NULL; + + // + // Open PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &Private->PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Read the PCI Configuration Header from the PCI Device again to figure out the model. + // + Status = Private->PciIo->Pci.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + goto Error; + } + + Private->DeviceType = Pci.Hdr.DeviceId; + + // + // Save original PCI attributes + // + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Private->OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + PciAttributesSaved = TRUE; + + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | EFI_PCI_IO_ATTRIBUTE_VGA_IO, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Get ParentDevicePath + // + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (FeaturePcdGet (PcdSupportGop)) { + // + // Set Gop Device Path + // + if (RemainingDevicePath == NULL) { + ZeroMem (&AcpiDeviceNode, sizeof (ACPI_ADR_DEVICE_PATH)); + AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH; + AcpiDeviceNode.Header.SubType = ACPI_ADR_DP; + AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0); + SetDevicePathNodeLength (&AcpiDeviceNode.Header, sizeof (ACPI_ADR_DEVICE_PATH)); + + Private->GopDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode + ); + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // only scan the specified device by RemainingDevicePath + // + Private->GopDevicePath = AppendDevicePathNode (ParentDevicePath, RemainingDevicePath); + } else { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create child device and return EFI_SUCCESS + // + Private->GopDevicePath = NULL; + } + + if (Private->GopDevicePath != NULL) { + // + // Create child handle and device path protocol first + // + Private->Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->GopDevicePath, + NULL + ); + } + } + + // + // Now do some model-specific setup. + // + if (Private->DeviceType == VMSVGA_II_DEVICE_ID) { + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *IOPortDesc; + + // VMSVGA + Private->BarIndexFB = 1; + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + 0, // BAR 0 is the I/O port space + NULL, + (VOID**) &IOPortDesc + ); + Private->IOBase = (UINT16)IOPortDesc->AddrRangeMin; + + // + // Query the VRAM size (for proper mode filtering) + // + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_VRAM_SIZE); + Private->VRAMSize = ASMInU32(Private->IOBase + SVGA_VALUE_PORT); + +#if 0 + // Not used because of buggy emulation(?) which is not fully compatible + // with the simple "legacy" VMSVGA II register interface. + + // Enable the device, set initial mode + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_WIDTH); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 1024); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_HEIGHT); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 768); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_BYTES_PER_LINE); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 768 * 4); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_BITS_PER_PIXEL); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 32); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_CONFIG_DONE); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 1); + + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_ENABLE); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, SVGA_REG_ENABLE_ENABLE); +#endif + } else { + // VBoxVGA / VBoxSVGA + Private->BarIndexFB = 0; + // + // Get VRAM size, needed for constructing a correct video mode list + // + Private->VRAMSize = ASMInU32(VBE_DISPI_IOPORT_DATA); + } + + + // + // Construct video mode list + // + Status = VBoxVgaVideoModeSetup (Private); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (FeaturePcdGet (PcdSupportUga)) { + // + // Start the UGA Draw software stack. + // + Status = VBoxVgaUgaDrawConstructor (Private); + ASSERT_EFI_ERROR (Status); + + Private->UgaDevicePath = ParentDevicePath; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + //&gEfiUgaDrawProtocolGuid, + //&Private->UgaDraw, + &gEfiDevicePathProtocolGuid, + Private->UgaDevicePath, + NULL + ); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + NULL + ); + + } else if (FeaturePcdGet (PcdSupportGop)) { + if (Private->GopDevicePath == NULL) { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create child device and return EFI_SUCCESS + // + Status = EFI_SUCCESS; + } else { + + // + // Start the GOP software stack. + // + Status = VBoxVgaGraphicsOutputConstructor (Private); + ASSERT_EFI_ERROR (Status); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + &gEfiEdidDiscoveredProtocolGuid, + &Private->EdidDiscovered, + &gEfiEdidActiveProtocolGuid, + &Private->EdidActive, + NULL + ); + } + } else { + // + // This driver must support eithor GOP or UGA or both. + // + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + } + +Error: + if (EFI_ERROR (Status)) { + if (Private) { + if (Private->PciIo) { + if (PciAttributesSaved == TRUE) { + // + // Restore original PCI attributes + // + Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->OriginalPciAttributes, + NULL + ); + } + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Private->Handle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Private->Handle + ); + } + + gBS->FreePool (Private); + } + } + + return Status; +} + +/** + VBoxVgaControllerDriverStop + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: NumberOfChildren - add argument and description to function comment + TODO: ChildHandleBuffer - add argument and description to function comment + TODO: EFI_SUCCESS - add return value to function comment +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + EFI_STATUS Status; + VBOX_VGA_PRIVATE_DATA *Private; + + if (FeaturePcdGet (PcdSupportUga)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + (VOID **) &UgaDraw, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get our private context information + // + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (UgaDraw); + VBoxVgaUgaDrawDestructor (Private); + + if (FeaturePcdGet (PcdSupportGop)) { + VBoxVgaGraphicsOutputDestructor (Private); + // + // Remove the UGA and GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } else { + // + // Remove the UGA Draw interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + NULL + ); + } + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get our private context information + // + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (GraphicsOutput); + + VBoxVgaGraphicsOutputDestructor (Private); + // + // Remove the GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Private->ModeData) { + FreePool(Private->ModeData); + Private->ModeData = NULL; + } + + // + // Restore original PCI attributes + // + Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->OriginalPciAttributes, + NULL + ); + + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Free our instance data + // + gBS->FreePool (Private); + + return EFI_SUCCESS; +} + +/** + VBoxVgaUgaDrawDestructor + + TODO: Private - add argument and description to function comment + TODO: EFI_SUCCESS - add return value to function comment +**/ +EFI_STATUS +VBoxVgaUgaDrawDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + return EFI_SUCCESS; +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Index TODO: add argument description + @param Red TODO: add argument description + @param Green TODO: add argument description + @param Blue TODO: add argument description + + TODO: add return values + +**/ +VOID +SetPaletteColor ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN Index, + UINT8 Red, + UINT8 Green, + UINT8 Blue + ) +{ + ASMOutU8(PALETTE_INDEX_REGISTER, (UINT8) Index); + ASMOutU8(PALETTE_DATA_REGISTER, (UINT8) (Red >> 2)); + ASMOutU8(PALETTE_DATA_REGISTER, (UINT8) (Green >> 2)); + ASMOutU8(PALETTE_DATA_REGISTER, (UINT8) (Blue >> 2)); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +SetDefaultPalette ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ +#if 1 + UINTN Index; + UINTN RedIndex; + UINTN GreenIndex; + UINTN BlueIndex; + Index = 0; + for (RedIndex = 0; RedIndex < 8; RedIndex++) { + for (GreenIndex = 0; GreenIndex < 8; GreenIndex++) { + for (BlueIndex = 0; BlueIndex < 4; BlueIndex++) { + SetPaletteColor (Private, Index, (UINT8) (RedIndex << 5), (UINT8) (GreenIndex << 5), (UINT8) (BlueIndex << 6)); + Index++; + } + } + } +#else + { + int i; + static const UINT8 s_a3bVgaDac[64*3] = + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2A, + 0x00, 0x2A, 0x00, + 0x00, 0x2A, 0x2A, + 0x2A, 0x00, 0x00, + 0x2A, 0x00, 0x2A, + 0x2A, 0x2A, 0x00, + 0x2A, 0x2A, 0x2A, + 0x00, 0x00, 0x15, + 0x00, 0x00, 0x3F, + 0x00, 0x2A, 0x15, + 0x00, 0x2A, 0x3F, + 0x2A, 0x00, 0x15, + 0x2A, 0x00, 0x3F, + 0x2A, 0x2A, 0x15, + 0x2A, 0x2A, 0x3F, + 0x00, 0x15, 0x00, + 0x00, 0x15, 0x2A, + 0x00, 0x3F, 0x00, + 0x00, 0x3F, 0x2A, + 0x2A, 0x15, 0x00, + 0x2A, 0x15, 0x2A, + 0x2A, 0x3F, 0x00, + 0x2A, 0x3F, 0x2A, + 0x00, 0x15, 0x15, + 0x00, 0x15, 0x3F, + 0x00, 0x3F, 0x15, + 0x00, 0x3F, 0x3F, + 0x2A, 0x15, 0x15, + 0x2A, 0x15, 0x3F, + 0x2A, 0x3F, 0x15, + 0x2A, 0x3F, 0x3F, + 0x15, 0x00, 0x00, + 0x15, 0x00, 0x2A, + 0x15, 0x2A, 0x00, + 0x15, 0x2A, 0x2A, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x2A, + 0x3F, 0x2A, 0x00, + 0x3F, 0x2A, 0x2A, + 0x15, 0x00, 0x15, + 0x15, 0x00, 0x3F, + 0x15, 0x2A, 0x15, + 0x15, 0x2A, 0x3F, + 0x3F, 0x00, 0x15, + 0x3F, 0x00, 0x3F, + 0x3F, 0x2A, 0x15, + 0x3F, 0x2A, 0x3F, + 0x15, 0x15, 0x00, + 0x15, 0x15, 0x2A, + 0x15, 0x3F, 0x00, + 0x15, 0x3F, 0x2A, + 0x3F, 0x15, 0x00, + 0x3F, 0x15, 0x2A, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x2A, + 0x15, 0x15, 0x15, + 0x15, 0x15, 0x3F, + 0x15, 0x3F, 0x15, + 0x15, 0x3F, 0x3F, + 0x3F, 0x15, 0x15, + 0x3F, 0x15, 0x3F, + 0x3F, 0x3F, 0x15, + 0x3F, 0x3F, 0x3F + }; + + for (i = 0; i < 64; ++i) + { + ASMOutU8(PALETTE_INDEX_REGISTER, (UINT8)i); + ASMOutU8(PALETTE_DATA_REGISTER, s_a3bVgaDac[i*3 + 0]); + ASMOutU8(PALETTE_DATA_REGISTER, s_a3bVgaDac[i*3 + 1]); + ASMOutU8(PALETTE_DATA_REGISTER, s_a3bVgaDac[i*3 + 2]); + } + } + +#endif +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +ClearScreen ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL blt; + blt.Blue = 0; + blt.Green = 0; + blt.Red = 0; + blt.Reserved = 0; + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + 0, + Private->ModeData[Private->CurrentMode].HorizontalResolution + * Private->ModeData[Private->CurrentMode].VerticalResolution, + &blt + ); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +DrawLogo ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN ScreenWidth, + UINTN ScreenHeight + ) +{ + DEBUG((DEBUG_INFO, "UGA is %a GOP is %a\n", + FeaturePcdGet(PcdSupportUga) ? "on" : "off", + FeaturePcdGet(PcdSupportGop) ? "on" : "off" + )); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param ModeData TODO: add argument description + + TODO: add return values + +**/ +VOID +InitializeGraphicsMode ( + VBOX_VGA_PRIVATE_DATA *Private, + VBOX_VGA_VIDEO_MODES *ModeData + ) +{ + UINT16 DeviceId; + EFI_STATUS Status; + int i; + + DEBUG((DEBUG_INFO, "%a:%d InitializeGraphicsMode: %dx%d bpp:%d\n", __FILE__, __LINE__, ModeData->Width, ModeData->Height, ModeData->ColorDepth)); + + // + // Read the PCI ID from the PCI Device (dummy) + // + Status = Private->PciIo->Pci.Read ( + Private->PciIo, + EfiPciIoWidthUint16, + PCI_DEVICE_ID_OFFSET, + 1, + &DeviceId + ); + ASSERT_EFI_ERROR(Status); + + ASMOutU8(MISC_OUTPUT_REGISTER, 0xc3); + ASMOutU16(SEQ_ADDRESS_REGISTER, 0x0204); + + ASMInU8(INPUT_STATUS_1_REGISTER); // reset attribute address/data flip-flop + ASMOutU8(ATT_ADDRESS_REGISTER, 0); // blank screen using the attribute address register + + ASMOutU16(CRTC_ADDRESS_REGISTER, 0x0011); + + ASMOutU16(SEQ_ADDRESS_REGISTER, 0x0100); + if (ModeData->SeqSettings) + BOUTB(ModeData->SeqSettings, 5, SEQ_ADDRESS_REGISTER, SEQ_DATA_REGISTER); + else + BOUTB(Seq_Default, 5, SEQ_ADDRESS_REGISTER, SEQ_DATA_REGISTER); + ASMOutU16(SEQ_ADDRESS_REGISTER, 0x0300); + + BOUTB(GraphicsController, 9, GRAPH_ADDRESS_REGISTER, GRAPH_DATA_REGISTER); + + ASMInU8(INPUT_STATUS_1_REGISTER); // reset attribute address/data flip-flop + BOUTB(AttributeController, 21, ATT_ADDRESS_REGISTER, ATT_DATA_REGISTER); + + ASMOutU8(MISC_OUTPUT_REGISTER, ModeData->MiscSetting); + + if (ModeData->ColorDepth <= 8) + { + ASMOutU8(DAC_PIXEL_MASK_REGISTER, 0xff); + SetDefaultPalette(Private); + } + + if (!ModeData->CrtcSettings) + { + // No CRTC settings, use VBE + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x00); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0xb0c0); // ID + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x04); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // ENABLE + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x01); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Width); // XRES + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x02); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Height); // YRES + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x03); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->ColorDepth); // BPP + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x05); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // BANK + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x06); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Width); // VIRT_WIDTH + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x07); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Height); // VIRT_HEIGHT + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x08); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // X_OFFSET + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x09); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // Y_OFFSET + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x04); ASMOutU16(VBE_DISPI_IOPORT_DATA, 1); // ENABLE + /// @todo enabling VBE is automatically tweaking the CRTC, GC, SC, clears the + // screen and at the end unblanks graphics. So make sure that nothing is done + // after this which needs blanking. Way too much magic, but that's how it is... + } + else + { + BOUTB(ModeData->CrtcSettings, 25, CRTC_ADDRESS_REGISTER, CRTC_DATA_REGISTER); + } + + ASMInU8(INPUT_STATUS_1_REGISTER); // reset attribute address/data flip-flop + ASMOutU8(ATT_ADDRESS_REGISTER, 0x20); // unblank screen + + ClearScreen(Private); +} + +/** Aka know as AppleGraphInfoProtocolGuid in other sources. */ +#define EFI_UNKNOWN_2_PROTOCOL_GUID \ + { 0xE316E100, 0x0751, 0x4C49, {0x90, 0x56, 0x48, 0x6C, 0x7E, 0x47, 0x29, 0x03} } + +EFI_GUID gEfiAppleFrameBufferInfoGuid = EFI_UNKNOWN_2_PROTOCOL_GUID; + +EFI_STATUS EFIAPI +GetFrameBufferInfo(IN APPLE_FRAMEBUFFERINFO_PROTOCOL *This, + OUT UINT32 *BaseAddr, + OUT UINT32 *Something, + OUT UINT32 *RowBytes, + OUT UINT32 *Width, + OUT UINT32 *Height, + OUT UINT32 *Depth) +{ + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FrameBufDesc; + UINT32 W, H, BPP; + VBOX_VGA_PRIVATE_DATA *Private = This->Private; + UINTN CurrentModeNumber = Private->CurrentMode; + VBOX_VGA_MODE_DATA const *pCurrentMode = &Private->ModeData[CurrentModeNumber]; + + W = pCurrentMode->HorizontalResolution; + H = pCurrentMode->VerticalResolution; + BPP = pCurrentMode->ColorDepth; + DEBUG((DEBUG_INFO, "%a:%d GetFrameBufferInfo: %dx%d bpp:%d\n", __FILE__, __LINE__, W, H, BPP)); + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + Private->BarIndexFB, + NULL, + (VOID**) &FrameBufDesc + ); + + + /* EFI firmware remaps it here */ + *BaseAddr = (UINT32)FrameBufDesc->AddrRangeMin; + *RowBytes = W * BPP / 8; + *Width = W; + *Height = H; + *Depth = BPP; + // what *Something shall be? + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +InitializeVBoxVga ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gVBoxVgaDriverBinding, + ImageHandle, + &gVBoxVgaComponentName, + &gVBoxVgaComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Install EFI Driver Supported EFI Version Protocol required for + // EFI drivers that are on PCI and other plug in cards. + // + gVBoxVgaDriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion); + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDriverSupportedEfiVersionProtocolGuid, + &gVBoxVgaDriverSupportedEfiVersion, + &gEfiAppleFrameBufferInfoGuid, + &gAppleFrameBufferInfo, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.h new file mode 100644 index 00000000..25ff1a4e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.h @@ -0,0 +1,473 @@ +/* $Id: VBoxVga.h $ */ +/** @file + * VBoxVga.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Cirrus Logic 5430 Controller Driver + + Copyright (c) 2006 - 2007, Intel Corporation + 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. + +*/ + +// +// VirtualBox VGA Controller Driver +// + +#ifndef _VBOX_VGA_H_ +#define _VBOX_VGA_H_ + + +#include <Uefi.h> +#include <Protocol/UgaDraw.h> +#include <Protocol/GraphicsOutput.h> +#include <Protocol/PciIo.h> +#include <Protocol/DriverSupportedEfiVersion.h> +#include <Protocol/EdidOverride.h> +#include <Protocol/EdidDiscovered.h> +#include <Protocol/EdidActive.h> +#include <Protocol/DevicePath.h> + +#include <Library/DebugLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiLib.h> +#include <Library/PcdLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DevicePathLib.h> +#include <Library/TimerLib.h> + +#include <IndustryStandard/Pci.h> + +#include "VBoxPkg.h" +#include "DevEFI.h" +#include "VBox/Graphics/VBoxVideoVBE.h" +#include "VBox/Graphics/VBoxVideoVBEPrivate.h" + +// +// VirtualBox VGA PCI Configuration Header values +// +#define VBOX_VENDOR_ID 0x80ee +#define VBOX_VGA_DEVICE_ID 0xbeef + + +// +// VMSVGA II PCI Configuration Header values +// +#define VMSVGA_VENDOR_ID 0x15ad +#define VMSVGA_II_DEVICE_ID 0x0405 + +// Port offsets relative to BAR 0 +#define SVGA_INDEX_PORT 0 +#define SVGA_VALUE_PORT 1 + +// SVGA_REG_ENABLE bits +#define SVGA_REG_ENABLE_DISABLE 0 +#define SVGA_REG_ENABLE_ENABLE 1 + +// Registers +#define SVGA_REG_ENABLE 1 +#define SVGA_REG_WIDTH 2 +#define SVGA_REG_HEIGHT 3 +#define SVGA_REG_MAX_WIDTH 4 +#define SVGA_REG_MAX_HEIGHT 5 +#define SVGA_REG_DEPTH 6 +#define SVGA_REG_BITS_PER_PIXEL 7 +#define SVGA_REG_BYTES_PER_LINE 12 +#define SVGA_REG_FB_START 13 +#define SVGA_REG_FB_OFFSET 14 +#define SVGA_REG_VRAM_SIZE 15 +#define SVGA_REG_CONFIG_DONE 20 ///@todo: Why do we need this? + +// +// VirtualBox VGA Graphical Mode Data +// +typedef struct { + UINT32 ModeNumber; + UINT32 HorizontalResolution; + UINT32 VerticalResolution; + UINT32 ColorDepth; + UINT32 RefreshRate; +} VBOX_VGA_MODE_DATA; + +#define GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER 0xffff + +// +// VirtualBox VGA Private Data Structure +// +#define VBOX_VGA_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('V', 'B', 'V', 'D') + +typedef struct { + UINT64 Signature; + EFI_HANDLE Handle; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; + EFI_UGA_DRAW_PROTOCOL UgaDraw; + EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutput; + EFI_EDID_DISCOVERED_PROTOCOL EdidDiscovered; + EFI_EDID_ACTIVE_PROTOCOL EdidActive; + EFI_DEVICE_PATH_PROTOCOL *GopDevicePath; + EFI_DEVICE_PATH_PROTOCOL *UgaDevicePath; + UINTN CurrentMode; + UINTN MaxMode; + VBOX_VGA_MODE_DATA *ModeData; + BOOLEAN HardwareNeedsStarting; + UINT8 BarIndexFB; + UINT16 DeviceType; + UINT16 IOBase; + UINT32 VRAMSize; +} VBOX_VGA_PRIVATE_DATA; + +/// +/// Video Mode structure +/// +typedef struct { + UINT32 Width; + UINT32 Height; + UINT32 ColorDepth; + UINT32 RefreshRate; + /// CRTC settings are optional. If NULL then VBE is used + UINT8 *CrtcSettings; + /// Sequencer settings are optional. If NULL then defaults are used + UINT8 *SeqSettings; + UINT8 MiscSetting; +} VBOX_VGA_VIDEO_MODES; + +#define VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS(a) \ + CR(a, VBOX_VGA_PRIVATE_DATA, UgaDraw, VBOX_VGA_PRIVATE_DATA_SIGNATURE) + +#define VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS(a) \ + CR(a, VBOX_VGA_PRIVATE_DATA, GraphicsOutput, VBOX_VGA_PRIVATE_DATA_SIGNATURE) + + +// +// Global Variables +// +extern UINT8 AttributeController[]; +extern UINT8 GraphicsController[]; +extern UINT8 Crtc_640_480_256_60[]; +extern UINT8 Seq_640_480_256_60[]; +extern UINT8 Crtc_800_600_256_60[]; +extern UINT8 Seq_800_600_256_60[]; +extern UINT8 Crtc_1024_768_256_60[]; +extern UINT8 Seq_1024_768_256_60[]; +extern VBOX_VGA_VIDEO_MODES VBoxVgaVideoModes[]; +extern const UINT32 VBoxVgaVideoModeCount; +extern EFI_DRIVER_BINDING_PROTOCOL gVBoxVgaDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gVBoxVgaComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gVBoxVgaComponentName2; +extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gVBoxVgaDriverSupportedEfiVersion; + +// +// Io Registers defined by VGA +// +#define CRTC_ADDRESS_REGISTER 0x3d4 +#define CRTC_DATA_REGISTER 0x3d5 +#define SEQ_ADDRESS_REGISTER 0x3c4 +#define SEQ_DATA_REGISTER 0x3c5 +#define GRAPH_ADDRESS_REGISTER 0x3ce +#define GRAPH_DATA_REGISTER 0x3cf +#define ATT_ADDRESS_REGISTER 0x3c0 +#define ATT_DATA_REGISTER 0x3c1 +#define MISC_OUTPUT_REGISTER 0x3c2 +#define INPUT_STATUS_1_REGISTER 0x3da +#define DAC_PIXEL_MASK_REGISTER 0x3c6 +#define PALETTE_INDEX_REGISTER 0x3c8 +#define PALETTE_DATA_REGISTER 0x3c9 + + +// +// UGA Draw Hardware abstraction internal worker functions +// +EFI_STATUS +VBoxVgaUgaDrawConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +EFI_STATUS +VBoxVgaUgaDrawDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +// +// Graphics Output Hardware abstraction internal worker functions +// +EFI_STATUS +VBoxVgaGraphicsOutputConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +EFI_STATUS +VBoxVgaGraphicsOutputDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + + +// +// EFI_DRIVER_BINDING_PROTOCOL Protocol Interface +// +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param RemainingDevicePath TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param RemainingDevicePath TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param NumberOfChildren TODO: add argument description + @param ChildHandleBuffer TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VBoxVgaComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VBoxVgaComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// Local Function Prototypes +// +VOID +InitializeGraphicsMode ( + VBOX_VGA_PRIVATE_DATA *Private, + VBOX_VGA_VIDEO_MODES *ModeData + ); + +VOID +SetPaletteColor ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN Index, + UINT8 Red, + UINT8 Green, + UINT8 Blue + ); + +VOID +SetDefaultPalette ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +VOID +DrawLogo ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN ScreenWidth, + UINTN ScreenHeight + ); + +EFI_STATUS +VBoxVgaVideoModeSetup ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +UINT32 VBoxVgaGetVmVariable(UINT32 Variable, CHAR8* Buffer, UINT32 Size); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaDxe.inf new file mode 100644 index 00000000..79f6dc38 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaDxe.inf @@ -0,0 +1,133 @@ +# $Id: VBoxVgaDxe.inf $ +## @file +# VBoxVgaDxe.inf +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# +# This code is based on: +# +#/** @file +# Component description file for CirrusLogic5430 module +# +# Cirrus Logic 5430 Controller Driver.This driver is a sample implementation +# of the UGA Draw Protocol for the Cirrus Logic 5430 family of PCI video controllers. +# This driver is only usable in the EFI pre-boot environment. This sample is +# intended to show how the UGA Draw Protocol is able to function. The UGA I/O +# Protocol is not implemented in this sample. A fully compliant EFI UGA driver +# requires both the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's +# documentation on UGA for details on how to write a UGA driver that is able +# to function both in the EFI pre-boot environment and from the OS runtime. +# Copyright (c) 2006 - 2009, Intel Corporation +# +# 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. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxVgaDxe + FILE_GUID = b8a784bc-af4d-4d95-bdb1-ba28236a54f4 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeVBoxVga + + PCI_VENDOR_ID = 0x80EE + PCI_DEVICE_ID = 0xBEEF + PCI_CLASS_CODE = 0x030000 + PCI_REVISION = 0x00 + COMPRESS = TRUE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = VBoxVgaDriverBinding +# COMPONENT_NAME = VBoxVgaComponentName +# + +[Sources.common] + ComponentName.c + DriverSupportedEfiVersion.c + VBoxVgaUgaDraw.c + VBoxVgaGraphicsOutput.c + VBoxVga.c + VBoxVga.h + Edid.c + VBoxVgaI2c.h + VBoxVgaI2c.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + UefiLib + UefiDriverEntryPoint + DebugLib + BaseMemoryLib + DevicePathLib + TimerLib + +[Protocols] + gEfiDriverSupportedEfiVersionProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiUgaDrawProtocolGuid # PROTOCOL BY_START + gEfiGraphicsOutputProtocolGuid # PROTOCOL BY_START + gEfiEdidDiscoveredProtocolGuid # PROTOCOL BY_START + gEfiEdidActiveProtocolGuid # PROTOCOL BY_START + gEfiDevicePathProtocolGuid # PROTOCOL BY_START + gEfiPciIoProtocolGuid # PROTOCOL TO_START + gEfiEdidOverrideProtocolGuid # PROTOCOL TO_START + + +[FeaturePcd.common] + gVBoxVgaPkgTokenSpaceGuid.PcdSupportGop + gVBoxVgaPkgTokenSpaceGuid.PcdSupportUga + +[Pcd] + gVBoxVgaPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion + gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution ## PRODUCES + gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution ## PRODUCES diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaGraphicsOutput.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaGraphicsOutput.c new file mode 100644 index 00000000..f5706441 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaGraphicsOutput.c @@ -0,0 +1,544 @@ +/* $Id: VBoxVgaGraphicsOutput.c $ */ +/** @file + * VBoxVgaGraphicsOutput.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + +Copyright (c) 2007, Intel Corporation +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. + +Module Name: + + UefiVBoxVgaGraphicsOutput.c + +Abstract: + + This file produces the graphics abstraction of Graphics Output Protocol. It is called by + VBoxVga.c file which deals with the EFI 1.1 driver model. + This file just does graphics. + +*/ +#include "VBoxVga.h" +#include <IndustryStandard/Acpi.h> + + +STATIC +VOID +VBoxVgaCompleteModeInfo ( + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info + ) +{ + Info->Version = 0; + Info->PixelFormat = PixelBlueGreenRedReserved8BitPerColor; + Info->PixelsPerScanLine = Info->HorizontalResolution; +} + + +STATIC +EFI_STATUS +VBoxVgaCompleteModeData ( + IN VBOX_VGA_PRIVATE_DATA *Private, + OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode + ) +{ + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FrameBufDesc; + + Info = Mode->Info; + VBoxVgaCompleteModeInfo (Info); + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + Private->BarIndexFB, + NULL, + (VOID**) &FrameBufDesc + ); + + DEBUG((DEBUG_INFO, "%a:%d FrameBufferBase:%x\n", __FILE__, __LINE__, FrameBufDesc->AddrRangeMin)); + Mode->FrameBufferBase = FrameBufDesc->AddrRangeMin; + Mode->FrameBufferSize = Info->PixelsPerScanLine * Info->VerticalResolution + * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); /* 32bpp only! */ + + return EFI_SUCCESS; +} + + +// +// Graphics Output Protocol Member Functions +// +EFI_STATUS +EFIAPI +VBoxVgaGraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +/*++ + +Routine Description: + + Graphics Output protocol interface to query video mode + + Arguments: + This - Protocol instance pointer. + ModeNumber - The mode number to return information on. + Info - Caller allocated buffer that returns information about ModeNumber. + SizeOfInfo - A pointer to the size, in bytes, of the Info buffer. + + Returns: + EFI_SUCCESS - Mode information returned. + EFI_BUFFER_TOO_SMALL - The Info buffer was too small. + EFI_DEVICE_ERROR - A hardware error occurred trying to retrieve the video mode. + EFI_NOT_STARTED - Video display is not initialized. Call SetMode () + EFI_INVALID_PARAMETER - One of the input args was NULL. + +--*/ +{ + VBOX_VGA_PRIVATE_DATA *Private; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + if (Private->HardwareNeedsStarting) { + return EFI_NOT_STARTED; + } + + if (Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + + *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + if (*Info == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + (*Info)->HorizontalResolution = Private->ModeData[ModeNumber].HorizontalResolution; + (*Info)->VerticalResolution = Private->ModeData[ModeNumber].VerticalResolution; + VBoxVgaCompleteModeInfo (*Info); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VBoxVgaGraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber + ) +/*++ + +Routine Description: + + Graphics Output protocol interface to set video mode + + Arguments: + This - Protocol instance pointer. + ModeNumber - The mode number to be set. + + Returns: + EFI_SUCCESS - Graphics mode was changed. + EFI_DEVICE_ERROR - The device had an error and could not complete the request. + EFI_UNSUPPORTED - ModeNumber is not supported by this device. + +--*/ +{ + VBOX_VGA_PRIVATE_DATA *Private; + VBOX_VGA_MODE_DATA *ModeData; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + DEBUG((DEBUG_INFO, "%a:%d mode:%d\n", __FILE__, __LINE__, ModeNumber)); + if (ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + ModeData = &Private->ModeData[ModeNumber]; + + InitializeGraphicsMode (Private, &VBoxVgaVideoModes[ModeData->ModeNumber]); + + This->Mode->Mode = ModeNumber; + This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution; + This->Mode->Info->VerticalResolution = ModeData->VerticalResolution; + This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + VBoxVgaCompleteModeData (Private, This->Mode); + + Private->HardwareNeedsStarting = FALSE; + /* update current mode */ + Private->CurrentMode = ModeNumber; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VBoxVgaGraphicsOutputBlt ( + 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 + ) +/*++ + +Routine Description: + + Graphics Output protocol instance to block transfer for CirrusLogic device + +Arguments: + + This - Pointer to Graphics Output protocol instance + BltBuffer - The data to transfer to screen + BltOperation - The operation to perform + SourceX - The X coordinate of the source for BltOperation + SourceY - The Y coordinate of the source for BltOperation + DestinationX - The X coordinate of the destination for BltOperation + DestinationY - The Y coordinate of the destination for BltOperation + Width - The width of a rectangle in the blt rectangle in pixels + Height - The height of a rectangle in the blt rectangle in pixels + Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + +Returns: + + EFI_INVALID_PARAMETER - Invalid parameter passed in + EFI_SUCCESS - Blt operation success + +--*/ +{ + VBOX_VGA_PRIVATE_DATA *Private; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + UINT32 CurrentMode; + UINTN ScreenWidth; + UINTN ScreenHeight; + EFI_STATUS Status; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + CurrentMode = This->Mode->Mode; + ScreenWidth = Private->ModeData[CurrentMode].HorizontalResolution; + ScreenHeight = Private->ModeData[CurrentMode].VerticalResolution; + + if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) { + return EFI_INVALID_PARAMETER; + } + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + // code below assumes a Delta value in pixels, not bytes + Delta /= sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + + // + // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters + // are valid for the operation and the current screen geometry. + // + if (BltOperation == EfiBltVideoToBltBuffer || BltOperation == EfiBltVideoToVideo) { + if (SourceY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + if (BltOperation == EfiBltBufferToVideo || BltOperation == EfiBltVideoToVideo || BltOperation == EfiBltVideoFill) { + if (DestinationY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiBltVideoToBltBuffer: + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY) && BltBuffer; SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width, + BltBuffer + (DstY * Delta) + DestinationX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiBltBufferToVideo: + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + (SrcY * Delta) + SourceX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiBltVideoToVideo: + // + // Video to Video: Source is Video, destination is Video + // + if (DestinationY <= SourceY) { + // forward copy + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } else { + // reverse copy + for (SrcY = SourceY + Height - 1, DstY = DestinationY + Height - 1; SrcY >= SourceY && SrcY <= SourceY + Height - 1; SrcY--, DstY--) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } + break; + + case EfiBltVideoFill: + // + // Video Fill: Source is BltBuffer, destination is Video + // + if (DestinationX == 0 && Width == ScreenWidth) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + DestinationY * ScreenWidth * 4, + (Width * Height), + BltBuffer + ); + ASSERT_EFI_ERROR((Status)); + } else { + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + ); + ASSERT_EFI_ERROR((Status)); + } + } + break; + + default: + ASSERT (FALSE); + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +EFI_STATUS +VBoxVgaGraphicsOutputConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + UINT32 Index; + UINT32 HorizontalResolution = 1024; + UINT32 VerticalResolution = 768; + UINT32 ColorDepth = 32; + + DEBUG((DEBUG_INFO, "%a:%d construct\n", __FILE__, __LINE__)); + + GraphicsOutput = &Private->GraphicsOutput; + GraphicsOutput->QueryMode = VBoxVgaGraphicsOutputQueryMode; + GraphicsOutput->SetMode = VBoxVgaGraphicsOutputSetMode; + GraphicsOutput->Blt = VBoxVgaGraphicsOutputBlt; + + // + // Initialize the private data + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), + (VOID **) &Private->GraphicsOutput.Mode + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), + (VOID **) &Private->GraphicsOutput.Mode->Info + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private->GraphicsOutput.Mode->MaxMode = (UINT32) Private->MaxMode; + Private->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER; + Private->HardwareNeedsStarting = TRUE; + + // + // Initialize the hardware + // + VBoxVgaGetVmVariable(EFI_INFO_INDEX_HORIZONTAL_RESOLUTION, (CHAR8 *)&HorizontalResolution, + sizeof(HorizontalResolution)); + VBoxVgaGetVmVariable(EFI_INFO_INDEX_VERTICAL_RESOLUTION, (CHAR8 *)&VerticalResolution, + sizeof(VerticalResolution)); + for (Index = 0; Index < Private->MaxMode; Index++) + { + if ( HorizontalResolution == Private->ModeData[Index].HorizontalResolution + && VerticalResolution == Private->ModeData[Index].VerticalResolution + && ColorDepth == Private->ModeData[Index].ColorDepth) + break; + } + // not found? try mode number + if (Index >= Private->MaxMode) + { + VBoxVgaGetVmVariable(EFI_INFO_INDEX_GRAPHICS_MODE, (CHAR8 *)&Index, sizeof(Index)); + // try with mode 2 (usually 1024x768) as a fallback + if (Index >= Private->MaxMode) + Index = 2; + // try with mode 0 (usually 640x480) as a fallback + if (Index >= Private->MaxMode) + Index = 0; + } + + // skip mode setting completely if there is no valid mode + if (Index >= Private->MaxMode) + return EFI_UNSUPPORTED; + + GraphicsOutput->SetMode (GraphicsOutput, Index); + + DrawLogo ( + Private, + Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution, + Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution + ); + + PcdSet32S(PcdVideoHorizontalResolution, Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution); + PcdSet32S(PcdVideoVerticalResolution, Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution); + + return EFI_SUCCESS; +} + +EFI_STATUS +VBoxVgaGraphicsOutputDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +/*++ + +Routine Description: + +Arguments: + +Returns: + + None + +--*/ +{ + if (Private->GraphicsOutput.Mode != NULL) { + if (Private->GraphicsOutput.Mode->Info != NULL) { + gBS->FreePool (Private->GraphicsOutput.Mode->Info); + } + gBS->FreePool (Private->GraphicsOutput.Mode); + } + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.c new file mode 100644 index 00000000..ba1fceb9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.c @@ -0,0 +1,469 @@ +/* $Id: VBoxVgaI2c.c $ */ +/** @file + * VBoxVgaI2c.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + I2C Bus implementation upon CirrusLogic. + + Copyright (c) 2008 - 2009, Intel Corporation + 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 "VBoxVga.h" +#include "VBoxVgaI2c.h" + +#define SEQ_ADDRESS_REGISTER 0x3c4 +#define SEQ_DATA_REGISTER 0x3c5 + +#define I2C_CONTROL 0x08 +#define I2CDAT_IN 7 +#define I2CCLK_IN 2 +#define I2CDAT_OUT 1 +#define I2CCLK_OUT 0 + +#define I2C_BUS_SPEED 100 //100kbps + +/** + PCI I/O byte write function. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Address The bit map of I2C Data or I2C Clock pins. + @param Data The date to write. + +**/ +VOID +I2cOutb ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINTN Address, + UINT8 Data + ) +{ + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); +} +/** + PCI I/O byte read function. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Address The bit map of I2C Data or I2C Clock pins. + + return byte value read from PCI I/O space. + +**/ +UINT8 +I2cInb ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINTN Address + ) +{ + UINT8 Data; + + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); + return Data; +} + +/** + Read status of I2C Data and I2C Clock Pins. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Blt The bit map of I2C Data or I2C Clock pins. + + @retval 0 Low on I2C Data or I2C Clock Pin. + @retval 1 High on I2C Data or I2C Clock Pin. + +**/ +UINT8 +I2cPinRead ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Bit + ) +{ + I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); + return (UINT8) ((I2cInb (PciIo, SEQ_DATA_REGISTER) >> Bit ) & 0xfe); +} + + +/** + Set/Clear I2C Data and I2C Clock Pins. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Blt The bit map to controller I2C Data or I2C Clock pins. + @param Value 1 or 0 stands for Set or Clear I2C Data and I2C Clock Pins. + +**/ +VOID +I2cPinWrite ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Bit, + UINT8 Value + ) +{ + UINT8 Byte; + I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); + Byte = (UINT8) (I2cInb (PciIo, SEQ_DATA_REGISTER) & (UINT8) ~(1 << Bit)) ; + Byte = (UINT8) (Byte | ((Value & 0x01) << Bit)); + I2cOutb (PciIo, SEQ_DATA_REGISTER, (UINT8) (Byte | 0x40)); + return; +} + +/** + Read/write delay according to I2C Bus Speed. + +**/ +VOID +I2cDelay ( + VOID + ) +{ + MicroSecondDelay (1000 / I2C_BUS_SPEED); +} + +/** + Write a 8-bit data onto I2C Data Pin. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Data The byte data to write. + +**/ +VOID +I2cSendByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Data + ) +{ + UINTN Index; + // + // Send byte data onto I2C Bus + // + for (Index = 0; Index < 8; Index --) { + I2cPinWrite (PciIo, I2CDAT_OUT, (UINT8) (Data >> (7 - Index))); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); + } +} + +/** + Read a 8-bit data from I2C Data Pin. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + + Return the byte data read from I2C Data Pin. +**/ +UINT8 +I2cReceiveByte ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT8 Data; + UINTN Index; + + Data = 0; + // + // Read byte data from I2C Bus + // + for (Index = 0; Index < 8; Index --) { + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + Data = (UINT8) (Data << 1); + Data = (UINT8) (Data | I2cPinRead (PciIo, I2CDAT_IN)); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); + } + + return Data; +} + +/** + Receive an ACK signal from I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +BOOLEAN +I2cWaitAck ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Wait for ACK signal + // + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + if (I2cPinRead (PciIo, I2CDAT_IN) == 0) { + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + return TRUE; + } else { + return FALSE; + } +} + +/** + Send an ACK signal onto I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cSendAck ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); +} + +/** + Start a I2C transfer on I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cStart ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Init CLK and DAT pins + // + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + // + // Start a I2C transfer, set SDA low from high, when SCL is high + // + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); +} + +/** + Stop a I2C transfer on I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cStop ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Stop a I2C transfer, set SDA high from low, when SCL is high + // + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); +} + +/** + Read one byte data on I2C Bus. + + Read one byte data from the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to returned data if EFI_SUCCESS returned. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cReadByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ) +{ + ASSERT (Data != NULL); + + // + // Start I2C transfer + // + I2cStart (PciIo); + + // + // Send slave address with enabling write flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send register address + // + I2cSendByte (PciIo, RegisterAddress); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send slave address with enabling read flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress | 0x01)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Read byte data from I2C Bus + // + *Data = I2cReceiveByte (PciIo); + + // + // Send ACK signal onto I2C Bus + // + I2cSendAck (PciIo); + + // + // Stop a I2C transfer + // + I2cStop (PciIo); + + return EFI_SUCCESS; +} + +/** + Write one byte data onto I2C Bus. + + Write one byte data to the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to write data. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cWriteByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ) +{ + ASSERT (Data != NULL); + + I2cStart (PciIo); + // + // Send slave address with enabling write flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send register address + // + I2cSendByte (PciIo, RegisterAddress); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send byte data onto I2C Bus + // + I2cSendByte (PciIo, *Data); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Stop a I2C transfer + // + I2cStop (PciIo); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.h new file mode 100644 index 00000000..c88c0587 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.h @@ -0,0 +1,106 @@ +/* $Id: VBoxVgaI2c.h $ */ +/** @file + * VBoxVgaI2c.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + I2c Bus byte read/write functions. + + Copyright (c) 2008 - 2009, Intel Corporation + 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. + +*/ + +#ifndef _CIRRUS_LOGIC_I2C_H_ +#define _CIRRUS_LOGIC_I2C_H_ + +#include <Protocol/PciIo.h> + +/** + Read one byte data on I2C Bus. + + Read one byte data from the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to returned data if EFI_SUCCESS returned. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cReadByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ); + +/** + Write one byte data onto I2C Bus. + + Write one byte data to the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to write data. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cWriteByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaUgaDraw.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaUgaDraw.c new file mode 100644 index 00000000..79b38073 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaUgaDraw.c @@ -0,0 +1,417 @@ +/* $Id: VBoxVgaUgaDraw.c $ */ +/** @file + * VBoxVgaUgaDraw.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + This file produces the graphics abstraction of UGA Draw. It is called by + VBoxVga.c file which deals with the EFI 1.1 driver model. + This file just does graphics. + + Copyright (c) 2006, Intel Corporation + 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 "VBoxVga.h" + +// +// UGA Draw Protocol Member Functions +// +EFI_STATUS +EFIAPI +VBoxVgaUgaDrawGetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + OUT UINT32 *HorizontalResolution, + OUT UINT32 *VerticalResolution, + OUT UINT32 *ColorDepth, + OUT UINT32 *RefreshRate + ) +{ + VBOX_VGA_PRIVATE_DATA *Private; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + if (Private->HardwareNeedsStarting) { + return EFI_NOT_STARTED; + } + + if ((HorizontalResolution == NULL) || + (VerticalResolution == NULL) || + (ColorDepth == NULL) || + (RefreshRate == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *HorizontalResolution = Private->ModeData[Private->CurrentMode].HorizontalResolution; + *VerticalResolution = Private->ModeData[Private->CurrentMode].VerticalResolution; + *ColorDepth = Private->ModeData[Private->CurrentMode].ColorDepth; + *RefreshRate = Private->ModeData[Private->CurrentMode].RefreshRate; + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VBoxVgaUgaDrawSetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + IN UINT32 ColorDepth, + IN UINT32 RefreshRate + ) +{ + VBOX_VGA_PRIVATE_DATA *Private; + UINTN Index; + + DEBUG((DEBUG_INFO, "%a:%d VIDEO: %dx%d %d bpp\n", __FILE__, __LINE__, HorizontalResolution, VerticalResolution, ColorDepth)); + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + for (Index = 0; Index < Private->MaxMode; Index++) { + + if (HorizontalResolution != Private->ModeData[Index].HorizontalResolution) { + continue; + } + + if (VerticalResolution != Private->ModeData[Index].VerticalResolution) { + continue; + } + + if (ColorDepth != Private->ModeData[Index].ColorDepth) { + continue; + } + +#if 0 + if (RefreshRate != Private->ModeData[Index].RefreshRate) { + continue; + } +#endif + + InitializeGraphicsMode (Private, &VBoxVgaVideoModes[Private->ModeData[Index].ModeNumber]); + + Private->CurrentMode = Index; + + Private->HardwareNeedsStarting = FALSE; + + /* update current mode */ + Private->CurrentMode = Index; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +EFIAPI +VBoxVgaUgaDrawBlt ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN EFI_UGA_PIXEL *BltBuffer, OPTIONAL + IN EFI_UGA_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + VBOX_VGA_PRIVATE_DATA *Private; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + UINTN ScreenWidth; + UINTN ScreenHeight; + EFI_STATUS Status; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + ScreenWidth = Private->ModeData[Private->CurrentMode].HorizontalResolution; + ScreenHeight = Private->ModeData[Private->CurrentMode].VerticalResolution; + + if ((BltOperation < 0) || (BltOperation >= EfiUgaBltMax)) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_UGA_PIXEL); + } + // code below assumes a Delta value in pixels, not bytes + Delta /= sizeof (EFI_UGA_PIXEL); + + // + // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters + // are valid for the operation and the current screen geometry. + // + if (BltOperation == EfiUgaVideoToBltBuffer || BltOperation == EfiUgaVideoToVideo) { + if (SourceY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + if (BltOperation == EfiUgaBltBufferToVideo || BltOperation == EfiUgaVideoToVideo || BltOperation == EfiUgaVideoFill) { + if (DestinationY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiUgaVideoToBltBuffer: + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width, + BltBuffer + (DstY * Delta) + DestinationX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiUgaBltBufferToVideo: + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + (SrcY * Delta) + SourceX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiUgaVideoToVideo: + // + // Video to Video: Source is Video, destination is Video + // + if (DestinationY <= SourceY) { + // forward copy + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } else { + // reverse copy + for (SrcY = SourceY + Height - 1, DstY = DestinationY + Height - 1; SrcY >= SourceY && SrcY <= SourceY + Height - 1; SrcY--, DstY--) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } + break; + + case EfiUgaVideoFill: + // + // Video Fill: Source is BltBuffer, destination is Video + // + if (DestinationX == 0 && Width == ScreenWidth) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + DestinationY * ScreenWidth * 4, + (Width * Height), + BltBuffer + ); + ASSERT_EFI_ERROR((Status)); + } else { + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_UGA_PIXEL + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + ); + } + } + break; + + default: + ASSERT (FALSE); + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +// +// Construction and Destruction functions +// +EFI_STATUS +VBoxVgaUgaDrawConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + UINT32 Index; + UINT32 HorizontalResolution = 1024; + UINT32 VerticalResolution = 768; + UINT32 ColorDepth = 32; + + // + // Fill in Private->UgaDraw protocol + // + UgaDraw = &Private->UgaDraw; + + UgaDraw->GetMode = VBoxVgaUgaDrawGetMode; + UgaDraw->SetMode = VBoxVgaUgaDrawSetMode; + UgaDraw->Blt = VBoxVgaUgaDrawBlt; + + // + // Initialize the private data + // + Private->CurrentMode = 0; + Private->HardwareNeedsStarting = TRUE; + + // + // Initialize the hardware + // + VBoxVgaGetVmVariable(EFI_INFO_INDEX_HORIZONTAL_RESOLUTION, (CHAR8 *)&HorizontalResolution, + sizeof(HorizontalResolution)); + VBoxVgaGetVmVariable(EFI_INFO_INDEX_VERTICAL_RESOLUTION, (CHAR8 *)&VerticalResolution, + sizeof(VerticalResolution)); + for (Index = 0; Index < Private->MaxMode; Index++) + { + if ( HorizontalResolution == Private->ModeData[Index].HorizontalResolution + && VerticalResolution == Private->ModeData[Index].VerticalResolution + && ColorDepth == Private->ModeData[Index].ColorDepth) + break; + } + // not found? try mode number + if (Index >= Private->MaxMode) + { + VBoxVgaGetVmVariable(EFI_INFO_INDEX_GRAPHICS_MODE, (CHAR8 *)&Index, sizeof(Index)); + // try with mode 2 (usually 1024x768) as a fallback + if (Index >= Private->MaxMode) + Index = 2; + // try with mode 0 (usually 640x480) as a fallback + if (Index >= Private->MaxMode) + Index = 0; + + // get the resolution from the mode if valid + if (Index < Private->MaxMode) + { + HorizontalResolution = Private->ModeData[Index].HorizontalResolution; + VerticalResolution = Private->ModeData[Index].VerticalResolution; + ColorDepth = Private->ModeData[Index].ColorDepth; + } + } + + // skip mode setting completely if there is no valid mode + if (Index >= Private->MaxMode) + return EFI_UNSUPPORTED; + + UgaDraw->SetMode ( + UgaDraw, + HorizontalResolution, + VerticalResolution, + ColorDepth, + 60 + ); + + DrawLogo ( + Private, + Private->ModeData[Private->CurrentMode].HorizontalResolution, + Private->ModeData[Private->CurrentMode].VerticalResolution + ); + + PcdSet32S(PcdVideoHorizontalResolution, HorizontalResolution); + PcdSet32S(PcdVideoVerticalResolution, VerticalResolution); + + return EFI_SUCCESS; +} + |