447 lines
14 KiB
C
447 lines
14 KiB
C
//----------------------------------------------------------------------------//
|
|
// OS on Kaleid //
|
|
// //
|
|
// Desc: Interrupt related functions //
|
|
// //
|
|
// //
|
|
// Copyright © 2018-2021 The OS/K Team //
|
|
// //
|
|
// This file is part of OS/K. //
|
|
// //
|
|
// OS/K is free software: you can redistribute it and/or modify //
|
|
// it under the terms of the GNU General Public License as published by //
|
|
// the Free Software Foundation, either version 3 of the License, or //
|
|
// any later version. //
|
|
// //
|
|
// OS/K is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY//without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU General Public License for more details. //
|
|
// //
|
|
// You should have received a copy of the GNU General Public License //
|
|
// along with OS/K. If not, see <https://www.gnu.org/licenses/>. //
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#include <libbuf.h>
|
|
#include <init/boot.h>
|
|
#include <ke/idt.h>
|
|
#include <io/vga.h>
|
|
#include <io/spkr.h>
|
|
#include <ke/time.h>
|
|
|
|
IdtEntry_t idt[256] = { 0 };
|
|
IdtPtr_t _KeIdtPtr;
|
|
bool KeIdtIsInitialized = 0;
|
|
ulong KeSpuriousCount = 0;
|
|
|
|
static ISRList_t isrList = { 0 };
|
|
|
|
static char *ExceptionsChar[32] = {
|
|
"Divide Error Fault",
|
|
"Debug Exception Trap",
|
|
"Non-maskable Interrupt",
|
|
"Breakpoint Trap",
|
|
"Overflow Trap",
|
|
"Bound Range Exceeded Fault",
|
|
"Invalid Opcode Fault",
|
|
"Device Not Available or No Math Coprocessor Fault",
|
|
"Double Fault Abort",
|
|
"Coprocessor Segment Overrun Fault (Legacy)",
|
|
"Invalid TSS Fault",
|
|
"Segment Not Present Fault",
|
|
"Stack Segment fault",
|
|
"General Protection Fault",
|
|
"Page Fault",
|
|
"Intel Reserved",
|
|
"x87 FPU Floating Point or Math Fault",
|
|
"Alignment Check Fault",
|
|
"Machine Check Abort",
|
|
"SIMD Floating Point Fault",
|
|
"Virtualization Exception Fault",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Intel Reserved",
|
|
"Security Exception",
|
|
"Intel Reserved"
|
|
};
|
|
|
|
static void EnablePIC(void);
|
|
static void EarlyExceptionHandler(ISRFrame_t *regs);
|
|
static void DoubleFaultHandler(ISRFrame_t *regs);
|
|
|
|
//
|
|
// Registers an isr with his IRQ to handle driver interrupts
|
|
//
|
|
error_t KeRegisterISR(void (*isr)(ISRFrame_t *regs), uchar isrNo)
|
|
{
|
|
uchar n = isrList.n;
|
|
int OverWriting = 0;
|
|
|
|
assert(KeIdtIsInitialized); // IDT initialized
|
|
|
|
if (n == 0) goto settingUp;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
if (isrNo == isrList.entry[i].isrNo) {
|
|
n = i;
|
|
OverWriting++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((n == 255)) // IRQs not filled
|
|
return ENOMEM;
|
|
|
|
settingUp:
|
|
isrList.entry[n].isr = isr;
|
|
isrList.entry[n].isrNo = isrNo;
|
|
if (!OverWriting) isrList.n++;
|
|
|
|
DebugLog("Interrupt %d registered to function %p\n", isrNo, isr);
|
|
return EOK;
|
|
}
|
|
|
|
//
|
|
// Installs the IDT in order to activate the interrupts handling
|
|
//
|
|
void KeSetupIDT(void)
|
|
{
|
|
// XXX detect the APIC with cpuid and, perhaps, use it !
|
|
EnablePIC();
|
|
|
|
ushort codeSeg = (ushort)(ulong)BtLoaderInfo.codeSegment;
|
|
|
|
// Set IDT ptr
|
|
_KeIdtPtr.limit = (sizeof(IdtEntry_t) * 256) - 1;
|
|
_KeIdtPtr.base = &idt;
|
|
|
|
// Set IDT Exception Gates
|
|
KeSetIDTGate(0x00, (ulong)isr0, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x01, (ulong)isr1, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x02, (ulong)isr2, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x03, (ulong)isr3, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x04, (ulong)isr4, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x05, (ulong)isr5, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x06, (ulong)isr6, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x07, (ulong)isr7, codeSeg, 0x8E, 2); // XXX device not available, useful for FPU save/restore when multitasking
|
|
KeSetIDTGate(0x08, (ulong)isr8, codeSeg, 0x8E, 1); // DOUBLE FAULT
|
|
KeSetIDTGate(0x09, (ulong)isr9, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x0A, (ulong)isr10, codeSeg, 0x8E, 0); // INVALID TSS
|
|
KeSetIDTGate(0x0B, (ulong)isr11, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x0C, (ulong)isr12, codeSeg, 0x8E, 1); // STACK SEGMENT FAULT
|
|
KeSetIDTGate(0x0D, (ulong)isr13, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x0E, (ulong)isr14, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x0F, (ulong)isr15, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x10, (ulong)isr16, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x11, (ulong)isr17, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x12, (ulong)isr18, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x13, (ulong)isr19, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x14, (ulong)isr20, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x15, (ulong)isr21, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x16, (ulong)isr22, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x17, (ulong)isr23, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x18, (ulong)isr24, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x19, (ulong)isr25, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x1A, (ulong)isr26, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x1B, (ulong)isr27, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x1C, (ulong)isr28, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x1D, (ulong)isr29, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
KeSetIDTGate(0x1E, (ulong)isr30, codeSeg, 0x8E, 2);
|
|
KeSetIDTGate(0x1F, (ulong)isr31, codeSeg, 0x8E, 2); // INTEL RESERVED
|
|
|
|
// Set IDT IRQs Gates
|
|
KeSetIDTGate(0x20, (ulong)isr32, codeSeg, 0x8E, 0);
|
|
KeSetIDTGate(0x21, (ulong)isr33, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x22, (ulong)isr34, codeSeg, 0x8E, 3); // NEVER RAISED : cascaded
|
|
KeSetIDTGate(0x23, (ulong)isr35, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x24, (ulong)isr36, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x25, (ulong)isr37, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x26, (ulong)isr38, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x27, (ulong)isr39, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x28, (ulong)isr40, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x29, (ulong)isr41, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x2A, (ulong)isr42, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x2B, (ulong)isr43, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x2C, (ulong)isr44, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x2D, (ulong)isr45, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x2E, (ulong)isr46, codeSeg, 0x8E, 3);
|
|
KeSetIDTGate(0x2F, (ulong)isr47, codeSeg, 0x8E, 3);
|
|
|
|
KeIdtIsInitialized++;
|
|
|
|
//Setup Early Exception handler
|
|
for (uchar i = 0 ; i < 0x20 ; i++) {
|
|
KeRegisterISR(EarlyExceptionHandler, i);
|
|
}
|
|
|
|
KeRegisterISR(KeBrkDumpRegisters, 0x3);
|
|
KeRegisterISR(DoubleFaultHandler, 0x8);
|
|
|
|
// Load IDT
|
|
KeLoadIDT();
|
|
//DebugLog("\tInterrupt table initialized at %p\n", _KeIdtPtr.base);
|
|
DebugLog("Interrupts activated\n");
|
|
}
|
|
|
|
//
|
|
// Set an interrupt gate
|
|
//
|
|
void KeSetIDTGate(uchar rank, ulong base, ushort selector, uchar flags, uchar ist)
|
|
{
|
|
// Set Base Address
|
|
idt[rank].baseLow = base & 0xFFFF;
|
|
idt[rank].baseMid = (base >> 16) & 0xFFFF;
|
|
idt[rank].baseHigh = (base >> 32) & 0xFFFFFFFF;
|
|
|
|
// Set Selector
|
|
idt[rank].selector = selector;
|
|
idt[rank].flags = flags;
|
|
|
|
// Set Reserved Areas to Zero
|
|
idt[rank].ist = ist;
|
|
idt[rank].reserved = 0;
|
|
}
|
|
|
|
//
|
|
// Enable and initializes the PIC to work correctly
|
|
//
|
|
static void EnablePIC(void)
|
|
{
|
|
// Set ICW1 - begin init of the PIC
|
|
IoWriteByteOnPort(0x20, 0x11);
|
|
IoWriteByteOnPort(0xa0, 0x11);
|
|
|
|
// Set ICW2 (IRQ base offsets)
|
|
IoWriteByteOnPort(0x21, 0x20); // 0x20 is the first free interrupt for IRQ0
|
|
IoWriteByteOnPort(0xa1, 0x28); // PIC2 is offseted to 0x28
|
|
|
|
// Set ICW3
|
|
IoWriteByteOnPort(0x21, 0x4); // A slave exists
|
|
IoWriteByteOnPort(0xa1, 0x2); // You're a slave
|
|
|
|
// Set ICW4
|
|
IoWriteByteOnPort(0x21, 0x1);
|
|
IoWriteByteOnPort(0xa1, 0x1);
|
|
|
|
// Set OCW1 (interrupt masks)
|
|
IoWriteByteOnPort(0x21, 0xff);
|
|
IoWriteByteOnPort(0xa1, 0xff);
|
|
|
|
char readIrqs = IoReadByteFromPort(0x21);
|
|
IoWriteByteOnPort(0x21, 0xFB & readIrqs); // Enables IRQ forwarding from PIC2 to PIC 1
|
|
}
|
|
|
|
//
|
|
// Ends the current interrupt handling
|
|
//
|
|
void KeSendEOItoPIC(uchar isr)
|
|
{
|
|
if(isr >= 8)
|
|
IoWriteByteOnPort(0xa0,0x20);
|
|
|
|
IoWriteByteOnPort(0x20,0x20);
|
|
}
|
|
|
|
void KeMaskIRQ(uchar isr)
|
|
{
|
|
uchar port;
|
|
uchar value;
|
|
|
|
if(isr < 8) {
|
|
port = 0x21;
|
|
} else {
|
|
port = 0xA1;
|
|
isr -= 8;
|
|
}
|
|
|
|
value = IoReadByteFromPort(port) | (1 << isr);
|
|
IoWriteByteOnPort(port, value);
|
|
DebugLog("ISR masked : %d\n", isr);
|
|
}
|
|
|
|
void KeUnmaskIRQ(uchar isr)
|
|
{
|
|
uchar port;
|
|
uchar value;
|
|
|
|
if(isr < 0x8) {
|
|
port = 0x21;
|
|
} else {
|
|
port = 0xA1;
|
|
isr -= 8;
|
|
}
|
|
|
|
value = IoReadByteFromPort(port) & ~(1 << isr);
|
|
IoWriteByteOnPort(port, value);
|
|
DebugLog("ISR unmasked : %d\n", isr);
|
|
}
|
|
|
|
void KeEnableNMI(void)
|
|
{
|
|
IoWriteByteOnPort(0x70, IoReadByteFromPort(0x70) & 0x7F);
|
|
DebugLog("NMI Interrupts enabled\n");
|
|
}
|
|
|
|
void KeDisableNMI(void)
|
|
{
|
|
IoWriteByteOnPort(0x70, IoReadByteFromPort(0x70) | 0x80);
|
|
DebugLog("NMI Interrupts disabled\n");
|
|
}
|
|
|
|
|
|
//
|
|
// Get the content of a register of the PIC. Parameter is a ocw3 command :
|
|
// - 0x0a : read the IRR
|
|
// - 0x0b : read the ISR
|
|
//
|
|
static inline ushort KeGetIrqRegister(int ocw3)
|
|
{
|
|
IoWriteByteOnPort(0x20, ocw3);
|
|
IoWriteByteOnPort(0xA0, ocw3);
|
|
return ((IoReadByteFromPort(0xA0) << 8) | IoReadByteFromPort(0x20)); // We MUST read the COMMAND PORT
|
|
}
|
|
|
|
|
|
//
|
|
// The main ISR handler
|
|
//
|
|
void _KeHandleISR(ISRFrame_t *regs)
|
|
{
|
|
if ((regs->intNo >= 0x15) && (regs->intNo <= 0x1D))
|
|
return; // INTEL RESERVED
|
|
|
|
if ((regs->intNo == 0x0F) || (regs->intNo == 0x1F))
|
|
return; // INTEL RESERVED
|
|
|
|
// Spurious interrupt handling.
|
|
if (regs->intNo > 0x20 && !(KeGetIrqRegister(0x0b) & (1<<(regs->intNo - 0x20)))) {
|
|
KeSpuriousCount++;
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < isrList.n; i++) {
|
|
if (regs->intNo == isrList.entry[i].isrNo) {
|
|
isrList.entry[i].isr(regs);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bprintf(BStdDbg, "[%C%8d%C]\tISR 0x%x %s\n",
|
|
VGA_COLOR_BROWN,
|
|
KeGetTicks(),
|
|
VGA_COLOR_LIGHT_GREY,
|
|
regs->intNo,
|
|
"Unknown ISR Exception");
|
|
KeSendEOItoPIC(regs->intNo);
|
|
}
|
|
|
|
//
|
|
// Early CPU Exception handler
|
|
//
|
|
static void EarlyExceptionHandler(ISRFrame_t *regs)
|
|
{
|
|
bprintf(BStdOut, "\n\n%CPANIC\n[ISR 0x%x] Irrecoverable Kernel %s\n\n"
|
|
" Error code : 0x%x (%b)",
|
|
|
|
VGA_COLOR_LIGHT_RED,
|
|
regs->intNo,
|
|
ExceptionsChar[regs->intNo],
|
|
regs->ErrorCode,
|
|
regs->ErrorCode
|
|
);
|
|
|
|
KeBrkDumpRegisters(regs);
|
|
|
|
BStdOut->flusher(BStdOut);
|
|
|
|
KeHaltCPU();
|
|
}
|
|
|
|
//
|
|
// Double Fault handling and stack overflow detection
|
|
//
|
|
static void DoubleFaultHandler(ISRFrame_t *regs)
|
|
{
|
|
bprintf(BStdOut,
|
|
"\n\n%CPANIC\n[ISR 0x8] Irrecoverable Kernel Double Fault Abort\n\n"
|
|
" Error code : 0x%x (%b)",
|
|
|
|
VGA_COLOR_LIGHT_RED,
|
|
regs->ErrorCode,
|
|
regs->ErrorCode
|
|
);
|
|
|
|
KeBrkDumpRegisters(regs);
|
|
|
|
BStdOut->flusher(BStdOut);
|
|
|
|
KeHaltCPU();
|
|
}
|
|
|
|
void KeBrkDumpRegisters(ISRFrame_t *regs)
|
|
{
|
|
IoDoBeepNoIdt();
|
|
|
|
size_t N = 3;
|
|
char *ptr = (char *)lmax((ulong)BStdDbg->buf,
|
|
(ulong)(BStdDbg->wp - BStdDbg->lastLF - (BStdDbg->lineLen * N)));
|
|
|
|
bprintf(BStdOut, "\n%C Debug messages last %d lines: \n %s\n",
|
|
VGA_COLOR_LIGHT_GREY,
|
|
N,
|
|
ptr);
|
|
|
|
bprintf(BStdOut,
|
|
"%C RIP: %#016lx RSP: %#016lx RBP: %#016lx\n\n"
|
|
|
|
" SS: %#016lx CS: %#016lx CR0: %#016lx\n"
|
|
" CR2: %#016lx CR3: %#016lx CR4: %#016lx\n"
|
|
" CR8: %#016lx EFE: %#016lx \n\n"
|
|
|
|
" RAX: %#016lx RBX: %#016lx RCX: %#016lx\n"
|
|
" RDX: %#016lx RSI: %#016lx RDI: %#016lx\n"
|
|
|
|
" R8: %#016lx R9: %#016lx R10: %#016lx\n"
|
|
" R11: %#016lx R12: %#016lx R13: %#016lx\n"
|
|
" R14: %#016lx R15: %#016lx \n\n"
|
|
|
|
" RFLAGS: %#022b (%#06x)",
|
|
VGA_COLOR_LIGHT_RED,
|
|
regs->rip,
|
|
regs->rsp,
|
|
regs->rbp,
|
|
regs->ss,
|
|
regs->cs,
|
|
regs->cr0,
|
|
regs->cr2,
|
|
regs->cr3,
|
|
regs->cr4,
|
|
regs->cr8,
|
|
regs->efer,
|
|
regs->rax,
|
|
regs->rbx,
|
|
regs->rcx,
|
|
regs->rdx,
|
|
regs->rsi,
|
|
regs->rdi,
|
|
regs->r8,
|
|
regs->r9,
|
|
regs->r10,
|
|
regs->r11,
|
|
regs->r12,
|
|
regs->r13,
|
|
regs->r14,
|
|
regs->r15,
|
|
regs->rflags,
|
|
regs->rflags
|
|
);
|
|
|
|
BStdOut->flusher(BStdOut);
|
|
}
|