/* We first remap the interrupt controllers, and then we install * the appropriate ISRs to the correct entries in the IDT. This * is just like installing the exception handlers */ void irq::init() { debug_bochs_printf( "irq_install... " ); irq_remap(); int sel = 0x08; idt::set_gate(32, (unsigned)irq0, sel, 0x8E); idt::set_gate(33, (unsigned)irq1, sel, 0x8E); idt::set_gate(34, (unsigned)irq2, sel, 0x8E); idt::set_gate(35, (unsigned)irq3, sel, 0x8E); idt::set_gate(36, (unsigned)irq4, sel, 0x8E); idt::set_gate(37, (unsigned)irq5, sel, 0x8E); idt::set_gate(38, (unsigned)irq6, sel, 0x8E); idt::set_gate(39, (unsigned)irq7, sel, 0x8E); idt::set_gate(40, (unsigned)irq8, sel, 0x8E); idt::set_gate(41, (unsigned)irq9, sel, 0x8E); idt::set_gate(42, (unsigned)irq10, sel, 0x8E); idt::set_gate(43, (unsigned)irq11, sel, 0x8E); idt::set_gate(44, (unsigned)irq12, sel, 0x8E); idt::set_gate(45, (unsigned)irq13, sel, 0x8E); idt::set_gate(46, (unsigned)irq14, sel, 0x8E); idt::set_gate(47, (unsigned)irq15, sel, 0x8E); debug_bochs_printf( "done\n" ); }
/* Installs the IDT */ void idt::init() { debug_bochs_printf( "idt::init..." ); struct idt_ptr idtp = { (sizeof s_idt) - 1, s_idt }; debug_bochs_printf( "Installing IDT from ptr %x, base = %x\n", &idtp, idtp.base ); idt_load( &idtp ); }
/* Each of the IRQ ISRs point to this function, rather than * the 'fault_handler' in 'isrs.c'. The IRQ Controllers need * to be told when you are done servicing them, so you need * to send them an "End of Interrupt" command (0x20). There * are two 8259 chips: The first exists at 0x20, the second * exists at 0xA0. If the second controller (an IRQ from 8 to * 15) gets an interrupt, you need to acknowledge the * interrupt at BOTH controllers, otherwise, you only send * an EOI command to the first controller. If you don't send * an EOI, you won't raise any more IRQs */ extern "C" struct cpu_state* irq_handler( struct cpu_state *r ) { /* Find out if we have a custom handler to run for this * IRQ, and then finally, run it */ irq::handler_proc handler = irq_routines[r->int_no - 32]; if (handler) { r = handler(r); } else { debug_bochs_printf( "No handler registered for IRQ %x\n", r->int_no - 32 ); } /* If the IDT entry that was invoked was greater than 40 * (meaning IRQ8 - 15), then we need to send an EOI to * the slave controller */ if (r->int_no >= 40) { outportb(0xA0, 0x20); } /* In either case, we need to send an EOI to the master * interrupt controller too */ outportb(0x20, 0x20); return r; }
void mm::physical::init( const multiboot_info* mbt, uintptr_t kernel_static_end, uintptr_t kernel_dynamic_end, uintptr_t himem_end ) { if( !himem_end ) { PANIC( "NO MEMORY FOUND!\n" ); } size_t dma_pool_size = (DMA_MEMORY_LIMIT < kernel_dynamic_end) ? DMA_MEMORY_LIMIT : kernel_dynamic_end; size_t default_pool_size = kernel_dynamic_end - dma_pool_size; size_t himem_pool_size = himem_end - kernel_dynamic_end; size_t dma_pool_pages = dma_pool_size / PAGE_SIZE; size_t default_pool_pages = default_pool_size / PAGE_SIZE; size_t himem_pool_pages = himem_pool_size / PAGE_SIZE; int* dma_pool_bitmap = linear_alloc<int>( dma_pool_pages * sizeof(int), 16, &kernel_static_end ); int* default_pool_bitmap = linear_alloc<int>( default_pool_pages * sizeof(int), 16, &kernel_static_end ); int* himem_pool_bitmap = linear_alloc<int>( himem_pool_pages * sizeof(int), 16, &kernel_static_end ); uintptr_t dma_pool_start = 0; uintptr_t default_pool_start = dma_pool_start + dma_pool_size; uintptr_t himem_pool_start = default_pool_start + default_pool_size; DMAPool.init( dma_pool_start, dma_pool_pages, dma_pool_bitmap ); DefaultPool.init( default_pool_start, default_pool_pages, default_pool_bitmap ); HiMemPool.init( himem_pool_start, himem_pool_pages, himem_pool_bitmap ); debug_bochs_printf( "Physical memory layout:\n" ); debug_bochs_printf( " DMA memory %x-%x\n", DMAPool.base(), DMAPool.limit() ); debug_bochs_printf( " Default memory %x-%x\n", DefaultPool.base(), DefaultPool.limit() ); debug_bochs_printf( " High memory %x-%x\n", HiMemPool.base(), HiMemPool.limit() ); multiboot::iterate_mmap( mbt, [](const multiboot_mmap_entry* mmap) { if( !(mmap->type & MULTIBOOT_MEMORY_AVAILABLE) ) { //console.printf( "Found reserved memory %lx - %lx\n", mmap->addr, mmap->addr + mmap->len ); reserve_range( mmap->addr, mmap->len ); } } ); // Reserve kernel code + pmem bitmaps (added above to kernel_static_end) reserve_range( 0, kernel_static_end ); }
uintptr_t mm::physical::alloc_himem(unsigned int pages, unsigned int align) { auto p = HiMemPool.alloc(pages, align); if( !p ) debug_bochs_printf( "mm::physical::alloc_himem: OUT OF MEMORY\n" ); return p; }
/* This is a very repetitive function... it's not hard, it's * just annoying. As you can see, we set the first 32 entries * in the IDT to the first 32 ISRs. We can't use a for loop * for this, because there is no way to get the function names * that correspond to that given entry. We set the access * flags to 0x8E. This means that the entry is present, is * running in ring 0 (kernel level), and has the lower 5 bits * set to the required '14', which is represented by 'E' in * hex. */ void isr::init() { debug_bochs_printf( "isrs_install... " ); int sel = 0x08; idt::set_gate(0, (unsigned)isr0, sel, 0x8E); idt::set_gate(1, (unsigned)isr1, sel, 0x8E); idt::set_gate(2, (unsigned)isr2, sel, 0x8E); idt::set_gate(3, (unsigned)isr3, sel, 0x8E); idt::set_gate(4, (unsigned)isr4, sel, 0x8E); idt::set_gate(5, (unsigned)isr5, sel, 0x8E); idt::set_gate(6, (unsigned)isr6, sel, 0x8E); idt::set_gate(7, (unsigned)isr7, sel, 0x8E); idt::set_gate(8, (unsigned)isr8, sel, 0x8E); idt::set_gate(9, (unsigned)isr9, sel, 0x8E); idt::set_gate(10, (unsigned)isr10, sel, 0x8E); idt::set_gate(11, (unsigned)isr11, sel, 0x8E); idt::set_gate(12, (unsigned)isr12, sel, 0x8E); idt::set_gate(13, (unsigned)isr13, sel, 0x8E); idt::set_gate(14, (unsigned)isr14, sel, 0x8E); idt::set_gate(15, (unsigned)isr15, sel, 0x8E); idt::set_gate(16, (unsigned)isr16, sel, 0x8E); idt::set_gate(17, (unsigned)isr17, sel, 0x8E); idt::set_gate(18, (unsigned)isr18, sel, 0x8E); idt::set_gate(19, (unsigned)isr19, sel, 0x8E); idt::set_gate(20, (unsigned)isr20, sel, 0x8E); idt::set_gate(21, (unsigned)isr21, sel, 0x8E); idt::set_gate(22, (unsigned)isr22, sel, 0x8E); idt::set_gate(23, (unsigned)isr23, sel, 0x8E); idt::set_gate(24, (unsigned)isr24, sel, 0x8E); idt::set_gate(25, (unsigned)isr25, sel, 0x8E); idt::set_gate(26, (unsigned)isr26, sel, 0x8E); idt::set_gate(27, (unsigned)isr27, sel, 0x8E); idt::set_gate(28, (unsigned)isr28, sel, 0x8E); idt::set_gate(29, (unsigned)isr29, sel, 0x8E); idt::set_gate(30, (unsigned)isr30, sel, 0x8E); idt::set_gate(31, (unsigned)isr31, sel, 0x8E); debug_bochs_printf( "done\n" ); }
cpu_state* syscall_handler( cpu_state* s ) { switch( (syscall::ID)s->eax ) { case syscall::ID::Yield: return scheduler::task_yield( s ); default: debug_bochs_printf( "syscall(%d)\n", s->eax ); return s; } }
/* All of our Exception handling Interrupt Service Routines will * point to this function. This will tell us what exception has * happened! Right now, we simply halt the system by hitting an * endless loop. All ISRs disable interrupts while they are being * serviced as a 'locking' mechanism to prevent an IRQ from * happening and messing up kernel data structures */ extern "C" void fault_handler(struct cpu_state *r) { if (r->int_no < 32) { bool handled = false; switch( r->int_no ) { case 14: // Page fault handled = paging_handle_fault( r, read_cr2() ); break; default: debug_bochs_printf( "EXCEPTION!" ); debug_bochs_printf( " (%x) (err_code = %x)\n", r->int_no, r->err_code ); debug_bochs_printf( "%s Exception\n", exception_messages[r->int_no] ); break; } if( !handled ) { debug_bochs_printf( "\nRegister dump:\n" ); /* unsigned int gs, fs, es, ds; unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax; unsigned int int_no, err_code; unsigned int eip, cs, eflags, useresp, ss; */ debug_bochs_printf( "EAX = %x EBX = %x ECX = %x EDX = %x\n", r->eax, r->ebx, r->ecx, r->edx ); debug_bochs_printf( "ESP = %x EBP = %x ESI = %x EDI = %x\n", r->esp, r->ebp, r->esi, r->edi ); debug_bochs_printf( "GS = %x FS = %x ES = %x DS = %x\n", r->gs, r->fs, r->es, r->ds ); debug_bochs_printf( "EIP = %x CS = %x EFLAGS = %x SS = %x\n", r->eip, r->cs, r->eflags, r->ss ); debug_bochs_printf( "System Halted!\n" ); for (;;); } } }
/* Installs the keyboard handler into IRQ1 */ void keyboard::init( void ) { debug_bochs_printf( "keyboard_install..." ); irq::install_handler(1, keyboard_handler); debug_bochs_printf( "done\n" ); }
/* Handles the keyboard interrupt */ struct cpu_state* keyboard_handler(struct cpu_state *r) { unsigned char scancode; /* Read from the keyboard's data buffer */ scancode = inportb(0x60); /* If the top bit of the byte we read from the keyboard is * set, that means that a key has just been released */ if (scancode & 0x80) { scancode &= ~0x80; /* You can use this one to see if the user released the * shift, alt, or control keys... */ if(scancode == 0x2a || scancode == 0x36) shift = false; } else { /* Here, a key was just pressed. Please note that if you * hold a key down, you will get repeated key press * interrupts. */ //2a = left shift //3a = capslock //36 = right shift if(scancode == 0x2a || scancode == 0x36) shift = true; /* Just to show you how this works, we simply translate * the keyboard scancode into an ASCII value, and then * display it to the screen. You can get creative and * use some flags to see if a shift is pressed and use a * different layout, or you can add another 128 entries * to the above layout to correspond to 'shift' being * held. If shift is held using the larger lookup table, * you would add 128 to the scancode when you look for it */ //The LEDs and the lockflags... handle_lock_flags(scancode); handle_kbdleds(); debug_bochs_printf( "%d", scancode ); if(kbdus[scancode] != 0) { if(!shift) { console.printf( "%c", kbdger[scancode] ); } else { console.printf( "%c", kbdger_upper[scancode] ); } } } return r; }