kern: add KPageGroup::CopyRangeTo

This commit is contained in:
Michael Scire 2024-03-28 02:50:37 -07:00 committed by SciresM
parent 952188fc73
commit 6922eae3e7
3 changed files with 58 additions and 23 deletions

View File

@ -145,6 +145,8 @@ namespace ams::kern {
bool IsEquivalentTo(const KPageGroup &rhs) const; bool IsEquivalentTo(const KPageGroup &rhs) const;
Result CopyRangeTo(KPageGroup &out, size_t offset, size_t size) const;
ALWAYS_INLINE bool operator==(const KPageGroup &rhs) const { ALWAYS_INLINE bool operator==(const KPageGroup &rhs) const {
return this->IsEquivalentTo(rhs); return this->IsEquivalentTo(rhs);
} }

View File

@ -79,29 +79,7 @@ namespace ams::kern {
/* Create a page group representing the segment. */ /* Create a page group representing the segment. */
KPageGroup segment_pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer()); KPageGroup segment_pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer());
if (size_t remaining_size = util::AlignUp(seg_size, PageSize); remaining_size != 0) { MESOSPHERE_R_ABORT_UNLESS(pg.CopyRangeTo(segment_pg, seg_offset, util::AlignUp(seg_size, PageSize)));
/* Find the pages whose data corresponds to the segment. */
size_t cur_offset = 0;
for (auto it = pg.begin(); it != pg.end() && remaining_size > 0; ++it) {
/* Get the current size. */
const size_t cur_size = it->GetSize();
/* Determine if the offset is in range. */
const size_t rel_diff = seg_offset - cur_offset;
const bool is_before = cur_offset <= seg_offset;
cur_offset += cur_size;
if (is_before && seg_offset < cur_offset) {
/* It is, so add the block. */
const size_t block_size = std::min<size_t>(cur_size - rel_diff, remaining_size);
MESOSPHERE_R_ABORT_UNLESS(segment_pg.AddBlock(it->GetAddress() + rel_diff, block_size / PageSize));
/* Advance. */
cur_offset = seg_offset + block_size;
remaining_size -= block_size;
seg_offset += block_size;
}
}
}
/* Setup the new page group's memory so that we can load the segment. */ /* Setup the new page group's memory so that we can load the segment. */
{ {
@ -226,6 +204,9 @@ namespace ams::kern {
const uintptr_t map_end = map_start + map_size; const uintptr_t map_end = map_start + map_size;
MESOSPHERE_ABORT_UNLESS(start_address == 0); MESOSPHERE_ABORT_UNLESS(start_address == 0);
/* Default fields in parameter to zero. */
*out = {};
/* Set fields in parameter. */ /* Set fields in parameter. */
out->code_address = map_start + start_address; out->code_address = map_start + start_address;
out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize; out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize;

View File

@ -84,6 +84,58 @@ namespace ams::kern {
R_SUCCEED(); R_SUCCEED();
} }
Result KPageGroup::CopyRangeTo(KPageGroup &out, size_t range_offset, size_t range_size) const {
/* Get the previous last block for the group. */
KBlockInfo * const out_last = out.m_last_block;
const auto out_last_addr = out_last != nullptr ? out_last->GetAddress() : Null<KPhysicalAddress>;
const auto out_last_np = out_last != nullptr ? out_last->GetNumPages() : 0;
/* Ensure we cleanup the group on failure. */
ON_RESULT_FAILURE {
KBlockInfo *cur = out_last != nullptr ? out_last->GetNext() : out.m_first_block;
while (cur != nullptr) {
KBlockInfo *next = cur->GetNext();
out.m_manager->Free(cur);
cur = next;
}
if (out_last != nullptr) {
out_last->Initialize(out_last_addr, out_last_np);
out_last->SetNext(nullptr);
} else {
out.m_first_block = nullptr;
}
out.m_last_block = out_last;
};
/* Find the pages within the requested range. */
size_t cur_offset = 0, remaining_size = range_size;
for (auto it = this->begin(); it != this->end() && remaining_size > 0; ++it) {
/* Get the current size. */
const size_t cur_size = it->GetSize();
/* Determine if the offset is in range. */
const size_t rel_diff = range_offset - cur_offset;
const bool is_before = cur_offset <= range_offset;
cur_offset += cur_size;
if (is_before && range_offset < cur_offset) {
/* It is, so add the block. */
const size_t block_size = std::min<size_t>(cur_size - rel_diff, remaining_size);
R_TRY(out.AddBlock(it->GetAddress() + rel_diff, block_size / PageSize));
/* Advance. */
cur_offset = range_offset + block_size;
remaining_size -= block_size;
range_offset += block_size;
}
}
/* Check that we successfully copied the range. */
MESOSPHERE_ABORT_UNLESS(remaining_size == 0);
R_SUCCEED();
}
void KPageGroup::Open() const { void KPageGroup::Open() const {
auto &mm = Kernel::GetMemoryManager(); auto &mm = Kernel::GetMemoryManager();