| Address | Name | Description | |
|---|---|---|---|
| PcRtcGetCentury | |||
| CpuPause | |||
| ReadTsc | |||
| EnableInterrupts | |||
| DisableInterrupts | |||
| GetCallerEflags | |||
| SetJump | |||
| LongJump | |||
| PcRtcValidateJumpBuffer | |||
| DebugPrint | |||
| DebugAssert | |||
| CompareGuidPair | |||
| ReadUnaligned64 | |||
| PcRtcSmmCpuWrite | |||
| PcdGet32 | |||
| PciExpressWrite500 | |||
| ReadMmio32 | |||
| MicroSecondDelay | |||
| SmmCpuReadSaveState | |||
| IsAddressInSmram | |||
| FreePoolMmramAware | |||
| PcRtcReadReg | |||
| PcRtcWriteReg | |||
| DecimalToBcd | |||
| BcdToDecimal | |||
| PcRtcWaitForUpdateComplete | |||
| PcRtcIsLeapYear | |||
| PcRtcIsDayValid | |||
| PcRtcValidateTime | |||
| PcRtcConvertFromRegisterFormat | |||
| PcRtcConvertToRegisterFormat | |||
| PcRtcIsTimeNearby | |||
| PcRtcGetTime | |||
| PcRtcSmmGetTimeHandler | |||
| PcRtcSetTime | |||
| PcRtcSmmSetTimeHandler | |||
| PcRtcGetWakeupTime | |||
| PcRtcSmmGetAlarmHandler | |||
| PcRtcSetWakeupTime | |||
| PcRtcSmmSetAlarmHandler | |||
| PcRtcInitRtcHardware | |||
| PcRtcInitialize | |||
| PcRtcSmmDriverInit | |||
| SmmCoreEntry | |||
| _ModuleEntryPoint | |||
| SmmMain | |||
| SmmInstallConfigurationTable | |||
| RTC | Register Ports (SMM alias) | ||
| RTC | Register Indices | ||
| Register | A bits | ||
| Update | In Progress | ||
| Register | B bits | ||
| Daylight | Savings Enable | ||
| Square | Wave Enable | ||
| Update | Ended Interrupt Enable | ||
| Alarm | Interrupt Enable | ||
| Register | D bits | ||
| Valid | RAM and Time | ||
| Helper | macros for RTC register access via SMM I/O ports | ||
| Module | Global Variables (mapped from .data section at 0x2A20-0x2C40) | ||
| Standard | EFI globals (set by UefiBootServicesTableLib / UefiRuntimeServicesTableLib) | ||
| EFI_HANDLE | gImageHandle = NULL; // 0x2AB8 | ||
| 0x2AA8 | EFI_BOOT_SERVICES *gBS = NULL; // 0x2AB0 | ||
| 0x2AC0 | // | ||
| SMM | globals (resolved via SmmServicesTableLib) | ||
| EFI_SMM_SYSTEM_TABLE2 | *gSmst = NULL; // 0x2AC8 | ||
| resolved | from GUID at 0x2A60 | ||
| SMM | Runtime RSC Handler Protocol | ||
| 96 | SmmEndOfDxe +104 SmmAllocatePages +112 SmmFreePages +120 SmmAllocatePool | ||
| VOID | *gSmmRuntimeRsc = NULL; // 0x2AA0 (qword_2AA0) | ||
| 0x2AE8 | (qword_2AE8, lazy-init from GUID 0x2A20) | ||
| 0x2AF0 | (qword_2AF0, SMM CPU I/O2 interface) | ||
| SMM | Variable Protocol (lazy-init) | ||
| VOID | *gSmmVariable = NULL; // 0x2AD0 (qword_2AD0) | ||
| Platform | type from PCD protocol | ||
| UINT64 | gPcdDbValue = 0; // 0x2AD8: PcdGet32(PcdPlatformType) result | ||
| 0x2AF8 | (PCD protocol pointer) | ||
| HOB | list (lazy-init via gEfiHobListGuid) | ||
| VOID | *gHobList = NULL; // 0x2AE0 (qword_2AE0) | ||
| SMRAM | range tracking | ||
| UINT64 | *gSmramRanges = NULL; // 0x2C18 | ||
| RTC | state globals | ||
| UINT16 | gRtcTimeZone = 2047; // 0x2C38: EFI_UNSPECIFIED_TIMEZONE | ||
| 0x2C3A | UINT8 gRtcCenturyOffset = 0x32; // 0x2C3B (n50): CMOS offset for century byte | ||
| RTC | Config Structure (28 bytes at 0x2C20: unk_2C20) | ||
| Used | by sub_D20 (SetTime) and sub_EC8 (GetWakeupTime) to save/restore | ||
| configuration | across operations. | ||
| PC_RTC_CONFIG | gRtcConfig; // 0x2C20 | ||
| EFI | System Return Status for module entry | ||
| UINT64 | gModuleEntryStatus = 0x8000000000000001ULL; // 0x2C08 | ||
| UINT8 | gJumpBuffer[320]; // 0x2B10 (unk_2B10) | ||
| Debug | port scratch (byte at 0x2B00) | ||
| UINT8 | gDebugPortScratch; // 0x2B00 | ||
| Forward | declarations for internal functions | ||
| STATIC | EFI_STATUS | ||
| CPU | intrinsic wrappers (sub_470, sub_480, sub_490, sub_4A0, sub_4B0) | ||
| Library | support functions | ||
| actually | JUMP_BUFFER * (at least 320 bytes) | ||
| Validate | the jump buffer pointer and alignment | ||
| PcRtcValidateJumpBuffer | (JumpBuffer); | ||
| Save | registers: rbx, rbp, rdi, rsi, r12, r13, r14, r15 | ||
| followed | by return address and MXCSR | ||
| stack | pointer proxy | ||
| Save | XMM6-XMM15 (128-bit each) | ||
| Jump | through the saved return address (offset 72 in jump buffer) | ||
| Handle | overlap: if Source < Destination and Source + Length >= Destination | ||
| copy | backwards from end. | ||
| if | (Source < Destination && | ||
| Copy | in reverse 8-byte chunks | ||
| for | (; Length >= 8; Length -= 8) | ||
| Copy | remaining bytes in reverse | ||
| for | (; Length > 0; Length--) | ||
| Copy | forward 8-byte chunks via qmemcpy | ||
| UINTN | Remaining = Length; | ||
| Copy | remaining 1-7 bytes | ||
| Zero | 8-byte aligned chunks | ||
| UINTN | Chunks = Length >> 3; | ||
| Zero | remaining bytes | ||
| UINTN | Remaining = Length & 7; | ||
| Validation | helpers (sub_199C at 0x199C and sub_1ABC at 0x1ABC / sub_1A34 at 0x1A34) | ||
| Fetch | the SMM variable protocol if needed (lazy init) | ||
| if | (gSmmVariable == NULL) | ||
| Read | debug level from CMOS (port 0x70 index 0x4C, preserving NMI bit) | ||
| IoWrite8 | **(0x70, (IoRead8 (0x70) & 0x80) | 0x4C);** | |
| Determine | the filter level from CMOS | ||
| if | (CmosDebugLevel > 3) | ||
| DEBUG_VERBOSE | if (CmosDebugLevel == 1) | ||
| DEBUG_INFO | } | ||
| Enter | infinite loop (no return) | ||
| while | (TRUE); | ||
| Write | register via SMM CPU protocol | ||
| 1073741826 | = 0x40000002 (some CPU register write) | ||
| 50724874 | = 0x0306000A | ||
| return | ((EFI_SMM_CPU_PROTOCOL *)gSmmCpuProtocol)->WriteRegister ( | ||
| Check | alignment | ||
| if | (((UINTN)Address & 1) != 0) | ||
| Decompose | microseconds into: | ||
| Iterations | = MicroSeconds >> 22; | ||
| Wait | for the initial TSC + per-iteration ticks to pass | ||
| do | { | ||
| Loop | until TSC >= TargetTsc | ||
| while | (((TargetTsc - (UINT32)__indword (1288)) & 0x800000) == 0) | ||
| next | iteration uses full tick range | ||
| MicroSecondDelay | (35); | ||
| SMRAM | helper functions | ||
| Check | if the address is within SMRAM | use Smst to free inside SMRAM, | |
| or | BootServices to free outside. | ||
| if | (IsAddressInSmram ((UINT64)Buffer)) | ||
| CopyMem | wrapper (sub_1B64 at 0x1B64) | ||
| RTC | Register Access | ||
| BCD | conversion (DecimalToBcd = sub_18F4 at 0x18F4 | ||
| BcdToDecimal | = sub_1940 at 0x1940) | ||
| PcRtcWaitForUpdateComplete | (sub_1400 at 0x1400) | ||
| Check | Register D VRT bit | ||
| RegisterD | = PcRtcReadReg (RTC_REGISTER_D); | ||
| Poll | Register A UIP with timeout | ||
| Index | = 10001; | ||
| Read | Register A and check UIP | ||
| RegisterA | = PcRtcReadReg (RTC_REGISTER_A); | ||
| Verify | VRT again after the update completes | ||
| PcRtcDaysInMonth | table (used by IsDayValid at sub_1500) | ||
| Days | in each month (non-leap year February = 28, leap February = 29) | ||
| Stored | as an array of 12 UINTN values for SIMD loading in sub_1500. | ||
| xmmword_2870 | = days for months 1-2 | ||
| xmmword_2880 | = days for months 3-12 starting at 30 | ||
| STATIC | CONST UINT8 mDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | ||
| PcRtcIsLeapYear | // ============================================================================ | ||
| Leap | year: divisible by 4, and not (divisible by 100 unless divisible by 400) | ||
| if | ((Year & 3) != 0) | ||
| PcRtcIsDayValid | (sub_1500 at 0x1500) | ||
| Look | up days in this month | ||
| DaysInMonth | = mDaysInMonth[Time->Month - 1]; | ||
| February | special: check for leap year | ||
| if | (Time->Month == 2) | ||
| PcRtcValidateTime | (sub_1478 at 0x1478) | ||
| PcRtcConvertFromRegisterFormat | (sub_1260 at 0x1260) | ||
| Extract | PM flag from Hour bit 7 and clear it | ||
| RawHour | = Time->Hour; | ||
| Convert | BCD to decimal for all time fields | ||
| Check | for invalid BCD conversion (0xFF indicates failure) | ||
| if | **(Time->Year == 0xFF | ** | |
| Read | century byte from RTC CMOS | ||
| if | (gRtcCenturyOffset != 0) | ||
| Convert | from 12-hour to 24-hour format | ||
| if | (!Is24Hour) | ||
| if | (Time->Hour < 12) | ||
| if | (Time->Hour == 12) | ||
| Clear | nanosecond (time register reads don't provide nanoseconds) | ||
| PcRtcConvertToRegisterFormat | (sub_1640 at 0x1640) | ||
| Convert | Hour from 24-hour to 12-hour | ||
| if | (Time->Hour > 12) | ||
| Extract | century: Year / 100 | ||
| Year | = Time->Year; | ||
| Convert | to BCD | ||
| Write | century byte to CMOS | ||
| Convert | Second to BCD (return value) | ||
| if | (!IsBinary) | ||
| Set | PM flag in Hour if in 12-hour mode and PM | ||
| if | (!Is24Hour && RegisterB == 0) | ||
| Actually | check if we need PM bit: the 12-hour conversion already set it. | ||
| Just | make sure it's persistent. | ||
| PcRtcTimeCompare | (sub_16F8 at 0x16F8) | ||
| Hours | equal, check minutes | ||
| if | (Time1->Minute > Time2->Minute) | ||
| Minutes | equal, check seconds | ||
| if | (Time1->Second > Time2->Second) | ||
| PcRtcIsTimeNearby | (sub_1744 at 0x1744) | ||
| Allow | Dec 31 -> Jan 1 cross-year | ||
| if | **((UINT16)From->Year + 1 != (UINT16)Target->Year | ** | |
| Allow | month rollover: From->Month + 1 == Target->Month and Target->Day == 1 | ||
| if | (FromMonth + 1 == (UINTN)Target->Month && Target->Day == 1) | ||
| if | (PcRtcIsLeapYear (From->Year)) | ||
| For | other months, check days-in-month | ||
| IsValidEndOfMonth | = (From->Day == mDaysInMonth[FromMonth - 1]); | ||
| Same | year, same month | check day rollover | |
| if | (From->Day + 1 == Target->Day) | ||
| Same | day | alarm must be >= current time | |
| if | (From->Day == Target->Day && PcRtcTimeCompare (From, Target) <= 0) | ||
| PcRtcGetTime | - SMM GetTime handler (sub_84C -> sub_C28 at 0xC28) | ||
| Read | Register B to determine format | ||
| RegisterB | = PcRtcReadReg (RTC_REGISTER_B); | ||
| Read | all time registers | ||
| Set | TimeZone and Daylight from globals | ||
| Convert | from register format (BCD->decimal, 12hr->24hr, add century) | ||
| Status | = PcRtcConvertFromRegisterFormat (Time, RegisterB); | ||
| Validate | the converted time | ||
| Status | = PcRtcValidateTime (Time); | ||
| Report | capabilities | ||
| if | (Capabilities != NULL) | ||
| 1 | Hz | ||
| PcRtcGetTime | thunk (sub_84C at 0x84C) | ||
| PcRtcSetTime | - SMM SetTime handler (sub_854 -> sub_D20 at 0xD20) | ||
| Validate | the time | ||
| Copy | time for internal manipulation | ||
| CopyMemWrapper | (&LocalTime, Time, sizeof (EFI_TIME)); | ||
| Wait | for update complete before writing | ||
| Status | = PcRtcWaitForUpdateComplete (); | ||
| Delete | the "RTC" SMM variable to invalidate DXE-side cache | ||
| if | (gSmmRuntimeRsc != NULL) | ||
| The | RSC protocol has the SmmDeleteVariable at offset 88 | ||
| Set | SET bit in Register B to freeze RTC updates | ||
| Write | century byte to CMOS (offset from Config or default 0x32) | ||
| if | (Config != NULL && Config->CenturyRegister != 0) | ||
| Convert | time to register format | ||
| PcRtcConvertToRegisterFormat | (&LocalTime, RegisterB); | ||
| PcRtcWriteReg | (RTC_SECONDS, LocalTime.Second); | ||
| Clear | SET bit to resume RTC updates | ||
| PcRtcWriteReg | (RTC_REGISTER_B, RegisterB & ~RTC_B_SET); | ||
| Save | TimeZone and Daylight to config structure | ||
| if | (Config != NULL) | ||
| PcRtcSetTime | thunk (sub_854 at 0x854) | ||
| PcRtcGetWakeupTime | - SMM GetWakeupTime handler (sub_870 -> sub_EC8 at 0xEC8) | ||
| Read | Register B and C for alarm status | ||
| Read | alarm registers (seconds, minutes, hours) | ||
| Read | date registers (day, month, year) | ||
| Check | Register D for century storage | ||
| UINT8 | RegisterD; | ||
| Apply | TimeZone/Daylight from config structure | ||
| Try | to read the "RTCALARM" SMM variable to get Year/Month | ||
| DataSize | = sizeof (EFI_TIME); | ||
| Update | Year/Month/Day from the stored alarm variable | ||
| Convert | from register format | ||
| PcRtcGetWakeupTime | thunk (sub_870 at 0x870) | ||
| PcRtcSetWakeupTime | - SMM SetWakeupTime handler (sub_87C -> sub_1074 at 0x1074) | ||
| Get | current RTC time to validate proximity (alarm must be within 24 hours) | ||
| Status | = PcRtcGetTime (&CurrentTime, NULL); | ||
| Check | if alarm time is nearby (within 1 day) | ||
| if | (!PcRtcIsTimeNearby (&CurrentTime, Time)) | ||
| Copy | alarm time | ||
| CopyMemWrapper | (&AlarmTime, Time, sizeof (EFI_TIME)); | ||
| Convert | alarm to register format | ||
| PcRtcConvertToRegisterFormat | (&AlarmTime, RegisterB); | ||
| Save | current alarm register values (for restoring previous alarm) | ||
| Read | Register D century | ||
| RegisterD | = PcRtcReadReg (RTC_REGISTER_D) & 0x3F; | ||
| Apply | TimeZone/Daylight from globals | ||
| Write | "RTCALARM" SMM variable | ||
| Status | = ((SMM_RSC_HANDLER_PROTOCOL *)gSmmRuntimeRsc)->SmmSetVariable ( | ||
| Set | Register B SET bit to freeze updates | ||
| RegisterBNew | **= RegisterB | RTC_B_SET;** | |
| Write | alarm registers | ||
| PcRtcWriteReg | (RTC_SECONDS_ALARM, AlarmTime.Second); | ||
| Read | and update Register D with the alarm day/century | ||
| PcRtcReadReg | (RTC_REGISTER_C); // Clear pending AF | ||
| PcRtcWriteReg | (RTC_REGISTER_D, AlarmTime.Day & 0x3F); | ||
| Clear | pending interrupt by reading Register C | ||
| PcRtcReadReg | (RTC_REGISTER_C); | ||
| Read | existing alarm values and keep them (only clear AIE) | ||
| Get | date | ||
| Write | alarm registers (redundant for Enabled, restores current for not) | ||
| if | (!Enabled) | ||
| Dummy | read | ||
| Write | Month/Year | ||
| PcRtcWriteReg | (RTC_MONTH, AlarmTime.Month); | ||
| Toggle | AIE bit: set if enabled, clear if not | ||
| if | (Enabled) | ||
| Write | final Register B value (clear SET, optionally set AIE) | ||
| PcRtcWriteReg | (RTC_REGISTER_B, RegisterBNew & 0x7F); | ||
| PcRtcSetWakeupTime | thunk (sub_87C at 0x87C) | ||
| PcRtcInitRtcHardware | - RTC chip initialization (sub_984 at 0x984) | ||
| Step | 1: Initialize RTC hardware registers | ||
| PcRtcWriteReg | (RTC_REGISTER_A, 0x26); // 32768 Hz, divider reset | ||
| Clear | pending interrupt | ||
| Clear | VRT (will be set by hardware) | ||
| Wait | for UIP to clear | ||
| Step | 2: Read the current RTC time from hardware | ||
| Step | 3: Configure Register B | ||
| Preserve | bits: AIE (5), DM (2) = 0x24 | ||
| RegisterBNew | **= (RegisterB & 0x24) | 0x02;** | |
| Step | 4: Try to read the "RTC" SMM variable for TimeZone | ||
| DataSize | = sizeof (UINT64); | ||
| Step | 5: Try to convert and validate the current time | ||
| Status | = PcRtcConvertFromRegisterFormat (&CurrentTime, RegisterBNew); | ||
| Time | invalid | reset to factory defaults | |
| Software | reset via SMM CPU protocol write | ||
| PcRtcSmmCpuWrite | (); | ||
| Ensure | SET (bit 7) is clear, AIE (bit 5) is set, 24HR (bit 1) is set | ||
| PcRtcWriteReg | **(RTC_REGISTER_B, RegisterBNew & 0xDF | 0x02);** | |
| Step | 6: Write back the SMM variable with the current time + config | ||
| Status | = PcRtcSetTime (&CurrentTime, &gRtcConfig); | ||
| Step | 7: Get wakeup alarm time | ||
| Status | = PcRtcGetWakeupTime (&AlarmEnabled, &AlarmPending, &AlarmTime, &gRtcConfig); | ||
| Step | 8: If no alarm pending, set default alarm (2000-01-01, clear AIE) | ||
| if | (!AlarmEnabled && EFI_ERROR (Status)) | ||
| Read | the current config timezone/daylight | ||
| gRtcTimeZone | = gRtcConfig.TimeZone; | ||
| Set | default alarm time: 2000-01-01 00:00:00 | ||
| Convert | to register format | ||
| PcRtcConvertToRegisterFormat | (&CurrentTime, RegisterB); | ||
| Delete | the "RTCALARM" variable | ||
| Set | Register B SET bit, write alarm registers, clear SET bit | ||
| PcRtcWriteReg | **(RTC_REGISTER_B, RegisterB | RTC_B_SET);** | |
| PcRtcInitialize | - Protocol registration (sub_884 at 0x884) | ||
| Scan | the protocol database for gEfiSmmRscHandlerGuid | ||
| ProtocolCount | = Smst->NumberOfProtocols; | ||
| Get | protocol entry from Smst->ProtocolRegistry | ||
| Each | entry is 24 bytes: | ||
| EFI_GUID | EntryGuid = (EFI_GUID )((UINT64 )(Smst + 160) + (Index * 24)); | ||
| Could | not find SMM Runtime Services Protocol | ||
| DebugPrint | (4, "Couldn't find SMM Runtime Services\n"); | ||
| Initialize | RTC hardware | ||
| Status | = PcRtcInitRtcHardware (gSmmRuntimeRsc); | ||
| Register | the four RTC handlers in the RSC protocol | ||
| PcRtcSmmDriverInit | - Full driver entry (sub_560 at 0x560) | ||
| Step | 1: Save EFI table pointers to globals | ||
| gImageHandle | = ImageHandle; | ||
| Step | 2: Locate SMM Base2 protocol | ||
| SmmBase2 | = NULL; | ||
| Step | 3: Get Smst location | ||
| Step | 4: Locate SMM Access2 protocol | ||
| CpuIo2Handle | = 0; | ||
| Step | 5: Get SMRAM capabilities (query size first) | ||
| Step | 6: Allocate buffer for SMRAM ranges | ||
| gSmramRanges | = AllocateSmramPool (EfiRuntimeServicesData, SmramRangeBufferSize); | ||
| Step | 7: Re-read SMRAM ranges into the allocated buffer | ||
| Status | = ((EFI_SMM_ACCESS2_PROTOCOL *)(UINTN)CpuIo2Handle)->GetCapabilities ( | ||
| Step | 8: Calculate SMRAM range count (each range is 32 bytes = 5 qwords >> 5) | ||
| gSmramRangeCount | = SmramRangeBufferSize >> 5; | ||
| Step | 9: Get PCD protocol and read platform type | ||
| gPcdDbValue | = ((PCD_PROTOCOL *)GetPcdProtocol ())->Get32 (5); | ||
| Step | 10: Initialize HOB list | ||
| Step | 11: Check PCIe config address for Express presence | ||
| if | ((INT8)((UINT8 )PciExpressReadAddress (1024068)) >= 0) | ||
| Write | 0x500 to the word at PCIe config address 0x100100 (1024064) | ||
| PciExpressWrite500 | ((UINT16 *)PciExpressReadAddress (1024064)); | ||
| Set | bit 7 on the byte at 0x100104 (1024068) | enable memory space | |
| Step | 12: TSC-based short delay (approx 357 TSC cycles) | ||
| TargetEflags | = GetCallerEflags (); | ||
| Read | initial TSC | ||
| CmosDelayTsc | = ReadMmio32 (1288) & 0xFFFFFF; | ||
| Calculate | target TSC: initial + 357 cycles | ||
| InitialTsc | = ReadTsc (); | ||
| Restore | previous interrupt state | ||
| if | (InterruptsEnabled) | ||
| SmmCoreEntry | (sub_480 at 0x480 -> sub_560 at 0x560) | ||
| _ModuleEntryPoint | (0x4B4) | ||
| Initialize | the driver | ||
| PcRtcSmmDriverInit | (ImageHandle, SystemTable); | ||
| SetJump | for error recovery | ||
| if | (SetJump (&gJumpBuffer) == 0) | ||
| First | execution: initialize RTC and register handlers | ||
| Status | = PcRtcInitialize (gSmst); | ||
| Reset | the jump buffer and prepare for re-initialization | ||
| PcRtcValidateJumpBuffer | (&gJumpBuffer); | ||
| These | ASSERTs are reached only via AutoGen.c recovery: | ||
| DebugAssert | ( | ||
| Error | path: free SMRAM pool | ||
| FreePoolMmramAware | (gSmramRanges); | ||
| SmmMain | - backward compatibility entry (forwarded from entry point) | ||
| Save | the SMM system table | ||
| gSmst | = Smst; | ||
| All | remaining init happens in _ModuleEntryPoint via PcRtcSmmDriverInit | ||
| and | PcRtcInitialize. | ||
| return | EFI_SUCCESS; | ||
| This | function is a no-op in the reconstructed driver; the original | ||
| source | allocated and populated the configuration table via |
Generated by HR650X BIOS Decompilation Project