diff options
Diffstat (limited to 'src/kernel/arch/amd64/time.c')
-rw-r--r-- | src/kernel/arch/amd64/time.c | 71 |
1 files changed, 56 insertions, 15 deletions
diff --git a/src/kernel/arch/amd64/time.c b/src/kernel/arch/amd64/time.c index ef76249..44bd84e 100644 --- a/src/kernel/arch/amd64/time.c +++ b/src/kernel/arch/amd64/time.c @@ -6,40 +6,83 @@ static Proc *scheduled = NULL; -static void *hpet = NULL; +static volatile void *hpet = NULL; static uint64_t hpet_period; +static void hpet_irq(void); + +enum { + /* bitfields */ + HpetIs64Bit = 1<<13, + HpetHasLegacyMap = 1<<15, + + HpetEnabled = 1<<0, + HpetUseLegacyMap = 1<<1, + + HpetTimerTrigger = 1<<1, /* true if level triggered */ + HpetTimerEnable = 1<<2, + HpetTimerPeriodic = 1<<3, + HpetTimer64 = 1<<5, +}; + void hpet_init(void *base) { if (hpet) panic_invalid_state(); hpet = base; - uint64_t *info = hpet; - uint64_t *cfg = hpet + 0x10; + volatile uint64_t *info = hpet; + volatile uint64_t *cfg = hpet + 0x10; hpet_period = *info >> 32; - *cfg |= 1<<0; /* enable */ + if (!(*info & HpetIs64Bit)) panic_unimplemented(); + if (!(*info & HpetHasLegacyMap)) panic_unimplemented(); + + volatile uint64_t *timcfg = hpet + 0x120; + if (!(*timcfg & HpetTimer64)) panic_unimplemented(); + *timcfg |= HpetTimerTrigger; + *timcfg &= ~HpetTimerPeriodic; + *cfg |= HpetEnabled | HpetUseLegacyMap; + + irq_fn[IRQ_HPET] = hpet_irq; } -uint64_t uptime_ns(void) { +static void hpet_sched(void) { + volatile uint64_t *status = hpet + 0x20; + volatile uint64_t *timcfg = hpet + 0x120; + volatile uint64_t *timval = hpet + 0x128; + if (scheduled) { + *timval = scheduled->waits4timer.goal; + *timcfg |= HpetTimerEnable; + } else { + *timcfg &= ~HpetTimerEnable; + } + *status = ~0; +} + +static uint64_t get_counter(void) { if (hpet == NULL) panic_invalid_state(); - uint64_t *counter = hpet + 0xF0; - unsigned __int128 femto = *counter * hpet_period; + volatile uint64_t *counter = hpet + 0xF0; + return *counter; +} + +uint64_t uptime_ns(void) { + unsigned __int128 femto = get_counter() * hpet_period; uint64_t nano = femto / 1000000; return nano; } -static void pit_irq(void) { +static void hpet_irq(void) { Proc *p = scheduled; - if (p && p->waits4timer.goal < uptime_ns()) { + if (p && p->waits4timer.goal < get_counter()) { scheduled = p->waits4timer.next; proc_setstate(p, PS_RUNNING); } + hpet_sched(); } void timer_schedule(Proc *p, uint64_t time) { - uint64_t now = uptime_ns(); + uint64_t now = get_counter(); /* to prevent leaking the current uptime, saturate instead of overflowing */ - time = now + time; + time = now + (time * 1000000 / hpet_period); if (time < now) { time = ~0; } @@ -54,6 +97,7 @@ void timer_schedule(Proc *p, uint64_t time) { } p->waits4timer.next = *slot; *slot = p; + hpet_sched(); // TODO can sometimes be skipped } void timer_deschedule(Proc *p) { @@ -66,8 +110,5 @@ void timer_deschedule(Proc *p) { *slot = p->waits4timer.next; proc_setstate(p, PS_RUNNING); -} - -void timer_init(void) { - irq_fn[IRQ_PIT] = pit_irq; + hpet_sched(); // TODO can sometimes be skipped } |