mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
kern: implement process(pagetable) init
This commit is contained in:
parent
fba8fb539d
commit
05a3e95834
16 changed files with 502 additions and 24 deletions
|
@ -172,6 +172,7 @@ namespace ams::kern::arch::arm64 {
|
|||
}
|
||||
|
||||
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();
|
||||
private:
|
||||
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
|
||||
|
|
|
@ -103,6 +103,7 @@ namespace ams::kern::arch::arm64 {
|
|||
constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ }
|
||||
|
||||
NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end);
|
||||
NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end);
|
||||
L1PageTableEntry *Finalize();
|
||||
|
||||
bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const;
|
||||
|
|
|
@ -24,6 +24,19 @@ namespace ams::kern::arch::arm64 {
|
|||
KPageTable page_table;
|
||||
public:
|
||||
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); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,9 @@ namespace ams::kern {
|
|||
util::BitPack32 intended_kernel_version;
|
||||
u32 program_type{};
|
||||
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 GetPriorityMask() const { return this->priority_mask; }
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace ams::kern {
|
|||
/* TODO: perm/attr operations */
|
||||
};
|
||||
|
||||
static constexpr size_t RegionAlignment = KernelAslrAlignment;
|
||||
|
||||
struct PageLinkedList {
|
||||
private:
|
||||
|
@ -87,6 +88,19 @@ namespace ams::kern {
|
|||
static_assert(std::is_trivially_destructible<PageLinkedList>::value);
|
||||
|
||||
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:
|
||||
class KScopedPageTableUpdater {
|
||||
private:
|
||||
|
@ -122,7 +136,7 @@ namespace ams::kern {
|
|||
KPageTableImpl impl;
|
||||
KMemoryBlockManager memory_block_manager;
|
||||
u32 allocate_option;
|
||||
u32 address_space_size;
|
||||
u32 address_space_width;
|
||||
bool is_kernel;
|
||||
bool enable_aslr;
|
||||
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(),
|
||||
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(),
|
||||
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(),
|
||||
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 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();
|
||||
|
||||
constexpr bool IsKernel() const { return this->is_kernel; }
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
KProcessAddress GetRegionAddress(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:
|
||||
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;
|
||||
|
@ -231,6 +246,7 @@ namespace ams::kern {
|
|||
|
||||
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);
|
||||
public:
|
||||
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {
|
||||
|
|
|
@ -115,10 +115,14 @@ namespace ams::kern {
|
|||
KMemoryBlockSlabManager memory_block_slab_manager{};
|
||||
KBlockInfoManager block_info_manager{};
|
||||
KPageTableManager page_table_manager{};
|
||||
private:
|
||||
Result Initialize(const ams::svc::CreateProcessParameter ¶ms);
|
||||
public:
|
||||
constexpr KProcess() { /* ... */ }
|
||||
virtual ~KProcess() { /* ... */ }
|
||||
|
||||
Result Initialize(const ams::svc::CreateProcessParameter ¶ms, 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 GetCoreMask() const { return this->capabilities.GetCoreMask(); }
|
||||
|
|
|
@ -17,4 +17,3 @@
|
|||
#include <mesosphere/svc/kern_svc_results.hpp>
|
||||
#include <mesosphere/svc/kern_svc_k_user_pointer.hpp>
|
||||
#include <mesosphere/svc/kern_svc_prototypes.hpp>
|
||||
#include <mesosphere/svc/kern_svc_tables.hpp>
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
static constexpr size_t NumSupervisorCalls = 0x80;
|
||||
|
||||
#define AMS_KERN_SVC_DECLARE_PROTOTYPE_64(ID, RETURN_TYPE, NAME, ...) \
|
||||
NOINLINE RETURN_TYPE NAME##64(__VA_ARGS__);
|
||||
#define AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32(ID, RETURN_TYPE, NAME, ...) \
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
static constexpr size_t NumSupervisorCalls = 0x80;
|
||||
using SvcTableEntry = void (*)();
|
||||
|
||||
/* TODO: 32-bit ABI */
|
||||
|
|
|
@ -17,6 +17,85 @@
|
|||
|
||||
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) {
|
||||
/* Nothing actually needed here. */
|
||||
}
|
||||
|
@ -39,6 +118,37 @@ namespace ams::kern::arch::arm64 {
|
|||
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() {
|
||||
MESOSPHERE_TODO_IMPLEMENT();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,12 @@ namespace ams::kern::arch::arm64 {
|
|||
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() {
|
||||
return this->table;
|
||||
}
|
||||
|
|
|
@ -217,7 +217,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||
}
|
||||
};
|
||||
|
||||
class KAsidManager {
|
||||
class KDeviceAsidManager {
|
||||
private:
|
||||
using WordType = u32;
|
||||
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));
|
||||
}
|
||||
public:
|
||||
constexpr KAsidManager() : state(), lock() {
|
||||
constexpr KDeviceAsidManager() : state(), lock() {
|
||||
for (size_t i = 0; i < NumReservedAsids; i++) {
|
||||
this->ReserveImpl(ReservedAsids[i]);
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||
size_t num_reserved = 0;
|
||||
for (size_t i = 0; i < NumWords; i++) {
|
||||
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;
|
||||
out[num_reserved++] = static_cast<u8>(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit));
|
||||
R_UNLESS(num_reserved != num_desired, ResultSuccess());
|
||||
|
@ -279,7 +279,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||
u8 g_reserved_asid;
|
||||
KPhysicalAddress g_memory_controller_address;
|
||||
KPhysicalAddress g_reserved_table_phys_addr;
|
||||
KAsidManager g_asid_manager;
|
||||
KDeviceAsidManager g_asid_manager;
|
||||
|
||||
/* Memory controller access functionality. */
|
||||
void WriteMcRegister(size_t offset, u32 value) {
|
||||
|
|
|
@ -85,7 +85,7 @@ namespace ams::kern {
|
|||
MESOSPHERE_ABORT_UNLESS(new_process != nullptr);
|
||||
|
||||
/* 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");
|
||||
|
||||
/* Register the process. */
|
||||
MESOSPHERE_TODO("Register the process");
|
||||
KProcess::Register(new_process);
|
||||
|
||||
/* Save the process info. */
|
||||
infos[i].process = new_process;
|
||||
|
|
24
libraries/libmesosphere/source/kern_k_capabilities.cpp
Normal file
24
libraries/libmesosphere/source/kern_k_capabilities.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,7 @@ namespace ams::kern {
|
|||
|
||||
Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) {
|
||||
/* 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_end = KProcessAddress(GetInteger(end));
|
||||
this->is_kernel = true;
|
||||
|
@ -63,6 +63,213 @@ namespace ams::kern {
|
|||
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() {
|
||||
this->memory_block_manager.Finalize(this->memory_block_slab_manager);
|
||||
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 last = end - 1;
|
||||
|
||||
|
@ -426,7 +633,7 @@ namespace ams::kern {
|
|||
MESOSPHERE_ASSERT(util::IsAligned(alignment, PageSize) && alignment >= PageSize);
|
||||
|
||||
/* 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());
|
||||
|
||||
/* 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());
|
||||
R_UNLESS(addr != Null<KProcessAddress>, svc::ResultOutOfMemory());
|
||||
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));
|
||||
|
||||
/* Create an update allocator. */
|
||||
|
@ -471,7 +678,7 @@ namespace ams::kern {
|
|||
|
||||
/* Ensure this is a valid map request. */
|
||||
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());
|
||||
|
||||
/* Lock the table. */
|
||||
|
@ -480,7 +687,7 @@ namespace ams::kern {
|
|||
/* Find a random address to map at. */
|
||||
KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, 0, this->GetNumGuardPages());
|
||||
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));
|
||||
|
||||
/* Create an update allocator. */
|
||||
|
@ -502,13 +709,45 @@ namespace ams::kern {
|
|||
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) {
|
||||
MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread());
|
||||
|
||||
/* Ensure this is a valid unmap request. */
|
||||
const size_t num_pages = pg.GetNumPages();
|
||||
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. */
|
||||
KScopedLightLock lk(this->general_lock);
|
||||
|
|
|
@ -17,10 +17,72 @@
|
|||
|
||||
namespace ams::kern {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 InitialProcessIdMin = 1;
|
||||
constexpr u64 InitialProcessIdMax = 0x50;
|
||||
std::atomic<u64> g_initial_process_id = InitialProcessIdMin;
|
||||
|
||||
}
|
||||
|
||||
void KProcess::Finalize() {
|
||||
MESOSPHERE_TODO_IMPLEMENT();
|
||||
}
|
||||
|
||||
Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms) {
|
||||
MESOSPHERE_TODO_IMPLEMENT();
|
||||
}
|
||||
|
||||
Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, 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() {
|
||||
MESOSPHERE_TODO_IMPLEMENT();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue