diff options
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r-- | src/core/hle/kernel/object.h | 3 | ||||
-rw-r--r-- | src/core/hle/kernel/process.cpp | 105 | ||||
-rw-r--r-- | src/core/hle/kernel/process.h | 67 | ||||
-rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 14 | ||||
-rw-r--r-- | src/core/hle/kernel/scheduler.h | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 91 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 61 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 13 | ||||
-rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 180 | ||||
-rw-r--r-- | src/core/hle/kernel/vm_manager.h | 100 | ||||
-rw-r--r-- | src/core/hle/kernel/wait_object.h | 2 |
12 files changed, 477 insertions, 167 deletions
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index b054cbf7d..9eb72315c 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -6,7 +6,6 @@ #include <atomic> #include <string> -#include <utility> #include <boost/smart_ptr/intrusive_ptr.hpp> @@ -97,7 +96,7 @@ using SharedPtr = boost::intrusive_ptr<T>; template <typename T> inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { - return boost::static_pointer_cast<T>(std::move(object)); + return boost::static_pointer_cast<T>(object); } return nullptr; } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 914bbe0a1..a8e3098ca 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -7,10 +7,13 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/program_metadata.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" @@ -32,14 +35,21 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { process->name = std::move(name); process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); + process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = kernel.CreateNewProcessID(); + process->svc_access_mask.set(); kernel.AppendNewProcess(process); return process; } +void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { + program_id = metadata.GetTitleID(); + vm_manager.Reset(metadata.GetAddressSpaceType()); +} + void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { for (std::size_t i = 0; i < len; ++i) { u32 descriptor = kernel_caps[i]; @@ -117,7 +127,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part // of the user address space. vm_manager - .MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size, + .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Mapped) .Unwrap(); @@ -128,6 +138,91 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this); } +void Process::PrepareForTermination() { + status = ProcessStatus::Exited; + + const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { + for (auto& thread : thread_list) { + if (thread->owner_process != this) + continue; + + if (thread == 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(); + } + }; + + auto& system = Core::System::GetInstance(); + stop_threads(system.Scheduler(0)->GetThreadList()); + stop_threads(system.Scheduler(1)->GetThreadList()); + stop_threads(system.Scheduler(2)->GetThreadList()); + stop_threads(system.Scheduler(3)->GetThreadList()); +} + +/** + * 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<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( + const std::vector<std::bitset<8>>& 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); +} + +VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { + auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); + const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress(); + + if (needs_allocation) { + 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 + + // Allocate some memory from the end of the linear heap for this region. + auto& tls_memory = thread.GetTLSMemory(); + tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0); + + vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); + + vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, + Memory::PAGE_SIZE, MemoryState::ThreadLocal); + } + + tls_slots[available_page].set(available_slot); + + return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; +} + +void Process::FreeTLSSlot(VAddr tls_address) { + const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); + const VAddr tls_page = tls_base / Memory::PAGE_SIZE; + const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; + + tls_slots[tls_page].reset(tls_slot); +} + void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { @@ -145,8 +240,8 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { - if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || - target + size < target) { + if (target < vm_manager.GetHeapRegionBaseAddress() || + target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { return ERR_INVALID_ADDRESS; } @@ -181,8 +276,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per } ResultCode Process::HeapFree(VAddr target, u32 size) { - if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || - target + size < target) { + if (target < vm_manager.GetHeapRegionBaseAddress() || + target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { return ERR_INVALID_ADDRESS; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 81538f70c..adb03c228 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -17,6 +17,10 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" +namespace FileSys { +class ProgramMetadata; +} + namespace Kernel { class KernelCore; @@ -131,6 +135,24 @@ public: return HANDLE_TYPE; } + /// Gets the current status of the process + ProcessStatus GetStatus() const { + return status; + } + + /// Gets the unique ID that identifies this particular process. + u32 GetProcessID() const { + return process_id; + } + + /** + * Loads process-specifics configuration info with metadata provided + * by an executable. + * + * @param metadata The provided metadata to load process specific info. + */ + void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); + /// Title ID corresponding to the process u64 program_id; @@ -154,11 +176,6 @@ public: u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; u32 allowed_thread_priority_mask = 0xFFFFFFFF; u32 is_virtual_address_memory_enabled = 0; - /// Current status of the process - ProcessStatus status; - - /// The ID of this process - u32 process_id = 0; /** * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them @@ -171,13 +188,42 @@ public: */ void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); + /** + * Prepares a process for termination by stopping all of its threads + * and clearing any other resources. + */ + void PrepareForTermination(); + void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); /////////////////////////////////////////////////////////////////////////////////////////////// // Memory Management + // Marks the next available region as used and returns the address of the slot. + VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread); + + // Frees a used TLS slot identified by the given address + void FreeTLSSlot(VAddr tls_address); + + ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); + ResultCode HeapFree(VAddr target, u32 size); + + ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); + + ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); + VMManager vm_manager; +private: + explicit Process(KernelCore& kernel); + ~Process() override; + + /// Current status of the process + ProcessStatus status; + + /// The ID of this process + u32 process_id = 0; + // Memory used to back the allocations in the regular heap. A single vector is used to cover // the entire virtual address space extents that bound the allocations, including any holes. // This makes deallocation and reallocation of holes fast and keeps process memory contiguous @@ -197,17 +243,6 @@ public: std::vector<std::bitset<8>> tls_slots; std::string name; - - ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); - ResultCode HeapFree(VAddr target, u32 size); - - ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); - - ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); - -private: - explicit Process(KernelCore& kernel); - ~Process() override; }; } // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 69c812f16..9faf903cf 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -17,7 +17,7 @@ namespace Kernel { std::mutex Scheduler::scheduler_mutex; -Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {} +Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {} Scheduler::~Scheduler() { for (auto& thread : thread_list) { @@ -59,9 +59,9 @@ void Scheduler::SwitchContext(Thread* new_thread) { // Save context for previous thread if (previous_thread) { previous_thread->last_running_ticks = CoreTiming::GetTicks(); - cpu_core->SaveContext(previous_thread->context); + cpu_core.SaveContext(previous_thread->context); // Save the TPIDR_EL0 system register in case it was modified. - previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0(); + previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0(); if (previous_thread->status == ThreadStatus::Running) { // This is only the case when a reschedule is triggered without the current thread @@ -91,10 +91,10 @@ void Scheduler::SwitchContext(Thread* new_thread) { SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); } - cpu_core->LoadContext(new_thread->context); - cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); - cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); - cpu_core->ClearExclusiveState(); + cpu_core.LoadContext(new_thread->context); + cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); } else { current_thread = nullptr; // Note: We do not reset the current process and current page table when idling because diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 744990c9b..2c94641ec 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -19,7 +19,7 @@ namespace Kernel { class Scheduler final { public: - explicit Scheduler(Core::ARM_Interface* cpu_core); + explicit Scheduler(Core::ARM_Interface& cpu_core); ~Scheduler(); /// Returns whether there are any threads that are ready to run. @@ -72,7 +72,7 @@ private: SharedPtr<Thread> current_thread = nullptr; - Core::ARM_Interface* cpu_core; + Core::ARM_Interface& cpu_core; static std::mutex scheduler_mutex; }; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index abb1d09cd..9b78c8cb5 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -8,6 +8,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" @@ -71,7 +72,8 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( shared_memory->other_permissions = other_permissions; shared_memory->backing_block = std::move(heap_block); shared_memory->backing_block_offset = offset; - shared_memory->base_address = Memory::HEAP_VADDR + offset; + shared_memory->base_address = + kernel.CurrentProcess()->vm_manager.GetHeapRegionBaseAddress() + offset; return shared_memory; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 371fc439e..44bbaf0c8 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -51,8 +51,9 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { } auto& process = *Core::CurrentProcess(); + const VAddr heap_base = process.vm_manager.GetHeapRegionBaseAddress(); CASCADE_RESULT(*heap_addr, - process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); + process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite)); return RESULT_SUCCESS; } @@ -169,7 +170,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { return ERR_INVALID_HANDLE; } - *process_id = process->process_id; + *process_id = process->GetProcessID(); return RESULT_SUCCESS; } @@ -325,26 +326,27 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); - const auto& vm_manager = Core::CurrentProcess()->vm_manager; + const auto& current_process = Core::CurrentProcess(); + const auto& vm_manager = current_process->vm_manager; switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: - *result = Core::CurrentProcess()->allowed_processor_mask; + *result = current_process->allowed_processor_mask; break; case GetInfoType::AllowedThreadPrioBitmask: - *result = Core::CurrentProcess()->allowed_thread_priority_mask; + *result = current_process->allowed_thread_priority_mask; break; case GetInfoType::MapRegionBaseAddr: - *result = Memory::MAP_REGION_VADDR; + *result = vm_manager.GetMapRegionBaseAddress(); break; case GetInfoType::MapRegionSize: - *result = Memory::MAP_REGION_SIZE; + *result = vm_manager.GetMapRegionSize(); break; case GetInfoType::HeapRegionBaseAddr: - *result = Memory::HEAP_VADDR; + *result = vm_manager.GetHeapRegionBaseAddress(); break; case GetInfoType::HeapRegionSize: - *result = Memory::HEAP_SIZE; + *result = vm_manager.GetHeapRegionSize(); break; case GetInfoType::TotalMemoryUsage: *result = vm_manager.GetTotalMemoryUsage(); @@ -359,22 +361,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = 0; break; case GetInfoType::AddressSpaceBaseAddr: - *result = vm_manager.GetAddressSpaceBaseAddr(); + *result = vm_manager.GetCodeRegionBaseAddress(); break; - case GetInfoType::AddressSpaceSize: - *result = vm_manager.GetAddressSpaceSize(); + case GetInfoType::AddressSpaceSize: { + const u64 width = vm_manager.GetAddressSpaceWidth(); + + switch (width) { + case 32: + *result = 0xFFE00000; + break; + case 36: + *result = 0xFF8000000; + break; + case 39: + *result = 0x7FF8000000; + break; + } break; + } case GetInfoType::NewMapRegionBaseAddr: - *result = Memory::NEW_MAP_REGION_VADDR; + *result = vm_manager.GetNewMapRegionBaseAddress(); break; case GetInfoType::NewMapRegionSize: - *result = Memory::NEW_MAP_REGION_SIZE; + *result = vm_manager.GetNewMapRegionSize(); break; case GetInfoType::IsVirtualAddressMemoryEnabled: - *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; + *result = current_process->is_virtual_address_memory_enabled; break; case GetInfoType::TitleId: - *result = Core::CurrentProcess()->program_id; + *result = current_process->program_id; break; case GetInfoType::PrivilegedProcessId: LOG_WARNING(Kernel_SVC, @@ -530,35 +545,13 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd /// Exits the current process static void ExitProcess() { - LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); + auto& current_process = Core::CurrentProcess(); - ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, + LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); + ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, "Process has already exited"); - Core::CurrentProcess()->status = ProcessStatus::Exited; - - auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) { - for (auto& thread : thread_list) { - if (thread->owner_process != Core::CurrentProcess()) - continue; - - if (thread == 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(); - } - }; - - auto& system = Core::System::GetInstance(); - stop_threads(system.Scheduler(0)->GetThreadList()); - stop_threads(system.Scheduler(1)->GetThreadList()); - stop_threads(system.Scheduler(2)->GetThreadList()); - stop_threads(system.Scheduler(3)->GetThreadList()); + current_process->PrepareForTermination(); // Kill the current thread GetCurrentThread()->Stop(); @@ -1039,7 +1032,7 @@ static const FunctionDef SVC_Table[] = { {0x2B, nullptr, "FlushDataCache"}, {0x2C, nullptr, "MapPhysicalMemory"}, {0x2D, nullptr, "UnmapPhysicalMemory"}, - {0x2E, nullptr, "GetNextThreadInfo"}, + {0x2E, nullptr, "GetFutureThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, {0x30, nullptr, "GetResourceLimitLimitValue"}, {0x31, nullptr, "GetResourceLimitCurrentValue"}, @@ -1065,11 +1058,11 @@ static const FunctionDef SVC_Table[] = { {0x45, nullptr, "CreateEvent"}, {0x46, nullptr, "Unknown"}, {0x47, nullptr, "Unknown"}, - {0x48, nullptr, "AllocateUnsafeMemory"}, - {0x49, nullptr, "FreeUnsafeMemory"}, - {0x4A, nullptr, "SetUnsafeAllocationLimit"}, - {0x4B, nullptr, "CreateJitMemory"}, - {0x4C, nullptr, "MapJitMemory"}, + {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, + {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, + {0x4A, nullptr, "SetUnsafeLimit"}, + {0x4B, nullptr, "CreateCodeMemory"}, + {0x4C, nullptr, "ControlCodeMemory"}, {0x4D, nullptr, "SleepSystem"}, {0x4E, nullptr, "ReadWriteRegister"}, {0x4F, nullptr, "SetProcessActivity"}, @@ -1104,7 +1097,7 @@ static const FunctionDef SVC_Table[] = { {0x6C, nullptr, "SetHardwareBreakPoint"}, {0x6D, nullptr, "GetDebugThreadParam"}, {0x6E, nullptr, "Unknown"}, - {0x6F, nullptr, "GetMemoryInfo"}, + {0x6F, nullptr, "GetSystemInfo"}, {0x70, nullptr, "CreatePort"}, {0x71, nullptr, "ManageNamedPort"}, {0x72, nullptr, "ConnectToPort"}, diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index c2d7535c9..064ed908d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -65,10 +65,7 @@ void Thread::Stop() { wait_objects.clear(); // Mark the TLS slot in the thread's page as free. - const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; - const u64 tls_slot = - ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; - Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); + owner_process->FreeTLSSlot(tls_address); } void WaitCurrentThread_Sleep() { @@ -178,32 +175,6 @@ void Thread::ResumeFromWait() { } /** - * 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<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot( - const std::vector<std::bitset<8>>& 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 * @param stack_top Address of the top of the stack @@ -264,32 +235,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name thread->owner_process = owner_process; thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); thread->scheduler->AddThread(thread, priority); - - // 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) { - 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 - - // Allocate some memory from the end of the linear heap for this region. - const std::size_t offset = thread->tls_memory->size(); - thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); - - auto& vm_manager = owner_process->vm_manager; - vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get()); - - vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - thread->tls_memory, 0, Memory::PAGE_SIZE, - MemoryState::ThreadLocal); - } - - // Mark the slot as used - tls_slots[available_page].set(available_slot); - thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + - available_slot * Memory::TLS_ENTRY_SIZE; + thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context @@ -316,8 +262,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri SetCurrentPageTable(&owner_process.vm_manager.page_table); // Initialize new "main" thread + const VAddr stack_top = owner_process.vm_manager.GetTLSIORegionEndAddress(); auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, - Memory::STACK_AREA_VADDR_END, &owner_process); + stack_top, &owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 91e9b79ec..4250144c3 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -62,6 +62,9 @@ enum class ThreadWakeupReason { class Thread final : public WaitObject { public: + using TLSMemory = std::vector<u8>; + using TLSMemoryPtr = std::shared_ptr<TLSMemory>; + /** * Creates and returns a new thread. The new thread is immediately scheduled * @param kernel The kernel instance this thread will be created under. @@ -134,6 +137,14 @@ public: return thread_id; } + TLSMemoryPtr& GetTLSMemory() { + return tls_memory; + } + + const TLSMemoryPtr& GetTLSMemory() const { + return tls_memory; + } + /** * Resumes a thread from waiting */ @@ -269,7 +280,7 @@ private: explicit Thread(KernelCore& kernel); ~Thread() override; - std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>(); + TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); }; /** diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 608cbd57b..e412309fd 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/file_sys/program_metadata.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" @@ -54,30 +55,32 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { } VMManager::VMManager() { - Reset(); + // Default to assuming a 39-bit address space. This way we have a sane + // starting point with executables that don't provide metadata. + Reset(FileSys::ProgramAddressSpaceType::Is39Bit); } VMManager::~VMManager() { - Reset(); + Reset(FileSys::ProgramAddressSpaceType::Is39Bit); } -void VMManager::Reset() { - vma_map.clear(); +void VMManager::Reset(FileSys::ProgramAddressSpaceType type) { + Clear(); + + InitializeMemoryRegionRanges(type); + + page_table.Resize(address_space_width); // Initialize the map with a single free region covering the entire managed space. VirtualMemoryArea initial_vma; - initial_vma.size = MAX_ADDRESS; + initial_vma.size = address_space_end; vma_map.emplace(initial_vma.base, initial_vma); - page_table.pointers.fill(nullptr); - page_table.special_regions.clear(); - page_table.attributes.fill(Memory::PageType::Unmapped); - UpdatePageTableForVMA(initial_vma); } VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { - if (target >= MAX_ADDRESS) { + if (target >= address_space_end) { return vma_map.end(); } else { return std::prev(vma_map.upper_bound(target)); @@ -291,7 +294,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { const VAddr target_end = target + size; ASSERT(target_end >= target); - ASSERT(target_end <= MAX_ADDRESS); + ASSERT(target_end <= address_space_end); ASSERT(size > 0); VMAIter begin_vma = StripIterConstness(FindVMA(target)); @@ -382,6 +385,85 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { } } +void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) { + u64 map_region_size = 0; + u64 heap_region_size = 0; + u64 new_map_region_size = 0; + u64 tls_io_region_size = 0; + + switch (type) { + case FileSys::ProgramAddressSpaceType::Is32Bit: + address_space_width = 32; + code_region_base = 0x200000; + code_region_end = code_region_base + 0x3FE00000; + map_region_size = 0x40000000; + heap_region_size = 0x40000000; + break; + case FileSys::ProgramAddressSpaceType::Is36Bit: + address_space_width = 36; + code_region_base = 0x8000000; + code_region_end = code_region_base + 0x78000000; + map_region_size = 0x180000000; + heap_region_size = 0x180000000; + break; + case FileSys::ProgramAddressSpaceType::Is32BitNoMap: + address_space_width = 32; + code_region_base = 0x200000; + code_region_end = code_region_base + 0x3FE00000; + map_region_size = 0; + heap_region_size = 0x80000000; + break; + case FileSys::ProgramAddressSpaceType::Is39Bit: + address_space_width = 39; + code_region_base = 0x8000000; + code_region_end = code_region_base + 0x80000000; + map_region_size = 0x1000000000; + heap_region_size = 0x180000000; + new_map_region_size = 0x80000000; + tls_io_region_size = 0x1000000000; + break; + default: + UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type)); + return; + } + + address_space_base = 0; + address_space_end = 1ULL << address_space_width; + + map_region_base = code_region_end; + map_region_end = map_region_base + map_region_size; + + heap_region_base = map_region_end; + heap_region_end = heap_region_base + heap_region_size; + + new_map_region_base = heap_region_end; + new_map_region_end = new_map_region_base + new_map_region_size; + + tls_io_region_base = new_map_region_end; + tls_io_region_end = tls_io_region_base + tls_io_region_size; + + if (new_map_region_size == 0) { + new_map_region_base = address_space_base; + new_map_region_end = address_space_end; + } +} + +void VMManager::Clear() { + ClearVMAMap(); + ClearPageTable(); +} + +void VMManager::ClearVMAMap() { + vma_map.clear(); +} + +void VMManager::ClearPageTable() { + std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); + page_table.special_regions.clear(); + std::fill(page_table.attributes.begin(), page_table.attributes.end(), + Memory::PageType::Unmapped); +} + u64 VMManager::GetTotalMemoryUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; @@ -392,14 +474,80 @@ u64 VMManager::GetTotalHeapUsage() const { return 0x0; } -VAddr VMManager::GetAddressSpaceBaseAddr() const { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x8000000; +VAddr VMManager::GetAddressSpaceBaseAddress() const { + return address_space_base; +} + +VAddr VMManager::GetAddressSpaceEndAddress() const { + return address_space_end; } u64 VMManager::GetAddressSpaceSize() const { - LOG_WARNING(Kernel, "(STUBBED) called"); - return MAX_ADDRESS; + return address_space_end - address_space_base; +} + +u64 VMManager::GetAddressSpaceWidth() const { + return address_space_width; +} + +VAddr VMManager::GetCodeRegionBaseAddress() const { + return code_region_base; +} + +VAddr VMManager::GetCodeRegionEndAddress() const { + return code_region_end; +} + +u64 VMManager::GetCodeRegionSize() const { + return code_region_end - code_region_base; +} + +VAddr VMManager::GetHeapRegionBaseAddress() const { + return heap_region_base; +} + +VAddr VMManager::GetHeapRegionEndAddress() const { + return heap_region_end; +} + +u64 VMManager::GetHeapRegionSize() const { + return heap_region_end - heap_region_base; +} + +VAddr VMManager::GetMapRegionBaseAddress() const { + return map_region_base; +} + +VAddr VMManager::GetMapRegionEndAddress() const { + return map_region_end; +} + +u64 VMManager::GetMapRegionSize() const { + return map_region_end - map_region_base; +} + +VAddr VMManager::GetNewMapRegionBaseAddress() const { + return new_map_region_base; +} + +VAddr VMManager::GetNewMapRegionEndAddress() const { + return new_map_region_end; +} + +u64 VMManager::GetNewMapRegionSize() const { + return new_map_region_end - new_map_region_base; +} + +VAddr VMManager::GetTLSIORegionBaseAddress() const { + return tls_io_region_base; +} + +VAddr VMManager::GetTLSIORegionEndAddress() const { + return tls_io_region_end; +} + +u64 VMManager::GetTLSIORegionSize() const { + return tls_io_region_end - tls_io_region_base; } } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index de75036c0..015559a64 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -12,6 +12,10 @@ #include "core/memory.h" #include "core/memory_hook.h" +namespace FileSys { +enum class ProgramAddressSpaceType : u8; +} + namespace Kernel { enum class VMAType : u8 { @@ -111,12 +115,6 @@ struct VirtualMemoryArea { class VMManager final { public: /** - * The maximum amount of address space managed by the kernel. - * @todo This was selected arbitrarily, and should be verified for Switch OS. - */ - static constexpr VAddr MAX_ADDRESS{0x1000000000ULL}; - - /** * A map covering the entirety of the managed address space, keyed by the `base` field of each * VMA. It must always be modified by splitting or merging VMAs, so that the invariant * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be @@ -130,7 +128,7 @@ public: ~VMManager(); /// Clears the address space map, re-initializing with a single free area. - void Reset(); + void Reset(FileSys::ProgramAddressSpaceType type); /// Finds the VMA in which the given address is included in, or `vma_map.end()`. VMAHandle FindVMA(VAddr target) const; @@ -195,12 +193,63 @@ public: /// Gets the total heap usage, used by svcGetInfo u64 GetTotalHeapUsage() const; - /// Gets the total address space base address, used by svcGetInfo - VAddr GetAddressSpaceBaseAddr() const; + /// Gets the address space base address + VAddr GetAddressSpaceBaseAddress() const; - /// Gets the total address space address size, used by svcGetInfo + /// Gets the address space end address + VAddr GetAddressSpaceEndAddress() const; + + /// Gets the total address space address size in bytes u64 GetAddressSpaceSize() const; + /// Gets the address space width in bits. + u64 GetAddressSpaceWidth() const; + + /// Gets the base address of the code region. + VAddr GetCodeRegionBaseAddress() const; + + /// Gets the end address of the code region. + VAddr GetCodeRegionEndAddress() const; + + /// Gets the total size of the code region in bytes. + u64 GetCodeRegionSize() const; + + /// Gets the base address of the heap region. + VAddr GetHeapRegionBaseAddress() const; + + /// Gets the end address of the heap region; + VAddr GetHeapRegionEndAddress() const; + + /// Gets the total size of the heap region in bytes. + u64 GetHeapRegionSize() const; + + /// Gets the base address of the map region. + VAddr GetMapRegionBaseAddress() const; + + /// Gets the end address of the map region. + VAddr GetMapRegionEndAddress() const; + + /// Gets the total size of the map region in bytes. + u64 GetMapRegionSize() const; + + /// Gets the base address of the new map region. + VAddr GetNewMapRegionBaseAddress() const; + + /// Gets the end address of the new map region. + VAddr GetNewMapRegionEndAddress() const; + + /// Gets the total size of the new map region in bytes. + u64 GetNewMapRegionSize() const; + + /// Gets the base address of the TLS IO region. + VAddr GetTLSIORegionBaseAddress() const; + + /// Gets the end address of the TLS IO region. + VAddr GetTLSIORegionEndAddress() const; + + /// Gets the total size of the TLS IO region in bytes. + u64 GetTLSIORegionSize() const; + /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. Memory::PageTable page_table; @@ -240,5 +289,36 @@ private: /// Updates the pages corresponding to this VMA so they match the VMA's attributes. void UpdatePageTableForVMA(const VirtualMemoryArea& vma); + + /// Initializes memory region ranges to adhere to a given address space type. + void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type); + + /// Clears the underlying map and page table. + void Clear(); + + /// Clears out the VMA map, unmapping any previously mapped ranges. + void ClearVMAMap(); + + /// Clears out the page table + void ClearPageTable(); + + u32 address_space_width = 0; + VAddr address_space_base = 0; + VAddr address_space_end = 0; + + VAddr code_region_base = 0; + VAddr code_region_end = 0; + + VAddr heap_region_base = 0; + VAddr heap_region_end = 0; + + VAddr map_region_base = 0; + VAddr map_region_end = 0; + + VAddr new_map_region_base = 0; + VAddr new_map_region_end = 0; + + VAddr tls_io_region_base = 0; + VAddr tls_io_region_end = 0; }; } // namespace Kernel diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 0bd97133c..f4367ee28 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -69,7 +69,7 @@ private: template <> inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { if (object != nullptr && object->IsWaitable()) { - return boost::static_pointer_cast<WaitObject>(std::move(object)); + return boost::static_pointer_cast<WaitObject>(object); } return nullptr; } |