blob: a86e5104c5f629f9321b2a2dcd9da09d27325208 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/devices/bus/drivers/pci/allocation.h"
#include <err.h>
#include <lib/zx/resource.h>
#include <lib/zx/result.h>
#include <lib/zx/vmo.h>
#include <zircon/rights.h>
#include <zircon/status.h>
#include <cassert>
#include <cinttypes>
#include <cstring>
#include <memory>
#include <fbl/algorithm.h>
namespace pci {
zx::result<zx::vmo> PciAllocation::CreateVmo() const {
zx::vmo vmo;
zx_status_t status = zx::vmo::create_physical(resource(), base(), size(), &vmo);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(vmo));
}
zx::result<zx::resource> PciAllocation::CreateResource() const {
zx::resource resource;
// A BAR allocation will already be sized to the BAR, so we can simply
// duplicate the resource rather than creating a sub-resource.
zx_status_t status = resource_.duplicate(ZX_RIGHT_SAME_RIGHTS, &resource);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(resource));
}
zx::result<std::unique_ptr<PciAllocation>> PciRootAllocator::Allocate(
std::optional<zx_paddr_t> base, size_t size) {
zx_paddr_t in_base = (base) ? *base : 0;
zx_paddr_t out_base = {};
zx::resource res = {};
zx::eventpair ep = {};
zx_status_t status = pciroot_.GetAddressSpace(in_base, size, type(), low_, &out_base, &res, &ep);
if (status != ZX_OK) {
bool mmio = type() == PCI_ADDRESS_SPACE_MEMORY;
// This error may not be fatal, the Device probe/allocation methods will know for sure.
zxlogf(DEBUG, "failed to allocate %s %s [%#8lx, %#8lx) from root: %s", (mmio) ? "mmio" : "io",
(mmio) ? ((low_) ? "<4GB" : ">4GB") : "", in_base, in_base + size,
zx_status_get_string(status));
return zx::error(status);
}
auto allocation = std::unique_ptr<PciAllocation>(
new PciRootAllocation(pciroot_, type(), std::move(res), std::move(ep), out_base, size));
return zx::ok(std::move(allocation));
}
zx::result<std::unique_ptr<PciAllocation>> PciRegionAllocator::Allocate(
std::optional<zx_paddr_t> base, size_t size) {
if (!parent_alloc_) {
return zx::error(ZX_ERR_NO_MEMORY);
}
RegionAllocator::Region::UPtr region_uptr;
zx_status_t status = ZX_OK;
// Only use base if it is non-zero. RegionAllocator's interface is overloaded so we have
// to call it differently.
if (base) {
ralloc_region_t request = {
.base = (base) ? *base : 0,
.size = size,
};
status = allocator_.GetRegion(request, region_uptr);
} else {
status = allocator_.GetRegion(size, zx_system_get_page_size(), region_uptr);
}
if (status != ZX_OK) {
return zx::error(status);
}
zx::resource out_resource = {};
// TODO(https://fxbug.dev/42108122): When the resource subset CL lands, make this a smaller resource.
status = parent_alloc_->resource().duplicate(ZX_DEFAULT_RESOURCE_RIGHTS, &out_resource);
if (status != ZX_OK) {
return zx::error(status);
}
zxlogf(TRACE, "bridge: assigned [%#lx, %#lx) downstream", region_uptr->base,
region_uptr->base + size);
auto allocation = std::unique_ptr<PciAllocation>(
new PciRegionAllocation(type(), std::move(out_resource), std::move(region_uptr)));
return zx::ok(std::move(allocation));
}
zx_status_t PciRegionAllocator::SetParentAllocation(std::unique_ptr<PciAllocation> alloc) {
ZX_DEBUG_ASSERT(!parent_alloc_);
parent_alloc_ = std::move(alloc);
auto base = parent_alloc_->base();
auto size = parent_alloc_->size();
return allocator_.AddRegion({.base = base, .size = size});
}
} // namespace pci