diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 30db8c18..9f66d318 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -19,13 +19,12 @@ set(SOURCES src/memory/SharedMemory.cpp src/boot/Init.cpp src/arch/Serial.cpp - src/arch/Timer.cpp src/arch/PCI.cpp src/lib/Mutex.cpp src/thread/Thread.cpp src/thread/ThreadImage.cpp src/thread/Scheduler.cpp - src/thread/Timer.cpp + src/thread/Clock.cpp src/sys/Syscall.cpp src/sys/exit.cpp src/sys/clock_gettime.cpp @@ -93,7 +92,7 @@ if("${LUNA_ARCH}" MATCHES "x86_64") src/arch/x86_64/Serial.cpp src/arch/x86_64/MMU.cpp src/arch/x86_64/CPU.cpp - src/arch/x86_64/Timer.cpp + src/arch/x86_64/Clock.cpp src/arch/x86_64/Thread.cpp src/arch/x86_64/PCI.cpp src/arch/x86_64/Keyboard.cpp diff --git a/kernel/src/Log.cpp b/kernel/src/Log.cpp index 9d2e3055..9c7baf64 100644 --- a/kernel/src/Log.cpp +++ b/kernel/src/Log.cpp @@ -1,7 +1,7 @@ #include "Log.h" #include "arch/CPU.h" #include "arch/Serial.h" -#include "arch/Timer.h" +#include "thread/Clock.h" #include "video/TextConsole.h" #include #include @@ -35,9 +35,10 @@ static void log_serial(LogLevel level, const char* format, va_list origin) "\x1b[0m ", ansi_color_codes_per_log_level[(int)level], log_level_letters[(int)level]); - auto* time = Timer::monotonic_clock(); + struct timespec time; + g_monotonic_clock.get_time(time); - Serial::printf("%4zu.%.3zu ", time->tv_sec, time->tv_nsec / 1'000'000); + Serial::printf("%4zu.%.3zu ", time.tv_sec, time.tv_nsec / 1'000'000); // NOTE: We do this manually because of a lack of vprintf() in both Serial and TextConsole. cstyle_format( diff --git a/kernel/src/arch/Timer.cpp b/kernel/src/arch/Timer.cpp deleted file mode 100644 index cec949e5..00000000 --- a/kernel/src/arch/Timer.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "arch/Timer.h" -#include "Log.h" -#include "arch/Serial.h" -#include "boot/bootboot.h" -#include - -static struct timespec s_monotonic_clock = { 0, 0 }; -static struct timespec s_realtime_clock; - -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 -{ - static struct timespec s_interval = { .tv_sec = 0, .tv_nsec = ARCH_TIMER_RESOLUTION * 1000 }; - - void tick() - { - timespecadd(&s_monotonic_clock, &s_interval, &s_monotonic_clock); - timespecadd(&s_realtime_clock, &s_interval, &s_realtime_clock); - } - - usize ticks_ms() - { - return (s_monotonic_clock.tv_sec * 1000) + (s_monotonic_clock.tv_nsec / 1'000'000); - } - - struct timespec* monotonic_clock() - { - return &s_monotonic_clock; - } - - struct timespec* realtime_clock() - { - return &s_realtime_clock; - } - - void init() - { - s_realtime_clock.tv_sec = bootloader_time_to_unix(bootboot.datetime); - s_realtime_clock.tv_nsec = 0; - arch_init(); - } -} - -bool should_invoke_scheduler() -{ - return (s_realtime_clock.tv_nsec % 1'000'000) == 0; -} diff --git a/kernel/src/arch/Timer.h b/kernel/src/arch/Timer.h deleted file mode 100644 index 0b6d3fa3..00000000 --- a/kernel/src/arch/Timer.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include - -#ifdef ARCH_X86_64 -#include "arch/x86_64/Timer.h" -#else -#error "Unknown architecture." -#endif - -static const usize MS_PER_SECOND = 1000; -static const usize US_PER_SECOND = MS_PER_SECOND * 1000; -static const usize NS_PER_SECOND = US_PER_SECOND * 1000; - -namespace Timer -{ - void tick(); - - usize ticks_ms(); - - struct timespec* monotonic_clock(); - - struct timespec* realtime_clock(); - - void arch_init(); - void init(); -} - -bool should_invoke_scheduler(); diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index cd0d753f..cb94d306 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -3,13 +3,13 @@ #include "Symbols.h" #include "api/Mouse.h" #include "arch/Keyboard.h" -#include "arch/Timer.h" #include "arch/x86_64/CPU.h" #include "arch/x86_64/IO.h" #include "fs/devices/KeyboardDevice.h" #include "fs/devices/MouseDevice.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" +#include "thread/Clock.h" #include "thread/Scheduler.h" #include "video/TextConsole.h" #include @@ -164,9 +164,17 @@ void io_thread() } } +static bool should_invoke_scheduler() +{ + struct timespec time; + g_realtime_clock.get_time(time); + return (time.tv_nsec % 1'000'000) == 0; +} + static void timer_interrupt(Registers* regs, void*) { - Timer::tick(); + g_realtime_clock.tick(); + g_monotonic_clock.tick(); if (should_invoke_scheduler()) Scheduler::invoke(regs); } diff --git a/kernel/src/arch/x86_64/Timer.cpp b/kernel/src/arch/x86_64/Clock.cpp similarity index 53% rename from kernel/src/arch/x86_64/Timer.cpp rename to kernel/src/arch/x86_64/Clock.cpp index 05aab10b..c98cdc55 100644 --- a/kernel/src/arch/x86_64/Timer.cpp +++ b/kernel/src/arch/x86_64/Clock.cpp @@ -1,15 +1,24 @@ -#include "arch/Timer.h" +#include "thread/Clock.h" #include "arch/x86_64/IO.h" +// Every timer tick is equivalent to 250 microseconds. +// FIXME: Change ARCH_TIMER_RESOLUTION to use nanoseconds. +const usize ARCH_TIMER_RESOLUTION = 250; + #define PIT_CHANNEL_0 0x40 const u64 base_frequency = 1193182; -void Timer::arch_init() +void Clock::arch_init() { constexpr u16 divisor = (u16)(base_frequency / ((1000 / ARCH_TIMER_RESOLUTION) * 1000)); static_assert(divisor >= 100, "ARCH_TIMER_RESOLUTION is too low"); IO::outb(PIT_CHANNEL_0, (u8)(divisor & 0xFF)); IO::outb(0x80, 0); // short delay IO::outb(PIT_CHANNEL_0, (u8)((divisor & 0xFF00) >> 8)); + + long resolution = ARCH_TIMER_RESOLUTION * 1000; + + g_realtime_clock.set_resolution(resolution); + g_monotonic_clock.set_resolution(resolution); } diff --git a/kernel/src/arch/x86_64/Timer.h b/kernel/src/arch/x86_64/Timer.h deleted file mode 100644 index 52774e88..00000000 --- a/kernel/src/arch/x86_64/Timer.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include - -// Every timer tick is equivalent to 250 microseconds. -// FIXME: Change ARCH_TIMER_RESOLUTION to use nanoseconds. -const usize ARCH_TIMER_RESOLUTION = 250; diff --git a/kernel/src/arch/x86_64/disk/ATA.cpp b/kernel/src/arch/x86_64/disk/ATA.cpp index a38d1fbe..014e6fcf 100644 --- a/kernel/src/arch/x86_64/disk/ATA.cpp +++ b/kernel/src/arch/x86_64/disk/ATA.cpp @@ -2,10 +2,10 @@ #include "Log.h" #include "arch/CPU.h" #include "arch/Serial.h" -#include "arch/Timer.h" #include "arch/x86_64/IO.h" #include "fs/MBR.h" #include "memory/MemoryManager.h" +#include "thread/Clock.h" #include "thread/Scheduler.h" #include #include @@ -34,6 +34,13 @@ static usize copy_ata_string(char* out, u16* in, usize size) return size; } +static u64 ticks_ms() +{ + struct timespec time; + g_monotonic_clock.get_time(time); + return time.tv_sec * 1000 + time.tv_nsec / 1'000'000; +} + namespace ATA { Result Controller::scan() @@ -199,24 +206,24 @@ namespace ATA bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout) { - u64 begin = Timer::ticks_ms(); + u64 begin = ticks_ms(); while (true) { u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg); if (reg_value & value) return true; - if ((Timer::ticks_ms() - begin) >= timeout) return false; + if ((ticks_ms() - begin) >= timeout) return false; kernel_sleep(1); } } bool Channel::wait_for_reg_clear(Register reg, u8 value, u64 timeout) { - u64 begin = Timer::ticks_ms(); + u64 begin = ticks_ms(); while (true) { u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg); if ((reg_value & value) == 0) return true; - if ((Timer::ticks_ms() - begin) >= timeout) return false; + if ((ticks_ms() - begin) >= timeout) return false; kernel_sleep(1); } } diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index dc75c405..ad852240 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -1,5 +1,5 @@ #pragma once -#include "arch/Timer.h" +#include "thread/Clock.h" #include #include #include @@ -163,7 +163,7 @@ namespace VFS virtual Result set_metadata(const InodeMetadata& metadata) { m_metadata = metadata; - m_metadata.ctime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.ctime); return {}; } diff --git a/kernel/src/fs/devpts/FileSystem.cpp b/kernel/src/fs/devpts/FileSystem.cpp index b7d29cac..2ca49f3f 100644 --- a/kernel/src/fs/devpts/FileSystem.cpp +++ b/kernel/src/fs/devpts/FileSystem.cpp @@ -1,5 +1,4 @@ #include "fs/devpts/FileSystem.h" -#include "arch/Timer.h" #include "fs/devices/DeviceRegistry.h" #include "fs/devpts/Inode.h" #include @@ -22,7 +21,9 @@ namespace DevPTS root->set_fs(*fs, {}); root->set_inode_number(); root->m_metadata.mode = 0755; - root->m_metadata.atime = root->m_metadata.ctime = root->m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(root->m_metadata.ctime); + g_realtime_clock.get_time(root->m_metadata.atime); + g_realtime_clock.get_time(root->m_metadata.mtime); fs->set_root(root); TRY(g_devpts_instances.try_append(fs.ptr())); diff --git a/kernel/src/fs/devpts/Inode.cpp b/kernel/src/fs/devpts/Inode.cpp index 6efb94fe..5c42b487 100644 --- a/kernel/src/fs/devpts/Inode.cpp +++ b/kernel/src/fs/devpts/Inode.cpp @@ -43,7 +43,7 @@ namespace DevPTS inode->did_link(); - m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.mtime); return {}; } @@ -61,7 +61,7 @@ namespace DevPTS inode->did_unlink(); - m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.mtime); return {}; } diff --git a/kernel/src/fs/tmpfs/FileSystem.cpp b/kernel/src/fs/tmpfs/FileSystem.cpp index 2bf94a61..faf1ed62 100644 --- a/kernel/src/fs/tmpfs/FileSystem.cpp +++ b/kernel/src/fs/tmpfs/FileSystem.cpp @@ -1,7 +1,7 @@ #include "fs/tmpfs/FileSystem.h" -#include "arch/Timer.h" #include "fs/devices/DeviceRegistry.h" #include "fs/tmpfs/Inode.h" +#include "thread/Clock.h" #include #include #include @@ -27,7 +27,9 @@ namespace TmpFS inode->set_fs(*this, {}); inode->set_inode_number(m_next_inode_number++, {}); inode->m_metadata.mode = mode; - inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(inode->m_metadata.mtime); + g_realtime_clock.get_time(inode->m_metadata.atime); + g_realtime_clock.get_time(inode->m_metadata.ctime); return (SharedPtr)inode; } @@ -37,7 +39,9 @@ namespace TmpFS inode->set_fs(*this, {}); TRY(inode->set_link(link, {})); inode->set_inode_number(m_next_inode_number++, {}); - inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(inode->m_metadata.mtime); + g_realtime_clock.get_time(inode->m_metadata.atime); + g_realtime_clock.get_time(inode->m_metadata.ctime); return (SharedPtr)inode; } @@ -52,7 +56,9 @@ namespace TmpFS inode->set_fs(*this, {}); inode->set_inode_number(m_next_inode_number++, {}); inode->m_metadata.mode = mode; - inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(inode->m_metadata.mtime); + g_realtime_clock.get_time(inode->m_metadata.atime); + g_realtime_clock.get_time(inode->m_metadata.ctime); return (SharedPtr)inode; } @@ -69,7 +75,9 @@ namespace TmpFS // device ID atm. inode->set_device_id(luna_dev_makedev(major, minor), {}); inode->m_metadata.mode = mode; - inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(inode->m_metadata.mtime); + g_realtime_clock.get_time(inode->m_metadata.atime); + g_realtime_clock.get_time(inode->m_metadata.ctime); inode->m_metadata.size = device->size(); return (SharedPtr)inode; diff --git a/kernel/src/fs/tmpfs/Inode.cpp b/kernel/src/fs/tmpfs/Inode.cpp index b8ce908d..5616cee3 100644 --- a/kernel/src/fs/tmpfs/Inode.cpp +++ b/kernel/src/fs/tmpfs/Inode.cpp @@ -45,7 +45,7 @@ namespace TmpFS inode->did_link(); - m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.mtime); return {}; } @@ -63,7 +63,7 @@ namespace TmpFS inode->did_unlink(); - m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.mtime); return {}; } @@ -118,7 +118,7 @@ namespace TmpFS m_metadata.size = m_data_buffer.size(); - m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.mtime); return length; } @@ -133,7 +133,7 @@ namespace TmpFS m_metadata.size = m_data_buffer.size(); - m_metadata.mtime = *Timer::realtime_clock(); + g_realtime_clock.get_time(m_metadata.mtime); return {}; } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 3edecb66..19838856 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,7 +1,6 @@ #include "Log.h" #include "Symbols.h" #include "arch/CPU.h" -#include "arch/Timer.h" #include "binfmt/BinaryFormat.h" #include "boot/Init.h" #include "config.h" @@ -11,6 +10,7 @@ #include "fs/devices/PTYMultiplexer.h" #include "fs/tmpfs/FileSystem.h" #include "memory/MemoryManager.h" +#include "thread/Clock.h" #include "thread/Scheduler.h" #include @@ -96,7 +96,7 @@ extern "C" [[noreturn]] void _start() Init::check_magic(); Init::early_init(); - Timer::init(); + Clock::init(); Thread::init(); Scheduler::init(); diff --git a/kernel/src/sys/alarm.cpp b/kernel/src/sys/alarm.cpp index cf4da65c..652d08c1 100644 --- a/kernel/src/sys/alarm.cpp +++ b/kernel/src/sys/alarm.cpp @@ -2,6 +2,7 @@ #include "sys/Syscall.h" #include "thread/Scheduler.h" #include "thread/Timer.h" +#include Result sys_alarm(Registers*, SyscallArgs args) { @@ -11,19 +12,24 @@ Result sys_alarm(Registers*, SyscallArgs args) TRY(check_pledge(current, Promise::p_stdio)); - u64 ticks_left = current->timer ? current->timer->ticks_left() : 0; + Clock* clock = nullptr; + + u64 ticks_left = current->timer ? current->timer->clock->ticks_left(current->timer) : 0; if (current->timer) { - if (current->timer->delta_ticks > 0) Scheduler::remove_from_timer_queue(current->timer); + clock = current->timer->clock; + if (clock) clock->remove_from_timer_queue(current->timer); } else - current->timer = TRY(make()); + current->timer = TRY(make()); - current->timer->total_ticks = seconds * 1000; + if (!clock) clock = &g_realtime_clock; + + current->timer->total_ticks = ceil_div((static_cast(seconds) * 1'000'000'000), clock->resolution()); current->timer->thread = current; - Scheduler::add_to_timer_queue(current->timer); + clock->add_to_timer_queue(current->timer); - return ticks_left * 1000; + return (ticks_left * clock->resolution()) / 1'000'000'000; } diff --git a/kernel/src/sys/clock_gettime.cpp b/kernel/src/sys/clock_gettime.cpp index 09bfd163..9511d312 100644 --- a/kernel/src/sys/clock_gettime.cpp +++ b/kernel/src/sys/clock_gettime.cpp @@ -1,7 +1,7 @@ #include "Pledge.h" -#include "arch/Timer.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" +#include "thread/Clock.h" #include "thread/Scheduler.h" #include #include @@ -15,18 +15,24 @@ Result sys_clock_gettime(Registers*, SyscallArgs args) TRY(check_pledge(current, Promise::p_stdio)); + Clock* clock; + switch (id) { case CLOCK_MONOTONIC: { - if (!MemoryManager::copy_to_user_typed(ts, Timer::monotonic_clock())) return err(EFAULT); + clock = &g_monotonic_clock; break; } case CLOCK_REALTIME: { - if (!MemoryManager::copy_to_user_typed(ts, Timer::realtime_clock())) return err(EFAULT); + clock = &g_realtime_clock; break; } default: return err(EINVAL); } + struct timespec time; + clock->get_time(time); + if (!MemoryManager::copy_to_user_typed(ts, &time)) return err(EFAULT); + return 0; } diff --git a/kernel/src/sys/file.cpp b/kernel/src/sys/file.cpp index 5de0152f..4ba20d5d 100644 --- a/kernel/src/sys/file.cpp +++ b/kernel/src/sys/file.cpp @@ -318,7 +318,7 @@ Result sys_utimensat(Registers*, SyscallArgs args) auto metadata = inode->metadata(); if (ktimes[0].tv_nsec != UTIME_OMIT) { - if (ktimes[0].tv_nsec == UTIME_NOW) metadata.atime = *Timer::realtime_clock(); + if (ktimes[0].tv_nsec == UTIME_NOW) g_realtime_clock.get_time(metadata.atime); else { if (ktimes[0].tv_nsec < 0 || ktimes[0].tv_nsec > 999'999'999) return err(EINVAL); @@ -327,7 +327,7 @@ Result sys_utimensat(Registers*, SyscallArgs args) } if (ktimes[1].tv_nsec != UTIME_OMIT) { - if (ktimes[1].tv_nsec == UTIME_NOW) metadata.mtime = *Timer::realtime_clock(); + if (ktimes[1].tv_nsec == UTIME_NOW) g_realtime_clock.get_time(metadata.mtime); else { if (ktimes[1].tv_nsec < 0 || ktimes[1].tv_nsec > 999'999'999) return err(EINVAL); diff --git a/kernel/src/thread/Clock.cpp b/kernel/src/thread/Clock.cpp new file mode 100644 index 00000000..edf72f16 --- /dev/null +++ b/kernel/src/thread/Clock.cpp @@ -0,0 +1,161 @@ +#include "thread/Clock.h" +#include "Log.h" +#include "arch/Serial.h" +#include "boot/bootboot.h" +#include "thread/Thread.h" + +Clock g_realtime_clock; +Clock g_monotonic_clock; + +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; + +void Clock::init() +{ + struct timespec time = { .tv_sec = (time_t)bootloader_time_to_unix(bootboot.datetime), .tv_nsec = 0 }; + g_realtime_clock.update(time); + arch_init(); +} + +void Clock::set_resolution(long resolution) +{ + m_resolution = resolution; +} + +void Clock::update(const struct timespec& time) +{ + m_time = time; +} + +void Clock::add_to_timer_queue(Timer* timer) +{ + check(timer->clock == nullptr); + + timer->delta_ticks = timer->total_ticks; + + for (auto* t : m_timer_queue) + { + if (timer->delta_ticks <= t->delta_ticks) + { + t->delta_ticks -= timer->delta_ticks; + m_timer_queue.add_before(t, timer); + return; + } + timer->delta_ticks -= t->delta_ticks; + } + + m_timer_queue.append(timer); + + timer->clock = this; +} + +void Clock::remove_from_timer_queue(Timer* timer) +{ + check(timer->clock == this); + + auto maybe_next = m_timer_queue.next(timer); + if (maybe_next.has_value()) + { + auto next = maybe_next.value(); + next->delta_ticks += timer->delta_ticks; + } + m_timer_queue.remove(timer); + + timer->clock = nullptr; +} + +void Clock::tick() +{ + struct timespec interval = { .tv_sec = 0, .tv_nsec = m_resolution }; + + timespecadd(&m_time, &interval, &m_time); + + auto maybe_first = m_timer_queue.first(); + if (!maybe_first.has_value()) return; + + auto first = *maybe_first; + first->delta_ticks--; + + LinkedList timers_to_be_restarted; + + m_timer_queue.delayed_for_each([&](Timer* t) { + if (t->delta_ticks == 0) + { + this->m_timer_queue.remove(t); + t->clock = nullptr; + t->thread->send_signal(t->signo); + if (t->restart) timers_to_be_restarted.append(t); + return true; + } + + return false; + }); + + timers_to_be_restarted.consume([this](Timer* t) { add_to_timer_queue(t); }); +} + +void Clock::get_time(struct timespec& out) +{ + out = m_time; +} + +long Clock::resolution() +{ + return m_resolution; +} + +u64 Clock::ticks_left(Timer* timer) +{ + check(timer->clock == this); + + u64 total = 0; + if (timer->delta_ticks == 0) return 0; + for (auto* t : m_timer_queue) + { + total += t->delta_ticks; + if (t == timer) break; + } + return total; +} diff --git a/kernel/src/thread/Clock.h b/kernel/src/thread/Clock.h new file mode 100644 index 00000000..b2aaee56 --- /dev/null +++ b/kernel/src/thread/Clock.h @@ -0,0 +1,36 @@ +#pragma once +#include "thread/Timer.h" +#include + +struct Clock +{ + static void init(); + static void arch_init(); + + void set_resolution(long resolution); + + void update(const struct timespec& time); + + void tick(); + + void add_to_timer_queue(Timer* timer); + void remove_from_timer_queue(Timer* timer); + + void get_time(struct timespec& out); + long resolution(); + + u64 ticks_left(Timer* timer); + + private: + struct timespec m_time + { + .tv_sec = 0, .tv_nsec = 0 + }; + + long m_resolution = 1'000'000'000; + + LinkedList m_timer_queue; +}; + +extern Clock g_realtime_clock; +extern Clock g_monotonic_clock; diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 5d166cd0..56be87ec 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -219,7 +219,7 @@ namespace Scheduler if (thread->timer) { - Scheduler::remove_from_timer_queue(thread->timer); + thread->timer->clock->remove_from_timer_queue(thread->timer); delete thread->timer; } @@ -300,8 +300,6 @@ namespace Scheduler { CPU::disable_interrupts(); - tick_queue(); - if (is_in_kernel(regs)) g_current->kernel_ticks_self++; else g_current->user_ticks_self++; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index eefb33fc..f5767f34 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -19,10 +19,7 @@ #error "Unknown architecture." #endif -namespace Scheduler -{ - class Timer; -} +class Timer; enum class ThreadState { @@ -101,7 +98,7 @@ struct Thread : public LinkedListNode mode_t umask { 0 }; - Scheduler::Timer* timer { nullptr }; + Timer* timer { nullptr }; StaticString<128> cmdline; diff --git a/kernel/src/thread/Timer.cpp b/kernel/src/thread/Timer.cpp deleted file mode 100644 index d58ea281..00000000 --- a/kernel/src/thread/Timer.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "thread/Timer.h" -#include "Log.h" - -static LinkedList g_timer_queue; - -namespace Scheduler -{ - void add_to_timer_queue(Timer* timer) - { - timer->delta_ticks = timer->total_ticks; - - for (auto* t : g_timer_queue) - { - if (timer->delta_ticks <= t->delta_ticks) - { - t->delta_ticks -= timer->delta_ticks; - g_timer_queue.add_before(t, timer); - return; - } - timer->delta_ticks -= t->delta_ticks; - } - - g_timer_queue.append(timer); - } - - void remove_from_timer_queue(Timer* timer) - { - auto maybe_next = g_timer_queue.next(timer); - if (maybe_next.has_value()) - { - auto next = maybe_next.value(); - next->delta_ticks += timer->delta_ticks; - } - g_timer_queue.remove(timer); - } - - void tick_queue() - { - auto maybe_first = g_timer_queue.first(); - if (!maybe_first.has_value()) return; - - auto first = *maybe_first; - first->delta_ticks--; - - LinkedList timers_to_be_restarted; - - g_timer_queue.delayed_for_each([&](Timer* t) { - if (t->delta_ticks == 0) - { - g_timer_queue.remove(t); - t->thread->send_signal(t->signo); - if (t->restart) timers_to_be_restarted.append(t); - return true; - } - - return false; - }); - - timers_to_be_restarted.consume([](Timer* t) { add_to_timer_queue(t); }); - } - - u64 Timer::ticks_left() - { - u64 total = 0; - if (delta_ticks == 0) return 0; - for (auto* t : g_timer_queue) - { - total += t->delta_ticks; - if (t == this) break; - } - return total; - } -} diff --git a/kernel/src/thread/Timer.h b/kernel/src/thread/Timer.h index 51419741..f63942f3 100644 --- a/kernel/src/thread/Timer.h +++ b/kernel/src/thread/Timer.h @@ -1,23 +1,18 @@ #pragma once -#include "thread/Thread.h" +#include #include -namespace Scheduler +struct Thread; +struct Clock; + +class Timer : public LinkedListNode { - class Timer : public LinkedListNode - { - public: - u64 delta_ticks; - u64 total_ticks; - Thread* thread; - int signo { SIGALRM }; - bool restart { false }; + public: + u64 delta_ticks; + u64 total_ticks; + Thread* thread; + int signo { SIGALRM }; + bool restart { false }; - u64 ticks_left(); - }; - - void add_to_timer_queue(Timer* timer); - void remove_from_timer_queue(Timer* timer); - - void tick_queue(); -} + Clock* clock; +};