forked from Bananymous/banan-os
				
			
			update main #1
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -13,7 +13,7 @@
 | 
			
		|||
#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)
 | 
			
		||||
 | 
			
		||||
namespace IDT
 | 
			
		||||
namespace Kernel::IDT
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	struct Registers
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ namespace IDT
 | 
			
		|||
	static IDTR				s_idtr;
 | 
			
		||||
	static GateDescriptor*	s_idt = nullptr;
 | 
			
		||||
 | 
			
		||||
	static void(*s_irq_handlers[0x10])() { nullptr };
 | 
			
		||||
	static Interruptable* s_interruptables[0x10] {};
 | 
			
		||||
 | 
			
		||||
	enum ISR
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -160,53 +160,62 @@ namespace IDT
 | 
			
		|||
		"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
 | 
			
		||||
		bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
 | 
			
		||||
		if (from_userspace)
 | 
			
		||||
			Kernel::Thread::current().save_sse();
 | 
			
		||||
			Thread::current().save_sse();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		pid_t tid = Kernel::Scheduler::current_tid();
 | 
			
		||||
		pid_t pid = tid ? Kernel::Process::current().pid() : 0;
 | 
			
		||||
		pid_t tid = Scheduler::current_tid();
 | 
			
		||||
		pid_t pid = tid ? Process::current().pid() : 0;
 | 
			
		||||
 | 
			
		||||
		if (pid && isr == ISR::PageFault)
 | 
			
		||||
		if (tid)
 | 
			
		||||
		{
 | 
			
		||||
			PageFaultError page_fault_error;
 | 
			
		||||
			page_fault_error.raw = error;
 | 
			
		||||
			Thread::current().set_return_rsp(interrupt_stack.rsp);
 | 
			
		||||
			Thread::current().set_return_rip(interrupt_stack.rip);
 | 
			
		||||
 | 
			
		||||
			// Try demand paging on non present pages
 | 
			
		||||
			if (!page_fault_error.present)
 | 
			
		||||
			if (isr == ISR::PageFault)
 | 
			
		||||
			{
 | 
			
		||||
				asm volatile("sti");
 | 
			
		||||
				auto result = Kernel::Process::current().allocate_page_for_demand_paging(regs->cr2);
 | 
			
		||||
				asm volatile("cli");
 | 
			
		||||
 | 
			
		||||
				if (!result.is_error() && result.value())
 | 
			
		||||
					return;
 | 
			
		||||
 | 
			
		||||
				if (result.is_error())
 | 
			
		||||
				// Check if stack is OOB
 | 
			
		||||
				auto& stack = Thread::current().stack();
 | 
			
		||||
				if (interrupt_stack.rsp < stack.vaddr())
 | 
			
		||||
				{
 | 
			
		||||
					dwarnln("Demand paging: {}", result.error());
 | 
			
		||||
					Kernel::Thread::current().handle_signal(SIGTERM);
 | 
			
		||||
					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
 | 
			
		||||
				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 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;
 | 
			
		||||
			auto* machine_code = (const uint8_t*)interrupt_stack.rip;
 | 
			
		||||
			dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
 | 
			
		||||
				machine_code[0],
 | 
			
		||||
				machine_code[1],
 | 
			
		||||
| 
						 | 
				
			
			@ -233,16 +242,10 @@ namespace IDT
 | 
			
		|||
			regs->cr0, regs->cr2, regs->cr3, regs->cr4
 | 
			
		||||
		);
 | 
			
		||||
		if (isr == ISR::PageFault)
 | 
			
		||||
			Kernel::PageTable::current().debug_dump();
 | 
			
		||||
			PageTable::current().debug_dump();
 | 
			
		||||
		Debug::dump_stack_trace();
 | 
			
		||||
 | 
			
		||||
		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())
 | 
			
		||||
		if (tid && Thread::current().is_userspace())
 | 
			
		||||
		{
 | 
			
		||||
			// TODO: Confirm and fix the exception to signal mappings
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -270,36 +273,38 @@ namespace IDT
 | 
			
		|||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Kernel::Thread::current().handle_signal(signal);
 | 
			
		||||
			Thread::current().handle_signal(signal);
 | 
			
		||||
		}
 | 
			
		||||
		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 (from_userspace)
 | 
			
		||||
		{
 | 
			
		||||
			ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
 | 
			
		||||
			Kernel::Thread::current().load_sse();
 | 
			
		||||
			ASSERT(Thread::current().state() == Thread::State::Executing);
 | 
			
		||||
			Thread::current().load_sse();
 | 
			
		||||
		}
 | 
			
		||||
#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
 | 
			
		||||
		bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
 | 
			
		||||
		if (from_userspace)
 | 
			
		||||
			Kernel::Thread::current().save_sse();
 | 
			
		||||
			Thread::current().save_sse();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		if (Kernel::Scheduler::current_tid())
 | 
			
		||||
		if (Scheduler::current_tid())
 | 
			
		||||
		{
 | 
			
		||||
			Kernel::Thread::current().set_return_rsp(interrupt_stack.rsp);
 | 
			
		||||
			Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
 | 
			
		||||
			Thread::current().set_return_rsp(interrupt_stack.rsp);
 | 
			
		||||
			Thread::current().set_return_rip(interrupt_stack.rip);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!InterruptController::get().is_in_service(irq))
 | 
			
		||||
| 
						 | 
				
			
			@ -307,21 +312,21 @@ namespace IDT
 | 
			
		|||
		else
 | 
			
		||||
		{
 | 
			
		||||
			InterruptController::get().eoi(irq);
 | 
			
		||||
			if (s_irq_handlers[irq])
 | 
			
		||||
				s_irq_handlers[irq]();
 | 
			
		||||
			if (s_interruptables[irq])
 | 
			
		||||
				s_interruptables[irq]->handle_irq();
 | 
			
		||||
			else
 | 
			
		||||
				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 (from_userspace)
 | 
			
		||||
		{
 | 
			
		||||
			ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
 | 
			
		||||
			Kernel::Thread::current().load_sse();
 | 
			
		||||
			ASSERT(Thread::current().state() == Thread::State::Executing);
 | 
			
		||||
			Thread::current().load_sse();
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -349,9 +354,9 @@ namespace IDT
 | 
			
		|||
		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();
 | 
			
		||||
| 
						 | 
				
			
			@ -480,4 +485,4 @@ namespace IDT
 | 
			
		|||
		ASSERT_NOT_REACHED();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,51 +4,56 @@
 | 
			
		|||
#include <kernel/InterruptController.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:
 | 
			
		||||
	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
 | 
			
		||||
	class APIC final : public InterruptController
 | 
			
		||||
	{
 | 
			
		||||
		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,
 | 
			
		||||
			OnlineCapable = 2,
 | 
			
		||||
			enum Flags : uint8_t
 | 
			
		||||
			{
 | 
			
		||||
				Enabled = 1,
 | 
			
		||||
				OnlineCapable = 2,
 | 
			
		||||
			};
 | 
			
		||||
			uint8_t processor_id;
 | 
			
		||||
			uint8_t apic_id;
 | 
			
		||||
			uint8_t flags;
 | 
			
		||||
		};
 | 
			
		||||
		uint8_t processor_id;
 | 
			
		||||
		uint8_t apic_id;
 | 
			
		||||
		uint8_t flags;
 | 
			
		||||
 | 
			
		||||
		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] {};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
namespace IDT
 | 
			
		||||
namespace Kernel::IDT
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	void initialize();
 | 
			
		||||
	void register_irq_handler(uint8_t irq, void(*f)());
 | 
			
		||||
	[[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
 | 
			
		||||
 | 
			
		||||
#include <kernel/Device/Device.h>
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel::Input
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	class PS2Device : public CharacterDevice
 | 
			
		||||
	class PS2Device : public CharacterDevice, public Interruptable
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		virtual ~PS2Device() {}
 | 
			
		||||
		virtual void on_byte(uint8_t) = 0;
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		PS2Device()
 | 
			
		||||
			: CharacterDevice(Mode::IRUSR | Mode::IRGRP, 0, 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -33,11 +33,8 @@ namespace Kernel::Input
 | 
			
		|||
		BAN::ErrorOr<void> reset_device(uint8_t);
 | 
			
		||||
		BAN::ErrorOr<void> set_scanning(uint8_t, bool);
 | 
			
		||||
 | 
			
		||||
		static void device0_irq();
 | 
			
		||||
		static void device1_irq();
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		PS2Device* m_devices[2] { nullptr, nullptr };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ namespace Kernel::Input
 | 
			
		|||
	public:
 | 
			
		||||
		static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
 | 
			
		||||
 | 
			
		||||
		virtual void on_byte(uint8_t) override;
 | 
			
		||||
		virtual void handle_irq() override;
 | 
			
		||||
		virtual void update() override;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,22 +5,42 @@
 | 
			
		|||
#define DISABLE_INTERRUPTS() asm volatile("cli")
 | 
			
		||||
#define ENABLE_INTERRUPTS() asm volatile("sti")
 | 
			
		||||
 | 
			
		||||
class InterruptController
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	virtual ~InterruptController() {}
 | 
			
		||||
 | 
			
		||||
	virtual void eoi(uint8_t) = 0;
 | 
			
		||||
	virtual void enable_irq(uint8_t) = 0;
 | 
			
		||||
	virtual bool is_in_service(uint8_t) = 0;
 | 
			
		||||
	class Interruptable
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		Interruptable() = default;
 | 
			
		||||
 | 
			
		||||
	static void initialize(bool force_pic);
 | 
			
		||||
	static InterruptController& get();
 | 
			
		||||
		void set_irq(int irq);
 | 
			
		||||
		void enable_interrupt();
 | 
			
		||||
		void disable_interrupt();
 | 
			
		||||
 | 
			
		||||
	void enter_acpi_mode();
 | 
			
		||||
		virtual void handle_irq() = 0;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	bool m_using_apic { false };
 | 
			
		||||
};
 | 
			
		||||
	private:
 | 
			
		||||
		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>
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
	static void mask_all();
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static PIC* create();
 | 
			
		||||
	friend class InterruptController;
 | 
			
		||||
};
 | 
			
		||||
		static void remap();
 | 
			
		||||
		static void mask_all();
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		static PIC* create();
 | 
			
		||||
		friend class InterruptController;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <BAN/Errors.h>
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
#include <kernel/SpinLock.h>
 | 
			
		||||
#include <kernel/Storage/ATAController.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +10,7 @@ namespace Kernel
 | 
			
		|||
 | 
			
		||||
	class ATADevice;
 | 
			
		||||
 | 
			
		||||
	class ATABus
 | 
			
		||||
	class ATABus : public Interruptable
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		enum class DeviceType
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +28,7 @@ namespace Kernel
 | 
			
		|||
 | 
			
		||||
		ATAController& controller() { return m_controller; }
 | 
			
		||||
 | 
			
		||||
		void on_irq();
 | 
			
		||||
		virtual void handle_irq() override;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		ATABus(ATAController& controller, uint16_t base, uint16_t ctrl)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <BAN/CircularQueue.h>
 | 
			
		||||
#include <BAN/Errors.h>
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
#include <kernel/Terminal/TTY.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +36,7 @@ namespace Kernel
 | 
			
		|||
		uint32_t m_height { 0 };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class SerialTTY final : public TTY
 | 
			
		||||
	class SerialTTY final : public TTY, public Interruptable
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		static BAN::ErrorOr<BAN::RefPtr<SerialTTY>> create(Serial);
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +47,8 @@ namespace Kernel
 | 
			
		|||
 | 
			
		||||
		virtual void update() override;
 | 
			
		||||
 | 
			
		||||
		virtual void handle_irq() override;
 | 
			
		||||
 | 
			
		||||
	protected:
 | 
			
		||||
		virtual BAN::StringView name() const override { return m_name; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +59,7 @@ namespace Kernel
 | 
			
		|||
	private:
 | 
			
		||||
		BAN::String m_name;
 | 
			
		||||
		Serial m_serial;
 | 
			
		||||
		BAN::CircularQueue<uint8_t, 128> m_input;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		virtual dev_t rdev() const override { return m_rdev; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,12 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
#include <kernel/Timer/Timer.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	class HPET final : public Timer
 | 
			
		||||
	class HPET final : public Timer, public Interruptable
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		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 timespec time_since_boot() const override;
 | 
			
		||||
 | 
			
		||||
		virtual void handle_irq() override;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		HPET() = default;
 | 
			
		||||
		BAN::ErrorOr<void> initialize(bool force_pic);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,12 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
#include <kernel/Timer/Timer.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define PIT_IRQ 0
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	class PIT final : public Timer
 | 
			
		||||
	class PIT final : public Timer, public Interruptable
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		static BAN::ErrorOr<BAN::UniqPtr<PIT>> create();
 | 
			
		||||
| 
						 | 
				
			
			@ -16,8 +14,13 @@ namespace Kernel
 | 
			
		|||
		virtual uint64_t ms_since_boot() const override;
 | 
			
		||||
		virtual timespec time_since_boot() const override;
 | 
			
		||||
 | 
			
		||||
		virtual void handle_irq() override;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		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
 | 
			
		||||
 | 
			
		||||
struct MADT : public Kernel::ACPI::SDTHeader
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
	uint32_t local_apic;
 | 
			
		||||
	uint32_t flags;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct MADTEntry
 | 
			
		||||
{
 | 
			
		||||
	uint8_t type;
 | 
			
		||||
	uint8_t length;
 | 
			
		||||
	union
 | 
			
		||||
	struct MADT : public Kernel::ACPI::SDTHeader
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t local_apic;
 | 
			
		||||
		uint32_t flags;
 | 
			
		||||
	} __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
 | 
			
		||||
		{
 | 
			
		||||
			uint8_t acpi_processor_id;
 | 
			
		||||
			uint8_t apic_id;
 | 
			
		||||
			uint32_t flags;
 | 
			
		||||
		} __attribute__((packed)) entry0;
 | 
			
		||||
			uint64_t vector				: 8;
 | 
			
		||||
			uint64_t delivery_mode		: 3;
 | 
			
		||||
			uint64_t destination_mode	: 1;
 | 
			
		||||
			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
 | 
			
		||||
		{
 | 
			
		||||
			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;
 | 
			
		||||
			uint32_t lo_dword;
 | 
			
		||||
			uint32_t hi_dword;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
union RedirectionEntry
 | 
			
		||||
{
 | 
			
		||||
	struct
 | 
			
		||||
	APIC* APIC::create()
 | 
			
		||||
	{
 | 
			
		||||
		uint64_t vector				: 8;
 | 
			
		||||
		uint64_t delivery_mode		: 3;
 | 
			
		||||
		uint64_t destination_mode	: 1;
 | 
			
		||||
		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)
 | 
			
		||||
		uint32_t ecx, edx;
 | 
			
		||||
		CPUID::get_features(ecx, edx);
 | 
			
		||||
		if (!(edx & CPUID::Features::EDX_APIC))
 | 
			
		||||
		{
 | 
			
		||||
			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;
 | 
			
		||||
			dprintln("Local APIC is not available");
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
		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)
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
		const MADT* madt = (const MADT*)Kernel::ACPI::get().get_header("APIC");
 | 
			
		||||
		if (madt == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			ioapic = &io;
 | 
			
		||||
			break;
 | 
			
		||||
			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:
 | 
			
		||||
					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;
 | 
			
		||||
	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
 | 
			
		||||
	uint32_t APIC::read_from_local_apic(ptrdiff_t offset)
 | 
			
		||||
	{
 | 
			
		||||
		return MMIO::read32(m_local_apic_vaddr + offset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	redir.vector = IRQ_VECTOR_BASE + irq;
 | 
			
		||||
	redir.mask = 0;
 | 
			
		||||
	redir.destination = m_processors.front().apic_id;
 | 
			
		||||
	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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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()
 | 
			
		||||
	{
 | 
			
		||||
		if (interrupts_enabled())
 | 
			
		||||
		if (Kernel::interrupts_enabled())
 | 
			
		||||
			s_debug_lock.lock();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void DebugLock::unlock()
 | 
			
		||||
	{
 | 
			
		||||
		if (interrupts_enabled())
 | 
			
		||||
		if (Kernel::interrupts_enabled())
 | 
			
		||||
			s_debug_lock.unlock();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
#include <kernel/ACPI.h>
 | 
			
		||||
#include <kernel/FS/DevFS/FileSystem.h>
 | 
			
		||||
#include <kernel/IDT.h>
 | 
			
		||||
#include <kernel/Input/PS2Config.h>
 | 
			
		||||
#include <kernel/Input/PS2Controller.h>
 | 
			
		||||
#include <kernel/Input/PS2Keyboard.h>
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -11,77 +12,6 @@
 | 
			
		|||
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 void controller_send_command(PS2::Command command)
 | 
			
		||||
| 
						 | 
				
			
			@ -136,20 +66,6 @@ namespace Kernel::Input
 | 
			
		|||
		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;
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> PS2Controller::initialize()
 | 
			
		||||
| 
						 | 
				
			
			@ -256,15 +172,15 @@ namespace Kernel::Input
 | 
			
		|||
 | 
			
		||||
		if (m_devices[0])
 | 
			
		||||
		{
 | 
			
		||||
			IDT::register_irq_handler(PS2::IRQ::DEVICE0, device0_irq);
 | 
			
		||||
			InterruptController::get().enable_irq(PS2::IRQ::DEVICE0);
 | 
			
		||||
			m_devices[0]->set_irq(PS2::IRQ::DEVICE0);
 | 
			
		||||
			m_devices[0]->enable_interrupt();
 | 
			
		||||
			config |= PS2::Config::INTERRUPT_FIRST_PORT;
 | 
			
		||||
			DevFileSystem::get().add_device("input0", m_devices[0]);
 | 
			
		||||
		}
 | 
			
		||||
		if (m_devices[1])
 | 
			
		||||
		{
 | 
			
		||||
			IDT::register_irq_handler(PS2::IRQ::DEVICE1, device1_irq);
 | 
			
		||||
			InterruptController::get().enable_irq(PS2::IRQ::DEVICE1);
 | 
			
		||||
			m_devices[1]->set_irq(PS2::IRQ::DEVICE1);
 | 
			
		||||
			m_devices[1]->enable_interrupt();
 | 
			
		||||
			config |= PS2::Config::INTERRUPT_SECOND_PORT;
 | 
			
		||||
			DevFileSystem::get().add_device("input1", m_devices[1]);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
#include <BAN/ScopeGuard.h>
 | 
			
		||||
#include <kernel/CriticalScope.h>
 | 
			
		||||
#include <kernel/FS/DevFS/FileSystem.h>
 | 
			
		||||
#include <kernel/Input/PS2Config.h>
 | 
			
		||||
#include <kernel/Input/PS2Keyboard.h>
 | 
			
		||||
#include <kernel/IO.h>
 | 
			
		||||
#include <kernel/Timer/Timer.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/sysmacros.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -12,35 +14,6 @@
 | 
			
		|||
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)
 | 
			
		||||
	{
 | 
			
		||||
		PS2Keyboard* keyboard = new PS2Keyboard(controller);
 | 
			
		||||
| 
						 | 
				
			
			@ -57,8 +30,10 @@ namespace Kernel::Input
 | 
			
		|||
		, 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
 | 
			
		||||
		//       that respond with more bytes than ACK
 | 
			
		||||
		switch (m_state)
 | 
			
		||||
| 
						 | 
				
			
			@ -71,11 +46,11 @@ namespace Kernel::Input
 | 
			
		|||
						m_command_queue.pop();
 | 
			
		||||
						m_state = State::Normal;
 | 
			
		||||
						break;
 | 
			
		||||
					case PS2::Response::RESEND:
 | 
			
		||||
					case PS2::KBResponse::RESEND:
 | 
			
		||||
						m_state = State::Normal;
 | 
			
		||||
						break;
 | 
			
		||||
					case PS2::Response::KEY_ERROR_OR_BUFFER_OVERRUN1:
 | 
			
		||||
					case PS2::Response::KEY_ERROR_OR_BUFFER_OVERRUN2:
 | 
			
		||||
					case PS2::KBResponse::KEY_ERROR_OR_BUFFER_OVERRUN1:
 | 
			
		||||
					case PS2::KBResponse::KEY_ERROR_OR_BUFFER_OVERRUN2:
 | 
			
		||||
						dwarnln("Key detection error or internal buffer overrun");
 | 
			
		||||
						break;
 | 
			
		||||
					default:
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +72,7 @@ namespace Kernel::Input
 | 
			
		|||
	BAN::ErrorOr<void> PS2Keyboard::initialize()
 | 
			
		||||
	{
 | 
			
		||||
		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);
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -234,11 +209,11 @@ namespace Kernel::Input
 | 
			
		|||
	{
 | 
			
		||||
		uint8_t new_leds = 0;
 | 
			
		||||
		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)
 | 
			
		||||
			new_leds |= PS2::Leds::NUM_LOCK;
 | 
			
		||||
			new_leds |= PS2::KBLeds::NUM_LOCK;
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,47 +5,73 @@
 | 
			
		|||
 | 
			
		||||
#include <lai/helpers/sci.h>
 | 
			
		||||
 | 
			
		||||
static InterruptController* s_instance = nullptr;
 | 
			
		||||
 | 
			
		||||
InterruptController& InterruptController::get()
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
	ASSERT(s_instance);
 | 
			
		||||
	return *s_instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InterruptController::initialize(bool force_pic)
 | 
			
		||||
{
 | 
			
		||||
	ASSERT(s_instance == nullptr);
 | 
			
		||||
	namespace IDT { void register_irq_handler(uint8_t irq, Interruptable*); }
 | 
			
		||||
 | 
			
		||||
	PIC::mask_all();
 | 
			
		||||
	PIC::remap();
 | 
			
		||||
	static InterruptController* s_instance = nullptr;
 | 
			
		||||
 | 
			
		||||
	if (!force_pic)
 | 
			
		||||
	void Interruptable::set_irq(int irq)
 | 
			
		||||
	{
 | 
			
		||||
		s_instance = APIC::create();
 | 
			
		||||
		if (s_instance)
 | 
			
		||||
		{
 | 
			
		||||
			s_instance->m_using_apic = true;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (m_irq != -1)
 | 
			
		||||
			IDT::register_irq_handler(m_irq, nullptr);
 | 
			
		||||
		m_irq = irq;
 | 
			
		||||
		IDT::register_irq_handler(irq, this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dprintln("Using PIC instead of APIC");
 | 
			
		||||
	s_instance = PIC::create();
 | 
			
		||||
	ASSERT(s_instance);
 | 
			
		||||
	void Interruptable::enable_interrupt()
 | 
			
		||||
	{
 | 
			
		||||
		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()
 | 
			
		||||
{
 | 
			
		||||
	if (lai_enable_acpi(m_using_apic ? 1 : 0) != 0)
 | 
			
		||||
		dwarnln("could not enter acpi mode");
 | 
			
		||||
}
 | 
			
		||||
	InterruptController& InterruptController::get()
 | 
			
		||||
	{
 | 
			
		||||
		ASSERT(s_instance);
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
		// 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)
 | 
			
		||||
			m_free_list->next = nullptr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,80 +17,85 @@
 | 
			
		|||
 | 
			
		||||
#define ICW4_8086	0x01
 | 
			
		||||
 | 
			
		||||
PIC* PIC::create()
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
	mask_all();
 | 
			
		||||
	remap();
 | 
			
		||||
	return new PIC;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PIC::remap()
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
	PIC* PIC::create()
 | 
			
		||||
	{
 | 
			
		||||
		port = PIC2_DATA;
 | 
			
		||||
		irq -= 8;
 | 
			
		||||
		mask_all();
 | 
			
		||||
		remap();
 | 
			
		||||
		return new PIC;
 | 
			
		||||
	}
 | 
			
		||||
	IO::outb(port, IO::inb(port) & ~(1 << irq));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PIC::is_in_service(uint8_t irq)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t port = PIC1_CMD;
 | 
			
		||||
	if (irq >= 8)
 | 
			
		||||
	void PIC::remap()
 | 
			
		||||
	{
 | 
			
		||||
		port = PIC2_CMD;
 | 
			
		||||
		irq -= 8;
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	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* bus = new ATABus(controller, base, ctrl);
 | 
			
		||||
| 
						 | 
				
			
			@ -55,11 +21,11 @@ namespace Kernel
 | 
			
		|||
 | 
			
		||||
	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];
 | 
			
		||||
		ASSERT(identify_buffer);
 | 
			
		||||
		BAN::ScopeGuard _([identify_buffer] { delete[] identify_buffer; });
 | 
			
		||||
		BAN::Vector<uint16_t> identify_buffer;
 | 
			
		||||
		MUST(identify_buffer.resize(256));
 | 
			
		||||
 | 
			
		||||
		for (uint8_t i = 0; i < 2; i++)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -72,11 +38,11 @@ namespace Kernel
 | 
			
		|||
 | 
			
		||||
			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)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			auto res = device.initialize(type, identify_buffer);
 | 
			
		||||
			auto res = device.initialize(type, identify_buffer.data());
 | 
			
		||||
			if (res.is_error())
 | 
			
		||||
			{
 | 
			
		||||
				dprintln("{}", res.error());
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +115,7 @@ namespace Kernel
 | 
			
		|||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void ATABus::on_irq()
 | 
			
		||||
	void ATABus::handle_irq()
 | 
			
		||||
	{
 | 
			
		||||
		ASSERT(!m_has_got_irq);
 | 
			
		||||
		if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
#include <BAN/Array.h>
 | 
			
		||||
#include <BAN/CircularQueue.h>
 | 
			
		||||
#include <kernel/CriticalScope.h>
 | 
			
		||||
#include <kernel/FS/DevFS/FileSystem.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 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_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 major = DevFileSystem::get().get_next_dev();
 | 
			
		||||
| 
						 | 
				
			
			@ -220,14 +191,14 @@ namespace Kernel
 | 
			
		|||
		if (serial.port() == COM1_PORT)
 | 
			
		||||
		{
 | 
			
		||||
			IO::outb(COM1_PORT + 1, 1);
 | 
			
		||||
			InterruptController::get().enable_irq(COM1_IRQ);
 | 
			
		||||
			IDT::register_irq_handler(COM1_IRQ, irq4_handler);
 | 
			
		||||
			tty->set_irq(COM1_IRQ);
 | 
			
		||||
			tty->enable_interrupt();
 | 
			
		||||
		}
 | 
			
		||||
		else if (serial.port() == COM2_PORT)
 | 
			
		||||
		{
 | 
			
		||||
			IO::outb(COM2_PORT + 1, 1);
 | 
			
		||||
			InterruptController::get().enable_irq(COM2_IRQ);
 | 
			
		||||
			IDT::register_irq_handler(COM2_IRQ, irq3_handler);
 | 
			
		||||
			tty->set_irq(COM2_IRQ);
 | 
			
		||||
			tty->enable_interrupt();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto ref_ptr = BAN::RefPtr<SerialTTY>::adopt(tty);
 | 
			
		||||
| 
						 | 
				
			
			@ -239,41 +210,45 @@ namespace Kernel
 | 
			
		|||
		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()
 | 
			
		||||
	{
 | 
			
		||||
		if (m_serial.port() != COM1_PORT && m_serial.port() != COM2_PORT)
 | 
			
		||||
			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())
 | 
			
		||||
					return;
 | 
			
		||||
				uint8_t* ptr = buffer;
 | 
			
		||||
				while (!input_queue.empty())
 | 
			
		||||
				{
 | 
			
		||||
					*ptr = input_queue.front();
 | 
			
		||||
					if (*ptr == '\r')
 | 
			
		||||
						*ptr = '\n';
 | 
			
		||||
					if (*ptr == 127)
 | 
			
		||||
						*ptr++ = '\b', *ptr++ = ' ', *ptr = '\b';
 | 
			
		||||
					input_queue.pop();
 | 
			
		||||
					ptr++;
 | 
			
		||||
				}
 | 
			
		||||
				*ptr = '\0';
 | 
			
		||||
				*ptr = m_input.front();
 | 
			
		||||
				if (*ptr == '\r')
 | 
			
		||||
					*ptr = '\n';
 | 
			
		||||
				if (*ptr == 127)
 | 
			
		||||
					*ptr++ = '\b', *ptr++ = ' ', *ptr = '\b';
 | 
			
		||||
				m_input.pop();
 | 
			
		||||
				ptr++;
 | 
			
		||||
			}
 | 
			
		||||
			*ptr = '\0';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
				ptr = buffer;
 | 
			
		||||
				while (*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);
 | 
			
		||||
		const uint8_t* ptr = buffer;
 | 
			
		||||
		while (*ptr)
 | 
			
		||||
			handle_input_byte(*ptr++);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint32_t SerialTTY::width() const
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,12 +131,17 @@ namespace Kernel
 | 
			
		|||
		for (int i = 1; i <= header->comparator_count; i++)
 | 
			
		||||
			write_register(HPET_REG_TIMER_CONFIG(i), 0);
 | 
			
		||||
 | 
			
		||||
		IDT::register_irq_handler(irq, [] { Scheduler::get().timer_reschedule(); });
 | 
			
		||||
		InterruptController::get().enable_irq(irq);
 | 
			
		||||
		set_irq(irq);
 | 
			
		||||
		enable_interrupt();
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void HPET::handle_irq()
 | 
			
		||||
	{
 | 
			
		||||
		Scheduler::get().timer_reschedule();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint64_t HPET::ms_since_boot() const
 | 
			
		||||
	{
 | 
			
		||||
		// FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@
 | 
			
		|||
#include <kernel/Scheduler.h>
 | 
			
		||||
#include <kernel/Timer/PIT.h>
 | 
			
		||||
 | 
			
		||||
#define PIT_IRQ 0
 | 
			
		||||
 | 
			
		||||
#define TIMER0_CTL			0x40
 | 
			
		||||
#define TIMER1_CTL			0x41
 | 
			
		||||
#define TIMER2_CTL			0x42
 | 
			
		||||
| 
						 | 
				
			
			@ -27,14 +29,6 @@
 | 
			
		|||
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()
 | 
			
		||||
	{
 | 
			
		||||
		PIT* pit = new PIT();
 | 
			
		||||
| 
						 | 
				
			
			@ -53,19 +47,24 @@ namespace Kernel
 | 
			
		|||
		IO::outb(TIMER0_CTL, (timer_reload >> 0) & 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
 | 
			
		||||
	{
 | 
			
		||||
		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
 | 
			
		||||
	{
 | 
			
		||||
		uint64_t ticks = s_system_time;
 | 
			
		||||
		uint64_t ticks = m_system_time;
 | 
			
		||||
		return timespec {
 | 
			
		||||
			.tv_sec = ticks / TICKS_PER_SECOND,
 | 
			
		||||
			.tv_nsec = (long)((ticks % TICKS_PER_SECOND) * (NS_PER_S / TICKS_PER_SECOND))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue