aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/errors.h2
-rw-r--r--src/core/hle/kernel/object.h3
-rw-r--r--src/core/hle/kernel/process.cpp29
-rw-r--r--src/core/hle/kernel/process.h113
-rw-r--r--src/core/hle/kernel/scheduler.cpp16
-rw-r--r--src/core/hle/kernel/scheduler.h4
-rw-r--r--src/core/hle/kernel/shared_memory.cpp16
-rw-r--r--src/core/hle/kernel/svc.cpp95
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp5
-rw-r--r--src/core/hle/kernel/vm_manager.cpp180
-rw-r--r--src/core/hle/kernel/vm_manager.h100
-rw-r--r--src/core/hle/kernel/wait_object.h2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp3
-rw-r--r--src/core/hle/service/fatal/fatal.cpp4
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp1
-rw-r--r--src/core/hle/service/ns/pl_u.cpp6
-rw-r--r--src/core/hle/service/sm/controller.cpp3
-rw-r--r--src/core/hle/service/vi/vi.cpp20
20 files changed, 494 insertions, 114 deletions
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 8c2be2681..e5fa67ae8 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -31,6 +31,7 @@ enum {
TooLarge = 119,
InvalidEnumValue = 120,
NoSuchEntry = 121,
+ AlreadyRegistered = 122,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
@@ -58,6 +59,7 @@ constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
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);
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 121f741fd..dc9fc8470 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -8,6 +8,7 @@
#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"
@@ -34,14 +35,22 @@ 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();
+ is_64bit_process = metadata.Is64BitProgram();
+ 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];
@@ -119,7 +128,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();
@@ -185,6 +194,7 @@ static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
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
@@ -197,18 +207,17 @@ VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
- vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
- tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal);
+ 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 Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
- available_slot * Memory::TLS_ENTRY_SIZE;
+ 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 - Memory::TLS_AREA_VADDR;
+ 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;
@@ -232,8 +241,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;
}
@@ -268,8 +277,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 04d74e572..590e0c73d 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,16 @@ public:
return HANDLE_TYPE;
}
+ /// Gets a reference to the process' memory manager.
+ Kernel::VMManager& VMManager() {
+ return vm_manager;
+ }
+
+ /// Gets a const reference to the process' memory manager.
+ const Kernel::VMManager& VMManager() const {
+ return vm_manager;
+ }
+
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -141,29 +155,52 @@ public:
return process_id;
}
- /// Title ID corresponding to the process
- u64 program_id;
+ /// Gets the title ID corresponding to this process.
+ u64 GetTitleID() const {
+ return program_id;
+ }
- /// Resource limit descriptor for this process
- SharedPtr<ResourceLimit> resource_limit;
+ /// Gets the resource limit descriptor for this process
+ ResourceLimit& GetResourceLimit() {
+ return *resource_limit;
+ }
- /// The process may only call SVCs which have the corresponding bit set.
- std::bitset<0x80> svc_access_mask;
- /// Maximum size of the handle table for the process.
- unsigned int handle_table_size = 0x200;
- /// Special memory ranges mapped into this processes address space. This is used to give
- /// processes access to specific I/O regions and device memory.
- boost::container::static_vector<AddressMapping, 8> address_mappings;
- ProcessFlags flags;
- /// Kernel compatibility version for this process
- u16 kernel_version = 0;
- /// The default CPU for this process, threads are scheduled on this cpu by default.
- u8 ideal_processor = 0;
- /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
- /// this value from the process header.
- u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
- u32 allowed_thread_priority_mask = 0xFFFFFFFF;
- u32 is_virtual_address_memory_enabled = 0;
+ /// Gets the resource limit descriptor for this process
+ const ResourceLimit& GetResourceLimit() const {
+ return *resource_limit;
+ }
+
+ /// Gets the default CPU ID for this process
+ u8 GetDefaultProcessorID() const {
+ return ideal_processor;
+ }
+
+ /// Gets the bitmask of allowed CPUs that this process' threads can run on.
+ u32 GetAllowedProcessorMask() const {
+ return allowed_processor_mask;
+ }
+
+ /// Gets the bitmask of allowed thread priorities.
+ u32 GetAllowedThreadPriorityMask() const {
+ return allowed_thread_priority_mask;
+ }
+
+ u32 IsVirtualMemoryEnabled() const {
+ return is_virtual_address_memory_enabled;
+ }
+
+ /// Whether this process is an AArch64 or AArch32 process.
+ bool Is64BitProcess() const {
+ return is_64bit_process;
+ }
+
+ /**
+ * 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);
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -200,18 +237,43 @@ public:
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
- VMManager vm_manager;
-
private:
explicit Process(KernelCore& kernel);
~Process() override;
+ /// Memory manager for this process.
+ Kernel::VMManager vm_manager;
+
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
+ /// Title ID corresponding to the process
+ u64 program_id;
+
+ /// Resource limit descriptor for this process
+ SharedPtr<ResourceLimit> resource_limit;
+
+ /// The process may only call SVCs which have the corresponding bit set.
+ std::bitset<0x80> svc_access_mask;
+ /// Maximum size of the handle table for the process.
+ u32 handle_table_size = 0x200;
+ /// Special memory ranges mapped into this processes address space. This is used to give
+ /// processes access to specific I/O regions and device memory.
+ boost::container::static_vector<AddressMapping, 8> address_mappings;
+ ProcessFlags flags;
+ /// Kernel compatibility version for this process
+ u16 kernel_version = 0;
+ /// The default CPU for this process, threads are scheduled on this cpu by default.
+ u8 ideal_processor = 0;
+ /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
+ /// this value from the process header.
+ u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
+ 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
@@ -230,6 +292,11 @@ private:
/// This vector will grow as more pages are allocated for new threads.
std::vector<std::bitset<8>> tls_slots;
+ /// Whether or not this process is AArch64, or AArch32.
+ /// By default, we currently assume this is true, unless otherwise
+ /// specified by metadata provided to the process during loading.
+ bool is_64bit_process = true;
+
std::string name;
};
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 69c812f16..1e82cfffb 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
@@ -88,13 +88,13 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_process != current_thread->owner_process) {
Core::CurrentProcess() = current_thread->owner_process;
- SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
+ SetCurrentPageTable(&Core::CurrentProcess()->VMManager().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..d061e6155 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"
@@ -34,11 +35,11 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
// Refresh the address mappings for the current process.
if (Core::CurrentProcess() != nullptr) {
- Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(
+ Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
}
} else {
- auto& vm_manager = shared_memory->owner_process->vm_manager;
+ auto& vm_manager = shared_memory->owner_process->VMManager();
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address);
@@ -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()->VMManager().GetHeapRegionBaseAddress() + offset;
return shared_memory;
}
@@ -105,7 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
VAddr target_address = address;
// Map the memory block into the target process
- auto result = target_process->vm_manager.MapMemoryBlock(
+ auto result = target_process->VMManager().MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
if (result.Failed()) {
LOG_ERROR(
@@ -115,14 +117,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return result.Code();
}
- return target_process->vm_manager.ReprotectRange(target_address, size,
- ConvertPermissions(permissions));
+ return target_process->VMManager().ReprotectRange(target_address, size,
+ ConvertPermissions(permissions));
}
ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
// mapped to a SharedMemory.
- return target_process->vm_manager.UnmapRange(address, size);
+ return target_process->VMManager().UnmapRange(address, size);
}
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index c9d212a4c..1cdaa740a 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.VMManager().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;
}
@@ -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->VMManager();
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:
- *result = Core::CurrentProcess()->allowed_processor_mask;
+ *result = current_process->GetAllowedProcessorMask();
break;
case GetInfoType::AllowedThreadPrioBitmask:
- *result = Core::CurrentProcess()->allowed_thread_priority_mask;
+ *result = current_process->GetAllowedThreadPriorityMask();
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->IsVirtualMemoryEnabled();
break;
case GetInfoType::TitleId:
- *result = Core::CurrentProcess()->program_id;
+ *result = current_process->GetTitleID();
break;
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
@@ -400,8 +415,36 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
}
/// Gets the thread context
-static ResultCode GetThreadContext(Handle handle, VAddr addr) {
- LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr);
+static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
+ if (!thread) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ const auto current_process = Core::CurrentProcess();
+ if (thread->owner_process != current_process) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (thread == GetCurrentThread()) {
+ return ERR_ALREADY_REGISTERED;
+ }
+
+ Core::ARM_Interface::ThreadContext ctx = thread->context;
+ // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
+ ctx.pstate &= 0xFF0FFE20;
+
+ // If 64-bit, we can just write the context registers directly and we're good.
+ // However, if 32-bit, we have to ensure some registers are zeroed out.
+ if (!current_process->Is64BitProcess()) {
+ std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0);
+ std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
+ }
+
+ Memory::WriteBlock(thread_context, &ctx, sizeof(ctx));
return RESULT_SUCCESS;
}
@@ -429,8 +472,8 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
- SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
- if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) {
+ const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
+ if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_NOT_AUTHORIZED;
}
@@ -504,9 +547,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
if (!process) {
return ERR_INVALID_HANDLE;
}
- auto vma = process->vm_manager.FindVMA(addr);
+ auto vma = process->VMManager().FindVMA(addr);
memory_info->attributes = 0;
- if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) {
+ if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) {
memory_info->base_address = 0;
memory_info->permission = static_cast<u32>(VMAPermission::None);
memory_info->size = 0;
@@ -553,14 +596,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_INVALID_THREAD_PRIORITY;
}
- SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
- if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) {
+ const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
+ if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_NOT_AUTHORIZED;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
- processor_id = Core::CurrentProcess()->ideal_processor;
+ processor_id = Core::CurrentProcess()->GetDefaultProcessorID();
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
@@ -887,10 +930,10 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
}
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
- ASSERT(thread->owner_process->ideal_processor !=
+ ASSERT(thread->owner_process->GetDefaultProcessorID() !=
static_cast<u8>(THREADPROCESSORID_DEFAULT));
// Set the target CPU to the one specified in the process' exheader.
- core = thread->owner_process->ideal_processor;
+ core = thread->owner_process->GetDefaultProcessorID();
mask = 1ull << core;
}
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index fea9ba5ea..22712e64f 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -64,6 +64,11 @@ void SvcWrap() {
FuncReturn(func(Param(0), (s32)Param(1)).raw);
}
+template <ResultCode func(u64, u32)>
+void SvcWrap() {
+ FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
+}
+
template <ResultCode func(u64*, u64)>
void SvcWrap() {
u64 param_1 = 0;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 315f65338..b5c16cfbb 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -259,11 +259,12 @@ void Thread::BoostPriority(u32 priority) {
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
Process& owner_process) {
// Setup page table so we can write to memory
- SetCurrentPageTable(&owner_process.vm_manager.page_table);
+ SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
+ const VAddr stack_top = owner_process.VMManager().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/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;
}
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 80ed4b152..6073f4ecd 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -10,6 +10,7 @@
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -65,7 +66,7 @@ private:
void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(renderer->GetState());
+ rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
LOG_DEBUG(Service_Audio, "called");
}
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 6de7edf9e..2f15ac2a6 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -6,11 +6,11 @@
#include <cstring>
#include <ctime>
#include <fmt/time.h>
-#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/swap.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/fatal/fatal.h"
@@ -51,7 +51,7 @@ enum class FatalType : u32 {
};
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
- const auto title_id = Core::CurrentProcess()->program_id;
+ const auto title_id = Core::CurrentProcess()->GetTitleID();
std::string crash_report =
fmt::format("Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index f8d2127d9..8c07a05c2 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/hid.h"
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index c1737defa..261ad539c 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -4,6 +4,7 @@
#include <chrono>
#include <ctime>
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nim/nim.h"
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 1069d103f..4b2f758a8 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -317,9 +317,9 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
- Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
- SHARED_FONT_MEM_SIZE,
- Kernel::MemoryState::Shared);
+ Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
+ SHARED_FONT_MEM_SIZE,
+ Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = Core::System::GetInstance().Kernel();
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index cdf328a26..98f6e4111 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -2,8 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/service/sm/controller.h"
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 2ee60f1ec..bbc02abcc 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -612,7 +612,7 @@ public:
{3000, nullptr, "ListDisplayModes"},
{3001, nullptr, "ListDisplayRgbRanges"},
{3002, nullptr, "ListDisplayContentTypes"},
- {3200, nullptr, "GetDisplayMode"},
+ {3200, &ISystemDisplayService::GetDisplayMode, "GetDisplayMode"},
{3201, nullptr, "SetDisplayMode"},
{3202, nullptr, "GetDisplayUnderscan"},
{3203, nullptr, "SetDisplayUnderscan"},
@@ -663,6 +663,24 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
visibility);
}
+
+ void GetDisplayMode(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+
+ if (Settings::values.use_docked_mode) {
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
+ } else {
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
+ }
+
+ rb.PushRaw<float>(60.0f);
+ rb.Push<u32>(0);
+
+ LOG_DEBUG(Service_VI, "called");
+ }
};
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {