Added PIC initialization and keyboard.

This commit is contained in:
Patrick 2024-01-18 02:24:42 +01:00
parent f03963a485
commit 04d25d498d
11 changed files with 1022 additions and 34 deletions

View File

@ -0,0 +1,36 @@
#pragma once
#if !defined(BAD_APPLE_OS_PORT_HPP_INCLUDED)
#define BAD_APPLE_OS_PORT_HPP_INCLUDED
#include <cstdint>
#define writePortByte(port, value) \
{ \
__asm__ __volatile__( \
"outb %0, %1" \
: \
: "a"(static_cast<std::uint8_t>(value)), \
"Nd"(static_cast<std::uint16_t>(port)) \
); \
}
#define readPortByte(port) \
[&]() \
{ \
std::uint8_t value = 0; \
__asm__ __volatile__( \
"inb %1, %0" \
: "=a"(value) \
: "Nd"(static_cast<std::uint16_t>(port)) \
); \
return value; \
}()
inline void ioWait() noexcept
{
writePortByte(0x80, 0);
}
#endif // !defined(BAD_APPLE_OS_PORT_HPP_INCLUDED)

View File

@ -6,6 +6,8 @@
#include <cstdint>
#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

View File

@ -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(

View File

@ -6,6 +6,10 @@
#include <efi.h>
// srsly, FUCK YOU
#undef ARROW_UP
#undef ARROW_DOWN
class EfiMemoryMapIterator
{
private:

View File

@ -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.

View File

@ -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<std::uint16_t>(Scancode::PRINT_SCREEN);
}
else
{
scancode |= 0xE000;
}
}
outEvent.scancode = static_cast<Scancode>(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";
}
}
}

View File

@ -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)

View File

@ -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);
}

View File

@ -0,0 +1,39 @@
#pragma once
#if !defined(BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED)
#define BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED
#include <cstdint>
#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)

View File

@ -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<InterruptDescriptor, 256> IDT alignas(16);
void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept
{
const uint64_t minAddr = reinterpret_cast<uint64_t>(&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<InterruptDescriptor, 256> 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<std::uint64_t>(handler);
IDT[index] = InterruptDescriptor{
.offsetLow = static_cast<unsigned>(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<unsigned>(event.scancode),
event.down ? "true" : "false",
event.repeat ? "true" : "false"
);
}
main();
}

View File

@ -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;