| // 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 |
| |
| #include "phys/allocation.h" |
| |
| #include <lib/fitx/result.h> |
| #include <lib/memalloc/pool.h> |
| #include <zircon/assert.h> |
| |
| #include <fbl/no_destructor.h> |
| #include <ktl/array.h> |
| #include <ktl/byte.h> |
| #include <ktl/move.h> |
| #include <ktl/string_view.h> |
| #include <phys/arch/arch-allocation.h> |
| #include <phys/main.h> |
| |
| #include <ktl/enforce.h> |
| |
| // Global memory allocation book-keeping. |
| memalloc::Pool& Allocation::GetPool() { |
| // Use fbl::NoDestructor to avoid generation of static destructors, |
| // which fails in the phys environment. |
| static fbl::NoDestructor<memalloc::Pool> allocator; |
| return *allocator; |
| } |
| |
| void Allocation::Init(ktl::span<memalloc::Range> mem_ranges, |
| ktl::span<memalloc::Range> special_ranges) { |
| auto& pool = Allocation::GetPool(); |
| ktl::array ranges{mem_ranges, special_ranges}; |
| // kAllocationMinAddr is defined in arch-allocation.h. |
| auto init_result = kAllocationMinAddr // ktl::nullopt if don't care. |
| ? pool.Init(ranges, *kAllocationMinAddr) |
| : pool.Init(ranges); |
| ZX_ASSERT(init_result.is_ok()); |
| } |
| |
| // This is where actual allocation happens. |
| // The returned object is default-constructed if it fails. |
| Allocation Allocation::New(fbl::AllocChecker& ac, memalloc::Type type, size_t size, |
| size_t alignment, ktl::optional<uint64_t> min_addr, |
| ktl::optional<uint64_t> max_addr) { |
| Allocation alloc; |
| fitx::result<fitx::failed, uint64_t> result = |
| GetPool().Allocate(type, size, alignment, min_addr, max_addr); |
| ac.arm(size, result.is_ok()); |
| if (result.is_ok()) { |
| alloc.data_ = {reinterpret_cast<ktl::byte*>(result.value()), size}; |
| alloc.alignment_ = alignment; |
| } |
| return alloc; |
| } |
| |
| // This is where actual deallocation happens. The destructor just calls this. |
| void Allocation::reset() { |
| if (!data_.empty()) { |
| auto result = GetPool().Free(reinterpret_cast<uint64_t>(data_.data()), data_.size()); |
| ZX_ASSERT(result.is_ok()); |
| data_ = {}; |
| } |
| } |
| |
| void Allocation::Resize(fbl::AllocChecker& ac, size_t new_size) { |
| ZX_ASSERT(!data_.empty()); |
| ZX_ASSERT(new_size > 0); |
| |
| if (new_size == size_bytes()) { |
| return; |
| } |
| |
| const memalloc::Range range = { |
| .addr = reinterpret_cast<uint64_t>(get()), |
| .size = size_bytes(), |
| .type = type_, |
| }; |
| auto result = GetPool().Resize(range, new_size, alignment_); |
| ac.arm(new_size, result.is_ok()); |
| if (result.is_ok()) { |
| auto* new_addr = reinterpret_cast<ktl::byte*>(ktl::move(result).value()); |
| if (new_addr != get()) { |
| memmove(new_addr, get(), size_bytes()); |
| } |
| data_ = {new_addr, new_size}; |
| } |
| } |