diff --git a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp index 2ab3b6090..911298199 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp @@ -34,8 +34,14 @@ namespace ams::kern { uintptr_t _08; }; + struct InitialProcessBinaryLayoutWithSize { + InitialProcessBinaryLayout layout; + size_t size; + }; + KPhysicalAddress GetInitialProcessBinaryPhysicalAddress(); - void SetInitialProcessBinaryPhysicalAddress(KPhysicalAddress phys_addr); + size_t GetInitialProcessBinarySize(); + void SetInitialProcessBinaryPhysicalAddress(KPhysicalAddress phys_addr, size_t size); u64 GetInitialProcessIdMin(); u64 GetInitialProcessIdMax(); diff --git a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp index 1d76900ee..b82e78f84 100644 --- a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp +++ b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp @@ -171,6 +171,9 @@ namespace ams::kern::init { const KMemoryRegion &slab_region = KMemoryLayout::GetSlabRegion(); KVirtualAddress address = slab_region.GetAddress(); + /* Clear the slab region. */ + std::memset(GetVoidPointer(address), 0, slab_region.GetSize()); + /* Initialize slab type array to be in sorted order. */ KSlabType slab_types[KSlabType_Count]; for (size_t i = 0; i < util::size(slab_types); i++) { slab_types[i] = static_cast(i); } diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index e435deee4..6b22a1784 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -27,6 +27,7 @@ namespace ams::kern { constinit KPhysicalAddress g_initial_process_binary_phys_addr = Null; constinit KVirtualAddress g_initial_process_binary_address = Null; + constinit size_t g_initial_process_binary_size = 0; constinit InitialProcessBinaryHeader g_initial_process_binary_header = {}; constinit size_t g_initial_process_secure_memory_size = 0; constinit u64 g_initial_process_id_min = std::numeric_limits::max(); @@ -275,10 +276,11 @@ namespace ams::kern { } - void SetInitialProcessBinaryPhysicalAddress(KPhysicalAddress phys_addr) { + void SetInitialProcessBinaryPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { MESOSPHERE_INIT_ABORT_UNLESS(g_initial_process_binary_phys_addr == Null); g_initial_process_binary_phys_addr = phys_addr; + g_initial_process_binary_size = size; } KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() { @@ -287,6 +289,12 @@ namespace ams::kern { return g_initial_process_binary_phys_addr; } + size_t GetInitialProcessBinarySize() { + MESOSPHERE_INIT_ABORT_UNLESS(g_initial_process_binary_phys_addr != Null); + + return g_initial_process_binary_size; + } + u64 GetInitialProcessIdMin() { return g_initial_process_id_min; } @@ -305,14 +313,17 @@ namespace ams::kern { LoadInitialProcessBinaryHeader(); if (g_initial_process_binary_header.num_processes > 0) { - /* Reserve pages for the initial process binary from the system resource limit. */ - const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); - MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size)); + /* Ensure that we have a non-zero size. */ + const size_t expected_size = g_initial_process_binary_size; + MESOSPHERE_INIT_ABORT_UNLESS(expected_size != 0); - /* The initial process binary is potentially over-allocated, so free any extra pages. */ - if (total_size < InitialProcessBinarySizeMax) { - Kernel::GetMemoryManager().Close(KMemoryLayout::GetLinearPhysicalAddress(g_initial_process_binary_address + total_size), (InitialProcessBinarySizeMax - total_size) / PageSize); - } + /* Ensure that the size we need to reserve is as we expect it to be. */ + const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); + MESOSPHERE_ABORT_UNLESS(total_size == expected_size); + MESOSPHERE_ABORT_UNLESS(total_size <= InitialProcessBinarySizeMax); + + /* Reserve pages for the initial process binary from the system resource limit. */ + MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size)); return total_size; } else { diff --git a/libraries/libmesosphere/source/kern_k_memory_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_manager.cpp index b9999be81..01dcd565a 100644 --- a/libraries/libmesosphere/source/kern_k_memory_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_manager.cpp @@ -108,7 +108,8 @@ namespace ams::kern { /* Free each region to its corresponding heap. */ size_t reserved_sizes[MaxManagerCount] = {}; const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress(); - const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax; + const size_t ini_size = GetInitialProcessBinarySize(); + const KPhysicalAddress ini_end = ini_start + ini_size; const KPhysicalAddress ini_last = ini_end - 1; for (const auto &it : KMemoryLayout::GetPhysicalMemoryRegionTree()) { if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { @@ -126,13 +127,13 @@ namespace ams::kern { } /* Open/reserve the ini memory. */ - manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); - reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; + manager.OpenFirst(ini_start, ini_size / PageSize); + reserved_sizes[it.GetAttributes()] += ini_size; /* Free memory after the ini to the heap. */ if (ini_last != cur_last) { MESOSPHERE_ABORT_UNLESS(cur_end != Null); - manager.Free(ini_end, cur_end - ini_end); + manager.Free(ini_end, (cur_end - ini_end) / PageSize); } } else { /* Ensure there's no partial overlap with the ini image. */ diff --git a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp index 240faf484..c8139fb49 100644 --- a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp +++ b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp @@ -45,7 +45,7 @@ namespace ams::kern::init { constinit KInitArguments g_init_arguments[cpu::NumCores]; /* Globals for passing data between InitializeCorePhase1 and InitializeCorePhase2. */ - constinit InitialProcessBinaryLayout g_phase2_initial_process_binary_layout{}; + constinit InitialProcessBinaryLayoutWithSize g_phase2_initial_process_binary_meta{}; constinit KPhysicalAddress g_phase2_resource_end_phys_addr = Null; constinit u64 g_phase2_linear_region_phys_to_virt_diff = 0; @@ -246,7 +246,7 @@ namespace ams::kern::init { /* Decode the initial state. */ const auto initial_page_allocator_state = *static_cast(initial_state[0]); - g_phase2_initial_process_binary_layout = *static_cast(initial_state[1]); + g_phase2_initial_process_binary_meta = *static_cast(initial_state[1]); /* Restore the page allocator state setup by kernel loader. */ g_initial_page_allocator.InitializeFromState(std::addressof(initial_page_allocator_state)); @@ -565,9 +565,6 @@ namespace ams::kern::init { } } - /* Clear the slab region. */ - std::memset(GetVoidPointer(slab_region_start), 0, slab_region_size); - /* NOTE: Unknown function is called here which is ifdef'd out on retail kernel. */ /* The unknown function is immediately before the function which gets an unknown debug region size, inside this translation unit. */ /* It's likely that this is some kind of initializer for this unknown debug region. */ @@ -624,9 +621,10 @@ namespace ams::kern::init { /* Set the initial process binary physical address. */ /* NOTE: Nintendo does this after pool partition setup, but it's a requirement that we do it before */ /* to retain compatibility with < 5.0.0. */ - const KPhysicalAddress ini_address = g_phase2_initial_process_binary_layout.address; + const KPhysicalAddress ini_address = g_phase2_initial_process_binary_meta.layout.address; + const size_t ini_size = g_phase2_initial_process_binary_meta.size; MESOSPHERE_INIT_ABORT_UNLESS(ini_address != Null); - SetInitialProcessBinaryPhysicalAddress(ini_address); + SetInitialProcessBinaryPhysicalAddress(ini_address, ini_size); /* Setup all other memory regions needed to arrange the pool partitions. */ SetupPoolPartitionMemoryRegions(); @@ -640,7 +638,7 @@ namespace ams::kern::init { /* Check that the region contains the ini. */ MESOSPHERE_INIT_ABORT_UNLESS(ini_region->GetAddress() <= GetInteger(ini_address)); - MESOSPHERE_INIT_ABORT_UNLESS(GetInteger(ini_address) + InitialProcessBinarySizeMax <= ini_region->GetEndAddress()); + MESOSPHERE_INIT_ABORT_UNLESS(GetInteger(ini_address) + ini_size <= ini_region->GetEndAddress()); MESOSPHERE_INIT_ABORT_UNLESS(ini_region->GetEndAddress() != 0); } diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp index 70e26866c..0fceac933 100644 --- a/mesosphere/kernel_ldr/source/kern_init_loader.cpp +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -45,7 +45,7 @@ namespace ams::kern::init::loader { constinit KInitialPageAllocator g_initial_page_allocator; constinit KInitialPageAllocator::State g_final_page_allocator_state; - constinit InitialProcessBinaryLayout g_initial_process_binary_layout; + constinit InitialProcessBinaryLayoutWithSize g_initial_process_binary_meta; constinit void *g_final_state[2]; @@ -165,19 +165,31 @@ namespace ams::kern::init::loader { const uintptr_t resource_end_address = base_address + resource_offset + resource_region_size; /* Setup the INI1 header in memory for the kernel. */ - KSystemControl::Init::GetInitialProcessBinaryLayout(std::addressof(g_initial_process_binary_layout)); - MESOSPHERE_INIT_ABORT_UNLESS(g_initial_process_binary_layout.address != 0); + { + /* Get the kernel layout. */ + KSystemControl::Init::GetInitialProcessBinaryLayout(std::addressof(g_initial_process_binary_meta.layout)); - if (ini_base_address != g_initial_process_binary_layout.address) { - /* The INI is not at the correct address, so we need to relocate it. */ + /* If there's no desired base address, use the ini in place. */ + if (g_initial_process_binary_meta.layout.address == 0) { + g_initial_process_binary_meta.layout.address = ini_base_address; + } + + + /* Validate and potentially relocate the INI. */ const InitialProcessBinaryHeader *ini_header = reinterpret_cast(ini_base_address); - if (ini_header->magic == InitialProcessBinaryMagic && ini_header->size <= InitialProcessBinarySizeMax) { - /* INI is valid, relocate it. */ - std::memmove(reinterpret_cast(g_initial_process_binary_layout.address), ini_header, ini_header->size); + size_t ini_size = 0; + if (ini_header->magic == InitialProcessBinaryMagic && (ini_size = ini_header->size) <= InitialProcessBinarySizeMax) { + /* INI is valid, relocate it if necessary. */ + if (ini_base_address != g_initial_process_binary_meta.layout.address) { + std::memmove(reinterpret_cast(g_initial_process_binary_meta.layout.address), ini_header, ini_size); + } } else { /* INI is invalid. Make the destination header invalid. */ - std::memset(reinterpret_cast(g_initial_process_binary_layout.address), 0, sizeof(InitialProcessBinaryHeader)); + std::memset(reinterpret_cast(g_initial_process_binary_meta.layout.address), 0, sizeof(InitialProcessBinaryHeader)); } + + /* Set the INI size in layout. */ + g_initial_process_binary_meta.size = util::AlignUp(ini_size, PageSize); } /* We want to start allocating page tables at the end of the resource region. */ @@ -238,7 +250,7 @@ namespace ams::kern::init::loader { /* Setup final kernel loader state. */ g_final_state[0] = std::addressof(g_final_page_allocator_state); - g_final_state[1] = std::addressof(g_initial_process_binary_layout); + g_final_state[1] = std::addressof(g_initial_process_binary_meta); return g_final_state; }