[bus][pci] first stab at PCI-e ECAM support
This commit is contained in:
@@ -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
142
dev/bus/pci/ecam.cpp
Normal 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
42
dev/bus/pci/ecam.h
Normal 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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
|
||||
Reference in New Issue
Block a user