kernel: Start scanning the PCI bus for devices
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2023-01-23 20:07:17 +01:00
parent de04c094e7
commit 6677c643a5
Signed by: apio
GPG Key ID: B8A7D06E42258954
5 changed files with 214 additions and 0 deletions

View File

@ -1,6 +1,9 @@
# The Moon kernel for Luna.
file(GLOB_RECURSE HEADERS src/*.h)
set(SOURCES
${HEADERS}
src/main.cpp
src/Log.cpp
src/cxxabi.cpp
@ -14,6 +17,7 @@ set(SOURCES
src/boot/Init.cpp
src/arch/Serial.cpp
src/arch/Timer.cpp
src/arch/PCI.cpp
src/thread/Spinlock.cpp
src/thread/Thread.cpp
src/thread/Scheduler.cpp
@ -36,6 +40,7 @@ if("${LUNA_ARCH}" MATCHES "x86_64")
src/arch/x86_64/CPU.cpp
src/arch/x86_64/Timer.cpp
src/arch/x86_64/Thread.cpp
src/arch/x86_64/PCI.cpp
src/arch/x86_64/init/GDT.cpp
src/arch/x86_64/init/IDT.cpp
src/arch/x86_64/init/PIC.cpp

99
kernel/src/arch/PCI.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "arch/PCI.h"
#include "Log.h"
namespace PCI
{
Device::ID read_id(const Device::Address& address)
{
u16 vendor = read16(address, Field::VendorID);
u16 device = read16(address, Field::DeviceID);
return { vendor, device };
}
Device::Type read_type(const Device::Address& address)
{
u8 klass = read8(address, Field::Class);
u8 subclass = read8(address, Field::Subclass);
u8 prog_if = read8(address, Field::ProgIF);
return { klass, subclass, prog_if };
}
constexpr u8 PCI_BRIDGE_CLASS = 0x06;
constexpr u8 PCI_TO_PCI_BRIDGE_SUBCLASS = 0x04;
static bool is_pci_to_pci_bridge(const Device::Address& address)
{
return read8(address, Field::Class) == PCI_BRIDGE_CLASS &&
read8(address, Field::Subclass) == PCI_TO_PCI_BRIDGE_SUBCLASS;
}
void scan_bus(u32);
void scan_function(const Device::Address& address)
{
Device::ID id = read_id(address);
Device::Type type = read_type(address);
kinfoln("Found PCI device %.4x:%.4x (Class %.2x, Subclass %.2x, ProgIF %.2x) at PCI address %u:%u:%u",
id.vendor, id.device, type.klass, type.subclass, type.prog_if, address.bus, address.slot,
address.function);
if (is_pci_to_pci_bridge(address))
{
kdbgln("PCI-to-PCI bridge detected, performing another bus scan");
scan_bus(read8(address, Field::SecondaryBus));
}
}
void scan_slot(u32 bus, u32 slot)
{
Device::Address address { bus, slot, 0 };
// No device on this slot!
if (read16(address, Field::VendorID) == PCI::INVALID_ID) return;
scan_function(address);
u8 header_type = read8(address, Field::HeaderType);
// Multiple-function PCI device
if ((header_type & 0x80) == 0x80)
{
for (u32 function = 1; function < 8; function++)
{
address.function = function;
if (read16(address, Field::VendorID) == PCI::INVALID_ID) continue;
scan_function(address);
}
}
}
void scan_bus(u32 bus)
{
for (u32 slot = 0; slot < 32; slot++) { scan_slot(bus, slot); }
}
void scan()
{
u8 header_type = read8({ 0, 0, 0 }, Field::HeaderType);
// Single-function PCI bus
if ((header_type & 0x80) == 0)
{
kdbgln("PCI bus is single-function");
scan_bus(0);
}
else
{
kdbgln("PCI bus is multiple-function");
for (u32 function = 0; function < 8; function++)
{
if (read16({ 0, 0, function }, Field::VendorID) != PCI::INVALID_ID)
{
kdbgln("PCI bus has function %u", function);
scan_bus(function);
}
}
}
}
}

60
kernel/src/arch/PCI.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <luna/Types.h>
namespace PCI
{
enum Field : u32
{
VendorID = 0x00,
DeviceID = 0x02,
Command = 0x04,
Status = 0x06,
RevisionID = 0x08,
ProgIF = 0x09,
Subclass = 0x0a,
Class = 0x0b,
HeaderType = 0x0e,
SecondaryBus = 0x19
};
namespace Device
{
struct ID
{
u16 vendor;
u16 device;
};
struct Type
{
u8 klass;
u8 subclass;
u8 prog_if;
};
struct Address
{
u32 bus;
u32 slot;
u32 function;
};
}
constexpr u16 INVALID_ID = 0xFFFF;
// Architecture-dependent.
u8 read8(const Device::Address& address, u32 field);
u16 read16(const Device::Address& address, u32 field);
u32 read32(const Device::Address& address, u32 field);
void write8(const Device::Address& address, u32 field, u8 value);
void write16(const Device::Address& address, u32 field, u16 value);
void write32(const Device::Address& address, u32 field, u32 value);
Device::ID read_id(const Device::Address& address);
Device::Type read_type(const Device::Address& address);
void scan();
}

View File

@ -0,0 +1,47 @@
#include "arch/PCI.h"
#include "arch/x86_64/IO.h"
#include <luna/Check.h>
#define PCI_ADDRESS_PORT 0xCF8
#define PCI_VALUE_PORT 0xCFC
static inline constexpr u32 make_pci_address(const PCI::Device::Address& address, u32 field)
{
return 0x80000000 | (address.bus << 16) | (address.slot << 11) | (address.function << 8) | ((field)&0xFC);
}
namespace PCI
{
u8 read8(const Device::Address& address, u32 field)
{
IO::outl(PCI_ADDRESS_PORT, make_pci_address(address, field));
return IO::inb(PCI_VALUE_PORT + (field & 3));
}
u16 read16(const Device::Address& address, u32 field)
{
IO::outl(PCI_ADDRESS_PORT, make_pci_address(address, field));
return IO::inw(PCI_VALUE_PORT + (field & 2));
}
u32 read32(const Device::Address& address, u32 field)
{
IO::outl(PCI_ADDRESS_PORT, make_pci_address(address, field));
return IO::inl(PCI_VALUE_PORT);
}
void write8(const Device::Address&, u32, u8)
{
todo();
}
void write16(const Device::Address&, u32, u16)
{
todo();
}
void write32(const Device::Address&, u32, u32)
{
todo();
}
}

View File

@ -3,6 +3,7 @@
#include "Log.h"
#include "arch/CPU.h"
#include "arch/MMU.h"
#include "arch/PCI.h"
#include "arch/Timer.h"
#include "boot/Init.h"
#include "config.h"
@ -69,6 +70,8 @@ Result<void> init()
TRY(Scheduler::new_kernel_thread(heap_thread));
TRY(Scheduler::new_kernel_thread(reap_thread));
PCI::scan();
CPU::platform_finish_init();
// Disable console logging before transferring control to userspace.