mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
os: implement SharedMemory, update AslrSpaceManager
This commit is contained in:
parent
101e3087fe
commit
82f3416799
20 changed files with 737 additions and 196 deletions
|
@ -34,6 +34,7 @@
|
||||||
#include <stratosphere/os/os_busy_mutex.hpp>
|
#include <stratosphere/os/os_busy_mutex.hpp>
|
||||||
#include <stratosphere/os/os_rw_busy_mutex.hpp>
|
#include <stratosphere/os/os_rw_busy_mutex.hpp>
|
||||||
#include <stratosphere/os/os_rw_lock.hpp>
|
#include <stratosphere/os/os_rw_lock.hpp>
|
||||||
|
#include <stratosphere/os/os_shared_memory.hpp>
|
||||||
#include <stratosphere/os/os_transfer_memory.hpp>
|
#include <stratosphere/os/os_transfer_memory.hpp>
|
||||||
#include <stratosphere/os/os_semaphore.hpp>
|
#include <stratosphere/os/os_semaphore.hpp>
|
||||||
#include <stratosphere/os/os_event.hpp>
|
#include <stratosphere/os/os_event.hpp>
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os/os_shared_memory_types.hpp>
|
||||||
|
#include <stratosphere/os/os_shared_memory_api.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
class SharedMemory {
|
||||||
|
NON_COPYABLE(SharedMemory);
|
||||||
|
NON_MOVEABLE(SharedMemory);
|
||||||
|
private:
|
||||||
|
SharedMemoryType m_shared_memory;
|
||||||
|
public:
|
||||||
|
constexpr SharedMemory() : m_shared_memory{ .state = SharedMemoryType::State_NotInitialized } {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemory(size_t size, MemoryPermission my_perm, MemoryPermission other_perm) {
|
||||||
|
R_ABORT_UNLESS(CreateSharedMemory(std::addressof(m_shared_memory), size, my_perm, other_perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemory(size_t size, Handle handle, bool managed) {
|
||||||
|
this->Attach(size, handle, managed);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SharedMemory() {
|
||||||
|
if (m_shared_memory.state == SharedMemoryType::State_NotInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DestroySharedMemory(std::addressof(m_shared_memory));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Attach(size_t size, Handle handle, bool managed) {
|
||||||
|
return AttachSharedMemory(std::addressof(m_shared_memory), size, handle, managed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Map(MemoryPermission perm) {
|
||||||
|
return MapSharedMemory(std::addressof(m_shared_memory), perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unmap() {
|
||||||
|
return UnmapSharedMemory(std::addressof(m_shared_memory));
|
||||||
|
}
|
||||||
|
|
||||||
|
void *GetMappedAddress() const {
|
||||||
|
return GetSharedMemoryAddress(std::addressof(m_shared_memory));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSize() const {
|
||||||
|
return GetSharedMemorySize(std::addressof(m_shared_memory));
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle GetHandle() const {
|
||||||
|
return GetSharedMemoryHandle(std::addressof(m_shared_memory));
|
||||||
|
}
|
||||||
|
|
||||||
|
operator SharedMemoryType &() {
|
||||||
|
return m_shared_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator const SharedMemoryType &() const {
|
||||||
|
return m_shared_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemoryType *GetBase() {
|
||||||
|
return std::addressof(m_shared_memory);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os/os_memory_permission.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
struct SharedMemoryType;
|
||||||
|
|
||||||
|
Result CreateSharedMemory(SharedMemoryType *shared_memory, size_t size, MemoryPermission my_perm, MemoryPermission other_perm);
|
||||||
|
|
||||||
|
void AttachSharedMemory(SharedMemoryType *shared_memory, size_t size, Handle handle, bool managed);
|
||||||
|
|
||||||
|
void DestroySharedMemory(SharedMemoryType *shared_memory);
|
||||||
|
|
||||||
|
void *MapSharedMemory(SharedMemoryType *shared_memory, MemoryPermission perm);
|
||||||
|
void UnmapSharedMemory(SharedMemoryType *shared_memory);
|
||||||
|
|
||||||
|
void *GetSharedMemoryAddress(const SharedMemoryType *shared_memory);
|
||||||
|
size_t GetSharedMemorySize(const SharedMemoryType *shared_memory);
|
||||||
|
Handle GetSharedMemoryHandle(const SharedMemoryType *shared_memory);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
struct SharedMemoryType {
|
||||||
|
enum State {
|
||||||
|
State_NotInitialized = 0,
|
||||||
|
State_Initialized = 1,
|
||||||
|
State_Mapped = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 state;
|
||||||
|
bool handle_managed;
|
||||||
|
bool allocated;
|
||||||
|
|
||||||
|
void *address;
|
||||||
|
size_t size;
|
||||||
|
Handle handle;
|
||||||
|
|
||||||
|
mutable impl::InternalCriticalSectionStorage cs_shared_memory;
|
||||||
|
};
|
||||||
|
static_assert(std::is_trivial<SharedMemoryType>::value);
|
||||||
|
|
||||||
|
}
|
|
@ -170,7 +170,7 @@ namespace ams::htcs::server {
|
||||||
|
|
||||||
/* Attach the transfer memory. */
|
/* Attach the transfer memory. */
|
||||||
os::TransferMemoryType tmem;
|
os::TransferMemoryType tmem;
|
||||||
R_ABORT_UNLESS(os::AttachTransferMemory(std::addressof(tmem), static_cast<size_t>(aligned_size), mem_handle.GetValue(), true));
|
os::AttachTransferMemory(std::addressof(tmem), static_cast<size_t>(aligned_size), mem_handle.GetValue(), true);
|
||||||
ON_SCOPE_EXIT { os::DestroyTransferMemory(std::addressof(tmem)); };
|
ON_SCOPE_EXIT { os::DestroyTransferMemory(std::addressof(tmem)); };
|
||||||
|
|
||||||
/* Map the transfer memory. */
|
/* Map the transfer memory. */
|
||||||
|
@ -201,7 +201,7 @@ namespace ams::htcs::server {
|
||||||
|
|
||||||
/* Attach the transfer memory. */
|
/* Attach the transfer memory. */
|
||||||
os::TransferMemoryType tmem;
|
os::TransferMemoryType tmem;
|
||||||
R_ABORT_UNLESS(os::AttachTransferMemory(std::addressof(tmem), static_cast<size_t>(aligned_size), mem_handle.GetValue(), true));
|
os::AttachTransferMemory(std::addressof(tmem), static_cast<size_t>(aligned_size), mem_handle.GetValue(), true);
|
||||||
ON_SCOPE_EXIT { os::DestroyTransferMemory(std::addressof(tmem)); };
|
ON_SCOPE_EXIT { os::DestroyTransferMemory(std::addressof(tmem)); };
|
||||||
|
|
||||||
/* Map the transfer memory. */
|
/* Map the transfer memory. */
|
||||||
|
|
|
@ -14,73 +14,114 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "os_address_space_allocator_types.hpp"
|
#include "os_address_space_allocator.hpp"
|
||||||
#include "os_rng_manager.hpp"
|
#include "os_rng_manager.hpp"
|
||||||
|
|
||||||
namespace ams::os::impl {
|
namespace ams::os::impl {
|
||||||
|
|
||||||
AddressSpaceAllocator::AddressSpaceAllocator(uintptr_t sa, uintptr_t ea, size_t gsz) : cs() {
|
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||||
/* Check preconditions. */
|
AddressSpaceAllocatorBase<AddressType, SizeType>::AddressSpaceAllocatorBase(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions) : m_critical_section() {
|
||||||
AMS_ASSERT(sa >= gsz);
|
/* Check pre-conditions. */
|
||||||
AMS_ASSERT(ea + gsz >= ea);
|
AMS_ASSERT(start_address >= guard_size);
|
||||||
|
AMS_ASSERT(end_address + guard_size >= end_address);
|
||||||
|
|
||||||
this->guard_page_count = util::DivideUp(gsz, MemoryPageSize);
|
/* Set member variables. */
|
||||||
this->start_page = sa / MemoryPageSize;
|
m_guard_page_count = util::DivideUp(guard_size, MemoryPageSize);
|
||||||
this->end_page = (ea / MemoryPageSize) + this->guard_page_count;
|
m_start_page = start_address / MemoryPageSize;
|
||||||
|
m_end_page = (end_address / MemoryPageSize) + m_guard_page_count;
|
||||||
|
|
||||||
|
/* Check forbidden region count. */
|
||||||
|
AMS_ASSERT(num_forbidden_regions <= MaxForbiddenRegions);
|
||||||
|
|
||||||
|
/* Set forbidden regions. */
|
||||||
|
for (size_t i = 0; i < num_forbidden_regions; ++i) {
|
||||||
|
if (const auto ®ion = forbidden_regions[i]; region.size > 0) {
|
||||||
|
/* Check region is valid. */
|
||||||
|
AMS_ASSERT(util::IsAligned(region.address, MemoryPageSize));
|
||||||
|
AMS_ASSERT(util::IsAligned(region.size, MemoryPageSize));
|
||||||
|
AMS_ASSERT((region.address / MemoryPageSize) >= m_guard_page_count);
|
||||||
|
AMS_ASSERT(region.address < region.address + region.size);
|
||||||
|
|
||||||
|
/* Set region. */
|
||||||
|
const auto idx = m_forbidden_region_count++;
|
||||||
|
m_forbidden_region_start_pages[idx] = (region.address / MemoryPageSize) - m_guard_page_count;
|
||||||
|
m_forbidden_region_end_pages[idx] = ((region.address + region.size) / MemoryPageSize) + m_guard_page_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddressSpaceAllocator::GetNextNonOverlappedNodeUnsafe(uintptr_t page, size_t page_num) {
|
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||||
AMS_ASSERT(page < (page + page_num));
|
bool AddressSpaceAllocatorBase<AddressType, SizeType>::CheckGuardSpace(AddressType address, SizeType size, SizeType guard_size) {
|
||||||
|
return this->CheckFreeSpace(address - guard_size, guard_size) && this->CheckFreeSpace(address + size, guard_size);
|
||||||
return CheckFreeSpace(page * MemoryPageSize, page_num * MemoryPageSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *AddressSpaceAllocator::AllocateSpace(size_t size, size_t align) {
|
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||||
|
bool AddressSpaceAllocatorBase<AddressType, SizeType>::GetNextNonOverlappedNodeUnsafe(AddressType page, SizeType page_count) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(page < page + page_count);
|
||||||
|
|
||||||
|
return this->CheckFreeSpace(page * MemoryPageSize, page_count * MemoryPageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||||
|
AddressType AddressSpaceAllocatorBase<AddressType, SizeType>::AllocateSpace(SizeType size, SizeType align, SizeType align_offset) {
|
||||||
/* Check pre-conditions. */
|
/* Check pre-conditions. */
|
||||||
AMS_ASSERT(align > 0);
|
AMS_ASSERT(align > 0);
|
||||||
|
AMS_ASSERT((align_offset & ~(align - 1)) == 0);
|
||||||
|
AMS_ASSERT(util::IsAligned(align_offset, MemoryPageSize));
|
||||||
|
AMS_ASSERT(util::IsAligned(align, MemoryPageSize));
|
||||||
|
|
||||||
/* Determine the page count. */
|
/* Determine the page count. */
|
||||||
const size_t page_count = util::DivideUp(size, MemoryPageSize);
|
const SizeType page_count = util::DivideUp(size, MemoryPageSize);
|
||||||
|
|
||||||
/* Determine the alignment pages. */
|
/* Determine alignment page counts. */
|
||||||
AMS_ASSERT(util::IsAligned(align, MemoryPageSize));
|
const SizeType align_offset_page_count = align_offset / MemoryPageSize;
|
||||||
const size_t align_page_count = align / MemoryPageSize;
|
const SizeType align_page_count = align / MemoryPageSize;
|
||||||
|
|
||||||
/* Check the page count. */
|
/* Check page counts. */
|
||||||
if (page_count > this->end_page - this->guard_page_count) {
|
if (page_count + align_offset_page_count > m_end_page - m_guard_page_count) {
|
||||||
return nullptr;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine the range to look in. */
|
/* Determine the range to look in. */
|
||||||
const uintptr_t rand_start = util::DivideUp(this->start_page + this->guard_page_count, align_page_count);
|
const AddressType rand_start = (align_offset_page_count <= m_start_page + m_guard_page_count) ? util::DivideUp(m_start_page + m_guard_page_count - align_offset_page_count, align_page_count) : 0;
|
||||||
const uintptr_t rand_end = (this->end_page - page_count - this->guard_page_count) / align_page_count;
|
const AddressType rand_end = (m_end_page - page_count - align_offset_page_count - m_guard_page_count) / align_page_count;
|
||||||
|
|
||||||
/* Check that we can find a space. */
|
/* Check that we can find a space. */
|
||||||
if (rand_start > rand_end) {
|
if (rand_start > rand_end) {
|
||||||
return nullptr;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to find space up to 512 times. */
|
/* Try to find a space up to 512 times. */
|
||||||
for (size_t i = 0; i < 512; ++i) {
|
for (size_t i = 0; i < 512; ++i) {
|
||||||
/* Acquire exclusive access before doing calculations. */
|
/* Acquire exclusive access before doing calculations. */
|
||||||
std::scoped_lock lk(this->cs);
|
std::scoped_lock lk(m_critical_section);
|
||||||
|
|
||||||
/* Determine a random page. */
|
/* Determine a random page. */
|
||||||
const uintptr_t target = ((GetRngManager().GenerateRandomU64() % (rand_end - rand_start + 1)) + rand_start) * align_page_count;
|
const AddressType target = (((GetRngManager().GenerateRandomU64() % (rand_end - rand_start + 1)) + rand_start) * align_page_count) + align_offset_page_count;
|
||||||
AMS_ASSERT(this->start_page <= target - this->guard_page_count && target + page_count + this->guard_page_count <= this->end_page);
|
AMS_ASSERT(m_start_page <= target - m_guard_page_count && target + page_count + m_guard_page_count <= m_end_page);
|
||||||
|
|
||||||
|
/* Check that the page is not forbidden. */
|
||||||
|
bool forbidden = false;
|
||||||
|
for (size_t j = 0; j < m_forbidden_region_count; ++j) {
|
||||||
|
if (m_forbidden_region_start_pages[j] < target + page_count && target < m_forbidden_region_end_pages[j]) {
|
||||||
|
forbidden = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If the page is valid, use it. */
|
/* If the page is valid, use it. */
|
||||||
if (GetNextNonOverlappedNodeUnsafe(target - this->guard_page_count, page_count + 2 * this->guard_page_count)) {
|
if (!forbidden && this->GetNextNonOverlappedNodeUnsafe(target - m_guard_page_count, page_count + 2 * m_guard_page_count)) {
|
||||||
return reinterpret_cast<void *>(target * MemoryPageSize);
|
return target * MemoryPageSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We failed to find space. */
|
/* We failed to find space. */
|
||||||
return nullptr;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddressSpaceAllocator::CheckGuardSpace(uintptr_t address, size_t size, size_t guard_size) {
|
/* Instantiate template. */
|
||||||
return CheckFreeSpace(address - guard_size, guard_size) && CheckFreeSpace(address + size, guard_size);
|
/* TODO: instantiate <u64, u64> on 32-bit? */
|
||||||
}
|
template class AddressSpaceAllocatorBase<uintptr_t, size_t>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||||
|
class AddressSpaceAllocatorBase {
|
||||||
|
NON_COPYABLE(AddressSpaceAllocatorBase);
|
||||||
|
NON_MOVEABLE(AddressSpaceAllocatorBase);
|
||||||
|
private:
|
||||||
|
static constexpr size_t MaxForbiddenRegions = 2;
|
||||||
|
private:
|
||||||
|
InternalCriticalSection m_critical_section;
|
||||||
|
AddressType m_start_page;
|
||||||
|
AddressType m_end_page;
|
||||||
|
SizeType m_guard_page_count;
|
||||||
|
AddressType m_forbidden_region_start_pages[MaxForbiddenRegions];
|
||||||
|
AddressType m_forbidden_region_end_pages[MaxForbiddenRegions];
|
||||||
|
size_t m_forbidden_region_count;
|
||||||
|
public:
|
||||||
|
AddressSpaceAllocatorBase(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions);
|
||||||
|
|
||||||
|
AddressType AllocateSpace(SizeType size, SizeType align, SizeType align_offset);
|
||||||
|
|
||||||
|
bool CheckGuardSpace(AddressType address, SizeType size, SizeType guard_size);
|
||||||
|
private:
|
||||||
|
bool GetNextNonOverlappedNodeUnsafe(AddressType page, SizeType page_count);
|
||||||
|
public:
|
||||||
|
virtual bool CheckFreeSpace(AddressType address, SizeType size) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ATMOSPHERE_OS_HORIZON
|
||||||
|
#include "os_address_space_allocator_impl.os.horizon.hpp"
|
||||||
|
#else
|
||||||
|
#error "Unknown OS for AddressSpaceAllocator"
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
struct AddressSpaceAllocatorForbiddenRegion {
|
||||||
|
u64 address;
|
||||||
|
u64 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -18,15 +18,22 @@
|
||||||
|
|
||||||
namespace ams::os::impl {
|
namespace ams::os::impl {
|
||||||
|
|
||||||
inline bool CheckFreeSpace(uintptr_t address, size_t size) {
|
class AddressSpaceAllocator final : public AddressSpaceAllocatorBase<uintptr_t, size_t> {
|
||||||
/* Query the memory. */
|
private:
|
||||||
svc::MemoryInfo memory_info;
|
using Base = AddressSpaceAllocatorBase<uintptr_t, size_t>;
|
||||||
svc::PageInfo page_info;
|
public:
|
||||||
const auto result = svc::QueryMemory(std::addressof(memory_info), std::addressof(page_info), address);
|
using Base::Base;
|
||||||
R_ASSERT(result);
|
public:
|
||||||
AMS_UNUSED(result);
|
virtual bool CheckFreeSpace(uintptr_t address, size_t size) override {
|
||||||
|
/* Query the memory. */
|
||||||
|
svc::MemoryInfo memory_info;
|
||||||
|
svc::PageInfo page_info;
|
||||||
|
const auto result = svc::QueryMemory(std::addressof(memory_info), std::addressof(page_info), address);
|
||||||
|
R_ASSERT(result);
|
||||||
|
AMS_UNUSED(result);
|
||||||
|
|
||||||
return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.addr + memory_info.size;
|
return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.addr + memory_info.size;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 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/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_OS_HORIZON
|
|
||||||
#include "os_address_space_allocator_impl.os.horizon.hpp"
|
|
||||||
#else
|
|
||||||
#error "Unknown OS for AddressSpaceAllocatorImpl"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ams::os::impl {
|
|
||||||
|
|
||||||
class AddressSpaceAllocator {
|
|
||||||
NON_COPYABLE(AddressSpaceAllocator);
|
|
||||||
NON_MOVEABLE(AddressSpaceAllocator);
|
|
||||||
private:
|
|
||||||
InternalCriticalSection cs;
|
|
||||||
uintptr_t start_page;
|
|
||||||
uintptr_t end_page;
|
|
||||||
size_t guard_page_count;
|
|
||||||
private:
|
|
||||||
bool GetNextNonOverlappedNodeUnsafe(uintptr_t page, size_t page_num);
|
|
||||||
public:
|
|
||||||
AddressSpaceAllocator(uintptr_t sa, uintptr_t ea, size_t gsz);
|
|
||||||
|
|
||||||
void *AllocateSpace(size_t size, size_t align);
|
|
||||||
bool CheckGuardSpace(uintptr_t address, size_t size, size_t guard_size);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||||
|
|
||||||
namespace ams::os::impl {
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
@ -28,22 +29,39 @@ namespace ams::os::impl {
|
||||||
static constexpr u64 AslrSize64BitDeprecated = 0x0078000000ul;
|
static constexpr u64 AslrSize64BitDeprecated = 0x0078000000ul;
|
||||||
static constexpr u64 AslrBase64Bit = 0x0008000000ul;
|
static constexpr u64 AslrBase64Bit = 0x0008000000ul;
|
||||||
static constexpr u64 AslrSize64Bit = 0x7FF8000000ul;
|
static constexpr u64 AslrSize64Bit = 0x7FF8000000ul;
|
||||||
|
|
||||||
|
static constexpr size_t ForbiddenRegionCount = 2;
|
||||||
private:
|
private:
|
||||||
static u64 GetAslrInfo(svc::InfoType type) {
|
static u64 GetAslrInfo(svc::InfoType type) {
|
||||||
u64 value;
|
u64 value;
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), type, svc::PseudoHandle::CurrentProcess, 0));
|
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), type, svc::PseudoHandle::CurrentProcess, 0));
|
||||||
|
|
||||||
static_assert(std::same_as<size_t, uintptr_t>);
|
static_assert(std::same_as<size_t, uintptr_t>);
|
||||||
AMS_ASSERT(value <= std::numeric_limits<size_t>::max());
|
AMS_ASSERT(value <= std::numeric_limits<size_t>::max());
|
||||||
|
|
||||||
return static_cast<u64>(value & std::numeric_limits<size_t>::max());
|
return static_cast<u64>(value & std::numeric_limits<size_t>::max());
|
||||||
}
|
}
|
||||||
|
private:
|
||||||
|
AddressSpaceAllocatorForbiddenRegion m_forbidden_regions[ForbiddenRegionCount];
|
||||||
public:
|
public:
|
||||||
constexpr AslrSpaceManagerHorizonImpl() = default;
|
AslrSpaceManagerHorizonImpl() {
|
||||||
|
m_forbidden_regions[0] = { .address = GetHeapSpaceBeginAddress(), .size = GetHeapSpaceSize() };
|
||||||
|
m_forbidden_regions[1] = { .address = GetAliasSpaceBeginAddress(), .size = GetAliasSpaceSize() };
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() const {
|
||||||
|
return m_forbidden_regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t GetForbiddenRegionCount() {
|
||||||
|
return ForbiddenRegionCount;
|
||||||
|
}
|
||||||
|
|
||||||
static u64 GetHeapSpaceBeginAddress() {
|
static u64 GetHeapSpaceBeginAddress() {
|
||||||
return GetAslrInfo(svc::InfoType_HeapRegionAddress);
|
return GetAslrInfo(svc::InfoType_HeapRegionAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 GetHeapSpaceBeginSize() {
|
static u64 GetHeapSpaceSize() {
|
||||||
return GetAslrInfo(svc::InfoType_HeapRegionSize);
|
return GetAslrInfo(svc::InfoType_HeapRegionSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +69,7 @@ namespace ams::os::impl {
|
||||||
return GetAslrInfo(svc::InfoType_AliasRegionAddress);
|
return GetAslrInfo(svc::InfoType_AliasRegionAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 GetAliasSpaceBeginSize() {
|
static u64 GetAliasSpaceSize() {
|
||||||
return GetAslrInfo(svc::InfoType_AliasRegionSize);
|
return GetAslrInfo(svc::InfoType_AliasRegionSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_address_space_allocator.hpp"
|
||||||
#include "os_address_space_allocator_types.hpp"
|
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_OS_HORIZON
|
#ifdef ATMOSPHERE_OS_HORIZON
|
||||||
#include "os_aslr_space_manager_impl.os.horizon.hpp"
|
#include "os_aslr_space_manager_impl.os.horizon.hpp"
|
||||||
|
@ -29,26 +28,67 @@ namespace ams::os::impl {
|
||||||
constexpr inline size_t AslrSpaceLargeAlign = 2_MB;
|
constexpr inline size_t AslrSpaceLargeAlign = 2_MB;
|
||||||
constexpr inline size_t AslrSpaceGuardSize = 4 * MemoryPageSize;
|
constexpr inline size_t AslrSpaceGuardSize = 4 * MemoryPageSize;
|
||||||
|
|
||||||
class AslrSpaceManager {
|
template<typename Allocator, typename Impl>
|
||||||
NON_COPYABLE(AslrSpaceManager);
|
class AslrSpaceManagerTemplate {
|
||||||
NON_MOVEABLE(AslrSpaceManager);
|
NON_COPYABLE(AslrSpaceManagerTemplate);
|
||||||
|
NON_MOVEABLE(AslrSpaceManagerTemplate);
|
||||||
private:
|
private:
|
||||||
AddressSpaceAllocator allocator;
|
Impl m_impl;
|
||||||
AslrSpaceManagerImpl impl;
|
Allocator m_allocator;
|
||||||
public:
|
public:
|
||||||
AslrSpaceManager() : allocator(impl.GetAslrSpaceBeginAddress(), impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize) { /* ... */ }
|
AslrSpaceManagerTemplate() : m_impl(), m_allocator(m_impl.GetAslrSpaceBeginAddress(), m_impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount()) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
void *AllocateSpace(size_t size) {
|
uintptr_t AllocateSpace(size_t size) {
|
||||||
void *address = this->allocator.AllocateSpace(size, AslrSpaceLargeAlign);
|
/* Try to allocate a large-aligned space, if we can. */
|
||||||
if (address == nullptr) {
|
if (size >= AslrSpaceLargeAlign) {
|
||||||
address = this->allocator.AllocateSpace(size, MemoryPageSize);
|
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, 0); large_align != 0) {
|
||||||
|
return large_align;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return address;
|
|
||||||
|
/* Allocate a page-aligned space. */
|
||||||
|
return m_allocator.AllocateSpace(size, MemoryPageSize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckGuardSpace(uintptr_t address, size_t size) {
|
bool CheckGuardSpace(uintptr_t address, size_t size) {
|
||||||
return this->allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize);
|
return m_allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename MapFunction, typename UnmapFunction>
|
||||||
|
Result MapAtRandomAddress(uintptr_t *out, size_t size, MapFunction map_function, UnmapFunction unmap_function) {
|
||||||
|
/* Try to map up to 64 times. */
|
||||||
|
for (int i = 0; i < 64; ++i) {
|
||||||
|
/* Reserve space to map the memory. */
|
||||||
|
const uintptr_t map_address = this->AllocateSpace(size);
|
||||||
|
if (map_address == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to map. */
|
||||||
|
R_TRY_CATCH(map_function(map_address, size)) {
|
||||||
|
/* If we failed to map at the address, retry. */
|
||||||
|
R_CATCH(os::ResultInvalidCurrentMemoryState) { continue; }
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
|
/* Check guard space. */
|
||||||
|
if (!this->CheckGuardSpace(map_address, size)) {
|
||||||
|
/* We don't have guard space, so unmap. */
|
||||||
|
unmap_function(map_address, size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We mapped successfully. */
|
||||||
|
*out = map_address;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We failed to map. */
|
||||||
|
return os::ResultOutOfAddressSpace();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using AslrSpaceManager = AslrSpaceManagerTemplate<AddressSpaceAllocator, AslrSpaceManagerImpl>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,37 +57,28 @@ namespace ams::os::impl {
|
||||||
/* Convert permission. */
|
/* Convert permission. */
|
||||||
const auto svc_perm = ConvertToSvcMemoryPermission(perm);
|
const auto svc_perm = ConvertToSvcMemoryPermission(perm);
|
||||||
|
|
||||||
/* NOTE: It is unknown what algorithm Nintendo uses for mapping, so we're using */
|
/* Map at a random address. */
|
||||||
/* the transfer memory algorithm for now. */
|
uintptr_t mapped_address;
|
||||||
|
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size,
|
||||||
|
[handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||||
|
R_TRY_CATCH(svc::MapIoRegion(handle, map_address, map_size, svc_perm)) {
|
||||||
|
/* TODO: What's the correct result for these? */
|
||||||
|
// R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
|
||||||
|
// R_CONVERT(svc::ResultInvalidSize, os::Result???())
|
||||||
|
// R_CONVERT(svc::ResultInvalidState, os::Result???())
|
||||||
|
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
/* Try to map up to 64 times. */
|
return ResultSuccess();
|
||||||
for (int i = 0; i < 64; ++i) {
|
},
|
||||||
/* Reserve space to map the memory. */
|
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||||
void *map_address = impl::GetAslrSpaceManager().AllocateSpace(size);
|
return IoRegionImpl::UnmapIoRegion(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||||
R_UNLESS(map_address != nullptr, os::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* Try to map. */
|
|
||||||
/* TODO: Result conversion/abort on unexpected result? */
|
|
||||||
R_TRY_CATCH(svc::MapIoRegion(handle, reinterpret_cast<uintptr_t>(map_address), size, svc_perm)) {
|
|
||||||
/* If we failed to map at the address, retry. */
|
|
||||||
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
|
|
||||||
R_CATCH(svc::ResultInvalidMemoryRegion) { continue; }
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
/* Check guard space via aslr manager. */
|
|
||||||
if (!impl::GetAslrSpaceManager().CheckGuardSpace(reinterpret_cast<uintptr_t>(map_address), size)) {
|
|
||||||
/* We don't have guard space, so unmap. */
|
|
||||||
UnmapIoRegion(handle, map_address, size);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
));
|
||||||
|
|
||||||
/* We mapped successfully. */
|
/* Return the address we mapped at. */
|
||||||
*out = map_address;
|
*out = reinterpret_cast<void *>(mapped_address);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
|
||||||
|
|
||||||
/* We failed to map. */
|
|
||||||
return os::ResultOutOfAddressSpace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IoRegionImpl::UnmapIoRegion(Handle handle, void *address, size_t size) {
|
void IoRegionImpl::UnmapIoRegion(Handle handle, void *address, size_t size) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace ams::os::impl {
|
||||||
class OsResourceManager {
|
class OsResourceManager {
|
||||||
private:
|
private:
|
||||||
RngManager rng_manager{};
|
RngManager rng_manager{};
|
||||||
AslrSpaceManager aslr_space_manager;
|
AslrSpaceManager aslr_space_manager{};
|
||||||
/* TODO */
|
/* TODO */
|
||||||
ThreadManager thread_manager{};
|
ThreadManager thread_manager{};
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
class SharedMemoryImpl {
|
||||||
|
public:
|
||||||
|
static Result Create(Handle *out, size_t size, MemoryPermission my_perm, MemoryPermission other_perm);
|
||||||
|
static void Close(Handle handle);
|
||||||
|
|
||||||
|
static Result Map(void **out, Handle handle, size_t size, MemoryPermission perm);
|
||||||
|
static void Unmap(Handle handle, void *address, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||||
|
#include "os_shared_memory_impl.hpp"
|
||||||
|
#include "os_aslr_space_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
svc::MemoryPermission ConvertToSvcMemoryPermission(os::MemoryPermission perm) {
|
||||||
|
switch (perm) {
|
||||||
|
case os::MemoryPermission_None: return svc::MemoryPermission_None;
|
||||||
|
case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read;
|
||||||
|
case os::MemoryPermission_WriteOnly: return svc::MemoryPermission_Write;
|
||||||
|
case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SharedMemoryImpl::Create(Handle *out, size_t size, MemoryPermission my_perm, MemoryPermission other_perm) {
|
||||||
|
/* Convert memory permissions. */
|
||||||
|
const auto svc_my_perm = ConvertToSvcMemoryPermission(my_perm);
|
||||||
|
const auto svc_other_perm = ConvertToSvcMemoryPermission(other_perm);
|
||||||
|
|
||||||
|
/* Create the memory. */
|
||||||
|
svc::Handle handle;
|
||||||
|
R_TRY_CATCH(svc::CreateSharedMemory(std::addressof(handle), size, svc_my_perm, svc_other_perm)) {
|
||||||
|
R_CONVERT(svc::ResultOutOfHandles, os::ResultOutOfHandles())
|
||||||
|
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
*out = handle;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedMemoryImpl::Close(Handle handle) {
|
||||||
|
R_ABORT_UNLESS(svc::CloseHandle(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SharedMemoryImpl::Map(void **out, Handle handle, size_t size, MemoryPermission perm) {
|
||||||
|
/* Convert memory permission. */
|
||||||
|
const auto svc_perm = ConvertToSvcMemoryPermission(perm);
|
||||||
|
|
||||||
|
/* Map at a random address. */
|
||||||
|
uintptr_t mapped_address;
|
||||||
|
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size,
|
||||||
|
[handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||||
|
R_TRY_CATCH(svc::MapSharedMemory(handle, map_address, map_size, svc_perm)) {
|
||||||
|
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
},
|
||||||
|
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||||
|
return SharedMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
/* Return the address we mapped at. */
|
||||||
|
*out = reinterpret_cast<void *>(mapped_address);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedMemoryImpl::Unmap(Handle handle, void *address, size_t size) {
|
||||||
|
R_ABORT_UNLESS(svc::UnmapSharedMemory(handle, reinterpret_cast<uintptr_t>(address), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ namespace ams::os::impl {
|
||||||
static Result Create(Handle *out, void *address, size_t size, MemoryPermission perm);
|
static Result Create(Handle *out, void *address, size_t size, MemoryPermission perm);
|
||||||
static void Close(Handle handle);
|
static void Close(Handle handle);
|
||||||
|
|
||||||
static Result Map(void **out, Handle handle, void *address, size_t size, MemoryPermission owner_perm);
|
static Result Map(void **out, Handle handle, size_t size, MemoryPermission owner_perm);
|
||||||
static void Unmap(Handle handle, void *address, size_t size);
|
static void Unmap(Handle handle, void *address, size_t size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "os_transfer_memory_impl.hpp"
|
#include "os_transfer_memory_impl.hpp"
|
||||||
|
#include "os_aslr_space_manager.hpp"
|
||||||
|
|
||||||
namespace ams::os::impl {
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ namespace ams::os::impl {
|
||||||
|
|
||||||
Result TransferMemoryImpl::Create(Handle *out, void *address, size_t size, MemoryPermission perm) {
|
Result TransferMemoryImpl::Create(Handle *out, void *address, size_t size, MemoryPermission perm) {
|
||||||
/* Convert memory permission. */
|
/* Convert memory permission. */
|
||||||
auto svc_perm = ConvertToSvcMemoryPermission(perm);
|
const auto svc_perm = ConvertToSvcMemoryPermission(perm);
|
||||||
|
|
||||||
/* Create the memory. */
|
/* Create the memory. */
|
||||||
svc::Handle handle;
|
svc::Handle handle;
|
||||||
|
@ -51,22 +52,30 @@ namespace ams::os::impl {
|
||||||
R_ABORT_UNLESS(svc::CloseHandle(handle));
|
R_ABORT_UNLESS(svc::CloseHandle(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result TransferMemoryImpl::Map(void **out, Handle handle, void *address, size_t size, MemoryPermission owner_perm) {
|
Result TransferMemoryImpl::Map(void **out, Handle handle, size_t size, MemoryPermission owner_perm) {
|
||||||
AMS_ASSERT(address != nullptr);
|
|
||||||
|
|
||||||
/* Convert memory permission. */
|
/* Convert memory permission. */
|
||||||
auto svc_owner_perm = ConvertToSvcMemoryPermission(owner_perm);
|
const auto svc_owner_perm = ConvertToSvcMemoryPermission(owner_perm);
|
||||||
|
|
||||||
/* Map the memory. */
|
/* Map at a random address. */
|
||||||
R_TRY_CATCH(svc::MapTransferMemory(handle, reinterpret_cast<uintptr_t>(address), size, svc_owner_perm)) {
|
uintptr_t mapped_address;
|
||||||
R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
|
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size,
|
||||||
R_CONVERT(svc::ResultInvalidSize, os::ResultInvalidTransferMemorySize())
|
[handle, svc_owner_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||||
R_CONVERT(svc::ResultInvalidState, os::ResultInvalidTransferMemoryState())
|
R_TRY_CATCH(svc::MapTransferMemory(handle, map_address, map_size, svc_owner_perm)) {
|
||||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
|
||||||
R_CONVERT(svc::ResultInvalidMemoryRegion, os::ResultInvalidCurrentMemoryState())
|
R_CONVERT(svc::ResultInvalidSize, os::ResultInvalidTransferMemorySize())
|
||||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
R_CONVERT(svc::ResultInvalidState, os::ResultInvalidTransferMemoryState())
|
||||||
|
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
*out = address;
|
return ResultSuccess();
|
||||||
|
},
|
||||||
|
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||||
|
return TransferMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
/* Return the address we mapped at. */
|
||||||
|
*out = reinterpret_cast<void *>(mapped_address);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
157
libraries/libstratosphere/source/os/os_shared_memory.cpp
Normal file
157
libraries/libstratosphere/source/os/os_shared_memory.cpp
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||||
|
#include "impl/os_thread_manager.hpp"
|
||||||
|
#include "impl/os_shared_memory_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void SetupSharedMemoryType(SharedMemoryType *shared_memory, size_t size, Handle handle, bool managed) {
|
||||||
|
/* Set members. */
|
||||||
|
shared_memory->handle = handle;
|
||||||
|
shared_memory->size = size;
|
||||||
|
shared_memory->address = nullptr;
|
||||||
|
shared_memory->allocated = false;
|
||||||
|
|
||||||
|
/* Set managed. */
|
||||||
|
shared_memory->handle_managed = managed;
|
||||||
|
|
||||||
|
/* Create the critical section. */
|
||||||
|
util::ConstructAt(shared_memory->cs_shared_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateSharedMemory(SharedMemoryType *shared_memory, size_t size, MemoryPermission my_perm, MemoryPermission other_perm) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(size > 0);
|
||||||
|
AMS_ASSERT(util::IsAligned(size, MemoryPageSize));
|
||||||
|
|
||||||
|
/* Create the memory. */
|
||||||
|
Handle handle;
|
||||||
|
R_TRY(impl::SharedMemoryImpl::Create(std::addressof(handle), size, my_perm, other_perm));
|
||||||
|
|
||||||
|
/* Setup the object. */
|
||||||
|
SetupSharedMemoryType(shared_memory, size, handle, true);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachSharedMemory(SharedMemoryType *shared_memory, size_t size, Handle handle, bool managed) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(size > 0);
|
||||||
|
AMS_ASSERT(util::IsAligned(size, MemoryPageSize));
|
||||||
|
AMS_ASSERT(handle != svc::InvalidHandle);
|
||||||
|
|
||||||
|
/* Setup the object. */
|
||||||
|
SetupSharedMemoryType(shared_memory, size, handle, managed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroySharedMemory(SharedMemoryType *shared_memory) {
|
||||||
|
/* Unmap the shared memory, if required. */
|
||||||
|
if (shared_memory->state == SharedMemoryType::State_Mapped) {
|
||||||
|
UnmapSharedMemory(shared_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the state. */
|
||||||
|
AMS_ASSERT(shared_memory->state == SharedMemoryType::State_Initialized);
|
||||||
|
|
||||||
|
/* Set state to not initialized. */
|
||||||
|
shared_memory->state = SharedMemoryType::State_NotInitialized;
|
||||||
|
|
||||||
|
/* Close the handle, if it's managed. */
|
||||||
|
if (shared_memory->handle_managed) {
|
||||||
|
impl::SharedMemoryImpl::Close(shared_memory->handle);
|
||||||
|
}
|
||||||
|
shared_memory->handle_managed = false;
|
||||||
|
|
||||||
|
/* Clear members. */
|
||||||
|
shared_memory->address = nullptr;
|
||||||
|
shared_memory->size = 0;
|
||||||
|
shared_memory->handle = svc::InvalidHandle;
|
||||||
|
|
||||||
|
/* Destroy the critical section. */
|
||||||
|
util::DestroyAt(shared_memory->cs_shared_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *MapSharedMemory(SharedMemoryType *shared_memory, MemoryPermission perm) {
|
||||||
|
/* Lock the current thread, and then the shared memory. */
|
||||||
|
std::scoped_lock thread_lk(util::GetReference(impl::GetCurrentThread()->cs_thread));
|
||||||
|
std::scoped_lock lk(util::GetReference(shared_memory->cs_shared_memory));
|
||||||
|
|
||||||
|
/* Ensure we're in a mappable state. */
|
||||||
|
AMS_ASSERT(shared_memory->state == SharedMemoryType::State_Initialized);
|
||||||
|
|
||||||
|
/* Try to map. */
|
||||||
|
void *mapped_address;
|
||||||
|
if (R_FAILED(impl::SharedMemoryImpl::Map(std::addressof(mapped_address), shared_memory->handle, shared_memory->size, perm))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set fields now that we've mapped successfully. */
|
||||||
|
shared_memory->allocated = true;
|
||||||
|
shared_memory->address = mapped_address;
|
||||||
|
shared_memory->state = SharedMemoryType::State_Mapped;
|
||||||
|
|
||||||
|
return mapped_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnmapSharedMemory(SharedMemoryType *shared_memory) {
|
||||||
|
/* Lock the memory. */
|
||||||
|
std::scoped_lock lk(util::GetReference(shared_memory->cs_shared_memory));
|
||||||
|
|
||||||
|
/* If the memory isn't mapped, we can't unmap it. */
|
||||||
|
if (shared_memory->state != SharedMemoryType::State_Mapped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unmap the memory. */
|
||||||
|
impl::SharedMemoryImpl::Unmap(shared_memory->handle, shared_memory->address, shared_memory->size);
|
||||||
|
|
||||||
|
/* Unmapped memory is necessarily not allocated. */
|
||||||
|
if (shared_memory->allocated) {
|
||||||
|
shared_memory->allocated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the address. */
|
||||||
|
shared_memory->address = nullptr;
|
||||||
|
shared_memory->state = SharedMemoryType::State_Initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *GetSharedMemoryAddress(const SharedMemoryType *shared_memory) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(shared_memory->state == SharedMemoryType::State_Initialized || shared_memory->state == SharedMemoryType::State_Mapped);
|
||||||
|
|
||||||
|
return shared_memory->address;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSharedMemorySize(const SharedMemoryType *shared_memory) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(shared_memory->state == SharedMemoryType::State_Initialized || shared_memory->state == SharedMemoryType::State_Mapped);
|
||||||
|
|
||||||
|
return shared_memory->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle GetSharedMemoryHandle(const SharedMemoryType *shared_memory) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(shared_memory->state == SharedMemoryType::State_Initialized || shared_memory->state == SharedMemoryType::State_Mapped);
|
||||||
|
|
||||||
|
return shared_memory->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,25 +16,11 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "impl/os_thread_manager.hpp"
|
#include "impl/os_thread_manager.hpp"
|
||||||
#include "impl/os_transfer_memory_impl.hpp"
|
#include "impl/os_transfer_memory_impl.hpp"
|
||||||
#include "impl/os_aslr_space_manager_types.hpp"
|
|
||||||
#include "impl/os_aslr_space_manager.hpp"
|
|
||||||
|
|
||||||
namespace ams::os {
|
namespace ams::os {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Result MapTransferMemoryWithAddressUnsafe(TransferMemoryType *tmem, void *address, os::MemoryPermission owner_perm) {
|
|
||||||
/* Map the transfer memory. */
|
|
||||||
void *mapped_address = nullptr;
|
|
||||||
R_TRY(impl::TransferMemoryImpl::Map(std::addressof(mapped_address), tmem->handle, address, tmem->size, owner_perm));
|
|
||||||
|
|
||||||
/* Set fields now that we've mapped. */
|
|
||||||
tmem->address = mapped_address;
|
|
||||||
tmem->state = TransferMemoryType::State_Mapped;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetupTransferMemoryType(TransferMemoryType *tmem, size_t size, Handle handle, bool managed) {
|
inline void SetupTransferMemoryType(TransferMemoryType *tmem, size_t size, Handle handle, bool managed) {
|
||||||
/* Set members. */
|
/* Set members. */
|
||||||
tmem->handle = handle;
|
tmem->handle = handle;
|
||||||
|
@ -127,41 +113,16 @@ namespace ams::os {
|
||||||
/* Ensure we're in a mappable state. */
|
/* Ensure we're in a mappable state. */
|
||||||
AMS_ASSERT(tmem->state == TransferMemoryType::State_Created);
|
AMS_ASSERT(tmem->state == TransferMemoryType::State_Created);
|
||||||
|
|
||||||
/* Try to map up to 64 times. */
|
/* Map. */
|
||||||
for (int i = 0; i < 64; ++i) {
|
void *mapped_address;
|
||||||
/* Reserve space to map the memory. */
|
R_TRY(impl::TransferMemoryImpl::Map(std::addressof(mapped_address), tmem->handle, tmem->size, owner_perm));
|
||||||
void *map_address = impl::GetAslrSpaceManager().AllocateSpace(tmem->size);
|
|
||||||
R_UNLESS(map_address != nullptr, os::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* Mark allocated. */
|
/* Set fields now that we've mapped. */
|
||||||
tmem->allocated = true;
|
tmem->allocated = true;
|
||||||
auto alloc_guard = SCOPE_GUARD { tmem->allocated = false; };
|
tmem->address = mapped_address;
|
||||||
|
tmem->state = TransferMemoryType::State_Mapped;
|
||||||
|
|
||||||
/* Try to map. */
|
return ResultSuccess();
|
||||||
R_TRY_CATCH(MapTransferMemoryWithAddressUnsafe(tmem, map_address, owner_perm)) {
|
|
||||||
/* If we failed to map at the address, retry. */
|
|
||||||
R_CATCH(os::ResultInvalidCurrentMemoryState) { continue; }
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
/* Check guard space via aslr manager. */
|
|
||||||
if (!impl::GetAslrSpaceManager().CheckGuardSpace(reinterpret_cast<uintptr_t>(tmem->address), tmem->size)) {
|
|
||||||
/* NOTE: Nintendo bug here. If this case occurs, they will return ResultSuccess() without actually mapping the transfer memory. */
|
|
||||||
/* This is because they basically do if (!os::ResultInvalidCurrentMemoryState::Includes(result)) { return result; }, and */
|
|
||||||
/* ResultSuccess() is not included by ResultInvalidCurrentMemoryState. */
|
|
||||||
|
|
||||||
/* We will do better than them, and will not falsely return ResultSuccess(). */
|
|
||||||
impl::TransferMemoryImpl::Unmap(tmem->handle, tmem->address, tmem->size);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We mapped successfully. */
|
|
||||||
alloc_guard.Cancel();
|
|
||||||
*out = tmem->address;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We failed to map. */
|
|
||||||
return os::ResultOutOfAddressSpace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnmapTransferMemory(TransferMemoryType *tmem) {
|
void UnmapTransferMemory(TransferMemoryType *tmem) {
|
||||||
|
|
Loading…
Reference in a new issue