// Kernel wall-clock service. This file owns boot-time conversion, timezone policy, and current-time queries. #include #include #include #include #include #include #include #include #include #define KE_TIME_FALLBACK_YEAR 2024u #define KE_TIME_FALLBACK_MONTH 1u #define KE_TIME_FALLBACK_DAY 1u #define KE_TZ_UNSPECIFIED SYS_TIME_UNSPECIFIED_TZ #define KE_CE_STANDARD_OFFSET 60 #define KE_DST_OFFSET_DELTA 60 typedef enum KE_DST_RULE { KeDstRuleNone = 0, KeDstRuleAlways, KeDstRuleCentralEurope, } KE_DST_RULE; typedef struct KE_TIME_ZONE_POLICY { int16_t StandardOffsetMinutes; // minutes east of UTC. KE_DST_RULE DstRule; bool TimeZoneKnown; } KE_TIME_ZONE_POLICY; typedef enum KE_HARDWARE_CLOCK_MODE { KeHardwareClockUtc = 0, KeHardwareClockLocal, } KE_HARDWARE_CLOCK_MODE; static const HAL_API* gTimeHal; static uint64_t gSourceRawEpochSeconds; static int16_t gSourceTimeZoneMinutes; static bool gSourceTimeZoneKnown; static uint32_t gSourceNanosecond; static uint64_t gSourceTickMicroseconds; static uint64_t gBaseUtcEpochSeconds; static uint64_t gBaseTickMicroseconds; static uint16_t gSourceFlags; static KE_TIME_ZONE_POLICY gTimeZonePolicy = { KE_CE_STANDARD_OFFSET, KeDstRuleCentralEurope, true, }; static KE_HARDWARE_CLOCK_MODE gHardwareClockMode = KeHardwareClockUtc; // Checks whether leap Year. static bool KiIsLeapYear(uint32_t year) { return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u); } // Implements the days In Month operation. static uint32_t KiDaysInMonth(uint32_t year, uint32_t month) { static const uint8_t days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2u && KiIsLeapYear(year)) return 29u; if (month < 1u || month > 12u) return 0u; return days[month - 1u]; } static bool KiValidCalendar(uint32_t year, uint32_t month, uint32_t day, uint32_t hour, uint32_t minute, uint32_t second, uint32_t nanosecond) { if (year < 1970u || year > 9999u) return false; if (month < 1u || month > 12u) return false; uint32_t dim = KiDaysInMonth(year, month); if (day < 1u || day > dim) return false; if (hour > 23u || minute > 59u || second > 59u) return false; if (nanosecond >= 1000000000u) return false; return true; } // Implements the days Before Year operation. static uint64_t KiDaysBeforeYear(uint32_t year) { uint64_t days = 0; for (uint32_t y = 1970u; y < year; y++) days += KiIsLeapYear(y) ? 366u : 365u; return days; } static uint64_t KiCalendarToEpochSeconds(uint32_t year, uint32_t month, uint32_t day, uint32_t hour, uint32_t minute, uint32_t second) { uint64_t days = KiDaysBeforeYear(year); for (uint32_t m = 1u; m < month; m++) days += KiDaysInMonth(year, m); days += (uint64_t)(day - 1u); return (((days * 24u) + hour) * 60u + minute) * 60u + second; } // Implements the epoch Seconds To Calendar operation. static void KiEpochSecondsToCalendar(uint64_t epochSeconds, SYS_SYSTEM_TIME* out) { uint64_t days = epochSeconds / 86400u; uint32_t sod = (uint32_t)(epochSeconds % 86400u); uint32_t year = 1970u; while (true) { uint32_t diy = KiIsLeapYear(year) ? 366u : 365u; if (days < diy) break; days -= diy; year++; } uint32_t month = 1u; while (true) { uint32_t dim = KiDaysInMonth(year, month); if (days < dim) break; days -= dim; month++; } uint64_t totalDays = epochSeconds / 86400u; out->Year = (uint16_t)year; out->Month = (uint8_t)month; out->Day = (uint8_t)(days + 1u); out->Hour = (uint8_t)(sod / 3600u); out->Minute = (uint8_t)((sod / 60u) % 60u); out->Second = (uint8_t)(sod % 60u); out->DayOfWeek = (uint8_t)((totalDays + 4u) % 7u); // 1970-01-01 was Thursday. } // Implements the day Of Week operation. static uint32_t KiDayOfWeek(uint32_t year, uint32_t month, uint32_t day) { uint64_t epoch = KiCalendarToEpochSeconds(year, month, day, 0, 0, 0); return (uint32_t)(((epoch / 86400u) + 4u) % 7u); } // Implements the last Sunday Of Month operation. static uint32_t KiLastSundayOfMonth(uint32_t year, uint32_t month) { uint32_t last = KiDaysInMonth(year, month); uint32_t dow = KiDayOfWeek(year, month, last); // Sunday == 0. return last - dow; } // Implements the central Europe Dst Active Utc operation. static bool KiCentralEuropeDstActiveUtc(uint64_t utcEpochSeconds) { SYS_SYSTEM_TIME utc; memset(&utc, 0, sizeof(utc)); KiEpochSecondsToCalendar(utcEpochSeconds, &utc); uint32_t startDay = KiLastSundayOfMonth(utc.Year, 3u); uint32_t endDay = KiLastSundayOfMonth(utc.Year, 10u); uint64_t startUtc = KiCalendarToEpochSeconds(utc.Year, 3u, startDay, 1u, 0u, 0u); uint64_t endUtc = KiCalendarToEpochSeconds(utc.Year, 10u, endDay, 1u, 0u, 0u); return utcEpochSeconds >= startUtc && utcEpochSeconds < endUtc; } // Implements the offset For Utc Epoch operation. static int16_t KiOffsetForUtcEpoch(uint64_t utcEpochSeconds, bool* daylight) { int32_t offset = gTimeZonePolicy.StandardOffsetMinutes; bool dst = false; if (gTimeZonePolicy.DstRule == KeDstRuleAlways) { dst = true; } else if (gTimeZonePolicy.DstRule == KeDstRuleCentralEurope) { dst = KiCentralEuropeDstActiveUtc(utcEpochSeconds); } if (dst) offset += KE_DST_OFFSET_DELTA; if (daylight) *daylight = dst; return (int16_t)offset; } // Implements the local Epoch To Utc operation. static uint64_t KiLocalEpochToUtc(uint64_t localEpochSeconds, int16_t offsetMinutes) { uint64_t offsetSeconds = (uint64_t)((offsetMinutes < 0) ? -offsetMinutes : offsetMinutes) * 60u; if (offsetMinutes >= 0) return (localEpochSeconds > offsetSeconds) ? (localEpochSeconds - offsetSeconds) : 0; return localEpochSeconds + offsetSeconds; } // Implements the rebase Utc From Source operation. static void KiRebaseUtcFromSource(void) { if (gSourceTimeZoneKnown) { gBaseUtcEpochSeconds = KiLocalEpochToUtc(gSourceRawEpochSeconds, gSourceTimeZoneMinutes); } else if (gHardwareClockMode == KeHardwareClockLocal) { bool daylight = false; int16_t offset = KiOffsetForUtcEpoch(gSourceRawEpochSeconds, &daylight); // The first pass treated the local clock as UTC only so we can choose // the correct rule-year. Run one refinement against the candidate UTC // instant to handle daylight-saving transitions cleanly. uint64_t candidateUtc = KiLocalEpochToUtc(gSourceRawEpochSeconds, offset); offset = KiOffsetForUtcEpoch(candidateUtc, &daylight); gBaseUtcEpochSeconds = KiLocalEpochToUtc(gSourceRawEpochSeconds, offset); } else { gBaseUtcEpochSeconds = gSourceRawEpochSeconds; } gBaseTickMicroseconds = gSourceTickMicroseconds; } // Implements the ascii Lower operation. static int KiAsciiLower(int c) { if (c >= 'A' && c <= 'Z') return c + ('a' - 'A'); return c; } // Implements the string Equals No Case operation. static bool KiStringEqualsNoCase(const char* a, const char* b) { if (!a || !b) return false; while (*a && *b) { if (KiAsciiLower((unsigned char)*a) != KiAsciiLower((unsigned char)*b)) return false; a++; b++; } return *a == '\0' && *b == '\0'; } // Parses int32. static bool KiParseInt32(const char* s, int32_t* out) { if (!s || !*s || !out) return false; int sign = 1; if (*s == '-') { sign = -1; s++; } else if (*s == '+') { s++; } if (*s < '0' || *s > '9') return false; int32_t value = 0; while (*s >= '0' && *s <= '9') { value = (value * 10) + (*s - '0'); s++; } if (*s != '\0') return false; *out = value * sign; return true; } // Implements the starts With No Case operation. static bool KiStartsWithNoCase(const char* s, const char* prefix) { if (!s || !prefix) return false; while (*prefix) { if (KiAsciiLower((unsigned char)*s) != KiAsciiLower((unsigned char)*prefix)) return false; s++; prefix++; } return true; } // Parses utc Offset. static bool KiParseUtcOffset(const char* s, int16_t* out) { if (!s || !out) return false; if (KiStringEqualsNoCase(s, "utc") || KiStringEqualsNoCase(s, "gmt") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(s, "z") || KiStringEqualsNoCase(s, "utcz")) { *out = 0; return true; } if (KiStartsWithNoCase(s, "utc")) { s += 3; } else if (KiStartsWithNoCase(s, "gmt")) { s += 3; } int sign = 1; if (*s == '+') { sign = 1; s++; } else if (*s == '-') { sign = -1; s++; } else { return false; } if (*s < '0' || *s > '9') return false; int hours = 0; int digits = 0; while (*s >= '0' && *s <= '9' && digits < 2) { hours = hours * 10 + (*s - '0'); s++; digits++; } int minutes = 0; if (*s == ':') { s++; if (s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9') return false; minutes = (s[0] - '0') * 10 + (s[1] - '0'); s += 2; } else if (*s >= '0' && *s <= '9') { // Compact form, e.g. UTC+0200. Exactly two minute digits must remain. if (s[1] < '0' || s[1] > '9' || s[2] != '\0') return false; minutes = (s[0] - '0') * 10 + (s[1] - '0'); s += 2; } if (*s != '\0') return false; if (hours > 14 || minutes > 59) return false; *out = (int16_t)(sign * (hours * 60 + minutes)); return true; } // Implements the apply Default Time Zone Policy operation. static void KiApplyDefaultTimeZonePolicy(void) { gTimeZonePolicy.StandardOffsetMinutes = KE_CE_STANDARD_OFFSET; gTimeZonePolicy.DstRule = KeDstRuleCentralEurope; gTimeZonePolicy.TimeZoneKnown = true; gHardwareClockMode = KeHardwareClockUtc; } // Implements the time Initialize operation. void KeTimeInitialize(const BOOT_INFO* bootInfo, const HAL_API* hal) { gTimeHal = hal; KiApplyDefaultTimeZonePolicy(); gSourceTickMicroseconds = (hal && hal->Timer.QueryTicks) ? hal->Timer.QueryTicks() : 0; const BOOT_TIME* bt = bootInfo ? &bootInfo->BootTime : 0; if (bt && (bt->Flags & BOOT_TIME_FLAG_VALID) && KiValidCalendar(bt->Year, bt->Month, bt->Day, bt->Hour, bt->Minute, bt->Second, bt->Nanosecond)) { gSourceRawEpochSeconds = KiCalendarToEpochSeconds(bt->Year, bt->Month, bt->Day, bt->Hour, bt->Minute, bt->Second); gSourceTimeZoneKnown = (bt->Flags & BOOT_TIME_FLAG_TZ_KNOWN) && bt->TimeZoneMinutes != BOOT_TIME_UNSPECIFIED_TZ; gSourceTimeZoneMinutes = gSourceTimeZoneKnown ? bt->TimeZoneMinutes : 0; gSourceNanosecond = bt->Nanosecond; gSourceFlags = SYS_TIME_FLAG_VALID | SYS_TIME_FLAG_UEFI; } else { HAL_RTC_TIME rtc; memset(&rtc, 0, sizeof(rtc)); if (hal && hal->Rtc.QueryTime && hal->Rtc.QueryTime(&rtc) == NTLSTATUS_SUCCESS && (rtc.Flags & HAL_RTC_TIME_FLAG_VALID) && KiValidCalendar(rtc.Year, rtc.Month, rtc.Day, rtc.Hour, rtc.Minute, rtc.Second, rtc.Nanosecond)) { gSourceRawEpochSeconds = KiCalendarToEpochSeconds(rtc.Year, rtc.Month, rtc.Day, rtc.Hour, rtc.Minute, rtc.Second); gSourceTimeZoneKnown = false; gSourceTimeZoneMinutes = 0; gSourceNanosecond = rtc.Nanosecond; gSourceFlags = SYS_TIME_FLAG_VALID | SYS_TIME_FLAG_RTC; } else { gSourceRawEpochSeconds = KiCalendarToEpochSeconds(KE_TIME_FALLBACK_YEAR, KE_TIME_FALLBACK_MONTH, KE_TIME_FALLBACK_DAY, 0, 0, 0); gSourceTimeZoneKnown = false; gSourceTimeZoneMinutes = 0; gSourceNanosecond = 0; gSourceFlags = SYS_TIME_FLAG_VALID | SYS_TIME_FLAG_FALLBACK; } } KiRebaseUtcFromSource(); SYS_SYSTEM_TIME now; if (KeQuerySystemTime(&now) == NTLSTATUS_SUCCESS) { DbgPrint("time: initialized %04u-%02u-%02u %02u:%02u:%02u.%03u flags=0x%x tz=%d hw=%s epoch=%llu\n", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond, now.Flags, now.TimeZoneMinutes, gHardwareClockMode == KeHardwareClockLocal ? "local" : "utc", now.EpochSeconds); } } // Implements the time Reload Settings operation. KSTATUS KeTimeReloadSettings(void) { KE_TIME_ZONE_POLICY policy = gTimeZonePolicy; char value[64]; bool fixedTimezone = false; bool allowStandardOffsetSetting = false; KSTATUS st = KeGetSettingString("time", "timezone", value, sizeof(value)); if (KSUCCESS(st)) { if (KiStringEqualsNoCase(value, "central-europe") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(value, "europe/warsaw")) { policy.StandardOffsetMinutes = KE_CE_STANDARD_OFFSET; policy.DstRule = KeDstRuleCentralEurope; policy.TimeZoneKnown = true; } else if (KiStringEqualsNoCase(value, "cet")) { policy.StandardOffsetMinutes = KE_CE_STANDARD_OFFSET; policy.DstRule = KeDstRuleNone; policy.TimeZoneKnown = true; fixedTimezone = true; } else if (KiStringEqualsNoCase(value, "cest")) { policy.StandardOffsetMinutes = KE_CE_STANDARD_OFFSET + KE_DST_OFFSET_DELTA; policy.DstRule = KeDstRuleNone; policy.TimeZoneKnown = true; fixedTimezone = true; } else if (KiStringEqualsNoCase(value, "custom") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(value, "manual")) { policy.DstRule = KeDstRuleNone; policy.TimeZoneKnown = true; fixedTimezone = true; allowStandardOffsetSetting = true; } else if (KiStringEqualsNoCase(value, "utc") || KiStringEqualsNoCase(value, "gmt") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(value, "z")) { policy.StandardOffsetMinutes = 0; policy.DstRule = KeDstRuleNone; policy.TimeZoneKnown = true; fixedTimezone = true; } else { int16_t parsedOffset = 0; if (KiParseUtcOffset(value, &parsedOffset)) { policy.StandardOffsetMinutes = parsedOffset; policy.DstRule = KeDstRuleNone; policy.TimeZoneKnown = true; fixedTimezone = true; } } } st = KeGetSettingString("time", "standard_offset_minutes", value, sizeof(value)); if (KSUCCESS(st) && allowStandardOffsetSetting) { int32_t minutes; if (KiParseInt32(value, &minutes) && minutes >= -14 * 60 && minutes <= 14 * 60) { policy.StandardOffsetMinutes = (int16_t)minutes; policy.TimeZoneKnown = true; fixedTimezone = true; } } st = KeGetSettingString("time", "daylight_saving", value, sizeof(value)); if (KSUCCESS(st)) { if (KiStringEqualsNoCase(value, "auto")) { if (!fixedTimezone) policy.DstRule = KeDstRuleCentralEurope; } else if (KiStringEqualsNoCase(value, "central-europe")) { policy.DstRule = KeDstRuleCentralEurope; } else if (KiStringEqualsNoCase(value, "on") || KiStringEqualsNoCase(value, "yes") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(value, "true") || KiStringEqualsNoCase(value, "1")) { policy.DstRule = KeDstRuleAlways; } else if (KiStringEqualsNoCase(value, "off") || KiStringEqualsNoCase(value, "no") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(value, "false") || KiStringEqualsNoCase(value, "0")) { policy.DstRule = KeDstRuleNone; } } KE_HARDWARE_CLOCK_MODE hardwareClockMode = gHardwareClockMode; st = KeGetSettingString("time", "hardware_clock", value, sizeof(value)); if (KSUCCESS(st)) { if (KiStringEqualsNoCase(value, "utc") || KiStringEqualsNoCase(value, "z")) { hardwareClockMode = KeHardwareClockUtc; } else if (KiStringEqualsNoCase(value, "local") || KiStringEqualsNoCase(value, "localtime") || // Implements the string Equals No Case operation. KiStringEqualsNoCase(value, "wall-clock")) { hardwareClockMode = KeHardwareClockLocal; } } gTimeZonePolicy = policy; gHardwareClockMode = hardwareClockMode; KiRebaseUtcFromSource(); SYS_SYSTEM_TIME now; if (KeQuerySystemTime(&now) == NTLSTATUS_SUCCESS) { DbgPrint("time: settings timezone=%d dst=%s hw=%s now=%04u-%02u-%02u %02u:%02u:%02u flags=0x%x epoch=%llu\n", now.TimeZoneMinutes, (now.Flags & SYS_TIME_FLAG_DAYLIGHT) ? "on" : "off", gHardwareClockMode == KeHardwareClockLocal ? "local" : "utc", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Flags, now.EpochSeconds); } return NTLSTATUS_SUCCESS; } // Returns system Time. __declspec(dllexport) KSTATUS KeQuerySystemTime(SYS_SYSTEM_TIME* outTime) { if (!outTime) return NTLSTATUS_INVALID_PARAMETER; uint64_t nowTick = (gTimeHal && gTimeHal->Timer.QueryTicks) ? gTimeHal->Timer.QueryTicks() : gBaseTickMicroseconds; uint64_t elapsedUs = nowTick - gBaseTickMicroseconds; uint64_t utcEpochSeconds = gBaseUtcEpochSeconds + (elapsedUs / 1000000u); uint64_t ns = (uint64_t)gSourceNanosecond + (elapsedUs % 1000000u) * 1000u; if (ns >= 1000000000u) { utcEpochSeconds += ns / 1000000000u; ns %= 1000000000u; } bool daylight = false; int16_t offset = KiOffsetForUtcEpoch(utcEpochSeconds, &daylight); uint64_t offsetSeconds = (uint64_t)((offset < 0) ? -offset : offset) * 60u; uint64_t localEpochSeconds; if (offset >= 0) { localEpochSeconds = utcEpochSeconds + offsetSeconds; } else { localEpochSeconds = (utcEpochSeconds > offsetSeconds) ? (utcEpochSeconds - offsetSeconds) : 0; } SYS_SYSTEM_TIME t; memset(&t, 0, sizeof(t)); KiEpochSecondsToCalendar(localEpochSeconds, &t); t.Nanosecond = (uint32_t)ns; t.Millisecond = (uint16_t)(ns / 1000000u); t.Flags = gSourceFlags; if (gTimeZonePolicy.TimeZoneKnown) t.Flags |= SYS_TIME_FLAG_TZ_KNOWN; if (daylight) t.Flags |= SYS_TIME_FLAG_DAYLIGHT; t.TimeZoneMinutes = gTimeZonePolicy.TimeZoneKnown ? offset : KE_TZ_UNSPECIFIED; t.EpochSeconds = utcEpochSeconds; t.TickMicroseconds = nowTick; *outTime = t; return NTLSTATUS_SUCCESS; } // Returns the monotonic platform counter in microseconds. uint64_t KeQueryMonotonicTicks(void) { return (gTimeHal && gTimeHal->Timer.QueryTicks) ? gTimeHal->Timer.QueryTicks() : 0; } // Returns a saturated monotonic deadline for a relative millisecond interval. uint64_t KeMonotonicDeadlineAfterMilliseconds(uint64_t milliseconds) { uint64_t now = KeQueryMonotonicTicks(); if (milliseconds > (UINT64_MAX - now) / 1000ull) return UINT64_MAX; return now + (milliseconds * 1000ull); } // Returns the remaining whole milliseconds, rounded up, until a monotonic deadline. uint64_t KeMonotonicMillisecondsUntil(uint64_t deadlineMicroseconds) { uint64_t now = KeQueryMonotonicTicks(); if (deadlineMicroseconds <= now) return 0; uint64_t remaining = deadlineMicroseconds - now; uint64_t milliseconds = remaining / 1000ull; if ((remaining % 1000ull) != 0) milliseconds++; return milliseconds; } // Returns unix Time Seconds. __declspec(dllexport) uint64_t KeQueryUnixTimeSeconds(void) { SYS_SYSTEM_TIME t; if (KeQuerySystemTime(&t) != NTLSTATUS_SUCCESS) return 0; return t.EpochSeconds; }