181 lines
4.3 KiB
C
181 lines
4.3 KiB
C
#include <lcrash/irq.h>
|
|
|
|
#include <lcrash/util.h>
|
|
#include <lcrash/debug/debug.h>
|
|
|
|
#define PICMCMD 0x0020
|
|
#define PICMDAT 0x0021
|
|
#define PICSCMD 0x00A0
|
|
#define PICSDAT 0x00A1
|
|
|
|
struct IrqHandlerGate {
|
|
/// Gate number
|
|
u8 GateNumber;
|
|
|
|
/// Hooks
|
|
struct IrqHandlerHook* Hooks;
|
|
};
|
|
|
|
struct [[gnu::packed]] IrqHandlerDescriptor {
|
|
u16 Offset0;
|
|
u16 Segment;
|
|
u16 Flags;
|
|
u16 Offset1;
|
|
u32 Offset2;
|
|
u32 Reserved;
|
|
};
|
|
|
|
struct [[gnu::packed]] IrqHandlerIDTR {
|
|
u16 Length;
|
|
struct IrqHandlerDescriptor* Offset;
|
|
};
|
|
|
|
struct IrqHandlerInterruptFrame {
|
|
u64 PrevInstructionAddress;
|
|
u64 PrevInstructionSegment;
|
|
u64 PrevFlags;
|
|
u64 PrevStackAddress;
|
|
u64 PrevStackSegment;
|
|
};
|
|
|
|
struct IrqHandlerGate IrqHandlerGates[256] = {};
|
|
|
|
[[gnu::aligned(16)]]
|
|
struct IrqHandlerDescriptor IrqHandlerDescriptors[256] = {};
|
|
|
|
/// Check for spurious IRQs
|
|
bool IrqHandlerCheckRealIRQ(u64 Interrupt) {
|
|
if (Interrupt == 0x27) {
|
|
PortWrite8(PICMCMD, 0x0B); // RDISR
|
|
if (PortRead8(PICMDAT) & 0x80) return false;
|
|
}
|
|
|
|
if (Interrupt >= 0x28 && Interrupt <= 0x2f) {
|
|
PortWrite8(PICSCMD, 0x0B); // RDISR
|
|
if (PortRead8(PICSDAT) & 0x80) {
|
|
PortWrite8(PICMCMD, 0x20);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Send an EOI to the PIC
|
|
void IrqHandlerSendEOI(u64 Interrupt) {
|
|
if (Interrupt >= 0x20 && Interrupt < 0x30) {
|
|
if (Interrupt >= 0x28) PortWrite8(PICSCMD, 0x20); // EOI
|
|
PortWrite8(PICMCMD, 0x20); // EOI
|
|
}
|
|
}
|
|
|
|
/// Handle an interrupt
|
|
[[gnu::target("general-regs-only")]]
|
|
void IrqHandlerHandleInterrupt(struct IrqHandlerInterruptFrame* Frame, u64 Interrupt) {
|
|
// Panic("Got interrupt");
|
|
|
|
IrqHandlerSendEOI(Interrupt);
|
|
|
|
return;
|
|
}
|
|
|
|
/// Handle an exception
|
|
[[gnu::target("general-regs-only")]]
|
|
void IrqHandlerHandleException(struct IrqHandlerInterruptFrame* Frame, u64 ErrorCode, u64 Interrupt) {
|
|
//Panic("Got exception");
|
|
|
|
IrqHandlerSendEOI(Interrupt);
|
|
|
|
return;
|
|
}
|
|
|
|
s32 IrqHandlerInitialize() {
|
|
extern int IrqHandlerISRTable;
|
|
void* ISRTable = &IrqHandlerISRTable;
|
|
|
|
// Table stating if an interrupt is an error or not
|
|
bool Error[256] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 0, 1, 1, 1, 1, 1, 0,
|
|
0, 1, 0, 0, 0, 1, 0, 0,
|
|
0, 0, 0, 0, 0, 1, 1, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
// Fill IDT
|
|
for (int i = 0; i < 256; i++) {
|
|
u64 Offset = (u64)ISRTable;
|
|
|
|
IrqHandlerDescriptors[i].Segment = 0x10; // TODO: This shouldnt be hardcoded
|
|
IrqHandlerDescriptors[i].Flags = Error[i] ? 0x8F00 : 0x8E00;
|
|
IrqHandlerDescriptors[i].Offset0 = Offset & 0xffff;
|
|
IrqHandlerDescriptors[i].Offset1 = (Offset >> 16) & 0xffff;
|
|
IrqHandlerDescriptors[i].Offset2 = Offset >> 32 & 0xffffffff;
|
|
|
|
ISRTable += 0x20;
|
|
}
|
|
|
|
// Load the IDT
|
|
struct IrqHandlerIDTR IDTR;
|
|
IDTR.Offset = IrqHandlerDescriptors;
|
|
IDTR.Length = 256 * 16 - 1;
|
|
asm ("lidt %0" :: "m" (IDTR));
|
|
|
|
// Drop to assembly and do a long jump to get our IRQ's working
|
|
asm ("call IrqHandlerApplyStateChange");
|
|
|
|
// TODO: Disable APIC until APIC support is added
|
|
|
|
// Reconfigure legacy PIC (TODO: Switch to APIC)
|
|
PortWrite8(PICMCMD, 0x11); // Initialize / cascade
|
|
PortWrite8(PICSCMD, 0x11);
|
|
PortWrite8(PICMDAT, 0x20); // Map master PIC IRQs to 0x20-0x27
|
|
PortWrite8(PICSDAT, 0x28); // Map slave PIC IRQS to 0x28-0x2f
|
|
PortWrite8(PICMDAT, 0x04); // Slave at IRQ2
|
|
PortWrite8(PICSDAT, 0x02); // Slave ID 2
|
|
PortWrite8(PICMDAT, 0x01); // Switch to 8086 mode
|
|
PortWrite8(PICSDAT, 0x01);
|
|
PortWrite8(PICMDAT, 0x01); // Respect all external interrupts except the PIC timer because its fucking stupid
|
|
PortWrite8(PICSDAT, 0x00);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void IrqHandlerEnableInterrupts() { asm ("sti"); }
|
|
void IrqHandlerDisableInterrupts() { asm ("cli"); }
|
|
|
|
s32 IrqHandlerAttach(u8 Gate, struct IrqHandlerHook* Hook) {
|
|
return -1;
|
|
}
|
|
|
|
s32 IrqHandlerDetach(struct IrqHandlerHook* Hook) {
|
|
return -1;
|
|
}
|