From 31263baf0a8d4683248ce3554e077fdeb3dd2044 Mon Sep 17 00:00:00 2001 From: Andrzej Stalke Date: Tue, 7 Nov 2023 16:06:22 +0100 Subject: [PATCH 1/3] hal: Rename argument of hal_timerSetWakeup - Reason: The old name `when` is misleadig, since it is time to wait and not a fixed moment in time. Changed it to `waitUs`. JIRA: RTOS-530 --- hal/armv7a/imx6ull/timer.c | 9 +++++---- hal/armv7a/zynq7000/timer.c | 2 +- hal/armv7m/imxrt/timer.c | 8 ++++---- hal/armv7m/stm32/l4/timer.c | 2 +- hal/armv8m/nrf/91/timer.c | 2 +- hal/ia32/timer.c | 2 +- hal/riscv64/timer.c | 2 +- hal/sparcv8leon3/gaisler/timer.c | 2 +- hal/timer.h | 2 +- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/hal/armv7a/imx6ull/timer.c b/hal/armv7a/imx6ull/timer.c index b747f08ef..93fa4d64c 100644 --- a/hal/armv7a/imx6ull/timer.c +++ b/hal/armv7a/imx6ull/timer.c @@ -87,15 +87,16 @@ static time_t hal_timerGetCyc(void) } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { spinlock_ctx_t sc; u32 cyc; - if (!when) - ++when; + if (waitUs == 0) { + ++waitUs; + } - cyc = when * 66; + cyc = waitUs * 66; hal_spinlockSet(&timer_common.lock, &sc); *(timer_common.epit1 + epit_lr) = cyc; diff --git a/hal/armv7a/zynq7000/timer.c b/hal/armv7a/zynq7000/timer.c index d96c5fdb7..093575b1c 100644 --- a/hal/armv7a/zynq7000/timer.c +++ b/hal/armv7a/zynq7000/timer.c @@ -90,7 +90,7 @@ static time_t hal_timerGetCyc(void) } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { } diff --git a/hal/armv7m/imxrt/timer.c b/hal/armv7m/imxrt/timer.c index e1f1d0c17..dd24277ff 100644 --- a/hal/armv7m/imxrt/timer.c +++ b/hal/armv7m/imxrt/timer.c @@ -85,17 +85,17 @@ static time_t hal_timerGetCyc(void) } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { spinlock_ctx_t sc; - if (when > timer_common.interval) { - when = timer_common.interval; + if (waitUs > timer_common.interval) { + waitUs = timer_common.interval; } hal_spinlockSet(&timer_common.sp, &sc); /* Modulo handled implicitly */ - *(timer_common.base + gpt_ocr2) = hal_timerUs2Cyc(when) + *(timer_common.base + gpt_cnt); + *(timer_common.base + gpt_ocr2) = hal_timerUs2Cyc(waitUs) + *(timer_common.base + gpt_cnt); *(timer_common.base + gpt_sr) |= 1 << 1; hal_cpuDataMemoryBarrier(); hal_spinlockClear(&timer_common.sp, &sc); diff --git a/hal/armv7m/stm32/l4/timer.c b/hal/armv7m/stm32/l4/timer.c index 602c63dcb..c56564f25 100644 --- a/hal/armv7m/stm32/l4/timer.c +++ b/hal/armv7m/stm32/l4/timer.c @@ -192,7 +192,7 @@ void timer_setAlarm(time_t us) } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { } diff --git a/hal/armv8m/nrf/91/timer.c b/hal/armv8m/nrf/91/timer.c index 2191897c4..5c5908a9d 100644 --- a/hal/armv8m/nrf/91/timer.c +++ b/hal/armv8m/nrf/91/timer.c @@ -68,7 +68,7 @@ static int timer_irqHandler(unsigned int n, cpu_context_t *ctx, void *arg) /* Interface functions */ -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { } diff --git a/hal/ia32/timer.c b/hal/ia32/timer.c index b15f6847d..319e333d3 100644 --- a/hal/ia32/timer.c +++ b/hal/ia32/timer.c @@ -39,7 +39,7 @@ static int timer_irqHandler(unsigned int n, cpu_context_t *ctx, void *arg) return 0; } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { } diff --git a/hal/riscv64/timer.c b/hal/riscv64/timer.c index 9383177ba..d325685eb 100644 --- a/hal/riscv64/timer.c +++ b/hal/riscv64/timer.c @@ -41,7 +41,7 @@ static int timer_irqHandler(unsigned int n, cpu_context_t *ctx, void *arg) } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { } diff --git a/hal/sparcv8leon3/gaisler/timer.c b/hal/sparcv8leon3/gaisler/timer.c index d19fb1499..fb807227b 100644 --- a/hal/sparcv8leon3/gaisler/timer.c +++ b/hal/sparcv8leon3/gaisler/timer.c @@ -98,7 +98,7 @@ time_t hal_timerGetUs(void) } -void hal_timerSetWakeup(u32 when) +void hal_timerSetWakeup(u32 waitUs) { } diff --git a/hal/timer.h b/hal/timer.h index 369cb02ac..c47a1b48a 100644 --- a/hal/timer.h +++ b/hal/timer.h @@ -23,7 +23,7 @@ extern time_t hal_timerGetUs(void); -extern void hal_timerSetWakeup(u32 when); +extern void hal_timerSetWakeup(u32 waitUs); extern int hal_timerRegister(int (*f)(unsigned int, cpu_context_t *, void *), void *data, intr_handler_t *h); From 2a909181224b2c73e6ee678d0303528f4ade5aa0 Mon Sep 17 00:00:00 2001 From: Andrzej Stalke Date: Thu, 26 Oct 2023 17:01:40 +0200 Subject: [PATCH 2/3] hal/ia32: Add support for Local APIC timer - Move code reading and writing to Local APIC to its own functions. - Add detection of Local APIC Timer's frequency. - Make Local APIC timer the default timer for scheduling JIRA: RTOS-530 --- hal/ia32/arch/interrupts.h | 2 + hal/ia32/cpu.c | 26 ++-- hal/ia32/hal.c | 1 - hal/ia32/init.h | 12 ++ hal/ia32/interrupts.c | 8 +- hal/ia32/timer.c | 279 ++++++++++++++++++++++++++++++++----- 6 files changed, 274 insertions(+), 54 deletions(-) diff --git a/hal/ia32/arch/interrupts.h b/hal/ia32/arch/interrupts.h index 51ef93f32..49ca25541 100644 --- a/hal/ia32/arch/interrupts.h +++ b/hal/ia32/arch/interrupts.h @@ -34,6 +34,8 @@ #define IOAPIC_INTPOL (1u << 13) #define IOAPIC_DESTMOD (1u << 11) +#define LAPIC_EOI 0 + /* Interrupt source override polarity flags */ #define MADT_ISO_POLAR_MASK 0x3 #define MADT_ISO_POLAR_BUS 0x0 diff --git a/hal/ia32/cpu.c b/hal/ia32/cpu.c index 5b3f3c0b3..c770cef69 100644 --- a/hal/ia32/cpu.c +++ b/hal/ia32/cpu.c @@ -27,6 +27,8 @@ #include "tlb.h" #include "init.h" +extern void hal_timerInitCore(const unsigned int id); + struct cpu_feature_t { const char *name; s32 eax; @@ -302,7 +304,7 @@ unsigned int hal_cpuGetCount(void) static inline unsigned int _hal_cpuGetID(void) { if (hal_isLapicPresent() == 1) { - return (*(volatile u32 *)(hal_config.localApicAddr + LAPIC_ID_REG)) >> 24; + return _hal_lapicRead(LAPIC_ID_REG) >> 24; } else { return 0; @@ -326,11 +328,9 @@ unsigned int hal_cpuGetID(void) /* Sends IPI to everyone but self */ void cpu_broadcastIPI(unsigned int intr) { - volatile u32 *p; if (hal_isLapicPresent() == 1) { - p = hal_config.localApicAddr + LAPIC_ICR_REG_0_31; - *p = intr | 0xc4000; - while (*p & (1 << 12)) { + _hal_lapicWrite(LAPIC_ICR_REG_0_31, intr | 0xc4000); + while ((_hal_lapicRead(LAPIC_ICR_REG_0_31) & (1 << 12)) != 0) { } } } @@ -338,14 +338,12 @@ void cpu_broadcastIPI(unsigned int intr) void hal_cpuSendIPI(unsigned int cpu, unsigned int intrAndFlags) { - volatile u32 *p, *q; if (hal_isLapicPresent() == 1) { - p = hal_config.localApicAddr + LAPIC_ICR_REG_0_31; - q = hal_config.localApicAddr + LAPIC_ICR_REG_32_63; /* Set destination */ - *q = (cpu & 0xff) << 24; - *p = intrAndFlags & 0xcdfff; - while (*p & (1 << 12)) { + /* TODO: Disable interrupts while we're writing */ + _hal_lapicWrite(LAPIC_ICR_REG_32_63, (cpu & 0xff) << 24); + _hal_lapicWrite(LAPIC_ICR_REG_0_31, intrAndFlags & 0xcdfff); + while ((_hal_lapicRead(LAPIC_ICR_REG_0_31) & (1 << 12)) != 0) { } } } @@ -377,13 +375,11 @@ static void _cpu_gdtInsert(unsigned int idx, u32 base, u32 limit, u32 type) void *_cpu_initCore(void) { - volatile u32 *p; const unsigned int id = hal_cpuGetID(); hal_cpuAtomAdd(&cpu.readyCount, 1); if (hal_isLapicPresent() == 1) { - p = hal_config.localApicAddr + LAPIC_SPUR_IRQ_REG; - *p = (*p | 0x11ffu); + _hal_lapicWrite(LAPIC_SPUR_IRQ_REG, _hal_lapicRead(LAPIC_SPUR_IRQ_REG) | 0x11ffu); } hal_memset(&cpu.tss[id], 0, sizeof(tss_t)); @@ -420,6 +416,7 @@ void *_cpu_initCore(void) /* clang-format on */ hal_tlbInitCore(id); + hal_timerInitCore(id); return (void *)cpu.tss[id].esp0; } @@ -433,6 +430,7 @@ static void _hal_cpuInitCores(void) /* Initialize BSP */ cpu.readyCount = 0; + _hal_timerInit(SYSTICK_INTERVAL); _cpu_initCore(); *(u32 *)(syspage->hs.stack + VADDR_KERNEL - 4) = 0; diff --git a/hal/ia32/hal.c b/hal/ia32/hal.c index fa1848ac8..ff9633754 100644 --- a/hal/ia32/hal.c +++ b/hal/ia32/hal.c @@ -76,7 +76,6 @@ void _hal_init(void) _hal_interruptsInit(); _hal_cpuInit(); - _hal_timerInit(SYSTICK_INTERVAL); _hal_pciInit(); hal_common.started = 0; diff --git a/hal/ia32/init.h b/hal/ia32/init.h index a12adfdc9..9046950a3 100644 --- a/hal/ia32/init.h +++ b/hal/ia32/init.h @@ -186,6 +186,18 @@ static inline int hal_isLapicPresent(void) } +static inline void _hal_lapicWrite(u32 reg, u32 value) +{ + *(volatile u32 *)(hal_config.localApicAddr + reg) = value; +} + + +static inline u32 _hal_lapicRead(u32 reg) +{ + return *(volatile u32 *)(hal_config.localApicAddr + reg); +} + + void _hal_configInit(syspage_t *s); diff --git a/hal/ia32/interrupts.c b/hal/ia32/interrupts.c index f20c155b8..9a104f925 100644 --- a/hal/ia32/interrupts.c +++ b/hal/ia32/interrupts.c @@ -163,14 +163,13 @@ static inline void _hal_ioapicRoundRobin(unsigned int n) static inline void _hal_interrupts_8259EOI(unsigned int n) { - volatile u32 *p = hal_config.localApicAddr + LAPIC_EOI_REG; if ((hal_isLapicPresent() != 0) && (n == TLB_IRQ)) { - *p = 0; + _hal_lapicWrite(LAPIC_EOI_REG, LAPIC_EOI); return; } /* Check for rare case, when we use 8259 PIC with multiple cores and APIC */ if (hal_cpuGetID() != 0) { - *p = 0; + _hal_lapicWrite(LAPIC_EOI_REG, LAPIC_EOI); return; } if (n < 8) { @@ -185,9 +184,8 @@ static inline void _hal_interrupts_8259EOI(unsigned int n) static inline void _hal_interruptsApicEOI(unsigned int n) { - volatile u32 *p = hal_config.localApicAddr + LAPIC_EOI_REG; _hal_ioapicRoundRobin(n); - *p = 0; + _hal_lapicWrite(LAPIC_EOI_REG, LAPIC_EOI); } diff --git a/hal/ia32/timer.c b/hal/ia32/timer.c index 319e333d3..acb8f876b 100644 --- a/hal/ia32/timer.c +++ b/hal/ia32/timer.c @@ -5,9 +5,9 @@ * * System timer driver * - * Copyright 2012, 2016 Phoenix Systems + * Copyright 2012, 2016, 2023 Phoenix Systems * Copyright 2001, 2005-2006 Pawel Pisarczyk - * Author: Pawel Pisarczyk + * Author: Pawel Pisarczyk, Andrzej Stalke * * This file is part of Phoenix-RTOS. * @@ -16,80 +16,291 @@ #include "hal/timer.h" #include "hal/cpu.h" +#include "init.h" #include "hal/interrupts.h" #include "hal/spinlock.h" #include "ia32.h" +#define PIT_FREQUENCY 1193 /* kHz */ + +#define PIT_BCD 0 +#define PIT_CHANNEL_0 (0 << 6) +#define PIT_CHANNEL_1 (1 << 6) +#define PIT_CHANNEL_2 (2 << 6) +#define PIT_ACCESS_BOTH (3 << 4) +#define PIT_OPERATING_ONE_SHOT (0 << 1) +#define PIT_OPERATING_RATE_GEN (2 << 1) + +#define LAPIC_TIMER_ONE_SHOT 0 +#define LAPIC_TIMER_DEFAULT_DIVIDER 3 /* 3 means 8 (1 << 3) */ + struct { intr_handler_t handler; - volatile time_t jiffies; spinlock_t sp; + u32 intervalUs; - u32 interval; + enum { timer_unknown, + timer_pit, + timer_lapic } timerType; + union { + struct { + volatile time_t jiffies; + } pit; + struct { + u32 frequency; + struct { + u64 cycles; /* How many ticks were there */ + u32 wait; /* Wait time (in cycles) of this CPU */ + } local[MAX_CPU_COUNT]; + } lapic; + }; } timer; -static int timer_irqHandler(unsigned int n, cpu_context_t *ctx, void *arg) +int hal_timerRegister(int (*f)(unsigned int, cpu_context_t *, void *), void *data, intr_handler_t *h) +{ + h->f = f; + h->n = SYSTICK_IRQ; + h->data = data; + + return hal_interruptsSetHandler(h); +} + +/* Programmable Interval Timer (Intel 8253/8254) */ + + +static int hal_pitTimerIrqHandler(unsigned int n, cpu_context_t *ctx, void *arg) { (void)n; (void)arg; (void)ctx; - timer.jiffies += timer.interval; + timer.pit.jiffies += timer.intervalUs; return 0; } -void hal_timerSetWakeup(u32 waitUs) + +static inline u16 _hal_pitCalculateDivider(u32 intervalUs) { + u32 tmp; + tmp = intervalUs; + tmp *= PIT_FREQUENCY; + tmp /= 1000; + if (tmp >= 65536) { + tmp = 0; + } + return (u16)tmp; } -time_t hal_timerGetUs(void) +static inline void _hal_pitSetTimer(u16 reloadValue, u8 opMode) { - spinlock_ctx_t sc; - time_t ret; + /* First generator, operation - CE write, work mode 2, binary counting */ + hal_outb(PORT_PIT_COMMAND, PIT_CHANNEL_0 | PIT_ACCESS_BOTH | opMode); + hal_outb(PORT_PIT_DATA_CHANNEL0, (u8)(reloadValue & 0xff)); + hal_outb(PORT_PIT_DATA_CHANNEL0, (u8)(reloadValue >> 8)); +} + + +static inline u16 _hal_pitReadTimer(void) +{ + u16 low, high; + hal_outb(PORT_PIT_COMMAND, PIT_CHANNEL_0); /* Latch command */ + low = hal_inb(PORT_PIT_DATA_CHANNEL0); + high = hal_inb(PORT_PIT_DATA_CHANNEL0); + return (high << 8) | low; +} + + +static void _hal_pitInit(u32 intervalUs) +{ + intervalUs /= hal_cpuGetCount(); + timer.intervalUs = intervalUs; + + _hal_pitSetTimer(_hal_pitCalculateDivider(intervalUs), PIT_OPERATING_RATE_GEN); + + timer.timerType = timer_pit; + + timer.pit.jiffies = 0; + + (void)hal_timerRegister(hal_pitTimerIrqHandler, NULL, &timer.handler); +} + + +/* Local APIC Timer */ + + +static inline void _hal_lapicTimerSetDivider(u8 divider) +{ + /* Divider is a power of 2 */ + if (divider == 0) { + /* Not recommended. It is claimed that it is bugged on some emulators */ + divider = 0xb; + } + else if (divider > 4) { + divider += 3; + } + else { + divider -= 1; + } + _hal_lapicWrite(LAPIC_LVT_TMR_DC_REG, divider); +} + + +static inline void _hal_lapicTimerStart(u32 counter) +{ + _hal_lapicWrite(LAPIC_LVT_TMR_IC_REG, counter); +} + + +static inline void _hal_lapicTimerStop(void) +{ + _hal_lapicWrite(LAPIC_LVT_TMR_IC_REG, 0); +} + +static inline u32 _hal_lapicTimerGetCounter(void) +{ + return _hal_lapicRead(LAPIC_LVT_TMR_CC_REG); +} + + +static inline void _hal_lapicTimerConfigure(u32 mode, u32 mask, u32 vector) +{ + _hal_lapicWrite(LAPIC_LVT_TIMER_REG, (vector & 0xff) | ((mask & 0x1) << 16) | ((mode & 0x3) << 17)); +} + + +static inline u32 _hal_lapicTimerCyc2Us(u64 cycles) +{ + return (u32)(((cycles << LAPIC_TIMER_DEFAULT_DIVIDER) * 1000) / (u64)timer.lapic.frequency); +} + + +static inline u64 _hal_lapicTimerUs2Cyc(u32 us) +{ + return (((u64)us) * (u64)timer.lapic.frequency) / (1000 << LAPIC_TIMER_DEFAULT_DIVIDER); +} + + +static int hal_lapicTimerIrqHandler(unsigned int n, cpu_context_t *ctx, void *arg) +{ + spinlock_ctx_t sc; + const unsigned int id = hal_cpuGetID(); hal_spinlockSet(&timer.sp, &sc); - ret = timer.jiffies; + timer.lapic.local[id].cycles += timer.lapic.local[id].wait; + timer.lapic.local[id].wait = _hal_lapicTimerUs2Cyc(timer.intervalUs); + _hal_lapicTimerStart(timer.lapic.local[id].wait); hal_spinlockClear(&timer.sp, &sc); - - return ret; + return 0; } -int hal_timerRegister(int (*f)(unsigned int, cpu_context_t *, void *), void *data, intr_handler_t *h) +static int _hal_lapicTimerInit(u32 intervalUs) { - h->f = f; - h->n = SYSTICK_IRQ; - h->data = data; + u64 freq; + u32 lapicDelta; + u16 pitDelta; + if (hal_isLapicPresent() == 1) { + lapicDelta = 0xffffffffu; + pitDelta = 0xffff; + _hal_lapicTimerConfigure(LAPIC_TIMER_ONE_SHOT, 0, SYSTICK_IRQ + INTERRUPTS_VECTOR_OFFSET); + _hal_lapicTimerSetDivider(LAPIC_TIMER_DEFAULT_DIVIDER); + _hal_pitSetTimer(pitDelta, PIT_OPERATING_ONE_SHOT); + _hal_lapicTimerStart(lapicDelta); + while (pitDelta > 0x0fff) { /* Wait is around 51.500 ms*/ + pitDelta = _hal_pitReadTimer(); + } + lapicDelta -= _hal_lapicTimerGetCounter(); + _hal_lapicTimerStop(); + pitDelta = 0xffff - pitDelta; /* timePassed = pitDelta / PIT_FREQUENCY */ - return hal_interruptsSetHandler(h); + freq = ((u64)lapicDelta) * ((u64)PIT_FREQUENCY); + freq <<= LAPIC_TIMER_DEFAULT_DIVIDER; + freq /= (u64)pitDelta; /* Frequency in kHz, with current technology it should fit in 32 bit */ + + timer.timerType = timer_lapic; + timer.intervalUs = intervalUs; + + timer.lapic.frequency = freq; + + (void)hal_timerRegister(hal_lapicTimerIrqHandler, NULL, &timer.handler); + return 0; + } + else { + return 1; + } +} + + +void hal_timerInitCore(const unsigned int id) +{ + switch (timer.timerType) { + case timer_lapic: + _hal_lapicTimerConfigure(LAPIC_TIMER_ONE_SHOT, 0, SYSTICK_IRQ + INTERRUPTS_VECTOR_OFFSET); + _hal_lapicTimerSetDivider(LAPIC_TIMER_DEFAULT_DIVIDER); + timer.lapic.local[id].cycles = 0; + timer.lapic.local[id].wait = 1; + _hal_lapicTimerStart(1); + break; + default: + break; + } } -__attribute__((section(".init"))) void _hal_timerInit(u32 interval) +time_t hal_timerGetUs(void) { - unsigned int t; + spinlock_ctx_t sc; + time_t ret; + hal_spinlockSet(&timer.sp, &sc); + switch (timer.timerType) { + case timer_lapic: + ret = _hal_lapicTimerCyc2Us(timer.lapic.local[0].cycles); + break; + case timer_pit: + ret = timer.pit.jiffies; + break; + default: + ret = -1; + break; + } + hal_spinlockClear(&timer.sp, &sc); - interval /= hal_cpuGetCount(); + return ret; +} - timer.interval = interval; - timer.jiffies = 0; - t = (u32)((interval * 1190) / 1000); +void hal_timerSetWakeup(u32 waitUs) +{ + unsigned int id; + spinlock_ctx_t sc; + if (waitUs > timer.intervalUs) { + waitUs = timer.intervalUs; + } - /* First generator, operation - CE write, work mode 2, binary counting */ - hal_outb(PORT_PIT_COMMAND, 0x34); + hal_spinlockSet(&timer.sp, &sc); + switch (timer.timerType) { + case timer_lapic: + id = hal_cpuGetID(); + timer.lapic.local[id].cycles += (timer.lapic.local[id].wait - _hal_lapicTimerGetCounter()); + timer.lapic.local[id].wait = _hal_lapicTimerUs2Cyc(waitUs); + _hal_lapicTimerStart(timer.lapic.local[id].wait); + break; + default: + /* Not supported */ + break; + } + hal_spinlockClear(&timer.sp, &sc); +} - /* Set counter */ - hal_outb(PORT_PIT_DATA_CHANNEL0, (u8)(t & 0xff)); - hal_outb(PORT_PIT_DATA_CHANNEL0, (u8)(t >> 8)); +void _hal_timerInit(u32 intervalUs) +{ + timer.timerType = timer_unknown; hal_spinlockCreate(&timer.sp, "timer"); - timer.handler.f = timer_irqHandler; - timer.handler.n = SYSTICK_IRQ; - timer.handler.data = NULL; - hal_interruptsSetHandler(&timer.handler); - return; + if (_hal_lapicTimerInit(intervalUs) != 0) { + _hal_pitInit(intervalUs); + } } From 1e848668755bac0995b7c6bd488b6321ae7350bb Mon Sep 17 00:00:00 2001 From: Andrzej Stalke Date: Thu, 9 Nov 2023 14:43:21 +0100 Subject: [PATCH 3/3] hal/ia32: Add HPET JIRA: RTOS-530 --- hal/ia32/init.c | 81 +++++++++++++ hal/ia32/init.h | 98 +++++++++++---- hal/ia32/interrupts.c | 2 +- hal/ia32/timer.c | 236 +++++++++++++++++++++++++++--------- include/arch/syspage-ia32.h | 6 +- 5 files changed, 339 insertions(+), 84 deletions(-) diff --git a/hal/ia32/init.c b/hal/ia32/init.c index a0f66b107..b1be13752 100644 --- a/hal/ia32/init.c +++ b/hal/ia32/init.c @@ -138,6 +138,9 @@ static int _hal_acpiInit(hal_config_t *config) if (syspage->hs.fadt != 0) { hal_config.fadt = _hal_configMapObjectBeforeStack(pdir, syspage->hs.fadt, syspage->hs.fadtLength, PGHD_WRITE); } + if (syspage->hs.hpet != 0) { + hal_config.hpet = _hal_configMapObjectBeforeStack(pdir, syspage->hs.hpet, syspage->hs.hpetLength, PGHD_WRITE); + } if (hal_config.madt != NULL) { config->localApicAddr = _hal_configMapDevice(pdir, hal_config.madt->localApicAddr, SIZE_PAGE, PGHD_WRITE); @@ -200,6 +203,83 @@ static inline void _hal_configMemoryInit(void) } +void _hal_gasAllocDevice(const hal_gas_t *gas, hal_gasMapped_t *mgas, size_t size) +{ + addr_t *pdir = (addr_t *)(VADDR_KERNEL + syspage->hs.pdir); + mgas->addressSpaceId = gas->addressSpaceId; + mgas->registerWidth = gas->registerWidth; + mgas->registerOffset = gas->registerOffset; + mgas->accessSize = gas->accessSize; + + switch (gas->addressSpaceId) { + case GAS_ADDRESS_SPACE_ID_MEMORY: + mgas->address = _hal_configMapDevice(pdir, (addr_t)gas->address, size, PGHD_WRITE); + break; + default: + mgas->address = (void *)((u32)gas->address); + break; + } +} + + +int _hal_gasWrite32(hal_gasMapped_t *gas, u32 offset, u32 val) +{ + int ret; + switch (gas->addressSpaceId) { + case GAS_ADDRESS_SPACE_ID_MEMORY: + *(volatile u32 *)(gas->address + offset) = val; + ret = 0; + break; + case GAS_ADDRESS_SPACE_ID_IOPORT: + hal_outl(gas->address + offset, val); + ret = 0; + break; + case GAS_ADDRESS_SPACE_ID_PCI: + /* TODO */ + ret = 1; + break; + case GAS_ADDRESS_SPACE_ID_PCIBAR: + /* TODO */ + ret = 1; + break; + default: + /* Unspecified */ + ret = 1; + break; + } + return ret; +} + + +int _hal_gasRead32(hal_gasMapped_t *gas, u32 offset, u32 *val) +{ + int ret; + switch (gas->addressSpaceId) { + case GAS_ADDRESS_SPACE_ID_MEMORY: + *val = *(volatile u32 *)(gas->address + offset); + ret = 0; + break; + case GAS_ADDRESS_SPACE_ID_IOPORT: + *val = hal_inl(gas->address + offset); + ret = 0; + break; + case GAS_ADDRESS_SPACE_ID_PCI: + /* TODO */ + ret = 1; + break; + case GAS_ADDRESS_SPACE_ID_PCIBAR: + /* TODO */ + ret = 1; + break; + default: + /* Unspecified */ + ret = 1; + break; + } + return ret; +} + + void _hal_configInit(syspage_t *s) { unsigned int ra, rb, rc, rd; @@ -215,6 +295,7 @@ void _hal_configInit(syspage_t *s) hal_config.ptable = NULL; hal_config.madt = NULL; hal_config.fadt = NULL; + hal_config.hpet = NULL; hal_config.devices = MMIO_DEVICES_VIRT_ADDR; hal_config.memMap.count = 0; diff --git a/hal/ia32/init.h b/hal/ia32/init.h index 9046950a3..876c0876b 100644 --- a/hal/ia32/init.h +++ b/hal/ia32/init.h @@ -50,6 +50,42 @@ typedef struct { } __attribute__((packed)) sdt_header_t; +#define GAS_ADDRESS_SPACE_ID_MEMORY 0x0 +#define GAS_ADDRESS_SPACE_ID_IOPORT 0x1 +#define GAS_ADDRESS_SPACE_ID_PCI 0x2 +#define GAS_ADDRESS_SPACE_ID_EMBEDD 0x03 /* EMBEDDED_CONTROLLER */ +#define GAS_ADDRESS_SPACE_ID_SMBUS 0x04 +#define GAS_ADDRESS_SPACE_ID_CMOS 0x05 +#define GAS_ADDRESS_SPACE_ID_PCIBAR 0x06 /* PCI_BAR_TARGET */ +#define GAS_ADDRESS_SPACE_ID_IPMI 0x07 +#define GAS_ADDRESS_SPACE_ID_GPIO 0x08 +#define GAS_ADDRESS_SPACE_ID_GSB 0x09 /* Generic Serial Bus*/ +#define GAS_ADDRESS_SPACE_ID_PCC 0x0A /* Platform Communications Channel */ +#define GAS_ADDRESS_SPACE_ID_PRM 0x0B /* Platform Runtime Mechanism */ + +#define GAS_ACCESS_SIZE_UNDEFINED 0 +#define GAS_ACCESS_SIZE_BYTE 1 +#define GAS_ACCESS_SIZE_WORD 2 +#define GAS_ACCESS_SIZE_DWORD 3 +#define GAS_ACCESS_SIZE_QWORD 4 + +typedef struct { + u8 addressSpaceId; + u8 registerWidth; + u8 registerOffset; + u8 accessSize; + u64 address; +} __attribute__ ((packed)) hal_gas_t; + +typedef struct { + u8 addressSpaceId; + u8 registerWidth; + u8 registerOffset; + u8 accessSize; + void *address; +} hal_gasMapped_t; + + typedef struct { sdt_header_t header; addr_t sdt[]; @@ -80,15 +116,18 @@ typedef struct { addr_t localApicAddr; u32 flags; u8 entries[]; /* It is an array of variable length elements */ -} __attribute__ ((packed)) madt_header_t; +} __attribute__ ((packed)) hal_madtHeader_t; + typedef struct { - u8 addressSpaceId; - u8 registerWidth; - u8 registerOffset; - u8 accessSize; - u64 address; -} __attribute__ ((packed)) generic_address_structure_t; + sdt_header_t header; + u32 eventTimerBlockID; + hal_gas_t baseAddress; + u8 hpetNumber; + u16 minPeriodicClockTick; + u8 pageProtection; +} __attribute__ ((packed)) hal_hpetHeader_t; + typedef struct { sdt_header_t header; @@ -130,25 +169,25 @@ typedef struct { u16 iapcBootArch; /* IAPC_BOOT_ARCH */ u8 reserved2; u32 flags; - generic_address_structure_t resetReg; /* RESET_REG */ - u8 resetValue; /* RESET_VALUE */ - u16 armBootArch; /* ARM_BOOT_ARCH */ + hal_gas_t resetReg; /* RESET_REG */ + u8 resetValue; /* RESET_VALUE */ + u16 armBootArch; /* ARM_BOOT_ARCH */ u8 fadtMinorVersion; - u64 xFirmwareCtrl; /* X_FIRMWARE_CTRL */ - u64 xDsdt; /* X_DSDT */ - generic_address_structure_t xPm1aEvtBlk; /* X_PM1a_EVT_BLK */ - generic_address_structure_t xPm1bEvtBlk; /* X_PM1b_EVT_BLK */ - generic_address_structure_t xPm1aCntBlk; /* X_PM1a_CNT_BLK */ - generic_address_structure_t xPm1bCntBlk; /* X_PM1b_CNT_BLK */ - generic_address_structure_t xPm2CntBlk; /* X_PM2_CNT_BLK */ - generic_address_structure_t xPmTmrBlk; /* X_PM_TMR_BLK */ - generic_address_structure_t xGpe0Blk; /* X_GPE0_BLK */ - generic_address_structure_t xGpe1Blk; /* X_GPE1_BLK */ - generic_address_structure_t sleepControlReg; /* SLEEP_CONTROL_REG */ - generic_address_structure_t sleepStatusReg; /* SLEEP_STATUS_REG */ + u64 xFirmwareCtrl; /* X_FIRMWARE_CTRL */ + u64 xDsdt; /* X_DSDT */ + hal_gas_t xPm1aEvtBlk; /* X_PM1a_EVT_BLK */ + hal_gas_t xPm1bEvtBlk; /* X_PM1b_EVT_BLK */ + hal_gas_t xPm1aCntBlk; /* X_PM1a_CNT_BLK */ + hal_gas_t xPm1bCntBlk; /* X_PM1b_CNT_BLK */ + hal_gas_t xPm2CntBlk; /* X_PM2_CNT_BLK */ + hal_gas_t xPmTmrBlk; /* X_PM_TMR_BLK */ + hal_gas_t xGpe0Blk; /* X_GPE0_BLK */ + hal_gas_t xGpe1Blk; /* X_GPE1_BLK */ + hal_gas_t sleepControlReg; /* SLEEP_CONTROL_REG */ + hal_gas_t sleepStatusReg; /* SLEEP_STATUS_REG */ u64 hypervisorVendorIdentity; -} __attribute__ ((packed)) fadt_header_t; +} __attribute__ ((packed)) hal_fadtHeader_t; typedef struct { @@ -167,8 +206,9 @@ typedef struct { addr_t maxAddr; void *heapStart; addr_t *ptable; - madt_header_t *madt; - fadt_header_t *fadt; + hal_madtHeader_t *madt; + hal_fadtHeader_t *fadt; + hal_hpetHeader_t *hpet; void *devices; /* Address space, where memory mapped devices go */ struct { u32 count; @@ -204,4 +244,12 @@ void _hal_configInit(syspage_t *s); void *_hal_configMapDevice(u32 *pdir, addr_t start, size_t size, int attr); +void _hal_gasAllocDevice(const hal_gas_t *gas, hal_gasMapped_t *mgas, size_t size); + + +int _hal_gasWrite32(hal_gasMapped_t *gas, u32 offset, u32 val); + + +int _hal_gasRead32(hal_gasMapped_t *gas, u32 offset, u32 *val); + #endif diff --git a/hal/ia32/interrupts.c b/hal/ia32/interrupts.c index 9a104f925..e53c848ab 100644 --- a/hal/ia32/interrupts.c +++ b/hal/ia32/interrupts.c @@ -371,7 +371,7 @@ static int _hal_ioapicInit(void) u32 flags; } __attribute__((packed)) *localApic; - madt_header_t *madt = hal_config.madt; + hal_madtHeader_t *madt = hal_config.madt; size_t i; u32 high, low, n; void *ptr; diff --git a/hal/ia32/timer.c b/hal/ia32/timer.c index acb8f876b..e38e3bb63 100644 --- a/hal/ia32/timer.c +++ b/hal/ia32/timer.c @@ -31,29 +31,52 @@ #define PIT_OPERATING_ONE_SHOT (0 << 1) #define PIT_OPERATING_RATE_GEN (2 << 1) -#define LAPIC_TIMER_ONE_SHOT 0 +#define LAPIC_TIMER_ONE_SHOT 0 #define LAPIC_TIMER_DEFAULT_DIVIDER 3 /* 3 means 8 (1 << 3) */ +/* 64 bit registers, access must be aligned + Trying to use exclusive-access mechanisms (ex. lock mov, xchg, etc.) is undefined */ +#define HPET_ID 0x00 +#define HPET_CONFIG 0x10 +#define HPET_IRQ_STATUS 0x20 +#define HPET_COUNTER 0xf0 + +#define HPET_ID_LEGACY_CAPABLE (1u << 15) +#define HPET_LEGACY_TMR1_IRQ 8 +#define HPET_CONFIG_TMR_IRQ_EN (1u << 2) +#define HPET_CONFIG_TMR_PERIODIC (1u << 3) +#define HPET_CONFIG_TMR_CAN_BE_PERIODIC (1u << 4) +#define HPET_CONFIG_TMR_PERIODIC_CAN_SET (1u << 6) +#define HPET_CONFIG_TMR_32BIT_MODE (1u << 8) + +typedef enum { timer_unknown, timer_pit, timer_lapic, timer_hpet } timerType_t; + struct { intr_handler_t handler; spinlock_t sp; u32 intervalUs; - enum { timer_unknown, - timer_pit, - timer_lapic } timerType; + timerType_t schedulerTimerType; + timerType_t timestampTimerType; union { struct { volatile time_t jiffies; } pit; + struct { + volatile u64 cycles; /* How many ticks were there on CPU0 */ + } lapic; + struct { + hal_gasMapped_t addr; + u32 period; + intr_handler_t tmr1; + } hpet; + } timestampTimer; + union { struct { u32 frequency; - struct { - u64 cycles; /* How many ticks were there */ - u32 wait; /* Wait time (in cycles) of this CPU */ - } local[MAX_CPU_COUNT]; + u32 wait[MAX_CPU_COUNT]; /* Wait time (in cycles) of this CPU */ } lapic; - }; + } schedulerTimer; } timer; @@ -75,7 +98,7 @@ static int hal_pitTimerIrqHandler(unsigned int n, cpu_context_t *ctx, void *arg) (void)arg; (void)ctx; - timer.pit.jiffies += timer.intervalUs; + (void)hal_cpuAtomAdd((volatile u32 *)&timer.timestampTimer.pit.jiffies, timer.intervalUs); return 0; } @@ -119,9 +142,10 @@ static void _hal_pitInit(u32 intervalUs) _hal_pitSetTimer(_hal_pitCalculateDivider(intervalUs), PIT_OPERATING_RATE_GEN); - timer.timerType = timer_pit; + timer.timestampTimerType = timer_pit; + timer.schedulerTimerType = timer_pit; - timer.pit.jiffies = 0; + timer.timestampTimer.pit.jiffies = 0; (void)hal_timerRegister(hal_pitTimerIrqHandler, NULL, &timer.handler); } @@ -173,13 +197,13 @@ static inline void _hal_lapicTimerConfigure(u32 mode, u32 mask, u32 vector) static inline u32 _hal_lapicTimerCyc2Us(u64 cycles) { - return (u32)(((cycles << LAPIC_TIMER_DEFAULT_DIVIDER) * 1000) / (u64)timer.lapic.frequency); + return (u32)(((cycles << LAPIC_TIMER_DEFAULT_DIVIDER) * 1000) / (u64)timer.schedulerTimer.lapic.frequency); } static inline u64 _hal_lapicTimerUs2Cyc(u32 us) { - return (((u64)us) * (u64)timer.lapic.frequency) / (1000 << LAPIC_TIMER_DEFAULT_DIVIDER); + return (((u64)us) * (u64)timer.schedulerTimer.lapic.frequency) / (1000 << LAPIC_TIMER_DEFAULT_DIVIDER); } @@ -188,59 +212,150 @@ static int hal_lapicTimerIrqHandler(unsigned int n, cpu_context_t *ctx, void *ar spinlock_ctx_t sc; const unsigned int id = hal_cpuGetID(); hal_spinlockSet(&timer.sp, &sc); - timer.lapic.local[id].cycles += timer.lapic.local[id].wait; - timer.lapic.local[id].wait = _hal_lapicTimerUs2Cyc(timer.intervalUs); - _hal_lapicTimerStart(timer.lapic.local[id].wait); + if ((timer.timestampTimerType == timer_lapic) && (id == 0)) { + timer.timestampTimer.lapic.cycles += timer.schedulerTimer.lapic.wait[id]; + } + timer.schedulerTimer.lapic.wait[id] = _hal_lapicTimerUs2Cyc(timer.intervalUs); + _hal_lapicTimerStart(timer.schedulerTimer.lapic.wait[id]); hal_spinlockClear(&timer.sp, &sc); return 0; } +/* High Precision Event Timers */ + + +static inline u32 _hal_hpetRead(u32 offset) +{ + u32 ret; + (void)_hal_gasRead32(&timer.timestampTimer.hpet.addr, offset, &ret); + return ret; +} + + +static inline void _hal_hpetWrite(u32 offset, u32 val) +{ + (void)_hal_gasWrite32(&timer.timestampTimer.hpet.addr, offset, val); +} + + +/* 0 disables, everything else enables */ +static inline void _hal_hpetEnable(int val) +{ + _hal_hpetWrite(HPET_CONFIG, (_hal_hpetRead(HPET_CONFIG) & 0x3) | (val != 0 ? 1 : 0)); +} + + +static inline u64 _hal_hpetGetCounter(void) +{ + u32 high, low; + do { + high = _hal_hpetRead(HPET_COUNTER + sizeof(u32)); + low = _hal_hpetRead(HPET_COUNTER); + } while (high != _hal_hpetRead(HPET_COUNTER + sizeof(u32))); + return ((u64)high) << 32 | low; +} + + +static inline void _hal_hpetSetCounter(u64 val) +{ + u32 high, low; + low = (u32)val; + high = (u32)(val >> 32); + _hal_hpetWrite(HPET_COUNTER, low); + _hal_hpetWrite(HPET_COUNTER + sizeof(u32), high); +} + + +static time_t _hal_hpetGetUs(void) +{ + u64 ret = _hal_hpetGetCounter(); + ret *= (u64)timer.timestampTimer.hpet.period; + ret /= 1000000000LLU; + return ret; +} + + +static int _hal_hpetInit(void) +{ + if (hal_config.hpet == NULL) { + return -1; + } + _hal_gasAllocDevice(&hal_config.hpet->baseAddress, &timer.timestampTimer.hpet.addr, 0x400); + if (_hal_gasRead32(&timer.timestampTimer.hpet.addr, HPET_ID + sizeof(u32), &timer.timestampTimer.hpet.period) != 0) { + return -1; + } + _hal_hpetSetCounter(0LLU); + timer.timestampTimerType = timer_hpet; + _hal_hpetEnable(1); + return 0; +} + + static int _hal_lapicTimerInit(u32 intervalUs) { - u64 freq; + u64 freq, hpetDelta; + time_t hpetStart, hpetEnd; u32 lapicDelta; u16 pitDelta; - if (hal_isLapicPresent() == 1) { - lapicDelta = 0xffffffffu; - pitDelta = 0xffff; - _hal_lapicTimerConfigure(LAPIC_TIMER_ONE_SHOT, 0, SYSTICK_IRQ + INTERRUPTS_VECTOR_OFFSET); - _hal_lapicTimerSetDivider(LAPIC_TIMER_DEFAULT_DIVIDER); - _hal_pitSetTimer(pitDelta, PIT_OPERATING_ONE_SHOT); - _hal_lapicTimerStart(lapicDelta); - while (pitDelta > 0x0fff) { /* Wait is around 51.500 ms*/ - pitDelta = _hal_pitReadTimer(); - } - lapicDelta -= _hal_lapicTimerGetCounter(); - _hal_lapicTimerStop(); - pitDelta = 0xffff - pitDelta; /* timePassed = pitDelta / PIT_FREQUENCY */ - - freq = ((u64)lapicDelta) * ((u64)PIT_FREQUENCY); - freq <<= LAPIC_TIMER_DEFAULT_DIVIDER; - freq /= (u64)pitDelta; /* Frequency in kHz, with current technology it should fit in 32 bit */ - - timer.timerType = timer_lapic; - timer.intervalUs = intervalUs; - - timer.lapic.frequency = freq; - - (void)hal_timerRegister(hal_lapicTimerIrqHandler, NULL, &timer.handler); - return 0; + if (hal_isLapicPresent() == 0) { + return -1; } - else { - return 1; + _hal_lapicTimerConfigure(LAPIC_TIMER_ONE_SHOT, 0, SYSTICK_IRQ + INTERRUPTS_VECTOR_OFFSET); + _hal_lapicTimerSetDivider(LAPIC_TIMER_DEFAULT_DIVIDER); + lapicDelta = 0xffffffffu; + switch (timer.timestampTimerType) { + case timer_pit: + pitDelta = 0xffff; + _hal_pitSetTimer(pitDelta, PIT_OPERATING_ONE_SHOT); + _hal_lapicTimerStart(lapicDelta); + while (pitDelta > 0x0fff) { /* Wait is around 51.500 ms*/ + pitDelta = _hal_pitReadTimer(); + } + lapicDelta -= _hal_lapicTimerGetCounter(); + _hal_lapicTimerStop(); + pitDelta = 0xffff - pitDelta; /* timePassed = pitDelta / PIT_FREQUENCY */ + + freq = ((u64)lapicDelta) * ((u64)PIT_FREQUENCY); + freq <<= LAPIC_TIMER_DEFAULT_DIVIDER; + freq /= (u64)pitDelta; /* Frequency in kHz, with current technology it should fit in 32 bit */ + timer.timestampTimerType = timer_lapic; + timer.timestampTimer.lapic.cycles = 0; + break; + case timer_hpet: + _hal_pitSetTimer(0, PIT_OPERATING_ONE_SHOT); /* Disable PIT */ + hpetStart = _hal_hpetGetUs(); + _hal_lapicTimerStart(lapicDelta); + do { + hpetEnd = _hal_hpetGetUs(); + } while (hpetEnd - hpetStart < 100000); /* 100ms */ + lapicDelta -= _hal_lapicTimerGetCounter(); + _hal_lapicTimerStop(); + hpetDelta = hpetEnd - hpetStart; + + freq = (((u64)lapicDelta) * 1000) << LAPIC_TIMER_DEFAULT_DIVIDER; + freq /= hpetDelta; + break; + default: + return -1; } + timer.schedulerTimerType = timer_lapic; + timer.intervalUs = intervalUs; + + timer.schedulerTimer.lapic.frequency = freq; + + (void)hal_timerRegister(hal_lapicTimerIrqHandler, NULL, &timer.handler); + return 0; } void hal_timerInitCore(const unsigned int id) { - switch (timer.timerType) { + switch (timer.schedulerTimerType) { case timer_lapic: _hal_lapicTimerConfigure(LAPIC_TIMER_ONE_SHOT, 0, SYSTICK_IRQ + INTERRUPTS_VECTOR_OFFSET); _hal_lapicTimerSetDivider(LAPIC_TIMER_DEFAULT_DIVIDER); - timer.lapic.local[id].cycles = 0; - timer.lapic.local[id].wait = 1; + timer.schedulerTimer.lapic.wait[id] = 1; _hal_lapicTimerStart(1); break; default: @@ -254,12 +369,15 @@ time_t hal_timerGetUs(void) spinlock_ctx_t sc; time_t ret; hal_spinlockSet(&timer.sp, &sc); - switch (timer.timerType) { + switch (timer.timestampTimerType) { case timer_lapic: - ret = _hal_lapicTimerCyc2Us(timer.lapic.local[0].cycles); + ret = _hal_lapicTimerCyc2Us(timer.timestampTimer.lapic.cycles); break; case timer_pit: - ret = timer.pit.jiffies; + ret = timer.timestampTimer.pit.jiffies; + break; + case timer_hpet: + ret = _hal_hpetGetUs(); break; default: ret = -1; @@ -280,12 +398,14 @@ void hal_timerSetWakeup(u32 waitUs) } hal_spinlockSet(&timer.sp, &sc); - switch (timer.timerType) { + switch (timer.schedulerTimerType) { case timer_lapic: id = hal_cpuGetID(); - timer.lapic.local[id].cycles += (timer.lapic.local[id].wait - _hal_lapicTimerGetCounter()); - timer.lapic.local[id].wait = _hal_lapicTimerUs2Cyc(waitUs); - _hal_lapicTimerStart(timer.lapic.local[id].wait); + if ((timer.timestampTimerType == timer_lapic) && (id == 0)) { + timer.timestampTimer.lapic.cycles += (timer.schedulerTimer.lapic.wait[id] - _hal_lapicTimerGetCounter()); + } + timer.schedulerTimer.lapic.wait[id] = _hal_lapicTimerUs2Cyc(waitUs); + _hal_lapicTimerStart(timer.schedulerTimer.lapic.wait[id]); break; default: /* Not supported */ @@ -297,9 +417,13 @@ void hal_timerSetWakeup(u32 waitUs) void _hal_timerInit(u32 intervalUs) { - timer.timerType = timer_unknown; + timer.schedulerTimerType = timer_unknown; + timer.timestampTimerType = timer_pit; + hal_spinlockCreate(&timer.sp, "timer"); + (void)_hal_hpetInit(); + if (_hal_lapicTimerInit(intervalUs) != 0) { _hal_pitInit(intervalUs); } diff --git a/include/arch/syspage-ia32.h b/include/arch/syspage-ia32.h index cc92d0adb..81bade04b 100644 --- a/include/arch/syspage-ia32.h +++ b/include/arch/syspage-ia32.h @@ -39,10 +39,12 @@ typedef struct { unsigned int ebda; unsigned int acpi_version; unsigned int localApicAddr; - unsigned int madt; + unsigned long madt; /* addr_t */ unsigned int madtLength; - unsigned int fadt; + unsigned long fadt; /* addr_t */ unsigned int fadtLength; + unsigned long hpet; /* addr_t */ + unsigned int hpetLength; } __attribute__((packed)) hal_syspage_t;