Compare commits

..

No commits in common. "bcf62c5f2e6368129d1b7f421c22ee940b69a675" and "7ce8e2d57bfa6c900a62a871288a89cc88d0bf6a" have entirely different histories.

25 changed files with 725 additions and 733 deletions

View File

@ -8,14 +8,16 @@ endif()
set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local) set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local)
if(EXISTS ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++) set(CMAKE_CXX_COMPILER_WORKS True)
set(CMAKE_CXX_COMPILER_WORKS True)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-gcc) set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-gcc)
set(CMAKE_C_COMPILER_WORKS True) set(CMAKE_C_COMPILER_WORKS True)
if(NOT EXISTS ${CMAKE_CXX_COMPILER})
set(CMAKE_CXX_COMPILER g++)
endif() endif()
if(DEFINED QEMU_ACCEL) if(DEFINED QEMU_ACCEL)

View File

@ -194,10 +194,10 @@ add_custom_command(
COMMAND cp ${CRTEND} . COMMAND cp ${CRTEND} .
) )
#add_custom_command( add_custom_command(
# TARGET kernel POST_BUILD TARGET kernel POST_BUILD
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
#) )
add_custom_command( add_custom_command(
OUTPUT font/prefs.psf.o OUTPUT font/prefs.psf.o

View File

@ -13,7 +13,7 @@
#define REGISTER_ISR_HANDLER(i) register_interrupt_handler(i, isr ## i) #define REGISTER_ISR_HANDLER(i) register_interrupt_handler(i, isr ## i)
#define REGISTER_IRQ_HANDLER(i) register_interrupt_handler(IRQ_VECTOR_BASE + i, irq ## i) #define REGISTER_IRQ_HANDLER(i) register_interrupt_handler(IRQ_VECTOR_BASE + i, irq ## i)
namespace Kernel::IDT namespace IDT
{ {
struct Registers struct Registers
@ -63,7 +63,7 @@ namespace Kernel::IDT
static IDTR s_idtr; static IDTR s_idtr;
static GateDescriptor* s_idt = nullptr; static GateDescriptor* s_idt = nullptr;
static Interruptable* s_interruptables[0x10] {}; static void(*s_irq_handlers[0x10])() { nullptr };
enum ISR enum ISR
{ {
@ -160,62 +160,53 @@ namespace Kernel::IDT
"Unkown Exception 0x1F", "Unkown Exception 0x1F",
}; };
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs) extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, Kernel::InterruptStack& interrupt_stack, const Registers* regs)
{ {
#if __enable_sse #if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11; bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace) if (from_userspace)
Thread::current().save_sse(); Kernel::Thread::current().save_sse();
#endif #endif
pid_t tid = Scheduler::current_tid(); pid_t tid = Kernel::Scheduler::current_tid();
pid_t pid = tid ? Process::current().pid() : 0; pid_t pid = tid ? Kernel::Process::current().pid() : 0;
if (tid) if (pid && isr == ISR::PageFault)
{ {
Thread::current().set_return_rsp(interrupt_stack.rsp); PageFaultError page_fault_error;
Thread::current().set_return_rip(interrupt_stack.rip); page_fault_error.raw = error;
if (isr == ISR::PageFault) // Try demand paging on non present pages
if (!page_fault_error.present)
{ {
// Check if stack is OOB asm volatile("sti");
auto& stack = Thread::current().stack(); auto result = Kernel::Process::current().allocate_page_for_demand_paging(regs->cr2);
if (interrupt_stack.rsp < stack.vaddr()) asm volatile("cli");
{
derrorln("Stack overflow");
goto done;
}
if (interrupt_stack.rsp >= stack.vaddr() + stack.size())
{
derrorln("Stack underflow");
goto done;
}
// Try demand paging on non present pages if (!result.is_error() && result.value())
PageFaultError page_fault_error; return;
page_fault_error.raw = error;
if (!page_fault_error.present) if (result.is_error())
{ {
asm volatile("sti"); dwarnln("Demand paging: {}", result.error());
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2); Kernel::Thread::current().handle_signal(SIGTERM);
asm volatile("cli");
if (!result.is_error() && result.value())
goto done;
if (result.is_error())
{
dwarnln("Demand paging: {}", result.error());
Thread::current().handle_signal(SIGTERM);
goto done;
}
} }
} }
} }
if (PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & PageTable::Flags::Present) if (tid)
{ {
auto* machine_code = (const uint8_t*)interrupt_stack.rip; auto start = Kernel::Thread::current().stack_base();
auto end = start + Kernel::Thread::current().stack_size();
if (interrupt_stack.rsp < start)
derrorln("Stack overflow");
if (interrupt_stack.rsp >= end)
derrorln("Stack underflow");
}
if (Kernel::PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & Kernel::PageTable::Flags::Present)
{
uint8_t* machine_code = (uint8_t*)interrupt_stack.rip;
dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}", dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
machine_code[0], machine_code[0],
machine_code[1], machine_code[1],
@ -242,10 +233,16 @@ namespace Kernel::IDT
regs->cr0, regs->cr2, regs->cr3, regs->cr4 regs->cr0, regs->cr2, regs->cr3, regs->cr4
); );
if (isr == ISR::PageFault) if (isr == ISR::PageFault)
PageTable::current().debug_dump(); Kernel::PageTable::current().debug_dump();
Debug::dump_stack_trace(); Debug::dump_stack_trace();
if (tid && Thread::current().is_userspace()) if (tid)
{
Kernel::Thread::current().set_return_rsp(interrupt_stack.rsp);
Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
}
if (tid && Kernel::Thread::current().is_userspace())
{ {
// TODO: Confirm and fix the exception to signal mappings // TODO: Confirm and fix the exception to signal mappings
@ -273,38 +270,36 @@ namespace Kernel::IDT
break; break;
} }
Thread::current().handle_signal(signal); Kernel::Thread::current().handle_signal(signal);
} }
else else
{ {
panic("Unhandled exception"); Kernel::panic("Unhandled exception");
} }
ASSERT(Thread::current().state() != Thread::State::Terminated); ASSERT(Kernel::Thread::current().state() != Kernel::Thread::State::Terminated);
done:
#if __enable_sse #if __enable_sse
if (from_userspace) if (from_userspace)
{ {
ASSERT(Thread::current().state() == Thread::State::Executing); ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
Thread::current().load_sse(); Kernel::Thread::current().load_sse();
} }
#endif #endif
return;
} }
extern "C" void cpp_irq_handler(uint64_t irq, InterruptStack& interrupt_stack) extern "C" void cpp_irq_handler(uint64_t irq, Kernel::InterruptStack& interrupt_stack)
{ {
#if __enable_sse #if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11; bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace) if (from_userspace)
Thread::current().save_sse(); Kernel::Thread::current().save_sse();
#endif #endif
if (Scheduler::current_tid()) if (Kernel::Scheduler::current_tid())
{ {
Thread::current().set_return_rsp(interrupt_stack.rsp); Kernel::Thread::current().set_return_rsp(interrupt_stack.rsp);
Thread::current().set_return_rip(interrupt_stack.rip); Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
} }
if (!InterruptController::get().is_in_service(irq)) if (!InterruptController::get().is_in_service(irq))
@ -312,21 +307,21 @@ done:
else else
{ {
InterruptController::get().eoi(irq); InterruptController::get().eoi(irq);
if (s_interruptables[irq]) if (s_irq_handlers[irq])
s_interruptables[irq]->handle_irq(); s_irq_handlers[irq]();
else else
dprintln("no handler for irq 0x{2H}\n", irq); dprintln("no handler for irq 0x{2H}\n", irq);
} }
Scheduler::get().reschedule_if_idling(); Kernel::Scheduler::get().reschedule_if_idling();
ASSERT(Thread::current().state() != Thread::State::Terminated); ASSERT(Kernel::Thread::current().state() != Kernel::Thread::State::Terminated);
#if __enable_sse #if __enable_sse
if (from_userspace) if (from_userspace)
{ {
ASSERT(Thread::current().state() == Thread::State::Executing); ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
Thread::current().load_sse(); Kernel::Thread::current().load_sse();
} }
#endif #endif
} }
@ -354,9 +349,9 @@ done:
s_idt[index].flags = 0xEE; s_idt[index].flags = 0xEE;
} }
void register_irq_handler(uint8_t irq, Interruptable* interruptable) void register_irq_handler(uint8_t irq, void(*handler)())
{ {
s_interruptables[irq] = interruptable; s_irq_handlers[irq] = handler;
} }
extern "C" void isr0(); extern "C" void isr0();
@ -485,4 +480,4 @@ done:
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
} }

View File

@ -4,56 +4,51 @@
#include <kernel/InterruptController.h> #include <kernel/InterruptController.h>
#include <kernel/Memory/Types.h> #include <kernel/Memory/Types.h>
namespace Kernel class APIC final : public InterruptController
{ {
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
class APIC final : public InterruptController private:
uint32_t read_from_local_apic(ptrdiff_t);
void write_to_local_apic(ptrdiff_t, uint32_t);
private:
~APIC() { ASSERT_NOT_REACHED(); }
static APIC* create();
friend class InterruptController;
private:
struct Processor
{ {
public: enum Flags : uint8_t
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
private:
uint32_t read_from_local_apic(ptrdiff_t);
void write_to_local_apic(ptrdiff_t, uint32_t);
private:
~APIC() { ASSERT_NOT_REACHED(); }
static APIC* create();
friend class InterruptController;
private:
struct Processor
{ {
enum Flags : uint8_t Enabled = 1,
{ OnlineCapable = 2,
Enabled = 1,
OnlineCapable = 2,
};
uint8_t processor_id;
uint8_t apic_id;
uint8_t flags;
}; };
uint8_t processor_id;
struct IOAPIC uint8_t apic_id;
{ uint8_t flags;
uint8_t id;
Kernel::paddr_t paddr;
Kernel::vaddr_t vaddr;
uint32_t gsi_base;
uint8_t max_redirs;
uint32_t read(uint8_t offset);
void write(uint8_t offset, uint32_t data);
};
private:
BAN::Vector<Processor> m_processors;
Kernel::paddr_t m_local_apic_paddr = 0;
Kernel::vaddr_t m_local_apic_vaddr = 0;
BAN::Vector<IOAPIC> m_io_apics;
uint8_t m_irq_overrides[0x100] {};
}; };
} struct IOAPIC
{
uint8_t id;
Kernel::paddr_t paddr;
Kernel::vaddr_t vaddr;
uint32_t gsi_base;
uint8_t max_redirs;
uint32_t read(uint8_t offset);
void write(uint8_t offset, uint32_t data);
};
private:
BAN::Vector<Processor> m_processors;
Kernel::paddr_t m_local_apic_paddr = 0;
Kernel::vaddr_t m_local_apic_vaddr = 0;
BAN::Vector<IOAPIC> m_io_apics;
uint8_t m_irq_overrides[0x100] {};
};

View File

@ -4,10 +4,11 @@
constexpr uint8_t IRQ_VECTOR_BASE = 0x20; constexpr uint8_t IRQ_VECTOR_BASE = 0x20;
namespace Kernel::IDT namespace IDT
{ {
void initialize(); void initialize();
void register_irq_handler(uint8_t irq, void(*f)());
[[noreturn]] void force_triple_fault(); [[noreturn]] void force_triple_fault();
} }

View File

@ -1,97 +0,0 @@
#pragma once
#include <stdint.h>
namespace Kernel::Input::PS2
{
enum IOPort : uint8_t
{
DATA = 0x60,
STATUS = 0x64,
COMMAND = 0x64,
};
enum Status : uint8_t
{
OUTPUT_FULL = (1 << 0),
INPUT_FULL = (1 << 1),
SYSTEM = (1 << 2),
DEVICE_OR_CONTROLLER = (1 << 3),
TIMEOUT_ERROR = (1 << 6),
PARITY_ERROR = (1 << 7),
};
enum Config : uint8_t
{
INTERRUPT_FIRST_PORT = (1 << 0),
INTERRUPT_SECOND_PORT = (1 << 1),
SYSTEM_FLAG = (1 << 2),
ZERO1 = (1 << 3),
CLOCK_FIRST_PORT = (1 << 4),
CLOCK_SECOND_PORT = (1 << 5),
TRANSLATION_FIRST_PORT = (1 << 6),
ZERO2 = (1 << 7),
};
enum Command : uint8_t
{
READ_CONFIG = 0x20,
WRITE_CONFIG = 0x60,
DISABLE_SECOND_PORT = 0xA7,
ENABLE_SECOND_PORT = 0xA8,
TEST_SECOND_PORT = 0xA9,
TEST_CONTROLLER = 0xAA,
TEST_FIRST_PORT = 0xAB,
DISABLE_FIRST_PORT = 0xAD,
ENABLE_FIRST_PORT = 0xAE,
WRITE_TO_SECOND_PORT = 0xD4,
};
enum Response : uint8_t
{
TEST_FIRST_PORT_PASS = 0x00,
TEST_SECOND_PORT_PASS = 0x00,
TEST_CONTROLLER_PASS = 0x55,
SELF_TEST_PASS = 0xAA,
ACK = 0xFA,
};
enum DeviceCommand : uint8_t
{
ENABLE_SCANNING = 0xF4,
DISABLE_SCANNING = 0xF5,
IDENTIFY = 0xF2,
RESET = 0xFF,
};
enum IRQ : uint8_t
{
DEVICE0 = 1,
DEVICE1 = 12,
};
enum KBResponse : uint8_t
{
KEY_ERROR_OR_BUFFER_OVERRUN1 = 0x00,
SELF_TEST_PASSED = 0xAA,
ECHO_RESPONSE = 0xEE,
RESEND = 0xFE,
KEY_ERROR_OR_BUFFER_OVERRUN2 = 0xFF,
};
enum KBScancode : uint8_t
{
SET_SCANCODE_SET1 = 1,
SET_SCANCODE_SET2 = 2,
SET_SCANCODE_SET3 = 3,
};
enum KBLeds : uint8_t
{
SCROLL_LOCK = (1 << 0),
NUM_LOCK = (1 << 1),
CAPS_LOCK = (1 << 2),
};
}

View File

@ -1,16 +1,16 @@
#pragma once #pragma once
#include <kernel/Device/Device.h> #include <kernel/Device/Device.h>
#include <kernel/InterruptController.h>
namespace Kernel::Input namespace Kernel::Input
{ {
class PS2Device : public CharacterDevice, public Interruptable class PS2Device : public CharacterDevice
{ {
public: public:
virtual ~PS2Device() {} virtual ~PS2Device() {}
virtual void on_byte(uint8_t) = 0;
public: public:
PS2Device() PS2Device()
: CharacterDevice(Mode::IRUSR | Mode::IRGRP, 0, 0) : CharacterDevice(Mode::IRUSR | Mode::IRGRP, 0, 0)
@ -33,8 +33,11 @@ namespace Kernel::Input
BAN::ErrorOr<void> reset_device(uint8_t); BAN::ErrorOr<void> reset_device(uint8_t);
BAN::ErrorOr<void> set_scanning(uint8_t, bool); BAN::ErrorOr<void> set_scanning(uint8_t, bool);
static void device0_irq();
static void device1_irq();
private: private:
PS2Device* m_devices[2] { nullptr, nullptr }; PS2Device* m_devices[2] { nullptr, nullptr };
}; };
} }

View File

@ -29,7 +29,7 @@ namespace Kernel::Input
public: public:
static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&); static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
virtual void handle_irq() override; virtual void on_byte(uint8_t) override;
virtual void update() override; virtual void update() override;
private: private:

View File

@ -5,42 +5,22 @@
#define DISABLE_INTERRUPTS() asm volatile("cli") #define DISABLE_INTERRUPTS() asm volatile("cli")
#define ENABLE_INTERRUPTS() asm volatile("sti") #define ENABLE_INTERRUPTS() asm volatile("sti")
namespace Kernel class InterruptController
{ {
public:
virtual ~InterruptController() {}
class Interruptable virtual void eoi(uint8_t) = 0;
{ virtual void enable_irq(uint8_t) = 0;
public: virtual bool is_in_service(uint8_t) = 0;
Interruptable() = default;
void set_irq(int irq); static void initialize(bool force_pic);
void enable_interrupt(); static InterruptController& get();
void disable_interrupt();
virtual void handle_irq() = 0; void enter_acpi_mode();
private: private:
int m_irq { -1 }; bool m_using_apic { false };
}; };
class InterruptController bool interrupts_enabled();
{
public:
virtual ~InterruptController() {}
virtual void eoi(uint8_t) = 0;
virtual void enable_irq(uint8_t) = 0;
virtual bool is_in_service(uint8_t) = 0;
static void initialize(bool force_pic);
static InterruptController& get();
void enter_acpi_mode();
private:
bool m_using_apic { false };
};
bool interrupts_enabled();
}

View File

@ -2,22 +2,17 @@
#include <kernel/InterruptController.h> #include <kernel/InterruptController.h>
namespace Kernel class PIC final : public InterruptController
{ {
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
class PIC final : public InterruptController static void remap();
{ static void mask_all();
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
static void remap(); private:
static void mask_all(); static PIC* create();
friend class InterruptController;
private: };
static PIC* create();
friend class InterruptController;
};
}

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <kernel/InterruptController.h>
#include <kernel/SpinLock.h> #include <kernel/SpinLock.h>
#include <kernel/Storage/ATAController.h> #include <kernel/Storage/ATAController.h>
@ -10,7 +9,7 @@ namespace Kernel
class ATADevice; class ATADevice;
class ATABus : public Interruptable class ATABus
{ {
public: public:
enum class DeviceType enum class DeviceType
@ -28,7 +27,7 @@ namespace Kernel
ATAController& controller() { return m_controller; } ATAController& controller() { return m_controller; }
virtual void handle_irq() override; void on_irq();
private: private:
ATABus(ATAController& controller, uint16_t base, uint16_t ctrl) ATABus(ATAController& controller, uint16_t base, uint16_t ctrl)

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include <BAN/CircularQueue.h>
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <kernel/InterruptController.h>
#include <kernel/Terminal/TTY.h> #include <kernel/Terminal/TTY.h>
namespace Kernel namespace Kernel
@ -36,7 +34,7 @@ namespace Kernel
uint32_t m_height { 0 }; uint32_t m_height { 0 };
}; };
class SerialTTY final : public TTY, public Interruptable class SerialTTY final : public TTY
{ {
public: public:
static BAN::ErrorOr<BAN::RefPtr<SerialTTY>> create(Serial); static BAN::ErrorOr<BAN::RefPtr<SerialTTY>> create(Serial);
@ -47,8 +45,6 @@ namespace Kernel
virtual void update() override; virtual void update() override;
virtual void handle_irq() override;
protected: protected:
virtual BAN::StringView name() const override { return m_name; } virtual BAN::StringView name() const override { return m_name; }
@ -59,7 +55,6 @@ namespace Kernel
private: private:
BAN::String m_name; BAN::String m_name;
Serial m_serial; Serial m_serial;
BAN::CircularQueue<uint8_t, 128> m_input;
public: public:
virtual dev_t rdev() const override { return m_rdev; } virtual dev_t rdev() const override { return m_rdev; }

View File

@ -1,12 +1,11 @@
#pragma once #pragma once
#include <kernel/InterruptController.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
namespace Kernel namespace Kernel
{ {
class HPET final : public Timer, public Interruptable class HPET final : public Timer
{ {
public: public:
static BAN::ErrorOr<BAN::UniqPtr<HPET>> create(bool force_pic); static BAN::ErrorOr<BAN::UniqPtr<HPET>> create(bool force_pic);
@ -14,8 +13,6 @@ namespace Kernel
virtual uint64_t ms_since_boot() const override; virtual uint64_t ms_since_boot() const override;
virtual timespec time_since_boot() const override; virtual timespec time_since_boot() const override;
virtual void handle_irq() override;
private: private:
HPET() = default; HPET() = default;
BAN::ErrorOr<void> initialize(bool force_pic); BAN::ErrorOr<void> initialize(bool force_pic);

View File

@ -1,12 +1,14 @@
#pragma once #pragma once
#include <kernel/InterruptController.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <stdint.h>
#define PIT_IRQ 0
namespace Kernel namespace Kernel
{ {
class PIT final : public Timer, public Interruptable class PIT final : public Timer
{ {
public: public:
static BAN::ErrorOr<BAN::UniqPtr<PIT>> create(); static BAN::ErrorOr<BAN::UniqPtr<PIT>> create();
@ -14,13 +16,8 @@ namespace Kernel
virtual uint64_t ms_since_boot() const override; virtual uint64_t ms_since_boot() const override;
virtual timespec time_since_boot() const override; virtual timespec time_since_boot() const override;
virtual void handle_irq() override;
private: private:
void initialize(); void initialize();
private:
volatile uint64_t m_system_time { 0 };
}; };
} }

View File

@ -20,242 +20,239 @@
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format // https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format
namespace Kernel struct MADT : public Kernel::ACPI::SDTHeader
{ {
uint32_t local_apic;
uint32_t flags;
} __attribute__((packed));
struct MADT : public Kernel::ACPI::SDTHeader struct MADTEntry
{ {
uint32_t local_apic; uint8_t type;
uint32_t flags; uint8_t length;
} __attribute__((packed)); union
struct MADTEntry
{
uint8_t type;
uint8_t length;
union
{
struct
{
uint8_t acpi_processor_id;
uint8_t apic_id;
uint32_t flags;
} __attribute__((packed)) entry0;
struct
{
uint8_t ioapic_id;
uint8_t reserved;
uint32_t ioapic_address;
uint32_t gsi_base;
} __attribute__((packed)) entry1;
struct
{
uint8_t bus_source;
uint8_t irq_source;
uint32_t gsi;
uint16_t flags;
} __attribute__((packed)) entry2;
struct
{
uint16_t reserved;
uint64_t address;
} __attribute__((packed)) entry5;
};
} __attribute__((packed));
union RedirectionEntry
{ {
struct struct
{ {
uint64_t vector : 8; uint8_t acpi_processor_id;
uint64_t delivery_mode : 3; uint8_t apic_id;
uint64_t destination_mode : 1; uint32_t flags;
uint64_t delivery_status : 1; } __attribute__((packed)) entry0;
uint64_t pin_polarity : 1;
uint64_t remote_irr : 1;
uint64_t trigger_mode : 1;
uint64_t mask : 1;
uint64_t reserved : 39;
uint64_t destination : 8;
};
struct struct
{ {
uint32_t lo_dword; uint8_t ioapic_id;
uint32_t hi_dword; uint8_t reserved;
}; uint32_t ioapic_address;
uint32_t gsi_base;
} __attribute__((packed)) entry1;
struct
{
uint8_t bus_source;
uint8_t irq_source;
uint32_t gsi;
uint16_t flags;
} __attribute__((packed)) entry2;
struct
{
uint16_t reserved;
uint64_t address;
} __attribute__((packed)) entry5;
}; };
} __attribute__((packed));
APIC* APIC::create() union RedirectionEntry
{
struct
{ {
uint32_t ecx, edx; uint64_t vector : 8;
CPUID::get_features(ecx, edx); uint64_t delivery_mode : 3;
if (!(edx & CPUID::Features::EDX_APIC)) uint64_t destination_mode : 1;
{ uint64_t delivery_status : 1;
dprintln("Local APIC is not available"); uint64_t pin_polarity : 1;
return nullptr; uint64_t remote_irr : 1;
} uint64_t trigger_mode : 1;
uint64_t mask : 1;
uint64_t reserved : 39;
uint64_t destination : 8;
};
struct
{
uint32_t lo_dword;
uint32_t hi_dword;
};
};
const MADT* madt = (const MADT*)Kernel::ACPI::get().get_header("APIC"); using namespace Kernel;
if (madt == nullptr)
{
dprintln("Could not find MADT header");
return nullptr;
}
APIC* apic = new APIC; APIC* APIC::create()
apic->m_local_apic_paddr = madt->local_apic; {
for (uint32_t i = 0x00; i <= 0xFF; i++) uint32_t ecx, edx;
apic->m_irq_overrides[i] = i; CPUID::get_features(ecx, edx);
if (!(edx & CPUID::Features::EDX_APIC))
uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT); {
while (madt_entry_addr < (uintptr_t)madt + madt->length) dprintln("Local APIC is not available");
{ return nullptr;
const MADTEntry* entry = (const MADTEntry*)madt_entry_addr;
switch (entry->type)
{
case 0:
Processor processor;
processor.processor_id = entry->entry0.acpi_processor_id;
processor.apic_id = entry->entry0.apic_id;
processor.flags = entry->entry0.flags & 0x03;
MUST(apic->m_processors.push_back(processor));
break;
case 1:
IOAPIC ioapic;
ioapic.id = entry->entry1.ioapic_id;
ioapic.paddr = entry->entry1.ioapic_address;
ioapic.gsi_base = entry->entry1.gsi_base;
ioapic.max_redirs = 0;
MUST(apic->m_io_apics.push_back(ioapic));
break;
case 2:
apic->m_irq_overrides[entry->entry2.irq_source] = entry->entry2.gsi;
break;
case 5:
apic->m_local_apic_paddr = entry->entry5.address;
break;
default:
dprintln("Unhandled madt entry, type {}", entry->type);
break;
}
madt_entry_addr += entry->length;
}
if (apic->m_local_apic_paddr == 0 || apic->m_io_apics.empty())
{
dprintln("MADT did not provide necessary information");
delete apic;
return nullptr;
}
// Map the local apic to kernel memory
{
vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
ASSERT(vaddr);
dprintln("lapic paddr {8H}", apic->m_local_apic_paddr);
apic->m_local_apic_vaddr = vaddr + (apic->m_local_apic_paddr % PAGE_SIZE);
dprintln("lapic vaddr {8H}", apic->m_local_apic_vaddr);
PageTable::kernel().map_page_at(
apic->m_local_apic_paddr & PAGE_ADDR_MASK,
apic->m_local_apic_vaddr & PAGE_ADDR_MASK,
PageTable::Flags::ReadWrite | PageTable::Flags::Present
);
}
// Map io apics to kernel memory
for (auto& io_apic : apic->m_io_apics)
{
vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
ASSERT(vaddr);
io_apic.vaddr = vaddr + (io_apic.paddr % PAGE_SIZE);
PageTable::kernel().map_page_at(
io_apic.paddr & PAGE_ADDR_MASK,
io_apic.vaddr & PAGE_ADDR_MASK,
PageTable::Flags::ReadWrite | PageTable::Flags::Present
);
io_apic.max_redirs = io_apic.read(IOAPIC_MAX_REDIRS);
}
// Mask all interrupts
uint32_t sivr = apic->read_from_local_apic(LAPIC_SIV_REG);
apic->write_to_local_apic(LAPIC_SIV_REG, sivr | 0x1FF);
#if DEBUG_PRINT_PROCESSORS
for (auto& processor : apic->m_processors)
{
dprintln("Processor{}", processor.processor_id);
dprintln(" lapic id: {}", processor.apic_id);
dprintln(" status: {}", (processor.flags & Processor::Flags::Enabled) ? "enabled" : (processor.flags & Processor::Flags::OnlineCapable) ? "can be enabled" : "disabled");
}
#endif
return apic;
} }
uint32_t APIC::read_from_local_apic(ptrdiff_t offset) const MADT* madt = (const MADT*)Kernel::ACPI::get().get_header("APIC");
if (madt == nullptr)
{ {
return MMIO::read32(m_local_apic_vaddr + offset); dprintln("Could not find MADT header");
return nullptr;
} }
void APIC::write_to_local_apic(ptrdiff_t offset, uint32_t data) APIC* apic = new APIC;
{ apic->m_local_apic_paddr = madt->local_apic;
MMIO::write32(m_local_apic_vaddr + offset, data); for (uint32_t i = 0x00; i <= 0xFF; i++)
} apic->m_irq_overrides[i] = i;
uint32_t APIC::IOAPIC::read(uint8_t offset) uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT);
while (madt_entry_addr < (uintptr_t)madt + madt->length)
{ {
MMIO::write32(vaddr, offset); const MADTEntry* entry = (const MADTEntry*)madt_entry_addr;
return MMIO::read32(vaddr + 16); switch (entry->type)
}
void APIC::IOAPIC::write(uint8_t offset, uint32_t data)
{
MMIO::write32(vaddr, offset);
MMIO::write32(vaddr + 16, data);
}
void APIC::eoi(uint8_t)
{
write_to_local_apic(LAPIC_EIO_REG, 0);
}
void APIC::enable_irq(uint8_t irq)
{
uint32_t gsi = m_irq_overrides[irq];
IOAPIC* ioapic = nullptr;
for (IOAPIC& io : m_io_apics)
{ {
if (io.gsi_base <= gsi && gsi <= io.gsi_base + io.max_redirs) case 0:
{ Processor processor;
ioapic = &io; processor.processor_id = entry->entry0.acpi_processor_id;
processor.apic_id = entry->entry0.apic_id;
processor.flags = entry->entry0.flags & 0x03;
MUST(apic->m_processors.push_back(processor));
break;
case 1:
IOAPIC ioapic;
ioapic.id = entry->entry1.ioapic_id;
ioapic.paddr = entry->entry1.ioapic_address;
ioapic.gsi_base = entry->entry1.gsi_base;
ioapic.max_redirs = 0;
MUST(apic->m_io_apics.push_back(ioapic));
break;
case 2:
apic->m_irq_overrides[entry->entry2.irq_source] = entry->entry2.gsi;
break;
case 5:
apic->m_local_apic_paddr = entry->entry5.address;
break;
default:
dprintln("Unhandled madt entry, type {}", entry->type);
break; break;
}
} }
ASSERT(ioapic); madt_entry_addr += entry->length;
RedirectionEntry redir;
redir.lo_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2);
redir.hi_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2 + 1);
ASSERT(redir.mask); // TODO: handle overlapping interrupts
redir.vector = IRQ_VECTOR_BASE + irq;
redir.mask = 0;
redir.destination = m_processors.front().apic_id;
ioapic->write(IOAPIC_REDIRS + gsi * 2, redir.lo_dword);
ioapic->write(IOAPIC_REDIRS + gsi * 2 + 1, redir.hi_dword);
} }
bool APIC::is_in_service(uint8_t irq) if (apic->m_local_apic_paddr == 0 || apic->m_io_apics.empty())
{ {
uint32_t dword = (irq + IRQ_VECTOR_BASE) / 32; dprintln("MADT did not provide necessary information");
uint32_t bit = (irq + IRQ_VECTOR_BASE) % 32; delete apic;
return nullptr;
uint32_t isr = read_from_local_apic(LAPIC_IS_REG + dword * 0x10);
return isr & (1 << bit);
} }
// Map the local apic to kernel memory
{
vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
ASSERT(vaddr);
dprintln("lapic paddr {8H}", apic->m_local_apic_paddr);
apic->m_local_apic_vaddr = vaddr + (apic->m_local_apic_paddr % PAGE_SIZE);
dprintln("lapic vaddr {8H}", apic->m_local_apic_vaddr);
PageTable::kernel().map_page_at(
apic->m_local_apic_paddr & PAGE_ADDR_MASK,
apic->m_local_apic_vaddr & PAGE_ADDR_MASK,
PageTable::Flags::ReadWrite | PageTable::Flags::Present
);
}
// Map io apics to kernel memory
for (auto& io_apic : apic->m_io_apics)
{
vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
ASSERT(vaddr);
io_apic.vaddr = vaddr + (io_apic.paddr % PAGE_SIZE);
PageTable::kernel().map_page_at(
io_apic.paddr & PAGE_ADDR_MASK,
io_apic.vaddr & PAGE_ADDR_MASK,
PageTable::Flags::ReadWrite | PageTable::Flags::Present
);
io_apic.max_redirs = io_apic.read(IOAPIC_MAX_REDIRS);
}
// Mask all interrupts
uint32_t sivr = apic->read_from_local_apic(LAPIC_SIV_REG);
apic->write_to_local_apic(LAPIC_SIV_REG, sivr | 0x1FF);
#if DEBUG_PRINT_PROCESSORS
for (auto& processor : apic->m_processors)
{
dprintln("Processor{}", processor.processor_id);
dprintln(" lapic id: {}", processor.apic_id);
dprintln(" status: {}", (processor.flags & Processor::Flags::Enabled) ? "enabled" : (processor.flags & Processor::Flags::OnlineCapable) ? "can be enabled" : "disabled");
}
#endif
return apic;
} }
uint32_t APIC::read_from_local_apic(ptrdiff_t offset)
{
return MMIO::read32(m_local_apic_vaddr + offset);
}
void APIC::write_to_local_apic(ptrdiff_t offset, uint32_t data)
{
MMIO::write32(m_local_apic_vaddr + offset, data);
}
uint32_t APIC::IOAPIC::read(uint8_t offset)
{
MMIO::write32(vaddr, offset);
return MMIO::read32(vaddr + 16);
}
void APIC::IOAPIC::write(uint8_t offset, uint32_t data)
{
MMIO::write32(vaddr, offset);
MMIO::write32(vaddr + 16, data);
}
void APIC::eoi(uint8_t)
{
write_to_local_apic(LAPIC_EIO_REG, 0);
}
void APIC::enable_irq(uint8_t irq)
{
uint32_t gsi = m_irq_overrides[irq];
IOAPIC* ioapic = nullptr;
for (IOAPIC& io : m_io_apics)
{
if (io.gsi_base <= gsi && gsi <= io.gsi_base + io.max_redirs)
{
ioapic = &io;
break;
}
}
ASSERT(ioapic);
RedirectionEntry redir;
redir.lo_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2);
redir.hi_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2 + 1);
ASSERT(redir.mask); // TODO: handle overlapping interrupts
redir.vector = IRQ_VECTOR_BASE + irq;
redir.mask = 0;
redir.destination = m_processors.front().apic_id;
ioapic->write(IOAPIC_REDIRS + gsi * 2, redir.lo_dword);
ioapic->write(IOAPIC_REDIRS + gsi * 2 + 1, redir.hi_dword);
}
bool APIC::is_in_service(uint8_t irq)
{
uint32_t dword = (irq + IRQ_VECTOR_BASE) / 32;
uint32_t bit = (irq + IRQ_VECTOR_BASE) % 32;
uint32_t isr = read_from_local_apic(LAPIC_IS_REG + dword * 0x10);
return isr & (1 << bit);
}

View File

@ -76,13 +76,13 @@ namespace Debug
void DebugLock::lock() void DebugLock::lock()
{ {
if (Kernel::interrupts_enabled()) if (interrupts_enabled())
s_debug_lock.lock(); s_debug_lock.lock();
} }
void DebugLock::unlock() void DebugLock::unlock()
{ {
if (Kernel::interrupts_enabled()) if (interrupts_enabled())
s_debug_lock.unlock(); s_debug_lock.unlock();
} }

View File

@ -2,7 +2,6 @@
#include <kernel/ACPI.h> #include <kernel/ACPI.h>
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/IDT.h> #include <kernel/IDT.h>
#include <kernel/Input/PS2Config.h>
#include <kernel/Input/PS2Controller.h> #include <kernel/Input/PS2Controller.h>
#include <kernel/Input/PS2Keyboard.h> #include <kernel/Input/PS2Keyboard.h>
#include <kernel/InterruptController.h> #include <kernel/InterruptController.h>
@ -12,6 +11,77 @@
namespace Kernel::Input namespace Kernel::Input
{ {
namespace PS2
{
enum IOPort : uint8_t
{
DATA = 0x60,
STATUS = 0x64,
COMMAND = 0x64,
};
enum Status : uint8_t
{
OUTPUT_FULL = (1 << 0),
INPUT_FULL = (1 << 1),
SYSTEM = (1 << 2),
DEVICE_OR_CONTROLLER = (1 << 3),
TIMEOUT_ERROR = (1 << 6),
PARITY_ERROR = (1 << 7),
};
enum Config : uint8_t
{
INTERRUPT_FIRST_PORT = (1 << 0),
INTERRUPT_SECOND_PORT = (1 << 1),
SYSTEM_FLAG = (1 << 2),
ZERO1 = (1 << 3),
CLOCK_FIRST_PORT = (1 << 4),
CLOCK_SECOND_PORT = (1 << 5),
TRANSLATION_FIRST_PORT = (1 << 6),
ZERO2 = (1 << 7),
};
enum Command : uint8_t
{
READ_CONFIG = 0x20,
WRITE_CONFIG = 0x60,
DISABLE_SECOND_PORT = 0xA7,
ENABLE_SECOND_PORT = 0xA8,
TEST_SECOND_PORT = 0xA9,
TEST_CONTROLLER = 0xAA,
TEST_FIRST_PORT = 0xAB,
DISABLE_FIRST_PORT = 0xAD,
ENABLE_FIRST_PORT = 0xAE,
WRITE_TO_SECOND_PORT = 0xD4,
};
enum Response
{
TEST_FIRST_PORT_PASS = 0x00,
TEST_SECOND_PORT_PASS = 0x00,
TEST_CONTROLLER_PASS = 0x55,
SELF_TEST_PASS = 0xAA,
ACK = 0xFA,
};
enum DeviceCommand
{
ENABLE_SCANNING = 0xF4,
DISABLE_SCANNING = 0xF5,
IDENTIFY = 0xF2,
RESET = 0xFF,
};
enum IRQ
{
DEVICE0 = 1,
DEVICE1 = 12,
};
}
static constexpr uint64_t s_device_timeout_ms = 100; static constexpr uint64_t s_device_timeout_ms = 100;
static void controller_send_command(PS2::Command command) static void controller_send_command(PS2::Command command)
@ -66,6 +136,20 @@ namespace Kernel::Input
return {}; return {};
} }
void PS2Controller::device0_irq()
{
auto& controller = PS2Controller::get();
ASSERT(controller.m_devices[0] != nullptr);
controller.m_devices[0]->on_byte(IO::inb(PS2::IOPort::DATA));
}
void PS2Controller::device1_irq()
{
auto& controller = PS2Controller::get();
ASSERT(controller.m_devices[1] != nullptr);
controller.m_devices[1]->on_byte(IO::inb(PS2::IOPort::DATA));
}
static PS2Controller* s_instance = nullptr; static PS2Controller* s_instance = nullptr;
BAN::ErrorOr<void> PS2Controller::initialize() BAN::ErrorOr<void> PS2Controller::initialize()
@ -172,15 +256,15 @@ namespace Kernel::Input
if (m_devices[0]) if (m_devices[0])
{ {
m_devices[0]->set_irq(PS2::IRQ::DEVICE0); IDT::register_irq_handler(PS2::IRQ::DEVICE0, device0_irq);
m_devices[0]->enable_interrupt(); InterruptController::get().enable_irq(PS2::IRQ::DEVICE0);
config |= PS2::Config::INTERRUPT_FIRST_PORT; config |= PS2::Config::INTERRUPT_FIRST_PORT;
DevFileSystem::get().add_device("input0", m_devices[0]); DevFileSystem::get().add_device("input0", m_devices[0]);
} }
if (m_devices[1]) if (m_devices[1])
{ {
m_devices[1]->set_irq(PS2::IRQ::DEVICE1); IDT::register_irq_handler(PS2::IRQ::DEVICE1, device1_irq);
m_devices[1]->enable_interrupt(); InterruptController::get().enable_irq(PS2::IRQ::DEVICE1);
config |= PS2::Config::INTERRUPT_SECOND_PORT; config |= PS2::Config::INTERRUPT_SECOND_PORT;
DevFileSystem::get().add_device("input1", m_devices[1]); DevFileSystem::get().add_device("input1", m_devices[1]);
} }

View File

@ -1,9 +1,7 @@
#include <BAN/ScopeGuard.h> #include <BAN/ScopeGuard.h>
#include <kernel/CriticalScope.h> #include <kernel/CriticalScope.h>
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Input/PS2Config.h>
#include <kernel/Input/PS2Keyboard.h> #include <kernel/Input/PS2Keyboard.h>
#include <kernel/IO.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
@ -14,6 +12,35 @@
namespace Kernel::Input namespace Kernel::Input
{ {
namespace PS2
{
enum Response
{
KEY_ERROR_OR_BUFFER_OVERRUN1 = 0x00,
SELF_TEST_PASSED = 0xAA,
ECHO_RESPONSE = 0xEE,
ACK = 0xFA,
RESEND = 0xFE,
KEY_ERROR_OR_BUFFER_OVERRUN2 = 0xFF,
};
enum Scancode
{
SET_SCANCODE_SET1 = 1,
SET_SCANCODE_SET2 = 2,
SET_SCANCODE_SET3 = 3,
};
enum Leds
{
SCROLL_LOCK = (1 << 0),
NUM_LOCK = (1 << 1),
CAPS_LOCK = (1 << 2),
};
}
BAN::ErrorOr<PS2Keyboard*> PS2Keyboard::create(PS2Controller& controller) BAN::ErrorOr<PS2Keyboard*> PS2Keyboard::create(PS2Controller& controller)
{ {
PS2Keyboard* keyboard = new PS2Keyboard(controller); PS2Keyboard* keyboard = new PS2Keyboard(controller);
@ -30,10 +57,8 @@ namespace Kernel::Input
, m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0)) , m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0))
{ } { }
void PS2Keyboard::handle_irq() void PS2Keyboard::on_byte(uint8_t byte)
{ {
uint8_t byte = IO::inb(PS2::IOPort::DATA);
// NOTE: This implementation does not allow using commands // NOTE: This implementation does not allow using commands
// that respond with more bytes than ACK // that respond with more bytes than ACK
switch (m_state) switch (m_state)
@ -46,11 +71,11 @@ namespace Kernel::Input
m_command_queue.pop(); m_command_queue.pop();
m_state = State::Normal; m_state = State::Normal;
break; break;
case PS2::KBResponse::RESEND: case PS2::Response::RESEND:
m_state = State::Normal; m_state = State::Normal;
break; break;
case PS2::KBResponse::KEY_ERROR_OR_BUFFER_OVERRUN1: case PS2::Response::KEY_ERROR_OR_BUFFER_OVERRUN1:
case PS2::KBResponse::KEY_ERROR_OR_BUFFER_OVERRUN2: case PS2::Response::KEY_ERROR_OR_BUFFER_OVERRUN2:
dwarnln("Key detection error or internal buffer overrun"); dwarnln("Key detection error or internal buffer overrun");
break; break;
default: default:
@ -72,7 +97,7 @@ namespace Kernel::Input
BAN::ErrorOr<void> PS2Keyboard::initialize() BAN::ErrorOr<void> PS2Keyboard::initialize()
{ {
append_command_queue(Command::SET_LEDS, 0x00); append_command_queue(Command::SET_LEDS, 0x00);
append_command_queue(Command::SCANCODE, PS2::KBScancode::SET_SCANCODE_SET2); append_command_queue(Command::SCANCODE, PS2::Scancode::SET_SCANCODE_SET2);
append_command_queue(Command::ENABLE_SCANNING); append_command_queue(Command::ENABLE_SCANNING);
return {}; return {};
} }
@ -209,11 +234,11 @@ namespace Kernel::Input
{ {
uint8_t new_leds = 0; uint8_t new_leds = 0;
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::ScrollLock) if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::ScrollLock)
new_leds |= PS2::KBLeds::SCROLL_LOCK; new_leds |= PS2::Leds::SCROLL_LOCK;
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::NumLock) if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::NumLock)
new_leds |= PS2::KBLeds::NUM_LOCK; new_leds |= PS2::Leds::NUM_LOCK;
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock) if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock)
new_leds |= PS2::KBLeds::CAPS_LOCK; new_leds |= PS2::Leds::CAPS_LOCK;
append_command_queue(Command::SET_LEDS, new_leds); append_command_queue(Command::SET_LEDS, new_leds);
} }

View File

@ -5,73 +5,47 @@
#include <lai/helpers/sci.h> #include <lai/helpers/sci.h>
namespace Kernel static InterruptController* s_instance = nullptr;
InterruptController& InterruptController::get()
{ {
ASSERT(s_instance);
namespace IDT { void register_irq_handler(uint8_t irq, Interruptable*); } return *s_instance;
}
static InterruptController* s_instance = nullptr;
void InterruptController::initialize(bool force_pic)
void Interruptable::set_irq(int irq) {
{ ASSERT(s_instance == nullptr);
if (m_irq != -1)
IDT::register_irq_handler(m_irq, nullptr); PIC::mask_all();
m_irq = irq; PIC::remap();
IDT::register_irq_handler(irq, this);
} if (!force_pic)
{
void Interruptable::enable_interrupt() s_instance = APIC::create();
{ if (s_instance)
ASSERT(m_irq != -1); {
InterruptController::get().enable_irq(m_irq); s_instance->m_using_apic = true;
} return;
}
void Interruptable::disable_interrupt() }
{
ASSERT_NOT_REACHED(); dprintln("Using PIC instead of APIC");
} s_instance = PIC::create();
ASSERT(s_instance);
InterruptController& InterruptController::get()
{ s_instance->m_using_apic = false;
ASSERT(s_instance); }
return *s_instance;
} void InterruptController::enter_acpi_mode()
{
void InterruptController::initialize(bool force_pic) if (lai_enable_acpi(m_using_apic ? 1 : 0) != 0)
{ dwarnln("could not enter acpi mode");
ASSERT(s_instance == nullptr); }
PIC::mask_all(); bool interrupts_enabled()
PIC::remap(); {
uintptr_t flags;
if (!force_pic) asm volatile("pushf; pop %0" : "=r"(flags) :: "memory");
{ return flags & (1 << 9);
s_instance = APIC::create();
if (s_instance)
{
s_instance->m_using_apic = true;
return;
}
}
dprintln("Using PIC instead of APIC");
s_instance = PIC::create();
ASSERT(s_instance);
s_instance->m_using_apic = false;
}
void InterruptController::enter_acpi_mode()
{
if (lai_enable_acpi(m_using_apic ? 1 : 0) != 0)
dwarnln("could not enter acpi mode");
}
bool interrupts_enabled()
{
uintptr_t flags;
asm volatile("pushf; pop %0" : "=r"(flags) :: "memory");
return flags & (1 << 9);
}
} }

View File

@ -66,7 +66,7 @@ namespace Kernel
ASSERT(page->next == nullptr); ASSERT(page->next == nullptr);
// Detatch page from top of the free list // Detatch page from top of the free list
m_free_list = m_free_list->prev; m_free_list = m_free_list->prev ? m_free_list->prev : nullptr;
if (m_free_list) if (m_free_list)
m_free_list->next = nullptr; m_free_list->next = nullptr;

View File

@ -17,85 +17,80 @@
#define ICW4_8086 0x01 #define ICW4_8086 0x01
namespace Kernel PIC* PIC::create()
{ {
mask_all();
PIC* PIC::create() remap();
{ return new PIC;
mask_all(); }
remap();
return new PIC; void PIC::remap()
} {
uint8_t a1 = IO::inb(PIC1_DATA);
void PIC::remap() uint8_t a2 = IO::inb(PIC2_DATA);
{
uint8_t a1 = IO::inb(PIC1_DATA); // Start the initialization sequence (in cascade mode)
uint8_t a2 = IO::inb(PIC2_DATA); IO::outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
IO::io_wait();
// Start the initialization sequence (in cascade mode) IO::outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
IO::outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4); IO::io_wait();
IO::io_wait();
IO::outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4); // ICW2
IO::io_wait(); IO::outb(PIC1_DATA, IRQ_VECTOR_BASE);
IO::io_wait();
// ICW2 IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08);
IO::outb(PIC1_DATA, IRQ_VECTOR_BASE); IO::io_wait();
IO::io_wait();
IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08); // ICW3
IO::io_wait(); IO::outb(PIC1_DATA, 4);
IO::io_wait();
// ICW3 IO::outb(PIC2_DATA, 2);
IO::outb(PIC1_DATA, 4); IO::io_wait();
IO::io_wait();
IO::outb(PIC2_DATA, 2); // ICW4
IO::io_wait(); IO::outb(PIC1_DATA, ICW4_8086);
IO::io_wait();
// ICW4 IO::outb(PIC2_DATA, ICW4_8086);
IO::outb(PIC1_DATA, ICW4_8086); IO::io_wait();
IO::io_wait();
IO::outb(PIC2_DATA, ICW4_8086); // Restore original masks
IO::io_wait(); IO::outb(PIC1_DATA, a1);
IO::outb(PIC2_DATA, a2);
// Restore original masks }
IO::outb(PIC1_DATA, a1);
IO::outb(PIC2_DATA, a2); void PIC::mask_all()
} {
// NOTE: don't mask irq 2 as it is needed for slave pic
void PIC::mask_all() IO::outb(PIC1_DATA, 0xFB);
{ IO::outb(PIC2_DATA, 0xFF);
// NOTE: don't mask irq 2 as it is needed for slave pic }
IO::outb(PIC1_DATA, 0xFB);
IO::outb(PIC2_DATA, 0xFF); void PIC::eoi(uint8_t irq)
} {
if (irq >= 8)
void PIC::eoi(uint8_t irq) IO::outb(PIC2_CMD, PIC_EOI);
{ IO::outb(PIC1_CMD, PIC_EOI);
if (irq >= 8) }
IO::outb(PIC2_CMD, PIC_EOI);
IO::outb(PIC1_CMD, PIC_EOI); void PIC::enable_irq(uint8_t irq)
} {
uint16_t port = PIC1_DATA;
void PIC::enable_irq(uint8_t irq) if(irq >= 8)
{ {
uint16_t port = PIC1_DATA; port = PIC2_DATA;
if(irq >= 8) irq -= 8;
{ }
port = PIC2_DATA; IO::outb(port, IO::inb(port) & ~(1 << irq));
irq -= 8; }
}
IO::outb(port, IO::inb(port) & ~(1 << irq)); bool PIC::is_in_service(uint8_t irq)
} {
uint16_t port = PIC1_CMD;
bool PIC::is_in_service(uint8_t irq) if (irq >= 8)
{ {
uint16_t port = PIC1_CMD; port = PIC2_CMD;
if (irq >= 8) irq -= 8;
{ }
port = PIC2_CMD; IO::outb(port, PIC_ISR);
irq -= 8; return IO::inb(port) & (1 << irq);
}
IO::outb(port, PIC_ISR);
return IO::inb(port) & (1 << irq);
}
} }

View File

@ -11,6 +11,40 @@
namespace Kernel namespace Kernel
{ {
static void bus_irq_handler0();
static void bus_irq_handler1();
struct BusIRQ
{
ATABus* bus { nullptr };
void (*handler)() { nullptr };
uint8_t irq { 0 };
};
static BusIRQ s_bus_irqs[] {
{ nullptr, bus_irq_handler0, 0 },
{ nullptr, bus_irq_handler1, 0 },
};
static void bus_irq_handler0() { ASSERT(s_bus_irqs[0].bus); s_bus_irqs[0].bus->on_irq(); }
static void bus_irq_handler1() { ASSERT(s_bus_irqs[1].bus); s_bus_irqs[1].bus->on_irq(); }
static void register_bus_irq_handler(ATABus* bus, uint8_t irq)
{
for (uint8_t i = 0; i < sizeof(s_bus_irqs) / sizeof(BusIRQ); i++)
{
if (s_bus_irqs[i].bus == nullptr)
{
s_bus_irqs[i].bus = bus;
s_bus_irqs[i].irq = irq;
IDT::register_irq_handler(irq, s_bus_irqs[i].handler);
InterruptController::get().enable_irq(irq);
return;
}
}
ASSERT_NOT_REACHED();
}
ATABus* ATABus::create(ATAController& controller, uint16_t base, uint16_t ctrl, uint8_t irq) ATABus* ATABus::create(ATAController& controller, uint16_t base, uint16_t ctrl, uint8_t irq)
{ {
ATABus* bus = new ATABus(controller, base, ctrl); ATABus* bus = new ATABus(controller, base, ctrl);
@ -21,11 +55,11 @@ namespace Kernel
void ATABus::initialize(uint8_t irq) void ATABus::initialize(uint8_t irq)
{ {
set_irq(irq); register_bus_irq_handler(this, irq);
enable_interrupt();
BAN::Vector<uint16_t> identify_buffer; uint16_t* identify_buffer = new uint16_t[256];
MUST(identify_buffer.resize(256)); ASSERT(identify_buffer);
BAN::ScopeGuard _([identify_buffer] { delete[] identify_buffer; });
for (uint8_t i = 0; i < 2; i++) for (uint8_t i = 0; i < 2; i++)
{ {
@ -38,11 +72,11 @@ namespace Kernel
BAN::ScopeGuard guard([this, i] { m_devices[i] = nullptr; }); BAN::ScopeGuard guard([this, i] { m_devices[i] = nullptr; });
auto type = identify(device, identify_buffer.data()); auto type = identify(device, identify_buffer);
if (type == DeviceType::None) if (type == DeviceType::None)
continue; continue;
auto res = device.initialize(type, identify_buffer.data()); auto res = device.initialize(type, identify_buffer);
if (res.is_error()) if (res.is_error())
{ {
dprintln("{}", res.error()); dprintln("{}", res.error());
@ -115,7 +149,7 @@ namespace Kernel
return type; return type;
} }
void ATABus::handle_irq() void ATABus::on_irq()
{ {
ASSERT(!m_has_got_irq); ASSERT(!m_has_got_irq);
if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR) if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)

View File

@ -1,4 +1,5 @@
#include <BAN/Array.h> #include <BAN/Array.h>
#include <BAN/CircularQueue.h>
#include <kernel/CriticalScope.h> #include <kernel/CriticalScope.h>
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/IDT.h> #include <kernel/IDT.h>
@ -37,9 +38,37 @@ namespace Kernel
static BAN::Array<Serial, sizeof(s_serial_ports) / sizeof(*s_serial_ports)> s_serial_drivers; static BAN::Array<Serial, sizeof(s_serial_ports) / sizeof(*s_serial_ports)> s_serial_drivers;
static bool s_has_devices { false }; static bool s_has_devices { false };
static BAN::CircularQueue<uint8_t, 128> s_com1_input;
static BAN::CircularQueue<uint8_t, 128> s_com2_input;
static BAN::RefPtr<SerialTTY> s_com1; static BAN::RefPtr<SerialTTY> s_com1;
static BAN::RefPtr<SerialTTY> s_com2; static BAN::RefPtr<SerialTTY> s_com2;
static void irq3_handler()
{
if (!s_serial_drivers[1].is_valid())
return;
uint8_t ch = IO::inb(COM2_PORT);
if (s_com2_input.full())
{
dwarnln("COM2 buffer full");
s_com2_input.pop();
}
s_com2_input.push(ch);
}
static void irq4_handler()
{
if (!s_serial_drivers[0].is_valid())
return;
uint8_t ch = IO::inb(COM1_PORT);
if (s_com1_input.full())
{
dwarnln("COM1 buffer full");
s_com1_input.pop();
}
s_com1_input.push(ch);
}
static dev_t next_rdev() static dev_t next_rdev()
{ {
static dev_t major = DevFileSystem::get().get_next_dev(); static dev_t major = DevFileSystem::get().get_next_dev();
@ -191,14 +220,14 @@ namespace Kernel
if (serial.port() == COM1_PORT) if (serial.port() == COM1_PORT)
{ {
IO::outb(COM1_PORT + 1, 1); IO::outb(COM1_PORT + 1, 1);
tty->set_irq(COM1_IRQ); InterruptController::get().enable_irq(COM1_IRQ);
tty->enable_interrupt(); IDT::register_irq_handler(COM1_IRQ, irq4_handler);
} }
else if (serial.port() == COM2_PORT) else if (serial.port() == COM2_PORT)
{ {
IO::outb(COM2_PORT + 1, 1); IO::outb(COM2_PORT + 1, 1);
tty->set_irq(COM2_IRQ); InterruptController::get().enable_irq(COM2_IRQ);
tty->enable_interrupt(); IDT::register_irq_handler(COM2_IRQ, irq3_handler);
} }
auto ref_ptr = BAN::RefPtr<SerialTTY>::adopt(tty); auto ref_ptr = BAN::RefPtr<SerialTTY>::adopt(tty);
@ -210,45 +239,41 @@ namespace Kernel
return ref_ptr; return ref_ptr;
} }
void SerialTTY::handle_irq()
{
uint8_t ch = IO::inb(m_serial.port());
if (m_input.full())
{
dwarnln("Serial buffer full");
m_input.pop();
}
m_input.push(ch);
}
void SerialTTY::update() void SerialTTY::update()
{ {
if (m_serial.port() != COM1_PORT && m_serial.port() != COM2_PORT) if (m_serial.port() != COM1_PORT && m_serial.port() != COM2_PORT)
return; return;
uint8_t buffer[128]; static uint8_t buffer[128];
{ auto update_com =
CriticalScope _; [&](auto& device, auto& input_queue)
if (m_input.empty())
return;
uint8_t* ptr = buffer;
while (!m_input.empty())
{ {
*ptr = m_input.front(); if (input_queue.empty())
if (*ptr == '\r') return;
*ptr = '\n'; uint8_t* ptr = buffer;
if (*ptr == 127) while (!input_queue.empty())
*ptr++ = '\b', *ptr++ = ' ', *ptr = '\b'; {
m_input.pop(); *ptr = input_queue.front();
ptr++; if (*ptr == '\r')
} *ptr = '\n';
*ptr = '\0'; if (*ptr == 127)
} *ptr++ = '\b', *ptr++ = ' ', *ptr = '\b';
input_queue.pop();
ptr++;
}
*ptr = '\0';
const uint8_t* ptr = buffer; ptr = buffer;
while (*ptr) while (*ptr)
handle_input_byte(*ptr++); device->handle_input_byte(*ptr++);
};
CriticalScope _;
if (m_serial.port() == COM1_PORT)
update_com(s_com1, s_com1_input);
if (m_serial.port() == COM2_PORT)
update_com(s_com2, s_com2_input);
} }
uint32_t SerialTTY::width() const uint32_t SerialTTY::width() const

View File

@ -131,17 +131,12 @@ namespace Kernel
for (int i = 1; i <= header->comparator_count; i++) for (int i = 1; i <= header->comparator_count; i++)
write_register(HPET_REG_TIMER_CONFIG(i), 0); write_register(HPET_REG_TIMER_CONFIG(i), 0);
set_irq(irq); IDT::register_irq_handler(irq, [] { Scheduler::get().timer_reschedule(); });
enable_interrupt(); InterruptController::get().enable_irq(irq);
return {}; return {};
} }
void HPET::handle_irq()
{
Scheduler::get().timer_reschedule();
}
uint64_t HPET::ms_since_boot() const uint64_t HPET::ms_since_boot() const
{ {
// FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads // FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads

View File

@ -4,8 +4,6 @@
#include <kernel/Scheduler.h> #include <kernel/Scheduler.h>
#include <kernel/Timer/PIT.h> #include <kernel/Timer/PIT.h>
#define PIT_IRQ 0
#define TIMER0_CTL 0x40 #define TIMER0_CTL 0x40
#define TIMER1_CTL 0x41 #define TIMER1_CTL 0x41
#define TIMER2_CTL 0x42 #define TIMER2_CTL 0x42
@ -29,6 +27,14 @@
namespace Kernel namespace Kernel
{ {
static volatile uint64_t s_system_time = 0;
void irq_handler()
{
s_system_time = s_system_time + 1;
Kernel::Scheduler::get().timer_reschedule();
}
BAN::ErrorOr<BAN::UniqPtr<PIT>> PIT::create() BAN::ErrorOr<BAN::UniqPtr<PIT>> PIT::create()
{ {
PIT* pit = new PIT(); PIT* pit = new PIT();
@ -47,24 +53,19 @@ namespace Kernel
IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff); IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff);
IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff); IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff);
set_irq(PIT_IRQ); IDT::register_irq_handler(PIT_IRQ, irq_handler);
enable_interrupt();
}
void PIT::handle_irq() InterruptController::get().enable_irq(PIT_IRQ);
{
m_system_time = m_system_time + 1;
Kernel::Scheduler::get().timer_reschedule();
} }
uint64_t PIT::ms_since_boot() const uint64_t PIT::ms_since_boot() const
{ {
return m_system_time * (MS_PER_S / TICKS_PER_SECOND); return s_system_time * (MS_PER_S / TICKS_PER_SECOND);
} }
timespec PIT::time_since_boot() const timespec PIT::time_since_boot() const
{ {
uint64_t ticks = m_system_time; uint64_t ticks = s_system_time;
return timespec { return timespec {
.tv_sec = ticks / TICKS_PER_SECOND, .tv_sec = ticks / TICKS_PER_SECOND,
.tv_nsec = (long)((ticks % TICKS_PER_SECOND) * (NS_PER_S / TICKS_PER_SECOND)) .tv_nsec = (long)((ticks % TICKS_PER_SECOND) * (NS_PER_S / TICKS_PER_SECOND))