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 | ||||
| 	{ | ||||
| 	public: | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<E1000>> create(const PCIDevice&); | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<E1000>> create(PCI::Device&); | ||||
| 		~E1000(); | ||||
| 
 | ||||
| 		virtual uint8_t* get_mac_address() override { return m_mac_address; } | ||||
|  | @ -24,12 +24,12 @@ namespace Kernel | |||
| 
 | ||||
| 	private: | ||||
| 		E1000() = default; | ||||
| 		BAN::ErrorOr<void> initialize(const PCIDevice&); | ||||
| 		BAN::ErrorOr<void> initialize(PCI::Device&); | ||||
| 
 | ||||
| 		static void interrupt_handler(); | ||||
| 
 | ||||
| 		void write32(uint16_t reg, uint32_t value); | ||||
| 		uint32_t read32(uint16_t reg); | ||||
| 		void write32(uint16_t reg, uint32_t value); | ||||
| 
 | ||||
| 		void detect_eeprom(); | ||||
| 		uint32_t eeprom_read(uint8_t addr); | ||||
|  | @ -44,8 +44,7 @@ namespace Kernel | |||
| 		void handle_receive(); | ||||
| 
 | ||||
| 	private: | ||||
| 		PCIDevice::BarType		m_bar_type {}; | ||||
| 		uint64_t				m_bar_addr {}; | ||||
| 		BAN::UniqPtr<PCI::BarRegion> m_bar_region; | ||||
| 		bool					m_has_eerprom { false }; | ||||
| 		uint8_t					m_mac_address[6] {}; | ||||
| 		uint16_t				m_rx_current {}; | ||||
|  |  | |||
|  | @ -1,28 +1,63 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <BAN/UniqPtr.h> | ||||
| #include <BAN/Vector.h> | ||||
| #include <kernel/Memory/Types.h> | ||||
| 
 | ||||
| namespace Kernel | ||||
| namespace Kernel::PCI | ||||
| { | ||||
| 
 | ||||
| 	class PCIDevice | ||||
| 	enum class BarType | ||||
| 	{ | ||||
| 	public: | ||||
| 		enum class BarType | ||||
| 		{ | ||||
| 			INVAL, | ||||
| 			MEM, | ||||
| 			IO, | ||||
| 		}; | ||||
| 		INVALID, | ||||
| 		MEM, | ||||
| 		IO, | ||||
| 	}; | ||||
| 
 | ||||
| 	class Device; | ||||
| 
 | ||||
| 	class BarRegion | ||||
| 	{ | ||||
| 		BAN_NON_COPYABLE(BarRegion); | ||||
| 
 | ||||
| 	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; | ||||
| 		uint16_t read_word(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 dev() const { return m_dev; } | ||||
|  | @ -32,17 +67,24 @@ namespace Kernel | |||
| 		uint8_t subclass() const { return m_subclass; } | ||||
| 		uint8_t prog_if() const { return m_prog_if; } | ||||
| 
 | ||||
| 		BarType read_bar_type(uint8_t) const; | ||||
| 		uint64_t read_bar_address(uint8_t) const; | ||||
| 		uint8_t header_type() const { return m_header_type; } | ||||
| 
 | ||||
| 		void enable_bus_mastering() const; | ||||
| 		void disable_bus_mastering() const; | ||||
| 		BAN::ErrorOr<BAN::UniqPtr<BarRegion>> allocate_bar_region(uint8_t bar_num); | ||||
| 
 | ||||
| 		void enable_memory_space() const; | ||||
| 		void disable_memory_space() const; | ||||
| 		void enable_bus_mastering(); | ||||
| 		void disable_bus_mastering(); | ||||
| 
 | ||||
| 		void enable_pin_interrupts() const; | ||||
| 		void disable_pin_interrupts() const; | ||||
| 		void enable_memory_space(); | ||||
| 		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: | ||||
| 		uint8_t m_bus; | ||||
|  | @ -56,19 +98,19 @@ namespace Kernel | |||
| 		uint8_t m_header_type; | ||||
| 	}; | ||||
| 
 | ||||
| 	class PCI | ||||
| 	class PCIManager | ||||
| 	{ | ||||
| 		BAN_NON_COPYABLE(PCI); | ||||
| 		BAN_NON_MOVABLE(PCI); | ||||
| 		BAN_NON_COPYABLE(PCIManager); | ||||
| 		BAN_NON_MOVABLE(PCIManager); | ||||
| 
 | ||||
| 	public: | ||||
| 		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: | ||||
| 		PCI() = default; | ||||
| 		PCIManager() = default; | ||||
| 		void check_function(uint8_t bus, uint8_t dev, uint8_t func); | ||||
| 		void check_device(uint8_t bus, uint8_t dev); | ||||
| 		void check_bus(uint8_t bus); | ||||
|  | @ -76,7 +118,7 @@ namespace Kernel | |||
| 		void initialize_devices(); | ||||
| 
 | ||||
| 	private: | ||||
| 		BAN::Vector<PCIDevice> m_devices; | ||||
| 		BAN::Vector<PCI::Device> m_devices; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -11,13 +11,13 @@ namespace Kernel | |||
| 	class ATAController final : public StorageController | ||||
| 	{ | ||||
| 	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; | ||||
| 
 | ||||
| 	private: | ||||
| 		ATAController(); | ||||
| 		BAN::ErrorOr<void> initialize(const PCIDevice& device); | ||||
| 		BAN::ErrorOr<void> initialize(const PCI::Device& device); | ||||
| 
 | ||||
| 	private: | ||||
| 		ATABus* m_buses[2] { nullptr, nullptr }; | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <kernel/MMIO.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_STATUS			0x0008 | ||||
|  | @ -112,7 +112,7 @@ namespace Kernel | |||
| 		volatile uint16_t special; | ||||
| 	} __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(); | ||||
| 		ASSERT(e1000); | ||||
|  | @ -126,42 +126,26 @@ namespace Kernel | |||
| 
 | ||||
| 	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); | ||||
| 		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); | ||||
| 		} | ||||
| 
 | ||||
| 		m_bar_region = TRY(pci_device.allocate_bar_region(0)); | ||||
| 		pci_device.enable_bus_mastering(); | ||||
| 
 | ||||
| 		detect_eeprom(); | ||||
| 
 | ||||
| 		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}", | ||||
| 			m_mac_address[0], | ||||
| 			m_mac_address[1], | ||||
|  | @ -170,52 +154,22 @@ namespace Kernel | |||
| 			m_mac_address[4], | ||||
| 			m_mac_address[5] | ||||
| 		); | ||||
| 
 | ||||
| 		initialize_rx(); | ||||
| 		initialize_tx(); | ||||
| 
 | ||||
| 		enable_link(); | ||||
| 		enable_interrupts(); | ||||
| 		 | ||||
| 		dprintln("  link up: {}", link_up()); | ||||
| 		if (link_up()) | ||||
| 			dprintln("  link speed: {} Mbps", link_speed()); | ||||
| #endif | ||||
| 
 | ||||
| 		return {}; | ||||
| 	} | ||||
| 
 | ||||
| 	void E1000::write32(uint16_t reg, uint32_t value) | ||||
| 	{ | ||||
| 		switch (m_bar_type) | ||||
| 		{ | ||||
| 			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(); | ||||
| 		} | ||||
| 		m_bar_region->write32(reg, value); | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t E1000::read32(uint16_t reg) | ||||
| 	{ | ||||
| 		uint32_t result = 0; | ||||
| 		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; | ||||
| 		return m_bar_region->read32(reg); | ||||
| 	} | ||||
| 
 | ||||
| 	void E1000::detect_eeprom() | ||||
|  |  | |||
|  | @ -1,33 +1,22 @@ | |||
| #include <kernel/IO.h> | ||||
| #include <kernel/Memory/PageTable.h> | ||||
| #include <kernel/MMIO.h> | ||||
| #include <kernel/Networking/E1000.h> | ||||
| #include <kernel/PCI.h> | ||||
| #include <kernel/Storage/ATAController.h> | ||||
| 
 | ||||
| #define INVALID 0xFFFF | ||||
| #define INVALID_VENDOR 0xFFFF | ||||
| #define MULTI_FUNCTION 0x80 | ||||
| 
 | ||||
| #define CONFIG_ADDRESS 0xCF8 | ||||
| #define CONFIG_DATA 0xCFC | ||||
| 
 | ||||
| namespace Kernel | ||||
| #define DEBUG_PCI 1 | ||||
| 
 | ||||
| namespace Kernel::PCI | ||||
| { | ||||
| 
 | ||||
| 	static PCI* 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 PCIManager* s_instance = nullptr; | ||||
| 
 | ||||
| 	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; | ||||
| 	} | ||||
| 
 | ||||
| 	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)); | ||||
| 		auto& device = m_devices.back(); | ||||
|  | @ -63,29 +67,29 @@ namespace Kernel | |||
| 			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; | ||||
| 		 | ||||
| 		check_function(bus, dev, 0); | ||||
| 		if (get_header_type(bus, dev, 0) & MULTI_FUNCTION) | ||||
| 			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); | ||||
| 	} | ||||
| 
 | ||||
| 	void PCI::check_bus(uint8_t bus) | ||||
| 	void PCIManager::check_bus(uint8_t bus) | ||||
| 	{ | ||||
| 		for (uint8_t dev = 0; dev < 32; dev++) | ||||
| 			check_device(bus, dev); | ||||
| 	} | ||||
| 
 | ||||
| 	void PCI::check_all_buses() | ||||
| 	void PCIManager::check_all_buses() | ||||
| 	{ | ||||
| 		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); | ||||
| 		} | ||||
| 		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()) | ||||
| 			{ | ||||
|  | @ -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) | ||||
| 	{ | ||||
| 		uint32_t type = read_word(0x0A); | ||||
|  | @ -142,87 +283,92 @@ namespace Kernel | |||
| 		m_subclass    = (uint8_t)(type); | ||||
| 		m_prog_if     = read_byte(0x09); | ||||
| 		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); | ||||
| 		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); | ||||
| 		uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); | ||||
| 		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); | ||||
| 		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); | ||||
| 		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); | ||||
| 		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; | ||||
| 		return BarRegion::create(*this, bar_num); | ||||
| 	} | ||||
| 
 | ||||
| 	uint64_t PCIDevice::read_bar_address(uint8_t bar) const | ||||
| 	void PCI::Device::enumerate_capabilites() | ||||
| 	{ | ||||
| 		ASSERT(m_header_type == 0x00); | ||||
| 		ASSERT(bar <= 5); | ||||
| 		uint16_t status = read_word(0x06); | ||||
| 		if (!(status & (1 << 4))) | ||||
| 			return; | ||||
| 
 | ||||
| 		uint64_t address = read_dword(0x10 + bar * 4); | ||||
| 		if (address & 1) | ||||
| 			return address & 0xFFFFFFFC; | ||||
| 		if ((address & 0b110) == 0b100) | ||||
| 			address |= (uint64_t)read_dword(0x10 + bar * 4 + 4) << 32; | ||||
| 		return address & 0xFFFFFFFFFFFFFFF0; | ||||
| 		uint8_t capabilities = read_byte(0x34) & 0xFC; | ||||
| 		while (capabilities) | ||||
| 		{ | ||||
| 			uint16_t next = read_word(capabilities); | ||||
| 			dprintln("  cap {2H}", next & 0xFF); | ||||
| 			capabilities = (next >> 8) & 0xFC; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void PCIDevice::enable_bus_mastering() const | ||||
| 	void PCI::Device::enable_bus_mastering() | ||||
| 	{ | ||||
| 		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)); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	void PCIDevice::enable_memory_space() const | ||||
| 	void PCI::Device::enable_memory_space() | ||||
| 	{ | ||||
| 		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)); | ||||
| 	} | ||||
| 
 | ||||
| 	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); | ||||
| 	} | ||||
| 
 | ||||
| 	void PCIDevice::disable_pin_interrupts() const | ||||
| 	void PCI::Device::disable_pin_interrupts() | ||||
| 	{ | ||||
| 		write_dword(0x04, read_dword(0x04) & ~(1u << 10)); | ||||
| 	} | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 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(); | ||||
| 		if (controller == nullptr) | ||||
|  | @ -50,7 +50,7 @@ namespace Kernel | |||
| 		: 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 | ||||
| 		{ | ||||
|  |  | |||
|  | @ -167,7 +167,7 @@ static void init2(void*) | |||
| 
 | ||||
| 	DevFileSystem::get().initialize_device_updater(); | ||||
| 
 | ||||
| 	PCI::initialize(); | ||||
| 	PCI::PCIManager::initialize(); | ||||
| 	dprintln("PCI initialized"); | ||||
| 
 | ||||
| 	VirtualFileSystem::initialize(cmdline.root); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue