diff options
author | Anton Kling <anton@kling.gg> | 2024-07-03 18:30:51 +0200 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2024-07-03 18:30:51 +0200 |
commit | 4e7918753175dbd8fc38bc7c5b176517e1dbef2f (patch) | |
tree | 723b3b3503d7502f3ce5338aeb6964cb5fa4c246 /kernel/timer.c | |
parent | 658c4e9645bf46268ed13bf5ef76d0ba60a347b9 (diff) |
Kernel/Time: Improve time keeping
This makes use of TSC and now provides a file system interface for
userland programs to change the system time.
Diffstat (limited to 'kernel/timer.c')
-rw-r--r-- | kernel/timer.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/kernel/timer.c b/kernel/timer.c new file mode 100644 index 0000000..46a30d3 --- /dev/null +++ b/kernel/timer.c @@ -0,0 +1,69 @@ +#include <arch/i386/tsc.h> +#include <drivers/cmos.h> +#include <fs/devfs.h> +#include <math.h> +#include <time.h> +#include <typedefs.h> + +i64 start_unix_time; +u64 start_tsc_time; + +void timer_init(void) { + tsc_init(); + start_tsc_time = tsc_get(); + start_unix_time = cmos_get_time(); +} + +u64 timer_get_uptime(void) { + return tsc_calculate_ms(tsc_get()); +} + +void timer_get(struct timespec *tp) { + u64 offset_tsc = tsc_get() - start_tsc_time; + i64 current_unix_time_seconds = + start_unix_time + tsc_calculate_ms(offset_tsc) / 1000; + tp->tv_sec = current_unix_time_seconds; + tp->tv_nsec = tsc_calculate_ms(offset_tsc); +} + +u64 timer_get_ms(void) { + u64 offset_tsc = tsc_get() - start_tsc_time; + return start_unix_time * 1000 + tsc_calculate_ms(offset_tsc); +} + +int clock_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { + (void)offset; + u64 r = timer_get_ms(); + u64 l = min(len, sizeof(u64)); + memcpy(buffer, &r, l); + return l; +} + +int clock_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { + (void)offset; + if (len != sizeof(i64)) { + return 0; + } + + i64 new_value_ms; + memcpy(&new_value_ms, buffer, sizeof(i64)); + i64 new_value_seconds = new_value_ms / 1000; + + u64 offset_tsc = tsc_get() - start_tsc_time; + i64 current_unix_time_seconds = + start_unix_time + tsc_calculate_ms(offset_tsc) / 1000; + + i64 delta = new_value_seconds - current_unix_time_seconds; + start_unix_time += delta; + cmos_set_time(new_value_seconds); + return sizeof(i64); +} + +int always_has_data(vfs_inode_t *inode); +int always_can_write(vfs_inode_t *inode); + +int timer_add_clock(void) { + devfs_add_file("/clock", clock_read, clock_write, NULL, always_has_data, + always_can_write, FS_TYPE_CHAR_DEVICE); + return 1; +} |