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.
An IRQ handler should always be ready to respond to any IRQ.
We must remember that hardware can generate IRQs without any interaction
from our code at all. Ignoring IRQs in such cases is obviously not the
right thing to do.
We can now properly initialize all processors without
crashing by sending SMP IPI messages to synchronize memory
between processors.
We now initialize the APs once we have the scheduler running.
This is so that we can process IPI messages from the other
cores.
Also rework interrupt handling a bit so that it's more of a
1:1 mapping. We need to allocate non-sharable interrupts for
IPIs.
This also fixes the occasional hang/crash because all
CPUs now synchronize memory with each other.
The GenericInterruptHandler class will be used to represent
an abstract interrupt handler. The InterruptManagement class will
represent a centralized component to manage interrupts.