Compare commits
3 Commits
7ce8e2d57b
...
bcf62c5f2e
Author | SHA1 | Date |
---|---|---|
Bananymous | bcf62c5f2e | |
Bananymous | 4d6322ff9c | |
Bananymous | 2eef581737 |
|
@ -8,16 +8,14 @@ endif()
|
||||||
|
|
||||||
set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local)
|
set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
if(EXISTS ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
set(CMAKE_CXX_COMPILER_WORKS True)
|
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
|
||||||
|
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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 IDT
|
namespace Kernel::IDT
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Registers
|
struct Registers
|
||||||
|
@ -63,7 +63,7 @@ namespace IDT
|
||||||
static IDTR s_idtr;
|
static IDTR s_idtr;
|
||||||
static GateDescriptor* s_idt = nullptr;
|
static GateDescriptor* s_idt = nullptr;
|
||||||
|
|
||||||
static void(*s_irq_handlers[0x10])() { nullptr };
|
static Interruptable* s_interruptables[0x10] {};
|
||||||
|
|
||||||
enum ISR
|
enum ISR
|
||||||
{
|
{
|
||||||
|
@ -160,53 +160,62 @@ namespace IDT
|
||||||
"Unkown Exception 0x1F",
|
"Unkown Exception 0x1F",
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, Kernel::InterruptStack& interrupt_stack, const Registers* regs)
|
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, 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)
|
||||||
Kernel::Thread::current().save_sse();
|
Thread::current().save_sse();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pid_t tid = Kernel::Scheduler::current_tid();
|
pid_t tid = Scheduler::current_tid();
|
||||||
pid_t pid = tid ? Kernel::Process::current().pid() : 0;
|
pid_t pid = tid ? Process::current().pid() : 0;
|
||||||
|
|
||||||
if (pid && isr == ISR::PageFault)
|
if (tid)
|
||||||
{
|
{
|
||||||
PageFaultError page_fault_error;
|
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||||
page_fault_error.raw = error;
|
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||||
|
|
||||||
// Try demand paging on non present pages
|
if (isr == ISR::PageFault)
|
||||||
if (!page_fault_error.present)
|
|
||||||
{
|
{
|
||||||
asm volatile("sti");
|
// Check if stack is OOB
|
||||||
auto result = Kernel::Process::current().allocate_page_for_demand_paging(regs->cr2);
|
auto& stack = Thread::current().stack();
|
||||||
asm volatile("cli");
|
if (interrupt_stack.rsp < stack.vaddr())
|
||||||
|
|
||||||
if (!result.is_error() && result.value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (result.is_error())
|
|
||||||
{
|
{
|
||||||
dwarnln("Demand paging: {}", result.error());
|
derrorln("Stack overflow");
|
||||||
Kernel::Thread::current().handle_signal(SIGTERM);
|
goto done;
|
||||||
|
}
|
||||||
|
if (interrupt_stack.rsp >= stack.vaddr() + stack.size())
|
||||||
|
{
|
||||||
|
derrorln("Stack underflow");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try demand paging on non present pages
|
||||||
|
PageFaultError page_fault_error;
|
||||||
|
page_fault_error.raw = error;
|
||||||
|
if (!page_fault_error.present)
|
||||||
|
{
|
||||||
|
asm volatile("sti");
|
||||||
|
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2);
|
||||||
|
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 (tid)
|
if (PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & PageTable::Flags::Present)
|
||||||
{
|
{
|
||||||
auto start = Kernel::Thread::current().stack_base();
|
auto* machine_code = (const uint8_t*)interrupt_stack.rip;
|
||||||
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],
|
||||||
|
@ -233,16 +242,10 @@ namespace 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)
|
||||||
Kernel::PageTable::current().debug_dump();
|
PageTable::current().debug_dump();
|
||||||
Debug::dump_stack_trace();
|
Debug::dump_stack_trace();
|
||||||
|
|
||||||
if (tid)
|
if (tid && Thread::current().is_userspace())
|
||||||
{
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -270,36 +273,38 @@ namespace IDT
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::Thread::current().handle_signal(signal);
|
Thread::current().handle_signal(signal);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Kernel::panic("Unhandled exception");
|
panic("Unhandled exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(Kernel::Thread::current().state() != Kernel::Thread::State::Terminated);
|
ASSERT(Thread::current().state() != Thread::State::Terminated);
|
||||||
|
|
||||||
|
done:
|
||||||
#if __enable_sse
|
#if __enable_sse
|
||||||
if (from_userspace)
|
if (from_userspace)
|
||||||
{
|
{
|
||||||
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
|
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||||
Kernel::Thread::current().load_sse();
|
Thread::current().load_sse();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void cpp_irq_handler(uint64_t irq, Kernel::InterruptStack& interrupt_stack)
|
extern "C" void cpp_irq_handler(uint64_t irq, 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)
|
||||||
Kernel::Thread::current().save_sse();
|
Thread::current().save_sse();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Kernel::Scheduler::current_tid())
|
if (Scheduler::current_tid())
|
||||||
{
|
{
|
||||||
Kernel::Thread::current().set_return_rsp(interrupt_stack.rsp);
|
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||||
Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
|
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InterruptController::get().is_in_service(irq))
|
if (!InterruptController::get().is_in_service(irq))
|
||||||
|
@ -307,21 +312,21 @@ namespace IDT
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InterruptController::get().eoi(irq);
|
InterruptController::get().eoi(irq);
|
||||||
if (s_irq_handlers[irq])
|
if (s_interruptables[irq])
|
||||||
s_irq_handlers[irq]();
|
s_interruptables[irq]->handle_irq();
|
||||||
else
|
else
|
||||||
dprintln("no handler for irq 0x{2H}\n", irq);
|
dprintln("no handler for irq 0x{2H}\n", irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::Scheduler::get().reschedule_if_idling();
|
Scheduler::get().reschedule_if_idling();
|
||||||
|
|
||||||
ASSERT(Kernel::Thread::current().state() != Kernel::Thread::State::Terminated);
|
ASSERT(Thread::current().state() != Thread::State::Terminated);
|
||||||
|
|
||||||
#if __enable_sse
|
#if __enable_sse
|
||||||
if (from_userspace)
|
if (from_userspace)
|
||||||
{
|
{
|
||||||
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
|
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||||
Kernel::Thread::current().load_sse();
|
Thread::current().load_sse();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -349,9 +354,9 @@ namespace IDT
|
||||||
s_idt[index].flags = 0xEE;
|
s_idt[index].flags = 0xEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_irq_handler(uint8_t irq, void(*handler)())
|
void register_irq_handler(uint8_t irq, Interruptable* interruptable)
|
||||||
{
|
{
|
||||||
s_irq_handlers[irq] = handler;
|
s_interruptables[irq] = interruptable;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void isr0();
|
extern "C" void isr0();
|
||||||
|
@ -480,4 +485,4 @@ namespace IDT
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,51 +4,56 @@
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
#include <kernel/Memory/Types.h>
|
#include <kernel/Memory/Types.h>
|
||||||
|
|
||||||
class APIC final : public InterruptController
|
namespace Kernel
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
virtual void eoi(uint8_t) override;
|
|
||||||
virtual void enable_irq(uint8_t) override;
|
|
||||||
virtual bool is_in_service(uint8_t) override;
|
|
||||||
|
|
||||||
private:
|
class APIC final : public InterruptController
|
||||||
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
|
public:
|
||||||
|
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
|
||||||
{
|
{
|
||||||
Enabled = 1,
|
enum Flags : uint8_t
|
||||||
OnlineCapable = 2,
|
{
|
||||||
|
Enabled = 1,
|
||||||
|
OnlineCapable = 2,
|
||||||
|
};
|
||||||
|
uint8_t processor_id;
|
||||||
|
uint8_t apic_id;
|
||||||
|
uint8_t flags;
|
||||||
};
|
};
|
||||||
uint8_t processor_id;
|
|
||||||
uint8_t apic_id;
|
struct IOAPIC
|
||||||
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] {};
|
|
||||||
};
|
|
||||||
|
|
|
@ -4,11 +4,10 @@
|
||||||
|
|
||||||
constexpr uint8_t IRQ_VECTOR_BASE = 0x20;
|
constexpr uint8_t IRQ_VECTOR_BASE = 0x20;
|
||||||
|
|
||||||
namespace IDT
|
namespace Kernel::IDT
|
||||||
{
|
{
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
void register_irq_handler(uint8_t irq, void(*f)());
|
|
||||||
[[noreturn]] void force_triple_fault();
|
[[noreturn]] void force_triple_fault();
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
#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),
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
class PS2Device : public CharacterDevice, public Interruptable
|
||||||
{
|
{
|
||||||
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,11 +33,8 @@ 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 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace Kernel::Input
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
|
static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
|
||||||
|
|
||||||
virtual void on_byte(uint8_t) override;
|
virtual void handle_irq() override;
|
||||||
virtual void update() override;
|
virtual void update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -5,22 +5,42 @@
|
||||||
#define DISABLE_INTERRUPTS() asm volatile("cli")
|
#define DISABLE_INTERRUPTS() asm volatile("cli")
|
||||||
#define ENABLE_INTERRUPTS() asm volatile("sti")
|
#define ENABLE_INTERRUPTS() asm volatile("sti")
|
||||||
|
|
||||||
class InterruptController
|
namespace Kernel
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
virtual ~InterruptController() {}
|
|
||||||
|
|
||||||
virtual void eoi(uint8_t) = 0;
|
class Interruptable
|
||||||
virtual void enable_irq(uint8_t) = 0;
|
{
|
||||||
virtual bool is_in_service(uint8_t) = 0;
|
public:
|
||||||
|
Interruptable() = default;
|
||||||
|
|
||||||
static void initialize(bool force_pic);
|
void set_irq(int irq);
|
||||||
static InterruptController& get();
|
void enable_interrupt();
|
||||||
|
void disable_interrupt();
|
||||||
|
|
||||||
void enter_acpi_mode();
|
virtual void handle_irq() = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_using_apic { false };
|
int m_irq { -1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
bool interrupts_enabled();
|
class InterruptController
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
|
@ -2,17 +2,22 @@
|
||||||
|
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
|
|
||||||
class PIC final : public InterruptController
|
namespace Kernel
|
||||||
{
|
{
|
||||||
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();
|
class PIC final : public InterruptController
|
||||||
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;
|
||||||
|
|
||||||
private:
|
static void remap();
|
||||||
static PIC* create();
|
static void mask_all();
|
||||||
friend class InterruptController;
|
|
||||||
};
|
private:
|
||||||
|
static PIC* create();
|
||||||
|
friend class InterruptController;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#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>
|
||||||
|
|
||||||
|
@ -9,7 +10,7 @@ namespace Kernel
|
||||||
|
|
||||||
class ATADevice;
|
class ATADevice;
|
||||||
|
|
||||||
class ATABus
|
class ATABus : public Interruptable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class DeviceType
|
enum class DeviceType
|
||||||
|
@ -27,7 +28,7 @@ namespace Kernel
|
||||||
|
|
||||||
ATAController& controller() { return m_controller; }
|
ATAController& controller() { return m_controller; }
|
||||||
|
|
||||||
void on_irq();
|
virtual void handle_irq() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ATABus(ATAController& controller, uint16_t base, uint16_t ctrl)
|
ATABus(ATAController& controller, uint16_t base, uint16_t ctrl)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#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
|
||||||
|
@ -34,7 +36,7 @@ namespace Kernel
|
||||||
uint32_t m_height { 0 };
|
uint32_t m_height { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class SerialTTY final : public TTY
|
class SerialTTY final : public TTY, public Interruptable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::RefPtr<SerialTTY>> create(Serial);
|
static BAN::ErrorOr<BAN::RefPtr<SerialTTY>> create(Serial);
|
||||||
|
@ -45,6 +47,8 @@ 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; }
|
||||||
|
|
||||||
|
@ -55,6 +59,7 @@ 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; }
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
#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
|
class HPET final : public Timer, public Interruptable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<HPET>> create(bool force_pic);
|
static BAN::ErrorOr<BAN::UniqPtr<HPET>> create(bool force_pic);
|
||||||
|
@ -13,6 +14,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:
|
||||||
HPET() = default;
|
HPET() = default;
|
||||||
BAN::ErrorOr<void> initialize(bool force_pic);
|
BAN::ErrorOr<void> initialize(bool force_pic);
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
#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
|
class PIT final : public Timer, public Interruptable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<PIT>> create();
|
static BAN::ErrorOr<BAN::UniqPtr<PIT>> create();
|
||||||
|
@ -16,8 +14,13 @@ 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 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,239 +20,242 @@
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
struct MADT : public Kernel::ACPI::SDTHeader
|
namespace Kernel
|
||||||
{
|
{
|
||||||
uint32_t local_apic;
|
|
||||||
uint32_t flags;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct MADTEntry
|
struct MADT : public Kernel::ACPI::SDTHeader
|
||||||
{
|
{
|
||||||
uint8_t type;
|
uint32_t local_apic;
|
||||||
uint8_t length;
|
uint32_t flags;
|
||||||
union
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
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
|
||||||
{
|
{
|
||||||
uint8_t acpi_processor_id;
|
uint64_t vector : 8;
|
||||||
uint8_t apic_id;
|
uint64_t delivery_mode : 3;
|
||||||
uint32_t flags;
|
uint64_t destination_mode : 1;
|
||||||
} __attribute__((packed)) entry0;
|
uint64_t delivery_status : 1;
|
||||||
|
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
|
||||||
{
|
{
|
||||||
uint8_t ioapic_id;
|
uint32_t lo_dword;
|
||||||
uint8_t reserved;
|
uint32_t hi_dword;
|
||||||
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
|
APIC* APIC::create()
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
{
|
||||||
uint64_t vector : 8;
|
uint32_t ecx, edx;
|
||||||
uint64_t delivery_mode : 3;
|
CPUID::get_features(ecx, edx);
|
||||||
uint64_t destination_mode : 1;
|
if (!(edx & CPUID::Features::EDX_APIC))
|
||||||
uint64_t delivery_status : 1;
|
|
||||||
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
|
|
||||||
{
|
|
||||||
uint32_t lo_dword;
|
|
||||||
uint32_t hi_dword;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using namespace Kernel;
|
|
||||||
|
|
||||||
APIC* APIC::create()
|
|
||||||
{
|
|
||||||
uint32_t ecx, edx;
|
|
||||||
CPUID::get_features(ecx, edx);
|
|
||||||
if (!(edx & CPUID::Features::EDX_APIC))
|
|
||||||
{
|
|
||||||
dprintln("Local APIC is not available");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MADT* madt = (const MADT*)Kernel::ACPI::get().get_header("APIC");
|
|
||||||
if (madt == nullptr)
|
|
||||||
{
|
|
||||||
dprintln("Could not find MADT header");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
APIC* apic = new APIC;
|
|
||||||
apic->m_local_apic_paddr = madt->local_apic;
|
|
||||||
for (uint32_t i = 0x00; i <= 0xFF; i++)
|
|
||||||
apic->m_irq_overrides[i] = i;
|
|
||||||
|
|
||||||
uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT);
|
|
||||||
while (madt_entry_addr < (uintptr_t)madt + madt->length)
|
|
||||||
{
|
|
||||||
const MADTEntry* entry = (const MADTEntry*)madt_entry_addr;
|
|
||||||
switch (entry->type)
|
|
||||||
{
|
{
|
||||||
case 0:
|
dprintln("Local APIC is not available");
|
||||||
Processor processor;
|
return nullptr;
|
||||||
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())
|
const MADT* madt = (const MADT*)Kernel::ACPI::get().get_header("APIC");
|
||||||
{
|
if (madt == nullptr)
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
dprintln("Could not find MADT header");
|
||||||
break;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
APIC* apic = new APIC;
|
||||||
|
apic->m_local_apic_paddr = madt->local_apic;
|
||||||
|
for (uint32_t i = 0x00; i <= 0xFF; i++)
|
||||||
|
apic->m_irq_overrides[i] = i;
|
||||||
|
|
||||||
|
uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT);
|
||||||
|
while (madt_entry_addr < (uintptr_t)madt + madt->length)
|
||||||
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
ASSERT(ioapic);
|
|
||||||
|
|
||||||
RedirectionEntry redir;
|
uint32_t APIC::read_from_local_apic(ptrdiff_t offset)
|
||||||
redir.lo_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2);
|
{
|
||||||
redir.hi_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2 + 1);
|
return MMIO::read32(m_local_apic_vaddr + offset);
|
||||||
ASSERT(redir.mask); // TODO: handle overlapping interrupts
|
}
|
||||||
|
|
||||||
redir.vector = IRQ_VECTOR_BASE + irq;
|
void APIC::write_to_local_apic(ptrdiff_t offset, uint32_t data)
|
||||||
redir.mask = 0;
|
{
|
||||||
redir.destination = m_processors.front().apic_id;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -76,13 +76,13 @@ namespace Debug
|
||||||
|
|
||||||
void DebugLock::lock()
|
void DebugLock::lock()
|
||||||
{
|
{
|
||||||
if (interrupts_enabled())
|
if (Kernel::interrupts_enabled())
|
||||||
s_debug_lock.lock();
|
s_debug_lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugLock::unlock()
|
void DebugLock::unlock()
|
||||||
{
|
{
|
||||||
if (interrupts_enabled())
|
if (Kernel::interrupts_enabled())
|
||||||
s_debug_lock.unlock();
|
s_debug_lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#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>
|
||||||
|
@ -11,77 +12,6 @@
|
||||||
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)
|
||||||
|
@ -136,20 +66,6 @@ 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()
|
||||||
|
@ -256,15 +172,15 @@ namespace Kernel::Input
|
||||||
|
|
||||||
if (m_devices[0])
|
if (m_devices[0])
|
||||||
{
|
{
|
||||||
IDT::register_irq_handler(PS2::IRQ::DEVICE0, device0_irq);
|
m_devices[0]->set_irq(PS2::IRQ::DEVICE0);
|
||||||
InterruptController::get().enable_irq(PS2::IRQ::DEVICE0);
|
m_devices[0]->enable_interrupt();
|
||||||
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])
|
||||||
{
|
{
|
||||||
IDT::register_irq_handler(PS2::IRQ::DEVICE1, device1_irq);
|
m_devices[1]->set_irq(PS2::IRQ::DEVICE1);
|
||||||
InterruptController::get().enable_irq(PS2::IRQ::DEVICE1);
|
m_devices[1]->enable_interrupt();
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#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>
|
||||||
|
@ -12,35 +14,6 @@
|
||||||
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);
|
||||||
|
@ -57,8 +30,10 @@ namespace Kernel::Input
|
||||||
, m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0))
|
, m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void PS2Keyboard::on_byte(uint8_t byte)
|
void PS2Keyboard::handle_irq()
|
||||||
{
|
{
|
||||||
|
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)
|
||||||
|
@ -71,11 +46,11 @@ namespace Kernel::Input
|
||||||
m_command_queue.pop();
|
m_command_queue.pop();
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
break;
|
break;
|
||||||
case PS2::Response::RESEND:
|
case PS2::KBResponse::RESEND:
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
break;
|
break;
|
||||||
case PS2::Response::KEY_ERROR_OR_BUFFER_OVERRUN1:
|
case PS2::KBResponse::KEY_ERROR_OR_BUFFER_OVERRUN1:
|
||||||
case PS2::Response::KEY_ERROR_OR_BUFFER_OVERRUN2:
|
case PS2::KBResponse::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:
|
||||||
|
@ -97,7 +72,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::Scancode::SET_SCANCODE_SET2);
|
append_command_queue(Command::SCANCODE, PS2::KBScancode::SET_SCANCODE_SET2);
|
||||||
append_command_queue(Command::ENABLE_SCANNING);
|
append_command_queue(Command::ENABLE_SCANNING);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -234,11 +209,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::Leds::SCROLL_LOCK;
|
new_leds |= PS2::KBLeds::SCROLL_LOCK;
|
||||||
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::NumLock)
|
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::NumLock)
|
||||||
new_leds |= PS2::Leds::NUM_LOCK;
|
new_leds |= PS2::KBLeds::NUM_LOCK;
|
||||||
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock)
|
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock)
|
||||||
new_leds |= PS2::Leds::CAPS_LOCK;
|
new_leds |= PS2::KBLeds::CAPS_LOCK;
|
||||||
append_command_queue(Command::SET_LEDS, new_leds);
|
append_command_queue(Command::SET_LEDS, new_leds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,47 +5,73 @@
|
||||||
|
|
||||||
#include <lai/helpers/sci.h>
|
#include <lai/helpers/sci.h>
|
||||||
|
|
||||||
static InterruptController* s_instance = nullptr;
|
namespace Kernel
|
||||||
|
|
||||||
InterruptController& InterruptController::get()
|
|
||||||
{
|
{
|
||||||
ASSERT(s_instance);
|
|
||||||
return *s_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterruptController::initialize(bool force_pic)
|
namespace IDT { void register_irq_handler(uint8_t irq, Interruptable*); }
|
||||||
{
|
|
||||||
ASSERT(s_instance == nullptr);
|
|
||||||
|
|
||||||
PIC::mask_all();
|
static InterruptController* s_instance = nullptr;
|
||||||
PIC::remap();
|
|
||||||
|
|
||||||
if (!force_pic)
|
void Interruptable::set_irq(int irq)
|
||||||
{
|
{
|
||||||
s_instance = APIC::create();
|
if (m_irq != -1)
|
||||||
if (s_instance)
|
IDT::register_irq_handler(m_irq, nullptr);
|
||||||
{
|
m_irq = irq;
|
||||||
s_instance->m_using_apic = true;
|
IDT::register_irq_handler(irq, this);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintln("Using PIC instead of APIC");
|
void Interruptable::enable_interrupt()
|
||||||
s_instance = PIC::create();
|
{
|
||||||
ASSERT(s_instance);
|
ASSERT(m_irq != -1);
|
||||||
|
InterruptController::get().enable_irq(m_irq);
|
||||||
|
}
|
||||||
|
|
||||||
s_instance->m_using_apic = false;
|
void Interruptable::disable_interrupt()
|
||||||
}
|
{
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
void InterruptController::enter_acpi_mode()
|
InterruptController& InterruptController::get()
|
||||||
{
|
{
|
||||||
if (lai_enable_acpi(m_using_apic ? 1 : 0) != 0)
|
ASSERT(s_instance);
|
||||||
dwarnln("could not enter acpi mode");
|
return *s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterruptController::initialize(bool force_pic)
|
||||||
|
{
|
||||||
|
ASSERT(s_instance == nullptr);
|
||||||
|
|
||||||
|
PIC::mask_all();
|
||||||
|
PIC::remap();
|
||||||
|
|
||||||
|
if (!force_pic)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
bool interrupts_enabled()
|
|
||||||
{
|
|
||||||
uintptr_t flags;
|
|
||||||
asm volatile("pushf; pop %0" : "=r"(flags) :: "memory");
|
|
||||||
return flags & (1 << 9);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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->prev : nullptr;
|
m_free_list = m_free_list->prev;
|
||||||
if (m_free_list)
|
if (m_free_list)
|
||||||
m_free_list->next = nullptr;
|
m_free_list->next = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -17,80 +17,85 @@
|
||||||
|
|
||||||
#define ICW4_8086 0x01
|
#define ICW4_8086 0x01
|
||||||
|
|
||||||
PIC* PIC::create()
|
namespace Kernel
|
||||||
{
|
{
|
||||||
mask_all();
|
|
||||||
remap();
|
|
||||||
return new PIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PIC::remap()
|
PIC* PIC::create()
|
||||||
{
|
|
||||||
uint8_t a1 = IO::inb(PIC1_DATA);
|
|
||||||
uint8_t a2 = IO::inb(PIC2_DATA);
|
|
||||||
|
|
||||||
// Start the initialization sequence (in cascade mode)
|
|
||||||
IO::outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
|
||||||
IO::io_wait();
|
|
||||||
IO::outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
|
|
||||||
IO::io_wait();
|
|
||||||
|
|
||||||
// ICW2
|
|
||||||
IO::outb(PIC1_DATA, IRQ_VECTOR_BASE);
|
|
||||||
IO::io_wait();
|
|
||||||
IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08);
|
|
||||||
IO::io_wait();
|
|
||||||
|
|
||||||
// ICW3
|
|
||||||
IO::outb(PIC1_DATA, 4);
|
|
||||||
IO::io_wait();
|
|
||||||
IO::outb(PIC2_DATA, 2);
|
|
||||||
IO::io_wait();
|
|
||||||
|
|
||||||
// ICW4
|
|
||||||
IO::outb(PIC1_DATA, ICW4_8086);
|
|
||||||
IO::io_wait();
|
|
||||||
IO::outb(PIC2_DATA, ICW4_8086);
|
|
||||||
IO::io_wait();
|
|
||||||
|
|
||||||
// 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
|
|
||||||
IO::outb(PIC1_DATA, 0xFB);
|
|
||||||
IO::outb(PIC2_DATA, 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PIC::eoi(uint8_t irq)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
if(irq >= 8)
|
|
||||||
{
|
{
|
||||||
port = PIC2_DATA;
|
mask_all();
|
||||||
irq -= 8;
|
remap();
|
||||||
|
return new PIC;
|
||||||
}
|
}
|
||||||
IO::outb(port, IO::inb(port) & ~(1 << irq));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PIC::is_in_service(uint8_t irq)
|
void PIC::remap()
|
||||||
{
|
|
||||||
uint16_t port = PIC1_CMD;
|
|
||||||
if (irq >= 8)
|
|
||||||
{
|
{
|
||||||
port = PIC2_CMD;
|
uint8_t a1 = IO::inb(PIC1_DATA);
|
||||||
irq -= 8;
|
uint8_t a2 = IO::inb(PIC2_DATA);
|
||||||
|
|
||||||
|
// Start the initialization sequence (in cascade mode)
|
||||||
|
IO::outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
||||||
|
IO::io_wait();
|
||||||
|
IO::outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
|
||||||
|
IO::io_wait();
|
||||||
|
|
||||||
|
// ICW2
|
||||||
|
IO::outb(PIC1_DATA, IRQ_VECTOR_BASE);
|
||||||
|
IO::io_wait();
|
||||||
|
IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08);
|
||||||
|
IO::io_wait();
|
||||||
|
|
||||||
|
// ICW3
|
||||||
|
IO::outb(PIC1_DATA, 4);
|
||||||
|
IO::io_wait();
|
||||||
|
IO::outb(PIC2_DATA, 2);
|
||||||
|
IO::io_wait();
|
||||||
|
|
||||||
|
// ICW4
|
||||||
|
IO::outb(PIC1_DATA, ICW4_8086);
|
||||||
|
IO::io_wait();
|
||||||
|
IO::outb(PIC2_DATA, ICW4_8086);
|
||||||
|
IO::io_wait();
|
||||||
|
|
||||||
|
// Restore original masks
|
||||||
|
IO::outb(PIC1_DATA, a1);
|
||||||
|
IO::outb(PIC2_DATA, a2);
|
||||||
}
|
}
|
||||||
IO::outb(port, PIC_ISR);
|
|
||||||
return IO::inb(port) & (1 << irq);
|
void PIC::mask_all()
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
IO::outb(PIC2_CMD, PIC_EOI);
|
||||||
|
IO::outb(PIC1_CMD, PIC_EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PIC::enable_irq(uint8_t irq)
|
||||||
|
{
|
||||||
|
uint16_t port = PIC1_DATA;
|
||||||
|
if(irq >= 8)
|
||||||
|
{
|
||||||
|
port = PIC2_DATA;
|
||||||
|
irq -= 8;
|
||||||
|
}
|
||||||
|
IO::outb(port, IO::inb(port) & ~(1 << irq));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PIC::is_in_service(uint8_t irq)
|
||||||
|
{
|
||||||
|
uint16_t port = PIC1_CMD;
|
||||||
|
if (irq >= 8)
|
||||||
|
{
|
||||||
|
port = PIC2_CMD;
|
||||||
|
irq -= 8;
|
||||||
|
}
|
||||||
|
IO::outb(port, PIC_ISR);
|
||||||
|
return IO::inb(port) & (1 << irq);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,40 +11,6 @@
|
||||||
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);
|
||||||
|
@ -55,11 +21,11 @@ namespace Kernel
|
||||||
|
|
||||||
void ATABus::initialize(uint8_t irq)
|
void ATABus::initialize(uint8_t irq)
|
||||||
{
|
{
|
||||||
register_bus_irq_handler(this, irq);
|
set_irq(irq);
|
||||||
|
enable_interrupt();
|
||||||
|
|
||||||
uint16_t* identify_buffer = new uint16_t[256];
|
BAN::Vector<uint16_t> identify_buffer;
|
||||||
ASSERT(identify_buffer);
|
MUST(identify_buffer.resize(256));
|
||||||
BAN::ScopeGuard _([identify_buffer] { delete[] identify_buffer; });
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 2; i++)
|
for (uint8_t i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
|
@ -72,11 +38,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);
|
auto type = identify(device, identify_buffer.data());
|
||||||
if (type == DeviceType::None)
|
if (type == DeviceType::None)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto res = device.initialize(type, identify_buffer);
|
auto res = device.initialize(type, identify_buffer.data());
|
||||||
if (res.is_error())
|
if (res.is_error())
|
||||||
{
|
{
|
||||||
dprintln("{}", res.error());
|
dprintln("{}", res.error());
|
||||||
|
@ -149,7 +115,7 @@ namespace Kernel
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATABus::on_irq()
|
void ATABus::handle_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)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#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>
|
||||||
|
@ -38,37 +37,9 @@ 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();
|
||||||
|
@ -220,14 +191,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);
|
||||||
InterruptController::get().enable_irq(COM1_IRQ);
|
tty->set_irq(COM1_IRQ);
|
||||||
IDT::register_irq_handler(COM1_IRQ, irq4_handler);
|
tty->enable_interrupt();
|
||||||
}
|
}
|
||||||
else if (serial.port() == COM2_PORT)
|
else if (serial.port() == COM2_PORT)
|
||||||
{
|
{
|
||||||
IO::outb(COM2_PORT + 1, 1);
|
IO::outb(COM2_PORT + 1, 1);
|
||||||
InterruptController::get().enable_irq(COM2_IRQ);
|
tty->set_irq(COM2_IRQ);
|
||||||
IDT::register_irq_handler(COM2_IRQ, irq3_handler);
|
tty->enable_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ref_ptr = BAN::RefPtr<SerialTTY>::adopt(tty);
|
auto ref_ptr = BAN::RefPtr<SerialTTY>::adopt(tty);
|
||||||
|
@ -239,41 +210,45 @@ 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;
|
||||||
|
|
||||||
static uint8_t buffer[128];
|
uint8_t buffer[128];
|
||||||
|
|
||||||
auto update_com =
|
{
|
||||||
[&](auto& device, auto& input_queue)
|
CriticalScope _;
|
||||||
|
if (m_input.empty())
|
||||||
|
return;
|
||||||
|
uint8_t* ptr = buffer;
|
||||||
|
while (!m_input.empty())
|
||||||
{
|
{
|
||||||
if (input_queue.empty())
|
*ptr = m_input.front();
|
||||||
return;
|
if (*ptr == '\r')
|
||||||
uint8_t* ptr = buffer;
|
*ptr = '\n';
|
||||||
while (!input_queue.empty())
|
if (*ptr == 127)
|
||||||
{
|
*ptr++ = '\b', *ptr++ = ' ', *ptr = '\b';
|
||||||
*ptr = input_queue.front();
|
m_input.pop();
|
||||||
if (*ptr == '\r')
|
ptr++;
|
||||||
*ptr = '\n';
|
}
|
||||||
if (*ptr == 127)
|
*ptr = '\0';
|
||||||
*ptr++ = '\b', *ptr++ = ' ', *ptr = '\b';
|
}
|
||||||
input_queue.pop();
|
|
||||||
ptr++;
|
|
||||||
}
|
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
ptr = buffer;
|
const uint8_t* ptr = buffer;
|
||||||
while (*ptr)
|
while (*ptr)
|
||||||
device->handle_input_byte(*ptr++);
|
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
|
||||||
|
|
|
@ -131,12 +131,17 @@ 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);
|
||||||
|
|
||||||
IDT::register_irq_handler(irq, [] { Scheduler::get().timer_reschedule(); });
|
set_irq(irq);
|
||||||
InterruptController::get().enable_irq(irq);
|
enable_interrupt();
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#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
|
||||||
|
@ -27,14 +29,6 @@
|
||||||
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();
|
||||||
|
@ -53,19 +47,24 @@ 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);
|
||||||
|
|
||||||
IDT::register_irq_handler(PIT_IRQ, irq_handler);
|
set_irq(PIT_IRQ);
|
||||||
|
enable_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
InterruptController::get().enable_irq(PIT_IRQ);
|
void PIT::handle_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 s_system_time * (MS_PER_S / TICKS_PER_SECOND);
|
return m_system_time * (MS_PER_S / TICKS_PER_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec PIT::time_since_boot() const
|
timespec PIT::time_since_boot() const
|
||||||
{
|
{
|
||||||
uint64_t ticks = s_system_time;
|
uint64_t ticks = m_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))
|
||||||
|
|
Loading…
Reference in New Issue