Kernel: PCI devices can now create region for BAR
This creates either MEM or IO region for read/write access to PCI device.
This commit is contained in:
		
							parent
							
								
									14a608effd
								
							
						
					
					
						commit
						7774f56ab6
					
				|  | @ -13,7 +13,7 @@ namespace Kernel | ||||||
| 	class E1000 final : public NetworkDriver | 	class E1000 final : public NetworkDriver | ||||||
| 	{ | 	{ | ||||||
| 	public: | 	public: | ||||||
| 		static BAN::ErrorOr<BAN::UniqPtr<E1000>> create(const PCIDevice&); | 		static BAN::ErrorOr<BAN::UniqPtr<E1000>> create(PCI::Device&); | ||||||
| 		~E1000(); | 		~E1000(); | ||||||
| 
 | 
 | ||||||
| 		virtual uint8_t* get_mac_address() override { return m_mac_address; } | 		virtual uint8_t* get_mac_address() override { return m_mac_address; } | ||||||
|  | @ -24,12 +24,12 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		E1000() = default; | 		E1000() = default; | ||||||
| 		BAN::ErrorOr<void> initialize(const PCIDevice&); | 		BAN::ErrorOr<void> initialize(PCI::Device&); | ||||||
| 
 | 
 | ||||||
| 		static void interrupt_handler(); | 		static void interrupt_handler(); | ||||||
| 
 | 
 | ||||||
| 		void write32(uint16_t reg, uint32_t value); |  | ||||||
| 		uint32_t read32(uint16_t reg); | 		uint32_t read32(uint16_t reg); | ||||||
|  | 		void write32(uint16_t reg, uint32_t value); | ||||||
| 
 | 
 | ||||||
| 		void detect_eeprom(); | 		void detect_eeprom(); | ||||||
| 		uint32_t eeprom_read(uint8_t addr); | 		uint32_t eeprom_read(uint8_t addr); | ||||||
|  | @ -44,8 +44,7 @@ namespace Kernel | ||||||
| 		void handle_receive(); | 		void handle_receive(); | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		PCIDevice::BarType		m_bar_type {}; | 		BAN::UniqPtr<PCI::BarRegion> m_bar_region; | ||||||
| 		uint64_t				m_bar_addr {}; |  | ||||||
| 		bool					m_has_eerprom { false }; | 		bool					m_has_eerprom { false }; | ||||||
| 		uint8_t					m_mac_address[6] {}; | 		uint8_t					m_mac_address[6] {}; | ||||||
| 		uint16_t				m_rx_current {}; | 		uint16_t				m_rx_current {}; | ||||||
|  |  | ||||||
|  | @ -1,28 +1,63 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <BAN/UniqPtr.h> | ||||||
| #include <BAN/Vector.h> | #include <BAN/Vector.h> | ||||||
|  | #include <kernel/Memory/Types.h> | ||||||
| 
 | 
 | ||||||
| namespace Kernel | namespace Kernel::PCI | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| 	class PCIDevice | 	enum class BarType | ||||||
| 	{ | 	{ | ||||||
| 	public: | 		INVALID, | ||||||
| 		enum class BarType | 		MEM, | ||||||
| 		{ | 		IO, | ||||||
| 			INVAL, | 	}; | ||||||
| 			MEM, | 
 | ||||||
| 			IO, | 	class Device; | ||||||
| 		}; | 
 | ||||||
|  | 	class BarRegion | ||||||
|  | 	{ | ||||||
|  | 		BAN_NON_COPYABLE(BarRegion); | ||||||
| 
 | 
 | ||||||
| 	public: | 	public: | ||||||
| 		PCIDevice(uint8_t, uint8_t, uint8_t); | 		static BAN::ErrorOr<BAN::UniqPtr<BarRegion>> create(PCI::Device&, uint8_t bar_num); | ||||||
|  | 		~BarRegion(); | ||||||
|  | 
 | ||||||
|  | 		BarType type() const { return m_type; } | ||||||
|  | 		vaddr_t vaddr() const { return m_vaddr; } | ||||||
|  | 		paddr_t paddr() const { return m_paddr; } | ||||||
|  | 		size_t size() const { return m_size; } | ||||||
|  | 
 | ||||||
|  | 		void write8(off_t, uint8_t); | ||||||
|  | 		void write16(off_t, uint16_t); | ||||||
|  | 		void write32(off_t, uint32_t); | ||||||
|  | 
 | ||||||
|  | 		uint8_t read8(off_t); | ||||||
|  | 		uint16_t read16(off_t); | ||||||
|  | 		uint32_t read32(off_t); | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		BarRegion(BarType, paddr_t, size_t); | ||||||
|  | 		BAN::ErrorOr<void> initialize(); | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		const BarType	m_type	{}; | ||||||
|  | 		const paddr_t	m_paddr	{}; | ||||||
|  | 		const size_t	m_size	{}; | ||||||
|  | 		vaddr_t			m_vaddr	{}; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class Device | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		Device(uint8_t, uint8_t, uint8_t); | ||||||
| 
 | 
 | ||||||
| 		uint32_t read_dword(uint8_t) const; | 		uint32_t read_dword(uint8_t) const; | ||||||
| 		uint16_t read_word(uint8_t) const; | 		uint16_t read_word(uint8_t) const; | ||||||
| 		uint8_t  read_byte(uint8_t) const; | 		uint8_t  read_byte(uint8_t) const; | ||||||
| 
 | 
 | ||||||
| 		void write_dword(uint8_t, uint32_t) const; | 		void write_dword(uint8_t, uint32_t); | ||||||
| 
 | 
 | ||||||
| 		uint8_t bus() const { return m_bus; } | 		uint8_t bus() const { return m_bus; } | ||||||
| 		uint8_t dev() const { return m_dev; } | 		uint8_t dev() const { return m_dev; } | ||||||
|  | @ -32,17 +67,24 @@ namespace Kernel | ||||||
| 		uint8_t subclass() const { return m_subclass; } | 		uint8_t subclass() const { return m_subclass; } | ||||||
| 		uint8_t prog_if() const { return m_prog_if; } | 		uint8_t prog_if() const { return m_prog_if; } | ||||||
| 
 | 
 | ||||||
| 		BarType read_bar_type(uint8_t) const; | 		uint8_t header_type() const { return m_header_type; } | ||||||
| 		uint64_t read_bar_address(uint8_t) const; |  | ||||||
| 
 | 
 | ||||||
| 		void enable_bus_mastering() const; | 		BAN::ErrorOr<BAN::UniqPtr<BarRegion>> allocate_bar_region(uint8_t bar_num); | ||||||
| 		void disable_bus_mastering() const; |  | ||||||
| 
 | 
 | ||||||
| 		void enable_memory_space() const; | 		void enable_bus_mastering(); | ||||||
| 		void disable_memory_space() const; | 		void disable_bus_mastering(); | ||||||
| 
 | 
 | ||||||
| 		void enable_pin_interrupts() const; | 		void enable_memory_space(); | ||||||
| 		void disable_pin_interrupts() const; | 		void disable_memory_space(); | ||||||
|  | 
 | ||||||
|  | 		void enable_io_space(); | ||||||
|  | 		void disable_io_space(); | ||||||
|  | 
 | ||||||
|  | 		void enable_pin_interrupts(); | ||||||
|  | 		void disable_pin_interrupts(); | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		void enumerate_capabilites(); | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		uint8_t m_bus; | 		uint8_t m_bus; | ||||||
|  | @ -56,19 +98,19 @@ namespace Kernel | ||||||
| 		uint8_t m_header_type; | 		uint8_t m_header_type; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	class PCI | 	class PCIManager | ||||||
| 	{ | 	{ | ||||||
| 		BAN_NON_COPYABLE(PCI); | 		BAN_NON_COPYABLE(PCIManager); | ||||||
| 		BAN_NON_MOVABLE(PCI); | 		BAN_NON_MOVABLE(PCIManager); | ||||||
| 
 | 
 | ||||||
| 	public: | 	public: | ||||||
| 		static void initialize(); | 		static void initialize(); | ||||||
| 		static PCI& get(); | 		static PCIManager& get(); | ||||||
| 		 | 		 | ||||||
| 		const BAN::Vector<PCIDevice>& devices() const { return m_devices; } | 		const BAN::Vector<PCI::Device>& devices() const { return m_devices; } | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		PCI() = default; | 		PCIManager() = default; | ||||||
| 		void check_function(uint8_t bus, uint8_t dev, uint8_t func); | 		void check_function(uint8_t bus, uint8_t dev, uint8_t func); | ||||||
| 		void check_device(uint8_t bus, uint8_t dev); | 		void check_device(uint8_t bus, uint8_t dev); | ||||||
| 		void check_bus(uint8_t bus); | 		void check_bus(uint8_t bus); | ||||||
|  | @ -76,7 +118,7 @@ namespace Kernel | ||||||
| 		void initialize_devices(); | 		void initialize_devices(); | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		BAN::Vector<PCIDevice> m_devices; | 		BAN::Vector<PCI::Device> m_devices; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -11,13 +11,13 @@ namespace Kernel | ||||||
| 	class ATAController final : public StorageController | 	class ATAController final : public StorageController | ||||||
| 	{ | 	{ | ||||||
| 	public: | 	public: | ||||||
| 		static BAN::ErrorOr<BAN::RefPtr<ATAController>> create(const PCIDevice&); | 		static BAN::ErrorOr<BAN::RefPtr<ATAController>> create(const PCI::Device&); | ||||||
| 
 | 
 | ||||||
| 		virtual BAN::Vector<BAN::RefPtr<StorageDevice>> devices() override; | 		virtual BAN::Vector<BAN::RefPtr<StorageDevice>> devices() override; | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		ATAController(); | 		ATAController(); | ||||||
| 		BAN::ErrorOr<void> initialize(const PCIDevice& device); | 		BAN::ErrorOr<void> initialize(const PCI::Device& device); | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		ATABus* m_buses[2] { nullptr, nullptr }; | 		ATABus* m_buses[2] { nullptr, nullptr }; | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| #include <kernel/MMIO.h> | #include <kernel/MMIO.h> | ||||||
| #include <kernel/Networking/E1000.h> | #include <kernel/Networking/E1000.h> | ||||||
| 
 | 
 | ||||||
| #define E1000_GENERAL_MEM_SIZE		(128 * 1024) | #define DEBUG_E1000 1 | ||||||
| 
 | 
 | ||||||
| #define E1000_REG_CTRL				0x0000 | #define E1000_REG_CTRL				0x0000 | ||||||
| #define E1000_REG_STATUS			0x0008 | #define E1000_REG_STATUS			0x0008 | ||||||
|  | @ -112,7 +112,7 @@ namespace Kernel | ||||||
| 		volatile uint16_t special; | 		volatile uint16_t special; | ||||||
| 	} __attribute__((packed)); | 	} __attribute__((packed)); | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<BAN::UniqPtr<E1000>> E1000::create(const PCIDevice& pci_device) | 	BAN::ErrorOr<BAN::UniqPtr<E1000>> E1000::create(PCI::Device& pci_device) | ||||||
| 	{ | 	{ | ||||||
| 		E1000* e1000 = new E1000(); | 		E1000* e1000 = new E1000(); | ||||||
| 		ASSERT(e1000); | 		ASSERT(e1000); | ||||||
|  | @ -126,42 +126,26 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 	E1000::~E1000() | 	E1000::~E1000() | ||||||
| 	{ | 	{ | ||||||
| 		if (m_bar_type == PCIDevice::BarType::MEM && m_bar_addr) |  | ||||||
| 			PageTable::kernel().unmap_range(m_bar_addr & PAGE_ADDR_MASK, E1000_GENERAL_MEM_SIZE); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<void> E1000::initialize(const PCIDevice& pci_device) | 	BAN::ErrorOr<void> E1000::initialize(PCI::Device& pci_device) | ||||||
| 	{ | 	{ | ||||||
| 		m_bar_type = pci_device.read_bar_type(0); | 		m_bar_region = TRY(pci_device.allocate_bar_region(0)); | ||||||
| 		if (m_bar_type == PCIDevice::BarType::INVAL) |  | ||||||
| 		{ |  | ||||||
| 			dwarnln("invalid bar0 type"); |  | ||||||
| 			return BAN::Error::from_errno(EINVAL); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (m_bar_type == PCIDevice::BarType::MEM) |  | ||||||
| 		{ |  | ||||||
| 			uint64_t bar_addr = pci_device.read_bar_address(0); |  | ||||||
| 
 |  | ||||||
| 			vaddr_t page_vaddr = PageTable::kernel().reserve_free_contiguous_pages(E1000_GENERAL_MEM_SIZE / PAGE_SIZE, KERNEL_OFFSET); |  | ||||||
| 			paddr_t page_paddr = bar_addr & PAGE_ADDR_MASK; |  | ||||||
| 			PageTable::kernel().map_range_at(page_paddr, page_vaddr, E1000_GENERAL_MEM_SIZE, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present); |  | ||||||
| 
 |  | ||||||
| 			m_bar_addr = page_vaddr + (bar_addr % PAGE_SIZE); |  | ||||||
| 		} |  | ||||||
| 		else if (m_bar_type == PCIDevice::BarType::IO) |  | ||||||
| 		{ |  | ||||||
| 			m_bar_addr = pci_device.read_bar_address(0); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		pci_device.enable_bus_mastering(); | 		pci_device.enable_bus_mastering(); | ||||||
| 
 | 
 | ||||||
| 		detect_eeprom(); | 		detect_eeprom(); | ||||||
| 
 | 
 | ||||||
| 		TRY(read_mac_address()); | 		TRY(read_mac_address()); | ||||||
| 		 | 		 | ||||||
| 		dprintln("E1000 at PCI {}:{}.{}", pci_device.bus(), pci_device.dev(), pci_device.func()); |  | ||||||
| 
 | 
 | ||||||
|  | 		initialize_rx(); | ||||||
|  | 		initialize_tx(); | ||||||
|  | 
 | ||||||
|  | 		enable_link(); | ||||||
|  | 		enable_interrupts(); | ||||||
|  | 		 | ||||||
|  | #if DEBUG_E1000 | ||||||
|  | 		dprintln("E1000 at PCI {}:{}.{}", pci_device.bus(), pci_device.dev(), pci_device.func()); | ||||||
| 		dprintln("  MAC: {2H}:{2H}:{2H}:{2H}:{2H}:{2H}", | 		dprintln("  MAC: {2H}:{2H}:{2H}:{2H}:{2H}:{2H}", | ||||||
| 			m_mac_address[0], | 			m_mac_address[0], | ||||||
| 			m_mac_address[1], | 			m_mac_address[1], | ||||||
|  | @ -170,52 +154,22 @@ namespace Kernel | ||||||
| 			m_mac_address[4], | 			m_mac_address[4], | ||||||
| 			m_mac_address[5] | 			m_mac_address[5] | ||||||
| 		); | 		); | ||||||
| 
 |  | ||||||
| 		initialize_rx(); |  | ||||||
| 		initialize_tx(); |  | ||||||
| 
 |  | ||||||
| 		enable_link(); |  | ||||||
| 		enable_interrupts(); |  | ||||||
| 		 |  | ||||||
| 		dprintln("  link up: {}", link_up()); | 		dprintln("  link up: {}", link_up()); | ||||||
| 		if (link_up()) | 		if (link_up()) | ||||||
| 			dprintln("  link speed: {} Mbps", link_speed()); | 			dprintln("  link speed: {} Mbps", link_speed()); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void E1000::write32(uint16_t reg, uint32_t value) | 	void E1000::write32(uint16_t reg, uint32_t value) | ||||||
| 	{ | 	{ | ||||||
| 		switch (m_bar_type) | 		m_bar_region->write32(reg, value); | ||||||
| 		{ |  | ||||||
| 			case PCIDevice::BarType::MEM: |  | ||||||
| 				MMIO::write32(m_bar_addr + reg, value); |  | ||||||
| 				break; |  | ||||||
| 			case PCIDevice::BarType::IO: |  | ||||||
| 				IO::outl(m_bar_addr, reg); |  | ||||||
| 				IO::outl(m_bar_addr + 4, value); |  | ||||||
| 				break; |  | ||||||
| 			default: |  | ||||||
| 				ASSERT_NOT_REACHED(); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uint32_t E1000::read32(uint16_t reg) | 	uint32_t E1000::read32(uint16_t reg) | ||||||
| 	{ | 	{ | ||||||
| 		uint32_t result = 0; | 		return m_bar_region->read32(reg); | ||||||
| 		switch (m_bar_type) |  | ||||||
| 		{ |  | ||||||
| 			case PCIDevice::BarType::MEM: |  | ||||||
| 				result = MMIO::read32(m_bar_addr + reg); |  | ||||||
| 				break; |  | ||||||
| 			case PCIDevice::BarType::IO: |  | ||||||
| 				IO::outl(m_bar_addr, reg); |  | ||||||
| 				result = IO::inl(m_bar_addr + 4); |  | ||||||
| 				break; |  | ||||||
| 			default: |  | ||||||
| 				ASSERT_NOT_REACHED(); |  | ||||||
| 		} |  | ||||||
| 		return result; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void E1000::detect_eeprom() | 	void E1000::detect_eeprom() | ||||||
|  |  | ||||||
|  | @ -1,33 +1,22 @@ | ||||||
| #include <kernel/IO.h> | #include <kernel/IO.h> | ||||||
|  | #include <kernel/Memory/PageTable.h> | ||||||
|  | #include <kernel/MMIO.h> | ||||||
| #include <kernel/Networking/E1000.h> | #include <kernel/Networking/E1000.h> | ||||||
| #include <kernel/PCI.h> | #include <kernel/PCI.h> | ||||||
| #include <kernel/Storage/ATAController.h> | #include <kernel/Storage/ATAController.h> | ||||||
| 
 | 
 | ||||||
| #define INVALID 0xFFFF | #define INVALID_VENDOR 0xFFFF | ||||||
| #define MULTI_FUNCTION 0x80 | #define MULTI_FUNCTION 0x80 | ||||||
| 
 | 
 | ||||||
| #define CONFIG_ADDRESS 0xCF8 | #define CONFIG_ADDRESS 0xCF8 | ||||||
| #define CONFIG_DATA 0xCFC | #define CONFIG_DATA 0xCFC | ||||||
| 
 | 
 | ||||||
| namespace Kernel | #define DEBUG_PCI 1 | ||||||
|  | 
 | ||||||
|  | namespace Kernel::PCI | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| 	static PCI* s_instance = nullptr; | 	static PCIManager* s_instance = nullptr; | ||||||
| 
 |  | ||||||
| 	void PCI::initialize() |  | ||||||
| 	{ |  | ||||||
| 		ASSERT(s_instance == nullptr); |  | ||||||
| 		s_instance = new PCI(); |  | ||||||
| 		ASSERT(s_instance); |  | ||||||
| 		s_instance->check_all_buses(); |  | ||||||
| 		s_instance->initialize_devices(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	PCI& PCI::get() |  | ||||||
| 	{ |  | ||||||
| 		ASSERT(s_instance); |  | ||||||
| 		return *s_instance; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	static uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) | 	static uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) | ||||||
| 	{ | 	{ | ||||||
|  | @ -55,7 +44,22 @@ namespace Kernel | ||||||
| 		return (dword >> 16) & 0xFF; | 		return (dword >> 16) & 0xFF; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCI::check_function(uint8_t bus, uint8_t dev, uint8_t func) | 	void PCIManager::initialize() | ||||||
|  | 	{ | ||||||
|  | 		ASSERT(s_instance == nullptr); | ||||||
|  | 		s_instance = new PCIManager(); | ||||||
|  | 		ASSERT(s_instance); | ||||||
|  | 		s_instance->check_all_buses(); | ||||||
|  | 		s_instance->initialize_devices(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	PCIManager& PCIManager::get() | ||||||
|  | 	{ | ||||||
|  | 		ASSERT(s_instance); | ||||||
|  | 		return *s_instance; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void PCIManager::check_function(uint8_t bus, uint8_t dev, uint8_t func) | ||||||
| 	{ | 	{ | ||||||
| 		MUST(m_devices.emplace_back(bus, dev, func)); | 		MUST(m_devices.emplace_back(bus, dev, func)); | ||||||
| 		auto& device = m_devices.back(); | 		auto& device = m_devices.back(); | ||||||
|  | @ -63,29 +67,29 @@ namespace Kernel | ||||||
| 			check_bus(device.read_byte(0x19)); | 			check_bus(device.read_byte(0x19)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCI::check_device(uint8_t bus, uint8_t dev) | 	void PCIManager::check_device(uint8_t bus, uint8_t dev) | ||||||
| 	{ | 	{ | ||||||
| 		if (get_vendor_id(bus, dev, 0) == INVALID) | 		if (get_vendor_id(bus, dev, 0) == INVALID_VENDOR) | ||||||
| 			return; | 			return; | ||||||
| 		 | 		 | ||||||
| 		check_function(bus, dev, 0); | 		check_function(bus, dev, 0); | ||||||
| 		if (get_header_type(bus, dev, 0) & MULTI_FUNCTION) | 		if (get_header_type(bus, dev, 0) & MULTI_FUNCTION) | ||||||
| 			for (uint8_t func = 1; func < 8; func++) | 			for (uint8_t func = 1; func < 8; func++) | ||||||
| 				if (get_vendor_id(bus, dev, func) != INVALID) | 				if (get_vendor_id(bus, dev, func) != INVALID_VENDOR) | ||||||
| 					check_function(bus, dev, func); | 					check_function(bus, dev, func); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCI::check_bus(uint8_t bus) | 	void PCIManager::check_bus(uint8_t bus) | ||||||
| 	{ | 	{ | ||||||
| 		for (uint8_t dev = 0; dev < 32; dev++) | 		for (uint8_t dev = 0; dev < 32; dev++) | ||||||
| 			check_device(bus, dev); | 			check_device(bus, dev); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCI::check_all_buses() | 	void PCIManager::check_all_buses() | ||||||
| 	{ | 	{ | ||||||
| 		if (get_header_type(0, 0, 0) & MULTI_FUNCTION) | 		if (get_header_type(0, 0, 0) & MULTI_FUNCTION) | ||||||
| 		{ | 		{ | ||||||
| 			for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID; func++) | 			for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID_VENDOR; func++) | ||||||
| 				check_bus(func); | 				check_bus(func); | ||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
|  | @ -94,9 +98,9 @@ namespace Kernel | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCI::initialize_devices() | 	void PCIManager::initialize_devices() | ||||||
| 	{ | 	{ | ||||||
| 		for (const auto& pci_device : PCI::get().devices()) | 		for (auto& pci_device : m_devices) | ||||||
| 		{ | 		{ | ||||||
| 			switch (pci_device.class_code()) | 			switch (pci_device.class_code()) | ||||||
| 			{ | 			{ | ||||||
|  | @ -134,7 +138,144 @@ namespace Kernel | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	PCIDevice::PCIDevice(uint8_t bus, uint8_t dev, uint8_t func) | 	BAN::ErrorOr<BAN::UniqPtr<BarRegion>> BarRegion::create(PCI::Device& device, uint8_t bar_num) | ||||||
|  | 	{ | ||||||
|  | 		ASSERT(device.header_type() == 0x00); | ||||||
|  | 
 | ||||||
|  | 		uint32_t command_status = device.read_dword(0x04); | ||||||
|  | 
 | ||||||
|  | 		// disable io/mem space while reading bar
 | ||||||
|  | 		device.write_dword(0x04, command_status & ~3); | ||||||
|  | 
 | ||||||
|  | 		uint8_t offset = 0x10 + bar_num * 8; | ||||||
|  | 
 | ||||||
|  | 		uint64_t addr = device.read_dword(offset); | ||||||
|  | 
 | ||||||
|  | 		device.write_dword(offset, 0xFFFFFFFF); | ||||||
|  | 		uint32_t size = device.read_dword(0x10 + bar_num * 8); | ||||||
|  | 		size = ~size + 1; | ||||||
|  | 		device.write_dword(offset, addr); | ||||||
|  | 
 | ||||||
|  | 		// determine bar type
 | ||||||
|  | 		BarType type = BarType::INVALID; | ||||||
|  | 		if (addr & 1) | ||||||
|  | 		{ | ||||||
|  | 			type = BarType::IO; | ||||||
|  | 			addr &= 0xFFFFFFFC; | ||||||
|  | 		} | ||||||
|  | 		else if ((addr & 0b110) == 0b000) | ||||||
|  | 		{ | ||||||
|  | 			type = BarType::MEM; | ||||||
|  | 			addr &= 0xFFFFFFF0; | ||||||
|  | 		} | ||||||
|  | 		else if ((addr & 0b110) == 0b100) | ||||||
|  | 		{ | ||||||
|  | 			type = BarType::MEM; | ||||||
|  | 			addr &= 0xFFFFFFF0; | ||||||
|  | 			addr |= (uint64_t)device.read_dword(offset + 8) << 32; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (type == BarType::INVALID) | ||||||
|  | 		{ | ||||||
|  | 			dwarnln("invalid pci device bar"); | ||||||
|  | 			return BAN::Error::from_errno(EINVAL); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		auto* region_ptr = new BarRegion(type, addr, size); | ||||||
|  | 		ASSERT(region_ptr); | ||||||
|  | 
 | ||||||
|  | 		auto region = BAN::UniqPtr<BarRegion>::adopt(region_ptr); | ||||||
|  | 		TRY(region->initialize()); | ||||||
|  | 
 | ||||||
|  | 		// restore old command register and enable correct IO/MEM
 | ||||||
|  | 		command_status |= (type == BarType::IO) ? 1 : 2; | ||||||
|  | 		device.write_dword(0x04, command_status); | ||||||
|  | 
 | ||||||
|  | #if DEBUG_PCI | ||||||
|  | 		dprintln("created BAR region for PCI {}:{}.{}", | ||||||
|  | 			device.bus(), | ||||||
|  | 			device.dev(), | ||||||
|  | 			device.func() | ||||||
|  | 		); | ||||||
|  | 		dprintln("  type: {}", region->type() == BarType::IO ? "IO" : "MEM"); | ||||||
|  | 		dprintln("  paddr {}", (void*)region->paddr()); | ||||||
|  | 		dprintln("  vaddr {}", (void*)region->vaddr()); | ||||||
|  | 		dprintln("  size  {}", region->size()); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		return region; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BarRegion::BarRegion(BarType type, paddr_t paddr, size_t size) | ||||||
|  | 		: m_type(type) | ||||||
|  | 		, m_paddr(paddr) | ||||||
|  | 		, m_size(size) | ||||||
|  | 	{ } | ||||||
|  | 
 | ||||||
|  | 	BarRegion::~BarRegion() | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::MEM && m_vaddr) | ||||||
|  | 			PageTable::kernel().unmap_range(m_vaddr, m_size); | ||||||
|  | 		m_vaddr = 0;		 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<void> BarRegion::initialize() | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return {}; | ||||||
|  | 
 | ||||||
|  | 		size_t needed_pages = BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE); | ||||||
|  | 		m_vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET); | ||||||
|  | 		if (m_vaddr == 0) | ||||||
|  | 			return BAN::Error::from_errno(ENOMEM); | ||||||
|  | 		PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present); | ||||||
|  | 
 | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void BarRegion::write8(off_t reg, uint8_t val) | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return IO::outb(m_vaddr + reg, val); | ||||||
|  | 		MMIO::write8(m_vaddr + reg, val); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void BarRegion::write16(off_t reg, uint16_t val) | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return IO::outw(m_vaddr + reg, val); | ||||||
|  | 		MMIO::write16(m_vaddr + reg, val); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void BarRegion::write32(off_t reg, uint32_t val) | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return IO::outl(m_vaddr + reg, val); | ||||||
|  | 		MMIO::write32(m_vaddr + reg, val); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	uint8_t BarRegion::read8(off_t reg) | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return IO::inb(m_vaddr + reg); | ||||||
|  | 		return MMIO::read8(m_vaddr + reg); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	uint16_t BarRegion::read16(off_t reg) | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return IO::inw(m_vaddr + reg); | ||||||
|  | 		return MMIO::read16(m_vaddr + reg); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	uint32_t BarRegion::read32(off_t reg) | ||||||
|  | 	{ | ||||||
|  | 		if (m_type == BarType::IO) | ||||||
|  | 			return IO::inl(m_vaddr + reg); | ||||||
|  | 		return MMIO::read32(m_vaddr + reg); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	PCI::Device::Device(uint8_t bus, uint8_t dev, uint8_t func) | ||||||
| 		: m_bus(bus), m_dev(dev), m_func(func) | 		: m_bus(bus), m_dev(dev), m_func(func) | ||||||
| 	{ | 	{ | ||||||
| 		uint32_t type = read_word(0x0A); | 		uint32_t type = read_word(0x0A); | ||||||
|  | @ -142,87 +283,92 @@ namespace Kernel | ||||||
| 		m_subclass    = (uint8_t)(type); | 		m_subclass    = (uint8_t)(type); | ||||||
| 		m_prog_if     = read_byte(0x09); | 		m_prog_if     = read_byte(0x09); | ||||||
| 		m_header_type = read_byte(0x0E); | 		m_header_type = read_byte(0x0E); | ||||||
|  | 
 | ||||||
|  | 		enumerate_capabilites(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uint32_t PCIDevice::read_dword(uint8_t offset) const | 	uint32_t PCI::Device::read_dword(uint8_t offset) const | ||||||
| 	{ | 	{ | ||||||
| 		ASSERT((offset & 0x03) == 0); | 		ASSERT((offset & 0x03) == 0); | ||||||
| 		return read_config_dword(m_bus, m_dev, m_func, offset); | 		return read_config_dword(m_bus, m_dev, m_func, offset); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uint16_t PCIDevice::read_word(uint8_t offset) const | 	uint16_t PCI::Device::read_word(uint8_t offset) const | ||||||
| 	{ | 	{ | ||||||
| 		ASSERT((offset & 0x01) == 0); | 		ASSERT((offset & 0x01) == 0); | ||||||
| 		uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); | 		uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); | ||||||
| 		return (uint16_t)(dword >> (8 * (offset & 0x03))); | 		return (uint16_t)(dword >> (8 * (offset & 0x03))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uint8_t PCIDevice::read_byte(uint8_t offset) const | 	uint8_t PCI::Device::read_byte(uint8_t offset) const | ||||||
| 	{ | 	{ | ||||||
| 		uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); | 		uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); | ||||||
| 		return (uint8_t)(dword >> (8 * (offset & 0x03))); | 		return (uint8_t)(dword >> (8 * (offset & 0x03))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::write_dword(uint8_t offset, uint32_t value) const | 	void PCI::Device::write_dword(uint8_t offset, uint32_t value) | ||||||
| 	{ | 	{ | ||||||
| 		ASSERT((offset & 0x03) == 0); | 		ASSERT((offset & 0x03) == 0); | ||||||
| 		write_config_dword(m_bus, m_dev, m_func, offset, value); | 		write_config_dword(m_bus, m_dev, m_func, offset, value); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	PCIDevice::BarType PCIDevice::read_bar_type(uint8_t bar) const | 	BAN::ErrorOr<BAN::UniqPtr<BarRegion>> PCI::Device::allocate_bar_region(uint8_t bar_num) | ||||||
| 	{ | 	{ | ||||||
| 		ASSERT(m_header_type == 0x00); | 		return BarRegion::create(*this, bar_num); | ||||||
| 		ASSERT(bar <= 5); |  | ||||||
| 
 |  | ||||||
| 		uint32_t type = read_dword(0x10 + bar * 4) & 0b111; |  | ||||||
| 		if (type & 1) |  | ||||||
| 			return BarType::IO; |  | ||||||
| 		type >>= 1; |  | ||||||
| 		if (type == 0x0 || type == 0x2) |  | ||||||
| 			return BarType::MEM; |  | ||||||
| 		return BarType::INVAL; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uint64_t PCIDevice::read_bar_address(uint8_t bar) const | 	void PCI::Device::enumerate_capabilites() | ||||||
| 	{ | 	{ | ||||||
| 		ASSERT(m_header_type == 0x00); | 		uint16_t status = read_word(0x06); | ||||||
| 		ASSERT(bar <= 5); | 		if (!(status & (1 << 4))) | ||||||
|  | 			return; | ||||||
| 
 | 
 | ||||||
| 		uint64_t address = read_dword(0x10 + bar * 4); | 		uint8_t capabilities = read_byte(0x34) & 0xFC; | ||||||
| 		if (address & 1) | 		while (capabilities) | ||||||
| 			return address & 0xFFFFFFFC; | 		{ | ||||||
| 		if ((address & 0b110) == 0b100) | 			uint16_t next = read_word(capabilities); | ||||||
| 			address |= (uint64_t)read_dword(0x10 + bar * 4 + 4) << 32; | 			dprintln("  cap {2H}", next & 0xFF); | ||||||
| 		return address & 0xFFFFFFFFFFFFFFF0; | 			capabilities = (next >> 8) & 0xFC; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::enable_bus_mastering() const | 	void PCI::Device::enable_bus_mastering() | ||||||
| 	{ | 	{ | ||||||
| 		write_dword(0x04, read_dword(0x04) | 1u << 2); | 		write_dword(0x04, read_dword(0x04) | 1u << 2); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::disable_bus_mastering() const | 	void PCI::Device::disable_bus_mastering() | ||||||
| 	{ | 	{ | ||||||
| 		write_dword(0x04, read_dword(0x04) & ~(1u << 2)); | 		write_dword(0x04, read_dword(0x04) & ~(1u << 2)); | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::enable_memory_space() const | 	void PCI::Device::enable_memory_space() | ||||||
| 	{ | 	{ | ||||||
| 		write_dword(0x04, read_dword(0x04) | 1u << 1); | 		write_dword(0x04, read_dword(0x04) | 1u << 1); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::disable_memory_space() const | 	void PCI::Device::disable_memory_space() | ||||||
| 	{ | 	{ | ||||||
| 		write_dword(0x04, read_dword(0x04) & ~(1u << 1)); | 		write_dword(0x04, read_dword(0x04) & ~(1u << 1)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::enable_pin_interrupts() const | 	void PCI::Device::enable_io_space() | ||||||
|  | 	{ | ||||||
|  | 		write_dword(0x04, read_dword(0x04) | 1u << 0); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void PCI::Device::disable_io_space() | ||||||
|  | 	{ | ||||||
|  | 		write_dword(0x04, read_dword(0x04) & ~(1u << 0)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void PCI::Device::enable_pin_interrupts() | ||||||
| 	{ | 	{ | ||||||
| 		write_dword(0x04, read_dword(0x04) | 1u << 10); | 		write_dword(0x04, read_dword(0x04) | 1u << 10); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void PCIDevice::disable_pin_interrupts() const | 	void PCI::Device::disable_pin_interrupts() | ||||||
| 	{ | 	{ | ||||||
| 		write_dword(0x04, read_dword(0x04) & ~(1u << 10)); | 		write_dword(0x04, read_dword(0x04) & ~(1u << 10)); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
| namespace Kernel | namespace Kernel | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<BAN::RefPtr<ATAController>> ATAController::create(const PCIDevice& device) | 	BAN::ErrorOr<BAN::RefPtr<ATAController>> ATAController::create(const PCI::Device& device) | ||||||
| 	{ | 	{ | ||||||
| 		ATAController* controller = new ATAController(); | 		ATAController* controller = new ATAController(); | ||||||
| 		if (controller == nullptr) | 		if (controller == nullptr) | ||||||
|  | @ -50,7 +50,7 @@ namespace Kernel | ||||||
| 		: m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0)) | 		: m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0)) | ||||||
| 	{ } | 	{ } | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<void> ATAController::initialize(const PCIDevice& pci_device) | 	BAN::ErrorOr<void> ATAController::initialize(const PCI::Device& pci_device) | ||||||
| 	{ | 	{ | ||||||
| 		struct Bus | 		struct Bus | ||||||
| 		{ | 		{ | ||||||
|  |  | ||||||
|  | @ -167,7 +167,7 @@ static void init2(void*) | ||||||
| 
 | 
 | ||||||
| 	DevFileSystem::get().initialize_device_updater(); | 	DevFileSystem::get().initialize_device_updater(); | ||||||
| 
 | 
 | ||||||
| 	PCI::initialize(); | 	PCI::PCIManager::initialize(); | ||||||
| 	dprintln("PCI initialized"); | 	dprintln("PCI initialized"); | ||||||
| 
 | 
 | ||||||
| 	VirtualFileSystem::initialize(cmdline.root); | 	VirtualFileSystem::initialize(cmdline.root); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue