xaizek / tos (License: GPLv3 only) (since 2018-12-07)
This is an alternative version of sources presented as part of Write Your Own OS video tutorial by Viktor Engelmann.
<root> / src / hwcomm / interrupts.cpp (32cc95ce4df51acb819d5d0d73ab9098372fefb5) (7,002B) (mode 100644) [raw]
#include "hwcomm/interrupts.hpp"

#include <new>

#include "hwcomm/Port.hpp"
#include "GDT.hpp"
#include "print.hpp"

using namespace hwcomm;

namespace {

union GateDescriptor {
    std::uint64_t forceAlignment;
    std::uint8_t data[8];
};

};

extern "C" {

extern const int irqBase;

void InterruptIgnore();

void HandleException0x00();
void HandleException0x01();
void HandleException0x02();
void HandleException0x03();
void HandleException0x04();
void HandleException0x05();
void HandleException0x06();
void HandleException0x07();
void HandleException0x08();
void HandleException0x09();
void HandleException0x0A();
void HandleException0x0B();
void HandleException0x0C();
void HandleException0x0D();
void HandleException0x0E();
void HandleException0x0F();
void HandleException0x10();
void HandleException0x11();
void HandleException0x12();
void HandleException0x13();

void HandleException0x80();

void HandleIRQ0x00();
void HandleIRQ0x01();
void HandleIRQ0x02();
void HandleIRQ0x03();
void HandleIRQ0x04();
void HandleIRQ0x05();
void HandleIRQ0x06();
void HandleIRQ0x07();
void HandleIRQ0x08();
void HandleIRQ0x09();
void HandleIRQ0x0A();
void HandleIRQ0x0B();
void HandleIRQ0x0C();
void HandleIRQ0x0D();
void HandleIRQ0x0E();
void HandleIRQ0x0F();

}

static void initIDT(GlobalDescriptorTable &gdt);
static void setIDTEntry(std::uint8_t interrupt, std::uint16_t cs,
                        void (*handler)(), std::uint8_t ring,
                        std::uint8_t type);
static void initPICs();
static void loadIDT();

static GateDescriptor IDT[256];
static InterruptHandler *handlers[256];

static Port8 picMasterCmd(0x20), picMasterData(0x21);
static Port8 picSlaveCmd(0xa0), picSlaveData(0xa1);

InterruptManager::InterruptManager(GlobalDescriptorTable &gdt)
{
    initIDT(gdt);
    initPICs();
    loadIDT();
}

static void
initIDT(GlobalDescriptorTable &gdt)
{
    std::uint16_t cs = gdt.getCS();

    const std::uint8_t IdtInterruptGate = 0xE;
    for (int i = 0; i < 256; ++i) {
        setIDTEntry(i, cs, &InterruptIgnore, 0, IdtInterruptGate);
    }

    setIDTEntry(0x00, cs, &HandleException0x00, 0, IdtInterruptGate);
    setIDTEntry(0x01, cs, &HandleException0x01, 0, IdtInterruptGate);
    setIDTEntry(0x02, cs, &HandleException0x02, 0, IdtInterruptGate);
    setIDTEntry(0x03, cs, &HandleException0x03, 0, IdtInterruptGate);
    setIDTEntry(0x04, cs, &HandleException0x04, 0, IdtInterruptGate);
    setIDTEntry(0x05, cs, &HandleException0x05, 0, IdtInterruptGate);
    setIDTEntry(0x06, cs, &HandleException0x06, 0, IdtInterruptGate);
    setIDTEntry(0x07, cs, &HandleException0x07, 0, IdtInterruptGate);
    setIDTEntry(0x08, cs, &HandleException0x08, 0, IdtInterruptGate);
    setIDTEntry(0x09, cs, &HandleException0x09, 0, IdtInterruptGate);
    setIDTEntry(0x0A, cs, &HandleException0x0A, 0, IdtInterruptGate);
    setIDTEntry(0x0B, cs, &HandleException0x0B, 0, IdtInterruptGate);
    setIDTEntry(0x0C, cs, &HandleException0x0C, 0, IdtInterruptGate);
    setIDTEntry(0x0D, cs, &HandleException0x0D, 0, IdtInterruptGate);
    setIDTEntry(0x0E, cs, &HandleException0x0E, 0, IdtInterruptGate);
    setIDTEntry(0x0F, cs, &HandleException0x0F, 0, IdtInterruptGate);
    setIDTEntry(0x10, cs, &HandleException0x10, 0, IdtInterruptGate);
    setIDTEntry(0x11, cs, &HandleException0x11, 0, IdtInterruptGate);
    setIDTEntry(0x12, cs, &HandleException0x12, 0, IdtInterruptGate);
    setIDTEntry(0x13, cs, &HandleException0x13, 0, IdtInterruptGate);

    setIDTEntry(0x80, cs, &HandleException0x80, 0, IdtInterruptGate);

    setIDTEntry(irqBase + 0x00, cs, &HandleIRQ0x00, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x01, cs, &HandleIRQ0x01, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x02, cs, &HandleIRQ0x02, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x03, cs, &HandleIRQ0x03, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x04, cs, &HandleIRQ0x04, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x05, cs, &HandleIRQ0x05, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x06, cs, &HandleIRQ0x06, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x07, cs, &HandleIRQ0x07, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x08, cs, &HandleIRQ0x08, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x09, cs, &HandleIRQ0x09, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x0A, cs, &HandleIRQ0x0A, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x0B, cs, &HandleIRQ0x0B, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x0C, cs, &HandleIRQ0x0C, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x0D, cs, &HandleIRQ0x0D, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x0E, cs, &HandleIRQ0x0E, 0, IdtInterruptGate);
    setIDTEntry(irqBase + 0x0F, cs, &HandleIRQ0x0F, 0, IdtInterruptGate);
}

static void
setIDTEntry(std::uint8_t interrupt, std::uint16_t cs, void (*handler)(),
            std::uint8_t ring, std::uint8_t type)
{
    const std::uint8_t IdtDescPresent = 0x80;
    auto ptr = reinterpret_cast<std::uint32_t>(handler);

    IDT[interrupt].data[0] = ptr & 0xff;
    IDT[interrupt].data[1] = (ptr >> 8) & 0xff;
    IDT[interrupt].data[2] = cs & 0xff;
    IDT[interrupt].data[3] = (cs >> 8) & 0xff;
    IDT[interrupt].data[4] = 0;
    IDT[interrupt].data[5] = IdtDescPresent | ((ring & 3) << 5) | type;
    IDT[interrupt].data[6] = (ptr >> 16) & 0xff;
    IDT[interrupt].data[7] = (ptr >> 24) & 0xff;
}

static void
initPICs()
{
    picMasterCmd.write(0x11);
    picSlaveCmd.write(0x11);

    // remap
    picMasterData.write(irqBase);
    picSlaveData.write(irqBase + 8);

    picMasterData.write(0x04);
    picSlaveData.write(0x02);

    picMasterData.write(0x01);
    picSlaveData.write(0x01);

    picMasterData.write(0x00);
    picSlaveData.write(0x00);
}

static void
loadIDT()
{
    std::uint8_t idtr[6];
    new(&idtr[0]) std::uint16_t(sizeof(IDT));
    new(&idtr[2]) std::uint32_t(reinterpret_cast<std::uint32_t>(&IDT));
    asm volatile("lidt %0" : : "m"(idtr));
}

InterruptManager::~InterruptManager()
{
    deactivate();
}

std::uint16_t
InterruptManager::getIRQBase() const
{
    return irqBase;
}

void
InterruptManager::activate()
{
    asm("sti");
}

void
InterruptManager::deactivate()
{
    asm("cli");
}

extern "C" std::uint32_t
handleInterrupt(std::uint8_t interrupt, std::uint32_t esp)
{
    if (handlers[interrupt] != nullptr) {
        esp = handlers[interrupt]->handleInterrupt(esp);
    } else if (interrupt != irqBase) {
        kprint("UNHANDLED INTERRUPT 0x", interrupt, "\n");
    }

    // Hardware interrupts must be acknowledged.
    if (irqBase <= interrupt && interrupt < irqBase + 16) {
        picMasterCmd.write(0x20);
        if (irqBase + 8 <= interrupt) {
            picSlaveCmd.write(0x20);
        }
    }

    return esp;
}

InterruptHandler::InterruptHandler(std::uint8_t interruptNumber)
    : interruptNumber(interruptNumber)
{
    handlers[interruptNumber] = this;
}

InterruptHandler::~InterruptHandler()
{
    if (handlers[interruptNumber] == this) {
        handlers[interruptNumber] = nullptr;
    }
}
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/tos

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/tos

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master