Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
/*
* 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;
}