SVC: use context and generic templates

This commit is contained in:
Weiyi Wang 2018-11-05 10:38:35 -05:00
parent 28513c5177
commit aec8b1e375
8 changed files with 568 additions and 483 deletions

View file

@ -116,7 +116,6 @@ add_library(core STATIC
hle/kernel/errors.h hle/kernel/errors.h
hle/kernel/event.cpp hle/kernel/event.cpp
hle/kernel/event.h hle/kernel/event.h
hle/kernel/function_wrappers.h
hle/kernel/handle_table.cpp hle/kernel/handle_table.cpp
hle/kernel/handle_table.h hle/kernel/handle_table.h
hle/kernel/hle_ipc.cpp hle/kernel/hle_ipc.cpp
@ -148,6 +147,7 @@ add_library(core STATIC
hle/kernel/shared_page.h hle/kernel/shared_page.h
hle/kernel/svc.cpp hle/kernel/svc.cpp
hle/kernel/svc.h hle/kernel/svc.h
hle/kernel/svc_wrapper.h
hle/kernel/thread.cpp hle/kernel/thread.cpp
hle/kernel/thread.h hle/kernel/thread.h
hle/kernel/timer.cpp hle/kernel/timer.cpp

View file

@ -72,7 +72,7 @@ private:
class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks { class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks {
public: public:
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) explicit DynarmicUserCallbacks(ARM_Dynarmic& parent)
: parent(parent), timing(parent.system.CoreTiming()) {} : parent(parent), timing(parent.system.CoreTiming()), svc_context(parent.system) {}
~DynarmicUserCallbacks() = default; ~DynarmicUserCallbacks() = default;
std::uint8_t MemoryRead8(VAddr vaddr) override { std::uint8_t MemoryRead8(VAddr vaddr) override {
@ -123,7 +123,7 @@ public:
} }
void CallSVC(std::uint32_t swi) override { void CallSVC(std::uint32_t swi) override {
Kernel::CallSVC(swi); svc_context.CallSVC(swi);
} }
void ExceptionRaised(VAddr pc, Dynarmic::A32::Exception exception) override { void ExceptionRaised(VAddr pc, Dynarmic::A32::Exception exception) override {
@ -158,6 +158,7 @@ public:
ARM_Dynarmic& parent; ARM_Dynarmic& parent;
Core::Timing& timing; Core::Timing& timing;
Kernel::SVC svc_context;
}; };
ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode) ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode)

View file

@ -3864,7 +3864,7 @@ SWI_INST : {
cpu->NumInstrsToExecute = cpu->NumInstrsToExecute =
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
num_instrs = 0; num_instrs = 0;
Kernel::CallSVC(inst_cream->num & 0xFFFF); Kernel::SVC{Core::System::GetInstance()}.CallSVC(inst_cream->num & 0xFFFF);
// The kernel would call ERET to get here, which clears exclusive memory state. // The kernel would call ERET to get here, which clears exclusive memory state.
cpu->UnsetExclusiveMemoryAddress(); cpu->UnsetExclusiveMemoryAddress();
} }

View file

@ -172,6 +172,8 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
timing = std::make_unique<Timing>(); timing = std::make_unique<Timing>();
kernel = std::make_unique<Kernel::KernelSystem>(system_mode);
if (Settings::values.use_cpu_jit) { if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE); cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE);
@ -197,7 +199,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this); archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
HW::Init(); HW::Init();
kernel = std::make_unique<Kernel::KernelSystem>(system_mode);
Service::Init(*this); Service::Init(*this);
GDBStub::Init(); GDBStub::Init();

View file

@ -1,265 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace HLE {
static inline u32 Param(int n) {
return Core::CPU().GetReg(n);
}
/**
* HLE a function return from the current ARM11 userland process
* @param res Result to return
*/
static inline void FuncReturn(u32 res) {
Core::CPU().SetReg(0, res);
}
/**
* HLE a function return (64-bit) from the current ARM11 userland process
* @param res Result to return (64-bit)
* @todo Verify that this function is correct
*/
static inline void FuncReturn64(u64 res) {
Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF));
Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type ResultCode
template <ResultCode func(u32, u32, u32, u32)>
void Wrap() {
FuncReturn(func(Param(0), Param(1), Param(2), Param(3)).raw);
}
template <ResultCode func(u32*, u32, u32, u32, u32, u32)>
void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(0), Param(1), Param(2), Param(3), Param(4)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32*, u32, u32, u32, u32, s32)>
void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(0), Param(1), Param(2), Param(3), Param(4)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(s32*, VAddr, s32, bool, s64)>
void Wrap() {
s32 param_1 = 0;
s32 retval =
func(&param_1, Param(1), (s32)Param(2), (Param(3) != 0), (((s64)Param(4) << 32) | Param(0)))
.raw;
Core::CPU().SetReg(1, (u32)param_1);
FuncReturn(retval);
}
template <ResultCode func(s32*, VAddr, s32, u32)>
void Wrap() {
s32 param_1 = 0;
u32 retval = func(&param_1, Param(1), (s32)Param(2), Param(3)).raw;
Core::CPU().SetReg(1, (u32)param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, u32, u32, u32, s64)>
void Wrap() {
FuncReturn(
func(Param(0), Param(1), Param(2), Param(3), (((s64)Param(5) << 32) | Param(4))).raw);
}
template <ResultCode func(u32*)>
void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, s64)>
void Wrap() {
s32 retval = func(Param(0), (((s64)Param(3) << 32) | Param(2))).raw;
FuncReturn(retval);
}
template <ResultCode func(Kernel::MemoryInfo*, Kernel::PageInfo*, u32)>
void Wrap() {
Kernel::MemoryInfo memory_info = {};
Kernel::PageInfo page_info = {};
u32 retval = func(&memory_info, &page_info, Param(2)).raw;
Core::CPU().SetReg(1, memory_info.base_address);
Core::CPU().SetReg(2, memory_info.size);
Core::CPU().SetReg(3, memory_info.permission);
Core::CPU().SetReg(4, memory_info.state);
Core::CPU().SetReg(5, page_info.flags);
FuncReturn(retval);
}
template <ResultCode func(Kernel::MemoryInfo*, Kernel::PageInfo*, Kernel::Handle, u32)>
void Wrap() {
Kernel::MemoryInfo memory_info = {};
Kernel::PageInfo page_info = {};
u32 retval = func(&memory_info, &page_info, Param(2), Param(3)).raw;
Core::CPU().SetReg(1, memory_info.base_address);
Core::CPU().SetReg(2, memory_info.size);
Core::CPU().SetReg(3, memory_info.permission);
Core::CPU().SetReg(4, memory_info.state);
Core::CPU().SetReg(5, page_info.flags);
FuncReturn(retval);
}
template <ResultCode func(s32*, u32)>
void Wrap() {
s32 param_1 = 0;
u32 retval = func(&param_1, Param(1)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, s32)>
void Wrap() {
FuncReturn(func(Param(0), (s32)Param(1)).raw);
}
template <ResultCode func(u32*, u32)>
void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(1)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32)>
void Wrap() {
FuncReturn(func(Param(0)).raw);
}
template <ResultCode func(u32*, s32, s32)>
void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(s32*, u32, s32)>
void Wrap() {
s32 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(s64*, u32, s32)>
void Wrap() {
s64 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2)).raw;
Core::CPU().SetReg(1, (u32)param_1);
Core::CPU().SetReg(2, (u32)(param_1 >> 32));
FuncReturn(retval);
}
template <ResultCode func(u32*, u32, u32, u32, u32)>
void Wrap() {
u32 param_1 = 0;
// The last parameter is passed in R0 instead of R4
u32 retval = func(&param_1, Param(1), Param(2), Param(3), Param(0)).raw;
Core::CPU().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, s64, s64)>
void Wrap() {
s64 param1 = ((u64)Param(3) << 32) | Param(2);
s64 param2 = ((u64)Param(4) << 32) | Param(1);
FuncReturn(func(Param(0), param1, param2).raw);
}
template <ResultCode func(s64*, Kernel::Handle, u32)>
void Wrap() {
s64 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2)).raw;
Core::CPU().SetReg(1, (u32)param_1);
Core::CPU().SetReg(2, (u32)(param_1 >> 32));
FuncReturn(retval);
}
template <ResultCode func(Kernel::Handle, u32)>
void Wrap() {
FuncReturn(func(Param(0), Param(1)).raw);
}
template <ResultCode func(Kernel::Handle*, Kernel::Handle*, VAddr, u32)>
void Wrap() {
Kernel::Handle param_1 = 0;
Kernel::Handle param_2 = 0;
u32 retval = func(&param_1, &param_2, Param(2), Param(3)).raw;
Core::CPU().SetReg(1, param_1);
Core::CPU().SetReg(2, param_2);
FuncReturn(retval);
}
template <ResultCode func(Kernel::Handle*, Kernel::Handle*)>
void Wrap() {
Kernel::Handle param_1 = 0;
Kernel::Handle param_2 = 0;
u32 retval = func(&param_1, &param_2).raw;
Core::CPU().SetReg(1, param_1);
Core::CPU().SetReg(2, param_2);
FuncReturn(retval);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
template <u32 func()>
void Wrap() {
FuncReturn(func());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type s64
template <s64 func()>
void Wrap() {
FuncReturn64(func());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Function wrappers that return type void
template <void func(s64)>
void Wrap() {
func(((s64)Param(1) << 32) | Param(0));
}
template <void func(VAddr, int len)>
void Wrap() {
func(Param(0), Param(1));
}
template <void func(u8)>
void Wrap() {
func((u8)Param(0));
}
} // namespace HLE

View file

@ -10,13 +10,13 @@
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/function_wrappers.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/ipc.h" #include "core/hle/kernel/ipc.h"
#include "core/hle/kernel/memory.h" #include "core/hle/kernel/memory.h"
@ -28,6 +28,7 @@
#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h" #include "core/hle/kernel/session.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h" #include "core/hle/kernel/timer.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
@ -56,7 +57,7 @@ enum ControlMemoryOperation {
}; };
/// Map application or GSP heap memory /// Map application or GSP heap memory
static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, ResultCode SVC::ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32 operation,
u32 permissions) { u32 permissions) {
LOG_DEBUG(Kernel_SVC, LOG_DEBUG(Kernel_SVC,
"called operation=0x{:08X}, addr0=0x{:08X}, addr1=0x{:08X}, " "called operation=0x{:08X}, addr0=0x{:08X}, addr1=0x{:08X}, "
@ -83,7 +84,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
} }
VMAPermission vma_permissions = (VMAPermission)permissions; VMAPermission vma_permissions = (VMAPermission)permissions;
auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess(); auto& process = *kernel.GetCurrentProcess();
switch (operation & MEMOP_OPERATION_MASK) { switch (operation & MEMOP_OPERATION_MASK) {
case MEMOP_FREE: { case MEMOP_FREE: {
@ -140,8 +141,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static void ExitProcess() { void SVC::ExitProcess() {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Process> current_process = kernel.GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->process_id); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->process_id);
@ -169,19 +169,18 @@ static void ExitProcess() {
// Kill the current thread // Kill the current thread
kernel.GetThreadManager().GetCurrentThread()->Stop(); kernel.GetThreadManager().GetCurrentThread()->Stop();
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
} }
/// Maps a memory block to specified address /// Maps a memory block to specified address
static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { ResultCode SVC::MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
LOG_TRACE(Kernel_SVC, LOG_TRACE(Kernel_SVC,
"called memblock=0x{:08X}, addr=0x{:08X}, mypermissions=0x{:08X}, " "called memblock=0x{:08X}, addr=0x{:08X}, mypermissions=0x{:08X}, "
"otherpermission={}", "otherpermission={}",
handle, addr, permissions, other_permissions); handle, addr, permissions, other_permissions);
SharedPtr<SharedMemory> shared_memory = SharedPtr<SharedMemory> shared_memory =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<SharedMemory>( kernel.GetCurrentProcess()->handle_table.Get<SharedMemory>(handle);
handle);
if (shared_memory == nullptr) if (shared_memory == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -195,8 +194,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
case MemoryPermission::WriteExecute: case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute: case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare: case MemoryPermission::DontCare:
return shared_memory->Map(Core::System::GetInstance().Kernel().GetCurrentProcess().get(), return shared_memory->Map(kernel.GetCurrentProcess().get(), addr, permissions_type,
addr, permissions_type,
static_cast<MemoryPermission>(other_permissions)); static_cast<MemoryPermission>(other_permissions));
default: default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions);
@ -205,12 +203,12 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
return ERR_INVALID_COMBINATION; return ERR_INVALID_COMBINATION;
} }
static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { ResultCode SVC::UnmapMemoryBlock(Handle handle, u32 addr) {
LOG_TRACE(Kernel_SVC, "called memblock=0x{:08X}, addr=0x{:08X}", handle, addr); LOG_TRACE(Kernel_SVC, "called memblock=0x{:08X}, addr=0x{:08X}", handle, addr);
// TODO(Subv): Return E0A01BF5 if the address is not in the application's heap // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
SharedPtr<Process> current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
SharedPtr<SharedMemory> shared_memory = current_process->handle_table.Get<SharedMemory>(handle); SharedPtr<SharedMemory> shared_memory = current_process->handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr) if (shared_memory == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -219,7 +217,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
} }
/// Connect to an OS service given the port name, returns the handle to the port to out /// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) { ResultCode SVC::ConnectToPort(Handle* out_handle, VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address)) if (!Memory::IsValidVirtualAddress(port_name_address))
return ERR_NOT_FOUND; return ERR_NOT_FOUND;
@ -231,8 +229,6 @@ static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) {
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
KernelSystem& kernel = Core::System::GetInstance().Kernel();
auto it = kernel.named_ports.find(port_name); auto it = kernel.named_ports.find(port_name);
if (it == kernel.named_ports.end()) { if (it == kernel.named_ports.end()) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
@ -250,8 +246,7 @@ static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) {
} }
/// Makes a blocking IPC call to an OS service. /// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) { ResultCode SVC::SendSyncRequest(Handle handle) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<ClientSession> session = SharedPtr<ClientSession> session =
kernel.GetCurrentProcess()->handle_table.Get<ClientSession>(handle); kernel.GetCurrentProcess()->handle_table.Get<ClientSession>(handle);
if (session == nullptr) { if (session == nullptr) {
@ -260,20 +255,19 @@ static ResultCode SendSyncRequest(Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
return session->SendSyncRequest(kernel.GetThreadManager().GetCurrentThread()); return session->SendSyncRequest(kernel.GetThreadManager().GetCurrentThread());
} }
/// Close a handle /// Close a handle
static ResultCode CloseHandle(Handle handle) { ResultCode SVC::CloseHandle(Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
return Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Close(handle); return kernel.GetCurrentProcess()->handle_table.Close(handle);
} }
/// Wait for a handle to synchronize, timeout after the specified nanoseconds /// Wait for a handle to synchronize, timeout after the specified nanoseconds
static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle); auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle);
Thread* thread = kernel.GetThreadManager().GetCurrentThread(); Thread* thread = kernel.GetThreadManager().GetCurrentThread();
@ -311,7 +305,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
// don't have to do anything else here. // don't have to do anything else here.
}; };
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
// resumes due to a signal in its wait objects. // resumes due to a signal in its wait objects.
@ -325,9 +319,8 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
} }
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count,
bool wait_all, s64 nano_seconds) { bool wait_all, s64 nano_seconds) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
Thread* thread = kernel.GetThreadManager().GetCurrentThread(); Thread* thread = kernel.GetThreadManager().GetCurrentThread();
if (!Memory::IsValidVirtualAddress(handles_address)) if (!Memory::IsValidVirtualAddress(handles_address))
@ -400,7 +393,7 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand
// The wait_all case does not update the output index. // The wait_all case does not update the output index.
}; };
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
// This value gets set to -1 by default in this case, it is not modified after this. // This value gets set to -1 by default in this case, it is not modified after this.
*out = -1; *out = -1;
@ -460,7 +453,7 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
}; };
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
// signal in one of its wait objects. // signal in one of its wait objects.
@ -498,7 +491,7 @@ static ResultCode ReceiveIPCRequest(SharedPtr<ServerSession> server_session,
} }
/// In a single operation, sends a IPC reply and waits for a new request. /// In a single operation, sends a IPC reply and waits for a new request.
static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count, ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count,
Handle reply_target) { Handle reply_target) {
if (!Memory::IsValidVirtualAddress(handles_address)) if (!Memory::IsValidVirtualAddress(handles_address))
return ERR_INVALID_POINTER; return ERR_INVALID_POINTER;
@ -510,7 +503,6 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_
using ObjectPtr = SharedPtr<WaitObject>; using ObjectPtr = SharedPtr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count); std::vector<ObjectPtr> objects(handle_count);
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Process> current_process = kernel.GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
for (int i = 0; i < handle_count; ++i) { for (int i = 0; i < handle_count; ++i) {
@ -614,7 +606,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
}; };
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
// signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error.
@ -624,8 +616,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_
} }
/// Create an address arbiter (to allocate access to shared resources) /// Create an address arbiter (to allocate access to shared resources)
static ResultCode CreateAddressArbiter(Handle* out_handle) { ResultCode SVC::CreateAddressArbiter(Handle* out_handle) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<AddressArbiter> arbiter = kernel.CreateAddressArbiter(); SharedPtr<AddressArbiter> arbiter = kernel.CreateAddressArbiter();
CASCADE_RESULT(*out_handle, CASCADE_RESULT(*out_handle,
kernel.GetCurrentProcess()->handle_table.Create(std::move(arbiter))); kernel.GetCurrentProcess()->handle_table.Create(std::move(arbiter)));
@ -634,13 +625,10 @@ static ResultCode CreateAddressArbiter(Handle* out_handle) {
} }
/// Arbitrate address /// Arbitrate address
static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, ResultCode SVC::ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, s64 nanoseconds) {
s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}, address=0x{:08X}, type=0x{:08X}, value=0x{:08X}", LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}, address=0x{:08X}, type=0x{:08X}, value=0x{:08X}",
handle, address, type, value); handle, address, type, value);
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<AddressArbiter> arbiter = SharedPtr<AddressArbiter> arbiter =
kernel.GetCurrentProcess()->handle_table.Get<AddressArbiter>(handle); kernel.GetCurrentProcess()->handle_table.Get<AddressArbiter>(handle);
if (arbiter == nullptr) if (arbiter == nullptr)
@ -651,12 +639,12 @@ static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 val
static_cast<ArbitrationType>(type), address, value, nanoseconds); static_cast<ArbitrationType>(type), address, value, nanoseconds);
// TODO(Subv): Identify in which specific cases this call should cause a reschedule. // TODO(Subv): Identify in which specific cases this call should cause a reschedule.
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
return res; return res;
} }
static void Break(u8 break_reason) { void SVC::Break(u8 break_reason) {
LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!");
std::string reason_str; std::string reason_str;
switch (break_reason) { switch (break_reason) {
@ -677,7 +665,7 @@ static void Break(u8 break_reason) {
} }
/// Used to output a message on a debug hardware unit - does nothing on a retail unit /// Used to output a message on a debug hardware unit - does nothing on a retail unit
static void OutputDebugString(VAddr address, int len) { void SVC::OutputDebugString(VAddr address, s32 len) {
if (len <= 0) { if (len <= 0) {
return; return;
} }
@ -688,10 +676,10 @@ static void OutputDebugString(VAddr address, int len) {
} }
/// Get resource limit /// Get resource limit
static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle) { ResultCode SVC::GetResourceLimit(Handle* resource_limit, Handle process_handle) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
SharedPtr<Process> current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
SharedPtr<Process> process = current_process->handle_table.Get<Process>(process_handle); SharedPtr<Process> process = current_process->handle_table.Get<Process>(process_handle);
if (process == nullptr) if (process == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -702,14 +690,13 @@ static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle
} }
/// Get resource limit current values /// Get resource limit current values
static ResultCode GetResourceLimitCurrentValues(VAddr values, Handle resource_limit_handle, ResultCode SVC::GetResourceLimitCurrentValues(VAddr values, Handle resource_limit_handle,
VAddr names, u32 name_count) { VAddr names, u32 name_count) {
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}", LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}",
resource_limit_handle, names, name_count); resource_limit_handle, names, name_count);
SharedPtr<ResourceLimit> resource_limit = SharedPtr<ResourceLimit> resource_limit =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<ResourceLimit>( kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle);
resource_limit_handle);
if (resource_limit == nullptr) if (resource_limit == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -723,14 +710,13 @@ static ResultCode GetResourceLimitCurrentValues(VAddr values, Handle resource_li
} }
/// Get resource limit max values /// Get resource limit max values
static ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, ResultCode SVC::GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, VAddr names,
VAddr names, u32 name_count) { u32 name_count) {
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}", LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}",
resource_limit_handle, names, name_count); resource_limit_handle, names, name_count);
SharedPtr<ResourceLimit> resource_limit = SharedPtr<ResourceLimit> resource_limit =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<ResourceLimit>( kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle);
resource_limit_handle);
if (resource_limit == nullptr) if (resource_limit == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -744,15 +730,15 @@ static ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limi
} }
/// Creates a new thread /// Creates a new thread
static ResultCode CreateThread(Handle* out_handle, u32 priority, u32 entry_point, u32 arg, ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top,
u32 stack_top, s32 processor_id) { u32 priority, s32 processor_id) {
std::string name = fmt::format("thread-{:08X}", entry_point); std::string name = fmt::format("thread-{:08X}", entry_point);
if (priority > ThreadPrioLowest) { if (priority > ThreadPrioLowest) {
return ERR_OUT_OF_RANGE; return ERR_OUT_OF_RANGE;
} }
SharedPtr<Process> current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
SharedPtr<ResourceLimit>& resource_limit = current_process->resource_limit; SharedPtr<ResourceLimit>& resource_limit = current_process->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
@ -782,16 +768,16 @@ static ResultCode CreateThread(Handle* out_handle, u32 priority, u32 entry_point
break; break;
} }
CASCADE_RESULT(SharedPtr<Thread> thread, Core::System::GetInstance().Kernel().CreateThread( CASCADE_RESULT(SharedPtr<Thread> thread,
name, entry_point, priority, arg, processor_id, kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top,
stack_top, *current_process)); *current_process));
thread->context->SetFpscr(FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | thread->context->SetFpscr(FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO |
FPSCR_ROUND_TOZERO); // 0x03C00000 FPSCR_ROUND_TOZERO); // 0x03C00000
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(thread))); CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(thread)));
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
LOG_TRACE(Kernel_SVC, LOG_TRACE(Kernel_SVC,
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
@ -802,17 +788,16 @@ static ResultCode CreateThread(Handle* out_handle, u32 priority, u32 entry_point
} }
/// Called when a thread exits /// Called when a thread exits
static void ExitThread() { void SVC::ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CPU().GetPC()); LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CPU().GetPC());
Core::System::GetInstance().Kernel().GetThreadManager().ExitCurrentThread(); kernel.GetThreadManager().ExitCurrentThread();
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
} }
/// Gets the priority for the specified thread /// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) { ResultCode SVC::GetThreadPriority(u32* priority, Handle handle) {
const SharedPtr<Thread> thread = const SharedPtr<Thread> thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Thread>(handle);
if (thread == nullptr) if (thread == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -821,20 +806,18 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) {
} }
/// Sets the priority for the specified thread /// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Handle handle, u32 priority) { ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
if (priority > ThreadPrioLowest) { if (priority > ThreadPrioLowest) {
return ERR_OUT_OF_RANGE; return ERR_OUT_OF_RANGE;
} }
SharedPtr<Thread> thread = SharedPtr<Thread> thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Thread>(handle);
if (thread == nullptr) if (thread == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
// Note: The kernel uses the current process's resource limit instead of // Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit. // the one from the thread owner's resource limit.
SharedPtr<ResourceLimit>& resource_limit = SharedPtr<ResourceLimit>& resource_limit = kernel.GetCurrentProcess()->resource_limit;
Core::System::GetInstance().Kernel().GetCurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED; return ERR_NOT_AUTHORIZED;
} }
@ -846,15 +829,14 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
for (auto& mutex : thread->pending_mutexes) for (auto& mutex : thread->pending_mutexes)
mutex->UpdatePriority(); mutex->UpdatePriority();
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
/// Create a mutex /// Create a mutex
static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0); SharedPtr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0);
mutex->name = fmt::format("mutex-{:08x}", Core::CPU().GetReg(14)); mutex->name = fmt::format("mutex-{:08x}", system.CPU().GetReg(14));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex))); CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex)));
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}", LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
@ -864,11 +846,9 @@ static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) {
} }
/// Release a mutex /// Release a mutex
static ResultCode ReleaseMutex(Handle handle) { ResultCode SVC::ReleaseMutex(Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle);
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Mutex> mutex = kernel.GetCurrentProcess()->handle_table.Get<Mutex>(handle); SharedPtr<Mutex> mutex = kernel.GetCurrentProcess()->handle_table.Get<Mutex>(handle);
if (mutex == nullptr) if (mutex == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -877,12 +857,11 @@ static ResultCode ReleaseMutex(Handle handle) {
} }
/// Get the ID of the specified process /// Get the ID of the specified process
static ResultCode GetProcessId(u32* process_id, Handle process_handle) { ResultCode SVC::GetProcessId(u32* process_id, Handle process_handle) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
const SharedPtr<Process> process = const SharedPtr<Process> process =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Process>( kernel.GetCurrentProcess()->handle_table.Get<Process>(process_handle);
process_handle);
if (process == nullptr) if (process == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -891,12 +870,11 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
} }
/// Get the ID of the process that owns the specified thread /// Get the ID of the process that owns the specified thread
static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) { ResultCode SVC::GetProcessIdOfThread(u32* process_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
const SharedPtr<Thread> thread = const SharedPtr<Thread> thread =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Thread>( kernel.GetCurrentProcess()->handle_table.Get<Thread>(thread_handle);
thread_handle);
if (thread == nullptr) if (thread == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -909,11 +887,10 @@ static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) {
} }
/// Get the ID for the specified thread. /// Get the ID for the specified thread.
static ResultCode GetThreadId(u32* thread_id, Handle handle) { ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", handle);
const SharedPtr<Thread> thread = const SharedPtr<Thread> thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Thread>(handle);
if (thread == nullptr) if (thread == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -922,11 +899,10 @@ static ResultCode GetThreadId(u32* thread_id, Handle handle) {
} }
/// Creates a semaphore /// Creates a semaphore
static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) { ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Semaphore> semaphore, CASCADE_RESULT(SharedPtr<Semaphore> semaphore,
kernel.CreateSemaphore(initial_count, max_count)); kernel.CreateSemaphore(initial_count, max_count));
semaphore->name = fmt::format("semaphore-{:08x}", Core::CPU().GetReg(14)); semaphore->name = fmt::format("semaphore-{:08x}", system.CPU().GetReg(14));
CASCADE_RESULT(*out_handle, CASCADE_RESULT(*out_handle,
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore))); kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore)));
@ -936,12 +912,11 @@ static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max
} }
/// Releases a certain number of slots in a semaphore /// Releases a certain number of slots in a semaphore
static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { ResultCode SVC::ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
LOG_TRACE(Kernel_SVC, "called release_count={}, handle=0x{:08X}", release_count, handle); LOG_TRACE(Kernel_SVC, "called release_count={}, handle=0x{:08X}", release_count, handle);
SharedPtr<Semaphore> semaphore = SharedPtr<Semaphore> semaphore =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Semaphore>( kernel.GetCurrentProcess()->handle_table.Get<Semaphore>(handle);
handle);
if (semaphore == nullptr) if (semaphore == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -951,11 +926,10 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count)
} }
/// Query process memory /// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, ResultCode SVC::QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info,
Handle process_handle, u32 addr) { Handle process_handle, u32 addr) {
SharedPtr<Process> process = SharedPtr<Process> process =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Process>( kernel.GetCurrentProcess()->handle_table.Get<Process>(process_handle);
process_handle);
if (process == nullptr) if (process == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -975,15 +949,14 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf
} }
/// Query memory /// Query memory
static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) { ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) {
return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr);
} }
/// Create an event /// Create an event
static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Event> evt = kernel.CreateEvent(static_cast<ResetType>(reset_type), SharedPtr<Event> evt = kernel.CreateEvent(static_cast<ResetType>(reset_type),
fmt::format("event-{:08x}", Core::CPU().GetReg(14))); fmt::format("event-{:08x}", system.CPU().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt))); CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@ -992,20 +965,17 @@ static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) {
} }
/// Duplicates a kernel handle /// Duplicates a kernel handle
static ResultCode DuplicateHandle(Handle* out, Handle handle) { ResultCode SVC::DuplicateHandle(Handle* out, Handle handle) {
CASCADE_RESULT( CASCADE_RESULT(*out, kernel.GetCurrentProcess()->handle_table.Duplicate(handle));
*out,
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Duplicate(handle));
LOG_TRACE(Kernel_SVC, "duplicated 0x{:08X} to 0x{:08X}", handle, *out); LOG_TRACE(Kernel_SVC, "duplicated 0x{:08X} to 0x{:08X}", handle, *out);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
/// Signals an event /// Signals an event
static ResultCode SignalEvent(Handle handle) { ResultCode SVC::SignalEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called event=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called event=0x{:08X}", handle);
SharedPtr<Event> evt = SharedPtr<Event> evt = kernel.GetCurrentProcess()->handle_table.Get<Event>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Event>(handle);
if (evt == nullptr) if (evt == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -1015,11 +985,10 @@ static ResultCode SignalEvent(Handle handle) {
} }
/// Clears an event /// Clears an event
static ResultCode ClearEvent(Handle handle) { ResultCode SVC::ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called event=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called event=0x{:08X}", handle);
SharedPtr<Event> evt = SharedPtr<Event> evt = kernel.GetCurrentProcess()->handle_table.Get<Event>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Event>(handle);
if (evt == nullptr) if (evt == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -1028,10 +997,9 @@ static ResultCode ClearEvent(Handle handle) {
} }
/// Creates a timer /// Creates a timer
static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) { ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Timer> timer = kernel.CreateTimer( SharedPtr<Timer> timer = kernel.CreateTimer(
static_cast<ResetType>(reset_type), fmt ::format("timer-{:08x}", Core::CPU().GetReg(14))); static_cast<ResetType>(reset_type), fmt ::format("timer-{:08x}", system.CPU().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer))); CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@ -1040,11 +1008,10 @@ static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) {
} }
/// Clears a timer /// Clears a timer
static ResultCode ClearTimer(Handle handle) { ResultCode SVC::ClearTimer(Handle handle) {
LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle);
SharedPtr<Timer> timer = SharedPtr<Timer> timer = kernel.GetCurrentProcess()->handle_table.Get<Timer>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Timer>(handle);
if (timer == nullptr) if (timer == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -1053,15 +1020,14 @@ static ResultCode ClearTimer(Handle handle) {
} }
/// Starts a timer /// Starts a timer
static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { ResultCode SVC::SetTimer(Handle handle, s64 initial, s64 interval) {
LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle);
if (initial < 0 || interval < 0) { if (initial < 0 || interval < 0) {
return ERR_OUT_OF_RANGE_KERNEL; return ERR_OUT_OF_RANGE_KERNEL;
} }
SharedPtr<Timer> timer = SharedPtr<Timer> timer = kernel.GetCurrentProcess()->handle_table.Get<Timer>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Timer>(handle);
if (timer == nullptr) if (timer == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -1071,11 +1037,10 @@ static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
} }
/// Cancels a timer /// Cancels a timer
static ResultCode CancelTimer(Handle handle) { ResultCode SVC::CancelTimer(Handle handle) {
LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle); LOG_TRACE(Kernel_SVC, "called timer=0x{:08X}", handle);
SharedPtr<Timer> timer = SharedPtr<Timer> timer = kernel.GetCurrentProcess()->handle_table.Get<Timer>(handle);
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Timer>(handle);
if (timer == nullptr) if (timer == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -1085,10 +1050,9 @@ static ResultCode CancelTimer(Handle handle) {
} }
/// Sleep the current thread /// Sleep the current thread
static void SleepThread(s64 nanoseconds) { void SVC::SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
KernelSystem& kernel = Core::System::GetInstance().Kernel();
ThreadManager& thread_manager = kernel.GetThreadManager(); ThreadManager& thread_manager = kernel.GetThreadManager();
// Don't attempt to yield execution if there are no available threads to run, // Don't attempt to yield execution if there are no available threads to run,
@ -1102,20 +1066,20 @@ static void SleepThread(s64 nanoseconds) {
// Create an event to wake the thread up after the specified nanosecond delay has passed // Create an event to wake the thread up after the specified nanosecond delay has passed
thread_manager.GetCurrentThread()->WakeAfterDelay(nanoseconds); thread_manager.GetCurrentThread()->WakeAfterDelay(nanoseconds);
Core::System::GetInstance().PrepareReschedule(); system.PrepareReschedule();
} }
/// This returns the total CPU ticks elapsed since the CPU was powered-on /// This returns the total CPU ticks elapsed since the CPU was powered-on
static s64 GetSystemTick() { s64 SVC::GetSystemTick() {
s64 result = Core::System::GetInstance().CoreTiming().GetTicks(); s64 result = system.CoreTiming().GetTicks();
// Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
// Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
Core::System::GetInstance().CoreTiming().AddTicks(150); system.CoreTiming().AddTicks(150);
return result; return result;
} }
/// Creates a memory block at the specified address with the specified permissions and size /// Creates a memory block at the specified address with the specified permissions and size
static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission, ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
u32 other_permission) { u32 other_permission) {
if (size % Memory::PAGE_SIZE != 0) if (size % Memory::PAGE_SIZE != 0)
return ERR_MISALIGNED_SIZE; return ERR_MISALIGNED_SIZE;
@ -1148,7 +1112,7 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
return ERR_INVALID_ADDRESS; return ERR_INVALID_ADDRESS;
} }
SharedPtr<Process> current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
// When trying to create a memory block with address = 0, // When trying to create a memory block with address = 0,
// if the process has the Shared Device Memory flag in the exheader, // if the process has the Shared Device Memory flag in the exheader,
@ -1158,7 +1122,7 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
if (addr == 0 && current_process->flags.shared_device_mem) if (addr == 0 && current_process->flags.shared_device_mem)
region = current_process->flags.memory_region; region = current_process->flags.memory_region;
shared_memory = Core::System::GetInstance().Kernel().CreateSharedMemory( shared_memory = kernel.CreateSharedMemory(
current_process.get(), size, static_cast<MemoryPermission>(my_permission), current_process.get(), size, static_cast<MemoryPermission>(my_permission),
static_cast<MemoryPermission>(other_permission), addr, region); static_cast<MemoryPermission>(other_permission), addr, region);
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(shared_memory))); CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(shared_memory)));
@ -1167,12 +1131,11 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode CreatePort(Handle* server_port, Handle* client_port, VAddr name_address, ResultCode SVC::CreatePort(Handle* server_port, Handle* client_port, VAddr name_address,
u32 max_sessions) { u32 max_sessions) {
// TODO(Subv): Implement named ports. // TODO(Subv): Implement named ports.
ASSERT_MSG(name_address == 0, "Named ports are currently unimplemented"); ASSERT_MSG(name_address == 0, "Named ports are currently unimplemented");
KernelSystem& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Process> current_process = kernel.GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
auto ports = kernel.CreatePortPair(max_sessions); auto ports = kernel.CreatePortPair(max_sessions);
@ -1187,8 +1150,8 @@ static ResultCode CreatePort(Handle* server_port, Handle* client_port, VAddr nam
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) { ResultCode SVC::CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) {
SharedPtr<Process> current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
SharedPtr<ClientPort> client_port = SharedPtr<ClientPort> client_port =
current_process->handle_table.Get<ClientPort>(client_port_handle); current_process->handle_table.Get<ClientPort>(client_port_handle);
if (client_port == nullptr) if (client_port == nullptr)
@ -1199,8 +1162,7 @@ static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode CreateSession(Handle* server_session, Handle* client_session) { ResultCode SVC::CreateSession(Handle* server_session, Handle* client_session) {
KernelSystem& kernel = Core::System::GetInstance().Kernel();
auto sessions = kernel.CreateSessionPair(); auto sessions = kernel.CreateSessionPair();
SharedPtr<Process> current_process = kernel.GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
@ -1215,8 +1177,8 @@ static ResultCode CreateSession(Handle* server_session, Handle* client_session)
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) { ResultCode SVC::AcceptSession(Handle* out_server_session, Handle server_port_handle) {
SharedPtr<Process> current_process = Core::System::GetInstance().Kernel().GetCurrentProcess(); SharedPtr<Process> current_process = kernel.GetCurrentProcess();
SharedPtr<ServerPort> server_port = SharedPtr<ServerPort> server_port =
current_process->handle_table.Get<ServerPort>(server_port_handle); current_process->handle_table.Get<ServerPort>(server_port_handle);
if (server_port == nullptr) if (server_port == nullptr)
@ -1227,11 +1189,9 @@ static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_h
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { ResultCode SVC::GetSystemInfo(s64* out, u32 type, s32 param) {
LOG_TRACE(Kernel_SVC, "called type={} param={}", type, param); LOG_TRACE(Kernel_SVC, "called type={} param={}", type, param);
KernelSystem& kernel = Core::System::GetInstance().Kernel();
switch ((SystemInfoType)type) { switch ((SystemInfoType)type) {
case SystemInfoType::REGION_MEMORY_USAGE: case SystemInfoType::REGION_MEMORY_USAGE:
switch ((SystemInfoMemUsageRegion)param) { switch ((SystemInfoMemUsageRegion)param) {
@ -1272,12 +1232,11 @@ static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { ResultCode SVC::GetProcessInfo(s64* out, Handle process_handle, u32 type) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} type={}", process_handle, type); LOG_TRACE(Kernel_SVC, "called process=0x{:08X} type={}", process_handle, type);
SharedPtr<Process> process = SharedPtr<Process> process =
Core::System::GetInstance().Kernel().GetCurrentProcess()->handle_table.Get<Process>( kernel.GetCurrentProcess()->handle_table.Get<Process>(process_handle);
process_handle);
if (process == nullptr) if (process == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -1319,79 +1278,69 @@ static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
namespace { const SVC::FunctionDef SVC::SVC_Table[] = {
struct FunctionDef {
using Func = void();
u32 id;
Func* func;
const char* name;
};
} // namespace
static const FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"}, {0x00, nullptr, "Unknown"},
{0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, {0x01, &SVC::Wrap<&SVC::ControlMemory>, "ControlMemory"},
{0x02, HLE::Wrap<QueryMemory>, "QueryMemory"}, {0x02, &SVC::Wrap<&SVC::QueryMemory>, "QueryMemory"},
{0x03, ExitProcess, "ExitProcess"}, {0x03, &SVC::ExitProcess, "ExitProcess"},
{0x04, nullptr, "GetProcessAffinityMask"}, {0x04, nullptr, "GetProcessAffinityMask"},
{0x05, nullptr, "SetProcessAffinityMask"}, {0x05, nullptr, "SetProcessAffinityMask"},
{0x06, nullptr, "GetProcessIdealProcessor"}, {0x06, nullptr, "GetProcessIdealProcessor"},
{0x07, nullptr, "SetProcessIdealProcessor"}, {0x07, nullptr, "SetProcessIdealProcessor"},
{0x08, HLE::Wrap<CreateThread>, "CreateThread"}, {0x08, &SVC::Wrap<&SVC::CreateThread>, "CreateThread"},
{0x09, ExitThread, "ExitThread"}, {0x09, &SVC::ExitThread, "ExitThread"},
{0x0A, HLE::Wrap<SleepThread>, "SleepThread"}, {0x0A, &SVC::Wrap<&SVC::SleepThread>, "SleepThread"},
{0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"}, {0x0B, &SVC::Wrap<&SVC::GetThreadPriority>, "GetThreadPriority"},
{0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"}, {0x0C, &SVC::Wrap<&SVC::SetThreadPriority>, "SetThreadPriority"},
{0x0D, nullptr, "GetThreadAffinityMask"}, {0x0D, nullptr, "GetThreadAffinityMask"},
{0x0E, nullptr, "SetThreadAffinityMask"}, {0x0E, nullptr, "SetThreadAffinityMask"},
{0x0F, nullptr, "GetThreadIdealProcessor"}, {0x0F, nullptr, "GetThreadIdealProcessor"},
{0x10, nullptr, "SetThreadIdealProcessor"}, {0x10, nullptr, "SetThreadIdealProcessor"},
{0x11, nullptr, "GetCurrentProcessorNumber"}, {0x11, nullptr, "GetCurrentProcessorNumber"},
{0x12, nullptr, "Run"}, {0x12, nullptr, "Run"},
{0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, {0x13, &SVC::Wrap<&SVC::CreateMutex>, "CreateMutex"},
{0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, {0x14, &SVC::Wrap<&SVC::ReleaseMutex>, "ReleaseMutex"},
{0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"}, {0x15, &SVC::Wrap<&SVC::CreateSemaphore>, "CreateSemaphore"},
{0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"}, {0x16, &SVC::Wrap<&SVC::ReleaseSemaphore>, "ReleaseSemaphore"},
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, {0x17, &SVC::Wrap<&SVC::CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, {0x18, &SVC::Wrap<&SVC::SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, {0x19, &SVC::Wrap<&SVC::ClearEvent>, "ClearEvent"},
{0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"}, {0x1A, &SVC::Wrap<&SVC::CreateTimer>, "CreateTimer"},
{0x1B, HLE::Wrap<SetTimer>, "SetTimer"}, {0x1B, &SVC::Wrap<&SVC::SetTimer>, "SetTimer"},
{0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"}, {0x1C, &SVC::Wrap<&SVC::CancelTimer>, "CancelTimer"},
{0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"}, {0x1D, &SVC::Wrap<&SVC::ClearTimer>, "ClearTimer"},
{0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, {0x1E, &SVC::Wrap<&SVC::CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, {0x1F, &SVC::Wrap<&SVC::MapMemoryBlock>, "MapMemoryBlock"},
{0x20, HLE::Wrap<UnmapMemoryBlock>, "UnmapMemoryBlock"}, {0x20, &SVC::Wrap<&SVC::UnmapMemoryBlock>, "UnmapMemoryBlock"},
{0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, {0x21, &SVC::Wrap<&SVC::CreateAddressArbiter>, "CreateAddressArbiter"},
{0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"}, {0x22, &SVC::Wrap<&SVC::ArbitrateAddress>, "ArbitrateAddress"},
{0x23, HLE::Wrap<CloseHandle>, "CloseHandle"}, {0x23, &SVC::Wrap<&SVC::CloseHandle>, "CloseHandle"},
{0x24, HLE::Wrap<WaitSynchronization1>, "WaitSynchronization1"}, {0x24, &SVC::Wrap<&SVC::WaitSynchronization1>, "WaitSynchronization1"},
{0x25, HLE::Wrap<WaitSynchronizationN>, "WaitSynchronizationN"}, {0x25, &SVC::Wrap<&SVC::WaitSynchronizationN>, "WaitSynchronizationN"},
{0x26, nullptr, "SignalAndWait"}, {0x26, nullptr, "SignalAndWait"},
{0x27, HLE::Wrap<DuplicateHandle>, "DuplicateHandle"}, {0x27, &SVC::Wrap<&SVC::DuplicateHandle>, "DuplicateHandle"},
{0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"}, {0x28, &SVC::Wrap<&SVC::GetSystemTick>, "GetSystemTick"},
{0x29, nullptr, "GetHandleInfo"}, {0x29, nullptr, "GetHandleInfo"},
{0x2A, HLE::Wrap<GetSystemInfo>, "GetSystemInfo"}, {0x2A, &SVC::Wrap<&SVC::GetSystemInfo>, "GetSystemInfo"},
{0x2B, HLE::Wrap<GetProcessInfo>, "GetProcessInfo"}, {0x2B, &SVC::Wrap<&SVC::GetProcessInfo>, "GetProcessInfo"},
{0x2C, nullptr, "GetThreadInfo"}, {0x2C, nullptr, "GetThreadInfo"},
{0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"}, {0x2D, &SVC::Wrap<&SVC::ConnectToPort>, "ConnectToPort"},
{0x2E, nullptr, "SendSyncRequest1"}, {0x2E, nullptr, "SendSyncRequest1"},
{0x2F, nullptr, "SendSyncRequest2"}, {0x2F, nullptr, "SendSyncRequest2"},
{0x30, nullptr, "SendSyncRequest3"}, {0x30, nullptr, "SendSyncRequest3"},
{0x31, nullptr, "SendSyncRequest4"}, {0x31, nullptr, "SendSyncRequest4"},
{0x32, HLE::Wrap<SendSyncRequest>, "SendSyncRequest"}, {0x32, &SVC::Wrap<&SVC::SendSyncRequest>, "SendSyncRequest"},
{0x33, nullptr, "OpenProcess"}, {0x33, nullptr, "OpenProcess"},
{0x34, nullptr, "OpenThread"}, {0x34, nullptr, "OpenThread"},
{0x35, HLE::Wrap<GetProcessId>, "GetProcessId"}, {0x35, &SVC::Wrap<&SVC::GetProcessId>, "GetProcessId"},
{0x36, HLE::Wrap<GetProcessIdOfThread>, "GetProcessIdOfThread"}, {0x36, &SVC::Wrap<&SVC::GetProcessIdOfThread>, "GetProcessIdOfThread"},
{0x37, HLE::Wrap<GetThreadId>, "GetThreadId"}, {0x37, &SVC::Wrap<&SVC::GetThreadId>, "GetThreadId"},
{0x38, HLE::Wrap<GetResourceLimit>, "GetResourceLimit"}, {0x38, &SVC::Wrap<&SVC::GetResourceLimit>, "GetResourceLimit"},
{0x39, HLE::Wrap<GetResourceLimitLimitValues>, "GetResourceLimitLimitValues"}, {0x39, &SVC::Wrap<&SVC::GetResourceLimitLimitValues>, "GetResourceLimitLimitValues"},
{0x3A, HLE::Wrap<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"}, {0x3A, &SVC::Wrap<&SVC::GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
{0x3B, nullptr, "GetThreadContext"}, {0x3B, nullptr, "GetThreadContext"},
{0x3C, HLE::Wrap<Break>, "Break"}, {0x3C, &SVC::Wrap<&SVC::Break>, "Break"},
{0x3D, HLE::Wrap<OutputDebugString>, "OutputDebugString"}, {0x3D, &SVC::Wrap<&SVC::OutputDebugString>, "OutputDebugString"},
{0x3E, nullptr, "ControlPerformanceCounter"}, {0x3E, nullptr, "ControlPerformanceCounter"},
{0x3F, nullptr, "Unknown"}, {0x3F, nullptr, "Unknown"},
{0x40, nullptr, "Unknown"}, {0x40, nullptr, "Unknown"},
@ -1401,15 +1350,15 @@ static const FunctionDef SVC_Table[] = {
{0x44, nullptr, "Unknown"}, {0x44, nullptr, "Unknown"},
{0x45, nullptr, "Unknown"}, {0x45, nullptr, "Unknown"},
{0x46, nullptr, "Unknown"}, {0x46, nullptr, "Unknown"},
{0x47, HLE::Wrap<CreatePort>, "CreatePort"}, {0x47, &SVC::Wrap<&SVC::CreatePort>, "CreatePort"},
{0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"}, {0x48, &SVC::Wrap<&SVC::CreateSessionToPort>, "CreateSessionToPort"},
{0x49, HLE::Wrap<CreateSession>, "CreateSession"}, {0x49, &SVC::Wrap<&SVC::CreateSession>, "CreateSession"},
{0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"}, {0x4A, &SVC::Wrap<&SVC::AcceptSession>, "AcceptSession"},
{0x4B, nullptr, "ReplyAndReceive1"}, {0x4B, nullptr, "ReplyAndReceive1"},
{0x4C, nullptr, "ReplyAndReceive2"}, {0x4C, nullptr, "ReplyAndReceive2"},
{0x4D, nullptr, "ReplyAndReceive3"}, {0x4D, nullptr, "ReplyAndReceive3"},
{0x4E, nullptr, "ReplyAndReceive4"}, {0x4E, nullptr, "ReplyAndReceive4"},
{0x4F, HLE::Wrap<ReplyAndReceive>, "ReplyAndReceive"}, {0x4F, &SVC::Wrap<&SVC::ReplyAndReceive>, "ReplyAndReceive"},
{0x50, nullptr, "BindInterrupt"}, {0x50, nullptr, "BindInterrupt"},
{0x51, nullptr, "UnbindInterrupt"}, {0x51, nullptr, "UnbindInterrupt"},
{0x52, nullptr, "InvalidateProcessDataCache"}, {0x52, nullptr, "InvalidateProcessDataCache"},
@ -1455,10 +1404,10 @@ static const FunctionDef SVC_Table[] = {
{0x7A, nullptr, "AddCodeSegment"}, {0x7A, nullptr, "AddCodeSegment"},
{0x7B, nullptr, "Backdoor"}, {0x7B, nullptr, "Backdoor"},
{0x7C, nullptr, "KernelSetState"}, {0x7C, nullptr, "KernelSetState"},
{0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, {0x7D, &SVC::Wrap<&SVC::QueryProcessMemory>, "QueryProcessMemory"},
}; };
static const FunctionDef* GetSVCInfo(u32 func_num) { const SVC::FunctionDef* SVC::GetSVCInfo(u32 func_num) {
if (func_num >= ARRAY_SIZE(SVC_Table)) { if (func_num >= ARRAY_SIZE(SVC_Table)) {
LOG_ERROR(Kernel_SVC, "unknown svc=0x{:02X}", func_num); LOG_ERROR(Kernel_SVC, "unknown svc=0x{:02X}", func_num);
return nullptr; return nullptr;
@ -1468,24 +1417,33 @@ static const FunctionDef* GetSVCInfo(u32 func_num) {
MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
void CallSVC(u32 immediate) { void SVC::CallSVC(u32 immediate) {
MICROPROFILE_SCOPE(Kernel_SVC); MICROPROFILE_SCOPE(Kernel_SVC);
// Lock the global kernel mutex when we enter the kernel HLE. // Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
DEBUG_ASSERT_MSG(Core::System::GetInstance().Kernel().GetCurrentProcess()->status == DEBUG_ASSERT_MSG(kernel.GetCurrentProcess()->status == ProcessStatus::Running,
ProcessStatus::Running,
"Running threads from exiting processes is unimplemented"); "Running threads from exiting processes is unimplemented");
const FunctionDef* info = GetSVCInfo(immediate); const FunctionDef* info = GetSVCInfo(immediate);
if (info) { if (info) {
if (info->func) { if (info->func) {
info->func(); (this->*(info->func))();
} else { } else {
LOG_ERROR(Kernel_SVC, "unimplemented SVC function {}(..)", info->name); LOG_ERROR(Kernel_SVC, "unimplemented SVC function {}(..)", info->name);
} }
} }
} }
SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()) {}
u32 SVC::GetReg(std::size_t n) {
return system.CPU().GetReg(static_cast<int>(n));
}
void SVC::SetReg(std::size_t n, u32 value) {
system.CPU().SetReg(static_cast<int>(n), value);
}
} // namespace Kernel } // namespace Kernel

View file

@ -5,9 +5,20 @@
#pragma once #pragma once
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/svc_wrapper.h"
#include "core/hle/result.h"
class ARM_Interface;
namespace Core {
class System;
} // namespace Core
namespace Kernel { namespace Kernel {
class KernelSystem;
struct MemoryInfo { struct MemoryInfo {
u32 base_address; u32 base_address;
u32 size; u32 size;
@ -48,6 +59,91 @@ enum class SystemInfoMemUsageRegion {
BASE = 3, BASE = 3,
}; };
class SVC : public SVCWrapper<SVC> {
public:
SVC(Core::System& system);
void CallSVC(u32 immediate); void CallSVC(u32 immediate);
private:
Core::System& system;
Kernel::KernelSystem& kernel;
friend class SVCWrapper<SVC>;
// ARM interfaces
u32 GetReg(std::size_t n);
void SetReg(std::size_t n, u32 value);
// SVC interfaces
ResultCode ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32 operation,
u32 permissions);
void ExitProcess();
ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions);
ResultCode UnmapMemoryBlock(Handle handle, u32 addr);
ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address);
ResultCode SendSyncRequest(Handle handle);
ResultCode CloseHandle(Handle handle);
ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds);
ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count,
bool wait_all, s64 nano_seconds);
ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count,
Handle reply_target);
ResultCode CreateAddressArbiter(Handle* out_handle);
ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, s64 nanoseconds);
void Break(u8 break_reason);
void OutputDebugString(VAddr address, s32 len);
ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle);
ResultCode GetResourceLimitCurrentValues(VAddr values, Handle resource_limit_handle,
VAddr names, u32 name_count);
ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, VAddr names,
u32 name_count);
ResultCode CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top,
u32 priority, s32 processor_id);
void ExitThread();
ResultCode GetThreadPriority(u32* priority, Handle handle);
ResultCode SetThreadPriority(Handle handle, u32 priority);
ResultCode CreateMutex(Handle* out_handle, u32 initial_locked);
ResultCode ReleaseMutex(Handle handle);
ResultCode GetProcessId(u32* process_id, Handle process_handle);
ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle);
ResultCode GetThreadId(u32* thread_id, Handle handle);
ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count);
ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info,
Handle process_handle, u32 addr);
ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr);
ResultCode CreateEvent(Handle* out_handle, u32 reset_type);
ResultCode DuplicateHandle(Handle* out, Handle handle);
ResultCode SignalEvent(Handle handle);
ResultCode ClearEvent(Handle handle);
ResultCode CreateTimer(Handle* out_handle, u32 reset_type);
ResultCode ClearTimer(Handle handle);
ResultCode SetTimer(Handle handle, s64 initial, s64 interval);
ResultCode CancelTimer(Handle handle);
void SleepThread(s64 nanoseconds);
s64 GetSystemTick();
ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
u32 other_permission);
ResultCode CreatePort(Handle* server_port, Handle* client_port, VAddr name_address,
u32 max_sessions);
ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle);
ResultCode CreateSession(Handle* server_session, Handle* client_session);
ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle);
ResultCode GetSystemInfo(s64* out, u32 type, s32 param);
ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type);
struct FunctionDef {
using Func = void (SVC::*)();
u32 id;
Func func;
const char* name;
};
static const FunctionDef SVC_Table[];
static const FunctionDef* GetSVCInfo(u32 func_num);
};
} // namespace Kernel } // namespace Kernel

View file

@ -0,0 +1,294 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstring>
#include <type_traits>
#include "common/common_types.h"
namespace Kernel {
/*
* This class defines the SVC ABI, and provides a wrapper template for translating between ARM
* registers and SVC parameters and return value. The SVC context class should inherit from this
* class using CRTP (`class SVC : public SVCWrapper<SVC> {...}`), and use the Wrap() template to
* convert a SVC function interface to a void()-type function that interacts with registers
* interface GetReg() and SetReg().
*/
template <typename Context>
class SVCWrapper {
protected:
template <auto F>
void Wrap() {
WrapHelper<decltype(F)>::Call(*static_cast<Context*>(this), F);
};
private:
// A special param index represents the return value
static constexpr std::size_t INDEX_RETURN = ~(std::size_t)0;
struct ParamSlot {
// whether the slot is used for a param
bool used;
// index of a param in the function signature, starting from 0, or special value
// INDEX_RETURN
std::size_t param_index;
// index of a 32-bit word within the param
std::size_t word_index;
};
// Size in words of the given type in ARM ABI
template <typename T>
static constexpr std::size_t WordSize() {
static_assert(std::is_trivially_copyable_v<T>);
if constexpr (std::is_pointer_v<T>) {
return 1;
} else if constexpr (std::is_same_v<T, bool>) {
return 1;
} else {
return (sizeof(T) - 1) / 4 + 1;
}
}
// Size in words of the given type in ARM ABI with pointer removed
template <typename T>
static constexpr std::size_t OutputWordSize() {
if constexpr (std::is_pointer_v<T>) {
return WordSize<std::remove_pointer_t<T>>();
} else {
return 0;
}
}
// Describes the register assignments of a SVC
struct SVCABI {
static constexpr std::size_t RegCount = 8;
// Register assignments for input params.
// For example, in[0] records which input param and word r0 stores
std::array<ParamSlot, RegCount> in{};
// Register assignments for output params.
// For example, out[0] records which output param (with pointer removed) and word r0 stores
std::array<ParamSlot, RegCount> out{};
// Search the register assignments for a word of a param
constexpr std::size_t GetRegisterIndex(std::size_t param_index,
std::size_t word_index) const {
for (std::size_t slot = 0; slot < RegCount; ++slot) {
if (in[slot].used && in[slot].param_index == param_index &&
in[slot].word_index == word_index) {
return slot;
}
if (out[slot].used && out[slot].param_index == param_index &&
out[slot].word_index == word_index) {
return slot;
}
}
// should throw, but gcc bugs out here
return 0x12345678;
}
};
// Generates ABI info for a given SVC signature R(Context::)(T...)
template <typename R, typename... T>
static constexpr SVCABI GetSVCABI() {
constexpr std::size_t param_count = sizeof...(T);
std::array<bool, param_count> param_is_output{{std::is_pointer_v<T>...}};
std::array<std::size_t, param_count> param_size{{WordSize<T>()...}};
std::array<std::size_t, param_count> output_param_size{{OutputWordSize<T>()...}};
std::array<bool, param_count> param_need_align{
{(std::is_same_v<T, u64> || std::is_same_v<T, s64>)...}};
// derives ARM ABI, which assigns all params to r0~r3 and then stack
std::array<ParamSlot, 4> armabi_reg{};
std::array<ParamSlot, 4> armabi_stack{};
std::size_t reg_pos = 0;
std::size_t stack_pos = 0;
for (std::size_t i = 0; i < param_count; ++i) {
if (param_need_align[i]) {
reg_pos += reg_pos % 2;
}
for (std::size_t j = 0; j < param_size[i]; ++j) {
if (reg_pos == 4) {
armabi_stack[stack_pos++] = ParamSlot{true, i, j};
} else {
armabi_reg[reg_pos++] = ParamSlot{true, i, j};
}
}
}
// now derives SVC ABI which is a modified version of ARM ABI
SVCABI mod_abi{};
std::size_t out_pos = 0; // next available output register
// assign return value to output registers
if constexpr (!std::is_void_v<R>) {
for (std::size_t j = 0; j < WordSize<R>(); ++j) {
mod_abi.out[out_pos++] = {true, INDEX_RETURN, j};
}
}
// assign output params (pointer-type params) to output registers
for (std::size_t i = 0; i < param_count; ++i) {
if (param_is_output[i]) {
for (std::size_t j = 0; j < output_param_size[i]; ++j) {
mod_abi.out[out_pos++] = ParamSlot{true, i, j};
}
}
}
// assign input params (non-pointer-type params) to input registers
stack_pos = 0; // next available stack param
for (std::size_t k = 0; k < mod_abi.in.size(); ++k) {
if (k < armabi_reg.size() && armabi_reg[k].used &&
!param_is_output[armabi_reg[k].param_index]) {
// If this is within the ARM ABI register range and it is a used input param, use
// the same register position
mod_abi.in[k] = armabi_reg[k];
} else {
// Otherwise, assign it with the next available stack input
// If all stack inputs have been allocated, this would do nothing
// and leaves the slot unused.
// Loop until an input stack param is found
while (stack_pos < armabi_stack.size() &&
(!armabi_stack[stack_pos].used ||
param_is_output[armabi_stack[stack_pos].param_index])) {
++stack_pos;
}
// Reassign the stack param to the free register
if (stack_pos < armabi_stack.size()) {
mod_abi.in[k] = armabi_stack[stack_pos++];
}
}
}
return mod_abi;
}
template <std::size_t param_index, std::size_t word_size, std::size_t... indices>
static constexpr std::array<std::size_t, word_size> GetRegIndicesImpl(
SVCABI abi, std::index_sequence<indices...>) {
return {{abi.GetRegisterIndex(param_index, indices)...}};
}
// Gets assigned register index for the param_index-th param of word_size in a function with
// signature R(Context::)(Ts...)
template <std::size_t param_index, std::size_t word_size, typename R, typename... Ts>
static constexpr std::array<std::size_t, word_size> GetRegIndices() {
constexpr SVCABI abi = GetSVCABI<R, Ts...>();
return GetRegIndicesImpl<param_index, word_size>(abi,
std::make_index_sequence<word_size>{});
}
// Gets the value for the param_index-th param of word_size in a function with signature
// R(Context::)(Ts...)
// T is the param type, which must be a non-pointer as it is an input param.
// Must hold: Ts[param_index] == T
template <std::size_t param_index, typename T, typename R, typename... Ts>
static void GetParam(Context& context, T& value) {
static_assert(!std::is_pointer_v<T>, "T should not be a pointer");
constexpr auto regi = GetRegIndices<param_index, WordSize<T>(), R, Ts...>();
if constexpr (std::is_class_v<T> || std::is_union_v<T>) {
std::array<u32, WordSize<T>()> buf;
for (std::size_t i = 0; i < WordSize<T>(); ++i) {
buf[i] = context.GetReg(regi[i]);
}
std::memcpy(&value, &buf, sizeof(T));
} else if constexpr (WordSize<T>() == 2) {
u64 l = context.GetReg(regi[0]);
u64 h = context.GetReg(regi[1]);
value = static_cast<T>(l | (h << 32));
} else if constexpr (std::is_same_v<T, bool>) {
// TODO: Is this correct or should only test the lowest byte?
value = context.GetReg(regi[0]) != 0;
} else {
value = static_cast<T>(context.GetReg(regi[0]));
}
}
// Sets the value for the param_index-th param of word_size in a function with signature
// R(Context::)(Ts...)
// T is the param type with pointer removed, which was originally a pointer-type output param
// Must hold: (Ts[param_index] == T*) || (R==T && param_index == INDEX_RETURN)
template <std::size_t param_index, typename T, typename R, typename... Ts>
static void SetParam(Context& context, const T& value) {
static_assert(!std::is_pointer_v<T>, "T should have pointer removed before passing in");
constexpr auto regi = GetRegIndices<param_index, WordSize<T>(), R, Ts...>();
if constexpr (std::is_class_v<T> || std::is_union_v<T>) {
std::array<u32, WordSize<T>()> buf;
std::memcpy(&buf, &value, sizeof(T));
for (std::size_t i = 0; i < WordSize<T>(); ++i) {
context.SetReg(regi[i], buf[i]);
}
} else if constexpr (WordSize<T>() == 2) {
u64 u = static_cast<u64>(value);
context.SetReg(regi[0], static_cast<u32>(u & 0xFFFFFFFF));
context.SetReg(regi[1], static_cast<u32>(u >> 32));
} else {
u32 u = static_cast<u32>(value);
context.SetReg(regi[0], u);
}
}
template <typename SVCT, typename R, typename... Ts>
struct WrapPass;
template <typename SVCT, typename R, typename T, typename... Ts>
struct WrapPass<SVCT, R, T, Ts...> {
// Do I/O for the param T in function R(Context::svc)(Us..., T, Ts...) and then move on to
// the next param.
// Us are params whose I/O is already handled.
// T is the current param to do I/O.
// Ts are params whose I/O is not handled yet.
template <typename... Us>
static void Call(Context& context, SVCT svc, Us... u) {
static_assert(std::is_same_v<SVCT, R (Context::*)(Us..., T, Ts...)>);
constexpr std::size_t current_param_index = sizeof...(Us);
if constexpr (std::is_pointer_v<T>) {
using OutputT = std::remove_pointer_t<T>;
OutputT output;
WrapPass<SVCT, R, Ts...>::Call(context, svc, u..., &output);
SetParam<current_param_index, OutputT, R, Us..., T, Ts...>(context, output);
} else {
T input;
GetParam<current_param_index, T, R, Us..., T, Ts...>(context, input);
WrapPass<SVCT, R, Ts...>::Call(context, svc, u..., input);
}
}
};
template <typename SVCT, typename R>
struct WrapPass<SVCT, R /*empty for T, Ts...*/> {
// Call function R(Context::svc)(Us...) and transfer the return value to registers
template <typename... Us>
static void Call(Context& context, SVCT svc, Us... u) {
static_assert(std::is_same_v<SVCT, R (Context::*)(Us...)>);
if constexpr (std::is_void_v<R>) {
(context.*svc)(u...);
} else {
R r = (context.*svc)(u...);
SetParam<INDEX_RETURN, R, R, Us...>(context, r);
}
}
};
template <typename T>
struct WrapHelper;
template <typename R, typename... T>
struct WrapHelper<R (Context::*)(T...)> {
static void Call(Context& context, R (Context::*svc)(T...)) {
WrapPass<decltype(svc), R, T...>::Call(context, svc /*Empty for Us*/);
}
};
};
} // namespace Kernel