blob: 9889ef1c83e74ee10e2e9a8e0f8a26435a1a176b [file] [log] [blame]
// Copyright 2023 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.
#ifndef HWREG_ARRAY_H_
#define HWREG_ARRAY_H_
#include <lib/stdcompat/span.h>
#include <array>
#include <atomic>
#include <type_traits>
#include "internal.h"
namespace hwreg {
// An hwreg::ArrayIo is an IoProvider for use with "bitfields.h" types
// that simply holds a std::span of the underlying integer type and does normal
// memory access on it. This can be either a fixed-size or a dynamic span. The
// `offset` arguments to the hwreg API methods are used as the index into the
// span. The templated methods require that the type parameter in the access
// (i.e. the underlying type of the given hwreg type) be exactly ElementType.
//
// For example:
// ```
// using PageTableIo = hwreg::ArrayIo<PageTableEntry::ValueType, 512>;
// PageTableIo io{page_table_array};
// auto pte = PageTableEntry::Get(pfn);
// pte.set_p(false);
// pte.WriteTo(&io);
// ```
template <typename ElementType, size_t ElementCount = cpp20::dynamic_extent,
typename Ref = ElementType&>
class ArrayIo {
public:
using Span = cpp20::span<ElementType, ElementCount>;
constexpr explicit ArrayIo(Span array) : array_(array) {}
constexpr Span get() const { return array_; }
template <typename IntType>
constexpr void Write(IntType val, uint32_t offset) const {
static_assert(std::is_same_v<IntType, ElementType>,
"hwreg::ArrayIo<T> supports Write<T>, not Write<OtherT>");
At(offset) = val;
}
template <typename IntType>
constexpr IntType Read(uint32_t offset) const {
static_assert(std::is_same_v<IntType, ElementType>,
"hwreg::ArrayIo<T> supports Read<T>, not Read<OtherT>");
return static_cast<ElementType>(At(offset));
}
private:
constexpr Ref At(uint32_t offset) const {
if constexpr (std::is_reference_v<Ref>) {
return static_cast<Ref>(array_[offset]);
} else {
return Ref{array_[offset]};
}
}
Span array_;
};
template <typename ElementType, size_t ElementCount>
ArrayIo(cpp20::span<ElementType, ElementCount>) -> ArrayIo<ElementType, ElementCount>;
// hwreg::AtomicArray<MO>::Io<...> is like hwreg::ArrayIo<...> but it uses
// cpp20::atomic_ref load and store methods with the MO argument to access
// the elements in the span.
template <std::memory_order MemoryOrder>
struct AtomicArray {
// This can't just be an alias since then it couldn't have a deduction guide.
template <typename ElementType, size_t ElementCount = cpp20::dynamic_extent>
struct Io : public ArrayIo<ElementType, ElementCount,
internal::AtomicArrayIoRef<ElementType, MemoryOrder>> {
using ArrayIo<ElementType, ElementCount,
internal::AtomicArrayIoRef<ElementType, MemoryOrder>>::ArrayIo;
};
template <typename ElementType, size_t ElementCount>
Io(cpp20::span<ElementType, ElementCount>) -> Io<ElementType, ElementCount>;
};
// Convenience type for an array that's naturally-aligned to its whole size.
// It's zero-initialized if declared or used with new (e.g. placement new).
// It will usually be used via pointers to pre-existing aligned memory such as
// a page table. The main purpose of the type is to manufacture ArrayIo or
// AtomicArrayIo accessors for using elements as the underlying integer type
// for hwreg register types.
template <typename ElementType, size_t ElementCount>
struct AlignedTableStorage {
constexpr auto table() { return cpp20::span(table_); }
constexpr auto direct_io() { return hwreg::ArrayIo(table()); }
constexpr auto atomic_io() { return hwreg::AtomicArray<std::memory_order_relaxed>::Io(table()); }
private:
using Table = std::array<ElementType, ElementCount>;
alignas(sizeof(Table)) Table table_{};
};
} // namespace hwreg
#endif // HWREG_ARRAY_H_