mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-02-11 10:31:06 +00:00
Compare commits
1 commit
01f28f3055
...
fea4be2380
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fea4be2380 |
23 changed files with 68 additions and 158 deletions
|
@ -109,9 +109,6 @@ namespace ams::kern::arch::arm64 {
|
||||||
KPageTableManager &GetPageTableManager() const { return *m_manager; }
|
KPageTableManager &GetPageTableManager() const { return *m_manager; }
|
||||||
private:
|
private:
|
||||||
constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const {
|
constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const {
|
||||||
/* Check that the property is not kernel execute. */
|
|
||||||
MESOSPHERE_ABORT_UNLESS((properties.perm & KMemoryPermission_KernelExecute) == 0);
|
|
||||||
|
|
||||||
/* Set basic attributes. */
|
/* Set basic attributes. */
|
||||||
PageTableEntry entry{PageTableEntry::ExtensionFlag_Valid};
|
PageTableEntry entry{PageTableEntry::ExtensionFlag_Valid};
|
||||||
entry.SetPrivilegedExecuteNever(true);
|
entry.SetPrivilegedExecuteNever(true);
|
||||||
|
@ -125,24 +122,22 @@ namespace ams::kern::arch::arm64 {
|
||||||
/* Set page attribute. */
|
/* Set page attribute. */
|
||||||
if (properties.io) {
|
if (properties.io) {
|
||||||
MESOSPHERE_ABORT_UNLESS(!properties.uncached);
|
MESOSPHERE_ABORT_UNLESS(!properties.uncached);
|
||||||
MESOSPHERE_ABORT_UNLESS((properties.perm & KMemoryPermission_UserExecute) == 0);
|
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
|
||||||
|
|
||||||
entry.SetPageAttribute(PageTableEntry::PageAttribute_Device_nGnRnE)
|
entry.SetPageAttribute(PageTableEntry::PageAttribute_Device_nGnRnE)
|
||||||
.SetUserExecuteNever(true);
|
.SetUserExecuteNever(true);
|
||||||
} else if (properties.uncached) {
|
} else if (properties.uncached) {
|
||||||
MESOSPHERE_ABORT_UNLESS((properties.perm & KMemoryPermission_UserExecute) == 0);
|
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
|
||||||
|
|
||||||
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable)
|
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable);
|
||||||
.SetUserExecuteNever(true);
|
|
||||||
} else {
|
} else {
|
||||||
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemory);
|
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemory);
|
||||||
|
|
||||||
if ((properties.perm & KMemoryPermission_UserExecute) != 0) {
|
|
||||||
/* Check that the permission is either r--/--x or r--/r-x. */
|
|
||||||
MESOSPHERE_ABORT_UNLESS((properties.perm & ~ams::svc::MemoryPermission_Read) == (KMemoryPermission_KernelRead | KMemoryPermission_UserExecute));
|
|
||||||
} else {
|
|
||||||
entry.SetUserExecuteNever(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set user execute never bit. */
|
||||||
|
if (properties.perm != KMemoryPermission_UserReadExecute) {
|
||||||
|
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
|
||||||
|
entry.SetUserExecuteNever(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set AP[1] based on perm. */
|
/* Set AP[1] based on perm. */
|
||||||
|
|
|
@ -21,18 +21,7 @@ namespace ams::kern::arch::arm64 {
|
||||||
void UserspaceAccessFunctionAreaBegin();
|
void UserspaceAccessFunctionAreaBegin();
|
||||||
|
|
||||||
class UserspaceAccess {
|
class UserspaceAccess {
|
||||||
private:
|
|
||||||
static bool CopyMemoryFromUserSize32BitWithSupervisorAccessImpl(void *dst, const void *src);
|
|
||||||
public:
|
public:
|
||||||
static bool CopyMemoryFromUserSize32BitWithSupervisorAccess(void *dst, const void *src) {
|
|
||||||
/* Check that the address is within the valid userspace range. */
|
|
||||||
if (const uintptr_t src_uptr = reinterpret_cast<uintptr_t>(src); src_uptr < ams::svc::AddressNullGuard32Size || (src_uptr + sizeof(u32) - 1) >= ams::svc::AddressMemoryRegion39Size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CopyMemoryFromUserSize32BitWithSupervisorAccessImpl(dst, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -24,8 +24,4 @@ namespace ams::kern {
|
||||||
constexpr inline size_t MainMemorySize = 4_GB;
|
constexpr inline size_t MainMemorySize = 4_GB;
|
||||||
constexpr inline size_t MainMemorySizeMax = 8_GB;
|
constexpr inline size_t MainMemorySizeMax = 8_GB;
|
||||||
|
|
||||||
constexpr inline u32 MinimumMemoryManagerAlignmentShifts[] = {
|
|
||||||
0, 0, 0, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,6 @@ namespace ams::kern {
|
||||||
size_t m_num_managers;
|
size_t m_num_managers;
|
||||||
u64 m_optimized_process_ids[Pool_Count];
|
u64 m_optimized_process_ids[Pool_Count];
|
||||||
bool m_has_optimized_process[Pool_Count];
|
bool m_has_optimized_process[Pool_Count];
|
||||||
s32 m_min_heap_indexes[Pool_Count];
|
|
||||||
private:
|
private:
|
||||||
Impl &GetManager(KPhysicalAddress address) {
|
Impl &GetManager(KPhysicalAddress address) {
|
||||||
return m_managers[KMemoryLayout::GetPhysicalLinearRegion(address).GetAttributes()];
|
return m_managers[KMemoryLayout::GetPhysicalLinearRegion(address).GetAttributes()];
|
||||||
|
@ -189,12 +188,12 @@ namespace ams::kern {
|
||||||
Result AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random, s32 min_heap_index);
|
Result AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random, s32 min_heap_index);
|
||||||
public:
|
public:
|
||||||
KMemoryManager()
|
KMemoryManager()
|
||||||
: m_pool_locks(), m_pool_managers_head(), m_pool_managers_tail(), m_managers(), m_num_managers(), m_optimized_process_ids(), m_has_optimized_process(), m_min_heap_indexes()
|
: m_pool_locks(), m_pool_managers_head(), m_pool_managers_tail(), m_managers(), m_num_managers(), m_optimized_process_ids(), m_has_optimized_process()
|
||||||
{
|
{
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
NOINLINE void Initialize(KVirtualAddress management_region, size_t management_region_size, const u32 *min_align_shifts);
|
NOINLINE void Initialize(KVirtualAddress management_region, size_t management_region_size);
|
||||||
|
|
||||||
NOINLINE Result InitializeOptimizedMemory(u64 process_id, Pool pool);
|
NOINLINE Result InitializeOptimizedMemory(u64 process_id, Pool pool);
|
||||||
NOINLINE void FinalizeOptimizedMemory(u64 process_id, Pool pool);
|
NOINLINE void FinalizeOptimizedMemory(u64 process_id, Pool pool);
|
||||||
|
@ -300,10 +299,6 @@ namespace ams::kern {
|
||||||
manager->DumpFreeList();
|
manager->DumpFreeList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetMinimumAlignment(Pool pool) {
|
|
||||||
return KPageHeap::GetBlockSize(m_min_heap_indexes[pool]);
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
static size_t CalculateManagementOverheadSize(size_t region_size) {
|
static size_t CalculateManagementOverheadSize(size_t region_size) {
|
||||||
return Impl::CalculateManagementOverheadSize(region_size);
|
return Impl::CalculateManagementOverheadSize(region_size);
|
||||||
|
|
|
@ -100,8 +100,6 @@ namespace ams::kern::arch::arm64 {
|
||||||
u32 insn_value = 0;
|
u32 insn_value = 0;
|
||||||
if (UserspaceAccess::CopyMemoryFromUser(std::addressof(insn_value), reinterpret_cast<u32 *>(context->pc), sizeof(insn_value))) {
|
if (UserspaceAccess::CopyMemoryFromUser(std::addressof(insn_value), reinterpret_cast<u32 *>(context->pc), sizeof(insn_value))) {
|
||||||
insn = insn_value;
|
insn = insn_value;
|
||||||
} else if (KTargetSystem::IsDebugMode() && (context->pc & 3) == 0 && UserspaceAccess::CopyMemoryFromUserSize32BitWithSupervisorAccess(std::addressof(insn_value), reinterpret_cast<u32 *>(context->pc))) {
|
|
||||||
insn = insn_value;
|
|
||||||
} else {
|
} else {
|
||||||
insn = 0;
|
insn = 0;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +112,33 @@ namespace ams::kern::arch::arm64 {
|
||||||
bool should_process_user_exception = KTargetSystem::IsUserExceptionHandlersEnabled();
|
bool should_process_user_exception = KTargetSystem::IsUserExceptionHandlersEnabled();
|
||||||
|
|
||||||
const u64 ec = (esr >> 26) & 0x3F;
|
const u64 ec = (esr >> 26) & 0x3F;
|
||||||
|
switch (ec) {
|
||||||
|
case EsrEc_Unknown:
|
||||||
|
case EsrEc_IllegalExecution:
|
||||||
|
case EsrEc_Svc32:
|
||||||
|
case EsrEc_Svc64:
|
||||||
|
case EsrEc_PcAlignmentFault:
|
||||||
|
case EsrEc_SpAlignmentFault:
|
||||||
|
case EsrEc_SErrorInterrupt:
|
||||||
|
case EsrEc_BreakPointEl0:
|
||||||
|
case EsrEc_SoftwareStepEl0:
|
||||||
|
case EsrEc_WatchPointEl0:
|
||||||
|
case EsrEc_BkptInstruction:
|
||||||
|
case EsrEc_BrkInstruction:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
/* If the fault address's state is KMemoryState_Code and the user can't read the address, force processing exception. */
|
||||||
|
KMemoryInfo info;
|
||||||
|
ams::svc::PageInfo pi;
|
||||||
|
if (R_SUCCEEDED(cur_process.GetPageTable().QueryInfo(std::addressof(info), std::addressof(pi), far))) {
|
||||||
|
if (info.GetState() == KMemoryState_Code && ((info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead)) {
|
||||||
|
should_process_user_exception = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* In the event that we return from this exception, we want SPSR.SS set so that we advance an instruction if single-stepping. */
|
/* In the event that we return from this exception, we want SPSR.SS set so that we advance an instruction if single-stepping. */
|
||||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||||
|
|
|
@ -21,15 +21,6 @@ namespace ams::kern::arch::arm64 {
|
||||||
void UserModeThreadStarter();
|
void UserModeThreadStarter();
|
||||||
void SupervisorModeThreadStarter();
|
void SupervisorModeThreadStarter();
|
||||||
|
|
||||||
void InvokeSupervisorModeThread(uintptr_t argument, uintptr_t entrypoint) {
|
|
||||||
/* Invoke the function. */
|
|
||||||
using SupervisorModeFunctionType = void (*)(uintptr_t);
|
|
||||||
reinterpret_cast<SupervisorModeFunctionType>(entrypoint)(argument);
|
|
||||||
|
|
||||||
/* Wait forever. */
|
|
||||||
AMS_INFINITE_LOOP();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnThreadStart() {
|
void OnThreadStart() {
|
||||||
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
|
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
|
||||||
/* Send KDebug event for this thread's creation. */
|
/* Send KDebug event for this thread's creation. */
|
||||||
|
|
|
@ -154,21 +154,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize32BitEPvPKv:
|
||||||
mov x0, #1
|
mov x0, #1
|
||||||
ret
|
ret
|
||||||
|
|
||||||
/* ams::kern::arch::arm64::UserspaceAccess::CopyMemoryFromUserSize32BitWithSupervisorAccessImpl(void *dst, const void *src) */
|
|
||||||
.section .text._ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv, "ax", %progbits
|
|
||||||
.global _ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv
|
|
||||||
.type _ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv, %function
|
|
||||||
.balign 0x10
|
|
||||||
_ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv:
|
|
||||||
/* Just load and store a u32. */
|
|
||||||
/* NOTE: This is done with supervisor access permissions. */
|
|
||||||
ldr w2, [x1]
|
|
||||||
str w2, [x0]
|
|
||||||
|
|
||||||
/* We're done. */
|
|
||||||
mov x0, #1
|
|
||||||
ret
|
|
||||||
|
|
||||||
/* ams::kern::arch::arm64::UserspaceAccess::CopyStringFromUser(void *dst, const void *src, size_t size) */
|
/* ams::kern::arch::arm64::UserspaceAccess::CopyStringFromUser(void *dst, const void *src, size_t size) */
|
||||||
.section .text._ZN3ams4kern4arch5arm6415UserspaceAccess18CopyStringFromUserEPvPKvm, "ax", %progbits
|
.section .text._ZN3ams4kern4arch5arm6415UserspaceAccess18CopyStringFromUserEPvPKvm, "ax", %progbits
|
||||||
.global _ZN3ams4kern4arch5arm6415UserspaceAccess18CopyStringFromUserEPvPKvm
|
.global _ZN3ams4kern4arch5arm6415UserspaceAccess18CopyStringFromUserEPvPKvm
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace ams::kern {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryManager::Initialize(KVirtualAddress management_region, size_t management_region_size, const u32 *min_align_shifts) {
|
void KMemoryManager::Initialize(KVirtualAddress management_region, size_t management_region_size) {
|
||||||
/* Clear the management region to zero. */
|
/* Clear the management region to zero. */
|
||||||
const KVirtualAddress management_region_end = management_region + management_region_size;
|
const KVirtualAddress management_region_end = management_region + management_region_size;
|
||||||
std::memset(GetVoidPointer(management_region), 0, management_region_size);
|
std::memset(GetVoidPointer(management_region), 0, management_region_size);
|
||||||
|
@ -154,17 +154,6 @@ namespace ams::kern {
|
||||||
for (size_t i = 0; i < m_num_managers; ++i) {
|
for (size_t i = 0; i < m_num_managers; ++i) {
|
||||||
m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
|
m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine the min heap size for all pools. */
|
|
||||||
for (size_t i = 0; i < Pool_Count; ++i) {
|
|
||||||
/* Determine the min alignment for the pool in pages. */
|
|
||||||
const size_t min_align_pages = 1 << min_align_shifts[i];
|
|
||||||
|
|
||||||
/* Determine a heap index. */
|
|
||||||
if (const auto heap_index = KPageHeap::GetAlignedBlockIndex(min_align_pages, min_align_pages); heap_index >= 0) {
|
|
||||||
m_min_heap_indexes[i] = heap_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
|
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
|
||||||
|
@ -203,19 +192,8 @@ namespace ams::kern {
|
||||||
return Null<KPhysicalAddress>;
|
return Null<KPhysicalAddress>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine the pool and direction we're allocating from. */
|
|
||||||
const auto [pool, dir] = DecodeOption(option);
|
|
||||||
|
|
||||||
/* Check that we're allocating a correctly aligned number of pages. */
|
|
||||||
const size_t min_align_pages = KPageHeap::GetBlockNumPages(m_min_heap_indexes[pool]);
|
|
||||||
if (!util::IsAligned(num_pages, min_align_pages)) {
|
|
||||||
return Null<KPhysicalAddress>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update our alignment. */
|
|
||||||
align_pages = std::max(align_pages, min_align_pages);
|
|
||||||
|
|
||||||
/* Lock the pool that we're allocating from. */
|
/* Lock the pool that we're allocating from. */
|
||||||
|
const auto [pool, dir] = DecodeOption(option);
|
||||||
KScopedLightLock lk(m_pool_locks[pool]);
|
KScopedLightLock lk(m_pool_locks[pool]);
|
||||||
|
|
||||||
/* Choose a heap based on our page size request. */
|
/* Choose a heap based on our page size request. */
|
||||||
|
@ -248,13 +226,6 @@ namespace ams::kern {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random, s32 min_heap_index) {
|
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random, s32 min_heap_index) {
|
||||||
/* Check that we're allocating a correctly aligned number of pages. */
|
|
||||||
const size_t min_align_pages = KPageHeap::GetBlockNumPages(m_min_heap_indexes[pool]);
|
|
||||||
R_UNLESS(util::IsAligned(num_pages, min_align_pages), svc::ResultInvalidSize());
|
|
||||||
|
|
||||||
/* Adjust our min heap index to the pool minimum if needed. */
|
|
||||||
min_heap_index = std::max(min_heap_index, m_min_heap_indexes[pool]);
|
|
||||||
|
|
||||||
/* Choose a heap based on our page size request. */
|
/* Choose a heap based on our page size request. */
|
||||||
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
|
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
|
||||||
R_UNLESS(0 <= heap_index, svc::ResultOutOfMemory());
|
R_UNLESS(0 <= heap_index, svc::ResultOutOfMemory());
|
||||||
|
|
|
@ -1787,11 +1787,6 @@ namespace ams::kern {
|
||||||
/* We're going to perform an update, so create a helper. */
|
/* We're going to perform an update, so create a helper. */
|
||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
/* If we're creating an executable mapping, take and immediately release the scheduler lock. This will force a reschedule. */
|
|
||||||
if (is_x) {
|
|
||||||
KScopedSchedulerLock sl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform mapping operation. */
|
/* Perform mapping operation. */
|
||||||
const KPageProperties properties = { new_perm, false, false, DisableMergeAttribute_None };
|
const KPageProperties properties = { new_perm, false, false, DisableMergeAttribute_None };
|
||||||
const auto operation = was_x ? OperationType_ChangePermissionsAndRefreshAndFlush : OperationType_ChangePermissions;
|
const auto operation = was_x ? OperationType_ChangePermissionsAndRefreshAndFlush : OperationType_ChangePermissions;
|
||||||
|
@ -3834,15 +3829,15 @@ namespace ams::kern {
|
||||||
switch (dst_state) {
|
switch (dst_state) {
|
||||||
case KMemoryState_Ipc:
|
case KMemoryState_Ipc:
|
||||||
test_state = KMemoryState_FlagCanUseIpc;
|
test_state = KMemoryState_FlagCanUseIpc;
|
||||||
test_attr_mask = KMemoryAttribute_All & (~(KMemoryAttribute_PermissionLocked | KMemoryAttribute_IpcLocked));
|
test_attr_mask = KMemoryAttribute_Uncached | KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked;
|
||||||
break;
|
break;
|
||||||
case KMemoryState_NonSecureIpc:
|
case KMemoryState_NonSecureIpc:
|
||||||
test_state = KMemoryState_FlagCanUseNonSecureIpc;
|
test_state = KMemoryState_FlagCanUseNonSecureIpc;
|
||||||
test_attr_mask = KMemoryAttribute_All & (~(KMemoryAttribute_PermissionLocked | KMemoryAttribute_DeviceShared | KMemoryAttribute_IpcLocked));
|
test_attr_mask = KMemoryAttribute_Uncached | KMemoryAttribute_Locked;
|
||||||
break;
|
break;
|
||||||
case KMemoryState_NonDeviceIpc:
|
case KMemoryState_NonDeviceIpc:
|
||||||
test_state = KMemoryState_FlagCanUseNonDeviceIpc;
|
test_state = KMemoryState_FlagCanUseNonDeviceIpc;
|
||||||
test_attr_mask = KMemoryAttribute_All & (~(KMemoryAttribute_PermissionLocked | KMemoryAttribute_DeviceShared | KMemoryAttribute_IpcLocked));
|
test_attr_mask = KMemoryAttribute_Uncached | KMemoryAttribute_Locked;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
R_THROW(svc::ResultInvalidCombination());
|
R_THROW(svc::ResultInvalidCombination());
|
||||||
|
|
|
@ -924,6 +924,7 @@ namespace ams::kern {
|
||||||
MESOSPHERE_ABORT_UNLESS(m_main_thread_stack_size == 0);
|
MESOSPHERE_ABORT_UNLESS(m_main_thread_stack_size == 0);
|
||||||
|
|
||||||
/* Ensure that we're allocating a valid stack. */
|
/* Ensure that we're allocating a valid stack. */
|
||||||
|
stack_size = util::AlignUp(stack_size, PageSize);
|
||||||
R_UNLESS(stack_size + m_code_size <= m_max_process_memory, svc::ResultOutOfMemory());
|
R_UNLESS(stack_size + m_code_size <= m_max_process_memory, svc::ResultOutOfMemory());
|
||||||
R_UNLESS(stack_size + m_code_size >= m_code_size, svc::ResultOutOfMemory());
|
R_UNLESS(stack_size + m_code_size >= m_code_size, svc::ResultOutOfMemory());
|
||||||
|
|
||||||
|
|
|
@ -56,9 +56,8 @@ namespace ams::kern {
|
||||||
{
|
{
|
||||||
const auto &management_region = KMemoryLayout::GetPoolManagementRegion();
|
const auto &management_region = KMemoryLayout::GetPoolManagementRegion();
|
||||||
MESOSPHERE_ABORT_UNLESS(management_region.GetEndAddress() != 0);
|
MESOSPHERE_ABORT_UNLESS(management_region.GetEndAddress() != 0);
|
||||||
static_assert(util::size(MinimumMemoryManagerAlignmentShifts) == KMemoryManager::Pool_Count);
|
|
||||||
|
|
||||||
Kernel::GetMemoryManager().Initialize(management_region.GetAddress(), management_region.GetSize(), MinimumMemoryManagerAlignmentShifts);
|
Kernel::GetMemoryManager().Initialize(management_region.GetAddress(), management_region.GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the Initial Process Binary to safe memory. */
|
/* Copy the Initial Process Binary to safe memory. */
|
||||||
|
|
|
@ -48,9 +48,8 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
Result MapPhysicalMemory(uintptr_t address, size_t size) {
|
Result MapPhysicalMemory(uintptr_t address, size_t size) {
|
||||||
/* Validate address / size. */
|
/* Validate address / size. */
|
||||||
const size_t min_alignment = Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool());
|
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||||
R_UNLESS(util::IsAligned(address, min_alignment), svc::ResultInvalidAddress());
|
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||||
R_UNLESS(util::IsAligned(size, min_alignment), svc::ResultInvalidSize());
|
|
||||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||||
R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion());
|
R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion());
|
||||||
|
|
||||||
|
@ -70,9 +69,8 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
Result UnmapPhysicalMemory(uintptr_t address, size_t size) {
|
Result UnmapPhysicalMemory(uintptr_t address, size_t size) {
|
||||||
/* Validate address / size. */
|
/* Validate address / size. */
|
||||||
const size_t min_alignment = Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool());
|
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||||
R_UNLESS(util::IsAligned(address, min_alignment), svc::ResultInvalidAddress());
|
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||||
R_UNLESS(util::IsAligned(size, min_alignment), svc::ResultInvalidSize());
|
|
||||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||||
R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion());
|
R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion());
|
||||||
|
|
||||||
|
|
|
@ -296,9 +296,7 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
Result StartProcess(ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
|
Result StartProcess(ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
|
||||||
/* Validate stack size. */
|
/* Validate stack size. */
|
||||||
const uint64_t aligned_stack_size = util::AlignUp(main_thread_stack_size, Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool()));
|
R_UNLESS(main_thread_stack_size == static_cast<size_t>(main_thread_stack_size), svc::ResultOutOfMemory());
|
||||||
R_UNLESS(aligned_stack_size >= main_thread_stack_size, svc::ResultOutOfMemory());
|
|
||||||
R_UNLESS(aligned_stack_size == static_cast<size_t>(aligned_stack_size), svc::ResultOutOfMemory());
|
|
||||||
|
|
||||||
/* Get the target process. */
|
/* Get the target process. */
|
||||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||||
|
@ -316,7 +314,7 @@ namespace ams::kern::svc {
|
||||||
process->SetIdealCoreId(core_id);
|
process->SetIdealCoreId(core_id);
|
||||||
|
|
||||||
/* Run the process. */
|
/* Run the process. */
|
||||||
R_RETURN(process->Run(priority, static_cast<size_t>(aligned_stack_size)));
|
R_RETURN(process->Run(priority, static_cast<size_t>(main_thread_stack_size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result TerminateProcess(ams::svc::Handle process_handle) {
|
Result TerminateProcess(ams::svc::Handle process_handle) {
|
||||||
|
|
|
@ -27,7 +27,6 @@ namespace ams::kern::svc {
|
||||||
case ams::svc::MemoryPermission_Read:
|
case ams::svc::MemoryPermission_Read:
|
||||||
case ams::svc::MemoryPermission_ReadWrite:
|
case ams::svc::MemoryPermission_ReadWrite:
|
||||||
case ams::svc::MemoryPermission_ReadExecute:
|
case ams::svc::MemoryPermission_ReadExecute:
|
||||||
case ams::svc::MemoryPermission_Execute:
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -226,7 +226,6 @@ namespace ams::ldr {
|
||||||
MetaFlag_OptimizeMemoryAllocation = (1 << 4),
|
MetaFlag_OptimizeMemoryAllocation = (1 << 4),
|
||||||
MetaFlag_DisableDeviceAddressSpaceMerge = (1 << 5),
|
MetaFlag_DisableDeviceAddressSpaceMerge = (1 << 5),
|
||||||
MetaFlag_EnableAliasRegionExtraSize = (1 << 6),
|
MetaFlag_EnableAliasRegionExtraSize = (1 << 6),
|
||||||
MetaFlag_PreventCodeReads = (1 << 7),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AddressSpaceType {
|
enum AddressSpaceType {
|
||||||
|
|
|
@ -135,15 +135,14 @@ namespace ams::ro {
|
||||||
class NroHeader {
|
class NroHeader {
|
||||||
public:
|
public:
|
||||||
static constexpr u32 Magic = util::FourCC<'N','R','O','0'>::Code;
|
static constexpr u32 Magic = util::FourCC<'N','R','O','0'>::Code;
|
||||||
static constexpr u32 FlagAlignedHeader = 1;
|
|
||||||
private:
|
private:
|
||||||
u32 m_entrypoint_insn;
|
u32 m_entrypoint_insn;
|
||||||
u32 m_mod_offset;
|
u32 m_mod_offset;
|
||||||
u8 m_reserved_08[0x8];
|
u8 m_reserved_08[0x8];
|
||||||
u32 m_magic;
|
u32 m_magic;
|
||||||
u8 m_version;
|
u8 m_reserved_14[0x4];
|
||||||
u32 m_size;
|
u32 m_size;
|
||||||
u32 m_flags;
|
u8 m_reserved_1C[0x4];
|
||||||
u32 m_text_offset;
|
u32 m_text_offset;
|
||||||
u32 m_text_size;
|
u32 m_text_size;
|
||||||
u32 m_ro_offset;
|
u32 m_ro_offset;
|
||||||
|
@ -159,22 +158,10 @@ namespace ams::ro {
|
||||||
return m_magic == Magic;
|
return m_magic == Magic;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetVersion() const {
|
|
||||||
return m_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetSize() const {
|
u32 GetSize() const {
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetFlags() const {
|
|
||||||
return m_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsAlignedHeader() const {
|
|
||||||
return m_flags & FlagAlignedHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetTextOffset() const {
|
u32 GetTextOffset() const {
|
||||||
return m_text_offset;
|
return m_text_offset;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ namespace ams::os::impl {
|
||||||
case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read;
|
case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read;
|
||||||
case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite;
|
case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite;
|
||||||
case os::MemoryPermission_ReadExecute: return svc::MemoryPermission_ReadExecute;
|
case os::MemoryPermission_ReadExecute: return svc::MemoryPermission_ReadExecute;
|
||||||
case os::MemoryPermission_ExecuteOnly: return svc::MemoryPermission_Execute;
|
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -444,7 +444,6 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv:
|
||||||
ERET_WITH_SPECULATION_BARRIER
|
ERET_WITH_SPECULATION_BARRIER
|
||||||
|
|
||||||
2: /* The exception wasn't an triggered by copying memory from userspace. */
|
2: /* The exception wasn't an triggered by copying memory from userspace. */
|
||||||
/* NOTE: The following is, as of 19.0.0, now ifdef'd out on NX non-debug kernel. */
|
|
||||||
ldr x0, [sp, #8]
|
ldr x0, [sp, #8]
|
||||||
ldr x1, [sp, #16]
|
ldr x1, [sp, #16]
|
||||||
|
|
||||||
|
|
|
@ -76,9 +76,6 @@ _ZN3ams4kern4arch5arm6427SupervisorModeThreadStarterEv:
|
||||||
/* v */
|
/* v */
|
||||||
/* | u64 argument | u64 entrypoint | KThread::StackParameters (size 0x30) | */
|
/* | u64 argument | u64 entrypoint | KThread::StackParameters (size 0x30) | */
|
||||||
|
|
||||||
/* Clear the link register. */
|
|
||||||
mov x30, #0
|
|
||||||
|
|
||||||
/* Load the argument and entrypoint. */
|
/* Load the argument and entrypoint. */
|
||||||
ldp x0, x1, [sp], #0x10
|
ldp x0, x1, [sp], #0x10
|
||||||
|
|
||||||
|
@ -87,6 +84,4 @@ _ZN3ams4kern4arch5arm6427SupervisorModeThreadStarterEv:
|
||||||
|
|
||||||
/* Mask I bit in DAIF */
|
/* Mask I bit in DAIF */
|
||||||
msr daifclr, #2
|
msr daifclr, #2
|
||||||
|
br x1
|
||||||
/* Invoke the function (by calling ams::kern::arch::arm64::InvokeSupervisorModeThread(argument, entrypoint)). */
|
|
||||||
b _ZN3ams4kern4arch5arm6426InvokeSupervisorModeThreadEmm
|
|
||||||
|
|
|
@ -555,7 +555,7 @@ namespace ams::ldr {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size, bool prevent_code_reads) {
|
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
|
||||||
/* Map and read data from file. */
|
/* Map and read data from file. */
|
||||||
{
|
{
|
||||||
/* Map the process memory. */
|
/* Map the process memory. */
|
||||||
|
@ -594,7 +594,7 @@ namespace ams::ldr {
|
||||||
const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize);
|
const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize);
|
||||||
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
|
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
|
||||||
if (text_size) {
|
if (text_size) {
|
||||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, os::MemoryPermission_ReadExecute));
|
||||||
}
|
}
|
||||||
if (ro_size) {
|
if (ro_size) {
|
||||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||||
|
@ -606,7 +606,7 @@ namespace ams::ldr {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, bool prevent_code_reads) {
|
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
|
||||||
/* Load each NSO. */
|
/* Load each NSO. */
|
||||||
for (size_t i = 0; i < Nso_Count; i++) {
|
for (size_t i = 0; i < Nso_Count; i++) {
|
||||||
if (has_nso[i]) {
|
if (has_nso[i]) {
|
||||||
|
@ -614,7 +614,7 @@ namespace ams::ldr {
|
||||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
|
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
|
||||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||||
|
|
||||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i], prevent_code_reads));
|
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +658,7 @@ namespace ams::ldr {
|
||||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||||
|
|
||||||
/* Load all auto load modules. */
|
/* Load all auto load modules. */
|
||||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument, (meta->npdm->flags & ldr::Npdm::MetaFlag_PreventCodeReads) != 0));
|
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,15 +51,11 @@ namespace ams::ro::impl {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size, bool is_aligned_header) {
|
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) {
|
||||||
const u64 rx_offset = is_aligned_header ? os::MemoryPageSize : 0;
|
const u64 rx_offset = 0;
|
||||||
const u64 ro_offset = rx_offset + rx_size;
|
const u64 ro_offset = rx_offset + rx_size;
|
||||||
const u64 rw_offset = ro_offset + ro_size;
|
const u64 rw_offset = ro_offset + ro_size;
|
||||||
|
|
||||||
if (is_aligned_header) {
|
|
||||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address, os::MemoryPageSize, os::MemoryPermission_ReadOnly));
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute));
|
||||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace ams::ro::impl {
|
||||||
|
|
||||||
/* Utilities for working with NROs. */
|
/* Utilities for working with NROs. */
|
||||||
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
||||||
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size, bool is_aligned_header);
|
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
|
||||||
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
||||||
|
|
||||||
}
|
}
|
|
@ -247,7 +247,7 @@ namespace ams::ro::impl {
|
||||||
R_THROW(ro::ResultNotAuthorized());
|
R_THROW(ro::ResultNotAuthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, bool *out_aligned_header, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
|
Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
|
||||||
/* Map the NRO. */
|
/* Map the NRO. */
|
||||||
void *mapped_memory = nullptr;
|
void *mapped_memory = nullptr;
|
||||||
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size, ro::impl::GenerateSecureRandom)) {
|
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size, ro::impl::GenerateSecureRandom)) {
|
||||||
|
@ -306,7 +306,6 @@ namespace ams::ro::impl {
|
||||||
*out_rx_size = text_size;
|
*out_rx_size = text_size;
|
||||||
*out_ro_size = ro_size;
|
*out_ro_size = ro_size;
|
||||||
*out_rw_size = rw_size;
|
*out_rw_size = rw_size;
|
||||||
*out_aligned_header = header->IsAlignedHeader();
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,11 +557,10 @@ namespace ams::ro::impl {
|
||||||
|
|
||||||
/* Validate the NRO (parsing region extents). */
|
/* Validate the NRO (parsing region extents). */
|
||||||
u64 rx_size = 0, ro_size = 0, rw_size = 0;
|
u64 rx_size = 0, ro_size = 0, rw_size = 0;
|
||||||
bool aligned_header = false;
|
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size));
|
||||||
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), std::addressof(aligned_header), nro_info->base_address, nro_size, bss_size));
|
|
||||||
|
|
||||||
/* Set NRO perms. */
|
/* Set NRO perms. */
|
||||||
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size, aligned_header));
|
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size));
|
||||||
|
|
||||||
context->SetNroInfoInUse(nro_info, true);
|
context->SetNroInfoInUse(nro_info, true);
|
||||||
nro_info->code_size = rx_size + ro_size;
|
nro_info->code_size = rx_size + ro_size;
|
||||||
|
|
Loading…
Add table
Reference in a new issue