Files
lk/dev/bus/pci/backend/ecam.cpp
Travis Geiselbrecht 611bd1cba0 [bus][pci] ask for 4MB alignment when mapping the ecam
This helps any architecture that might have a better chance with using
large pages.
2022-10-21 23:47:47 -07:00

149 lines
5.0 KiB
C++

/*
* 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
// ask for 4MB aligned regions (log2 22) to help with the mmu on most architectures
//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_, 22, 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) {
//
// | 27 - 20 | 19 - 15 | 14 - 12 | 11 - 8 | 7 - 2 | 1 - 0 |
// | Bus Nr | Dev Nr | Function Nr | Ext. Register Nr | Register Nr | Byte Enable |
// TODO: clamp or assert on invalid offset
size_t offset = (size_t)state.bus << 20;
offset += (size_t)state.dev << 15;
offset += (size_t)state.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 %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.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 %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.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 %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.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 %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.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 %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.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 %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
return write_config(state, reg, value, ecam_ptr_);
}