blob: b9e96ca5f4ddc312d408540567b4b903c11a2cf4 [file] [log] [blame]
// Copyright 2023 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_VM_INCLUDE_VM_MAPPING_CURSOR_H_
#define ZIRCON_KERNEL_VM_INCLUDE_VM_MAPPING_CURSOR_H_
#include <assert.h>
#include <sys/types.h>
// Helper class for MMU implementations to track virtual and physical address ranges when installing
// and removing mappings.
class MappingCursor {
public:
MappingCursor() = default;
MappingCursor(vaddr_t vaddr, size_t size) : vaddr_(vaddr), size_(size) {}
MappingCursor(const paddr_t* paddrs, size_t paddr_count, size_t page_size, vaddr_t vaddr)
: paddrs_(paddrs), page_size_(page_size), vaddr_(vaddr), size_(page_size * paddr_count) {
#ifdef DEBUG_ASSERT_IMPLEMENTED
paddr_count_ = paddr_count;
#endif
}
// Sets offset used for |vaddr_rel| and returns true if the cursor lies within that offset and
// some specified maximum.
bool SetVaddrRelativeOffset(vaddr_t vaddr_rel_offset, size_t vaddr_rel_max) {
vaddr_t vaddr_rel = vaddr_ - vaddr_rel_offset;
if (vaddr_rel > vaddr_rel_max - size_ || size_ > vaddr_rel_max) {
return false;
}
vaddr_rel_offset_ = vaddr_rel_offset;
return true;
}
// Update the cursor to skip over a not-present page table entry.
void SkipEntry(size_t ps) {
// Cannot just increase by given size as the both the current or final vaddr may not be aligned
// to this given size. These cases only happen as the very first or very last entry we will
// examine respectively, but still must be handled here.
// Calculate the amount the cursor should skip to get to the next entry at
// this page table level.
const size_t next_entry_offset = ps - (vaddr_ & (ps - 1));
// If our endpoint was in the middle of this range, clamp the
// amount we remove from the cursor
const size_t consume = (size_ > next_entry_offset) ? next_entry_offset : size_;
DEBUG_ASSERT(size_ >= consume);
size_ -= consume;
vaddr_ += consume;
// Skipping entries has no meaning if we are attempting to create new mappings as we cannot
// manipulate the paddr in a sensible way, so we expect this to not get called.
DEBUG_ASSERT(!paddrs_);
DEBUG_ASSERT(page_size_ == 0);
}
void ConsumePAddr(size_t ps) {
DEBUG_ASSERT(paddrs_);
paddr_consumed_ += ps;
DEBUG_ASSERT(paddr_consumed_ <= page_size_);
vaddr_ += ps;
DEBUG_ASSERT(size_ >= ps);
size_ -= ps;
if (paddr_consumed_ == page_size_) {
paddrs_++;
paddr_consumed_ = 0;
#ifdef DEBUG_ASSERT_IMPLEMENTED
DEBUG_ASSERT(paddr_count_ > 0);
paddr_count_--;
#endif
}
}
void ConsumeVAddr(size_t ps) {
// If physical addresses are being tracked for creating mappings then ConsumePAddr should be
// called, so validate there are no paddrs we are going to desync with.
DEBUG_ASSERT(!paddrs_);
DEBUG_ASSERT(page_size_ == 0);
vaddr_ += ps;
DEBUG_ASSERT(size_ >= ps);
size_ -= ps;
}
// Provides a way to transition a mapping cursor from one that tracks paddrs to one that just
// tracks the remaining virtual range. This is useful when a cursor was being used to track
// mapping pages but then needs to be used to just track the virtual range to unmap / rollback.
void DropPAddrs() {
paddrs_ = nullptr;
page_size_ = 0;
paddr_consumed_ = 0;
}
paddr_t paddr() const {
DEBUG_ASSERT(paddrs_);
DEBUG_ASSERT(size_ > 0);
return (*paddrs_) + paddr_consumed_;
}
size_t PageRemaining() const {
DEBUG_ASSERT(paddrs_);
return page_size_ - paddr_consumed_;
}
vaddr_t vaddr() const { return vaddr_; }
vaddr_t vaddr_rel() const { return vaddr_ - vaddr_rel_offset_; }
size_t size() const { return size_; }
private:
// Physical address is optional and only applies when mapping in new pages, and not manipulating
// existing mappings.
const paddr_t* paddrs_ = nullptr;
#ifdef DEBUG_ASSERT_IMPLEMENTED
// We have no need to actually track the total number of elements in the paddrs array, as this
// should be a simple size/paddr_size. To guard against code mistakes though, we separately track
// this just in debug mode.
size_t paddr_count_ = 0;
#endif
size_t paddr_consumed_ = 0;
size_t page_size_ = 0;
vaddr_t vaddr_;
vaddr_t vaddr_rel_offset_ = 0;
size_t size_;
};
#endif // ZIRCON_KERNEL_VM_INCLUDE_VM_MAPPING_CURSOR_H_