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(CMAKE_CXX_STANDARD 20) | ||||
| set(CMAKE_CXX_STANDARD_REQUIRED True) | ||||
| set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++) | ||||
| set(CMAKE_CXX_COMPILER_WORKS True) | ||||
| if(EXISTS ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++) | ||||
| 	set(CMAKE_CXX_STANDARD 20) | ||||
| 	set(CMAKE_CXX_STANDARD_REQUIRED 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_WORKS True) | ||||
| 
 | ||||
| if(NOT EXISTS ${CMAKE_CXX_COMPILER}) | ||||
| 	set(CMAKE_CXX_COMPILER g++) | ||||
| 	set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-gcc) | ||||
| 	set(CMAKE_C_COMPILER_WORKS True) | ||||
| endif() | ||||
| 
 | ||||
| if(DEFINED QEMU_ACCEL) | ||||
|  |  | |||
|  | @ -194,10 +194,10 @@ add_custom_command( | |||
| 	COMMAND cp ${CRTEND} . | ||||
| ) | ||||
| 
 | ||||
| add_custom_command( | ||||
| 	TARGET kernel POST_BUILD | ||||
| 	COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel | ||||
| ) | ||||
| #add_custom_command( | ||||
| #	TARGET kernel POST_BUILD | ||||
| #	COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel | ||||
| #) | ||||
| 
 | ||||
| add_custom_command( | ||||
| 	OUTPUT font/prefs.psf.o | ||||
|  |  | |||
|  | @ -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