diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp index a0d150a08..2ab0a4670 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp @@ -145,6 +145,8 @@ namespace ams::kern { 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 { return this->IsEquivalentTo(rhs); } diff --git a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp index 727ddd476..c5ef13ac5 100644 --- a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp +++ b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp @@ -79,29 +79,7 @@ namespace ams::kern { /* Create a page group representing the segment. */ KPageGroup segment_pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer()); - if (size_t remaining_size = util::AlignUp(seg_size, PageSize); remaining_size != 0) { - /* 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(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; - } - } - } + MESOSPHERE_R_ABORT_UNLESS(pg.CopyRangeTo(segment_pg, seg_offset, util::AlignUp(seg_size, PageSize))); /* 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; MESOSPHERE_ABORT_UNLESS(start_address == 0); + /* Default fields in parameter to zero. */ + *out = {}; + /* Set fields in parameter. */ out->code_address = map_start + start_address; out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize; diff --git a/libraries/libmesosphere/source/kern_k_page_group.cpp b/libraries/libmesosphere/source/kern_k_page_group.cpp index af363240f..a51e0a1ff 100644 --- a/libraries/libmesosphere/source/kern_k_page_group.cpp +++ b/libraries/libmesosphere/source/kern_k_page_group.cpp @@ -84,6 +84,58 @@ namespace ams::kern { 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; + 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(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 { auto &mm = Kernel::GetMemoryManager();