//********************************************************************** //********************************************************************** //** ** //** (C)Copyright 1985-2014, American Megatrends, Inc. ** //** ** //** All Rights Reserved. ** //** ** //** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 ** //** ** //** Phone: (770)-246-8600 ** //** ** //********************************************************************** //********************************************************************** //********************************************************************** // $Header: /Alaska/SOURCE/Modules/SecureFlashPkg/SecureSmiFlash/SecSMIFlash.c 61 9/10/14 5:03p Alexp $ // // $Revision: 61 $ // // $Date: 9/10/14 5:03p $ //********************************************************************** // Revision History // ---------------- // $Log: /Alaska/SOURCE/Modules/SecureFlashPkg/SecureSmiFlash/SecSMIFlash.c $ // // 61 9/10/14 5:03p Alexp // EIP176297: Harden external parameter checks in SW SMI API // Fixes are made to address: // #7. Arbitrary SMRAM overwrite in SetFlUpdMethod() // #8. Integer overflow leading to buffer overflow in // LoadFwImage in SecSMIFlash SMI handler // // 60 8/04/14 11:59a Alexp // 1. Use internal RomLayout map to process Flash Erase/Write requests. // Issue- new RomMap may be different - exposing rom holes in wrong // places // 2. SetFlUpdMethod() - SecSmiFlash.RomLayout is updated to a map from a // new image but not used. // Later may compare the maps to find inconsistencies. // 3. BeforeSecureUpdate() - bug in calculating the flash range within // RomArea // // 59 7/29/14 12:07p Alexp // Add a code to erase existing CapsuleUpdate and FlashUdate vars // Fixes the case when vars exist with different attributes which prevent // SecSmiFlash to set new valid var. // // 57 7/03/14 10:16a Alexp // EIP176297: Harden external parameter checks in SW SMI API // // 56 6/11/14 10:16a Alexp // EIP#168391:The power button does not work with failed NVRAM // - harden SmiS5CapsuleCallback() // - memory allocation for gpEfiCapsuleHdr changed from runtime memory to // SMRAM. // // 55 3/20/14 3:20p Alexp // 1. EIP#159507 : S3 resume time improvement: Use EfiReservedMemoryType // instead of EfiACPIMemoryNVS for large buffer allocations. // FwCapsule buffer to be allocated below 4GB. // 2. Allow gpPubKeyHndl to be accessed from externally linked code // // 54 1/24/14 4:02p Alexp // SetFlUpdMethod() // 1. Set SecSmiFlash.RomLayout only for Runtime and Capsule update // 2. removed dummy code " SecSmiFlash.pFwCapsule = // SecSmiFlash.pFwCapsule" // // 53 10/11/13 2:50p Alexp // // 52 6/21/13 10:56a Alexp // EIP#125800: add more error checking in release mode inside // InSmmFunction() // // 51 6/12/13 3:52p Alexp // CapsuleValidate() made external function in SecSmiFlash API // // 50 6/06/13 4:03p Alexp // EIP#125800 : Privilege escalation into SMM via Secure SMI Flash SMM // driver via GetFlUpdPolicy and SetFlUpdMethod - BugID 305294 // add IsAddressInSmram() checks inside exposed API functions // // 49 4/23/13 2:49p Alexp // GetFlUpdPolicy() to return FwKey up to 256 bytes. // // // 47 2/26/13 6:14p Alexp // Set data size for Capsule Upd variable as // size of (EFI_PHYSICAL_ADDRESS) // // // 45 12/28/12 2:19p Alexp // fix "cppcheck: code style issues // // 42 11/21/12 10:42a Alexp // Replace direct calls to Hash() in CryptoLib with API calls // // 41 11/13/12 3:26p Alexp // 1. Calculate offset to RomLayout table within FwCapsHdr instead of // using hardwired location. Pkcs#7 // cert will differ in size and offset may change // 2. Remove assert after GetKey function. // // 40 11/09/12 5:44p Alexp // fixed branch for FWCAPSULE_2_0_SUPPORT // // 39 10/18/12 10:17a Alexp // Bug fix: make allocation of HashCtx buffer not to be dependent on // CAPSULE_RECOVERY support. // // 38 8/28/12 4:12p Alexp // fix spelling // // 37 8/22/12 9:40a Alexp // Allow to insrtall SecSmi Flash handler if RomLayout map not detected // during module Init // // 36 8/16/12 11:31a Alexp // use global pSmmBase ptr in InSmmFunction() // // 35 7/25/12 6:21p Alexp // replaced custom Capsule module defined _EFI_CAPSULE_BLOCK_DESCRIPTOR_ // with generic UEFI EFI_CAPSULE_BLOCK_DESCRIPTOR structure // // 34 6/26/12 9:49a Alexp // include GetRomLayout under // #if RUNTIME_SECURE_UPDATE_FLOW == 1 // // 33 6/12/12 5:33p Alexp // EIP74625:New Capsule PPI required by latest Intel's MRC code // -Fw Capsule upload supports new Capsule_2_0 format without extra // CapHdr in the Mailbox // EIP90678: MonotonicCounter variable guid changed // -Use AMI_GLOBAL_VARIABLE_GUID // // 30 5/21/12 4:55p Alexp // keep a pointer to FwCaps Hdr withing Capsule image. Streamlines // creation of Capsule Mailbox. // // 29 5/18/12 5:27p Alexp // 1. Replace Hash PPI calls with calls to Crypto lib functions. // 2. Enforce flash update security by replacing SMM Flash protocol // Write/Erase functions with local functions // 3. Include RomLayout map pointer to SecSmiFlash protocol table. Local // RomMap table is replaced with new one from valid FwCapsule image // // 28 2/29/12 4:15p Alexp // Removed "gAmiSig->GetKey" from driver entry point. Will use VerifyKey // instead. FW Key may be stored as a Hash in FW Key file storage. // // 27 2/16/12 9:38a Alexp // SetFlUpdMethod() - removed condition #if NEW_BIOS_MEM_ALLOC != 0 left // out from older logic // // 26 2/03/12 2:42p Alexp // Use SHA256 Hash for Hash table // // 25 1/27/12 12:14p Alexp // move defines for HASH_TBL_... to SecSmiFlash header file // // 23 1/13/12 4:18p Alexp // Replace "AFU_FLASH_PAGE_SIZE" with "FLASH_BLOCK_SIZE" // // 22 1/10/12 6:22p Alexp // 1. Always perform Hash match for new bios data block with one // calculated during Capsule verification // 2. Flash block available for hashing is determined by SDL token // FLASH_BLOCK_SIZE and not by AFU interface // // 21 12/16/11 1:19p Alexp // Bug fix: UpdateCapsule() // While preparing MailBox discriptor, the pointer gpCapsuleMailboxPtr // went outside of allocated memory pEfiCapsuleHdr // // 19 12/01/11 3:34p Alexp // 1.SmiS5CapsuleCallback-> Introduce a call to Chipset defined // SBLib_ResetSystem(WarmReset) // Call is included based on the SDL switch CSLIB_WARM_RESET_SUPPORTED // It replaces sample oimplementation of WarmReset via S3 & RTC wake up // 2. SetFlUpdMethod-> Add temp w/a for AFU rev 3.0. Wrong image size(0) // is reported by AFU in pSessionBlock->FlUpdBlock.ImageSize // 3. Updated conditional statements to match newly defined SDL tokens: // INSTALL_SECURE_FLASH_SW_SMI_HNDL and CSLIB_WARM_RESET_SUPPORTED // // 17 11/30/11 8:11p Alexp // 1. Replaced dependencies from WARM_REBOOT flag to generic // FWCAPSULE_RECOVERY_SUPPORT // 2. SetFlUpdMethod() corercted behavior of some AFU implementations for // CApsule Set mode. Size should have been provided // 3. Memory for Capsule Mailbox allocation moved form "EfiRuntime" to // more appropriate "AcpiNvs" // // 16 11/11/11 12:50p Alexp // fixed InSmmFunction() when building without FWCAPSULE_RECOVERY_SUPPORT // // 15 11/03/11 6:45p Alexp // skip SecSMIFlashSMIHandler() if OFBD Secure Flash module is active // // 14 10/17/11 11:25a Alexp // update Hdr & Footer. Clear BIOS buffer // // 13 10/11/11 12:28p Alexp // add OFBD dependency // // 12 9/30/11 4:39p Alexp // add porting notes // // 11 8/24/11 6:51p Alexp // replaced CAPSULE_SUPPORT check to FWCAPSULE_RECOVERY_SUPPORT // // 10 8/24/11 11:30a Alexp // Clear Capsule update capability in gFlUpdatePolicy.FlashUpdate if no // Capsule Driver or WarmReboot tokens are defined // // 9 8/09/11 7:45p Alexp // // 8 8/09/11 9:54a Alexp // bug fix: NEW #if NEW_BIOS_MEM_ALLOC != 0, OLD: #if NEW_BIOS_MEM_ALLOC // // 7 8/08/11 7:23p Alexp // SetFlashUpdateVar - assign Attributes to SetVar via CounterHi variable. // old init method "A | B | C" had wrong Attributes passed to the SetVar // // 6 8/06/11 11:35a Alexp // LoadImage-> clear out FSHndl. Need to be set to valid value only if // image Verification complete inside SetFlMethod // // 5 8/05/11 3:43p Alexp // hardwire Flash Upd policy (staticly provided from SDL tokens) // // 4 7/21/11 3:13p Alexp // removed mistakenly put while(1){} statement at the end of // SmiS5CapsuleCallback() // // 3 7/20/11 7:20p Alexp // removed dependency on Capsule module. // // 2 7/20/11 6:22p Alexp // include sample implementation to enter S3 on AMD chipsets // // 1 7/01/11 4:41p Alexp // // 1 7/01/11 4:37p Alexp // // 10 6/24/11 4:32p Alexp // move around debug comments // // 9 6/24/11 2:09p Alexp // SetFlUpd: abort if capsule size passed by AFU is greater then allocated // buffer in gRomFileSize // // 8 6/23/11 7:08p Alexp // SetFlashUpd: update local buffer with Ptr to AFU allocated buffer with // rom image // // 14 6/20/11 2:10p Alexp // // AFU updates the address in CapsuleMailboxPtr if // SetFlashMethod: update for the case if AFU provided address in memory // for new BIOS image // // 7 6/17/11 5:47p Alexp // bug fix: GetKey expects valid buffer size on input // // 6 6/01/11 12:35p Alexp // fix FSHandle init value // // 5 5/25/11 8:31p Alexp // forse Implemented flag to "1" if SecSMIFlash supported. ASFU would // check for combination, Version>-12 and this flag to determine if SecSMI // Flash API is supported // // 4 5/17/11 12:58p Alexp // add build switch for location of temp BIOS image // // 3 5/12/11 7:58p Alexp // // 2 5/10/11 5:10p Alexp // Hash guids are defined globally // // 1 5/10/11 10:01a Alexp // // 13 5/06/11 11:59a Alexp // // 12 5/02/11 3:22p Alexp // merged capsule hdr and mailbox into one mem allocation unit // // 11 4/28/11 6:21p Alexp // tbd: in release mode get FW pub key may fail // // 10 4/28/11 10:45a Alexp // update Capsule mailbox format. Add extra Efi Capsule Hdr that will be // discarded by CApsul PEI service while coalescing the Capsule into a Hob // // 8 4/22/11 4:36p Alexp // temp debug: init MC field in FlashUpd var with 0 if MC Var is missing. // // 7 4/19/11 6:42p Alexp // tested Reboot and Online flash. Recovery flow fails. // // 6 4/18/11 7:22p Alexp // working version. Need to review HashTable as it may not be practical if // block sizes do not mach BLOCK_SIZE // // 5 4/13/11 7:14p Alexp // locate RomMap for easy location of Fid.ffs for VersionControl // Create HashTable for uplaoded Fw Image. Used for runtime updates only // generate FSHandle for future use // // 4 4/11/11 2:10p Alexp // -Revision 12 and upper to support "Installed" field in Flash Info as // bit mask // -Add support for new CApsule SigningCert header // - Replace PKpub with new FW Sign key as a root Platform key to verify // Capsule Sig with // // 3 4/05/11 6:38p Alexp // use GetVariable to get PK Pub key // // 2 3/11/11 6:51p Alexp // // 1 3/10/11 4:59p Alexp // // 1 3/03/11 6:34p Alexp // //********************************************************************** #include #include #include #include #include #include #include #include #include //PI 1.1 ++ #if defined(PI_SPECIFICATION_VERSION)&&(PI_SPECIFICATION_VERSION>=0x0001000A) #include #else #include #endif #if FWCAPSULE_RECOVERY_SUPPORT == 1 #include #endif #include #include #include //---------------------------------------------------------------------- // Module defined global variables static EFI_GUID gSwSmiCpuTriggerGuid = SW_SMI_CPU_TRIGGER_GUID; static EFI_GUID gEfiSmmSwDispatchProtocolGuid = EFI_SMM_SW_DISPATCH_PROTOCOL_GUID; static EFI_GUID gEfiSmmSxDispatchProtocolGuid = EFI_SMM_SX_DISPATCH_PROTOCOL_GUID; //extern EFI_GUID gEfiSmmSxDispatchProtocolGuid = EFI_SMM_SX_DISPATCH_PROTOCOL_GUID; // AMI_GLOBAL_VARIABLE_GUID must be defined in AmiLib.h (Core 4.6.5.4 +) #if defined(AMI_GLOBAL_VARIABLE_GUID) static EFI_GUID gAmiGlobalVariableGuid = AMI_GLOBAL_VARIABLE_GUID; #else static EFI_GUID gAmiGlobalVariableGuid = EFI_GLOBAL_VARIABLE; #endif static EFI_GUID FlashUpdGuid = FLASH_UPDATE_GUID; EFI_GUID gFWCapsuleGuid = APTIO_FW_CAPSULE_GUID; EFI_GUID gPRKeyGuid = PR_KEY_GUID; EFI_GUID gFwCapFfsGuid = AMI_FW_CAPSULE_FFS_GUID; static FLASH_UPD_POLICY FlUpdatePolicy = {FlashUpdatePolicy, BBUpdatePolicy}; EFI_SHA256_HASH *gHashTbl = NULL; UINT8 gHashDB[SHA256_DIGEST_SIZE]; CRYPT_HANDLE gpPubKeyHndl; AMI_DIGITAL_SIGNATURE_PROTOCOL *gAmiSig; #if FWCAPSULE_RECOVERY_SUPPORT == 1 static EFI_GUID CapsuleVendorGuid = EFI_CAPSULE_AMI_GUID; EFI_CAPSULE_BLOCK_DESCRIPTOR *gpEfiCapsuleHdr = NULL; #endif // BIOS allocates the space in AcpiNVS for new BIOS image to be uploaded by Flash tool // Alternatively the buffer may be reserved within the SMM TSEG. Check NEW_BIOS_MEM_ALLOC Token // AFU would have to execute a sequence of SW SMI calls to load new BIOS image to mem UINTN gFwCapMaxSize = FWCAPSULE_IMAGE_SIZE; UINT32 *pFwCapsuleLowMem = NULL; static EFI_SMRAM_DESCRIPTOR *mSmramRanges; static UINTN mSmramRangeCount; //---------------------------------------------------------------------- // Flash Upd Protocol defines //---------------------------------------------------------------------- typedef EFI_STATUS (EFIAPI *FLASH_READ_WRITE)( VOID* FlashAddress, UINTN Size, VOID* DataBuffer ); typedef EFI_STATUS (EFIAPI *FLASH_ERASE)( VOID* FlashAddress, UINTN Size ); FLASH_PROTOCOL *Flash; FLASH_READ_WRITE pFlashWrite; // Original Ptr inside FlashAPI FLASH_ERASE pFlashErase; static UINT32 Flash4GBMapStart; ROM_AREA *RomLayout = NULL; //---------------------------------------------------------------------- //---------------------------------------------------------------------- // UpFront Function definitions BOOLEAN SupportUpdateCapsuleReset ( VOID ); EFI_STATUS CapsuleValidate ( IN OUT UINT8 **pFwCapsule, IN OUT APTIO_FW_CAPSULE_HEADER **pFwCapsuleHdr ); EFI_STATUS LoadFwImage( IN OUT FUNC_BLOCK *pFuncBlock ); EFI_STATUS GetFlUpdPolicy( IN OUT FLASH_POLICY_INFO_BLOCK *InfoBlock ); EFI_STATUS SetFlUpdMethod( IN OUT FUNC_FLASH_SESSION_BLOCK *pSessionBlock ); EFI_STATUS FindCapHdrFFS( IN VOID *pCapsule, OUT UINT8 **pFfsData ); BOOLEAN IsAddressInSmram ( IN EFI_PHYSICAL_ADDRESS Buffer, IN UINT64 Length ); //---------------------------------------------------------------------- // //---------------------------------------------------------------------- // Procedure: SecSmiFlash // // Description: // // Input: // // Output: // // Returns: // //---------------------------------------------------------------------- // EFI_SEC_SMI_FLASH_PROTOCOL SecSmiFlash = { LoadFwImage, GetFlUpdPolicy, SetFlUpdMethod, CapsuleValidate, 0,// pFwImageLowMem 0,// RomLayout, 0,// gSha256HashTbl, 0,// FSHandle }; #if FWCAPSULE_RECOVERY_SUPPORT == 1 #if CSLIB_WARM_RESET_SUPPORTED == 0 //#if (defined x64_BUILD && x64_BUILD == 1) //VOID flushcaches(); void DisableCacheInCR0(); //#endif // //--------------------------------------------------------------------------- // // FUNCTION: ReadRtcIndexedRegister // // DESCRIPTION: Used to read RTC register indexed by the argument // // Input: // IN UINT8 Index Index of the register to read // // // Output: // UINT8 Current value of the register // //--------------------------------------------------------------------------- // UINT8 ReadRtcIndexedRegister(IN UINT8 Index){ UINT8 Byte = IoRead8(0x70) & 0x80; // preserve bit 7 IoWrite8(0x70, Index | Byte); Byte = IoRead8(0x71); return Byte; } // //--------------------------------------------------------------------------- // // FUNCTION: ReadRtcIndexedRegister // // DESCRIPTION: Used to write to RTC register indexed by the argument // // Input: // IN UINT8 Index Index of the register to write to // // IN UINT8 Value Value to write to the RTC register // // Output: // VOID // //--------------------------------------------------------------------------- // VOID WriteRtcIndexedRegister(IN UINT8 Index, IN UINT8 Value){ IoWrite8(0x70,Index | (IoRead8(0x70) & 0x80)); IoWrite8(0x71,Value); } // //---------------------------------------------------------------------------- // // Procedure: S3RTCresume // // Description: This function puts system into ACPI S3 State. // if token ENABLE_RTC_ONE_SECOND_WAKEUP = 1, then it setups RTC // 1 second alarm as well. // // Input: None // // Output: None, system will enter ACPI S3 State. //---------------------------------------------------------------------------- // VOID S3RTCresume (VOID) { UINT32 IoData; UINT8 Hour, Minute, Second; BOOLEAN inBCD = TRUE; //flush caches befor going to S3 //#if (defined x64_BUILD && x64_BUILD == 1) // flushcaches(); DisableCacheInCR0(); //#else // _asm wbinvd //#endif // determine if RTC is in BCD mode if( ReadRtcIndexedRegister(0xB) & 0x4 ) // bit 2 inBCD = FALSE; // wait for time update to complete before reading the values while( ReadRtcIndexedRegister(0xA) & 0x80 ); // while bit 7 is set the // time update is in progress //read current hour, minute, second Hour = ReadRtcIndexedRegister(0x4); Minute = ReadRtcIndexedRegister(0x2); Second = ReadRtcIndexedRegister(0x0); //convert second to decimal from BCD and increment by 1 if(inBCD) Second = (Second >> 4) * 10 + (Second & 0x0F); Second += 2; if(Second > 59){ Second -= 60; if(inBCD) Minute = (Minute >> 4) * 10 + (Minute & 0x0F); Minute++; if(Minute > 59){ Minute = 0; if(inBCD) Hour = (Hour >> 4) * 10 + (Hour & 0x0F); Hour++; // check 24 hour mode/12 hour mode if( ReadRtcIndexedRegister(0xB) & 0x2 ) {// bit 1 1=24hour else 12 hour if(Hour > 23) Hour = 0; } else { if(Hour > 11) Hour = 0; } if(inBCD) Hour = Hour % 10 + ( (Hour / 10) << 4 ) ; } if(inBCD) Minute = Minute % 10 + ( (Minute / 10) << 4 ) ; } //convert from decimal to BCD if(inBCD) Second = Second % 10 + ( (Second / 10) << 4 ) ; //set the alarm WriteRtcIndexedRegister(0x5, Hour); WriteRtcIndexedRegister(0x3, Minute); WriteRtcIndexedRegister(0x1, Second); //enable the alarm WriteRtcIndexedRegister(0xB, ( ReadRtcIndexedRegister(0xB) | ((UINT8)( 1 << 5 )) )); // ========== PORTING REQUIRED =========================================================== // Current implementation to simulate the Warm Reboot may not be sufficient on some platforms. // S3 transition may require additional Chipset/Platform coding. // If needed add any necessary OEM hooks to be able to put the system into S3 at the end of this handler //======================================================================================== //set RTC_EN bit in PM1_EN to wake up from the alarm IoWrite16(PM_BASE_ADDRESS + 0x02, ( IoRead16(PM_BASE_ADDRESS + 0x02) | (1 << 10) )); //Disable Sleep SMI to avoid SMI re-entrance. // IoWrite16(PM_BASE_ADDRESS + 0x30, ( IoRead16(PM_BASE_ADDRESS + 0x30) & (~BIT4) )); //modify power management control register to reflect S3 IoData = IoRead32(PM_BASE_ADDRESS + 0x04); //following code is applicable to Intel PCH only. IoData &= ~(0x1C00); IoData |= 0x1400; //Suspend to RAM /* // AMD w/a to enter S3 state IoData |= 0x2C00; //Suspend to RAM { UINT8 Temp8; IoWrite8(0xCD6, 0x004); Temp8 = IoRead8(0xCD7); Temp8 &= ~(BIT7); IoWrite8(0xCD6, 0x004); IoWrite8(0xCD7, Temp8); IoWrite8(0xCD6, 0x007); IoWrite8(0xCD7, BIT7); } } */ IoWrite32(PM_BASE_ADDRESS + 0x04, IoData ); } //#else //extern SBLib_ResetSystem( IN EFI_RESET_TYPE ResetType ); #endif extern SBLib_ResetSystem( IN EFI_RESET_TYPE ResetType ); // //--------------------------------------------------------------------------- // // FUNCTION: SmiS5CapsuleCallback // // DESCRIPTION: SMI handler to perform capsule reset (bounce from S5 to S3) // ========== PORTING REQUIRED =========================================================== // Current implementation to simulate the Warm Reboot may not be sufficient on some platforms. // S3 transition may require additional Chipset/Platform coding. // If needed add any necessary OEM hooks to be able to put the system into S3 at the end of this handler //======================================================================================== // // Input: // IN EFI_HANDLE DispatchHandle Handle of SMI dispatch // protocol // IN EFI_SMM_SX_DISPATCH_CONTEXT* DispatchContext Pointer to SMI dispatch // context structure // // Output: // VOID // //--------------------------------------------------------------------------- // VOID SmiS5CapsuleCallback ( IN EFI_HANDLE DispatchHandle, IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext ){ EFI_PHYSICAL_ADDRESS IoData; UINTN Size; EFI_CAPSULE_HEADER *CapsuleHeader; AMI_FLASH_UPDATE_BLOCK FlUpdateBlock; TRACE((TRACE_ALWAYS,"SecSMI. S5 Trap\n")); // //Check if the Capsule update is supported by platform policy // if (!SupportUpdateCapsuleReset()) return; Size=sizeof(AMI_FLASH_UPDATE_BLOCK); if(EFI_ERROR(pRS->GetVariable(FLASH_UPDATE_VAR,&FlashUpdGuid, NULL, &Size, &FlUpdateBlock)) || FlUpdateBlock.FlashOpType != FlCapsule) return; // verify the FW capsule is in memory. Size = sizeof(EFI_PHYSICAL_ADDRESS); if(EFI_ERROR(pRS->GetVariable(CAPSULE_UPDATE_VAR,&CapsuleVendorGuid, NULL, &Size, &IoData))) return; if(IoData != (EFI_PHYSICAL_ADDRESS)gpEfiCapsuleHdr || !IsAddressInSmram((EFI_PHYSICAL_ADDRESS)IoData, sizeof(EFI_PHYSICAL_ADDRESS))) return; CapsuleHeader = (EFI_CAPSULE_HEADER*)gpEfiCapsuleHdr[0].DataBlock; // // Compare GUID with APTIO_FW_CAPSULE_GUID // if (guidcmp (&CapsuleHeader->CapsuleGuid, &gFWCapsuleGuid)) return; #if CSLIB_WARM_RESET_SUPPORTED == 1 SBLib_ResetSystem(EfiResetWarm); #else S3RTCresume(); #endif } // //---------------------------------------------------------------------------- // Procedure: SupportUpdateCapsuleReset // // Description: This function returns platform policy capability for capsule update via a system reset. // // Input: None // // Output: TRUE - memory can be preserved across reset // FALSE - memory integrity across reset is not guaranteed // //---------------------------------------------------------------------------- // BOOLEAN SupportUpdateCapsuleReset ( VOID ) { // //If the platform has a way to guarantee the memory integrity across a system reset, return //TRUE, else FALSE. // if( (FlUpdatePolicy.FlashUpdate & FlCapsule) || (FlUpdatePolicy.BBUpdate & FlCapsule)) return TRUE; return FALSE; } // //---------------------------------------------------------------------------- // Procedure: UpdateCapsule // // Description: This code prepares Capsule Update EFI Variable // // Input: // IN EFI_CAPSULE_HEADER **CapsuleHeaderArray - array of pointers to capsule headers passed in // // Output: EFI_SUCCESS - capsule processed successfully // EFI_INVALID_PARAMETER - CapsuleCount is less than 1,CapsuleGuid is not supported // EFI_DEVICE_ERROR - capsule processing failed // //---------------------------------------------------------------------------- // EFI_STATUS UpdateCapsule ( IN FUNC_FLASH_SESSION_BLOCK *pSessionBlock, IN APTIO_FW_CAPSULE_HEADER *pFwCapsuleHdr ){ EFI_STATUS Status; EFI_CAPSULE_HEADER *pEfiCapsuleHdr = NULL; EFI_CAPSULE_BLOCK_DESCRIPTOR *pCapsuleMailboxPtr; UINT32 Attributes, Index; // //Compare GUID with APTIO_FW_CAPSULE_GUID // if (!pFwCapsuleHdr || guidcmp (&pFwCapsuleHdr->CapHdr.CapsuleGuid, &gFWCapsuleGuid) ) return EFI_DEVICE_ERROR; pCapsuleMailboxPtr = gpEfiCapsuleHdr; Index = 0; #if !defined(FWCAPSULE_2_0_SUPPORT) || FWCAPSULE_2_0_SUPPORT == 0 pEfiCapsuleHdr = (EFI_CAPSULE_HEADER*)&pCapsuleMailboxPtr[4]; // New Capsule PPI supports single CapHdr. // construct dummy EfiCapHdr struct within pEfiCapsuleHdr as 1st element to be linked from Mailbox MemCpy((VOID*)pEfiCapsuleHdr, &gFWCapsuleGuid, sizeof(EFI_GUID)); pEfiCapsuleHdr->Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; pEfiCapsuleHdr->HeaderSize = sizeof(EFI_CAPSULE_HEADER); pEfiCapsuleHdr->CapsuleImageSize = pFwCapsuleHdr->CapHdr.CapsuleImageSize + sizeof(EFI_CAPSULE_HEADER); // create ScatterGather list: use pre-allocated runtime memory pCapsuleMailboxPtr[Index].Length = pEfiCapsuleHdr->HeaderSize; pCapsuleMailboxPtr[Index].DataBlock = (EFI_PHYSICAL_ADDRESS)pEfiCapsuleHdr; Index++; #endif pCapsuleMailboxPtr[Index].Length = pFwCapsuleHdr->CapHdr.HeaderSize; pCapsuleMailboxPtr[Index].DataBlock = (EFI_PHYSICAL_ADDRESS)pFwCapsuleHdr; pCapsuleMailboxPtr[Index+1].Length = pFwCapsuleHdr->CapHdr.CapsuleImageSize-pFwCapsuleHdr->CapHdr.HeaderSize; if((UINT32*)pFwCapsuleLowMem == (UINT32*)pFwCapsuleHdr) { // Fw Cap Hdr is on top of Payload pCapsuleMailboxPtr[Index+1].DataBlock = pCapsuleMailboxPtr[Index].DataBlock+pCapsuleMailboxPtr[Index].Length; } else { // Fw Cap Hdr is embedded inside Payload pCapsuleMailboxPtr[Index+1].DataBlock = (EFI_PHYSICAL_ADDRESS)pFwCapsuleLowMem; } pCapsuleMailboxPtr[Index+2].Length = 0; pCapsuleMailboxPtr[Index+2].DataBlock = 0; // //Check if the platform supports update capsule across a system reset // if (!SupportUpdateCapsuleReset()) { return EFI_UNSUPPORTED; } // Erase prev copy Status = pRS->SetVariable ( CAPSULE_UPDATE_VAR, &CapsuleVendorGuid,0,0,NULL); Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS; Status = pRS->SetVariable ( CAPSULE_UPDATE_VAR, &CapsuleVendorGuid, Attributes, sizeof(EFI_PHYSICAL_ADDRESS),(VOID*)&pCapsuleMailboxPtr); if(!EFI_ERROR(Status)) return Status; return EFI_DEVICE_ERROR; } #endif //#if FWCAPSULE_RECOVERY_SUPPORT == 1 // //---------------------------------------------------------------------------- // Procedure: SetFlashUpdateVar // // Description: This code finds if the capsule needs reset to update, if no, update immediately. // // Input: // IN EFI_CAPSULE_HEADER **CapsuleHeaderArray - array of pointers to capsule headers passed in // IN UINTN CapsuleCount - number of capsule // IN EFI_PHYSICAL_ADDRESS ScatterGatherList - physical address of datablock list points to capsule // // Output: EFI_SUCCESS - capsule processed successfully // EFI_INVALID_PARAMETER - CapsuleCount is less than 1,CapsuleGuid is not supported // EFI_DEVICE_ERROR - capsule processing failed // //---------------------------------------------------------------------------- // EFI_STATUS SetFlashUpdateVar ( IN FUNC_FLASH_SESSION_BLOCK *pSessionBlock ){ EFI_STATUS Status; UINTN Size; UINT32 CounterHi; if(pSessionBlock->FlUpdBlock.FlashOpType == FlRecovery && pSessionBlock->FlUpdBlock.FwImage.AmiRomFileName[0] == 0 ) return EFI_DEVICE_ERROR; CounterHi = 0; Size = sizeof(UINT32); // MonotonicCounter is a boot time service, hence the variable may have restricted access in runtime if(EFI_ERROR(pRS->GetVariable(L"MonotonicCounter", &gAmiGlobalVariableGuid, NULL, &Size, &CounterHi)) ) // return Status;//EFI_DEVICE_ERROR; //SetMode should set FlashUpd even if no MC var detected. CounterHi=0xffffffff; pSessionBlock->FlUpdBlock.MonotonicCounter = CounterHi; CounterHi = (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS); // Erase prev copy Status = pRS->SetVariable ( FLASH_UPDATE_VAR, &FlashUpdGuid,0,0,NULL); Status = pRS->SetVariable ( FLASH_UPDATE_VAR, &FlashUpdGuid, CounterHi, sizeof(AMI_FLASH_UPDATE_BLOCK), (VOID*) &pSessionBlock->FlUpdBlock ); if(!EFI_ERROR(Status)) return Status; return EFI_DEVICE_ERROR; } // //---------------------------------------------------------------------- // Procedure: GetFlUpdPolicy // // Description: // // Input: // // Output: // // Returns: // //---------------------------------------------------------------------- // EFI_STATUS GetFlUpdPolicy( IN OUT FLASH_POLICY_INFO_BLOCK *InfoBlock ) { UINT32 KeySize = DEFAULT_RSA_KEY_MODULUS_LEN; //TRACE((TRACE_ALWAYS,"SecSMI. GetPolicy. %X_%X\n",FlUpdatePolicy.FlashUpdate, FlUpdatePolicy.BBUpdate)); if(IsAddressInSmram((EFI_PHYSICAL_ADDRESS)InfoBlock, sizeof(FLASH_POLICY_INFO_BLOCK))) return EFI_DEVICE_ERROR; MemCpy(&InfoBlock->FlUpdPolicy, &FlUpdatePolicy, sizeof(FLASH_UPD_POLICY)); MemSet(&InfoBlock->PKpub, KeySize, 0xFF); if(gpPubKeyHndl.BlobSize < KeySize) KeySize = gpPubKeyHndl.BlobSize; MemCpy(&InfoBlock->PKpub, gpPubKeyHndl.Blob, KeySize); InfoBlock->ErrorCode = 0; return EFI_SUCCESS; } // //---------------------------------------------------------------------- // Procedure: SetFlUpdMethod // // Description: // // Input: // // Output: // // Returns: // //---------------------------------------------------------------------- // EFI_STATUS SetFlUpdMethod( IN OUT FUNC_FLASH_SESSION_BLOCK *pSessionBlock ) { EFI_STATUS Status = EFI_DEVICE_ERROR; #if RUNTIME_SECURE_UPDATE_FLOW == 1 UINT32 HashBlock; UINT32 BlockSize; UINT8 *BlockAddr; #endif UINT32 *FSHandl; APTIO_FW_CAPSULE_HEADER *pFwCapsuleHdr; //TRACE((TRACE_ALWAYS,"SecSMI. SetFlash\nSize : %X\n",pSessionBlock->FlUpdBlock.ImageSize)); if(IsAddressInSmram((EFI_PHYSICAL_ADDRESS)pSessionBlock, sizeof(FUNC_FLASH_SESSION_BLOCK))) return EFI_DEVICE_ERROR; //if(pSessionBlock->FlUpdBlock.FlashOpType == FlRecovery) //TRACE((TRACE_ALWAYS,"File Name: %s\n",pSessionBlock->FlUpdBlock.FwImage.AmiRomFileName)); //else //TRACE((TRACE_ALWAYS,"Image Adr: %X\n",pSessionBlock->FlUpdBlock.FwImage.CapsuleMailboxPtr[0])); //TRACE((TRACE_ALWAYS,"ROMmap : %X\n",pSessionBlock->FlUpdBlock.ROMSection)); //TRACE((TRACE_ALWAYS,"FlOpType : %X\n",pSessionBlock->FlUpdBlock.FlashOpType)); // Verify if chosen Flash method is compatible with FlUpd Policy if(((pSessionBlock->FlUpdBlock.ROMSection & (1<FlUpdBlock.FlashOpType & FlUpdatePolicy.BBUpdate)) || (!(pSessionBlock->FlUpdBlock.ROMSection & (1<FlUpdBlock.FlashOpType & FlUpdatePolicy.FlashUpdate)) ){ //TRACE((TRACE_ALWAYS,"Buff Adr : %X\nBuff Size: %X\n",pFwCapsuleLowMem, gRomFileSize)); //!!! make sure Flash blocks BOOT_BLOCK, MAIN_, NV_ and EC_ are matching enum types in FlashUpd.h // Get Flash Update mode switch(pSessionBlock->FlUpdBlock.FlashOpType) { #if FWCAPSULE_RECOVERY_SUPPORT == 1 case FlCapsule: #endif case FlRuntime: // common for FlRuntime or Capsule if(pSessionBlock->FlUpdBlock.ImageSize > gFwCapMaxSize) break; // suspecting buffer overrun. SecSmiFlash.pFwCapsule = pFwCapsuleLowMem; // AFU updates the address in CapsuleMailboxPtr if // it's capable of allocating large buffer to load entire FW Capsule image if(pSessionBlock->FlUpdBlock.FwImage.CapsuleMailboxPtr[0] != 0 ) { if(IsAddressInSmram((EFI_PHYSICAL_ADDRESS)pSessionBlock->FlUpdBlock.FwImage.CapsuleMailboxPtr[0], pSessionBlock->FlUpdBlock.ImageSize)) return EFI_DEVICE_ERROR; #if NEW_BIOS_MEM_ALLOC != 2 if(SecSmiFlash.pFwCapsule != NULL) { // prevent leaking of the SMM code to the external buffer if(!IsAddressInSmram((EFI_PHYSICAL_ADDRESS)SecSmiFlash.pFwCapsule, sizeof(UINTN))) return EFI_DEVICE_ERROR; MemCpy((UINT8*)SecSmiFlash.pFwCapsule, (UINT8*)pSessionBlock->FlUpdBlock.FwImage.CapsuleMailboxPtr[0], pSessionBlock->FlUpdBlock.ImageSize); } else #endif SecSmiFlash.pFwCapsule = (UINT32*)pSessionBlock->FlUpdBlock.FwImage.CapsuleMailboxPtr[0]; } // else AFU must've uploaded the image to designated SMM space using LoadFw command // verify we got a capsule at pFwCapsuleLowMem, update a ptr to FwCapHdr within Payload image Status = CapsuleValidate((UINT8**)&(SecSmiFlash.pFwCapsule), &pFwCapsuleHdr); if(!pFwCapsuleHdr || EFI_ERROR(Status)) break; // capture RomLayout from new Secure Image if it's loaded in memory and validated SecSmiFlash.RomLayout = (ROM_AREA *)(UINTN)((UINT32)pFwCapsuleHdr+pFwCapsuleHdr->RomLayoutOffset); if(pSessionBlock->FlUpdBlock.FlashOpType == FlRuntime) { #if RUNTIME_SECURE_UPDATE_FLOW == 1 // Fill in gShaHashTbl Hash Table BlockSize = FLASH_BLOCK_SIZE; BlockAddr = (UINT8*)SecSmiFlash.pFwCapsule; for(HashBlock = 0; HashBlock < SEC_FLASH_HASH_TBL_BLOCK_COUNT; HashBlock++) { Status = gAmiSig->Hash(gAmiSig, &gEfiHashAlgorithmSha256Guid, 1, &BlockAddr, (const UINTN*)&BlockSize, gHashTbl[HashBlock]); if (EFI_ERROR(Status)) break; BlockAddr+= (UINTN)(BlockSize); } #endif // done for Runtime Upd break; } // Set Capsule EFI Var if Capsule(Verify Capsule Mailbox points to FW_CAPSULE) pSessionBlock->FlUpdBlock.ImageSize = pFwCapsuleHdr->CapHdr.CapsuleImageSize; #if FWCAPSULE_RECOVERY_SUPPORT == 1 Status = UpdateCapsule (pSessionBlock, pFwCapsuleHdr); if(EFI_ERROR(Status)) break; #endif // common for Recovery or Capsule case FlRecovery: // Set FlUpd EFI Var (Get MC, verify RecFileName) Status = SetFlashUpdateVar (pSessionBlock); break; default: Status = EFI_DEVICE_ERROR; } } // Set Error Status if (Status != EFI_SUCCESS) { SecSmiFlash.FSHandle = 0; SecSmiFlash.pFwCapsule = NULL; SecSmiFlash.RomLayout = RomLayout; // back to default RomLayout pSessionBlock->FSHandle = 0; pSessionBlock->ErrorCode = 1; return EFI_DEVICE_ERROR; } // FSHandle is updated if Capsule validation passed. // Create FSHandle as 1st 4 bytes of gHashTbl. It must be different each time // SetMethod is called with new Image FSHandl = (UINT32*)gHashTbl; SecSmiFlash.FSHandle = *FSHandl; // should be unique per Capsule; pSessionBlock->FSHandle = SecSmiFlash.FSHandle; pSessionBlock->ErrorCode = 0; return EFI_SUCCESS; } // //---------------------------------------------------------------------- // Procedure: LoadFwImage // // Description: Routine is called in a loop by the Flash tool. // Depending on the OS environment, Flash tool passes either an entire // Flash Image into SMM buffer or block by block. // E.g AFUDOS could allocate a contiguous buffer for the entire ROM buffer, // while certain OSes (Linux) may only allocate limited buffer sizes // // Input: FUNC_BLOCK -> Address, size // // Output: FUNC_BLOCK -> Status // // Returns: // //---------------------------------------------------------------------- // EFI_STATUS LoadFwImage( IN OUT FUNC_BLOCK *pFuncBlock ) { if(IsAddressInSmram((EFI_PHYSICAL_ADDRESS)pFuncBlock, sizeof(FUNC_BLOCK))) return EFI_DEVICE_ERROR; // prevent leaking of the SMM code to the external buffer if(IsAddressInSmram((EFI_PHYSICAL_ADDRESS)pFuncBlock->BufAddr, pFuncBlock->BlockSize)) return EFI_DEVICE_ERROR; pFuncBlock->ErrorCode = 1; SecSmiFlash.FSHandle = 0; // clear out Hndl. Will be set to valid number in SetFlashMethod SecSmiFlash.pFwCapsule = NULL; SecSmiFlash.RomLayout = RomLayout; // back to default RomLayout //TRACE((TRACE_ALWAYS,"SecSMI. LoadImage at %X, size %X\n",(UINT32)pFwCapsuleLowMem + pFuncBlock->BlockAddr, pFuncBlock->BlockSize)); if(pFwCapsuleLowMem == NULL) return EFI_DEVICE_ERROR; // assuming the address in 0 based offset in new ROM image if((UINT64)((UINT32)pFwCapsuleLowMem + pFuncBlock->BlockAddr + pFuncBlock->BlockSize) > (UINT64)((UINT32)pFwCapsuleLowMem + gFwCapMaxSize) ) return EFI_DEVICE_ERROR; MemCpy((VOID*)((UINT32)pFwCapsuleLowMem+pFuncBlock->BlockAddr), (UINT8*)pFuncBlock->BufAddr, pFuncBlock->BlockSize); pFuncBlock->ErrorCode = (UINT8)MemCmp( (VOID*)((UINT32)pFwCapsuleLowMem+pFuncBlock->BlockAddr), (VOID*)pFuncBlock->BufAddr, pFuncBlock->BlockSize); pFuncBlock->ErrorCode = 0; return EFI_SUCCESS; } // End Secured Flash Update API #if RUNTIME_SECURE_UPDATE_FLOW == 1 // //---------------------------------------------------------------------------- // // Name: BeforeSecureUpdate // // Description: Verifies if the Update range is protected by Signature // 1. return Success if flash region is inside unSigned RomArea // 2. if region is signed - compare its hash with pre-calculated Hash in smm // and return pointer to internal DataBuffer // // Input: // VOID* FlashAddress Pointer to address of a flash // // Output: Status // //-------------------------------------------------------------------------- // EFI_STATUS BeforeSecureUpdate ( VOID* FlashAddress, UINTN Size, UINT8 **DataBuffer ) { EFI_STATUS Status = EFI_SUCCESS; ROM_AREA *Area; UINT8 *BuffAddr; UINT8 *PageAddr; UINT8 HashCounter; UINTN PageSize; UINTN PageCount; UINT32 *FSHandl; // enforce write protection if RomArea undefined if ( RomLayout == NULL ) Status = EFI_WRITE_PROTECTED; for (Area = RomLayout; Area && Area->Size!=0; Area++) { if(Area->Address == 0) // construct an Address field if not initialized Area->Address = (UINT64)((0xFFFFFFFF - FLASH_SIZE) + 1)+Area->Offset; //TRACE((-1, "RomArea %8X(%8X) + Size %8X = %8X, Attr %X\n",Area->Address,Area->Offset, Area->Size, (UINT64)((UINTN)Area->Address+Area->Size), Area->Attributes)); if( (((UINT64)FlashAddress >= (UINT64)(Area->Address)) && ((UINT64)FlashAddress < (UINT64)(Area->Address+Area->Size))) || (((UINT64)(Area->Address) >= (UINT64)FlashAddress) && ((UINT64)(Area->Address) < (UINT64)((UINT64)FlashAddress + Size))) ) { if (Area->Attributes & ROM_AREA_FV_SIGNED) { Status = EFI_WRITE_PROTECTED; break; } //TRACE((-1, "\nSignAttr %x(%x)\nRomArea %8X, Size %8X, (%8X)\nFlsAddr %8X, Size %8X, (%8X)\n", Area->Attributes, (Area->Attributes & ROM_AREA_FV_SIGNED), // Area->Address, Area->Size, (UINT64)((UINTN)Area->Address+Area->Size), // (UINTN)FlashAddress, Size, (UINT64)((UINTN)FlashAddress+Size))); } } //if(Status != EFI_WRITE_PROTECTED) { // TRACE((-1, "SpiOffs %8X, Size %8X, (%8X)\n", (0-(UINTN)FlashAddress), Size, (0-(EFI_PHYSICAL_ADDRESS)((UINTN)FlashAddress+Size)))); //} if(Status == EFI_WRITE_PROTECTED && (FlUpdatePolicy.FlashUpdate & FlRuntime) ){ // check Verify status by comparing FSHandl with gHashTbl[0] // should be unique per Capsule; FSHandl = (UINT32*)gHashTbl; if(SecSmiFlash.FSHandle == 0 || SecSmiFlash.FSHandle != *FSHandl) return Status; // EFI_WRITE_PROTECTED PageSize = FLASH_BLOCK_SIZE; PageCount=( (UINTN)FlashAddress - Flash4GBMapStart) / PageSize; if(SecSmiFlash.pFwCapsule != NULL) { // Flash Write -> Update ptr to internal Acpi NVS or SMM Buffer BuffAddr = (UINT8*)SecSmiFlash.pFwCapsule; PageAddr = (UINT8*)((UINTN)BuffAddr + (PageSize * PageCount)); BuffAddr = (UINT8*)((UINTN)BuffAddr + ((UINTN)FlashAddress - Flash4GBMapStart)); Status = EFI_SUCCESS; HashCounter = 2; // addr may rollover to next flash page while(HashCounter-- && PageCount < SEC_FLASH_HASH_TBL_BLOCK_COUNT) { // compare calculated block hash with corresponding hash from the Hw Hash Table // if no match -> make Size=0 to skip Flash Write Op Status = gAmiSig->Hash(gAmiSig, &gEfiHashAlgorithmSha256Guid, 1, (const UINT8**)&PageAddr, (const UINTN*)&PageSize, gHashDB); if(EFI_ERROR(Status) || MemCmp(gHashDB, SecSmiFlash.HashTbl[PageCount], SHA256_DIGEST_SIZE) ){ //TRACE((-1, "Hash Err! FlashBuff = %8X, Data = %8X, BlockAddr=%x, BlockSize=%x\n", BuffAddr, *((UINT32*)BuffAddr), PageAddr, Size)); return EFI_WRITE_PROTECTED; } // repeat Hash check on next Flash Block if Write Block overlaps the Flash Block boundary PageCount++; PageAddr = (UINT8*)((UINTN)PageAddr + PageSize); if((BuffAddr+Size) <= PageAddr) break; } // Erase if(DataBuffer != NULL) *DataBuffer = BuffAddr; } } return Status; } // //---------------------------------------------------------------------------- // // Name: SecureFlashWrite // // Description: Allows to write to flash device is Secure Capsule is loaded into memory // Function replacing Flash->Write API call // // Input: VOID* FlashAddress, UINTN Size, VOID* DataBuffer // // // Output: EFI_SUCCESS // //-------------------------------------------------------------------------- // EFI_STATUS SecureFlashWrite ( VOID* FlashAddress, UINTN Size, VOID* DataBuffer ) { EFI_STATUS Status; UINT8 *CurrBuff; CurrBuff = (UINT8*)DataBuffer; Status = BeforeSecureUpdate(FlashAddress, Size, &CurrBuff); //TRACE((-1, "SecSMIFlash Write %X, BuffAddr=%X(%X) Lock Status=%r\n", FlashAddress, DataBuffer, CurrBuff, Status)); if(!EFI_ERROR(Status)) return pFlashWrite(FlashAddress, Size, CurrBuff); return Status; } // //---------------------------------------------------------------------------- // // Name: SecureFlashErase // // Description: Allows erase of flash device is Secure Capsule is loaded into memory // Function replacing Flash->Erase API call // // Input: NON // // // Output: EFI_SUCCESS // //-------------------------------------------------------------------------- // EFI_STATUS SecureFlashErase ( VOID* FlashAddress, UINTN Size ) { EFI_STATUS Status; Status = BeforeSecureUpdate(FlashAddress, Size, NULL); //TRACE((-1, "SecSMIFlash Erase %X - %X Lock Status=%r\n", FlashAddress, Size, Status)); if(!EFI_ERROR(Status)) return pFlashErase(FlashAddress, Size); return Status;//EFI_SUCCESS; } //********************************************************************** // // // Procedure: GetFwCapFfs // // Description: Loads binary from RAW section of X firwmare volume // // Input: // NameGuid - The guid of binary file // Buffer - Returns a pointer to allocated memory. Caller must free it when done. // Size - Returns the size of the binary loaded into the buffer. // // Output: Buffer - returns a pointer to allocated memory. Caller // must free it when done. // Size - returns the size of the binary loaded into the // buffer. // EFI_NOT_FOUND - Can't find the binary. // EFI_LOAD_ERROR - Load fail. // EFI_SUCCESS - Load success. // // //********************************************************************** EFI_STATUS GetFwCapFfs ( IN EFI_GUID *NameGuid, IN OUT VOID **Buffer, IN OUT UINTN *Size ) { EFI_STATUS Status; UINTN HandleCount; UINTN Index; EFI_FIRMWARE_VOLUME_PROTOCOL *Fv; EFI_HANDLE *HandleBuff; UINT32 AuthenticationStatus; *Buffer=0; *Size=0; Status = pBS->LocateHandleBuffer (ByProtocol,&gEfiFirmwareVolumeProtocolGuid,NULL,&HandleCount,&HandleBuff); if (EFI_ERROR (Status) || HandleCount == 0) { return EFI_NOT_FOUND; } // // Find desired image in all Fvs // for (Index = 0; Index < HandleCount; Index++) { Status = pBS->HandleProtocol (HandleBuff[Index],&gEfiFirmwareVolumeProtocolGuid,&Fv); if (EFI_ERROR (Status)) { continue;//return EFI_LOAD_ERROR; } // // Try a raw file // Status = Fv->ReadSection ( Fv, NameGuid, //&gFwCapFfsGuid, EFI_SECTION_FREEFORM_SUBTYPE_GUID,//EFI_SECTION_RAW 0, //Instance Buffer, Size, &AuthenticationStatus ); if (Status == EFI_SUCCESS) break; } pBS->FreePool(HandleBuff); if (Index >= HandleCount) { return EFI_NOT_FOUND; } return EFI_SUCCESS; } // //---------------------------------------------------------------------------- // // Name: GetRomLayout // // Description: // // Input: // IN EFI_HANDLE ImageHandle Image Handle // IN EFI_SYSTEM_TABLE *SystemTable Pointer to System Table // // Output: EFI_STATUS // //-------------------------------------------------------------------------- // EFI_STATUS GetRomLayout( IN EFI_SYSTEM_TABLE *SystemTable, EFI_SMM_BASE_PROTOCOL *SmmBase, OUT ROM_AREA **RomLayout ) { EFI_STATUS Status; static EFI_GUID HobListGuid = HOB_LIST_GUID; static EFI_GUID AmiRomLayoutHobGuid = AMI_ROM_LAYOUT_HOB_GUID; ROM_LAYOUT_HOB *RomLayoutHob; UINTN RomLayoutSize=0, Size; ROM_AREA *Area; APTIO_FW_CAPSULE_HEADER *FwCapHdr; UINT8* pFwCapHdr=NULL; // 1. Try to locate RomLayout from embedded CapHdr Ffs Status = GetFwCapFfs(&gFwCapFfsGuid, &pFwCapHdr, &Size); if(!EFI_ERROR(Status)) { // Skip over Section GUID FwCapHdr = (APTIO_FW_CAPSULE_HEADER*)pFwCapHdr; (UINT8*)FwCapHdr += sizeof (EFI_GUID); Size -= sizeof (EFI_GUID); *RomLayout = (ROM_AREA *)(UINTN)((UINT32)FwCapHdr+FwCapHdr->RomLayoutOffset); RomLayoutSize = sizeof(ROM_AREA); for (Area=*RomLayout; Area->Size!=0 && RomLayoutSize<=(Size - FwCapHdr->RomLayoutOffset); Area++) { RomLayoutSize+=sizeof(ROM_AREA); } // TRACE((-1, "Get Rom Map from the FwCap FFS at %X(size 0x%X)\nRomLayout offs %X(size 0x%X)\n", FwCapHdr, Size, FwCapHdr->RomLayoutOffset, RomLayoutSize)); Area=*RomLayout; } else { // 2. Backup: Use primary RomLayout from Rom Layout HOB. // This one does not yet report the Rom Hole regions //----- Get HobList ------------------------------------- RomLayoutHob = GetEfiConfigurationTable(SystemTable, &HobListGuid); if (RomLayoutHob!=NULL) { // -------- Get RomLayoutHob ---------------------- if (!EFI_ERROR( FindNextHobByGuid(&AmiRomLayoutHobGuid, &RomLayoutHob) )) { RomLayoutSize = RomLayoutHob->Header.Header.HobLength - sizeof(ROM_LAYOUT_HOB); Area=(ROM_AREA*)((UINT8*)RomLayoutHob+1); //TRACE((-1, "Get Default Rom Map from the Hob at %X\n", Area)); } } } if(RomLayoutSize) { //---Allocate memory in SMRAM for RomLayout--- *RomLayout = NULL; Status = pSmst->SmmAllocatePool(EfiRuntimeServicesData, RomLayoutSize,(void **)RomLayout); if (EFI_ERROR(Status) || *RomLayout == NULL) return EFI_NOT_FOUND; pBS->CopyMem( *RomLayout, Area, RomLayoutSize); if(pFwCapHdr) pBS->FreePool(pFwCapHdr); return EFI_SUCCESS; } return EFI_NOT_FOUND; } #endif //#if RUNTIME_SECURE_UPDATE_FLOW == 1 // //--------------------------------------------------------------------------- // // Procedure: IsAddressInSmram // // Description: CThis function check if the address is in SMRAM // // Input: // Address - the buffer address to be checked. // Range - the buffer length to be checked // // Output: // TRUE this address is in SMRAM. // FALSE this address is NOT in SMRAM. // //--------------------------------------------------------------------------- // BOOLEAN IsAddressInSmram ( IN EFI_PHYSICAL_ADDRESS Buffer, IN UINT64 Length ) { UINTN Index; //TRACE((TRACE_ALWAYS,"Addr in SMRAM %X_%X\n",Buffer, Length)); for (Index = 0; Index < mSmramRangeCount; Index ++) { if (((Buffer >= mSmramRanges[Index].CpuStart) && (Buffer < mSmramRanges[Index].CpuStart + mSmramRanges[Index].PhysicalSize)) || ((mSmramRanges[Index].CpuStart >= Buffer) && (mSmramRanges[Index].CpuStart < Buffer + Length))) { //TRACE((TRACE_ALWAYS,"TRUE\n")); return TRUE; } } //TRACE((TRACE_ALWAYS,"FALSE\n")); return FALSE; } // !!! do not install if OFBD SecFlash is installed #if INSTALL_SECURE_FLASH_SW_SMI_HNDL == 1 // //---------------------------------------------------------------------- // Procedure: SecSMIFlashSMIHandler // // Description: // // Input: // // Output: // // Returns: // //---------------------------------------------------------------------- // VOID SecSMIFlashSMIHandler ( IN EFI_HANDLE DispatchHandle, IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext ) { EFI_SMM_CPU_SAVE_STATE *pCpuSaveState; SW_SMI_CPU_TRIGGER *SwSmiCpuTrigger; UINT8 Data; UINT64 pCommBuff; UINT32 HighBufferAddress = 0; UINT32 LowBufferAddress = 0; UINTN i; UINTN Cpu = pSmst->CurrentlyExecutingCpu - 1; for (i = 0; i < pSmst->NumberOfTableEntries; ++i) { if (guidcmp(&pSmst->SmmConfigurationTable[i].VendorGuid,&gSwSmiCpuTriggerGuid) == 0) { break; } } //If found table, check for the CPU that caused the software Smi. if (i != pSmst->NumberOfTableEntries) { SwSmiCpuTrigger = pSmst->SmmConfigurationTable[i].VendorTable; Cpu = SwSmiCpuTrigger->Cpu; } // // Found Invalid CPU number, return // if(Cpu == (UINTN) -1) { return; } Data = (UINT8)DispatchContext->SwSmiInputValue; pCpuSaveState = pSmst->CpuSaveState; HighBufferAddress = pCpuSaveState[Cpu].Ia32SaveState.ECX; LowBufferAddress = pCpuSaveState[Cpu].Ia32SaveState.EBX; pCommBuff = HighBufferAddress; pCommBuff = Shl64(pCommBuff, 32); pCommBuff += LowBufferAddress; //TRACE((-1, "Sec SW SMI Flash Hook == 0x%x\n", Data)); switch(Data) { case SecSMIflash_Load: // 0x1d Send Flash Block to memory LoadFwImage((FUNC_BLOCK *)pCommBuff); break; case SecSMIflash_GetPolicy: // 0x1e Get Fl Upd Policy GetFlUpdPolicy((FLASH_POLICY_INFO_BLOCK *)pCommBuff); break; case SecSMIflash_SetFlash: // 0x1f Set Flash method SetFlUpdMethod((FUNC_FLASH_SESSION_BLOCK *)pCommBuff); break; } } #endif // //---------------------------------------------------------------------- // Procedure: InSmmFunction // // Description: // // Input: // // Output: // // Returns: // //---------------------------------------------------------------------- // EFI_STATUS InSmmFunction(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { #if INSTALL_SECURE_FLASH_SW_SMI_HNDL == 1 EFI_SMM_SW_DISPATCH_PROTOCOL *pSwDispatch = NULL; EFI_SMM_SW_DISPATCH_CONTEXT SwContext; UINTN Index; #endif #if FWCAPSULE_RECOVERY_SUPPORT == 1 EFI_SMM_SX_DISPATCH_CONTEXT SxDispatchContext; EFI_SMM_SX_DISPATCH_PROTOCOL *SxDispatchProtocol; #endif EFI_HANDLE Handle = NULL; EFI_HANDLE DummyHandle = NULL; EFI_STATUS Status; UINTN Size = 0; UINT8 MinSMIPort = SecSMIflash_Load; //0x1d //UINT8 MinSMIPort = SecSMIflash_GetPolicy; //0x1e; UINT8 MaxSMIPort = SecSMIflash_SetFlash; //0x1f; //PI 1.1 ++ #if defined(PI_SPECIFICATION_VERSION)&&(PI_SPECIFICATION_VERSION>=0x0001000A) EFI_SMM_ACCESS2_PROTOCOL *SmmAccess; #else EFI_SMM_ACCESS_PROTOCOL *SmmAccess; #endif InitAmiSmmLib( ImageHandle, SystemTable ); Status = pBS->LocateProtocol(&gAmiSmmDigitalSignatureProtocolGuid, NULL, &gAmiSig); ASSERT_EFI_ERROR (Status); if (EFI_ERROR(Status)) return Status; // Test if Root Platform Key is available,else - don't install Flash Upd security measures. gpPubKeyHndl.Blob = NULL; gpPubKeyHndl.BlobSize = 0; Status = gAmiSig->GetKey(gAmiSig, &gpPubKeyHndl, &gPRKeyGuid, gpPubKeyHndl.BlobSize, 0); //TRACE((TRACE_ALWAYS,"GetKey %r (%x, %d bytes)\n",Status, gpPubKeyHndl.Blob,gpPubKeyHndl.BlobSize)); if (EFI_ERROR(Status) || gpPubKeyHndl.Blob == NULL) { if(Status == EFI_BUFFER_TOO_SMALL) return EFI_SUCCESS; return Status; } // // Get SMRAM information // //PI 1.1 ++ #if defined(PI_SPECIFICATION_VERSION)&&(PI_SPECIFICATION_VERSION>=0x0001000A) Status = pBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess); ASSERT_EFI_ERROR (Status); if (EFI_ERROR(Status)) return Status; #else Status = pBS->LocateProtocol(&gEfiSmmAccessProtocolGuid, NULL, &SmmAccess); ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status)) return Status; #endif Size = 0; Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL); ASSERT (Status == EFI_BUFFER_TOO_SMALL); if (Size==0) return EFI_NOT_FOUND; Status = pSmst->SmmAllocatePool (EfiRuntimeServicesData,Size,(VOID **)&mSmramRanges); ASSERT_EFI_ERROR (Status); if (EFI_ERROR(Status)) return Status; Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges); if (EFI_ERROR(Status)) return Status; mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR); // // Allocate scratch buffer to hold entire Signed BIOS image for Secure Capsule and Runtime Flash Updates // AFU would have to execute a sequence of SW SMI calls to push entire BIOS image to SMM // //NEW_BIOS_MEM_ALLOC == 2 AFU will allocate a buffer and provide pointer via SET_FLASH_METHOD API call. // #if NEW_BIOS_MEM_ALLOC == 0 // // Alternatively the buffer may be reserved within the SMM TSEG memory // Status = pSmmBase->SmmAllocatePool(pSmmBase, EfiRuntimeServicesData, gFwCapMaxSize, (void**)&pFwCapsuleLowMem); //TRACE((TRACE_ALWAYS,"SecSmiFlash: Alloc 0x%X bytes in SMM, %r\n",gRomFileSize, Status)); #else #if NEW_BIOS_MEM_ALLOC == 1 // // The buffer allocated in OS reserved memory below 4GB // // Status = pST->BootServices->AllocatePool(EfiReservedMemoryType, gRomFileSize, &pFwCapsuleLowMem); pFwCapsuleLowMem = (UINT32*)0xFFFFFFFF; Status = pST->BootServices->AllocatePages(AllocateMaxAddress, EfiReservedMemoryType, EFI_SIZE_TO_PAGES(gFwCapMaxSize), (EFI_PHYSICAL_ADDRESS*)&pFwCapsuleLowMem); //TRACE((TRACE_ALWAYS,"SecSmiFlash: AllocatePages=%X,(0x%x) %r\n",pFwCapsuleLowMem,gRomFileSize, Status)); #endif #endif ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status)) return Status; #if NEW_BIOS_MEM_ALLOC < 2 MemSet((void*)pFwCapsuleLowMem, gFwCapMaxSize, 0 ); #endif // // Allocate space to hold a Hash table for all Flash blocks // Size = SEC_FLASH_HASH_TBL_SIZE; Status = pSmmBase->SmmAllocatePool(pSmmBase, EfiRuntimeServicesData, Size, (void**)&gHashTbl); if (EFI_ERROR(Status)) return Status; MemSet((void*)gHashTbl, Size, 0xdb ); #if FWCAPSULE_RECOVERY_SUPPORT == 0 FlUpdatePolicy.FlashUpdate &=~FlCapsule; FlUpdatePolicy.BBUpdate &=~FlCapsule; #else // // Reserve pool in smm runtime memory for capsule's mailbox list // Size = 4*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR) + sizeof(EFI_CAPSULE_HEADER); // (4*16)+28 Status = pSmmBase->SmmAllocatePool(pSmmBase, EfiRuntimeServicesData, Size, (void**)&gpEfiCapsuleHdr); //TRACE((TRACE_ALWAYS,"Mailbox: AllocatePages=%X,(0x%x) %r\n",gpEfiCapsuleHdr,Size, Status)); if (EFI_ERROR(Status)) return Status; MemSet((void*)gpEfiCapsuleHdr, Size, 0 ); // // Install callback on S5 Sleep Type SMI. Needed to transition to S3 if Capsule's mailbox ie pending // Locate the Sx Dispatch Protocol // // gEfiSmmSxDispatch2ProtocolGuid Status = pBS->LocateProtocol (&gEfiSmmSxDispatchProtocolGuid,NULL,&SxDispatchProtocol); ASSERT_EFI_ERROR (Status); if (EFI_ERROR(Status)) return Status; // // Register the callback for S5 entry // if (SxDispatchProtocol && SupportUpdateCapsuleReset()) { SxDispatchContext.Type = SxS5; SxDispatchContext.Phase = SxEntry; Status = SxDispatchProtocol->Register (SxDispatchProtocol,SmiS5CapsuleCallback,&SxDispatchContext,&Handle); ASSERT_EFI_ERROR (Status); } if (EFI_ERROR(Status)) goto Done; #endif #if RUNTIME_SECURE_UPDATE_FLOW == 1 /* AFU For Rom Holes in Runtime/Capsule upd 1. Read full ROM image to ROM buffer 2. Merge Rom Hole from input file to ROM buffer 3. call "LoadImage" for full BIOS 3. call "SetFlash" with Runtime update (NVRAM block should be unsigned!!!) 4. calls to upd Rom hole -erase,write should pass */ Status = GetRomLayout(SystemTable, pSmmBase, &RomLayout); //TRACE((TRACE_ALWAYS,"SecSmiFlash: Get Rom Layout ptr=%X, %r\n",RomLayout, Status)); // Rom Layout HOB may not be found in Recovery mode and if FW does not include built in FwCapsule Hdr file // if (EFI_ERROR(Status)) goto Done; // // Trap the original Flash Driver API calls to enforce // Flash Write protection in SMM at the driver API level // Status = pBS->LocateProtocol(&gFlashSmmProtocolGuid, NULL, &Flash); //TRACE((TRACE_ALWAYS,"SecSmiFlash: Flash Protocol Fixup %X->%X\n",Flash->Write,SecureFlashWrite)); if (EFI_ERROR(Status)) goto Done; // preserve org Flash API pFlashWrite = Flash->Write; pFlashErase = Flash->Erase; // replace with local functions Flash->Erase = SecureFlashErase; Flash->Write = SecureFlashWrite; // Calculate the flash mapping start address. This is calculated // as follows: // 1. Find the total size of the flash (FLASH_BLOCK_SIZE * NUMBER_OF_BLOCKS) // 2. Subtract the total flash size from 4GB Flash4GBMapStart = 0xFFFFFFFF - (FLASH_BLOCK_SIZE * NUMBER_OF_BLOCKS); Flash4GBMapStart ++; #endif // // Install Secure SMI Flash Protocol // SecSmiFlash.pFwCapsule = pFwCapsuleLowMem; SecSmiFlash.HashTbl = gHashTbl; SecSmiFlash.RomLayout = RomLayout; // SecSmiFlash.FSHandle = 0; Status = pBS->InstallMultipleProtocolInterfaces( &DummyHandle, &gSecureSMIFlashProtocolGuid,&SecSmiFlash, NULL ); ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status)) return Status; // // Install SW SMI callbacks for 3 SecSMI Flash functions // !!! do not install if OFBD SecFlash is installed // #if INSTALL_SECURE_FLASH_SW_SMI_HNDL == 1 Status = pBS->LocateProtocol(&gEfiSmmSwDispatchProtocolGuid, NULL, &pSwDispatch); ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status)) return EFI_SUCCESS; for(Index=MinSMIPort;Index<=MaxSMIPort;Index++) { SwContext.SwSmiInputValue = Index; Status = pSwDispatch->Register(pSwDispatch, SecSMIFlashSMIHandler, &SwContext, &Handle); ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status)) break; //If any errors,unregister any registered SwSMI by this driver. //If error, and driver is unloaded, then a serious problem would exist. } #endif Done: return EFI_SUCCESS; } // //---------------------------------------------------------------------- // Procedure: SecSMIFlashDriverEntryPoint // // Description: Secure SMI Flash driver init // // Input: // // Output: // // Returns: // //---------------------------------------------------------------------- // EFI_STATUS SecSMIFlashDriverEntryPoint( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { InitAmiLib(ImageHandle, SystemTable); return InitSmmHandler(ImageHandle, SystemTable, InSmmFunction, NULL); } //********************************************************************** //********************************************************************** //** ** //** (C)Copyright 1985-2014, American Megatrends, Inc. ** //** ** //** All Rights Reserved. ** //** ** //** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 ** //** ** //** Phone: (770)-246-8600 ** //** ** //********************************************************************** //**********************************************************************