diff --git a/targets/_any/include/os/port.hpp b/targets/_any/include/os/port.hpp new file mode 100644 index 0000000..8b50c39 --- /dev/null +++ b/targets/_any/include/os/port.hpp @@ -0,0 +1,36 @@ + +#pragma once + +#if !defined(BAD_APPLE_OS_PORT_HPP_INCLUDED) +#define BAD_APPLE_OS_PORT_HPP_INCLUDED + +#include + +#define writePortByte(port, value) \ +{ \ + __asm__ __volatile__( \ + "outb %0, %1" \ + : \ + : "a"(static_cast(value)), \ + "Nd"(static_cast(port)) \ + ); \ +} + +#define readPortByte(port) \ +[&]() \ +{ \ + std::uint8_t value = 0; \ + __asm__ __volatile__( \ + "inb %1, %0" \ + : "=a"(value) \ + : "Nd"(static_cast(port)) \ + ); \ + return value; \ +}() + +inline void ioWait() noexcept +{ + writePortByte(0x80, 0); +} + +#endif // !defined(BAD_APPLE_OS_PORT_HPP_INCLUDED) diff --git a/targets/_any/include/os/serial.hpp b/targets/_any/include/os/serial.hpp index 0fd8fda..6361eb8 100644 --- a/targets/_any/include/os/serial.hpp +++ b/targets/_any/include/os/serial.hpp @@ -6,6 +6,8 @@ #include +#include "./port.hpp" + inline constexpr std::uint16_t PORT_COM1 = 0x3F8; inline constexpr std::uint16_t PORT_COM2 = 0x2F8; inline constexpr std::uint16_t PORT_COM3 = 0x3E8; @@ -15,26 +17,6 @@ inline constexpr std::uint16_t PORT_COM6 = 0x4F8; inline constexpr std::uint16_t PORT_COM7 = 0x5E8; inline constexpr std::uint16_t PORT_COM8 = 0x4E8; -inline void writePortByte(std::uint16_t port, std::uint8_t value) noexcept -{ - __asm__ __volatile__( - "outb %0, %1" - : - : "a"(value), "Nd"(port) - ); -} - -inline std::uint8_t readPortByte(std::uint16_t port) noexcept -{ - std::uint8_t value = 0; - __asm__ __volatile__( - "inb %1, %0" - : "=a"(value) - : "Nd"(port) - ); - return value; -} - [[nodiscard]] inline bool initSerialPort(std::uint16_t port) noexcept { writePortByte(port + 1, 0x00); // Disable all interrupts diff --git a/targets/x86_64/SConscript b/targets/x86_64/SConscript index 1cba6a8..1e3a82c 100644 --- a/targets/x86_64/SConscript +++ b/targets/x86_64/SConscript @@ -27,12 +27,16 @@ crti_o = kernel_env.Object('src/crt/crti.s') crtn_o = kernel_env.Object('src/crt/crtn.s') kernel_env['LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS -lgcc {crtend_o} {crtn_o[0].abspath}') +kernel_irs_sources = Split(''' + src/kernel/keyboard.cpp + src/kernel/pic.cpp +''') kernel_sources = env['KERNEL_SOURCES'] + Split(''' src/cstdlib/memory.s src/kernel/boot.s src/kernel/startup.cpp -''') +''') + [kernel_env.Object(f, CCFLAGS = kernel_env['CCFLAGS'] + ['-mgeneral-regs-only']) for f in kernel_irs_sources] kernel_target = kernel_env.File('#kernel.x86_64.bin') prog_kernel = kernel_env.Program( diff --git a/targets/x86_64/include/boot.hpp b/targets/x86_64/include/boot.hpp index 91f81f3..22cc9e3 100644 --- a/targets/x86_64/include/boot.hpp +++ b/targets/x86_64/include/boot.hpp @@ -6,6 +6,10 @@ #include +// srsly, FUCK YOU +#undef ARROW_UP +#undef ARROW_DOWN + class EfiMemoryMapIterator { private: diff --git a/targets/x86_64/src/kernel/boot.s b/targets/x86_64/src/kernel/boot.s index 7ffc2a8..def8705 100644 --- a/targets/x86_64/src/kernel/boot.s +++ b/targets/x86_64/src/kernel/boot.s @@ -63,6 +63,23 @@ __setIDT: lidt __idt_storage ret +.global __reloadSegments +.type __reloadSegments @function +// void __reloadSegments(uint64_t code [%rdi], uint64_t data [%rsi]) +__reloadSegments: + push %rdi + lea (.reloadCS), %rax + push %rax + lretq +.reloadCS: + movw %si, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + ret + /* Set the size of the _start symbol to the current location '.' minus its start. This is useful when debugging or when you implement call tracing. diff --git a/targets/x86_64/src/kernel/keyboard.cpp b/targets/x86_64/src/kernel/keyboard.cpp new file mode 100644 index 0000000..bb1a088 --- /dev/null +++ b/targets/x86_64/src/kernel/keyboard.cpp @@ -0,0 +1,560 @@ + +#include "./keyboard.hpp" + +namespace +{ +inline constexpr std::uint16_t PORT_PS2_DATA = 0x60; +inline constexpr std::uint16_t PORT_PS2_STATUS = 0x64; +inline constexpr std::uint8_t FLAG_PS2_OUTPUT_FULL = (1 << 0); +inline constexpr std::uint8_t FLAG_PS2_INPUT_FULL = (1 << 1); +inline constexpr std::uint8_t PS2_CMD_DISABLE_FIRST = 0xAD; +inline constexpr std::uint8_t PS2_CMD_ENABLE_FIRST = 0xAE; +inline constexpr std::uint8_t PS2_CMD_DISABLE_SECOND = 0xA7; +inline constexpr std::uint8_t PS2_CMD_ENABLE_SECOND = 0xA8; +inline constexpr std::uint8_t PS2_CMD_READ_CONF_BYTE = 0x20; +inline constexpr std::uint8_t PS2_CMD_WRITE_CONF_BYTE = 0x60; +inline constexpr std::uint8_t PS2_CMD_SELFTEST = 0xAA; +inline constexpr std::uint8_t PS2_CMD_PORTTEST_FIRST = 0xAB; +inline constexpr std::uint8_t PS2_CMD_PORTTEST_SECOND = 0xA9; +inline constexpr std::uint8_t PS2_CMD_WRITE_TO_SECOND = 0xD4; +inline constexpr std::uint8_t PS2_DEVICE_CMD_RESET = 0xFF; +inline constexpr std::uint8_t PS2_RESPONSE_ACK = 0xFA; +inline constexpr std::uint8_t PS2_RESPONSE_RESEND = 0xFE; +inline constexpr std::uint8_t PS2_RESPONSE_SELFTEST_PASSED = 0x55; +inline constexpr std::uint8_t PS2_RESPONSE_SELFTEST_FAILED = 0xFC; +inline constexpr std::uint8_t PS2_RESPONSE_PORTTEST_PASSED = 0x00; +inline constexpr std::uint8_t PS2_CONFIGFLAG_IRQ_ENABLE_FIRST = (1 << 0); +inline constexpr std::uint8_t PS2_CONFIGFLAG_IRQ_ENABLE_SECOND = (1 << 1); +inline constexpr std::uint8_t PS2_CONFIGFLAG_CLOCK_FIRST = (1 << 4); +inline constexpr std::uint8_t PS2_CONFIGFLAG_CLOCK_SECOND = (1 << 5); +inline constexpr std::uint8_t PS2_CONFIGFLAG_TRANSLATION = (1 << 6); + +inline constexpr unsigned KEY_BUFFER_LENGTH = 16; +std::uint8_t gKeyboardBuffer[KEY_BUFFER_LENGTH]; +unsigned gBufferedKeys = 0; +unsigned gKeyPos = 0; + +__attribute__((no_caller_saved_registers)) +void pushKey(std::uint8_t data) noexcept +{ + if (gBufferedKeys == KEY_BUFFER_LENGTH) { + return; + } + gKeyboardBuffer[gKeyPos] = data; + ++gBufferedKeys; + gKeyPos = (gKeyPos + 1) % KEY_BUFFER_LENGTH; +} +} + +namespace kbd +{ +inline bool waitForResponse(unsigned tries = -1) noexcept +{ + for (unsigned t = 0; t < tries; ++t) + { + if (readPortByte(PORT_PS2_STATUS) & FLAG_PS2_OUTPUT_FULL) { + return true; + } + } + return false; +} + +inline void waitForInputFree() noexcept +{ + while (readPortByte(PORT_PS2_STATUS) & FLAG_PS2_INPUT_FULL); +} + +inline std::uint8_t cmdWithResponse(std::uint8_t cmd) noexcept +{ + writePortByte(PORT_PS2_STATUS, cmd); + waitForResponse(); + return readPortByte(PORT_PS2_DATA); +} + +inline void cmdWithData(std::uint8_t cmd, std::uint8_t data) noexcept +{ + writePortByte(PORT_PS2_STATUS, cmd); + waitForInputFree(); + writePortByte(PORT_PS2_DATA, data); +} + +inline void sendToFirstDevice(std::uint8_t cmd) noexcept +{ + waitForInputFree(); + writePortByte(PORT_PS2_DATA, cmd); +} + +inline void sendToSecondDevice(std::uint8_t cmd) noexcept +{ + cmdWithData(PS2_CMD_WRITE_TO_SECOND, cmd); + waitForResponse(); +} + +bool initialize() noexcept +{ + // disable PS2 + writePortByte(PORT_PS2_STATUS, PS2_CMD_DISABLE_FIRST); + writePortByte(PORT_PS2_STATUS, PS2_CMD_DISABLE_SECOND); + + // flush output register + readPortByte(PORT_PS2_DATA); + + // read configuration + std::uint8_t config = cmdWithResponse(PS2_CMD_READ_CONF_BYTE); + + // adjust configuration + const bool secondDevice = (config & PS2_CONFIGFLAG_CLOCK_SECOND) != 0; + config &= ~PS2_CONFIGFLAG_IRQ_ENABLE_FIRST; + config &= ~PS2_CONFIGFLAG_IRQ_ENABLE_SECOND; + config &= ~PS2_CONFIGFLAG_TRANSLATION; + + // write configuration + cmdWithData(PS2_CMD_WRITE_CONF_BYTE, config); + + // run self-test + const std::uint8_t testResult = cmdWithResponse(PS2_CMD_SELFTEST); + if (testResult != PS2_RESPONSE_SELFTEST_PASSED) { + return false; + } + + // write configuration again, in case it was reset + cmdWithData(PS2_CMD_WRITE_CONF_BYTE, config); + + // check if dual-channel + bool dualChannel = false; + if ((config & PS2_CONFIGFLAG_CLOCK_SECOND) != 0) // it may be + { + writePortByte(PORT_PS2_STATUS, PS2_CMD_ENABLE_SECOND); + dualChannel = (cmdWithResponse(PS2_CMD_READ_CONF_BYTE) & PS2_CONFIGFLAG_CLOCK_SECOND) == 0; + if (dualChannel) + { + writePortByte(PORT_PS2_STATUS, PS2_CMD_DISABLE_SECOND); + } + } + + // test interfaces + if (cmdWithResponse(PS2_CMD_PORTTEST_FIRST) != PS2_RESPONSE_PORTTEST_PASSED) + { + return false; + } + if (dualChannel && cmdWithResponse(PS2_CMD_PORTTEST_SECOND) != PS2_RESPONSE_PORTTEST_PASSED) + { + return false; + } + + // enable devices + writePortByte(PORT_PS2_STATUS, PS2_CMD_ENABLE_FIRST); + if (dualChannel) { + writePortByte(PORT_PS2_STATUS, PS2_CMD_ENABLE_SECOND); + } + + // enable interrupts + config |= PS2_CONFIGFLAG_IRQ_ENABLE_FIRST; + if (dualChannel) { + config |= PS2_CONFIGFLAG_IRQ_ENABLE_SECOND; + } + cmdWithData(PS2_CMD_WRITE_CONF_BYTE, config); + + // reset + sendToFirstDevice(PS2_DEVICE_CMD_RESET); + bool firstOk = false; + while (waitForResponse(10)) + { + const std::uint8_t response = readPortByte(PORT_PS2_DATA); + if (response == PS2_RESPONSE_SELFTEST_FAILED) { + return false; + } + firstOk = true; + } + if (!firstOk) { + return false; + } + if (dualChannel) + { + sendToSecondDevice(PS2_DEVICE_CMD_RESET); + bool secondOk = false; + while (waitForResponse(10)) + { + const std::uint8_t response = readPortByte(PORT_PS2_DATA); + if (response == PS2_RESPONSE_SELFTEST_FAILED) { + return false; + } + secondOk = true; + } + if (!secondOk) { + return false; + } + } + + return true; +} + +__attribute__((interrupt)) +void isrKeyboard(InterruptFrame* interruptFrame) noexcept +{ + (void) interruptFrame; + const std::uint8_t data = readPortByte(PORT_PS2_DATA); + if (data != 0) + { + pushKey(data); + } + sendEndOfInterrupt(1); +} + +[[nodiscard]] bool readRaw(std::uint8_t& outData) noexcept +{ + maskIRQ(1); + bool result = false; + if (gBufferedKeys > 0) + { + outData = gKeyboardBuffer[(gKeyPos + KEY_BUFFER_LENGTH - gBufferedKeys) % KEY_BUFFER_LENGTH]; + --gBufferedKeys; + result = true; + } + unmaskIRQ(1); + return result; +} + +bool gNextIsRelease = false; +bool gNextIsExtended = false; +bool gNextIsMoreExtended = false; +bool gNextIsExtendedest = false; +bool gPrevWasExtendedest = false; + +[[nodiscard]] bool read(KeyEvent& outEvent) noexcept +{ + if (gPrevWasExtendedest) + { + gPrevWasExtendedest = gNextIsExtended = gNextIsMoreExtended = gNextIsExtendedest = false; + outEvent.scancode = Scancode::PAUSE; + outEvent.down = false; + outEvent.repeat = false; + return true; + } + std::uint8_t data = 0; + if (!readRaw(data)) { + return false; + } + if (gNextIsExtendedest) + { + if (data == 0x77) + { + // second 0x77, our most special key is done + if (gNextIsExtended) + { + gPrevWasExtendedest = true; // the next scan will return the key up event + outEvent.scancode = Scancode::PAUSE; + outEvent.down = true; + outEvent.repeat = false; + return true; + } + else + { + // first 0x77 in sequence, wait for the second one + gNextIsExtended = true; + return false; + } + } + return false; // ignore everything else until we finished the most special key + } + + switch (data) + { + case 0xF0: + gNextIsRelease = true; + return false; + case 0xE0: + gNextIsExtended = true; + return false; + case 0x12: + if (gNextIsExtended) + { + gNextIsMoreExtended = true; + return false; + } + break; + case 0xE1: + gNextIsExtendedest = true; + return false; + default: break; + } + + std::uint16_t scancode = data; + if (gNextIsExtended) + { + if (gNextIsMoreExtended) + { + scancode = static_cast(Scancode::PRINT_SCREEN); + } + else + { + scancode |= 0xE000; + } + } + outEvent.scancode = static_cast(scancode); + outEvent.down = !gNextIsRelease; + outEvent.repeat = false; // TODO + + // reset state + gNextIsExtendedest = gNextIsMoreExtended = gNextIsRelease = gNextIsExtended = false; + return true; +} + +[[nodiscard]] const char* keyName(Scancode scancode) noexcept +{ + switch (scancode) + { + case Scancode::INVALID: + default: + return "INVALID"; + case Scancode::F9: + return "F9"; + case Scancode::F5: + return "F5"; + case Scancode::F3: + return "F3"; + case Scancode::F1: + return "F1"; + case Scancode::F2: + return "F2"; + case Scancode::F12: + return "F12"; + case Scancode::F10: + return "F10"; + case Scancode::F8: + return "F8"; + case Scancode::F6: + return "F6"; + case Scancode::F4: + return "F4"; + case Scancode::TAB: + return "TAB"; + case Scancode::BACKTICK: + return "BACKTICK"; + case Scancode::LEFT_ALT: + return "LEFT_ALT"; + case Scancode::LEFT_SHIFT: + return "LEFT_SHIFT"; + case Scancode::LEFT_CONTROL: + return "LEFT_CONTROL"; + case Scancode::Q: + return "Q"; + case Scancode::_1: + return "_1"; + case Scancode::Z: + return "Z"; + case Scancode::S: + return "S"; + case Scancode::A: + return "A"; + case Scancode::W: + return "W"; + case Scancode::_2: + return "_2"; + case Scancode::C: + return "C"; + case Scancode::X: + return "X"; + case Scancode::D: + return "D"; + case Scancode::E: + return "E"; + case Scancode::_4: + return "_4"; + case Scancode::_3: + return "_3"; + case Scancode::SPACE: + return "SPACE"; + case Scancode::V: + return "V"; + case Scancode::F: + return "F"; + case Scancode::T: + return "T"; + case Scancode::R: + return "R"; + case Scancode::_5: + return "_5"; + case Scancode::N: + return "N"; + case Scancode::B: + return "B"; + case Scancode::H: + return "H"; + case Scancode::G: + return "G"; + case Scancode::Y: + return "Y"; + case Scancode::_6: + return "_6"; + case Scancode::M: + return "M"; + case Scancode::J: + return "J"; + case Scancode::U: + return "U"; + case Scancode::_7: + return "_7"; + case Scancode::_8: + return "_8"; + case Scancode::COMMA: + return "COMMA"; + case Scancode::K: + return "K"; + case Scancode::I: + return "I"; + case Scancode::O: + return "O"; + case Scancode::_0: + return "_0"; + case Scancode::_9: + return "_9"; + case Scancode::DOT: + return "DOT"; + case Scancode::SLASH: + return "SLASH"; + case Scancode::L: + return "L"; + case Scancode::SEMICOLON: + return "SEMICOLON"; + case Scancode::P: + return "P"; + case Scancode::MINUS: + return "MINUS"; + case Scancode::APOSTROPHE: + return "APOSTROPHE"; + case Scancode::BRACKET_OPEN: + return "BRACKET_OPEN"; + case Scancode::EQUALS: + return "EQUALS"; + case Scancode::CAPSLOCK: + return "CAPSLOCK"; + case Scancode::RIGHT_SHIFT: + return "RIGHT_SHIFT"; + case Scancode::ENTER: + return "ENTER"; + case Scancode::BRACKET_CLOSE: + return "BRACKET_CLOSE"; + case Scancode::BACKSLASH: + return "BACKSLASH"; + case Scancode::BACKSPACE: + return "BACKSPACE"; + case Scancode::KP_1: + return "KP_1"; + case Scancode::KP_4: + return "KP_4"; + case Scancode::KP_7: + return "KP_7"; + case Scancode::KP_0: + return "KP_0"; + case Scancode::KP_DOT: + return "KP_DOT"; + case Scancode::KP_2: + return "KP_2"; + case Scancode::KP_5: + return "KP_5"; + case Scancode::KP_6: + return "KP_6"; + case Scancode::KP_8: + return "KP_8"; + case Scancode::ESCAPE: + return "ESCAPE"; + case Scancode::NUMLOCK: + return "NUMLOCK"; + case Scancode::F11: + return "F11"; + case Scancode::KP_PLUS: + return "KP_PLUS"; + case Scancode::KP_3: + return "KP_3"; + case Scancode::KP_MINUS: + return "KP_MINUS"; + case Scancode::KP_ASTERISK: + return "KP_ASTERISK"; + case Scancode::KP_9: + return "KP_9"; + case Scancode::SCROLLOCK: + return "SCROLLOCK"; + case Scancode::F7: + return "F7"; + case Scancode::MM_WWW_SEARCH: + return "MM_WWW_SEARCH"; + case Scancode::RIGHT_ALT: + return "RIGHT_ALT"; + case Scancode::RIGHT_CONTROL: + return "RIGHT_CONTROL"; + case Scancode::MM_PREV_TRACK: + return "MM_PREV_TRACK"; + case Scancode::LEFT_GUI: + return "LEFT_GUI"; + case Scancode::MM_WWW_FAVORITES: + return "MM_WWW_FAVORITES"; + case Scancode::MM_VOLUME_DOWN: + return "MM_VOLUME_DOWN"; + case Scancode::MM_MUTE: + return "MM_MUTE"; + case Scancode::RIGHT_GUI: + return "RIGHT_GUI"; + case Scancode::MM_WWW_STOP: + return "MM_WWW_STOP"; + case Scancode::MM_CALCULATOR: + return "MM_CALCULATOR"; + case Scancode::APPS: + return "APPS"; + case Scancode::MM_WWW_FORWARD: + return "MM_WWW_FORWARD"; + case Scancode::MM_VOLUME_UP: + return "MM_VOLUME_UP"; + case Scancode::MM_PLAY_PAUSE: + return "MM_PLAY_PAUSE"; + case Scancode::ACPI_POWER: + return "ACPI_POWER"; + case Scancode::MM_WWW_BACK: + return "MM_WWW_BACK"; + case Scancode::MM_WWW_HOME: + return "MM_WWW_HOME"; + case Scancode::MM_STOP: + return "MM_STOP"; + case Scancode::ACPI_SLEEP: + return "ACPI_SLEEP"; + case Scancode::MM_MY_COMPUTER: + return "MM_MY_COMPUTER"; + case Scancode::MM_EMAIL: + return "MM_EMAIL"; + case Scancode::KP_SLASH: + return "KP_SLASH"; + case Scancode::MM_NEXT_TRACK: + return "MM_NEXT_TRACK"; + case Scancode::MM_MEDIA_SELECT: + return "MM_MEDIA_SELECT"; + case Scancode::KP_ENTER: + return "KP_ENTER"; + case Scancode::ACPI_WAKE: + return "ACPI_WAKE"; + case Scancode::END: + return "END"; + case Scancode::ARROW_LEFT: + return "ARROW_LEFT"; + case Scancode::HOME: + return "HOME"; + case Scancode::INSERT: + return "INSERT"; + case Scancode::DELETE: + return "DELETE"; + case Scancode::ARROW_DOWN: + return "ARROW_DOWN"; + case Scancode::ARROW_RIGHT: + return "ARROW_RIGHT"; + case Scancode::ARROW_UP: + return "ARROW_UP"; + case Scancode::PAGE_DOWN: + return "PAGE_DOWN"; + case Scancode::PAGE_UP: + return "PAGE_UP"; + case Scancode::PRINT_SCREEN: + return "PRINT_SCREEN"; + case Scancode::PAUSE: + return "PAUSE"; + } +} +} diff --git a/targets/x86_64/src/kernel/keyboard.hpp b/targets/x86_64/src/kernel/keyboard.hpp new file mode 100644 index 0000000..577890e --- /dev/null +++ b/targets/x86_64/src/kernel/keyboard.hpp @@ -0,0 +1,161 @@ + +#pragma once + +#if !defined(BAD_APPLE_OS_KERNEL_KEYBOARD_HPP_INCLUDED) +#define BAD_APPLE_OS_KERNEL_KEYBOARD_HPP_INCLUDED + +#include "./pic.hpp" + +namespace kbd +{ +[[nodiscard]] bool initialize() noexcept; + +__attribute__((interrupt)) +void isrKeyboard(InterruptFrame* interruptFrame) noexcept; + +enum class Scancode +{ + INVALID = 0, + + F9 = 0x01, + F5 = 0x03, + F3 = 0x04, + F1 = 0x05, + F2 = 0x06, + F12 = 0x07, + F10 = 0x09, + F8 = 0x0A, + F6 = 0x0B, + F4 = 0x0C, + TAB = 0x0D, + BACKTICK = 0x0E, + LEFT_ALT = 0x11, + LEFT_SHIFT = 0x12, + LEFT_CONTROL = 0x14, + Q = 0x15, + _1 = 0x16, + Z = 0x1A, + S = 0x1B, + A = 0x1C, + W = 0x1D, + _2 = 0x1E, + C = 0x21, + X = 0x22, + D = 0x23, + E = 0x24, + _4 = 0x25, + _3 = 0x26, + SPACE = 0x29, + V = 0x2A, + F = 0x2B, + T = 0x2C, + R = 0x2D, + _5 = 0x2E, + N = 0x31, + B = 0x32, + H = 0x33, + G = 0x34, + Y = 0x35, + _6 = 0x36, + M = 0x3A, + J = 0x3B, + U = 0x3C, + _7 = 0x3D, + _8 = 0x3E, + COMMA = 0x41, + K = 0x42, + I = 0x43, + O = 0x44, + _0 = 0x45, + _9 = 0x46, + DOT = 0x49, + SLASH = 0x4A, + L = 0x4B, + SEMICOLON = 0x4C, + P = 0x4D, + MINUS = 0x4E, + APOSTROPHE = 0x52, + BRACKET_OPEN = 0x54, + EQUALS = 0x55, + CAPSLOCK = 0x58, + RIGHT_SHIFT = 0x59, + ENTER = 0x5A, + BRACKET_CLOSE = 0x5B, + BACKSLASH = 0x5D, + BACKSPACE = 0x66, + KP_1 = 0x69, + KP_4 = 0x6B, + KP_7 = 0x6C, + KP_0 = 0x70, + KP_DOT = 0x71, + KP_2 = 0x72, + KP_5 = 0x73, + KP_6 = 0x74, + KP_8 = 0x75, + ESCAPE = 0x76, + NUMLOCK = 0x77, + F11 = 0x78, + KP_PLUS = 0x79, + KP_3 = 0x7A, + KP_MINUS = 0x7B, + KP_ASTERISK = 0x7C, + KP_9 = 0x7D, + SCROLLOCK = 0x7E, + F7 = 0x83, + + MM_WWW_SEARCH = 0xE010, + RIGHT_ALT = 0xE011, + RIGHT_CONTROL = 0xE014, + MM_PREV_TRACK = 0xE015, + LEFT_GUI = 0xE01F, + MM_WWW_FAVORITES = 0xE020, + MM_VOLUME_DOWN = 0xE021, + MM_MUTE = 0xE023, + RIGHT_GUI = 0xE027, + MM_WWW_STOP = 0xE028, + MM_CALCULATOR = 0xE02B, + APPS = 0xE02F, + MM_WWW_FORWARD = 0xE030, + MM_VOLUME_UP = 0xE032, + MM_PLAY_PAUSE = 0xE034, + ACPI_POWER = 0xE037, + MM_WWW_BACK = 0xE038, + MM_WWW_HOME = 0xE03A, + MM_STOP = 0xE03B, + ACPI_SLEEP = 0xE03F, + MM_MY_COMPUTER = 0xE040, + MM_EMAIL = 0xE048, + KP_SLASH = 0xE04A, + MM_NEXT_TRACK = 0xE04D, + MM_MEDIA_SELECT = 0xE050, + KP_ENTER = 0xE05A, + ACPI_WAKE = 0xE05E, + END = 0xE069, + ARROW_LEFT = 0xE06B, + HOME = 0xE06C, + INSERT = 0xE070, + DELETE = 0xE071, + ARROW_DOWN = 0xE072, + ARROW_RIGHT = 0xE074, + ARROW_UP = 0xE075, + PAGE_DOWN = 0xE07A, + PAGE_UP = 0xE07D, + + PRINT_SCREEN = 0xF000, + PAUSE = 0xF001 +}; + +struct KeyEvent +{ + Scancode scancode = Scancode::INVALID; + bool down : 1 = false; + bool repeat : 1 = false; +}; + +[[nodiscard]] bool readRaw(std::uint8_t& outData) noexcept; +[[nodiscard]] bool read(KeyEvent& outEvent) noexcept; + +[[nodiscard]] const char* keyName(Scancode scancode) noexcept; +} + +#endif // !defined(BAD_APPLE_OS_KERNEL_KEYBOARD_HPP_INCLUDED) diff --git a/targets/x86_64/src/kernel/pic.cpp b/targets/x86_64/src/kernel/pic.cpp new file mode 100644 index 0000000..c9403d5 --- /dev/null +++ b/targets/x86_64/src/kernel/pic.cpp @@ -0,0 +1,121 @@ + +#include "./pic.hpp" + +namespace +{ +inline constexpr std::uint8_t PIC_CMD_INIT = 0x10; +inline constexpr std::uint8_t PIC_CMD_READ_IRR = 0x0a; +inline constexpr std::uint8_t PIC_CMD_READ_ISR = 0x0b; +inline constexpr std::uint8_t PIC_INIT_WITH_ICW4 = 0x01; +inline constexpr std::uint8_t PIC_CFG_8086 = 0x01; + +void picWriteCmd(std::uint16_t port, std::uint8_t command) noexcept +{ + writePortByte(port, command); +} + +[[nodiscard]] std::uint8_t picReadResponse(std::uint8_t port) noexcept +{ + return readPortByte(port); +} + +void picWriteData(std::uint16_t port, std::uint8_t data) noexcept +{ + writePortByte(port + 1, data); +} + +[[nodiscard]] std::uint8_t picReadData(std::uint16_t port) noexcept +{ + return readPortByte(port + 1); +} + +[[nodiscard]] std::uint16_t readPICIRQRegisters(std::uint8_t readCommand) noexcept +{ + picWriteCmd(PORT_PIC1, readCommand); + picWriteCmd(PORT_PIC2, readCommand); + return (picReadResponse(PORT_PIC1) << 8) | picReadResponse(PORT_PIC2); +} +} + +bool initPICs(std::uint8_t masterOffset, std::uint8_t slaveOffset) noexcept +{ + if (!isValidPICOffset(masterOffset) || !isValidPICOffset(slaveOffset)) + { + return false; + } + + const uint8_t mask1 = picReadData(PORT_PIC1); + const uint8_t mask2 = picReadData(PORT_PIC2); + + // ICW1 - init command + picWriteCmd(PORT_PIC1, PIC_CMD_INIT | PIC_INIT_WITH_ICW4); + ioWait(); + picWriteCmd(PORT_PIC2, PIC_CMD_INIT | PIC_INIT_WITH_ICW4); + ioWait(); + + // ICW2 - offset + picWriteData(PORT_PIC1, masterOffset); + ioWait(); + picWriteData(PORT_PIC2, slaveOffset); + ioWait(); + + // ICW3 - master/slave config + picWriteData(PORT_PIC1, (1 << 2)); // there is a slave at IRQ2 + ioWait(); + picWriteData(PORT_PIC2, 2); // you are slave no. 2 + ioWait(); + + // ICW3 - additional config + picWriteData(PORT_PIC1, PIC_CFG_8086); + ioWait(); + picWriteData(PORT_PIC2, PIC_CFG_8086); + ioWait(); + + // restore masks + picWriteData(PORT_PIC1, mask1); + picWriteData(PORT_PIC2, mask2); + + return true; +} + +void sendEndOfInterrupt(std::uint8_t irq) noexcept +{ + if (irq >= 8) { + writePortByte(PORT_PIC2, PIC_CMD_EOI); + } + writePortByte(PORT_PIC1, PIC_CMD_EOI); +} + +void maskIRQ(std::uint8_t irq) noexcept +{ + std::uint16_t port = PORT_PIC1; + if (irq >= 8) + { + port = PORT_PIC2; + irq -= 8; + } + const std::uint8_t currentMask = picReadData(port); + picWriteData(port, currentMask | (1 << irq)); +} + +void unmaskIRQ(std::uint8_t irq) noexcept +{ + std::uint16_t port = PORT_PIC1; + if (irq >= 8) + { + port = PORT_PIC2; + irq -= 8; + } + const std::uint8_t currentMask = picReadData(port); + picWriteData(port, currentMask & ~(1 << irq)); +} + +std::uint16_t readPICIRR() noexcept +{ + return readPICIRQRegisters(PIC_CMD_READ_IRR); +} + +std::uint16_t readPICISR() noexcept +{ + return readPICIRQRegisters(PIC_CMD_READ_ISR); +} diff --git a/targets/x86_64/src/kernel/pic.hpp b/targets/x86_64/src/kernel/pic.hpp new file mode 100644 index 0000000..6c7d045 --- /dev/null +++ b/targets/x86_64/src/kernel/pic.hpp @@ -0,0 +1,39 @@ + +#pragma once + +#if !defined(BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED) +#define BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED + +#include + +#include "os/port.hpp" + +inline constexpr std::uint16_t PORT_PIC1 = 0x20; +inline constexpr std::uint16_t PORT_PIC2 = 0xA0; +inline constexpr std::uint8_t PIC_CMD_EOI = 0x20; + +[[nodiscard]] bool initPICs(std::uint8_t masterOffset, std::uint8_t slaveOffset) noexcept; +__attribute__((no_caller_saved_registers)) +void sendEndOfInterrupt(std::uint8_t irq) noexcept; +void maskIRQ(std::uint8_t irq) noexcept; +void unmaskIRQ(std::uint8_t irq) noexcept; +[[nodiscard]] std::uint16_t readPICIRR() noexcept; +[[nodiscard]] std::uint16_t readPICISR() noexcept; + +[[nodiscard]] constexpr bool isValidPICOffset(std::uint8_t offset) noexcept +{ + return (offset & 7) == 0; +} + +struct InterruptFrame +{ + std::uint64_t ip; + std::uint64_t cs; + std::uint64_t flags; + std::uint64_t sp; + std::uint64_t ss; +}; + +using interrupt_handler_t = __attribute__((interrupt)) void(*)(InterruptFrame*); + +#endif // !defined(BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED) diff --git a/targets/x86_64/src/kernel/startup.cpp b/targets/x86_64/src/kernel/startup.cpp index 51218a1..b276110 100644 --- a/targets/x86_64/src/kernel/startup.cpp +++ b/targets/x86_64/src/kernel/startup.cpp @@ -7,12 +7,39 @@ #include "os/draw.hpp" #include "os/serial.hpp" #include "os/tty.hpp" +#include "./keyboard.hpp" +#include "./pic.hpp" #include "./x86_64.hpp" extern "C" int gKernelEnd; namespace { +inline constexpr std::uint8_t MASTER_PIC_OFFSET = 0x20; +inline constexpr std::uint8_t SLAVE_PIC_OFFSET = 0x28; +static_assert(isValidPICOffset(MASTER_PIC_OFFSET)); +static_assert(isValidPICOffset(SLAVE_PIC_OFFSET)); + +constexpr std::uint8_t irqToInterrupt(std::uint8_t irq) noexcept +{ + if (irq < 8) { + return MASTER_PIC_OFFSET + irq; + } + return SLAVE_PIC_OFFSET + irq; +} +inline constexpr std::uint8_t INTERRUPT_KEYBOARD = irqToInterrupt(1); + +constinit std::array GDT alignas(16) = { + SEGMENT_NULL, + SEGMENT_KERNEL_CODE, + SEGMENT_KERNEL_DATA, + SEGMENT_USER_CODE, + SEGMENT_USER_DATA, + SEGMENT_NULL +}; + +constinit std::array IDT alignas(16); + void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept { const uint64_t minAddr = reinterpret_cast(&gKernelEnd); @@ -86,24 +113,17 @@ void initGlobals() } } -constinit std::array GDT alignas(16) = { - SEGMENT_NULL, - SEGMENT_KERNEL_CODE, - SEGMENT_KERNEL_DATA, - SEGMENT_USER_CODE, - SEGMENT_USER_DATA -}; - -constinit std::array IDT alignas(16); extern "C" void __setGDT(uint16_t limit, void* base); extern "C" void __setIDT(uint16_t limit, void* base); +extern "C" void __reloadSegments(uint64_t code, uint64_t data); -void setupInterrupt(std::uint8_t index, void* handler, std::uint8_t flags) noexcept +void setupInterrupt(std::uint8_t index, interrupt_handler_t handler, std::uint8_t flags) noexcept { const std::uint64_t offset = std::bit_cast(handler); IDT[index] = InterruptDescriptor{ .offsetLow = static_cast(offset & 0xFFFF), - .selector = 1, // kernel code + .selectorPrivilegeLevel = 0, + .selectorIndex = 1, // kernel code .interruptStackTable = 0, .gateType = InterruptGateType::INTERRUPT_GATE, .privilegeLevel = 0, @@ -119,10 +139,21 @@ EfiBootInfo* gBootInfo; void main(); +extern void __isr21(InterruptFrame*); + void kernel_main() { + setupInterrupt(INTERRUPT_KEYBOARD, &kbd::isrKeyboard, 0); + __setGDT(sizeof(GDT), &GDT); __setIDT(sizeof(IDT), &IDT); + __reloadSegments(0x08, 0x10); + + const bool picsInited = initPICs(MASTER_PIC_OFFSET, SLAVE_PIC_OFFSET); + unmaskIRQ(1); + + // initialize the hardware + const bool kbdInited = kbd::initialize(); // done initializing OS stuff, enable interrupts __asm__ __volatile__("sti"); @@ -150,7 +181,31 @@ void kernel_main() } serialWriteString(PORT_COM1, "\r\n\r\n"); // write newlines to separate from UEFI output + if (!picsInited) + { + std::puts("Error initializing the PICs!\n"); + return; + } + + if (!kbdInited) + { + std::puts("Error initializing the keyboard!\n"); + return; + } + std::puts("This is BadAppleOS and everything is fine!\n"); + while (true) + { + kbd::KeyEvent event; + while (!kbd::read(event)); + std::printf( + "Key event: scancode=%s(0x%X), down=%s, repeat=%s\n", + kbd::keyName(event.scancode), + static_cast(event.scancode), + event.down ? "true" : "false", + event.repeat ? "true" : "false" + ); + } main(); } diff --git a/targets/x86_64/src/kernel/x86_64.hpp b/targets/x86_64/src/kernel/x86_64.hpp index e1f0b7f..5ab9ad5 100644 --- a/targets/x86_64/src/kernel/x86_64.hpp +++ b/targets/x86_64/src/kernel/x86_64.hpp @@ -148,17 +148,26 @@ inline constexpr const SegmentDescriptor SEGMENT_USER_DATA = { enum class InterruptGateType { + NONE = 0, INTERRUPT_GATE = 0xE, TRAP_GATE = 0xF }; +enum class InterruptSelectorTable +{ + GDT = 0, + LDT = 1 +}; + struct InterruptDescriptor { /* 0 */ unsigned offsetLow : 16 = 0; - /* 16 */ unsigned selector : 16 = 0; + /* 16 */ unsigned selectorPrivilegeLevel : 2 = 0; + /* 18 */ InterruptSelectorTable selectorTable : 1 = InterruptSelectorTable::GDT; + /* 19 */ unsigned selectorIndex : 13 = 0; /* 32 */ unsigned interruptStackTable : 3 = 0; /* 35 */ unsigned reserved0_ : 5 = 0; - /* 40 */ InterruptGateType gateType : 4 = InterruptGateType::INTERRUPT_GATE; + /* 40 */ InterruptGateType gateType : 4 = InterruptGateType::NONE; /* 44 */ unsigned reserved1_ : 1 = 0; /* 45 */ unsigned privilegeLevel : 2 = 0; /* 47 */ bool present : 1 = 0;