From d6d1bdfefafff50b7b6d15d218c0a188570be541 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 10 Jun 2017 21:26:24 +0200 Subject: 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 --- src/hardware/interrupts.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 src/hardware/interrupts.c (limited to 'src/hardware/interrupts.c') 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( ÷_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; +} -- cgit v1.2.3-54-g00ecf