kern: add KProcess::Initialize (for non-kip processes)

This commit is contained in:
Michael Scire 2020-07-21 22:13:16 -07:00 committed by SciresM
parent 8759cb4da3
commit 51311a7332
10 changed files with 232 additions and 8 deletions

View file

@ -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);
}

View file

@ -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);
};
}

View file

@ -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; }

View file

@ -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);

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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 &params, svc::KUserPointer<const u32 *> caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) {
MESOSPHERE_UNIMPLEMENTED();
Result KProcess::Initialize(const ams::svc::CreateProcessParameter &params, 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() {