kernel: Start scanning the PCI bus for devices
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
de04c094e7
commit
6677c643a5
@ -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
99
kernel/src/arch/PCI.cpp
Normal 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
60
kernel/src/arch/PCI.h
Normal 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();
|
||||
}
|
47
kernel/src/arch/x86_64/PCI.cpp
Normal file
47
kernel/src/arch/x86_64/PCI.cpp
Normal 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();
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user