mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-11 18:20:43 +09:00
Kernel: Set up and calibrate APIC timer, and enable timer on all CPUs
This enables the APIC timer on all CPUs, which means Scheduler::timer_tick is now called on all CPUs independently. We still don't do anything on the APs as it instantly crashes due to a number of other problems.
This commit is contained in:
parent
9d347352a1
commit
fe615e601a
Notes:
sideshowbarker
2024-07-19 01:43:30 +09:00
Author: https://github.com/tomuta
Commit: fe615e601a
Pull-request: https://github.com/SerenityOS/serenity/pull/3850
14 changed files with 478 additions and 70 deletions
|
@ -36,6 +36,7 @@
|
|||
#include <Kernel/Interrupts/APIC.h>
|
||||
#include <Kernel/Interrupts/SpuriousInterruptHandler.h>
|
||||
#include <Kernel/Thread.h>
|
||||
#include <Kernel/Time/APICTimer.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
#include <Kernel/VM/PageDirectory.h>
|
||||
#include <Kernel/VM/TypedMapping.h>
|
||||
|
@ -43,6 +44,7 @@
|
|||
//#define APIC_DEBUG
|
||||
//#define APIC_SMP_DEBUG
|
||||
|
||||
#define IRQ_APIC_TIMER (0xfc - IRQ_VECTOR_BASE)
|
||||
#define IRQ_APIC_IPI (0xfd - IRQ_VECTOR_BASE)
|
||||
#define IRQ_APIC_ERR (0xfe - IRQ_VECTOR_BASE)
|
||||
#define IRQ_APIC_SPURIOUS (0xff - IRQ_VECTOR_BASE)
|
||||
|
@ -66,6 +68,9 @@
|
|||
#define APIC_REG_LVT_LINT0 0x350
|
||||
#define APIC_REG_LVT_LINT1 0x360
|
||||
#define APIC_REG_LVT_ERR 0x370
|
||||
#define APIC_REG_TIMER_INITIAL_COUNT 0x380
|
||||
#define APIC_REG_TIMER_CURRENT_COUNT 0x390
|
||||
#define APIC_REG_TIMER_CONFIGURATION 0x3e0
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
@ -197,9 +202,13 @@ void APIC::write_icr(const ICRReg& icr)
|
|||
write_register(APIC_REG_ICR_LOW, icr.low());
|
||||
}
|
||||
|
||||
#define APIC_LVT_TIMER_ONESHOT 0
|
||||
#define APIC_LVT_TIMER_PERIODIC (1 << 17)
|
||||
#define APIC_LVT_TIMER_TSCDEADLINE (1 << 18)
|
||||
|
||||
#define APIC_LVT_MASKED (1 << 16)
|
||||
#define APIC_LVT_TRIGGER_LEVEL (1 << 14)
|
||||
#define APIC_LVT(iv, dm) ((iv & 0xff) | ((dm & 0x7) << 8))
|
||||
#define APIC_LVT(iv, dm) (((iv)&0xff) | (((dm)&0x7) << 8))
|
||||
|
||||
extern "C" void apic_ap_start(void);
|
||||
extern "C" u16 apic_ap_start_size;
|
||||
|
@ -519,6 +528,83 @@ void APIC::send_ipi(u32 cpu)
|
|||
write_icr(ICRReg(IRQ_APIC_IPI + IRQ_VECTOR_BASE, ICRReg::Fixed, ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::NoShorthand, 1u << cpu));
|
||||
}
|
||||
|
||||
APICTimer* APIC::initialize_timers(HardwareTimerBase& calibration_timer)
|
||||
{
|
||||
if (!m_apic_base)
|
||||
return nullptr;
|
||||
|
||||
// We should only initialize and calibrate the APIC timer once on the BSP!
|
||||
ASSERT(Processor::current().id() == 0);
|
||||
ASSERT(!m_apic_timer);
|
||||
|
||||
m_apic_timer = APICTimer::initialize(IRQ_APIC_TIMER, calibration_timer);
|
||||
return m_apic_timer;
|
||||
}
|
||||
|
||||
void APIC::setup_local_timer(u32 ticks, TimerMode timer_mode, bool enable)
|
||||
{
|
||||
u32 flags = 0;
|
||||
switch (timer_mode) {
|
||||
case TimerMode::OneShot:
|
||||
flags |= APIC_LVT_TIMER_ONESHOT;
|
||||
break;
|
||||
case TimerMode::Periodic:
|
||||
flags |= APIC_LVT_TIMER_PERIODIC;
|
||||
break;
|
||||
case TimerMode::TSCDeadline:
|
||||
flags |= APIC_LVT_TIMER_TSCDEADLINE;
|
||||
break;
|
||||
}
|
||||
if (!enable)
|
||||
flags |= APIC_LVT_MASKED;
|
||||
write_register(APIC_REG_LVT_TIMER, APIC_LVT(IRQ_APIC_TIMER + IRQ_VECTOR_BASE, 0) | flags);
|
||||
|
||||
u32 config = read_register(APIC_REG_TIMER_CONFIGURATION);
|
||||
config &= ~0xf; // clear divisor (bits 0-3)
|
||||
switch (get_timer_divisor()) {
|
||||
case 1:
|
||||
config |= (1 << 3) | 3;
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
case 4:
|
||||
config |= 1;
|
||||
break;
|
||||
case 8:
|
||||
config |= 2;
|
||||
break;
|
||||
case 16:
|
||||
config |= 3;
|
||||
break;
|
||||
case 32:
|
||||
config |= (1 << 3);
|
||||
break;
|
||||
case 64:
|
||||
config |= (1 << 3) | 1;
|
||||
break;
|
||||
case 128:
|
||||
config |= (1 << 3) | 2;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
config |= 3; // divide by 16
|
||||
write_register(APIC_REG_TIMER_CONFIGURATION, config);
|
||||
|
||||
if (timer_mode == TimerMode::Periodic)
|
||||
write_register(APIC_REG_TIMER_INITIAL_COUNT, ticks / get_timer_divisor());
|
||||
}
|
||||
|
||||
u32 APIC::get_timer_current_count()
|
||||
{
|
||||
return read_register(APIC_REG_TIMER_CURRENT_COUNT);
|
||||
}
|
||||
|
||||
u32 APIC::get_timer_divisor()
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
void APICIPIInterruptHandler::handle_interrupt(const RegisterState&)
|
||||
{
|
||||
#ifdef APIC_SMP_DEBUG
|
||||
|
@ -546,4 +632,10 @@ bool APICErrInterruptHandler::eoi()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool HardwareTimer<GenericInterruptHandler>::eoi()
|
||||
{
|
||||
APIC::the().eoi();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue