diff --git a/src/mem/arenamap.h b/src/mem/arenamap.h new file mode 100644 index 0000000..bf12816 --- /dev/null +++ b/src/mem/arenamap.h @@ -0,0 +1,130 @@ +#include "../ds/ptrwrap.h" +#include "pagemap.h" + +namespace snmalloc +{ + struct default_alloc_size_t + { + /* + * Just make something up for non-StrictProvenance architectures. + * Ultimately, this is going to flow only to FlatPagemap's template argument + * for the number of bits it's covering but the whole thing will be + * discarded by the time we resolve all the conditionals behind the + * AuthPagemap type. To avoid pathologies where COVERED_BITS ends up being + * bit-width of the machine (meaning 1ULL << COVERED_BITS becomes undefined) + * and where sizeof(std::atomic[ENTRIES]) is either undefined or + * enormous, we choose a value that dodges both endpoints and still results + * in a small table. + */ + static constexpr size_t capptr_root_alloc_size = + bits::one_at_bit(bits::ADDRESS_BITS - 8); + }; + + /* + * Compute the block allocation size to use for AlignedAllocations. This + * is either PAL::capptr_root_alloc_size, on architectures that require + * StrictProvenance, or the placeholder from above. + */ + template + static constexpr size_t AUTHMAP_ALLOC_SIZE = std::conditional_t< + aal_supports, + PAL, + default_alloc_size_t>::capptr_root_alloc_size; + + template + static constexpr size_t + AUTHMAP_BITS = bits::next_pow2_bits_const(AUTHMAP_ALLOC_SIZE); + + template + static constexpr bool + AUTHMAP_USE_FLATPAGEMAP = pal_supports || + (PAGEMAP_NODE_SIZE >= sizeof(FlatPagemap, void*>)); + + struct default_auth_pagemap + { + static SNMALLOC_FAST_PATH void* get(address_t a) + { + UNUSED(a); + return nullptr; + } + }; + + template + using AuthPagemap = std::conditional_t< + aal_supports, + std::conditional_t< + AUTHMAP_USE_FLATPAGEMAP, + FlatPagemap, void*>, + Pagemap, void*, nullptr, PrimAlloc>>, + default_auth_pagemap>; + + struct ForAuthmap + {}; + template + using GlobalAuthmap = + GlobalPagemapTemplate, ForAuthmap>; + + template + struct DefaultArenaMapTemplate + { + /* + * Without AlignedAllocation, we (below) adopt a fallback mechanism that + * over-allocates and then finds an aligned region within the too-large + * region. The "trimmings" from either side are also registered in hopes + * that they can be used for later allocations. + * + * Unfortunately, that strategy does not work for this ArenaMap: trimmings + * may be smaller than the granularity of our backing PageMap, and so we + * would be unable to amplify authority. Eventually we may arrive at a need + * for an ArenaMap that is compatible with this approach, but for the moment + * it's far simpler to assume that we can always ask for memory sufficiently + * aligned to cover an entire PageMap granule. + */ + static_assert( + !aal_supports || pal_supports, + "StrictProvenance requires platform support for aligned allocation"); + + static constexpr size_t alloc_size = AUTHMAP_ALLOC_SIZE; + + /* + * Because we assume that we can `capptr_amplify` and then + * `Superslab::get()` on the result to get to the Superslab metadata + * headers, it must be the case that provenance roots cover entire + * Superslabs. + */ + static_assert( + !aal_supports || + ((alloc_size > 0) && (alloc_size % SUPERSLAB_SIZE == 0)), + "Provenance root granule must encompass whole superslabs"); + + static void register_root(CapPtr root) + { + if constexpr (aal_supports) + { + PagemapProvider::pagemap().set(address_cast(root), root.unsafe_capptr); + } + else + { + UNUSED(root); + } + } + + template + static SNMALLOC_FAST_PATH CapPtr capptr_amplify(CapPtr r) + { + static_assert( + B == CBAllocE || B == CBAlloc, + "Attempting to amplify an unexpectedly high pointer"); + return Aal::capptr_rebound( + CapPtr( + PagemapProvider::pagemap().get(address_cast(r))), + r) + .template as_static(); + } + }; + + template + using DefaultArenaMap = + DefaultArenaMapTemplate>; + +} // namespace snmalloc