Compare commits

...

12 commits

49 changed files with 773 additions and 594 deletions

View file

@ -1,6 +1,6 @@
# Key: debugmode, default: 1. # Key: debugmode, default: 1.
# Desc: Controls whether kernel is debug mode. # Desc: Controls whether kernel is debug mode.
# Disabling this may break Atmosphere's debugger in a future release. # Disabling this will break Atmosphere.
# Key: debugmode_user, default: 0. # Key: debugmode_user, default: 0.
# Desc: Controls whether userland is debug mode. # Desc: Controls whether userland is debug mode.

View file

@ -93,9 +93,13 @@ namespace ams::kern::arch::arm64 {
MESOSPHERE_ASSERT(alignment < L1BlockSize); MESOSPHERE_ASSERT(alignment < L1BlockSize);
return KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(KPageTable::GetBlockType(alignment) + 1)); return KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(KPageTable::GetBlockType(alignment) + 1));
} }
public:
/* TODO: How should this size be determined. Does the KProcess slab count need to go in a header as a define? */
static constexpr size_t NumTtbr0Entries = 81;
private:
static constinit inline const volatile u64 s_ttbr0_entries[NumTtbr0Entries] = {};
private: private:
KPageTableManager *m_manager; KPageTableManager *m_manager;
u64 m_ttbr;
u8 m_asid; u8 m_asid;
protected: protected:
Result OperateImpl(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); Result OperateImpl(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);
@ -168,17 +172,28 @@ namespace ams::kern::arch::arm64 {
return entry; return entry;
} }
public: public:
constexpr explicit KPageTable(util::ConstantInitializeTag) : KPageTableBase(util::ConstantInitialize), m_manager(), m_ttbr(), m_asid() { /* ... */ } constexpr explicit KPageTable(util::ConstantInitializeTag) : KPageTableBase(util::ConstantInitialize), m_manager(), m_asid() { /* ... */ }
explicit KPageTable() { /* ... */ } explicit KPageTable() { /* ... */ }
static NOINLINE void Initialize(s32 core_id); static NOINLINE void Initialize(s32 core_id);
ALWAYS_INLINE void Activate(u32 proc_id) { static const volatile u64 &GetTtbr0Entry(size_t index) { return s_ttbr0_entries[index]; }
cpu::SwitchProcess(m_ttbr, proc_id);
static ALWAYS_INLINE u64 GetKernelTtbr0() {
return s_ttbr0_entries[0];
}
static ALWAYS_INLINE void ActivateKernel() {
/* Activate, using asid 0 and process id = 0xFFFFFFFF */
cpu::SwitchProcess(GetKernelTtbr0(), 0xFFFFFFFF);
}
static ALWAYS_INLINE void ActivateProcess(size_t proc_idx, u32 proc_id) {
cpu::SwitchProcess(s_ttbr0_entries[proc_idx + 1], proc_id);
} }
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end); NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag flags, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit); NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag flags, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit, size_t process_index);
Result Finalize(); Result Finalize();
private: private:
Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll); Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll);

View file

@ -23,13 +23,13 @@ namespace ams::kern::arch::arm64 {
private: private:
KPageTable m_page_table; KPageTable m_page_table;
public: public:
void Activate(u64 id) { void Activate(size_t process_index, u64 id) {
/* Activate the page table with the specified contextidr. */ /* Activate the page table with the specified contextidr. */
m_page_table.Activate(id); m_page_table.ActivateProcess(process_index, id);
} }
Result Initialize(ams::svc::CreateProcessFlag flags, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit) { Result Initialize(ams::svc::CreateProcessFlag flags, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit, size_t process_index) {
R_RETURN(m_page_table.InitializeForProcess(flags, from_back, pool, code_address, code_size, system_resource, resource_limit)); R_RETURN(m_page_table.InitializeForProcess(flags, from_back, pool, code_address, code_size, system_resource, resource_limit, process_index));
} }
void Finalize() { m_page_table.Finalize(); } void Finalize() { m_page_table.Finalize(); }
@ -154,8 +154,8 @@ namespace ams::kern::arch::arm64 {
R_RETURN(m_page_table.InvalidateCurrentProcessDataCache(address, size)); R_RETURN(m_page_table.InvalidateCurrentProcessDataCache(address, size));
} }
Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size) { Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size, bool force_debug_prod) {
R_RETURN(m_page_table.ReadDebugMemory(buffer, address, size)); R_RETURN(m_page_table.ReadDebugMemory(buffer, address, size, force_debug_prod));
} }
Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size, KMemoryState state) { Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size, KMemoryState state) {

View file

@ -29,8 +29,7 @@ namespace ams::kern::arch::arm64 {
NOINLINE void Initialize(s32 core_id); NOINLINE void Initialize(s32 core_id);
void Activate() { void Activate() {
/* Activate, using process id = 0xFFFFFFFF */ m_page_table.ActivateKernel();
m_page_table.Activate(0xFFFFFFFF);
} }
void ActivateForInit() { void ActivateForInit() {

View file

@ -25,6 +25,7 @@ namespace ams::kern::arch::arm64 {
static bool CopyMemoryFromUser(void *dst, const void *src, size_t size); static bool CopyMemoryFromUser(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserAligned32Bit(void *dst, const void *src, size_t size); static bool CopyMemoryFromUserAligned32Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserAligned64Bit(void *dst, const void *src, size_t size); static bool CopyMemoryFromUserAligned64Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserSize64Bit(void *dst, const void *src);
static bool CopyMemoryFromUserSize32Bit(void *dst, const void *src); static bool CopyMemoryFromUserSize32Bit(void *dst, const void *src);
static s32 CopyStringFromUser(void *dst, const void *src, size_t size); static s32 CopyStringFromUser(void *dst, const void *src, size_t size);

View file

@ -45,6 +45,7 @@ namespace ams::kern::board::nintendo::nx {
}; };
public: public:
/* Initialization. */ /* Initialization. */
static NOINLINE void ConfigureKTargetSystem();
static NOINLINE void InitializePhase1(); static NOINLINE void InitializePhase1();
static NOINLINE void InitializePhase2(); static NOINLINE void InitializePhase2();
static NOINLINE u32 GetCreateProcessMemoryPool(); static NOINLINE u32 GetCreateProcessMemoryPool();

View file

@ -39,14 +39,16 @@ namespace ams::kern {
} }
} }
Result WaitForAddress(uintptr_t addr, ams::svc::ArbitrationType type, s32 value, s64 timeout) { Result WaitForAddress(uintptr_t addr, ams::svc::ArbitrationType type, s64 value, s64 timeout) {
switch (type) { switch (type) {
case ams::svc::ArbitrationType_WaitIfLessThan: case ams::svc::ArbitrationType_WaitIfLessThan:
R_RETURN(this->WaitIfLessThan(addr, value, false, timeout)); R_RETURN(this->WaitIfLessThan(addr, static_cast<s32>(value), false, timeout));
case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan: case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan:
R_RETURN(this->WaitIfLessThan(addr, value, true, timeout)); R_RETURN(this->WaitIfLessThan(addr, static_cast<s32>(value), true, timeout));
case ams::svc::ArbitrationType_WaitIfEqual: case ams::svc::ArbitrationType_WaitIfEqual:
R_RETURN(this->WaitIfEqual(addr, value, timeout)); R_RETURN(this->WaitIfEqual(addr, static_cast<s32>(value), timeout));
case ams::svc::ArbitrationType_WaitIfEqual64:
R_RETURN(this->WaitIfEqual64(addr, value, timeout));
MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
} }
} }
@ -56,6 +58,7 @@ namespace ams::kern {
Result SignalAndModifyByWaitingCountIfEqual(uintptr_t addr, s32 value, s32 count); Result SignalAndModifyByWaitingCountIfEqual(uintptr_t addr, s32 value, s32 count);
Result WaitIfLessThan(uintptr_t addr, s32 value, bool decrement, s64 timeout); Result WaitIfLessThan(uintptr_t addr, s32 value, bool decrement, s64 timeout);
Result WaitIfEqual(uintptr_t addr, s32 value, s64 timeout); Result WaitIfEqual(uintptr_t addr, s32 value, s64 timeout);
Result WaitIfEqual64(uintptr_t addr, s64 value, s64 timeout);
}; };
} }

View file

@ -37,8 +37,8 @@ namespace ams::kern {
size_t m_size; size_t m_size;
Type m_type; Type m_type;
public: public:
static uintptr_t GetAddressSpaceStart(size_t width, Type type); static uintptr_t GetAddressSpaceStart(ams::svc::CreateProcessFlag flags, Type type);
static size_t GetAddressSpaceSize(size_t width, Type type); static size_t GetAddressSpaceSize(ams::svc::CreateProcessFlag flags, Type type);
static void SetAddressSpaceSize(size_t width, Type type, size_t size); static void SetAddressSpaceSize(size_t width, Type type, size_t size);

View file

@ -169,8 +169,9 @@ namespace ams::kern {
using IdBits = Field<0, CapabilityId<CapabilityType::DebugFlags> + 1>; using IdBits = Field<0, CapabilityId<CapabilityType::DebugFlags> + 1>;
DEFINE_FIELD(AllowDebug, IdBits, 1, bool); DEFINE_FIELD(AllowDebug, IdBits, 1, bool);
DEFINE_FIELD(ForceDebug, AllowDebug, 1, bool); DEFINE_FIELD(ForceDebugProd, AllowDebug, 1, bool);
DEFINE_FIELD(Reserved, ForceDebug, 13); DEFINE_FIELD(ForceDebug, ForceDebugProd, 1, bool);
DEFINE_FIELD(Reserved, ForceDebug, 12);
}; };
#undef DEFINE_FIELD #undef DEFINE_FIELD
@ -255,6 +256,10 @@ namespace ams::kern {
return m_debug_capabilities.Get<DebugFlags::AllowDebug>(); return m_debug_capabilities.Get<DebugFlags::AllowDebug>();
} }
constexpr bool CanForceDebugProd() const {
return m_debug_capabilities.Get<DebugFlags::ForceDebugProd>();
}
constexpr bool CanForceDebug() const { constexpr bool CanForceDebug() const {
return m_debug_capabilities.Get<DebugFlags::ForceDebug>(); return m_debug_capabilities.Get<DebugFlags::ForceDebug>();
} }

View file

@ -32,6 +32,7 @@ namespace ams::kern {
KLightLock m_lock; KLightLock m_lock;
KProcess::State m_old_process_state; KProcess::State m_old_process_state;
bool m_is_attached; bool m_is_attached;
bool m_is_force_debug_prod;
public: public:
explicit KDebugBase() { /* ... */ } explicit KDebugBase() { /* ... */ }
protected: protected:
@ -62,6 +63,10 @@ namespace ams::kern {
return m_is_attached; return m_is_attached;
} }
ALWAYS_INLINE bool IsForceDebugProd() const {
return m_is_force_debug_prod;
}
ALWAYS_INLINE bool OpenProcess() { ALWAYS_INLINE bool OpenProcess() {
return m_process_holder.Open(); return m_process_holder.Open();
} }

View file

@ -200,7 +200,8 @@ namespace ams::kern {
KMemoryBlockDisableMergeAttribute_DeviceLeft = (1u << 1), KMemoryBlockDisableMergeAttribute_DeviceLeft = (1u << 1),
KMemoryBlockDisableMergeAttribute_IpcLeft = (1u << 2), KMemoryBlockDisableMergeAttribute_IpcLeft = (1u << 2),
KMemoryBlockDisableMergeAttribute_Locked = (1u << 3), KMemoryBlockDisableMergeAttribute_Locked = (1u << 3),
KMemoryBlockDisableMergeAttribute_DeviceRight = (1u << 4), /* ... */
KMemoryBlockDisableMergeAttribute_DeviceRight = (1u << 5),
KMemoryBlockDisableMergeAttribute_AllLeft = KMemoryBlockDisableMergeAttribute_Normal | KMemoryBlockDisableMergeAttribute_DeviceLeft | KMemoryBlockDisableMergeAttribute_IpcLeft | KMemoryBlockDisableMergeAttribute_Locked, KMemoryBlockDisableMergeAttribute_AllLeft = KMemoryBlockDisableMergeAttribute_Normal | KMemoryBlockDisableMergeAttribute_DeviceLeft | KMemoryBlockDisableMergeAttribute_IpcLeft | KMemoryBlockDisableMergeAttribute_Locked,
KMemoryBlockDisableMergeAttribute_AllRight = KMemoryBlockDisableMergeAttribute_DeviceRight, KMemoryBlockDisableMergeAttribute_AllRight = KMemoryBlockDisableMergeAttribute_DeviceRight,
@ -288,18 +289,18 @@ namespace ams::kern {
class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> { class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
private: private:
u16 m_device_disable_merge_left_count;
u16 m_device_disable_merge_right_count;
KProcessAddress m_address;
size_t m_num_pages;
KMemoryState m_memory_state;
u16 m_ipc_lock_count;
u16 m_device_use_count;
u16 m_ipc_disable_merge_count;
KMemoryPermission m_permission; KMemoryPermission m_permission;
KMemoryPermission m_original_permission; KMemoryPermission m_original_permission;
KMemoryAttribute m_attribute; KMemoryAttribute m_attribute;
KMemoryBlockDisableMergeAttribute m_disable_merge_attribute; KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
KProcessAddress m_address;
u32 m_num_pages;
KMemoryState m_memory_state;
u16 m_ipc_lock_count;
u16 m_ipc_disable_merge_count;
u16 m_device_use_count;
u16 m_device_disable_merge_left_count;
u16 m_device_disable_merge_right_count;
public: public:
static constexpr ALWAYS_INLINE int Compare(const KMemoryBlock &lhs, const KMemoryBlock &rhs) { static constexpr ALWAYS_INLINE int Compare(const KMemoryBlock &lhs, const KMemoryBlock &rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) { if (lhs.GetAddress() < rhs.GetAddress()) {
@ -343,6 +344,10 @@ namespace ams::kern {
return m_ipc_disable_merge_count; return m_ipc_disable_merge_count;
} }
constexpr u16 GetDeviceUseCount() const {
return m_device_use_count;
}
constexpr KMemoryPermission GetPermission() const { constexpr KMemoryPermission GetPermission() const {
return m_permission; return m_permission;
} }
@ -374,16 +379,15 @@ namespace ams::kern {
public: public:
explicit KMemoryBlock() { /* ... */ } explicit KMemoryBlock() { /* ... */ }
constexpr KMemoryBlock(util::ConstantInitializeTag, KProcessAddress addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) constexpr KMemoryBlock(util::ConstantInitializeTag, KProcessAddress addr, u32 np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr)
: util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(util::ConstantInitialize), m_device_disable_merge_left_count(), : util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(util::ConstantInitialize), m_permission(p), m_original_permission(KMemoryPermission_None),
m_device_disable_merge_right_count(), m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0), m_attribute(attr), m_disable_merge_attribute(), m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0),
m_device_use_count(0), m_ipc_disable_merge_count(), m_permission(p), m_original_permission(KMemoryPermission_None), m_ipc_disable_merge_count(), m_device_use_count(0), m_device_disable_merge_left_count(), m_device_disable_merge_right_count()
m_attribute(attr), m_disable_merge_attribute()
{ {
/* ... */ /* ... */
} }
constexpr void Initialize(KProcessAddress addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) { constexpr void Initialize(KProcessAddress addr, u32 np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) {
MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT_THIS();
m_device_disable_merge_left_count = 0; m_device_disable_merge_left_count = 0;
m_device_disable_merge_right_count = 0; m_device_disable_merge_right_count = 0;

View file

@ -318,7 +318,7 @@ namespace ams::kern {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr)); R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr));
} }
Result CheckMemoryState(const KMemoryInfo &info, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) const; Result CheckMemoryState(KMemoryBlockManager::const_iterator it, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) const;
Result CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, size_t *out_blocks_needed, KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const; Result CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, size_t *out_blocks_needed, KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, size_t *out_blocks_needed, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const; Result CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, size_t *out_blocks_needed, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t *out_blocks_needed, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const { Result CheckMemoryState(size_t *out_blocks_needed, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const {
@ -328,6 +328,8 @@ namespace ams::kern {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr)); R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
} }
bool CanReadWriteDebugMemory(KProcessAddress addr, size_t size, bool force_debug_prod);
Result LockMemoryAndOpen(KPageGroup *out_pg, KPhysicalAddress *out_paddr, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr); Result LockMemoryAndOpen(KPageGroup *out_pg, KPhysicalAddress *out_paddr, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr);
Result UnlockMemory(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr, const KPageGroup *pg); Result UnlockMemory(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr, const KPageGroup *pg);
@ -421,7 +423,7 @@ namespace ams::kern {
Result InvalidateProcessDataCache(KProcessAddress address, size_t size); Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size); Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size);
Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size); Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size, bool force_debug_prod);
Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size, KMemoryState state); Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size, KMemoryState state);
Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size); Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size);

View file

@ -206,6 +206,10 @@ namespace ams::kern {
return m_capabilities.IsPermittedDebug(); return m_capabilities.IsPermittedDebug();
} }
constexpr bool CanForceDebugProd() const {
return m_capabilities.CanForceDebugProd();
}
constexpr bool CanForceDebug() const { constexpr bool CanForceDebug() const {
return m_capabilities.CanForceDebug(); return m_capabilities.CanForceDebug();
} }
@ -360,7 +364,7 @@ namespace ams::kern {
R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count)); R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count));
} }
Result WaitAddressArbiter(uintptr_t address, ams::svc::ArbitrationType arb_type, s32 value, s64 timeout) { Result WaitAddressArbiter(uintptr_t address, ams::svc::ArbitrationType arb_type, s64 value, s64 timeout) {
R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout)); R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout));
} }
@ -374,7 +378,7 @@ namespace ams::kern {
/* Update the current page table. */ /* Update the current page table. */
if (next_process) { if (next_process) {
next_process->GetPageTable().Activate(next_process->GetProcessId()); next_process->GetPageTable().Activate(next_process->GetSlabIndex(), next_process->GetProcessId());
} else { } else {
Kernel::GetKernelPageTable().Activate(); Kernel::GetKernelPageTable().Activate();
} }

View file

@ -69,6 +69,7 @@ namespace ams::kern {
static NOINLINE void InitializePhase1Base(u64 seed); static NOINLINE void InitializePhase1Base(u64 seed);
public: public:
/* Initialization. */ /* Initialization. */
static NOINLINE void ConfigureKTargetSystem();
static NOINLINE void InitializePhase1(); static NOINLINE void InitializePhase1();
static NOINLINE void InitializePhase2(); static NOINLINE void InitializePhase2();
static NOINLINE u32 GetCreateProcessMemoryPool(); static NOINLINE u32 GetCreateProcessMemoryPool();

View file

@ -24,29 +24,36 @@ namespace ams::kern {
friend class KSystemControlBase; friend class KSystemControlBase;
friend class KSystemControl; friend class KSystemControl;
private: private:
static inline constinit bool s_is_debug_mode; struct KTargetSystemData {
static inline constinit bool s_enable_debug_logging; bool is_debug_mode;
static inline constinit bool s_enable_user_exception_handlers; bool enable_debug_logging;
static inline constinit bool s_enable_debug_memory_fill; bool enable_user_exception_handlers;
static inline constinit bool s_enable_user_pmu_access; bool enable_debug_memory_fill;
static inline constinit bool s_enable_kernel_debugging; bool enable_user_pmu_access;
static inline constinit bool s_enable_dynamic_resource_limits; bool enable_kernel_debugging;
bool enable_dynamic_resource_limits;
};
private: private:
static ALWAYS_INLINE void SetIsDebugMode(bool en) { s_is_debug_mode = en; } static inline constinit bool s_is_initialized = false;
static ALWAYS_INLINE void EnableDebugLogging(bool en) { s_enable_debug_logging = en; } static inline constinit const volatile KTargetSystemData s_data = {
static ALWAYS_INLINE void EnableUserExceptionHandlers(bool en) { s_enable_user_exception_handlers = en; } .is_debug_mode = true,
static ALWAYS_INLINE void EnableDebugMemoryFill(bool en) { s_enable_debug_memory_fill = en; } .enable_debug_logging = true,
static ALWAYS_INLINE void EnableUserPmuAccess(bool en) { s_enable_user_pmu_access = en; } .enable_user_exception_handlers = true,
static ALWAYS_INLINE void EnableKernelDebugging(bool en) { s_enable_kernel_debugging = en; } .enable_debug_memory_fill = true,
static ALWAYS_INLINE void EnableDynamicResourceLimits(bool en) { s_enable_dynamic_resource_limits = en; } .enable_user_pmu_access = true,
.enable_kernel_debugging = true,
.enable_dynamic_resource_limits = false,
};
private:
static ALWAYS_INLINE void SetInitialized() { s_is_initialized = true; }
public: public:
static ALWAYS_INLINE bool IsDebugMode() { return s_is_debug_mode; } static ALWAYS_INLINE bool IsDebugMode() { return s_is_initialized && s_data.is_debug_mode; }
static ALWAYS_INLINE bool IsDebugLoggingEnabled() { return s_enable_debug_logging; } static ALWAYS_INLINE bool IsDebugLoggingEnabled() { return s_is_initialized && s_data.enable_debug_logging; }
static ALWAYS_INLINE bool IsUserExceptionHandlersEnabled() { return s_enable_user_exception_handlers; } static ALWAYS_INLINE bool IsUserExceptionHandlersEnabled() { return s_is_initialized && s_data.enable_user_exception_handlers; }
static ALWAYS_INLINE bool IsDebugMemoryFillEnabled() { return s_enable_debug_memory_fill; } static ALWAYS_INLINE bool IsDebugMemoryFillEnabled() { return s_is_initialized && s_data.enable_debug_memory_fill; }
static ALWAYS_INLINE bool IsUserPmuAccessEnabled() { return s_enable_user_pmu_access; } static ALWAYS_INLINE bool IsUserPmuAccessEnabled() { return s_is_initialized && s_data.enable_user_pmu_access; }
static ALWAYS_INLINE bool IsKernelDebuggingEnabled() { return s_enable_kernel_debugging; } static ALWAYS_INLINE bool IsKernelDebuggingEnabled() { return s_is_initialized && s_data.enable_kernel_debugging; }
static ALWAYS_INLINE bool IsDynamicResourceLimitsEnabled() { return s_enable_dynamic_resource_limits; } static ALWAYS_INLINE bool IsDynamicResourceLimitsEnabled() { return s_is_initialized && s_data.enable_dynamic_resource_limits; }
}; };
} }

View file

@ -48,6 +48,22 @@ namespace ams::kern {
KProcess *GetOwner() const { return m_owner; } KProcess *GetOwner() const { return m_owner; }
KProcessAddress GetSourceAddress() { return m_address; } KProcessAddress GetSourceAddress() { return m_address; }
size_t GetSize() const { return m_is_initialized ? GetReference(m_page_group).GetNumPages() * PageSize : 0; } size_t GetSize() const { return m_is_initialized ? GetReference(m_page_group).GetNumPages() * PageSize : 0; }
constexpr uintptr_t GetHint() const {
/* Get memory size. */
const size_t size = this->GetSize();
/* TODO: Is this architecture specific? */
if (size >= 2_MB) {
return GetInteger(m_address) & (2_MB - 1);
} else if (size >= 64_KB) {
return GetInteger(m_address) & (64_KB - 1);
} else if (size >= 4_KB) {
return GetInteger(m_address) & (4_KB - 1);
} else {
return 0;
}
}
}; };
} }

View file

@ -79,6 +79,12 @@ namespace ams::kern::arch::arm {
/* Setup all interrupt lines. */ /* Setup all interrupt lines. */
SetupInterruptLines(core_id); SetupInterruptLines(core_id);
/* Clear pointers, if needed. */
if (core_id == 0) {
m_gicd = nullptr;
m_gicc = nullptr;
}
} }
void KInterruptController::SaveCoreLocal(LocalState *state) const { void KInterruptController::SaveCoreLocal(LocalState *state) const {

View file

@ -15,6 +15,28 @@
*/ */
#include <mesosphere.hpp> #include <mesosphere.hpp>
/* <stratosphere/rocrt/rocrt.hpp> */
namespace ams::rocrt {
constexpr inline const u32 ModuleHeaderVersion = util::FourCC<'M','O','D','0'>::Code;
struct ModuleHeader {
u32 signature;
u32 dynamic_offset;
u32 bss_start_offset;
u32 bss_end_offset;
u32 exception_info_start_offset;
u32 exception_info_end_offset;
u32 module_offset;
};
struct ModuleHeaderLocation {
u32 pad;
u32 header_offset;
};
}
namespace ams::kern::arch::arm64 { namespace ams::kern::arch::arm64 {
namespace { namespace {
@ -657,7 +679,7 @@ namespace ams::kern::arch::arm64 {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
} }
dyn_address = module.start_address + mod_offset + temp_32; dyn_address = base_address + mod_offset + temp_32;
} }
/* Locate tables inside .dyn. */ /* Locate tables inside .dyn. */

View file

@ -85,77 +85,6 @@ namespace ams::kern::arch::arm64 {
return (static_cast<u64>(asid) << 48) | (static_cast<u64>(GetInteger(table))); 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 m_state[NumWords];
KLightLock m_lock;
u8 m_hint;
private:
constexpr bool TestImpl(u8 asid) const {
return m_state[asid / BitsPerWord] & (1u << (asid % BitsPerWord));
}
constexpr void ReserveImpl(u8 asid) {
MESOSPHERE_ASSERT(!this->TestImpl(asid));
m_state[asid / BitsPerWord] |= (1u << (asid % BitsPerWord));
}
constexpr void ReleaseImpl(u8 asid) {
MESOSPHERE_ASSERT(this->TestImpl(asid));
m_state[asid / BitsPerWord] &= ~(1u << (asid % BitsPerWord));
}
constexpr u8 FindAvailable() const {
for (size_t i = 0; i < util::size(m_state); i++) {
if (m_state[i] == FullWord) {
continue;
}
const WordType clear_bit = (m_state[i] + 1) ^ (m_state[i]);
return BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit);
}
if (m_state[util::size(m_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() : m_state(), m_lock(), m_hint() {
for (size_t i = 0; i < NumReservedAsids; i++) {
this->ReserveImpl(ReservedAsids[i]);
}
}
u8 Reserve() {
KScopedLightLock lk(m_lock);
if (this->TestImpl(m_hint)) {
m_hint = this->FindAvailable();
}
this->ReserveImpl(m_hint);
return m_hint++;
}
void Release(u8 asid) {
KScopedLightLock lk(m_lock);
this->ReleaseImpl(asid);
}
};
KPageTableAsidManager g_asid_manager;
} }
ALWAYS_INLINE void KPageTable::NoteUpdated() const { ALWAYS_INLINE void KPageTable::NoteUpdated() const {
@ -184,6 +113,7 @@ namespace ams::kern::arch::arm64 {
this->OnKernelTableSinglePageUpdated(virt_addr); this->OnKernelTableSinglePageUpdated(virt_addr);
} }
void KPageTable::Initialize(s32 core_id) { void KPageTable::Initialize(s32 core_id) {
/* Nothing actually needed here. */ /* Nothing actually needed here. */
MESOSPHERE_UNUSED(core_id); MESOSPHERE_UNUSED(core_id);
@ -194,38 +124,29 @@ namespace ams::kern::arch::arm64 {
m_asid = 0; m_asid = 0;
m_manager = Kernel::GetSystemSystemResource().GetPageTableManagerPointer(); m_manager = Kernel::GetSystemSystemResource().GetPageTableManagerPointer();
/* Allocate a page for ttbr. */
/* NOTE: It is a postcondition of page table manager allocation that the page is all-zero. */
const u64 asid_tag = (static_cast<u64>(m_asid) << 48ul);
const KVirtualAddress page = m_manager->Allocate();
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
/* Initialize the base page table. */ /* Initialize the base page table. */
MESOSPHERE_R_ABORT_UNLESS(KPageTableBase::InitializeForKernel(true, table, start, end)); MESOSPHERE_R_ABORT_UNLESS(KPageTableBase::InitializeForKernel(true, table, start, end));
R_SUCCEED(); R_SUCCEED();
} }
Result KPageTable::InitializeForProcess(ams::svc::CreateProcessFlag flags, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit) { Result KPageTable::InitializeForProcess(ams::svc::CreateProcessFlag flags, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit, size_t process_index) {
/* Get an ASID */ /* Determine our ASID */
m_asid = g_asid_manager.Reserve(); m_asid = process_index + 1;
ON_RESULT_FAILURE { g_asid_manager.Release(m_asid); }; MESOSPHERE_ABORT_UNLESS(0 < m_asid && m_asid < util::size(s_ttbr0_entries));
/* Set our manager. */ /* Set our manager. */
m_manager = system_resource->GetPageTableManagerPointer(); m_manager = system_resource->GetPageTableManagerPointer();
/* Allocate a new table, and set our ttbr value. */ /* Get the virtual address of our L1 table. */
const KVirtualAddress new_table = m_manager->Allocate(); const KPhysicalAddress ttbr0_phys = KPhysicalAddress(s_ttbr0_entries[m_asid] & UINT64_C(0xFFFFFFFFFFFE));
R_UNLESS(new_table != Null<KVirtualAddress>, svc::ResultOutOfResource()); const KVirtualAddress ttbr0_virt = KMemoryLayout::GetLinearVirtualAddress(ttbr0_phys);
m_ttbr = EncodeTtbr(GetPageTablePhysicalAddress(new_table), m_asid);
ON_RESULT_FAILURE_2 { m_manager->Free(new_table); };
/* Initialize our base table. */ /* Initialize our base table. */
const size_t as_width = GetAddressSpaceWidth(flags); const size_t as_width = GetAddressSpaceWidth(flags);
const KProcessAddress as_start = 0; const KProcessAddress as_start = 0;
const KProcessAddress as_end = (1ul << as_width); const KProcessAddress as_end = (1ul << as_width);
R_TRY(KPageTableBase::InitializeForProcess(flags, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, system_resource, resource_limit)); R_TRY(KPageTableBase::InitializeForProcess(flags, from_back, pool, GetVoidPointer(ttbr0_virt), as_start, as_end, code_address, code_size, system_resource, resource_limit));
/* Note that we've updated the table (since we created it). */ /* Note that we've updated the table (since we created it). */
this->NoteUpdated(); this->NoteUpdated();
@ -329,20 +250,16 @@ namespace ams::kern::arch::arm64 {
} }
} }
/* Free the L1 table. */ /* Clear the L1 table. */
{ {
const KVirtualAddress l1_table = reinterpret_cast<uintptr_t>(impl.Finalize()); const KVirtualAddress l1_table = reinterpret_cast<uintptr_t>(impl.Finalize());
ClearPageTable(l1_table); ClearPageTable(l1_table);
this->GetPageTableManager().Free(l1_table);
} }
/* Perform inherited finalization. */ /* Perform inherited finalization. */
KPageTableBase::Finalize(); KPageTableBase::Finalize();
} }
/* Release our asid. */
g_asid_manager.Release(m_asid);
R_SUCCEED(); R_SUCCEED();
} }

View file

@ -126,6 +126,20 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess30CopyMemoryFromUserAligned64BitEPvPKvm:
mov x0, #1 mov x0, #1
ret ret
/* ams::kern::arch::arm64::UserspaceAccess::CopyMemoryFromUserSize64Bit(void *dst, const void *src) */
.section .text._ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize64BitEPvPKv, "ax", %progbits
.global _ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize64BitEPvPKv
.type _ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize64BitEPvPKv, %function
.balign 0x10
_ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize64BitEPvPKv:
/* Just load and store a u64. */
ldtr x2, [x1]
str x2, [x0]
/* We're done. */
mov x0, #1
ret
/* ams::kern::arch::arm64::UserspaceAccess::CopyMemoryFromUserSize32Bit(void *dst, const void *src) */ /* ams::kern::arch::arm64::UserspaceAccess::CopyMemoryFromUserSize32Bit(void *dst, const void *src) */
.section .text._ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize32BitEPvPKv, "ax", %progbits .section .text._ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize32BitEPvPKv, "ax", %progbits
.global _ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize32BitEPvPKv .global _ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize32BitEPvPKv

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 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/>.
*/
/* ams::kern::svc::CallWaitForAddress64From32() */
.section .text._ZN3ams4kern3svc26CallWaitForAddress64From32Ev, "ax", %progbits
.global _ZN3ams4kern3svc26CallWaitForAddress64From32Ev
.type _ZN3ams4kern3svc26CallWaitForAddress64From32Ev, %function
_ZN3ams4kern3svc26CallWaitForAddress64From32Ev:
/* Save LR + callee-save registers. */
str x30, [sp, #-16]!
stp x6, x7, [sp, #-16]!
/* Gather the arguments into correct registers. */
/* NOTE: This has to be manually implemented via asm, */
/* in order to avoid breaking ABI with pre-19.0.0. */
orr x2, x2, x5, lsl#32
orr x3, x3, x4, lsl#32
/* Invoke the svc handler. */
bl _ZN3ams4kern3svc22WaitForAddress64From32ENS_3svc7AddressENS2_15ArbitrationTypeEll
/* Clean up registers. */
mov x1, xzr
mov x2, xzr
mov x3, xzr
mov x4, xzr
mov x5, xzr
ldp x6, x7, [sp], #0x10
ldr x30, [sp], #0x10
ret
/* ams::kern::svc::CallWaitForAddress64() */
.section .text._ZN3ams4kern3svc20CallWaitForAddress64Ev, "ax", %progbits
.global _ZN3ams4kern3svc20CallWaitForAddress64Ev
.type _ZN3ams4kern3svc20CallWaitForAddress64Ev, %function
_ZN3ams4kern3svc20CallWaitForAddress64Ev:
/* Save LR + FP. */
stp x29, x30, [sp, #-16]!
/* Invoke the svc handler. */
bl _ZN3ams4kern3svc22WaitForAddress64From32ENS_3svc7AddressENS2_15ArbitrationTypeEll
/* Clean up registers. */
mov x1, xzr
mov x2, xzr
mov x3, xzr
mov x4, xzr
mov x5, xzr
mov x6, xzr
mov x7, xzr
ldp x29, x30, [sp], #0x10
ret

View file

@ -36,6 +36,10 @@ namespace ams::kern::svc {
/* Declare special prototype for (unsupported) CallCallSecureMonitor64From32. */ /* Declare special prototype for (unsupported) CallCallSecureMonitor64From32. */
void CallCallSecureMonitor64From32(); void CallCallSecureMonitor64From32();
/* Declare special prototypes for WaitForAddress. */
void CallWaitForAddress64();
void CallWaitForAddress64From32();
namespace { namespace {
#ifndef MESOSPHERE_USE_STUBBED_SVC_TABLES #ifndef MESOSPHERE_USE_STUBBED_SVC_TABLES
@ -81,6 +85,8 @@ namespace ams::kern::svc {
table[svc::SvcId_CallSecureMonitor] = CallCallSecureMonitor64From32; table[svc::SvcId_CallSecureMonitor] = CallCallSecureMonitor64From32;
table[svc::SvcId_WaitForAddress] = CallWaitForAddress64From32;
return table; return table;
}(); }();
@ -97,6 +103,8 @@ namespace ams::kern::svc {
table[svc::SvcId_ReturnFromException] = CallReturnFromException64; table[svc::SvcId_ReturnFromException] = CallReturnFromException64;
table[svc::SvcId_WaitForAddress] = CallWaitForAddress64;
return table; return table;
}(); }();

View file

@ -31,7 +31,6 @@ namespace ams::kern::board::nintendo::nx {
/* Struct representing registers saved on wake/sleep. */ /* Struct representing registers saved on wake/sleep. */
class SavedSystemRegisters { class SavedSystemRegisters {
private: private:
u64 ttbr0_el1;
u64 elr_el1; u64 elr_el1;
u64 sp_el0; u64 sp_el0;
u64 spsr_el1; u64 spsr_el1;
@ -90,7 +89,6 @@ namespace ams::kern::board::nintendo::nx {
void SavedSystemRegisters::Save() { void SavedSystemRegisters::Save() {
/* Save system registers. */ /* Save system registers. */
this->ttbr0_el1 = cpu::GetTtbr0El1();
this->tpidr_el0 = cpu::GetTpidrEl0(); this->tpidr_el0 = cpu::GetTpidrEl0();
this->elr_el1 = cpu::GetElrEl1(); this->elr_el1 = cpu::GetElrEl1();
this->sp_el0 = cpu::GetSpEl0(); this->sp_el0 = cpu::GetSpEl0();
@ -405,7 +403,7 @@ namespace ams::kern::board::nintendo::nx {
cpu::EnsureInstructionConsistency(); cpu::EnsureInstructionConsistency();
/* Restore system registers. */ /* Restore system registers. */
cpu::SetTtbr0El1 (this->ttbr0_el1); cpu::SetTtbr0El1 (KPageTable::GetKernelTtbr0());
cpu::SetTpidrEl0 (this->tpidr_el0); cpu::SetTpidrEl0 (this->tpidr_el0);
cpu::SetElrEl1 (this->elr_el1); cpu::SetElrEl1 (this->elr_el1);
cpu::SetSpEl0 (this->sp_el0); cpu::SetSpEl0 (this->sp_el0);

View file

@ -26,7 +26,7 @@ namespace ams::kern::board::nintendo::nx {
constexpr size_t SecureSizeMax = util::AlignDown(512_MB - 1, SecureAlignment); constexpr size_t SecureSizeMax = util::AlignDown(512_MB - 1, SecureAlignment);
/* Global variables for panic. */ /* Global variables for panic. */
constinit bool g_call_smc_on_panic; constinit const volatile bool g_call_smc_on_panic = false;
/* Global variables for secure memory. */ /* Global variables for secure memory. */
constinit KSpinLock g_secure_applet_lock; constinit KSpinLock g_secure_applet_lock;
@ -401,34 +401,67 @@ namespace ams::kern::board::nintendo::nx {
} }
/* System Initialization. */ /* System Initialization. */
void KSystemControl::InitializePhase1() { void KSystemControl::ConfigureKTargetSystem() {
/* Configure KTargetSystem. */ /* Configure KTargetSystem. */
volatile auto *ts = const_cast<volatile KTargetSystem::KTargetSystemData *>(std::addressof(KTargetSystem::s_data));
{ {
/* Set IsDebugMode. */ /* Set IsDebugMode. */
{ {
KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode)); ts->is_debug_mode = GetConfigBool(smc::ConfigItem::IsDebugMode);
/* If debug mode, we want to initialize uart logging. */ /* If debug mode, we want to initialize uart logging. */
KTargetSystem::EnableDebugLogging(KTargetSystem::IsDebugMode()); ts->enable_debug_logging = ts->is_debug_mode;
} }
/* Set Kernel Configuration. */ /* Set Kernel Configuration. */
{ {
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)}; const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
KTargetSystem::EnableDebugMemoryFill(kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>()); ts->enable_debug_memory_fill = kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>();
KTargetSystem::EnableUserExceptionHandlers(kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>()); ts->enable_user_exception_handlers = kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>();
KTargetSystem::EnableDynamicResourceLimits(!kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>()); ts->enable_dynamic_resource_limits = !kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>();
KTargetSystem::EnableUserPmuAccess(kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>()); ts->enable_user_pmu_access = kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>();
g_call_smc_on_panic = kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>(); /* Configure call smc on panic. */
*const_cast<volatile bool *>(std::addressof(g_call_smc_on_panic)) = kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>();
} }
/* Set Kernel Debugging. */ /* Set Kernel Debugging. */
{ {
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */ /* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */ /* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
KTargetSystem::EnableKernelDebugging(GetConfigBool(smc::ConfigItem::DisableProgramVerification)); ts->enable_kernel_debugging = GetConfigBool(smc::ConfigItem::DisableProgramVerification);
}
}
}
void KSystemControl::InitializePhase1() {
/* Enable KTargetSystem. */
KTargetSystem::SetInitialized();
/* Check KTargetSystem was configured correctly. */
{
/* Check IsDebugMode. */
{
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDebugMode() == GetConfigBool(smc::ConfigItem::IsDebugMode));
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDebugLoggingEnabled() == GetConfigBool(smc::ConfigItem::IsDebugMode));
}
/* Check Kernel Configuration. */
{
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDebugMemoryFillEnabled() == kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>());
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsUserExceptionHandlersEnabled() == kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>());
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled() == !kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>());
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsUserPmuAccessEnabled() == kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>());
MESOSPHERE_ABORT_UNLESS(g_call_smc_on_panic == kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>());
}
/* Check Kernel Debugging. */
{
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsKernelDebuggingEnabled() == GetConfigBool(smc::ConfigItem::DisableProgramVerification));
} }
} }

View file

@ -23,6 +23,10 @@ namespace ams::kern {
return UserspaceAccess::CopyMemoryFromUserSize32Bit(out, GetVoidPointer(address)); return UserspaceAccess::CopyMemoryFromUserSize32Bit(out, GetVoidPointer(address));
} }
ALWAYS_INLINE bool ReadFromUser(s64 *out, KProcessAddress address) {
return UserspaceAccess::CopyMemoryFromUserSize64Bit(out, GetVoidPointer(address));
}
ALWAYS_INLINE bool DecrementIfLessThan(s32 *out, KProcessAddress address, s32 value) { ALWAYS_INLINE bool DecrementIfLessThan(s32 *out, KProcessAddress address, s32 value) {
/* NOTE: If scheduler lock is not held here, interrupt disable is required. */ /* NOTE: If scheduler lock is not held here, interrupt disable is required. */
/* KScopedInterruptDisable di; */ /* KScopedInterruptDisable di; */
@ -279,4 +283,51 @@ namespace ams::kern {
R_RETURN(cur_thread->GetWaitResult()); R_RETURN(cur_thread->GetWaitResult());
} }
Result KAddressArbiter::WaitIfEqual64(uintptr_t addr, s64 value, s64 timeout) {
/* Prepare to wait. */
KThread *cur_thread = GetCurrentThreadPointer();
KHardwareTimer *timer;
ThreadQueueImplForKAddressArbiter wait_queue(std::addressof(m_tree));
{
KScopedSchedulerLockAndSleep slp(std::addressof(timer), cur_thread, timeout);
/* Check that the thread isn't terminating. */
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
R_THROW(svc::ResultTerminationRequested());
}
/* Read the value from userspace. */
s64 user_value;
if (!ReadFromUser(std::addressof(user_value), addr)) {
slp.CancelSleep();
R_THROW(svc::ResultInvalidCurrentMemory());
}
/* Check that the value is equal. */
if (value != user_value) {
slp.CancelSleep();
R_THROW(svc::ResultInvalidState());
}
/* Check that the timeout is non-zero. */
if (timeout == 0) {
slp.CancelSleep();
R_THROW(svc::ResultTimedOut());
}
/* Set the arbiter. */
cur_thread->SetAddressArbiter(std::addressof(m_tree), addr);
m_tree.insert(*cur_thread);
/* Wait for the thread to finish. */
wait_queue.SetHardwareTimer(timer);
cur_thread->BeginWait(std::addressof(wait_queue));
}
/* Get the wait result. */
R_RETURN(cur_thread->GetWaitResult());
}
} }

View file

@ -37,6 +37,24 @@ namespace ams::kern {
{ 39, Invalid, ams::svc::AddressMemoryRegionStack39Size, KAddressSpaceInfo::Type_Stack, }, { 39, Invalid, ams::svc::AddressMemoryRegionStack39Size, KAddressSpaceInfo::Type_Stack, },
}; };
constexpr u8 FlagsToAddressSpaceWidthTable[4] = {
32, 36, 32, 39
};
constexpr size_t GetAddressSpaceWidth(ams::svc::CreateProcessFlag flags) {
/* Convert the input flags to an array index. */
const size_t idx = (flags & ams::svc::CreateProcessFlag_AddressSpaceMask) >> ams::svc::CreateProcessFlag_AddressSpaceShift;
MESOSPHERE_ABORT_UNLESS(idx < sizeof(FlagsToAddressSpaceWidthTable));
/* Return the width. */
return FlagsToAddressSpaceWidthTable[idx];
}
static_assert(GetAddressSpaceWidth(ams::svc::CreateProcessFlag_AddressSpace32Bit) == 32);
static_assert(GetAddressSpaceWidth(ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated) == 36);
static_assert(GetAddressSpaceWidth(ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias) == 32);
static_assert(GetAddressSpaceWidth(ams::svc::CreateProcessFlag_AddressSpace64Bit) == 39);
KAddressSpaceInfo &GetAddressSpaceInfo(size_t width, KAddressSpaceInfo::Type type) { KAddressSpaceInfo &GetAddressSpaceInfo(size_t width, KAddressSpaceInfo::Type type) {
for (auto &info : AddressSpaceInfos) { for (auto &info : AddressSpaceInfos) {
if (info.GetWidth() == width && info.GetType() == type) { if (info.GetWidth() == width && info.GetType() == type) {
@ -48,12 +66,12 @@ namespace ams::kern {
} }
uintptr_t KAddressSpaceInfo::GetAddressSpaceStart(size_t width, KAddressSpaceInfo::Type type) { uintptr_t KAddressSpaceInfo::GetAddressSpaceStart(ams::svc::CreateProcessFlag flags, KAddressSpaceInfo::Type type) {
return GetAddressSpaceInfo(width, type).GetAddress(); return GetAddressSpaceInfo(GetAddressSpaceWidth(flags), type).GetAddress();
} }
size_t KAddressSpaceInfo::GetAddressSpaceSize(size_t width, KAddressSpaceInfo::Type type) { size_t KAddressSpaceInfo::GetAddressSpaceSize(ams::svc::CreateProcessFlag flags, KAddressSpaceInfo::Type type) {
return GetAddressSpaceInfo(width, type).GetSize(); return GetAddressSpaceInfo(GetAddressSpaceWidth(flags), type).GetSize();
} }
void KAddressSpaceInfo::SetAddressSpaceSize(size_t width, Type type, size_t size) { void KAddressSpaceInfo::SetAddressSpaceSize(size_t width, Type type, size_t size) {

View file

@ -262,7 +262,14 @@ namespace ams::kern {
/* Validate. */ /* Validate. */
R_UNLESS(cap.Get<DebugFlags::Reserved>() == 0, svc::ResultReservedUsed()); R_UNLESS(cap.Get<DebugFlags::Reserved>() == 0, svc::ResultReservedUsed());
u32 total = 0;
if (cap.Get<DebugFlags::AllowDebug>()) { ++total; }
if (cap.Get<DebugFlags::ForceDebugProd>()) { ++total; }
if (cap.Get<DebugFlags::ForceDebug>()) { ++total; }
R_UNLESS(total <= 1, svc::ResultInvalidCombination());
m_debug_capabilities.Set<DebugFlags::AllowDebug>(cap.Get<DebugFlags::AllowDebug>()); m_debug_capabilities.Set<DebugFlags::AllowDebug>(cap.Get<DebugFlags::AllowDebug>());
m_debug_capabilities.Set<DebugFlags::ForceDebugProd>(cap.Get<DebugFlags::ForceDebugProd>());
m_debug_capabilities.Set<DebugFlags::ForceDebug>(cap.Get<DebugFlags::ForceDebug>()); m_debug_capabilities.Set<DebugFlags::ForceDebug>(cap.Get<DebugFlags::ForceDebug>());
R_SUCCEED(); R_SUCCEED();
} }

View file

@ -28,6 +28,7 @@ namespace ams::kern {
void KDebugBase::Initialize() { void KDebugBase::Initialize() {
/* Clear the continue flags. */ /* Clear the continue flags. */
m_continue_flags = 0; m_continue_flags = 0;
m_is_force_debug_prod = GetCurrentProcess().CanForceDebugProd();
} }
bool KDebugBase::Is64Bit() const { bool KDebugBase::Is64Bit() const {
@ -120,8 +121,11 @@ namespace ams::kern {
/* Read the memory. */ /* Read the memory. */
if (info.GetSvcState() != ams::svc::MemoryState_Io) { if (info.GetSvcState() != ams::svc::MemoryState_Io) {
/* The memory is normal memory. */ /* The memory is normal memory. */
R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size)); R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size, this->IsForceDebugProd()));
} else { } else {
/* Only allow IO memory to be read if not force debug prod. */
R_UNLESS(!this->IsForceDebugProd(), svc::ResultInvalidCurrentMemory());
/* The memory is IO memory. */ /* The memory is IO memory. */
R_TRY(target_pt.ReadDebugIoMemory(GetVoidPointer(buffer), cur_address, cur_size, info.GetState())); R_TRY(target_pt.ReadDebugIoMemory(GetVoidPointer(buffer), cur_address, cur_size, info.GetState()));
} }
@ -269,6 +273,9 @@ namespace ams::kern {
switch (state) { switch (state) {
case KProcess::State_Created: case KProcess::State_Created:
case KProcess::State_Running: case KProcess::State_Running:
/* Created and running processes can only be debugged if the debugger is not ForceDebugProd. */
R_UNLESS(!this->IsForceDebugProd(), svc::ResultInvalidState());
break;
case KProcess::State_Crashed: case KProcess::State_Crashed:
break; break;
case KProcess::State_CreatedAttached: case KProcess::State_CreatedAttached:
@ -408,69 +415,6 @@ namespace ams::kern {
/* Get the process pointer. */ /* Get the process pointer. */
KProcess * const target = this->GetProcessUnsafe(); KProcess * const target = this->GetProcessUnsafe();
/* Detach from the process. */
{
/* Lock both ourselves and the target process. */
KScopedLightLock state_lk(target->GetStateLock());
KScopedLightLock list_lk(target->GetListLock());
KScopedLightLock this_lk(m_lock);
/* Check that we're still attached. */
if (this->IsAttached()) {
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Get the process's state. */
const KProcess::State state = target->GetState();
/* Check that the process is in a state where we can terminate it. */
R_UNLESS(state != KProcess::State_Created, svc::ResultInvalidState());
R_UNLESS(state != KProcess::State_CreatedAttached, svc::ResultInvalidState());
/* Decide on a new state for the process. */
KProcess::State new_state;
if (state == KProcess::State_RunningAttached) {
/* If the process is running, transition it accordingly. */
new_state = KProcess::State_Running;
} else if (state == KProcess::State_DebugBreak) {
/* If the process is debug breaked, transition it accordingly. */
new_state = KProcess::State_Crashed;
/* Suspend all the threads in the process. */
{
auto end = target->GetThreadList().end();
for (auto it = target->GetThreadList().begin(); it != end; ++it) {
/* Request that we suspend the thread. */
it->RequestSuspend(KThread::SuspendType_Debug);
}
}
} else {
/* Otherwise, don't transition. */
new_state = state;
}
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Clear single step on all threads. */
{
auto end = target->GetThreadList().end();
for (auto it = target->GetThreadList().begin(); it != end; ++it) {
it->ClearHardwareSingleStep();
}
}
#endif
/* Detach from the process. */
target->ClearDebugObject(new_state);
m_is_attached = false;
/* Close the initial reference opened to our process. */
this->CloseProcess();
/* Clear our continue flags. */
m_continue_flags = 0;
}
}
/* Terminate the process. */ /* Terminate the process. */
target->Terminate(); target->Terminate();
@ -962,8 +906,13 @@ namespace ams::kern {
case ams::svc::DebugException_UndefinedInstruction: case ams::svc::DebugException_UndefinedInstruction:
{ {
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1); MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1);
/* Only save the instruction if the caller is not force debug prod. */
if (this->IsForceDebugProd()) {
out->info.exception.specific.undefined_instruction.insn = 0;
} else {
out->info.exception.specific.undefined_instruction.insn = info->info.exception.exception_data[0]; out->info.exception.specific.undefined_instruction.insn = info->info.exception.exception_data[0];
} }
}
break; break;
case ams::svc::DebugException_BreakPoint: case ams::svc::DebugException_BreakPoint:
{ {

View file

@ -193,40 +193,25 @@ namespace ams::kern {
R_UNLESS(this->Is64Bit(), svc::ResultInvalidCombination()); R_UNLESS(this->Is64Bit(), svc::ResultInvalidCombination());
} }
using ASType = KAddressSpaceInfo::Type;
const uintptr_t start_address = rx_address; const uintptr_t start_address = rx_address;
const uintptr_t end_address = bss_size > 0 ? bss_address + bss_size : rw_address + rw_size; const uintptr_t end_address = bss_size > 0 ? bss_address + bss_size : rw_address + rw_size;
const size_t as_width = this->Is64BitAddressSpace() ? ((GetTargetFirmware() >= TargetFirmware_2_0_0) ? 39 : 36) : 32;
const ASType as_type = this->Is64BitAddressSpace() ? ((GetTargetFirmware() >= TargetFirmware_2_0_0) ? KAddressSpaceInfo::Type_Map39Bit : KAddressSpaceInfo::Type_MapSmall) : KAddressSpaceInfo::Type_MapSmall;
const uintptr_t map_start = KAddressSpaceInfo::GetAddressSpaceStart(as_width, as_type);
const size_t map_size = KAddressSpaceInfo::GetAddressSpaceSize(as_width, as_type);
const uintptr_t map_end = map_start + map_size;
MESOSPHERE_ABORT_UNLESS(start_address == 0); MESOSPHERE_ABORT_UNLESS(start_address == 0);
/* Default fields in parameter to zero. */ /* Default fields in parameter to zero. */
*out = {}; *out = {};
/* Set fields in parameter. */ /* Set fields in parameter. */
out->code_address = map_start + start_address; out->code_address = 0;
out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize; out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize;
out->program_id = m_kip_header.GetProgramId(); out->program_id = m_kip_header.GetProgramId();
out->version = m_kip_header.GetVersion(); out->version = m_kip_header.GetVersion();
out->flags = 0; out->flags = 0;
out->reslimit = ams::svc::InvalidHandle; out->reslimit = ams::svc::InvalidHandle;
out->system_resource_num_pages = 0; out->system_resource_num_pages = 0;
MESOSPHERE_ABORT_UNLESS((out->code_address / PageSize) + out->code_num_pages <= (map_end / PageSize));
/* Copy name field. */ /* Copy name field. */
m_kip_header.GetName(out->name, sizeof(out->name)); m_kip_header.GetName(out->name, sizeof(out->name));
/* Apply ASLR, if needed. */
if (enable_aslr) {
const size_t choices = (map_end / KernelAslrAlignment) - (util::AlignUp(out->code_address + out->code_num_pages * PageSize, KernelAslrAlignment) / KernelAslrAlignment);
out->code_address += KSystemControl::GenerateRandomRange(0, choices) * KernelAslrAlignment;
out->flags |= ams::svc::CreateProcessFlag_EnableAslr;
}
/* Apply other flags. */ /* Apply other flags. */
if (this->Is64Bit()) { if (this->Is64Bit()) {
out->flags |= ams::svc::CreateProcessFlag_Is64Bit; out->flags |= ams::svc::CreateProcessFlag_Is64Bit;
@ -236,10 +221,28 @@ namespace ams::kern {
} else { } else {
out->flags |= ams::svc::CreateProcessFlag_AddressSpace32Bit; out->flags |= ams::svc::CreateProcessFlag_AddressSpace32Bit;
} }
if (enable_aslr) {
out->flags |= ams::svc::CreateProcessFlag_EnableAslr;
}
/* All initial processes should disable device address space merge. */ /* All initial processes should disable device address space merge. */
out->flags |= ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge; out->flags |= ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
/* Set and check code address. */
using ASType = KAddressSpaceInfo::Type;
const ASType as_type = this->Is64BitAddressSpace() ? ((GetTargetFirmware() >= TargetFirmware_2_0_0) ? KAddressSpaceInfo::Type_Map39Bit : KAddressSpaceInfo::Type_MapSmall) : KAddressSpaceInfo::Type_MapSmall;
const uintptr_t map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(out->flags), as_type);
const size_t map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(out->flags), as_type);
const uintptr_t map_end = map_start + map_size;
out->code_address = map_start + start_address;
MESOSPHERE_ABORT_UNLESS((out->code_address / PageSize) + out->code_num_pages <= (map_end / PageSize));
/* Apply ASLR, if needed. */
if (enable_aslr) {
const size_t choices = (map_end / KernelAslrAlignment) - (util::AlignUp(out->code_address + out->code_num_pages * PageSize, KernelAslrAlignment) / KernelAslrAlignment);
out->code_address += KSystemControl::GenerateRandomRange(0, choices) * KernelAslrAlignment;
}
R_SUCCEED(); R_SUCCEED();
} }

View file

@ -54,11 +54,11 @@ namespace ams::kern {
return "Unknown "; return "Unknown ";
} }
constexpr const char *GetMemoryPermissionString(const KMemoryInfo &info) { constexpr const char *GetMemoryPermissionString(const KMemoryBlock &block) {
if (info.m_state == KMemoryState_Free) { if (block.GetState() == KMemoryState_Free) {
return " "; return " ";
} else { } else {
switch (info.m_permission) { switch (block.GetPermission()) {
case KMemoryPermission_UserReadExecute: case KMemoryPermission_UserReadExecute:
return "r-x"; return "r-x";
case KMemoryPermission_UserRead: case KMemoryPermission_UserRead:
@ -71,19 +71,19 @@ namespace ams::kern {
} }
} }
void DumpMemoryInfo(const KMemoryInfo &info) { void DumpMemoryBlock(const KMemoryBlock &block) {
const char *state = GetMemoryStateName(info.m_state); const char *state = GetMemoryStateName(block.GetState());
const char *perm = GetMemoryPermissionString(info); const char *perm = GetMemoryPermissionString(block);
const uintptr_t start = info.GetAddress(); const uintptr_t start = GetInteger(block.GetAddress());
const uintptr_t end = info.GetLastAddress(); const uintptr_t end = GetInteger(block.GetLastAddress());
const size_t kb = info.GetSize() / 1_KB; const size_t kb = block.GetSize() / 1_KB;
const char l = (info.m_attribute & KMemoryAttribute_Locked) ? 'L' : '-'; const char l = (block.GetAttribute() & KMemoryAttribute_Locked) ? 'L' : '-';
const char i = (info.m_attribute & KMemoryAttribute_IpcLocked) ? 'I' : '-'; const char i = (block.GetAttribute() & KMemoryAttribute_IpcLocked) ? 'I' : '-';
const char d = (info.m_attribute & KMemoryAttribute_DeviceShared) ? 'D' : '-'; const char d = (block.GetAttribute() & KMemoryAttribute_DeviceShared) ? 'D' : '-';
const char u = (info.m_attribute & KMemoryAttribute_Uncached) ? 'U' : '-'; const char u = (block.GetAttribute() & KMemoryAttribute_Uncached) ? 'U' : '-';
MESOSPHERE_LOG("0x%10lx - 0x%10lx (%9zu KB) %s %s %c%c%c%c [%d, %d]\n", start, end, kb, perm, state, l, i, d, u, info.m_ipc_lock_count, info.m_device_use_count); MESOSPHERE_LOG("0x%10lx - 0x%10lx (%9zu KB) %s %s %c%c%c%c [%d, %d]\n", start, end, kb, perm, state, l, i, d, u, block.GetIpcLockCount(), block.GetDeviceUseCount());
} }
} }
@ -123,15 +123,14 @@ namespace ams::kern {
const KProcessAddress region_end = region_start + region_num_pages * PageSize; const KProcessAddress region_end = region_start + region_num_pages * PageSize;
const KProcessAddress region_last = region_end - 1; const KProcessAddress region_last = region_end - 1;
for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend(); it++) { for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend(); it++) {
const KMemoryInfo info = it->GetMemoryInfo(); if (region_last < it->GetAddress()) {
if (region_last < info.GetAddress()) {
break; break;
} }
if (info.m_state != KMemoryState_Free) { if (it->GetState() != KMemoryState_Free) {
continue; continue;
} }
KProcessAddress area = (info.GetAddress() <= GetInteger(region_start)) ? region_start : info.GetAddress(); KProcessAddress area = (it->GetAddress() <= GetInteger(region_start)) ? region_start : it->GetAddress();
area += guard_pages * PageSize; area += guard_pages * PageSize;
const KProcessAddress offset_area = util::AlignDown(GetInteger(area), alignment) + offset; const KProcessAddress offset_area = util::AlignDown(GetInteger(area), alignment) + offset;
@ -140,7 +139,7 @@ namespace ams::kern {
const KProcessAddress area_end = area + num_pages * PageSize + guard_pages * PageSize; const KProcessAddress area_end = area + num_pages * PageSize + guard_pages * PageSize;
const KProcessAddress area_last = area_end - 1; const KProcessAddress area_last = area_end - 1;
if (info.GetAddress() <= GetInteger(area) && area < area_last && area_last <= region_last && GetInteger(area_last) <= info.GetLastAddress()) { if (GetInteger(it->GetAddress()) <= GetInteger(area) && area < area_last && area_last <= region_last && GetInteger(area_last) <= GetInteger(it->GetLastAddress())) {
return area; return area;
} }
} }
@ -171,7 +170,7 @@ namespace ams::kern {
it = prev; it = prev;
} }
if (address + num_pages * PageSize < it->GetMemoryInfo().GetEndAddress()) { if (address + num_pages * PageSize < it->GetEndAddress()) {
break; break;
} }
} }
@ -189,43 +188,39 @@ namespace ams::kern {
while (remaining_pages > 0) { while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize; const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if (it->HasProperties(state, perm, attr)) { if (it->HasProperties(state, perm, attr)) {
/* If we already have the right properties, just advance. */ /* If we already have the right properties, just advance. */
if (cur_address + remaining_size < cur_info.GetEndAddress()) { if (cur_address + remaining_size < it->GetEndAddress()) {
remaining_pages = 0; remaining_pages = 0;
cur_address += remaining_size; cur_address += remaining_size;
} else { } else {
remaining_pages = (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; remaining_pages = (cur_address + remaining_size - it->GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress(); cur_address = it->GetEndAddress();
} }
} else { } else {
/* If we need to, create a new block before and insert it. */ /* If we need to, create a new block before and insert it. */
if (cur_info.GetAddress() != GetInteger(cur_address)) { if (it->GetAddress() != GetInteger(cur_address)) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address); it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
it++; it++;
cur_info = it->GetMemoryInfo(); cur_address = it->GetAddress();
cur_address = cur_info.GetAddress();
} }
/* If we need to, create a new block after and insert it. */ /* If we need to, create a new block after and insert it. */
if (cur_info.GetSize() > remaining_size) { if (it->GetSize() > remaining_size) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size); it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
} }
/* Update block state. */ /* Update block state. */
it->Update(state, perm, attr, it->GetAddress() == address, set_disable_attr, clear_disable_attr); it->Update(state, perm, attr, it->GetAddress() == address, set_disable_attr, clear_disable_attr);
cur_address += cur_info.GetSize(); cur_address += it->GetSize();
remaining_pages -= cur_info.GetNumPages(); remaining_pages -= it->GetNumPages();
} }
it++; it++;
} }
@ -245,42 +240,38 @@ namespace ams::kern {
while (remaining_pages > 0) { while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize; const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if (it->HasProperties(test_state, test_perm, test_attr) && !it->HasProperties(state, perm, attr)) { if (it->HasProperties(test_state, test_perm, test_attr) && !it->HasProperties(state, perm, attr)) {
/* If we need to, create a new block before and insert it. */ /* If we need to, create a new block before and insert it. */
if (cur_info.GetAddress() != GetInteger(cur_address)) { if (it->GetAddress() != GetInteger(cur_address)) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address); it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
it++; it++;
cur_info = it->GetMemoryInfo(); cur_address = it->GetAddress();
cur_address = cur_info.GetAddress();
} }
/* If we need to, create a new block after and insert it. */ /* If we need to, create a new block after and insert it. */
if (cur_info.GetSize() > remaining_size) { if (it->GetSize() > remaining_size) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size); it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
} }
/* Update block state. */ /* Update block state. */
it->Update(state, perm, attr, it->GetAddress() == address, set_disable_attr, clear_disable_attr); it->Update(state, perm, attr, it->GetAddress() == address, set_disable_attr, clear_disable_attr);
cur_address += cur_info.GetSize(); cur_address += it->GetSize();
remaining_pages -= cur_info.GetNumPages(); remaining_pages -= it->GetNumPages();
} else { } else {
/* If we already have the right properties, just advance. */ /* If we already have the right properties, just advance. */
if (cur_address + remaining_size < cur_info.GetEndAddress()) { if (cur_address + remaining_size < it->GetEndAddress()) {
remaining_pages = 0; remaining_pages = 0;
cur_address += remaining_size; cur_address += remaining_size;
} else { } else {
remaining_pages = (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; remaining_pages = (cur_address + remaining_size - it->GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress(); cur_address = it->GetEndAddress();
} }
} }
it++; it++;
@ -302,34 +293,30 @@ namespace ams::kern {
while (remaining_pages > 0) { while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize; const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
/* If we need to, create a new block before and insert it. */ /* If we need to, create a new block before and insert it. */
if (cur_info.m_address != GetInteger(cur_address)) { if (it->GetAddress() != cur_address) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address); it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
it++; it++;
cur_info = it->GetMemoryInfo(); cur_address = it->GetAddress();
cur_address = cur_info.GetAddress();
} }
if (cur_info.GetSize() > remaining_size) { if (it->GetSize() > remaining_size) {
/* If we need to, create a new block after and insert it. */ /* If we need to, create a new block after and insert it. */
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size); it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
} }
/* Call the locked update function. */ /* Call the locked update function. */
(std::addressof(*it)->*lock_func)(perm, cur_info.GetAddress() == address, cur_info.GetEndAddress() == end_address); (std::addressof(*it)->*lock_func)(perm, it->GetAddress() == address, it->GetEndAddress() == end_address);
cur_address += cur_info.GetSize(); cur_address += it->GetSize();
remaining_pages -= cur_info.GetNumPages(); remaining_pages -= it->GetNumPages();
it++; it++;
} }
@ -347,43 +334,39 @@ namespace ams::kern {
while (remaining_pages > 0) { while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize; const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if ((it->GetAttribute() & mask) != attr) { if ((it->GetAttribute() & mask) != attr) {
/* If we need to, create a new block before and insert it. */ /* If we need to, create a new block before and insert it. */
if (cur_info.GetAddress() != GetInteger(cur_address)) { if (it->GetAddress() != GetInteger(cur_address)) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address); it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
it++; it++;
cur_info = it->GetMemoryInfo(); cur_address = it->GetAddress();
cur_address = cur_info.GetAddress();
} }
/* If we need to, create a new block after and insert it. */ /* If we need to, create a new block after and insert it. */
if (cur_info.GetSize() > remaining_size) { if (it->GetSize() > remaining_size) {
KMemoryBlock *new_block = allocator->Allocate(); KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size); it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block); it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
} }
/* Update block state. */ /* Update block state. */
it->UpdateAttribute(mask, attr); it->UpdateAttribute(mask, attr);
cur_address += cur_info.GetSize(); cur_address += it->GetSize();
remaining_pages -= cur_info.GetNumPages(); remaining_pages -= it->GetNumPages();
} else { } else {
/* If we already have the right attributes, just advance. */ /* If we already have the right attributes, just advance. */
if (cur_address + remaining_size < cur_info.GetEndAddress()) { if (cur_address + remaining_size < it->GetEndAddress()) {
remaining_pages = 0; remaining_pages = 0;
cur_address += remaining_size; cur_address += remaining_size;
} else { } else {
remaining_pages = (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; remaining_pages = (cur_address + remaining_size - it->GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress(); cur_address = it->GetEndAddress();
} }
} }
it++; it++;
@ -401,8 +384,6 @@ namespace ams::kern {
auto it = m_memory_block_tree.cbegin(); auto it = m_memory_block_tree.cbegin();
auto prev = it++; auto prev = it++;
while (it != m_memory_block_tree.cend()) { while (it != m_memory_block_tree.cend()) {
const KMemoryInfo prev_info = prev->GetMemoryInfo();
const KMemoryInfo cur_info = it->GetMemoryInfo();
/* Sequential blocks which can be merged should be merged. */ /* Sequential blocks which can be merged should be merged. */
if (prev->CanMergeWith(*it)) { if (prev->CanMergeWith(*it)) {
@ -410,17 +391,17 @@ namespace ams::kern {
} }
/* Sequential blocks should be sequential. */ /* Sequential blocks should be sequential. */
if (prev_info.GetEndAddress() != cur_info.GetAddress()) { if (prev->GetEndAddress() != it->GetAddress()) {
return false; return false;
} }
/* If the block is ipc locked, it must have a count. */ /* If the block is ipc locked, it must have a count. */
if ((cur_info.m_attribute & KMemoryAttribute_IpcLocked) != 0 && cur_info.m_ipc_lock_count == 0) { if ((it->GetAttribute() & KMemoryAttribute_IpcLocked) != 0 && it->GetIpcLockCount() == 0) {
return false; return false;
} }
/* If the block is device shared, it must have a count. */ /* If the block is device shared, it must have a count. */
if ((cur_info.m_attribute & KMemoryAttribute_DeviceShared) != 0 && cur_info.m_device_use_count == 0) { if ((it->GetAttribute() & KMemoryAttribute_DeviceShared) != 0 && it->GetDeviceUseCount() == 0) {
return false; return false;
} }
@ -430,14 +411,13 @@ namespace ams::kern {
/* Our loop will miss checking the last block, potentially, so check it. */ /* Our loop will miss checking the last block, potentially, so check it. */
if (prev != m_memory_block_tree.cend()) { if (prev != m_memory_block_tree.cend()) {
const KMemoryInfo prev_info = prev->GetMemoryInfo();
/* If the block is ipc locked, it must have a count. */ /* If the block is ipc locked, it must have a count. */
if ((prev_info.m_attribute & KMemoryAttribute_IpcLocked) != 0 && prev_info.m_ipc_lock_count == 0) { if ((prev->GetAttribute() & KMemoryAttribute_IpcLocked) != 0 && prev->GetIpcLockCount() == 0) {
return false; return false;
} }
/* If the block is device shared, it must have a count. */ /* If the block is device shared, it must have a count. */
if ((prev_info.m_attribute & KMemoryAttribute_DeviceShared) != 0 && prev_info.m_device_use_count == 0) { if ((prev->GetAttribute() & KMemoryAttribute_DeviceShared) != 0 && prev->GetDeviceUseCount() == 0) {
return false; return false;
} }
} }
@ -450,7 +430,7 @@ namespace ams::kern {
void KMemoryBlockManager::DumpBlocks() const { void KMemoryBlockManager::DumpBlocks() const {
/* Dump each block. */ /* Dump each block. */
for (const auto &block : m_memory_block_tree) { for (const auto &block : m_memory_block_tree) {
DumpMemoryInfo(block.GetMemoryInfo()); DumpMemoryBlock(block);
} }
} }
} }

View file

@ -141,10 +141,10 @@ namespace ams::kern {
/* Define helpers. */ /* Define helpers. */
auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA { auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA {
return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); return KAddressSpaceInfo::GetAddressSpaceStart(flags, type);
}; };
auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA { auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA {
return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type); return KAddressSpaceInfo::GetAddressSpaceSize(flags, type);
}; };
/* Default to zero alias region extra size. */ /* Default to zero alias region extra size. */
@ -664,11 +664,11 @@ namespace ams::kern {
} }
} }
Result KPageTableBase::CheckMemoryState(const KMemoryInfo &info, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) const { Result KPageTableBase::CheckMemoryState(KMemoryBlockManager::const_iterator it, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) const {
/* Validate the states match expectation. */ /* Validate the states match expectation. */
R_UNLESS((info.m_state & state_mask) == state, svc::ResultInvalidCurrentMemory()); R_UNLESS((it->GetState() & state_mask) == state, svc::ResultInvalidCurrentMemory());
R_UNLESS((info.m_permission & perm_mask) == perm, svc::ResultInvalidCurrentMemory()); R_UNLESS((it->GetPermission() & perm_mask) == perm, svc::ResultInvalidCurrentMemory());
R_UNLESS((info.m_attribute & attr_mask) == attr, svc::ResultInvalidCurrentMemory()); R_UNLESS((it->GetAttribute() & attr_mask) == attr, svc::ResultInvalidCurrentMemory());
R_SUCCEED(); R_SUCCEED();
} }
@ -679,28 +679,26 @@ namespace ams::kern {
/* Get information about the first block. */ /* Get information about the first block. */
const KProcessAddress last_addr = addr + size - 1; const KProcessAddress last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
KMemoryInfo info = it->GetMemoryInfo();
/* If the start address isn't aligned, we need a block. */ /* If the start address isn't aligned, we need a block. */
const size_t blocks_for_start_align = (util::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0; const size_t blocks_for_start_align = (util::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) ? 1 : 0;
while (true) { while (true) {
/* Validate against the provided masks. */ /* Validate against the provided masks. */
R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); R_TRY(this->CheckMemoryState(it, state_mask, state, perm_mask, perm, attr_mask, attr));
/* Break once we're done. */ /* Break once we're done. */
if (last_addr <= info.GetLastAddress()) { if (last_addr <= it->GetLastAddress()) {
break; break;
} }
/* Advance our iterator. */ /* Advance our iterator. */
it++; it++;
MESOSPHERE_ASSERT(it != m_memory_block_manager.cend()); MESOSPHERE_ASSERT(it != m_memory_block_manager.cend());
info = it->GetMemoryInfo();
} }
/* If the end address isn't aligned, we need a block. */ /* If the end address isn't aligned, we need a block. */
const size_t blocks_for_end_align = (util::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0; const size_t blocks_for_end_align = (util::AlignUp(GetInteger(addr) + size, PageSize) != it->GetEndAddress()) ? 1 : 0;
if (out_blocks_needed != nullptr) { if (out_blocks_needed != nullptr) {
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align; *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
@ -712,31 +710,27 @@ namespace ams::kern {
Result KPageTableBase::CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, size_t *out_blocks_needed, KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr) const { Result KPageTableBase::CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, size_t *out_blocks_needed, KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr) const {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
/* Get information about the first block. */
KMemoryInfo info = it->GetMemoryInfo();
/* Validate all blocks in the range have correct state. */ /* Validate all blocks in the range have correct state. */
const KMemoryState first_state = info.m_state; const KMemoryState first_state = it->GetState();
const KMemoryPermission first_perm = info.m_permission; const KMemoryPermission first_perm = it->GetPermission();
const KMemoryAttribute first_attr = info.m_attribute; const KMemoryAttribute first_attr = it->GetAttribute();
while (true) { while (true) {
/* Validate the current block. */ /* Validate the current block. */
R_UNLESS(info.m_state == first_state, svc::ResultInvalidCurrentMemory()); R_UNLESS(it->GetState() == first_state, svc::ResultInvalidCurrentMemory());
R_UNLESS(info.m_permission == first_perm, svc::ResultInvalidCurrentMemory()); R_UNLESS(it->GetPermission() == first_perm, svc::ResultInvalidCurrentMemory());
R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr), svc::ResultInvalidCurrentMemory()); R_UNLESS((it->GetAttribute() | ignore_attr) == (first_attr | ignore_attr), svc::ResultInvalidCurrentMemory());
/* Validate against the provided masks. */ /* Validate against the provided masks. */
R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); R_TRY(this->CheckMemoryState(it, state_mask, state, perm_mask, perm, attr_mask, attr));
/* Break once we're done. */ /* Break once we're done. */
if (last_addr <= info.GetLastAddress()) { if (last_addr <= it->GetLastAddress()) {
break; break;
} }
/* Advance our iterator. */ /* Advance our iterator. */
it++; it++;
MESOSPHERE_ASSERT(it != m_memory_block_manager.cend()); MESOSPHERE_ASSERT(it != m_memory_block_manager.cend());
info = it->GetMemoryInfo();
} }
/* Write output state. */ /* Write output state. */
@ -752,7 +746,7 @@ namespace ams::kern {
/* If the end address isn't aligned, we need a block. */ /* If the end address isn't aligned, we need a block. */
if (out_blocks_needed != nullptr) { if (out_blocks_needed != nullptr) {
const size_t blocks_for_end_align = (util::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress()) ? 1 : 0; const size_t blocks_for_end_align = (util::AlignDown(GetInteger(last_addr), PageSize) + PageSize != it->GetEndAddress()) ? 1 : 0;
*out_blocks_needed = blocks_for_end_align; *out_blocks_needed = blocks_for_end_align;
} }
@ -1176,17 +1170,14 @@ namespace ams::kern {
{ {
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address); KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address);
while (true) { while (true) {
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* Check if the memory has code flag. */ /* Check if the memory has code flag. */
if ((info.GetState() & KMemoryState_FlagCode) != 0) { if ((it->GetState() & KMemoryState_FlagCode) != 0) {
any_code_pages = true; any_code_pages = true;
break; break;
} }
/* Check if we're done. */ /* Check if we're done. */
if (dst_address + size - 1 <= info.GetLastAddress()) { if (dst_address + size - 1 <= it->GetLastAddress()) {
break; break;
} }
@ -1355,14 +1346,13 @@ namespace ams::kern {
const size_t random_offset = KSystemControl::GenerateRandomRange(0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * alignment; const size_t random_offset = KSystemControl::GenerateRandomRange(0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * alignment;
const KProcessAddress candidate = util::AlignDown(GetInteger(region_start + random_offset), alignment) + offset; const KProcessAddress candidate = util::AlignDown(GetInteger(region_start + random_offset), alignment) + offset;
KMemoryInfo info; KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(candidate);
ams::svc::PageInfo page_info; MESOSPHERE_ABORT_UNLESS(it != m_memory_block_manager.end());
MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), candidate));
if (info.m_state != KMemoryState_Free) { continue; } if (it->GetState() != KMemoryState_Free) { continue; }
if (!(region_start <= candidate)) { continue; } if (!(region_start <= candidate)) { continue; }
if (!(info.GetAddress() + guard_pages * PageSize <= GetInteger(candidate))) { continue; } if (!(it->GetAddress() + guard_pages * PageSize <= GetInteger(candidate))) { continue; }
if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <= info.GetLastAddress())) { continue; } if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <= it->GetLastAddress())) { continue; }
if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <= region_start + region_num_pages * PageSize - 1)) { continue; } if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <= region_start + region_num_pages * PageSize - 1)) { continue; }
address = candidate; address = candidate;
@ -1393,10 +1383,8 @@ namespace ams::kern {
/* Iterate, counting blocks with the desired state. */ /* Iterate, counting blocks with the desired state. */
size_t total_size = 0; size_t total_size = 0;
for (KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(m_address_space_start); it != m_memory_block_manager.end(); ++it) { for (KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(m_address_space_start); it != m_memory_block_manager.end(); ++it) {
/* Get the memory info. */ if (it->GetState() == state) {
const KMemoryInfo info = it->GetMemoryInfo(); total_size += it->GetSize();
if (info.GetState() == state) {
total_size += info.GetSize();
} }
} }
@ -1488,17 +1476,14 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* Determine the range to map. */ /* Determine the range to map. */
KProcessAddress map_address = std::max(info.GetAddress(), GetInteger(start_address)); KProcessAddress map_address = std::max(GetInteger(it->GetAddress()), GetInteger(start_address));
const KProcessAddress map_end_address = std::min(info.GetEndAddress(), GetInteger(end_address)); const KProcessAddress map_end_address = std::min(GetInteger(it->GetEndAddress()), GetInteger(end_address));
MESOSPHERE_ABORT_UNLESS(map_end_address != map_address); MESOSPHERE_ABORT_UNLESS(map_end_address != map_address);
/* Determine if we should disable head merge. */ /* Determine if we should disable head merge. */
const bool disable_head_merge = info.GetAddress() >= GetInteger(start_address) && (info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute_Normal) != 0; const bool disable_head_merge = it->GetAddress() >= GetInteger(start_address) && (it->GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute_Normal) != 0;
const KPageProperties map_properties = { info.GetPermission(), false, false, disable_head_merge ? DisableMergeAttribute_DisableHead : DisableMergeAttribute_None }; const KPageProperties map_properties = { it->GetPermission(), false, false, disable_head_merge ? DisableMergeAttribute_DisableHead : DisableMergeAttribute_None };
/* While we have pages to map, map them. */ /* While we have pages to map, map them. */
size_t map_pages = (map_end_address - map_address) / PageSize; size_t map_pages = (map_end_address - map_address) / PageSize;
@ -1527,7 +1512,7 @@ namespace ams::kern {
} }
/* Check if we're done. */ /* Check if we're done. */
if (last_address <= info.GetLastAddress()) { if (last_address <= it->GetLastAddress()) {
break; break;
} }
@ -2032,19 +2017,18 @@ namespace ams::kern {
address = util::AlignDown(GetInteger(address), PageSize); address = util::AlignDown(GetInteger(address), PageSize);
/* Verify that we can query the address. */ /* Verify that we can query the address. */
KMemoryInfo info; KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
ams::svc::PageInfo page_info; R_UNLESS(it != m_memory_block_manager.end(), svc::ResultInvalidCurrentMemory());
R_TRY(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), address));
/* Check the memory state. */ /* Check the memory state. */
R_TRY(this->CheckMemoryState(info, KMemoryState_FlagCanQueryPhysical, KMemoryState_FlagCanQueryPhysical, KMemoryPermission_UserReadExecute, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)); R_TRY(this->CheckMemoryState(it, KMemoryState_FlagCanQueryPhysical, KMemoryState_FlagCanQueryPhysical, KMemoryPermission_UserReadExecute, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None));
/* Prepare to traverse. */ /* Prepare to traverse. */
KPhysicalAddress phys_addr; KPhysicalAddress phys_addr;
size_t phys_size; size_t phys_size;
KProcessAddress virt_addr = info.GetAddress(); KProcessAddress virt_addr = it->GetAddress();
KProcessAddress end_addr = info.GetEndAddress(); KProcessAddress end_addr = it->GetEndAddress();
/* Perform traversal. */ /* Perform traversal. */
{ {
@ -2711,18 +2695,37 @@ namespace ams::kern {
R_RETURN(cpu::InvalidateDataCache(GetVoidPointer(address), size)); R_RETURN(cpu::InvalidateDataCache(GetVoidPointer(address), size));
} }
Result KPageTableBase::ReadDebugMemory(void *buffer, KProcessAddress address, size_t size) { bool KPageTableBase::CanReadWriteDebugMemory(KProcessAddress address, size_t size, bool force_debug_prod) {
/* Check pre-conditions. */
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
/* If the memory is debuggable and user-readable, we can perform the access. */
if (R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_FlagCanDebug, KMemoryState_FlagCanDebug, KMemoryPermission_NotMapped | KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None))) {
return true;
}
/* If we're in debug mode, and the process isn't force debug prod, check if the memory is debuggable and kernel-readable and user-executable. */
if (KTargetSystem::IsDebugMode() && !force_debug_prod) {
if (R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_FlagCanDebug, KMemoryState_FlagCanDebug, KMemoryPermission_KernelRead | KMemoryPermission_UserExecute, KMemoryPermission_KernelRead | KMemoryPermission_UserExecute, KMemoryAttribute_None, KMemoryAttribute_None))) {
return true;
}
}
/* If neither of the above checks passed, we can't access the memory. */
return false;
}
Result KPageTableBase::ReadDebugMemory(void *buffer, KProcessAddress address, size_t size, bool force_debug_prod) {
/* Lightly validate the region is in range. */ /* Lightly validate the region is in range. */
R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory());
/* Lock the table. */ /* Lock the table. */
KScopedLightLock lk(m_general_lock); KScopedLightLock lk(m_general_lock);
/* Require that the memory either be user readable or debuggable. */ /* Require that the memory either be user-readable-and-mapped or debug-accessible. */
const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)); const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_None, KMemoryState_None, KMemoryPermission_NotMapped | KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None));
if (!can_read) { if (!can_read) {
const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_FlagCanDebug, KMemoryState_FlagCanDebug, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); R_UNLESS(this->CanReadWriteDebugMemory(address, size, force_debug_prod), svc::ResultInvalidCurrentMemory());
R_UNLESS(can_debug, svc::ResultInvalidCurrentMemory());
} }
/* Get the impl. */ /* Get the impl. */
@ -2804,11 +2807,10 @@ namespace ams::kern {
/* Lock the table. */ /* Lock the table. */
KScopedLightLock lk(m_general_lock); KScopedLightLock lk(m_general_lock);
/* Require that the memory either be user writable or debuggable. */ /* Require that the memory either be user-writable-and-mapped or debug-accessible. */
const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None)); const bool can_write = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_None, KMemoryState_None, KMemoryPermission_NotMapped | KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None));
if (!can_read) { if (!can_write) {
const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, KMemoryState_FlagCanDebug, KMemoryState_FlagCanDebug, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); R_UNLESS(this->CanReadWriteDebugMemory(address, size, false), svc::ResultInvalidCurrentMemory());
R_UNLESS(can_debug, svc::ResultInvalidCurrentMemory());
} }
/* Get the impl. */ /* Get the impl. */
@ -3854,26 +3856,24 @@ namespace ams::kern {
/* Iterate, mapping as needed. */ /* Iterate, mapping as needed. */
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start); KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start);
while (true) { while (true) {
const KMemoryInfo info = it->GetMemoryInfo();
/* Validate the current block. */ /* Validate the current block. */
R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm, test_attr_mask, KMemoryAttribute_None)); R_TRY(this->CheckMemoryState(it, test_state, test_state, test_perm, test_perm, test_attr_mask, KMemoryAttribute_None));
if (mapping_src_start < mapping_src_end && GetInteger(mapping_src_start) < info.GetEndAddress() && info.GetAddress() < GetInteger(mapping_src_end)) { if (mapping_src_start < mapping_src_end && GetInteger(mapping_src_start) < GetInteger(it->GetEndAddress()) && GetInteger(it->GetAddress()) < GetInteger(mapping_src_end)) {
const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start) ? info.GetAddress() : GetInteger(mapping_src_start); const auto cur_start = it->GetAddress() >= GetInteger(mapping_src_start) ? GetInteger(it->GetAddress()) : GetInteger(mapping_src_start);
const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress() : GetInteger(mapping_src_end); const auto cur_end = mapping_src_last >= GetInteger(it->GetLastAddress()) ? GetInteger(it->GetEndAddress()) : GetInteger(mapping_src_end);
const size_t cur_size = cur_end - cur_start; const size_t cur_size = cur_end - cur_start;
if (info.GetAddress() < GetInteger(mapping_src_start)) { if (GetInteger(it->GetAddress()) < GetInteger(mapping_src_start)) {
++blocks_needed; ++blocks_needed;
} }
if (mapping_src_last < info.GetLastAddress()) { if (mapping_src_last < GetInteger(it->GetLastAddress())) {
++blocks_needed; ++blocks_needed;
} }
/* Set the permissions on the block, if we need to. */ /* Set the permissions on the block, if we need to. */
if ((info.GetPermission() & KMemoryPermission_IpcLockChangeMask) != src_perm) { if ((it->GetPermission() & KMemoryPermission_IpcLockChangeMask) != src_perm) {
const DisableMergeAttribute head_body_attr = (GetInteger(mapping_src_start) >= info.GetAddress()) ? DisableMergeAttribute_DisableHeadAndBody : DisableMergeAttribute_None; const DisableMergeAttribute head_body_attr = (GetInteger(mapping_src_start) >= GetInteger(it->GetAddress())) ? DisableMergeAttribute_DisableHeadAndBody : DisableMergeAttribute_None;
const DisableMergeAttribute tail_attr = (cur_end == GetInteger(mapping_src_end)) ? DisableMergeAttribute_DisableTail : DisableMergeAttribute_None; const DisableMergeAttribute tail_attr = (cur_end == GetInteger(mapping_src_end)) ? DisableMergeAttribute_DisableTail : DisableMergeAttribute_None;
const KPageProperties properties = { src_perm, false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr) }; const KPageProperties properties = { src_perm, false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr) };
R_TRY(this->Operate(page_list, cur_start, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, false)); R_TRY(this->Operate(page_list, cur_start, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, false));
@ -3884,7 +3884,7 @@ namespace ams::kern {
} }
/* If the block is at the end, we're done. */ /* If the block is at the end, we're done. */
if (aligned_src_last <= info.GetLastAddress()) { if (aligned_src_last <= GetInteger(it->GetLastAddress())) {
break; break;
} }
@ -4248,56 +4248,50 @@ namespace ams::kern {
const auto mapped_last = mapped_end - 1; const auto mapped_last = mapped_end - 1;
/* Get current and next iterators. */ /* Get current and next iterators. */
KMemoryBlockManager::const_iterator start_it = m_memory_block_manager.FindIterator(mapping_start); KMemoryBlockManager::const_iterator cur_it = m_memory_block_manager.FindIterator(mapping_start);
KMemoryBlockManager::const_iterator next_it = start_it; KMemoryBlockManager::const_iterator next_it = cur_it;
++next_it; ++next_it;
/* Get the current block info. */
KMemoryInfo cur_info = start_it->GetMemoryInfo();
/* Create tracking variables. */ /* Create tracking variables. */
KProcessAddress cur_address = cur_info.GetAddress(); KProcessAddress cur_address = cur_it->GetAddress();
size_t cur_size = cur_info.GetSize(); size_t cur_size = cur_it->GetSize();
bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); bool cur_perm_eq = cur_it->GetPermission() == cur_it->GetOriginalPermission();
bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; bool cur_needs_set_perm = !cur_perm_eq && cur_it->GetIpcLockCount() == 1;
bool first = cur_info.GetIpcDisableMergeCount() == 1 && (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute_Locked) == 0; bool first = cur_it->GetIpcDisableMergeCount() == 1 && (cur_it->GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute_Locked) == 0;
while ((GetInteger(cur_address) + cur_size - 1) < mapped_last) { while ((GetInteger(cur_address) + cur_size - 1) < mapped_last) {
/* Check that we have a next block. */ /* Check that we have a next block. */
MESOSPHERE_ABORT_UNLESS(next_it != m_memory_block_manager.end()); MESOSPHERE_ABORT_UNLESS(next_it != m_memory_block_manager.end());
/* Get the next info. */
const KMemoryInfo next_info = next_it->GetMemoryInfo();
/* Check if we can consolidate the next block's permission set with the current one. */ /* Check if we can consolidate the next block's permission set with the current one. */
const bool next_perm_eq = next_info.GetPermission() == next_info.GetOriginalPermission(); const bool next_perm_eq = next_it->GetPermission() == next_it->GetOriginalPermission();
const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; const bool next_needs_set_perm = !next_perm_eq && next_it->GetIpcLockCount() == 1;
if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && cur_it->GetOriginalPermission() == next_it->GetOriginalPermission()) {
/* We can consolidate the reprotection for the current and next block into a single call. */ /* We can consolidate the reprotection for the current and next block into a single call. */
cur_size += next_info.GetSize(); cur_size += next_it->GetSize();
} else { } else {
/* We have to operate on the current block. */ /* We have to operate on the current block. */
if ((cur_needs_set_perm || first) && !cur_perm_eq) { if ((cur_needs_set_perm || first) && !cur_perm_eq) {
const KPageProperties properties = { cur_info.GetPermission(), false, false, first ? DisableMergeAttribute_EnableAndMergeHeadBodyTail : DisableMergeAttribute_None }; const KPageProperties properties = { cur_it->GetPermission(), false, false, first ? DisableMergeAttribute_EnableAndMergeHeadBodyTail : DisableMergeAttribute_None };
MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, true)); MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, true));
} }
/* Advance. */ /* Advance. */
cur_address = next_info.GetAddress(); cur_address = next_it->GetAddress();
cur_size = next_info.GetSize(); cur_size = next_it->GetSize();
first = false; first = false;
} }
/* Advance. */ /* Advance. */
cur_info = next_info;
cur_perm_eq = next_perm_eq; cur_perm_eq = next_perm_eq;
cur_needs_set_perm = next_needs_set_perm; cur_needs_set_perm = next_needs_set_perm;
++next_it;
cur_it = next_it++;
} }
/* Process the last block. */ /* Process the last block. */
if ((first || cur_needs_set_perm) && !cur_perm_eq) { if ((first || cur_needs_set_perm) && !cur_perm_eq) {
const KPageProperties properties = { cur_info.GetPermission(), false, false, first ? DisableMergeAttribute_EnableAndMergeHeadBodyTail : DisableMergeAttribute_None }; const KPageProperties properties = { cur_it->GetPermission(), false, false, first ? DisableMergeAttribute_EnableAndMergeHeadBodyTail : DisableMergeAttribute_None };
MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, true)); MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, true));
} }
} }
@ -4306,41 +4300,37 @@ namespace ams::kern {
/* Iterate, reprotecting as needed. */ /* Iterate, reprotecting as needed. */
{ {
/* Get current and next iterators. */ /* Get current and next iterators. */
KMemoryBlockManager::const_iterator start_it = m_memory_block_manager.FindIterator(mapping_start); KMemoryBlockManager::const_iterator cur_it = m_memory_block_manager.FindIterator(mapping_start);
KMemoryBlockManager::const_iterator next_it = start_it; KMemoryBlockManager::const_iterator next_it = cur_it;
++next_it; ++next_it;
/* Validate the current block. */ /* Validate the current block. */
KMemoryInfo cur_info = start_it->GetMemoryInfo(); MESOSPHERE_R_ABORT_UNLESS(this->CheckMemoryState(cur_it, test_state, test_state, KMemoryPermission_None, KMemoryPermission_None, test_attr_mask | KMemoryAttribute_IpcLocked, KMemoryAttribute_IpcLocked));
MESOSPHERE_R_ABORT_UNLESS(this->CheckMemoryState(cur_info, test_state, test_state, KMemoryPermission_None, KMemoryPermission_None, test_attr_mask | KMemoryAttribute_IpcLocked, KMemoryAttribute_IpcLocked));
/* Create tracking variables. */ /* Create tracking variables. */
KProcessAddress cur_address = cur_info.GetAddress(); KProcessAddress cur_address = cur_it->GetAddress();
size_t cur_size = cur_info.GetSize(); size_t cur_size = cur_it->GetSize();
bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); bool cur_perm_eq = cur_it->GetPermission() == cur_it->GetOriginalPermission();
bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; bool cur_needs_set_perm = !cur_perm_eq && cur_it->GetIpcLockCount() == 1;
bool first = cur_info.GetIpcDisableMergeCount() == 1 && (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute_Locked) == 0; bool first = cur_it->GetIpcDisableMergeCount() == 1 && (cur_it->GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute_Locked) == 0;
while ((cur_address + cur_size - 1) < mapping_last) { while ((cur_address + cur_size - 1) < mapping_last) {
/* Check that we have a next block. */ /* Check that we have a next block. */
MESOSPHERE_ABORT_UNLESS(next_it != m_memory_block_manager.end()); MESOSPHERE_ABORT_UNLESS(next_it != m_memory_block_manager.end());
/* Get the next info. */
const KMemoryInfo next_info = next_it->GetMemoryInfo();
/* Validate the next block. */ /* Validate the next block. */
MESOSPHERE_R_ABORT_UNLESS(this->CheckMemoryState(next_info, test_state, test_state, KMemoryPermission_None, KMemoryPermission_None, test_attr_mask | KMemoryAttribute_IpcLocked, KMemoryAttribute_IpcLocked)); MESOSPHERE_R_ABORT_UNLESS(this->CheckMemoryState(next_it, test_state, test_state, KMemoryPermission_None, KMemoryPermission_None, test_attr_mask | KMemoryAttribute_IpcLocked, KMemoryAttribute_IpcLocked));
/* Check if we can consolidate the next block's permission set with the current one. */ /* Check if we can consolidate the next block's permission set with the current one. */
const bool next_perm_eq = next_info.GetPermission() == next_info.GetOriginalPermission(); const bool next_perm_eq = next_it->GetPermission() == next_it->GetOriginalPermission();
const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; const bool next_needs_set_perm = !next_perm_eq && next_it->GetIpcLockCount() == 1;
if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && cur_it->GetOriginalPermission() == next_it->GetOriginalPermission()) {
/* We can consolidate the reprotection for the current and next block into a single call. */ /* We can consolidate the reprotection for the current and next block into a single call. */
cur_size += next_info.GetSize(); cur_size += next_it->GetSize();
} else { } else {
/* We have to operate on the current block. */ /* We have to operate on the current block. */
if ((cur_needs_set_perm || first) && !cur_perm_eq) { if ((cur_needs_set_perm || first) && !cur_perm_eq) {
const KPageProperties properties = { cur_needs_set_perm ? cur_info.GetOriginalPermission() : cur_info.GetPermission(), false, false, first ? DisableMergeAttribute_EnableHeadAndBody : DisableMergeAttribute_None }; const KPageProperties properties = { cur_needs_set_perm ? cur_it->GetOriginalPermission() : cur_it->GetPermission(), false, false, first ? DisableMergeAttribute_EnableHeadAndBody : DisableMergeAttribute_None };
R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, false)); R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, false));
} }
@ -4348,24 +4338,24 @@ namespace ams::kern {
mapped_size += cur_size; mapped_size += cur_size;
/* Advance. */ /* Advance. */
cur_address = next_info.GetAddress(); cur_address = next_it->GetAddress();
cur_size = next_info.GetSize(); cur_size = next_it->GetSize();
first = false; first = false;
} }
/* Advance. */ /* Advance. */
cur_info = next_info;
cur_perm_eq = next_perm_eq; cur_perm_eq = next_perm_eq;
cur_needs_set_perm = next_needs_set_perm; cur_needs_set_perm = next_needs_set_perm;
++next_it;
cur_it = next_it++;
} }
/* Process the last block. */ /* Process the last block. */
const auto lock_count = cur_info.GetIpcLockCount() + (next_it != m_memory_block_manager.end() ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) : 0); const auto lock_count = cur_it->GetIpcLockCount() + (next_it != m_memory_block_manager.end() ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) : 0);
if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) { if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) {
const DisableMergeAttribute head_body_attr = first ? DisableMergeAttribute_EnableHeadAndBody : DisableMergeAttribute_None; const DisableMergeAttribute head_body_attr = first ? DisableMergeAttribute_EnableHeadAndBody : DisableMergeAttribute_None;
const DisableMergeAttribute tail_attr = lock_count == 1 ? DisableMergeAttribute_EnableTail : DisableMergeAttribute_None; const DisableMergeAttribute tail_attr = lock_count == 1 ? DisableMergeAttribute_EnableTail : DisableMergeAttribute_None;
const KPageProperties properties = { cur_needs_set_perm ? cur_info.GetOriginalPermission() : cur_info.GetPermission(), false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr) }; const KPageProperties properties = { cur_needs_set_perm ? cur_it->GetOriginalPermission() : cur_it->GetPermission(), false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr) };
R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, false)); R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, false));
} }
} }
@ -4398,38 +4388,36 @@ namespace ams::kern {
/* Iterate over blocks, fixing permissions. */ /* Iterate over blocks, fixing permissions. */
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address); KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
while (true) { while (true) {
const KMemoryInfo info = it->GetMemoryInfo(); const auto cur_start = it->GetAddress() >= GetInteger(src_map_start) ? it->GetAddress() : GetInteger(src_map_start);
const auto cur_end = src_map_last <= it->GetLastAddress() ? src_map_end : it->GetEndAddress();
const auto cur_start = info.GetAddress() >= GetInteger(src_map_start) ? info.GetAddress() : GetInteger(src_map_start);
const auto cur_end = src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress();
/* If we can, fix the protections on the block. */ /* If we can, fix the protections on the block. */
if ((info.GetIpcLockCount() == 0 && (info.GetPermission() & KMemoryPermission_IpcLockChangeMask) != prot_perm) || if ((it->GetIpcLockCount() == 0 && (it->GetPermission() & KMemoryPermission_IpcLockChangeMask) != prot_perm) ||
(info.GetIpcLockCount() != 0 && (info.GetOriginalPermission() & KMemoryPermission_IpcLockChangeMask) != prot_perm)) (it->GetIpcLockCount() != 0 && (it->GetOriginalPermission() & KMemoryPermission_IpcLockChangeMask) != prot_perm))
{ {
/* Check if we actually need to fix the protections on the block. */ /* Check if we actually need to fix the protections on the block. */
if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) || (info.GetPermission() & KMemoryPermission_IpcLockChangeMask) != prot_perm) { if (cur_end == src_map_end || it->GetAddress() <= GetInteger(src_map_start) || (it->GetPermission() & KMemoryPermission_IpcLockChangeMask) != prot_perm) {
const bool start_nc = (info.GetAddress() == GetInteger(src_map_start)) ? ((info.GetDisableMergeAttribute() & (KMemoryBlockDisableMergeAttribute_Locked | KMemoryBlockDisableMergeAttribute_IpcLeft)) == 0) : info.GetAddress() <= GetInteger(src_map_start); const bool start_nc = (it->GetAddress() == GetInteger(src_map_start)) ? ((it->GetDisableMergeAttribute() & (KMemoryBlockDisableMergeAttribute_Locked | KMemoryBlockDisableMergeAttribute_IpcLeft)) == 0) : it->GetAddress() <= GetInteger(src_map_start);
const DisableMergeAttribute head_body_attr = start_nc ? DisableMergeAttribute_EnableHeadAndBody : DisableMergeAttribute_None; const DisableMergeAttribute head_body_attr = start_nc ? DisableMergeAttribute_EnableHeadAndBody : DisableMergeAttribute_None;
DisableMergeAttribute tail_attr; DisableMergeAttribute tail_attr;
if (cur_end == src_map_end && info.GetEndAddress() == src_map_end) { if (cur_end == src_map_end && it->GetEndAddress() == src_map_end) {
auto next_it = it; auto next_it = it;
++next_it; ++next_it;
const auto lock_count = info.GetIpcLockCount() + (next_it != m_memory_block_manager.end() ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) : 0); const auto lock_count = it->GetIpcLockCount() + (next_it != m_memory_block_manager.end() ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) : 0);
tail_attr = lock_count == 0 ? DisableMergeAttribute_EnableTail : DisableMergeAttribute_None; tail_attr = lock_count == 0 ? DisableMergeAttribute_EnableTail : DisableMergeAttribute_None;
} else { } else {
tail_attr = DisableMergeAttribute_None; tail_attr = DisableMergeAttribute_None;
} }
const KPageProperties properties = { info.GetPermission(), false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr) }; const KPageProperties properties = { it->GetPermission(), false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr) };
MESOSPHERE_R_ABORT_UNLESS(this->Operate(page_list, cur_start, (cur_end - cur_start) / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, true)); MESOSPHERE_R_ABORT_UNLESS(this->Operate(page_list, cur_start, (cur_end - cur_start) / PageSize, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissions, true));
} }
} }
/* If we're past the end of the region, we're done. */ /* If we're past the end of the region, we're done. */
if (src_map_last <= info.GetLastAddress()) { if (src_map_last <= it->GetLastAddress()) {
break; break;
} }
@ -4468,24 +4456,21 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* Check if we're done. */ /* Check if we're done. */
if (last_address <= info.GetLastAddress()) { if (last_address <= it->GetLastAddress()) {
if (info.GetState() != KMemoryState_Free) { if (it->GetState() != KMemoryState_Free) {
mapped_size += (last_address + 1 - cur_address); mapped_size += (last_address + 1 - cur_address);
} }
break; break;
} }
/* Track the memory if it's mapped. */ /* Track the memory if it's mapped. */
if (info.GetState() != KMemoryState_Free) { if (it->GetState() != KMemoryState_Free) {
mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address; mapped_size += it->GetEndAddress() - cur_address;
} }
/* Advance. */ /* Advance. */
cur_address = info.GetEndAddress(); cur_address = it->GetEndAddress();
++it; ++it;
} }
@ -4527,21 +4512,18 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */ const bool is_free = it->GetState() == KMemoryState_Free;
const KMemoryInfo info = it->GetMemoryInfo();
const bool is_free = info.GetState() == KMemoryState_Free;
if (is_free) { if (is_free) {
if (info.GetAddress() < GetInteger(address)) { if (it->GetAddress() < GetInteger(address)) {
++num_allocator_blocks; ++num_allocator_blocks;
} }
if (last_address < info.GetLastAddress()) { if (last_address < it->GetLastAddress()) {
++num_allocator_blocks; ++num_allocator_blocks;
} }
} }
/* Check if we're done. */ /* Check if we're done. */
if (last_address <= info.GetLastAddress()) { if (last_address <= it->GetLastAddress()) {
if (!is_free) { if (!is_free) {
checked_mapped_size += (last_address + 1 - cur_address); checked_mapped_size += (last_address + 1 - cur_address);
} }
@ -4550,11 +4532,11 @@ namespace ams::kern {
/* Track the memory if it's mapped. */ /* Track the memory if it's mapped. */
if (!is_free) { if (!is_free) {
checked_mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address; checked_mapped_size += it->GetEndAddress() - cur_address;
} }
/* Advance. */ /* Advance. */
cur_address = info.GetEndAddress(); cur_address = it->GetEndAddress();
++it; ++it;
} }
@ -4594,26 +4576,23 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* If the memory state is free, we mapped it and need to unmap it. */ /* If the memory state is free, we mapped it and need to unmap it. */
if (info.GetState() == KMemoryState_Free) { if (it->GetState() == KMemoryState_Free) {
/* Determine the range to unmap. */ /* Determine the range to unmap. */
const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None };
const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address, last_unmap_address + 1 - cur_address) / PageSize; const size_t cur_pages = std::min(it->GetEndAddress() - cur_address, last_unmap_address + 1 - cur_address) / PageSize;
/* Unmap. */ /* Unmap. */
MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, true)); MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, true));
} }
/* Check if we're done. */ /* Check if we're done. */
if (last_unmap_address <= info.GetLastAddress()) { if (last_unmap_address <= it->GetLastAddress()) {
break; break;
} }
/* Advance. */ /* Advance. */
cur_address = info.GetEndAddress(); cur_address = it->GetEndAddress();
++it; ++it;
} }
} }
@ -4632,14 +4611,11 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* If it's unmapped, we need to map it. */ /* If it's unmapped, we need to map it. */
if (info.GetState() == KMemoryState_Free) { if (it->GetState() == KMemoryState_Free) {
/* Determine the range to map. */ /* Determine the range to map. */
const KPageProperties map_properties = { KMemoryPermission_UserReadWrite, false, false, cur_address == this->GetAliasRegionStart() ? DisableMergeAttribute_DisableHead : DisableMergeAttribute_None }; const KPageProperties map_properties = { KMemoryPermission_UserReadWrite, false, false, cur_address == this->GetAliasRegionStart() ? DisableMergeAttribute_DisableHead : DisableMergeAttribute_None };
size_t map_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address, last_address + 1 - cur_address) / PageSize; size_t map_pages = std::min(it->GetEndAddress() - cur_address, last_address + 1 - cur_address) / PageSize;
/* While we have pages to map, map them. */ /* While we have pages to map, map them. */
{ {
@ -4680,12 +4656,12 @@ namespace ams::kern {
} }
/* Check if we're done. */ /* Check if we're done. */
if (last_address <= info.GetLastAddress()) { if (last_address <= it->GetLastAddress()) {
break; break;
} }
/* Advance. */ /* Advance. */
cur_address = info.GetEndAddress(); cur_address = it->GetEndAddress();
++it; ++it;
} }
@ -4737,26 +4713,23 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* Verify the memory's state. */ /* Verify the memory's state. */
const bool is_normal = info.GetState() == KMemoryState_Normal && info.GetAttribute() == 0; const bool is_normal = it->GetState() == KMemoryState_Normal && it->GetAttribute() == 0;
const bool is_free = info.GetState() == KMemoryState_Free; const bool is_free = it->GetState() == KMemoryState_Free;
R_UNLESS(is_normal || is_free, svc::ResultInvalidCurrentMemory()); R_UNLESS(is_normal || is_free, svc::ResultInvalidCurrentMemory());
if (is_normal) { if (is_normal) {
R_UNLESS(info.GetAttribute() == KMemoryAttribute_None, svc::ResultInvalidCurrentMemory()); R_UNLESS(it->GetAttribute() == KMemoryAttribute_None, svc::ResultInvalidCurrentMemory());
if (map_start_address == Null<KProcessAddress>) { if (map_start_address == Null<KProcessAddress>) {
map_start_address = cur_address; map_start_address = cur_address;
} }
map_last_address = (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address; map_last_address = (last_address >= it->GetLastAddress()) ? it->GetLastAddress() : last_address;
if (info.GetAddress() < GetInteger(address)) { if (it->GetAddress() < GetInteger(address)) {
++num_allocator_blocks; ++num_allocator_blocks;
} }
if (last_address < info.GetLastAddress()) { if (last_address < it->GetLastAddress()) {
++num_allocator_blocks; ++num_allocator_blocks;
} }
@ -4764,12 +4737,12 @@ namespace ams::kern {
} }
/* Check if we're done. */ /* Check if we're done. */
if (last_address <= info.GetLastAddress()) { if (last_address <= it->GetLastAddress()) {
break; break;
} }
/* Advance. */ /* Advance. */
cur_address = info.GetEndAddress(); cur_address = it->GetEndAddress();
++it; ++it;
} }
@ -4802,26 +4775,23 @@ namespace ams::kern {
/* Check that the iterator is valid. */ /* Check that the iterator is valid. */
MESOSPHERE_ASSERT(it != m_memory_block_manager.end()); MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
/* If the memory state is normal, we need to unmap it. */ /* If the memory state is normal, we need to unmap it. */
if (info.GetState() == KMemoryState_Normal) { if (it->GetState() == KMemoryState_Normal) {
/* Determine the range to unmap. */ /* Determine the range to unmap. */
const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None };
const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address, last_address + 1 - cur_address) / PageSize; const size_t cur_pages = std::min(it->GetEndAddress() - cur_address, last_address + 1 - cur_address) / PageSize;
/* Unmap. */ /* Unmap. */
MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, false)); MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), cur_address, cur_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, false));
} }
/* Check if we're done. */ /* Check if we're done. */
if (last_address <= info.GetLastAddress()) { if (last_address <= it->GetLastAddress()) {
break; break;
} }
/* Advance. */ /* Advance. */
cur_address = info.GetEndAddress(); cur_address = it->GetEndAddress();
++it; ++it;
} }

View file

@ -220,18 +220,8 @@ namespace ams::kern {
m_running_thread_switch_counts[i] = 0; m_running_thread_switch_counts[i] = 0;
} }
/* Set max memory based on address space type. */ /* Set max memory. */
switch ((params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask)) {
case ams::svc::CreateProcessFlag_AddressSpace32Bit:
case ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated:
case ams::svc::CreateProcessFlag_AddressSpace64Bit:
m_max_process_memory = m_page_table.GetHeapRegionSize(); m_max_process_memory = m_page_table.GetHeapRegionSize();
break;
case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
m_max_process_memory = m_page_table.GetHeapRegionSize() + m_page_table.GetAliasRegionSize();
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* Generate random entropy. */ /* Generate random entropy. */
KSystemControl::GenerateRandom(m_entropy, util::size(m_entropy)); KSystemControl::GenerateRandom(m_entropy, util::size(m_entropy));
@ -299,7 +289,7 @@ namespace ams::kern {
/* Setup page table. */ /* Setup page table. */
{ {
const bool from_back = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) == 0; const bool from_back = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) == 0;
R_TRY(m_page_table.Initialize(static_cast<ams::svc::CreateProcessFlag>(params.flags), from_back, pool, params.code_address, params.code_num_pages * PageSize, m_system_resource, res_limit)); R_TRY(m_page_table.Initialize(static_cast<ams::svc::CreateProcessFlag>(params.flags), from_back, pool, params.code_address, params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetSlabIndex()));
} }
ON_RESULT_FAILURE_2 { m_page_table.Finalize(); }; ON_RESULT_FAILURE_2 { m_page_table.Finalize(); };
@ -378,7 +368,7 @@ namespace ams::kern {
/* Setup page table. */ /* Setup page table. */
{ {
const bool from_back = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) == 0; const bool from_back = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) == 0;
R_TRY(m_page_table.Initialize(static_cast<ams::svc::CreateProcessFlag>(params.flags), from_back, pool, params.code_address, code_size, m_system_resource, res_limit)); R_TRY(m_page_table.Initialize(static_cast<ams::svc::CreateProcessFlag>(params.flags), from_back, pool, params.code_address, code_size, m_system_resource, res_limit, this->GetSlabIndex()));
} }
ON_RESULT_FAILURE_2 { m_page_table.Finalize(); }; ON_RESULT_FAILURE_2 { m_page_table.Finalize(); };

View file

@ -124,31 +124,14 @@ namespace ams::kern {
} }
/* System Initialization. */ /* System Initialization. */
void KSystemControlBase::ConfigureKTargetSystem() {
/* By default, use the default config set in the KTargetSystem header. */
}
void KSystemControlBase::InitializePhase1() { void KSystemControlBase::InitializePhase1() {
/* Configure KTargetSystem. */ /* Enable KTargetSystem. */
{ {
/* Set IsDebugMode. */ KTargetSystem::SetInitialized();
{
KTargetSystem::SetIsDebugMode(true);
/* If debug mode, we want to initialize uart logging. */
KTargetSystem::EnableDebugLogging(true);
}
/* Set Kernel Configuration. */
{
KTargetSystem::EnableDebugMemoryFill(false);
KTargetSystem::EnableUserExceptionHandlers(true);
KTargetSystem::EnableDynamicResourceLimits(true);
KTargetSystem::EnableUserPmuAccess(false);
}
/* Set Kernel Debugging. */
{
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
KTargetSystem::EnableKernelDebugging(true);
}
} }
/* Initialize random and resource limit. */ /* Initialize random and resource limit. */

View file

@ -41,16 +41,21 @@ namespace ams::kern::svc {
case ams::svc::ArbitrationType_WaitIfLessThan: case ams::svc::ArbitrationType_WaitIfLessThan:
case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan: case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan:
case ams::svc::ArbitrationType_WaitIfEqual: case ams::svc::ArbitrationType_WaitIfEqual:
case ams::svc::ArbitrationType_WaitIfEqual64:
return true; return true;
default: default:
return false; return false;
} }
} }
Result WaitForAddress(uintptr_t address, ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) { Result WaitForAddress(uintptr_t address, ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
/* Validate input. */ /* Validate input. */
R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory()); R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory());
if (arb_type == ams::svc::ArbitrationType_WaitIfEqual64) {
R_UNLESS(util::IsAligned(address, sizeof(int64_t)), svc::ResultInvalidAddress());
} else {
R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress()); R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress());
}
R_UNLESS(IsValidArbitrationType(arb_type), svc::ResultInvalidEnumValue()); R_UNLESS(IsValidArbitrationType(arb_type), svc::ResultInvalidEnumValue());
/* Convert timeout from nanoseconds to ticks. */ /* Convert timeout from nanoseconds to ticks. */
@ -85,7 +90,7 @@ namespace ams::kern::svc {
/* ============================= 64 ABI ============================= */ /* ============================= 64 ABI ============================= */
Result WaitForAddress64(ams::svc::Address address, ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) { Result WaitForAddress64(ams::svc::Address address, ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
R_RETURN(WaitForAddress(address, arb_type, value, timeout_ns)); R_RETURN(WaitForAddress(address, arb_type, value, timeout_ns));
} }
@ -95,7 +100,7 @@ namespace ams::kern::svc {
/* ============================= 64From32 ABI ============================= */ /* ============================= 64From32 ABI ============================= */
Result WaitForAddress64From32(ams::svc::Address address, ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) { Result WaitForAddress64From32(ams::svc::Address address, ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
R_RETURN(WaitForAddress(address, arb_type, value, timeout_ns)); R_RETURN(WaitForAddress(address, arb_type, value, timeout_ns));
} }

View file

@ -24,6 +24,9 @@ namespace ams::kern::svc {
constexpr inline int32_t MaximumDebuggableThreadCount = 0x60; constexpr inline int32_t MaximumDebuggableThreadCount = 0x60;
Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) { Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) {
/* Check that the SVC can be used. */
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
/* Get the process from its id. */ /* Get the process from its id. */
KProcess *process = KProcess::GetProcessFromId(process_id); KProcess *process = KProcess::GetProcessFromId(process_id);
R_UNLESS(process != nullptr, svc::ResultInvalidProcessId()); R_UNLESS(process != nullptr, svc::ResultInvalidProcessId());
@ -32,9 +35,8 @@ namespace ams::kern::svc {
ON_SCOPE_EXIT { process->Close(); }; ON_SCOPE_EXIT { process->Close(); };
/* Check that the debugging is allowed. */ /* Check that the debugging is allowed. */
if (!process->IsPermittedDebug()) { const bool allowable = process->IsPermittedDebug() || GetCurrentProcess().CanForceDebug() || GetCurrentProcess().CanForceDebugProd();
R_UNLESS(GetCurrentProcess().CanForceDebug(), svc::ResultInvalidState()); R_UNLESS(allowable, svc::ResultInvalidState());
}
/* Disallow debugging one's own processs, to prevent softlocks. */ /* Disallow debugging one's own processs, to prevent softlocks. */
R_UNLESS(process != GetCurrentProcessPointer(), svc::ResultInvalidState()); R_UNLESS(process != GetCurrentProcessPointer(), svc::ResultInvalidState());
@ -92,6 +94,9 @@ namespace ams::kern::svc {
template<typename EventInfoType> template<typename EventInfoType>
Result GetDebugEvent(KUserPointer<EventInfoType *> out_info, ams::svc::Handle debug_handle) { Result GetDebugEvent(KUserPointer<EventInfoType *> out_info, ams::svc::Handle debug_handle) {
/* Only allow invoking the svc on development hardware or if force debug prod. */
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
/* Get the debug object. */ /* Get the debug object. */
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle); KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
@ -164,6 +169,9 @@ namespace ams::kern::svc {
} }
Result GetDebugThreadContext(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) { Result GetDebugThreadContext(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
/* Only allow invoking the svc on development hardware or if force debug prod. */
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
/* Validate the context flags. */ /* Validate the context flags. */
R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue()); R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue());
@ -220,6 +228,9 @@ namespace ams::kern::svc {
} }
Result QueryDebugProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, uintptr_t address) { Result QueryDebugProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, uintptr_t address) {
/* Only allow invoking the svc on development hardware or if force debug prod. */
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
/* Get the debug object. */ /* Get the debug object. */
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle); KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
@ -261,6 +272,9 @@ namespace ams::kern::svc {
} }
Result ReadDebugProcessMemory(uintptr_t buffer, ams::svc::Handle debug_handle, uintptr_t address, size_t size) { Result ReadDebugProcessMemory(uintptr_t buffer, ams::svc::Handle debug_handle, uintptr_t address, size_t size) {
/* Only allow invoking the svc on development hardware or if force debug prod. */
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
/* Validate address / size. */ /* Validate address / size. */
R_UNLESS(size > 0, svc::ResultInvalidSize()); R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory()); R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
@ -306,6 +320,9 @@ namespace ams::kern::svc {
} }
Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) { Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
/* Only allow invoking the svc on development hardware or if force debug prod. */
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
/* Get the debug object. */ /* Get the debug object. */
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle); KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());

View file

@ -314,6 +314,19 @@ namespace ams::kern::svc {
*out = io_region->GetHint(); *out = io_region->GetHint();
} }
break; break;
case ams::svc::InfoType_TransferMemoryHint:
{
/* Verify the sub-type is valid. */
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
/* Get the transfer memory from its handle. */
KScopedAutoObject transfer_memory = GetCurrentProcess().GetHandleTable().GetObject<KTransferMemory>(handle);
R_UNLESS(transfer_memory.IsNotNull(), svc::ResultInvalidHandle());
/* Get the transfer memory's address hint. */
*out = transfer_memory->GetHint();
}
break;
case ams::svc::InfoType_MesosphereMeta: case ams::svc::InfoType_MesosphereMeta:
{ {
/* Verify the handle is invalid. */ /* Verify the handle is invalid. */

View file

@ -71,6 +71,9 @@ namespace ams::kern::svc {
} }
Result GetProcessList(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) { Result GetProcessList(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
/* Only allow invoking the svc on development hardware. */
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
/* Validate that the out count is valid. */ /* Validate that the out count is valid. */
R_UNLESS((0 <= max_out_count && max_out_count <= static_cast<int32_t>(std::numeric_limits<int32_t>::max() / sizeof(u64))), svc::ResultOutOfRange()); R_UNLESS((0 <= max_out_count && max_out_count <= static_cast<int32_t>(std::numeric_limits<int32_t>::max() / sizeof(u64))), svc::ResultOutOfRange());
@ -110,8 +113,8 @@ namespace ams::kern::svc {
case ams::svc::CreateProcessFlag_AddressSpace32Bit: case ams::svc::CreateProcessFlag_AddressSpace32Bit:
case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias: case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
{ {
map_start = KAddressSpaceInfo::GetAddressSpaceStart(32, KAddressSpaceInfo::Type_MapSmall); map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall);
map_size = KAddressSpaceInfo::GetAddressSpaceSize(32, KAddressSpaceInfo::Type_MapSmall); map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall);
map_end = map_start + map_size; map_end = map_start + map_size;
} }
break; break;
@ -120,8 +123,8 @@ namespace ams::kern::svc {
/* 64-bit address space requires 64-bit process. */ /* 64-bit address space requires 64-bit process. */
R_UNLESS(is_64_bit, svc::ResultInvalidCombination()); R_UNLESS(is_64_bit, svc::ResultInvalidCombination());
map_start = KAddressSpaceInfo::GetAddressSpaceStart(36, KAddressSpaceInfo::Type_MapSmall); map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall);
map_size = KAddressSpaceInfo::GetAddressSpaceSize(36, KAddressSpaceInfo::Type_MapSmall); map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall);
map_end = map_start + map_size; map_end = map_start + map_size;
} }
break; break;
@ -130,10 +133,10 @@ namespace ams::kern::svc {
/* 64-bit address space requires 64-bit process. */ /* 64-bit address space requires 64-bit process. */
R_UNLESS(is_64_bit, svc::ResultInvalidCombination()); R_UNLESS(is_64_bit, svc::ResultInvalidCombination());
map_start = KAddressSpaceInfo::GetAddressSpaceStart(39, KAddressSpaceInfo::Type_Map39Bit); map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_Map39Bit);
map_end = map_start + KAddressSpaceInfo::GetAddressSpaceSize(39, KAddressSpaceInfo::Type_Map39Bit); map_end = map_start + KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_Map39Bit);
map_size = KAddressSpaceInfo::GetAddressSpaceSize(39, KAddressSpaceInfo::Type_Heap); map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_Heap);
} }
break; break;
default: default:

View file

@ -217,6 +217,9 @@ namespace ams::kern::svc {
} }
Result GetThreadList(int32_t *out_num_threads, KUserPointer<uint64_t *> out_thread_ids, int32_t max_out_count, ams::svc::Handle debug_handle) { Result GetThreadList(int32_t *out_num_threads, KUserPointer<uint64_t *> out_thread_ids, int32_t max_out_count, ams::svc::Handle debug_handle) {
/* Only allow invoking the svc on development hardware. */
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
/* Validate that the out count is valid. */ /* Validate that the out count is valid. */
R_UNLESS((0 <= max_out_count && max_out_count <= static_cast<int32_t>(std::numeric_limits<int32_t>::max() / sizeof(u64))), svc::ResultOutOfRange()); R_UNLESS((0 <= max_out_count && max_out_count <= static_cast<int32_t>(std::numeric_limits<int32_t>::max() / sizeof(u64))), svc::ResultOutOfRange());
@ -225,10 +228,6 @@ namespace ams::kern::svc {
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(out_thread_ids.GetUnsafePointer()), max_out_count * sizeof(u64)), svc::ResultInvalidCurrentMemory()); R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(out_thread_ids.GetUnsafePointer()), max_out_count * sizeof(u64)), svc::ResultInvalidCurrentMemory());
} }
if (debug_handle == ams::svc::InvalidHandle) {
/* If passed invalid handle, we should return the global thread list. */
R_TRY(KThread::GetThreadList(out_num_threads, out_thread_ids, max_out_count));
} else {
/* Get the handle table. */ /* Get the handle table. */
auto &handle_table = GetCurrentProcess().GetHandleTable(); auto &handle_table = GetCurrentProcess().GetHandleTable();
@ -243,12 +242,20 @@ namespace ams::kern::svc {
/* Get the thread list. */ /* Get the thread list. */
R_TRY(debug->GetProcessUnsafe()->GetThreadList(out_num_threads, out_thread_ids, max_out_count)); R_TRY(debug->GetProcessUnsafe()->GetThreadList(out_num_threads, out_thread_ids, max_out_count));
} else { } else {
/* Only allow getting as a process (or global) if the caller does not have ForceDebugProd. */
R_UNLESS(!GetCurrentProcess().CanForceDebugProd(), svc::ResultInvalidHandle());
/* Try to get as a process. */ /* Try to get as a process. */
KScopedAutoObject process = handle_table.GetObjectWithoutPseudoHandle<KProcess>(debug_handle); KScopedAutoObject process = handle_table.GetObjectWithoutPseudoHandle<KProcess>(debug_handle);
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle()); if (process.IsNotNull()) {
/* Get the thread list. */ /* Get the thread list. */
R_TRY(process->GetThreadList(out_num_threads, out_thread_ids, max_out_count)); R_TRY(process->GetThreadList(out_num_threads, out_thread_ids, max_out_count));
} else {
/* If the object is not a process, the caller may want the global thread list. */
R_UNLESS(debug_handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
/* If passed invalid handle, we should return the global thread list. */
R_TRY(KThread::GetThreadList(out_num_threads, out_thread_ids, max_out_count));
} }
} }

View file

@ -231,7 +231,7 @@
R_RETURN(::svcGetThreadContext3(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), thread_handle)); R_RETURN(::svcGetThreadContext3(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), thread_handle));
} }
ALWAYS_INLINE Result WaitForAddress(::ams::svc::Address address, ::ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) { ALWAYS_INLINE Result WaitForAddress(::ams::svc::Address address, ::ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
R_RETURN(::svcWaitForAddress(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), arb_type, value, timeout_ns)); R_RETURN(::svcWaitForAddress(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), arb_type, value, timeout_ns));
} }

View file

@ -67,7 +67,7 @@
HANDLER(0x31, Result, GetResourceLimitCurrentValue, OUTPUT(int64_t, out_current_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \ HANDLER(0x31, Result, GetResourceLimitCurrentValue, OUTPUT(int64_t, out_current_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \
HANDLER(0x32, Result, SetThreadActivity, INPUT(::ams::svc::Handle, thread_handle), INPUT(::ams::svc::ThreadActivity, thread_activity)) \ HANDLER(0x32, Result, SetThreadActivity, INPUT(::ams::svc::Handle, thread_handle), INPUT(::ams::svc::ThreadActivity, thread_activity)) \
HANDLER(0x33, Result, GetThreadContext3, OUTPTR(::ams::svc::ThreadContext, out_context), INPUT(::ams::svc::Handle, thread_handle)) \ HANDLER(0x33, Result, GetThreadContext3, OUTPTR(::ams::svc::ThreadContext, out_context), INPUT(::ams::svc::Handle, thread_handle)) \
HANDLER(0x34, Result, WaitForAddress, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::ArbitrationType, arb_type), INPUT(int32_t, value), INPUT(int64_t, timeout_ns)) \ HANDLER(0x34, Result, WaitForAddress, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::ArbitrationType, arb_type), INPUT(int64_t, value), INPUT(int64_t, timeout_ns)) \
HANDLER(0x35, Result, SignalToAddress, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::SignalType, signal_type), INPUT(int32_t, value), INPUT(int32_t, count)) \ HANDLER(0x35, Result, SignalToAddress, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::SignalType, signal_type), INPUT(int32_t, value), INPUT(int32_t, count)) \
HANDLER(0x36, void, SynchronizePreemptionState) \ HANDLER(0x36, void, SynchronizePreemptionState) \
HANDLER(0x37, Result, GetResourceLimitPeakValue, OUTPUT(int64_t, out_peak_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \ HANDLER(0x37, Result, GetResourceLimitPeakValue, OUTPUT(int64_t, out_peak_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \

View file

@ -191,6 +191,8 @@ namespace ams::svc {
InfoType_IsSvcPermitted = 26, InfoType_IsSvcPermitted = 26,
InfoType_IoRegionHint = 27, InfoType_IoRegionHint = 27,
InfoType_AliasRegionExtraSize = 28, InfoType_AliasRegionExtraSize = 28,
/* ... */
InfoType_TransferMemoryHint = 34,
InfoType_MesosphereMeta = 65000, InfoType_MesosphereMeta = 65000,
InfoType_MesosphereCurrentProcess = 65001, InfoType_MesosphereCurrentProcess = 65001,
@ -261,6 +263,7 @@ namespace ams::svc {
ArbitrationType_WaitIfLessThan = 0, ArbitrationType_WaitIfLessThan = 0,
ArbitrationType_DecrementAndWaitIfLessThan = 1, ArbitrationType_DecrementAndWaitIfLessThan = 1,
ArbitrationType_WaitIfEqual = 2, ArbitrationType_WaitIfEqual = 2,
ArbitrationType_WaitIfEqual64 = 3,
}; };
enum YieldType : s64 { enum YieldType : s64 {

View file

@ -125,6 +125,8 @@ SECTIONS
.gnu.version_r : { *(.gnu.version_r) } :rodata .gnu.version_r : { *(.gnu.version_r) } :rodata
.note.gnu.build-id : { *(.note.gnu.build-id) } :rodata .note.gnu.build-id : { *(.note.gnu.build-id) } :rodata
__rodata_end = .;
/* =========== DATA section =========== */ /* =========== DATA section =========== */
. = ALIGN(0x1000); . = ALIGN(0x1000);
__data_start = . ; __data_start = . ;

View file

@ -15,6 +15,9 @@
*/ */
#include <mesosphere.hpp> #include <mesosphere.hpp>
extern "C" void __rodata_start();
extern "C" void __rodata_end();
extern "C" void __bin_start__(); extern "C" void __bin_start__();
extern "C" void __bin_end__(); extern "C" void __bin_end__();
@ -220,6 +223,31 @@ namespace ams::kern::init {
}; };
static_assert(kern::arch::arm64::init::IsInitialPageAllocator<KInitialPageAllocatorForFinalizeIdentityMapping>); static_assert(kern::arch::arm64::init::IsInitialPageAllocator<KInitialPageAllocatorForFinalizeIdentityMapping>);
void SetupAllTtbr0Entries(KInitialPageTable &init_pt, KInitialPageAllocator &allocator) {
/* Validate that the ttbr0 array is in rodata. */
const uintptr_t rodata_start = reinterpret_cast<uintptr_t>(__rodata_start);
const uintptr_t rodata_end = reinterpret_cast<uintptr_t>(__rodata_end);
MESOSPHERE_INIT_ABORT_UNLESS(rodata_start < rodata_end);
MESOSPHERE_INIT_ABORT_UNLESS(rodata_start <= reinterpret_cast<uintptr_t>(std::addressof(KPageTable::GetTtbr0Entry(0))));
MESOSPHERE_INIT_ABORT_UNLESS(reinterpret_cast<uintptr_t>(std::addressof(KPageTable::GetTtbr0Entry(KPageTable::NumTtbr0Entries))) < rodata_end);
/* Allocate pages for all ttbr0 entries. */
for (size_t i = 0; i < KPageTable::NumTtbr0Entries; ++i) {
/* Allocate a page. */
KPhysicalAddress page = allocator.Allocate(PageSize);
MESOSPHERE_INIT_ABORT_UNLESS(page != Null<KPhysicalAddress>);
/* Check that the page is allowed to be a ttbr0 entry. */
MESOSPHERE_INIT_ABORT_UNLESS((GetInteger(page) & UINT64_C(0xFFFF000000000001)) == 0);
/* Get the physical address of the ttbr0 entry. */
const auto ttbr0_phys_ptr = init_pt.GetPhysicalAddress(KVirtualAddress(std::addressof(KPageTable::GetTtbr0Entry(i))));
/* Set the entry to the newly allocated page. */
*reinterpret_cast<volatile u64 *>(GetInteger(ttbr0_phys_ptr)) = (static_cast<u64>(i) << 48) | GetInteger(page);
}
}
void FinalizeIdentityMapping(KInitialPageTable &init_pt, KInitialPageAllocator &allocator, u64 phys_to_virt_offset) { void FinalizeIdentityMapping(KInitialPageTable &init_pt, KInitialPageAllocator &allocator, u64 phys_to_virt_offset) {
/* Create an allocator for identity mapping finalization. */ /* Create an allocator for identity mapping finalization. */
KInitialPageAllocatorForFinalizeIdentityMapping finalize_allocator(allocator, phys_to_virt_offset); KInitialPageAllocatorForFinalizeIdentityMapping finalize_allocator(allocator, phys_to_virt_offset);
@ -591,6 +619,9 @@ namespace ams::kern::init {
/* Create page table object for use during remaining initialization. */ /* Create page table object for use during remaining initialization. */
KInitialPageTable init_pt; KInitialPageTable init_pt;
/* Setup all ttbr0 pages. */
SetupAllTtbr0Entries(init_pt, g_initial_page_allocator);
/* Unmap the identity mapping. */ /* Unmap the identity mapping. */
FinalizeIdentityMapping(init_pt, g_initial_page_allocator, g_phase2_linear_region_phys_to_virt_diff); FinalizeIdentityMapping(init_pt, g_initial_page_allocator, g_phase2_linear_region_phys_to_virt_diff);

View file

@ -111,4 +111,8 @@ namespace ams::kern {
KThread &Kernel::GetMainThread(s32 core_id) { return g_main_threads.m_arr[core_id]; } KThread &Kernel::GetMainThread(s32 core_id) { return g_main_threads.m_arr[core_id]; }
KThread &Kernel::GetIdleThread(s32 core_id) { return g_idle_threads.m_arr[core_id]; } KThread &Kernel::GetIdleThread(s32 core_id) { return g_idle_threads.m_arr[core_id]; }
__attribute__((constructor)) void ConfigureKTargetSystem() {
KSystemControl::ConfigureKTargetSystem();
}
} }

View file

@ -110,6 +110,7 @@
"type": "debug_flags", "type": "debug_flags",
"value": { "value": {
"allow_debug": false, "allow_debug": false,
"force_debug_prod": false,
"force_debug": true "force_debug": true
} }
} }

View file

@ -105,6 +105,7 @@
"type": "debug_flags", "type": "debug_flags",
"value": { "value": {
"allow_debug": false, "allow_debug": false,
"force_debug_prod": false,
"force_debug": true "force_debug": true
} }
}] }]

View file

@ -104,6 +104,7 @@
"type": "debug_flags", "type": "debug_flags",
"value": { "value": {
"allow_debug": false, "allow_debug": false,
"force_debug_prod": false,
"force_debug": true "force_debug": true
} }
}] }]

View file

@ -309,9 +309,18 @@ namespace ams::ldr {
DEFINE_CAPABILITY_CLASS(DebugFlags, DEFINE_CAPABILITY_CLASS(DebugFlags,
DEFINE_CAPABILITY_FIELD(AllowDebug, IdBits, 1, bool); DEFINE_CAPABILITY_FIELD(AllowDebug, IdBits, 1, bool);
DEFINE_CAPABILITY_FIELD(ForceDebug, AllowDebug, 1, bool); DEFINE_CAPABILITY_FIELD(ForceDebugProd, AllowDebug, 1, bool);
DEFINE_CAPABILITY_FIELD(ForceDebug, ForceDebugProd, 1, bool);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const { bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
u32 total = 0;
if (this->GetAllowDebug()) { ++total; }
if (this->GetForceDebugProd()) { ++total; }
if (this->GetForceDebug()) { ++total; }
if (total > 1) {
return false;
}
for (size_t i = 0; i < kac_count; i++) { for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) { if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]); const auto restriction = Decode(kac[i]);
@ -319,12 +328,14 @@ namespace ams::ldr {
return (restriction.GetValue() & this->GetValue()) == this->GetValue(); return (restriction.GetValue() & this->GetValue()) == this->GetValue();
} }
} }
return false; return false;
} }
static constexpr util::BitPack32 Encode(bool allow_debug, bool force_debug) { static constexpr util::BitPack32 Encode(bool allow_debug, bool force_debug_prod, bool force_debug) {
util::BitPack32 encoded{IdBitsValue}; util::BitPack32 encoded{IdBitsValue};
encoded.Set<AllowDebug>(allow_debug); encoded.Set<AllowDebug>(allow_debug);
encoded.Set<ForceDebugProd>(force_debug_prod);
encoded.Set<ForceDebug>(force_debug); encoded.Set<ForceDebug>(force_debug);
return encoded; return encoded;
} }
@ -406,7 +417,7 @@ namespace ams::ldr {
kac[i] = CapabilityApplicationType::Encode(flags & ProgramInfoFlag_ApplicationTypeMask); kac[i] = CapabilityApplicationType::Encode(flags & ProgramInfoFlag_ApplicationTypeMask);
break; break;
case CapabilityId::DebugFlags: case CapabilityId::DebugFlags:
kac[i] = CapabilityDebugFlags::Encode((flags & ProgramInfoFlag_AllowDebug) != 0, CapabilityDebugFlags::Decode(cap).GetForceDebug()); kac[i] = CapabilityDebugFlags::Encode((flags & ProgramInfoFlag_AllowDebug) != 0, CapabilityDebugFlags::Decode(cap).GetForceDebugProd(), CapabilityDebugFlags::Decode(cap).GetForceDebug());
break; break;
default: default:
break; break;

View file

@ -85,6 +85,7 @@
"type": "debug_flags", "type": "debug_flags",
"value": { "value": {
"allow_debug": false, "allow_debug": false,
"force_debug_prod": false,
"force_debug": true "force_debug": true
} }
} }