blob: 4a07e4f132e3b8ecd021656bf9b19fee5685fe37 [file] [log] [blame] [edit]
// Copyright 2020 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
#ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MSI_ALLOCATION_H_
#define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MSI_ALLOCATION_H_
#include <lib/zircon-internal/thread_annotations.h>
#include <sys/types.h>
#include <zircon/rights.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <dev/interrupt.h>
#include <fbl/ref_counted.h>
#include <kernel/spinlock.h>
#include <ktl/limits.h>
#include <ktl/move.h>
#include <object/resource_dispatcher.h>
// An MsiAllocation is a wrapper around an allocated block oVf MSI interrupts.
// It allows for multiple MsiDispatchers to share an allocated block, and
// synchronize access to an MSI capability dealing with multiple IRQs.
//
// By default, all MSI Allocations use the platform's kernel msi_*
// implementation for management of MSI blocks, but tests can override the
// interface via Create() parameters. Since those methods are used in allocation
// of interrupts but not dispatch the indirection of those calls is an
// acceptable cost to have the benefit of not making the type signature more
// complex with other compile-time approaches.
class MsiAllocation : public fbl::RefCounted<MsiAllocation> {
public:
using MsiAllocFn = zx_status_t (*)(uint32_t, bool, bool, msi_block_t*);
using MsiFreeFn = void (*)(msi_block_t*);
using MsiSupportedFn = bool (*)();
// For now limit the max number of allocations in a block to the limit of standard
// MSI. MSI-X's enhanced allocation limits are not going to come into play until
// we move interrupt allocation off of the bootstrap CPU.
using IdBitMaskType = uint32_t;
using MsiId = uint32_t;
static constexpr uint32_t kMsiAllocationCountMax = ktl::numeric_limits<IdBitMaskType>::digits;
static zx_status_t Create(uint32_t irq_cnt, fbl::RefPtr<MsiAllocation>* obj,
// Defaults to allow test mocks to override.
MsiAllocFn msi_alloc_fn = msi_alloc_block,
MsiFreeFn msi_free_fn = msi_free_block,
MsiSupportedFn msi_support_fn = msi_is_supported,
ResourceDispatcher::ResourceStorage* rsrc_storage = nullptr);
~MsiAllocation();
void GetInfo(zx_info_msi* info) const TA_EXCL(lock_);
static zx_obj_type_t get_type() { return ZX_OBJ_TYPE_MSI_ALLOCATION; }
const msi_block_t& block() const TA_REQ(lock_) { return block_; }
// Interface for MsiDispatchers to reserve a given MSI id for management.
zx_status_t ReserveId(MsiId msi_id) TA_EXCL(lock_);
zx_status_t ReleaseId(MsiId msi_id) TA_EXCL(lock_);
Lock<SpinLock>& lock() TA_EXCL(lock_) TA_RET_CAP(lock_) { return lock_; }
private:
explicit MsiAllocation(fbl::RefPtr<ResourceDispatcher>&& resource, const msi_block_t block,
MsiFreeFn msi_free_fn)
: resource_(ktl::move(resource)), msi_free_fn_(msi_free_fn), block_(block) {}
// MsiAllocationInterface abstraction methods to allow mocking but not add complexity
// to users of the object via requiring templates.
fbl::RefPtr<ResourceDispatcher> resource_;
// A pointer to the function to free the block when the object is released.
MsiFreeFn msi_free_fn_;
// Function pointers for MSI platform functions to facilitate unit tests.
msi_block_t block_ TA_GUARDED(lock_) = {};
// A bitfield of MSI ids currently associated with MsiDispatchers.
IdBitMaskType ids_in_use_ TA_GUARDED(lock_) = 0;
// Used to synchronize access to an MSI vector control register for
// MSI blocks that consist of multiple vectors and MsiInterruptDispatchers.
// It is not used to protect the MsiAllocation itself.
mutable DECLARE_SPINLOCK(MsiAllocation) lock_;
};
#endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MSI_ALLOCATION_H_