summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorBenji Dial <benji@benjidial.net>2024-07-31 13:36:53 -0400
committerBenji Dial <benji@benjidial.net>2024-07-31 13:36:53 -0400
commitb1cf9e5dfbc8967bd7cb2a22ec1e5e521f4e0e6e (patch)
tree00837891f9b9bf232e540a6f9b3e16f2438865c3 /kernel
parent86b343f17175ef3e1fad2197636f75770466aa7c (diff)
downloadhilbert-os-b1cf9e5dfbc8967bd7cb2a22ec1e5e521f4e0e6e.tar.gz
add clock
Diffstat (limited to 'kernel')
-rw-r--r--kernel/include/hilbert/kernel/timer.hpp18
-rw-r--r--kernel/include/hilbert/kernel/utility.hpp19
-rw-r--r--kernel/makefile2
-rw-r--r--kernel/source/entry.cpp2
-rw-r--r--kernel/source/interrupts.asm26
-rw-r--r--kernel/source/syscall.cpp30
-rw-r--r--kernel/source/timer.asm162
-rw-r--r--kernel/source/timer.cpp117
8 files changed, 371 insertions, 5 deletions
diff --git a/kernel/include/hilbert/kernel/timer.hpp b/kernel/include/hilbert/kernel/timer.hpp
new file mode 100644
index 0000000..f4ce17e
--- /dev/null
+++ b/kernel/include/hilbert/kernel/timer.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <hilbert/kernel/application.hpp>
+
+namespace hilbert::kernel::timer {
+
+ void init_timer();
+
+ //"mibiseconds" (1 second / 1024) since january 1st 2000
+ extern uint64_t current_time;
+
+ //when current_time >= sleeping_until, puts thread into paused threads
+ void register_sleeping_thread(
+ uint64_t sleeping_until, application::thread *thread);
+
+ void on_timer_interrupt();
+
+}
diff --git a/kernel/include/hilbert/kernel/utility.hpp b/kernel/include/hilbert/kernel/utility.hpp
index a21d3fe..b0ced32 100644
--- a/kernel/include/hilbert/kernel/utility.hpp
+++ b/kernel/include/hilbert/kernel/utility.hpp
@@ -126,6 +126,25 @@ namespace hilbert::kernel::utility {
last = n;
}
+ //if other == 0, then insert at the end
+ void insert_before(value_t &&value, node *other) {
+ node *n = new node {};
+ n->value = value;
+ n->next = other;
+ if (other) {
+ n->prev = other->prev;
+ other->prev = n;
+ }
+ else {
+ n->prev = last;
+ last = n;
+ }
+ if (n->prev)
+ n->prev->next = n;
+ else
+ first = n;
+ }
+
void clear() {
if (first) {
for (node *n = first->next; n; n = n->next)
diff --git a/kernel/makefile b/kernel/makefile
index 6d32537..608e67e 100644
--- a/kernel/makefile
+++ b/kernel/makefile
@@ -2,7 +2,7 @@ SOURCES = \
storage/bd/memory.cpp storage/fs/tarfs.cpp application.asm application.cpp \
framebuffer.cpp interrupts.asm interrupts.cpp allocator.cpp storage.cpp \
syscall.cpp utility.cpp paging.asm paging.cpp entry.cpp input.cpp panic.cpp \
- vfile.cpp serial.asm app-memory.cpp load-app.cpp
+ vfile.cpp serial.asm app-memory.cpp load-app.cpp timer.cpp timer.asm
build/%.asm.o: source/%.asm
@mkdir -p $(@D)
diff --git a/kernel/source/entry.cpp b/kernel/source/entry.cpp
index efa70fd..73e79c7 100644
--- a/kernel/source/entry.cpp
+++ b/kernel/source/entry.cpp
@@ -7,6 +7,7 @@
#include <hilbert/kernel/serial.hpp>
#include <hilbert/kernel/input.hpp>
#include <hilbert/kernel/panic.hpp>
+#include <hilbert/kernel/timer.hpp>
#include <hilbert/kernel/vfile.hpp>
#include <limine.h>
@@ -179,6 +180,7 @@ extern "C" [[noreturn]] void entry() {
if (!have_initfs)
panic(0x5f8860);
+ timer::init_timer();
input::init_input();
application::init_applications();
diff --git a/kernel/source/interrupts.asm b/kernel/source/interrupts.asm
index e4912ee..df8e7ce 100644
--- a/kernel/source/interrupts.asm
+++ b/kernel/source/interrupts.asm
@@ -267,6 +267,22 @@ isr_end:
ret
+extern on_rtc_interrupt
+
+rtc_isr:
+
+ call isr_start
+
+ call on_rtc_interrupt
+
+ mov al, 0x20
+ out 0x20, al
+ out 0xa0, al
+
+ call isr_end
+
+ iretq
+
extern on_keyboard_interrupt
keyboard_isr:
@@ -343,7 +359,7 @@ load_gdt_and_idt:
out 0x21, al
mov al, 0x01
out 0x21, al
- mov al, 0xf9 ;mask all but irq 1 and 2
+ mov al, 0xf9 ;mask all but irqs 1 and 2
out 0x21, al
mov al, 0x11
@@ -354,9 +370,15 @@ load_gdt_and_idt:
out 0xa1, al
mov al, 0x01
out 0xa1, al
- mov al, 0xef ;mask all but irq 12
+ mov al, 0xee ;mask all but irqs 8 and 12
out 0xa1, al
+ ;register rtc interrupt
+
+ mov rdi, 0x28
+ mov rsi, rtc_isr
+ call set_isr
+
;register keyboard and mouse interrupts
mov rdi, 0x21
diff --git a/kernel/source/syscall.cpp b/kernel/source/syscall.cpp
index c631df1..803f7d2 100644
--- a/kernel/source/syscall.cpp
+++ b/kernel/source/syscall.cpp
@@ -4,6 +4,7 @@
#include <hilbert/kernel/paging.hpp>
#include <hilbert/kernel/input.hpp>
#include <hilbert/kernel/panic.hpp>
+#include <hilbert/kernel/timer.hpp>
#include <hilbert/kernel/vfile.hpp>
namespace hilbert::kernel::syscall {
@@ -800,6 +801,29 @@ namespace hilbert::kernel::syscall {
}
+ void sleep_syscall(
+ uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
+
+ uint64_t mis = rdi;
+ set_zero(rax, rdi, rsi, rdx);
+
+ auto *t = application::running_thread;
+
+ timer::register_sleeping_thread(
+ timer::current_time + mis, t);
+
+ application::yield(t->saved_state);
+
+ }
+
+ void get_time_syscall(
+ uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
+
+ set_zero(rax, rdi, rsi, rdx);
+ rax = timer::current_time;
+
+ }
+
void (*handlers[])(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) = {
@@ -827,11 +851,13 @@ namespace hilbert::kernel::syscall {
&clear_socket_read_queue_syscall,
&get_environment_variable_length_syscall,
&get_environment_variable_value_syscall,
- &set_thread_name_syscall
+ &set_thread_name_syscall,
+ &sleep_syscall,
+ &get_time_syscall
};
- static constexpr int max_syscall_number = 24;
+ static constexpr int max_syscall_number = 26;
}
diff --git a/kernel/source/timer.asm b/kernel/source/timer.asm
new file mode 100644
index 0000000..d5f6f73
--- /dev/null
+++ b/kernel/source/timer.asm
@@ -0,0 +1,162 @@
+bits 64
+
+section .rodata
+
+section .text
+
+read_cmos_byte:
+;dil in = register number
+;al out = value
+;rdx and rcx are not touched
+
+ mov al, dil
+ out 0x70, al
+ in al, 0x71
+ ret
+
+global enable_rtc_interrupts
+enable_rtc_interrupts:
+
+ ;secondary status register
+ mov dil, 11
+ call read_cmos_byte
+
+ ;enable interrupts
+ or al, 0x40
+ mov cl, al
+
+ ;do cmos write
+ mov al, 11
+ out 0x70, al
+ mov al, cl
+ out 0x71, al
+
+ ret
+
+global acknowledge_rtc_interrupt
+acknowledge_rtc_interrupt:
+ mov dil, 12
+ jmp read_cmos_byte
+
+convert_bcd:
+;al in = byte (possibly bcd)
+;al out = byte (not bcd)
+;sil 0x02 = bcd
+;does not touch rdx, rcx, sil
+ test sil, 0x02
+ jz .no_convert
+
+ mov dil, al
+ and dil, 0xf0
+ and al, 0x0f
+ shr dil, 3
+ add al, dil
+ shl dil, 2
+ add al, dil
+
+.no_convert:
+ ret
+
+convert_hours:
+;al in = byte (possibly bcd and 12 hour)
+;al out = byte (not bcd or 12 hour)
+;sil 0x02 = bcd, sil 0x01 = 12 hour
+;does not touch rdx, rcx, sil
+ test sil, 0x01
+ jz convert_bcd
+
+ test al, 0x80
+ jz .am
+
+ and al, 0x7f
+ call convert_bcd
+ cmp al, 12
+ je .noon
+
+ add al, 12
+ ret
+
+.noon:
+ ret
+
+.am:
+ call convert_bcd
+ cmp al, 12
+ je .midnight
+
+ ret
+
+.midnight:
+ xor al, al
+ ret
+
+global get_time_from_rtc
+get_time_from_rtc:
+;rax out = time (see timer.cpp for encoding)
+;we assume the year is 20xx (sorry)
+
+ mov dil, 11
+ call read_cmos_byte
+
+ shr al, 1
+ not al
+ and al, 3
+ mov sil, al
+
+ xor rdx, rdx
+
+.outer_loop:
+ mov rcx, rdx
+
+.wait_for_update_loop:
+ ;status register - 0x80 is update in progress
+ mov dil, 10
+ call read_cmos_byte
+ test al, 0x80
+ jnz .wait_for_update_loop
+
+ ;years
+ mov dil, 9
+ call read_cmos_byte
+ call convert_bcd
+ mov dh, al
+
+ ;months
+ mov dil, 8
+ call read_cmos_byte
+ call convert_bcd
+ mov dl, al
+
+ shl edx, 16
+
+ ;days
+ mov dil, 7
+ call read_cmos_byte
+ call convert_bcd
+ mov dh, al
+
+ ;hours
+ mov dil, 4
+ call read_cmos_byte
+ call convert_hours
+ mov dl, al
+
+ shl rdx, 16
+
+ ;minutes
+ mov dil, 2
+ call read_cmos_byte
+ call convert_bcd
+ mov dh, al
+
+ ;seconds
+ xor dil, dil
+ call read_cmos_byte
+ call convert_bcd
+ mov dl, al
+
+ cmp rdx, rcx
+ jne .outer_loop
+
+ mov rax, rdx
+ ret
diff --git a/kernel/source/timer.cpp b/kernel/source/timer.cpp
new file mode 100644
index 0000000..d6d37d7
--- /dev/null
+++ b/kernel/source/timer.cpp
@@ -0,0 +1,117 @@
+#include <hilbert/kernel/timer.hpp>
+
+namespace hilbert::kernel::timer {
+
+ struct sleeping_thread {
+ uint64_t sleeping_until;
+ application::thread *thread;
+ };
+
+ //sorted ascending by sleeping_until
+ static utility::list<sleeping_thread> *sleeping_threads;
+
+ uint64_t current_time;
+
+ //output is
+ // seconds | (minutes << 8) | (hours << 16) |
+ // (days << 24) | ( months << 32) | (years << 40)
+ extern "C" uint64_t get_time_from_rtc();
+
+ //index is (year % 4) * 12 + month - 1;
+ //output is days from january 1st 2000 to [month] 1st [year]
+ static uint16_t month_table[] {
+ 0, 31, 60, 91, 121, 152,
+ 182, 213, 244, 274, 305, 335,
+ 366, 397, 425, 456, 486, 517,
+ 547, 578, 609, 639, 670, 700,
+ 731, 762, 790, 821, 851, 882,
+ 912, 943, 974, 1004, 1035, 1065,
+ 1096, 1127, 1155, 1186, 1216, 1247,
+ 1277, 1308, 1339, 1369, 1400, 1430
+ };
+
+ //index is (year % 4) * 12 + month - 1;
+ //output is days in that month
+ static uint8_t month_table_2[] = {
+ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ void clamp(uint8_t &value, uint8_t min, uint8_t max) {
+ if (value < min)
+ value = min;
+ else if (value > max)
+ value = max;
+ }
+
+ extern "C" void enable_rtc_interrupts();
+
+ void init_timer() {
+
+ sleeping_threads = new utility::list<sleeping_thread>();
+
+ uint64_t rtc_time = get_time_from_rtc();
+
+ uint8_t years = rtc_time >> 40;
+ uint8_t months = (rtc_time >> 32) & 0xff;
+ uint8_t days = (rtc_time >> 24) & 0xff;
+ uint8_t hours = (rtc_time >> 16) & 0xff;
+ uint8_t minutes = (rtc_time >> 8) & 0xff;
+ uint8_t seconds = rtc_time & 0xff;
+
+ uint8_t month_table_index =
+ (years % 4) * 12 + months - 1;
+
+ clamp( years, 0, 99);
+ clamp( months, 1, 12);
+ clamp( days, 1, month_table_2[month_table_index]);
+ clamp( hours, 0, 23);
+ clamp(minutes, 0, 59);
+ clamp(seconds, 0, 59);
+
+ current_time = 1461 * (years / 4);
+ current_time += month_table[month_table_index];
+ current_time += days - 1;
+ current_time *= 86400;
+ current_time += hours * 3600;
+ current_time += minutes * 60;
+ current_time += seconds;
+ current_time *= 1024;
+
+ enable_rtc_interrupts();
+
+ }
+
+ void register_sleeping_thread(
+ uint64_t sleeping_until, application::thread *thread) {
+
+ auto *after = sleeping_threads->first;
+ while (after && after->value.sleeping_until < sleeping_until)
+ after = after->next;
+
+ sleeping_threads->insert_before(
+ { .sleeping_until = sleeping_until, .thread = thread }, after);
+
+ }
+
+ extern "C" void acknowledge_rtc_interrupt();
+
+ extern "C" void on_rtc_interrupt() {
+
+ ++current_time;
+
+ while (sleeping_threads->first &&
+ sleeping_threads->first->value.sleeping_until <=
+ current_time) {
+ application::paused_threads->insert(
+ sleeping_threads->first->value.thread);
+ sleeping_threads->remove(sleeping_threads->first);
+ }
+
+ acknowledge_rtc_interrupt();
+
+ }
+
+}