From a9ca75c98b8e775cb48204c56d6ad02b272d6767 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 22 Oct 2018 12:58:29 +0200 Subject: [PATCH] Add IRQHandler class that can be subclasses to handle an IRQ. Also move Keyboard to a class implementation using this pattern. --- Kernel/IRQHandler.cpp | 25 +++++++++++++ Kernel/IRQHandler.h | 21 +++++++++++ Kernel/Keyboard.cpp | 84 +++++++++---------------------------------- Kernel/Keyboard.h | 22 ++++++------ Kernel/Makefile | 3 +- Kernel/i386.cpp | 65 +++++++++++++++++++++++++++++++++ Kernel/i386.h | 4 +++ Kernel/init.cpp | 28 +++++---------- 8 files changed, 152 insertions(+), 100 deletions(-) create mode 100644 Kernel/IRQHandler.cpp create mode 100644 Kernel/IRQHandler.h diff --git a/Kernel/IRQHandler.cpp b/Kernel/IRQHandler.cpp new file mode 100644 index 00000000000000..187a25483d9f39 --- /dev/null +++ b/Kernel/IRQHandler.cpp @@ -0,0 +1,25 @@ +#include "IRQHandler.h" +#include "i386.h" +#include "PIC.h" + +IRQHandler::IRQHandler(byte irq) + : m_irqNumber(irq) +{ + registerIRQHandler(m_irqNumber, *this); +} + +IRQHandler::~IRQHandler() +{ + unregisterIRQHandler(m_irqNumber, *this); +} + +void IRQHandler::enableIRQ() +{ + PIC::enable(m_irqNumber); +} + +void IRQHandler::disableIRQ() +{ + PIC::disable(m_irqNumber); +} + diff --git a/Kernel/IRQHandler.h b/Kernel/IRQHandler.h new file mode 100644 index 00000000000000..f79c96a795cee6 --- /dev/null +++ b/Kernel/IRQHandler.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +class IRQHandler { +public: + virtual ~IRQHandler(); + virtual void handleIRQ() = 0; + + byte irqNumber() const { return m_irqNumber; } + + void enableIRQ(); + void disableIRQ(); + +protected: + explicit IRQHandler(byte irq); + +private: + byte m_irqNumber { 0 }; +}; + diff --git a/Kernel/Keyboard.cpp b/Kernel/Keyboard.cpp index 11bb8eecf9a36b..9436bae64b6f3d 100644 --- a/Kernel/Keyboard.cpp +++ b/Kernel/Keyboard.cpp @@ -1,11 +1,10 @@ #include "types.h" #include "i386.h" #include "IO.h" -#include "IPC.h" -#include "Task.h" #include "VGA.h" #include "PIC.h" #include "Keyboard.h" +#include #define IRQ_KEYBOARD 1 @@ -16,36 +15,6 @@ #define DATA_AVAILABLE 0x01 #define I8042_ACK 0xFA -extern "C" void handleKeyboardInterrupt(); -extern "C" void keyboard_ISR(); - -static BYTE s_ledState; - -asm( - ".globl keyboard_ISR \n" - "keyboard_ISR: \n" - " pusha\n" - " pushw %ds\n" - " pushw %es\n" - " pushw %ss\n" - " pushw %ss\n" - " popw %ds\n" - " popw %es\n" - " call handleKeyboardInterrupt\n" - " popw %es\n" - " popw %ds\n" - " popa\n" - " iret\n" -); - -void handleKeyboardInterrupt() -{ - IRQHandlerScope scope(IRQ_KEYBOARD); - Keyboard::handleInterrupt(); -} - -namespace Keyboard { - #define MOD_ALT 1 #define MOD_CTRL 2 #define MOD_SHIFT 4 @@ -66,19 +35,18 @@ static char shift_map[0x100] = 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', }; -static BYTE s_modifiers; -void handleInterrupt() +void Keyboard::handleIRQ() { while (IO::in8(0x64) & 1) { BYTE ch = IO::in8(0x60); switch (ch) { - case 0x38: s_modifiers |= MOD_ALT; break; - case 0xB8: s_modifiers &= ~MOD_ALT; break; - case 0x1D: s_modifiers |= MOD_CTRL; break; - case 0x9D: s_modifiers &= ~MOD_CTRL; break; - case 0x2A: s_modifiers |= MOD_SHIFT; break; - case 0xAA: s_modifiers &= ~MOD_SHIFT; break; + case 0x38: m_modifiers |= MOD_ALT; break; + case 0xB8: m_modifiers &= ~MOD_ALT; break; + case 0x1D: m_modifiers |= MOD_CTRL; break; + case 0x9D: m_modifiers &= ~MOD_CTRL; break; + case 0x2A: m_modifiers |= MOD_SHIFT; break; + case 0xAA: m_modifiers &= ~MOD_SHIFT; break; case 0x1C: /* enter */ kprintf("\n"); break; case 0xFA: /* i8042 ack */ break; default: @@ -86,50 +54,30 @@ void handleInterrupt() // key has been depressed break; } - if (!s_modifiers) + if (!m_modifiers) kprintf("%c", map[ch]); - else if (s_modifiers & MOD_SHIFT) + else if (m_modifiers & MOD_SHIFT) kprintf("%c", shift_map[ch]); - else if (s_modifiers & MOD_CTRL) + else if (m_modifiers & MOD_CTRL) kprintf("^%c", shift_map[ch]); } //break; } } -void initialize() +Keyboard::Keyboard() + : IRQHandler(IRQ_KEYBOARD) { - s_modifiers = 0; - s_ledState = 0; - // Empty the buffer of any pending data. // I don't care what you've been pressing until now! while (IO::in8(I8042_STATUS ) & DATA_AVAILABLE) IO::in8(I8042_BUFFER); - registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_KEYBOARD, keyboard_ISR); - - PIC::enable(IRQ_KEYBOARD); + enableIRQ(); } -void setLED(LED led) +Keyboard::~Keyboard() { - s_ledState |= (BYTE)led & 7; - - while (IO::in8(I8042_STATUS) & DATA_AVAILABLE); - IO::out8(I8042_BUFFER, SET_LEDS); - while (IO::in8(I8042_BUFFER) != I8042_ACK); - IO::out8(I8042_BUFFER, s_ledState); + ASSERT_NOT_REACHED(); } -void unsetLED(LED led) -{ - s_ledState &= ~((BYTE)led & 7); - - while (IO::in8(I8042_STATUS) & DATA_AVAILABLE); - IO::out8(I8042_BUFFER, SET_LEDS); - while (IO::in8(I8042_BUFFER) != I8042_ACK); - IO::out8(I8042_BUFFER, s_ledState); -} - -} diff --git a/Kernel/Keyboard.h b/Kernel/Keyboard.h index a2ed06fa7edbb0..02aa5cec06e9ce 100644 --- a/Kernel/Keyboard.h +++ b/Kernel/Keyboard.h @@ -1,16 +1,16 @@ #pragma once -namespace Keyboard { +#include +#include "IRQHandler.h" -enum class LED { - ScrollLock = 1 << 0, - NumLock = 1 << 1, - CapsLock = 1 << 2, -}; +class Keyboard final : public IRQHandler { +public: + virtual ~Keyboard() override; + Keyboard(); + +private: + virtual void handleIRQ() override; -void initialize(); -void setLED(LED); -void unsetLED(LED); -void handleInterrupt(); + byte m_modifiers { 0 }; +}; -} diff --git a/Kernel/Makefile b/Kernel/Makefile index 7729cefa1f2a3f..2acd132f2fc34c 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -19,7 +19,8 @@ KERNEL_OBJS = \ Userspace.o \ IDEDiskDevice.o \ MemoryManager.o \ - Console.o + Console.o \ + IRQHandler.o VFS_OBJS = \ ../VirtualFileSystem/DiskDevice.o \ diff --git a/Kernel/i386.cpp b/Kernel/i386.cpp index 233ebc1d114f61..91d676abedec3a 100644 --- a/Kernel/i386.cpp +++ b/Kernel/i386.cpp @@ -5,6 +5,8 @@ #include "Assertions.h" #include "Task.h" #include "MemoryManager.h" +#include "IRQHandler.h" +#include "PIC.h" struct DescriptorTablePointer { WORD size; @@ -16,6 +18,8 @@ static DescriptorTablePointer s_gdtr; static Descriptor* s_idt; static Descriptor* s_gdt; +static IRQHandler** s_irqHandler; + static WORD s_gdtLength; WORD allocateGDTEntry() @@ -27,6 +31,26 @@ WORD allocateGDTEntry() return newGDTEntry; } +extern "C" void handleIRQ(); +extern "C" void commonIRQEntry(); + +asm( + ".globl commonIRQEntry\n" + "commonIRQEntry: \n" + " pusha\n" + " pushw %ds\n" + " pushw %es\n" + " pushw %ss\n" + " pushw %ss\n" + " popw %ds\n" + " popw %es\n" + " call handleIRQ\n" + " popw %es\n" + " popw %ds\n" + " popa\n" + " iret\n" +); + extern volatile dword exception_state_dump; extern volatile word exception_code; asm( @@ -290,6 +314,20 @@ static void unimp_trap() HANG; } +void registerIRQHandler(byte irq, IRQHandler& handler) +{ + ASSERT(!s_irqHandler[irq]); + s_irqHandler[irq] = &handler; + kprintf("irq handler for %u: %p\n", irq, &handler); + registerInterruptHandler(IRQ_VECTOR_BASE + irq, commonIRQEntry); +} + +void unregisterIRQHandler(byte irq, IRQHandler& handler) +{ + ASSERT(s_irqHandler[irq] == &handler); + s_irqHandler[irq] = nullptr; +} + void registerInterruptHandler(BYTE index, void (*f)()) { s_idt[index].low = 0x00080000 | LSW((f)); @@ -351,6 +389,11 @@ void idt_init() registerInterruptHandler(0x57, irq7_handler); + s_irqHandler = new IRQHandler*[16]; + for (byte i = 0; i < 16; ++i) { + s_irqHandler[i] = nullptr; + } + flushIDT(); } @@ -358,3 +401,25 @@ void loadTaskRegister(WORD selector) { asm("ltr %0"::"r"(selector)); } + +void handleIRQ() +{ + WORD isr = PIC::getISR(); + if (!isr) { + kprintf("Spurious IRQ\n"); + return; + } + + byte irq; + for (byte i = 0; i < 16; ++i) { + if (isr & (1 << i)) { + irq = i; + break; + } + } + + if (s_irqHandler[irq]) + s_irqHandler[irq]->handleIRQ(); + PIC::eoi(irq); +} + diff --git a/Kernel/i386.h b/Kernel/i386.h index 666a026a7c2956..24481f688577b7 100644 --- a/Kernel/i386.h +++ b/Kernel/i386.h @@ -55,10 +55,14 @@ union Descriptor { } } PACKED; +class IRQHandler; + void gdt_init(); void idt_init(); void registerInterruptHandler(BYTE number, void (*f)()); void registerUserCallableInterruptHandler(BYTE number, void (*f)()); +void registerIRQHandler(BYTE number, IRQHandler&); +void unregisterIRQHandler(BYTE number, IRQHandler&); void flushIDT(); void flushGDT(); void loadTaskRegister(WORD selector); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index fb0ca20b98ac78..65d784923b3886 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -27,25 +27,8 @@ #include "Console.h" #define TEST_VFS -#define TEST_ELF_LOADER -#define TEST_CRASHY_USER_PROCESSES - -#if 0 -/* Keyboard LED disco task ;^) */ - -static void led_disco() NORETURN; - -static void led_disco() -{ - BYTE b = 0; - for (;;) { - sleep(0.5 * TICKS_PER_SECOND); - Keyboard::unsetLED((Keyboard::LED)b++); - b &= 7; - Keyboard::setLED((Keyboard::LED)b); - } -} -#endif +//#define TEST_ELF_LOADER +//#define TEST_CRASHY_USER_PROCESSES static void motd_main() NORETURN; static void motd_main() @@ -115,7 +98,6 @@ static void init_stage2() NORETURN; static void init_stage2() { kprintf("init stage2...\n"); - Keyboard::initialize(); // Anything that registers interrupts goes *after* PIC and IDT for obvious reasons. Syscall::initialize(); @@ -192,10 +174,14 @@ static void init_stage2() kprintf("init stage2 is done!\n"); +#if 0 + // It would be nice to exit this process, but right now it instantiates all kinds of things. + // At the very least it needs to be made sure those things stick around as appropriate. DO_SYSCALL_A1(Syscall::PosixExit, 413); kprintf("uh, we're still going after calling sys$exit...\n"); HANG; +#endif for (;;) { asm("hlt"); @@ -217,6 +203,8 @@ void init() MemoryManager::initialize(); + auto keyboard = make(); + PIT::initialize(); memset(&system, 0, sizeof(system));