blob: 43b5a9cbbd66a72ce32e92085fb092d3c99928bc [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors
// Copyright (c) 2019, Google, Inc. All rights reserved
//
// 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 <ddktl/protocol/pciroot.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include <fbl/unique_ptr.h>
#include <region-alloc/region-alloc.h>
#include <sys/types.h>
#include <zircon/types.h>
// UpstreamNode
//
// A class responsible for maintaining the state of a node in the graph of
// PCI/PCIe devices which can have downstream children. UpstreamNodes are
// not instantiated directly, instead they serve as the base class of
// PCI/PCIe bridges and roots.
namespace pci {
// A forward declaration until pci::Device is ported to userspace.
class Device : public fbl::DoublyLinkedListable<pci::Device*> {};
class PciAllocator;
class UpstreamNode {
public:
enum class Type { ROOT,
BRIDGE };
virtual ~UpstreamNode();
// Disallow copying, assigning and moving.
DISALLOW_COPY_ASSIGN_AND_MOVE(UpstreamNode);
Type type() const { return type_; }
uint32_t managed_bus_id() const { return managed_bus_id_; }
virtual PciAllocator& pf_mmio_regions() = 0;
virtual PciAllocator& mmio_lo_regions() = 0;
virtual PciAllocator& mmio_hi_regions() = 0;
virtual PciAllocator& pio_regions() = 0;
zx_status_t LinkDevice(pci::Device* device);
zx_status_t UnlinkDevice(pci::Device* device);
protected:
UpstreamNode(Type type, uint32_t mbus_id)
: type_(type),
managed_bus_id_(mbus_id) {}
virtual void AllocateDownstreamBars();
virtual void DisableDownstream();
virtual void ScanDownstream();
virtual void UnplugDownstream();
// The list of all devices immediately under this root/bridge.
fbl::DoublyLinkedList<pci::Device*> downstream_list_;
private:
const Type type_;
const uint32_t managed_bus_id_; // The ID of the downstream bus which this node manages.
};
// PciAllocations and PciAllocators are concepts internal to UpstreamNodes which
// track address space allocations across roots and bridges. PciAllocator is an
// interface for roots and bridges to provide allocators to downstream bridges
// for their own allocations. Roots allocate across the PciRoot protocol so the
// dtors of PciRootAllocations will make a protocol call to release the address
// space if for some reason a root's allocations go out of scope. A bridge works
// similarly, except its allocations from from a bridge or upstream root's
// region allocators, and holds a given region for its lifecycle. When it is
// released the region can go through the normal region lifecycle and be
// released back to the region allocator.
class PciAllocation {
public:
// These should not be copied, assigned, or moved
PciAllocation(const PciAllocation&) = delete;
PciAllocation(PciAllocation&) = delete;
PciAllocation& operator=(const PciAllocation&) = delete;
PciAllocation& operator=(PciAllocation&&) = delete;
virtual ~PciAllocation() {}
virtual zx_paddr_t base() = 0;
virtual size_t size() = 0;
protected:
PciAllocation() = default;
};
class PciRootAllocation final : public PciAllocation {
public:
PciRootAllocation(zx_paddr_t base, size_t size)
: base_(base), size_(size) {}
virtual ~PciRootAllocation() {}
zx_paddr_t base() final { return base_; }
size_t size() final { return size_; }
private:
const zx_paddr_t base_;
const size_t size_;
};
class PciRegionAllocation final : public PciAllocation {
public:
PciRegionAllocation(RegionAllocator::Region::UPtr&& region)
: region_(std::move(region)){};
zx_paddr_t base() final { return region_->base; }
size_t size() final { return region_->size; }
private:
RegionAllocator::Region::UPtr region_;
};
class PciAllocator {
public:
virtual ~PciAllocator() {}
virtual zx_status_t GetRegion(zx_paddr_t base,
size_t size,
fbl::unique_ptr<PciAllocation> out_alloc) = 0;
virtual zx_status_t AddAddressSpace(fbl::unique_ptr<PciAllocation> alloc) = 0;
protected:
PciAllocator() {};
};
// PciRootAllocators are an implementation of PciAllocator designed
// to use the Pciroot protocol for allocation, fulfilling the requirements
// for a PciRoot to implement the UpstreamNode interface.
class PciRootAllocator : public PciAllocator {
public:
PciRootAllocator(ddk::PcirootProtocolClient* proto, pci_address_space_t type, bool low)
: pciroot_(proto), type_(type), low_(low) {}
// These should not be copied, assigned, or moved
PciRootAllocator(const PciRootAllocator&) = delete;
PciRootAllocator(PciRootAllocator&&) = delete;
PciRootAllocator& operator=(const PciRootAllocator&) = delete;
PciRootAllocator& operator=(PciRootAllocator&&) = delete;
virtual ~PciRootAllocator(){};
zx_status_t GetRegion(zx_paddr_t base,
size_t size,
fbl::unique_ptr<PciAllocation> alloc) final;
zx_status_t AddAddressSpace(fbl::unique_ptr<PciAllocation> alloc) final;
private:
// The bus driver outlives allocator objects.
ddk::PcirootProtocolClient* const pciroot_;
const pci_address_space_t type_;
// This denotes whether this allocator requests memory < 4GB. More detail
// can be found in the explanation for mmio_lo in root.h.
const bool low_;
};
// PciRegionAllocators are a wrapper around RegionAllocators to allow
// Bridge objects to implement the UpstreamNode interface by using regions
// they get other bridges & the root upstream..
//
// TODO(cja) implement this when bridge is ported over in the near future.
class PciRegionAllocator : public PciAllocator {
public:
PciRegionAllocator(){};
// These should not be copied, assigned, or moved
PciRegionAllocator(const PciRegionAllocator&) = delete;
PciRegionAllocator(PciRegionAllocator&&) = delete;
PciRegionAllocator& operator=(const PciRegionAllocator&) = delete;
PciRegionAllocator& operator=(PciRegionAllocator&&) = delete;
zx_status_t GetRegion(zx_paddr_t base,
size_t size,
fbl::unique_ptr<PciAllocation> alloc) final {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t AddAddressSpace(fbl::unique_ptr<PciAllocation> alloc) final {
return ZX_ERR_NOT_SUPPORTED;
}
};
} // namespace pci