mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-01 22:09:15 +00:00
kern: add KProcess::Initialize (for non-kip processes)
This commit is contained in:
parent
8759cb4da3
commit
51311a7332
10 changed files with 232 additions and 8 deletions
|
@ -100,6 +100,10 @@ namespace ams::kern::arch::arm64 {
|
|||
return this->page_table.MapPages(out_addr, num_pages, state, perm);
|
||||
}
|
||||
|
||||
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||
return this->page_table.MapPages(address, num_pages, state, perm);
|
||||
}
|
||||
|
||||
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
|
||||
return this->page_table.UnmapPages(addr, num_pages, state);
|
||||
}
|
||||
|
|
|
@ -68,8 +68,10 @@ namespace ams::kern::board::nintendo::nx {
|
|||
/* User access. */
|
||||
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||
|
||||
/* Constant calculations. */
|
||||
/* Secure Memory. */
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||
static Result AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool);
|
||||
static void FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool);
|
||||
};
|
||||
|
||||
}
|
|
@ -253,10 +253,12 @@ namespace ams::kern {
|
|||
|
||||
Result SetCapability(const util::BitPack32 cap, u32 &set_flags, u32 &set_svc, KProcessPageTable *page_table);
|
||||
Result SetCapabilities(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
Result SetCapabilities(svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
public:
|
||||
constexpr KCapabilities() = default;
|
||||
|
||||
Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
Result Initialize(svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
|
||||
constexpr u64 GetCoreMask() const { return this->core_mask; }
|
||||
constexpr u64 GetPriorityMask() const { return this->priority_mask; }
|
||||
|
|
|
@ -53,6 +53,12 @@ namespace ams::kern {
|
|||
class Impl {
|
||||
private:
|
||||
using RefCount = u16;
|
||||
public:
|
||||
static size_t CalculateMetadataOverheadSize(size_t region_size);
|
||||
|
||||
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
|
||||
return (util::AlignUp((region_size / PageSize), BITSIZEOF(u64)) / BITSIZEOF(u64)) * sizeof(u64);
|
||||
}
|
||||
private:
|
||||
KPageHeap heap;
|
||||
RefCount *page_reference_counts;
|
||||
|
@ -68,6 +74,7 @@ namespace ams::kern {
|
|||
KVirtualAddress AllocateBlock(s32 index, bool random) { return this->heap.AllocateBlock(index, random); }
|
||||
void Free(KVirtualAddress addr, size_t num_pages) { this->heap.Free(addr, num_pages); }
|
||||
|
||||
void InitializeOptimizedMemory() { std::memset(GetVoidPointer(this->metadata_region), 0, CalculateOptimizedProcessOverheadSize(this->heap.GetSize())); }
|
||||
void TrackAllocationForOptimizedProcess(KVirtualAddress block, size_t num_pages);
|
||||
|
||||
constexpr size_t GetSize() const { return this->heap.GetSize(); }
|
||||
|
@ -127,8 +134,6 @@ namespace ams::kern {
|
|||
this->Free(this->heap.GetAddress() + free_start * PageSize, free_count);
|
||||
}
|
||||
}
|
||||
public:
|
||||
static size_t CalculateMetadataOverheadSize(size_t region_size);
|
||||
};
|
||||
private:
|
||||
KLightLock pool_locks[Pool_Count];
|
||||
|
@ -165,6 +170,8 @@ namespace ams::kern {
|
|||
|
||||
NOINLINE void Initialize(KVirtualAddress metadata_region, size_t metadata_region_size);
|
||||
|
||||
NOINLINE Result InitializeOptimizedMemory(u64 process_id, Pool pool);
|
||||
|
||||
NOINLINE KVirtualAddress AllocateContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
NOINLINE Result Allocate(KPageGroup *out, size_t num_pages, u32 option);
|
||||
|
||||
|
|
|
@ -294,7 +294,9 @@ namespace ams::kern {
|
|||
return this->MapPages(out_addr, num_pages, PageSize, Null<KPhysicalAddress>, false, this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, state, perm);
|
||||
}
|
||||
|
||||
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm);
|
||||
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
|
||||
|
||||
Result MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
|
||||
Result MapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm);
|
||||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state);
|
||||
|
|
|
@ -475,7 +475,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||
}
|
||||
}
|
||||
|
||||
/* Constant calculations. */
|
||||
/* Secure Memory. */
|
||||
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
||||
if (pool == KMemoryManager::Pool_Applet) {
|
||||
return 0;
|
||||
|
@ -483,4 +483,12 @@ namespace ams::kern::board::nintendo::nx {
|
|||
return size;
|
||||
}
|
||||
|
||||
Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void KSystemControl::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
}
|
|
@ -34,6 +34,14 @@ namespace ams::kern {
|
|||
return this->SetCapabilities(caps, num_caps, page_table);
|
||||
}
|
||||
|
||||
Result KCapabilities::Initialize(svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KProcessPageTable *page_table) {
|
||||
/* We're initializing a user process. */
|
||||
/* Most fields have already been cleared by our constructor. */
|
||||
|
||||
/* Parse the user capabilities array. */
|
||||
return this->SetCapabilities(user_caps, num_caps, page_table);
|
||||
}
|
||||
|
||||
Result KCapabilities::SetCorePriorityCapability(const util::BitPack32 cap) {
|
||||
/* We can't set core/priority if we've already set them. */
|
||||
R_UNLESS(this->core_mask == 0, svc::ResultInvalidArgument());
|
||||
|
@ -258,4 +266,35 @@ namespace ams::kern {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KCapabilities::SetCapabilities(svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KProcessPageTable *page_table) {
|
||||
u32 set_flags = 0, set_svc = 0;
|
||||
|
||||
for (s32 i = 0; i < num_caps; i++) {
|
||||
/* Read the cap from userspace. */
|
||||
u32 cap0;
|
||||
R_TRY(user_caps.CopyArrayElementTo(std::addressof(cap0), i));
|
||||
|
||||
const util::BitPack32 cap = { cap0 };
|
||||
if (GetCapabilityType(cap) == CapabilityType::MapRange) {
|
||||
/* Check that the pair cap exists. */
|
||||
R_UNLESS((++i) < num_caps, svc::ResultInvalidCombination());
|
||||
|
||||
/* Read the second cap from userspace. */
|
||||
u32 cap1;
|
||||
R_TRY(user_caps.CopyArrayElementTo(std::addressof(cap1), i));
|
||||
|
||||
/* Check the pair cap is a map range cap. */
|
||||
const util::BitPack32 size_cap = { cap1 };
|
||||
R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange, svc::ResultInvalidCombination());
|
||||
|
||||
/* Map the range. */
|
||||
R_TRY(this->MapRange(cap, size_cap, page_table));
|
||||
} else {
|
||||
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -87,6 +87,26 @@ namespace ams::kern {
|
|||
}
|
||||
}
|
||||
|
||||
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
|
||||
/* Lock the pool. */
|
||||
KScopedLightLock lk(this->pool_locks[pool]);
|
||||
|
||||
/* Check that we don't already have an optimized process. */
|
||||
R_UNLESS(!this->has_optimized_process[pool], svc::ResultBusy());
|
||||
|
||||
/* Set the optimized process id. */
|
||||
this->optimized_process_ids[pool] = process_id;
|
||||
this->has_optimized_process[pool] = true;
|
||||
|
||||
/* Clear the management area for the optimized process. */
|
||||
for (auto *manager = this->GetFirstManager(pool, Direction_FromFront); manager != nullptr; manager = this->GetNextManager(manager, Direction_FromFront)) {
|
||||
manager->InitializeOptimizedMemory();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
KVirtualAddress KMemoryManager::AllocateContinuous(size_t num_pages, size_t align_pages, u32 option) {
|
||||
/* Early return if we're allocating no pages. */
|
||||
if (num_pages == 0) {
|
||||
|
@ -199,7 +219,7 @@ namespace ams::kern {
|
|||
size_t KMemoryManager::Impl::Initialize(const KMemoryRegion *region, Pool p, KVirtualAddress metadata, KVirtualAddress metadata_end) {
|
||||
/* Calculate metadata sizes. */
|
||||
const size_t ref_count_size = (region->GetSize() / PageSize) * sizeof(u16);
|
||||
const size_t optimize_map_size = (util::AlignUp((region->GetSize() / PageSize), BITSIZEOF(u64)) / BITSIZEOF(u64)) * sizeof(u64);
|
||||
const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(region->GetSize());
|
||||
const size_t manager_size = util::AlignUp(optimize_map_size + ref_count_size, PageSize);
|
||||
const size_t page_heap_size = KPageHeap::CalculateMetadataOverheadSize(region->GetSize());
|
||||
const size_t total_metadata_size = manager_size + page_heap_size;
|
||||
|
|
|
@ -1350,13 +1350,41 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
/* Update the blocks. */
|
||||
this->memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None);
|
||||
this->memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, KMemoryAttribute_None);
|
||||
|
||||
/* We successfully mapped the pages. */
|
||||
*out_addr = addr;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KPageTableBase::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||
/* Check that the map is in range. */
|
||||
const size_t size = num_pages * PageSize;
|
||||
R_UNLESS(this->CanContain(address, size, state), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Lock the table. */
|
||||
KScopedLightLock lk(this->general_lock);
|
||||
|
||||
/* Check the memory state. */
|
||||
R_TRY(this->CheckMemoryState(address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None));
|
||||
|
||||
/* Create an update allocator. */
|
||||
KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager);
|
||||
R_TRY(allocator.GetResult());
|
||||
|
||||
/* We're going to perform an update, so create a helper. */
|
||||
KScopedPageTableUpdater updater(this);
|
||||
|
||||
/* Map the pages. */
|
||||
const KPageProperties properties = { perm, false, false, false };
|
||||
R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, properties));
|
||||
|
||||
/* Update the blocks. */
|
||||
this->memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, KMemoryAttribute_None);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KPageTableBase::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
|
||||
/* Check that the unmap is in range. */
|
||||
const size_t size = num_pages * PageSize;
|
||||
|
|
|
@ -21,7 +21,12 @@ namespace ams::kern {
|
|||
|
||||
constexpr u64 InitialProcessIdMin = 1;
|
||||
constexpr u64 InitialProcessIdMax = 0x50;
|
||||
|
||||
constexpr u64 ProcessIdMin = InitialProcessIdMax + 1;
|
||||
constexpr u64 ProcessIdMax = std::numeric_limits<u64>::max();
|
||||
|
||||
std::atomic<u64> g_initial_process_id = InitialProcessIdMin;
|
||||
std::atomic<u64> g_process_id = ProcessIdMin;
|
||||
|
||||
}
|
||||
|
||||
|
@ -153,8 +158,115 @@ namespace ams::kern {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, svc::KUserPointer<const u32 *> caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(res_limit != nullptr);
|
||||
|
||||
/* Set pool and resource limit. */
|
||||
this->memory_pool = pool;
|
||||
this->resource_limit = res_limit;
|
||||
|
||||
/* Get the memory sizes. */
|
||||
const size_t code_num_pages = params.code_num_pages;
|
||||
const size_t system_resource_num_pages = params.system_resource_num_pages;
|
||||
const size_t code_size = code_num_pages * PageSize;
|
||||
const size_t system_resource_size = system_resource_num_pages * PageSize;
|
||||
|
||||
/* Reserve memory for the system resource. */
|
||||
KScopedResourceReservation memory_reservation(this, ams::svc::LimitableResource_PhysicalMemoryMax, code_size + KSystemControl::CalculateRequiredSecureMemorySize(system_resource_size, pool));
|
||||
R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached());
|
||||
|
||||
/* Setup page table resource objects. */
|
||||
KMemoryBlockSlabManager *mem_block_manager;
|
||||
KBlockInfoManager *block_info_manager;
|
||||
KPageTableManager *pt_manager;
|
||||
|
||||
this->system_resource_address = Null<KVirtualAddress>;
|
||||
this->system_resource_num_pages = 0;
|
||||
|
||||
if (system_resource_num_pages != 0) {
|
||||
/* Allocate secure memory. */
|
||||
R_TRY(KSystemControl::AllocateSecureMemory(std::addressof(this->system_resource_address), system_resource_size, pool));
|
||||
|
||||
/* Set the number of system resource pages. */
|
||||
MESOSPHERE_ASSERT(this->system_resource_address != Null<KVirtualAddress>);
|
||||
this->system_resource_num_pages = system_resource_num_pages;
|
||||
|
||||
/* Initialize managers. */
|
||||
const size_t rc_size = util::AlignUp(KPageTableManager::CalculateReferenceCountSize(system_resource_size), PageSize);
|
||||
this->dynamic_page_manager.Initialize(this->system_resource_address + rc_size, system_resource_size - rc_size);
|
||||
this->page_table_manager.Initialize(std::addressof(this->dynamic_page_manager), GetPointer<KPageTableManager::RefCount>(this->system_resource_address));
|
||||
this->memory_block_slab_manager.Initialize(std::addressof(this->dynamic_page_manager));
|
||||
this->block_info_manager.Initialize(std::addressof(this->dynamic_page_manager));
|
||||
|
||||
mem_block_manager = std::addressof(this->memory_block_slab_manager);
|
||||
block_info_manager = std::addressof(this->block_info_manager);
|
||||
pt_manager = std::addressof(this->page_table_manager);
|
||||
} else {
|
||||
const bool is_app = (params.flags & ams::svc::CreateProcessFlag_IsApplication);
|
||||
mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager());
|
||||
block_info_manager = std::addressof(Kernel::GetBlockInfoManager());
|
||||
pt_manager = std::addressof(Kernel::GetPageTableManager());
|
||||
}
|
||||
|
||||
/* Ensure we don't leak any secure memory we allocated. */
|
||||
auto sys_resource_guard = SCOPE_GUARD {
|
||||
if (this->system_resource_address != Null<KVirtualAddress>) {
|
||||
/* Check that we have no outstanding allocations. */
|
||||
MESOSPHERE_ABORT_UNLESS(this->memory_block_slab_manager.GetUsed() == 0);
|
||||
MESOSPHERE_ABORT_UNLESS(this->block_info_manager.GetUsed() == 0);
|
||||
MESOSPHERE_ABORT_UNLESS(this->page_table_manager.GetUsed() == 0);
|
||||
|
||||
/* Free the memory. */
|
||||
KSystemControl::FreeSecureMemory(this->system_resource_address, system_resource_size, pool);
|
||||
|
||||
/* Clear our tracking variables. */
|
||||
this->system_resource_address = Null<KVirtualAddress>;
|
||||
this->system_resource_num_pages = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* Setup page table. */
|
||||
/* NOTE: Nintendo passes process ID despite not having set it yet. */
|
||||
/* This goes completely unused, but even so... */
|
||||
{
|
||||
const auto as_type = static_cast<ams::svc::CreateProcessFlag>(params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask);
|
||||
const bool enable_aslr = (params.flags & ams::svc::CreateProcessFlag_EnableAslr);
|
||||
R_TRY(this->page_table.Initialize(this->process_id, as_type, enable_aslr, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager));
|
||||
}
|
||||
auto pt_guard = SCOPE_GUARD { this->page_table.Finalize(); };
|
||||
|
||||
/* Ensure we can insert the code region. */
|
||||
R_UNLESS(this->page_table.CanContain(params.code_address, code_size, KMemoryState_Code), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Map the code region. */
|
||||
R_TRY(this->page_table.MapPages(params.code_address, code_num_pages, KMemoryState_Code, static_cast<KMemoryPermission>(KMemoryPermission_KernelRead | KMemoryPermission_NotMapped)));
|
||||
|
||||
/* Initialize capabilities. */
|
||||
R_TRY(this->capabilities.Initialize(user_caps, num_caps, std::addressof(this->page_table)));
|
||||
|
||||
/* Initialize the process id. */
|
||||
this->process_id = g_process_id++;
|
||||
MESOSPHERE_ABORT_UNLESS(ProcessIdMin <= this->process_id);
|
||||
MESOSPHERE_ABORT_UNLESS(this->process_id <= ProcessIdMax);
|
||||
|
||||
/* If we should optimize memory allocations, do so. */
|
||||
if (this->system_resource_address != Null<KVirtualAddress>) {
|
||||
R_TRY(Kernel::GetMemoryManager().InitializeOptimizedMemory(this->process_id, pool));
|
||||
}
|
||||
|
||||
/* Initialize the rest of the process. */
|
||||
R_TRY(this->Initialize(params));
|
||||
|
||||
/* Open a reference to the resource limit. */
|
||||
this->resource_limit->Open();
|
||||
|
||||
/* We succeeded, so commit our memory reservation and cancel our guards. */
|
||||
sys_resource_guard.Cancel();
|
||||
pt_guard.Cancel();
|
||||
memory_reservation.Commit();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void KProcess::DoWorkerTask() {
|
||||
|
|
Loading…
Reference in a new issue