diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/errors.h | 89 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.cpp | 90 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.h | 38 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 28 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.h | 19 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_port.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 232 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.h | 31 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 54 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 102 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 24 |
18 files changed, 489 insertions, 265 deletions
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 885259618..8b58d701d 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -8,71 +8,28 @@ namespace Kernel { -namespace ErrCodes { -enum { - // TODO(Subv): Remove these 3DS OS error codes. - SessionClosedByRemote = 26, - NoPendingSessions = 35, - InvalidBufferDescriptor = 48, - - // Confirmed Switch OS error codes - MaxConnectionsReached = 7, - InvalidSize = 101, - InvalidAddress = 102, - HandleTableFull = 105, - InvalidMemoryState = 106, - InvalidMemoryPermissions = 108, - InvalidMemoryRange = 110, - InvalidThreadPriority = 112, - InvalidProcessorId = 113, - InvalidHandle = 114, - InvalidCombination = 116, - Timeout = 117, - SynchronizationCanceled = 118, - TooLarge = 119, - InvalidEnumValue = 120, - NoSuchEntry = 121, - AlreadyRegistered = 122, - InvalidState = 125, - ResourceLimitExceeded = 132, -}; -} - -// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always -// double check that the code matches before re-using the constant. - -// TODO(bunnei): Replace -1 with correct errors for Switch OS -constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); -constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); -constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); -constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, - ErrCodes::MaxConnectionsReached); -constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); -constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); -constexpr ResultCode ERR_INVALID_COMBINATION(-1); -constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, - ErrCodes::InvalidCombination); -constexpr ResultCode ERR_OUT_OF_MEMORY(-1); -constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); -constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); -constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, - ErrCodes::InvalidMemoryPermissions); -constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange); -constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); -constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); -constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); -constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); -constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); -constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, - ErrCodes::InvalidThreadPriority); -constexpr ResultCode ERR_INVALID_POINTER(-1); -constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); -constexpr ResultCode ERR_NOT_AUTHORIZED(-1); -/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. -constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); -constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); -constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); -/// Returned when Accept() is called on a port with no sessions to be accepted. -constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); +// Confirmed Switch kernel error codes + +constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; +constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; +constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; +constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; +constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; +constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; +constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110}; +constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; +constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112}; +constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114}; +constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115}; +constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116}; +constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117}; +constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; +constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; +constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; +constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; +constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122}; +constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; +constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; +constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f01491daa..a38e34b74 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -161,8 +161,12 @@ public: return buffer_c_desciptors; } - const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { - return domain_message_header; + const IPC::DomainMessageHeader* GetDomainMessageHeader() const { + return domain_message_header.get(); + } + + bool HasDomainMessageHeader() const { + return domain_message_header != nullptr; } /// Helper function to read a buffer using the appropriate buffer descriptor diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 4b6b32dd5..1fd4ba5d2 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -32,7 +32,7 @@ namespace Kernel { */ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) { const auto proper_handle = static_cast<Handle>(thread_handle); - auto& system = Core::System::GetInstance(); + const auto& system = Core::System::GetInstance(); // Lock the global kernel mutex when we enter the kernel HLE. std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); @@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_ /// The timer callback event, called when a timer is fired static void TimerCallback(u64 timer_handle, int cycles_late) { const auto proper_handle = static_cast<Handle>(timer_handle); - auto& system = Core::System::GetInstance(); + const auto& system = Core::System::GetInstance(); SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle); if (timer == nullptr) { diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index dd541ffcc..0743670ad 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -6,8 +6,6 @@ #include <utility> #include <vector> -#include <boost/range/algorithm_ext/erase.hpp> - #include "common/assert.h" #include "core/core.h" #include "core/hle/kernel/errors.h" diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 073dd5a7d..a257c3726 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -5,11 +5,9 @@ #include <algorithm> #include <memory> #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" @@ -17,6 +15,7 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" +#include "core/settings.h" namespace Kernel { @@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { process->process_id = kernel.CreateNewProcessID(); process->svc_access_mask.set(); + std::mt19937 rng(Settings::values.rng_seed.value_or(0)); + std::uniform_int_distribution<u64> distribution; + std::generate(process->random_entropy.begin(), process->random_entropy.end(), + [&] { return distribution(rng); }); + kernel.AppendNewProcess(process); return process; } @@ -232,86 +236,24 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); + + // Clear instruction cache in CPU JIT + Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { - if (target < vm_manager.GetHeapRegionBaseAddress() || - target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { - return ERR_INVALID_ADDRESS; - } - - if (heap_memory == nullptr) { - // Initialize heap - heap_memory = std::make_shared<std::vector<u8>>(); - heap_start = heap_end = target; - } else { - vm_manager.UnmapRange(heap_start, heap_end - heap_start); - } - - // If necessary, expand backing vector to cover new heap extents. - if (target < heap_start) { - heap_memory->insert(begin(*heap_memory), heap_start - target, 0); - heap_start = target; - vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); - } - if (target + size > heap_end) { - heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0); - heap_end = target + size; - vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); - } - ASSERT(heap_end - heap_start == heap_memory->size()); - - CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start, - size, MemoryState::Heap)); - vm_manager.Reprotect(vma, perms); - - heap_used = size; - - return MakeResult<VAddr>(heap_end - size); + return vm_manager.HeapAllocate(target, size, perms); } ResultCode Process::HeapFree(VAddr target, u32 size) { - if (target < vm_manager.GetHeapRegionBaseAddress() || - target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { - return ERR_INVALID_ADDRESS; - } - - if (size == 0) { - return RESULT_SUCCESS; - } - - ResultCode result = vm_manager.UnmapRange(target, size); - if (result.IsError()) - return result; - - heap_used -= size; - - return RESULT_SUCCESS; + return vm_manager.HeapFree(target, size); } -ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { - auto vma = vm_manager.FindVMA(src_addr); - - ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); - ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); - - // The returned VMA might be a bigger one encompassing the desired address. - auto vma_offset = src_addr - vma->first; - ASSERT_MSG(vma_offset + size <= vma->second.size, - "Shared memory exceeds bounds of mapped block"); - - const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; - std::size_t backing_block_offset = vma->second.offset + vma_offset; - - CASCADE_RESULT(auto new_vma, - vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, - MemoryState::Mapped)); - // Protect mirror with permissions from old region - vm_manager.Reprotect(new_vma, vma->second.permissions); - // Remove permissions from old region - vm_manager.Reprotect(vma, VMAPermission::None); - - return RESULT_SUCCESS; +ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { + return vm_manager.MirrorMemory(dst_addr, src_addr, size, state); } ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 148478488..230e395ff 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -8,6 +8,7 @@ #include <bitset> #include <cstddef> #include <memory> +#include <random> #include <string> #include <vector> #include <boost/container/static_vector.hpp> @@ -119,6 +120,8 @@ struct CodeSet final { class Process final : public Object { public: + static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; + static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); std::string GetTypeName() const override { @@ -202,6 +205,21 @@ public: return is_64bit_process; } + /// Gets the total running time of the process instance in ticks. + u64 GetCPUTimeTicks() const { + return total_process_running_time_ticks; + } + + /// Updates the total running time, adding the given ticks to it. + void UpdateCPUTimeTicks(u64 ticks) { + total_process_running_time_ticks += ticks; + } + + /// Gets 8 bytes of random data for svcGetInfo RandomEntropy + u64 GetRandomEntropy(std::size_t index) const { + return random_entropy.at(index); + } + /** * Loads process-specifics configuration info with metadata provided * by an executable. @@ -241,7 +259,8 @@ public: 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 MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, + MemoryState state = MemoryState::Mapped); ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); @@ -282,17 +301,6 @@ private: u32 allowed_thread_priority_mask = 0xFFFFFFFF; u32 is_virtual_address_memory_enabled = 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 - // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. - std::shared_ptr<std::vector<u8>> heap_memory; - - // The left/right bounds of the address space covered by heap_memory. - VAddr heap_start = 0; - VAddr heap_end = 0; - u64 heap_used = 0; - /// 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 /// holds the TLS for a specific thread. This vector contains which parts are in use for each @@ -305,9 +313,15 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; + /// Total running time for the process in ticks. + u64 total_process_running_time_ticks = 0; + /// Per-process handle table for storing created object handles in. HandleTable handle_table; + /// Random values for svcGetInfo RandomEntropy + std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; + std::string name; }; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1342c597e..5a5f4cef1 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" @@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const { return current_thread.get(); } +u64 Scheduler::GetLastContextSwitchTicks() const { + return last_context_switch_time; +} + Thread* Scheduler::PopNextReadyThread() { Thread* next = nullptr; Thread* thread = GetCurrentThread(); @@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() { } void Scheduler::SwitchContext(Thread* new_thread) { - Thread* previous_thread = GetCurrentThread(); + Thread* const previous_thread = GetCurrentThread(); + Process* const previous_process = Core::CurrentProcess(); + + UpdateLastContextSwitchTime(previous_thread, previous_process); // Save context for previous thread if (previous_thread) { @@ -78,8 +86,6 @@ void Scheduler::SwitchContext(Thread* new_thread) { // Cancel any outstanding wakeup events for this thread new_thread->CancelWakeupTimer(); - auto* const previous_process = Core::CurrentProcess(); - current_thread = new_thread; ready_queue.remove(new_thread->GetPriority(), new_thread); @@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) { } } +void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { + const u64 prev_switch_ticks = last_context_switch_time; + const u64 most_recent_switch_ticks = CoreTiming::GetTicks(); + const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; + + if (thread != nullptr) { + thread->UpdateCPUTimeTicks(update_ticks); + } + + if (process != nullptr) { + process->UpdateCPUTimeTicks(update_ticks); + } + + last_context_switch_time = most_recent_switch_ticks; +} + void Scheduler::Reschedule() { std::lock_guard<std::mutex> lock(scheduler_mutex); diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 2c94641ec..c63032b7d 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -17,6 +17,8 @@ class ARM_Interface; namespace Kernel { +class Process; + class Scheduler final { public: explicit Scheduler(Core::ARM_Interface& cpu_core); @@ -31,6 +33,9 @@ public: /// Gets the current running thread Thread* GetCurrentThread() const; + /// Gets the timestamp for the last context switch in ticks. + u64 GetLastContextSwitchTicks() const; + /// Adds a new thread to the scheduler void AddThread(SharedPtr<Thread> thread, u32 priority); @@ -64,6 +69,19 @@ private: */ void SwitchContext(Thread* new_thread); + /** + * Called on every context switch to update the internal timestamp + * This also updates the running time ticks for the given thread and + * process using the following difference: + * + * ticks += most_recent_ticks - last_context_switch_ticks + * + * The internal tick timestamp for the scheduler is simply the + * most recent tick count retrieved. No special arithmetic is + * applied to it. + */ + void UpdateLastContextSwitchTime(Thread* thread, Process* process); + /// Lists all thread ids that aren't deleted/etc. std::vector<SharedPtr<Thread>> thread_list; @@ -73,6 +91,7 @@ private: SharedPtr<Thread> current_thread = nullptr; Core::ARM_Interface& cpu_core; + u64 last_context_switch_time = 0; static std::mutex scheduler_mutex; }; diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 3792e3e18..d6ceeb2da 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default; ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { if (pending_sessions.empty()) { - return ERR_NO_PENDING_SESSIONS; + return ERR_NOT_FOUND; } auto session = std::move(pending_sessions.back()); @@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { bool ServerPort::ShouldWait(Thread* thread) const { // If there are no pending sessions, we wait until a new one is added. - return pending_sessions.size() == 0; + return pending_sessions.empty(); } void ServerPort::Acquire(Thread* thread) { diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 5fc320403..80897f3a4 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) { } ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { - auto& domain_message_header = context.GetDomainMessageHeader(); + auto* const domain_message_header = context.GetDomainMessageHeader(); if (domain_message_header) { // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs context.SetDomainRequestHandlers(domain_request_handlers); @@ -111,7 +111,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { ResultCode result = RESULT_SUCCESS; // If the session has been converted to a domain, handle the domain request - if (IsDomain() && context.GetDomainMessageHeader()) { + if (IsDomain() && context.HasDomainMessageHeader()) { result = HandleDomainSyncRequest(context); // If there is no domain header, the regular session handler is used } else if (hle_handler != nullptr) { diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d061e6155..a016a86b6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { - - MemoryPermission own_other_permissions = + const MemoryPermission own_other_permissions = target_process == owner_process ? 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) { - return ERR_INVALID_COMBINATION; + return ERR_INVALID_MEMORY_PERMISSIONS; } // Error out if the requested permissions don't match what the creator process allows. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", GetObjectId(), address, name); - return ERR_INVALID_COMBINATION; + return ERR_INVALID_MEMORY_PERMISSIONS; } // Error out if the provided permissions are not compatible with what the creator process needs. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 67ea67666..75dbfc31d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -34,6 +34,7 @@ #include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/service.h" +#include "core/settings.h" namespace Kernel { namespace { @@ -122,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { return RESULT_SUCCESS; } +static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { + LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); + + if (!Common::Is4KBAligned(addr)) { + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(addr, size)) { + return ERR_INVALID_ADDRESS_STATE; + } + + const auto permission = static_cast<MemoryPermission>(prot); + if (permission != MemoryPermission::None && permission != MemoryPermission::Read && + permission != MemoryPermission::ReadWrite) { + return ERR_INVALID_MEMORY_PERMISSIONS; + } + + auto* const current_process = Core::CurrentProcess(); + auto& vm_manager = current_process->VMManager(); + + if (!IsInsideAddressSpace(vm_manager, addr, size)) { + return ERR_INVALID_ADDRESS_STATE; + } + + const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); + if (iter == vm_manager.vma_map.end()) { + return ERR_INVALID_ADDRESS_STATE; + } + + LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented."); + // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't + // make sense to allow changing permissions on kernel memory itself, etc). + + const auto converted_permissions = SharedMemory::ConvertPermissions(permission); + + return vm_manager.ReprotectRange(addr, size, converted_permissions); +} + static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr, @@ -171,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address // Read 1 char beyond the max allowed port name to detect names that are too long. std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); if (port_name.size() > PortNameMaxLength) { - return ERR_PORT_NAME_TOO_LONG; + return ERR_OUT_OF_RANGE; } LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); @@ -267,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 static constexpr u64 MaxHandles = 0x40; - if (handle_count > MaxHandles) - return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); + if (handle_count > MaxHandles) { + return ERR_OUT_OF_RANGE; + } auto* const thread = GetCurrentThread(); @@ -333,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) { } ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); - thread->SetWaitSynchronizationResult( - ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled)); + thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); thread->ResumeFromWait(); return RESULT_SUCCESS; } @@ -375,9 +418,19 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { return Mutex::Release(mutex_addr); } +enum class BreakType : u32 { + Panic = 0, + AssertionFailed = 1, + PreNROLoad = 3, + PostNROLoad = 4, + PreNROUnload = 5, + PostNROUnload = 6, +}; + struct BreakReason { union { u32 raw; + BitField<0, 30, BreakType> break_type; BitField<31, 1, u32> signal_debugger; }; }; @@ -385,16 +438,80 @@ struct BreakReason { /// Break program execution static void Break(u32 reason, u64 info1, u64 info2) { BreakReason break_reason{reason}; - if (break_reason.signal_debugger) { - LOG_ERROR( + bool has_dumped_buffer{}; + + const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { + if (sz == 0 || addr == 0 || has_dumped_buffer) { + return; + } + + // This typically is an error code so we're going to assume this is the case + if (sz == sizeof(u32)) { + LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); + } else { + // We don't know what's in here so we'll hexdump it + std::vector<u8> debug_buffer(sz); + Memory::ReadBlock(addr, debug_buffer.data(), sz); + std::string hexdump; + for (std::size_t i = 0; i < debug_buffer.size(); i++) { + hexdump += fmt::format("{:02X} ", debug_buffer[i]); + if (i != 0 && i % 16 == 0) { + hexdump += '\n'; + } + } + LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump); + } + has_dumped_buffer = true; + }; + switch (break_reason.break_type) { + case BreakType::Panic: + LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", + info1, info2); + handle_debug_buffer(info1, info2); + break; + case BreakType::AssertionFailed: + LOG_CRITICAL(Debug_Emulated, + "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", + info1, info2); + handle_debug_buffer(info1, info2); + break; + case BreakType::PreNROLoad: + LOG_WARNING( Debug_Emulated, - "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", - reason, info1, info2); - } else { + "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", + info1, info2); + break; + case BreakType::PostNROLoad: + LOG_WARNING(Debug_Emulated, + "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); + break; + case BreakType::PreNROUnload: + LOG_WARNING( + Debug_Emulated, + "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", + info1, info2); + break; + case BreakType::PostNROUnload: + LOG_WARNING(Debug_Emulated, + "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); + break; + default: + LOG_WARNING( + Debug_Emulated, + "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", + static_cast<u32>(break_reason.break_type.Value()), info1, info2); + handle_debug_buffer(info1, info2); + break; + } + + if (!break_reason.signal_debugger) { LOG_CRITICAL( Debug_Emulated, "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", reason, info1, info2); + handle_debug_buffer(info1, info2); ASSERT(false); Core::CurrentProcess()->PrepareForTermination(); @@ -421,6 +538,37 @@ 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); + enum class GetInfoType : u64 { + // 1.0.0+ + AllowedCpuIdBitmask = 0, + AllowedThreadPrioBitmask = 1, + MapRegionBaseAddr = 2, + MapRegionSize = 3, + HeapRegionBaseAddr = 4, + HeapRegionSize = 5, + TotalMemoryUsage = 6, + TotalHeapUsage = 7, + IsCurrentProcessBeingDebugged = 8, + ResourceHandleLimit = 9, + IdleTickCount = 10, + RandomEntropy = 11, + PerformanceCounter = 0xF0000002, + // 2.0.0+ + ASLRRegionBaseAddr = 12, + ASLRRegionSize = 13, + NewMapRegionBaseAddr = 14, + NewMapRegionSize = 15, + // 3.0.0+ + IsVirtualAddressMemoryEnabled = 16, + PersonalMmHeapUsage = 17, + TitleId = 18, + // 4.0.0+ + PrivilegedProcessId = 19, + // 5.0.0+ + UserExceptionContextAddr = 20, + ThreadTickCount = 0xF0000002, + }; + const auto* current_process = Core::CurrentProcess(); const auto& vm_manager = current_process->VMManager(); @@ -453,7 +601,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = 0; break; case GetInfoType::RandomEntropy: - *result = 0; + if (handle != 0) { + return ERR_INVALID_HANDLE; + } + + if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) { + return ERR_INVALID_COMBINATION; + } + + *result = current_process->GetRandomEntropy(info_sub_id); + return RESULT_SUCCESS; break; case GetInfoType::ASLRRegionBaseAddr: *result = vm_manager.GetASLRRegionBaseAddress(); @@ -483,6 +640,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) "(STUBBED) Attempted to query user exception context address, returned 0"); *result = 0; break; + case GetInfoType::ThreadTickCount: { + constexpr u64 num_cpus = 4; + if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { + return ERR_INVALID_COMBINATION; + } + + const auto thread = + current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); + if (!thread) { + return ERR_INVALID_HANDLE; + } + + const auto& system = Core::System::GetInstance(); + const auto& scheduler = system.CurrentScheduler(); + const auto* const current_thread = scheduler.GetCurrentThread(); + const bool same_thread = current_thread == thread; + + const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks(); + u64 out_ticks = 0; + if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { + const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); + + out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks); + } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { + out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks; + } + + *result = out_ticks; + break; + } default: UNIMPLEMENTED(); } @@ -548,16 +735,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { } const auto* const current_process = Core::CurrentProcess(); - SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); - if (!thread) { - return ERR_INVALID_HANDLE; - } // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { - return ERR_NOT_AUTHORIZED; + return ERR_INVALID_THREAD_PRIORITY; + } + + SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); + if (!thread) { + return ERR_INVALID_HANDLE; } thread->SetPriority(priority); @@ -699,7 +887,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V auto* const current_process = Core::CurrentProcess(); const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { - return ERR_NOT_AUTHORIZED; + return ERR_INVALID_THREAD_PRIORITY; } if (processor_id == THREADPROCESSORID_DEFAULT) { @@ -993,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) { /// Reset an event static ResultCode ResetSignal(Handle handle) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); + LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); auto event = handle_table.Get<Event>(handle); @@ -1048,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { } if (mask == 0) { - return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); + return ERR_INVALID_COMBINATION; } /// This value is used to only change the affinity mask without changing the current ideal core. @@ -1057,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { if (core == OnlyChangeMask) { core = thread->GetIdealCore(); } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { - return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); + return ERR_INVALID_PROCESSOR_ID; } // Error out if the input core isn't enabled in the input mask. if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { - return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); + return ERR_INVALID_COMBINATION; } thread->ChangeCore(core, mask); @@ -1151,7 +1339,7 @@ struct FunctionDef { static const FunctionDef SVC_Table[] = { {0x00, nullptr, "Unknown"}, {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"}, - {0x02, nullptr, "SetMemoryPermission"}, + {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"}, {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"}, {0x04, SvcWrap<MapMemory>, "MapMemory"}, {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"}, diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 554a5e328..b06aac4ec 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -24,37 +24,6 @@ struct PageInfo { u64 flags; }; -/// Values accepted by svcGetInfo -enum class GetInfoType : u64 { - // 1.0.0+ - AllowedCpuIdBitmask = 0, - AllowedThreadPrioBitmask = 1, - MapRegionBaseAddr = 2, - MapRegionSize = 3, - HeapRegionBaseAddr = 4, - HeapRegionSize = 5, - TotalMemoryUsage = 6, - TotalHeapUsage = 7, - IsCurrentProcessBeingDebugged = 8, - ResourceHandleLimit = 9, - IdleTickCount = 10, - RandomEntropy = 11, - PerformanceCounter = 0xF0000002, - // 2.0.0+ - ASLRRegionBaseAddr = 12, - ASLRRegionSize = 13, - NewMapRegionBaseAddr = 14, - NewMapRegionSize = 15, - // 3.0.0+ - IsVirtualAddressMemoryEnabled = 16, - PersonalMmHeapUsage = 17, - TitleId = 18, - // 4.0.0+ - PrivilegedProcessId = 19, - // 5.0.0+ - UserExceptionContextAddr = 20, -}; - void CallSVC(u32 immediate); } // namespace Kernel diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index b09753c80..233a99fb0 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -121,6 +121,11 @@ void SvcWrap() { FuncReturn(func(Param(0), Param(1), Param(2)).raw); } +template <ResultCode func(u64, u64, u32)> +void SvcWrap() { + FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw); +} + template <ResultCode func(u32, u64, u64, u32)> void SvcWrap() { FuncReturn( diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 59bc9e0af..4ffb76818 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,9 +4,9 @@ #include <algorithm> #include <cinttypes> +#include <optional> #include <vector> -#include <boost/optional.hpp> #include <boost/range/algorithm_ext/erase.hpp> #include "common/assert.h" @@ -94,7 +94,7 @@ void Thread::CancelWakeupTimer() { CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle); } -static boost::optional<s32> GetNextProcessorId(u64 mask) { +static std::optional<s32> GetNextProcessorId(u64 mask) { for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { if (mask & (1ULL << index)) { if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) { @@ -142,36 +142,7 @@ void Thread::ResumeFromWait() { status = ThreadStatus::Ready; - boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); - if (!new_processor_id) { - new_processor_id = processor_id; - } - if (ideal_core != -1 && - Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) { - new_processor_id = ideal_core; - } - - ASSERT(*new_processor_id < 4); - - // Add thread to new core's scheduler - auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); - - if (*new_processor_id != processor_id) { - // Remove thread from previous core's scheduler - scheduler->RemoveThread(this); - next_scheduler->AddThread(this, current_priority); - } - - processor_id = *new_processor_id; - - // If the thread was ready, unschedule from the previous core and schedule on the new core - scheduler->UnscheduleThread(this, current_priority); - next_scheduler->ScheduleThread(this, current_priority); - - // Change thread's scheduler - scheduler = next_scheduler; - - Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); + ChangeScheduler(); } /** @@ -364,42 +335,45 @@ void Thread::UpdatePriority() { void Thread::ChangeCore(u32 core, u64 mask) { ideal_core = core; affinity_mask = mask; + ChangeScheduler(); +} +void Thread::ChangeScheduler() { if (status != ThreadStatus::Ready) { return; } - boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; + auto& system = Core::System::GetInstance(); + std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; if (!new_processor_id) { new_processor_id = processor_id; } - if (ideal_core != -1 && - Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) { + if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) { new_processor_id = ideal_core; } ASSERT(*new_processor_id < 4); // Add thread to new core's scheduler - auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); + auto& next_scheduler = system.Scheduler(*new_processor_id); if (*new_processor_id != processor_id) { // Remove thread from previous core's scheduler scheduler->RemoveThread(this); - next_scheduler->AddThread(this, current_priority); + next_scheduler.AddThread(this, current_priority); } processor_id = *new_processor_id; // If the thread was ready, unschedule from the previous core and schedule on the new core scheduler->UnscheduleThread(this, current_priority); - next_scheduler->ScheduleThread(this, current_priority); + next_scheduler.ScheduleThread(this, current_priority); // Change thread's scheduler - scheduler = next_scheduler; + scheduler = &next_scheduler; - Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); + system.CpuCore(processor_id).PrepareReschedule(); } bool Thread::AllWaitObjectsReady() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f4d7bd235..d384d50db 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -258,6 +258,14 @@ public: return last_running_ticks; } + u64 GetTotalCPUTimeTicks() const { + return total_cpu_time_ticks; + } + + void UpdateCPUTimeTicks(u64 ticks) { + total_cpu_time_ticks += ticks; + } + s32 GetProcessorID() const { return processor_id; } @@ -366,6 +374,8 @@ private: explicit Thread(KernelCore& kernel); ~Thread() override; + void ChangeScheduler(); + Core::ARM_Interface::ThreadContext context{}; u32 thread_id = 0; @@ -378,7 +388,8 @@ private: u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application u32 current_priority = 0; ///< Current thread priority, can be temporarily changed - u64 last_running_ticks = 0; ///< CPU tick when thread was last running + u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. + u64 last_running_ticks = 0; ///< CPU tick when thread was last running s32 processor_id = 0; diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e1a34eef1..100f8f6bf 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); } +ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { + // Find the first Free VMA. + const VAddr base = GetASLRRegionBaseAddress(); + const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { + if (vma.second.type != VMAType::Free) + return false; + + const VAddr vma_end = vma.second.base + vma.second.size; + return vma_end > base && vma_end >= base + size; + }); + + if (vma_handle == vma_map.end()) { + // TODO(Subv): Find the correct error code here. + return ResultCode(-1); + } + + const VAddr target = std::max(base, vma_handle->second.base); + return MakeResult<VAddr>(target); +} + ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, Memory::MemoryHookPointer mmio_handler) { @@ -223,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p return RESULT_SUCCESS; } +ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { + if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || + target + size < target) { + return ERR_INVALID_ADDRESS; + } + + if (heap_memory == nullptr) { + // Initialize heap + heap_memory = std::make_shared<std::vector<u8>>(); + heap_start = heap_end = target; + } else { + UnmapRange(heap_start, heap_end - heap_start); + } + + // If necessary, expand backing vector to cover new heap extents. + if (target < heap_start) { + heap_memory->insert(begin(*heap_memory), heap_start - target, 0); + heap_start = target; + RefreshMemoryBlockMappings(heap_memory.get()); + } + if (target + size > heap_end) { + heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0); + heap_end = target + size; + RefreshMemoryBlockMappings(heap_memory.get()); + } + ASSERT(heap_end - heap_start == heap_memory->size()); + + CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size, + MemoryState::Heap)); + Reprotect(vma, perms); + + heap_used = size; + + return MakeResult<VAddr>(heap_end - size); +} + +ResultCode VMManager::HeapFree(VAddr target, u64 size) { + if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || + target + size < target) { + return ERR_INVALID_ADDRESS; + } + + if (size == 0) { + return RESULT_SUCCESS; + } + + const ResultCode result = UnmapRange(target, size); + if (result.IsError()) { + return result; + } + + heap_used -= size; + return RESULT_SUCCESS; +} + +ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { + const auto vma = FindVMA(src_addr); + + ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); + ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); + + // The returned VMA might be a bigger one encompassing the desired address. + const auto vma_offset = src_addr - vma->first; + ASSERT_MSG(vma_offset + size <= vma->second.size, + "Shared memory exceeds bounds of mapped block"); + + const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; + const std::size_t backing_block_offset = vma->second.offset + vma_offset; + + CASCADE_RESULT(auto new_vma, + MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state)); + // Protect mirror with permissions from old region + Reprotect(new_vma, vma->second.permissions); + // Remove permissions from old region + Reprotect(vma, VMAPermission::None); + + return RESULT_SUCCESS; +} + void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { // If this ever proves to have a noticeable performance impact, allow users of the function to // specify a specific range of addresses to limit the scan to. @@ -475,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const { } u64 VMManager::GetTotalHeapUsage() const { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x0; + return heap_used; } VAddr VMManager::GetAddressSpaceBaseAddress() const { diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 84c890224..d522404fe 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -158,6 +158,14 @@ public: ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); /** + * Finds the first free address that can hold a region of the desired size. + * + * @param size Size of the desired region. + * @return The found free address. + */ + ResultVal<VAddr> FindFreeRegion(u64 size) const; + + /** * Maps a memory-mapped IO region at a given address. * * @param target The guest address to start the mapping at. @@ -178,6 +186,12 @@ public: /// Changes the permissions of a range of addresses, splitting VMAs as necessary. ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); + ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); + ResultCode HeapFree(VAddr target, u64 size); + + ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, + MemoryState state = MemoryState::Mapped); + /** * Scans all VMAs and updates the page table range of any that use the given vector as backing * memory. This should be called after any operation that causes reallocation of the vector. @@ -335,5 +349,15 @@ private: VAddr tls_io_region_base = 0; VAddr tls_io_region_end = 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 + // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. + std::shared_ptr<std::vector<u8>> heap_memory; + // The left/right bounds of the address space covered by heap_memory. + VAddr heap_start = 0; + VAddr heap_end = 0; + u64 heap_used = 0; }; } // namespace Kernel |
