diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index d1ad8529f..5ce3c2597 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -49,4 +49,5 @@ #include #include #include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_io_region.hpp b/libraries/libstratosphere/include/stratosphere/os/os_io_region.hpp new file mode 100644 index 000000000..5b499de70 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_io_region.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + /* TODO: class IoRegion ? */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_io_region_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_io_region_api.hpp new file mode 100644 index 000000000..3a780dbd5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_io_region_api.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct IoRegionType; + + Result CreateIoRegion(IoRegionType *io_region, Handle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission); + + void AttachIoRegion(IoRegionType *io_region, size_t size, Handle handle, bool managed); + + void DestroyIoRegion(IoRegionType *io_region); + + Handle GetIoRegionHandle(const IoRegionType *io_region); + + Result MapIoRegion(void **out, IoRegionType *io_region, MemoryPermission perm); + void UnmapIoRegion(IoRegionType *io_region); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_io_region_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_io_region_types.hpp new file mode 100644 index 000000000..81acecb98 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_io_region_types.hpp @@ -0,0 +1,40 @@ +/* + * 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 . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct IoRegionType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + State_Mapped = 2, + }; + + Handle handle; + u8 state; + size_t size; + void *mapped_address; + bool handle_managed; + + mutable impl::InternalCriticalSectionStorage cs_io_region; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp index 070e3a8ab..dbc364c00 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp @@ -30,4 +30,7 @@ namespace ams::os { MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly, }; + using MemoryMapping = svc::MemoryMapping; + using enum svc::MemoryMapping; + } diff --git a/libraries/libstratosphere/source/os/impl/os_io_region_impl.hpp b/libraries/libstratosphere/source/os/impl/os_io_region_impl.hpp new file mode 100644 index 000000000..dd5d3f46b --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_io_region_impl.hpp @@ -0,0 +1,29 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::os::impl { + + class IoRegionImpl { + public: + static Result CreateIoRegion(Handle *out, Handle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission); + + static Result MapIoRegion(void **out, Handle handle, size_t size, MemoryPermission perm); + static void UnmapIoRegion(Handle handle, void *address, size_t size); + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp new file mode 100644 index 000000000..a68bc22d0 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_io_region_impl.os.horizon.cpp @@ -0,0 +1,97 @@ +/* + * 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 . + */ +#include +#include "os_io_region_impl.hpp" +#include "os_aslr_space_manager_types.hpp" +#include "os_aslr_space_manager.hpp" + +namespace ams::os::impl { + + namespace { + + constexpr 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(); + } + } + + constexpr svc::MemoryMapping ConvertToSvcMemoryMapping(os::MemoryMapping mapping) { + static_assert(std::same_as); + return mapping; + } + + } + + Result IoRegionImpl::CreateIoRegion(Handle *out, Handle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission) { + /* Convert mapping/permission. */ + const auto svc_mapping = ConvertToSvcMemoryMapping(mapping); + const auto svc_perm = ConvertToSvcMemoryPermission(permission); + + /* Create the io region. */ + /* TODO: Result conversion/abort on unexpected result? */ + svc::Handle handle; + R_TRY(svc::CreateIoRegion(std::addressof(handle), io_pool_handle, address, size, svc_mapping, svc_perm)); + + *out = handle; + return ResultSuccess(); + } + + Result IoRegionImpl::MapIoRegion(void **out, Handle handle, size_t size, MemoryPermission perm) { + /* Convert permission. */ + const auto svc_perm = ConvertToSvcMemoryPermission(perm); + + /* NOTE: It is unknown what algorithm Nintendo uses for mapping, so we're using */ + /* the transfer memory algorithm for now. */ + + /* Try to map up to 64 times. */ + for (int i = 0; i < 64; ++i) { + /* Reserve space to map the memory. */ + void *map_address = impl::GetAslrSpaceManager().AllocateSpace(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(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(map_address), size)) { + /* We don't have guard space, so unmap. */ + UnmapIoRegion(handle, map_address, size); + continue; + } + + /* We mapped successfully. */ + *out = map_address; + return ResultSuccess(); + } + + /* We failed to map. */ + return os::ResultOutOfAddressSpace(); + } + + void IoRegionImpl::UnmapIoRegion(Handle handle, void *address, size_t size) { + R_ABORT_UNLESS(svc::UnmapIoRegion(handle, reinterpret_cast(address), size)); + } + +} diff --git a/libraries/libstratosphere/source/os/os_io_region.cpp b/libraries/libstratosphere/source/os/os_io_region.cpp new file mode 100644 index 000000000..f60645334 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_io_region.cpp @@ -0,0 +1,166 @@ +/* + * 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 . + */ +#include +#include "impl/os_io_region_impl.hpp" + +namespace ams::os { + + namespace { + + void InitializeIoRegion(IoRegionType *io_region, Handle handle, size_t size, bool managed) { + /* Set state. */ + io_region->state = IoRegionType::State_Initialized; + + /* Set member variables. */ + io_region->handle = handle; + io_region->size = size; + io_region->handle_managed = managed; + io_region->mapped_address = nullptr; + + /* Create critical section. */ + util::ConstructAt(io_region->cs_io_region); + } + + } + + Result CreateIoRegion(IoRegionType *io_region, Handle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission) { + /* Check pre-conditions. */ + AMS_ASSERT(io_region != nullptr); + AMS_ASSERT(io_pool_handle != svc::InvalidHandle); + AMS_ASSERT(util::IsAligned(address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(mapping == MemoryMapping_IoRegister || mapping == MemoryMapping_Uncached || mapping == MemoryMapping_Memory); + AMS_ASSERT(permission == MemoryPermission_ReadOnly || permission == MemoryPermission_ReadWrite); + + /* If we fail to create, ensure we reset the state. */ + auto state_guard = SCOPE_GUARD { io_region->state = IoRegionType::State_NotInitialized; }; + + /* Create the io region. */ + Handle handle; + R_TRY(impl::IoRegionImpl::CreateIoRegion(std::addressof(handle), io_pool_handle, address, size, mapping, permission)); + + /* Setup the object. */ + InitializeIoRegion(io_region, handle, size, true); + + state_guard.Cancel(); + return ResultSuccess(); + } + + void AttachIoRegion(IoRegionType *io_region, size_t size, Handle handle, bool managed) { + /* Check pre-conditions. */ + AMS_ASSERT(io_region != nullptr); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(handle != svc::InvalidHandle); + + /* Setup the object. */ + InitializeIoRegion(io_region, handle, size, managed); + } + + void DestroyIoRegion(IoRegionType *io_region) { + /* Check pre-conditions. */ + AMS_ASSERT(io_region->state != IoRegionType::State_NotInitialized); + + /* Acquire exclusive access to the io region. */ + std::scoped_lock lk(util::GetReference(io_region->cs_io_region)); + + /* Check that we can destroy. */ + AMS_ASSERT(io_region->state == IoRegionType::State_Initialized); + + /* If managed, close the handle. */ + if (io_region->handle_managed) { + /* TODO: os::CloseNativeHandle */ + R_ABORT_UNLESS(svc::CloseHandle(io_region->handle)); + } + + /* Clear members. */ + io_region->handle = svc::InvalidHandle; + io_region->handle_managed = false; + io_region->mapped_address = nullptr; + io_region->size = 0; + + /* Mark not initialized. */ + io_region->state = IoRegionType::State_NotInitialized; + } + + Handle GetIoRegionHandle(const IoRegionType *io_region) { + /* Check pre-conditions. */ + AMS_ASSERT(io_region->state != IoRegionType::State_NotInitialized); + + /* Acquire exclusive access to the io region. */ + std::scoped_lock lk(util::GetReference(io_region->cs_io_region)); + + /* Get the handle. */ + return io_region->handle; + } + + Result MapIoRegion(void **out, IoRegionType *io_region, MemoryPermission perm) { + /* Check pre-conditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(io_region != nullptr); + AMS_ASSERT(io_region->state != IoRegionType::State_NotInitialized); + AMS_ASSERT(perm == MemoryPermission_ReadOnly || perm == MemoryPermission_ReadWrite); + + /* Acquire exclusive access to the io region. */ + std::scoped_lock lk(util::GetReference(io_region->cs_io_region)); + + /* Check that we can map. */ + AMS_ASSERT(io_region->state == IoRegionType::State_Initialized); + + /* Get the size. */ + const size_t size = io_region->size; + AMS_ASSERT(size == io_region->size); + + /* Map. */ + void *mapped_address; + R_TRY(impl::IoRegionImpl::MapIoRegion(std::addressof(mapped_address), io_region->handle, size, perm)); + + /* Set mapped address. */ + io_region->mapped_address = mapped_address; + + /* Set mapped. */ + io_region->state = IoRegionType::State_Mapped; + + /* Set the output address. */ + *out = mapped_address; + + return ResultSuccess(); + } + + void UnmapIoRegion(IoRegionType *io_region) { + /* Check pre-conditions. */ + AMS_ASSERT(io_region->state != IoRegionType::State_NotInitialized); + + /* Acquire exclusive access to the io region. */ + std::scoped_lock lk(util::GetReference(io_region->cs_io_region)); + + /* Check that we can unmap. */ + AMS_ASSERT(io_region->state == IoRegionType::State_Mapped); + + /* Get the size. */ + const size_t size = io_region->size; + AMS_ASSERT(size == io_region->size); + + /* Unmap the io region. */ + impl::IoRegionImpl::UnmapIoRegion(io_region->handle, io_region->mapped_address, size); + + /* Clear mapped address. */ + io_region->mapped_address = nullptr; + + /* Set not-mapped. */ + io_region->state = IoRegionType::State_NotInitialized; + } + +} diff --git a/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp b/libraries/libstratosphere/source/os/os_transfer_memory.cpp similarity index 100% rename from libraries/libstratosphere/source/os/os_transfer_memory_api.cpp rename to libraries/libstratosphere/source/os/os_transfer_memory.cpp