blob: cfdceaefad4d73554bba61074d2f5299011c1264 [file] [log] [blame]
// Copyright 2016 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
#pragma once
#include <fbl/intrusive_double_list.h>
#include <fbl/macros.h>
#include <trace.h>
#include <vm/pmm.h>
#include <zircon/types.h>
#define PMM_ENABLE_FREE_FILL 0
#define PMM_FREE_FILL_BYTE 0x42
class PmmArena : public fbl::DoublyLinkedListable<PmmArena*> {
public:
PmmArena(const pmm_arena_info_t* info);
~PmmArena();
DISALLOW_COPY_ASSIGN_AND_MOVE(PmmArena);
// set up the per page structures, allocated out of the boot time allocator
void BootAllocArray();
#if PMM_ENABLE_FREE_FILL
void EnforceFill();
#endif
void Dump(bool dump_pages, bool dump_free_ranges);
// accessors
const pmm_arena_info_t& info() const { return info_; }
const char* name() const { return info_.name; }
paddr_t base() const { return info_.base; }
size_t size() const { return info_.size; }
unsigned int flags() const { return info_.flags; }
unsigned int priority() const { return info_.priority; }
size_t free_count() const { return free_count_; };
// Counts the number of pages in every state. For each page in the arena,
// increments the corresponding VM_PAGE_STATE_*-indexed entry of
// |state_count|. Does not zero out the entries first.
void CountStates(size_t state_count[_VM_PAGE_STATE_COUNT]) const;
vm_page_t* get_page(size_t index) { return &page_array_[index]; }
// main allocation routines
vm_page_t* AllocPage(paddr_t* pa);
vm_page_t* AllocSpecific(paddr_t pa);
size_t AllocPages(size_t count, list_node* list);
size_t AllocContiguous(size_t count, uint8_t alignment_log2, paddr_t* pa, struct list_node* list);
zx_status_t FreePage(vm_page_t* page);
// helpers
bool page_belongs_to_arena(const vm_page* page) const {
uintptr_t page_addr = reinterpret_cast<uintptr_t>(page);
uintptr_t page_array_base = reinterpret_cast<uintptr_t>(page_array_);
return ((page_addr >= page_array_base) &&
(page_addr < (page_array_base + (info_.size / PAGE_SIZE) * VM_PAGE_STRUCT_SIZE)));
}
paddr_t page_address_from_arena(const vm_page* page) const {
uintptr_t page_addr = reinterpret_cast<uintptr_t>(page);
uintptr_t page_array_base = reinterpret_cast<uintptr_t>(page_array_);
return ((paddr_t)(((page_addr - page_array_base) / VM_PAGE_STRUCT_SIZE) * PAGE_SIZE + info_.base));
}
bool address_in_arena(paddr_t address) const {
return (address >= info_.base && address <= info_.base + info_.size - 1);
}
private:
#if PMM_ENABLE_FREE_FILL
void FreeFill(vm_page_t* page);
void CheckFreeFill(vm_page_t* page);
#endif
const pmm_arena_info_t info_;
vm_page_t* page_array_ = nullptr;
size_t free_count_ = 0;
list_node free_list_ = LIST_INITIAL_VALUE(free_list_);
#if PMM_ENABLE_FREE_FILL
bool enforce_fill_ = false;
#endif
};