/* * 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 #include #include #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; }