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 <cstdint>
#include "./port.hpp"
inline constexpr std::uint16_t PORT_COM1 = 0x3F8; inline constexpr std::uint16_t PORT_COM1 = 0x3F8;
inline constexpr std::uint16_t PORT_COM2 = 0x2F8; inline constexpr std::uint16_t PORT_COM2 = 0x2F8;
inline constexpr std::uint16_t PORT_COM3 = 0x3E8; 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_COM7 = 0x5E8;
inline constexpr std::uint16_t PORT_COM8 = 0x4E8; 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 [[nodiscard]] inline bool initSerialPort(std::uint16_t port) noexcept
{ {
writePortByte(port + 1, 0x00); // Disable all interrupts 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') 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_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(''' kernel_sources = env['KERNEL_SOURCES'] + Split('''
src/cstdlib/memory.s src/cstdlib/memory.s
src/kernel/boot.s src/kernel/boot.s
src/kernel/startup.cpp 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') kernel_target = kernel_env.File('#kernel.x86_64.bin')
prog_kernel = kernel_env.Program( prog_kernel = kernel_env.Program(

View File

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

View File

@ -63,6 +63,23 @@ __setIDT:
lidt __idt_storage lidt __idt_storage
ret 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. 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. 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/draw.hpp"
#include "os/serial.hpp" #include "os/serial.hpp"
#include "os/tty.hpp" #include "os/tty.hpp"
#include "./keyboard.hpp"
#include "./pic.hpp"
#include "./x86_64.hpp" #include "./x86_64.hpp"
extern "C" int gKernelEnd; extern "C" int gKernelEnd;
namespace 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 void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept
{ {
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd); 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 __setGDT(uint16_t limit, void* base);
extern "C" void __setIDT(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); const std::uint64_t offset = std::bit_cast<std::uint64_t>(handler);
IDT[index] = InterruptDescriptor{ IDT[index] = InterruptDescriptor{
.offsetLow = static_cast<unsigned>(offset & 0xFFFF), .offsetLow = static_cast<unsigned>(offset & 0xFFFF),
.selector = 1, // kernel code .selectorPrivilegeLevel = 0,
.selectorIndex = 1, // kernel code
.interruptStackTable = 0, .interruptStackTable = 0,
.gateType = InterruptGateType::INTERRUPT_GATE, .gateType = InterruptGateType::INTERRUPT_GATE,
.privilegeLevel = 0, .privilegeLevel = 0,
@ -119,10 +139,21 @@ EfiBootInfo* gBootInfo;
void main(); void main();
extern void __isr21(InterruptFrame*);
void kernel_main() void kernel_main()
{ {
setupInterrupt(INTERRUPT_KEYBOARD, &kbd::isrKeyboard, 0);
__setGDT(sizeof(GDT), &GDT); __setGDT(sizeof(GDT), &GDT);
__setIDT(sizeof(IDT), &IDT); __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 // done initializing OS stuff, enable interrupts
__asm__ __volatile__("sti"); __asm__ __volatile__("sti");
@ -150,7 +181,31 @@ void kernel_main()
} }
serialWriteString(PORT_COM1, "\r\n\r\n"); // write newlines to separate from UEFI output 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"); 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(); main();
} }

View File

@ -148,17 +148,26 @@ inline constexpr const SegmentDescriptor SEGMENT_USER_DATA = {
enum class InterruptGateType enum class InterruptGateType
{ {
NONE = 0,
INTERRUPT_GATE = 0xE, INTERRUPT_GATE = 0xE,
TRAP_GATE = 0xF TRAP_GATE = 0xF
}; };
enum class InterruptSelectorTable
{
GDT = 0,
LDT = 1
};
struct InterruptDescriptor struct InterruptDescriptor
{ {
/* 0 */ unsigned offsetLow : 16 = 0; /* 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; /* 32 */ unsigned interruptStackTable : 3 = 0;
/* 35 */ unsigned reserved0_ : 5 = 0; /* 35 */ unsigned reserved0_ : 5 = 0;
/* 40 */ InterruptGateType gateType : 4 = InterruptGateType::INTERRUPT_GATE; /* 40 */ InterruptGateType gateType : 4 = InterruptGateType::NONE;
/* 44 */ unsigned reserved1_ : 1 = 0; /* 44 */ unsigned reserved1_ : 1 = 0;
/* 45 */ unsigned privilegeLevel : 2 = 0; /* 45 */ unsigned privilegeLevel : 2 = 0;
/* 47 */ bool present : 1 = 0; /* 47 */ bool present : 1 = 0;