kern: implement process(pagetable) init

This commit is contained in:
Michael Scire 2020-02-19 01:22:27 -08:00
parent fba8fb539d
commit 05a3e95834
16 changed files with 502 additions and 24 deletions

View file

@ -172,6 +172,7 @@ namespace ams::kern::arch::arm64 {
} }
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end); NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager);
Result Finalize(); Result Finalize();
private: private:
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll); Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);

View file

@ -103,6 +103,7 @@ namespace ams::kern::arch::arm64 {
constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ } constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ }
NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end); NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end);
NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end);
L1PageTableEntry *Finalize(); L1PageTableEntry *Finalize();
bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const; bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const;

View file

@ -24,6 +24,19 @@ namespace ams::kern::arch::arm64 {
KPageTable page_table; KPageTable page_table;
public: public:
constexpr KProcessPageTable() : page_table() { /* ... */ } constexpr KProcessPageTable() : page_table() { /* ... */ }
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) {
return this->page_table.InitializeForProcess(id, as_type, enable_aslr, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager);
}
void Finalize() { this->page_table.Finalize(); }
Result MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPageGroup(addr, pg, state, perm);
}
bool CanContain(KProcessAddress addr, size_t size) const { return this->page_table.CanContain(addr, size); }
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { return this->page_table.CanContain(addr, size, state); }
}; };
} }

View file

@ -34,7 +34,9 @@ namespace ams::kern {
util::BitPack32 intended_kernel_version; util::BitPack32 intended_kernel_version;
u32 program_type{}; u32 program_type{};
public: public:
constexpr KCapabilities() : svc_access_flags(), debug_capabilities(0), intended_kernel_version(0) { /* ... */ } constexpr KCapabilities() : debug_capabilities(0), intended_kernel_version(0) { /* ... */ }
Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
constexpr u64 GetCoreMask() const { return this->core_mask; } constexpr u64 GetCoreMask() const { return this->core_mask; }
constexpr u64 GetPriorityMask() const { return this->priority_mask; } constexpr u64 GetPriorityMask() const { return this->priority_mask; }

View file

@ -53,6 +53,7 @@ namespace ams::kern {
/* TODO: perm/attr operations */ /* TODO: perm/attr operations */
}; };
static constexpr size_t RegionAlignment = KernelAslrAlignment;
struct PageLinkedList { struct PageLinkedList {
private: private:
@ -87,6 +88,19 @@ namespace ams::kern {
static_assert(std::is_trivially_destructible<PageLinkedList>::value); static_assert(std::is_trivially_destructible<PageLinkedList>::value);
static constexpr u32 DefaultMemoryIgnoreAttr = KMemoryAttribute_DontCareMask | KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared; static constexpr u32 DefaultMemoryIgnoreAttr = KMemoryAttribute_DontCareMask | KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
static constexpr size_t GetAddressSpaceWidth(ams::svc::CreateProcessFlag as_type) {
switch (static_cast<ams::svc::CreateProcessFlag>(as_type & ams::svc::CreateProcessFlag_AddressSpaceMask)) {
case ams::svc::CreateProcessFlag_AddressSpace64Bit:
return 39;
case ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated:
return 36;
case ams::svc::CreateProcessFlag_AddressSpace32Bit:
case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
return 32;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
private: private:
class KScopedPageTableUpdater { class KScopedPageTableUpdater {
private: private:
@ -122,7 +136,7 @@ namespace ams::kern {
KPageTableImpl impl; KPageTableImpl impl;
KMemoryBlockManager memory_block_manager; KMemoryBlockManager memory_block_manager;
u32 allocate_option; u32 allocate_option;
u32 address_space_size; u32 address_space_width;
bool is_kernel; bool is_kernel;
bool enable_aslr; bool enable_aslr;
KMemoryBlockSlabManager *memory_block_slab_manager; KMemoryBlockSlabManager *memory_block_slab_manager;
@ -139,7 +153,7 @@ namespace ams::kern {
alias_region_start(), alias_region_end(), stack_region_start(), stack_region_end(), kernel_map_region_start(), alias_region_start(), alias_region_end(), stack_region_start(), stack_region_end(), kernel_map_region_start(),
kernel_map_region_end(), alias_code_region_start(), alias_code_region_end(), code_region_start(), code_region_end(), kernel_map_region_end(), alias_code_region_start(), alias_code_region_end(), code_region_start(), code_region_end(),
max_heap_size(), max_physical_memory_size(), general_lock(), map_physical_memory_lock(), impl(), memory_block_manager(), max_heap_size(), max_physical_memory_size(), general_lock(), map_physical_memory_lock(), impl(), memory_block_manager(),
allocate_option(), address_space_size(), is_kernel(), enable_aslr(), memory_block_slab_manager(), block_info_manager(), allocate_option(), address_space_width(), is_kernel(), enable_aslr(), memory_block_slab_manager(), block_info_manager(),
cached_physical_linear_region(), cached_physical_heap_region(), cached_virtual_heap_region(), cached_physical_linear_region(), cached_physical_heap_region(), cached_virtual_heap_region(),
heap_fill_value(), ipc_fill_value(), stack_fill_value() heap_fill_value(), ipc_fill_value(), stack_fill_value()
{ {
@ -147,23 +161,24 @@ namespace ams::kern {
} }
NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end); NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager);
void Finalize(); void Finalize();
constexpr bool IsKernel() const { return this->is_kernel; } constexpr bool IsKernel() const { return this->is_kernel; }
constexpr bool IsAslrEnabled() const { return this->enable_aslr; } constexpr bool IsAslrEnabled() const { return this->enable_aslr; }
constexpr bool Contains(KProcessAddress addr) const { constexpr bool CanContain(KProcessAddress addr) const {
return this->address_space_start <= addr && addr <= this->address_space_end - 1; return this->address_space_start <= addr && addr <= this->address_space_end - 1;
} }
constexpr bool Contains(KProcessAddress addr, size_t size) const { constexpr bool CanContain(KProcessAddress addr, size_t size) const {
return this->address_space_start <= addr && addr < addr + size && addr + size - 1 <= this->address_space_end - 1; return this->address_space_start <= addr && addr < addr + size && addr + size - 1 <= this->address_space_end - 1;
} }
KProcessAddress GetRegionAddress(KMemoryState state) const; KProcessAddress GetRegionAddress(KMemoryState state) const;
size_t GetRegionSize(KMemoryState state) const; size_t GetRegionSize(KMemoryState state) const;
bool Contains(KProcessAddress addr, size_t size, KMemoryState state) const; bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
protected: protected:
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, OperationType operation, bool reuse_ll) = 0; virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, OperationType operation, bool reuse_ll) = 0;
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup *page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) = 0; virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup *page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) = 0;
@ -231,6 +246,7 @@ namespace ams::kern {
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state); 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 *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); Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state);
public: public:
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) { static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {

View file

@ -115,10 +115,14 @@ namespace ams::kern {
KMemoryBlockSlabManager memory_block_slab_manager{}; KMemoryBlockSlabManager memory_block_slab_manager{};
KBlockInfoManager block_info_manager{}; KBlockInfoManager block_info_manager{};
KPageTableManager page_table_manager{}; KPageTableManager page_table_manager{};
private:
Result Initialize(const ams::svc::CreateProcessParameter &params);
public: public:
constexpr KProcess() { /* ... */ } constexpr KProcess() { /* ... */ }
virtual ~KProcess() { /* ... */ } virtual ~KProcess() { /* ... */ }
Result Initialize(const ams::svc::CreateProcessParameter &params, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool);
constexpr u64 GetProcessId() const { return this->process_id; } constexpr u64 GetProcessId() const { return this->process_id; }
constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); } constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); }

View file

@ -17,4 +17,3 @@
#include <mesosphere/svc/kern_svc_results.hpp> #include <mesosphere/svc/kern_svc_results.hpp>
#include <mesosphere/svc/kern_svc_k_user_pointer.hpp> #include <mesosphere/svc/kern_svc_k_user_pointer.hpp>
#include <mesosphere/svc/kern_svc_prototypes.hpp> #include <mesosphere/svc/kern_svc_prototypes.hpp>
#include <mesosphere/svc/kern_svc_tables.hpp>

View file

@ -20,6 +20,8 @@
namespace ams::kern::svc { namespace ams::kern::svc {
static constexpr size_t NumSupervisorCalls = 0x80;
#define AMS_KERN_SVC_DECLARE_PROTOTYPE_64(ID, RETURN_TYPE, NAME, ...) \ #define AMS_KERN_SVC_DECLARE_PROTOTYPE_64(ID, RETURN_TYPE, NAME, ...) \
NOINLINE RETURN_TYPE NAME##64(__VA_ARGS__); NOINLINE RETURN_TYPE NAME##64(__VA_ARGS__);
#define AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32(ID, RETURN_TYPE, NAME, ...) \ #define AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32(ID, RETURN_TYPE, NAME, ...) \

View file

@ -19,7 +19,6 @@
namespace ams::kern::svc { namespace ams::kern::svc {
static constexpr size_t NumSupervisorCalls = 0x80;
using SvcTableEntry = void (*)(); using SvcTableEntry = void (*)();
/* TODO: 32-bit ABI */ /* TODO: 32-bit ABI */

View file

@ -17,6 +17,85 @@
namespace ams::kern::arch::arm64 { namespace ams::kern::arch::arm64 {
namespace {
constexpr u64 EncodeTtbr(KPhysicalAddress table, u8 asid) {
return (static_cast<u64>(asid) << 48) | (static_cast<u64>(GetInteger(table)));
}
class KPageTableAsidManager {
private:
using WordType = u32;
static constexpr u8 ReservedAsids[] = { 0 };
static constexpr size_t NumReservedAsids = util::size(ReservedAsids);
static constexpr size_t BitsPerWord = BITSIZEOF(WordType);
static constexpr size_t AsidCount = 0x100;
static constexpr size_t NumWords = AsidCount / BitsPerWord;
static constexpr WordType FullWord = ~WordType(0u);
private:
WordType state[NumWords];
KLightLock lock;
u8 hint;
private:
constexpr bool TestImpl(u8 asid) const {
return this->state[asid / BitsPerWord] & (1u << (asid % BitsPerWord));
}
constexpr void ReserveImpl(u8 asid) {
MESOSPHERE_ASSERT(!this->TestImpl(asid));
this->state[asid / BitsPerWord] |= (1u << (asid % BitsPerWord));
}
constexpr void ReleaseImpl(u8 asid) {
MESOSPHERE_ASSERT(this->TestImpl(asid));
this->state[asid / BitsPerWord] &= ~(1u << (asid % BitsPerWord));
}
constexpr u8 FindAvailable() const {
for (size_t i = 0; i < util::size(this->state); i++) {
if (this->state[i] == FullWord) {
continue;
}
const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]);
return BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit);
}
if (this->state[util::size(this->state)-1] == FullWord) {
MESOSPHERE_PANIC("Unable to reserve ASID");
}
__builtin_unreachable();
}
static constexpr ALWAYS_INLINE WordType ClearLeadingZero(WordType value) {
return __builtin_clzll(value) - (BITSIZEOF(unsigned long long) - BITSIZEOF(WordType));
}
public:
constexpr KPageTableAsidManager() : state(), lock(), hint() {
for (size_t i = 0; i < NumReservedAsids; i++) {
this->ReserveImpl(ReservedAsids[i]);
}
}
u8 Reserve() {
KScopedLightLock lk(this->lock);
if (this->TestImpl(this->hint)) {
this->hint = this->FindAvailable();
}
this->ReserveImpl(this->hint);
return this->hint++;
}
void Release(u8 asid) {
KScopedLightLock lk(this->lock);
this->ReleaseImpl(asid);
}
};
KPageTableAsidManager g_asid_manager;
}
void KPageTable::Initialize(s32 core_id) { void KPageTable::Initialize(s32 core_id) {
/* Nothing actually needed here. */ /* Nothing actually needed here. */
} }
@ -39,6 +118,37 @@ namespace ams::kern::arch::arm64 {
return ResultSuccess(); return ResultSuccess();
} }
Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) {
/* Convert the address space type to a width. */
/* Get an ASID */
this->asid = g_asid_manager.Reserve();
auto asid_guard = SCOPE_GUARD { g_asid_manager.Release(this->asid); };
/* Set our manager. */
this->manager = pt_manager;
/* Allocate a new table, and set our ttbr value. */
const KVirtualAddress new_table = this->manager->Allocate();
R_UNLESS(new_table != Null<KVirtualAddress>, svc::ResultOutOfResource());
this->ttbr = EncodeTtbr(GetPageTablePhysicalAddress(new_table), asid);
auto table_guard = SCOPE_GUARD { this->manager->Free(new_table); };
/* Initialize our base table. */
const size_t as_width = GetAddressSpaceWidth(as_type);
const KProcessAddress as_start = 0;
const KProcessAddress as_end = (1ul << as_width);
R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager));
/* We succeeded! */
table_guard.Cancel();
asid_guard.Cancel();
/* Note that we've updated the table (since we created it). */
this->NoteUpdated();
return ResultSuccess();
}
Result KPageTable::Finalize() { Result KPageTable::Finalize() {
MESOSPHERE_TODO_IMPLEMENT(); MESOSPHERE_TODO_IMPLEMENT();
} }

View file

@ -23,6 +23,12 @@ namespace ams::kern::arch::arm64 {
this->num_entries = util::AlignUp(end - start, L1BlockSize) / L1BlockSize; this->num_entries = util::AlignUp(end - start, L1BlockSize) / L1BlockSize;
} }
void KPageTableImpl::InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end) {
this->table = static_cast<L1PageTableEntry *>(tb);
this->is_kernel = false;
this->num_entries = util::AlignUp(end - start, L1BlockSize) / L1BlockSize;
}
L1PageTableEntry *KPageTableImpl::Finalize() { L1PageTableEntry *KPageTableImpl::Finalize() {
return this->table; return this->table;
} }

View file

@ -217,7 +217,7 @@ namespace ams::kern::board::nintendo::nx {
} }
}; };
class KAsidManager { class KDeviceAsidManager {
private: private:
using WordType = u32; using WordType = u32;
static constexpr u8 ReservedAsids[] = { 0, 1, 2, 3 }; static constexpr u8 ReservedAsids[] = { 0, 1, 2, 3 };
@ -241,7 +241,7 @@ namespace ams::kern::board::nintendo::nx {
return __builtin_clzll(value) - (BITSIZEOF(unsigned long long) - BITSIZEOF(WordType)); return __builtin_clzll(value) - (BITSIZEOF(unsigned long long) - BITSIZEOF(WordType));
} }
public: public:
constexpr KAsidManager() : state(), lock() { constexpr KDeviceAsidManager() : state(), lock() {
for (size_t i = 0; i < NumReservedAsids; i++) { for (size_t i = 0; i < NumReservedAsids; i++) {
this->ReserveImpl(ReservedAsids[i]); this->ReserveImpl(ReservedAsids[i]);
} }
@ -254,7 +254,7 @@ namespace ams::kern::board::nintendo::nx {
size_t num_reserved = 0; size_t num_reserved = 0;
for (size_t i = 0; i < NumWords; i++) { for (size_t i = 0; i < NumWords; i++) {
while (this->state[i] != FullWord) { while (this->state[i] != FullWord) {
WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]); const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]);
this->state[i] |= clear_bit; this->state[i] |= clear_bit;
out[num_reserved++] = static_cast<u8>(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit)); out[num_reserved++] = static_cast<u8>(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit));
R_UNLESS(num_reserved != num_desired, ResultSuccess()); R_UNLESS(num_reserved != num_desired, ResultSuccess());
@ -279,7 +279,7 @@ namespace ams::kern::board::nintendo::nx {
u8 g_reserved_asid; u8 g_reserved_asid;
KPhysicalAddress g_memory_controller_address; KPhysicalAddress g_memory_controller_address;
KPhysicalAddress g_reserved_table_phys_addr; KPhysicalAddress g_reserved_table_phys_addr;
KAsidManager g_asid_manager; KDeviceAsidManager g_asid_manager;
/* Memory controller access functionality. */ /* Memory controller access functionality. */
void WriteMcRegister(size_t offset, u32 value) { void WriteMcRegister(size_t offset, u32 value) {

View file

@ -85,7 +85,7 @@ namespace ams::kern {
MESOSPHERE_ABORT_UNLESS(new_process != nullptr); MESOSPHERE_ABORT_UNLESS(new_process != nullptr);
/* Initialize the process. */ /* Initialize the process. */
MESOSPHERE_TODO("Initialize the process"); MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool));
} }
} }
@ -93,7 +93,7 @@ namespace ams::kern {
MESOSPHERE_TODO("Set process's memory permissions"); MESOSPHERE_TODO("Set process's memory permissions");
/* Register the process. */ /* Register the process. */
MESOSPHERE_TODO("Register the process"); KProcess::Register(new_process);
/* Save the process info. */ /* Save the process info. */
infos[i].process = new_process; infos[i].process = new_process;

View file

@ -0,0 +1,24 @@
/*
* 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 KCapabilities::Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table) {
MESOSPHERE_TODO_IMPLEMENT();
}
}

View file

@ -20,7 +20,7 @@ namespace ams::kern {
Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) { Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) {
/* Initialize our members. */ /* Initialize our members. */
this->address_space_size = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32); this->address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32);
this->address_space_start = KProcessAddress(GetInteger(start)); this->address_space_start = KProcessAddress(GetInteger(start));
this->address_space_end = KProcessAddress(GetInteger(end)); this->address_space_end = KProcessAddress(GetInteger(end));
this->is_kernel = true; this->is_kernel = true;
@ -63,6 +63,213 @@ namespace ams::kern {
return ResultSuccess(); return ResultSuccess();
} }
Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager) {
/* Validate the region. */
MESOSPHERE_ABORT_UNLESS(start <= code_address);
MESOSPHERE_ABORT_UNLESS(code_address < code_address + code_size);
MESOSPHERE_ABORT_UNLESS(code_address + code_size - 1 <= end - 1);
/* Declare variables to hold our region sizes. */
/* Define helpers. */
auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA {
return KAddressSpaceInfo::GetAddressSpaceStart(this->address_space_width, type);
};
auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA {
return KAddressSpaceInfo::GetAddressSpaceSize(this->address_space_width, type);
};
/* Set our width and heap/alias sizes. */
this->address_space_width = GetAddressSpaceWidth(as_type);
size_t alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Alias);
size_t heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Heap);
/* Adjust heap/alias size if we don't have an alias region. */
if ((as_type & ams::svc::CreateProcessFlag_AddressSpaceMask) == ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias) {
heap_region_size += alias_region_size;
alias_region_size = 0;
}
/* Set code regions and determine remaining sizes. */
KProcessAddress process_code_start;
KProcessAddress process_code_end;
size_t stack_region_size;
size_t kernel_map_region_size;
if (this->address_space_width == 39) {
alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Alias);
heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Heap);
stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Stack);
kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type_32Bit);
this->code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_Large64Bit);
this->code_region_end = this->code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_Large64Bit);
this->alias_code_region_start = this->code_region_start;
this->alias_code_region_end = this->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;
this->code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_32Bit);
this->code_region_end = this->code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_32Bit);
this->stack_region_start = this->code_region_start;
this->alias_code_region_start = this->code_region_start;
this->alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type_Small64Bit) + GetSpaceSize(KAddressSpaceInfo::Type_Small64Bit);
this->stack_region_end = this->code_region_end;
this->kernel_map_region_start = this->code_region_start;
this->kernel_map_region_end = this->code_region_end;
process_code_start = this->code_region_start;
process_code_end = this->code_region_end;
}
/* Set other basic fields. */
this->enable_aslr = enable_aslr;
this->address_space_start = start;
this->address_space_end = end;
this->is_kernel = false;
this->memory_block_slab_manager = mem_block_slab_manager;
this->block_info_manager = block_info_manager;
/* Determine the region we can place our undetermineds in. */
KProcessAddress alloc_start;
size_t alloc_size;
if ((GetInteger(process_code_start) - GetInteger(this->code_region_start)) >= (GetInteger(end) - GetInteger(process_code_end))) {
alloc_start = this->code_region_start;
alloc_size = this->code_region_end - alloc_start;
} else {
alloc_start = process_code_end;
alloc_size = GetInteger(end) - GetInteger(process_code_end);
}
const size_t needed_size = (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size);
R_UNLESS(alloc_size >= needed_size, svc::ResultOutOfMemory());
const size_t remaining_size = alloc_size - needed_size;
/* Determine random placements for each region. */
size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0;
if (enable_aslr) {
alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
}
/* Setup heap and alias regions. */
this->alias_region_start = alloc_start + alias_rnd;
this->alias_region_end = this->alias_region_start + alias_region_size;
this->heap_region_start = alloc_start + heap_rnd;
this->heap_region_end = this->heap_region_start + heap_region_size;
if (alias_rnd <= heap_rnd) {
this->heap_region_start += alias_region_size;
this->heap_region_end += alias_region_size;
} else {
this->alias_region_start += heap_region_size;
this->alias_region_end += heap_region_size;
}
/* Setup stack region. */
if (stack_region_size) {
this->stack_region_start = alloc_start + stack_rnd;
this->stack_region_end = this->stack_region_start + stack_region_size;
if (alias_rnd < stack_rnd) {
this->stack_region_start += alias_region_size;
this->stack_region_end += alias_region_size;
} else {
this->alias_region_start += stack_region_size;
this->alias_region_end += stack_region_size;
}
if (heap_rnd < stack_rnd) {
this->stack_region_start += heap_region_size;
this->stack_region_end += heap_region_size;
} else {
this->heap_region_start += stack_region_size;
this->heap_region_end += stack_region_size;
}
}
/* Setup kernel map region. */
if (kernel_map_region_size) {
this->kernel_map_region_start = alloc_start + kmap_rnd;
this->kernel_map_region_end = this->kernel_map_region_start + kernel_map_region_size;
if (alias_rnd < kmap_rnd) {
this->kernel_map_region_start += alias_region_size;
this->kernel_map_region_end += alias_region_size;
} else {
this->alias_region_start += kernel_map_region_size;
this->alias_region_end += kernel_map_region_size;
}
if (heap_rnd < kmap_rnd) {
this->kernel_map_region_start += heap_region_size;
this->kernel_map_region_end += heap_region_size;
} else {
this->heap_region_start += kernel_map_region_size;
this->heap_region_end += kernel_map_region_size;
}
if (stack_region_size) {
if (stack_rnd < kmap_rnd) {
this->kernel_map_region_start += stack_region_size;
this->kernel_map_region_end += stack_region_size;
} else {
this->stack_region_start += kernel_map_region_size;
this->stack_region_end += kernel_map_region_size;
}
}
}
/* Set heap and fill members. */
this->current_heap_end = this->heap_region_start;
this->max_heap_size = 0;
this->max_physical_memory_size = 0;
const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled();
this->heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero;
this->ipc_fill_value = fill_memory ? MemoryFillValue_Ipc : MemoryFillValue_Zero;
this->stack_fill_value = fill_memory ? MemoryFillValue_Stack : MemoryFillValue_Zero;
/* Set allocation option. */
this->allocate_option = KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction_FromBack : KMemoryManager::Direction_FromFront);
/* Ensure that we regions inside our address space. */
auto IsInAddressSpace = [&](KProcessAddress addr) ALWAYS_INLINE_LAMBDA { return this->address_space_start <= addr && addr <= this->address_space_end; };
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->alias_region_start));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->alias_region_end));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->heap_region_start));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->heap_region_end));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->stack_region_start));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->stack_region_end));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->kernel_map_region_start));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->kernel_map_region_end));
/* Ensure that we selected regions that don't overlap. */
const KProcessAddress alias_start = this->alias_region_start;
const KProcessAddress alias_last = this->alias_region_end - 1;
const KProcessAddress heap_start = this->heap_region_start;
const KProcessAddress heap_last = this->heap_region_end - 1;
const KProcessAddress stack_start = this->stack_region_start;
const KProcessAddress stack_last = this->stack_region_end - 1;
const KProcessAddress kmap_start = this->kernel_map_region_start;
const KProcessAddress kmap_last = this->kernel_map_region_end - 1;
MESOSPHERE_ABORT_UNLESS(alias_last < heap_start || heap_last < alias_start);
MESOSPHERE_ABORT_UNLESS(alias_last < stack_start || stack_last < alias_start);
MESOSPHERE_ABORT_UNLESS(alias_last < kmap_start || kmap_last < alias_start);
MESOSPHERE_ABORT_UNLESS(heap_last < stack_start || stack_last < heap_start);
MESOSPHERE_ABORT_UNLESS(heap_last < kmap_start || kmap_last < heap_start);
/* Initialize our implementation. */
this->impl.InitializeForProcess(table, GetInteger(start), GetInteger(end));
/* Initialize our memory block manager. */
return this->memory_block_manager.Initialize(this->address_space_start, this->address_space_end, this->memory_block_slab_manager);
return ResultSuccess();
}
void KPageTableBase::Finalize() { void KPageTableBase::Finalize() {
this->memory_block_manager.Finalize(this->memory_block_slab_manager); this->memory_block_manager.Finalize(this->memory_block_slab_manager);
MESOSPHERE_TODO("cpu::InvalidateEntireInstructionCache();"); MESOSPHERE_TODO("cpu::InvalidateEntireInstructionCache();");
@ -134,7 +341,7 @@ namespace ams::kern {
} }
} }
bool KPageTableBase::Contains(KProcessAddress addr, size_t size, KMemoryState state) const { bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
const KProcessAddress end = addr + size; const KProcessAddress end = addr + size;
const KProcessAddress last = end - 1; const KProcessAddress last = end - 1;
@ -426,7 +633,7 @@ namespace ams::kern {
MESOSPHERE_ASSERT(util::IsAligned(alignment, PageSize) && alignment >= PageSize); MESOSPHERE_ASSERT(util::IsAligned(alignment, PageSize) && alignment >= PageSize);
/* Ensure this is a valid map request. */ /* Ensure this is a valid map request. */
R_UNLESS(this->Contains(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory()); R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory());
R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory()); R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory());
/* Lock the table. */ /* Lock the table. */
@ -436,7 +643,7 @@ namespace ams::kern {
KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, this->GetNumGuardPages()); KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, this->GetNumGuardPages());
R_UNLESS(addr != Null<KProcessAddress>, svc::ResultOutOfMemory()); R_UNLESS(addr != Null<KProcessAddress>, svc::ResultOutOfMemory());
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), alignment)); MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), alignment));
MESOSPHERE_ASSERT(this->Contains(addr, num_pages * PageSize, state)); MESOSPHERE_ASSERT(this->CanContain(addr, num_pages * PageSize, state));
MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None));
/* Create an update allocator. */ /* Create an update allocator. */
@ -471,7 +678,7 @@ namespace ams::kern {
/* Ensure this is a valid map request. */ /* Ensure this is a valid map request. */
const size_t num_pages = pg.GetNumPages(); const size_t num_pages = pg.GetNumPages();
R_UNLESS(this->Contains(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory()); R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory());
R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory()); R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory());
/* Lock the table. */ /* Lock the table. */
@ -480,7 +687,7 @@ namespace ams::kern {
/* Find a random address to map at. */ /* Find a random address to map at. */
KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, 0, this->GetNumGuardPages()); KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, 0, this->GetNumGuardPages());
R_UNLESS(addr != Null<KProcessAddress>, svc::ResultOutOfMemory()); R_UNLESS(addr != Null<KProcessAddress>, svc::ResultOutOfMemory());
MESOSPHERE_ASSERT(this->Contains(addr, num_pages * PageSize, state)); MESOSPHERE_ASSERT(this->CanContain(addr, num_pages * PageSize, state));
MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None));
/* Create an update allocator. */ /* Create an update allocator. */
@ -502,13 +709,45 @@ namespace ams::kern {
return ResultSuccess(); return ResultSuccess();
} }
Result KPageTableBase::MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) {
MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread());
/* Ensure this is a valid map request. */
const size_t num_pages = pg.GetNumPages();
const size_t size = num_pages * PageSize;
R_UNLESS(this->CanContain(addr, size, state), svc::ResultInvalidCurrentMemory());
/* Lock the table. */
KScopedLightLock lk(this->general_lock);
/* Check if state allows us to map. */
R_TRY(this->CheckMemoryState(addr, 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);
/* Perform mapping operation. */
const KPageProperties properties = { perm, state == KMemoryState_Io, false, false };
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
/* Update the blocks. */
this->memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None);
/* We successfully mapped the pages. */
return ResultSuccess();
}
Result KPageTableBase::UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state) { Result KPageTableBase::UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state) {
MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread()); MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread());
/* Ensure this is a valid unmap request. */ /* Ensure this is a valid unmap request. */
const size_t num_pages = pg.GetNumPages(); const size_t num_pages = pg.GetNumPages();
const size_t size = num_pages * PageSize; const size_t size = num_pages * PageSize;
R_UNLESS(this->Contains(address, size, state), svc::ResultInvalidCurrentMemory()); R_UNLESS(this->CanContain(address, size, state), svc::ResultInvalidCurrentMemory());
/* Lock the table. */ /* Lock the table. */
KScopedLightLock lk(this->general_lock); KScopedLightLock lk(this->general_lock);

View file

@ -17,10 +17,72 @@
namespace ams::kern { namespace ams::kern {
namespace {
constexpr u64 InitialProcessIdMin = 1;
constexpr u64 InitialProcessIdMax = 0x50;
std::atomic<u64> g_initial_process_id = InitialProcessIdMin;
}
void KProcess::Finalize() { void KProcess::Finalize() {
MESOSPHERE_TODO_IMPLEMENT(); MESOSPHERE_TODO_IMPLEMENT();
} }
Result KProcess::Initialize(const ams::svc::CreateProcessParameter &params) {
MESOSPHERE_TODO_IMPLEMENT();
}
Result KProcess::Initialize(const ams::svc::CreateProcessParameter &params, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(res_limit != nullptr);
MESOSPHERE_ABORT_UNLESS((params.code_num_pages * PageSize) / PageSize == params.code_num_pages);
/* Set members. */
this->memory_pool = pool;
this->resource_limit = resource_limit;
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);
const bool is_app = (params.flags & ams::svc::CreateProcessFlag_IsApplication);
auto *mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager());
auto *block_info_manager = std::addressof(Kernel::GetBlockInfoManager());
auto *pt_manager = std::addressof(Kernel::GetPageTableManager());
R_TRY(this->page_table.Initialize(this->process_id, as_type, enable_aslr, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, 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, params.code_num_pages * PageSize, KMemoryState_Code), svc::ResultInvalidMemoryRegion());
/* Map the code region. */
R_TRY(this->page_table.MapPageGroup(params.code_address, pg, KMemoryState_Code, KMemoryPermission_KernelRead));
/* Initialize capabilities. */
R_TRY(this->capabilities.Initialize(caps, num_caps, std::addressof(this->page_table)));
/* Initialize the process id. */
this->process_id = g_initial_process_id++;
MESOSPHERE_ABORT_UNLESS(InitialProcessIdMin <= this->process_id);
MESOSPHERE_ABORT_UNLESS(this->process_id <= InitialProcessIdMax);
/* Initialize the rest of the process. */
R_TRY(this->Initialize(params));
/* Open a reference to the resource limit. */
this->resource_limit->Open();
/* We succeeded! */
pt_guard.Cancel();
return ResultSuccess();
}
void KProcess::DoWorkerTask() { void KProcess::DoWorkerTask() {
MESOSPHERE_TODO_IMPLEMENT(); MESOSPHERE_TODO_IMPLEMENT();
} }