Atmosphere/libraries/libmesosphere/source/kern_k_shared_memory.cpp
2021-10-23 21:13:26 -07:00

106 lines
4.0 KiB
C++

/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
Result KSharedMemory::Initialize(KProcess *owner, size_t size, ams::svc::MemoryPermission own_perm, ams::svc::MemoryPermission rem_perm) {
MESOSPHERE_ASSERT_THIS();
/* Set members. */
m_owner_process_id = owner->GetId();
m_owner_perm = own_perm;
m_remote_perm = rem_perm;
/* Get the number of pages. */
const size_t num_pages = util::DivideUp(size, PageSize);
MESOSPHERE_ASSERT(num_pages > 0);
/* Get the resource limit. */
KResourceLimit *reslimit = owner->GetResourceLimit();
/* Reserve memory for ourselves. */
KScopedResourceReservation memory_reservation(reslimit, ams::svc::LimitableResource_PhysicalMemoryMax, size);
R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached());
/* Allocate the memory. */
R_TRY(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(m_page_group), num_pages, owner->GetAllocateOption()));
/* Commit our reservation. */
memory_reservation.Commit();
/* Set our resource limit. */
m_resource_limit = reslimit;
m_resource_limit->Open();
/* Mark initialized. */
m_is_initialized = true;
/* Clear all pages in the memory. */
for (const auto &block : m_page_group) {
std::memset(GetVoidPointer(KMemoryLayout::GetLinearVirtualAddress(block.GetAddress())), 0, block.GetSize());
}
return ResultSuccess();
}
void KSharedMemory::Finalize() {
MESOSPHERE_ASSERT_THIS();
/* Get the number of pages. */
const size_t num_pages = m_page_group.GetNumPages();
const size_t size = num_pages * PageSize;
/* Close and finalize the page group. */
m_page_group.Close();
m_page_group.Finalize();
/* Release the memory reservation. */
m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, size);
m_resource_limit->Close();
}
Result KSharedMemory::Map(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process, ams::svc::MemoryPermission map_perm) {
MESOSPHERE_ASSERT_THIS();
/* Validate the size. */
R_UNLESS(m_page_group.GetNumPages() == util::DivideUp(size, PageSize), svc::ResultInvalidSize());
/* Validate the permission. */
const ams::svc::MemoryPermission test_perm = (process->GetId() == m_owner_process_id) ? m_owner_perm : m_remote_perm;
if (test_perm == ams::svc::MemoryPermission_DontCare) {
MESOSPHERE_ASSERT(map_perm == ams::svc::MemoryPermission_Read || map_perm == ams::svc::MemoryPermission_ReadWrite);
} else {
R_UNLESS(map_perm == test_perm, svc::ResultInvalidNewMemoryPermission());
}
/* Map the memory. */
return table->MapPageGroup(address, m_page_group, KMemoryState_Shared, ConvertToKMemoryPermission(map_perm));
}
Result KSharedMemory::Unmap(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_UNUSED(process);
/* Validate the size. */
R_UNLESS(m_page_group.GetNumPages() == util::DivideUp(size, PageSize), svc::ResultInvalidSize());
/* Unmap the memory. */
return table->UnmapPageGroup(address, m_page_group, KMemoryState_Shared);
}
}