citra-emu
/
citra
Archived
1
0
Fork 0

Implement svcGetHandleInfo, svcOpenProcess/Thread, svcGetProcessList (#6243)

* Implement svcGetHandleInfo, svcOpenProcess/Thread, svcGetProcessList

* Apply suggestions

* Add comment to stubbed enum values in svcGetHandleInfo

* Revert u32 -> size_t
This commit is contained in:
PabloMK7 2023-01-15 17:20:54 +01:00 committed by GitHub
parent a298e4969b
commit 9c6035f254
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 145 additions and 12 deletions

View File

@ -224,6 +224,10 @@ public:
/// Retrieves a process from the current list of processes. /// Retrieves a process from the current list of processes.
std::shared_ptr<Process> GetProcessById(u32 process_id) const; std::shared_ptr<Process> GetProcessById(u32 process_id) const;
const std::vector<std::shared_ptr<Process>>& GetProcessList() const {
return process_list;
}
std::shared_ptr<Process> GetCurrentProcess() const; std::shared_ptr<Process> GetCurrentProcess() const;
void SetCurrentProcess(std::shared_ptr<Process> process); void SetCurrentProcess(std::shared_ptr<Process> process);
void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id); void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id);

View File

@ -44,6 +44,7 @@ void Process::serialize(Archive& ar, const unsigned int file_version) {
ar& ideal_processor; ar& ideal_processor;
ar& status; ar& status;
ar& process_id; ar& process_id;
ar& creation_time_ticks;
ar& vm_manager; ar& vm_manager;
ar& memory_used; ar& memory_used;
ar& memory_region; ar& memory_region;
@ -72,6 +73,7 @@ std::shared_ptr<Process> KernelSystem::CreateProcess(std::shared_ptr<CodeSet> co
process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->status = ProcessStatus::Created; process->status = ProcessStatus::Created;
process->process_id = ++next_process_id; process->process_id = ++next_process_id;
process->creation_time_ticks = timing.GetTicks();
process_list.push_back(process); process_list.push_back(process);
return process; return process;

View File

@ -185,6 +185,9 @@ public:
/// The id of this process /// The id of this process
u32 process_id; u32 process_id;
// Creation time in ticks of the process.
u64 creation_time_ticks;
/** /**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
* to this process. * to this process.

View File

@ -80,6 +80,22 @@ struct PageInfo {
u32 flags; u32 flags;
}; };
// Values accepted by svcGetHandleInfo.
enum class HandleInfoType {
/**
* Returns the time in ticks the KProcess referenced by the handle was created.
*/
KPROCESS_ELAPSED_TICKS = 0,
/**
* Get internal refcount for kernel object.
*/
REFERENCE_COUNT = 1,
STUBBED_1 = 2,
STUBBED_2 = 0x32107,
};
/// Values accepted by svcGetSystemInfo's type parameter. /// Values accepted by svcGetSystemInfo's type parameter.
enum class SystemInfoType { enum class SystemInfoType {
/** /**
@ -358,6 +374,8 @@ private:
ResultCode UnmapMemoryBlock(Handle handle, u32 addr); ResultCode UnmapMemoryBlock(Handle handle, u32 addr);
ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address); ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address);
ResultCode SendSyncRequest(Handle handle); ResultCode SendSyncRequest(Handle handle);
ResultCode OpenProcess(Handle* out_handle, u32 process_id);
ResultCode OpenThread(Handle* out_handle, Handle process_handle, u32 thread_id);
ResultCode CloseHandle(Handle handle); ResultCode CloseHandle(Handle handle);
ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds); ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds);
ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count,
@ -399,6 +417,7 @@ private:
ResultCode CancelTimer(Handle handle); ResultCode CancelTimer(Handle handle);
void SleepThread(s64 nanoseconds); void SleepThread(s64 nanoseconds);
s64 GetSystemTick(); s64 GetSystemTick();
ResultCode GetHandleInfo(s64* out, Handle handle, u32 type);
ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission, ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
u32 other_permission); u32 other_permission);
ResultCode CreatePort(Handle* server_port, Handle* client_port, VAddr name_address, ResultCode CreatePort(Handle* server_port, Handle* client_port, VAddr name_address,
@ -409,6 +428,8 @@ private:
ResultCode GetSystemInfo(s64* out, u32 type, s32 param); ResultCode GetSystemInfo(s64* out, u32 type, s32 param);
ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type); ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type);
ResultCode GetThreadInfo(s64* out, Handle thread_handle, u32 type); ResultCode GetThreadInfo(s64* out, Handle thread_handle, u32 type);
ResultCode GetProcessList(s32* process_count, VAddr out_process_array,
s32 out_process_array_count);
ResultCode InvalidateInstructionCacheRange(u32 addr, u32 size); ResultCode InvalidateInstructionCacheRange(u32 addr, u32 size);
ResultCode InvalidateEntireInstructionCache(); ResultCode InvalidateEntireInstructionCache();
u32 ConvertVaToPa(u32 addr); u32 ConvertVaToPa(u32 addr);
@ -594,14 +615,16 @@ ResultCode SVC::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
ResultCode SVC::ConnectToPort(Handle* out_handle, VAddr port_name_address) { ResultCode SVC::ConnectToPort(Handle* out_handle, VAddr port_name_address) {
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address)) if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address)) {
return ERR_NOT_FOUND; return ERR_NOT_FOUND;
}
static constexpr std::size_t PortNameMaxLength = 11; static constexpr std::size_t PortNameMaxLength = 11;
// Read 1 char beyond the max allowed port name to detect names that are too long. // 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); std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
if (port_name.size() > PortNameMaxLength) if (port_name.size() > PortNameMaxLength) {
return ERR_PORT_NAME_TOO_LONG; return ERR_PORT_NAME_TOO_LONG;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@ -642,6 +665,50 @@ ResultCode SVC::SendSyncRequest(Handle handle) {
return session->SendSyncRequest(thread); return session->SendSyncRequest(thread);
} }
ResultCode SVC::OpenProcess(Handle* out_handle, u32 process_id) {
std::shared_ptr<Process> process = kernel.GetProcessById(process_id);
if (!process) {
// Result 0xd9001818 (process not found?)
return ResultCode(24, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
auto result_handle = kernel.GetCurrentProcess()->handle_table.Create(process);
if (result_handle.empty()) {
return result_handle.Code();
}
*out_handle = result_handle.Unwrap();
return RESULT_SUCCESS;
}
ResultCode SVC::OpenThread(Handle* out_handle, Handle process_handle, u32 thread_id) {
if (process_handle == 0) {
LOG_ERROR(Kernel_SVC, "Uninplemented svcOpenThread process_handle=0");
// Result 0xd9001819 (thread not found?)
return ResultCode(25, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
std::shared_ptr<Process> process =
kernel.GetCurrentProcess()->handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
for (u32 core_id = 0; core_id < system.GetNumCores(); core_id++) {
auto& thread_list = kernel.GetThreadManager(core_id).GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process.lock() == process && thread.get()->thread_id == thread_id) {
auto result_handle = kernel.GetCurrentProcess()->handle_table.Create(thread);
if (result_handle.empty()) {
return result_handle.Code();
}
*out_handle = result_handle.Unwrap();
return RESULT_SUCCESS;
}
}
}
// Result 0xd9001819 (thread not found?)
return ResultCode(25, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
/// Close a handle /// Close a handle
ResultCode SVC::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);
@ -761,16 +828,18 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
bool wait_all, s64 nano_seconds) { bool wait_all, s64 nano_seconds) {
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread(); Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) {
return ERR_INVALID_POINTER; return ERR_INVALID_POINTER;
}
// NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
// this happens, the running application will crash. // this happens, the running application will crash.
ASSERT_MSG(out != nullptr, "invalid output pointer specified!"); ASSERT_MSG(out != nullptr, "invalid output pointer specified!");
// Check if 'handle_count' is invalid // Check if 'handle_count' is invalid
if (handle_count < 0) if (handle_count < 0) {
return ERR_OUT_OF_RANGE; return ERR_OUT_OF_RANGE;
}
using ObjectPtr = std::shared_ptr<WaitObject>; using ObjectPtr = std::shared_ptr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count); std::vector<ObjectPtr> objects(handle_count);
@ -907,12 +976,14 @@ static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::Memory
/// 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.
ResultCode SVC::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(*kernel.GetCurrentProcess(), handles_address)) if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) {
return ERR_INVALID_POINTER; return ERR_INVALID_POINTER;
}
// Check if 'handle_count' is invalid // Check if 'handle_count' is invalid
if (handle_count < 0) if (handle_count < 0) {
return ERR_OUT_OF_RANGE; return ERR_OUT_OF_RANGE;
}
using ObjectPtr = std::shared_ptr<WaitObject>; using ObjectPtr = std::shared_ptr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count); std::vector<ObjectPtr> objects(handle_count);
@ -1522,6 +1593,40 @@ s64 SVC::GetSystemTick() {
return result; return result;
} }
// Returns information of the specified handle
ResultCode SVC::GetHandleInfo(s64* out, Handle handle, u32 type) {
std::shared_ptr<Object> KObject = kernel.GetCurrentProcess()->handle_table.GetGeneric(handle);
if (!KObject) {
return ERR_INVALID_HANDLE;
}
// Not initialized in real kernel, but we don't want to leak memory.
s64 value = 0;
std::shared_ptr<Process> process;
switch (static_cast<HandleInfoType>(type)) {
case HandleInfoType::KPROCESS_ELAPSED_TICKS:
process = DynamicObjectCast<Process>(KObject);
if (process) {
value = process->creation_time_ticks;
}
break;
case HandleInfoType::REFERENCE_COUNT:
// This is the closest approximation we can get without a full KObject impl.
value = KObject.use_count() - 1;
break;
// These values are stubbed in real kernel, they do nothing.
case HandleInfoType::STUBBED_1:
case HandleInfoType::STUBBED_2:
break;
default:
return ERR_INVALID_ENUM_VALUE;
}
*out = value;
return RESULT_SUCCESS;
}
/// 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
ResultCode SVC::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) {
@ -1833,6 +1938,25 @@ ResultCode SVC::GetThreadInfo(s64* out, Handle thread_handle, u32 type) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode SVC::GetProcessList(s32* process_count, VAddr out_process_array,
s32 out_process_array_count) {
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), out_process_array)) {
return ERR_INVALID_POINTER;
}
s32 written = 0;
for (const auto process : kernel.GetProcessList()) {
if (written >= out_process_array_count) {
break;
}
if (process) {
memory.Write32(out_process_array + written++ * sizeof(u32), process->process_id);
}
}
*process_count = written;
return RESULT_SUCCESS;
}
ResultCode SVC::InvalidateInstructionCacheRange(u32 addr, u32 size) { ResultCode SVC::InvalidateInstructionCacheRange(u32 addr, u32 size) {
Core::GetRunningCore().InvalidateCacheRange(addr, size); Core::GetRunningCore().InvalidateCacheRange(addr, size);
return RESULT_SUCCESS; return RESULT_SUCCESS;
@ -1952,8 +2076,8 @@ ResultCode SVC::ControlProcess(Handle process_handle, u32 process_OP, u32 varg2,
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
case ControlProcessOP::PROCESSOP_SCHEDULE_THREADS_WITHOUT_TLS_MAGIC: { case ControlProcessOP::PROCESSOP_SCHEDULE_THREADS_WITHOUT_TLS_MAGIC: {
for (u32 i = 0; i < system.GetNumCores(); i++) { for (u32 core_id = 0; core_id < system.GetNumCores(); core_id++) {
auto& thread_list = kernel.GetThreadManager(i).GetThreadList(); auto& thread_list = kernel.GetThreadManager(core_id).GetThreadList();
for (auto& thread : thread_list) { for (auto& thread : thread_list) {
if (thread->owner_process.lock() != process) { if (thread->owner_process.lock() != process) {
continue; continue;
@ -2026,7 +2150,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
{0x26, nullptr, "SignalAndWait"}, {0x26, nullptr, "SignalAndWait"},
{0x27, &SVC::Wrap<&SVC::DuplicateHandle>, "DuplicateHandle"}, {0x27, &SVC::Wrap<&SVC::DuplicateHandle>, "DuplicateHandle"},
{0x28, &SVC::Wrap<&SVC::GetSystemTick>, "GetSystemTick"}, {0x28, &SVC::Wrap<&SVC::GetSystemTick>, "GetSystemTick"},
{0x29, nullptr, "GetHandleInfo"}, {0x29, &SVC::Wrap<&SVC::GetHandleInfo>, "GetHandleInfo"},
{0x2A, &SVC::Wrap<&SVC::GetSystemInfo>, "GetSystemInfo"}, {0x2A, &SVC::Wrap<&SVC::GetSystemInfo>, "GetSystemInfo"},
{0x2B, &SVC::Wrap<&SVC::GetProcessInfo>, "GetProcessInfo"}, {0x2B, &SVC::Wrap<&SVC::GetProcessInfo>, "GetProcessInfo"},
{0x2C, &SVC::Wrap<&SVC::GetThreadInfo>, "GetThreadInfo"}, {0x2C, &SVC::Wrap<&SVC::GetThreadInfo>, "GetThreadInfo"},
@ -2036,8 +2160,8 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
{0x30, nullptr, "SendSyncRequest3"}, {0x30, nullptr, "SendSyncRequest3"},
{0x31, nullptr, "SendSyncRequest4"}, {0x31, nullptr, "SendSyncRequest4"},
{0x32, &SVC::Wrap<&SVC::SendSyncRequest>, "SendSyncRequest"}, {0x32, &SVC::Wrap<&SVC::SendSyncRequest>, "SendSyncRequest"},
{0x33, nullptr, "OpenProcess"}, {0x33, &SVC::Wrap<&SVC::OpenProcess>, "OpenProcess"},
{0x34, nullptr, "OpenThread"}, {0x34, &SVC::Wrap<&SVC::OpenThread>, "OpenThread"},
{0x35, &SVC::Wrap<&SVC::GetProcessId>, "GetProcessId"}, {0x35, &SVC::Wrap<&SVC::GetProcessId>, "GetProcessId"},
{0x36, &SVC::Wrap<&SVC::GetProcessIdOfThread>, "GetProcessIdOfThread"}, {0x36, &SVC::Wrap<&SVC::GetProcessIdOfThread>, "GetProcessIdOfThread"},
{0x37, &SVC::Wrap<&SVC::GetThreadId>, "GetThreadId"}, {0x37, &SVC::Wrap<&SVC::GetThreadId>, "GetThreadId"},
@ -2086,7 +2210,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
{0x62, nullptr, "TerminateDebugProcess"}, {0x62, nullptr, "TerminateDebugProcess"},
{0x63, nullptr, "GetProcessDebugEvent"}, {0x63, nullptr, "GetProcessDebugEvent"},
{0x64, nullptr, "ContinueDebugEvent"}, {0x64, nullptr, "ContinueDebugEvent"},
{0x65, nullptr, "GetProcessList"}, {0x65, &SVC::Wrap<&SVC::GetProcessList>, "GetProcessList"},
{0x66, nullptr, "GetThreadList"}, {0x66, nullptr, "GetThreadList"},
{0x67, nullptr, "GetDebugThreadContext"}, {0x67, nullptr, "GetDebugThreadContext"},
{0x68, nullptr, "SetDebugThreadContext"}, {0x68, nullptr, "SetDebugThreadContext"},