From b1cf9e5dfbc8967bd7cb2a22ec1e5e521f4e0e6e Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Wed, 31 Jul 2024 13:36:53 -0400 Subject: add clock --- kernel/include/hilbert/kernel/timer.hpp | 18 ++++ kernel/include/hilbert/kernel/utility.hpp | 19 ++++ kernel/makefile | 2 +- kernel/source/entry.cpp | 2 + kernel/source/interrupts.asm | 26 ++++- kernel/source/syscall.cpp | 30 +++++- kernel/source/timer.asm | 162 ++++++++++++++++++++++++++++++ kernel/source/timer.cpp | 117 +++++++++++++++++++++ 8 files changed, 371 insertions(+), 5 deletions(-) create mode 100644 kernel/include/hilbert/kernel/timer.hpp create mode 100644 kernel/source/timer.asm create mode 100644 kernel/source/timer.cpp (limited to 'kernel') 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 + +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 #include #include +#include #include #include @@ -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 #include #include +#include #include 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 + +namespace hilbert::kernel::timer { + + struct sleeping_thread { + uint64_t sleeping_until; + application::thread *thread; + }; + + //sorted ascending by sleeping_until + static utility::list *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(); + + 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(); + + } + +} -- cgit v1.2.3