/** @file ;****************************************************************************** ;* Copyright (c) 2013 - 2020, Insyde Software Corp. 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include EFI_ACPI_S3_SAVE mS3Save; /** To get PersistentMemory Pages. @param[out] PersistentMemPages PersistentMemory Pages @return EFI_SUCCESS Get PersistentMemory Pages success @return Other Get PersistentMemory Pages fail. **/ EFI_STATUS GetPersistentMemoryPages ( OUT UINT64 *PersistentMemPages ) { EFI_STATUS Status; EFI_MEMORY_DESCRIPTOR *EfiMemoryMap; UINTN EfiMapKey; UINTN EfiDescriptorSize; UINTN EfiMemoryMapSize; EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; EFI_MEMORY_DESCRIPTOR *EfiEntry; UINT32 EfiDescriptorVersion; EfiMemoryMapSize = 0; EfiMemoryMap = NULL; Status = gBS->GetMemoryMap ( &EfiMemoryMapSize, EfiMemoryMap, &EfiMapKey, &EfiDescriptorSize, &EfiDescriptorVersion ); while (Status == EFI_BUFFER_TOO_SMALL) { EfiMemoryMap = AllocatePool (EfiMemoryMapSize); if (EfiMemoryMap == NULL) { return EFI_OUT_OF_RESOURCES; } Status = gBS->GetMemoryMap ( &EfiMemoryMapSize, EfiMemoryMap, &EfiMapKey, &EfiDescriptorSize, &EfiDescriptorVersion ); if (EFI_ERROR (Status)) { FreePool (EfiMemoryMap); EfiMemoryMap = NULL; } } if (EfiMemoryMap == NULL) { return EFI_ABORTED; } EfiEntry = EfiMemoryMap; EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize); *PersistentMemPages = 0; while (EfiEntry < EfiMemoryMapEnd) { if (EfiEntry->Type == EfiPersistentMemory){ *PersistentMemPages += EfiEntry->NumberOfPages; } EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize); } FreePool (EfiMemoryMap); return Status; } /** To get secure boot state hardware signature value. @return UINT32 Hardware signature value for secure boot state. **/ STATIC UINT32 GetSecureBootHardwareSignatureValue ( VOID ) { UINT8 SecureBoot; UINTN BufferSize; EFI_STATUS Status; UINT8 Buffer[21]; UINT32 Crc32; // // Using L"SecureBoot" and variable data to calculate CRC32 value fro secure boot state hardware // signature to identify the differences between other hardware signature settings. // BufferSize = sizeof (UINT8); Status = gRT->GetVariable (L"SecureBoot", &gEfiGlobalVariableGuid, NULL, &BufferSize, &SecureBoot); if (EFI_ERROR (Status)) { SecureBoot = 0xFF; } CopyMem (Buffer, L"SecureBoot", StrLen (L"SecureBoot") * sizeof (CHAR16)); Buffer [StrLen (L"SecureBoot") * sizeof (CHAR16)] = SecureBoot; Crc32 = 0; Status = gBS->CalculateCrc32 (Buffer, StrLen (L"SecureBoot") * sizeof (CHAR16) + sizeof (UINT8), &Crc32); ASSERT_EFI_ERROR (Status); return Crc32; } /** To get GOP resolution hardware signature value. @return Hardware signature value for GOP resolution or zero if there is no GOP on system. **/ UINT32 GetGopResolutionHardwareSignatureValue ( VOID ) { EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **) &GraphicsOutput); if (EFI_ERROR(Status)) { return 0; } return (GraphicsOutput->Mode->Info->HorizontalResolution * 2) + GraphicsOutput->Mode->Info->VerticalResolution; } /** To get TPM hardware signature value. @return Hardware signature value for TPM **/ UINT32 GetTpmHardwareSignatureValue ( VOID ) { EFI_STATUS Status; UINT32 Crc32; Crc32 = 0; Status = gBS->CalculateCrc32 ((VOID *)PcdGetPtr (PcdTpmInstanceGuid), sizeof (EFI_GUID), &Crc32); ASSERT_EFI_ERROR (Status); return Crc32; } /** To get BIOS version hardware signature value. @return UINT32 Hardware signature value for BIOS version. **/ STATIC UINT32 GetBiosVersionSignatureValue ( VOID ) { UINTN StrLen; CHAR16 Str[BVDT_MAX_STR_SIZE]; EFI_STATUS Status; UINT32 Crc32; StrLen = BVDT_MAX_STR_SIZE; Status = GetBvdtInfo ((BVDT_TYPE) BvdtBiosVer, &StrLen, Str); if (EFI_ERROR (Status)) { return 0; } Crc32 = 0; Status = gBS->CalculateCrc32 (Str, StrLen * sizeof (CHAR16), &Crc32); ASSERT_EFI_ERROR (Status); return Crc32; } /** To determine Hardware Signature by kernel default algorithm. @param[out] HardwareSignature Value of Hardware Signature. @retval None. **/ VOID DefaultUpdateAcpiFacsHardwareSignature ( OUT UINT32 *HardwareSignature ) { EFI_STATUS Status; UINTN Bus; UINTN BusLimit; UINTN Device; UINTN DeviceLimit; UINTN Function; UINTN FunctionLimit; UINT16 ClassCode; UINT8 HeaderType; UINT8 Value8; UINT64 PersistentMemPages; Bus = 0; BusLimit = 0; Device = 0; DeviceLimit = 0; Function = 0; FunctionLimit = 0; ClassCode = 0; HeaderType = 0; Value8 = 0; // // Set Hardware Signature, the algorithm to calculate signature value depends on customization. // *HardwareSignature = 0; BusLimit = 1; for ( Bus = 0 ; Bus <= BusLimit ; Bus++ ) { for ( Device = 0 ; Device <= 0x1F ; Device++ ) { FunctionLimit = 7; for ( Function = 0 ; Function <= FunctionLimit ; Function++ ) { // // 1. Check header type & class code // HeaderType = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS ( Bus, Device, Function, PCI_HEADER_TYPE_OFFSET)); ClassCode = PciExpressRead16 (PCI_EXPRESS_LIB_ADDRESS ( Bus, Device, Function, PCI_CLASSCODE_OFFSET + 1)); if (Function == 0) { if (ClassCode == 0xFFFF) { break; } if (!(HeaderType & HEADER_TYPE_MULTI_FUNCTION)) { FunctionLimit = 0; } } if (ClassCode == 0xFFFF) { continue; } if (ClassCode == ((PCI_CLASS_BRIDGE << 8) | PCI_CLASS_BRIDGE_P2P)) { Value8 = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS ( Bus, Device, Function, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET)); if (Value8 > BusLimit) { BusLimit = Value8; } } *HardwareSignature += PciExpressRead16 (PCI_EXPRESS_LIB_ADDRESS ( Bus, Device, Function, PCI_VENDOR_ID_OFFSET)); *HardwareSignature += PciExpressRead16 (PCI_EXPRESS_LIB_ADDRESS ( Bus, Device, Function, PCI_DEVICE_ID_OFFSET)); *HardwareSignature += PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS ( Bus, Device, Function, PCI_REVISION_ID_OFFSET)); } } } *HardwareSignature += GetSecureBootHardwareSignatureValue (); *HardwareSignature += GetGopResolutionHardwareSignatureValue (); *HardwareSignature += GetTpmHardwareSignatureValue (); *HardwareSignature += GetBiosVersionSignatureValue (); // // ACPI 6.0 add PersistentMemory included FACS hardware signature. // PersistentMemPages = 0; Status = GetPersistentMemoryPages (&PersistentMemPages); if (!EFI_ERROR (Status)) { *HardwareSignature += (UINT32) PersistentMemPages; } } /** Internal function to initialize H2O_BDS_CP_UPDATE_ACPI_FACS_DATA data and trigger gH2OBdsCpUpdateAcpiFacsGuid checkpoint. @param[in] AcpiFacs Pointer to ACPI FACS table. @retval EFI_SUCCESS Trigger gH2OBdsCpLegacyPrepareToBootAfterGuid checkpoint successfully. @return Other Other error occurred while triggering gH2OBdsCpLegacyPrepareToBootAfterGuid checkpoint. **/ STATIC EFI_STATUS TriggerBdsCpUpdateAcpiFacs ( IN EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *AcpiFacs ) { EFI_STATUS Status; H2O_BDS_CP_UPDATE_ACPI_FACS_DATA CpData; EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE AcpiFacsBuf; CopyMem (&AcpiFacsBuf, AcpiFacs, sizeof (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE)); CpData.Size = sizeof (H2O_BDS_CP_UPDATE_ACPI_FACS_DATA); CpData.Status = H2O_CP_TASK_NORMAL; CpData.AcpiFacs = &AcpiFacsBuf; DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpUpdateAcpiFacsGuid)); Status = H2OCpTrigger (&gH2OBdsCpUpdateAcpiFacsGuid, &CpData); DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", CpData.Status)); if (CpData.Status == H2O_CP_TASK_UPDATE) { CopyMem (AcpiFacs, CpData.AcpiFacs, sizeof (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE)); } return Status; } /** Update FACS Hardware Signature @return None. **/ VOID EFIAPI UpdateFacsHardwareSignatureFn ( VOID ) { EFI_STATUS Status; EFI_ACPI_DESCRIPTION_HEADER *Table; INTN Index; UINTN Handle; EFI_ACPI_TABLE_VERSION Version; EFI_ACPI_SUPPORT_PROTOCOL *AcpiSupport; Index = 0; Status = gBS->LocateProtocol (&gEfiAcpiSupportProtocolGuid, NULL, (VOID **)&AcpiSupport); ASSERT_EFI_ERROR (Status); // // Search for FACS table // while (1) { Table = NULL; Status = AcpiSupport->GetAcpiTable (AcpiSupport, Index, (VOID **)&Table, &Version, &Handle); if (Status == EFI_NOT_FOUND) { break; } // // Updates once the table found // if (Table->Signature == EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) { // // Update Hardware Signature with kernel default algorithm. // DefaultUpdateAcpiFacsHardwareSignature ( &(((EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)Table)->HardwareSignature) ); if (FeaturePcdGet (PcdH2OBdsCpUpdateAcpiFacsSupported)) { TriggerBdsCpUpdateAcpiFacs ((EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)Table); } // // OemServices for updating Hardware Signature. // DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcUpdateAcpiFacsHardwareSignature \n")); Status = OemSvcUpdateAcpiFacsHardwareSignature ( &(((EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)Table)->HardwareSignature) ); DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcUpdateAcpiFacsHardwareSignature Status: %r\n", Status)); if (!EFI_ERROR (Status)) { // // Oemservices has already updated hardware signature so free the acpi table. // FreePool ( Table ); return ; } // // If OemServices status is not EFI_SUCCESS (EFI_MEDIA_CHANGED or EFI_UNSUPPORTED), // kernel will update hardware signature. // AcpiSupport->SetAcpiTable (AcpiSupport, Table, TRUE, Version, &Handle); FreePool ( Table ); return ; } FreePool ( Table ); Index++; } } /** Recheck Update FACS Hardware Signature event. @param Event Event instance @param Context Event Context. @return None. **/ STATIC VOID EFIAPI FacsHwSignReadyToBootCallBack ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save; gBS->CloseEvent (Event); Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **)&AcpiS3Save); if (EFI_ERROR (Status)) { UpdateFacsHardwareSignatureFn(); } } /** Update FACS Hardware Signature, and call S3Save function. @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance. @param LegacyMemoryAddress The base address of legacy memory. @return None. **/ VOID EFIAPI UpdateFacsHardwareSignature ( IN EFI_ACPI_S3_SAVE_PROTOCOL *This, IN VOID *LegacyMemoryAddress ) { This->S3Save = mS3Save; UpdateFacsHardwareSignatureFn(); This->S3Save (This, LegacyMemoryAddress); } /** Notification event handler to do AcpiS3Save hook point @param[in] Event The Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. @retval None **/ VOID EFIAPI AcpiS3SaveHookCallBack ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save; Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **)&AcpiS3Save); if (EFI_ERROR (Status)) { return; } mS3Save = AcpiS3Save->S3Save; AcpiS3Save->S3Save = (EFI_ACPI_S3_SAVE)UpdateFacsHardwareSignature; } /** AcpiS3Save hook point, we have to promise update Facs table before save s3 information. So, hook AcpiS3Save->S3Save. It will not hook this point if it does not install gEfiAcpiS3SaveProtocolGuid. @param None. @return EFI_SUCCESS **/ EFI_STATUS AcpiS3SaveHook ( VOID ) { EFI_STATUS Status; EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save; EFI_EVENT UpdateFacsHardwareSignatureEvent; VOID *Registration; Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **)&AcpiS3Save); if (EFI_ERROR (Status)) { EfiCreateProtocolNotifyEvent ( &gEfiAcpiS3SaveProtocolGuid, TPL_NOTIFY, AcpiS3SaveHookCallBack, NULL, &Registration ); Status = EfiCreateEventReadyToBootEx ( TPL_CALLBACK - 1, FacsHwSignReadyToBootCallBack, NULL, &UpdateFacsHardwareSignatureEvent ); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } mS3Save = AcpiS3Save->S3Save; AcpiS3Save->S3Save = (EFI_ACPI_S3_SAVE)UpdateFacsHardwareSignature; return EFI_SUCCESS; } /** Update FACS table content Depends on ACPI Version option in Setup Variable for the 64 bits table support. @param Table The table to update @param SetupVariable SETUP Variable pointer @return EFI_SUCCESS Update table success **/ EFI_STATUS FacsUpdateCommon ( IN OUT EFI_ACPI_COMMON_HEADER *Table ) { KERNEL_CONFIGURATION SetupVariable; EFI_STATUS Status; Status = GetKernelConfiguration (&SetupVariable); if (EFI_ERROR (Status)) { SetupVariable.AcpiVer = 3; } switch (SetupVariable.AcpiVer) { case 0x07: case 0x06: case 0x05: case 0x04: case 0x03: case 0x02: ((EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(Table))->Version = EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION; //0x02 ((EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(Table))->OspmFlags = 0; (Table)->Length = sizeof(EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE); ((EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(Table))->Flags &= ~EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F; break; case 0x01: (Table)->Length = sizeof(EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE); break; case 0: default: ((EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(Table))->Version = 0; (Table)->Length = sizeof(EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE); break; } AcpiS3SaveHook (); return EFI_SUCCESS; }