kernel/amd64: add HPET support, slightly rework time handling
This commit is contained in:
parent
458978e7b8
commit
4ade8ff9e6
|
@ -72,7 +72,7 @@ t('out/boot.iso', 'out/fs/boot/kernel out/fs/boot/grub/grub.cfg out/fs/boot/init
|
|||
])
|
||||
|
||||
t('out/fs/boot/kernel', 'src/kernel/arch/amd64/linker.ld' + srcobj('src/kernel/') + srcobj('src/libk/'), [
|
||||
'$(CC) -nostdlib -Wl,-zmax-page-size=4096 -Wl,--no-warn-mismatch -Wl,-no-pie -T $^ -o $@',
|
||||
'$(CC) -nostdlib -Wl,-zmax-page-size=4096 -Wl,--no-warn-mismatch -Wl,-no-pie -T $^ -o $@ -lgcc',
|
||||
'grub-file --is-x86-multiboot2 $@ || echo "$@ has an invalid multiboot2 header"',
|
||||
'grub-file --is-x86-multiboot2 $@ || rm $@; test -e $@'
|
||||
])
|
||||
|
|
|
@ -271,7 +271,11 @@ static void test_sleep(void) {
|
|||
long ret = _sys_read(reader, buf, sizeof buf, 0);
|
||||
if (ret == 0) break;
|
||||
test(pos + ret <= target);
|
||||
test(memcmp(buf, expected + pos, ret) == 0);
|
||||
if (memcmp(buf, expected + pos, ret) != 0) {
|
||||
buf[ret] = '\0';
|
||||
printf("got %s\n", buf);
|
||||
test(0);
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
test(pos == target);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
#include <kernel/arch/amd64/acpi.h>
|
||||
#include <kernel/panic.h>
|
||||
#include <shared/mem.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
char sig[8];
|
||||
uint8_t checksum;
|
||||
char oemid[6];
|
||||
uint8_t rev;
|
||||
uint32_t rsdt;
|
||||
} __attribute__((packed)) RDSP;
|
||||
|
||||
typedef struct {
|
||||
char sig[4];
|
||||
uint32_t len;
|
||||
uint8_t rev;
|
||||
uint8_t checksum;
|
||||
char oemID[6];
|
||||
char oemTID[8];
|
||||
uint32_t oemRev;
|
||||
uint32_t creatorId;
|
||||
uint32_t creatorRev;
|
||||
char data[0];
|
||||
} __attribute__((packed)) SDT;
|
||||
|
||||
/* https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf */
|
||||
typedef struct {
|
||||
uint8_t where;
|
||||
uint8_t bitwidth;
|
||||
uint8_t bitoffset;
|
||||
uint8_t _reserved;
|
||||
uint64_t addr;
|
||||
} __attribute__((packed)) GAS; /* Generic Address Structure */
|
||||
|
||||
typedef struct {
|
||||
SDT hdr;
|
||||
uint32_t _someid;
|
||||
GAS addr;
|
||||
/* ... */
|
||||
} __attribute__((packed)) HPET;
|
||||
|
||||
void
|
||||
acpi_parse(const void *rdsp_v)
|
||||
{
|
||||
const RDSP *rdsp = rdsp_v;
|
||||
if (memcmp(rdsp->sig, "RSD PTR ", 8) != 0) {
|
||||
panic_invalid_state();
|
||||
}
|
||||
|
||||
SDT *rsdt = (void*)(uintptr_t)rdsp->rsdt;
|
||||
if (memcmp(rsdt->sig, "RSDT ", 4) != 0) {
|
||||
panic_invalid_state();
|
||||
}
|
||||
|
||||
uint32_t *sub = (void*)rsdt->data;
|
||||
int len = (rsdt->len - sizeof(SDT)) / 4;
|
||||
for (int i = 0; i < len; i++) {
|
||||
SDT *it = (void*)(uintptr_t)sub[i];
|
||||
if (memcmp(it->sig, "HPET", 4) == 0) {
|
||||
HPET *hpet = (void*)it;
|
||||
GAS *gas = &hpet->addr;
|
||||
if (gas->where == 0) { /* in memory */
|
||||
hpet_init((void*)gas->addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
void acpi_parse(const void *rsdp);
|
||||
void hpet_init(void *base);
|
|
@ -1,4 +1,5 @@
|
|||
#include <kernel/arch/amd64/3rdparty/multiboot2.h>
|
||||
#include <kernel/arch/amd64/acpi.h>
|
||||
#include <kernel/arch/amd64/boot.h>
|
||||
#include <kernel/arch/amd64/driver/driver.h>
|
||||
#include <kernel/arch/amd64/driver/serial.h>
|
||||
|
@ -71,6 +72,12 @@ void kmain_early(void *mbi) {
|
|||
video_init(vid);
|
||||
pata_init();
|
||||
|
||||
{
|
||||
struct multiboot_tag_old_acpi *mod;
|
||||
mod = mbi_tag(mbi, MULTIBOOT_TAG_TYPE_ACPI_OLD);
|
||||
acpi_parse(mod->rsdp);
|
||||
}
|
||||
|
||||
kprintf("pci...\n");
|
||||
pci_init();
|
||||
|
||||
|
|
|
@ -1,31 +1,49 @@
|
|||
#include <kernel/arch/amd64/acpi.h>
|
||||
#include <kernel/arch/amd64/boot.h>
|
||||
#include <kernel/arch/amd64/interrupts.h>
|
||||
#include <kernel/arch/amd64/time.h>
|
||||
#include <kernel/panic.h>
|
||||
#include <kernel/proc.h>
|
||||
|
||||
static uint64_t uptime = 0, goal = ~0;
|
||||
static Proc *scheduled = NULL;
|
||||
|
||||
uint64_t uptime_ms(void) { return uptime; }
|
||||
static void *hpet = NULL;
|
||||
static uint64_t hpet_period;
|
||||
|
||||
static void update_goal(void) {
|
||||
goal = scheduled ? scheduled->waits4timer.goal : ~(uint64_t)0;
|
||||
void hpet_init(void *base) {
|
||||
if (hpet) panic_invalid_state();
|
||||
hpet = base;
|
||||
|
||||
uint64_t *info = hpet;
|
||||
uint64_t *cfg = hpet + 0x10;
|
||||
hpet_period = *info >> 32;
|
||||
*cfg |= 1<<0; /* enable */
|
||||
}
|
||||
|
||||
void pit_irq(void) {
|
||||
// TODO inefficient - getting here executes a lot of code which could just be a few lines of asm
|
||||
uptime++;
|
||||
if (uptime < goal) return;
|
||||
uint64_t uptime_ns(void) {
|
||||
if (hpet == NULL) panic_invalid_state();
|
||||
uint64_t *counter = hpet + 0xF0;
|
||||
unsigned __int128 femto = *counter * hpet_period;
|
||||
uint64_t nano = femto / 1000000;
|
||||
return nano;
|
||||
}
|
||||
|
||||
static void pit_irq(void) {
|
||||
Proc *p = scheduled;
|
||||
assert(p);
|
||||
scheduled = p->waits4timer.next;
|
||||
proc_setstate(p, PS_RUNNING);
|
||||
update_goal();
|
||||
if (p && p->waits4timer.goal < uptime_ns()) {
|
||||
scheduled = p->waits4timer.next;
|
||||
proc_setstate(p, PS_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void timer_schedule(Proc *p, uint64_t time) {
|
||||
uint64_t now = uptime_ns();
|
||||
/* to prevent leaking the current uptime, saturate instead of overflowing */
|
||||
time = now + time;
|
||||
if (time < now) {
|
||||
time = ~0;
|
||||
}
|
||||
|
||||
proc_setstate(p, PS_WAITS4TIMER);
|
||||
p->waits4timer.goal = time;
|
||||
|
||||
|
@ -36,7 +54,6 @@ void timer_schedule(Proc *p, uint64_t time) {
|
|||
}
|
||||
p->waits4timer.next = *slot;
|
||||
*slot = p;
|
||||
update_goal();
|
||||
}
|
||||
|
||||
void timer_deschedule(Proc *p) {
|
||||
|
@ -49,7 +66,6 @@ void timer_deschedule(Proc *p) {
|
|||
*slot = p->waits4timer.next;
|
||||
|
||||
proc_setstate(p, PS_RUNNING);
|
||||
update_goal();
|
||||
}
|
||||
|
||||
void timer_init(void) {
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
#include <kernel/arch/generic.h>
|
||||
|
||||
void pit_irq(void);
|
|
@ -23,8 +23,8 @@ void shutdown(void);
|
|||
/** on x86: waits for an IRQ */
|
||||
void cpu_pause(void);
|
||||
|
||||
uint64_t uptime_ms(void);
|
||||
void timer_schedule(Proc *p, uint64_t time);
|
||||
uint64_t uptime_ns(void);
|
||||
void timer_schedule(Proc *p, uint64_t ns);
|
||||
void timer_deschedule(Proc *p);
|
||||
|
||||
// src/arch/i386/sysenter.s
|
||||
|
|
|
@ -347,8 +347,7 @@ long _sys_pipe(hid_t __user user_ends[2], int flags) {
|
|||
}
|
||||
|
||||
void _sys_sleep(long ms) {
|
||||
// TODO no overflow check - can leak current uptime
|
||||
timer_schedule(proc_cur, uptime_ms() + ms);
|
||||
timer_schedule(proc_cur, ms * 1000000);
|
||||
}
|
||||
|
||||
void _sys_filicide(void) {
|
||||
|
|
Loading…
Reference in New Issue