/** @file Provide swap operation of boot block. ;****************************************************************************** ;* Copyright (c) 2020 - 2021, 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 #define R_PCH_SPI_STRP_DSCR_P_A0 0x4C #define R_PCH_SPI_STRP_DSCR_S_A0 0x5C #define R_PCH_SPI_STRP_DSCR_S_B0 0x68 #define N_PCH_SPI_STRP_DSCR_TSBS (BIT4|BIT5|BIT6) #define V_PCH_SPI_STRP_DSCR_TSBS_64KB 0x0 //(000) #define V_PCH_SPI_STRP_DSCR_TSBS_128KB 0x1 //(001) #define V_PCH_SPI_STRP_DSCR_TSBS_256KB 0x2 //(010) #define V_PCH_SPI_STRP_DSCR_TSBS_512KB 0x3 //(011) #define V_PCH_SPI_STRP_DSCR_TSBS_1MB 0x4 //(100) #define V_PCH_SPI_STRP_DSCR_TSBS_2MB 0x5 //(101) #define V_PCH_SPI_STRP_DSCR_TSBS_4MB 0x6 //(110) #define V_PCH_SPI_STRP_DSCR_TSBS_8MB 0x7 //(111) #define R_SPI_CFG_BC 0xDC #define B_SPI_CFG_BC_TSS BIT4 EFI_SWAP_ADDRESS_RANGE_PROTOCOL mSwapAddressRange; BOOLEAN mSwapLocked = FALSE; UINT32 GetTopSwapSize ( VOID ) { EFI_STATUS Status; PCH_SPI_PROTOCOL *SpiProtocol = NULL; UINT8 PchStrap; UINT8 TSBS; UINT32 TopswapSize; UINT16 FlashDescriptorRecords; DEBUG ((DEBUG_INFO, "Check PlatformCheckFtSupported.\n")); PchStrap = 0; TSBS = 0; TopswapSize = 0; // // @TODO: Determine if we need an API from SI or not. // Status = gBS->LocateProtocol (&gPchSpiProtocolGuid, NULL, (VOID **) &SpiProtocol); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to locate PCH SPI protocol\n")); return 0; } switch (PchSeries ()) { case PCH_S: switch (PchStepping ()) { case PCH_A0: FlashDescriptorRecords = R_PCH_SPI_STRP_DSCR_S_A0; break; case PCH_B0: default: FlashDescriptorRecords = R_PCH_SPI_STRP_DSCR_S_B0; break; } break; case PCH_P: default: switch (PchStepping ()) { case PCH_A0: default: FlashDescriptorRecords = R_PCH_SPI_STRP_DSCR_P_A0; break; } break; } Status = SpiProtocol->ReadPchSoftStrap (SpiProtocol, FlashDescriptorRecords, sizeof (PchStrap), &PchStrap); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to read PCH soft strap\n")); return 0; } DEBUG ((DEBUG_INFO, "PCH Rescriptor Record: 0x%x\n", PchStrap)); TSBS = (PchStrap & N_PCH_SPI_STRP_DSCR_TSBS) >> 4; switch (TSBS) { case V_PCH_SPI_STRP_DSCR_TSBS_64KB: TopswapSize = SIZE_64KB; break; case V_PCH_SPI_STRP_DSCR_TSBS_128KB: TopswapSize = SIZE_128KB; break; case V_PCH_SPI_STRP_DSCR_TSBS_256KB: TopswapSize = SIZE_256KB; break; case V_PCH_SPI_STRP_DSCR_TSBS_512KB: TopswapSize = SIZE_512KB; break; case V_PCH_SPI_STRP_DSCR_TSBS_1MB: TopswapSize = SIZE_1MB; break; case V_PCH_SPI_STRP_DSCR_TSBS_2MB: TopswapSize = SIZE_2MB; break; case V_PCH_SPI_STRP_DSCR_TSBS_4MB: TopswapSize = SIZE_4MB; break; case V_PCH_SPI_STRP_DSCR_TSBS_8MB: TopswapSize = SIZE_8MB; break; default: TopswapSize = 0; break; } return TopswapSize; } /** This function gets the address range location of boot block and backup block. @param This Indicates the calling context. @param BootBlockBase The base address of current boot block. @param BootBlockSize The size (in bytes) of current boot block. @param BackupBlockBase The base address of current backup block. @param BackupBlockSize The size (in bytes) of current backup block. @retval EFI_SUCCESS The call was successful. **/ EFI_STATUS EFIAPI GetRangeLocation ( IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL *This, OUT EFI_PHYSICAL_ADDRESS *BootBlockBase, OUT UINTN *BootBlockSize, OUT EFI_PHYSICAL_ADDRESS *BackupBlockBase, OUT UINTN *BackupBlockSize ) { *BootBlockBase = (EFI_PHYSICAL_ADDRESS)FixedPcdGet32 (PcdFlashPbbBase); *BootBlockSize = (UINTN) GetTopSwapSize(); //FixedPcdGet32 (PcdFlashPbbSize); *BackupBlockBase = (EFI_PHYSICAL_ADDRESS)FixedPcdGet32 (PcdFlashPbbRBase); *BackupBlockSize = *BootBlockSize; return EFI_SUCCESS; } /** This service checks if the boot block and backup block has been swapped. @param This Indicates the calling context. @param SwapState True if the boot block and backup block has been swapped. False if the boot block and backup block has not been swapped. @retval EFI_SUCCESS The call was successful. **/ EFI_STATUS EFIAPI GetSwapState ( IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL *This, OUT BOOLEAN *SwapState ) { EFI_HOB_GUID_TYPE *GuidHob; BOOLEAN TopSwapStatus; TopSwapStatus = !!(PciSegmentRead32 (SpiPciCfgBase () + R_SPI_CFG_BC) & B_SPI_CFG_BC_TSS); if (!TopSwapStatus) { GuidHob = GetFirstGuidHob (&gChasmfallsTopSwapStatusGuid); if (GuidHob != NULL) { TopSwapStatus = *((BOOLEAN *) GET_GUID_HOB_DATA (GuidHob)); } } *SwapState = TopSwapStatus; return EFI_SUCCESS; } /** This service swaps the boot block and backup block, or swaps them back. It also acquires and releases software swap lock during operation. The setting of the new swap state is not affected by the old swap state. @param This Indicates the calling context. @param NewSwapState True to swap real boot block and backup block, False to swap them back. @retval EFI_SUCCESS The call was successful. @retval EFI_ABORTED Set swap state error. **/ EFI_STATUS EFIAPI SetSwapState ( IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL *This, IN BOOLEAN NewSwapState ) { if (mSwapLocked) { return EFI_ABORTED; } if (NewSwapState) { DEBUG ((DEBUG_INFO, "Enable TopSwap via SwSmi (0x%x)\n", PcdGet8 (PcdTopSwapEnableSwSmi))); IoWrite8 (R_PCH_IO_APM_CNT, PcdGet8 (PcdTopSwapEnableSwSmi)); } else { DEBUG ((DEBUG_INFO, "Disable TopSwap via SwSmi (0x%x)\n", PcdGet8 (PcdTopSwapDisableSwSmi))); IoWrite8 (R_PCH_IO_APM_CNT, PcdGet8 (PcdTopSwapDisableSwSmi)); } return EFI_SUCCESS; } /** This service checks if a Real Time Clock (RTC) power failure happened. If parameter RtcPowerFailed is true after the function returns, RTC power supply failed or was removed. It is recommended to check RTC power status before calling GetSwapState(). @param This Indicates the calling context. @param RtcPowerFailed True if the RTC (Real Time Clock) power failed or was removed. @retval EFI_SUCCESS The call was successful. @retval EFI_UNSUPPORTED The service is unsupported. **/ EFI_STATUS EFIAPI GetRtcPowerStatus ( IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL *This, OUT BOOLEAN *RtcPowerFailed ) { return EFI_UNSUPPORTED; } /** This service returns all lock methods for swap operations that the current platform supports. Could be software lock, hardware lock, or unsupport lock. Note that software and hardware lock methods can be used simultaneously. @param This Indicates the calling context. @param LockCapability The current lock method for swap operations. @retval EFI_SUCCESS The call was successful. **/ EFI_STATUS EFIAPI GetSwapLockCapability ( IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL *This, OUT EFI_SWAP_LOCK_CAPABILITY *LockCapability ) { *LockCapability = EFI_SOFTWARE_LOCK; return EFI_SUCCESS; } /** This service is used to acquire or release appointed kind of lock for Swap Address Range operations. Note that software and hardware lock mothod can be used simultaneously. @param This Indicates the calling context. @param LockCapability Indicates which lock to acquire or release. @param NewLockState True to acquire lock; False to release lock. @retval EFI_SUCCESS The call was successful. **/ EFI_STATUS EFIAPI SetSwapLock ( IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL *This, IN EFI_SWAP_LOCK_CAPABILITY LockCapability, IN BOOLEAN NewLockState ) { if (LockCapability == EFI_SOFTWARE_LOCK) { mSwapLocked = NewLockState; } return EFI_SUCCESS; } /** Function performs a software lock for Swap. @param[in] Event - A pointer to the Event that triggered the callback. @param[in] Context - A pointer to private data registered with the callback function. **/ VOID EFIAPI SwapLockCallback ( IN EFI_EVENT Event, IN VOID *Context ) { gBS->CloseEvent (Event); mSwapLocked = TRUE; } /** The driver's entry point. It publishes Swap Address Range Protocol. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI DriverEntry ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle; EFI_EVENT EndOfDxeEvent; mSwapAddressRange.GetRangeLocation = GetRangeLocation; mSwapAddressRange.GetRtcPowerStatus = GetRtcPowerStatus; mSwapAddressRange.GetSwapLockCapability = GetSwapLockCapability; mSwapAddressRange.GetSwapState = GetSwapState; mSwapAddressRange.SetSwapLock = SetSwapLock; mSwapAddressRange.SetSwapState = SetSwapState; Handle = NULL; Status = gBS->InstallProtocolInterface ( &Handle, &gEfiSwapAddressRangeProtocolGuid, EFI_NATIVE_INTERFACE, &mSwapAddressRange ); ASSERT_EFI_ERROR (Status); // // Register End of DXE event // Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, SwapLockCallback, NULL, &gEfiEndOfDxeEventGroupGuid, &EndOfDxeEvent ); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }