diff options
author | Liav A <liavalb@gmail.com> | 2019-12-31 13:04:30 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2020-01-02 00:50:09 +0100 |
commit | e5ffa960d7b585f4aba0eb18b89a14dee9e7e2b5 (patch) | |
tree | 4f6333ceb99e4010936b30082ce990d4ce0def1c /Kernel | |
parent | d85874be4bae26f414cbb573ff2d67f491a331d6 (diff) | |
download | serenity-e5ffa960d7b585f4aba0eb18b89a14dee9e7e2b5.zip |
Kernel: Create support for PCI ECAM
The new PCI subsystem is initialized during runtime.
PCI::Initializer is supposed to be called during early boot, to
perform a few tests, and initialize the proper configuration space
access mechanism. Kernel boot parameters can be specified by a user to
determine what tests will occur, to aid debugging on problematic
machines.
After that, PCI::Initializer should be dismissed.
PCI::IOAccess is a class that is derived from PCI::Access
class and implements PCI configuration space access mechanism via x86
IO ports.
PCI::MMIOAccess is a class that is derived from PCI::Access
and implements PCI configurtaion space access mechanism via memory
access.
The new PCI subsystem also supports determination of IO/MMIO space
needed by a device by checking a given BAR.
In addition, Every device or component that use the PCI subsystem has
changed to match the last changes.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Devices/BXVGADevice.cpp | 2 | ||||
-rw-r--r-- | Kernel/Devices/PATAChannel.h | 2 | ||||
-rw-r--r-- | Kernel/FileSystem/ProcFS.cpp | 3 | ||||
-rw-r--r-- | Kernel/Makefile | 9 | ||||
-rw-r--r-- | Kernel/Net/E1000NetworkAdapter.cpp | 3 | ||||
-rw-r--r-- | Kernel/Net/E1000NetworkAdapter.h | 2 | ||||
-rw-r--r-- | Kernel/Net/RTL8139NetworkAdapter.cpp | 1 | ||||
-rw-r--r-- | Kernel/Net/RTL8139NetworkAdapter.h | 2 | ||||
-rw-r--r-- | Kernel/PCI/Access.cpp | 142 | ||||
-rw-r--r-- | Kernel/PCI/Access.h | 62 | ||||
-rw-r--r-- | Kernel/PCI/Definitions.h | 113 | ||||
-rw-r--r-- | Kernel/PCI/IOAccess.cpp | 63 | ||||
-rw-r--r-- | Kernel/PCI/IOAccess.h | 25 | ||||
-rw-r--r-- | Kernel/PCI/Initializer.cpp | 126 | ||||
-rw-r--r-- | Kernel/PCI/Initializer.h | 22 | ||||
-rw-r--r-- | Kernel/PCI/MMIOAccess.cpp | 215 | ||||
-rw-r--r-- | Kernel/PCI/MMIOAccess.h | 53 | ||||
-rw-r--r-- | Kernel/init.cpp | 40 |
18 files changed, 873 insertions, 12 deletions
diff --git a/Kernel/Devices/BXVGADevice.cpp b/Kernel/Devices/BXVGADevice.cpp index a45305f1d6..5da0b771f7 100644 --- a/Kernel/Devices/BXVGADevice.cpp +++ b/Kernel/Devices/BXVGADevice.cpp @@ -1,6 +1,6 @@ #include <Kernel/Devices/BXVGADevice.h> #include <Kernel/IO.h> -#include <Kernel/PCI.h> +#include <Kernel/PCI/Access.h> #include <Kernel/Process.h> #include <Kernel/VM/AnonymousVMObject.h> #include <Kernel/VM/MemoryManager.h> diff --git a/Kernel/Devices/PATAChannel.h b/Kernel/Devices/PATAChannel.h index da1cb95421..6db5953b44 100644 --- a/Kernel/Devices/PATAChannel.h +++ b/Kernel/Devices/PATAChannel.h @@ -14,7 +14,7 @@ #include <AK/RefPtr.h> #include <Kernel/IRQHandler.h> #include <Kernel/Lock.h> -#include <Kernel/PCI.h> +#include <Kernel/PCI/Access.h> #include <Kernel/VM/PhysicalAddress.h> #include <Kernel/VM/PhysicalPage.h> #include <Kernel/WaitQueue.h> diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 0c97e47611..b75db41862 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -22,7 +22,7 @@ #include <Kernel/Net/Routing.h> #include <Kernel/Net/TCPSocket.h> #include <Kernel/Net/UDPSocket.h> -#include <Kernel/PCI.h> +#include <Kernel/PCI/Access.h> #include <Kernel/Profiling.h> #include <Kernel/VM/MemoryManager.h> #include <Kernel/VM/PurgeableVMObject.h> @@ -289,6 +289,7 @@ Optional<KBuffer> procfs$pci(InodeIdentifier) JsonArraySerializer array { builder }; PCI::enumerate_all([&array](PCI::Address address, PCI::ID id) { auto obj = array.add_object(); + obj.add("seg", address.seg()); obj.add("bus", address.bus()); obj.add("slot", address.slot()); obj.add("function", address.function()); diff --git a/Kernel/Makefile b/Kernel/Makefile index 1473108265..53fedf078f 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -71,7 +71,10 @@ OBJS = \ Net/Socket.o \ Net/TCPSocket.o \ Net/UDPSocket.o \ - PCI.o \ + PCI/Access.o \ + PCI/IOAccess.o \ + PCI/MMIOAccess.o \ + PCI/Initializer.o \ Process.o \ ProcessTracer.o \ Profiling.o \ @@ -97,6 +100,10 @@ OBJS = \ VM/RangeAllocator.o \ VM/Region.o \ VM/VMObject.o \ + ACPI/ACPIParser.o \ + ACPI/ACPIStaticParser.o \ + ACPI/ACPIDynamicParser.o \ + ACPI/DMIDecoder.o \ WaitQueue.o \ init.o \ kprintf.o diff --git a/Kernel/Net/E1000NetworkAdapter.cpp b/Kernel/Net/E1000NetworkAdapter.cpp index ff9ee3f538..d35cec36ce 100644 --- a/Kernel/Net/E1000NetworkAdapter.cpp +++ b/Kernel/Net/E1000NetworkAdapter.cpp @@ -1,6 +1,5 @@ #include <Kernel/IO.h> #include <Kernel/Net/E1000NetworkAdapter.h> -#include <Kernel/PCI.h> #include <Kernel/Thread.h> #define REG_CTRL 0x0000 @@ -119,6 +118,7 @@ E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address pci_address, u8 irq) enable_bus_mastering(m_pci_address); m_mmio_base = PhysicalAddress(PCI::get_BAR0(m_pci_address)); + u32 mmio_base_size = PCI::get_BAR_Space_Size(pci_address, 0); MM.map_for_kernel(VirtualAddress(m_mmio_base.get()), m_mmio_base); MM.map_for_kernel(VirtualAddress(m_mmio_base.offset(4096).get()), m_mmio_base.offset(4096)); MM.map_for_kernel(VirtualAddress(m_mmio_base.offset(8192).get()), m_mmio_base.offset(8192)); @@ -129,6 +129,7 @@ E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address pci_address, u8 irq) m_interrupt_line = PCI::get_interrupt_line(m_pci_address); kprintf("E1000: IO port base: %w\n", m_io_base); kprintf("E1000: MMIO base: P%x\n", m_mmio_base); + kprintf("E1000: MMIO base size: %u bytes\n", mmio_base_size); kprintf("E1000: Interrupt line: %u\n", m_interrupt_line); detect_eeprom(); kprintf("E1000: Has EEPROM? %u\n", m_has_eeprom); diff --git a/Kernel/Net/E1000NetworkAdapter.h b/Kernel/Net/E1000NetworkAdapter.h index a437feda26..b1c99e5774 100644 --- a/Kernel/Net/E1000NetworkAdapter.h +++ b/Kernel/Net/E1000NetworkAdapter.h @@ -3,7 +3,7 @@ #include <AK/OwnPtr.h> #include <Kernel/IRQHandler.h> #include <Kernel/Net/NetworkAdapter.h> -#include <Kernel/PCI.h> +#include <Kernel/PCI/Access.h> class E1000NetworkAdapter final : public NetworkAdapter , public IRQHandler { diff --git a/Kernel/Net/RTL8139NetworkAdapter.cpp b/Kernel/Net/RTL8139NetworkAdapter.cpp index 5ab7282430..5b29750d59 100644 --- a/Kernel/Net/RTL8139NetworkAdapter.cpp +++ b/Kernel/Net/RTL8139NetworkAdapter.cpp @@ -1,6 +1,5 @@ #include <Kernel/IO.h> #include <Kernel/Net/RTL8139NetworkAdapter.h> -#include <Kernel/PCI.h> //#define RTL8139_DEBUG diff --git a/Kernel/Net/RTL8139NetworkAdapter.h b/Kernel/Net/RTL8139NetworkAdapter.h index 7724f18289..77071db4b9 100644 --- a/Kernel/Net/RTL8139NetworkAdapter.h +++ b/Kernel/Net/RTL8139NetworkAdapter.h @@ -3,7 +3,7 @@ #include <AK/OwnPtr.h> #include <Kernel/IRQHandler.h> #include <Kernel/Net/NetworkAdapter.h> -#include <Kernel/PCI.h> +#include <Kernel/PCI/Access.h> #define RTL8139_TX_BUFFER_COUNT 4 diff --git a/Kernel/PCI/Access.cpp b/Kernel/PCI/Access.cpp new file mode 100644 index 0000000000..6e342027f8 --- /dev/null +++ b/Kernel/PCI/Access.cpp @@ -0,0 +1,142 @@ +#include <Kernel/PCI/Access.h> +#include <Kernel/PCI/IOAccess.h> + +static PCI::Access* s_access; + +PCI::Access& PCI::Access::the() +{ + if (s_access == nullptr) { + ASSERT_NOT_REACHED(); // We failed to initialize the PCI subsystem, so stop here! + } + return *s_access; +} + +bool PCI::Access::is_initialized() +{ + return (s_access != nullptr); +} + +PCI::Access::Access() +{ + s_access = this; +} + +void PCI::Access::enumerate_functions(int type, u8 bus, u8 slot, u8 function, Function<void(Address, ID)>& callback) +{ + Address address(0, bus, slot, function); + if (type == -1 || type == read_type(address)) + callback(address, { read16_field(address, PCI_VENDOR_ID), read16_field(address, PCI_DEVICE_ID) }); + if (read_type(address) == PCI_TYPE_BRIDGE) { + u8 secondary_bus = read8_field(address, PCI_SECONDARY_BUS); +#ifdef PCI_DEBUG + kprintf("PCI: Found secondary bus: %u\n", secondary_bus); +#endif + ASSERT(secondary_bus != bus); + enumerate_bus(type, secondary_bus, callback); + } +} + +void PCI::Access::enumerate_slot(int type, u8 bus, u8 slot, Function<void(Address, ID)>& callback) +{ + Address address(0, bus, slot, 0); + if (read16_field(address, PCI_VENDOR_ID) == PCI_NONE) + return; + enumerate_functions(type, bus, slot, 0, callback); + if (!(read8_field(address, PCI_HEADER_TYPE) & 0x80)) + return; + for (u8 function = 1; function < 8; ++function) { + Address address(0, bus, slot, function); + if (read16_field(address, PCI_VENDOR_ID) != PCI_NONE) + enumerate_functions(type, bus, slot, function, callback); + } +} + +void PCI::Access::enumerate_bus(int type, u8 bus, Function<void(Address, ID)>& callback) +{ + for (u8 slot = 0; slot < 32; ++slot) + enumerate_slot(type, bus, slot, callback); +} + +void PCI::Access::enable_bus_mastering(Address address) +{ + auto value = read16_field(address, PCI_COMMAND); + value |= (1 << 2); + value |= (1 << 0); + write16_field(address, PCI_COMMAND, value); +} + +void PCI::Access::disable_bus_mastering(Address address) +{ + auto value = read16_field(address, PCI_COMMAND); + value &= ~(1 << 2); + value |= (1 << 0); + write16_field(address, PCI_COMMAND, value); +} + +namespace PCI { +void enumerate_all(Function<void(Address, ID)> callback) +{ + PCI::Access::the().enumerate_all(callback); +} + +u8 get_interrupt_line(Address address) +{ + return PCI::Access::the().get_interrupt_line(address); +} +u32 get_BAR0(Address address) +{ + return PCI::Access::the().get_BAR0(address); +} +u32 get_BAR1(Address address) +{ + return PCI::Access::the().get_BAR1(address); +} +u32 get_BAR2(Address address) +{ + return PCI::Access::the().get_BAR2(address); +} +u32 get_BAR3(Address address) +{ + return PCI::Access::the().get_BAR3(address); +} +u32 get_BAR4(Address address) +{ + return PCI::Access::the().get_BAR4(address); +} +u32 get_BAR5(Address address) +{ + return PCI::Access::the().get_BAR5(address); +} +u8 get_revision_id(Address address) +{ + return PCI::Access::the().get_revision_id(address); +} +u8 get_subclass(Address address) +{ + return PCI::Access::the().get_subclass(address); +} +u8 get_class(Address address) +{ + return PCI::Access::the().get_class(address); +} +u16 get_subsystem_id(Address address) +{ + return PCI::Access::the().get_subsystem_id(address); +} +u16 get_subsystem_vendor_id(Address address) +{ + return PCI::Access::the().get_subsystem_vendor_id(address); +} +void enable_bus_mastering(Address address) +{ + PCI::Access::the().enable_bus_mastering(address); +} +void disable_bus_mastering(Address address) +{ + PCI::Access::the().disable_bus_mastering(address); +} +u32 get_BAR_Space_Size(Address address, u8 bar_number) +{ + return PCI::Access::the().get_BAR_Space_Size(address, bar_number); +} +} diff --git a/Kernel/PCI/Access.h b/Kernel/PCI/Access.h new file mode 100644 index 0000000000..d2f7cdc053 --- /dev/null +++ b/Kernel/PCI/Access.h @@ -0,0 +1,62 @@ +#pragma once + +#include <AK/String.h> +#include <Kernel/PCI/Definitions.h> + +class PCI::Access { +public: + virtual void enumerate_all(Function<void(Address, ID)>&) = 0; + virtual u8 get_interrupt_line(Address address) { return read8_field(address, PCI_INTERRUPT_LINE); } + virtual u32 get_BAR0(Address address) { return read32_field(address, PCI_BAR0); } + virtual u32 get_BAR1(Address address) { return read32_field(address, PCI_BAR1); } + virtual u32 get_BAR2(Address address) { return read32_field(address, PCI_BAR2); } + virtual u32 get_BAR3(Address address) { return read32_field(address, PCI_BAR3); } + virtual u32 get_BAR4(Address address) { return read32_field(address, PCI_BAR4); } + virtual u32 get_BAR5(Address address) { return read32_field(address, PCI_BAR5); } + + virtual u32 get_BAR_Space_Size(Address address, u8 bar_number) + { + // See PCI Spec 2.3, Page 222 + ASSERT(bar_number < 6); + u8 field = (PCI_BAR0 + (bar_number << 2)); + u32 bar_reserved = read32_field(address, field); + write32_field(address, field, 0xFFFFFFFF); + u32 space_size = read32_field(address, field); + write32_field(address, field, bar_reserved); + space_size &= 0xfffffff0; + space_size = (~space_size) + 1; + return space_size; + } + + virtual u8 get_revision_id(Address address) { return read8_field(address, PCI_REVISION_ID); } + virtual u8 get_subclass(Address address) { return read8_field(address, PCI_SUBCLASS); } + virtual u8 get_class(Address address) { return read8_field(address, PCI_CLASS); } + virtual u16 get_subsystem_id(Address address) { return read16_field(address, PCI_SUBSYSTEM_ID); } + virtual u16 get_subsystem_vendor_id(Address address) { return read16_field(address, PCI_SUBSYSTEM_VENDOR_ID); } + virtual u16 read_type(Address address) { return (read8_field(address, PCI_CLASS) << 8u) | read8_field(address, PCI_SUBCLASS); } + + virtual void enable_bus_mastering(Address) final; + virtual void disable_bus_mastering(Address) final; + + virtual void enumerate_bus(int type, u8 bus, Function<void(Address, ID)>&) final; + virtual void enumerate_functions(int type, u8 bus, u8 slot, u8 function, Function<void(Address, ID)>& callback) final; + virtual void enumerate_slot(int type, u8 bus, u8 slot, Function<void(Address, ID)>& callback) final; + + static Access& the(); + static bool is_initialized(); + virtual uint32_t get_segments_count() = 0; + virtual uint8_t get_segment_start_bus(u32 segment) = 0; + virtual uint8_t get_segment_end_bus(u32 segment) = 0; + virtual String get_access_type() = 0; + +protected: + Access(); + + virtual u8 read8_field(Address address, u32 field) = 0; + virtual u16 read16_field(Address address, u32 field) = 0; + virtual u32 read32_field(Address address, u32 field) = 0; + + virtual void write8_field(Address address, u32 field, u8 value) = 0; + virtual void write16_field(Address address, u32 field, u16 value) = 0; + virtual void write32_field(Address address, u32 field, u32 value) = 0; +}; diff --git a/Kernel/PCI/Definitions.h b/Kernel/PCI/Definitions.h new file mode 100644 index 0000000000..e2954b05ef --- /dev/null +++ b/Kernel/PCI/Definitions.h @@ -0,0 +1,113 @@ +#pragma once + +#include <AK/Function.h> +#include <AK/Types.h> + +#define PCI_VENDOR_ID 0x00 // word +#define PCI_DEVICE_ID 0x02 // word +#define PCI_COMMAND 0x04 // word +#define PCI_STATUS 0x06 // word +#define PCI_REVISION_ID 0x08 // byte +#define PCI_PROG_IF 0x09 // byte +#define PCI_SUBCLASS 0x0a // byte +#define PCI_CLASS 0x0b // byte +#define PCI_CACHE_LINE_SIZE 0x0c // byte +#define PCI_LATENCY_TIMER 0x0d // byte +#define PCI_HEADER_TYPE 0x0e // byte +#define PCI_BIST 0x0f // byte +#define PCI_BAR0 0x10 // u32 +#define PCI_BAR1 0x14 // u32 +#define PCI_BAR2 0x18 // u32 +#define PCI_BAR3 0x1C // u32 +#define PCI_BAR4 0x20 // u32 +#define PCI_BAR5 0x24 // u32 +#define PCI_SUBSYSTEM_ID 0x2C // u16 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2E // u16 +#define PCI_INTERRUPT_LINE 0x3C // byte +#define PCI_SECONDARY_BUS 0x19 // byte +#define PCI_HEADER_TYPE_DEVICE 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_TYPE_BRIDGE 0x0604 +#define PCI_ADDRESS_PORT 0xCF8 +#define PCI_VALUE_PORT 0xCFC +#define PCI_NONE 0xFFFF +#define PCI_MAX_DEVICES_PER_BUS 32 +#define PCI_MAX_BUSES 256 +#define PCI_MAX_FUNCTIONS_PER_DEVICE 8 + +//#define PCI_DEBUG 1 + +namespace PCI { +struct ID { + u16 vendor_id { 0 }; + u16 device_id { 0 }; + + bool is_null() const { return !vendor_id && !device_id; } + + bool operator==(const ID& other) const + { + return vendor_id == other.vendor_id && device_id == other.device_id; + } +}; + +struct Address { + Address() {} + Address(u16 seg) + : m_seg(seg) + , m_bus(0) + , m_slot(0) + , m_function(0) + { + } + Address(u16 seg, u8 bus, u8 slot, u8 function) + : m_seg(seg) + , m_bus(bus) + , m_slot(slot) + , m_function(function) + { + } + + bool is_null() const { return !m_bus && !m_slot && !m_function; } + operator bool() const { return !is_null(); } + + u16 seg() const { return m_seg; } + u8 bus() const { return m_bus; } + u8 slot() const { return m_slot; } + u8 function() const { return m_function; } + + u32 io_address_for_field(u8 field) const + { + return 0x80000000u | (m_bus << 16u) | (m_slot << 11u) | (m_function << 8u) | (field & 0xfc); + } + +private: + u32 m_seg { 0 }; + u8 m_bus { 0 }; + u8 m_slot { 0 }; + u8 m_function { 0 }; +}; + +void enumerate_all(Function<void(Address, ID)> callback); +u8 get_interrupt_line(Address); +u32 get_BAR0(Address); +u32 get_BAR1(Address); +u32 get_BAR2(Address); +u32 get_BAR3(Address); +u32 get_BAR4(Address); +u32 get_BAR5(Address); +u8 get_revision_id(Address); +u8 get_subclass(Address); +u8 get_class(Address); +u16 get_subsystem_id(Address); +u16 get_subsystem_vendor_id(Address); +u32 get_BAR_Space_Size(Address, u8); +void enable_bus_mastering(Address); +void disable_bus_mastering(Address); + +class Initializer; +class Access; +class MMIOAccess; +class IOAccess; +class MMIOSegment; + +} diff --git a/Kernel/PCI/IOAccess.cpp b/Kernel/PCI/IOAccess.cpp new file mode 100644 index 0000000000..20c92bfe00 --- /dev/null +++ b/Kernel/PCI/IOAccess.cpp @@ -0,0 +1,63 @@ +#include <Kernel/IO.h> +#include <Kernel/PCI/IOAccess.h> + +void PCI::IOAccess::initialize() +{ + if (!PCI::Access::is_initialized()) + new PCI::IOAccess(); +} + +PCI::IOAccess::IOAccess() +{ + kprintf("PCI: Using IO Mechanism for PCI Configuartion Space Access\n"); +} + +u8 PCI::IOAccess::read8_field(Address address, u32 field) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + return IO::in8(PCI_VALUE_PORT + (field & 3)); +} + +u16 PCI::IOAccess::read16_field(Address address, u32 field) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + return IO::in16(PCI_VALUE_PORT + (field & 2)); +} + +u32 PCI::IOAccess::read32_field(Address address, u32 field) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + return IO::in32(PCI_VALUE_PORT); +} + +void PCI::IOAccess::write8_field(Address address, u32 field, u8 value) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + IO::out8(PCI_VALUE_PORT + (field & 3), value); +} +void PCI::IOAccess::write16_field(Address address, u32 field, u16 value) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + IO::out16(PCI_VALUE_PORT + (field & 2), value); +} +void PCI::IOAccess::write32_field(Address address, u32 field, u32 value) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + IO::out32(PCI_VALUE_PORT, value); +} + +void PCI::IOAccess::enumerate_all(Function<void(Address, ID)>& callback) +{ + // Single PCI host controller. + if ((read8_field(Address(), PCI_HEADER_TYPE) & 0x80) == 0) { + enumerate_bus(-1, 0, callback); + return; + } + + // Multiple PCI host controllers. + for (u8 function = 0; function < 8; ++function) { + if (read16_field(Address(0, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE) + break; + enumerate_bus(-1, function, callback); + } +} diff --git a/Kernel/PCI/IOAccess.h b/Kernel/PCI/IOAccess.h new file mode 100644 index 0000000000..d132a6fe40 --- /dev/null +++ b/Kernel/PCI/IOAccess.h @@ -0,0 +1,25 @@ +#pragma once +#include <Kernel/PCI/Access.h> + +class PCI::IOAccess final : public PCI::Access { +public: + static void initialize(); + virtual void enumerate_all(Function<void(Address, ID)>&) override final; + + virtual String get_access_type() override final { return "IO-Access"; }; + +protected: + IOAccess(); + +private: + virtual u8 read8_field(Address address, u32) override final; + virtual u16 read16_field(Address address, u32) override final; + virtual u32 read32_field(Address address, u32) override final; + virtual void write8_field(Address address, u32, u8) override final; + virtual void write16_field(Address address, u32, u16) override final; + virtual void write32_field(Address address, u32, u32) override final; + + virtual uint32_t get_segments_count() { return 1; }; + virtual uint8_t get_segment_start_bus(u32) { return 0x0; }; + virtual uint8_t get_segment_end_bus(u32) { return 0xFF; }; +};
\ No newline at end of file diff --git a/Kernel/PCI/Initializer.cpp b/Kernel/PCI/Initializer.cpp new file mode 100644 index 0000000000..40020f64cb --- /dev/null +++ b/Kernel/PCI/Initializer.cpp @@ -0,0 +1,126 @@ +#include <Kernel/ACPI/ACPIParser.h> +#include <Kernel/ACPI/DMIDecoder.h> +#include <Kernel/IO.h> +#include <Kernel/KParams.h> +#include <Kernel/PCI/IOAccess.h> +#include <Kernel/PCI/Initializer.h> +#include <Kernel/PCI/MMIOAccess.h> + +static PCI::Initializer* s_pci_initializer; + +PCI::Initializer& PCI::Initializer::the() +{ + if (s_pci_initializer == nullptr) { + s_pci_initializer = new PCI::Initializer(); + } + return *s_pci_initializer; +} +void PCI::Initializer::initialize_pci_mmio_access(ACPI_RAW::MCFG& mcfg) +{ + PCI::MMIOAccess::initialize(mcfg); +} +void PCI::Initializer::initialize_pci_io_access() +{ + PCI::IOAccess::initialize(); +} +void PCI::Initializer::test_and_initialize(bool disable_pci_mmio, bool pci_force_probing) +{ + if (disable_pci_mmio) { + if (test_pci_io(pci_force_probing)) { + initialize_pci_io_access(); + } else { + kprintf("No PCI Bus Access Method Detected, Halt!\n"); + ASSERT_NOT_REACHED(); // NO PCI Access ?! + } + return; + } + if (test_acpi()) { + if (test_pci_mmio()) { + initialize_pci_mmio_access_after_test(); + } else { + if (test_pci_io(pci_force_probing)) { + initialize_pci_io_access(); + } else { + kprintf("No PCI Bus Access Method Detected, Halt!\n"); + ASSERT_NOT_REACHED(); // NO PCI Access ?! + } + } + } else { + if (test_pci_io(pci_force_probing)) { + initialize_pci_io_access(); + } else { + kprintf("No PCI Bus Access Method Detected, Halt!\n"); + ASSERT_NOT_REACHED(); // NO PCI Access ?! + } + } +} +PCI::Initializer::Initializer() +{ +} +bool PCI::Initializer::test_acpi() +{ + if ((KParams::the().has("noacpi")) || !ACPIParser::the().is_operable()) + return false; + else + return true; +} + +bool PCI::Initializer::test_pci_io(bool pci_force_probing) +{ + kprintf("Testing PCI via SMBIOS...\n"); + + if (!pci_force_probing) { + if (DMIDecoder::the().is_reliable()) { + if ((DMIDecoder::the().get_bios_characteristics() & (1 << 3)) != 0) { + kprintf("DMIDecoder: Warning, BIOS characteristics are not supported, skipping\n"); + } else if ((DMIDecoder::the().get_bios_characteristics() & (u32)SMBIOS::BIOSCharacteristics::PCI_support) != 0) { + kprintf("PCI *should* be supported according to SMBIOS...\n"); + return true; + } else { + kprintf("SMBIOS does not list PCI as supported, Falling back to manual probing!\n"); + } + } else { + kprintf("DMI is classified as unreliable, ignore it!\n"); + } + } else { + kprintf("Requested to force PCI probing...\n"); + } + + kprintf("Testing PCI via manual probing... "); + + u32 tmp = 0x80000000; + IO::out32(PCI_ADDRESS_PORT, tmp); + tmp = IO::in32(PCI_ADDRESS_PORT); + if (tmp == 0x80000000) { + kprintf("PCI IO Supported!\n"); + return true; + } + + kprintf("PCI IO Not Supported!\n"); + return false; +} + +bool PCI::Initializer::test_pci_mmio() +{ + if (ACPIParser::the().find_table("MCFG") != nullptr) + return true; + else + return false; +} + +void PCI::Initializer::initialize_pci_mmio_access_after_test() +{ + initialize_pci_mmio_access(*(ACPI_RAW::MCFG*)(ACPIParser::the().find_table("MCFG"))); +} + +void PCI::Initializer::dismiss() +{ + if (s_pci_initializer == nullptr) + return; + kprintf("PCI Subsystem Initializer dismissed.\n"); + s_pci_initializer->~Initializer(); +} + +PCI::Initializer::~Initializer() +{ +}
\ No newline at end of file diff --git a/Kernel/PCI/Initializer.h b/Kernel/PCI/Initializer.h new file mode 100644 index 0000000000..875ba2afa6 --- /dev/null +++ b/Kernel/PCI/Initializer.h @@ -0,0 +1,22 @@ +#pragma once + +#include <AK/Types.h> +#include <Kernel/ACPI/Definitions.h> +#include <Kernel/PCI/Definitions.h> + +class PCI::Initializer { +public: + static PCI::Initializer& the(); + void initialize_pci_mmio_access(ACPI_RAW::MCFG& mcfg); + void initialize_pci_io_access(); + void test_and_initialize(bool disable_pci_mmio, bool pci_force_probing); + static void dismiss(); + +private: + ~Initializer(); + Initializer(); + bool test_acpi(); + bool test_pci_io(bool pci_force_probing); + bool test_pci_mmio(); + void initialize_pci_mmio_access_after_test(); +};
\ No newline at end of file diff --git a/Kernel/PCI/MMIOAccess.cpp b/Kernel/PCI/MMIOAccess.cpp new file mode 100644 index 0000000000..3b148ae979 --- /dev/null +++ b/Kernel/PCI/MMIOAccess.cpp @@ -0,0 +1,215 @@ +#include <AK/Optional.h> +#include <Kernel/IO.h> +#include <Kernel/PCI/MMIOAccess.h> +#include <Kernel/VM/MemoryManager.h> + +#define PCI_MMIO_CONFIG_SPACE_SIZE 4096 + +uint32_t PCI::MMIOAccess::get_segments_count() +{ + return m_segments.size(); +} +uint8_t PCI::MMIOAccess::get_segment_start_bus(u32 seg) +{ + ASSERT(m_segments.contains(seg)); + return m_segments.get(seg).value()->get_start_bus(); +} +uint8_t PCI::MMIOAccess::get_segment_end_bus(u32 seg) +{ + ASSERT(m_segments.contains(seg)); + return m_segments.get(seg).value()->get_end_bus(); +} + +void PCI::MMIOAccess::initialize(ACPI_RAW::MCFG& mcfg) +{ + if (!PCI::Access::is_initialized()) + new PCI::MMIOAccess(mcfg); +} + +PCI::MMIOAccess::MMIOAccess(ACPI_RAW::MCFG& raw_mcfg) + : m_mcfg(raw_mcfg) + , m_segments(*new HashMap<u16, MMIOSegment*>()) +{ + kprintf("PCI: Using MMIO Mechanism for PCI Configuartion Space Access\n"); + m_mmio_segment = MM.allocate_kernel_region(PAGE_ROUND_UP(PCI_MMIO_CONFIG_SPACE_SIZE), "PCI MMIO", Region::Access::Read | Region::Access::Write); + + OwnPtr<Region> checkup_region = MM.allocate_kernel_region((PAGE_SIZE * 2), "PCI MCFG Checkup", Region::Access::Read | Region::Access::Write); +#ifdef PCI_DEBUG + dbgprintf("PCI: Checking MCFG Table length to choose the correct mapping size\n"); +#endif + mmap_region(*checkup_region, PhysicalAddress((u32)&raw_mcfg & 0xfffff000)); + ACPI_RAW::SDTHeader* sdt = (ACPI_RAW::SDTHeader*)(checkup_region->vaddr().get() + ((u32)&raw_mcfg & 0xfff)); + u32 length = sdt->length; + u8 revision = sdt->revision; + + kprintf("PCI: MCFG, length - %u, revision %d\n", length, revision); + checkup_region->unmap(); + + auto mcfg_region = MM.allocate_kernel_region(PAGE_ROUND_UP(length) + PAGE_SIZE, "PCI Parsing MCFG", Region::Access::Read | Region::Access::Write); + mmap_region(*mcfg_region, PhysicalAddress((u32)&raw_mcfg & 0xfffff000)); + + ACPI_RAW::MCFG& mcfg = *((ACPI_RAW::MCFG*)(mcfg_region->vaddr().get() + ((u32)&raw_mcfg & 0xfff))); +#ifdef PCI_DEBUG + dbgprintf("PCI: Checking MCFG @ V 0x%x, P 0x%x\n", &mcfg, &raw_mcfg); +#endif + + for (u32 index = 0; index < ((mcfg.header.length - sizeof(ACPI_RAW::MCFG)) / sizeof(ACPI_RAW::PCI_MMIO_Descriptor)); index++) { + u8 start_bus = mcfg.descriptors[index].start_pci_bus; + u8 end_bus = mcfg.descriptors[index].end_pci_bus; + u32 lower_addr = mcfg.descriptors[index].base_addr; + + m_segments.set(index, new PCI::MMIOSegment(PhysicalAddress(lower_addr), start_bus, end_bus)); + kprintf("PCI: New PCI segment @ P 0x%x, PCI buses (%d-%d)\n", lower_addr, start_bus, end_bus); + } + mcfg_region->unmap(); + kprintf("PCI: MMIO segments - %d\n", m_segments.size()); + map_device(Address(0, 0, 0, 0)); +} + +void PCI::MMIOAccess::map_device(Address address) +{ + // FIXME: Map and put some lock! +#ifdef PCI_DEBUG + dbgprintf("PCI: Mapping Device @ pci (%d:%d:%d:%d)\n", address.seg(), address.bus(), address.slot(), address.function()); +#endif + ASSERT(m_segments.contains(address.seg())); + auto segment = m_segments.get(address.seg()); + PhysicalAddress segment_lower_addr = segment.value()->get_paddr(); + PhysicalAddress device_physical_mmio_space = segment_lower_addr.offset( + PCI_MMIO_CONFIG_SPACE_SIZE * address.function() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE) * address.slot() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS) * (address.bus() - segment.value()->get_start_bus())); +#ifdef PCI_DEBUG + dbgprintf("PCI: Mapping (%d:%d:%d:%d), V 0x%x, P 0x%x\n", address.seg(), address.bus(), address.slot(), address.function(), m_mmio_segment->vaddr().get(), device_physical_mmio_space.get()); +#endif + MM.map_for_kernel(m_mmio_segment->vaddr(), device_physical_mmio_space, false); +} + +u8 PCI::MMIOAccess::read8_field(Address address, u32 field) +{ + ASSERT(field <= 0xfff); +#ifdef PCI_DEBUG + dbgprintf("PCI: Reading field %u, Address(%u:%u:%u:%u)\n", field, address.seg(), address.bus(), address.slot(), address.function()); +#endif + map_device(address); + return *((u8*)(m_mmio_segment->vaddr().get() + (field & 0xfff))); +} + +u16 PCI::MMIOAccess::read16_field(Address address, u32 field) +{ + ASSERT(field < 0xfff); +#ifdef PCI_DEBUG + dbgprintf("PCI: Reading field %u, Address(%u:%u:%u:%u)\n", field, address.seg(), address.bus(), address.slot(), address.function()); +#endif + map_device(address); + return *((u16*)(m_mmio_segment->vaddr().get() + (field & 0xfff))); +} + +u32 PCI::MMIOAccess::read32_field(Address address, u32 field) +{ + ASSERT(field <= 0xffc); +#ifdef PCI_DEBUG + dbgprintf("PCI: Reading field %u, Address(%u:%u:%u:%u)\n", field, address.seg(), address.bus(), address.slot(), address.function()); +#endif + map_device(address); + return *((u32*)(m_mmio_segment->vaddr().get() + (field & 0xfff))); +} + +void PCI::MMIOAccess::write8_field(Address address, u32 field, u8 value) +{ + ASSERT(field <= 0xfff); +#ifdef PCI_DEBUG + dbgprintf("PCI: Write to field %u, Address(%u:%u:%u:%u), value 0x%x\n", field, address.seg(), address.bus(), address.slot(), address.function(), value); +#endif + map_device(address); + *((u8*)(m_mmio_segment->vaddr().get() + (field & 0xfff))) = value; +} +void PCI::MMIOAccess::write16_field(Address address, u32 field, u16 value) +{ + ASSERT(field < 0xfff); +#ifdef PCI_DEBUG + dbgprintf("PCI: Write to field %u, Address(%u:%u:%u:%u), value 0x%x\n", field, address.seg(), address.bus(), address.slot(), address.function(), value); +#endif + map_device(address); + *((u16*)(m_mmio_segment->vaddr().get() + (field & 0xfff))) = value; +} +void PCI::MMIOAccess::write32_field(Address address, u32 field, u32 value) +{ + ASSERT(field <= 0xffc); +#ifdef PCI_DEBUG + dbgprintf("PCI: Write to field %u, Address(%u:%u:%u:%u), value 0x%x\n", field, address.seg(), address.bus(), address.slot(), address.function(), value); +#endif + map_device(address); + *((u32*)(m_mmio_segment->vaddr().get() + (field & 0xfff))) = value; +} + +void PCI::MMIOAccess::enumerate_all(Function<void(Address, ID)>& callback) +{ + for (u16 seg = 0; seg < m_segments.size(); seg++) { +#ifdef PCI_DEBUG + dbgprintf("PCI: Enumerating Memory mapped IO segment %u\n", seg); +#endif + // Single PCI host controller. + if ((read8_field(Address(seg), PCI_HEADER_TYPE) & 0x80) == 0) { + enumerate_bus(-1, 0, callback); + return; + } + + // Multiple PCI host controllers. + for (u8 function = 0; function < 8; ++function) { + if (read16_field(Address(seg, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE) + break; + enumerate_bus(-1, function, callback); + } + } +} + +void PCI::MMIOAccess::mmap(VirtualAddress vaddr, PhysicalAddress paddr, u32 length) +{ + unsigned i = 0; + while (length >= PAGE_SIZE) { + MM.map_for_kernel(VirtualAddress(vaddr.offset(i * PAGE_SIZE).get()), PhysicalAddress(paddr.offset(i * PAGE_SIZE).get())); +#ifdef ACPI_DEBUG + dbgprintf("PCI: map - V 0x%x -> P 0x%x\n", vaddr.offset(i * PAGE_SIZE).get(), paddr.offset(i * PAGE_SIZE).get()); +#endif + length -= PAGE_SIZE; + i++; + } + if (length > 0) { + MM.map_for_kernel(vaddr.offset(i * PAGE_SIZE), paddr.offset(i * PAGE_SIZE), true); + } +#ifdef ACPI_DEBUG + dbgprintf("PCI: Finished mapping\n"); +#endif +} + +void PCI::MMIOAccess::mmap_region(Region& region, PhysicalAddress paddr) +{ +#ifdef PCI_DEBUG + dbgprintf("PCI: Mapping region, size - %u\n", region.size()); +#endif + mmap(region.vaddr(), paddr, region.size()); +} + +PCI::MMIOSegment::MMIOSegment(PhysicalAddress segment_base_addr, u8 start_bus, u8 end_bus) + : m_base_addr(segment_base_addr) + , m_start_bus(start_bus) + , m_end_bus(end_bus) +{ +} +u8 PCI::MMIOSegment::get_start_bus() +{ + return m_start_bus; +} +u8 PCI::MMIOSegment::get_end_bus() +{ + return m_end_bus; +} + +size_t PCI::MMIOSegment::get_size() +{ + return (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS * (get_end_bus() - get_start_bus())); +} + +PhysicalAddress PCI::MMIOSegment::get_paddr() +{ + return m_base_addr; +} diff --git a/Kernel/PCI/MMIOAccess.h b/Kernel/PCI/MMIOAccess.h new file mode 100644 index 0000000000..de48563383 --- /dev/null +++ b/Kernel/PCI/MMIOAccess.h @@ -0,0 +1,53 @@ +#pragma once +#include <AK/HashMap.h> +#include <AK/OwnPtr.h> +#include <AK/Types.h> +#include <Kernel/ACPI/Definitions.h> +#include <Kernel/PCI/Access.h> +#include <Kernel/VM/PhysicalRegion.h> +#include <Kernel/VM/Region.h> + +class PCI::MMIOAccess final : public PCI::Access { +public: + static void initialize(ACPI_RAW::MCFG&); + virtual void enumerate_all(Function<void(Address, ID)>&) override final; + + virtual String get_access_type() override final { return "MMIO-Access"; }; + +protected: + MMIOAccess(ACPI_RAW::MCFG&); + +private: + virtual u8 read8_field(Address address, u32) override final; + virtual u16 read16_field(Address address, u32) override final; + virtual u32 read32_field(Address address, u32) override final; + virtual void write8_field(Address address, u32, u8) override final; + virtual void write16_field(Address address, u32, u16) override final; + virtual void write32_field(Address address, u32, u32) override final; + + void map_device(Address address); + void mmap(VirtualAddress preferred_vaddr, PhysicalAddress paddr, u32); + void mmap_region(Region& region, PhysicalAddress paddr); + + virtual u32 get_segments_count(); + virtual u8 get_segment_start_bus(u32); + virtual u8 get_segment_end_bus(u32); + + ACPI_RAW::MCFG& m_mcfg; + HashMap<u16, MMIOSegment*>& m_segments; + OwnPtr<Region> m_mmio_segment; +}; + +class PCI::MMIOSegment { +public: + MMIOSegment(PhysicalAddress, u8, u8); + u8 get_start_bus(); + u8 get_end_bus(); + size_t get_size(); + PhysicalAddress get_paddr(); + +private: + PhysicalAddress m_base_addr; + u8 m_start_bus; + u8 m_end_bus; +};
\ No newline at end of file diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 01ce09a943..24f1fd0ea8 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -5,6 +5,9 @@ #include "Scheduler.h" #include "kstdio.h" #include <AK/Types.h> +#include <Kernel/ACPI/ACPIDynamicParser.h> +#include <Kernel/ACPI/ACPIStaticParser.h> +#include <Kernel/ACPI/DMIDecoder.h> #include <Kernel/Arch/i386/APIC.h> #include <Kernel/Arch/i386/CPU.h> #include <Kernel/Arch/i386/PIC.h> @@ -39,7 +42,8 @@ #include <Kernel/Net/LoopbackAdapter.h> #include <Kernel/Net/NetworkTask.h> #include <Kernel/Net/RTL8139NetworkAdapter.h> -#include <Kernel/PCI.h> +#include <Kernel/PCI/Access.h> +#include <Kernel/PCI/Initializer.h> #include <Kernel/TTY/PTYMultiplexer.h> #include <Kernel/TTY/VirtualConsole.h> #include <Kernel/VM/MemoryManager.h> @@ -245,6 +249,35 @@ extern "C" [[noreturn]] void init(u32 physical_address_for_kernel_page_tables) new KParams(String(reinterpret_cast<const char*>(multiboot_info_ptr->cmdline))); bool text_debug = KParams::the().has("text_debug"); + bool complete_acpi_disable = KParams::the().has("noacpi"); + bool dynamic_acpi_disable = KParams::the().has("noacpi_aml"); + bool pci_mmio_disable = KParams::the().has("nopci_mmio"); + bool pci_force_probing = KParams::the().has("pci_nodmi"); + bool dmi_unreliable = KParams::the().has("dmi_unreliable"); + + MemoryManager::initialize(physical_address_for_kernel_page_tables); + + if (dmi_unreliable) { + DMIDecoder::initialize_untrusted(); + } else { + DMIDecoder::initialize(); + } + + if (complete_acpi_disable) { + ACPIParser::initialize_limited(); + } else { + if (!dynamic_acpi_disable) { + ACPIDynamicParser::initialize_without_rsdp(); + } else { + ACPIStaticParser::initialize_without_rsdp(); + } + } + + // Sample test to see if the ACPI parser is working... + kprintf("ACPI: HPET table @ P 0x%x\n", ACPIParser::the().find_table("HPET")); + + PCI::Initializer::the().test_and_initialize(pci_mmio_disable, pci_force_probing); + PCI::Initializer::the().dismiss(); vfs = new VFS; dev_debuglog = new DebugLogDevice; @@ -298,15 +331,14 @@ extern "C" [[noreturn]] void init(u32 physical_address_for_kernel_page_tables) tty1 = new VirtualConsole(1); VirtualConsole::switch_to(0); - MemoryManager::initialize(physical_address_for_kernel_page_tables); - if (APIC::init()) APIC::enable(0); PIT::initialize(); PCI::enumerate_all([](const PCI::Address& address, PCI::ID id) { - kprintf("PCI device: bus=%d slot=%d function=%d id=%w:%w\n", + kprintf("PCI: device @ %w:%b:%b.%d [%w:%w]\n", + address.seg(), address.bus(), address.slot(), address.function(), |