blob: 628237a61da58a639a719cb85e98e3a6b3ebf14c [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 that tracks a virtual address range for performing MMU operations. This can be used
// directly, or by as part of a MappingCursor.
class VirtualAddressCursor {
public:
VirtualAddressCursor(vaddr_t vaddr, size_t size)
: start_vaddr_(vaddr), vaddr_(vaddr), size_(size) {}
// Sets offset used for |vaddr_rel| and returns true if the cursor lies within that offset and
// some specified maximum. Should only be called before the cursor has started to be used.
bool SetVaddrRelativeOffset(vaddr_t vaddr_rel_offset, size_t vaddr_rel_max) {
DEBUG_ASSERT(start_vaddr_ == vaddr_);
vaddr_t vaddr_rel = start_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;
}
void Consume(size_t ps) {
DEBUG_ASSERT(size_ >= ps);
vaddr_ += ps;
size_ -= ps;
}
// Returns a new cursor to the, possibly empty, virtual range that has already been processed by
// this cursor. The returned cursor will always be a subset of the original cursors range.
VirtualAddressCursor ProcessedRange() const {
VirtualAddressCursor ret(start_vaddr_, vaddr_ - start_vaddr_);
// As our new cursor is a subrange we know the relative offset will always be valid.
ret.vaddr_rel_offset_ = vaddr_rel_offset_;
return ret;
}
vaddr_t vaddr() const { return vaddr_; }
vaddr_t vaddr_rel() const { return vaddr_ - vaddr_rel_offset_; }
size_t size() const { return size_; }
private:
vaddr_t start_vaddr_;
vaddr_t vaddr_;
vaddr_t vaddr_rel_offset_ = 0;
size_t size_;
};
// Helper class for MMU implementations to track physical address ranges when installing mappings.
// If just processing a virtual address range, such as for unmapping, then the VirtualAddressCursor
// can be used instead.
class MappingCursor {
public:
MappingCursor(const paddr_t* paddrs, size_t paddr_count, size_t page_size, vaddr_t vaddr)
: paddrs_(paddrs), page_size_(page_size), vaddr_cursor_(vaddr, page_size * paddr_count) {
#ifdef DEBUG_ASSERT_IMPLEMENTED
paddr_count_ = paddr_count;
#endif
}
// See VirtualAddressCursor::SetVaddrRelativeOffset.
bool SetVaddrRelativeOffset(vaddr_t vaddr_rel_offset, size_t vaddr_rel_max) {
return vaddr_cursor_.SetVaddrRelativeOffset(vaddr_rel_offset, vaddr_rel_max);
}
void Consume(size_t ps) {
paddr_consumed_ += ps;
DEBUG_ASSERT(paddr_consumed_ <= page_size_);
if (paddr_consumed_ == page_size_) {
paddrs_++;
paddr_consumed_ = 0;
#ifdef DEBUG_ASSERT_IMPLEMENTED
DEBUG_ASSERT(paddr_count_ > 0);
paddr_count_--;
#endif
}
vaddr_cursor_.Consume(ps);
}
paddr_t paddr() const {
DEBUG_ASSERT(paddr_consumed_ < page_size_);
return (*paddrs_) + paddr_consumed_;
}
size_t PageRemaining() const { return page_size_ - paddr_consumed_; }
// Returns a new cursor to the, possibly empty, virtual range that has already been processed by
// this cursor. The returned cursor will always be a subset of the original cursors range and
// does not include the paddrs.
VirtualAddressCursor ProcessedRange() const { return vaddr_cursor_.ProcessedRange(); }
vaddr_t vaddr() const { return vaddr_cursor_.vaddr(); }
vaddr_t vaddr_rel() const { return vaddr_cursor_.vaddr_rel(); }
size_t size() const { return vaddr_cursor_.size(); }
private:
const paddr_t* paddrs_;
#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_;
VirtualAddressCursor vaddr_cursor_;
};
#endif // ZIRCON_KERNEL_VM_INCLUDE_VM_MAPPING_CURSOR_H_