/** @file OcRtcLib - library with RTC I/O functions Copyright (c) 2020, vit9696 All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include #include #include #include #include #include #include #include #include #include "OcRtcLibInternal.h" STATIC EFI_LOCK mAppleRtcRamLock; STATIC UINT8 mEmulatedRtcArea[APPLE_RTC_TOTAL_SIZE]; STATIC BOOLEAN mEmulatedRtcStatus[APPLE_RTC_TOTAL_SIZE]; STATIC EFI_STATUS SyncRtcRead ( IN UINT8 Address, IN UINT8 *ValuePtr ) { EFI_STATUS Status; if (mEmulatedRtcStatus[Address]) { return mEmulatedRtcArea[Address]; } Status = EfiAcquireLockOrFail (&mAppleRtcRamLock); if (EFI_ERROR (Status)) { return Status; } *ValuePtr = OcRtcRead (Address); EfiReleaseLock (&mAppleRtcRamLock); return EFI_SUCCESS; } STATIC EFI_STATUS SyncRtcWrite ( IN UINT8 Address, IN UINT8 Value ) { EFI_STATUS Status; if (mEmulatedRtcStatus[Address]) { mEmulatedRtcArea[Address] = Value; return EFI_SUCCESS; } Status = EfiAcquireLockOrFail (&mAppleRtcRamLock); if (EFI_ERROR (Status)) { return Status; } OcRtcWrite (Address, Value); EfiReleaseLock (&mAppleRtcRamLock); return EFI_SUCCESS; } STATIC EFI_STATUS SyncRtcWaitForReady ( VOID ) { EFI_STATUS Status; UINTN Count; UINT8 RegisterA; for (Count = 0; Count < 100; ++Count) { Status = EfiAcquireLockOrFail (&mAppleRtcRamLock); if (!EFI_ERROR (Status)) { RegisterA = OcRtcRead (RTC_ADDRESS_REGISTER_A); EfiReleaseLock (&mAppleRtcRamLock); if ((RegisterA & RTC_UPDATE_IN_PROGRESS) == 0) { return EFI_SUCCESS; } } // // Wait 1 ms and retry. // gBS->Stall (1000); } return EFI_TIMEOUT; } STATIC UINTN EFIAPI AppleRtcGetAvailableMemory ( IN APPLE_RTC_RAM_PROTOCOL *This ) { return APPLE_RTC_TOTAL_SIZE; } STATIC EFI_STATUS EFIAPI AppleRtcRamReadData ( IN APPLE_RTC_RAM_PROTOCOL *This, OUT UINT8 *Buffer, IN UINTN BufferSize, IN UINTN Address ) { EFI_STATUS Status; UINTN Index; UINT8 Temp; if (Buffer == NULL || BufferSize == 0 || Address >= APPLE_RTC_TOTAL_SIZE || BufferSize >= APPLE_RTC_TOTAL_SIZE || Address + BufferSize > APPLE_RTC_TOTAL_SIZE) { return EFI_INVALID_PARAMETER; } for (Index = 0; Index < BufferSize; ++Index) { Status = SyncRtcWaitForReady (); if (EFI_ERROR (Status)) { return Status; } Status = SyncRtcRead ((UINT8) Address, Buffer); if (EFI_ERROR (Status)) { return Status; } if (Address == APPLE_RTC_BG_COLOR_ADDR) { Status = SyncRtcWaitForReady (); if (EFI_ERROR (Status)) { return Status; } Status = SyncRtcRead (APPLE_RTC_BG_COMPLEMENT_ADDR, &Temp); if (EFI_ERROR (Status)) { return Status; } if ((Temp ^ *Buffer) != 0xFF) { *Buffer = 0; } } ++Buffer; ++Address; } return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI AppleRtcRamWriteData ( IN APPLE_RTC_RAM_PROTOCOL *This, IN CONST UINT8 *Buffer, IN UINTN BufferSize, IN UINTN Address ) { EFI_STATUS Status; UINT8 TempBuffer[APPLE_RTC_TOTAL_SIZE]; UINTN Index; UINT16 Checksum; if (Buffer == NULL || BufferSize == 0 || Address < APPLE_RTC_CHECKSUM_START || Address >= APPLE_RTC_TOTAL_SIZE || BufferSize >= APPLE_RTC_TOTAL_SIZE || Address + BufferSize > APPLE_RTC_TOTAL_SIZE) { return EFI_INVALID_PARAMETER; } Status = AppleRtcRamReadData (This, TempBuffer, APPLE_RTC_TOTAL_SIZE, 0); if (EFI_ERROR (Status)) { return Status; ///< This is not checked in the original. } for (Index = 0; Index < BufferSize; ++Index) { if (Address != APPLE_RTC_BG_COMPLEMENT_ADDR) { TempBuffer[Address] = *Buffer; if (Address == APPLE_RTC_BG_COLOR_ADDR) { TempBuffer[APPLE_RTC_BG_COMPLEMENT_ADDR] = (UINT8) ~((UINT32) *Buffer); } } ++Buffer; ++Address; } Checksum = OcRtcChecksumApple (TempBuffer, APPLE_RTC_CORE_CHECKSUM_ADDR1); TempBuffer[APPLE_RTC_CORE_CHECKSUM_ADDR1] = APPLE_RTC_CORE_CHECKSUM_BYTE1 (Checksum); TempBuffer[APPLE_RTC_CORE_CHECKSUM_ADDR2] = APPLE_RTC_CORE_CHECKSUM_BYTE2 (Checksum); TempBuffer[APPLE_RTC_MAIN_CHECKSUM_ADDR1] = 0; TempBuffer[APPLE_RTC_MAIN_CHECKSUM_ADDR2] = 0; Checksum = OcRtcChecksumApple (TempBuffer, APPLE_RTC_TOTAL_SIZE); TempBuffer[APPLE_RTC_MAIN_CHECKSUM_ADDR1] = APPLE_RTC_MAIN_CHECKSUM_BYTE1 (Checksum); TempBuffer[APPLE_RTC_MAIN_CHECKSUM_ADDR2] = APPLE_RTC_MAIN_CHECKSUM_BYTE2 (Checksum); for (Index = APPLE_RTC_CHECKSUM_START; Index < APPLE_RTC_TOTAL_SIZE; ++Index) { SyncRtcWrite ((UINT8) Index, TempBuffer[Index]); ///< Does not check the error code. } return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI AppleRtcRamReset ( IN APPLE_RTC_RAM_PROTOCOL *This ) { EFI_STATUS Status; UINTN Index; UINT8 Buffer[APPLE_RTC_TOTAL_SIZE]; ZeroMem (Buffer, sizeof (APPLE_RTC_TOTAL_SIZE)); for (Index = APPLE_RTC_CHECKSUM_START; Index < APPLE_RTC_CORE_SIZE; ++Index) { Status = SyncRtcWaitForReady (); if (!EFI_ERROR (Status)) { SyncRtcRead ((UINT8) Index, &Buffer[Index]); } } Status = SyncRtcWaitForReady (); if (!EFI_ERROR (Status)) { SyncRtcRead ((UINT8) APPLE_RTC_FIRMWARE_57_ADDR, &Buffer[APPLE_RTC_FIRMWARE_57_ADDR]); } for (Index = APPLE_RTC_RESERVED_ADDR; Index < APPLE_RTC_RESERVED_ADDR + APPLE_RTC_RESERVED_LENGTH; ++Index) { Status = SyncRtcWaitForReady (); if (!EFI_ERROR (Status)) { SyncRtcRead ((UINT8) Index, &Buffer[Index]); } } return AppleRtcRamWriteData ( This, Buffer + APPLE_RTC_CHECKSUM_START, APPLE_RTC_TOTAL_SIZE - APPLE_RTC_CHECKSUM_START, APPLE_RTC_CHECKSUM_START ); } STATIC APPLE_RTC_RAM_PROTOCOL mAppleRtcRamProtocol = { AppleRtcGetAvailableMemory, AppleRtcRamReadData, AppleRtcRamWriteData, AppleRtcRamReset, }; APPLE_RTC_RAM_PROTOCOL * OcAppleRtcRamInstallProtocol ( IN BOOLEAN Reinstall ) { EFI_STATUS Status; APPLE_RTC_RAM_PROTOCOL *Protocol; UINT8 *RtcBlacklist; UINTN Index; UINTN RtcBlacklistSize; DEBUG ((DEBUG_VERBOSE, "OCRTC: OcAppleRtcRamInstallProtocol\n")); if (Reinstall) { Status = OcUninstallAllProtocolInstances (&gAppleRtcRamProtocolGuid); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCRTC: Uninstall failed: %r\n", Status)); return NULL; } } else { Status = gBS->LocateProtocol ( &gAppleRtcRamProtocolGuid, NULL, (VOID *) &Protocol ); if (!EFI_ERROR (Status)) { return Protocol; } } DEBUG (( DEBUG_INFO, "OCRTC: Wake log is 0x%02X 0x%02X % 3d 0x%02X\n", OcRtcRead (APPLE_RTC_TRACE_DATA_ADDR), OcRtcRead (APPLE_RTC_WL_MASK_ADDR), OcRtcRead (APPLE_RTC_WL_EVENT_ADDR), OcRtcRead (APPLE_RTC_WL_EVENT_EXTRA_ADDR) )); Status = GetVariable2 ( OC_RTC_BLACKLIST_VARIABLE_NAME, &gOcVendorVariableGuid, (VOID **) &RtcBlacklist, &RtcBlacklistSize ); if (!EFI_ERROR (Status)) { for (Index = 0; Index < RtcBlacklistSize; ++Index) { mEmulatedRtcStatus[RtcBlacklist[Index]] = TRUE; DEBUG ((DEBUG_INFO, "OCRTC: Blacklisted %02x address\n", RtcBlacklist[Index])); } FreePool (RtcBlacklist); } // // Note, for debugging on QEMU this will need to changed to TPL_CALLBACK. // By default we follow AppleRtcRam implementation. // Also, AppleRtcRam function may be called after ExitBootServices or around // GetMemoryMap, so debugging is not perfectly safe. // EfiInitializeLock (&mAppleRtcRamLock, TPL_NOTIFY); // // Note, Apple implementation calls AppleRtcRamReset on checksum mismatch. // Status = gBS->InstallMultipleProtocolInterfaces ( &gImageHandle, &gAppleRtcRamProtocolGuid, (VOID *) &mAppleRtcRamProtocol, NULL ); if (EFI_ERROR (Status)) { return NULL; } return &mAppleRtcRamProtocol; }