From f21e0f89e27d34c16e1d1c706e007a5735afb6b5 Mon Sep 17 00:00:00 2001
From: Colin Xu <colin.xu@intel.com>
Date: Sun, 25 Apr 2021 12:50:17 -0600
Subject: [PATCH] OvmfPkg: add Platform GOP Policy

GOP driver has dependency on PlatformGopPolicy protocol, especially
the GetVbtData() interface, here we return the vbt from host opregion.

V2:
Fix VBT checksum so that the pass-through IGD can always use the correct
VBT matching the actual.

Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.com>
Signed-off-by: Colin Xu <colin.xu@intel.com>
---
 OvmfPkg/Include/Protocol/PlatformGopPolicy.h  |  68 ++++++
 OvmfPkg/OvmfPkg.dec                           |   1 +
 OvmfPkg/OvmfPkgX64.dsc                        |   1 +
 OvmfPkg/OvmfPkgX64.fdf                        |   1 +
 OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.c | 206 ++++++++++++++++++
 .../PlatformGopPolicy/PlatformGopPolicy.inf   |  50 +++++
 6 files changed, 327 insertions(+)
 create mode 100644 OvmfPkg/Include/Protocol/PlatformGopPolicy.h
 create mode 100644 OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.c
 create mode 100644 OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.inf

diff --git a/OvmfPkg/Include/Protocol/PlatformGopPolicy.h b/OvmfPkg/Include/Protocol/PlatformGopPolicy.h
new file mode 100644
index 000000000000..e29c8690fd7c
--- /dev/null
+++ b/OvmfPkg/Include/Protocol/PlatformGopPolicy.h
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c)  1999  - 2019, 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 that 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.    
+                                                                                   
+
+--*/
+
+/** @file
+**/
+
+#ifndef _PLATFORM_GOP_POLICY_PROTOCOL_H_
+#define _PLATFORM_GOP_POLICY_PROTOCOL_H_
+
+#define EFI_PLATFORM_GOP_POLICY_PROTOCOL_GUID \
+  { 0xec2e931b, 0x3281, 0x48a5, 0x81, 0x7, 0xdf, 0x8a, 0x8b, 0xed, 0x3c, 0x5d }
+
+#define PLATFORM_GOP_POLICY_PROTOCOL_REVISION_01 0x01
+#define PLATFORM_GOP_POLICY_PROTOCOL_REVISION_02 x0222
+
+#pragma pack(1)
+
+typedef enum {
+  LidClosed,
+  LidOpen,
+  LidStatusMax
+} LID_STATUS;
+
+typedef enum {
+  Docked,
+  UnDocked,
+  DockStatusMax
+} DOCK_STATUS;
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_PLATFORM_LID_STATUS) (
+   OUT LID_STATUS *CurrentLidStatus
+);
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_VBT_DATA) (
+   OUT EFI_PHYSICAL_ADDRESS *VbtAddress,
+   OUT UINT32 *VbtSize
+);
+
+#pragma pack()
+
+typedef struct _PLATFORM_GOP_POLICY_PROTOCOL {
+  UINT32                             Revision;
+  GET_PLATFORM_LID_STATUS            GetPlatformLidStatus;
+  GET_VBT_DATA                       GetVbtData;
+} PLATFORM_GOP_POLICY_PROTOCOL;
+
+//
+// Extern the GUID for protocol users.
+//
+extern EFI_GUID  gPlatformGOPPolicyGuid;
+
+#endif
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 6abde4fd9351..b46b5778e28e 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -136,6 +136,7 @@
   gEfiLegacyInterruptProtocolGuid       = {0x31ce593d, 0x108a, 0x485d, {0xad, 0xb2, 0x78, 0xf2, 0x1f, 0x29, 0x66, 0xbe}}
   gEfiVgaMiniPortProtocolGuid           = {0xc7735a2f, 0x88f5, 0x4882, {0xae, 0x63, 0xfa, 0xac, 0x8c, 0x8b, 0x86, 0xb3}}
   gOvmfLoadedX86LinuxKernelProtocolGuid = {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}}
+  gPlatformGOPPolicyGuid                = {0xec2e931b, 0x3281, 0x48a5, {0x81, 0x07, 0xdf, 0x8a, 0x8b, 0xed, 0x3c, 0x5d}}
 
 [PcdsFixedAtBuild]
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase|0x0|UINT32|0
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 565e0573b24d..f3a27f33f518 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -784,6 +784,7 @@
   OvmfPkg/XenBusDxe/XenBusDxe.inf
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
   OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf
+  OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.inf
 !if $(PVSCSI_ENABLE) == TRUE
   OvmfPkg/PvScsiDxe/PvScsiDxe.inf
 !endif
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 00098b18d911..a782f86ffec8 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -241,6 +241,7 @@ INF  OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
 INF  OvmfPkg/XenBusDxe/XenBusDxe.inf
 INF  OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
 INF  OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf
+INF  OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.inf
 !if $(PVSCSI_ENABLE) == TRUE
 INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
 !endif
diff --git a/OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.c b/OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.c
new file mode 100644
index 000000000000..d94f154c0279
--- /dev/null
+++ b/OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.c
@@ -0,0 +1,206 @@
+/*++
+
+Copyright (c)  1999  - 2019, 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 that 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.    
+                                                                                   
+
+--*/
+
+/** @file
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/PlatformGopPolicy.h>
+
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/PciLib.h>
+
+#include <IndustryStandard/AssignedIgd.h>
+#include <IndustryStandard/IgdOpRegion.h>
+
+PLATFORM_GOP_POLICY_PROTOCOL  mPlatformGOPPolicy;
+EFI_PHYSICAL_ADDRESS mVbt;
+
+//
+// Function implementations
+//
+
+/**
+  The function will execute with as the platform policy, and gives
+  the Platform Lid Status. IBV/OEM can customize this code for their specific
+  policy action.
+
+  @param CurrentLidStatus  Gives the current LID Status
+
+  @retval EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+GetPlatformLidStatus (
+   OUT LID_STATUS *CurrentLidStatus
+)
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  The function will execute and gives the Video Bios Table Size and Address.
+
+  @param VbtAddress  Gives the Physical Address of Video BIOS Table
+
+  @param VbtSize     Gives the Size of Video BIOS Table
+
+  @retval EFI_STATUS.
+
+**/
+
+EFI_STATUS
+EFIAPI
+GetVbtData (
+   OUT EFI_PHYSICAL_ADDRESS *VbtAddress,
+   OUT UINT32 *VbtSize
+)
+{
+  IGD_OPREGION_STRUCTURE *OpRegion;
+  EFI_STATUS Status = EFI_INVALID_PARAMETER;
+  UINT16 VerMajor, VerMinor = 0;
+  UINT32 VbtSizeMax = 0;
+
+  OpRegion = (IGD_OPREGION_STRUCTURE*)(UINTN)PciRead32 (
+    PCI_LIB_ADDRESS (
+      ASSIGNED_IGD_PCI_BUS,
+      ASSIGNED_IGD_PCI_DEVICE,
+      ASSIGNED_IGD_PCI_FUNCTION,
+      ASSIGNED_IGD_PCI_ASLS_OFFSET));
+
+  /* Validate IGD OpRegion signature and version */
+  if (OpRegion) {
+    if (CompareMem (OpRegion->Header.SIGN, IGD_OPREGION_HEADER_SIGN, sizeof(OpRegion->Header.SIGN)) != 0) {
+      DEBUG ((EFI_D_ERROR, "%a: Invalid OpRegion signature, expect %s\n",
+        __FUNCTION__, IGD_OPREGION_HEADER_SIGN));
+      return EFI_INVALID_PARAMETER;
+    } else {
+      VerMajor = OpRegion->Header.OVER >> 24;
+      VerMinor = OpRegion->Header.OVER >> 16 & 0xff;
+      if (VerMajor < 2 || OpRegion->MBox3.RVDA == 0) {
+        VbtSizeMax = IGD_OPREGION_VBT_SIZE_6K;
+        if (((VBT_HEADER*)&OpRegion->MBox4)->Table_Size > IGD_OPREGION_VBT_SIZE_6K) {
+          DEBUG ((EFI_D_ERROR, "%a: VBT Header reports larger size (0x%x) than OpRegion VBT Mailbox (0x%x)\n",
+            __FUNCTION__,
+            ((VBT_HEADER*)&OpRegion->MBox4)->Table_Size, IGD_OPREGION_VBT_SIZE_6K));
+          VbtSizeMax = 0;
+          return EFI_INVALID_PARAMETER;
+        }
+      } else {
+        DEBUG ((EFI_D_ERROR, "%a: Unsupported OpRegion version %d.%d\n",
+          __FUNCTION__, VerMajor, VerMinor));
+        return EFI_UNSUPPORTED;
+      }
+    }
+  }
+
+  if (mVbt) {
+    Status = gBS->FreePages (
+                    mVbt,
+                    EFI_SIZE_TO_PAGES (VbtSizeMax)
+                    );
+  }
+
+  if (VbtSizeMax == IGD_OPREGION_VBT_SIZE_6K) {
+    mVbt = SIZE_4GB - 1;
+  }
+
+  /* Only operates VBT on support OpRegion */
+  if (VbtSizeMax) {
+    Status = gBS->AllocatePages (
+                    AllocateMaxAddress,
+                    EfiReservedMemoryType,
+                    EFI_SIZE_TO_PAGES (VbtSizeMax),
+                    &mVbt
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "%a: AllocatePages failed for VBT size 0x%x status %d\n",
+        __FUNCTION__, VbtSizeMax, Status));
+      return EFI_OUT_OF_RESOURCES;
+    } else {
+      UINT8 CheckSum = 0;
+
+      /* Zero-out first*/
+      ZeroMem ((VOID*)mVbt, VbtSizeMax);
+      /* Only copy with size as specified in VBT table */
+      CopyMem((VOID*)mVbt, (VOID*)OpRegion->MBox4.RVBT, ((VBT_HEADER*)&OpRegion->MBox4)->Table_Size);
+
+      /* Fix the checksum */
+      for (UINT32 i = 0; i < ((VBT_HEADER*)mVbt)->Table_Size; i++) {
+        CheckSum = (CheckSum + ((UINT8*)mVbt)[i]) & 0xFF;
+      }
+      ((VBT_HEADER*)mVbt)->Checksum += (0x100 - CheckSum);
+
+      *VbtAddress = mVbt;
+      *VbtSize = ((VBT_HEADER*)mVbt)->Table_Size;
+      DEBUG ((DEBUG_INFO, "%a: VBT Version %d size 0x%x\n", __FUNCTION__,
+        ((VBT_BIOS_DATA_HEADER*)(mVbt + ((VBT_HEADER*)mVbt)->Bios_Data_Offset))->BDB_Version,
+        ((VBT_HEADER*)mVbt)->Table_Size));
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Entry point for the Platform GOP Policy Driver.
+
+  @param ImageHandle       Image handle of this driver.
+  @param SystemTable       Global system service table.
+
+  @retval EFI_SUCCESS           Initialization complete.
+  @retval EFI_OUT_OF_RESOURCES  Do not have enough resources to initialize the driver.
+
+**/
+
+EFI_STATUS
+EFIAPI
+PlatformGOPPolicyEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+
+{
+  EFI_STATUS  Status = EFI_SUCCESS;
+
+  gBS = SystemTable->BootServices;
+
+  gBS->SetMem (
+         &mPlatformGOPPolicy,
+         sizeof (PLATFORM_GOP_POLICY_PROTOCOL),
+         0
+         );
+
+  mPlatformGOPPolicy.Revision                = PLATFORM_GOP_POLICY_PROTOCOL_REVISION_01;
+  mPlatformGOPPolicy.GetPlatformLidStatus    = GetPlatformLidStatus;
+  mPlatformGOPPolicy.GetVbtData              = GetVbtData;
+
+  //
+  // Install protocol to allow access to this Policy.
+  //  
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &ImageHandle,
+                  &gPlatformGOPPolicyGuid,
+                  &mPlatformGOPPolicy,
+                  NULL
+                  );
+
+  return Status;
+}
diff --git a/OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.inf b/OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.inf
new file mode 100644
index 000000000000..705b4960b8cd
--- /dev/null
+++ b/OvmfPkg/PlatformGopPolicy/PlatformGopPolicy.inf
@@ -0,0 +1,50 @@
+#
+#
+# Copyright (c)  1999  - 2019, 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 that 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                      = PlatformGOPPolicy
+  FILE_GUID                      = 9737D7CA-D869-45e5-A5EF-75D9438688DE
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PlatformGOPPolicyEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32
+#
+
+[Sources.common]
+  PlatformGopPolicy.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  UefiDriverEntryPoint
+  UefiRuntimeServicesTableLib
+  PciLib
+
+[Protocols]
+  gPlatformGOPPolicyGuid
+
+[Depex]
+  TRUE
-- 
2.31.1

