// Copyright 2018 The Fuchsia Authors // // 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 #include #include #include #include #include #define LOCAL_TRACE 0 MmioPcieAddressProvider::~MmioPcieAddressProvider() { // Unmap and free all of our mapped ECAM regions ecam_regions_.clear(); } zx_status_t MmioPcieAddressProvider::Translate(uint8_t bus_id, uint8_t device_id, uint8_t function_id, vaddr_t* virt, paddr_t* phys) { // Find the region which would contain this bus_id, if any. // add does not overlap with any already defined regions. fbl::AutoLock ecam_region_lock(&ecam_region_lock_); auto iter = ecam_regions_.upper_bound(static_cast(bus_id)); --iter; if (!iter.IsValid()) { return ZX_ERR_NOT_FOUND; } if ((bus_id < iter->ecam().bus_start) || (bus_id > iter->ecam().bus_end)) { return ZX_ERR_NOT_FOUND; } bus_id = static_cast(bus_id - iter->ecam().bus_start); size_t offset = (static_cast(bus_id) << 20) | (static_cast(device_id) << 15) | (static_cast(function_id) << 12); if (phys) { *phys = iter->ecam().phys_base + offset; } // TODO(cja): Move to a BDF based associative container for better lookup time // and insert or find behavior. *virt = reinterpret_cast(static_cast(iter->vaddr()) + offset); return ZX_OK; } zx_status_t MmioPcieAddressProvider::AddEcamRegion(const PciEcamRegion& ecam) { // Sanity check the region first. if (ecam.bus_start > ecam.bus_end) { return ZX_ERR_INVALID_ARGS; } size_t bus_count = static_cast(ecam.bus_end) - ecam.bus_start + 1u; if (ecam.size != (PCIE_ECAM_BYTE_PER_BUS * bus_count)){ return ZX_ERR_INVALID_ARGS; } // Grab the ECAM lock and make certain that the region we have been asked to // add does not overlap with any already defined regions. fbl::AutoLock ecam_region_lock(&ecam_region_lock_); auto iter = ecam_regions_.upper_bound(ecam.bus_start); --iter; // If iter is valid, it now points to the region with the largest bus_start // which is <= ecam.bus_start. If any region overlaps with the region we // are attempting to add, it will be this one. if (iter.IsValid()) { const uint8_t iter_start = iter->ecam().bus_start; const size_t iter_len = iter->ecam().bus_end - iter->ecam().bus_start + 1; const uint8_t bus_start = ecam.bus_start; const size_t bus_len = ecam.bus_end - ecam.bus_start + 1; if (Intersects(iter_start, iter_len, bus_start, bus_len)) { return ZX_ERR_BAD_STATE; } } // Looks good. Attempt to allocate and map this ECAM region. fbl::AllocChecker ac; ktl::unique_ptr region(new (&ac) MappedEcamRegion(ecam)); if (!ac.check()) { TRACEF("Failed to allocate ECAM region for bus range [0x%02x, 0x%02x]\n", ecam.bus_start, ecam.bus_end); return ZX_ERR_NO_MEMORY; } zx_status_t res = region->MapEcam(); if (res != ZX_OK) { TRACEF("Failed to map ECAM region for bus range [0x%02x, 0x%02x]\n", ecam.bus_start, ecam.bus_end); return res; } // Everything checks out. Add the new region to our set of regions and we are done. ecam_regions_.insert(ktl::move(region)); return ZX_OK; } fbl::RefPtr MmioPcieAddressProvider::CreateConfig(const uintptr_t addr) { return PciConfig::Create(addr, PciAddrSpace::MMIO); }