summaryrefslogtreecommitdiff
path: root/src/hardware/interrupts.c
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2017-06-10 21:26:24 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2017-06-10 21:26:24 +0200
commitd6d1bdfefafff50b7b6d15d218c0a188570be541 (patch)
tree15ee8de727d0be5d126efda146b2879de0a72773 /src/hardware/interrupts.c
parenteea5bf4b859eb56c5772c58ca54937a90a10e7ee (diff)
downloadabaos-d6d1bdfefafff50b7b6d15d218c0a188570be541.tar.gz
abaos-d6d1bdfefafff50b7b6d15d218c0a188570be541.tar.bz2
some big renames into subdirs of aspects
updated README removed size_t in sys/types.h and sys/types.h itself, size_t is in stddef.h
Diffstat (limited to 'src/hardware/interrupts.c')
-rw-r--r--src/hardware/interrupts.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/hardware/interrupts.c b/src/hardware/interrupts.c
new file mode 100644
index 0000000..614dbf0
--- /dev/null
+++ b/src/hardware/interrupts.c
@@ -0,0 +1,215 @@
+#include "interrupts.h"
+
+#include "string.h"
+#include "stdio.h"
+
+#include "kernel.h"
+
+#include "keyboard.h"
+
+// TODO: for now the code segment is the first entry in the boot GDT
+#define GDT_CODE_SEGMENT_SELECTOR 8
+
+// the Present bit in a IDT entry
+#define IDT_PRESENT_BIT 0x80
+
+// DPL rings
+#define KERNEL_RING 0
+
+// types of IDT entries
+#define IDT_TYPE_INTERRUPT_GATE 0xE
+
+// PIC constants
+
+#define ICW1_ICW4 0x01 // support a 4th initialization word (ICW4)
+#define ICW1_INIT 0x10 // bit indicating initialization of chip
+
+// offset for hardware interrupts (for PIC ICW2)
+#define ICW2_IRQ_BASE_MASTER IRQ_BASE
+#define ICW2_IRQ_BASE_SLAVE ICW2_IRQ_BASE_MASTER + 8
+
+// IRQ selection (master and slave, ICW3)
+#define ICW3_MASTER_IR0 0x01
+#define ICW3_MASTER_IR1 0x02
+#define ICW3_MASTER_IR2 0x04
+#define ICW3_MASTER_IR3 0x08
+#define ICW3_MASTER_IR4 0x10
+#define ICW3_MASTER_IR5 0x20
+#define ICW3_MASTER_IR6 0x40
+#define ICW3_MASTER_IR7 0x80
+#define ICW3_SLAVE_IR0 0x00
+#define ICW3_SLAVE_IR1 0x01
+#define ICW3_SLAVE_IR2 0x02
+#define ICW3_SLAVE_IR3 0x03
+#define ICW3_SLAVE_IR4 0x04
+#define ICW3_SLAVE_IR5 0x05
+#define ICW3_SLAVE_IR6 0x06
+#define ICW3_SLAVE_IR7 0x07
+
+// special mode PIC ICW4
+#define ICW4_8086 0x01
+#define ICW4_AUTO_ACK_EOI 0x02
+#define ICW4_BUF_MASTER 0x04
+#define ICW4_BUFFER 0x08
+#define ICW4_FULLY_NESTED 0x10
+
+// OCW2
+#define OCW2_EOI 0x20 // non-specific End Of Interrupt
+
+void interrupt_handler_init_void( interrupt_handler_t *handler )
+{
+ memset( handler, 0, sizeof( interrupt_handler_t ) );
+}
+
+void interrupt_handler_init( interrupt_handler_t *handler, uint8_t interrupt_no, struct interrupt_t *interrupt, interrupt_handler_func_t handle, void *driver )
+{
+ memset( handler, 0, sizeof( interrupt_handler_t ) );
+
+ handler->interrupt_no = interrupt_no;
+ handler->interrupt = interrupt;
+ handler->handle = handle;
+ handler->driver = driver;
+}
+
+// must be global: when called via assembly code we loose the context
+// of the interrupt handler structure
+static interrupt_t *active_interrupt;
+
+void interrupts_init( interrupt_t *interrupt )
+{
+ memset( interrupt, 0, sizeof( interrupt_t ) );
+
+ interrupt_handler_t empty_interrupt_handler;
+ interrupt_handler_init_void( &empty_interrupt_handler );
+
+ for( int i = 0; i < NOF_INTERRUPTS; i++ ) {
+ interrupts_register_interrupt_gate( interrupt, i, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_ignore_request, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ interrupts_register_interrupt_handler( empty_interrupt_handler );
+ }
+
+ // divide-by-zero exception 0x00
+ interrupt_handler_t divide_by_zero_interrupt_handler;
+ interrupt_handler_init( &divide_by_zero_interrupt_handler, 0x00, interrupt, interrupts_exception_division_by_zero, NULL );
+
+ interrupts_register_interrupt_gate( interrupt, 0x00, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_exception_0x00, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ interrupts_register_interrupt_handler( divide_by_zero_interrupt_handler );
+
+ // IRQ 0 - PIT - programmable interrupt timer
+ interrupt_handler_t pit_interrupt_handler;
+ interrupt_handler_init( &pit_interrupt_handler, IRQ_BASE + 0x00, interrupt, interrupts_interrupt_PIT, NULL );
+
+ interrupts_register_interrupt_gate( interrupt, IRQ_BASE + 0x00, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_irq_0x00, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ interrupts_register_interrupt_handler( pit_interrupt_handler );
+
+ // IRQ 1 - PS/2 keyboard
+ interrupts_register_interrupt_gate( interrupt, IRQ_BASE + 0x01, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_irq_0x01, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+
+ // IRQ 12 - PS/2 mouse
+ interrupts_register_interrupt_gate( interrupt, IRQ_BASE + 0x0C, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_irq_0x0C, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+
+ port8_init( &interrupt->PIC_master_control, 0x20 );
+ port8_init( &interrupt->PIC_master_data, 0x21 );
+ port8_init( &interrupt->PIC_slave_control, 0xA0 );
+ port8_init( &interrupt->PIC_slave_data, 0xA1 );
+
+ // initialize hardware management PICs (ICW1)
+ port8_write( &interrupt->PIC_master_control, ICW1_ICW4 | ICW1_INIT );
+ port8_write( &interrupt->PIC_slave_control, ICW1_ICW4 | ICW1_INIT );
+
+ // set IRQ base of both PICS (ICW2), remap them so they
+ // don't collide with CPU exceptions (this is true after 0x20)
+ port8_write( &interrupt->PIC_master_data, ICW2_IRQ_BASE_MASTER );
+ port8_write( &interrupt->PIC_slave_data, ICW2_IRQ_BASE_SLAVE );
+
+ // use IRQ2 for master slave communication (ICW3)
+ port8_write( &interrupt->PIC_master_data, ICW3_MASTER_IR2 );
+ port8_write( &interrupt->PIC_slave_data, ICW3_SLAVE_IR2 );
+
+ // unbuffered, manual acknoledgment and 8086 mode (ICW4)
+ // TODO: use buffered mode later?
+ port8_write( &interrupt->PIC_master_data, ICW4_8086 );
+ port8_write( &interrupt->PIC_slave_data, ICW4_8086 );
+
+ // done, enable all IRQs by setting the interrupt mask register
+ // 0 for all bits. We are not interested in the old state of
+ // the mask as we are initializing in protected mode
+ // TODO: later we will mask uninmported interrupts maybe?
+ port8_write( &interrupt->PIC_master_data, 0 );
+ port8_write( &interrupt->PIC_slave_data, 0 );
+
+ // tell CPU where the IDT is
+ interrupt->idt_pointer.size = NOF_INTERRUPTS * sizeof( interrupt_gate_descriptor_t ) - 1;
+ interrupt->idt_pointer.base = (uint32_t)interrupt->descriptor_table;
+
+ interrupts_load_idt( &interrupt->idt_pointer );
+
+ active_interrupt = interrupt;
+}
+
+void interrupts_register_interrupt_gate( interrupt_t *interrupts,
+ uint8_t interrupt_no, uint16_t gdt_code_segment_selector,
+ void (*helper_handler)( ), uint8_t privilege_level, uint8_t descriptor_type )
+{
+ interrupt_gate_descriptor_t *descr = &interrupts->descriptor_table[interrupt_no];
+
+ descr->handler_address_low_bits = ( (uint32_t )helper_handler ) & 0xFFFF;
+ descr->handler_address_high_bits = ( ( (uint32_t )helper_handler ) >> 16 ) & 0xFFFF;
+ descr->gdt_code_segment_selector = gdt_code_segment_selector;
+ descr->access = IDT_PRESENT_BIT | (( privilege_level & 0x3 ) << 5 ) | descriptor_type;
+ descr->reserved = 0;
+}
+
+void interrupts_register_interrupt_handler( interrupt_handler_t handler )
+{
+ handler.interrupt->interrupt_handler[handler.interrupt_no] = handler;
+}
+
+uint32_t interrupts_exception_division_by_zero( interrupt_handler_t *handler, uint32_t esp )
+{
+ // TODO: caused by kernel code or user land code? or a task we can kill?
+ // For now, the kernel has no task management, so we panic
+ kernel_panic( "Division by zero with ESP 0x%X\n", esp );
+
+ return esp;
+}
+
+uint32_t interrupts_interrupt_PIT( interrupt_handler_t *handler, uint32_t esp )
+{
+ // for now do noting with the timer tick
+ return esp;
+}
+
+uint32_t interrupts_handle_static_interrupt( uint8_t interrupt_no, uint32_t esp )
+{
+ if( active_interrupt == NULL ) {
+ kernel_panic( "No active interrupt handler!" );
+ }
+
+ return interrupts_handle_interrupt( active_interrupt, interrupt_no, esp );
+}
+
+uint32_t interrupts_handle_interrupt( interrupt_t *interrupt, uint8_t interrupt_no, uint32_t esp )
+{
+ interrupt_handler_t *handler = &interrupt->interrupt_handler[interrupt_no];
+
+ if( handler->handle == NULL ) {
+ kernel_panic( "Unhandled interrupt 0x%X with ESP 0x%X\n", interrupt_no, esp );
+ }
+
+ uint32_t new_esp = handler->handle( handler, esp );
+
+ // send ACK to PIC for hardware interrups
+ if( interrupt_no >= IRQ_BASE && interrupt_no <= IRQ_BASE + 16 ) {
+ port8_write( &interrupt->PIC_master_control, OCW2_EOI );
+ if( interrupt_no >= IRQ_BASE + 8 ) {
+ port8_write( &interrupt->PIC_slave_control, OCW2_EOI );
+ }
+ }
+
+ return new_esp;
+}