From 5e6769036c8c9037d55f2f2488cb6dbdf2b6455c Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 16 Sep 2018 18:56:01 -0700 Subject: [PATCH] APIC timer calibration Now the APIC timer is calibrated against the PIT, and the interval for timer_enable takes a number of microseconds instead of raw ticks and a divisor. --- src/kernel/apic.cpp | 60 +++++++++++++++++++++++++++- src/kernel/apic.h | 14 +++++-- src/kernel/device_manager.cpp | 7 ---- src/kernel/interrupts.cpp | 75 +++++++++++++++++++++-------------- src/kernel/main.cpp | 8 +++- src/kernel/scheduler.cpp | 8 ++-- src/kernel/scheduler.h | 1 + 7 files changed, 126 insertions(+), 47 deletions(-) diff --git a/src/kernel/apic.cpp b/src/kernel/apic.cpp index 192e679..32ef3d6 100644 --- a/src/kernel/apic.cpp +++ b/src/kernel/apic.cpp @@ -1,6 +1,7 @@ #include "kutil/assert.h" #include "apic.h" #include "interrupts.h" +#include "io.h" #include "log.h" #include "page_manager.h" @@ -50,7 +51,46 @@ lapic::lapic(uint32_t *base, isr spurious) : } void -lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat) +lapic::calibrate_timer() +{ + interrupts_disable(); + + log::info(logs::apic, "Calibrating APIC timer..."); + + // Set up PIT sleep + uint8_t command = 0x30; // channel 0, loybyte/highbyte, mode 0 + outb(0x43, command); + + const uint32_t initial = -1u; + enable_timer_internal(isr::isrIgnore0, 1, initial, false); + + const int iterations = 5; + for (int i=0; i> 8) & 0xff); + + + while (pit_count <= pit_33ms) { + outb(0x43, 0); // latch counter values + pit_count = + static_cast(inb(0x40)) | + static_cast(inb(0x40)) << 8; + } + } + + uint32_t remain = stop_timer(); + uint32_t ticks_total = initial - remain; + m_ticks_per_us = ticks_total / (iterations * 33000); + log::info(logs::apic, "APIC timer ticks %d times per nanosecond.", m_ticks_per_us); + + interrupts_enable(); +} + +uint32_t +lapic::enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat) { uint32_t divbits = 0; @@ -76,12 +116,28 @@ lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat) apic_write(m_base, 0x3e0, divbits); reset_timer(count); + return count; +} + +uint32_t +lapic::enable_timer(isr vector, uint64_t interval, bool repeat) +{ + uint64_t ticks = interval * m_ticks_per_us; + + int divisor = 1; + while (ticks > -1u) { + ticks /= 2; + divisor *= 2; + } + + log::debug(logs::apic, "Enabling APIC timer count %ld, divisor %d.", ticks, divisor); + return enable_timer_internal(vector, divisor, static_cast(ticks), repeat); } uint32_t lapic::reset_timer(uint32_t count) { - uint32_t remaining = apic_read(m_base, 0x380); + uint32_t remaining = apic_read(m_base, 0x390); apic_write(m_base, 0x380, count); return remaining; } diff --git a/src/kernel/apic.h b/src/kernel/apic.h index 3360ba3..9f333bd 100644 --- a/src/kernel/apic.h +++ b/src/kernel/apic.h @@ -32,10 +32,10 @@ public: /// Enable interrupts for the LAPIC timer. /// \arg vector Interrupt vector the timer should use - /// \arg divisor The frequency divisor of the bus Hz (power of 2, <= 128) - /// \arg count The count of ticks before an interrupt + /// \arg interval The timer interval, in microseconds /// \arg repeat If false, this timer is one-off, otherwise repeating - void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true); + /// \returns The count of ticks the timer is set for + uint32_t enable_timer(isr vector, uint64_t interval, bool repeat = true); /// Reset the timer countdown. /// \arg count The count of ticks before an interrupt, or 0 to stop the timer @@ -55,6 +55,14 @@ public: void enable(); ///< Enable servicing of interrupts void disable(); ///< Disable (temporarily) servicing of interrupts + + /// Calibrate the timer speed against the PIT + void calibrate_timer(); + +private: + uint32_t enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat); + + uint32_t m_ticks_per_us; }; diff --git a/src/kernel/device_manager.cpp b/src/kernel/device_manager.cpp index a68b8f1..82a465a 100644 --- a/src/kernel/device_manager.cpp +++ b/src/kernel/device_manager.cpp @@ -51,10 +51,6 @@ acpi_table_header::validate(uint32_t expected_type) const void irq2_callback(void *) { - console *cons = console::get(); - cons->set_color(11); - cons->puts("."); - cons->set_color(); } void irq4_callback(void *) @@ -212,8 +208,6 @@ device_manager::load_apic(const acpi_apic *apic) p += length; } - // m_lapic->enable_timer(isr::isrTimer, 128, 3000000); - for (uint8_t i = 0; i < m_ioapics[0]->get_num_gsi(); ++i) { switch (i) { case 2: break; @@ -221,7 +215,6 @@ device_manager::load_apic(const acpi_apic *apic) } } - m_ioapics[0]->dump_redirs(); m_lapic->enable(); } diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index eecd509..e999355 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -1,6 +1,7 @@ #include #include "kutil/memory.h" +#include "apic.h" #include "console.h" #include "cpu.h" #include "debug.h" @@ -12,6 +13,9 @@ #include "scheduler.h" #include "syscall.h" +static const uint16_t PIC1 = 0x20; +static const uint16_t PIC2 = 0xa0; + extern "C" { void _halt(); @@ -58,12 +62,9 @@ get_irq(unsigned vector) static void disable_legacy_pic() { - static const uint16_t PIC1 = 0x20; - static const uint16_t PIC2 = 0xa0; - // Mask all interrupts - outb(0xa1, 0xff); - outb(0x21, 0xff); + outb(PIC2+1, 0xfc); + outb(PIC1+1, 0xff); // Start initialization sequence outb(PIC1, 0x11); io_wait(); @@ -71,7 +72,7 @@ disable_legacy_pic() // Remap into ignore ISRs outb(PIC1+1, static_cast(isr::isrIgnore0)); io_wait(); - outb(PIC2+1, static_cast(isr::isrIgnore0)); io_wait(); + outb(PIC2+1, static_cast(isr::isrIgnore8)); io_wait(); // Tell PICs about each other outb(PIC1+1, 0x04); io_wait(); @@ -110,29 +111,6 @@ isr_handler(addr_t return_rsp, cpu_state regs) console *cons = console::get(); switch (static_cast(regs.interrupt & 0xff)) { - case isr::isrTimer: { - scheduler &s = scheduler::get(); - return_rsp = s.tick(return_rsp); - } - break; - - case isr::isrLINT0: - cons->puts("\nLINT0\n"); - break; - - case isr::isrLINT1: - cons->puts("\nLINT1\n"); - break; - - case isr::isrIgnore0: - case isr::isrIgnore1: - case isr::isrIgnore2: - case isr::isrIgnore3: - case isr::isrIgnore4: - case isr::isrIgnore5: - case isr::isrIgnore6: - case isr::isrIgnore7: - break; case isr::isrGPFault: { cons->set_color(9); @@ -199,6 +177,20 @@ isr_handler(addr_t return_rsp, cpu_state regs) _halt(); break; + case isr::isrTimer: { + scheduler &s = scheduler::get(); + return_rsp = s.tick(return_rsp); + } + break; + + case isr::isrLINT0: + cons->puts("\nLINT0\n"); + break; + + case isr::isrLINT1: + cons->puts("\nLINT1\n"); + break; + case isr::isrAssert: { cons->set_color(); print_regs(regs); @@ -212,6 +204,31 @@ isr_handler(addr_t return_rsp, cpu_state regs) } break; + case isr::isrIgnore0: + case isr::isrIgnore1: + case isr::isrIgnore2: + case isr::isrIgnore3: + case isr::isrIgnore4: + case isr::isrIgnore5: + case isr::isrIgnore6: + case isr::isrIgnore7: + //cons->printf("\nIGNORED: %02x\n", regs.interrupt); + outb(PIC1, 0x20); + break; + + case isr::isrIgnore8: + case isr::isrIgnore9: + case isr::isrIgnoreA: + case isr::isrIgnoreB: + case isr::isrIgnoreC: + case isr::isrIgnoreD: + case isr::isrIgnoreE: + case isr::isrIgnoreF: + //cons->printf("\nIGNORED: %02x\n", regs.interrupt); + outb(PIC1, 0x20); + outb(PIC2, 0x20); + break; + default: cons->set_color(9); cons->printf("\nReceived %02x interrupt:\n", diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 3977adc..010a3ef 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -4,6 +4,7 @@ #include "initrd/initrd.h" #include "kutil/assert.h" #include "kutil/memory.h" +#include "apic.h" #include "block_device.h" #include "console.h" #include "cpu.h" @@ -22,7 +23,6 @@ extern "C" { void kernel_main(popcorn_data *header); - void *__bss_start, *__bss_end; } @@ -40,7 +40,7 @@ init_console() cons->puts(GIT_VERSION " booting...\n"); log::init(cons); - log::enable(logs::apic, log::level::info); + log::enable(logs::apic, log::level::debug); log::enable(logs::device, log::level::info); log::enable(logs::driver, log::level::debug); log::enable(logs::memory, log::level::info); @@ -141,13 +141,17 @@ kernel_main(popcorn_data *header) } */ + devices->get_lapic()->calibrate_timer(); + syscall_enable(); scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic()); + /* for (auto &f : ird.files()) { if (f.executable()) sched->create_process(f.name(), f.data(), f.size()); } + */ sched->start(); } diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index a818ffe..0a71e66 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -13,8 +13,8 @@ #include "kutil/assert.h" scheduler scheduler::s_instance(nullptr); -static const uint32_t quantum = 2000000; -//static const uint32_t quantum = 20000000; +static const uint64_t quantum_micros = 1000000; +//static const uint32_t quantum_micros = 20000000; const int stack_size = 0x1000; const uint64_t rflags_noint = 0x002; @@ -174,7 +174,7 @@ void scheduler::start() { log::info(logs::task, "Starting scheduler."); - m_apic->enable_timer(isr::isrTimer, 128, quantum, false); + m_tick_count = m_apic->enable_timer(isr::isrTimer, quantum_micros, false); } void scheduler::prune(uint64_t now) @@ -274,7 +274,7 @@ scheduler::tick(addr_t rsp0) // TODO: action based on the task using the whole quantum rsp0 = schedule(rsp0); - m_apic->reset_timer(quantum); + m_apic->reset_timer(m_tick_count); return rsp0; } diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index b686754..283916a 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -65,6 +65,7 @@ private: lapic *m_apic; uint32_t m_next_pid; + uint32_t m_tick_count; using process_slab = kutil::slab_allocator; process_slab m_process_allocator;