timekeeper: redo timekeeping

This commit is contained in:
Matheus
2024-09-29 11:20:40 -03:00
parent bb910d9e20
commit b8acbc5bcd
12 changed files with 280 additions and 96 deletions
+1 -10
View File
@@ -178,8 +178,6 @@ void arch_apic_initap() {
}
}
printf("processor id: %lu (APIC) %lu (ACPI)\n", current_cpu()->id, current_cpu()->acpiid);
isr_t *nmiisr = interrupt_allocate(nmi, NULL, IPL_MAX); // MAX IPL because NMI
__assert(nmiisr);
@@ -225,16 +223,9 @@ void arch_apic_timerinit() {
writelapic(APIC_TIMER_DIVIDE, 11);
void (*uswait)(time_t) = NULL;
if (arch_hpet_exists())
uswait = arch_hpet_waitus;
__assert(uswait);
writelapic(APIC_TIMER_INITIALCOUNT, 0xffffffff);
uswait(50000);
timekeeper_wait_us(50000);
time_t ticksperus = (0xffffffff - readlapic(APIC_TIMER_COUNT)) / 50000;
+78 -52
View File
@@ -27,84 +27,84 @@
#define HPET_CAP_LEGACYCAPABLE (1 << 15)
#define HPET_CAP_FSPERTICK(x) (((x) >> 32) & 0xffffffff)
static volatile uint64_t *hpet;
static time_t ticksperus;
static uint64_t tickspassed;
// in order of preference
#define TYPE_64BIT 0
#define TYPE_LEGACY 1
static int type;
static MUTEX_DEFINE(init_mutex);
static struct {
volatile uint64_t *hpet;
uint64_t tickspassed;
int type;
} hpet_private;
static timekeeper_source_info_t timekeeper_source_info = {
.private = &hpet_private
};
static uint64_t read64(int reg) {
return hpet[reg];
return hpet_private.hpet[reg];
}
static void write64(int reg, uint64_t v) {
hpet[reg] = v;
}
time_t arch_hpet_ticks() {
if (type == TYPE_64BIT) {
return read64(HPET_REG_COUNTER);
} else {
uint64_t ticks;
do {
ticks = __atomic_load_n(&tickspassed, __ATOMIC_SEQ_CST) + (read64(HPET_REG_COUNTER) & 0xffffffff);
} while (ticks < __atomic_load_n(&tickspassed, __ATOMIC_SEQ_CST));
return ticks;
}
}
void arch_hpet_waitticks(time_t ticks) {
uint64_t target = arch_hpet_ticks() + ticks;
while (target > arch_hpet_ticks()) asm volatile("pause");
}
void arch_hpet_waitus(time_t us) {
arch_hpet_waitticks(us * ticksperus);
hpet_private.hpet[reg] = v;
}
static void counterirq(isr_t *isr, context_t *context) {
write64(HPET_REG_CONFIG, 0);
__atomic_add_fetch(&tickspassed, read64(HPET_REG_COUNTER), __ATOMIC_SEQ_CST);
__atomic_add_fetch(&hpet_private.tickspassed, read64(HPET_REG_COUNTER), __ATOMIC_SEQ_CST);
write64(HPET_REG_COUNTER, 0);
write64(HPET_REG_CONFIG, 1 + ((type == TYPE_LEGACY) ? 2 : 0));
write64(HPET_REG_CONFIG, 1 + ((hpet_private.type == TYPE_LEGACY) ? 2 : 0));
}
bool arch_hpet_exists() {
// This is assumed by arch_hpet_init below
return true;
}
time_t arch_hpet_init() {
uacpi_table tbl;
static bool hpet_probe(void) {
bool usable = false;
struct acpi_hpet *table;
uacpi_table tbl;
if (uacpi_table_find_by_signature("HPET", &tbl) != UACPI_STATUS_OK) {
printf("hpet: no timers\n");
return 0;
}
if (uacpi_table_find_by_signature("HPET", &tbl) != UACPI_STATUS_OK)
return false;
table = tbl.ptr;
if (table->address.address_space_id != ACPI_AS_ID_SYS_MEM)
goto leave;
usable = true;
leave:
uacpi_table_unref(&tbl);
return usable;
}
static timekeeper_source_info_t *hpet_init(void) {
uacpi_table tbl;
struct acpi_hpet *table;
MUTEX_ACQUIRE(&init_mutex, false);
if (timekeeper_source_info.ticks_per_us) {
// already initialized by someone else
goto leave;
}
__assert(uacpi_table_find_by_signature("HPET", &tbl) == UACPI_STATUS_OK);
table = tbl.ptr;
printf("hpet%lu: %lu bits %lu comparators\n", table->number,
(table->block_id & ACPI_HPET_COUNT_SIZE_CAP) ? 64 : 32,
(table->block_id >> ACPI_HPET_NUMBER_OF_COMPARATORS_SHIFT) & ACPI_HPET_NUMBER_OF_COMPARATORS_MASK);
__assert(table->address.address_space_id == ACPI_AS_ID_SYS_MEM);
uint64_t page_offset = table->address.address % PAGE_SIZE;
hpet = vmm_map(NULL, (PAGE_SIZE + page_offset), VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, (void *)(table->address.address - page_offset));
__assert(hpet);
hpet = (void *)((uintptr_t)hpet + page_offset);
hpet_private.hpet = vmm_map(NULL, (PAGE_SIZE + page_offset), VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, (void *)(table->address.address - page_offset));
__assert(hpet_private.hpet);
hpet_private.hpet = (void *)((uintptr_t)hpet_private.hpet + page_offset);
uint64_t capabilities = read64(HPET_REG_CAPS);
ticksperus = 1000000000 / HPET_CAP_FSPERTICK(capabilities);
printf("hpet%lu: %lu ticks per us (%lu fs per tick)\n", table->number, ticksperus, HPET_CAP_FSPERTICK(capabilities));
__assert(ticksperus);
timekeeper_source_info.ticks_per_us = 1000000000 / HPET_CAP_FSPERTICK(capabilities);
printf("hpet%lu: %lu ticks per us (%lu fs per tick)\n", table->number, timekeeper_source_info.ticks_per_us, HPET_CAP_FSPERTICK(capabilities));
__assert(timekeeper_source_info.ticks_per_us);
write64(HPET_REG_CONFIG, 0);
write64(HPET_REG_COUNTER, 0);
@@ -120,7 +120,7 @@ time_t arch_hpet_init() {
// TODO FSB mapping
// timer 0 will be used with the timer irq replacing the PIT's as irq0.
if (capabilities & HPET_CAP_LEGACYCAPABLE) {
type = TYPE_LEGACY;
hpet_private.type = TYPE_LEGACY;
arch_ioapic_setirq(0, isr->id & 0xff, current_cpu_id(), false);
@@ -139,11 +139,37 @@ time_t arch_hpet_init() {
write64(HPET_TIMER_COMPARATOR(selectedtimer), 0x80000000);
printf("hpet%d: timer %d on isr %d using %s\n", table->number, selectedtimer, isr->id & 0xff, "legacy replacement");
} else {
type = TYPE_64BIT;
hpet_private.type = TYPE_64BIT;
}
write64(HPET_REG_CONFIG, 1 + ((type == TYPE_LEGACY) ? 2 : 0));
write64(HPET_REG_CONFIG, 1 + ((hpet_private.type == TYPE_LEGACY) ? 2 : 0));
uacpi_table_unref(&tbl);
return ticksperus;
leave:
MUTEX_RELEASE(&init_mutex);
return &timekeeper_source_info;
}
time_t hpet_ticks(timekeeper_source_info_t *) {
if (hpet_private.type == TYPE_64BIT) {
return read64(HPET_REG_COUNTER);
} else {
uint64_t ticks;
do {
ticks = __atomic_load_n(&hpet_private.tickspassed, __ATOMIC_SEQ_CST) + (read64(HPET_REG_COUNTER) & 0xffffffff);
} while (ticks < __atomic_load_n(&hpet_private.tickspassed, __ATOMIC_SEQ_CST));
return ticks;
}
}
TIMEKEEPER_SOURCE(
hpet_source,
.name = "HPET",
.priority = 100,
.probe = hpet_probe,
.init = hpet_init,
.ticks = hpet_ticks,
.flags = TIMEKEEPER_SOURCE_FLAGS_EARLY
);
+9 -2
View File
@@ -26,6 +26,13 @@ SECTIONS
*(.rodata .rodata.*);
} :rodata
. = ALIGN(8);
.timekeeper_sources : {
timekeeper_sources = .;
*(.timekeeper_sources);
timekeeper_sources_end = .;
} :rodata
. = ALIGN(4K);
_rodata_end = .;
_data_start = .;
@@ -33,13 +40,13 @@ SECTIONS
data : {
*(.data .data.*);
} :data
. = ALIGN(8);
.bss : {
*(COMMON)
*(.bss .bss.*)
} :data
. = ALIGN(4K);
_data_end = .;
}
+8 -6
View File
@@ -47,6 +47,7 @@ void kernel_entry() {
cpu_set(&bsp_cpu);
logging_init();
logging_sethook(arch_e9_putc);
arch_gdt_reload();
arch_idt_setup();
arch_idt_reload();
@@ -60,16 +61,17 @@ void kernel_entry() {
cmdline_parse();
acpi_early_init();
arch_apic_init();
timekeeper_early_init(0);
cpu_initstate();
// XXX fall back to another clock source
time_t hpet_ticks_per_us = arch_hpet_init();
__assert(hpet_ticks_per_us);
timekeeper_init(arch_hpet_ticks, hpet_ticks_per_us);
arch_apic_timerinit();
sched_init();
timekeeper_init();
arch_smp_wakeup();
pci_init();
+8 -1
View File
@@ -21,6 +21,8 @@ static void cpuwakeuphalt(struct limine_smp_info *info) {
for (;;) CPU_HALT();
}
static time_t us_offset;
static void cpuwakeup(struct limine_smp_info *info) {
cpu_set((cpu_t *)info->extra_argument);
@@ -31,11 +33,14 @@ static void cpuwakeup(struct limine_smp_info *info) {
arch_mmu_apswitch();
vmm_apinit();
timekeeper_early_init(us_offset);
cpu_initstate();
arch_apic_timerinit();
__atomic_add_fetch(&arch_smp_cpusawake, 1, __ATOMIC_SEQ_CST);
timekeeper_init();
__atomic_add_fetch(&arch_smp_cpusawake, 1, __ATOMIC_SEQ_CST);
sched_ap_entry();
}
@@ -72,6 +77,8 @@ void arch_smp_wakeup() {
void (*wakeupfn)(struct limine_smp_info *) = cmdline_get("nosmp") ? cpuwakeuphalt : cpuwakeup;
us_offset = timespec_us(timekeeper_timefromboot());
// make the other processors jump to cpuwakeup()
for (int i = 0; i < response->cpu_count; ++i) {
// skip the bootstrap processor