251 lines
6.4 KiB
C++
Executable File
251 lines
6.4 KiB
C++
Executable File
//
|
|
// kern_rtc.hpp
|
|
// Lilu
|
|
//
|
|
// Copyright © 2018 vit9696. All rights reserved.
|
|
//
|
|
|
|
#ifndef kern_rtc_h
|
|
#define kern_rtc_h
|
|
|
|
#include <Headers/kern_util.hpp>
|
|
#include <IOKit/IOService.h>
|
|
#include <IOKit/acpi/IOACPIPlatformDevice.h>
|
|
#include <IOKit/IOUserClient.h>
|
|
|
|
class RTCStorage {
|
|
/**
|
|
* Apple-specific RTC checksum addresses
|
|
*/
|
|
static constexpr uint8_t APPLERTC_HASHED_ADDR = 0x0E;
|
|
static constexpr uint8_t APPLERTC_CHECKSUM_ADDR1 = 0x58;
|
|
static constexpr uint8_t APPLERTC_CHECKSUM_ADDR2 = 0x59;
|
|
|
|
/**
|
|
* AppleRTC service handle
|
|
*/
|
|
IOService *rtcSrv {nullptr};
|
|
|
|
/**
|
|
* Low-level RTC read (does not check memory availability).
|
|
*
|
|
* @param dev RTC ACPI device
|
|
* @param offset offset
|
|
*
|
|
* @result read value
|
|
*/
|
|
static uint8_t readByte(IOACPIPlatformDevice *dev, uint8_t offset);
|
|
|
|
/**
|
|
* Low-level RTC write (does not check memory availability).
|
|
*
|
|
* @param dev RTC ACPI device
|
|
* @param offset offset
|
|
* @param value value
|
|
*/
|
|
static void writeByte(IOACPIPlatformDevice *dev, uint8_t offset, uint8_t value);
|
|
public:
|
|
/**
|
|
* General access RTC ports on x86 systems.
|
|
*/
|
|
static constexpr uint8_t R_PCH_RTC_INDEX = 0x70;
|
|
static constexpr uint8_t R_PCH_RTC_TARGET = 0x71;
|
|
static constexpr uint8_t R_PCH_RTC_EXT_INDEX = 0x72;
|
|
static constexpr uint8_t R_PCH_RTC_EXT_TARGET = 0x73;
|
|
|
|
/**
|
|
* RTC has N banks (we support up to 2) of memory.
|
|
*/
|
|
static constexpr uint8_t RTC_BANK_SIZE = 0x80;
|
|
|
|
/**
|
|
* Non-ext RTC index register uses higher bit for nmi.
|
|
*/
|
|
static constexpr uint8_t RTC_DATA_MASK = 0x7F;
|
|
static constexpr uint8_t RTC_NMI_MASK = 0x80;
|
|
|
|
/**
|
|
* Time offsets.
|
|
*/
|
|
static constexpr uint8_t RTC_SEC = 0x00;
|
|
static constexpr uint8_t RTC_MIN = 0x02;
|
|
static constexpr uint8_t RTC_HOUR = 0x04;
|
|
|
|
static constexpr uint8_t RTC_DAY = 0x07;
|
|
static constexpr uint8_t RTC_MON = 0x08;
|
|
static constexpr uint8_t RTC_YEAR = 0x09;
|
|
|
|
#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5
|
|
using t_UserClientExternalMethod = IOReturn (*)(IORegistryEntry *service, uint32_t selector, IOExternalMethodArguments * arguments,
|
|
IOExternalMethodDispatch * dispatch, OSObject * target, void * reference);
|
|
static constexpr size_t UserClientExternalMethodIndex = 0x129;
|
|
#endif
|
|
|
|
/**
|
|
* Attempt to connect to active RTC service
|
|
*
|
|
* @param wait wait for service availability
|
|
*
|
|
* @return true on success
|
|
*/
|
|
EXPORT bool init(bool wait=true);
|
|
|
|
/**
|
|
* Release obtained RTC service
|
|
*/
|
|
EXPORT void deinit();
|
|
|
|
/**
|
|
* Check whether extended (higher 128 bytes) is available
|
|
*
|
|
* @return true on success
|
|
*/
|
|
EXPORT bool checkExtendedMemory();
|
|
|
|
/**
|
|
* Read memory from RTC
|
|
*
|
|
* @param off offset to read data from
|
|
* @param size data size
|
|
* @param buffer data buffer to read to
|
|
*
|
|
* @return true on success
|
|
*/
|
|
EXPORT bool read(uint64_t off, uint32_t size, uint8_t *buffer);
|
|
|
|
/**
|
|
* Write memory to RTC
|
|
*
|
|
* @param off offset to write data to
|
|
* @param size data size
|
|
* @param buffer data buffer to write from
|
|
*
|
|
* @return true on success
|
|
*/
|
|
EXPORT bool write(uint64_t off, uint32_t size, uint8_t *buffer);
|
|
|
|
/**
|
|
* Obtain RTC device for direct writing.
|
|
* Written as inline to avoid IOACPIPlatformDevice dependency.
|
|
*
|
|
* @param name device name
|
|
*
|
|
* @return RTC ACPI device for I/O access, must be released
|
|
*/
|
|
static inline IOACPIPlatformDevice *getRTCDevice(const char *name = "PNP0B00") {
|
|
IOService *rtcDev = nullptr;
|
|
auto matching = IOService::nameMatching(name);
|
|
if (matching) {
|
|
#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_6
|
|
rtcDev = IOService::waitForService(matching);
|
|
if (rtcDev)
|
|
rtcDev->retain();
|
|
#else
|
|
rtcDev = IOService::waitForMatchingService(matching);
|
|
matching->release();
|
|
#endif
|
|
|
|
} else {
|
|
SYSLOG("rtc", "failed to allocate rtc device matching");
|
|
}
|
|
|
|
if (rtcDev) {
|
|
DBGLOG("rtc", "got rtc device");
|
|
auto acpiDev = OSDynamicCast(IOACPIPlatformDevice, rtcDev);
|
|
if (acpiDev) {
|
|
DBGLOG("rtc", "got rtc acpi device");
|
|
return acpiDev;
|
|
} else {
|
|
SYSLOG("rtc", "failed to obtain rtc acpi device");
|
|
rtcDev->release();
|
|
}
|
|
}
|
|
|
|
SYSLOG("rtc", "failed to get rtc device");
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Directly read RTC memory (UNSAFE, usage with caution!)
|
|
*
|
|
* @param dev RTC device
|
|
* @param off offset to read data from
|
|
* @param size data size
|
|
* @param buffer data buffer to read to
|
|
* @param introff turn interrupts off
|
|
*
|
|
* @return true on success
|
|
*/
|
|
EXPORT static void readDirect(IOACPIPlatformDevice *dev, uint8_t off, uint16_t size, uint8_t *buffer, bool introff);
|
|
|
|
/**
|
|
* Directly write RTC memory (UNSAFE, usage with caution!)
|
|
*
|
|
* @param dev RTC device
|
|
* @param off offset to read data from
|
|
* @param size data size
|
|
* @param buffer data buffer to read to
|
|
* @param updatecrc recalculate crc on write
|
|
* @param introff turn interrupts off
|
|
*
|
|
* @return true on success
|
|
*/
|
|
EXPORT static void writeDirect(IOACPIPlatformDevice *dev, uint8_t off, uint16_t size, uint8_t *buffer, bool updatecrc, bool introff);
|
|
|
|
|
|
/**
|
|
* Directly read RTC memory (UNSAFE, usage with caution!), this is just a compatibility function.
|
|
*
|
|
* @param off offset to read data from
|
|
* @param size data size
|
|
* @param buffer data buffer to read to
|
|
* @param introff turn interrupts off
|
|
*
|
|
* @return true on success
|
|
*/
|
|
static inline bool readDirect(uint8_t off, uint16_t size, uint8_t *buffer, bool introff) {
|
|
if (size > RTC_BANK_SIZE*2 - off) {
|
|
SYSLOG("rtc", "reading unsupported size");
|
|
return false;
|
|
}
|
|
|
|
auto rtc = getRTCDevice();
|
|
if (rtc) {
|
|
readDirect(rtc, off, size, buffer, introff);
|
|
rtc->release();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Directly write RTC memory (UNSAFE, usage with caution!), this is just a compatibility function.
|
|
*
|
|
* @param off offset to read data from
|
|
* @param size data size
|
|
* @param buffer data buffer to read to
|
|
* @param updatecrc recalculate crc on write
|
|
* @param introff turn interrupts off
|
|
*
|
|
* @return true on success
|
|
*/
|
|
static inline bool writeDirect(uint8_t off, uint16_t size, uint8_t *buffer, bool updatecrc, bool introff) {
|
|
if (size > RTC_BANK_SIZE*2 - off) {
|
|
SYSLOG("rtc", "writing unsupported size");
|
|
return false;
|
|
}
|
|
|
|
auto rtc = getRTCDevice();
|
|
if (rtc) {
|
|
writeDirect(rtc, off, size, buffer, updatecrc, introff);
|
|
rtc->release();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
#endif /* kern_rtc_h */
|