kernel/amd64: add HPET support, slightly rework time handling

This commit is contained in:
dzwdz 2023-09-16 20:48:28 +02:00
parent 458978e7b8
commit 4ade8ff9e6
9 changed files with 119 additions and 25 deletions

2
configure vendored
View File

@ -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 $@'
])

View File

@ -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);

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,4 @@
#pragma once
void acpi_parse(const void *rsdp);
void hpet_init(void *base);

View File

@ -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();

View File

@ -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) {

View File

@ -1,4 +0,0 @@
#pragma once
#include <kernel/arch/generic.h>
void pit_irq(void);

View File

@ -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

View File

@ -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) {