diff options
Diffstat (limited to 'kernel/source')
-rw-r--r-- | kernel/source/entry.cpp | 2 | ||||
-rw-r--r-- | kernel/source/interrupts.asm | 26 | ||||
-rw-r--r-- | kernel/source/syscall.cpp | 30 | ||||
-rw-r--r-- | kernel/source/timer.asm | 162 | ||||
-rw-r--r-- | kernel/source/timer.cpp | 117 |
5 files changed, 333 insertions, 4 deletions
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(); + + } + +} |