kern/svc: implement IoPool/Region svc support

This commit is contained in:
Michael Scire 2021-09-18 13:26:21 -07:00 committed by SciresM
parent ce7dd55257
commit f6fb5f2c8d
24 changed files with 944 additions and 301 deletions

View file

@ -78,8 +78,7 @@
#include <mesosphere/kern_select_debug.hpp>
#include <mesosphere/kern_k_process.hpp>
#include <mesosphere/kern_k_resource_limit.hpp>
#include <mesosphere/kern_k_alpha.hpp>
#include <mesosphere/kern_k_beta.hpp>
#include <mesosphere/kern_k_io_pool.hpp>
/* More Miscellaneous objects. */
#include <mesosphere/kern_k_object_name.hpp>

View file

@ -96,6 +96,14 @@ namespace ams::kern::arch::arm64 {
return m_page_table.MapIo(phys_addr, size, perm);
}
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
return m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm);
}
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size) {
return m_page_table.UnmapIoRegion(dst_address, phys_addr, size);
}
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
return m_page_table.MapStatic(phys_addr, size, perm);
}

View file

@ -34,8 +34,8 @@ namespace ams::kern::init {
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
size_t num_KAlpha;
size_t num_KBeta;
size_t num_KIoPool;
size_t num_KIoRegion;
};
NOINLINE void InitializeSlabResourceCounts();

View file

@ -1,39 +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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KAlpha final : public KAutoObjectWithSlabHeapAndContainer<KAlpha, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KAlpha, KAutoObject);
private:
/* NOTE: Official KAlpha has size 0x50, corresponding to 0x20 bytes of fields. */
/* TODO: Add these fields, if KAlpha is ever instantiable in the NX kernel. */
public:
explicit KAlpha() {
/* ... */
}
/* virtual void Finalize() override; */
virtual bool IsInitialized() const override { return false /* TODO */; }
static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ }
};
}

View file

@ -110,10 +110,8 @@ namespace ams::kern {
KDeviceAddressSpace,
KSessionRequest,
KCodeMemory,
/* NOTE: True order for these has not been determined yet. */
KAlpha,
KBeta,
KIoPool,
KIoRegion,
FinalClassesEnd = FinalClassesStart + NumFinalClasses,
};

View file

@ -17,30 +17,34 @@
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_k_io_region.hpp>
namespace ams::kern {
class KProcess;
class KBeta final : public KAutoObjectWithSlabHeapAndContainer<KBeta, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KBeta, KAutoObject);
class KIoPool final : public KAutoObjectWithSlabHeapAndContainer<KIoPool, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KIoPool, KAutoObject);
private:
friend class KProcess;
using IoRegionList = util::IntrusiveListMemberTraits<&KIoRegion::m_pool_list_node>::ListType;
private:
/* NOTE: Official KBeta has size 0x88, corresponding to 0x58 bytes of fields. */
/* TODO: Add these fields, if KBeta is ever instantiable in the NX kernel. */
util::IntrusiveListNode m_process_list_node;
KLightLock m_lock;
IoRegionList m_io_region_list;
ams::svc::IoPoolType m_pool_type;
bool m_is_initialized;
public:
explicit KBeta()
: m_process_list_node()
{
static bool IsValidIoPoolType(ams::svc::IoPoolType pool_type);
public:
explicit KIoPool() : m_lock(), m_io_region_list(), m_is_initialized(false) {
/* ... */
}
/* virtual void Finalize() override; */
Result Initialize(ams::svc::IoPoolType pool_type);
virtual void Finalize() override;
virtual bool IsInitialized() const override { return false /* TODO */; }
virtual bool IsInitialized() const override { return m_is_initialized; }
static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ }
Result AddIoRegion(KIoRegion *region);
void RemoveIoRegion(KIoRegion *region);
};
}

View file

@ -0,0 +1,66 @@
/*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KProcess;
class KIoPool;
class KIoRegion final : public KAutoObjectWithSlabHeapAndContainer<KIoRegion, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KIoRegion, KAutoObject);
private:
friend class KProcess;
friend class KIoPool;
private:
KLightLock m_lock;
KIoPool *m_pool;
KPhysicalAddress m_physical_address;
size_t m_size;
ams::svc::MemoryMapping m_mapping;
ams::svc::MemoryPermission m_perm;
bool m_is_initialized;
bool m_is_mapped;
util::IntrusiveListNode m_process_list_node;
util::IntrusiveListNode m_pool_list_node;
public:
explicit KIoRegion()
: m_lock(), m_pool(nullptr), m_is_initialized(false), m_process_list_node(), m_pool_list_node()
{
/* ... */
}
Result Initialize(KIoPool *pool, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm);
virtual void Finalize() override;
virtual bool IsInitialized() const override { return m_is_initialized; }
static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ }
Result Map(KProcessAddress address, size_t size, ams::svc::MemoryPermission map_perm);
Result Unmap(KProcessAddress address, size_t size);
bool Overlaps(KPhysicalAddress address, size_t size) const {
return m_physical_address <= (address + size - 1) && address <= (m_physical_address + m_size - 1);
}
ALWAYS_INLINE KPhysicalAddress GetAddress() const { return m_physical_address; }
ALWAYS_INLINE size_t GetSize() const { return m_size; }
};
}

View file

@ -339,6 +339,8 @@ namespace ams::kern {
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm);
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size);
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);

View file

@ -22,7 +22,7 @@
#include <mesosphere/kern_k_thread.hpp>
#include <mesosphere/kern_k_thread_local_page.hpp>
#include <mesosphere/kern_k_shared_memory_info.hpp>
#include <mesosphere/kern_k_beta.hpp>
#include <mesosphere/kern_k_io_region.hpp>
#include <mesosphere/kern_k_worker_task.hpp>
#include <mesosphere/kern_select_page_table.hpp>
#include <mesosphere/kern_k_condition_variable.hpp>
@ -53,7 +53,7 @@ namespace ams::kern {
static constexpr size_t AslrAlignment = KernelAslrAlignment;
private:
using SharedMemoryInfoList = util::IntrusiveListBaseTraits<KSharedMemoryInfo>::ListType;
using BetaList = util::IntrusiveListMemberTraits<&KBeta::m_process_list_node>::ListType;
using IoRegionList = util::IntrusiveListMemberTraits<&KIoRegion::m_process_list_node>::ListType;
using TLPTree = util::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
using TLPIterator = TLPTree::iterator;
private:
@ -96,7 +96,7 @@ namespace ams::kern {
KThread *m_exception_thread{};
ThreadList m_thread_list{};
SharedMemoryInfoList m_shared_memory_list{};
BetaList m_beta_list{};
IoRegionList m_io_region_list{};
bool m_is_suspended{};
bool m_is_immortal{};
bool m_is_jit_debug{};
@ -275,6 +275,9 @@ namespace ams::kern {
Result AddSharedMemory(KSharedMemory *shmem, KProcessAddress address, size_t size);
void RemoveSharedMemory(KSharedMemory *shmem, KProcessAddress address, size_t size);
void AddIoRegion(KIoRegion *io_region);
void RemoveIoRegion(KIoRegion *io_region);
Result CreateThreadLocalRegion(KProcessAddress *out);
Result DeleteThreadLocalRegion(KProcessAddress addr);
void *GetThreadLocalRegionPointer(KProcessAddress addr);

View file

@ -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/>.
*/
constexpr IoRegionExtents g_io_region_extents[4] = {
{ KPhysicalAddress(0x12000000), 224_MB }, /* PCIE_A2 */
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
};
constexpr bool IsValidIoPoolTypeImpl(ams::svc::IoPoolType pool_type) {
return pool_type == ams::svc::IoPoolType_PcieA2;
}

View file

@ -39,8 +39,8 @@ namespace ams::kern::init {
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ## __VA_ARGS__) \
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ## __VA_ARGS__) \
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ## __VA_ARGS__) \
HANDLER(KAlpha, (SLAB_COUNT(KAlpha)), ## __VA_ARGS__) \
HANDLER(KBeta, (SLAB_COUNT(KBeta)), ## __VA_ARGS__)
HANDLER(KIoPool, (SLAB_COUNT(KIoPool)), ## __VA_ARGS__) \
HANDLER(KIoRegion, (SLAB_COUNT(KIoRegion)), ## __VA_ARGS__)
namespace {
@ -69,8 +69,8 @@ namespace ams::kern::init {
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = cpu::NumCores;
constexpr size_t SlabCountKAlpha = 1;
constexpr size_t SlabCountKBeta = 6;
constexpr size_t SlabCountKIoPool = 1;
constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabCountExtraKThread = 160;
@ -97,8 +97,8 @@ namespace ams::kern::init {
.num_KObjectName = SlabCountKObjectName,
.num_KResourceLimit = SlabCountKResourceLimit,
.num_KDebug = SlabCountKDebug,
.num_KAlpha = SlabCountKAlpha,
.num_KBeta = SlabCountKBeta,
.num_KIoPool = SlabCountKIoPool,
.num_KIoRegion = SlabCountKIoRegion,
};
template<typename T>

View file

@ -346,8 +346,8 @@ namespace ams::kern::KDumpObject {
DUMP_KSLABOBJ(KEventInfo);
DUMP_KSLABOBJ(KSessionRequest);
DUMP_KSLABOBJ(KResourceLimit);
DUMP_KSLABOBJ(KAlpha);
DUMP_KSLABOBJ(KBeta);
DUMP_KSLABOBJ(KIoPool);
DUMP_KSLABOBJ(KIoRegion);
#undef DUMP_KSLABOBJ

View file

@ -0,0 +1,129 @@
/*
* 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 <mesosphere.hpp>
namespace ams::kern {
namespace {
constinit KLightLock g_io_pool_lock;
constinit bool g_pool_used[ams::svc::IoPoolType_Count];
struct IoRegionExtents {
KPhysicalAddress address;
size_t size;
};
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "board/nintendo/nx/kern_k_io_pool.board.nintendo_nx.inc"
#elif defined(AMS_SVC_IO_POOL_NOT_SUPPORTED)
#include "kern_k_io_pool.unsupported.inc"
#else
#error "Unknown context for IoPoolType!"
#endif
constexpr bool IsValidIoRegionImpl(ams::svc::IoPoolType pool_type, KPhysicalAddress address, size_t size) {
/* NOTE: It seems likely this depends on pool type, but this isn't confirmable as of now. */
MESOSPHERE_UNUSED(pool_type);
/* Check if the address/size falls within any allowable extents. */
for (const auto &extents : g_io_region_extents) {
if (extents.address <= address && address + size - 1 <= extents.address + extents.size - 1) {
return true;
}
}
return false;
}
}
bool KIoPool::IsValidIoPoolType(ams::svc::IoPoolType pool_type) {
return IsValidIoPoolTypeImpl(pool_type);
}
Result KIoPool::Initialize(ams::svc::IoPoolType pool_type) {
MESOSPHERE_ASSERT_THIS();
/* Register the pool type. */
{
/* Lock the pool used table. */
KScopedLightLock lk(g_io_pool_lock);
/* Check that the pool isn't already used. */
R_UNLESS(!g_pool_used[pool_type], svc::ResultBusy());
/* Set the pool as used. */
g_pool_used[pool_type] = true;
}
/* Set our fields. */
m_pool_type = pool_type;
m_is_initialized = true;
return ResultSuccess();
}
void KIoPool::Finalize() {
MESOSPHERE_ASSERT_THIS();
/* Lock the pool used table. */
KScopedLightLock lk(g_io_pool_lock);
/* Check that the pool is used. */
MESOSPHERE_ASSERT(g_pool_used[m_pool_type]);
/* Set the pool as unused. */
g_pool_used[m_pool_type] = false;
}
Result KIoPool::AddIoRegion(KIoRegion *new_region) {
MESOSPHERE_ASSERT_THIS();
/* Check that the region is allowed. */
R_UNLESS(IsValidIoRegionImpl(m_pool_type, new_region->GetAddress(), new_region->GetSize()), svc::ResultInvalidMemoryRegion());
/* Lock ourselves. */
KScopedLightLock lk(m_lock);
/* Check that the desired range isn't already in our pool. */
for (const auto &region : m_io_region_list) {
R_UNLESS(!region.Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy());
}
/* Add the region to our pool. */
m_io_region_list.push_back(*new_region);
return ResultSuccess();
}
void KIoPool::RemoveIoRegion(KIoRegion *region) {
MESOSPHERE_ASSERT_THIS();
/* Lock ourselves. */
KScopedLightLock lk(m_lock);
/* Remove the region from our list. */
m_io_region_list.erase(m_io_region_list.iterator_to(*region));
}
}

View file

@ -0,0 +1,27 @@
/*
* 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/>.
*/
constexpr IoRegionExtents g_io_region_extents[4] = {
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
};
constexpr bool IsValidIoPoolTypeImpl(ams::svc::IoPoolType pool_type) {
MESOSPHERE_UNUSED(pool_type);
return false;
}

View file

@ -0,0 +1,99 @@
/*
* 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 <mesosphere.hpp>
namespace ams::kern {
Result KIoRegion::Initialize(KIoPool *pool, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
MESOSPHERE_ASSERT_THIS();
/* Set fields. */
m_physical_address = phys_addr;
m_size = size;
m_mapping = mapping;
m_perm = perm;
m_pool = pool;
m_is_mapped = false;
/* Add ourselves to our pool. */
R_TRY(m_pool->AddIoRegion(this));
/* Open a reference to our pool. */
m_pool->Open();
/* Mark ourselves as initialized. */
m_is_initialized = true;
return ResultSuccess();
}
void KIoRegion::Finalize() {
/* Remove ourselves from our pool. */
m_pool->RemoveIoRegion(this);
/* Close our reference to our pool. */
m_pool->Close();
}
Result KIoRegion::Map(KProcessAddress address, size_t size, ams::svc::MemoryPermission map_perm) {
MESOSPHERE_ASSERT_THIS();
/* Check that the desired perm is allowable. */
R_UNLESS((m_perm | map_perm) == m_perm, svc::ResultInvalidNewMemoryPermission());
/* Check that the size is correct. */
R_UNLESS(size == m_size, svc::ResultInvalidSize());
/* Lock ourselves. */
KScopedLightLock lk(m_lock);
/* Check that we're not already mapped. */
R_UNLESS(!m_is_mapped, svc::ResultInvalidState());
/* Map ourselves. */
R_TRY(GetCurrentProcess().GetPageTable().MapIoRegion(address, m_physical_address, size, m_mapping, map_perm));
/* Add ourselves to the current process. */
GetCurrentProcess().AddIoRegion(this);
/* Note that we're mapped. */
m_is_mapped = true;
return ResultSuccess();
}
Result KIoRegion::Unmap(KProcessAddress address, size_t size) {
MESOSPHERE_ASSERT_THIS();
/* Check that the size is correct. */
R_UNLESS(size == m_size, svc::ResultInvalidSize());
/* Lock ourselves. */
KScopedLightLock lk(m_lock);
/* Unmap ourselves. */
R_TRY(GetCurrentProcess().GetPageTable().UnmapIoRegion(address, m_physical_address, size));
/* Remove ourselves from the current process. */
GetCurrentProcess().RemoveIoRegion(this);
/* Note that we're unmapped. */
m_is_mapped = false;
return ResultSuccess();
}
}

View file

@ -368,10 +368,10 @@ namespace ams::kern {
return m_alias_region_start;
case KMemoryState_Stack:
return m_stack_region_start;
case KMemoryState_Io:
case KMemoryState_Static:
case KMemoryState_ThreadLocal:
return m_kernel_map_region_start;
case KMemoryState_Io:
case KMemoryState_Shared:
case KMemoryState_AliasCode:
case KMemoryState_AliasCodeData:
@ -402,10 +402,10 @@ namespace ams::kern {
return m_alias_region_end - m_alias_region_start;
case KMemoryState_Stack:
return m_stack_region_end - m_stack_region_start;
case KMemoryState_Io:
case KMemoryState_Static:
case KMemoryState_ThreadLocal:
return m_kernel_map_region_end - m_kernel_map_region_start;
case KMemoryState_Io:
case KMemoryState_Shared:
case KMemoryState_AliasCode:
case KMemoryState_AliasCodeData:
@ -1823,10 +1823,12 @@ namespace ams::kern {
const KPhysicalAddress last = phys_addr + size - 1;
/* Get region extents. */
const KProcessAddress region_start = this->GetRegionAddress(KMemoryState_Io);
const size_t region_size = this->GetRegionSize(KMemoryState_Io);
const KProcessAddress region_start = m_kernel_map_region_start;
const size_t region_size = m_kernel_map_region_end - m_kernel_map_region_start;
const size_t region_num_pages = region_size / PageSize;
MESOSPHERE_ASSERT(this->CanContain(region_start, region_size, KMemoryState_Io));
/* Locate the memory region. */
const KMemoryRegion *region = KMemoryLayout::Find(phys_addr);
R_UNLESS(region != nullptr, svc::ResultInvalidAddress());
@ -1906,6 +1908,87 @@ namespace ams::kern {
return ResultSuccess();
}
Result KPageTableBase::MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
/* Lock the table. */
KScopedLightLock lk(m_general_lock);
/* Validate the memory state. */
size_t num_allocator_blocks;
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_None, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None));
/* Create an update allocator. */
Result allocator_result;
KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
R_TRY(allocator_result);
/* We're going to perform an update, so create a helper. */
KScopedPageTableUpdater updater(this);
/* Perform mapping operation. */
const KMemoryPermission perm = ConvertToKMemoryPermission(svc_perm);
const KPageProperties properties = { perm, mapping == ams::svc::MemoryMapping_IoRegister, mapping == ams::svc::MemoryMapping_Uncached, DisableMergeAttribute_DisableHead };
R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, phys_addr, true, properties, OperationType_Map, false));
/* Update the blocks. */
m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, KMemoryState_Io, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None);
/* We successfully mapped the pages. */
return ResultSuccess();
}
Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size) {
const size_t num_pages = size / PageSize;
/* Lock the table. */
KScopedLightLock lk(m_general_lock);
/* Validate the memory state. */
size_t num_allocator_blocks;
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None));
/* Validate that the region being unmapped corresponds to the physical range described. */
{
/* Get the impl. */
auto &impl = this->GetImpl();
/* Begin traversal. */
TraversalContext context;
TraversalEntry next_entry;
MESOSPHERE_ABORT_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
/* Check that the physical region matches. */
R_UNLESS(next_entry.phys_addr == phys_addr, svc::ResultInvalidMemoryRegion());
/* Iterate. */
for (size_t checked_size = next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1)); checked_size < size; checked_size += next_entry.block_size) {
/* Continue the traversal. */
MESOSPHERE_ABORT_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
/* Check that the physical region matches. */
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, svc::ResultInvalidMemoryRegion());
}
}
/* Create an update allocator. */
Result allocator_result;
KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
R_TRY(allocator_result);
/* We're going to perform an update, so create a helper. */
KScopedPageTableUpdater updater(this);
/* Perform the unmap. */
const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None };
R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, false));
/* Update the blocks. */
m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal);
return ResultSuccess();
}
Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_ASSERT(util::IsAligned(size, PageSize));

View file

@ -141,14 +141,14 @@ namespace ams::kern {
}
}
/* Close all references to our betas. */
/* Close all references to our io regions. */
{
auto it = m_beta_list.begin();
while (it != m_beta_list.end()) {
KBeta *beta = std::addressof(*it);
it = m_beta_list.erase(it);
auto it = m_io_region_list.begin();
while (it != m_io_region_list.end()) {
KIoRegion *io_region = std::addressof(*it);
it = m_io_region_list.erase(it);
beta->Close();
io_region->Close();
}
}
@ -597,6 +597,32 @@ namespace ams::kern {
shmem->Close();
}
void KProcess::AddIoRegion(KIoRegion *io_region) {
/* Lock ourselves, to prevent concurrent access. */
KScopedLightLock lk(m_state_lock);
/* Open a reference to the region. */
io_region->Open();
/* Add the region to our list. */
m_io_region_list.push_back(*io_region);
}
void KProcess::RemoveIoRegion(KIoRegion *io_region) {
/* Remove the region from our list. */
{
/* Lock ourselves, to prevent concurrent access. */
KScopedLightLock lk(m_state_lock);
/* Remove the region from our list. */
m_io_region_list.erase(m_io_region_list.iterator_to(*io_region));
}
/* Close our reference to the io region. */
io_region->Close();
}
Result KProcess::CreateThreadLocalRegion(KProcessAddress *out) {
KThreadLocalPage *tlp = nullptr;
KProcessAddress tlr = Null<KProcessAddress>;

View file

@ -1,79 +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/>.
*/
#include <mesosphere.hpp>
namespace ams::kern::svc {
/* ============================= Common ============================= */
namespace {
Result Unknown39() {
return svc::ResultNotImplemented();
}
Result Unknown3A() {
return svc::ResultNotImplemented();
}
Result Unknown46() {
return svc::ResultNotImplemented();
}
Result Unknown47() {
return svc::ResultNotImplemented();
}
}
/* ============================= 64 ABI ============================= */
Result Unknown3964() {
return Unknown39();
}
Result Unknown3A64() {
/* NOTE: From official stubs, true API to this is something like Unknown3A(u64 *, u32_or_u64, u64, u64, u64_or_u32, u64_or_u32); */
return Unknown3A();
}
Result Unknown4664() {
return Unknown46();
}
Result Unknown4764() {
return Unknown47();
}
/* ============================= 64From32 ABI ============================= */
Result Unknown3964From32() {
return Unknown39();
}
Result Unknown3A64From32() {
return Unknown3A();
}
Result Unknown4664From32() {
return Unknown46();
}
Result Unknown4764From32() {
return Unknown47();
}
}

View file

@ -0,0 +1,214 @@
/*
* 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 <mesosphere.hpp>
namespace ams::kern::svc {
/* ============================= Common ============================= */
namespace {
#if defined(AMS_SVC_IO_POOL_NOT_SUPPORTED)
constexpr bool IsIoPoolApiSupported = false;
#else
constexpr bool IsIoPoolApiSupported = true;
#endif
[[maybe_unused]] constexpr bool IsValidIoRegionMapping(ams::svc::MemoryMapping mapping) {
switch (mapping) {
case ams::svc::MemoryMapping_IoRegister:
case ams::svc::MemoryMapping_Uncached:
case ams::svc::MemoryMapping_Memory:
return true;
default:
return false;
}
}
[[maybe_unused]] constexpr bool IsValidIoRegionPermission(ams::svc::MemoryPermission perm) {
switch (perm) {
case ams::svc::MemoryPermission_Read:
case ams::svc::MemoryPermission_ReadWrite:
return true;
default:
return false;
}
}
Result CreateIoPool(ams::svc::Handle *out, ams::svc::IoPoolType pool_type) {
if constexpr (IsIoPoolApiSupported) {
/* Validate that we're allowed to create a pool for the given type. */
R_UNLESS(KIoPool::IsValidIoPoolType(pool_type), svc::ResultNotFound());
/* Create the io pool. */
KIoPool *io_pool = KIoPool::Create();
R_UNLESS(io_pool != nullptr, svc::ResultOutOfResource());
/* Ensure the only reference is in the handle table when we're done. */
ON_SCOPE_EXIT { io_pool->Close(); };
/* Initialize the io pool. */
R_TRY(io_pool->Initialize(pool_type));
/* Register the io pool. */
KIoPool::Register(io_pool);
/* Add the io pool to the handle table. */
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, io_pool));
return ResultSuccess();
} else {
MESOSPHERE_UNUSED(out, pool_type);
return svc::ResultNotImplemented();
}
}
Result CreateIoRegion(ams::svc::Handle *out, ams::svc::Handle io_pool_handle, uint64_t phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
if constexpr (IsIoPoolApiSupported) {
/* Validate the address/size. */
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(util::IsAligned(phys_addr, PageSize), svc::ResultInvalidAddress());
R_UNLESS((phys_addr < phys_addr + size), svc::ResultInvalidMemoryRegion());
/* Validate the mapping/permissions. */
R_UNLESS(IsValidIoRegionMapping(mapping), svc::ResultInvalidEnumValue());
R_UNLESS(IsValidIoRegionPermission(perm), svc::ResultInvalidEnumValue());
/* Get the current handle table. */
auto &handle_table = GetCurrentProcess().GetHandleTable();
/* Get the io pool. */
KScopedAutoObject io_pool = handle_table.GetObject<KIoPool>(io_pool_handle);
R_UNLESS(io_pool.IsNotNull(), svc::ResultInvalidHandle());
/* Create the io region. */
KIoRegion *io_region = KIoRegion::Create();
R_UNLESS(io_region != nullptr, svc::ResultOutOfResource());
/* Ensure the only reference is in the handle table when we're done. */
ON_SCOPE_EXIT { io_region->Close(); };
/* Initialize the io region. */
R_TRY(io_region->Initialize(io_pool.GetPointerUnsafe(), phys_addr, size, mapping, perm));
/* Register the io region. */
KIoRegion::Register(io_region);
/* Add the io region to the handle table. */
R_TRY(handle_table.Add(out, io_region));
return ResultSuccess();
} else {
MESOSPHERE_UNUSED(out, io_pool_handle, phys_addr, size, mapping, perm);
return svc::ResultNotImplemented();
}
}
Result MapIoRegion(ams::svc::Handle io_region_handle, uintptr_t address, size_t size, ams::svc::MemoryPermission map_perm) {
if constexpr (IsIoPoolApiSupported) {
/* Validate the address/size. */
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
/* Verify that the mapping is in range. */
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_Io), svc::ResultInvalidMemoryRegion());
/* Validate the map permission. */
R_UNLESS(IsValidIoRegionPermission(map_perm), svc::ResultInvalidNewMemoryPermission());
/* Get the io region. */
KScopedAutoObject io_region = GetCurrentProcess().GetHandleTable().GetObject<KIoRegion>(io_region_handle);
R_UNLESS(io_region.IsNotNull(), svc::ResultInvalidHandle());
/* Map the io region. */
R_TRY(io_region->Map(address, size, map_perm));
/* We succeeded. */
return ResultSuccess();
} else {
MESOSPHERE_UNUSED(io_region_handle, address, size, map_perm);
return svc::ResultNotImplemented();
}
}
Result UnmapIoRegion(ams::svc::Handle io_region_handle, uintptr_t address, size_t size) {
if constexpr (IsIoPoolApiSupported) {
/* Validate the address/size. */
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
/* Verify that the mapping is in range. */
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_Io), svc::ResultInvalidMemoryRegion());
/* Get the io region. */
KScopedAutoObject io_region = GetCurrentProcess().GetHandleTable().GetObject<KIoRegion>(io_region_handle);
R_UNLESS(io_region.IsNotNull(), svc::ResultInvalidHandle());
/* Unmap the io region. */
R_TRY(io_region->Unmap(address, size));
/* We succeeded. */
return ResultSuccess();
} else {
MESOSPHERE_UNUSED(io_region_handle, address, size);
return svc::ResultNotImplemented();
}
}
}
/* ============================= 64 ABI ============================= */
Result CreateIoPool64(ams::svc::Handle *out_handle, ams::svc::IoPoolType pool_type) {
return CreateIoPool(out_handle, pool_type);
}
Result CreateIoRegion64(ams::svc::Handle *out_handle, ams::svc::Handle io_pool, ams::svc::PhysicalAddress physical_address, ams::svc::Size size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
return CreateIoRegion(out_handle, io_pool, physical_address, size, mapping, perm);
}
Result MapIoRegion64(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission perm) {
return MapIoRegion(io_region, address, size, perm);
}
Result UnmapIoRegion64(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size) {
return UnmapIoRegion(io_region, address, size);
}
/* ============================= 64From32 ABI ============================= */
Result CreateIoPool64From32(ams::svc::Handle *out_handle, ams::svc::IoPoolType pool_type) {
return CreateIoPool(out_handle, pool_type);
}
Result CreateIoRegion64From32(ams::svc::Handle *out_handle, ams::svc::Handle io_pool, ams::svc::PhysicalAddress physical_address, ams::svc::Size size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
return CreateIoRegion(out_handle, io_pool, physical_address, size, mapping, perm);
}
Result MapIoRegion64From32(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission perm) {
return MapIoRegion(io_region, address, size, perm);
}
Result UnmapIoRegion64From32(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size) {
return UnmapIoRegion(io_region, address, size);
}
}

View file

@ -0,0 +1,27 @@
/*
* 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/svc/svc_types_common.hpp>
namespace ams::svc::board::nintendo::nx {
enum IoPoolType : u32 {
IoPoolType_PcieA2 = 0, /* NOTE: Name is not official. */
IoPoolType_Count = 1,
};
}

View file

@ -85,8 +85,8 @@
HANDLER(0x36, void, SynchronizePreemptionState) \
HANDLER(0x37, Result, GetResourceLimitPeakValue, OUTPUT(int64_t, out_peak_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \
\
HANDLER(0x39, Result, Unknown39) \
HANDLER(0x3A, Result, Unknown3A) \
HANDLER(0x39, Result, CreateIoPool, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::IoPoolType, which)) \
HANDLER(0x3A, Result, CreateIoRegion, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Handle, io_pool), INPUT(::ams::svc::PhysicalAddress, physical_address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryMapping, mapping), INPUT(::ams::svc::MemoryPermission, perm)) \
\
HANDLER(0x3C, void, KernelDebug, INPUT(::ams::svc::KernelDebugType, kern_debug_type), INPUT(uint64_t, arg0), INPUT(uint64_t, arg1), INPUT(uint64_t, arg2)) \
HANDLER(0x3D, void, ChangeKernelTraceState, INPUT(::ams::svc::KernelTraceState, kern_trace_state)) \
@ -97,8 +97,8 @@
HANDLER(0x43, Result, ReplyAndReceive, OUTPUT(int32_t, out_index), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, num_handles), INPUT(::ams::svc::Handle, reply_target), INPUT(int64_t, timeout_ns)) \
HANDLER(0x44, Result, ReplyAndReceiveWithUserBuffer, OUTPUT(int32_t, out_index), INPUT(::ams::svc::Address, message_buffer), INPUT(::ams::svc::Size, message_buffer_size), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, num_handles), INPUT(::ams::svc::Handle, reply_target), INPUT(int64_t, timeout_ns)) \
HANDLER(0x45, Result, CreateEvent, OUTPUT(::ams::svc::Handle, out_write_handle), OUTPUT(::ams::svc::Handle, out_read_handle)) \
HANDLER(0x46, Result, Unknown46) \
HANDLER(0x47, Result, Unknown47) \
HANDLER(0x46, Result, MapIoRegion, INPUT(::ams::svc::Handle, io_region), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, perm)) \
HANDLER(0x47, Result, UnmapIoRegion, INPUT(::ams::svc::Handle, io_region), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
HANDLER(0x48, Result, MapPhysicalMemoryUnsafe, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
HANDLER(0x49, Result, UnmapPhysicalMemoryUnsafe, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
HANDLER(0x4A, Result, SetUnsafeLimit, INPUT(::ams::svc::Size, limit)) \

View file

@ -0,0 +1,43 @@
/*
* 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/svc/svc_common.hpp>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include <vapours/svc/board/nintendo/nx/svc_io_pool_type.hpp>
namespace ams::svc {
using IoPoolType = ::ams::svc::board::nintendo::nx::IoPoolType;
using enum ::ams::svc::board::nintendo::nx::IoPoolType;
}
#else
#define AMS_SVC_IO_POOL_NOT_SUPPORTED
namespace ams::svc {
enum IoPoolType : u32 {
/* Not supported. */
IoPoolType_Count = 0,
};
}
#endif

View file

@ -23,3 +23,4 @@
#include <vapours/svc/svc_types_dd.hpp>
#include <vapours/svc/svc_types_dmnt.hpp>
#include <vapours/svc/svc_types_priv.hpp>
#include <vapours/svc/svc_select_io_pool_type.hpp>

View file

@ -117,6 +117,12 @@ namespace ams::svc {
MemoryAttribute_Uncached = (1 << 3),
};
enum MemoryMapping : u32 {
MemoryMapping_IoRegister = 0,
MemoryMapping_Uncached = 1,
MemoryMapping_Memory = 2,
};
constexpr inline size_t HeapSizeAlignment = 2_MB;
struct PageInfo {