#include "arch/Timer.h" #include "Log.h" #include "arch/Serial.h" #include "boot/bootboot.h" #include // NOTE: Storing these values as unsigned integers doesn't allow for pre-epoch times. // We are in 2023 anyway, not sure why anybody would want to set their computer's time to 1945. static u64 timer_ticks = 0; static u64 boot_timestamp; static inline constexpr bool isleap(u32 year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } static constexpr u32 make_yday(u32 year, u32 month) { constexpr u16 upto[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; u32 yd = upto[month - 1]; if (month > 2 && isleap(year)) yd++; return yd; } // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16 static constexpr u64 broken_down_to_unix(u64 year, u64 yday, u64 hour, u64 min, u64 sec) { return sec + min * 60 + hour * 3600 + yday * 86400 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400 - ((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400; } // The bootloader encodes the date and time in Binary-Coded Decimal (BCD), which represents decimal digits using // hexadecimal digits. For example, BCD 0x22 is 22 in decimal. // https://gitlab.com/bztsrc/bootboot/-/blob/master/bootboot_spec_1st_ed.pdf, page 15. static inline constexpr u32 bcd_number_to_decimal(u32 num) { return ((num >> 4) * 10) + (num & 0xf); } static u64 bootloader_time_to_unix(const u8 boottime[8]) { const u32 year = bcd_number_to_decimal(boottime[0]) * 100 + bcd_number_to_decimal(boottime[1]); const u32 month = bcd_number_to_decimal(boottime[2]); const u32 day = bcd_number_to_decimal(boottime[3]); const u32 hour = bcd_number_to_decimal(boottime[4]); const u32 minute = bcd_number_to_decimal(boottime[5]); const u32 second = bcd_number_to_decimal(boottime[6]); // "The last byte can store 1/100th second precision, but in lack of support on most platforms, it is 0x00". // Therefore, let's not rely on it. kinfoln("Current time: %.2d/%.2d/%d %.2d:%.2d:%.2d UTC", day, month, year, hour, minute, second); return broken_down_to_unix(year - 1900, make_yday(year, month) + (day - 1), hour, minute, second); } extern const BOOTBOOT bootboot; namespace Timer { void tick() { timer_ticks++; } usize raw_ticks() { return timer_ticks; } usize ticks() { return ticks_ms() / 1000; } usize ticks_ms() { return timer_ticks / ARCH_TIMER_FREQ; } usize ticks_us() // We want a bit of precision; if there are 10 ticks/ms, do not return the truncated ms value * // 1000, but ticks * 100 (1000/10), which is more precise { if constexpr (ARCH_TIMER_FREQ > 1000) return timer_ticks / (ARCH_TIMER_FREQ / 1000); else return timer_ticks * (1000 / ARCH_TIMER_FREQ); } usize ticks_ns() { return ticks_us() * 1000; } usize boot() { return boot_timestamp; } usize boot_ms() { return boot_timestamp * MS_PER_SECOND; } usize boot_us() { return boot_timestamp * US_PER_SECOND; } usize boot_ns() { return boot_timestamp * NS_PER_SECOND; } usize clock() { return boot() + ticks(); } usize clock_ms() { return boot_ms() + ticks_ms(); } usize clock_us() { return boot_us() + ticks_us(); } usize clock_ns() { return boot_ns() + ticks_ns(); } void init() { boot_timestamp = bootloader_time_to_unix(bootboot.datetime); arch_init(); } } static_assert(IsPowerOfTwo, "ARCH_TIMER_FREQ must be a power of two"); bool should_invoke_scheduler() { return (timer_ticks % ARCH_TIMER_FREQ) == 0; }