Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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; +}