diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 680b10dee7..936ce3f137 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -135,10 +135,10 @@ public: std::shared_ptr CreateProcess(std::shared_ptr code_set); /** - * Removes a process from the kernel process list - * @param process Process to remove + * Terminates a process, killing its threads and removing it from the process list. + * @param process Process to terminate. */ - void RemoveProcess(std::shared_ptr process); + void TerminateProcess(std::shared_ptr process); /** * Creates and returns a new thread. The new thread is immediately scheduled @@ -208,7 +208,7 @@ public: * @param name Optional object name, used for debugging purposes. */ ResultVal> CreateSharedMemory( - Process* owner_process, u32 size, MemoryPermission permissions, + std::shared_ptr owner_process, u32 size, MemoryPermission permissions, MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown"); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 7fb5c6aa22..1027c0e123 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -79,7 +79,18 @@ std::shared_ptr KernelSystem::CreateProcess(std::shared_ptr co return process; } -void KernelSystem::RemoveProcess(std::shared_ptr process) { +void KernelSystem::TerminateProcess(std::shared_ptr process) { + LOG_INFO(Kernel_SVC, "Process {} exiting", process->process_id); + + ASSERT_MSG(process->status == ProcessStatus::Running, "Process has already exited"); + process->status = ProcessStatus::Exited; + + // Stop all process threads. + for (u32 core = 0; core < Core::GetNumCores(); core++) { + GetThreadManager(core).TerminateProcessThreads(process); + } + + process->Exit(); std::erase(process_list, process); } @@ -268,6 +279,7 @@ ResultVal Process::HeapAllocate(VAddr target, u32 size, VMAPermission per interval_target += interval_size; } + holding_memory += allocated_fcram; memory_used += size; resource_limit->current_commit += size; @@ -288,13 +300,14 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { // Free heaps block by block CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(target, size)); - for (const auto& [backing_memory, block_size] : backing_blocks) { - memory_region->Free(kernel.memory.GetFCRAMOffset(backing_memory.GetPtr()), block_size); + for (const auto& backing_block : backing_blocks) { + memory_region->Free(backing_block.lower(), backing_block.upper() - backing_block.lower()); } ResultCode result = vm_manager.UnmapRange(target, size); ASSERT(result.IsSuccess()); + holding_memory -= backing_blocks; memory_used -= size; resource_limit->current_commit -= size; @@ -340,6 +353,7 @@ ResultVal Process::LinearAllocate(VAddr target, u32 size, VMAPermission p ASSERT(vma.Succeeded()); vm_manager.Reprotect(vma.Unwrap(), perms); + holding_memory += MemoryRegionInfo::Interval(physical_offset, physical_offset + size); memory_used += size; resource_limit->current_commit += size; @@ -365,15 +379,86 @@ ResultCode Process::LinearFree(VAddr target, u32 size) { return result; } - memory_used -= size; - resource_limit->current_commit -= size; - u32 physical_offset = target - GetLinearHeapAreaAddress(); // relative to FCRAM memory_region->Free(physical_offset, size); + holding_memory -= MemoryRegionInfo::Interval(physical_offset, physical_offset + size); + memory_used -= size; + resource_limit->current_commit -= size; + return RESULT_SUCCESS; } +ResultVal Process::AllocateThreadLocalStorage() { + std::size_t tls_page; + std::size_t tls_slot; + bool needs_allocation = true; + + // Iterate over all the allocated pages, and try to find one where not all slots are used. + for (tls_page = 0; tls_page < tls_slots.size(); ++tls_page) { + const auto& page_tls_slots = tls_slots[tls_page]; + if (!page_tls_slots.all()) { + // We found a page with at least one free slot, find which slot it is. + for (tls_slot = 0; tls_slot < page_tls_slots.size(); ++tls_slot) { + if (!page_tls_slots.test(tls_slot)) { + needs_allocation = false; + break; + } + } + + if (!needs_allocation) { + break; + } + } + } + + if (needs_allocation) { + tls_page = tls_slots.size(); + tls_slot = 0; + + LOG_DEBUG(Kernel, "Allocating new TLS page in slot {}", tls_page); + + // There are no already-allocated pages with free slots, lets allocate a new one. + // TLS pages are allocated from the BASE region in the linear heap. + auto base_memory_region = kernel.GetMemoryRegion(MemoryRegion::BASE); + + // Allocate some memory from the end of the linear heap for this region. + auto offset = base_memory_region->LinearAllocate(Memory::CITRA_PAGE_SIZE); + if (!offset) { + LOG_ERROR(Kernel_SVC, + "Not enough space in BASE linear region to allocate a new TLS page"); + return ERR_OUT_OF_MEMORY; + } + + holding_tls_memory += + MemoryRegionInfo::Interval(*offset, *offset + Memory::CITRA_PAGE_SIZE); + memory_used += Memory::CITRA_PAGE_SIZE; + + // The page is completely available at the start. + tls_slots.emplace_back(0); + + // Map the page to the current process' address space. + auto tls_page_addr = + Memory::TLS_AREA_VADDR + static_cast(tls_page) * Memory::CITRA_PAGE_SIZE; + vm_manager.MapBackingMemory(tls_page_addr, kernel.memory.GetFCRAMRef(*offset), + Memory::CITRA_PAGE_SIZE, MemoryState::Locked); + + LOG_DEBUG(Kernel, "Allocated TLS page at addr={:08X}", tls_page_addr); + } else { + LOG_DEBUG(Kernel, "Allocating TLS in existing page slot {}", tls_page); + } + + // Mark the slot as used + tls_slots[tls_page].set(tls_slot); + + auto tls_address = Memory::TLS_AREA_VADDR + + static_cast(tls_page) * Memory::CITRA_PAGE_SIZE + + static_cast(tls_slot) * Memory::TLS_ENTRY_SIZE; + kernel.memory.ZeroBlock(*this, tls_address, Memory::TLS_ENTRY_SIZE); + + return tls_address; +} + ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged) { LOG_DEBUG(Kernel, "Map memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", target, @@ -419,7 +504,9 @@ ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perm CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size)); VAddr interval_target = target; - for (const auto& [backing_memory, block_size] : backing_blocks) { + for (const auto& backing_block : backing_blocks) { + auto backing_memory = kernel.memory.GetFCRAMRef(backing_block.lower()); + auto block_size = backing_block.upper() - backing_block.lower(); auto target_vma = vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, target_state); ASSERT(target_vma.Succeeded()); @@ -471,6 +558,42 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe return RESULT_SUCCESS; } +void Process::FreeAllMemory() { + if (memory_region == nullptr || resource_limit == nullptr) { + return; + } + + // Free any heap/linear memory allocations. + for (auto& entry : holding_memory) { + LOG_DEBUG(Kernel, "Freeing process memory region 0x{:08X} - 0x{:08X}", entry.lower(), + entry.upper()); + auto size = entry.upper() - entry.lower(); + memory_region->Free(entry.lower(), size); + memory_used -= size; + resource_limit->current_commit -= size; + } + holding_memory.clear(); + + // Free any TLS memory allocations. + auto base_memory_region = kernel.GetMemoryRegion(MemoryRegion::BASE); + for (auto& entry : holding_tls_memory) { + LOG_DEBUG(Kernel, "Freeing process TLS memory region 0x{:08X} - 0x{:08X}", entry.lower(), + entry.upper()); + auto size = entry.upper() - entry.lower(); + base_memory_region->Free(entry.lower(), size); + memory_used -= size; + } + holding_tls_memory.clear(); + tls_slots.clear(); + + // Diagnostics for debugging. + // TODO: The way certain non-application shared memory is allocated can result in very slight + // leaks in these values still. + LOG_DEBUG(Kernel, "Remaining memory used after process cleanup: 0x{:08X}", memory_used); + LOG_DEBUG(Kernel, "Remaining memory resource commit after process cleanup: 0x{:08X}", + resource_limit->current_commit); +} + Kernel::Process::Process(KernelSystem& kernel) : Object(kernel), handle_table(kernel), vm_manager(kernel.memory, *this), kernel(kernel) { kernel.memory.RegisterPageTable(vm_manager.page_table); @@ -484,6 +607,7 @@ Kernel::Process::~Process() { // memory etc.) even if they are still referenced by other processes. handle_table.Clear(); + FreeAllMemory(); kernel.memory.UnregisterPageTable(vm_manager.page_table); } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index e41eac3094..1c55cce82c 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -217,6 +217,8 @@ public: u32 memory_used = 0; std::shared_ptr memory_region = nullptr; + MemoryRegionInfo::IntervalSet holding_memory; + MemoryRegionInfo::IntervalSet holding_tls_memory; /// The Thread Local Storage area is allocated as processes create threads, /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part @@ -237,12 +239,16 @@ public: ResultVal LinearAllocate(VAddr target, u32 size, VMAPermission perms); ResultCode LinearFree(VAddr target, u32 size); + ResultVal AllocateThreadLocalStorage(); + ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged = false); ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged = false); private: + void FreeAllMemory(); + KernelSystem& kernel; friend class boost::serialization::access; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 73a7959828..a28590ad57 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include "common/archives.h" #include "common/logging/log.h" #include "core/hle/kernel/errors.h" @@ -20,15 +19,21 @@ SharedMemory::~SharedMemory() { kernel.GetMemoryRegion(MemoryRegion::SYSTEM) ->Free(interval.lower(), interval.upper() - interval.lower()); } - if (base_address != 0 && owner_process != nullptr) { - owner_process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked, - VMAPermission::None, MemoryState::Private, - VMAPermission::ReadWrite); + + auto process = owner_process.lock(); + if (process) { + if (base_address != 0) { + process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked, + VMAPermission::None, MemoryState::Private, + VMAPermission::ReadWrite); + } else { + process->memory_used -= size; + } } } ResultVal> KernelSystem::CreateSharedMemory( - Process* owner_process, u32 size, MemoryPermission permissions, + std::shared_ptr owner_process, u32 size, MemoryPermission permissions, MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) { auto shared_memory{std::make_shared(*this)}; @@ -52,11 +57,11 @@ ResultVal> KernelSystem::CreateSharedMemory( shared_memory->linear_heap_phys_offset = *offset; // Increase the amount of used linear heap memory for the owner process. - if (shared_memory->owner_process != nullptr) { - shared_memory->owner_process->memory_used += size; + if (owner_process != nullptr) { + owner_process->memory_used += size; } } else { - auto& vm_manager = shared_memory->owner_process->vm_manager; + auto& vm_manager = owner_process->vm_manager; // The memory is already available and mapped in the owner process. CASCADE_CODE(vm_manager.ChangeMemoryState(address, size, MemoryState::Private, @@ -65,7 +70,10 @@ ResultVal> KernelSystem::CreateSharedMemory( auto backing_blocks = vm_manager.GetBackingBlocksForRange(address, size); ASSERT(backing_blocks.Succeeded()); // should success after verifying memory state above - shared_memory->backing_blocks = std::move(backing_blocks).Unwrap(); + for (const auto& interval : backing_blocks.Unwrap()) { + shared_memory->backing_blocks.emplace_back(memory.GetFCRAMRef(interval.lower()), + interval.upper() - interval.lower()); + } } shared_memory->base_address = address; @@ -82,14 +90,14 @@ std::shared_ptr KernelSystem::CreateSharedMemoryForApplet( auto backing_blocks = memory_region->HeapAllocate(size); ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!"); shared_memory->holding_memory = backing_blocks; - shared_memory->owner_process = nullptr; + shared_memory->owner_process = std::weak_ptr(); shared_memory->name = std::move(name); shared_memory->size = size; shared_memory->permissions = permissions; shared_memory->other_permissions = other_permissions; for (const auto& interval : backing_blocks) { - shared_memory->backing_blocks.push_back( - {memory.GetFCRAMRef(interval.lower()), interval.upper() - interval.lower()}); + shared_memory->backing_blocks.emplace_back(memory.GetFCRAMRef(interval.lower()), + interval.upper() - interval.lower()); std::fill(memory.GetFCRAMPointer(interval.lower()), memory.GetFCRAMPointer(interval.upper()), 0); } @@ -102,7 +110,7 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi MemoryPermission other_permissions) { MemoryPermission own_other_permissions = - &target_process == owner_process ? this->permissions : this->other_permissions; + &target_process == owner_process.lock().get() ? this->permissions : this->other_permissions; // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index dfcbcd6710..d5cec7678b 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/common_types.h" #include "common/memory_ref.h" #include "core/hle/kernel/object.h" @@ -98,7 +99,7 @@ private: /// Permission restrictions applied to other processes mapping the block. MemoryPermission other_permissions{}; /// Process that created this shared memory block. - Process* owner_process; + std::weak_ptr owner_process; /// Address of shared memory block in the owner process if specified. VAddr base_address = 0; /// Name of shared memory object. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index ab7c663bc4..385e64aac7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -369,6 +369,7 @@ private: ResultCode ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32 operation, u32 permissions); void ExitProcess(); + ResultCode TerminateProcess(Handle handle); ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions); ResultCode UnmapMemoryBlock(Handle handle, u32 addr); ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address); @@ -535,41 +536,18 @@ ResultCode SVC::ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32 } void SVC::ExitProcess() { - std::shared_ptr current_process = kernel.GetCurrentProcess(); - LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->process_id); + kernel.TerminateProcess(kernel.GetCurrentProcess()); +} - ASSERT_MSG(current_process->status == ProcessStatus::Running, "Process has already exited"); - - current_process->status = ProcessStatus::Exited; - - // Stop all the process threads that are currently waiting for objects. - const auto thread_list = kernel.GetCurrentThreadManager().GetThreadList(); - for (auto& thread : thread_list) { - if (thread->owner_process.lock() != current_process) { - continue; - } - - if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread()) { - continue; - } - - // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || - thread->status == ThreadStatus::WaitSynchAll, - "Exiting processes with non-waiting threads is currently unimplemented"); - - thread->Stop(); +ResultCode SVC::TerminateProcess(Handle handle) { + std::shared_ptr process = + kernel.GetCurrentProcess()->handle_table.Get(handle); + if (process == nullptr) { + return ERR_INVALID_HANDLE; } - current_process->Exit(); - - // Kill the current thread - kernel.GetCurrentThreadManager().GetCurrentThread()->Stop(); - - // Remove kernel reference to process so it can be cleaned up. - kernel.RemoveProcess(current_process); - - system.PrepareReschedule(); + kernel.TerminateProcess(process); + return RESULT_SUCCESS; } /// Maps a memory block to specified address @@ -1690,7 +1668,7 @@ ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my CASCADE_RESULT(shared_memory, kernel.CreateSharedMemory( - current_process.get(), size, static_cast(my_permission), + current_process, size, static_cast(my_permission), static_cast(other_permission), addr, region)); CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(shared_memory))); @@ -2244,7 +2222,7 @@ const std::array SVC::SVC_Table{{ {0x73, nullptr, "CreateCodeSet"}, {0x74, nullptr, "RandomStub"}, {0x75, nullptr, "CreateProcess"}, - {0x76, nullptr, "TerminateProcess"}, + {0x76, &SVC::Wrap<&SVC::TerminateProcess>, "TerminateProcess"}, {0x77, nullptr, "SetProcessResourceLimits"}, {0x78, nullptr, "CreateResourceLimit"}, {0x79, nullptr, "SetResourceLimitValues"}, diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 10007849aa..5c2c44b4ad 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -199,11 +199,34 @@ void ThreadManager::WaitCurrentThread_Sleep() { } void ThreadManager::ExitCurrentThread() { - Thread* thread = GetCurrentThread(); - thread->Stop(); - thread_list.erase(std::remove_if(thread_list.begin(), thread_list.end(), - [thread](const auto& p) { return p.get() == thread; }), - thread_list.end()); + current_thread->Stop(); + std::erase(thread_list, current_thread); + kernel.PrepareReschedule(); +} + +void ThreadManager::TerminateProcessThreads(std::shared_ptr process) { + auto iter = thread_list.begin(); + while (iter != thread_list.end()) { + auto& thread = *iter; + if (thread == current_thread || thread->owner_process.lock() != process) { + iter++; + continue; + } + + if (thread->status != ThreadStatus::WaitSynchAny && + thread->status != ThreadStatus::WaitSynchAll) { + // TODO: How does the real kernel handle non-waiting threads? + LOG_WARNING(Kernel, "Terminating non-waiting thread {}", thread->thread_id); + } + + thread->Stop(); + iter = thread_list.erase(iter); + } + + // Kill the current thread last, if applicable. + if (current_thread != nullptr && current_thread->owner_process.lock() == process) { + ExitCurrentThread(); + } } void ThreadManager::ThreadWakeupCallback(u64 thread_id, s64 cycles_late) { @@ -295,32 +318,6 @@ void ThreadManager::DebugThreadQueue() { } } -/** - * Finds a free location for the TLS section of a thread. - * @param tls_slots The TLS page array of the thread's owner process. - * Returns a tuple of (page, slot, alloc_needed) where: - * page: The index of the first allocated TLS page that has free slots. - * slot: The index of the first free slot in the indicated page. - * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). - */ -static std::tuple GetFreeThreadLocalSlot( - std::span> tls_slots) { - // Iterate over all the allocated pages, and try to find one where not all slots are used. - for (std::size_t page = 0; page < tls_slots.size(); ++page) { - const auto& page_tls_slots = tls_slots[page]; - if (!page_tls_slots.all()) { - // We found a page with at least one free slot, find which slot it is - for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { - if (!page_tls_slots.test(slot)) { - return std::make_tuple(page, slot, false); - } - } - } - } - - return std::make_tuple(0, 0, true); -} - /** * Resets a thread context, making it ready to be scheduled and run by the CPU * @param context Thread context to reset @@ -376,45 +373,7 @@ ResultVal> KernelSystem::CreateThread( thread->name = std::move(name); thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get(); thread->owner_process = owner_process; - - // Find the next available TLS index, and mark it as used - auto& tls_slots = owner_process->tls_slots; - - auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots); - - if (needs_allocation) { - // There are no already-allocated pages with free slots, lets allocate a new one. - // TLS pages are allocated from the BASE region in the linear heap. - auto memory_region = GetMemoryRegion(MemoryRegion::BASE); - - // Allocate some memory from the end of the linear heap for this region. - auto offset = memory_region->LinearAllocate(Memory::CITRA_PAGE_SIZE); - if (!offset) { - LOG_ERROR(Kernel_SVC, - "Not enough space in region to allocate a new TLS page for thread"); - return ERR_OUT_OF_MEMORY; - } - owner_process->memory_used += Memory::CITRA_PAGE_SIZE; - - tls_slots.emplace_back(0); // The page is completely available at the start - available_page = tls_slots.size() - 1; - available_slot = 0; // Use the first slot in the new page - - auto& vm_manager = owner_process->vm_manager; - - // Map the page to the current process' address space. - vm_manager.MapBackingMemory( - Memory::TLS_AREA_VADDR + static_cast(available_page) * Memory::CITRA_PAGE_SIZE, - memory.GetFCRAMRef(*offset), Memory::CITRA_PAGE_SIZE, MemoryState::Locked); - } - - // Mark the slot as used - tls_slots[available_page].set(available_slot); - thread->tls_address = Memory::TLS_AREA_VADDR + - static_cast(available_page) * Memory::CITRA_PAGE_SIZE + - static_cast(available_slot) * Memory::TLS_ENTRY_SIZE; - - memory.ZeroBlock(*owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE); + CASCADE_RESULT(thread->tls_address, owner_process->AllocateThreadLocalStorage()); // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 71cb978b8c..543b8fa3c1 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -111,6 +111,11 @@ public: */ void ExitCurrentThread(); + /** + * Terminates all threads belonging to a specific process. + */ + void TerminateProcessThreads(std::shared_ptr process); + /** * Get a const reference to the thread list for debug use */ diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index f2c798750d..f187b92bf9 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -391,9 +391,9 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { plgldr->OnMemoryChanged(process, Core::System::GetInstance().Kernel()); } -ResultVal>> VMManager::GetBackingBlocksForRange(VAddr address, - u32 size) { - std::vector> backing_blocks; +ResultVal VMManager::GetBackingBlocksForRange(VAddr address, + u32 size) { + MemoryRegionInfo::IntervalSet backing_blocks; VAddr interval_target = address; while (interval_target != address + size) { auto vma = FindVMA(interval_target); @@ -404,8 +404,10 @@ ResultVal>> VMManager::GetBackingBlocksFor VAddr interval_end = std::min(address + size, vma->second.base + vma->second.size); u32 interval_size = interval_end - interval_target; - auto backing_memory = vma->second.backing_memory + (interval_target - vma->second.base); - backing_blocks.push_back({backing_memory, interval_size}); + auto backing_memory = memory.GetFCRAMOffset(vma->second.backing_memory + + (interval_target - vma->second.base)); + backing_blocks += + MemoryRegionInfo::Interval(backing_memory, backing_memory + interval_size); interval_target += interval_size; } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 87ecbc0375..ea0e14b2e0 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -205,8 +205,7 @@ public: void LogLayout(Common::Log::Level log_level) const; /// Gets a list of backing memory blocks for the specified range - ResultVal>> GetBackingBlocksForRange(VAddr address, - u32 size); + ResultVal GetBackingBlocksForRange(VAddr address, u32 size); /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 6a10d70169..9d00973f9f 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -988,13 +988,18 @@ ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType // Save the title data to send it to the Home Menu when DoApplicationJump is called. auto application_slot_data = GetAppletSlot(AppletSlot::Application); app_jump_parameters.current_title_id = application_slot_data->title_id; - // TODO(Subv): Retrieve the correct media type of the currently-running application. For now - // just assume NAND. - app_jump_parameters.current_media_type = FS::MediaType::NAND; - app_jump_parameters.next_title_id = flags == ApplicationJumpFlags::UseCurrentParameters - ? application_slot_data->title_id - : title_id; - app_jump_parameters.next_media_type = media_type; + // TODO: Basic heuristic to guess media type, needs proper implementation. + app_jump_parameters.current_media_type = + ((application_slot_data->title_id >> 32) & 0xFFFFFFFF) == 0x00040000 + ? Service::FS::MediaType::SDMC + : Service::FS::MediaType::NAND; + if (flags == ApplicationJumpFlags::UseCurrentParameters) { + app_jump_parameters.next_title_id = app_jump_parameters.current_title_id; + app_jump_parameters.next_media_type = app_jump_parameters.current_media_type; + } else { + app_jump_parameters.next_title_id = title_id; + app_jump_parameters.next_media_type = media_type; + } app_jump_parameters.flags = flags; // Note: The real console uses the Home Menu to perform the application jump, therefore the menu @@ -1020,45 +1025,51 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) { deliver_arg->source_program_id = title_id; } - // TODO(Subv): Terminate the current Application. + if (GetAppletSlot(AppletSlot::HomeMenu)->registered) { + // If the home menu is running, use it to jump to the next application. + // The home menu will call GetProgramIdOnApplicationJump and + // PrepareToStartApplication/StartApplication to launch the title. + active_slot = AppletSlot::HomeMenu; + SendParameter({ + .sender_id = AppletId::Application, + .destination_id = AppletId::HomeMenu, + .signal = SignalType::WakeupToLaunchApplication, + }); - // Note: The real console sends signal 17 (WakeupToLaunchApplication) to the Home Menu, this - // prompts it to call GetProgramIdOnApplicationJump and - // PrepareToStartApplication/StartApplication on the title to launch. - active_slot = AppletSlot::Application; + // TODO: APT terminates the application here, usually it will exit itself properly though. + return RESULT_SUCCESS; + } else { + // Otherwise, work around the missing home menu by launching the title directly. - // Perform a soft-reset if we're trying to relaunch the same title. - // TODO(Subv): Note that this reboots the entire emulated system, a better way would be to - // simply re-launch the title without closing all services, but this would only work for - // installed titles since we have no way of getting the file path of an arbitrary game dump - // based only on the title id. + // TODO: The emulator does not support terminating the old process immediately. + // We could call TerminateProcess but references to the process are still held elsewhere, + // preventing clean up. This code is left commented for when this is implemented, for now we + // cannot use NS as the old process resources would interfere with the new ones. + /* + auto process = + NS::LaunchTitle(app_jump_parameters.next_media_type, app_jump_parameters.next_title_id); + if (!process) { + LOG_CRITICAL(Service_APT, "Failed to launch title during application jump, exiting."); + system.RequestShutdown(); + } + return RESULT_SUCCESS; + */ - auto new_path = Service::AM::GetTitleContentPath(app_jump_parameters.next_media_type, - app_jump_parameters.next_title_id); - if (new_path.empty() || !FileUtil::Exists(new_path)) { - LOG_CRITICAL( - Service_APT, - "Failed to find title during application jump: {} Resetting current title instead.", - new_path); - new_path.clear(); + auto new_path = Service::AM::GetTitleContentPath(app_jump_parameters.next_media_type, + app_jump_parameters.next_title_id); + if (new_path.empty() || !FileUtil::Exists(new_path)) { + // TODO: This can happen if the requested title is not installed. Need a way to find + // non-installed titles in the game list. + LOG_CRITICAL( + Service_APT, + "Failed to find title during application jump: {} Resetting current title instead.", + new_path); + new_path.clear(); + } + + system.RequestReset(new_path); + return RESULT_SUCCESS; } - - system.RequestReset(new_path); - return RESULT_SUCCESS; - - // Launch the title directly. - // The emulator does not suport terminating old processes, would require a lot of cleanup - // This code is left commented for when this is implemented, for now we cannot use NS - // as the old process resources would interfere with the new ones - /* - auto process = - NS::LaunchTitle(app_jump_parameters.next_media_type, app_jump_parameters.next_title_id); - if (!process) { - LOG_CRITICAL(Service_APT, "Failed to launch title during application jump, exiting."); - system.RequestShutdown(); - } - return RESULT_SUCCESS; - */ } ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) {