[bus][pci] first stab at PCI-e ECAM support

This commit is contained in:
Travis Geiselbrecht
2021-11-12 00:01:12 -08:00
parent 7285a2d1fd
commit 0c27d8fe45
8 changed files with 255 additions and 31 deletions

View File

@@ -29,9 +29,11 @@ static void pci_list(void) {
printf("Scanning...\n");
for (state.bus = 0; state.bus <= pci_get_last_bus(); state.bus++) {
for (int bus = 0; bus <= (int)pci_get_last_bus(); bus++) {
busses++;
state.bus = bus;
for (devfn = 0; devfn < 256; devfn++) {
state.dev_fn = devfn;

142
dev/bus/pci/ecam.cpp Normal file
View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2009 Corey Tabaka
* Copyright (c) 2020 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "ecam.h"
#include <lk/debug.h>
#include <lk/err.h>
#include <stdlib.h>
#include <string.h>
#include <kernel/thread.h>
#include <kernel/spinlock.h>
#include <dev/bus/pci.h>
#include <lk/trace.h>
#if WITH_KERNEL_VM
#include <kernel/vm.h>
#endif
#include "pci_priv.h"
#define LOCAL_TRACE 0
pci_ecam::pci_ecam(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) :
base_(base), segment_(segment), start_bus_(start_bus), end_bus_(end_bus) {}
pci_ecam::~pci_ecam() {
LTRACE_ENTRY;
#if WITH_KERNEL_VM
if (ecam_ptr_) {
vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)ecam_ptr_);
}
#endif
}
pci_ecam *pci_ecam::detect(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) {
LTRACEF("base %#lx, segment %hu, bus [%hhu...%hhu]\n", base, segment, start_bus, end_bus);
// we only support a limited configuration at the moment
if (segment != 0 || start_bus != 0) {
return nullptr;
}
auto ecam = new pci_ecam(base, segment, start_bus, end_bus);
// initialize the object, which may fail
status_t err = ecam->initialize();
if (err != NO_ERROR) {
delete ecam;
return nullptr;
}
return ecam;
}
status_t pci_ecam::initialize() {
// compute the aperture size of this
size_t size = ((size_t)end_bus_ - (size_t)start_bus_ + 1) << 20; // each bus occupies 20 bits of address space
LTRACEF("aperture size %#zx\n", size);
#if WITH_KERNEL_VM
// try to map the aperture
//status_t vmm_alloc_physical(vmm_aspace_t *aspace, const char *name, size_t size, void **ptr, uint8_t align_log2, paddr_t paddr, uint vmm_flags, uint arch_mmu_flags)
status_t err = vmm_alloc_physical(vmm_get_kernel_aspace(), "pci_ecam", size, (void **)&ecam_ptr_, 0, base_, 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
LTRACEF("vmm_alloc_physical returns %d, ptr %p\n", err, ecam_ptr_);
if (err != NO_ERROR) {
ecam_ptr_ = nullptr;
return err;
}
#else
// no vm, so can directly access the aperture
ecam_ptr_ = (uint8_t *)base_;
#endif
set_last_bus(end_bus_);
return NO_ERROR;
}
// compute the offset into the ecam given the location and register offset
inline size_t location_to_offset(const pci_location_t *state, uint32_t reg) {
// TODO: clamp or assert on invalid offset
size_t offset = (size_t)state->bus << 20;
offset += (size_t)state->dev_fn << 12;
offset += reg;
return offset;
}
// templatized routines to access the pci config space using a specific type
template <typename T>
inline int read_config(const pci_location_t *state, uint32_t reg, T *value, const uint8_t *ecam_ptr) {
auto off = location_to_offset(state, reg);
*value = *reinterpret_cast<const volatile T *>(&ecam_ptr[off]);
return NO_ERROR;
}
template <typename T>
inline int write_config(const pci_location_t *state, uint32_t reg, T value, uint8_t *ecam_ptr) {
auto off = location_to_offset(state, reg);
*reinterpret_cast<volatile T *>(&ecam_ptr[off]) = value;
return NO_ERROR;
}
int pci_ecam::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
return read_config(state, reg, value, ecam_ptr_);
}
int pci_ecam::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
return read_config(state, reg, value, ecam_ptr_);
}
int pci_ecam::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
return read_config(state, reg, value, ecam_ptr_);
}
int pci_ecam::write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
return write_config(state, reg, value, ecam_ptr_);
}
int pci_ecam::write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
return write_config(state, reg, value, ecam_ptr_);
}
int pci_ecam::write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
return write_config(state, reg, value, ecam_ptr_);
}

42
dev/bus/pci/ecam.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "pci_backend.h"
class pci_ecam final : public pci_backend {
public:
virtual ~pci_ecam();
// factory to detect and create an instance
static pci_ecam *detect(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus);
// a few overridden methods
int read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) override;
int read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) override;
int read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) override;
int write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) override;
int write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) override;
int write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) override;
private:
// only created via the detect() factory
pci_ecam(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus);
// allocate vm resources for the object
status_t initialize();
paddr_t base_;
uint16_t segment_;
uint16_t start_bus_;
uint16_t end_bus_;
// vm region where the ecam is mapped
uint8_t *ecam_ptr_ = nullptr;
};

View File

@@ -147,22 +147,27 @@ typedef struct {
uint8_t reserved;
} __PACKED irq_routing_entry;
void pci_init(void);
// only use one of these two:
// try to detect PCI based on legacy PC PCI accessor methods
status_t pci_init_legacy(void);
// try to detect PCI based on a known ecam base.
status_t pci_init_ecam(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus);
int pci_get_last_bus(void);
int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index);
status_t pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
status_t pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index);
int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value);
int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value);
int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value);
status_t pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value);
status_t pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value);
status_t pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value);
int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value);
int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value);
int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value);
status_t pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value);
status_t pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value);
status_t pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value);
int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs);
int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
status_t pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs);
status_t pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
__END_CDECLS

View File

@@ -20,6 +20,8 @@
#define LOCAL_TRACE 0
// Largely C level api for the PCI bus manager
namespace {
SpinLock lock;
pci_backend *pcib = nullptr;
@@ -27,14 +29,14 @@ pci_backend *pcib = nullptr;
int pci_get_last_bus() {
if (!pcib) {
return -1;
return ERR_NOT_CONFIGURED;
}
return pcib->get_last_bus();
}
/* user facing routines */
int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
status_t pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
LTRACEF("device_id dev %#hx vendor %#hx index %#hx\n", device_id, vendor_id, index);
if (!pcib) return ERR_NOT_CONFIGURED;
@@ -46,7 +48,7 @@ int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vend
return res;
}
int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
status_t pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
LTRACEF("device_id class %#x index %#hx\n", class_code, index);
if (!pcib) return ERR_NOT_CONFIGURED;
@@ -58,7 +60,7 @@ int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t
return res;
}
int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
status_t pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -67,7 +69,7 @@ int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *val
return res;
}
int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
status_t pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -77,7 +79,7 @@ int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *va
return res;
}
int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
status_t pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -87,7 +89,7 @@ int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *va
return res;
}
int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
status_t pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -97,7 +99,7 @@ int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t val
return res;
}
int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
status_t pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -107,7 +109,7 @@ int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t va
return res;
}
int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
status_t pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -117,7 +119,7 @@ int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t va
return res;
}
int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs) {
status_t pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs) {
if (!pcib) return ERR_NOT_CONFIGURED;
// TODO: highly bios32 specific, abstract this differently
@@ -138,7 +140,7 @@ int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uin
return res;
}
int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
status_t pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
if (!pcib) return ERR_NOT_CONFIGURED;
AutoSpinLock guard(&lock);
@@ -148,22 +150,43 @@ int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq
return res;
}
void pci_init() {
// try a series of detection mechanisms
status_t pci_init_legacy() {
LTRACE_ENTRY;
DEBUG_ASSERT(pcib == nullptr);
// try a series of detection mechanisms based on legacy PCI access on x86 PCs
// try to BIOS32 access first, if present
if ((pcib = pci_bios32::detect())) {
dprintf(INFO, "PCI: pci bios functions installed\n");
dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
return;
return NO_ERROR;
}
// try type 1 access
if ((pcib = pci_type1::detect())) {
dprintf(INFO, "PCI: pci type1 functions installed\n");
dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
return;
return NO_ERROR;
}
// if we couldn't find anything, leave pcib null
return ERR_NOT_FOUND;
}
status_t pci_init_ecam(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) {
LTRACEF("base %#lx, segment %hu, bus [%hhu...%hhu]\n", ecam_base, segment, start_bus, end_bus);
DEBUG_ASSERT(pcib == nullptr);
if ((pcib = pci_ecam::detect(ecam_base, segment, start_bus, end_bus))) {
dprintf(INFO, "PCI: pci ecam functions installed\n");
dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
return NO_ERROR;
}
// if we couldn't find anything, leave pcib null
return ERR_NOT_FOUND;
}

View File

@@ -12,7 +12,8 @@
#include <lk/compiler.h>
#include <dev/bus/pci.h>
#include "type1.h"
#include "bios32.h"
#include "ecam.h"
#include "type1.h"

View File

@@ -5,6 +5,7 @@ MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/bios32.cpp \
$(LOCAL_DIR)/debug.cpp \
$(LOCAL_DIR)/ecam.cpp \
$(LOCAL_DIR)/pci.cpp \
$(LOCAL_DIR)/type1.cpp \