From d9731ff54cbbba9def423cf6bb531cceaaa06a8c Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Mon, 19 Oct 2020 02:54:51 -0400 Subject: [PATCH] apic: add apic timer Add APIC LVT timer Signed-off-by: Daniele Ahmed --- arch/x86/apic.c | 58 +++++++++++++++++++++++++++++++++++++++++ arch/x86/entry.S | 1 + arch/x86/traps.c | 4 +++ common/setup.c | 3 ++- include/arch/x86/apic.h | 18 +++++++++---- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/arch/x86/apic.c b/arch/x86/apic.c index abf3d3a3..26282883 100644 --- a/arch/x86/apic.c +++ b/arch/x86/apic.c @@ -24,12 +24,16 @@ */ #include #include +#include #include #include #include #include +#include static apic_mode_t apic_mode = APIC_MODE_UNKNOWN; +static volatile uint64_t ticks = 0; +static bool apic_timer_enabled; static const char *apic_mode_names[] = { /* clang-format off */ @@ -155,3 +159,57 @@ void init_apic(unsigned int cpu, apic_mode_t mode) { spiv.apic_enable = 1; apic_write(APIC_SPIV, spiv.reg); } + +void init_apic_timer(void) { + ASSERT(apic_get_mode() >= APIC_MODE_XAPIC); + uint32_t min_ticks = _U32(-1); + int i; + + apic_lvt_timer_t timer; + printk("Initializing local APIC timer\n"); + + /* Spend 1s calibrating the timer, 10 iterations of 100ms each */ + for (i = 0; i < 10; ++i) { + /* Set the counter to the max value (0xFFFFFFFF) */ + apic_write(APIC_TMR_DCR, APIC_TIMER_DIVIDE_BY_16); + apic_write(APIC_TMR_ICR, _U32(-1)); + + /* One shot mode to see how many ticks over 100ms */ + timer.timer_mode = APIC_LVT_TIMER_ONE_SHOT; + apic_write(APIC_LVT_TIMER, timer.reg); + + /* Sleep for 100ms to calibrate, count the ticks */ + pit_sleep(100); + + /* Calibrate */ + uint32_t elapsed_ticks = (_U32(-1) - apic_read(APIC_TMR_CCR)) / 100; + min_ticks = min(min_ticks, elapsed_ticks); + } + + /* Interrupt every min_ticks ticks */ + apic_write(APIC_TMR_DCR, APIC_TIMER_DIVIDE_BY_16); + apic_write(APIC_TMR_ICR, min_ticks); + + /* Switch to periodic mode */ + timer.vector = APIC_TIMER_IRQ_OFFSET; + timer.timer_mode = APIC_LVT_TIMER_PERIODIC; + apic_write(APIC_LVT_TIMER, timer.reg); + + apic_timer_enabled = true; + pit_disable(); +} + +bool is_apic_timer_enabled(void) { return apic_timer_enabled; } + +void apic_timer_interrupt_handler(void) { + ++ticks; + apic_EOI(); +} + +void apic_timer_sleep(uint64_t ms) { + BUG_ON(!is_apic_timer_enabled()); + uint64_t end = ticks + ms; + while (ticks < end) { + cpu_relax(); + } +} diff --git a/arch/x86/entry.S b/arch/x86/entry.S index 6b484a6c..dcdbc14c 100644 --- a/arch/x86/entry.S +++ b/arch/x86/entry.S @@ -97,6 +97,7 @@ END_FUNC(handle_exception) interrupt_handler pit pit_interrupt_handler interrupt_handler uart uart_interrupt_handler interrupt_handler keyboard keyboard_interrupt_handler +interrupt_handler apic_timer apic_timer_interrupt_handler ENTRY(usermode_call_asm) /* FIXME: Add 32-bit support */ diff --git a/arch/x86/traps.c b/arch/x86/traps.c index 9abaf677..5d1cbbde 100644 --- a/arch/x86/traps.c +++ b/arch/x86/traps.c @@ -23,6 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -42,6 +43,7 @@ extern void asm_interrupt_handler_uart(void); extern void asm_interrupt_handler_pit(void); extern void asm_interrupt_handler_keyboard(void); +extern void asm_interrupt_handler_apic_timer(void); static void ret2kern_handler(void) { /* clang-format off */ @@ -164,6 +166,8 @@ void init_traps(unsigned int cpu) { _ul(asm_interrupt_handler_pit), GATE_DPL0, GATE_PRESENT, 0); set_intr_gate(&percpu->idt[KEYBOARD_PORT1_IRQ0_OFFSET], __KERN_CS, _ul(asm_interrupt_handler_keyboard), GATE_DPL0, GATE_PRESENT, 0); + set_intr_gate(&percpu->idt[APIC_TIMER_IRQ_OFFSET], __KERN_CS, + _ul(asm_interrupt_handler_apic_timer), GATE_DPL0, GATE_PRESENT, 0); barrier(); lidt(&percpu->idt_ptr); diff --git a/common/setup.c b/common/setup.c index 6d646d63..e13f465a 100644 --- a/common/setup.c +++ b/common/setup.c @@ -233,8 +233,9 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic, /* Initialize console input */ uart_input_init(get_bsp_cpu_id()); - /* Initialize Programmable Interrupt Timer */ + /* Initialize timers */ init_pit(get_bsp_cpu_id()); + init_apic_timer(); /* Initialize keyboard */ init_keyboard(get_bsp_cpu_id()); diff --git a/include/arch/x86/apic.h b/include/arch/x86/apic.h index 57afbae3..20119ea8 100644 --- a/include/arch/x86/apic.h +++ b/include/arch/x86/apic.h @@ -25,10 +25,14 @@ #ifndef KTF_APIC_H #define KTF_APIC_H +#include #include #include #include +#define APIC_IRQ_BASE PIC_IRQ_END_OFFSET +#define APIC_TIMER_IRQ_OFFSET (APIC_IRQ_BASE + 0x00) + #define MSR_X2APIC_REGS 0x800U #ifndef __ASSEMBLY__ @@ -83,7 +87,7 @@ enum xapic_regs { APIC_LVT_LINT1 = 0x360, /* LVT LINT1 Register */ APIC_LVT_ERROR = 0x370, /* LVT Error Register */ APIC_TMR_ICR = 0x380, /* Timer Initial Count Register */ - APIC_TMR_CCR = 0x380, /* Timer Current Count Register */ + APIC_TMR_CCR = 0x390, /* Timer Current Count Register */ APIC_TMR_DCR = 0x3e0, /* Timer Divide Configuration Register */ @@ -304,12 +308,12 @@ typedef union apic_esr apic_esr_t; enum apic_timer_divide { APIC_TIMER_DIVIDE_BY_2 = 0x00, - APIC_TIMER_DIVIDE_BY_4 = 0x08, + APIC_TIMER_DIVIDE_BY_4 = 0x01, APIC_TIMER_DIVIDE_BY_8 = 0x02, - APIC_TIMER_DIVIDE_BY_16 = 0x0a, - APIC_TIMER_DIVIDE_BY_32 = 0x01, + APIC_TIMER_DIVIDE_BY_16 = 0x03, + APIC_TIMER_DIVIDE_BY_32 = 0x08, APIC_TIMER_DIVIDE_BY_64 = 0x09, - APIC_TIMER_DIVIDE_BY_128 = 0x03, + APIC_TIMER_DIVIDE_BY_128 = 0x0a, APIC_TIMER_DIVIDE_BY_1 = 0x0b, }; typedef enum apic_timer_divide apic_timer_divide_t; @@ -457,6 +461,10 @@ extern void init_apic(unsigned int cpu, apic_mode_t mode); extern apic_icr_t apic_icr_read(void); extern void apic_icr_write(const apic_icr_t *icr); +extern void init_apic_timer(void); +extern void apic_timer_sleep(uint64_t ms); +extern bool is_apic_timer_enabled(void); + /* Static declarations */ static inline xapic_regs_t xapic_reg(x2apic_regs_t reg) {