aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/svc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
-rw-r--r--src/core/hle/kernel/svc.cpp802
1 files changed, 623 insertions, 179 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 1f19d5576..7cfecb68c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -20,21 +20,22 @@
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
-#include "core/settings.h"
+#include "core/memory.h"
namespace Kernel {
namespace {
@@ -63,43 +64,77 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
vm.GetNewMapRegionEndAddress());
}
+// 8 GiB
+constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
+
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
- if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
+ if (!Common::Is4KBAligned(dst_addr)) {
+ LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (!Common::Is4KBAligned(src_addr)) {
+ LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ dst_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsValidAddressRange(src_addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
+ src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
+ dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
const VAddr dst_end_address = dst_addr + size;
if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
vm_manager.GetHeapRegionEndAddress() > dst_addr) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination does not fit within the heap region, addr=0x{:016X}, "
+ "size=0x{:016X}, end_addr=0x{:016X}",
+ dst_addr, size, dst_end_address);
return ERR_INVALID_MEMORY_RANGE;
}
if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
vm_manager.GetMapRegionEndAddress() > dst_addr) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination does not fit within the map region, addr=0x{:016X}, "
+ "size=0x{:016X}, end_addr=0x{:016X}",
+ dst_addr, size, dst_end_address);
return ERR_INVALID_MEMORY_RANGE;
}
@@ -143,15 +178,28 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty
static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
- // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 4GB.
- if ((heap_size & 0xFFFFFFFE001FFFFF) != 0) {
+ // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
+ if ((heap_size % 0x200000) != 0) {
+ LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
+ heap_size);
return ERR_INVALID_SIZE;
}
- auto& process = *Core::CurrentProcess();
- const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
- CASCADE_RESULT(*heap_addr,
- process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
+ if (heap_size >= 0x200000000) {
+ LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
+ return ERR_INVALID_SIZE;
+ }
+
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
+ const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
+ const auto alloc_result =
+ vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
+
+ if (alloc_result.Failed()) {
+ return alloc_result.Code();
+ }
+
+ *heap_addr = *alloc_result;
return RESULT_SUCCESS;
}
@@ -159,20 +207,31 @@ 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)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto permission = static_cast<MemoryPermission>(prot);
if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
permission != MemoryPermission::ReadWrite) {
+ LOG_ERROR(Kernel_SVC, "Invalid memory permission specified, Got memory permission=0x{:08X}",
+ static_cast<u32>(permission));
return ERR_INVALID_MEMORY_PERMISSIONS;
}
@@ -180,11 +239,15 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
auto& vm_manager = current_process->VMManager();
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
return ERR_INVALID_ADDRESS_STATE;
}
const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
- if (iter == vm_manager.vma_map.end()) {
+ if (!vm_manager.IsValidHandle(iter)) {
+ LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr);
return ERR_INVALID_ADDRESS_STATE;
}
@@ -197,11 +260,52 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
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,
- size, state0, state1);
- return RESULT_SUCCESS;
+static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) {
+ LOG_DEBUG(Kernel_SVC,
+ "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
+ size, mask, attribute);
+
+ if (!Common::Is4KBAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
+ size);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!IsValidAddressRange(address, size)) {
+ LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
+ address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto mem_attribute = static_cast<MemoryAttribute>(attribute);
+ const auto mem_mask = static_cast<MemoryAttribute>(mask);
+ const auto attribute_with_mask = mem_attribute | mem_mask;
+
+ if (attribute_with_mask != mem_mask) {
+ LOG_ERROR(Kernel_SVC,
+ "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
+ attribute, mask);
+ return ERR_INVALID_COMBINATION;
+ }
+
+ if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
+ LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8).");
+ return ERR_INVALID_COMBINATION;
+ }
+
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
+ if (!IsInsideAddressSpace(vm_manager, address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Given address (0x{:016X}) is outside the bounds of the address space.", address);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute);
}
/// Maps a memory range into a different range.
@@ -209,15 +313,14 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto* const current_process = Core::CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
-
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
- if (result != RESULT_SUCCESS) {
+
+ if (result.IsError()) {
return result;
}
- return current_process->MirrorMemory(dst_addr, src_addr, size);
+ return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -225,20 +328,22 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto* const current_process = Core::CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
-
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
- if (result != RESULT_SUCCESS) {
+
+ if (result.IsError()) {
return result;
}
- return current_process->UnmapMemory(dst_addr, src_addr, size);
+ return vm_manager.UnmapRange(dst_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
+ port_name_address);
return ERR_NOT_FOUND;
}
@@ -246,6 +351,8 @@ 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) {
+ LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
+ port_name.size());
return ERR_OUT_OF_RANGE;
}
@@ -288,12 +395,13 @@ static ResultCode SendSyncRequest(Handle handle) {
}
/// Get the ID for the specified thread.
-static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
+static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
return ERR_INVALID_HANDLE;
}
@@ -301,18 +409,33 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
return RESULT_SUCCESS;
}
-/// Get the ID of the specified process
-static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
- LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
+/// Gets the ID of the specified process or a specified thread's owning process.
+static ResultCode GetProcessId(u64* process_id, Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
- if (!process) {
- return ERR_INVALID_HANDLE;
+ const SharedPtr<Process> process = handle_table.Get<Process>(handle);
+ if (process) {
+ *process_id = process->GetProcessID();
+ return RESULT_SUCCESS;
}
- *process_id = process->GetProcessID();
- return RESULT_SUCCESS;
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
+ if (thread) {
+ const Process* const owner_process = thread->GetOwnerProcess();
+ if (!owner_process) {
+ LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
+ return ERR_INVALID_HANDLE;
+ }
+
+ *process_id = owner_process->GetProcessID();
+ return RESULT_SUCCESS;
+ }
+
+ // NOTE: This should also handle debug objects before returning.
+
+ LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
+ return ERR_INVALID_HANDLE;
}
/// Default thread wakeup callback for WaitSynchronization
@@ -337,12 +460,18 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
handles_address, handle_count, nano_seconds);
- if (!Memory::IsValidVirtualAddress(handles_address))
+ if (!Memory::IsValidVirtualAddress(handles_address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Handle address is not a valid virtual address, handle_address=0x{:016X}",
+ handles_address);
return ERR_INVALID_POINTER;
+ }
static constexpr u64 MaxHandles = 0x40;
if (handle_count > MaxHandles) {
+ LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}",
+ MaxHandles, handle_count);
return ERR_OUT_OF_RANGE;
}
@@ -357,6 +486,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
const auto object = handle_table.Get<WaitObject>(handle);
if (object == nullptr) {
+ LOG_ERROR(Kernel_SVC, "Object is a nullptr");
return ERR_INVALID_HANDLE;
}
@@ -380,11 +510,13 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
// If a timeout value of 0 was provided, just return the Timeout error code instead of
// suspending the thread.
- if (nano_seconds == 0)
+ if (nano_seconds == 0) {
return RESULT_TIMEOUT;
+ }
- for (auto& object : objects)
+ for (auto& object : objects) {
object->AddWaitingThread(thread);
+ }
thread->SetWaitObjects(std::move(objects));
thread->SetStatus(ThreadStatus::WaitSynchAny);
@@ -405,6 +537,8 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
@@ -423,10 +557,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
holding_thread_handle, mutex_addr, requesting_thread_handle);
if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
+ mutex_addr);
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
return ERR_INVALID_ADDRESS;
}
@@ -440,10 +577,13 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
+ mutex_addr);
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
return ERR_INVALID_ADDRESS;
}
@@ -457,6 +597,7 @@ enum class BreakType : u32 {
PostNROLoad = 4,
PreNROUnload = 5,
PostNROUnload = 6,
+ CppException = 7,
};
struct BreakReason {
@@ -529,6 +670,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
+ case BreakType::CppException:
+ LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
+ break;
default:
LOG_WARNING(
Debug_Emulated,
@@ -544,6 +688,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
+ Core::System::GetInstance()
+ .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
+ .LogBacktrace();
ASSERT(false);
Core::CurrentProcess()->PrepareForTermination();
@@ -572,8 +719,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
enum class GetInfoType : u64 {
// 1.0.0+
- AllowedCpuIdBitmask = 0,
- AllowedThreadPrioBitmask = 1,
+ AllowedCPUCoreMask = 0,
+ AllowedThreadPriorityMask = 1,
MapRegionBaseAddr = 2,
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
@@ -581,7 +728,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
TotalMemoryUsage = 6,
TotalHeapUsage = 7,
IsCurrentProcessBeingDebugged = 8,
- ResourceHandleLimit = 9,
+ RegisterResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
PerformanceCounter = 0xF0000002,
@@ -601,86 +748,172 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
ThreadTickCount = 0xF0000002,
};
- const auto* current_process = Core::CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
+ const auto info_id_type = static_cast<GetInfoType>(info_id);
- switch (static_cast<GetInfoType>(info_id)) {
- case GetInfoType::AllowedCpuIdBitmask:
- *result = current_process->GetAllowedProcessorMask();
- break;
- case GetInfoType::AllowedThreadPrioBitmask:
- *result = current_process->GetAllowedThreadPriorityMask();
- break;
+ switch (info_id_type) {
+ case GetInfoType::AllowedCPUCoreMask:
+ case GetInfoType::AllowedThreadPriorityMask:
case GetInfoType::MapRegionBaseAddr:
- *result = vm_manager.GetMapRegionBaseAddress();
- break;
case GetInfoType::MapRegionSize:
- *result = vm_manager.GetMapRegionSize();
- break;
case GetInfoType::HeapRegionBaseAddr:
- *result = vm_manager.GetHeapRegionBaseAddress();
- break;
case GetInfoType::HeapRegionSize:
- *result = vm_manager.GetHeapRegionSize();
- break;
+ case GetInfoType::ASLRRegionBaseAddr:
+ case GetInfoType::ASLRRegionSize:
+ case GetInfoType::NewMapRegionBaseAddr:
+ case GetInfoType::NewMapRegionSize:
case GetInfoType::TotalMemoryUsage:
- *result = vm_manager.GetTotalMemoryUsage();
- break;
case GetInfoType::TotalHeapUsage:
- *result = vm_manager.GetTotalHeapUsage();
- break;
+ case GetInfoType::IsVirtualAddressMemoryEnabled:
+ case GetInfoType::PersonalMmHeapUsage:
+ case GetInfoType::TitleId:
+ case GetInfoType::UserExceptionContextAddr: {
+ if (info_sub_id != 0) {
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
+ if (!process) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ switch (info_id_type) {
+ case GetInfoType::AllowedCPUCoreMask:
+ *result = process->GetCoreMask();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::AllowedThreadPriorityMask:
+ *result = process->GetPriorityMask();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::MapRegionBaseAddr:
+ *result = process->VMManager().GetMapRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::MapRegionSize:
+ *result = process->VMManager().GetMapRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::HeapRegionBaseAddr:
+ *result = process->VMManager().GetHeapRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::HeapRegionSize:
+ *result = process->VMManager().GetHeapRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::ASLRRegionBaseAddr:
+ *result = process->VMManager().GetASLRRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::ASLRRegionSize:
+ *result = process->VMManager().GetASLRRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::NewMapRegionBaseAddr:
+ *result = process->VMManager().GetNewMapRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::NewMapRegionSize:
+ *result = process->VMManager().GetNewMapRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TotalMemoryUsage:
+ *result = process->VMManager().GetTotalMemoryUsage();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TotalHeapUsage:
+ *result = process->VMManager().GetTotalHeapUsage();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::IsVirtualAddressMemoryEnabled:
+ *result = process->IsVirtualMemoryEnabled();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TitleId:
+ *result = process->GetTitleID();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::UserExceptionContextAddr:
+ LOG_WARNING(Kernel_SVC,
+ "(STUBBED) Attempted to query user exception context address, returned 0");
+ *result = 0;
+ return RESULT_SUCCESS;
+
+ default:
+ break;
+ }
+
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
case GetInfoType::IsCurrentProcessBeingDebugged:
*result = 0;
- break;
+ return RESULT_SUCCESS;
+
+ case GetInfoType::RegisterResourceLimit: {
+ if (handle != 0) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (info_sub_id != 0) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ Process* const current_process = Core::CurrentProcess();
+ HandleTable& handle_table = current_process->GetHandleTable();
+ const auto resource_limit = current_process->GetResourceLimit();
+ if (!resource_limit) {
+ *result = KernelHandle::InvalidHandle;
+ // Yes, the kernel considers this a successful operation.
+ return RESULT_SUCCESS;
+ }
+
+ const auto table_result = handle_table.Create(resource_limit);
+ if (table_result.Failed()) {
+ return table_result.Code();
+ }
+
+ *result = *table_result;
+ return RESULT_SUCCESS;
+ }
+
case GetInfoType::RandomEntropy:
if (handle != 0) {
+ LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
+ handle);
return ERR_INVALID_HANDLE;
}
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
+ LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
+ Process::RANDOM_ENTROPY_SIZE, info_sub_id);
return ERR_INVALID_COMBINATION;
}
- *result = current_process->GetRandomEntropy(info_sub_id);
+ *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id);
return RESULT_SUCCESS;
- break;
- case GetInfoType::ASLRRegionBaseAddr:
- *result = vm_manager.GetASLRRegionBaseAddress();
- break;
- case GetInfoType::ASLRRegionSize:
- *result = vm_manager.GetASLRRegionSize();
- break;
- case GetInfoType::NewMapRegionBaseAddr:
- *result = vm_manager.GetNewMapRegionBaseAddress();
- break;
- case GetInfoType::NewMapRegionSize:
- *result = vm_manager.GetNewMapRegionSize();
- break;
- case GetInfoType::IsVirtualAddressMemoryEnabled:
- *result = current_process->IsVirtualMemoryEnabled();
- break;
- case GetInfoType::TitleId:
- *result = current_process->GetTitleID();
- break;
+
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
*result = 0;
- break;
- case GetInfoType::UserExceptionContextAddr:
- LOG_WARNING(Kernel_SVC,
- "(STUBBED) Attempted to query user exception context address, returned 0");
- *result = 0;
- break;
+ return RESULT_SUCCESS;
+
case GetInfoType::ThreadTickCount: {
constexpr u64 num_cpus = 4;
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
+ LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
+ info_sub_id);
return ERR_INVALID_COMBINATION;
}
const auto thread =
- current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
+ Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
+ static_cast<Handle>(handle));
return ERR_INVALID_HANDLE;
}
@@ -700,19 +933,45 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
*result = out_ticks;
- break;
+ return RESULT_SUCCESS;
}
+
default:
LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ERR_INVALID_ENUM_VALUE;
}
-
- return RESULT_SUCCESS;
}
/// Sets the thread activity
-static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
- LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
+static ResultCode SetThreadActivity(Handle handle, u32 activity) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
+ if (activity > static_cast<u32>(ThreadActivity::Paused)) {
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ const auto* current_process = Core::CurrentProcess();
+ const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (thread->GetOwnerProcess() != current_process) {
+ LOG_ERROR(Kernel_SVC,
+ "The current process does not own the current thread, thread_handle={:08X} "
+ "thread_pid={}, "
+ "current_process_pid={}",
+ handle, thread->GetOwnerProcess()->GetProcessID(),
+ current_process->GetProcessID());
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (thread == GetCurrentThread()) {
+ LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
+ return ERR_BUSY;
+ }
+
+ thread->SetActivity(static_cast<ThreadActivity>(activity));
return RESULT_SUCCESS;
}
@@ -723,15 +982,23 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
const auto* current_process = Core::CurrentProcess();
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
if (thread->GetOwnerProcess() != current_process) {
+ LOG_ERROR(Kernel_SVC,
+ "The current process does not own the current thread, thread_handle={:08X} "
+ "thread_pid={}, "
+ "current_process_pid={}",
+ handle, thread->GetOwnerProcess()->GetProcessID(),
+ current_process->GetProcessID());
return ERR_INVALID_HANDLE;
}
if (thread == GetCurrentThread()) {
- return ERR_ALREADY_REGISTERED;
+ LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
+ return ERR_BUSY;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
@@ -751,9 +1018,12 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called");
+
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
@@ -763,7 +1033,13 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) {
/// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Handle handle, u32 priority) {
+ LOG_TRACE(Kernel_SVC, "called");
+
if (priority > THREADPRIO_LOWEST) {
+ LOG_ERROR(
+ Kernel_SVC,
+ "An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
+ THREADPRIO_LOWEST, priority, handle);
return ERR_INVALID_THREAD_PRIORITY;
}
@@ -771,6 +1047,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
@@ -793,32 +1070,46 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
shared_memory_handle, addr, size, permissions);
if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto permissions_type = static_cast<MemoryPermission>(permissions);
if (permissions_type != MemoryPermission::Read &&
permissions_type != MemoryPermission::ReadWrite) {
- LOG_ERROR(Kernel_SVC, "Invalid permissions=0x{:08X}", permissions);
+ LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
+ permissions);
return ERR_INVALID_MEMORY_PERMISSIONS;
}
auto* const current_process = Core::CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
+ LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
+ shared_memory_handle);
return ERR_INVALID_HANDLE;
}
const auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}",
+ addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
@@ -830,61 +1121,82 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
shared_memory_handle, addr, size);
if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
auto* const current_process = Core::CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
+ LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
+ shared_memory_handle);
return ERR_INVALID_HANDLE;
}
const auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}",
+ addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
return shared_memory->Unmap(*current_process, addr);
}
-/// Query process memory
-static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
- Handle process_handle, u64 addr) {
+static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address,
+ Handle process_handle, VAddr address) {
+ LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
+ LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
+ process_handle);
return ERR_INVALID_HANDLE;
}
- auto vma = process->VMManager().FindVMA(addr);
- memory_info->attributes = 0;
- if (vma == process->VMManager().vma_map.end()) {
- memory_info->base_address = 0;
- memory_info->permission = static_cast<u32>(VMAPermission::None);
- memory_info->size = 0;
- memory_info->type = static_cast<u32>(MemoryState::Unmapped);
- } else {
- memory_info->base_address = vma->second.base;
- memory_info->permission = static_cast<u32>(vma->second.permissions);
- memory_info->size = vma->second.size;
- memory_info->type = static_cast<u32>(vma->second.meminfo_state);
- }
- LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr);
+ const auto& vm_manager = process->VMManager();
+ const MemoryInfo memory_info = vm_manager.QueryMemory(address);
+
+ Memory::Write64(memory_info_address, memory_info.base_address);
+ Memory::Write64(memory_info_address + 8, memory_info.size);
+ Memory::Write32(memory_info_address + 16, memory_info.state);
+ Memory::Write32(memory_info_address + 20, memory_info.attributes);
+ Memory::Write32(memory_info_address + 24, memory_info.permission);
+ Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count);
+ Memory::Write32(memory_info_address + 28, memory_info.device_ref_count);
+ Memory::Write32(memory_info_address + 36, 0);
+
+ // Page info appears to be currently unused by the kernel and is always set to zero.
+ Memory::Write32(page_info_address, 0);
+
return RESULT_SUCCESS;
}
-/// Query memory
-static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) {
- LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr);
- return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr);
+static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address,
+ VAddr query_address) {
+ LOG_TRACE(Kernel_SVC,
+ "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
+ "query_address=0x{:016X}",
+ memory_info_address, page_info_address, query_address);
+
+ return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess,
+ query_address);
}
/// Exits the current process
@@ -906,29 +1218,42 @@ static void ExitProcess() {
/// Creates a new thread
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
u32 priority, s32 processor_id) {
- if (priority > THREADPRIO_LOWEST) {
- return ERR_INVALID_THREAD_PRIORITY;
- }
+ LOG_TRACE(Kernel_SVC,
+ "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
+ "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
+ entry_point, arg, stack_top, priority, processor_id, *out_handle);
auto* const current_process = Core::CurrentProcess();
- if (processor_id == THREADPROCESSORID_DEFAULT) {
- // Set the target CPU to the one specified in the process' exheader.
- processor_id = current_process->GetDefaultProcessorID();
- ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
+ if (processor_id == THREADPROCESSORID_IDEAL) {
+ // Set the target CPU to the one specified by the process.
+ processor_id = current_process->GetIdealCore();
+ ASSERT(processor_id != THREADPROCESSORID_IDEAL);
}
- switch (processor_id) {
- case THREADPROCESSORID_0:
- case THREADPROCESSORID_1:
- case THREADPROCESSORID_2:
- case THREADPROCESSORID_3:
- break;
- default:
+ if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
+ const u64 core_mask = current_process->GetCoreMask();
+ if ((core_mask | (1ULL << processor_id)) != core_mask) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
+ return ERR_INVALID_PROCESSOR_ID;
+ }
+
+ if (priority > THREADPRIO_LOWEST) {
+ LOG_ERROR(Kernel_SVC,
+ "Invalid thread priority specified ({}). Must be within the range 0-64",
+ priority);
+ return ERR_INVALID_THREAD_PRIORITY;
+ }
+
+ if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
+ return ERR_INVALID_THREAD_PRIORITY;
+ }
+
const std::string name = fmt::format("thread-{:X}", entry_point);
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
@@ -937,6 +1262,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
if (new_guest_handle.Failed()) {
+ LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
+ new_guest_handle.Code().raw);
return new_guest_handle.Code();
}
thread->SetGuestHandle(*new_guest_handle);
@@ -944,11 +1271,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
- LOG_TRACE(Kernel_SVC,
- "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
- "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
- entry_point, name, arg, stack_top, priority, processor_id, *out_handle);
-
return RESULT_SUCCESS;
}
@@ -959,13 +1281,18 @@ static ResultCode StartThread(Handle thread_handle) {
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
thread->ResumeFromWait();
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+
+ if (thread->GetStatus() == ThreadStatus::Ready) {
+ Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+ }
return RESULT_SUCCESS;
}
@@ -982,18 +1309,38 @@ static void ExitThread() {
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
- // Don't attempt to yield execution if there are no available threads to run,
- // this way we avoid a useless reschedule to the idle thread.
- if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads())
- return;
+ enum class SleepType : s64 {
+ YieldWithoutLoadBalancing = 0,
+ YieldWithLoadBalancing = -1,
+ YieldAndWaitForLoadBalancing = -2,
+ };
- // Sleep current thread and check for next thread to schedule
- WaitCurrentThread_Sleep();
+ if (nanoseconds <= 0) {
+ auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
+ switch (static_cast<SleepType>(nanoseconds)) {
+ case SleepType::YieldWithoutLoadBalancing:
+ scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
+ break;
+ case SleepType::YieldWithLoadBalancing:
+ scheduler.YieldWithLoadBalancing(GetCurrentThread());
+ break;
+ case SleepType::YieldAndWaitForLoadBalancing:
+ scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
+ break;
+ default:
+ UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
+ }
+ } else {
+ // Sleep current thread and check for next thread to schedule
+ WaitCurrentThread_Sleep();
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ }
- Core::System::GetInstance().PrepareReschedule();
+ // Reschedule all CPU cores
+ for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
+ Core::System::GetInstance().CpuCore(i).PrepareReschedule();
}
/// Wait process wide key atomic
@@ -1138,10 +1485,12 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
address, type, value, timeout);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
// If the address is not properly aligned to 4 bytes, return invalid address.
- if (address % sizeof(u32) != 0) {
+ if (!Common::IsWordAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
@@ -1153,6 +1502,10 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
case AddressArbiter::ArbitrationType::WaitIfEqual:
return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
default:
+ LOG_ERROR(Kernel_SVC,
+ "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
+ "or WaitIfEqual but got {}",
+ type);
return ERR_INVALID_ENUM_VALUE;
}
}
@@ -1163,10 +1516,12 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
address, type, value, num_to_wake);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
// If the address is not properly aligned to 4 bytes, return invalid address.
- if (address % sizeof(u32) != 0) {
+ if (!Common::IsWordAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
@@ -1179,12 +1534,18 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
num_to_wake);
default:
+ LOG_ERROR(Kernel_SVC,
+ "Invalid signal type, expected Signal, IncrementAndSignalIfEqual "
+ "or ModifyByWaitingCountAndSignalIfEqual but got {}",
+ type);
return ERR_INVALID_ENUM_VALUE;
}
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
static u64 GetSystemTick() {
+ LOG_TRACE(Kernel_SVC, "called");
+
const u64 result{CoreTiming::GetTicks()};
// Advance time to defeat dumb games that busy-wait for the frame to end.
@@ -1201,17 +1562,24 @@ static ResultCode CloseHandle(Handle handle) {
return handle_table.Close(handle);
}
-/// Reset an event
+/// Clears the signaled state of an event or process.
static ResultCode ResetSignal(Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- auto event = handle_table.Get<Event>(handle);
- ASSERT(event != nullptr);
+ auto event = handle_table.Get<ReadableEvent>(handle);
+ if (event) {
+ return event->Reset();
+ }
+
+ auto process = handle_table.Get<Process>(handle);
+ if (process) {
+ return process->ClearSignalState();
+ }
- event->Clear();
- return RESULT_SUCCESS;
+ LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle);
+ return ERR_INVALID_HANDLE;
}
/// Creates a TransferMemory object
@@ -1244,9 +1612,9 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
}
auto& kernel = Core::System::GetInstance().Kernel();
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- const auto shared_mem_handle = SharedMemory::Create(
- kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
+ auto process = kernel.CurrentProcess();
+ auto& handle_table = process->GetHandleTable();
+ const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr);
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
return RESULT_SUCCESS;
@@ -1258,6 +1626,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
@@ -1268,26 +1638,29 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
}
static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
mask, core);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
- if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
- const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
+ if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
+ const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
- ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
+ ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
- // Set the target CPU to the one specified in the process' exheader.
- core = default_processor_id;
+ // Set the target CPU to the ideal core specified by the process.
+ core = ideal_cpu_core;
mask = 1ULL << core;
}
if (mask == 0) {
+ LOG_ERROR(Kernel_SVC, "Mask is 0");
return ERR_INVALID_COMBINATION;
}
@@ -1297,11 +1670,14 @@ 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)) {
+ LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core);
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) {
+ LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}",
+ core, mask);
return ERR_INVALID_COMBINATION;
}
@@ -1314,44 +1690,109 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
u32 remote_permissions) {
LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
local_permissions, remote_permissions);
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
+ return ERR_INVALID_SIZE;
+ }
- // Size must be a multiple of 4KB and be less than or equal to
- // approx. 8 GB (actually (1GB - 512B) * 8)
- if (size == 0 || (size & 0xFFFFFFFE00000FFF) != 0) {
+ if (size >= MAIN_MEMORY_SIZE) {
+ LOG_ERROR(Kernel_SVC, "Size is not less than 8GB, 0x{:016X}", size);
return ERR_INVALID_SIZE;
}
const auto local_perms = static_cast<MemoryPermission>(local_permissions);
if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) {
+ LOG_ERROR(Kernel_SVC,
+ "Invalid local memory permissions, expected Read or ReadWrite but got "
+ "local_permissions={}",
+ static_cast<u32>(local_permissions));
return ERR_INVALID_MEMORY_PERMISSIONS;
}
const auto remote_perms = static_cast<MemoryPermission>(remote_permissions);
if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite &&
remote_perms != MemoryPermission::DontCare) {
+ LOG_ERROR(Kernel_SVC,
+ "Invalid remote memory permissions, expected Read, ReadWrite or DontCare but got "
+ "remote_permissions={}",
+ static_cast<u32>(remote_permissions));
return ERR_INVALID_MEMORY_PERMISSIONS;
}
auto& kernel = Core::System::GetInstance().Kernel();
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- auto shared_mem_handle =
- SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
- local_perms, remote_perms);
+ auto process = kernel.CurrentProcess();
+ auto& handle_table = process->GetHandleTable();
+ auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms);
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
return RESULT_SUCCESS;
}
+static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) {
+ LOG_DEBUG(Kernel_SVC, "called");
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ const auto [readable_event, writable_event] =
+ WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
+
+ HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
+
+ const auto write_create_result = handle_table.Create(writable_event);
+ if (write_create_result.Failed()) {
+ return write_create_result.Code();
+ }
+ *write_handle = *write_create_result;
+
+ const auto read_create_result = handle_table.Create(readable_event);
+ if (read_create_result.Failed()) {
+ handle_table.Close(*write_create_result);
+ return read_create_result.Code();
+ }
+ *read_handle = *read_create_result;
+
+ LOG_DEBUG(Kernel_SVC,
+ "successful. Writable event handle=0x{:08X}, Readable event handle=0x{:08X}",
+ *write_create_result, *read_create_result);
+ return RESULT_SUCCESS;
+}
+
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- SharedPtr<Event> evt = handle_table.Get<Event>(handle);
- if (evt == nullptr) {
+
+ auto writable_event = handle_table.Get<WritableEvent>(handle);
+ if (writable_event) {
+ writable_event->Clear();
+ return RESULT_SUCCESS;
+ }
+
+ auto readable_event = handle_table.Get<ReadableEvent>(handle);
+ if (readable_event) {
+ readable_event->Clear();
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle);
+ return ERR_INVALID_HANDLE;
+}
+
+static ResultCode SignalEvent(Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
+
+ HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable();
+ auto writable_event = handle_table.Get<WritableEvent>(handle);
+
+ if (!writable_event) {
+ LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle);
return ERR_INVALID_HANDLE;
}
- evt->Clear();
+ writable_event->Signal();
return RESULT_SUCCESS;
}
@@ -1366,11 +1807,14 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const auto process = handle_table.Get<Process>(process_handle);
if (!process) {
+ LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
+ process_handle);
return ERR_INVALID_HANDLE;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
+ LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
return ERR_INVALID_ENUM_VALUE;
}
@@ -1487,7 +1931,7 @@ static const FunctionDef SVC_Table[] = {
{0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"},
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
- {0x11, nullptr, "SignalEvent"},
+ {0x11, SvcWrap<SignalEvent>, "SignalEvent"},
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
@@ -1539,7 +1983,7 @@ static const FunctionDef SVC_Table[] = {
{0x42, nullptr, "ReplyAndReceiveLight"},
{0x43, nullptr, "ReplyAndReceive"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
- {0x45, nullptr, "CreateEvent"},
+ {0x45, SvcWrap<CreateEvent>, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
@@ -1588,7 +2032,7 @@ static const FunctionDef SVC_Table[] = {
{0x73, nullptr, "SetProcessMemoryPermission"},
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
- {0x76, nullptr, "QueryProcessMemory"},
+ {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, nullptr, "MapProcessCodeMemory"},
{0x78, nullptr, "UnmapProcessCodeMemory"},
{0x79, nullptr, "CreateProcess"},