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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user