Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/task/time.c b/task/time.c
new file mode 100644
index 0000000..f81d496
--- /dev/null
+++ b/task/time.c
@@ -0,0 +1,183 @@
+/*
+ * This file controls the system clock and contains the functions related to
+ * getting and setting the time from various sources. It keeps a monotonic
+ * clock internally, but can also make use of the tasks' clocks, and the RTC.
+ */
+
+#include <stdint.h>
+#include <sys/times.h>
+#include <errno.h>
+#include "task.h"
+#include "../proc/proc.h"
+#include "../io.h"
+
+#define RTC_SECONDS 0x00
+#define RTC_MINUTES 0x02
+#define RTC_HOURS 0x04
+#define RTC_WEEKDAY 0x06
+#define RTC_DAY 0x07
+#define RTC_MONTH 0x08
+#define RTC_YEAR 0x09
+#define RTC_CENTURY 0x32
+
+#define LEAP_YEAR(x) ((((x % 4) == 0) && ((x % 100) != 0)) || ((x % 400) == 0))
+
+extern TaskQueue readyQueue;
+
+uint32_t monotonicClock = 0, millis = 0;
+uint8_t slice[MAX_CPUS] = {0};
+TaskQueue sleepQueue;
+
+/* Read an RTC register */
+static uint8_t
+read_rtc(uint8_t index)
+{
+ outb(0x70, 0x80 | index);
+ io_wait();
+ return inb(0x71);
+}
+
+/* Timer interrupt */
+void
+timer_handler(InterruptFrame *frame)
+{
+ /* Monotonic clock */
+ millis++;
+ if (millis == 1000) {
+ monotonicClock++;
+ millis = 0;
+ }
+
+ /* Sleeping processes */
+ Task *proc = sleepQueue.start, *prev = NULL;
+ for (proc = sleepQueue.start; proc; prev = proc, proc = proc->next) {
+ if (proc->sleepExpiry > (monotonicClock * 1000) + millis)
+ break;
+ if (prev)
+ prev->next = proc->next;
+ if (sleepQueue.start == proc)
+ sleepQueue.start = proc->next;
+ if (sleepQueue.end == proc)
+ sleepQueue.end = NULL;
+ add_to_queue(&readyQueue, proc);
+ }
+
+ /* Account timeslice */
+ slice[CPUID]++;
+ if (!current)
+ return;
+
+ /* Account task time */
+ if (in_syscall())
+ current->systime++;
+ else
+ current->usertime++;
+
+ /* Call scheduler */
+ if (slice[CPUID] < current->priority)
+ return;
+ slice[CPUID] = 0;
+ schedule();
+}
+
+/* Sleep for a specified time (milliseconds) */
+int
+sleep(uint32_t ms)
+{
+ current->sleepExpiry = (monotonicClock * 1000) + millis + ms;
+
+ /* Add to sorted sleep TaskQueue */
+ TaskQueue *q = &sleepQueue;
+ Task *task, *prev = NULL;
+ acquire(&q->lock);
+ if (!q->start) {
+ q->start = current;
+ q->end = current;
+ current->next = NULL;
+ } else {
+ for (task = q->start; task; prev = task, task = task->next)
+ if (task->sleepExpiry > current->sleepExpiry)
+ break;
+ if (!prev) {
+ current->next = q->start;
+ q->start = current;
+ } else {
+ current->next = task;
+ prev->next = current;
+ }
+ }
+ release(&q->lock);
+
+ block_task(SLEEP);
+ return 0;
+}
+
+/* Get epoch time */
+time_t
+time(time_t *tloc)
+{
+ if (!verify_access(tloc, sizeof(time_t), PROT_WRITE))
+ return -EFAULT;
+
+ /* Length of months for normal and leap years */
+ const uint16_t monthLen[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+ };
+
+ uint16_t i;
+ uint8_t second, minute, hour, day, month, year, cent;
+ time_t time = 0;
+ uint8_t years = 0, leapYears = 0;
+
+ second = read_rtc(RTC_SECONDS);
+ minute = read_rtc(RTC_MINUTES);
+ hour = read_rtc(RTC_HOURS);
+ day = read_rtc(RTC_DAY);
+ month = read_rtc(RTC_MONTH);
+ year = read_rtc(RTC_YEAR);
+ cent = read_rtc(RTC_CENTURY);
+
+ second = (second & 0x0F) + ((second / 16) * 10);
+ minute = (minute & 0x0F) + ((minute / 16) * 10);
+ hour = ((hour & 0x0F) + (((hour & 0x70) / 16) * 10)) | (hour & 0x80);
+ day = (day & 0x0F) + ((day / 16) * 10);
+ month = (month & 0x0F) + ((month / 16) * 10);
+ year = (year & 0x0F) + ((year / 16) * 10);
+ cent = (cent & 0x0F) + ((cent / 16) * 10);
+
+ for (i = 1970; i < (cent * 100) + year; i++)
+ if (LEAP_YEAR(i))
+ leapYears++;
+ else
+ years++;
+ time += ((years * 365) + (leapYears * 366)) * 86400;
+
+ for (i = 0; i < month - 1; i++)
+ time += monthLen[LEAP_YEAR(year)][i] * 86400;
+
+ time += (day - 1) * 86400;
+ time += hour * 3600;
+ time += minute * 60;
+ time += second;
+
+ if (tloc)
+ *tloc = time;
+
+ return time;
+}
+
+/* Get process times */
+clock_t
+times(Times *buf)
+{
+ if (!verify_access(buf, sizeof(Times), PROT_WRITE))
+ return -EFAULT;
+ if (buf) {
+ buf->utime = current->usertime;
+ buf->stime = current->systime;
+ buf->cutime = current->usertime;
+ buf->cstime = current->systime;
+ }
+ return (monotonicClock * 1000) + millis;
+}