Compare commits
	
		
			3 Commits
		
	
	
		
			7ce8e2d57b
			...
			bcf62c5f2e
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | bcf62c5f2e | |
|  | 4d6322ff9c | |
|  | 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(); | ||||||
|  |  | ||||||
|  | @ -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,15 +1,15 @@ | ||||||
| #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() | ||||||
|  | @ -33,9 +33,6 @@ 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