From 6f5aba866f01f5690438309629e96812fc099414 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Mon, 25 Dec 2023 14:57:12 -0500 Subject: kernel/amd64: lapic: Add initial LAPIC Timer logic The commit adds the initial Local APIC timer support in Hyra... However, the interrupt service routine is a stub and needs to be completed. Signed-off-by: Ian Moffett --- sys/arch/amd64/amd64/lapic.c | 130 ++++++++++++++++++++++++++++++++----- sys/arch/amd64/amd64/lapic_timer.S | 40 ++++++++++++ sys/include/arch/amd64/lapic.h | 2 + sys/include/arch/amd64/lapicvar.h | 17 ++--- 4 files changed, 160 insertions(+), 29 deletions(-) create mode 100644 sys/arch/amd64/amd64/lapic_timer.S (limited to 'sys') diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c index 0ac2670..52e5501 100644 --- a/sys/arch/amd64/amd64/lapic.c +++ b/sys/arch/amd64/amd64/lapic.c @@ -32,6 +32,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -57,6 +63,8 @@ __KERNEL_META("$Hyra$: lapic.c, Ian Marco Moffett, " static struct timer lapic_timer = { 0 }; +__naked void lapic_tmr_isr(void); + /* * Returns true if LAPIC is supported. * @@ -208,32 +216,109 @@ lapic_set_ldr(struct cpu_info *ci) lapic_writel(LAPIC_LDR, LAPIC_STARTUP_LID); } +/* + * Starts the Local APIC countdown timer... + * + * @mask: True to mask timer. + * @mode: Timer mode. + * @count: Count to start at. + */ +static inline void +lapic_timer_start(bool mask, uint8_t mode, uint32_t count) +{ + uint32_t tmp; + + tmp = (mode << 17) | (mask << 16) | SYSVEC_LAPIC_TIMER; + lapic_writel(LAPIC_LVT_TMR, tmp); + lapic_writel(LAPIC_DCR, 0); + lapic_writel(LAPIC_INIT_CNT, count); +} + +/* + * Start Local APIC timer oneshot with number + * of ticks to count down from. + * + * @mask: If `true', timer will be masked, `count' should be 0. + * @count: Number of ticks. + */ +void +lapic_timer_oneshot(bool mask, uint32_t count) +{ + lapic_timer_start(mask, LVT_TMR_ONESHOT, count); +} + +/* + * Start Local APIC timer oneshot in microseconds. + * + * @us: Microseconds. + */ + +void +lapic_timer_oneshot_us(uint32_t us) +{ + uint64_t ticks; + struct cpu_info *ci = this_cpu(); + + ticks = us * (ci->lapic_tmr_freq / 1000000); + lapic_timer_oneshot(false, ticks); +} + +/* + * Calibrates the Local APIC timer + * + * TODO: Disable interrupts and put them back in + * old state. + * + * XXX: Will unmask IRQs if masked; will restore + * IRQ mask state after. + */ void lapic_timer_init(size_t *freq_out) { - uint32_t ticks_per_10ms; - const uint32_t MAX_SAMPLES = 0xFFFFFFFF; + struct cpu_info *ci; + bool irq_mask = is_intr_mask(); + uint16_t init_tick, final_tick; + size_t total_ticks; + const uint16_t SAMPLES = 0xFFFF; - lapic_writel(LAPIC_DCR, 3); /* Use divider 16 */ - lapic_writel(LAPIC_INIT_CNT, MAX_SAMPLES); /* Set the initial count */ + ci = this_cpu(); - hpet_msleep(10); /* Wait 10ms (10000 usec) */ - lapic_writel(LAPIC_LVT_TMR, LAPIC_LVT_MASK); /* Stop the timer w/ LVT mask bit */ + if (irq_mask) { + __ASMV("sti"); + } - /* Sanity check */ - if (freq_out == NULL) - panic("lapic_timer_init() freq_out NULL\n"); + /* Stop the timer */ + lapic_timer_oneshot(true, 0); + + i8254_set_reload(SAMPLES); + init_tick = i8254_get_count(); + + lapic_writel(LAPIC_INIT_CNT, SAMPLES); + while (lapic_readl(LAPIC_CUR_CNT) != 0); - ticks_per_10ms = MAX_SAMPLES - lapic_readl(LAPIC_CUR_CNT); - *freq_out = ticks_per_10ms; + final_tick = i8254_get_count(); + total_ticks = init_tick - final_tick; + + /* Calculate the frequency */ + CPU_INFO_LOCK(ci); + ci->lapic_tmr_freq = (SAMPLES / total_ticks) * i8254_DIVIDEND; + if (freq_out != NULL) *freq_out = ci->lapic_tmr_freq; + CPU_INFO_UNLOCK(ci); + + /* Stop timer again */ + lapic_timer_oneshot(true, 0); + + if (irq_mask) { + __ASMV("cli"); + } } void lapic_init(void) { struct cpu_info *ci; + union tss_stack tmr_stack; uint64_t tmp; - size_t tmr_freq; if (!lapic_check_support()) { /* @@ -273,13 +358,24 @@ lapic_init(void) /* Register the timer for scheduler usage */ register_timer(TIMER_SCHED, &lapic_timer); - /* Calibrate timer */ - lapic_timer_init(&tmr_freq); - ci->lapic_tmr_freq = tmr_freq; - /* Set the Local APIC ID */ ci->id = lapic_get_id(ci); - BSP_KINFO("BSP Local APIC ID: %d\n", ci->id); + + /* Setup LAPIC Timer TSS stack */ + if (tss_alloc_stack(&tmr_stack, vm_get_page_size()) != 0) { + panic("Failed to allocate LAPIC TMR stack! (1 page of mem)\n"); + } + tss_update_ist(ci, tmr_stack, IST_SCHED); CPU_INFO_UNLOCK(ci); + + /* Calibrate timer */ + lapic_timer_init(NULL); + + /* Setup LAPIC Timer ISR */ + idt_set_desc(SYSVEC_LAPIC_TIMER, IDT_INT_GATE_USER, + (uintptr_t)lapic_tmr_isr, IST_SCHED); + + BSP_KINFO("LAPIC Timer on Interrupt Stack %d (IST_SCHED) with vector 0x%x\n", + IST_SCHED, SYSVEC_LAPIC_TIMER); } diff --git a/sys/arch/amd64/amd64/lapic_timer.S b/sys/arch/amd64/amd64/lapic_timer.S new file mode 100644 index 0000000..bd609f8 --- /dev/null +++ b/sys/arch/amd64/amd64/lapic_timer.S @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +.text + .globl lapic_tmr_isr + +lapic_tmr_isr: + lea stub_msg(%rip), %rdi + call kprintf + cli + hlt + +.section .rodata +stub_msg: .ascii "**LAPIC TIMER ISR IS A STUB; HALTING**\n\0" diff --git a/sys/include/arch/amd64/lapic.h b/sys/include/arch/amd64/lapic.h index 1e7caf6..1ef41fb 100644 --- a/sys/include/arch/amd64/lapic.h +++ b/sys/include/arch/amd64/lapic.h @@ -36,6 +36,8 @@ #define LAPIC_TMR_PERIODIC 0x01 void lapic_timer_init(size_t *freq_out); +void lapic_timer_oneshot(bool mask, uint32_t count); +void lapic_timer_oneshot_us(uint32_t us); void lapic_init(void); #endif /* !_AMD64_LAPIC_H_ */ diff --git a/sys/include/arch/amd64/lapicvar.h b/sys/include/arch/amd64/lapicvar.h index aad9fa8..43c89a6 100644 --- a/sys/include/arch/amd64/lapicvar.h +++ b/sys/include/arch/amd64/lapicvar.h @@ -90,18 +90,11 @@ /* LVT bits */ #define LAPIC_LVT_MASK __BIT(16) +#define LVT_TMR_ONESHOT 0x00 +#define LVT_TMR_PERIODIC 0x01 +#define LVT_TMR_TSC_DEADLINE 0x02 -/* - * Local APIC Interrupt stack [IST VALUE]. - * - * This value should be non-zero and reserved - * for only 1 interrupt vector to prevent clobbering - * of the interrupt stacks. - * - * XXX TODO: The value is currently 0, however, this needs - * to be updated to a non-zero value as soon as - * possible. - */ -#define LAPIC_TMR_INTSTACK 0 +/* LAPIC timer interrupt stack size in bytes */ +#define LAPIC_TMR_STACKSZ 4096 #endif /* !_AMD64_LAPICVAR_H_ */ -- cgit v1.2.3