aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2023-12-25 14:57:12 -0500
committerIan Moffett <ian@osmora.org>2023-12-25 14:57:12 -0500
commit6f5aba866f01f5690438309629e96812fc099414 (patch)
tree9cfd2a927d2ce676c65fca4dd12f902700cb2817
parent8e559cbb41d696eb119c5daccd33670ebb5207c3 (diff)
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 <ian@osmora.org>
-rw-r--r--sys/arch/amd64/amd64/lapic.c130
-rw-r--r--sys/arch/amd64/amd64/lapic_timer.S40
-rw-r--r--sys/include/arch/amd64/lapic.h2
-rw-r--r--sys/include/arch/amd64/lapicvar.h17
4 files changed, 160 insertions, 29 deletions
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 <machine/cpuid.h>
#include <machine/msr.h>
#include <machine/cpu.h>
+#include <machine/idt.h>
+#include <machine/intr.h>
+#include <vm/vm.h>
+#include <machine/sysvec.h>
+#include <machine/tss.h>
+#include <machine/isa/i8254.h>
#include <sys/cdefs.h>
#include <sys/timer.h>
#include <sys/syslog.h>
@@ -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_ */