diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index e684607..fca5605 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -5,6 +5,7 @@ */ #include "Syscall.hpp" +#include #include #include #include @@ -256,7 +257,18 @@ namespace Zenith { return (rows << 32) | (cols & 0xFFFFFFFF); } - static void Sys_Reset() { + static void Sys_GetTime(DateTime* out) { + if (out == nullptr) return; + Timekeeping::DateTime dt = Timekeeping::GetDateTime(); + out->Year = dt.Year; + out->Month = dt.Month; + out->Day = dt.Day; + out->Hour = dt.Hour; + out->Minute = dt.Minute; + out->Second = dt.Second; + } + + static void Sys_Reset() { /* Triple fault for now; TODO: implement UEFI runtime function for clean reboot. @@ -349,6 +361,9 @@ namespace Zenith { case SYS_SHUTDOWN: /* Unimplemented */ return -1; + case SYS_GETTIME: + Sys_GetTime((DateTime*)frame->arg1); + return 0; default: return -1; } @@ -375,7 +390,7 @@ namespace Zenith { Hal::WriteMSR(Hal::IA32_FMASK, 0x200); Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR=" - << kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 26 syscalls)"; + << kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 29 syscalls)"; } } diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index cd635e1..2077fb1 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -39,6 +39,16 @@ namespace Zenith { static constexpr uint64_t SYS_GETARGS = 25; static constexpr uint64_t SYS_RESET = 26; static constexpr uint64_t SYS_SHUTDOWN = 27; + static constexpr uint64_t SYS_GETTIME = 28; + + struct DateTime { + uint16_t Year; + uint8_t Month; + uint8_t Day; + uint8_t Hour; + uint8_t Minute; + uint8_t Second; + }; struct FbInfo { uint64_t width; diff --git a/kernel/src/Timekeeping/Time.cpp b/kernel/src/Timekeeping/Time.cpp index 3afea1d..2c527a6 100644 --- a/kernel/src/Timekeeping/Time.cpp +++ b/kernel/src/Timekeeping/Time.cpp @@ -4,12 +4,70 @@ */ #include "Time.hpp" +#include "ApicTimer.hpp" #include #include using namespace Kt; +static int64_t g_bootEpoch = 0; + +static bool IsLeapYear(int year) { + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); +} + +static int DaysInMonth(int month, int year) { + static const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + if (month == 2 && IsLeapYear(year)) return 29; + return days[month]; +} + +static int64_t DateToEpoch(int year, int month, int day, int hour, int minute, int second) { + int64_t days = 0; + for (int y = 1970; y < year; y++) { + days += IsLeapYear(y) ? 366 : 365; + } + for (int m = 1; m < month; m++) { + days += DaysInMonth(m, year); + } + days += day - 1; + return days * 86400 + hour * 3600 + minute * 60 + second; +} + +static Timekeeping::DateTime EpochToDate(int64_t epoch) { + Timekeeping::DateTime dt = {}; + int64_t rem = epoch; + int year = 1970; + while (true) { + int64_t daysInYear = IsLeapYear(year) ? 366 : 365; + if (rem < daysInYear * 86400) break; + rem -= daysInYear * 86400; + year++; + } + dt.Year = (uint16_t)year; + + int dayOfYear = (int)(rem / 86400); + rem -= (int64_t)dayOfYear * 86400; + + int month = 1; + while (month <= 12) { + int dim = DaysInMonth(month, year); + if (dayOfYear < dim) break; + dayOfYear -= dim; + month++; + } + dt.Month = (uint8_t)month; + dt.Day = (uint8_t)(dayOfYear + 1); + + dt.Hour = (uint8_t)(rem / 3600); + rem %= 3600; + dt.Minute = (uint8_t)(rem / 60); + dt.Second = (uint8_t)(rem % 60); + return dt; +} + void Timekeeping::Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second) { + g_bootEpoch = DateToEpoch(Year, Month, Day, Hour, Minute, Second); /* Hardcode CET for now */ TimeZone CET = { "Central European Time", @@ -63,4 +121,12 @@ void Timekeeping::Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, CString dateString = panelStr.c_str(); UpdatePanelBar(dateString); +} + +int64_t Timekeeping::GetUnixTimestamp() { + return g_bootEpoch + (int64_t)(Timekeeping::GetMilliseconds() / 1000); +} + +Timekeeping::DateTime Timekeeping::GetDateTime() { + return EpochToDate(GetUnixTimestamp()); } \ No newline at end of file diff --git a/kernel/src/Timekeeping/Time.hpp b/kernel/src/Timekeeping/Time.hpp index 3d473eb..23dd516 100644 --- a/kernel/src/Timekeeping/Time.hpp +++ b/kernel/src/Timekeeping/Time.hpp @@ -41,5 +41,16 @@ namespace Timekeeping { "December" }; + struct DateTime { + uint16_t Year; + uint8_t Month; + uint8_t Day; + uint8_t Hour; + uint8_t Minute; + uint8_t Second; + }; + void Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second); + int64_t GetUnixTimestamp(); + DateTime GetDateTime(); }; diff --git a/programs/bin/hello.elf b/programs/bin/hello.elf index 2db5c72..808e8e0 100755 Binary files a/programs/bin/hello.elf and b/programs/bin/hello.elf differ diff --git a/programs/bin/shell.elf b/programs/bin/shell.elf index d380bd3..bee4551 100755 Binary files a/programs/bin/shell.elf and b/programs/bin/shell.elf differ diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index e0ebfdd..2f5ec78 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -39,6 +39,16 @@ namespace Zenith { static constexpr uint64_t SYS_GETARGS = 25; static constexpr uint64_t SYS_RESET = 26; static constexpr uint64_t SYS_SHUTDOWN = 27; + static constexpr uint64_t SYS_GETTIME = 28; + + struct DateTime { + uint16_t Year; + uint8_t Month; + uint8_t Day; + uint8_t Hour; + uint8_t Minute; + uint8_t Second; + }; struct FbInfo { uint64_t width; diff --git a/programs/include/zenith/syscall.h b/programs/include/zenith/syscall.h index 15a17e5..48d1065 100644 --- a/programs/include/zenith/syscall.h +++ b/programs/include/zenith/syscall.h @@ -176,6 +176,9 @@ namespace zenith { if (rows) *rows = (int)(r >> 32); } + // Timekeeping (wall-clock) + inline void gettime(Zenith::DateTime* out) { syscall1(Zenith::SYS_GETTIME, (uint64_t)out); } + // Power management [[noreturn]] inline void reset() { syscall0(Zenith::SYS_RESET); diff --git a/programs/man/shell.1 b/programs/man/shell.1 index 2dcff32..993076c 100644 --- a/programs/man/shell.1 +++ b/programs/man/shell.1 @@ -47,6 +47,9 @@ round-trip times. Timeout is 3 seconds per request. Example: ping 10.0.2.2 +.SS date + Display the current date and time in UTC. + .SS uptime Display the system uptime in minutes, seconds, and milliseconds. diff --git a/programs/man/syscalls.2 b/programs/man/syscalls.2 index 32085fe..72f482f 100644 --- a/programs/man/syscalls.2 +++ b/programs/man/syscalls.2 @@ -3,7 +3,7 @@ syscalls - overview of ZenithOS system calls .SH DESCRIPTION - ZenithOS provides 26 system calls (numbers 0-25) for userspace + ZenithOS provides 29 system calls (numbers 0-28) for userspace programs. Syscalls use the x86-64 SYSCALL instruction with the following register convention: @@ -91,6 +91,12 @@ Get milliseconds elapsed since boot. uint64_t zenith::get_milliseconds(); +.B SYS_GETTIME (28) + Get the current wall-clock date and time (UTC). + Fills a Zenith::DateTime struct with Year, Month, Day, + Hour, Minute, and Second fields. + void zenith::gettime(Zenith::DateTime* out); + .SH SYSTEM .B SYS_GETINFO (15) Get OS name, version, and configuration. @@ -133,5 +139,14 @@ Get the argument string passed to this process at spawn time. int zenith::getargs(char* buf, uint64_t maxLen); +.SH POWER MANAGEMENT +.B SYS_RESET (26) + Reboot the system. + [[noreturn]] void zenith::reset(); + +.B SYS_SHUTDOWN (27) + Shut down the system (currently unimplemented). + [[noreturn]] void zenith::shutdown(); + .SH SEE ALSO spawn(2), file(2), framebuffer(2), malloc(3) diff --git a/programs/obj/hello/main.o b/programs/obj/hello/main.o index bead10b..ea63150 100644 Binary files a/programs/obj/hello/main.o and b/programs/obj/hello/main.o differ diff --git a/programs/obj/shell/main.o b/programs/obj/shell/main.o index cd52370..cceca0a 100644 Binary files a/programs/obj/shell/main.o and b/programs/obj/shell/main.o differ diff --git a/programs/src/shell/main.cpp b/programs/src/shell/main.cpp index e9c9f80..2593fb5 100644 --- a/programs/src/shell/main.cpp +++ b/programs/src/shell/main.cpp @@ -94,6 +94,7 @@ static void cmd_help() { zenith::print(" cat Display file contents\n"); zenith::print(" run Spawn a new process from an ELF file\n"); zenith::print(" ping Send ICMP echo requests\n"); + zenith::print(" date Show current date and time\n"); zenith::print(" uptime Show uptime in milliseconds\n"); zenith::print(" clear Clear the screen\n"); zenith::print(" reset Reboot the system\n"); @@ -390,6 +391,38 @@ static void cmd_man(const char* arg) { } } +static void print_int_padded(uint64_t n) { + if (n < 10) zenith::putchar('0'); + print_int(n); +} + +static const char* month_name(int m) { + static const char* months[] = { + "", "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }; + if (m >= 1 && m <= 12) return months[m]; + return "?"; +} + +static void cmd_date() { + Zenith::DateTime dt; + zenith::gettime(&dt); + + print_int(dt.Day); + zenith::putchar(' '); + zenith::print(month_name(dt.Month)); + zenith::putchar(' '); + print_int(dt.Year); + zenith::print(", "); + print_int(dt.Hour); + zenith::putchar(':'); + print_int_padded(dt.Minute); + zenith::putchar(':'); + print_int_padded(dt.Second); + zenith::print(" UTC\n"); +} + static void cmd_clear() { zenith::print("\033[2J"); // Clear entire screen zenith::print("\033[H"); // Move cursor to top-left @@ -428,6 +461,8 @@ static void process_command(const char* line) { cmd_ping(line + 5); } else if (streq(line, "ping")) { cmd_ping(""); + } else if (streq(line, "date")) { + cmd_date(); } else if (streq(line, "uptime")) { cmd_uptime(); } else if (streq(line, "clear")) { diff --git a/ramdisk.tar b/ramdisk.tar index c83d0b3..ff32589 100644 Binary files a/ramdisk.tar and b/ramdisk.tar differ