kern/svc: implement IoPool/Region svc support
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
129
libraries/libmesosphere/source/kern_k_io_pool.cpp
Normal file
129
libraries/libmesosphere/source/kern_k_io_pool.cpp
Normal 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 ®ion : 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
99
libraries/libmesosphere/source/kern_k_io_region.cpp
Normal file
99
libraries/libmesosphere/source/kern_k_io_region.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -163,23 +163,23 @@ namespace ams::kern {
|
||||
heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Heap);
|
||||
stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Stack);
|
||||
kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type_MapSmall);
|
||||
m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_Map39Bit);
|
||||
m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_Map39Bit);
|
||||
m_alias_code_region_start = m_code_region_start;
|
||||
m_alias_code_region_end = m_code_region_end;
|
||||
m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_Map39Bit);
|
||||
m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_Map39Bit);
|
||||
m_alias_code_region_start = m_code_region_start;
|
||||
m_alias_code_region_end = m_code_region_end;
|
||||
process_code_start = util::AlignDown(GetInteger(code_address), RegionAlignment);
|
||||
process_code_end = util::AlignUp(GetInteger(code_address) + code_size, RegionAlignment);
|
||||
} else {
|
||||
stack_region_size = 0;
|
||||
kernel_map_region_size = 0;
|
||||
m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_MapSmall);
|
||||
m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_MapSmall);
|
||||
m_stack_region_start = m_code_region_start;
|
||||
m_alias_code_region_start = m_code_region_start;
|
||||
m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type_MapLarge) + GetSpaceSize(KAddressSpaceInfo::Type_MapLarge);
|
||||
m_stack_region_end = m_code_region_end;
|
||||
m_kernel_map_region_start = m_code_region_start;
|
||||
m_kernel_map_region_end = m_code_region_end;
|
||||
m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_MapSmall);
|
||||
m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_MapSmall);
|
||||
m_stack_region_start = m_code_region_start;
|
||||
m_alias_code_region_start = m_code_region_start;
|
||||
m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type_MapLarge) + GetSpaceSize(KAddressSpaceInfo::Type_MapLarge);
|
||||
m_stack_region_end = m_code_region_end;
|
||||
m_kernel_map_region_start = m_code_region_start;
|
||||
m_kernel_map_region_end = m_code_region_end;
|
||||
process_code_start = m_code_region_start;
|
||||
process_code_end = m_code_region_end;
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
214
libraries/libmesosphere/source/svc/kern_svc_io_pool.cpp
Normal file
214
libraries/libmesosphere/source/svc/kern_svc_io_pool.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user