[init] Move PCIe probing to srv.init
This was kept in the kernel as a way to keep exercising the code, but it doesn't belong there. This moves it to init, which doesn't do anything but probe for devices currently - but at least it's executing the code in userspace now.
This commit is contained in:
104
src/user/srv.init/acpi.cpp
Normal file
104
src/user/srv.init/acpi.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <arch/acpi/tables.h>
|
||||
#include <bootproto/acpi.h>
|
||||
#include <bootproto/init.h>
|
||||
#include <j6/syslog.hh>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "loader.h"
|
||||
#include "pci.h"
|
||||
|
||||
inline constexpr size_t bus_mmio_size = 0x1000'0000;
|
||||
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
|
||||
|
||||
void
|
||||
probe_pci(j6_handle_t sys, pci_group &pci)
|
||||
{
|
||||
j6::syslog("Probing PCI group at base %016lx", pci.base);
|
||||
map_phys(sys, pci.base, bus_mmio_size, mmio_flags);
|
||||
|
||||
for (unsigned b = pci.bus_start; b <= pci.bus_end; ++b) {
|
||||
uint8_t bus = static_cast<uint8_t>(b);
|
||||
|
||||
for (uint8_t dev = 0; dev < 32; ++dev) {
|
||||
if (!pci.has_device(bus, dev, 0)) continue;
|
||||
|
||||
pci_device d0 {pci, bus, dev, 0};
|
||||
if (!d0.multi()) continue;
|
||||
|
||||
for (uint8_t i = 1; i < 8; ++i) {
|
||||
if (pci.has_device(bus, dev, i))
|
||||
pci_device dn {pci, bus, dev, i};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
load_mcfg(j6_handle_t sys, const acpi::table_header *header)
|
||||
{
|
||||
const auto *mcfg = acpi::check_get_table<acpi::mcfg>(header);
|
||||
|
||||
size_t count = acpi::table_entries(mcfg, sizeof(acpi::mcfg_entry));
|
||||
|
||||
/*
|
||||
m_pci.set_size(count);
|
||||
m_devices.set_capacity(16);
|
||||
*/
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
const acpi::mcfg_entry &mcfge = mcfg->entries[i];
|
||||
|
||||
pci_group group = {
|
||||
.group = mcfge.group,
|
||||
.bus_start = mcfge.bus_start,
|
||||
.bus_end = mcfge.bus_end,
|
||||
.base = reinterpret_cast<uint32_t*>(mcfge.base),
|
||||
};
|
||||
|
||||
probe_pci(sys, group);
|
||||
|
||||
j6::syslog(" Found MCFG entry: base %lx group %d bus %d-%d",
|
||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||
}
|
||||
|
||||
//probe_pci();
|
||||
}
|
||||
|
||||
void
|
||||
load_acpi(j6_handle_t sys, const bootproto::module *mod)
|
||||
{
|
||||
const bootproto::acpi *info = mod->data<bootproto::acpi>();
|
||||
const util::const_buffer ®ion = info->region;
|
||||
|
||||
map_phys(sys, region.pointer, region.count);
|
||||
|
||||
const void *root_table = info->root;
|
||||
if (!root_table) {
|
||||
j6::syslog("null ACPI root table pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
const acpi::rsdp2 *acpi2 =
|
||||
reinterpret_cast<const acpi::rsdp2 *>(root_table);
|
||||
|
||||
const auto *xsdt =
|
||||
acpi::check_get_table<acpi::xsdt>(acpi2->xsdt_address);
|
||||
|
||||
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi::table_header *header = xsdt->headers[i];
|
||||
if (!header->validate()) {
|
||||
j6::syslog("ACPI table at %lx failed validation", header);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (header->type) {
|
||||
case acpi::mcfg::type_id:
|
||||
load_mcfg(sys, header);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/user/srv.init/acpi.h
Normal file
12
src/user/srv.init/acpi.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
/// \file acpi.h
|
||||
/// Routines for loading and parsing ACPI tables
|
||||
|
||||
#include <util/counted.h>
|
||||
#include <j6/types.h>
|
||||
|
||||
namespace bootproto {
|
||||
struct module;
|
||||
}
|
||||
|
||||
void load_acpi(j6_handle_t sys, const bootproto::module *mod);
|
||||
@@ -6,10 +6,12 @@ init = module("srv.init",
|
||||
description = "Init server",
|
||||
ld_script = "init.ld",
|
||||
sources = [
|
||||
"acpi.cpp",
|
||||
"j6romfs.cpp",
|
||||
"loader.cpp",
|
||||
"main.cpp",
|
||||
"modules.cpp",
|
||||
"pci.cpp",
|
||||
"service_locator.cpp",
|
||||
"start.s",
|
||||
])
|
||||
|
||||
@@ -19,17 +19,14 @@ constexpr size_t stack_size = 0x10000;
|
||||
constexpr uintptr_t stack_top = 0x80000000000;
|
||||
|
||||
j6_handle_t
|
||||
map_phys(j6_handle_t sys, uintptr_t phys, size_t len, uintptr_t addr)
|
||||
map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags)
|
||||
{
|
||||
j6_handle_t vma = j6_handle_invalid;
|
||||
j6_status_t res = j6_system_map_phys(sys, &vma, phys, len, 0);
|
||||
j6_status_t res = j6_system_map_phys(sys, &vma, phys, len, flags);
|
||||
if (res != j6_status_ok)
|
||||
return j6_handle_invalid;
|
||||
|
||||
if (!addr)
|
||||
addr = phys;
|
||||
|
||||
res = j6_vma_map(vma, 0, addr);
|
||||
res = j6_vma_map(vma, 0, phys);
|
||||
if (res != j6_status_ok)
|
||||
return j6_handle_invalid;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/// Routines for loading and starting other programs
|
||||
|
||||
#include <j6/types.h>
|
||||
#include <j6/flags.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace bootproto {
|
||||
@@ -15,8 +16,8 @@ bool load_program(
|
||||
j6_handle_t sys, j6_handle_t slp,
|
||||
const bootproto::module *arg = nullptr);
|
||||
|
||||
j6_handle_t map_phys(j6_handle_t sys, uintptr_t phys, size_t len, uintptr_t addr = 0);
|
||||
j6_handle_t map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags = j6_vm_flag_none);
|
||||
|
||||
inline j6_handle_t map_phys(j6_handle_t sys, const void *phys, size_t len, uintptr_t addr = 0) {
|
||||
return map_phys(sys, reinterpret_cast<uintptr_t>(phys), len, addr);
|
||||
inline j6_handle_t map_phys(j6_handle_t sys, const void *phys, size_t len, j6_vm_flags flags = j6_vm_flag_none) {
|
||||
return map_phys(sys, reinterpret_cast<uintptr_t>(phys), len, flags);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
#include <j6/syslog.hh>
|
||||
#include <j6/types.h>
|
||||
|
||||
#include <bootproto/acpi.h>
|
||||
#include <bootproto/init.h>
|
||||
#include <bootproto/devices/framebuffer.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "j6romfs.h"
|
||||
#include "loader.h"
|
||||
#include "modules.h"
|
||||
@@ -86,6 +88,7 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
||||
load_modules(modules_addr, sys, 0, mods);
|
||||
|
||||
module const *initrd_module = nullptr;
|
||||
module const *acpi_module = nullptr;
|
||||
std::vector<module const*> devices;
|
||||
|
||||
for (auto mod : mods) {
|
||||
@@ -94,6 +97,10 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
||||
initrd_module = mod;
|
||||
break;
|
||||
|
||||
case module_type::acpi:
|
||||
acpi_module = mod;
|
||||
break;
|
||||
|
||||
case module_type::device:
|
||||
devices.push_back(mod);
|
||||
break;
|
||||
@@ -105,7 +112,10 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
||||
}
|
||||
|
||||
if (!initrd_module)
|
||||
return 1;
|
||||
return 2;
|
||||
|
||||
if (!acpi_module)
|
||||
return 3;
|
||||
|
||||
util::const_buffer initrd_buf = *initrd_module->data<util::const_buffer>();
|
||||
|
||||
@@ -120,6 +130,7 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
||||
// have driver_source objects..
|
||||
j6romfs::fs initrd {initrd_buf};
|
||||
|
||||
load_acpi(sys, acpi_module);
|
||||
|
||||
const j6romfs::inode *driver_dir = initrd.lookup_inode("/jsix/drivers");
|
||||
if (!driver_dir) {
|
||||
|
||||
182
src/user/srv.init/pci.cpp
Normal file
182
src/user/srv.init/pci.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <j6/syslog.hh>
|
||||
|
||||
#include "pci.h"
|
||||
|
||||
struct pci_cap_msi
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msi32
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
uint32_t address;
|
||||
uint16_t data;
|
||||
uint16_t reserved;
|
||||
uint32_t mask;
|
||||
uint32_t pending;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msi64
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
uint64_t address;
|
||||
uint16_t data;
|
||||
uint16_t reserved;
|
||||
uint32_t mask;
|
||||
uint32_t pending;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/*
|
||||
void dump_msi(pci_cap_msi *cap)
|
||||
{
|
||||
auto cons = console::get();
|
||||
cons->printf("MSI Cap:\n");
|
||||
cons->printf(" id: %02x\n", cap->id);
|
||||
cons->printf(" next: %02x\n", cap->next);
|
||||
cons->printf("control: %04x\n", cap->control);
|
||||
if (cap->control & 0x0080) {
|
||||
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
|
||||
cons->printf("address: %016x\n", cap64->address);
|
||||
cons->printf(" data: %04x\n", cap64->data);
|
||||
if (cap->control & 0x100) {
|
||||
cons->printf(" mask: %08x\n", cap64->mask);
|
||||
cons->printf("pending: %08x\n", cap64->pending);
|
||||
}
|
||||
} else {
|
||||
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
|
||||
cons->printf("address: %08x\n", cap32->address);
|
||||
cons->printf(" data: %04x\n", cap32->data);
|
||||
if (cap->control & 0x100) {
|
||||
cons->printf(" mask: %08x\n", cap32->mask);
|
||||
cons->printf("pending: %08x\n", cap32->pending);
|
||||
}
|
||||
}
|
||||
cons->putc('\n');
|
||||
};
|
||||
*/
|
||||
|
||||
pci_device::pci_device() :
|
||||
m_base(nullptr),
|
||||
m_bus_addr(0),
|
||||
m_vendor(0),
|
||||
m_device(0),
|
||||
m_class(0),
|
||||
m_subclass(0),
|
||||
m_progif(0),
|
||||
m_revision(0),
|
||||
m_header_type(0)
|
||||
{
|
||||
}
|
||||
|
||||
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
|
||||
m_base(group.base_for(bus, device, func)),
|
||||
m_msi(nullptr),
|
||||
m_bus_addr(bus_addr(bus, device, func))
|
||||
{
|
||||
m_vendor = m_base[0] & 0xffff;
|
||||
m_device = (m_base[0] >> 16) & 0xffff;
|
||||
|
||||
m_revision = m_base[2] & 0xff;
|
||||
m_progif = (m_base[2] >> 8) & 0xff;
|
||||
m_subclass = (m_base[2] >> 16) & 0xff;
|
||||
m_class = (m_base[2] >> 24) & 0xff;
|
||||
|
||||
m_header_type = (m_base[3] >> 16) & 0x7f;
|
||||
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
||||
|
||||
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
||||
*command |= 0x400; // Mask old INTx style interrupts
|
||||
|
||||
uint16_t *status = command + 1;
|
||||
|
||||
j6::syslog("Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
|
||||
bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
|
||||
|
||||
j6::syslog(" = BAR0 %016lld", get_bar(0));
|
||||
j6::syslog(" = BAR1 %016lld", get_bar(1));
|
||||
|
||||
if (*status & 0x0010) {
|
||||
// Walk the extended capabilities list
|
||||
uint8_t next = m_base[13] & 0xff;
|
||||
while (next) {
|
||||
pci_cap *cap = reinterpret_cast<pci_cap *>(util::offset_pointer(m_base, next));
|
||||
next = cap->next;
|
||||
//log::verbose(logs::device, " - found PCI cap type %02x", cap->id);
|
||||
|
||||
if (cap->id == pci_cap::type::msi) {
|
||||
m_msi = cap;
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
||||
mcap->control &= ~0x70; // at most 1 vector allocated
|
||||
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pci_device::get_bar(unsigned i)
|
||||
{
|
||||
if (m_header_type == 0) {
|
||||
assert(i < 6); // Device max BAR is 5
|
||||
} else {
|
||||
assert(m_header_type == 1); // Only device or bridge
|
||||
assert(i < 2); // Bridge max BAR is 1
|
||||
}
|
||||
|
||||
return m_base[4+i];
|
||||
}
|
||||
|
||||
void
|
||||
pci_device::set_bar(unsigned i, uint32_t val)
|
||||
{
|
||||
if (m_header_type == 0) {
|
||||
assert(i < 6); // Device max BAR is 5
|
||||
} else {
|
||||
assert(m_header_type == 1); // Only device or bridge
|
||||
assert(i < 2); // Bridge max BAR is 1
|
||||
}
|
||||
|
||||
m_base[4+i] = val;
|
||||
}
|
||||
|
||||
void
|
||||
pci_device::write_msi_regs(uintptr_t address, uint16_t data)
|
||||
{
|
||||
if (!m_msi)
|
||||
return;
|
||||
|
||||
if (m_msi->id == pci_cap::type::msi) {
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
|
||||
if (mcap->control & 0x0080) {
|
||||
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
|
||||
mcap64->address = address;
|
||||
mcap64->data = data;
|
||||
} else {
|
||||
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
|
||||
mcap32->address = address;
|
||||
mcap32->data = data;
|
||||
}
|
||||
uint16_t control = mcap->control;
|
||||
control &= 0xff8f; // We're allocating one vector, clear 6::4
|
||||
control |= 0x0001; // Enable MSI
|
||||
mcap->control = control;
|
||||
} else {
|
||||
assert(0 && "MIS-X is NYI");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
|
||||
}
|
||||
139
src/user/srv.init/pci.h
Normal file
139
src/user/srv.init/pci.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
/// \file pci.h
|
||||
/// PCI devices and groups
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
struct pci_group;
|
||||
enum class isr : uint8_t;
|
||||
|
||||
struct pci_cap
|
||||
{
|
||||
enum class type : uint8_t
|
||||
{
|
||||
msi = 0x05,
|
||||
msix = 0x11
|
||||
};
|
||||
|
||||
type id;
|
||||
uint8_t next;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/// Information about a discovered PCIe device
|
||||
class pci_device
|
||||
{
|
||||
public:
|
||||
/// Default constructor creates an empty object.
|
||||
pci_device();
|
||||
|
||||
/// Constructor
|
||||
/// \arg group The group of this device's bus
|
||||
/// \arg bus The bus number this device is on
|
||||
/// \arg device The device number of this device
|
||||
/// \arg func The function number of this device
|
||||
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
|
||||
|
||||
/// Check if this device is multi-function.
|
||||
/// \returns True if this device is multi-function
|
||||
inline bool multi() const { return m_multi; }
|
||||
|
||||
/// Get the bus number this device is on.
|
||||
/// \returns The bus number
|
||||
inline uint8_t bus() const { return (m_bus_addr >> 8); }
|
||||
|
||||
/// Get the device number of this device on its bus
|
||||
/// \returns The device number
|
||||
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
|
||||
|
||||
/// Get the function number of this device on its device
|
||||
/// \returns The function number
|
||||
inline uint8_t function() const { return m_bus_addr & 0x7; }
|
||||
|
||||
/// Get the device class
|
||||
/// \returns The PCI device class
|
||||
inline uint8_t devclass() const { return m_class; }
|
||||
|
||||
/// Get the device subclass
|
||||
/// \returns The PCI device subclass
|
||||
inline uint8_t subclass() const { return m_subclass; }
|
||||
|
||||
/// Get the device program interface
|
||||
/// \returns The PCI device program interface
|
||||
inline uint8_t progif() const { return m_progif; }
|
||||
|
||||
/// Read one of the device's Base Address Registers
|
||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||
/// \returns The contents of the BAR
|
||||
uint32_t get_bar(unsigned i);
|
||||
|
||||
/// Write one of the device's Base Address Registers
|
||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||
/// \arg val The value to write
|
||||
void set_bar(unsigned i, uint32_t val);
|
||||
|
||||
/// Write to the MSI registers
|
||||
/// \arg addr The address to write to the MSI address registers
|
||||
/// \arg data The value to write to the MSI data register
|
||||
void write_msi_regs(uintptr_t addr, uint16_t data);
|
||||
|
||||
/// Get a bus address, given the bus/device/function numbers.
|
||||
/// \arg bus Number of the bus
|
||||
/// \arg device Index of the device on the bus
|
||||
/// \arg func The function number within the device
|
||||
/// \returns The computed bus_addr
|
||||
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return bus << 8 | device << 3 | func;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t *m_base;
|
||||
pci_cap *m_msi;
|
||||
|
||||
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
||||
uint16_t m_bus_addr;
|
||||
|
||||
uint16_t m_vendor;
|
||||
uint16_t m_device;
|
||||
|
||||
uint8_t m_class;
|
||||
uint8_t m_subclass;
|
||||
uint8_t m_progif;
|
||||
uint8_t m_revision;
|
||||
bool m_multi;
|
||||
|
||||
uint8_t m_header_type;
|
||||
};
|
||||
|
||||
|
||||
/// Represents data about a PCI bus group from the ACPI MCFG
|
||||
struct pci_group
|
||||
{
|
||||
uint16_t group;
|
||||
uint16_t bus_start;
|
||||
uint16_t bus_end;
|
||||
|
||||
uint32_t *base;
|
||||
|
||||
/// Get the base address of the MMIO configuration registers for a device
|
||||
/// \arg bus The bus number of the device (relative to this group)
|
||||
/// \arg device The device number on the given bus
|
||||
/// \arg func The function number on the device
|
||||
/// \returns A pointer to the memory-mapped configuration registers
|
||||
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return util::offset_pointer(base,
|
||||
pci_device::bus_addr(bus, device, func) << 12);
|
||||
}
|
||||
|
||||
/// Check if the given device function is present.
|
||||
/// \arg bus The bus number of the device (relative to this group)
|
||||
/// \arg device The device number on the given bus
|
||||
/// \arg func The function number on the device
|
||||
/// \returns True if the device function is present
|
||||
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user