/** @file Override ACPI Tables for L05 Feature ;****************************************************************************** ;* Copyright (c) 2019, Insyde Software Corporation. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "AcpiOverride.h" EFI_ACPI_SUPPORT_PROTOCOL *mAcpiSupport = NULL; EFI_ACPI_SET_ACPI_TABLE mOrgSetAcpiTable = NULL; EFI_ACPI_TABLE_PROTOCOL *mAcpiTableProtocol = NULL; EFI_ACPI_TABLE_INSTALL_ACPI_TABLE mOrgInstallAcpiTable = NULL; PREFERRED_PM_PROFILE_CONVERT_TABLE mPreferredPmProfileConvertTable[] = { // SmbiosType03Type, PreferredPmProfile {MiscChassisTypeOther, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypeUnknown, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypeDeskTop, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypeLowProfileDesktop, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypePizzaBox, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypeMiniTower, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypeTower, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypePortable, EFI_ACPI_6_1_PM_PROFILE_MOBILE }, {MiscChassisTypeLapTop, EFI_ACPI_6_1_PM_PROFILE_MOBILE }, {MiscChassisTypeNotebook, EFI_ACPI_6_1_PM_PROFILE_MOBILE }, {MiscChassisTypeHandHeld, EFI_ACPI_6_1_PM_PROFILE_MOBILE }, {MiscChassisTypeDockingStation, EFI_ACPI_6_1_PM_PROFILE_WORKSTATION }, {MiscChassisTypeAllInOne, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypeSubNotebook, EFI_ACPI_6_1_PM_PROFILE_MOBILE }, {MiscChassisTypeSpaceSaving, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypeLunchBox, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisTypeMainServerChassis, EFI_ACPI_6_1_PM_PROFILE_ENTERPRISE_SERVER }, {MiscChassisTypeExpansionChassis, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypeSubChassis, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypeBusExpansionChassis, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypePeripheralChassis, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypeRaidChassis, EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED }, {MiscChassisTypeRackMountChassis, EFI_ACPI_6_1_PM_PROFILE_ENTERPRISE_SERVER }, {MiscChassisTypeSealedCasePc, EFI_ACPI_6_1_PM_PROFILE_DESKTOP }, {MiscChassisMultiSystemChassis, EFI_ACPI_6_1_PM_PROFILE_SOHO_SERVER }, {MiscChassisCompactPCI, EFI_ACPI_6_1_PM_PROFILE_APPLIANCE_PC }, {MiscChassisAdvancedTCA, EFI_ACPI_6_1_PM_PROFILE_PERFORMANCE_SERVER}, {MiscChassisBlade, EFI_ACPI_6_1_PM_PROFILE_PERFORMANCE_SERVER}, {MiscChassisBladeEnclosure, EFI_ACPI_6_1_PM_PROFILE_PERFORMANCE_SERVER}, {MiscChassisTablet, EFI_ACPI_6_1_PM_PROFILE_TABLET }, {MiscChassisConvertible, EFI_ACPI_6_1_PM_PROFILE_MOBILE }, {MiscChassisDetachable, EFI_ACPI_6_1_PM_PROFILE_TABLET } }; UINTN mPreferredPmProfileConvertTableCount = sizeof (mPreferredPmProfileConvertTable) / sizeof (PREFERRED_PM_PROFILE_CONVERT_TABLE); /** Override PS2 Keyboard HID or PS2 Touch Pad HID, CID on DSDT Table. @param DsdtTable The pointer to the DSDT table. @retval EFI_SUCCESS Return Success by Default. The return status will not be referenced. **/ EFI_STATUS L05UpdateHidCidInDsdtTable ( IN EFI_ACPI_DESCRIPTION_HEADER *DsdtTable ) { EFI_STATUS Status; UINT8 *DsdtPointer; UINT64 *Signature; UINT32 *Signature32; UINT64 OldKeyBoardHid; UINT64 NewKeyBoardHid; UINT64 OldTouchPadHid; UINT64 NewTouchPadHid; UINT16 NewTouchPadCid; UINT8 OffsetCid; UINT8 CidId[2]; BOOLEAN UpdateKeyBoardHid; BOOLEAN UpdateTouchPadHid; BOOLEAN UpdateTouchPadCid; Status = EFI_SUCCESS; DsdtPointer = NULL; Signature = NULL; Signature32 = NULL; OldKeyBoardHid = 0; NewKeyBoardHid = 0; OldTouchPadHid = 0; NewTouchPadHid = 0; NewTouchPadCid = 0; OffsetCid = 0; UpdateKeyBoardHid = FALSE; UpdateTouchPadHid = FALSE; UpdateTouchPadCid = FALSE; // // Get new Hardware ID & Compatible ID for Keyboard and determine update it or not // Status = OemSvcUpdatePs2KeyBoardHid ( &OldKeyBoardHid, &NewKeyBoardHid ); switch (Status) { case EFI_MEDIA_CHANGED: UpdateKeyBoardHid = TRUE; break; case EFI_UNSUPPORTED: default: break; } // // Get new Hardware ID & Compatible ID for Touch Pad and determine update it or not // Status = OemSvcUpdatePs2TouchPadHidCid ( &OldTouchPadHid, &NewTouchPadHid, &NewTouchPadCid, &OffsetCid ); switch (Status) { case EFI_MEDIA_CHANGED: UpdateTouchPadHid = TRUE; if (OffsetCid != 0x00) { UpdateTouchPadCid = TRUE; CidId[0] = (UINT8) ((NewTouchPadCid) & 0xFF); CidId[1] = (UINT8) ((NewTouchPadCid >> 8) & 0xFF); NewTouchPadCid = (UINT16) (CidId[0] << 8) + CidId[1]; } break; case EFI_UNSUPPORTED: default: break; } // // Do not execute this function until OemSvc be porting as above // if (!UpdateKeyBoardHid && !UpdateTouchPadHid) { return EFI_SUCCESS; } // // Set new Hardware ID & Compatible ID for Keyboard or Touch Pad // for (DsdtPointer = (UINT8 *) ((UINTN) DsdtTable + sizeof (EFI_ACPI_DESCRIPTION_HEADER)); DsdtPointer <= (UINT8 *) ((UINTN) DsdtTable + (UINTN) (DsdtTable->Length)); DsdtPointer++) { Signature = (UINT64 *) DsdtPointer; Signature32 = (UINT32 *) DsdtPointer; // // Update Keyboard HID // if (UpdateKeyBoardHid) { if (*Signature == OldKeyBoardHid) { *Signature = NewKeyBoardHid; UpdateKeyBoardHid = FALSE; } } // // Update Touch Pad HID // if (UpdateTouchPadHid) { if (*Signature == OldTouchPadHid) { *Signature = NewTouchPadHid; UpdateTouchPadHid = FALSE; } // // Update Touch Pad CID // if (!UpdateTouchPadCid) { continue; } switch (*Signature32) { case (SIGNATURE_32 ('P', 'S', '2', 'M')) : * (UINT16 *) (DsdtPointer + OffsetCid) = NewTouchPadCid; UpdateTouchPadCid = FALSE; break; default: break; } } // // End the for loop previously if all HID or CID be updated. // if (!UpdateKeyBoardHid && !UpdateTouchPadHid && !UpdateTouchPadCid) { break; } } return EFI_SUCCESS; } /** Convert FADT "Preferred_PM_Profile" vlaue from SMBIOS Type 3 Offset 05h Type. @param SmbiosType03Type SMBIOS Type 3 Offset 05h Type. @retval PreferredPmProfile Return convert "Preferred_PM_Profile" value. **/ UINT8 L05PreferredPmProfileConvert ( IN UINT8 SmbiosType03Type ) { UINT8 PreferredPmProfile; UINTN Index; PreferredPmProfile = EFI_ACPI_6_1_PM_PROFILE_UNSPECIFIED; for (Index = 0; Index < mPreferredPmProfileConvertTableCount; Index++) { if (mPreferredPmProfileConvertTable[Index].SmbiosType03Type == SmbiosType03Type) { PreferredPmProfile = mPreferredPmProfileConvertTable[Index].PreferredPmProfile; break; } } return PreferredPmProfile; } /** Override ACPI Table for L05 requirement. @param Table The pointer to the new table to add or update. @param Version Indicates to which version(s) of ACPI the table should be added. @retval EFI_SUCCESS This ACPI Table be overrided. @retval EFI_ALREADY_STARTED This ACPI Table do not be overrided. @retval EFI_UNSUPPORTED This ACPI Table need be removed. @retval EFI_INVALID_PARAMETER This ACPI Table can't be NULL before modified data. **/ EFI_STATUS L05ModifyAcpiTable ( IN OUT VOID *Table, IN OUT EFI_ACPI_TABLE_VERSION *Version ) { EFI_STATUS Status; EFI_ACPI_DESCRIPTION_HEADER *TableHeader; UINT64 OemId; UINT64 OemTableId; EFI_L05_DXE_SLP_20_PROTOCOL *L05Slp20Ptr; UINT8 L05Type03Type; EFI_ACPI_6_1_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer; if (Table == NULL) { return EFI_INVALID_PARAMETER; } TableHeader = (EFI_ACPI_DESCRIPTION_HEADER *) Table; Status = EFI_UNSUPPORTED; OemId = EFI_L05_ACPI_OEM_ID; OemTableId = EFI_L05_ACPI_OEM_TABLE_ID; L05Slp20Ptr = NULL; FadtPointer = NULL; switch (TableHeader->Signature) { case EFI_ACPI_5_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE: // "FACS" case 0: case SIGNATURE_32('S', 'C', 'T', 'T'): // "SCTT" case EFI_ACPI_5_0_PLATFORM_BINARY_TABLE_SIGNATURE: // "WPBT" // // Ignore to modify the table when: // // 1. The signature is "FACS" // // 2. The signature is 0 or "SCTT": // Regard it as a dummy table. // Actually, UEFI SCT - ACPI test calls InstallAcpiTable () with a dummmy table - all fields are zero except Checksum and Length. // In order to pass No. 5.16.1.1.5 Test in UEFI 2.3.1 Self Certification Test (SCT) Case Specification Ver.1.2, Set.17.1.1 InstallAcpiTable (), // feature should not modify the content of the table and should leave all of the necessary actions to Core code. // // 3. The signature is "WPBT": // Computrace binary will generate this table, do not modify. // return EFI_ALREADY_STARTED; break; case EFI_ACPI_5_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE: // // Provide a hook for updating PS2 Keyboard HID or PS2 Touch Pad HID, CID if Project need. // L05UpdateHidCidInDsdtTable (TableHeader); break; case EFI_ACPI_5_0_SOFTWARE_LICENSING_TABLE_SIGNATURE: // // By L05 OA Requirement 009, Section 1.1 // Feature must not install SLIC Table if no valid Marker injected in BIOS. // Status = gBS->LocateProtocol (&gEfiL05DxeSlp20ProtocolGuid, NULL, &L05Slp20Ptr); if (!EFI_ERROR (Status)) { if (!L05Slp20Ptr->IsValidMarker (Table)) { return EFI_UNSUPPORTED; } } break; case EFI_ACPI_6_1_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE: FadtPointer = (EFI_ACPI_6_1_FIXED_ACPI_DESCRIPTION_TABLE *) Table; // // By L05 Minimum BIOS Spec V1.37 3.3.7 ACPI Table // ACPI FADT "Preferred_PM_Profile" field need to be customized for Lenovo PCs. // The field should be synchronized with SMBIOS Type 3 offset 5. // L05Type03Type = PcdGet8 (PcdL05Type03Type); FadtPointer->PreferredPmProfile = L05PreferredPmProfileConvert (L05Type03Type); break; default: // // Do Nothing. // break; } // // Call OemSvc for project modify ACPI table // Status = OemSvcAcpiTableUpdate (Table); switch (Status) { case EFI_MEDIA_CHANGED: break; case EFI_UNSUPPORTED: default: // // By L05 Minimum BIOS Spec V1.26 3.4.8 ACPI Table // All of ACPI tables OEM information field need to be customized. // CopyMem (&TableHeader->OemId, &OemId, sizeof (TableHeader->OemId)); CopyMem (&TableHeader->OemTableId, &OemTableId, sizeof (UINT64)); TableHeader->OemRevision = PcdGet32 (PcdL05AcpiTableOemRevision); break; } return EFI_SUCCESS; } /** Used to add, remove, or update ACPI tables after be overrided by L05 feature. @param This A pointer to the EFI_ACPI_SUPPORT_PROTOCOL instance. @param Table The pointer to the new table to add or update. @param Checksum If TRUE, indicates that the checksum should be calculated for this table. @param Version Indicates to which version(s) of ACPI the table should be added. @param Handle The pointer to the handle of the table to remove or update. @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER *Handle was zero and Table was NULL. @retval EFI_ABORTED Could not complete the desired action. **/ EFI_STATUS EFIAPI L05SetAcpiTableHook ( IN EFI_ACPI_SUPPORT_PROTOCOL *This, IN VOID *Table OPTIONAL, IN BOOLEAN Checksum, IN EFI_ACPI_TABLE_VERSION Version, IN OUT UINTN *Handle ) { EFI_STATUS Status; Status = L05ModifyAcpiTable (Table, &Version); if (!EFI_ERROR (Status)) { // // Force to calculate checksum if ACPI Table be modified // Checksum = TRUE; } if (Status == EFI_UNSUPPORTED) { // // Do not install ACPI table if status is not Success // return EFI_SUCCESS; } return mOrgSetAcpiTable (This, Table, Checksum, Version, Handle); } /** Installs an ACPI table into the RSDT/XSDT after be overrided by L05 feature. @param This Protocol instance pointer. @param AcpiTableBuffer A pointer to a buffer containing the ACPI table to be installed. @param AcpiTableBufferSize Specifies the size, in bytes, of the AcpiTableBuffer buffer. @param TableKey Reurns a key to refer to the ACPI table. @retval EFI_SUCCESS The table was successfully inserted. @retval EFI_INVALID_PARAMETER Either AcpiTableBuffer is NULL, TableKey is NULL, or AcpiTableBufferSize and the size field embedded in the ACPI table pointed to by AcpiTableBuffer are not in sync. @retval EFI_OUT_OF_RESOURCES Insufficient resources exist to complete the request. @retval EFI_ACCESS_DENIED The table signature matches a table already present in the system and platform policy does not allow duplicate tables of this type. **/ EFI_STATUS EFIAPI L05InstallAcpiTableHook ( IN EFI_ACPI_TABLE_PROTOCOL *This, IN VOID *AcpiTableBuffer, IN UINTN AcpiTableBufferSize, OUT UINTN *TableKey ) { EFI_STATUS Status; Status = L05ModifyAcpiTable (AcpiTableBuffer, 0); if (Status == EFI_UNSUPPORTED) { // // Do not install ACPI table if status is not Success // return EFI_SUCCESS; } return mOrgInstallAcpiTable (This, AcpiTableBuffer, AcpiTableBufferSize, TableKey); } /** This main operation for override ACPI Tables @retval EFI_SUCCESS The operation completed successfully. **/ EFI_STATUS AcpiOverrideCore ( VOID ) { EFI_STATUS Status; EFI_ACPI_DESCRIPTION_HEADER *Table; EFI_ACPI_TABLE_VERSION Version; ACPI_TABLE_INFO *AcpiTableInfo; UINTN NumberOfTable; UINTN Handle; UINTN Index; Status = EFI_SUCCESS; Table = NULL; Version = 0; AcpiTableInfo = NULL; NumberOfTable = 0; Handle = 0; Index = 0; // // 1. Hook origin SetAcpiTable & InstallAcpiTable function // mOrgSetAcpiTable = mAcpiSupport->SetAcpiTable; mAcpiSupport->SetAcpiTable = L05SetAcpiTableHook; mOrgInstallAcpiTable = mAcpiTableProtocol->InstallAcpiTable; mAcpiTableProtocol->InstallAcpiTable = L05InstallAcpiTableHook; // // 2. Calculate total tables // while (TRUE) { Status = mAcpiSupport->GetAcpiTable (mAcpiSupport, NumberOfTable, &Table, &Version, &Handle); if (Status == EFI_NOT_FOUND) { break; } NumberOfTable++; if (Table != NULL) { gBS->FreePool (Table); Table = NULL; } } // // 3. Get all table data // Status = gBS->AllocatePool (EfiBootServicesData, sizeof (ACPI_TABLE_INFO) * NumberOfTable, (VOID **) &AcpiTableInfo); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < NumberOfTable; Index++) { Status = mAcpiSupport->GetAcpiTable (mAcpiSupport, Index, (VOID **) &Table, &Version, &Handle); if (Status == EFI_NOT_FOUND) { break; } AcpiTableInfo[Index].Table = Table; AcpiTableInfo[Index].Version = Version; AcpiTableInfo[Index].Handle = Handle; } // // 4. Re-Set exist ACPI Table from last table for L05 feature requirement // for (Index = 0; Index < NumberOfTable; Index++) { Table = AcpiTableInfo[Index].Table; Version = AcpiTableInfo[Index].Version; Handle = AcpiTableInfo[Index].Handle; Status = L05ModifyAcpiTable (Table, &Version); if (!EFI_ERROR (Status)) { mOrgSetAcpiTable (mAcpiSupport, Table, TRUE, Version, &Handle); } if (Status == EFI_UNSUPPORTED) { mOrgSetAcpiTable (mAcpiSupport, NULL, TRUE, Version, &Handle); } if (Table != NULL) { gBS->FreePool (Table); Table = NULL; } } if (AcpiTableInfo != NULL) { gBS->FreePool (AcpiTableInfo); Table = NULL; } return EFI_SUCCESS; } /** This is the event callback function for make sure ACPI Support Protocol and ACPI Table Protocol is ready for override ACPI Tables @param Event Pointer to this event @param Context Event hanlder private data **/ VOID EFIAPI AcpiOverrideCallBack ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; Status = gBS->LocateProtocol (&gEfiAcpiSupportProtocolGuid, NULL, &mAcpiSupport); if (EFI_ERROR (Status)) { return; } Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, &mAcpiTableProtocol); if (EFI_ERROR (Status)) { return; } // // There is no necessary to trigger this callback function again in any case. // gBS->CloseEvent (Event); AcpiOverrideCore (); return; } /** Callback from checkpoint H2O_DXE_CP_UPDATE_ACPI_DESC_HDR_DATA for L05 update OEM Table ID information @param[in] Event The Event this notify function registered to. @param[in] Handle The handle associated with a previously registered checkpoint handler. **/ VOID EFIAPI L05DxeCpUpdateAcpiDescHdrCallback ( IN EFI_EVENT Event, IN H2O_CP_HANDLE Handle ) { EFI_STATUS Status; H2O_DXE_CP_UPDATE_ACPI_DESC_HDR_DATA *DxeCpUpdateAcpiDescHdrData; EFI_GUID DxeCpUpdateAcpiDescHdrGuid; // // Get checkpoint data by H2OCpLookup() if need use checkpoint data in callback function // Status = H2OCpLookup (Handle, (VOID **) &DxeCpUpdateAcpiDescHdrData, &DxeCpUpdateAcpiDescHdrGuid); if (EFI_ERROR (Status)) { DEBUG_CP ((DEBUG_ERROR, "Checkpoint Data Not Found: %x (%r)\n", Handle, Status)); DEBUG_CP ((DEBUG_ERROR, " %a\n", __FUNCTION__)); return; } DxeCpUpdateAcpiDescHdrData->Status = H2O_CP_TASK_UPDATE; Status = L05ModifyAcpiTable ((VOID *) DxeCpUpdateAcpiDescHdrData->AcpiTableHeader, NULL); DEBUG_CP ((DEBUG_INFO, "Checkpoint Data Updated: %g\n", &DxeCpUpdateAcpiDescHdrGuid)); DEBUG_CP ((DEBUG_INFO, " %a\n", __FUNCTION__)); } /** This is the declaration of an EFI image entry point. This entry point is the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including both device drivers and bus drivers. @param ImageHandle The firmware allocated handle for the UEFI image. @param SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The operation completed successfully. @retval Others An unexpected error occurred. **/ EFI_STATUS EFIAPI AcpiOverrideDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; VOID *Registration; Status = EFI_SUCCESS; Registration = NULL; // // Register Event for override ACPI Tables. // EfiCreateProtocolNotifyEvent ( &gPublishAcpiTableDoneProtocolGuid, TPL_CALLBACK, AcpiOverrideCallBack, NULL, &Registration ); if (PcdGetBool (PcdH2ODxeCpUpdateAcpiDescHdrSupported)) { H2O_CP_HANDLE CpHandle; Status = H2OCpRegisterHandler ( &gH2ODxeCpUpdateAcpiDescHdrGuid, L05DxeCpUpdateAcpiDescHdrCallback, H2O_CP_MEDIUM, &CpHandle ); if (EFI_ERROR (Status)) { DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2ODxeCpUpdateAcpiDescHdrGuid, Status)); return Status; } DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2ODxeCpUpdateAcpiDescHdrGuid, Status)); } return EFI_SUCCESS; }