/* * 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 . */ #include 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); } }