[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:
Justin C. Miller
2023-02-20 11:23:49 -08:00
parent 15e2f8abf3
commit 3a7a18011c
16 changed files with 294 additions and 235 deletions

104
src/user/srv.init/acpi.cpp Normal file
View 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 &region = 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
View 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);

View File

@@ -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",
])

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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
View 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
View 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);
};