Refactor i386 and x86_64 interrupt support.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-01-31 13:19:27 +01:00
parent d50e8f1bce
commit a133a7717e
17 changed files with 655 additions and 553 deletions

View File

@ -45,6 +45,8 @@ ifdef X86FAMILY
$(CPU)/memorymanagement.o \
x86-family/memorymanagement.o \
$(CPU)/interrupt.o \
x86-family/interrupt.o \
x86-family/pic.o \
x86-family/gdt.o \
x86-family/idt.o \
$(CPU)/syscall.o \

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -18,7 +18,7 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/kernel/interrupt.h
High level interrupt service routines and interrupt request handlers.
High level interrupt services.
*******************************************************************************/
@ -30,13 +30,17 @@
#include <sortix/kernel/decl.h>
namespace Sortix {
namespace CPU {
struct InterruptRegisters;
} // namespace CPU
struct InterruptRegisters;
} // namespace CPU
} // namespace Sortix
namespace Sortix {
namespace Interrupt {
#if defined(__i386__) || defined(__x86_64__)
const unsigned int IRQ0 = 32;
const unsigned int IRQ1 = 33;
const unsigned int IRQ2 = 34;
@ -53,27 +57,60 @@ const unsigned int IRQ12 = 44;
const unsigned int IRQ13 = 45;
const unsigned int IRQ14 = 46;
const unsigned int IRQ15 = 47;
#endif
extern "C" unsigned long asm_interrupts_are_enabled();
extern "C" unsigned long asm_is_cpu_interrupted;
inline bool IsEnabled() { return asm_interrupts_are_enabled(); }
inline void Enable() { asm volatile("sti"); }
inline void Disable() { asm volatile("cli"); }
inline bool IsCPUInterrupted() { return asm_is_cpu_interrupted != 0; }
inline bool SetEnabled(bool isenabled)
inline bool IsEnabled()
{
#if defined(__i386__) || defined(__x86_64__)
unsigned long is_enabled;
asm("pushf\t\n"
"pop %0\t\n"
"and $0x000200, %0" : "=r"(is_enabled));
return is_enabled != 0;
#else
#warning "You need to implement checking if interrupts are on"
#endif
}
inline void Enable()
{
#if defined(__i386__) || defined(__x86_64__)
asm volatile("sti");
#else
#warning "You need to implement enabling interrupts"
#endif
}
inline void Disable()
{
#if defined(__i386__) || defined(__x86_64__)
asm volatile("cli");
#else
#warning "You need to implement disabling interrupts"
#endif
}
inline bool IsCPUInterrupted()
{
return asm_is_cpu_interrupted != 0;
}
inline bool SetEnabled(bool is_enabled)
{
bool wasenabled = IsEnabled();
if ( isenabled ) { Enable(); } else { Disable(); }
if ( is_enabled )
Enable();
else
Disable();
return wasenabled;
}
typedef void (*Handler)(CPU::InterruptRegisters* regs, void* user);
void RegisterHandler(unsigned int index, Handler handler, void* user);
typedef void (*RawHandler)(void);
void RegisterRawHandler(unsigned int index, RawHandler handler, bool userspace);
void Init();
void InitWorker();
void WorkerThread(void* user);
@ -84,57 +121,4 @@ bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize);
} // namespace Interrupt
} // namespace Sortix
extern "C" void isr0();
extern "C" void isr1();
extern "C" void isr2();
extern "C" void isr3();
extern "C" void isr4();
extern "C" void isr5();
extern "C" void isr6();
extern "C" void isr7();
extern "C" void isr8();
extern "C" void isr9();
extern "C" void isr10();
extern "C" void isr11();
extern "C" void isr12();
extern "C" void isr13();
extern "C" void isr14();
extern "C" void isr15();
extern "C" void isr16();
extern "C" void isr17();
extern "C" void isr18();
extern "C" void isr19();
extern "C" void isr20();
extern "C" void isr21();
extern "C" void isr22();
extern "C" void isr23();
extern "C" void isr24();
extern "C" void isr25();
extern "C" void isr26();
extern "C" void isr27();
extern "C" void isr28();
extern "C" void isr29();
extern "C" void isr30();
extern "C" void isr31();
extern "C" void isr128();
extern "C" void isr130();
extern "C" void isr131();
extern "C" void irq0();
extern "C" void irq1();
extern "C" void irq2();
extern "C" void irq3();
extern "C" void irq4();
extern "C" void irq5();
extern "C" void irq6();
extern "C" void irq7();
extern "C" void irq8();
extern "C" void irq9();
extern "C" void irq10();
extern "C" void irq11();
extern "C" void irq12();
extern "C" void irq13();
extern "C" void irq14();
extern "C" void irq15();
extern "C" void interrupt_handler_null();
#endif

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -28,20 +28,26 @@
#include <sortix/kernel/decl.h>
namespace Sortix {
class Process;
class Thread;
} // namespace Sortix
namespace Sortix {
namespace CPU {
struct InterruptRegisters;
} // namespace CPU
} // namespace Sortix
namespace Sortix {
enum ThreadState { NONE, RUNNABLE, BLOCKING, DEAD };
} // namespace Sortix
namespace Sortix {
namespace Scheduler {
void Init();
void Switch(CPU::InterruptRegisters* regs);
#if defined(__i386__) || defined(__x86_64__)
inline static void Yield() { asm volatile ("int $129"); }
__attribute__ ((noreturn))
inline static void ExitThread()
@ -49,6 +55,7 @@ inline static void ExitThread()
asm volatile ("int $132");
__builtin_unreachable();
}
#endif
void SetThreadState(Thread* thread, ThreadState state);
ThreadState GetThreadState(Thread* thread);
void SetIdleThread(Thread* thread);
@ -56,9 +63,10 @@ void SetDummyThreadOwner(Process* process);
void SetInitProcess(Process* init);
Process* GetInitProcess();
Process* GetKernelProcess();
void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* user);
void ThreadExitCPU(CPU::InterruptRegisters* regs, void* user);
} // namespace Scheduler
} // namespace Sortix
#endif

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -38,6 +38,4 @@ void Register(size_t index, void* funcptr);
} // namespace Syscall
} // namespace Sortix
extern "C" void syscall_handler();
#endif

View File

@ -18,7 +18,7 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
interrupt.cpp
High level interrupt service routines and interrupt request handlers.
High level interrupt services.
*******************************************************************************/
@ -37,292 +37,9 @@
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h>
#include "x86-family/idt.h"
namespace Sortix {
namespace Interrupt {
const uint16_t PIC_MASTER = 0x20;
const uint16_t PIC_SLAVE = 0xA0;
const uint16_t PIC_COMMAND = 0x00;
const uint16_t PIC_DATA = 0x01;
const uint8_t PIC_CMD_ENDINTR = 0x20;
const uint8_t PIC_ICW1_ICW4 = 0x01; // ICW4 (not) needed
const uint8_t PIC_ICW1_SINGLE = 0x02; // Single (cascade) mode
const uint8_t PIC_ICW1_INTERVAL4 = 0x04; // Call address interval 4 (8)
const uint8_t PIC_ICW1_LEVEL = 0x08; // Level triggered (edge) mode
const uint8_t PIC_CMD_INIT = 0x10;
const uint8_t PIC_MODE_8086 = 0x01; // 8086/88 (MCS-80/85) mode
const uint8_t PIC_MODE_AUTO = 0x02; // Auto (normal) EOI
const uint8_t PIC_MODE_BUF_SLAVE = 0x08; // Buffered mode/slave
const uint8_t PIC_MODE_BUF_MASTER = 0x0C; // Buffered mode/master
const uint8_t PIC_MODE_SFNM = 0x10; // Special fully nested (not)
extern "C" { unsigned long asm_is_cpu_interrupted = 0; }
const bool DEBUG_EXCEPTION = true;
const bool DEBUG_IRQ = false;
const bool DEBUG_ISR = false;
const bool CALLTRACE_KERNEL = false;
const bool CALLTRACE_USER = false;
const bool RUN_DEBUGGER_ON_CRASH = false;
const size_t NUM_KNOWN_EXCEPTIONS = 20;
const char* exceptions[] =
{
"Divide by zero", /* 0, 0x0 */
"Debug", /* 1, 0x1 */
"Non maskable interrupt", /* 2, 0x2 */
"Breakpoint", /* 3, 0x3 */
"Into detected overflow", /* 4, 0x4 */
"Out of bounds", /* 5, 0x5 */
"Invalid opcode", /* 6, 0x6 */
"No coprocessor", /* 7, 0x7 */
"Double fault", /* 8, 0x8 */
"Coprocessor segment overrun", /* 9, 0x9 */
"Bad TSS", /* 10, 0xA */
"Segment not present", /* 11, 0xB */
"Stack fault", /* 12, 0xC */
"General protection fault", /* 13, 0xD */
"Page fault", /* 14, 0xE */
"Unknown interrupt", /* 15, 0xF */
"Coprocessor fault", /* 16, 0x10 */
"Alignment check", /* 17, 0x11 */
"Machine check", /* 18, 0x12 */
"SIMD Floating-Point", /* 19, 0x13 */
};
const unsigned int NUM_INTERRUPTS = 256UL;
static Handler interrupt_handlers[NUM_INTERRUPTS];
static void* interrupt_handler_params[NUM_INTERRUPTS];
extern "C" void ReprogramPIC()
{
uint8_t master_mask = 0;
uint8_t slave_mask = 0;
CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_MASTER + PIC_DATA, IRQ0);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, IRQ8);
CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2
CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity
CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask);
}
extern "C" void DeprogramPIC()
{
uint8_t master_mask = 0;
uint8_t slave_mask = 0;
CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x08);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x70);
CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2
CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity
CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask);
}
void Init()
{
IDT::Init();
for ( unsigned int i = 0; i < NUM_INTERRUPTS; i++ )
{
interrupt_handlers[i] = NULL;
interrupt_handler_params[i] = NULL;
RegisterRawHandler(i, interrupt_handler_null, false);
}
// Remap the IRQ table on the PICs.
ReprogramPIC();
RegisterRawHandler(0, isr0, false);
RegisterRawHandler(1, isr1, false);
RegisterRawHandler(2, isr2, false);
RegisterRawHandler(3, isr3, false);
RegisterRawHandler(4, isr4, false);
RegisterRawHandler(5, isr5, false);
RegisterRawHandler(6, isr6, false);
RegisterRawHandler(7, isr7, false);
RegisterRawHandler(8, isr8, false);
RegisterRawHandler(9, isr9, false);
RegisterRawHandler(10, isr10, false);
RegisterRawHandler(11, isr11, false);
RegisterRawHandler(12, isr12, false);
RegisterRawHandler(13, isr13, false);
RegisterRawHandler(14, isr14, false);
RegisterRawHandler(15, isr15, false);
RegisterRawHandler(16, isr16, false);
RegisterRawHandler(17, isr17, false);
RegisterRawHandler(18, isr18, false);
RegisterRawHandler(19, isr19, false);
RegisterRawHandler(20, isr20, false);
RegisterRawHandler(21, isr21, false);
RegisterRawHandler(22, isr22, false);
RegisterRawHandler(23, isr23, false);
RegisterRawHandler(24, isr24, false);
RegisterRawHandler(25, isr25, false);
RegisterRawHandler(26, isr26, false);
RegisterRawHandler(27, isr27, false);
RegisterRawHandler(28, isr28, false);
RegisterRawHandler(29, isr29, false);
RegisterRawHandler(30, isr30, false);
RegisterRawHandler(31, isr31, false);
RegisterRawHandler(32, irq0, false);
RegisterRawHandler(33, irq1, false);
RegisterRawHandler(34, irq2, false);
RegisterRawHandler(35, irq3, false);
RegisterRawHandler(36, irq4, false);
RegisterRawHandler(37, irq5, false);
RegisterRawHandler(38, irq6, false);
RegisterRawHandler(39, irq7, false);
RegisterRawHandler(40, irq8, false);
RegisterRawHandler(41, irq9, false);
RegisterRawHandler(42, irq10, false);
RegisterRawHandler(43, irq11, false);
RegisterRawHandler(44, irq12, false);
RegisterRawHandler(45, irq13, false);
RegisterRawHandler(46, irq14, false);
RegisterRawHandler(47, irq15, false);
// TODO: Let the syscall.cpp code register this.
RegisterRawHandler(128, syscall_handler, true);
Interrupt::Enable();
}
void RegisterHandler(unsigned int index, Interrupt::Handler handler, void* user)
{
interrupt_handlers[index] = handler;
interrupt_handler_params[index] = user;
}
// TODO: This function contains magic IDT-related values!
void RegisterRawHandler(unsigned int index, RawHandler handler, bool userspace)
{
addr_t handler_entry = (addr_t) handler;
uint16_t sel = KCS;
uint8_t flags = 0x8E;
if ( userspace )
flags |= 0x60;
IDT::SetEntry(index, handler_entry, sel, flags);
}
void CrashHandler(CPU::InterruptRegisters* regs)
{
CurrentThread()->SaveRegisters(regs);
const char* message = regs->int_no < NUM_KNOWN_EXCEPTIONS
? exceptions[regs->int_no] : "Unknown";
if ( DEBUG_EXCEPTION )
{
regs->LogRegisters();
Log::Print("\n");
}
#if defined(__x86_64__)
addr_t ip = regs->rip;
#elif defined(__i386__)
addr_t ip = regs->eip;
#endif
// Halt and catch fire if we are the kernel.
unsigned code_mode = regs->cs & 0x3;
bool is_in_kernel = !code_mode;
bool is_in_user = !is_in_kernel;
if ( (is_in_kernel && CALLTRACE_KERNEL) || (is_in_user && CALLTRACE_USER) )
#if defined(__x86_64__)
Calltrace::Perform(regs->rbp);
#elif defined(__i386__)
Calltrace::Perform(regs->ebp);
#else
#error Please provide a calltrace implementation for your CPU.
#endif
if ( RUN_DEBUGGER_ON_CRASH )
Debugger::Run();
if ( is_in_kernel )
{
PanicF("Unhandled CPU Exception id %zu '%s' at ip=0x%zx "
"(cr2=0x%p, err_code=0x%p)", regs->int_no, message,
ip, regs->cr2, regs->err_code);
}
Interrupt::Enable();
Log::PrintF("The current program (pid %ji %s) has crashed and was terminated:\n",
(intmax_t) CurrentProcess()->pid, CurrentProcess()->program_image_path);
Log::PrintF("%s exception at ip=0x%zx (cr2=0x%p, err_code=0x%p)\n",
message, ip, regs->cr2, regs->err_code);
// Exit the process with the right error code.
// TODO: Sent a SIGINT, SIGBUS, or whatever instead.
CurrentProcess()->Exit(139);
Interrupt::Disable();
Signal::Dispatch(regs);
}
void ISRHandler(CPU::InterruptRegisters* regs)
{
unsigned int int_no = regs->int_no;
if ( DEBUG_ISR )
{
Log::PrintF("ISR%u ", int_no);
regs->LogRegisters();
Log::Print("\n");
}
// Run the desired interrupt handler.
if ( int_no < 32 && int_no != 7 )
CrashHandler(regs);
else if ( interrupt_handlers[regs->int_no] != NULL )
interrupt_handlers[int_no](regs, interrupt_handler_params[int_no]);
}
void IRQHandler(CPU::InterruptRegisters* regs)
{
// TODO: IRQ 7 and 15 might be spurious and might need to be ignored.
// See http://wiki.osdev.org/PIC for details (section Spurious IRQs).
if ( regs->int_no == 32 + 7 || regs->int_no == 32 + 15 )
return;
if ( DEBUG_IRQ )
{
Log::PrintF("IRQ%u ", regs->int_no-32);
regs->LogRegisters();
Log::Print("\n");
}
unsigned int int_no = regs->int_no;
// Send an EOI (end of interrupt) signal to the PICs.
if ( IRQ8 <= int_no )
CPU::OutPortB(PIC_SLAVE, PIC_CMD_ENDINTR);
CPU::OutPortB(PIC_MASTER, PIC_CMD_ENDINTR);
if ( interrupt_handlers[int_no] )
interrupt_handlers[int_no](regs, interrupt_handler_params[int_no]);
}
extern "C" void interrupt_handler(CPU::InterruptRegisters* regs)
{
if ( 32 <= regs->int_no && regs->int_no < 48 )
IRQHandler(regs);
else
ISRHandler(regs);
}
// TODO: This implementation is a bit hacky and can be optimized.
uint8_t* queue;

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -187,12 +187,12 @@ void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs)
}
}
static void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/)
void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/)
{
Switch(regs);
}
static void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/)
void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/)
{
// Can't use floating point instructions from now.
Float::NofityTaskExit(currentthread);
@ -298,9 +298,6 @@ static int sys_usleep(size_t usecs)
return 0;
}
extern "C" void yield_cpu_handler();
extern "C" void thread_exit_handler();
void Init()
{
premagic = postmagic = SCHED_MAGIC;
@ -317,13 +314,6 @@ void Init()
firstsleepingthread = NULL;
idlethread = NULL;
// Register our raw handler with user-space access. It calls our real
// handler after common interrupt preparation stuff has occured.
Interrupt::RegisterRawHandler(129, yield_cpu_handler, true);
Interrupt::RegisterHandler(129, InterruptYieldCPU, NULL);
Interrupt::RegisterRawHandler(132, thread_exit_handler, true);
Interrupt::RegisterHandler(132, ThreadExitCPU, NULL);
Syscall::Register(SYSCALL_SLEEP, (void*) sys_sleep);
Syscall::Register(SYSCALL_USLEEP, (void*) sys_usleep);
}

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -127,10 +127,6 @@ void Return(CPU::InterruptRegisters* regs, void* /*user*/)
void Init()
{
Interrupt::RegisterRawHandler(130, isr130, true);
Interrupt::RegisterHandler(130, Dispatch, NULL);
Interrupt::RegisterRawHandler(131, isr131, true);
Interrupt::RegisterHandler(131, Return, NULL);
}
} // namespace Signal

View File

@ -27,371 +27,318 @@
.global isr0
.type isr0, @function
isr0:
cli
pushq $0 # err_code
pushq $0 # int_no
jmp interrupt_handler_prepare
.global isr1
.type isr1, @function
isr1:
cli
pushq $0 # err_code
pushq $1 # int_no
jmp interrupt_handler_prepare
.global isr2
.type isr2, @function
isr2:
cli
pushq $0 # err_code
pushq $2 # int_no
jmp interrupt_handler_prepare
.global isr3
.type isr3, @function
isr3:
cli
pushq $0 # err_code
pushq $3 # int_no
jmp interrupt_handler_prepare
.global isr4
.type isr4, @function
isr4:
cli
pushq $0 # err_code
pushq $4 # int_no
jmp interrupt_handler_prepare
.global isr5
.type isr5, @function
isr5:
cli
pushq $0 # err_code
pushq $5 # int_no
jmp interrupt_handler_prepare
.global isr6
.type isr6, @function
isr6:
cli
pushq $0 # err_code
pushq $6 # int_no
jmp interrupt_handler_prepare
.global isr7
.type isr7, @function
isr7:
cli
pushq $0 # err_code
pushq $7 # int_no
jmp interrupt_handler_prepare
.global isr8
.type isr8, @function
isr8:
cli
# pushq $0 # err_code pushed by CPU
pushq $8 # int_no
jmp interrupt_handler_prepare
.global isr9
.type isr9, @function
isr9:
cli
pushq $0 # err_code
pushq $9 # int_no
jmp interrupt_handler_prepare
.global isr10
.type isr10, @function
isr10:
cli
# pushq $0 # err_code pushed by CPU
pushq $10 # int_no
jmp interrupt_handler_prepare
.global isr11
.type isr11, @function
isr11:
cli
# pushq $0 # err_code pushed by CPU
pushq $11 # int_no
jmp interrupt_handler_prepare
.global isr12
.type isr12, @function
isr12:
cli
# pushq $0 # err_code pushed by CPU
pushq $12 # int_no
jmp interrupt_handler_prepare
.global isr13
.type isr13, @function
isr13:
cli
# pushq $0 # err_code pushed by CPU
pushq $13 # int_no
jmp interrupt_handler_prepare
.global isr14
.type isr14, @function
isr14:
cli
# pushq $0 # err_code pushed by CPU
pushq $14 # int_no
jmp interrupt_handler_prepare
.global isr15
.type isr15, @function
isr15:
cli
pushq $0 # err_code
pushq $15 # int_no
jmp interrupt_handler_prepare
.global isr16
.type isr16, @function
isr16:
cli
pushq $0 # err_code
pushq $16 # int_no
jmp interrupt_handler_prepare
.global isr17
.type isr17, @function
isr17:
cli
pushq $0 # err_code
pushq $17 # int_no
jmp interrupt_handler_prepare
.global isr18
.type isr18, @function
isr18:
cli
pushq $0 # err_code
pushq $18 # int_no
jmp interrupt_handler_prepare
.global isr19
.type isr19, @function
isr19:
cli
pushq $0 # err_code
pushq $19 # int_no
jmp interrupt_handler_prepare
.global isr20
.type isr20, @function
isr20:
cli
pushq $0 # err_code
pushq $20 # int_no
jmp interrupt_handler_prepare
.global isr21
.type isr21, @function
isr21:
cli
pushq $0 # err_code
pushq $21 # int_no
jmp interrupt_handler_prepare
.global isr22
.type isr22, @function
isr22:
cli
pushq $0 # err_code
pushq $22 # int_no
jmp interrupt_handler_prepare
.global isr23
.type isr23, @function
isr23:
cli
pushq $0 # err_code
pushq $23 # int_no
jmp interrupt_handler_prepare
.global isr24
.type isr24, @function
isr24:
cli
pushq $0 # err_code
pushq $24 # int_no
jmp interrupt_handler_prepare
.global isr25
.type isr25, @function
isr25:
cli
pushq $0 # err_code
pushq $25 # int_no
jmp interrupt_handler_prepare
.global isr26
.type isr26, @function
isr26:
cli
pushq $0 # err_code
pushq $26 # int_no
jmp interrupt_handler_prepare
.global isr27
.type isr27, @function
isr27:
cli
pushq $0 # err_code
pushq $27 # int_no
jmp interrupt_handler_prepare
.global isr28
.type isr28, @function
isr28:
cli
pushq $0 # err_code
pushq $28 # int_no
jmp interrupt_handler_prepare
.global isr29
.type isr29, @function
isr29:
cli
pushq $0 # err_code
pushq $29 # int_no
jmp interrupt_handler_prepare
.global isr30
.type isr30, @function
isr30:
cli
pushq $0 # err_code
pushq $30 # int_no
jmp interrupt_handler_prepare
.global isr31
.type isr31, @function
isr31:
cli
pushq $0 # err_code
pushq $31 # int_no
jmp interrupt_handler_prepare
.global isr128
.type isr128, @function
isr128:
cli
pushq $0 # err_code
pushq $128 # int_no
jmp interrupt_handler_prepare
.global isr130
.type isr130, @function
isr130:
cli
pushq $0 # err_code
pushq $130 # int_no
jmp interrupt_handler_prepare
.global isr131
.type isr131, @function
isr131:
cli
pushq $0 # err_code
pushq $131 # int_no
jmp interrupt_handler_prepare
.global irq0
.type irq0, @function
irq0:
cli
pushq $0 # err_code
pushq $32 # int_no
jmp interrupt_handler_prepare
.global irq1
.type irq1, @function
irq1:
cli
pushq $0 # err_code
pushq $33 # int_no
jmp interrupt_handler_prepare
.global irq2
.type irq2, @function
irq2:
cli
pushq $0 # err_code
pushq $34 # int_no
jmp interrupt_handler_prepare
.global irq3
.type irq3, @function
irq3:
cli
pushq $0 # err_code
pushq $35 # int_no
jmp interrupt_handler_prepare
.global irq4
.type irq4, @function
irq4:
cli
pushq $0 # err_code
pushq $36 # int_no
jmp interrupt_handler_prepare
.global irq5
.type irq5, @function
irq5:
cli
pushq $0 # err_code
pushq $37 # int_no
jmp interrupt_handler_prepare
.global irq6
.type irq6, @function
irq6:
cli
pushq $0 # err_code
pushq $38 # int_no
jmp interrupt_handler_prepare
.global irq7
.type irq7, @function
irq7:
cli
pushq $0 # err_code
pushq $39 # int_no
jmp interrupt_handler_prepare
.global irq8
.type irq8, @function
irq8:
cli
pushq $0 # err_code
pushq $40 # int_no
jmp interrupt_handler_prepare
.global irq9
.type irq9, @function
irq9:
cli
pushq $0 # err_code
pushq $41 # int_no
jmp interrupt_handler_prepare
.global irq10
.type irq10, @function
irq10:
cli
pushq $0 # err_code
pushq $42 # int_no
jmp interrupt_handler_prepare
.global irq11
.type irq11, @function
irq11:
cli
pushq $0 # err_code
pushq $43 # int_no
jmp interrupt_handler_prepare
.global irq12
.type irq12, @function
irq12:
cli
pushq $0 # err_code
pushq $44 # int_no
jmp interrupt_handler_prepare
.global irq13
.type irq13, @function
irq13:
cli
pushq $0 # err_code
pushq $45 # int_no
jmp interrupt_handler_prepare
.global irq14
.type irq14, @function
irq14:
cli
pushq $0 # err_code
pushq $46 # int_no
jmp interrupt_handler_prepare
.global irq15
.type irq15, @function
irq15:
cli
pushq $0 # err_code
pushq $47 # int_no
jmp interrupt_handler_prepare
.global yield_cpu_handler
.type yield_cpu_handler, @function
yield_cpu_handler:
cli
pushq $0 # err_code
pushq $129 # int_no
jmp interrupt_handler_prepare
.global thread_exit_handler
.type thread_exit_handler, @function
thread_exit_handler:
cli
pushq $0 # err_code
pushq $132 # int_no
jmp interrupt_handler_prepare
@ -494,15 +441,6 @@ interrupt_handler_null:
iretq
.size interrupt_handler_null, . - interrupt_handler_null
.global asm_interrupts_are_enabled
.type asm_interrupts_are_enabled, @function
asm_interrupts_are_enabled:
pushfq
popq %rax
andq $0x000200, %rax # FLAGS_INTERRUPT
retq
.size asm_interrupts_are_enabled, . - asm_interrupts_are_enabled
.global load_registers
.type load_registers, @function
load_registers:

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -27,11 +27,6 @@
.section .text
.type syscall_handler, @function
syscall_handler:
# The processor disabled interrupts during the int $0x80 instruction,
# however Sortix system calls runs with interrupts enabled such that they
# can be pre-empted.
sti
movl $0, global_errno # Reset errno
pushq %rbp

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -30,52 +30,49 @@
namespace Sortix {
namespace IDT {
struct idt_entry
{
uint16_t handler_low;
uint16_t sel;
uint8_t reserved0;
uint8_t flags;
uint16_t handler_high;
#if defined(__x86_64__)
uint32_t handler_highest;
uint32_t reserved1;
#endif
};
struct idt_ptr
{
uint16_t limit;
#if defined(__x86_64__)
uint64_t idt_ptr;
#else
uint32_t idt_ptr;
#endif
} __attribute__((packed));
static struct idt_entry idt_entries[256];
void Init()
{
volatile struct idt_ptr ptr;
ptr.limit = sizeof(idt_entries) - 1;
ptr.idt_ptr = (unsigned long) &idt_entries;
asm volatile ("lidt (%0)" : : "r"(&ptr));
memset(&idt_entries, 0, sizeof(idt_entries));
Set(idt_entries, 256);
}
void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags)
void Set(struct idt_entry* table, size_t length)
{
idt_entries[num].flags = flags;
idt_entries[num].reserved0 = 0;
idt_entries[num].sel = sel;
idt_entries[num].handler_low = handler >> 0 & 0xFFFF;
idt_entries[num].handler_high = handler >> 16 & 0xFFFF;
size_t limit = sizeof(idt_entry) * length - 1;
#if defined(__x86_64__)
idt_entries[num].handler_highest = handler >> 32 & 0xFFFFFFFFU;
idt_entries[num].reserved1 = 0;
asm volatile ("subq $10, %%rsp\n\t"
"movw %w0, 0(%%rsp)\n\t"
"movq %1, 2(%%rsp)\n\t"
"lidt (%%rsp)\n\t"
"addq $10, %%rsp" : : "rN"(limit), "r"(table));
#else
asm volatile ("subl $6, %%esp\n\t"
"movw %w0, 0(%%esp)\n\t"
"movl %1, 2(%%esp)\n\t"
"lidt (%%esp)\n\t"
"addl $6, %%esp" : : "rN"(limit), "r"(table));
#endif
}
void SetEntry(struct idt_entry* entry, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist)
{
entry->flags = flags;
entry->ist = ist;
entry->selector = selector;
entry->handler_low = handler >> 0 & 0xFFFF;
entry->handler_high = handler >> 16 & 0xFFFF;
#if defined(__x86_64__)
entry->handler_highest = handler >> 32 & 0xFFFFFFFFU;
entry->reserved1 = 0;
#endif
}
void SetEntry(uint8_t num, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist)
{
SetEntry(idt_entries + num, handler, selector, flags, ist);
}
} // namespace IDT
} // namespace Sortix

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -25,12 +25,36 @@
#ifndef SORTIX_X86_FAMILY_IDT_H
#define SORTIX_X86_FAMILY_IDT_H
#include <stdint.h>
namespace Sortix {
namespace IDT {
void Init();
void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags);
void Flush();
struct idt_entry
{
uint16_t handler_low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t handler_high;
#if defined(__x86_64__)
uint32_t handler_highest;
uint32_t reserved1;
#endif
};
static const uint8_t FLAG_PRESENT = 1 << 7;
static const uint8_t FLAG_DPL_SHIFT = 5;
static const uint8_t FLAG_DPL_BITS = 2;
static const uint8_t FLAG_TYPE_SHIFT = 0;
static const uint8_t FLAG_TYPE_BITS = 4;
static const uint8_t TYPE_INTERRUPT = 0xE;
static const uint8_t TYPE_TRAP = 0xF;
void Set(struct idt_entry* table, size_t length);
void SetEntry(struct idt_entry* entry, uintptr_t handler, uint16_t selector,
uint8_t flags, uint8_t ist);
} // namespace IDT
} // namespace Sortix

View File

@ -0,0 +1,355 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
Sortix 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 (at your option) any later
version.
Sortix 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
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/interrupt.cpp
Interrupt support for i386 and x86_64 systems.
*******************************************************************************/
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sortix/kernel/calltrace.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/debugger.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/signal.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h>
#include "idt.h"
#include "pic.h"
extern "C" void isr0();
extern "C" void isr1();
extern "C" void isr2();
extern "C" void isr3();
extern "C" void isr4();
extern "C" void isr5();
extern "C" void isr6();
extern "C" void isr7();
extern "C" void isr8();
extern "C" void isr9();
extern "C" void isr10();
extern "C" void isr11();
extern "C" void isr12();
extern "C" void isr13();
extern "C" void isr14();
extern "C" void isr15();
extern "C" void isr16();
extern "C" void isr17();
extern "C" void isr18();
extern "C" void isr19();
extern "C" void isr20();
extern "C" void isr21();
extern "C" void isr22();
extern "C" void isr23();
extern "C" void isr24();
extern "C" void isr25();
extern "C" void isr26();
extern "C" void isr27();
extern "C" void isr28();
extern "C" void isr29();
extern "C" void isr30();
extern "C" void isr31();
extern "C" void isr128();
extern "C" void isr130();
extern "C" void isr131();
extern "C" void irq0();
extern "C" void irq1();
extern "C" void irq2();
extern "C" void irq3();
extern "C" void irq4();
extern "C" void irq5();
extern "C" void irq6();
extern "C" void irq7();
extern "C" void irq8();
extern "C" void irq9();
extern "C" void irq10();
extern "C" void irq11();
extern "C" void irq12();
extern "C" void irq13();
extern "C" void irq14();
extern "C" void irq15();
extern "C" void interrupt_handler_null();
extern "C" void syscall_handler();
extern "C" void yield_cpu_handler();
extern "C" void thread_exit_handler();
namespace Sortix {
namespace Interrupt {
extern "C" { unsigned long asm_is_cpu_interrupted = 0; }
const bool CALLTRACE_KERNEL = false;
const bool CALLTRACE_USER = false;
const bool RUN_DEBUGGER_ON_KERNEL_CRASH = false;
const bool RUN_DEBUGGER_ON_USER_CRASH = false;
const size_t NUM_KNOWN_EXCEPTIONS = 20;
const char* exception_names[] =
{
"Divide by zero", /* 0, 0x0 */
"Debug", /* 1, 0x1 */
"Non maskable interrupt", /* 2, 0x2 */
"Breakpoint", /* 3, 0x3 */
"Into detected overflow", /* 4, 0x4 */
"Out of bounds", /* 5, 0x5 */
"Invalid opcode", /* 6, 0x6 */
"No coprocessor", /* 7, 0x7 */
"Double fault", /* 8, 0x8 */
"Coprocessor segment overrun", /* 9, 0x9 */
"Bad TSS", /* 10, 0xA */
"Segment not present", /* 11, 0xB */
"Stack fault", /* 12, 0xC */
"General protection fault", /* 13, 0xD */
"Page fault", /* 14, 0xE */
"Unknown interrupt", /* 15, 0xF */
"Coprocessor fault", /* 16, 0x10 */
"Alignment check", /* 17, 0x11 */
"Machine check", /* 18, 0x12 */
"SIMD Floating-Point", /* 19, 0x13 */
};
const unsigned int NUM_INTERRUPTS = 256U;
static struct IDT::idt_entry interrupt_table[NUM_INTERRUPTS];
static Handler interrupt_handlers[NUM_INTERRUPTS];
static void* interrupt_handler_context[NUM_INTERRUPTS];
void RegisterHandler(unsigned int index,
Interrupt::Handler handler,
void* context)
{
assert(index < NUM_INTERRUPTS);
interrupt_handlers[index] = handler;
interrupt_handler_context[index] = context;
}
static void RegisterRawHandler(unsigned int index,
void (*handler)(void),
bool userspace,
bool preemptive)
{
assert(index < NUM_INTERRUPTS);
addr_t handler_entry = (addr_t) handler;
uint16_t selector = KCS;
uint8_t rpl = userspace ? URPL : KRPL;
uint8_t type = preemptive ? IDT::TYPE_TRAP : IDT::TYPE_INTERRUPT;
uint8_t ist = 0;
uint8_t flags = IDT::FLAG_PRESENT
| type << IDT::FLAG_TYPE_SHIFT
| rpl << IDT::FLAG_DPL_SHIFT;
IDT::SetEntry(&interrupt_table[index], handler_entry, selector, flags, ist);
}
void Init()
{
// Initialize the interrupt table entries to the null interrupt handler.
memset(&interrupt_table, 0, sizeof(interrupt_table));
for ( unsigned int i = 0; i < NUM_INTERRUPTS; i++ )
{
interrupt_handlers[i] = NULL;
interrupt_handler_context[i] = NULL;
RegisterRawHandler(i, interrupt_handler_null, false, false);
}
// Remap the IRQ table on the PICs.
PIC::ReprogramPIC();
RegisterRawHandler(0, isr0, false, false);
RegisterRawHandler(1, isr1, false, false);
RegisterRawHandler(2, isr2, false, false);
RegisterRawHandler(3, isr3, false, false);
RegisterRawHandler(4, isr4, false, false);
RegisterRawHandler(5, isr5, false, false);
RegisterRawHandler(6, isr6, false, false);
RegisterRawHandler(7, isr7, false, false);
RegisterRawHandler(8, isr8, false, false);
RegisterRawHandler(9, isr9, false, false);
RegisterRawHandler(10, isr10, false, false);
RegisterRawHandler(11, isr11, false, false);
RegisterRawHandler(12, isr12, false, false);
RegisterRawHandler(13, isr13, false, false);
RegisterRawHandler(14, isr14, false, false);
RegisterRawHandler(15, isr15, false, false);
RegisterRawHandler(16, isr16, false, false);
RegisterRawHandler(17, isr17, false, false);
RegisterRawHandler(18, isr18, false, false);
RegisterRawHandler(19, isr19, false, false);
RegisterRawHandler(20, isr20, false, false);
RegisterRawHandler(21, isr21, false, false);
RegisterRawHandler(22, isr22, false, false);
RegisterRawHandler(23, isr23, false, false);
RegisterRawHandler(24, isr24, false, false);
RegisterRawHandler(25, isr25, false, false);
RegisterRawHandler(26, isr26, false, false);
RegisterRawHandler(27, isr27, false, false);
RegisterRawHandler(28, isr28, false, false);
RegisterRawHandler(29, isr29, false, false);
RegisterRawHandler(30, isr30, false, false);
RegisterRawHandler(31, isr31, false, false);
RegisterRawHandler(32, irq0, false, false);
RegisterRawHandler(33, irq1, false, false);
RegisterRawHandler(34, irq2, false, false);
RegisterRawHandler(35, irq3, false, false);
RegisterRawHandler(36, irq4, false, false);
RegisterRawHandler(37, irq5, false, false);
RegisterRawHandler(38, irq6, false, false);
RegisterRawHandler(39, irq7, false, false);
RegisterRawHandler(40, irq8, false, false);
RegisterRawHandler(41, irq9, false, false);
RegisterRawHandler(42, irq10, false, false);
RegisterRawHandler(43, irq11, false, false);
RegisterRawHandler(44, irq12, false, false);
RegisterRawHandler(45, irq13, false, false);
RegisterRawHandler(46, irq14, false, false);
RegisterRawHandler(47, irq15, false, false);
RegisterRawHandler(128, syscall_handler, true, true);
RegisterRawHandler(129, yield_cpu_handler, true, false);
RegisterRawHandler(130, isr130, true, false);
RegisterRawHandler(131, isr131, true, false);
RegisterRawHandler(132, thread_exit_handler, true, false);
RegisterHandler(129, Scheduler::InterruptYieldCPU, NULL);
RegisterHandler(130, Signal::Dispatch, NULL);
RegisterHandler(131, Signal::Return, NULL);
RegisterHandler(132, Scheduler::ThreadExitCPU, NULL);
IDT::Set(interrupt_table, NUM_INTERRUPTS);
Interrupt::Enable();
}
const char* ExceptionName(const CPU::InterruptRegisters* regs)
{
if ( regs->int_no < NUM_KNOWN_EXCEPTIONS )
return exception_names[regs->int_no];
return "Unknown";
}
uintptr_t ExceptionLocation(const CPU::InterruptRegisters* regs)
{
#if defined(__x86_64__)
return regs->rip;
#elif defined(__i386__)
return regs->eip;
#endif
}
void CrashCalltrace(const CPU::InterruptRegisters* regs)
{
#if defined(__x86_64__)
Calltrace::Perform(regs->rbp);
#elif defined(__i386__)
Calltrace::Perform(regs->ebp);
#else
#warning "Please provide a calltrace implementation for your CPU."
#endif
}
__attribute__((noreturn))
void KernelCrashHandler(CPU::InterruptRegisters* regs)
{
CurrentThread()->SaveRegisters(regs);
// Walk and print the stack frames if this is a debug build.
if ( CALLTRACE_KERNEL )
CrashCalltrace(regs);
// Possibly switch to the kernel debugger in event of a crash.
if ( RUN_DEBUGGER_ON_KERNEL_CRASH )
Debugger::Run();
// Panic the kernel with a diagnostic message.
PanicF("Unhandled CPU Exception id %zu `%s' at ip=0x%zx (cr2=0x%zx, "
"err_code=0x%zx)", regs->int_no, ExceptionName(regs),
ExceptionLocation(regs), regs->cr2, regs->err_code);
}
void UserCrashHandler(CPU::InterruptRegisters* regs)
{
CurrentThread()->SaveRegisters(regs);
// Execute this crash handler with preemption on.
Interrupt::Enable();
// Walk and print the stack frames if this is a debug build.
if ( CALLTRACE_USER )
CrashCalltrace(regs);
// Possibly switch to the kernel debugger in event of a crash.
if ( RUN_DEBUGGER_ON_USER_CRASH )
Debugger::Run();
// Issue a diagnostic message to the kernel log concerning the crash.
Log::PrintF("The current process (pid %ji `%s') crashed and was terminated:\n",
(intmax_t) CurrentProcess()->pid, CurrentProcess()->program_image_path);
Log::PrintF("%s exception at ip=0x%zx (cr2=0x%zx, err_code=0x%zx)\n",
ExceptionName(regs), ExceptionLocation(regs), regs->cr2,
regs->err_code);
// Exit the process with the right error code.
// TODO: Send a SIGINT, SIGBUS, or whatever instead.
CurrentProcess()->Exit(139);
// TODO: Is it strictly needed or even desirable to disable preemption here?
Interrupt::Disable();
// Deliver signals to this thread so it can exit correctly.
Signal::Dispatch(regs);
}
extern "C" void interrupt_handler(CPU::InterruptRegisters* regs)
{
unsigned int int_no = regs->int_no;
// IRQ 7 and 15 might be spurious and might need to be ignored.
if ( int_no == IRQ7 && !(PIC::ReadISR() & (1 << 7)) )
return;
if ( int_no == IRQ15 && !(PIC::ReadISR() & (1 << 15)) )
{
PIC::SendMasterEOI();
return;
}
bool is_in_kernel = (regs->cs & 0x3) == KRPL;
bool is_in_user = !is_in_kernel;
bool is_crash = int_no < 32 && int_no != 7;
// Invoke the appropriate interrupt handler.
if ( is_crash && is_in_kernel )
KernelCrashHandler(regs);
else if ( is_crash && is_in_user )
UserCrashHandler(regs);
else if ( interrupt_handlers[int_no] )
interrupt_handlers[int_no](regs, interrupt_handler_context[int_no]);
// Send an end of interrupt signal to the PICs if we got an IRQ.
if ( IRQ0 <= int_no && int_no <= IRQ15 )
PIC::SendEOI(int_no - IRQ0);
}
} // namespace Interrupt
} // namespace Sortix

122
kernel/x86-family/pic.cpp Normal file
View File

@ -0,0 +1,122 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
Sortix 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 (at your option) any later
version.
Sortix 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
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/pic.cpp
Driver for the Programmable Interrupt Controller.
*******************************************************************************/
#include <stdint.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/interrupt.h>
#include "pic.h"
namespace Sortix {
namespace PIC {
const uint16_t PIC_MASTER = 0x20;
const uint16_t PIC_SLAVE = 0xA0;
const uint16_t PIC_COMMAND = 0x00;
const uint16_t PIC_DATA = 0x01;
const uint8_t PIC_CMD_ENDINTR = 0x20;
const uint8_t PIC_ICW1_ICW4 = 0x01; // ICW4 (not) needed
const uint8_t PIC_ICW1_SINGLE = 0x02; // Single (cascade) mode
const uint8_t PIC_ICW1_INTERVAL4 = 0x04; // Call address interval 4 (8)
const uint8_t PIC_ICW1_LEVEL = 0x08; // Level triggered (edge) mode
const uint8_t PIC_CMD_INIT = 0x10;
const uint8_t PIC_MODE_8086 = 0x01; // 8086/88 (MCS-80/85) mode
const uint8_t PIC_MODE_AUTO = 0x02; // Auto (normal) EOI
const uint8_t PIC_MODE_BUF_SLAVE = 0x08; // Buffered mode/slave
const uint8_t PIC_MODE_BUF_MASTER = 0x0C; // Buffered mode/master
const uint8_t PIC_MODE_SFNM = 0x10; // Special fully nested (not)
const uint8_t PIC_READ_IRR = 0x0A;
const uint8_t PIC_READ_ISR = 0x0B;
static uint16_t ReadRegister(uint8_t ocw3)
{
CPU::OutPortB(PIC_MASTER + PIC_COMMAND, ocw3);
CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, ocw3);
return CPU::InPortB(PIC_MASTER + PIC_COMMAND) << 0 |
CPU::InPortB(PIC_SLAVE + PIC_COMMAND) << 8;
}
uint16_t ReadIRR()
{
return ReadRegister(PIC_READ_IRR);
}
uint16_t ReadISR()
{
return ReadRegister(PIC_READ_ISR);
}
extern "C" void ReprogramPIC()
{
uint8_t master_mask = 0;
uint8_t slave_mask = 0;
CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_MASTER + PIC_DATA, Interrupt::IRQ0);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, Interrupt::IRQ8);
CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2
CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity
CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask);
}
extern "C" void DeprogramPIC()
{
uint8_t master_mask = 0;
uint8_t slave_mask = 0;
CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4);
CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x08);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x70);
CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2
CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity
CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086);
CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask);
}
void SendMasterEOI()
{
CPU::OutPortB(PIC_MASTER, PIC_CMD_ENDINTR);
}
void SendSlaveEOI()
{
CPU::OutPortB(PIC_SLAVE, PIC_CMD_ENDINTR);
}
void SendEOI(unsigned int irq)
{
if ( 8 <= irq )
SendSlaveEOI();
SendMasterEOI();
}
} // namespace PIC
} // namespace Sortix

43
kernel/x86-family/pic.h Normal file
View File

@ -0,0 +1,43 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
Sortix 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 (at your option) any later
version.
Sortix 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
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/pic.h
Driver for the Programmable Interrupt Controller.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_PIC_H
#define SORTIX_X86_FAMILY_PIC_H
namespace Sortix {
namespace PIC {
uint16_t ReadIRR();
uint16_t ReadISR();
bool IsSpuriousIRQ(unsigned int irq);
extern "C" void ReprogramPIC();
extern "C" void DeprogramPIC();
void SendMasterEOI();
void SendSlaveEOI();
void SendEOI(unsigned int irq);
} // namespace PIC
} // namespace Sortix
#endif

View File

@ -27,371 +27,318 @@
.global isr0
.type isr0, @function
isr0:
cli
pushl $0 # err_code
pushl $0 # int_no
jmp interrupt_handler_prepare
.global isr1
.type isr1, @function
isr1:
cli
pushl $0 # err_code
pushl $1 # int_no
jmp interrupt_handler_prepare
.global isr2
.type isr2, @function
isr2:
cli
pushl $0 # err_code
pushl $2 # int_no
jmp interrupt_handler_prepare
.global isr3
.type isr3, @function
isr3:
cli
pushl $0 # err_code
pushl $3 # int_no
jmp interrupt_handler_prepare
.global isr4
.type isr4, @function
isr4:
cli
pushl $0 # err_code
pushl $4 # int_no
jmp interrupt_handler_prepare
.global isr5
.type isr5, @function
isr5:
cli
pushl $0 # err_code
pushl $5 # int_no
jmp interrupt_handler_prepare
.global isr6
.type isr6, @function
isr6:
cli
pushl $0 # err_code
pushl $6 # int_no
jmp interrupt_handler_prepare
.global isr7
.type isr7, @function
isr7:
cli
pushl $0 # err_code
pushl $7 # int_no
jmp interrupt_handler_prepare
.global isr8
.type isr8, @function
isr8:
cli
# pushl $0 # err_code pushed by CPU
pushl $8 # int_no
jmp interrupt_handler_prepare
.global isr9
.type isr9, @function
isr9:
cli
pushl $0 # err_code
pushl $9 # int_no
jmp interrupt_handler_prepare
.global isr10
.type isr10, @function
isr10:
cli
# pushl $0 # err_code pushed by CPU
pushl $10 # int_no
jmp interrupt_handler_prepare
.global isr11
.type isr11, @function
isr11:
cli
# pushl $0 # err_code pushed by CPU
pushl $11 # int_no
jmp interrupt_handler_prepare
.global isr12
.type isr12, @function
isr12:
cli
# pushl $0 # err_code pushed by CPU
pushl $12 # int_no
jmp interrupt_handler_prepare
.global isr13
.type isr13, @function
isr13:
cli
# pushl $0 # err_code pushed by CPU
pushl $13 # int_no
jmp interrupt_handler_prepare
.global isr14
.type isr14, @function
isr14:
cli
# pushl $0 # err_code pushed by CPU
pushl $14 # int_no
jmp interrupt_handler_prepare
.global isr15
.type isr15, @function
isr15:
cli
pushl $0 # err_code
pushl $15 # int_no
jmp interrupt_handler_prepare
.global isr16
.type isr16, @function
isr16:
cli
pushl $0 # err_code
pushl $16 # int_no
jmp interrupt_handler_prepare
.global isr17
.type isr17, @function
isr17:
cli
pushl $0 # err_code
pushl $17 # int_no
jmp interrupt_handler_prepare
.global isr18
.type isr18, @function
isr18:
cli
pushl $0 # err_code
pushl $18 # int_no
jmp interrupt_handler_prepare
.global isr19
.type isr19, @function
isr19:
cli
pushl $0 # err_code
pushl $19 # int_no
jmp interrupt_handler_prepare
.global isr20
.type isr20, @function
isr20:
cli
pushl $0 # err_code
pushl $20 # int_no
jmp interrupt_handler_prepare
.global isr21
.type isr21, @function
isr21:
cli
pushl $0 # err_code
pushl $21 # int_no
jmp interrupt_handler_prepare
.global isr22
.type isr22, @function
isr22:
cli
pushl $0 # err_code
pushl $22 # int_no
jmp interrupt_handler_prepare
.global isr23
.type isr23, @function
isr23:
cli
pushl $0 # err_code
pushl $23 # int_no
jmp interrupt_handler_prepare
.global isr24
.type isr24, @function
isr24:
cli
pushl $0 # err_code
pushl $24 # int_no
jmp interrupt_handler_prepare
.global isr25
.type isr25, @function
isr25:
cli
pushl $0 # err_code
pushl $25 # int_no
jmp interrupt_handler_prepare
.global isr26
.type isr26, @function
isr26:
cli
pushl $0 # err_code
pushl $26 # int_no
jmp interrupt_handler_prepare
.global isr27
.type isr27, @function
isr27:
cli
pushl $0 # err_code
pushl $27 # int_no
jmp interrupt_handler_prepare
.global isr28
.type isr28, @function
isr28:
cli
pushl $0 # err_code
pushl $28 # int_no
jmp interrupt_handler_prepare
.global isr29
.type isr29, @function
isr29:
cli
pushl $0 # err_code
pushl $29 # int_no
jmp interrupt_handler_prepare
.global isr30
.type isr30, @function
isr30:
cli
pushl $0 # err_code
pushl $30 # int_no
jmp interrupt_handler_prepare
.global isr31
.type isr31, @function
isr31:
cli
pushl $0 # err_code
pushl $31 # int_no
jmp interrupt_handler_prepare
.global isr128
.type isr128, @function
isr128:
cli
pushl $0 # err_code
pushl $128 # int_no
jmp interrupt_handler_prepare
.global isr130
.type isr130, @function
isr130:
cli
pushl $0 # err_code
pushl $130 # int_no
jmp interrupt_handler_prepare
.global isr131
.type isr131, @function
isr131:
cli
pushl $0 # err_code
pushl $131 # int_no
jmp interrupt_handler_prepare
.global irq0
.type irq0, @function
irq0:
cli
pushl $0 # err_code
pushl $32 # int_no
jmp interrupt_handler_prepare
.global irq1
.type irq1, @function
irq1:
cli
pushl $0 # err_code
pushl $33 # int_no
jmp interrupt_handler_prepare
.global irq2
.type irq2, @function
irq2:
cli
pushl $0 # err_code
pushl $34 # int_no
jmp interrupt_handler_prepare
.global irq3
.type irq3, @function
irq3:
cli
pushl $0 # err_code
pushl $35 # int_no
jmp interrupt_handler_prepare
.global irq4
.type irq4, @function
irq4:
cli
pushl $0 # err_code
pushl $36 # int_no
jmp interrupt_handler_prepare
.global irq5
.type irq5, @function
irq5:
cli
pushl $0 # err_code
pushl $37 # int_no
jmp interrupt_handler_prepare
.global irq6
.type irq6, @function
irq6:
cli
pushl $0 # err_code
pushl $38 # int_no
jmp interrupt_handler_prepare
.global irq7
.type irq7, @function
irq7:
cli
pushl $0 # err_code
pushl $39 # int_no
jmp interrupt_handler_prepare
.global irq8
.type irq8, @function
irq8:
cli
pushl $0 # err_code
pushl $40 # int_no
jmp interrupt_handler_prepare
.global irq9
.type irq9, @function
irq9:
cli
pushl $0 # err_code
pushl $41 # int_no
jmp interrupt_handler_prepare
.global irq10
.type irq10, @function
irq10:
cli
pushl $0 # err_code
pushl $42 # int_no
jmp interrupt_handler_prepare
.global irq11
.type irq11, @function
irq11:
cli
pushl $0 # err_code
pushl $43 # int_no
jmp interrupt_handler_prepare
.global irq12
.type irq12, @function
irq12:
cli
pushl $0 # err_code
pushl $44 # int_no
jmp interrupt_handler_prepare
.global irq13
.type irq13, @function
irq13:
cli
pushl $0 # err_code
pushl $45 # int_no
jmp interrupt_handler_prepare
.global irq14
.type irq14, @function
irq14:
cli
pushl $0 # err_code
pushl $46 # int_no
jmp interrupt_handler_prepare
.global irq15
.type irq15, @function
irq15:
cli
pushl $0 # err_code
pushl $47 # int_no
jmp interrupt_handler_prepare
.global yield_cpu_handler
.type yield_cpu_handler, @function
yield_cpu_handler:
cli
pushl $0 # err_code
pushl $129 # int_no
jmp interrupt_handler_prepare
.global thread_exit_handler
.type thread_exit_handler, @function
thread_exit_handler:
cli
pushl $0 # err_code
pushl $132 # int_no
jmp interrupt_handler_prepare
@ -580,15 +527,6 @@ interrupt_handler_null:
iret
.size interrupt_handler_null, . - interrupt_handler_null
.global asm_interrupts_are_enabled
.type asm_interrupts_are_enabled, @function
asm_interrupts_are_enabled:
pushfl
popl %eax
andl $0x000200, %eax # FLAGS_INTERRUPT
retl
.size asm_interrupts_are_enabled, . - asm_interrupts_are_enabled
.global load_registers
.type load_registers, @function
load_registers:

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
This file is part of Sortix.
@ -27,11 +27,6 @@
.section .text
.type syscall_handler, @function
syscall_handler:
# The processor disabled interrupts during the int $0x80 instruction,
# however Sortix system calls runs with interrupts enabled such that they
# can be pre-empted.
sti
movl $0, global_errno # Reset errno
pushl %ebp