summaryrefslogtreecommitdiff
path: root/kernel/timer.c
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2024-07-03 18:30:51 +0200
committerAnton Kling <anton@kling.gg>2024-07-03 18:30:51 +0200
commit4e7918753175dbd8fc38bc7c5b176517e1dbef2f (patch)
tree723b3b3503d7502f3ce5338aeb6964cb5fa4c246 /kernel/timer.c
parent658c4e9645bf46268ed13bf5ef76d0ba60a347b9 (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.c69
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;
+}