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.
This commit is contained in:
Justin C. Miller
2018-09-16 18:56:01 -07:00
parent 482b9f50fc
commit 5e6769036c
7 changed files with 126 additions and 47 deletions

View File

@@ -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<iterations; ++i) {
const uint16_t pit_33ms = 39375;
uint16_t pit_count = pit_33ms;
outb(0x40, pit_count & 0xff);
io_wait();
outb(0x40, (pit_count >> 8) & 0xff);
while (pit_count <= pit_33ms) {
outb(0x43, 0); // latch counter values
pit_count =
static_cast<uint16_t>(inb(0x40)) |
static_cast<uint16_t>(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<uint32_t>(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;
}

View File

@@ -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;
};

View File

@@ -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();
}

View File

@@ -1,6 +1,7 @@
#include <stdint.h>
#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<uint8_t>(isr::isrIgnore0)); io_wait();
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
outb(PIC2+1, static_cast<uint8_t>(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<isr>(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",

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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>;
process_slab m_process_allocator;